1c7080e20SAlexandre Belloni // SPDX-License-Identifier: GPL-2.0 2a95579cdSAlessandro Zummo /* 3a95579cdSAlessandro Zummo * An RTC test device/driver 4a95579cdSAlessandro Zummo * Copyright (C) 2005 Tower Technologies 5a95579cdSAlessandro Zummo * Author: Alessandro Zummo <a.zummo@towertech.it> 6a95579cdSAlessandro Zummo */ 7a95579cdSAlessandro Zummo 8a95579cdSAlessandro Zummo #include <linux/module.h> 9a95579cdSAlessandro Zummo #include <linux/err.h> 10a95579cdSAlessandro Zummo #include <linux/rtc.h> 11a95579cdSAlessandro Zummo #include <linux/platform_device.h> 12a95579cdSAlessandro Zummo 135b257571SAlexandre Belloni #define MAX_RTC_TEST 3 145b257571SAlexandre Belloni 154dc2403bSAlexandre Belloni struct rtc_test_data { 164dc2403bSAlexandre Belloni struct rtc_device *rtc; 174dc2403bSAlexandre Belloni time64_t offset; 188be09029SAlexandre Belloni struct timer_list alarm; 198be09029SAlexandre Belloni bool alarm_en; 204dc2403bSAlexandre Belloni }; 214dc2403bSAlexandre Belloni 222b4f07e9SColin Ian King static struct platform_device *pdev[MAX_RTC_TEST]; 23a95579cdSAlessandro Zummo 248be09029SAlexandre Belloni static int test_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 25a95579cdSAlessandro Zummo { 268be09029SAlexandre Belloni struct rtc_test_data *rtd = dev_get_drvdata(dev); 278be09029SAlexandre Belloni time64_t alarm; 288be09029SAlexandre Belloni 298be09029SAlexandre Belloni alarm = (rtd->alarm.expires - jiffies) / HZ; 308be09029SAlexandre Belloni alarm += ktime_get_real_seconds() + rtd->offset; 318be09029SAlexandre Belloni 328be09029SAlexandre Belloni rtc_time64_to_tm(alarm, &alrm->time); 338be09029SAlexandre Belloni alrm->enabled = rtd->alarm_en; 348be09029SAlexandre Belloni 35a95579cdSAlessandro Zummo return 0; 36a95579cdSAlessandro Zummo } 37a95579cdSAlessandro Zummo 388be09029SAlexandre Belloni static int test_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 39a95579cdSAlessandro Zummo { 408be09029SAlexandre Belloni struct rtc_test_data *rtd = dev_get_drvdata(dev); 418be09029SAlexandre Belloni ktime_t timeout; 428be09029SAlexandre Belloni u64 expires; 438be09029SAlexandre Belloni 448be09029SAlexandre Belloni timeout = rtc_tm_to_time64(&alrm->time) - ktime_get_real_seconds(); 458be09029SAlexandre Belloni timeout -= rtd->offset; 468be09029SAlexandre Belloni 478be09029SAlexandre Belloni del_timer(&rtd->alarm); 488be09029SAlexandre Belloni 498be09029SAlexandre Belloni expires = jiffies + timeout * HZ; 508be09029SAlexandre Belloni if (expires > U32_MAX) 518be09029SAlexandre Belloni expires = U32_MAX; 528be09029SAlexandre Belloni 538be09029SAlexandre Belloni pr_err("ABE: %s +%d %s\n", __FILE__, __LINE__, __func__); 548be09029SAlexandre Belloni rtd->alarm.expires = expires; 558be09029SAlexandre Belloni 568be09029SAlexandre Belloni if (alrm->enabled) 578be09029SAlexandre Belloni add_timer(&rtd->alarm); 588be09029SAlexandre Belloni 598be09029SAlexandre Belloni rtd->alarm_en = alrm->enabled; 608be09029SAlexandre Belloni 61a95579cdSAlessandro Zummo return 0; 62a95579cdSAlessandro Zummo } 63a95579cdSAlessandro Zummo 644dc2403bSAlexandre Belloni static int test_rtc_read_time(struct device *dev, struct rtc_time *tm) 65a95579cdSAlessandro Zummo { 664dc2403bSAlexandre Belloni struct rtc_test_data *rtd = dev_get_drvdata(dev); 674dc2403bSAlexandre Belloni 684dc2403bSAlexandre Belloni rtc_time64_to_tm(ktime_get_real_seconds() + rtd->offset, tm); 694dc2403bSAlexandre Belloni 704d644ab8SXunlei Pang return 0; 714d644ab8SXunlei Pang } 724d644ab8SXunlei Pang 7343dae505SAlexandre Belloni static int test_rtc_set_time(struct device *dev, struct rtc_time *tm) 744d644ab8SXunlei Pang { 754dc2403bSAlexandre Belloni struct rtc_test_data *rtd = dev_get_drvdata(dev); 764dc2403bSAlexandre Belloni 7743dae505SAlexandre Belloni rtd->offset = rtc_tm_to_time64(tm) - ktime_get_real_seconds(); 784dc2403bSAlexandre Belloni 79a95579cdSAlessandro Zummo return 0; 80a95579cdSAlessandro Zummo } 81a95579cdSAlessandro Zummo 8216380c15SJohn Stultz static int test_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 83a95579cdSAlessandro Zummo { 848be09029SAlexandre Belloni struct rtc_test_data *rtd = dev_get_drvdata(dev); 858be09029SAlexandre Belloni 868be09029SAlexandre Belloni rtd->alarm_en = enable; 878be09029SAlexandre Belloni if (enable) 888be09029SAlexandre Belloni add_timer(&rtd->alarm); 898be09029SAlexandre Belloni else 908be09029SAlexandre Belloni del_timer(&rtd->alarm); 918be09029SAlexandre Belloni 92a95579cdSAlessandro Zummo return 0; 93a95579cdSAlessandro Zummo } 94a95579cdSAlessandro Zummo 951928906dSAlexandre Belloni static const struct rtc_class_ops test_rtc_ops_noalm = { 961928906dSAlexandre Belloni .read_time = test_rtc_read_time, 9743dae505SAlexandre Belloni .set_time = test_rtc_set_time, 981928906dSAlexandre Belloni .alarm_irq_enable = test_rtc_alarm_irq_enable, 991928906dSAlexandre Belloni }; 1001928906dSAlexandre Belloni 10178417682SAlexandre Belloni static const struct rtc_class_ops test_rtc_ops = { 102a95579cdSAlessandro Zummo .read_time = test_rtc_read_time, 10343dae505SAlexandre Belloni .set_time = test_rtc_set_time, 104a95579cdSAlessandro Zummo .read_alarm = test_rtc_read_alarm, 105a95579cdSAlessandro Zummo .set_alarm = test_rtc_set_alarm, 10616380c15SJohn Stultz .alarm_irq_enable = test_rtc_alarm_irq_enable, 107a95579cdSAlessandro Zummo }; 108a95579cdSAlessandro Zummo 1098be09029SAlexandre Belloni static void test_rtc_alarm_handler(struct timer_list *t) 1108be09029SAlexandre Belloni { 1118be09029SAlexandre Belloni struct rtc_test_data *rtd = from_timer(rtd, t, alarm); 1128be09029SAlexandre Belloni 1138be09029SAlexandre Belloni rtc_update_irq(rtd->rtc, 1, RTC_AF | RTC_IRQF); 1148be09029SAlexandre Belloni } 1158be09029SAlexandre Belloni 116a95579cdSAlessandro Zummo static int test_probe(struct platform_device *plat_dev) 117a95579cdSAlessandro Zummo { 1184dc2403bSAlexandre Belloni struct rtc_test_data *rtd; 119dd8d8137SJingoo Han 1204dc2403bSAlexandre Belloni rtd = devm_kzalloc(&plat_dev->dev, sizeof(*rtd), GFP_KERNEL); 1214dc2403bSAlexandre Belloni if (!rtd) 1224dc2403bSAlexandre Belloni return -ENOMEM; 1234dc2403bSAlexandre Belloni 1244dc2403bSAlexandre Belloni platform_set_drvdata(plat_dev, rtd); 1254dc2403bSAlexandre Belloni 1260b472ad2SAlexandre Belloni rtd->rtc = devm_rtc_allocate_device(&plat_dev->dev); 1274dc2403bSAlexandre Belloni if (IS_ERR(rtd->rtc)) 1284dc2403bSAlexandre Belloni return PTR_ERR(rtd->rtc); 129a95579cdSAlessandro Zummo 1301928906dSAlexandre Belloni switch (plat_dev->id) { 1311928906dSAlexandre Belloni case 0: 1321928906dSAlexandre Belloni rtd->rtc->ops = &test_rtc_ops_noalm; 1331928906dSAlexandre Belloni break; 1341928906dSAlexandre Belloni default: 1350b472ad2SAlexandre Belloni rtd->rtc->ops = &test_rtc_ops; 136*c19623dbSRoman Stratiienko device_init_wakeup(&plat_dev->dev, 1); 1371928906dSAlexandre Belloni } 1380b472ad2SAlexandre Belloni 1398be09029SAlexandre Belloni timer_setup(&rtd->alarm, test_rtc_alarm_handler, 0); 1408be09029SAlexandre Belloni rtd->alarm.expires = 0; 1418be09029SAlexandre Belloni 1420b472ad2SAlexandre Belloni return rtc_register_device(rtd->rtc); 143a95579cdSAlessandro Zummo } 144a95579cdSAlessandro Zummo 145c4646528SSam Ravnborg static struct platform_driver test_driver = { 146a95579cdSAlessandro Zummo .probe = test_probe, 147a95579cdSAlessandro Zummo .driver = { 148a95579cdSAlessandro Zummo .name = "rtc-test", 149a95579cdSAlessandro Zummo }, 150a95579cdSAlessandro Zummo }; 151a95579cdSAlessandro Zummo 152a95579cdSAlessandro Zummo static int __init test_init(void) 153a95579cdSAlessandro Zummo { 1545b257571SAlexandre Belloni int i, err; 155a95579cdSAlessandro Zummo 156540a11d8SAlexandre Belloni err = platform_driver_register(&test_driver); 157540a11d8SAlexandre Belloni if (err) 158a95579cdSAlessandro Zummo return err; 159a95579cdSAlessandro Zummo 160a95579cdSAlessandro Zummo err = -ENOMEM; 1615b257571SAlexandre Belloni for (i = 0; i < MAX_RTC_TEST; i++) { 1625b257571SAlexandre Belloni pdev[i] = platform_device_alloc("rtc-test", i); 1635b257571SAlexandre Belloni if (!pdev[i]) 1645b257571SAlexandre Belloni goto exit_free_mem; 165a95579cdSAlessandro Zummo } 166a95579cdSAlessandro Zummo 1675b257571SAlexandre Belloni for (i = 0; i < MAX_RTC_TEST; i++) { 1685b257571SAlexandre Belloni err = platform_device_add(pdev[i]); 1695b257571SAlexandre Belloni if (err) 1705b257571SAlexandre Belloni goto exit_device_del; 171a95579cdSAlessandro Zummo } 172a95579cdSAlessandro Zummo 173a95579cdSAlessandro Zummo return 0; 174a95579cdSAlessandro Zummo 1755b257571SAlexandre Belloni exit_device_del: 1765b257571SAlexandre Belloni for (; i > 0; i--) 1775b257571SAlexandre Belloni platform_device_del(pdev[i - 1]); 178a95579cdSAlessandro Zummo 1795b257571SAlexandre Belloni exit_free_mem: 1805b257571SAlexandre Belloni for (i = 0; i < MAX_RTC_TEST; i++) 1815b257571SAlexandre Belloni platform_device_put(pdev[i]); 182a95579cdSAlessandro Zummo 183c4646528SSam Ravnborg platform_driver_unregister(&test_driver); 184a95579cdSAlessandro Zummo return err; 185a95579cdSAlessandro Zummo } 186a95579cdSAlessandro Zummo 187a95579cdSAlessandro Zummo static void __exit test_exit(void) 188a95579cdSAlessandro Zummo { 1895b257571SAlexandre Belloni int i; 1905b257571SAlexandre Belloni 1915b257571SAlexandre Belloni for (i = 0; i < MAX_RTC_TEST; i++) 1925b257571SAlexandre Belloni platform_device_unregister(pdev[i]); 1935b257571SAlexandre Belloni 194c4646528SSam Ravnborg platform_driver_unregister(&test_driver); 195a95579cdSAlessandro Zummo } 196a95579cdSAlessandro Zummo 197a95579cdSAlessandro Zummo MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); 198a95579cdSAlessandro Zummo MODULE_DESCRIPTION("RTC test driver/device"); 199fd13c930SAlexandre Belloni MODULE_LICENSE("GPL v2"); 200a95579cdSAlessandro Zummo 201a95579cdSAlessandro Zummo module_init(test_init); 202a95579cdSAlessandro Zummo module_exit(test_exit); 203