linux/drivers/rtc/rtc-test.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * An RTC test device/driver
   4 * Copyright (C) 2005 Tower Technologies
   5 * Author: Alessandro Zummo <a.zummo@towertech.it>
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/err.h>
  10#include <linux/rtc.h>
  11#include <linux/platform_device.h>
  12
  13#define MAX_RTC_TEST 3
  14
  15struct rtc_test_data {
  16        struct rtc_device *rtc;
  17        time64_t offset;
  18        struct timer_list alarm;
  19        bool alarm_en;
  20};
  21
  22static struct platform_device *pdev[MAX_RTC_TEST];
  23
  24static int test_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
  25{
  26        struct rtc_test_data *rtd = dev_get_drvdata(dev);
  27        time64_t alarm;
  28
  29        alarm = (rtd->alarm.expires - jiffies) / HZ;
  30        alarm += ktime_get_real_seconds() + rtd->offset;
  31
  32        rtc_time64_to_tm(alarm, &alrm->time);
  33        alrm->enabled = rtd->alarm_en;
  34
  35        return 0;
  36}
  37
  38static int test_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
  39{
  40        struct rtc_test_data *rtd = dev_get_drvdata(dev);
  41        ktime_t timeout;
  42        u64 expires;
  43
  44        timeout = rtc_tm_to_time64(&alrm->time) - ktime_get_real_seconds();
  45        timeout -= rtd->offset;
  46
  47        del_timer(&rtd->alarm);
  48
  49        expires = jiffies + timeout * HZ;
  50        if (expires > U32_MAX)
  51                expires = U32_MAX;
  52
  53        pr_err("ABE: %s +%d %s\n", __FILE__, __LINE__, __func__);
  54        rtd->alarm.expires = expires;
  55
  56        if (alrm->enabled)
  57                add_timer(&rtd->alarm);
  58
  59        rtd->alarm_en = alrm->enabled;
  60
  61        return 0;
  62}
  63
  64static int test_rtc_read_time(struct device *dev, struct rtc_time *tm)
  65{
  66        struct rtc_test_data *rtd = dev_get_drvdata(dev);
  67
  68        rtc_time64_to_tm(ktime_get_real_seconds() + rtd->offset, tm);
  69
  70        return 0;
  71}
  72
  73static int test_rtc_set_time(struct device *dev, struct rtc_time *tm)
  74{
  75        struct rtc_test_data *rtd = dev_get_drvdata(dev);
  76
  77        rtd->offset = rtc_tm_to_time64(tm) - ktime_get_real_seconds();
  78
  79        return 0;
  80}
  81
  82static int test_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
  83{
  84        struct rtc_test_data *rtd = dev_get_drvdata(dev);
  85
  86        rtd->alarm_en = enable;
  87        if (enable)
  88                add_timer(&rtd->alarm);
  89        else
  90                del_timer(&rtd->alarm);
  91
  92        return 0;
  93}
  94
  95static const struct rtc_class_ops test_rtc_ops_noalm = {
  96        .read_time = test_rtc_read_time,
  97        .set_time = test_rtc_set_time,
  98        .alarm_irq_enable = test_rtc_alarm_irq_enable,
  99};
 100
 101static const struct rtc_class_ops test_rtc_ops = {
 102        .read_time = test_rtc_read_time,
 103        .set_time = test_rtc_set_time,
 104        .read_alarm = test_rtc_read_alarm,
 105        .set_alarm = test_rtc_set_alarm,
 106        .alarm_irq_enable = test_rtc_alarm_irq_enable,
 107};
 108
 109static void test_rtc_alarm_handler(struct timer_list *t)
 110{
 111        struct rtc_test_data *rtd = from_timer(rtd, t, alarm);
 112
 113        rtc_update_irq(rtd->rtc, 1, RTC_AF | RTC_IRQF);
 114}
 115
 116static int test_probe(struct platform_device *plat_dev)
 117{
 118        struct rtc_test_data *rtd;
 119
 120        rtd = devm_kzalloc(&plat_dev->dev, sizeof(*rtd), GFP_KERNEL);
 121        if (!rtd)
 122                return -ENOMEM;
 123
 124        platform_set_drvdata(plat_dev, rtd);
 125
 126        rtd->rtc = devm_rtc_allocate_device(&plat_dev->dev);
 127        if (IS_ERR(rtd->rtc))
 128                return PTR_ERR(rtd->rtc);
 129
 130        switch (plat_dev->id) {
 131        case 0:
 132                rtd->rtc->ops = &test_rtc_ops_noalm;
 133                break;
 134        default:
 135                rtd->rtc->ops = &test_rtc_ops;
 136                device_init_wakeup(&plat_dev->dev, 1);
 137        }
 138
 139        timer_setup(&rtd->alarm, test_rtc_alarm_handler, 0);
 140        rtd->alarm.expires = 0;
 141
 142        return rtc_register_device(rtd->rtc);
 143}
 144
 145static struct platform_driver test_driver = {
 146        .probe  = test_probe,
 147        .driver = {
 148                .name = "rtc-test",
 149        },
 150};
 151
 152static int __init test_init(void)
 153{
 154        int i, err;
 155
 156        err = platform_driver_register(&test_driver);
 157        if (err)
 158                return err;
 159
 160        err = -ENOMEM;
 161        for (i = 0; i < MAX_RTC_TEST; i++) {
 162                pdev[i] = platform_device_alloc("rtc-test", i);
 163                if (!pdev[i])
 164                        goto exit_free_mem;
 165        }
 166
 167        for (i = 0; i < MAX_RTC_TEST; i++) {
 168                err = platform_device_add(pdev[i]);
 169                if (err)
 170                        goto exit_device_del;
 171        }
 172
 173        return 0;
 174
 175exit_device_del:
 176        for (; i > 0; i--)
 177                platform_device_del(pdev[i - 1]);
 178
 179exit_free_mem:
 180        for (i = 0; i < MAX_RTC_TEST; i++)
 181                platform_device_put(pdev[i]);
 182
 183        platform_driver_unregister(&test_driver);
 184        return err;
 185}
 186
 187static void __exit test_exit(void)
 188{
 189        int i;
 190
 191        for (i = 0; i < MAX_RTC_TEST; i++)
 192                platform_device_unregister(pdev[i]);
 193
 194        platform_driver_unregister(&test_driver);
 195}
 196
 197MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
 198MODULE_DESCRIPTION("RTC test driver/device");
 199MODULE_LICENSE("GPL v2");
 200
 201module_init(test_init);
 202module_exit(test_exit);
 203