Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
0146f261 FT |
2 | /* |
3 | * rtc-mrst.c: Driver for Moorestown virtual RTC | |
4 | * | |
5 | * (C) Copyright 2009 Intel Corporation | |
6 | * Author: Jacob Pan (jacob.jun.pan@intel.com) | |
7 | * Feng Tang (feng.tang@intel.com) | |
8 | * | |
0146f261 FT |
9 | * Note: |
10 | * VRTC is emulated by system controller firmware, the real HW | |
11 | * RTC is located in the PMIC device. SCU FW shadows PMIC RTC | |
12 | * in a memory mapped IO space that is visible to the host IA | |
13 | * processor. | |
14 | * | |
15 | * This driver is based upon drivers/rtc/rtc-cmos.c | |
16 | */ | |
17 | ||
18 | /* | |
19 | * Note: | |
20 | * * vRTC only supports binary mode and 24H mode | |
21 | * * vRTC only support PIE and AIE, no UIE, and its PIE only happens | |
22 | * at 23:59:59pm everyday, no support for adjustable frequency | |
23 | * * Alarm function is also limited to hr/min/sec. | |
24 | */ | |
25 | ||
26 | #include <linux/mod_devicetable.h> | |
27 | #include <linux/platform_device.h> | |
28 | #include <linux/interrupt.h> | |
29 | #include <linux/spinlock.h> | |
30 | #include <linux/kernel.h> | |
463a8630 | 31 | #include <linux/mc146818rtc.h> |
0146f261 FT |
32 | #include <linux/module.h> |
33 | #include <linux/init.h> | |
34 | #include <linux/sfi.h> | |
35 | ||
0146f261 | 36 | #include <asm/intel_scu_ipc.h> |
05454c26 KS |
37 | #include <asm/intel-mid.h> |
38 | #include <asm/intel_mid_vrtc.h> | |
0146f261 FT |
39 | |
40 | struct mrst_rtc { | |
41 | struct rtc_device *rtc; | |
42 | struct device *dev; | |
43 | int irq; | |
0146f261 FT |
44 | |
45 | u8 enabled_wake; | |
46 | u8 suspend_ctrl; | |
47 | }; | |
48 | ||
49 | static const char driver_name[] = "rtc_mrst"; | |
50 | ||
51 | #define RTC_IRQMASK (RTC_PF | RTC_AF) | |
52 | ||
53 | static inline int is_intr(u8 rtc_intr) | |
54 | { | |
55 | if (!(rtc_intr & RTC_IRQF)) | |
56 | return 0; | |
57 | return rtc_intr & RTC_IRQMASK; | |
58 | } | |
59 | ||
168202c7 FT |
60 | static inline unsigned char vrtc_is_updating(void) |
61 | { | |
62 | unsigned char uip; | |
63 | unsigned long flags; | |
64 | ||
65 | spin_lock_irqsave(&rtc_lock, flags); | |
66 | uip = (vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP); | |
67 | spin_unlock_irqrestore(&rtc_lock, flags); | |
68 | return uip; | |
69 | } | |
70 | ||
0146f261 FT |
71 | /* |
72 | * rtc_time's year contains the increment over 1900, but vRTC's YEAR | |
73 | * register can't be programmed to value larger than 0x64, so vRTC | |
57e6319d | 74 | * driver chose to use 1972 (1970 is UNIX time start point) as the base, |
d3e1884b FT |
75 | * and does the translation at read/write time. |
76 | * | |
57e6319d | 77 | * Why not just use 1970 as the offset? it's because using 1972 will |
d3e1884b | 78 | * make it consistent in leap year setting for both vrtc and low-level |
57e6319d FT |
79 | * physical rtc devices. Then why not use 1960 as the offset? If we use |
80 | * 1960, for a device's first use, its YEAR register is 0 and the system | |
81 | * year will be parsed as 1960 which is not a valid UNIX time and will | |
82 | * cause many applications to fail mysteriously. | |
0146f261 FT |
83 | */ |
84 | static int mrst_read_time(struct device *dev, struct rtc_time *time) | |
85 | { | |
86 | unsigned long flags; | |
87 | ||
168202c7 | 88 | if (vrtc_is_updating()) |
c6b5eb8d | 89 | msleep(20); |
0146f261 FT |
90 | |
91 | spin_lock_irqsave(&rtc_lock, flags); | |
92 | time->tm_sec = vrtc_cmos_read(RTC_SECONDS); | |
93 | time->tm_min = vrtc_cmos_read(RTC_MINUTES); | |
94 | time->tm_hour = vrtc_cmos_read(RTC_HOURS); | |
95 | time->tm_mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); | |
96 | time->tm_mon = vrtc_cmos_read(RTC_MONTH); | |
97 | time->tm_year = vrtc_cmos_read(RTC_YEAR); | |
98 | spin_unlock_irqrestore(&rtc_lock, flags); | |
99 | ||
57e6319d FT |
100 | /* Adjust for the 1972/1900 */ |
101 | time->tm_year += 72; | |
0146f261 | 102 | time->tm_mon--; |
22652ba7 | 103 | return 0; |
0146f261 FT |
104 | } |
105 | ||
106 | static int mrst_set_time(struct device *dev, struct rtc_time *time) | |
107 | { | |
108 | int ret; | |
109 | unsigned long flags; | |
110 | unsigned char mon, day, hrs, min, sec; | |
111 | unsigned int yrs; | |
112 | ||
113 | yrs = time->tm_year; | |
114 | mon = time->tm_mon + 1; /* tm_mon starts at zero */ | |
115 | day = time->tm_mday; | |
116 | hrs = time->tm_hour; | |
117 | min = time->tm_min; | |
118 | sec = time->tm_sec; | |
119 | ||
f441f98f | 120 | if (yrs < 72 || yrs > 172) |
0146f261 | 121 | return -EINVAL; |
57e6319d | 122 | yrs -= 72; |
0146f261 FT |
123 | |
124 | spin_lock_irqsave(&rtc_lock, flags); | |
125 | ||
126 | vrtc_cmos_write(yrs, RTC_YEAR); | |
127 | vrtc_cmos_write(mon, RTC_MONTH); | |
128 | vrtc_cmos_write(day, RTC_DAY_OF_MONTH); | |
129 | vrtc_cmos_write(hrs, RTC_HOURS); | |
130 | vrtc_cmos_write(min, RTC_MINUTES); | |
131 | vrtc_cmos_write(sec, RTC_SECONDS); | |
132 | ||
133 | spin_unlock_irqrestore(&rtc_lock, flags); | |
134 | ||
135 | ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME); | |
136 | return ret; | |
137 | } | |
138 | ||
139 | static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t) | |
140 | { | |
141 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | |
142 | unsigned char rtc_control; | |
143 | ||
144 | if (mrst->irq <= 0) | |
145 | return -EIO; | |
146 | ||
0146f261 FT |
147 | /* vRTC only supports binary mode */ |
148 | spin_lock_irq(&rtc_lock); | |
149 | t->time.tm_sec = vrtc_cmos_read(RTC_SECONDS_ALARM); | |
150 | t->time.tm_min = vrtc_cmos_read(RTC_MINUTES_ALARM); | |
151 | t->time.tm_hour = vrtc_cmos_read(RTC_HOURS_ALARM); | |
152 | ||
153 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | |
154 | spin_unlock_irq(&rtc_lock); | |
155 | ||
156 | t->enabled = !!(rtc_control & RTC_AIE); | |
157 | t->pending = 0; | |
158 | ||
159 | return 0; | |
160 | } | |
161 | ||
162 | static void mrst_checkintr(struct mrst_rtc *mrst, unsigned char rtc_control) | |
163 | { | |
164 | unsigned char rtc_intr; | |
165 | ||
166 | /* | |
167 | * NOTE after changing RTC_xIE bits we always read INTR_FLAGS; | |
168 | * allegedly some older rtcs need that to handle irqs properly | |
169 | */ | |
170 | rtc_intr = vrtc_cmos_read(RTC_INTR_FLAGS); | |
171 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | |
172 | if (is_intr(rtc_intr)) | |
173 | rtc_update_irq(mrst->rtc, 1, rtc_intr); | |
174 | } | |
175 | ||
176 | static void mrst_irq_enable(struct mrst_rtc *mrst, unsigned char mask) | |
177 | { | |
178 | unsigned char rtc_control; | |
179 | ||
180 | /* | |
181 | * Flush any pending IRQ status, notably for update irqs, | |
182 | * before we enable new IRQs | |
183 | */ | |
184 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | |
185 | mrst_checkintr(mrst, rtc_control); | |
186 | ||
187 | rtc_control |= mask; | |
188 | vrtc_cmos_write(rtc_control, RTC_CONTROL); | |
189 | ||
190 | mrst_checkintr(mrst, rtc_control); | |
191 | } | |
192 | ||
193 | static void mrst_irq_disable(struct mrst_rtc *mrst, unsigned char mask) | |
194 | { | |
195 | unsigned char rtc_control; | |
196 | ||
197 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | |
198 | rtc_control &= ~mask; | |
199 | vrtc_cmos_write(rtc_control, RTC_CONTROL); | |
200 | mrst_checkintr(mrst, rtc_control); | |
201 | } | |
202 | ||
203 | static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t) | |
204 | { | |
205 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | |
206 | unsigned char hrs, min, sec; | |
207 | int ret = 0; | |
208 | ||
209 | if (!mrst->irq) | |
210 | return -EIO; | |
211 | ||
212 | hrs = t->time.tm_hour; | |
213 | min = t->time.tm_min; | |
214 | sec = t->time.tm_sec; | |
215 | ||
216 | spin_lock_irq(&rtc_lock); | |
217 | /* Next rtc irq must not be from previous alarm setting */ | |
218 | mrst_irq_disable(mrst, RTC_AIE); | |
219 | ||
220 | /* Update alarm */ | |
221 | vrtc_cmos_write(hrs, RTC_HOURS_ALARM); | |
222 | vrtc_cmos_write(min, RTC_MINUTES_ALARM); | |
223 | vrtc_cmos_write(sec, RTC_SECONDS_ALARM); | |
224 | ||
225 | spin_unlock_irq(&rtc_lock); | |
226 | ||
227 | ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM); | |
228 | if (ret) | |
229 | return ret; | |
230 | ||
231 | spin_lock_irq(&rtc_lock); | |
232 | if (t->enabled) | |
233 | mrst_irq_enable(mrst, RTC_AIE); | |
234 | ||
235 | spin_unlock_irq(&rtc_lock); | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
0146f261 | 240 | /* Currently, the vRTC doesn't support UIE ON/OFF */ |
16380c15 | 241 | static int mrst_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
0146f261 FT |
242 | { |
243 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | |
244 | unsigned long flags; | |
245 | ||
0146f261 | 246 | spin_lock_irqsave(&rtc_lock, flags); |
16380c15 | 247 | if (enabled) |
0146f261 | 248 | mrst_irq_enable(mrst, RTC_AIE); |
16380c15 JS |
249 | else |
250 | mrst_irq_disable(mrst, RTC_AIE); | |
0146f261 FT |
251 | spin_unlock_irqrestore(&rtc_lock, flags); |
252 | return 0; | |
253 | } | |
254 | ||
0146f261 | 255 | |
6fca3fc5 | 256 | #if IS_ENABLED(CONFIG_RTC_INTF_PROC) |
0146f261 FT |
257 | |
258 | static int mrst_procfs(struct device *dev, struct seq_file *seq) | |
259 | { | |
de96bc39 | 260 | unsigned char rtc_control; |
0146f261 FT |
261 | |
262 | spin_lock_irq(&rtc_lock); | |
263 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | |
0146f261 FT |
264 | spin_unlock_irq(&rtc_lock); |
265 | ||
4395eb1f JP |
266 | seq_printf(seq, |
267 | "periodic_IRQ\t: %s\n" | |
268 | "alarm\t\t: %s\n" | |
269 | "BCD\t\t: no\n" | |
270 | "periodic_freq\t: daily (not adjustable)\n", | |
271 | (rtc_control & RTC_PIE) ? "on" : "off", | |
272 | (rtc_control & RTC_AIE) ? "on" : "off"); | |
273 | ||
274 | return 0; | |
0146f261 FT |
275 | } |
276 | ||
277 | #else | |
278 | #define mrst_procfs NULL | |
279 | #endif | |
280 | ||
281 | static const struct rtc_class_ops mrst_rtc_ops = { | |
0146f261 FT |
282 | .read_time = mrst_read_time, |
283 | .set_time = mrst_set_time, | |
284 | .read_alarm = mrst_read_alarm, | |
285 | .set_alarm = mrst_set_alarm, | |
286 | .proc = mrst_procfs, | |
16380c15 | 287 | .alarm_irq_enable = mrst_rtc_alarm_irq_enable, |
0146f261 FT |
288 | }; |
289 | ||
290 | static struct mrst_rtc mrst_rtc; | |
291 | ||
292 | /* | |
293 | * When vRTC IRQ is captured by SCU FW, FW will clear the AIE bit in | |
294 | * Reg B, so no need for this driver to clear it | |
295 | */ | |
296 | static irqreturn_t mrst_rtc_irq(int irq, void *p) | |
297 | { | |
298 | u8 irqstat; | |
299 | ||
300 | spin_lock(&rtc_lock); | |
301 | /* This read will clear all IRQ flags inside Reg C */ | |
302 | irqstat = vrtc_cmos_read(RTC_INTR_FLAGS); | |
303 | spin_unlock(&rtc_lock); | |
304 | ||
305 | irqstat &= RTC_IRQMASK | RTC_IRQF; | |
306 | if (is_intr(irqstat)) { | |
307 | rtc_update_irq(p, 1, irqstat); | |
308 | return IRQ_HANDLED; | |
309 | } | |
310 | return IRQ_NONE; | |
311 | } | |
312 | ||
5a167f45 GKH |
313 | static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, |
314 | int rtc_irq) | |
0146f261 FT |
315 | { |
316 | int retval = 0; | |
317 | unsigned char rtc_control; | |
318 | ||
319 | /* There can be only one ... */ | |
320 | if (mrst_rtc.dev) | |
321 | return -EBUSY; | |
322 | ||
323 | if (!iomem) | |
324 | return -ENODEV; | |
325 | ||
32b41f93 AB |
326 | iomem = devm_request_mem_region(dev, iomem->start, resource_size(iomem), |
327 | driver_name); | |
0146f261 FT |
328 | if (!iomem) { |
329 | dev_dbg(dev, "i/o mem already in use.\n"); | |
330 | return -EBUSY; | |
331 | } | |
332 | ||
333 | mrst_rtc.irq = rtc_irq; | |
de97a21a FT |
334 | mrst_rtc.dev = dev; |
335 | dev_set_drvdata(dev, &mrst_rtc); | |
0146f261 | 336 | |
32b41f93 AB |
337 | mrst_rtc.rtc = devm_rtc_allocate_device(dev); |
338 | if (IS_ERR(mrst_rtc.rtc)) | |
339 | return PTR_ERR(mrst_rtc.rtc); | |
340 | ||
341 | mrst_rtc.rtc->ops = &mrst_rtc_ops; | |
0146f261 | 342 | |
0146f261 FT |
343 | rename_region(iomem, dev_name(&mrst_rtc.rtc->dev)); |
344 | ||
345 | spin_lock_irq(&rtc_lock); | |
346 | mrst_irq_disable(&mrst_rtc, RTC_PIE | RTC_AIE); | |
347 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | |
348 | spin_unlock_irq(&rtc_lock); | |
349 | ||
350 | if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) | |
351 | dev_dbg(dev, "TODO: support more than 24-hr BCD mode\n"); | |
352 | ||
353 | if (rtc_irq) { | |
32b41f93 AB |
354 | retval = devm_request_irq(dev, rtc_irq, mrst_rtc_irq, |
355 | 0, dev_name(&mrst_rtc.rtc->dev), | |
356 | mrst_rtc.rtc); | |
0146f261 FT |
357 | if (retval < 0) { |
358 | dev_dbg(dev, "IRQ %d is already in use, err %d\n", | |
359 | rtc_irq, retval); | |
32b41f93 | 360 | goto cleanup0; |
0146f261 FT |
361 | } |
362 | } | |
32b41f93 AB |
363 | |
364 | retval = rtc_register_device(mrst_rtc.rtc); | |
ca95ef7c | 365 | if (retval) |
32b41f93 | 366 | goto cleanup0; |
32b41f93 | 367 | |
0146f261 FT |
368 | dev_dbg(dev, "initialised\n"); |
369 | return 0; | |
370 | ||
0146f261 | 371 | cleanup0: |
de97a21a | 372 | mrst_rtc.dev = NULL; |
0146f261 FT |
373 | dev_err(dev, "rtc-mrst: unable to initialise\n"); |
374 | return retval; | |
375 | } | |
376 | ||
377 | static void rtc_mrst_do_shutdown(void) | |
378 | { | |
379 | spin_lock_irq(&rtc_lock); | |
380 | mrst_irq_disable(&mrst_rtc, RTC_IRQMASK); | |
381 | spin_unlock_irq(&rtc_lock); | |
382 | } | |
383 | ||
5a167f45 | 384 | static void rtc_mrst_do_remove(struct device *dev) |
0146f261 FT |
385 | { |
386 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | |
0146f261 FT |
387 | |
388 | rtc_mrst_do_shutdown(); | |
389 | ||
0146f261 | 390 | mrst->rtc = NULL; |
0146f261 | 391 | mrst->dev = NULL; |
0146f261 FT |
392 | } |
393 | ||
ddd2a30d LPC |
394 | #ifdef CONFIG_PM_SLEEP |
395 | static int mrst_suspend(struct device *dev) | |
0146f261 FT |
396 | { |
397 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | |
398 | unsigned char tmp; | |
399 | ||
400 | /* Only the alarm might be a wakeup event source */ | |
401 | spin_lock_irq(&rtc_lock); | |
402 | mrst->suspend_ctrl = tmp = vrtc_cmos_read(RTC_CONTROL); | |
403 | if (tmp & (RTC_PIE | RTC_AIE)) { | |
404 | unsigned char mask; | |
405 | ||
406 | if (device_may_wakeup(dev)) | |
407 | mask = RTC_IRQMASK & ~RTC_AIE; | |
408 | else | |
409 | mask = RTC_IRQMASK; | |
410 | tmp &= ~mask; | |
411 | vrtc_cmos_write(tmp, RTC_CONTROL); | |
412 | ||
413 | mrst_checkintr(mrst, tmp); | |
414 | } | |
415 | spin_unlock_irq(&rtc_lock); | |
416 | ||
417 | if (tmp & RTC_AIE) { | |
418 | mrst->enabled_wake = 1; | |
419 | enable_irq_wake(mrst->irq); | |
420 | } | |
421 | ||
422 | dev_dbg(&mrst_rtc.rtc->dev, "suspend%s, ctrl %02x\n", | |
423 | (tmp & RTC_AIE) ? ", alarm may wake" : "", | |
424 | tmp); | |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
429 | /* | |
430 | * We want RTC alarms to wake us from the deep power saving state | |
431 | */ | |
432 | static inline int mrst_poweroff(struct device *dev) | |
433 | { | |
ddd2a30d | 434 | return mrst_suspend(dev); |
0146f261 FT |
435 | } |
436 | ||
437 | static int mrst_resume(struct device *dev) | |
438 | { | |
439 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | |
440 | unsigned char tmp = mrst->suspend_ctrl; | |
441 | ||
442 | /* Re-enable any irqs previously active */ | |
443 | if (tmp & RTC_IRQMASK) { | |
444 | unsigned char mask; | |
445 | ||
446 | if (mrst->enabled_wake) { | |
447 | disable_irq_wake(mrst->irq); | |
448 | mrst->enabled_wake = 0; | |
449 | } | |
450 | ||
451 | spin_lock_irq(&rtc_lock); | |
452 | do { | |
453 | vrtc_cmos_write(tmp, RTC_CONTROL); | |
454 | ||
455 | mask = vrtc_cmos_read(RTC_INTR_FLAGS); | |
456 | mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; | |
457 | if (!is_intr(mask)) | |
458 | break; | |
459 | ||
460 | rtc_update_irq(mrst->rtc, 1, mask); | |
461 | tmp &= ~RTC_AIE; | |
462 | } while (mask & RTC_AIE); | |
463 | spin_unlock_irq(&rtc_lock); | |
464 | } | |
465 | ||
466 | dev_dbg(&mrst_rtc.rtc->dev, "resume, ctrl %02x\n", tmp); | |
467 | ||
468 | return 0; | |
469 | } | |
470 | ||
ddd2a30d LPC |
471 | static SIMPLE_DEV_PM_OPS(mrst_pm_ops, mrst_suspend, mrst_resume); |
472 | #define MRST_PM_OPS (&mrst_pm_ops) | |
473 | ||
0146f261 | 474 | #else |
ddd2a30d | 475 | #define MRST_PM_OPS NULL |
0146f261 FT |
476 | |
477 | static inline int mrst_poweroff(struct device *dev) | |
478 | { | |
479 | return -ENOSYS; | |
480 | } | |
481 | ||
482 | #endif | |
483 | ||
5a167f45 | 484 | static int vrtc_mrst_platform_probe(struct platform_device *pdev) |
0146f261 FT |
485 | { |
486 | return vrtc_mrst_do_probe(&pdev->dev, | |
487 | platform_get_resource(pdev, IORESOURCE_MEM, 0), | |
488 | platform_get_irq(pdev, 0)); | |
489 | } | |
490 | ||
5a167f45 | 491 | static int vrtc_mrst_platform_remove(struct platform_device *pdev) |
0146f261 FT |
492 | { |
493 | rtc_mrst_do_remove(&pdev->dev); | |
494 | return 0; | |
495 | } | |
496 | ||
497 | static void vrtc_mrst_platform_shutdown(struct platform_device *pdev) | |
498 | { | |
499 | if (system_state == SYSTEM_POWER_OFF && !mrst_poweroff(&pdev->dev)) | |
500 | return; | |
501 | ||
502 | rtc_mrst_do_shutdown(); | |
503 | } | |
504 | ||
505 | MODULE_ALIAS("platform:vrtc_mrst"); | |
506 | ||
507 | static struct platform_driver vrtc_mrst_platform_driver = { | |
508 | .probe = vrtc_mrst_platform_probe, | |
5a167f45 | 509 | .remove = vrtc_mrst_platform_remove, |
0146f261 FT |
510 | .shutdown = vrtc_mrst_platform_shutdown, |
511 | .driver = { | |
ddd2a30d LPC |
512 | .name = driver_name, |
513 | .pm = MRST_PM_OPS, | |
0146f261 FT |
514 | } |
515 | }; | |
516 | ||
0c4eae66 | 517 | module_platform_driver(vrtc_mrst_platform_driver); |
0146f261 FT |
518 | |
519 | MODULE_AUTHOR("Jacob Pan; Feng Tang"); | |
520 | MODULE_DESCRIPTION("Driver for Moorestown virtual RTC"); | |
521 | MODULE_LICENSE("GPL"); |