1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2019 BayLibre, SAS
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
7 #include <linux/module.h>
8 #include <linux/platform_device.h>
12 #include <linux/time64.h>
14 struct meson_vrtc_data
{
15 void __iomem
*io_alarm
;
16 struct rtc_device
*rtc
;
17 unsigned long alarm_time
;
21 static int meson_vrtc_read_time(struct device
*dev
, struct rtc_time
*tm
)
23 struct timespec64 time
;
25 dev_dbg(dev
, "%s\n", __func__
);
26 ktime_get_raw_ts64(&time
);
27 rtc_time64_to_tm(time
.tv_sec
, tm
);
32 static void meson_vrtc_set_wakeup_time(struct meson_vrtc_data
*vrtc
,
35 writel_relaxed(time
, vrtc
->io_alarm
);
38 static int meson_vrtc_set_alarm(struct device
*dev
, struct rtc_wkalrm
*alarm
)
40 struct meson_vrtc_data
*vrtc
= dev_get_drvdata(dev
);
42 dev_dbg(dev
, "%s: alarm->enabled=%d\n", __func__
, alarm
->enabled
);
44 vrtc
->alarm_time
= rtc_tm_to_time64(&alarm
->time
);
51 static int meson_vrtc_alarm_irq_enable(struct device
*dev
, unsigned int enabled
)
53 struct meson_vrtc_data
*vrtc
= dev_get_drvdata(dev
);
55 vrtc
->enabled
= enabled
;
59 static const struct rtc_class_ops meson_vrtc_ops
= {
60 .read_time
= meson_vrtc_read_time
,
61 .set_alarm
= meson_vrtc_set_alarm
,
62 .alarm_irq_enable
= meson_vrtc_alarm_irq_enable
,
65 static int meson_vrtc_probe(struct platform_device
*pdev
)
67 struct meson_vrtc_data
*vrtc
;
69 vrtc
= devm_kzalloc(&pdev
->dev
, sizeof(*vrtc
), GFP_KERNEL
);
73 vrtc
->io_alarm
= devm_platform_ioremap_resource(pdev
, 0);
74 if (IS_ERR(vrtc
->io_alarm
))
75 return PTR_ERR(vrtc
->io_alarm
);
77 device_init_wakeup(&pdev
->dev
, 1);
79 platform_set_drvdata(pdev
, vrtc
);
81 vrtc
->rtc
= devm_rtc_allocate_device(&pdev
->dev
);
82 if (IS_ERR(vrtc
->rtc
))
83 return PTR_ERR(vrtc
->rtc
);
85 vrtc
->rtc
->ops
= &meson_vrtc_ops
;
86 return devm_rtc_register_device(vrtc
->rtc
);
89 static int __maybe_unused
meson_vrtc_suspend(struct device
*dev
)
91 struct meson_vrtc_data
*vrtc
= dev_get_drvdata(dev
);
93 dev_dbg(dev
, "%s\n", __func__
);
94 if (vrtc
->alarm_time
) {
95 unsigned long local_time
;
97 struct timespec64 time
;
99 ktime_get_raw_ts64(&time
);
100 local_time
= time
.tv_sec
;
102 dev_dbg(dev
, "alarm_time = %lus, local_time=%lus\n",
103 vrtc
->alarm_time
, local_time
);
104 alarm_secs
= vrtc
->alarm_time
- local_time
;
105 if (alarm_secs
> 0) {
106 meson_vrtc_set_wakeup_time(vrtc
, alarm_secs
);
107 dev_dbg(dev
, "system will wakeup in %lds.\n",
110 dev_err(dev
, "alarm time already passed: %lds.\n",
118 static int __maybe_unused
meson_vrtc_resume(struct device
*dev
)
120 struct meson_vrtc_data
*vrtc
= dev_get_drvdata(dev
);
122 dev_dbg(dev
, "%s\n", __func__
);
124 vrtc
->alarm_time
= 0;
125 meson_vrtc_set_wakeup_time(vrtc
, 0);
129 static SIMPLE_DEV_PM_OPS(meson_vrtc_pm_ops
,
130 meson_vrtc_suspend
, meson_vrtc_resume
);
132 static const struct of_device_id meson_vrtc_dt_match
[] = {
133 { .compatible
= "amlogic,meson-vrtc"},
136 MODULE_DEVICE_TABLE(of
, meson_vrtc_dt_match
);
138 static struct platform_driver meson_vrtc_driver
= {
139 .probe
= meson_vrtc_probe
,
141 .name
= "meson-vrtc",
142 .of_match_table
= meson_vrtc_dt_match
,
143 .pm
= &meson_vrtc_pm_ops
,
147 module_platform_driver(meson_vrtc_driver
);
149 MODULE_DESCRIPTION("Amlogic Virtual Wakeup RTC Timer driver");
150 MODULE_LICENSE("GPL");