Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * An RTC test device/driver
0004  * Copyright (C) 2005 Tower Technologies
0005  * Author: Alessandro Zummo <a.zummo@towertech.it>
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/err.h>
0010 #include <linux/rtc.h>
0011 #include <linux/platform_device.h>
0012 
0013 #define MAX_RTC_TEST 3
0014 
0015 struct rtc_test_data {
0016     struct rtc_device *rtc;
0017     time64_t offset;
0018     struct timer_list alarm;
0019     bool alarm_en;
0020 };
0021 
0022 static struct platform_device *pdev[MAX_RTC_TEST];
0023 
0024 static int test_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
0025 {
0026     struct rtc_test_data *rtd = dev_get_drvdata(dev);
0027     time64_t alarm;
0028 
0029     alarm = (rtd->alarm.expires - jiffies) / HZ;
0030     alarm += ktime_get_real_seconds() + rtd->offset;
0031 
0032     rtc_time64_to_tm(alarm, &alrm->time);
0033     alrm->enabled = rtd->alarm_en;
0034 
0035     return 0;
0036 }
0037 
0038 static int test_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
0039 {
0040     struct rtc_test_data *rtd = dev_get_drvdata(dev);
0041     ktime_t timeout;
0042     u64 expires;
0043 
0044     timeout = rtc_tm_to_time64(&alrm->time) - ktime_get_real_seconds();
0045     timeout -= rtd->offset;
0046 
0047     del_timer(&rtd->alarm);
0048 
0049     expires = jiffies + timeout * HZ;
0050     if (expires > U32_MAX)
0051         expires = U32_MAX;
0052 
0053     rtd->alarm.expires = expires;
0054 
0055     if (alrm->enabled)
0056         add_timer(&rtd->alarm);
0057 
0058     rtd->alarm_en = alrm->enabled;
0059 
0060     return 0;
0061 }
0062 
0063 static int test_rtc_read_time(struct device *dev, struct rtc_time *tm)
0064 {
0065     struct rtc_test_data *rtd = dev_get_drvdata(dev);
0066 
0067     rtc_time64_to_tm(ktime_get_real_seconds() + rtd->offset, tm);
0068 
0069     return 0;
0070 }
0071 
0072 static int test_rtc_set_time(struct device *dev, struct rtc_time *tm)
0073 {
0074     struct rtc_test_data *rtd = dev_get_drvdata(dev);
0075 
0076     rtd->offset = rtc_tm_to_time64(tm) - ktime_get_real_seconds();
0077 
0078     return 0;
0079 }
0080 
0081 static int test_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
0082 {
0083     struct rtc_test_data *rtd = dev_get_drvdata(dev);
0084 
0085     rtd->alarm_en = enable;
0086     if (enable)
0087         add_timer(&rtd->alarm);
0088     else
0089         del_timer(&rtd->alarm);
0090 
0091     return 0;
0092 }
0093 
0094 static const struct rtc_class_ops test_rtc_ops_noalm = {
0095     .read_time = test_rtc_read_time,
0096     .set_time = test_rtc_set_time,
0097     .alarm_irq_enable = test_rtc_alarm_irq_enable,
0098 };
0099 
0100 static const struct rtc_class_ops test_rtc_ops = {
0101     .read_time = test_rtc_read_time,
0102     .set_time = test_rtc_set_time,
0103     .read_alarm = test_rtc_read_alarm,
0104     .set_alarm = test_rtc_set_alarm,
0105     .alarm_irq_enable = test_rtc_alarm_irq_enable,
0106 };
0107 
0108 static void test_rtc_alarm_handler(struct timer_list *t)
0109 {
0110     struct rtc_test_data *rtd = from_timer(rtd, t, alarm);
0111 
0112     rtc_update_irq(rtd->rtc, 1, RTC_AF | RTC_IRQF);
0113 }
0114 
0115 static int test_probe(struct platform_device *plat_dev)
0116 {
0117     struct rtc_test_data *rtd;
0118 
0119     rtd = devm_kzalloc(&plat_dev->dev, sizeof(*rtd), GFP_KERNEL);
0120     if (!rtd)
0121         return -ENOMEM;
0122 
0123     platform_set_drvdata(plat_dev, rtd);
0124 
0125     rtd->rtc = devm_rtc_allocate_device(&plat_dev->dev);
0126     if (IS_ERR(rtd->rtc))
0127         return PTR_ERR(rtd->rtc);
0128 
0129     switch (plat_dev->id) {
0130     case 0:
0131         rtd->rtc->ops = &test_rtc_ops_noalm;
0132         break;
0133     default:
0134         rtd->rtc->ops = &test_rtc_ops;
0135         device_init_wakeup(&plat_dev->dev, 1);
0136     }
0137 
0138     timer_setup(&rtd->alarm, test_rtc_alarm_handler, 0);
0139     rtd->alarm.expires = 0;
0140 
0141     return devm_rtc_register_device(rtd->rtc);
0142 }
0143 
0144 static struct platform_driver test_driver = {
0145     .probe  = test_probe,
0146     .driver = {
0147         .name = "rtc-test",
0148     },
0149 };
0150 
0151 static int __init test_init(void)
0152 {
0153     int i, err;
0154 
0155     err = platform_driver_register(&test_driver);
0156     if (err)
0157         return err;
0158 
0159     err = -ENOMEM;
0160     for (i = 0; i < MAX_RTC_TEST; i++) {
0161         pdev[i] = platform_device_alloc("rtc-test", i);
0162         if (!pdev[i])
0163             goto exit_free_mem;
0164     }
0165 
0166     for (i = 0; i < MAX_RTC_TEST; i++) {
0167         err = platform_device_add(pdev[i]);
0168         if (err)
0169             goto exit_device_del;
0170     }
0171 
0172     return 0;
0173 
0174 exit_device_del:
0175     for (; i > 0; i--)
0176         platform_device_del(pdev[i - 1]);
0177 
0178 exit_free_mem:
0179     for (i = 0; i < MAX_RTC_TEST; i++)
0180         platform_device_put(pdev[i]);
0181 
0182     platform_driver_unregister(&test_driver);
0183     return err;
0184 }
0185 
0186 static void __exit test_exit(void)
0187 {
0188     int i;
0189 
0190     for (i = 0; i < MAX_RTC_TEST; i++)
0191         platform_device_unregister(pdev[i]);
0192 
0193     platform_driver_unregister(&test_driver);
0194 }
0195 
0196 MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
0197 MODULE_DESCRIPTION("RTC test driver/device");
0198 MODULE_LICENSE("GPL v2");
0199 
0200 module_init(test_init);
0201 module_exit(test_exit);