]>
Commit | Line | Data |
---|---|---|
fc297911 TF |
1 | /* |
2 | * Copyright (c) 2014-2015 MediaTek Inc. | |
3 | * Author: Tianping.Fang <[email protected]> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | */ | |
14 | ||
15 | #include <linux/delay.h> | |
16 | #include <linux/init.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/regmap.h> | |
19 | #include <linux/rtc.h> | |
20 | #include <linux/irqdomain.h> | |
21 | #include <linux/platform_device.h> | |
22 | #include <linux/of_address.h> | |
23 | #include <linux/of_irq.h> | |
24 | #include <linux/io.h> | |
25 | #include <linux/mfd/mt6397/core.h> | |
26 | ||
27 | #define RTC_BBPU 0x0000 | |
28 | #define RTC_BBPU_CBUSY BIT(6) | |
29 | ||
30 | #define RTC_WRTGR 0x003c | |
31 | ||
32 | #define RTC_IRQ_STA 0x0002 | |
33 | #define RTC_IRQ_STA_AL BIT(0) | |
34 | #define RTC_IRQ_STA_LP BIT(3) | |
35 | ||
36 | #define RTC_IRQ_EN 0x0004 | |
37 | #define RTC_IRQ_EN_AL BIT(0) | |
38 | #define RTC_IRQ_EN_ONESHOT BIT(2) | |
39 | #define RTC_IRQ_EN_LP BIT(3) | |
40 | #define RTC_IRQ_EN_ONESHOT_AL (RTC_IRQ_EN_ONESHOT | RTC_IRQ_EN_AL) | |
41 | ||
42 | #define RTC_AL_MASK 0x0008 | |
43 | #define RTC_AL_MASK_DOW BIT(4) | |
44 | ||
45 | #define RTC_TC_SEC 0x000a | |
46 | /* Min, Hour, Dom... register offset to RTC_TC_SEC */ | |
47 | #define RTC_OFFSET_SEC 0 | |
48 | #define RTC_OFFSET_MIN 1 | |
49 | #define RTC_OFFSET_HOUR 2 | |
50 | #define RTC_OFFSET_DOM 3 | |
51 | #define RTC_OFFSET_DOW 4 | |
52 | #define RTC_OFFSET_MTH 5 | |
53 | #define RTC_OFFSET_YEAR 6 | |
54 | #define RTC_OFFSET_COUNT 7 | |
55 | ||
56 | #define RTC_AL_SEC 0x0018 | |
57 | ||
58 | #define RTC_PDN2 0x002e | |
59 | #define RTC_PDN2_PWRON_ALARM BIT(4) | |
60 | ||
61 | #define RTC_MIN_YEAR 1968 | |
62 | #define RTC_BASE_YEAR 1900 | |
63 | #define RTC_NUM_YEARS 128 | |
64 | #define RTC_MIN_YEAR_OFFSET (RTC_MIN_YEAR - RTC_BASE_YEAR) | |
65 | ||
66 | struct mt6397_rtc { | |
67 | struct device *dev; | |
68 | struct rtc_device *rtc_dev; | |
69 | struct mutex lock; | |
70 | struct regmap *regmap; | |
71 | int irq; | |
72 | u32 addr_base; | |
73 | }; | |
74 | ||
75 | static int mtk_rtc_write_trigger(struct mt6397_rtc *rtc) | |
76 | { | |
77 | unsigned long timeout = jiffies + HZ; | |
78 | int ret; | |
79 | u32 data; | |
80 | ||
81 | ret = regmap_write(rtc->regmap, rtc->addr_base + RTC_WRTGR, 1); | |
82 | if (ret < 0) | |
83 | return ret; | |
84 | ||
85 | while (1) { | |
86 | ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_BBPU, | |
87 | &data); | |
88 | if (ret < 0) | |
89 | break; | |
90 | if (!(data & RTC_BBPU_CBUSY)) | |
91 | break; | |
92 | if (time_after(jiffies, timeout)) { | |
93 | ret = -ETIMEDOUT; | |
94 | break; | |
95 | } | |
96 | cpu_relax(); | |
97 | } | |
98 | ||
99 | return ret; | |
100 | } | |
101 | ||
102 | static irqreturn_t mtk_rtc_irq_handler_thread(int irq, void *data) | |
103 | { | |
104 | struct mt6397_rtc *rtc = data; | |
105 | u32 irqsta, irqen; | |
106 | int ret; | |
107 | ||
108 | ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_STA, &irqsta); | |
109 | if ((ret >= 0) && (irqsta & RTC_IRQ_STA_AL)) { | |
110 | rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); | |
111 | irqen = irqsta & ~RTC_IRQ_EN_AL; | |
112 | mutex_lock(&rtc->lock); | |
113 | if (regmap_write(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, | |
114 | irqen) < 0) | |
115 | mtk_rtc_write_trigger(rtc); | |
116 | mutex_unlock(&rtc->lock); | |
117 | ||
118 | return IRQ_HANDLED; | |
119 | } | |
120 | ||
121 | return IRQ_NONE; | |
122 | } | |
123 | ||
124 | static int __mtk_rtc_read_time(struct mt6397_rtc *rtc, | |
125 | struct rtc_time *tm, int *sec) | |
126 | { | |
127 | int ret; | |
128 | u16 data[RTC_OFFSET_COUNT]; | |
129 | ||
130 | mutex_lock(&rtc->lock); | |
131 | ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC, | |
132 | data, RTC_OFFSET_COUNT); | |
133 | if (ret < 0) | |
134 | goto exit; | |
135 | ||
136 | tm->tm_sec = data[RTC_OFFSET_SEC]; | |
137 | tm->tm_min = data[RTC_OFFSET_MIN]; | |
138 | tm->tm_hour = data[RTC_OFFSET_HOUR]; | |
139 | tm->tm_mday = data[RTC_OFFSET_DOM]; | |
140 | tm->tm_mon = data[RTC_OFFSET_MTH]; | |
141 | tm->tm_year = data[RTC_OFFSET_YEAR]; | |
142 | ||
143 | ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC, sec); | |
144 | exit: | |
145 | mutex_unlock(&rtc->lock); | |
146 | return ret; | |
147 | } | |
148 | ||
149 | static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm) | |
150 | { | |
151 | time64_t time; | |
152 | struct mt6397_rtc *rtc = dev_get_drvdata(dev); | |
93939967 | 153 | int days, sec, ret; |
fc297911 TF |
154 | |
155 | do { | |
156 | ret = __mtk_rtc_read_time(rtc, tm, &sec); | |
157 | if (ret < 0) | |
158 | goto exit; | |
159 | } while (sec < tm->tm_sec); | |
160 | ||
161 | /* HW register use 7 bits to store year data, minus | |
162 | * RTC_MIN_YEAR_OFFSET before write year data to register, and plus | |
163 | * RTC_MIN_YEAR_OFFSET back after read year from register | |
164 | */ | |
165 | tm->tm_year += RTC_MIN_YEAR_OFFSET; | |
166 | ||
167 | /* HW register start mon from one, but tm_mon start from zero. */ | |
168 | tm->tm_mon--; | |
169 | time = rtc_tm_to_time64(tm); | |
170 | ||
171 | /* rtc_tm_to_time64 covert Gregorian date to seconds since | |
172 | * 01-01-1970 00:00:00, and this date is Thursday. | |
173 | */ | |
93939967 AB |
174 | days = div_s64(time, 86400); |
175 | tm->tm_wday = (days + 4) % 7; | |
fc297911 TF |
176 | |
177 | exit: | |
178 | return ret; | |
179 | } | |
180 | ||
181 | static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm) | |
182 | { | |
183 | struct mt6397_rtc *rtc = dev_get_drvdata(dev); | |
184 | int ret; | |
185 | u16 data[RTC_OFFSET_COUNT]; | |
186 | ||
187 | tm->tm_year -= RTC_MIN_YEAR_OFFSET; | |
188 | tm->tm_mon++; | |
189 | ||
190 | data[RTC_OFFSET_SEC] = tm->tm_sec; | |
191 | data[RTC_OFFSET_MIN] = tm->tm_min; | |
192 | data[RTC_OFFSET_HOUR] = tm->tm_hour; | |
193 | data[RTC_OFFSET_DOM] = tm->tm_mday; | |
194 | data[RTC_OFFSET_MTH] = tm->tm_mon; | |
195 | data[RTC_OFFSET_YEAR] = tm->tm_year; | |
196 | ||
197 | mutex_lock(&rtc->lock); | |
198 | ret = regmap_bulk_write(rtc->regmap, rtc->addr_base + RTC_TC_SEC, | |
199 | data, RTC_OFFSET_COUNT); | |
200 | if (ret < 0) | |
201 | goto exit; | |
202 | ||
203 | /* Time register write to hardware after call trigger function */ | |
204 | ret = mtk_rtc_write_trigger(rtc); | |
205 | ||
206 | exit: | |
207 | mutex_unlock(&rtc->lock); | |
208 | return ret; | |
209 | } | |
210 | ||
211 | static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) | |
212 | { | |
213 | struct rtc_time *tm = &alm->time; | |
214 | struct mt6397_rtc *rtc = dev_get_drvdata(dev); | |
215 | u32 irqen, pdn2; | |
216 | int ret; | |
217 | u16 data[RTC_OFFSET_COUNT]; | |
218 | ||
219 | mutex_lock(&rtc->lock); | |
220 | ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, &irqen); | |
221 | if (ret < 0) | |
222 | goto err_exit; | |
223 | ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_PDN2, &pdn2); | |
224 | if (ret < 0) | |
225 | goto err_exit; | |
226 | ||
227 | ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_AL_SEC, | |
228 | data, RTC_OFFSET_COUNT); | |
229 | if (ret < 0) | |
230 | goto err_exit; | |
231 | ||
232 | alm->enabled = !!(irqen & RTC_IRQ_EN_AL); | |
233 | alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM); | |
234 | mutex_unlock(&rtc->lock); | |
235 | ||
236 | tm->tm_sec = data[RTC_OFFSET_SEC]; | |
237 | tm->tm_min = data[RTC_OFFSET_MIN]; | |
238 | tm->tm_hour = data[RTC_OFFSET_HOUR]; | |
239 | tm->tm_mday = data[RTC_OFFSET_DOM]; | |
240 | tm->tm_mon = data[RTC_OFFSET_MTH]; | |
241 | tm->tm_year = data[RTC_OFFSET_YEAR]; | |
242 | ||
243 | tm->tm_year += RTC_MIN_YEAR_OFFSET; | |
244 | tm->tm_mon--; | |
245 | ||
246 | return 0; | |
247 | err_exit: | |
248 | mutex_unlock(&rtc->lock); | |
249 | return ret; | |
250 | } | |
251 | ||
252 | static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) | |
253 | { | |
254 | struct rtc_time *tm = &alm->time; | |
255 | struct mt6397_rtc *rtc = dev_get_drvdata(dev); | |
256 | int ret; | |
257 | u16 data[RTC_OFFSET_COUNT]; | |
258 | ||
259 | tm->tm_year -= RTC_MIN_YEAR_OFFSET; | |
260 | tm->tm_mon++; | |
261 | ||
262 | data[RTC_OFFSET_SEC] = tm->tm_sec; | |
263 | data[RTC_OFFSET_MIN] = tm->tm_min; | |
264 | data[RTC_OFFSET_HOUR] = tm->tm_hour; | |
265 | data[RTC_OFFSET_DOM] = tm->tm_mday; | |
266 | data[RTC_OFFSET_MTH] = tm->tm_mon; | |
267 | data[RTC_OFFSET_YEAR] = tm->tm_year; | |
268 | ||
269 | mutex_lock(&rtc->lock); | |
270 | if (alm->enabled) { | |
271 | ret = regmap_bulk_write(rtc->regmap, | |
272 | rtc->addr_base + RTC_AL_SEC, | |
273 | data, RTC_OFFSET_COUNT); | |
274 | if (ret < 0) | |
275 | goto exit; | |
276 | ret = regmap_write(rtc->regmap, rtc->addr_base + RTC_AL_MASK, | |
277 | RTC_AL_MASK_DOW); | |
278 | if (ret < 0) | |
279 | goto exit; | |
280 | ret = regmap_update_bits(rtc->regmap, | |
281 | rtc->addr_base + RTC_IRQ_EN, | |
282 | RTC_IRQ_EN_ONESHOT_AL, | |
283 | RTC_IRQ_EN_ONESHOT_AL); | |
284 | if (ret < 0) | |
285 | goto exit; | |
286 | } else { | |
287 | ret = regmap_update_bits(rtc->regmap, | |
288 | rtc->addr_base + RTC_IRQ_EN, | |
289 | RTC_IRQ_EN_ONESHOT_AL, 0); | |
290 | if (ret < 0) | |
291 | goto exit; | |
292 | } | |
293 | ||
294 | /* All alarm time register write to hardware after calling | |
295 | * mtk_rtc_write_trigger. This can avoid race condition if alarm | |
296 | * occur happen during writing alarm time register. | |
297 | */ | |
298 | ret = mtk_rtc_write_trigger(rtc); | |
299 | exit: | |
300 | mutex_unlock(&rtc->lock); | |
301 | return ret; | |
302 | } | |
303 | ||
34c7b3ac | 304 | static const struct rtc_class_ops mtk_rtc_ops = { |
fc297911 TF |
305 | .read_time = mtk_rtc_read_time, |
306 | .set_time = mtk_rtc_set_time, | |
307 | .read_alarm = mtk_rtc_read_alarm, | |
308 | .set_alarm = mtk_rtc_set_alarm, | |
309 | }; | |
310 | ||
311 | static int mtk_rtc_probe(struct platform_device *pdev) | |
312 | { | |
313 | struct resource *res; | |
314 | struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent); | |
315 | struct mt6397_rtc *rtc; | |
316 | int ret; | |
317 | ||
318 | rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL); | |
319 | if (!rtc) | |
320 | return -ENOMEM; | |
321 | ||
322 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
323 | rtc->addr_base = res->start; | |
324 | ||
325 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
326 | rtc->irq = irq_create_mapping(mt6397_chip->irq_domain, res->start); | |
327 | if (rtc->irq <= 0) | |
328 | return -EINVAL; | |
329 | ||
330 | rtc->regmap = mt6397_chip->regmap; | |
331 | rtc->dev = &pdev->dev; | |
332 | mutex_init(&rtc->lock); | |
333 | ||
334 | platform_set_drvdata(pdev, rtc); | |
335 | ||
336 | ret = request_threaded_irq(rtc->irq, NULL, | |
337 | mtk_rtc_irq_handler_thread, | |
338 | IRQF_ONESHOT | IRQF_TRIGGER_HIGH, | |
339 | "mt6397-rtc", rtc); | |
340 | if (ret) { | |
341 | dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", | |
342 | rtc->irq, ret); | |
343 | goto out_dispose_irq; | |
344 | } | |
345 | ||
baeca449 WNH |
346 | device_init_wakeup(&pdev->dev, 1); |
347 | ||
fc297911 TF |
348 | rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev, |
349 | &mtk_rtc_ops, THIS_MODULE); | |
350 | if (IS_ERR(rtc->rtc_dev)) { | |
351 | dev_err(&pdev->dev, "register rtc device failed\n"); | |
352 | ret = PTR_ERR(rtc->rtc_dev); | |
353 | goto out_free_irq; | |
354 | } | |
355 | ||
fc297911 TF |
356 | return 0; |
357 | ||
358 | out_free_irq: | |
359 | free_irq(rtc->irq, rtc->rtc_dev); | |
360 | out_dispose_irq: | |
361 | irq_dispose_mapping(rtc->irq); | |
362 | return ret; | |
363 | } | |
364 | ||
365 | static int mtk_rtc_remove(struct platform_device *pdev) | |
366 | { | |
367 | struct mt6397_rtc *rtc = platform_get_drvdata(pdev); | |
368 | ||
369 | rtc_device_unregister(rtc->rtc_dev); | |
370 | free_irq(rtc->irq, rtc->rtc_dev); | |
371 | irq_dispose_mapping(rtc->irq); | |
372 | ||
373 | return 0; | |
374 | } | |
375 | ||
d7f9777d HC |
376 | #ifdef CONFIG_PM_SLEEP |
377 | static int mt6397_rtc_suspend(struct device *dev) | |
378 | { | |
379 | struct mt6397_rtc *rtc = dev_get_drvdata(dev); | |
380 | ||
381 | if (device_may_wakeup(dev)) | |
382 | enable_irq_wake(rtc->irq); | |
383 | ||
384 | return 0; | |
385 | } | |
386 | ||
387 | static int mt6397_rtc_resume(struct device *dev) | |
388 | { | |
389 | struct mt6397_rtc *rtc = dev_get_drvdata(dev); | |
390 | ||
391 | if (device_may_wakeup(dev)) | |
392 | disable_irq_wake(rtc->irq); | |
393 | ||
394 | return 0; | |
395 | } | |
396 | #endif | |
397 | ||
398 | static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_rtc_suspend, | |
399 | mt6397_rtc_resume); | |
400 | ||
fc297911 TF |
401 | static const struct of_device_id mt6397_rtc_of_match[] = { |
402 | { .compatible = "mediatek,mt6397-rtc", }, | |
403 | { } | |
404 | }; | |
73798d5c | 405 | MODULE_DEVICE_TABLE(of, mt6397_rtc_of_match); |
fc297911 TF |
406 | |
407 | static struct platform_driver mtk_rtc_driver = { | |
408 | .driver = { | |
409 | .name = "mt6397-rtc", | |
410 | .of_match_table = mt6397_rtc_of_match, | |
d7f9777d | 411 | .pm = &mt6397_pm_ops, |
fc297911 TF |
412 | }, |
413 | .probe = mtk_rtc_probe, | |
414 | .remove = mtk_rtc_remove, | |
415 | }; | |
416 | ||
417 | module_platform_driver(mtk_rtc_driver); | |
418 | ||
419 | MODULE_LICENSE("GPL v2"); | |
420 | MODULE_AUTHOR("Tianping Fang <[email protected]>"); | |
421 | MODULE_DESCRIPTION("RTC Driver for MediaTek MT6397 PMIC"); |