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(addr: 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, addr: 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, val: 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(rtc: mt2712_rtc->rtc, num: 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: &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, val: tm->tm_sec & MT2712_SEC_MASK); |
177 | mt2712_writel(mt2712_rtc, MT2712_TC_MIN, val: tm->tm_min & MT2712_MIN_MASK); |
178 | mt2712_writel(mt2712_rtc, MT2712_TC_HOU, val: tm->tm_hour & MT2712_HOU_MASK); |
179 | mt2712_writel(mt2712_rtc, MT2712_TC_DOM, val: tm->tm_mday & MT2712_DOM_MASK); |
180 | mt2712_writel(mt2712_rtc, MT2712_TC_MTH, |
181 | val: (tm->tm_mon + 1) & MT2712_MTH_MASK); |
182 | mt2712_writel(mt2712_rtc, MT2712_TC_YEA, |
183 | val: (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, val: 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 | val: (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 | val: (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 | val: (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 | val: (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 | val: (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 | val: (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, enabled: 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, val: 0); |
278 | mt2712_writel(mt2712_rtc, MT2712_AL_MASK, val: 0); |
279 | /* necessary before set MT2712_POWERKEY */ |
280 | mt2712_writel(mt2712_rtc, MT2712_CON0, val: 0x4848); |
281 | mt2712_writel(mt2712_rtc, MT2712_CON1, val: 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 mt2712_rtc *mt2712_rtc; |
314 | int ret; |
315 | |
316 | mt2712_rtc = devm_kzalloc(dev: &pdev->dev, |
317 | size: sizeof(struct mt2712_rtc), GFP_KERNEL); |
318 | if (!mt2712_rtc) |
319 | return -ENOMEM; |
320 | |
321 | mt2712_rtc->base = devm_platform_ioremap_resource(pdev, index: 0); |
322 | if (IS_ERR(ptr: mt2712_rtc->base)) |
323 | return PTR_ERR(ptr: mt2712_rtc->base); |
324 | |
325 | /* rtc hw init */ |
326 | mt2712_rtc_hw_init(mt2712_rtc); |
327 | |
328 | mt2712_rtc->irq = platform_get_irq(pdev, 0); |
329 | if (mt2712_rtc->irq < 0) |
330 | return mt2712_rtc->irq; |
331 | |
332 | platform_set_drvdata(pdev, data: mt2712_rtc); |
333 | |
334 | mt2712_rtc->rtc = devm_rtc_allocate_device(dev: &pdev->dev); |
335 | if (IS_ERR(ptr: mt2712_rtc->rtc)) |
336 | return PTR_ERR(ptr: mt2712_rtc->rtc); |
337 | |
338 | ret = devm_request_threaded_irq(dev: &pdev->dev, irq: mt2712_rtc->irq, NULL, |
339 | thread_fn: rtc_irq_handler_thread, |
340 | IRQF_ONESHOT | IRQF_TRIGGER_LOW, |
341 | devname: dev_name(dev: &mt2712_rtc->rtc->dev), |
342 | dev_id: mt2712_rtc); |
343 | if (ret) { |
344 | dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n" , |
345 | mt2712_rtc->irq, ret); |
346 | return ret; |
347 | } |
348 | |
349 | device_init_wakeup(dev: &pdev->dev, enable: true); |
350 | |
351 | mt2712_rtc->rtc->ops = &mt2712_rtc_ops; |
352 | mt2712_rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; |
353 | mt2712_rtc->rtc->range_max = MT2712_RTC_TIMESTAMP_END_2127; |
354 | |
355 | return devm_rtc_register_device(mt2712_rtc->rtc); |
356 | } |
357 | |
358 | #ifdef CONFIG_PM_SLEEP |
359 | static int mt2712_rtc_suspend(struct device *dev) |
360 | { |
361 | int wake_status = 0; |
362 | struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); |
363 | |
364 | if (device_may_wakeup(dev)) { |
365 | wake_status = enable_irq_wake(irq: mt2712_rtc->irq); |
366 | if (!wake_status) |
367 | mt2712_rtc->irq_wake_enabled = true; |
368 | } |
369 | |
370 | return 0; |
371 | } |
372 | |
373 | static int mt2712_rtc_resume(struct device *dev) |
374 | { |
375 | int wake_status = 0; |
376 | struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); |
377 | |
378 | if (device_may_wakeup(dev) && mt2712_rtc->irq_wake_enabled) { |
379 | wake_status = disable_irq_wake(irq: mt2712_rtc->irq); |
380 | if (!wake_status) |
381 | mt2712_rtc->irq_wake_enabled = false; |
382 | } |
383 | |
384 | return 0; |
385 | } |
386 | |
387 | static SIMPLE_DEV_PM_OPS(mt2712_pm_ops, mt2712_rtc_suspend, |
388 | mt2712_rtc_resume); |
389 | #endif |
390 | |
391 | static const struct of_device_id mt2712_rtc_of_match[] = { |
392 | { .compatible = "mediatek,mt2712-rtc" , }, |
393 | { }, |
394 | }; |
395 | |
396 | MODULE_DEVICE_TABLE(of, mt2712_rtc_of_match); |
397 | |
398 | static struct platform_driver mt2712_rtc_driver = { |
399 | .driver = { |
400 | .name = "mt2712-rtc" , |
401 | .of_match_table = mt2712_rtc_of_match, |
402 | #ifdef CONFIG_PM_SLEEP |
403 | .pm = &mt2712_pm_ops, |
404 | #endif |
405 | }, |
406 | .probe = mt2712_rtc_probe, |
407 | }; |
408 | |
409 | module_platform_driver(mt2712_rtc_driver); |
410 | |
411 | MODULE_DESCRIPTION("MediaTek MT2712 SoC based RTC Driver" ); |
412 | MODULE_AUTHOR("Ran Bi <ran.bi@mediatek.com>" ); |
413 | MODULE_LICENSE("GPL" ); |
414 | |