aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Färber <afaerber@suse.de>2017-09-04 18:53:23 -0400
committerAlexandre Belloni <alexandre.belloni@free-electrons.com>2017-09-05 03:55:02 -0400
commitae930c912b3274d79319179c1f5ddd9423dd0a7d (patch)
treecff04e305d00946f0312b1d459fffd5325e2c248
parent69c9d96ce60b3a78ea4f311faed96f0e24ff655a (diff)
rtc: Add Realtek RTD1295
Based on QNAP's arch/arm/mach-rtk119x/driver/rtk_rtc_drv.c code and mach-rtk119x/driver/dc2vo/fpga/include/mis_reg.h register definitions. The base year 2014 was observed on all of Zidoo X9S, ProBox2 Ava and Beelink Lake I. Signed-off-by: Andreas Färber <afaerber@suse.de> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
-rw-r--r--drivers/rtc/Kconfig8
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-rtd119x.c242
3 files changed, 251 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a76a26e2292a..e0e58f3b1420 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1765,6 +1765,14 @@ config RTC_DRV_CPCAP
1765 Say y here for CPCAP rtc found on some Motorola phones 1765 Say y here for CPCAP rtc found on some Motorola phones
1766 and tablets such as Droid 4. 1766 and tablets such as Droid 4.
1767 1767
1768config RTC_DRV_RTD119X
1769 bool "Realtek RTD129x RTC"
1770 depends on ARCH_REALTEK || COMPILE_TEST
1771 default ARCH_REALTEK
1772 help
1773 If you say yes here, you get support for the RTD1295 SoC
1774 Real Time Clock.
1775
1768comment "HID Sensor RTC drivers" 1776comment "HID Sensor RTC drivers"
1769 1777
1770config RTC_DRV_HID_SENSOR_TIME 1778config RTC_DRV_HID_SENSOR_TIME
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index d995d49d8218..7230014c92af 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -131,6 +131,7 @@ obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
131obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o 131obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
132obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o 132obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
133obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o 133obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
134obj-$(CONFIG_RTC_DRV_RTD119X) += rtc-rtd119x.o
134obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o 135obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o
135obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o 136obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o
136obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o 137obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o
diff --git a/drivers/rtc/rtc-rtd119x.c b/drivers/rtc/rtc-rtd119x.c
new file mode 100644
index 000000000000..b233559d950b
--- /dev/null
+++ b/drivers/rtc/rtc-rtd119x.c
@@ -0,0 +1,242 @@
1/*
2 * Realtek RTD129x RTC
3 *
4 * Copyright (c) 2017 Andreas Färber
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 */
8
9#include <linux/clk.h>
10#include <linux/io.h>
11#include <linux/module.h>
12#include <linux/of.h>
13#include <linux/of_address.h>
14#include <linux/platform_device.h>
15#include <linux/rtc.h>
16#include <linux/spinlock.h>
17
18#define RTD_RTCSEC 0x00
19#define RTD_RTCMIN 0x04
20#define RTD_RTCHR 0x08
21#define RTD_RTCDATE1 0x0c
22#define RTD_RTCDATE2 0x10
23#define RTD_RTCACR 0x28
24#define RTD_RTCEN 0x2c
25#define RTD_RTCCR 0x30
26
27#define RTD_RTCSEC_RTCSEC_MASK 0x7f
28
29#define RTD_RTCMIN_RTCMIN_MASK 0x3f
30
31#define RTD_RTCHR_RTCHR_MASK 0x1f
32
33#define RTD_RTCDATE1_RTCDATE1_MASK 0xff
34
35#define RTD_RTCDATE2_RTCDATE2_MASK 0x7f
36
37#define RTD_RTCACR_RTCPWR BIT(7)
38
39#define RTD_RTCEN_RTCEN_MASK 0xff
40
41#define RTD_RTCCR_RTCRST BIT(6)
42
43struct rtd119x_rtc {
44 void __iomem *base;
45 struct clk *clk;
46 struct rtc_device *rtcdev;
47 unsigned int base_year;
48};
49
50static inline int rtd119x_rtc_days_in_year(int year)
51{
52 return 365 + (is_leap_year(year) ? 1 : 0);
53}
54
55static void rtd119x_rtc_reset(struct device *dev)
56{
57 struct rtd119x_rtc *data = dev_get_drvdata(dev);
58 u32 val;
59
60 val = readl_relaxed(data->base + RTD_RTCCR);
61 val |= RTD_RTCCR_RTCRST;
62 writel_relaxed(val, data->base + RTD_RTCCR);
63
64 val &= ~RTD_RTCCR_RTCRST;
65 writel(val, data->base + RTD_RTCCR);
66}
67
68static void rtd119x_rtc_set_enabled(struct device *dev, bool enable)
69{
70 struct rtd119x_rtc *data = dev_get_drvdata(dev);
71 u32 val;
72
73 val = readl_relaxed(data->base + RTD_RTCEN);
74 if (enable) {
75 if ((val & RTD_RTCEN_RTCEN_MASK) == 0x5a)
76 return;
77 writel_relaxed(0x5a, data->base + RTD_RTCEN);
78 } else {
79 writel_relaxed(0, data->base + RTD_RTCEN);
80 }
81}
82
83static int rtd119x_rtc_read_time(struct device *dev, struct rtc_time *tm)
84{
85 struct rtd119x_rtc *data = dev_get_drvdata(dev);
86 s32 day;
87 u32 sec;
88 unsigned int year;
89 int tries = 0;
90
91 while (true) {
92 tm->tm_sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1;
93 tm->tm_min = readl_relaxed(data->base + RTD_RTCMIN) & RTD_RTCMIN_RTCMIN_MASK;
94 tm->tm_hour = readl_relaxed(data->base + RTD_RTCHR) & RTD_RTCHR_RTCHR_MASK;
95 day = readl_relaxed(data->base + RTD_RTCDATE1) & RTD_RTCDATE1_RTCDATE1_MASK;
96 day |= (readl_relaxed(data->base + RTD_RTCDATE2) & RTD_RTCDATE2_RTCDATE2_MASK) << 8;
97 sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1;
98 tries++;
99
100 if (sec == tm->tm_sec)
101 break;
102
103 if (tries >= 3)
104 return -EINVAL;
105 }
106 if (tries > 1)
107 dev_dbg(dev, "%s: needed %i tries\n", __func__, tries);
108
109 year = data->base_year;
110 while (day >= rtd119x_rtc_days_in_year(year)) {
111 day -= rtd119x_rtc_days_in_year(year);
112 year++;
113 }
114 tm->tm_year = year - 1900;
115 tm->tm_yday = day;
116
117 tm->tm_mon = 0;
118 while (day >= rtc_month_days(tm->tm_mon, year)) {
119 day -= rtc_month_days(tm->tm_mon, year);
120 tm->tm_mon++;
121 }
122 tm->tm_mday = day + 1;
123
124 return 0;
125}
126
127static int rtd119x_rtc_set_time(struct device *dev, struct rtc_time *tm)
128{
129 struct rtd119x_rtc *data = dev_get_drvdata(dev);
130 unsigned int day;
131 int i;
132
133 if (1900 + tm->tm_year < data->base_year)
134 return -EINVAL;
135
136 day = 0;
137 for (i = data->base_year; i < 1900 + tm->tm_year; i++)
138 day += rtd119x_rtc_days_in_year(i);
139
140 day += tm->tm_yday;
141 if (day > 0x7fff)
142 return -EINVAL;
143
144 rtd119x_rtc_set_enabled(dev, false);
145
146 writel_relaxed((tm->tm_sec << 1) & RTD_RTCSEC_RTCSEC_MASK, data->base + RTD_RTCSEC);
147 writel_relaxed(tm->tm_min & RTD_RTCMIN_RTCMIN_MASK, data->base + RTD_RTCMIN);
148 writel_relaxed(tm->tm_hour & RTD_RTCHR_RTCHR_MASK, data->base + RTD_RTCHR);
149 writel_relaxed(day & RTD_RTCDATE1_RTCDATE1_MASK, data->base + RTD_RTCDATE1);
150 writel_relaxed((day >> 8) & RTD_RTCDATE2_RTCDATE2_MASK, data->base + RTD_RTCDATE2);
151
152 rtd119x_rtc_set_enabled(dev, true);
153
154 return 0;
155}
156
157static const struct rtc_class_ops rtd119x_rtc_ops = {
158 .read_time = rtd119x_rtc_read_time,
159 .set_time = rtd119x_rtc_set_time,
160};
161
162static const struct of_device_id rtd119x_rtc_dt_ids[] = {
163 { .compatible = "realtek,rtd1295-rtc" },
164 { }
165};
166
167static int rtd119x_rtc_probe(struct platform_device *pdev)
168{
169 struct rtd119x_rtc *data;
170 struct resource *res;
171 u32 val;
172 int ret;
173
174 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
175 if (!data)
176 return -ENOMEM;
177
178 platform_set_drvdata(pdev, data);
179 data->base_year = 2014;
180
181 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
182 data->base = devm_ioremap_resource(&pdev->dev, res);
183 if (IS_ERR(data->base))
184 return PTR_ERR(data->base);
185
186 data->clk = of_clk_get(pdev->dev.of_node, 0);
187 if (IS_ERR(data->clk))
188 return PTR_ERR(data->clk);
189
190 ret = clk_prepare_enable(data->clk);
191 if (ret) {
192 clk_put(data->clk);
193 return ret;
194 }
195
196 val = readl_relaxed(data->base + RTD_RTCACR);
197 if (!(val & RTD_RTCACR_RTCPWR)) {
198 writel_relaxed(RTD_RTCACR_RTCPWR, data->base + RTD_RTCACR);
199
200 rtd119x_rtc_reset(&pdev->dev);
201
202 writel_relaxed(0, data->base + RTD_RTCMIN);
203 writel_relaxed(0, data->base + RTD_RTCHR);
204 writel_relaxed(0, data->base + RTD_RTCDATE1);
205 writel_relaxed(0, data->base + RTD_RTCDATE2);
206 }
207
208 rtd119x_rtc_set_enabled(&pdev->dev, true);
209
210 data->rtcdev = devm_rtc_device_register(&pdev->dev, "rtc",
211 &rtd119x_rtc_ops, THIS_MODULE);
212 if (IS_ERR(data->rtcdev)) {
213 dev_err(&pdev->dev, "failed to register rtc device");
214 clk_disable_unprepare(data->clk);
215 clk_put(data->clk);
216 return PTR_ERR(data->rtcdev);
217 }
218
219 return 0;
220}
221
222static int rtd119x_rtc_remove(struct platform_device *pdev)
223{
224 struct rtd119x_rtc *data = platform_get_drvdata(pdev);
225
226 rtd119x_rtc_set_enabled(&pdev->dev, false);
227
228 clk_disable_unprepare(data->clk);
229 clk_put(data->clk);
230
231 return 0;
232}
233
234static struct platform_driver rtd119x_rtc_driver = {
235 .probe = rtd119x_rtc_probe,
236 .remove = rtd119x_rtc_remove,
237 .driver = {
238 .name = "rtd1295-rtc",
239 .of_match_table = rtd119x_rtc_dt_ids,
240 },
241};
242builtin_platform_driver(rtd119x_rtc_driver);