1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Real-time clock driver for MPC5121 |
4 | * |
5 | * Copyright 2007, Domen Puncer <domen.puncer@telargo.com> |
6 | * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved. |
7 | * Copyright 2011, Dmitry Eremin-Solenikov |
8 | */ |
9 | |
10 | #include <linux/init.h> |
11 | #include <linux/module.h> |
12 | #include <linux/rtc.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_irq.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/io.h> |
17 | #include <linux/slab.h> |
18 | |
19 | struct mpc5121_rtc_regs { |
20 | u8 set_time; /* RTC + 0x00 */ |
21 | u8 hour_set; /* RTC + 0x01 */ |
22 | u8 minute_set; /* RTC + 0x02 */ |
23 | u8 second_set; /* RTC + 0x03 */ |
24 | |
25 | u8 set_date; /* RTC + 0x04 */ |
26 | u8 month_set; /* RTC + 0x05 */ |
27 | u8 weekday_set; /* RTC + 0x06 */ |
28 | u8 date_set; /* RTC + 0x07 */ |
29 | |
30 | u8 write_sw; /* RTC + 0x08 */ |
31 | u8 sw_set; /* RTC + 0x09 */ |
32 | u16 year_set; /* RTC + 0x0a */ |
33 | |
34 | u8 alm_enable; /* RTC + 0x0c */ |
35 | u8 alm_hour_set; /* RTC + 0x0d */ |
36 | u8 alm_min_set; /* RTC + 0x0e */ |
37 | u8 int_enable; /* RTC + 0x0f */ |
38 | |
39 | u8 reserved1; |
40 | u8 hour; /* RTC + 0x11 */ |
41 | u8 minute; /* RTC + 0x12 */ |
42 | u8 second; /* RTC + 0x13 */ |
43 | |
44 | u8 month; /* RTC + 0x14 */ |
45 | u8 wday_mday; /* RTC + 0x15 */ |
46 | u16 year; /* RTC + 0x16 */ |
47 | |
48 | u8 int_alm; /* RTC + 0x18 */ |
49 | u8 int_sw; /* RTC + 0x19 */ |
50 | u8 alm_status; /* RTC + 0x1a */ |
51 | u8 sw_minute; /* RTC + 0x1b */ |
52 | |
53 | u8 bus_error_1; /* RTC + 0x1c */ |
54 | u8 int_day; /* RTC + 0x1d */ |
55 | u8 int_min; /* RTC + 0x1e */ |
56 | u8 int_sec; /* RTC + 0x1f */ |
57 | |
58 | /* |
59 | * target_time: |
60 | * intended to be used for hibernation but hibernation |
61 | * does not work on silicon rev 1.5 so use it for non-volatile |
62 | * storage of offset between the actual_time register and linux |
63 | * time |
64 | */ |
65 | u32 target_time; /* RTC + 0x20 */ |
66 | /* |
67 | * actual_time: |
68 | * readonly time since VBAT_RTC was last connected |
69 | */ |
70 | u32 actual_time; /* RTC + 0x24 */ |
71 | u32 keep_alive; /* RTC + 0x28 */ |
72 | }; |
73 | |
74 | struct mpc5121_rtc_data { |
75 | unsigned irq; |
76 | unsigned irq_periodic; |
77 | struct mpc5121_rtc_regs __iomem *regs; |
78 | struct rtc_device *rtc; |
79 | struct rtc_wkalrm wkalarm; |
80 | }; |
81 | |
82 | /* |
83 | * Update second/minute/hour registers. |
84 | * |
85 | * This is just so alarm will work. |
86 | */ |
87 | static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs, |
88 | struct rtc_time *tm) |
89 | { |
90 | out_8(®s->second_set, tm->tm_sec); |
91 | out_8(®s->minute_set, tm->tm_min); |
92 | out_8(®s->hour_set, tm->tm_hour); |
93 | |
94 | /* set time sequence */ |
95 | out_8(®s->set_time, 0x1); |
96 | out_8(®s->set_time, 0x3); |
97 | out_8(®s->set_time, 0x1); |
98 | out_8(®s->set_time, 0x0); |
99 | } |
100 | |
101 | static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm) |
102 | { |
103 | struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); |
104 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
105 | unsigned long now; |
106 | |
107 | /* |
108 | * linux time is actual_time plus the offset saved in target_time |
109 | */ |
110 | now = in_be32(®s->actual_time) + in_be32(®s->target_time); |
111 | |
112 | rtc_time64_to_tm(time: now, tm); |
113 | |
114 | /* |
115 | * update second minute hour registers |
116 | * so alarms will work |
117 | */ |
118 | mpc5121_rtc_update_smh(regs, tm); |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm) |
124 | { |
125 | struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); |
126 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
127 | unsigned long now; |
128 | |
129 | /* |
130 | * The actual_time register is read only so we write the offset |
131 | * between it and linux time to the target_time register. |
132 | */ |
133 | now = rtc_tm_to_time64(tm); |
134 | out_be32(®s->target_time, now - in_be32(®s->actual_time)); |
135 | |
136 | /* |
137 | * update second minute hour registers |
138 | * so alarms will work |
139 | */ |
140 | mpc5121_rtc_update_smh(regs, tm); |
141 | |
142 | return 0; |
143 | } |
144 | |
145 | static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm) |
146 | { |
147 | struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); |
148 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
149 | int tmp; |
150 | |
151 | tm->tm_sec = in_8(®s->second); |
152 | tm->tm_min = in_8(®s->minute); |
153 | |
154 | /* 12 hour format? */ |
155 | if (in_8(®s->hour) & 0x20) |
156 | tm->tm_hour = (in_8(®s->hour) >> 1) + |
157 | (in_8(®s->hour) & 1 ? 12 : 0); |
158 | else |
159 | tm->tm_hour = in_8(®s->hour); |
160 | |
161 | tmp = in_8(®s->wday_mday); |
162 | tm->tm_mday = tmp & 0x1f; |
163 | tm->tm_mon = in_8(®s->month) - 1; |
164 | tm->tm_year = in_be16(®s->year) - 1900; |
165 | tm->tm_wday = (tmp >> 5) % 7; |
166 | tm->tm_yday = rtc_year_days(day: tm->tm_mday, month: tm->tm_mon, year: tm->tm_year); |
167 | tm->tm_isdst = 0; |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm) |
173 | { |
174 | struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); |
175 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
176 | |
177 | mpc5121_rtc_update_smh(regs, tm); |
178 | |
179 | /* date */ |
180 | out_8(®s->month_set, tm->tm_mon + 1); |
181 | out_8(®s->weekday_set, tm->tm_wday ? tm->tm_wday : 7); |
182 | out_8(®s->date_set, tm->tm_mday); |
183 | out_be16(®s->year_set, tm->tm_year + 1900); |
184 | |
185 | /* set date sequence */ |
186 | out_8(®s->set_date, 0x1); |
187 | out_8(®s->set_date, 0x3); |
188 | out_8(®s->set_date, 0x1); |
189 | out_8(®s->set_date, 0x0); |
190 | |
191 | return 0; |
192 | } |
193 | |
194 | static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
195 | { |
196 | struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); |
197 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
198 | |
199 | *alarm = rtc->wkalarm; |
200 | |
201 | alarm->pending = in_8(®s->alm_status); |
202 | |
203 | return 0; |
204 | } |
205 | |
206 | static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
207 | { |
208 | struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); |
209 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
210 | |
211 | alarm->time.tm_mday = -1; |
212 | alarm->time.tm_mon = -1; |
213 | alarm->time.tm_year = -1; |
214 | |
215 | out_8(®s->alm_min_set, alarm->time.tm_min); |
216 | out_8(®s->alm_hour_set, alarm->time.tm_hour); |
217 | |
218 | out_8(®s->alm_enable, alarm->enabled); |
219 | |
220 | rtc->wkalarm = *alarm; |
221 | return 0; |
222 | } |
223 | |
224 | static irqreturn_t mpc5121_rtc_handler(int irq, void *dev) |
225 | { |
226 | struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev: (struct device *)dev); |
227 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
228 | |
229 | if (in_8(®s->int_alm)) { |
230 | /* acknowledge and clear status */ |
231 | out_8(®s->int_alm, 1); |
232 | out_8(®s->alm_status, 1); |
233 | |
234 | rtc_update_irq(rtc: rtc->rtc, num: 1, RTC_IRQF | RTC_AF); |
235 | return IRQ_HANDLED; |
236 | } |
237 | |
238 | return IRQ_NONE; |
239 | } |
240 | |
241 | static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev) |
242 | { |
243 | struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev: (struct device *)dev); |
244 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
245 | |
246 | if (in_8(®s->int_sec) && (in_8(®s->int_enable) & 0x1)) { |
247 | /* acknowledge */ |
248 | out_8(®s->int_sec, 1); |
249 | |
250 | rtc_update_irq(rtc: rtc->rtc, num: 1, RTC_IRQF | RTC_UF); |
251 | return IRQ_HANDLED; |
252 | } |
253 | |
254 | return IRQ_NONE; |
255 | } |
256 | |
257 | static int mpc5121_rtc_alarm_irq_enable(struct device *dev, |
258 | unsigned int enabled) |
259 | { |
260 | struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); |
261 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
262 | int val; |
263 | |
264 | if (enabled) |
265 | val = 1; |
266 | else |
267 | val = 0; |
268 | |
269 | out_8(®s->alm_enable, val); |
270 | rtc->wkalarm.enabled = val; |
271 | |
272 | return 0; |
273 | } |
274 | |
275 | static const struct rtc_class_ops mpc5121_rtc_ops = { |
276 | .read_time = mpc5121_rtc_read_time, |
277 | .set_time = mpc5121_rtc_set_time, |
278 | .read_alarm = mpc5121_rtc_read_alarm, |
279 | .set_alarm = mpc5121_rtc_set_alarm, |
280 | .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable, |
281 | }; |
282 | |
283 | static const struct rtc_class_ops mpc5200_rtc_ops = { |
284 | .read_time = mpc5200_rtc_read_time, |
285 | .set_time = mpc5200_rtc_set_time, |
286 | .read_alarm = mpc5121_rtc_read_alarm, |
287 | .set_alarm = mpc5121_rtc_set_alarm, |
288 | .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable, |
289 | }; |
290 | |
291 | static int mpc5121_rtc_probe(struct platform_device *op) |
292 | { |
293 | struct mpc5121_rtc_data *rtc; |
294 | int err = 0; |
295 | |
296 | rtc = devm_kzalloc(dev: &op->dev, size: sizeof(*rtc), GFP_KERNEL); |
297 | if (!rtc) |
298 | return -ENOMEM; |
299 | |
300 | rtc->regs = devm_platform_ioremap_resource(pdev: op, index: 0); |
301 | if (IS_ERR(ptr: rtc->regs)) { |
302 | dev_err(&op->dev, "%s: couldn't map io space\n" , __func__); |
303 | return PTR_ERR(ptr: rtc->regs); |
304 | } |
305 | |
306 | device_init_wakeup(dev: &op->dev, enable: 1); |
307 | |
308 | platform_set_drvdata(pdev: op, data: rtc); |
309 | |
310 | rtc->irq = irq_of_parse_and_map(node: op->dev.of_node, index: 1); |
311 | err = devm_request_irq(dev: &op->dev, irq: rtc->irq, handler: mpc5121_rtc_handler, irqflags: 0, |
312 | devname: "mpc5121-rtc" , dev_id: &op->dev); |
313 | if (err) { |
314 | dev_err(&op->dev, "%s: could not request irq: %i\n" , |
315 | __func__, rtc->irq); |
316 | goto out_dispose; |
317 | } |
318 | |
319 | rtc->irq_periodic = irq_of_parse_and_map(node: op->dev.of_node, index: 0); |
320 | err = devm_request_irq(dev: &op->dev, irq: rtc->irq_periodic, |
321 | handler: mpc5121_rtc_handler_upd, irqflags: 0, devname: "mpc5121-rtc_upd" , |
322 | dev_id: &op->dev); |
323 | if (err) { |
324 | dev_err(&op->dev, "%s: could not request irq: %i\n" , |
325 | __func__, rtc->irq_periodic); |
326 | goto out_dispose2; |
327 | } |
328 | |
329 | rtc->rtc = devm_rtc_allocate_device(dev: &op->dev); |
330 | if (IS_ERR(ptr: rtc->rtc)) { |
331 | err = PTR_ERR(ptr: rtc->rtc); |
332 | goto out_dispose2; |
333 | } |
334 | |
335 | rtc->rtc->ops = &mpc5200_rtc_ops; |
336 | set_bit(RTC_FEATURE_ALARM_RES_MINUTE, addr: rtc->rtc->features); |
337 | clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, addr: rtc->rtc->features); |
338 | rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; |
339 | rtc->rtc->range_max = 65733206399ULL; /* 4052-12-31 23:59:59 */ |
340 | |
341 | if (of_device_is_compatible(device: op->dev.of_node, "fsl,mpc5121-rtc" )) { |
342 | u32 ka; |
343 | ka = in_be32(&rtc->regs->keep_alive); |
344 | if (ka & 0x02) { |
345 | dev_warn(&op->dev, |
346 | "mpc5121-rtc: Battery or oscillator failure!\n" ); |
347 | out_be32(&rtc->regs->keep_alive, ka); |
348 | } |
349 | rtc->rtc->ops = &mpc5121_rtc_ops; |
350 | /* |
351 | * This is a limitation of the driver that abuses the target |
352 | * time register, the actual maximum year for the mpc5121 is |
353 | * also 4052. |
354 | */ |
355 | rtc->rtc->range_min = 0; |
356 | rtc->rtc->range_max = U32_MAX; |
357 | } |
358 | |
359 | err = devm_rtc_register_device(rtc->rtc); |
360 | if (err) |
361 | goto out_dispose2; |
362 | |
363 | return 0; |
364 | |
365 | out_dispose2: |
366 | irq_dispose_mapping(virq: rtc->irq_periodic); |
367 | out_dispose: |
368 | irq_dispose_mapping(virq: rtc->irq); |
369 | |
370 | return err; |
371 | } |
372 | |
373 | static void mpc5121_rtc_remove(struct platform_device *op) |
374 | { |
375 | struct mpc5121_rtc_data *rtc = platform_get_drvdata(pdev: op); |
376 | struct mpc5121_rtc_regs __iomem *regs = rtc->regs; |
377 | |
378 | /* disable interrupt, so there are no nasty surprises */ |
379 | out_8(®s->alm_enable, 0); |
380 | out_8(®s->int_enable, in_8(®s->int_enable) & ~0x1); |
381 | |
382 | irq_dispose_mapping(virq: rtc->irq); |
383 | irq_dispose_mapping(virq: rtc->irq_periodic); |
384 | } |
385 | |
386 | #ifdef CONFIG_OF |
387 | static const struct of_device_id mpc5121_rtc_match[] = { |
388 | { .compatible = "fsl,mpc5121-rtc" , }, |
389 | { .compatible = "fsl,mpc5200-rtc" , }, |
390 | {}, |
391 | }; |
392 | MODULE_DEVICE_TABLE(of, mpc5121_rtc_match); |
393 | #endif |
394 | |
395 | static struct platform_driver mpc5121_rtc_driver = { |
396 | .driver = { |
397 | .name = "mpc5121-rtc" , |
398 | .of_match_table = of_match_ptr(mpc5121_rtc_match), |
399 | }, |
400 | .probe = mpc5121_rtc_probe, |
401 | .remove_new = mpc5121_rtc_remove, |
402 | }; |
403 | |
404 | module_platform_driver(mpc5121_rtc_driver); |
405 | |
406 | MODULE_LICENSE("GPL" ); |
407 | MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>" ); |
408 | |