diff options
author | Arnd Bergmann <arnd@arndb.de> | 2014-09-25 12:15:34 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2014-09-25 12:15:34 -0400 |
commit | 8a87f1a6c8f6b78758f4e5e8e616b0df821ba2b2 (patch) | |
tree | f6861436c41fc7284e582025b0f847a4552c773b /drivers/rtc | |
parent | b2fc3f3c6d397d434174147eca3db1ec778195ce (diff) | |
parent | 64a1925c00706253aa887431fc45f96db48aa727 (diff) |
Merge tag 'sunxi-drivers-for-3.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into next/drivers
Pull "Allwinner drivers additions for 3.18" from Maxime Ripard:
Nothing major, just handling the RTC driver changes needed for the A31/A23.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
* tag 'sunxi-drivers-for-3.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux:
rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc
rtc: sun6i: Add sun6i RTC driver
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-sun6i.c | 447 |
3 files changed, 456 insertions, 1 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a168e96142b9..6b34bc94a3f6 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -1175,9 +1175,16 @@ config RTC_DRV_SUN4V | |||
1175 | If you say Y here you will get support for the Hypervisor | 1175 | If you say Y here you will get support for the Hypervisor |
1176 | based RTC on SUN4V systems. | 1176 | based RTC on SUN4V systems. |
1177 | 1177 | ||
1178 | config RTC_DRV_SUN6I | ||
1179 | tristate "Allwinner A31 RTC" | ||
1180 | depends on MACH_SUN6I || MACH_SUN8I | ||
1181 | help | ||
1182 | If you say Y here you will get support for the RTC found on | ||
1183 | Allwinner A31. | ||
1184 | |||
1178 | config RTC_DRV_SUNXI | 1185 | config RTC_DRV_SUNXI |
1179 | tristate "Allwinner sun4i/sun7i RTC" | 1186 | tristate "Allwinner sun4i/sun7i RTC" |
1180 | depends on ARCH_SUNXI | 1187 | depends on MACH_SUN4I || MACH_SUN7I |
1181 | help | 1188 | help |
1182 | If you say Y here you will get support for the RTC found on | 1189 | If you say Y here you will get support for the RTC found on |
1183 | Allwinner A10/A20. | 1190 | Allwinner A10/A20. |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 56f061c7c815..9055b7dd3dc5 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -128,6 +128,7 @@ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o | |||
128 | obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o | 128 | obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o |
129 | obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o | 129 | obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o |
130 | obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o | 130 | obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o |
131 | obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o | ||
131 | obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o | 132 | obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o |
132 | obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o | 133 | obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o |
133 | obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o | 134 | obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o |
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c new file mode 100644 index 000000000000..c169a2cd4727 --- /dev/null +++ b/drivers/rtc/rtc-sun6i.c | |||
@@ -0,0 +1,447 @@ | |||
1 | /* | ||
2 | * An RTC driver for Allwinner A31/A23 | ||
3 | * | ||
4 | * Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org> | ||
5 | * | ||
6 | * based on rtc-sunxi.c | ||
7 | * | ||
8 | * An RTC driver for Allwinner A10/A20 | ||
9 | * | ||
10 | * Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
20 | * more details. | ||
21 | */ | ||
22 | |||
23 | #include <linux/delay.h> | ||
24 | #include <linux/err.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/io.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/of.h> | ||
32 | #include <linux/of_address.h> | ||
33 | #include <linux/of_device.h> | ||
34 | #include <linux/platform_device.h> | ||
35 | #include <linux/rtc.h> | ||
36 | #include <linux/types.h> | ||
37 | |||
38 | /* Control register */ | ||
39 | #define SUN6I_LOSC_CTRL 0x0000 | ||
40 | #define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) | ||
41 | #define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) | ||
42 | #define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) | ||
43 | #define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) | ||
44 | |||
45 | /* RTC */ | ||
46 | #define SUN6I_RTC_YMD 0x0010 | ||
47 | #define SUN6I_RTC_HMS 0x0014 | ||
48 | |||
49 | /* Alarm 0 (counter) */ | ||
50 | #define SUN6I_ALRM_COUNTER 0x0020 | ||
51 | #define SUN6I_ALRM_CUR_VAL 0x0024 | ||
52 | #define SUN6I_ALRM_EN 0x0028 | ||
53 | #define SUN6I_ALRM_EN_CNT_EN BIT(0) | ||
54 | #define SUN6I_ALRM_IRQ_EN 0x002c | ||
55 | #define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0) | ||
56 | #define SUN6I_ALRM_IRQ_STA 0x0030 | ||
57 | #define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0) | ||
58 | |||
59 | /* Alarm 1 (wall clock) */ | ||
60 | #define SUN6I_ALRM1_EN 0x0044 | ||
61 | #define SUN6I_ALRM1_IRQ_EN 0x0048 | ||
62 | #define SUN6I_ALRM1_IRQ_STA 0x004c | ||
63 | #define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND BIT(0) | ||
64 | |||
65 | /* Alarm config */ | ||
66 | #define SUN6I_ALARM_CONFIG 0x0050 | ||
67 | #define SUN6I_ALARM_CONFIG_WAKEUP BIT(0) | ||
68 | |||
69 | /* | ||
70 | * Get date values | ||
71 | */ | ||
72 | #define SUN6I_DATE_GET_DAY_VALUE(x) ((x) & 0x0000001f) | ||
73 | #define SUN6I_DATE_GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8) | ||
74 | #define SUN6I_DATE_GET_YEAR_VALUE(x) (((x) & 0x003f0000) >> 16) | ||
75 | #define SUN6I_LEAP_GET_VALUE(x) (((x) & 0x00400000) >> 22) | ||
76 | |||
77 | /* | ||
78 | * Get time values | ||
79 | */ | ||
80 | #define SUN6I_TIME_GET_SEC_VALUE(x) ((x) & 0x0000003f) | ||
81 | #define SUN6I_TIME_GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8) | ||
82 | #define SUN6I_TIME_GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16) | ||
83 | |||
84 | /* | ||
85 | * Set date values | ||
86 | */ | ||
87 | #define SUN6I_DATE_SET_DAY_VALUE(x) ((x) & 0x0000001f) | ||
88 | #define SUN6I_DATE_SET_MON_VALUE(x) ((x) << 8 & 0x00000f00) | ||
89 | #define SUN6I_DATE_SET_YEAR_VALUE(x) ((x) << 16 & 0x003f0000) | ||
90 | #define SUN6I_LEAP_SET_VALUE(x) ((x) << 22 & 0x00400000) | ||
91 | |||
92 | /* | ||
93 | * Set time values | ||
94 | */ | ||
95 | #define SUN6I_TIME_SET_SEC_VALUE(x) ((x) & 0x0000003f) | ||
96 | #define SUN6I_TIME_SET_MIN_VALUE(x) ((x) << 8 & 0x00003f00) | ||
97 | #define SUN6I_TIME_SET_HOUR_VALUE(x) ((x) << 16 & 0x001f0000) | ||
98 | |||
99 | /* | ||
100 | * The year parameter passed to the driver is usually an offset relative to | ||
101 | * the year 1900. This macro is used to convert this offset to another one | ||
102 | * relative to the minimum year allowed by the hardware. | ||
103 | * | ||
104 | * The year range is 1970 - 2033. This range is selected to match Allwinner's | ||
105 | * driver, even though it is somewhat limited. | ||
106 | */ | ||
107 | #define SUN6I_YEAR_MIN 1970 | ||
108 | #define SUN6I_YEAR_MAX 2033 | ||
109 | #define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900) | ||
110 | |||
111 | struct sun6i_rtc_dev { | ||
112 | struct rtc_device *rtc; | ||
113 | struct device *dev; | ||
114 | void __iomem *base; | ||
115 | int irq; | ||
116 | unsigned long alarm; | ||
117 | }; | ||
118 | |||
119 | static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) | ||
120 | { | ||
121 | struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; | ||
122 | u32 val; | ||
123 | |||
124 | val = readl(chip->base + SUN6I_ALRM_IRQ_STA); | ||
125 | |||
126 | if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { | ||
127 | val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND; | ||
128 | writel(val, chip->base + SUN6I_ALRM_IRQ_STA); | ||
129 | |||
130 | rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); | ||
131 | |||
132 | return IRQ_HANDLED; | ||
133 | } | ||
134 | |||
135 | return IRQ_NONE; | ||
136 | } | ||
137 | |||
138 | static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) | ||
139 | { | ||
140 | u32 alrm_val = 0; | ||
141 | u32 alrm_irq_val = 0; | ||
142 | u32 alrm_wake_val = 0; | ||
143 | |||
144 | if (to) { | ||
145 | alrm_val = SUN6I_ALRM_EN_CNT_EN; | ||
146 | alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN; | ||
147 | alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP; | ||
148 | } else { | ||
149 | writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, | ||
150 | chip->base + SUN6I_ALRM_IRQ_STA); | ||
151 | } | ||
152 | |||
153 | writel(alrm_val, chip->base + SUN6I_ALRM_EN); | ||
154 | writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); | ||
155 | writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); | ||
156 | } | ||
157 | |||
158 | static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | ||
159 | { | ||
160 | struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); | ||
161 | u32 date, time; | ||
162 | |||
163 | /* | ||
164 | * read again in case it changes | ||
165 | */ | ||
166 | do { | ||
167 | date = readl(chip->base + SUN6I_RTC_YMD); | ||
168 | time = readl(chip->base + SUN6I_RTC_HMS); | ||
169 | } while ((date != readl(chip->base + SUN6I_RTC_YMD)) || | ||
170 | (time != readl(chip->base + SUN6I_RTC_HMS))); | ||
171 | |||
172 | rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time); | ||
173 | rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time); | ||
174 | rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time); | ||
175 | |||
176 | rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date); | ||
177 | rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date); | ||
178 | rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date); | ||
179 | |||
180 | rtc_tm->tm_mon -= 1; | ||
181 | |||
182 | /* | ||
183 | * switch from (data_year->min)-relative offset to | ||
184 | * a (1900)-relative one | ||
185 | */ | ||
186 | rtc_tm->tm_year += SUN6I_YEAR_OFF; | ||
187 | |||
188 | return rtc_valid_tm(rtc_tm); | ||
189 | } | ||
190 | |||
191 | static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) | ||
192 | { | ||
193 | struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); | ||
194 | u32 alrm_st; | ||
195 | u32 alrm_en; | ||
196 | |||
197 | alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); | ||
198 | alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); | ||
199 | wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); | ||
200 | wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); | ||
201 | rtc_time_to_tm(chip->alarm, &wkalrm->time); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) | ||
207 | { | ||
208 | struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); | ||
209 | struct rtc_time *alrm_tm = &wkalrm->time; | ||
210 | struct rtc_time tm_now; | ||
211 | unsigned long time_now = 0; | ||
212 | unsigned long time_set = 0; | ||
213 | unsigned long time_gap = 0; | ||
214 | int ret = 0; | ||
215 | |||
216 | ret = sun6i_rtc_gettime(dev, &tm_now); | ||
217 | if (ret < 0) { | ||
218 | dev_err(dev, "Error in getting time\n"); | ||
219 | return -EINVAL; | ||
220 | } | ||
221 | |||
222 | rtc_tm_to_time(alrm_tm, &time_set); | ||
223 | rtc_tm_to_time(&tm_now, &time_now); | ||
224 | if (time_set <= time_now) { | ||
225 | dev_err(dev, "Date to set in the past\n"); | ||
226 | return -EINVAL; | ||
227 | } | ||
228 | |||
229 | time_gap = time_set - time_now; | ||
230 | |||
231 | if (time_gap > U32_MAX) { | ||
232 | dev_err(dev, "Date too far in the future\n"); | ||
233 | return -EINVAL; | ||
234 | } | ||
235 | |||
236 | sun6i_rtc_setaie(0, chip); | ||
237 | writel(0, chip->base + SUN6I_ALRM_COUNTER); | ||
238 | usleep_range(100, 300); | ||
239 | |||
240 | writel(time_gap, chip->base + SUN6I_ALRM_COUNTER); | ||
241 | chip->alarm = time_set; | ||
242 | |||
243 | sun6i_rtc_setaie(wkalrm->enabled, chip); | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset, | ||
249 | unsigned int mask, unsigned int ms_timeout) | ||
250 | { | ||
251 | const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout); | ||
252 | u32 reg; | ||
253 | |||
254 | do { | ||
255 | reg = readl(chip->base + offset); | ||
256 | reg &= mask; | ||
257 | |||
258 | if (!reg) | ||
259 | return 0; | ||
260 | |||
261 | } while (time_before(jiffies, timeout)); | ||
262 | |||
263 | return -ETIMEDOUT; | ||
264 | } | ||
265 | |||
266 | static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) | ||
267 | { | ||
268 | struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); | ||
269 | u32 date = 0; | ||
270 | u32 time = 0; | ||
271 | int year; | ||
272 | |||
273 | year = rtc_tm->tm_year + 1900; | ||
274 | if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) { | ||
275 | dev_err(dev, "rtc only supports year in range %d - %d\n", | ||
276 | SUN6I_YEAR_MIN, SUN6I_YEAR_MAX); | ||
277 | return -EINVAL; | ||
278 | } | ||
279 | |||
280 | rtc_tm->tm_year -= SUN6I_YEAR_OFF; | ||
281 | rtc_tm->tm_mon += 1; | ||
282 | |||
283 | date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | | ||
284 | SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | | ||
285 | SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year); | ||
286 | |||
287 | if (is_leap_year(year)) | ||
288 | date |= SUN6I_LEAP_SET_VALUE(1); | ||
289 | |||
290 | time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | | ||
291 | SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | | ||
292 | SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour); | ||
293 | |||
294 | /* Check whether registers are writable */ | ||
295 | if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, | ||
296 | SUN6I_LOSC_CTRL_ACC_MASK, 50)) { | ||
297 | dev_err(dev, "rtc is still busy.\n"); | ||
298 | return -EBUSY; | ||
299 | } | ||
300 | |||
301 | writel(time, chip->base + SUN6I_RTC_HMS); | ||
302 | |||
303 | /* | ||
304 | * After writing the RTC HH-MM-SS register, the | ||
305 | * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not | ||
306 | * be cleared until the real writing operation is finished | ||
307 | */ | ||
308 | |||
309 | if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, | ||
310 | SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) { | ||
311 | dev_err(dev, "Failed to set rtc time.\n"); | ||
312 | return -ETIMEDOUT; | ||
313 | } | ||
314 | |||
315 | writel(date, chip->base + SUN6I_RTC_YMD); | ||
316 | |||
317 | /* | ||
318 | * After writing the RTC YY-MM-DD register, the | ||
319 | * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not | ||
320 | * be cleared until the real writing operation is finished | ||
321 | */ | ||
322 | |||
323 | if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, | ||
324 | SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) { | ||
325 | dev_err(dev, "Failed to set rtc time.\n"); | ||
326 | return -ETIMEDOUT; | ||
327 | } | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
333 | { | ||
334 | struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); | ||
335 | |||
336 | if (!enabled) | ||
337 | sun6i_rtc_setaie(enabled, chip); | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static const struct rtc_class_ops sun6i_rtc_ops = { | ||
343 | .read_time = sun6i_rtc_gettime, | ||
344 | .set_time = sun6i_rtc_settime, | ||
345 | .read_alarm = sun6i_rtc_getalarm, | ||
346 | .set_alarm = sun6i_rtc_setalarm, | ||
347 | .alarm_irq_enable = sun6i_rtc_alarm_irq_enable | ||
348 | }; | ||
349 | |||
350 | static int sun6i_rtc_probe(struct platform_device *pdev) | ||
351 | { | ||
352 | struct sun6i_rtc_dev *chip; | ||
353 | struct resource *res; | ||
354 | int ret; | ||
355 | |||
356 | chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); | ||
357 | if (!chip) | ||
358 | return -ENOMEM; | ||
359 | |||
360 | platform_set_drvdata(pdev, chip); | ||
361 | chip->dev = &pdev->dev; | ||
362 | |||
363 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
364 | chip->base = devm_ioremap_resource(&pdev->dev, res); | ||
365 | if (IS_ERR(chip->base)) | ||
366 | return PTR_ERR(chip->base); | ||
367 | |||
368 | chip->irq = platform_get_irq(pdev, 0); | ||
369 | if (chip->irq < 0) { | ||
370 | dev_err(&pdev->dev, "No IRQ resource\n"); | ||
371 | return chip->irq; | ||
372 | } | ||
373 | |||
374 | ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq, | ||
375 | 0, dev_name(&pdev->dev), chip); | ||
376 | if (ret) { | ||
377 | dev_err(&pdev->dev, "Could not request IRQ\n"); | ||
378 | return ret; | ||
379 | } | ||
380 | |||
381 | /* clear the alarm counter value */ | ||
382 | writel(0, chip->base + SUN6I_ALRM_COUNTER); | ||
383 | |||
384 | /* disable counter alarm */ | ||
385 | writel(0, chip->base + SUN6I_ALRM_EN); | ||
386 | |||
387 | /* disable counter alarm interrupt */ | ||
388 | writel(0, chip->base + SUN6I_ALRM_IRQ_EN); | ||
389 | |||
390 | /* disable week alarm */ | ||
391 | writel(0, chip->base + SUN6I_ALRM1_EN); | ||
392 | |||
393 | /* disable week alarm interrupt */ | ||
394 | writel(0, chip->base + SUN6I_ALRM1_IRQ_EN); | ||
395 | |||
396 | /* clear counter alarm pending interrupts */ | ||
397 | writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, | ||
398 | chip->base + SUN6I_ALRM_IRQ_STA); | ||
399 | |||
400 | /* clear week alarm pending interrupts */ | ||
401 | writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, | ||
402 | chip->base + SUN6I_ALRM1_IRQ_STA); | ||
403 | |||
404 | /* disable alarm wakeup */ | ||
405 | writel(0, chip->base + SUN6I_ALARM_CONFIG); | ||
406 | |||
407 | chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev, | ||
408 | &sun6i_rtc_ops, THIS_MODULE); | ||
409 | if (IS_ERR(chip->rtc)) { | ||
410 | dev_err(&pdev->dev, "unable to register device\n"); | ||
411 | return PTR_ERR(chip->rtc); | ||
412 | } | ||
413 | |||
414 | dev_info(&pdev->dev, "RTC enabled\n"); | ||
415 | |||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static int sun6i_rtc_remove(struct platform_device *pdev) | ||
420 | { | ||
421 | struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev); | ||
422 | |||
423 | rtc_device_unregister(chip->rtc); | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static const struct of_device_id sun6i_rtc_dt_ids[] = { | ||
429 | { .compatible = "allwinner,sun6i-a31-rtc" }, | ||
430 | { /* sentinel */ }, | ||
431 | }; | ||
432 | MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); | ||
433 | |||
434 | static struct platform_driver sun6i_rtc_driver = { | ||
435 | .probe = sun6i_rtc_probe, | ||
436 | .remove = sun6i_rtc_remove, | ||
437 | .driver = { | ||
438 | .name = "sun6i-rtc", | ||
439 | .of_match_table = sun6i_rtc_dt_ids, | ||
440 | }, | ||
441 | }; | ||
442 | |||
443 | module_platform_driver(sun6i_rtc_driver); | ||
444 | |||
445 | MODULE_DESCRIPTION("sun6i RTC driver"); | ||
446 | MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); | ||
447 | MODULE_LICENSE("GPL"); | ||