xref: /openbmc/linux/drivers/rtc/rtc-mpc5121.c (revision 25985edc)
1 /*
2  * Real-time clock driver for MPC5121
3  *
4  * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
5  * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/rtc.h>
15 #include <linux/of_device.h>
16 #include <linux/of_platform.h>
17 #include <linux/io.h>
18 #include <linux/slab.h>
19 
20 struct mpc5121_rtc_regs {
21 	u8 set_time;		/* RTC + 0x00 */
22 	u8 hour_set;		/* RTC + 0x01 */
23 	u8 minute_set;		/* RTC + 0x02 */
24 	u8 second_set;		/* RTC + 0x03 */
25 
26 	u8 set_date;		/* RTC + 0x04 */
27 	u8 month_set;		/* RTC + 0x05 */
28 	u8 weekday_set;		/* RTC + 0x06 */
29 	u8 date_set;		/* RTC + 0x07 */
30 
31 	u8 write_sw;		/* RTC + 0x08 */
32 	u8 sw_set;		/* RTC + 0x09 */
33 	u16 year_set;		/* RTC + 0x0a */
34 
35 	u8 alm_enable;		/* RTC + 0x0c */
36 	u8 alm_hour_set;	/* RTC + 0x0d */
37 	u8 alm_min_set;		/* RTC + 0x0e */
38 	u8 int_enable;		/* RTC + 0x0f */
39 
40 	u8 reserved1;
41 	u8 hour;		/* RTC + 0x11 */
42 	u8 minute;		/* RTC + 0x12 */
43 	u8 second;		/* RTC + 0x13 */
44 
45 	u8 month;		/* RTC + 0x14 */
46 	u8 wday_mday;		/* RTC + 0x15 */
47 	u16 year;		/* RTC + 0x16 */
48 
49 	u8 int_alm;		/* RTC + 0x18 */
50 	u8 int_sw;		/* RTC + 0x19 */
51 	u8 alm_status;		/* RTC + 0x1a */
52 	u8 sw_minute;		/* RTC + 0x1b */
53 
54 	u8 bus_error_1;		/* RTC + 0x1c */
55 	u8 int_day;		/* RTC + 0x1d */
56 	u8 int_min;		/* RTC + 0x1e */
57 	u8 int_sec;		/* RTC + 0x1f */
58 
59 	/*
60 	 * target_time:
61 	 *	intended to be used for hibernation but hibernation
62 	 *	does not work on silicon rev 1.5 so use it for non-volatile
63 	 *	storage of offset between the actual_time register and linux
64 	 *	time
65 	 */
66 	u32 target_time;	/* RTC + 0x20 */
67 	/*
68 	 * actual_time:
69 	 * 	readonly time since VBAT_RTC was last connected
70 	 */
71 	u32 actual_time;	/* RTC + 0x24 */
72 	u32 keep_alive;		/* RTC + 0x28 */
73 };
74 
75 struct mpc5121_rtc_data {
76 	unsigned irq;
77 	unsigned irq_periodic;
78 	struct mpc5121_rtc_regs __iomem *regs;
79 	struct rtc_device *rtc;
80 	struct rtc_wkalrm wkalarm;
81 };
82 
83 /*
84  * Update second/minute/hour registers.
85  *
86  * This is just so alarm will work.
87  */
88 static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs,
89 				   struct rtc_time *tm)
90 {
91 	out_8(&regs->second_set, tm->tm_sec);
92 	out_8(&regs->minute_set, tm->tm_min);
93 	out_8(&regs->hour_set, tm->tm_hour);
94 
95 	/* set time sequence */
96 	out_8(&regs->set_time, 0x1);
97 	out_8(&regs->set_time, 0x3);
98 	out_8(&regs->set_time, 0x1);
99 	out_8(&regs->set_time, 0x0);
100 }
101 
102 static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
103 {
104 	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
105 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
106 	unsigned long now;
107 
108 	/*
109 	 * linux time is actual_time plus the offset saved in target_time
110 	 */
111 	now = in_be32(&regs->actual_time) + in_be32(&regs->target_time);
112 
113 	rtc_time_to_tm(now, tm);
114 
115 	/*
116 	 * update second minute hour registers
117 	 * so alarms will work
118 	 */
119 	mpc5121_rtc_update_smh(regs, tm);
120 
121 	return rtc_valid_tm(tm);
122 }
123 
124 static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
125 {
126 	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
127 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
128 	int ret;
129 	unsigned long now;
130 
131 	/*
132 	 * The actual_time register is read only so we write the offset
133 	 * between it and linux time to the target_time register.
134 	 */
135 	ret = rtc_tm_to_time(tm, &now);
136 	if (ret == 0)
137 		out_be32(&regs->target_time, now - in_be32(&regs->actual_time));
138 
139 	/*
140 	 * update second minute hour registers
141 	 * so alarms will work
142 	 */
143 	mpc5121_rtc_update_smh(regs, tm);
144 
145 	return 0;
146 }
147 
148 static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
149 {
150 	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
151 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
152 
153 	*alarm = rtc->wkalarm;
154 
155 	alarm->pending = in_8(&regs->alm_status);
156 
157 	return 0;
158 }
159 
160 static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
161 {
162 	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
163 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
164 
165 	/*
166 	 * the alarm has no seconds so deal with it
167 	 */
168 	if (alarm->time.tm_sec) {
169 		alarm->time.tm_sec = 0;
170 		alarm->time.tm_min++;
171 		if (alarm->time.tm_min >= 60) {
172 			alarm->time.tm_min = 0;
173 			alarm->time.tm_hour++;
174 			if (alarm->time.tm_hour >= 24)
175 				alarm->time.tm_hour = 0;
176 		}
177 	}
178 
179 	alarm->time.tm_mday = -1;
180 	alarm->time.tm_mon = -1;
181 	alarm->time.tm_year = -1;
182 
183 	out_8(&regs->alm_min_set, alarm->time.tm_min);
184 	out_8(&regs->alm_hour_set, alarm->time.tm_hour);
185 
186 	out_8(&regs->alm_enable, alarm->enabled);
187 
188 	rtc->wkalarm = *alarm;
189 	return 0;
190 }
191 
192 static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)
193 {
194 	struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
195 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
196 
197 	if (in_8(&regs->int_alm)) {
198 		/* acknowledge and clear status */
199 		out_8(&regs->int_alm, 1);
200 		out_8(&regs->alm_status, 1);
201 
202 		rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
203 		return IRQ_HANDLED;
204 	}
205 
206 	return IRQ_NONE;
207 }
208 
209 static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)
210 {
211 	struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
212 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
213 
214 	if (in_8(&regs->int_sec) && (in_8(&regs->int_enable) & 0x1)) {
215 		/* acknowledge */
216 		out_8(&regs->int_sec, 1);
217 
218 		rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
219 		return IRQ_HANDLED;
220 	}
221 
222 	return IRQ_NONE;
223 }
224 
225 static int mpc5121_rtc_alarm_irq_enable(struct device *dev,
226 					unsigned int enabled)
227 {
228 	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
229 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
230 	int val;
231 
232 	if (enabled)
233 		val = 1;
234 	else
235 		val = 0;
236 
237 	out_8(&regs->alm_enable, val);
238 	rtc->wkalarm.enabled = val;
239 
240 	return 0;
241 }
242 
243 static const struct rtc_class_ops mpc5121_rtc_ops = {
244 	.read_time = mpc5121_rtc_read_time,
245 	.set_time = mpc5121_rtc_set_time,
246 	.read_alarm = mpc5121_rtc_read_alarm,
247 	.set_alarm = mpc5121_rtc_set_alarm,
248 	.alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
249 };
250 
251 static int __devinit mpc5121_rtc_probe(struct platform_device *op)
252 {
253 	struct mpc5121_rtc_data *rtc;
254 	int err = 0;
255 	u32 ka;
256 
257 	rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
258 	if (!rtc)
259 		return -ENOMEM;
260 
261 	rtc->regs = of_iomap(op->dev.of_node, 0);
262 	if (!rtc->regs) {
263 		dev_err(&op->dev, "%s: couldn't map io space\n", __func__);
264 		err = -ENOSYS;
265 		goto out_free;
266 	}
267 
268 	device_init_wakeup(&op->dev, 1);
269 
270 	dev_set_drvdata(&op->dev, rtc);
271 
272 	rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1);
273 	err = request_irq(rtc->irq, mpc5121_rtc_handler, IRQF_DISABLED,
274 						"mpc5121-rtc", &op->dev);
275 	if (err) {
276 		dev_err(&op->dev, "%s: could not request irq: %i\n",
277 							__func__, rtc->irq);
278 		goto out_dispose;
279 	}
280 
281 	rtc->irq_periodic = irq_of_parse_and_map(op->dev.of_node, 0);
282 	err = request_irq(rtc->irq_periodic, mpc5121_rtc_handler_upd,
283 				IRQF_DISABLED, "mpc5121-rtc_upd", &op->dev);
284 	if (err) {
285 		dev_err(&op->dev, "%s: could not request irq: %i\n",
286 						__func__, rtc->irq_periodic);
287 		goto out_dispose2;
288 	}
289 
290 	ka = in_be32(&rtc->regs->keep_alive);
291 	if (ka & 0x02) {
292 		dev_warn(&op->dev,
293 			"mpc5121-rtc: Battery or oscillator failure!\n");
294 		out_be32(&rtc->regs->keep_alive, ka);
295 	}
296 
297 	rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev,
298 					&mpc5121_rtc_ops, THIS_MODULE);
299 	if (IS_ERR(rtc->rtc)) {
300 		err = PTR_ERR(rtc->rtc);
301 		goto out_free_irq;
302 	}
303 
304 	return 0;
305 
306 out_free_irq:
307 	free_irq(rtc->irq_periodic, &op->dev);
308 out_dispose2:
309 	irq_dispose_mapping(rtc->irq_periodic);
310 	free_irq(rtc->irq, &op->dev);
311 out_dispose:
312 	irq_dispose_mapping(rtc->irq);
313 	iounmap(rtc->regs);
314 out_free:
315 	kfree(rtc);
316 
317 	return err;
318 }
319 
320 static int __devexit mpc5121_rtc_remove(struct platform_device *op)
321 {
322 	struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev);
323 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
324 
325 	/* disable interrupt, so there are no nasty surprises */
326 	out_8(&regs->alm_enable, 0);
327 	out_8(&regs->int_enable, in_8(&regs->int_enable) & ~0x1);
328 
329 	rtc_device_unregister(rtc->rtc);
330 	iounmap(rtc->regs);
331 	free_irq(rtc->irq, &op->dev);
332 	free_irq(rtc->irq_periodic, &op->dev);
333 	irq_dispose_mapping(rtc->irq);
334 	irq_dispose_mapping(rtc->irq_periodic);
335 	dev_set_drvdata(&op->dev, NULL);
336 	kfree(rtc);
337 
338 	return 0;
339 }
340 
341 static struct of_device_id mpc5121_rtc_match[] __devinitdata = {
342 	{ .compatible = "fsl,mpc5121-rtc", },
343 	{},
344 };
345 
346 static struct platform_driver mpc5121_rtc_driver = {
347 	.driver = {
348 		.name = "mpc5121-rtc",
349 		.owner = THIS_MODULE,
350 		.of_match_table = mpc5121_rtc_match,
351 	},
352 	.probe = mpc5121_rtc_probe,
353 	.remove = __devexit_p(mpc5121_rtc_remove),
354 };
355 
356 static int __init mpc5121_rtc_init(void)
357 {
358 	return platform_driver_register(&mpc5121_rtc_driver);
359 }
360 module_init(mpc5121_rtc_init);
361 
362 static void __exit mpc5121_rtc_exit(void)
363 {
364 	platform_driver_unregister(&mpc5121_rtc_driver);
365 }
366 module_exit(mpc5121_rtc_exit);
367 
368 MODULE_LICENSE("GPL");
369 MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>");
370