]>
Commit | Line | Data |
---|---|---|
64823360 RB |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2019 MediaTek Inc. | |
4 | * Author: Ran Bi <[email protected]> | |
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 | { | |
64823360 RB |
313 | struct mt2712_rtc *mt2712_rtc; |
314 | int ret; | |
315 | ||
316 | mt2712_rtc = devm_kzalloc(&pdev->dev, | |
317 | sizeof(struct mt2712_rtc), GFP_KERNEL); | |
318 | if (!mt2712_rtc) | |
319 | return -ENOMEM; | |
320 | ||
06030d50 | 321 | mt2712_rtc->base = devm_platform_ioremap_resource(pdev, 0); |
64823360 RB |
322 | if (IS_ERR(mt2712_rtc->base)) |
323 | return PTR_ERR(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); | |
944ed452 | 329 | if (mt2712_rtc->irq < 0) |
64823360 | 330 | return mt2712_rtc->irq; |
64823360 RB |
331 | |
332 | platform_set_drvdata(pdev, mt2712_rtc); | |
333 | ||
334 | mt2712_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); | |
335 | if (IS_ERR(mt2712_rtc->rtc)) | |
336 | return PTR_ERR(mt2712_rtc->rtc); | |
337 | ||
338 | ret = devm_request_threaded_irq(&pdev->dev, mt2712_rtc->irq, NULL, | |
339 | rtc_irq_handler_thread, | |
340 | IRQF_ONESHOT | IRQF_TRIGGER_LOW, | |
341 | dev_name(&mt2712_rtc->rtc->dev), | |
342 | 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(&pdev->dev, 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 | ||
3642b17e | 355 | return rtc_register_device(mt2712_rtc->rtc); |
64823360 RB |
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(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(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, | |
30a79065 | 402 | #ifdef CONFIG_PM_SLEEP |
64823360 | 403 | .pm = &mt2712_pm_ops, |
30a79065 | 404 | #endif |
64823360 RB |
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 <[email protected]>"); | |
413 | MODULE_LICENSE("GPL"); |