Commit | Line | Data |
---|---|---|
64823360 RB |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2019 MediaTek Inc. | |
4 | * Author: Ran Bi <ran.bi@mediatek.com> | |
5 | */ | |
6 | ||
7 | #include <linux/delay.h> | |
8 | #include <linux/init.h> | |
9 | #include <linux/io.h> | |
10 | #include <linux/irqdomain.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/of_address.h> | |
13 | #include <linux/of_irq.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/rtc.h> | |
16 | ||
17 | #define MT2712_BBPU 0x0000 | |
18 | #define MT2712_BBPU_CLRPKY BIT(4) | |
19 | #define MT2712_BBPU_RELOAD BIT(5) | |
20 | #define MT2712_BBPU_CBUSY BIT(6) | |
21 | #define MT2712_BBPU_KEY (0x43 << 8) | |
22 | ||
23 | #define MT2712_IRQ_STA 0x0004 | |
24 | #define MT2712_IRQ_STA_AL BIT(0) | |
25 | #define MT2712_IRQ_STA_TC BIT(1) | |
26 | ||
27 | #define MT2712_IRQ_EN 0x0008 | |
28 | #define MT2712_IRQ_EN_AL BIT(0) | |
29 | #define MT2712_IRQ_EN_TC BIT(1) | |
30 | #define MT2712_IRQ_EN_ONESHOT BIT(2) | |
31 | ||
32 | #define MT2712_CII_EN 0x000c | |
33 | ||
34 | #define MT2712_AL_MASK 0x0010 | |
35 | #define MT2712_AL_MASK_DOW BIT(4) | |
36 | ||
37 | #define MT2712_TC_SEC 0x0014 | |
38 | #define MT2712_TC_MIN 0x0018 | |
39 | #define MT2712_TC_HOU 0x001c | |
40 | #define MT2712_TC_DOM 0x0020 | |
41 | #define MT2712_TC_DOW 0x0024 | |
42 | #define MT2712_TC_MTH 0x0028 | |
43 | #define MT2712_TC_YEA 0x002c | |
44 | ||
45 | #define MT2712_AL_SEC 0x0030 | |
46 | #define MT2712_AL_MIN 0x0034 | |
47 | #define MT2712_AL_HOU 0x0038 | |
48 | #define MT2712_AL_DOM 0x003c | |
49 | #define MT2712_AL_DOW 0x0040 | |
50 | #define MT2712_AL_MTH 0x0044 | |
51 | #define MT2712_AL_YEA 0x0048 | |
52 | ||
53 | #define MT2712_SEC_MASK 0x003f | |
54 | #define MT2712_MIN_MASK 0x003f | |
55 | #define MT2712_HOU_MASK 0x001f | |
56 | #define MT2712_DOM_MASK 0x001f | |
57 | #define MT2712_DOW_MASK 0x0007 | |
58 | #define MT2712_MTH_MASK 0x000f | |
59 | #define MT2712_YEA_MASK 0x007f | |
60 | ||
61 | #define MT2712_POWERKEY1 0x004c | |
62 | #define MT2712_POWERKEY2 0x0050 | |
63 | #define MT2712_POWERKEY1_KEY 0xa357 | |
64 | #define MT2712_POWERKEY2_KEY 0x67d2 | |
65 | ||
66 | #define MT2712_CON0 0x005c | |
67 | #define MT2712_CON1 0x0060 | |
68 | ||
69 | #define MT2712_PROT 0x0070 | |
70 | #define MT2712_PROT_UNLOCK1 0x9136 | |
71 | #define MT2712_PROT_UNLOCK2 0x586a | |
72 | ||
73 | #define MT2712_WRTGR 0x0078 | |
74 | ||
75 | #define MT2712_RTC_TIMESTAMP_END_2127 4985971199LL | |
76 | ||
77 | struct mt2712_rtc { | |
78 | struct rtc_device *rtc; | |
79 | void __iomem *base; | |
80 | int irq; | |
81 | u8 irq_wake_enabled; | |
82 | u8 powerlost; | |
83 | }; | |
84 | ||
85 | static inline u32 mt2712_readl(struct mt2712_rtc *mt2712_rtc, u32 reg) | |
86 | { | |
87 | return readl(mt2712_rtc->base + reg); | |
88 | } | |
89 | ||
90 | static inline void mt2712_writel(struct mt2712_rtc *mt2712_rtc, | |
91 | u32 reg, u32 val) | |
92 | { | |
93 | writel(val, mt2712_rtc->base + reg); | |
94 | } | |
95 | ||
96 | static void mt2712_rtc_write_trigger(struct mt2712_rtc *mt2712_rtc) | |
97 | { | |
98 | unsigned long timeout = jiffies + HZ / 10; | |
99 | ||
100 | mt2712_writel(mt2712_rtc, MT2712_WRTGR, 1); | |
101 | while (1) { | |
102 | if (!(mt2712_readl(mt2712_rtc, MT2712_BBPU) | |
103 | & MT2712_BBPU_CBUSY)) | |
104 | break; | |
105 | ||
106 | if (time_after(jiffies, timeout)) { | |
107 | dev_err(&mt2712_rtc->rtc->dev, | |
108 | "%s time out!\n", __func__); | |
109 | break; | |
110 | } | |
111 | cpu_relax(); | |
112 | } | |
113 | } | |
114 | ||
115 | static void mt2712_rtc_writeif_unlock(struct mt2712_rtc *mt2712_rtc) | |
116 | { | |
117 | mt2712_writel(mt2712_rtc, MT2712_PROT, MT2712_PROT_UNLOCK1); | |
118 | mt2712_rtc_write_trigger(mt2712_rtc); | |
119 | mt2712_writel(mt2712_rtc, MT2712_PROT, MT2712_PROT_UNLOCK2); | |
120 | mt2712_rtc_write_trigger(mt2712_rtc); | |
121 | } | |
122 | ||
123 | static irqreturn_t rtc_irq_handler_thread(int irq, void *data) | |
124 | { | |
125 | struct mt2712_rtc *mt2712_rtc = data; | |
126 | u16 irqsta; | |
127 | ||
128 | /* Clear interrupt */ | |
129 | irqsta = mt2712_readl(mt2712_rtc, MT2712_IRQ_STA); | |
130 | if (irqsta & MT2712_IRQ_STA_AL) { | |
131 | rtc_update_irq(mt2712_rtc->rtc, 1, RTC_IRQF | RTC_AF); | |
132 | return IRQ_HANDLED; | |
133 | } | |
134 | ||
135 | return IRQ_NONE; | |
136 | } | |
137 | ||
138 | static void __mt2712_rtc_read_time(struct mt2712_rtc *mt2712_rtc, | |
139 | struct rtc_time *tm, int *sec) | |
140 | { | |
141 | tm->tm_sec = mt2712_readl(mt2712_rtc, MT2712_TC_SEC) | |
142 | & MT2712_SEC_MASK; | |
143 | tm->tm_min = mt2712_readl(mt2712_rtc, MT2712_TC_MIN) | |
144 | & MT2712_MIN_MASK; | |
145 | tm->tm_hour = mt2712_readl(mt2712_rtc, MT2712_TC_HOU) | |
146 | & MT2712_HOU_MASK; | |
147 | tm->tm_mday = mt2712_readl(mt2712_rtc, MT2712_TC_DOM) | |
148 | & MT2712_DOM_MASK; | |
149 | tm->tm_mon = (mt2712_readl(mt2712_rtc, MT2712_TC_MTH) - 1) | |
150 | & MT2712_MTH_MASK; | |
151 | tm->tm_year = (mt2712_readl(mt2712_rtc, MT2712_TC_YEA) + 100) | |
152 | & MT2712_YEA_MASK; | |
153 | ||
154 | *sec = mt2712_readl(mt2712_rtc, MT2712_TC_SEC) & MT2712_SEC_MASK; | |
155 | } | |
156 | ||
157 | static int mt2712_rtc_read_time(struct device *dev, struct rtc_time *tm) | |
158 | { | |
159 | struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); | |
160 | int sec; | |
161 | ||
162 | if (mt2712_rtc->powerlost) | |
163 | return -EINVAL; | |
164 | ||
165 | do { | |
166 | __mt2712_rtc_read_time(mt2712_rtc, tm, &sec); | |
167 | } while (sec < tm->tm_sec); /* SEC has carried */ | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
172 | static int mt2712_rtc_set_time(struct device *dev, struct rtc_time *tm) | |
173 | { | |
174 | struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); | |
175 | ||
176 | mt2712_writel(mt2712_rtc, MT2712_TC_SEC, tm->tm_sec & MT2712_SEC_MASK); | |
177 | mt2712_writel(mt2712_rtc, MT2712_TC_MIN, tm->tm_min & MT2712_MIN_MASK); | |
178 | mt2712_writel(mt2712_rtc, MT2712_TC_HOU, tm->tm_hour & MT2712_HOU_MASK); | |
179 | mt2712_writel(mt2712_rtc, MT2712_TC_DOM, tm->tm_mday & MT2712_DOM_MASK); | |
180 | mt2712_writel(mt2712_rtc, MT2712_TC_MTH, | |
181 | (tm->tm_mon + 1) & MT2712_MTH_MASK); | |
182 | mt2712_writel(mt2712_rtc, MT2712_TC_YEA, | |
183 | (tm->tm_year - 100) & MT2712_YEA_MASK); | |
184 | ||
185 | mt2712_rtc_write_trigger(mt2712_rtc); | |
186 | ||
187 | if (mt2712_rtc->powerlost) | |
188 | mt2712_rtc->powerlost = false; | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static int mt2712_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) | |
194 | { | |
195 | struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); | |
196 | struct rtc_time *tm = &alm->time; | |
197 | u16 irqen; | |
198 | ||
199 | irqen = mt2712_readl(mt2712_rtc, MT2712_IRQ_EN); | |
200 | alm->enabled = !!(irqen & MT2712_IRQ_EN_AL); | |
201 | ||
202 | tm->tm_sec = mt2712_readl(mt2712_rtc, MT2712_AL_SEC) & MT2712_SEC_MASK; | |
203 | tm->tm_min = mt2712_readl(mt2712_rtc, MT2712_AL_MIN) & MT2712_MIN_MASK; | |
204 | tm->tm_hour = mt2712_readl(mt2712_rtc, MT2712_AL_HOU) & MT2712_HOU_MASK; | |
205 | tm->tm_mday = mt2712_readl(mt2712_rtc, MT2712_AL_DOM) & MT2712_DOM_MASK; | |
206 | tm->tm_mon = (mt2712_readl(mt2712_rtc, MT2712_AL_MTH) - 1) | |
207 | & MT2712_MTH_MASK; | |
208 | tm->tm_year = (mt2712_readl(mt2712_rtc, MT2712_AL_YEA) + 100) | |
209 | & MT2712_YEA_MASK; | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
214 | static int mt2712_rtc_alarm_irq_enable(struct device *dev, | |
215 | unsigned int enabled) | |
216 | { | |
217 | struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); | |
218 | u16 irqen; | |
219 | ||
220 | irqen = mt2712_readl(mt2712_rtc, MT2712_IRQ_EN); | |
221 | if (enabled) | |
222 | irqen |= MT2712_IRQ_EN_AL; | |
223 | else | |
224 | irqen &= ~MT2712_IRQ_EN_AL; | |
225 | mt2712_writel(mt2712_rtc, MT2712_IRQ_EN, irqen); | |
226 | mt2712_rtc_write_trigger(mt2712_rtc); | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | static int mt2712_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) | |
232 | { | |
233 | struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); | |
234 | struct rtc_time *tm = &alm->time; | |
235 | ||
236 | dev_dbg(&mt2712_rtc->rtc->dev, "set al time: %ptR, alm en: %d\n", | |
237 | tm, alm->enabled); | |
238 | ||
239 | mt2712_writel(mt2712_rtc, MT2712_AL_SEC, | |
240 | (mt2712_readl(mt2712_rtc, MT2712_AL_SEC) | |
241 | & ~(MT2712_SEC_MASK)) | (tm->tm_sec & MT2712_SEC_MASK)); | |
242 | mt2712_writel(mt2712_rtc, MT2712_AL_MIN, | |
243 | (mt2712_readl(mt2712_rtc, MT2712_AL_MIN) | |
244 | & ~(MT2712_MIN_MASK)) | (tm->tm_min & MT2712_MIN_MASK)); | |
245 | mt2712_writel(mt2712_rtc, MT2712_AL_HOU, | |
246 | (mt2712_readl(mt2712_rtc, MT2712_AL_HOU) | |
247 | & ~(MT2712_HOU_MASK)) | (tm->tm_hour & MT2712_HOU_MASK)); | |
248 | mt2712_writel(mt2712_rtc, MT2712_AL_DOM, | |
249 | (mt2712_readl(mt2712_rtc, MT2712_AL_DOM) | |
250 | & ~(MT2712_DOM_MASK)) | (tm->tm_mday & MT2712_DOM_MASK)); | |
251 | mt2712_writel(mt2712_rtc, MT2712_AL_MTH, | |
252 | (mt2712_readl(mt2712_rtc, MT2712_AL_MTH) | |
253 | & ~(MT2712_MTH_MASK)) | |
254 | | ((tm->tm_mon + 1) & MT2712_MTH_MASK)); | |
255 | mt2712_writel(mt2712_rtc, MT2712_AL_YEA, | |
256 | (mt2712_readl(mt2712_rtc, MT2712_AL_YEA) | |
257 | & ~(MT2712_YEA_MASK)) | |
258 | | ((tm->tm_year - 100) & MT2712_YEA_MASK)); | |
259 | ||
260 | /* mask day of week */ | |
261 | mt2712_writel(mt2712_rtc, MT2712_AL_MASK, MT2712_AL_MASK_DOW); | |
262 | mt2712_rtc_write_trigger(mt2712_rtc); | |
263 | ||
264 | mt2712_rtc_alarm_irq_enable(dev, alm->enabled); | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
269 | /* Init RTC register */ | |
270 | static void mt2712_rtc_hw_init(struct mt2712_rtc *mt2712_rtc) | |
271 | { | |
272 | u32 p1, p2; | |
273 | ||
274 | mt2712_writel(mt2712_rtc, MT2712_BBPU, | |
275 | MT2712_BBPU_KEY | MT2712_BBPU_RELOAD); | |
276 | ||
277 | mt2712_writel(mt2712_rtc, MT2712_CII_EN, 0); | |
278 | mt2712_writel(mt2712_rtc, MT2712_AL_MASK, 0); | |
279 | /* necessary before set MT2712_POWERKEY */ | |
280 | mt2712_writel(mt2712_rtc, MT2712_CON0, 0x4848); | |
281 | mt2712_writel(mt2712_rtc, MT2712_CON1, 0x0048); | |
282 | ||
283 | mt2712_rtc_write_trigger(mt2712_rtc); | |
284 | ||
285 | p1 = mt2712_readl(mt2712_rtc, MT2712_POWERKEY1); | |
286 | p2 = mt2712_readl(mt2712_rtc, MT2712_POWERKEY2); | |
287 | if (p1 != MT2712_POWERKEY1_KEY || p2 != MT2712_POWERKEY2_KEY) { | |
288 | mt2712_rtc->powerlost = true; | |
289 | dev_dbg(&mt2712_rtc->rtc->dev, | |
290 | "powerkey not set (lost power)\n"); | |
291 | } else { | |
292 | mt2712_rtc->powerlost = false; | |
293 | } | |
294 | ||
295 | /* RTC need POWERKEY1/2 match, then goto normal work mode */ | |
296 | mt2712_writel(mt2712_rtc, MT2712_POWERKEY1, MT2712_POWERKEY1_KEY); | |
297 | mt2712_writel(mt2712_rtc, MT2712_POWERKEY2, MT2712_POWERKEY2_KEY); | |
298 | mt2712_rtc_write_trigger(mt2712_rtc); | |
299 | ||
300 | mt2712_rtc_writeif_unlock(mt2712_rtc); | |
301 | } | |
302 | ||
303 | static const struct rtc_class_ops mt2712_rtc_ops = { | |
304 | .read_time = mt2712_rtc_read_time, | |
305 | .set_time = mt2712_rtc_set_time, | |
306 | .read_alarm = mt2712_rtc_read_alarm, | |
307 | .set_alarm = mt2712_rtc_set_alarm, | |
308 | .alarm_irq_enable = mt2712_rtc_alarm_irq_enable, | |
309 | }; | |
310 | ||
311 | static int mt2712_rtc_probe(struct platform_device *pdev) | |
312 | { | |
313 | struct resource *res; | |
314 | struct mt2712_rtc *mt2712_rtc; | |
315 | int ret; | |
316 | ||
317 | mt2712_rtc = devm_kzalloc(&pdev->dev, | |
318 | sizeof(struct mt2712_rtc), GFP_KERNEL); | |
319 | if (!mt2712_rtc) | |
320 | return -ENOMEM; | |
321 | ||
322 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
323 | mt2712_rtc->base = devm_ioremap_resource(&pdev->dev, res); | |
324 | if (IS_ERR(mt2712_rtc->base)) | |
325 | return PTR_ERR(mt2712_rtc->base); | |
326 | ||
327 | /* rtc hw init */ | |
328 | mt2712_rtc_hw_init(mt2712_rtc); | |
329 | ||
330 | mt2712_rtc->irq = platform_get_irq(pdev, 0); | |
331 | if (mt2712_rtc->irq < 0) { | |
332 | dev_err(&pdev->dev, "No IRQ resource\n"); | |
333 | return mt2712_rtc->irq; | |
334 | } | |
335 | ||
336 | platform_set_drvdata(pdev, mt2712_rtc); | |
337 | ||
338 | mt2712_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); | |
339 | if (IS_ERR(mt2712_rtc->rtc)) | |
340 | return PTR_ERR(mt2712_rtc->rtc); | |
341 | ||
342 | ret = devm_request_threaded_irq(&pdev->dev, mt2712_rtc->irq, NULL, | |
343 | rtc_irq_handler_thread, | |
344 | IRQF_ONESHOT | IRQF_TRIGGER_LOW, | |
345 | dev_name(&mt2712_rtc->rtc->dev), | |
346 | mt2712_rtc); | |
347 | if (ret) { | |
348 | dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", | |
349 | mt2712_rtc->irq, ret); | |
350 | return ret; | |
351 | } | |
352 | ||
353 | device_init_wakeup(&pdev->dev, true); | |
354 | ||
355 | mt2712_rtc->rtc->ops = &mt2712_rtc_ops; | |
356 | mt2712_rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; | |
357 | mt2712_rtc->rtc->range_max = MT2712_RTC_TIMESTAMP_END_2127; | |
358 | ||
359 | ret = rtc_register_device(mt2712_rtc->rtc); | |
360 | if (ret) { | |
361 | dev_err(&pdev->dev, "register rtc device failed\n"); | |
362 | return ret; | |
363 | } | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
368 | #ifdef CONFIG_PM_SLEEP | |
369 | static int mt2712_rtc_suspend(struct device *dev) | |
370 | { | |
371 | int wake_status = 0; | |
372 | struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); | |
373 | ||
374 | if (device_may_wakeup(dev)) { | |
375 | wake_status = enable_irq_wake(mt2712_rtc->irq); | |
376 | if (!wake_status) | |
377 | mt2712_rtc->irq_wake_enabled = true; | |
378 | } | |
379 | ||
380 | return 0; | |
381 | } | |
382 | ||
383 | static int mt2712_rtc_resume(struct device *dev) | |
384 | { | |
385 | int wake_status = 0; | |
386 | struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); | |
387 | ||
388 | if (device_may_wakeup(dev) && mt2712_rtc->irq_wake_enabled) { | |
389 | wake_status = disable_irq_wake(mt2712_rtc->irq); | |
390 | if (!wake_status) | |
391 | mt2712_rtc->irq_wake_enabled = false; | |
392 | } | |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
397 | static SIMPLE_DEV_PM_OPS(mt2712_pm_ops, mt2712_rtc_suspend, | |
398 | mt2712_rtc_resume); | |
399 | #endif | |
400 | ||
401 | static const struct of_device_id mt2712_rtc_of_match[] = { | |
402 | { .compatible = "mediatek,mt2712-rtc", }, | |
403 | { }, | |
404 | }; | |
405 | ||
406 | MODULE_DEVICE_TABLE(of, mt2712_rtc_of_match); | |
407 | ||
408 | static struct platform_driver mt2712_rtc_driver = { | |
409 | .driver = { | |
410 | .name = "mt2712-rtc", | |
411 | .of_match_table = mt2712_rtc_of_match, | |
30a79065 | 412 | #ifdef CONFIG_PM_SLEEP |
64823360 | 413 | .pm = &mt2712_pm_ops, |
30a79065 | 414 | #endif |
64823360 RB |
415 | }, |
416 | .probe = mt2712_rtc_probe, | |
417 | }; | |
418 | ||
419 | module_platform_driver(mt2712_rtc_driver); | |
420 | ||
421 | MODULE_DESCRIPTION("MediaTek MT2712 SoC based RTC Driver"); | |
422 | MODULE_AUTHOR("Ran Bi <ran.bi@mediatek.com>"); | |
423 | MODULE_LICENSE("GPL"); |