xref: /openbmc/linux/drivers/rtc/rtc-ds1286.c (revision 5f119f29063c9a9bf1ab40112c02710c2db84f29)
1*5f119f29SThomas Bogendoerfer /*
2*5f119f29SThomas Bogendoerfer  * DS1286 Real Time Clock interface for Linux
3*5f119f29SThomas Bogendoerfer  *
4*5f119f29SThomas Bogendoerfer  * Copyright (C) 1998, 1999, 2000 Ralf Baechle
5*5f119f29SThomas Bogendoerfer  * Copyright (C) 2008 Thomas Bogendoerfer
6*5f119f29SThomas Bogendoerfer  *
7*5f119f29SThomas Bogendoerfer  * Based on code written by Paul Gortmaker.
8*5f119f29SThomas Bogendoerfer  *
9*5f119f29SThomas Bogendoerfer  * This program is free software; you can redistribute it and/or modify it
10*5f119f29SThomas Bogendoerfer  * under the terms of the GNU General Public License as published by the
11*5f119f29SThomas Bogendoerfer  * Free Software Foundation; either version 2 of the License, or (at your
12*5f119f29SThomas Bogendoerfer  * option) any later version.
13*5f119f29SThomas Bogendoerfer  */
14*5f119f29SThomas Bogendoerfer 
15*5f119f29SThomas Bogendoerfer #include <linux/module.h>
16*5f119f29SThomas Bogendoerfer #include <linux/rtc.h>
17*5f119f29SThomas Bogendoerfer #include <linux/platform_device.h>
18*5f119f29SThomas Bogendoerfer #include <linux/bcd.h>
19*5f119f29SThomas Bogendoerfer #include <linux/ds1286.h>
20*5f119f29SThomas Bogendoerfer 
21*5f119f29SThomas Bogendoerfer #define DRV_VERSION		"1.0"
22*5f119f29SThomas Bogendoerfer 
23*5f119f29SThomas Bogendoerfer struct ds1286_priv {
24*5f119f29SThomas Bogendoerfer 	struct rtc_device *rtc;
25*5f119f29SThomas Bogendoerfer 	u32 __iomem *rtcregs;
26*5f119f29SThomas Bogendoerfer 	size_t size;
27*5f119f29SThomas Bogendoerfer 	unsigned long baseaddr;
28*5f119f29SThomas Bogendoerfer 	spinlock_t lock;
29*5f119f29SThomas Bogendoerfer };
30*5f119f29SThomas Bogendoerfer 
31*5f119f29SThomas Bogendoerfer static inline u8 ds1286_rtc_read(struct ds1286_priv *priv, int reg)
32*5f119f29SThomas Bogendoerfer {
33*5f119f29SThomas Bogendoerfer 	return __raw_readl(&priv->rtcregs[reg]) & 0xff;
34*5f119f29SThomas Bogendoerfer }
35*5f119f29SThomas Bogendoerfer 
36*5f119f29SThomas Bogendoerfer static inline void ds1286_rtc_write(struct ds1286_priv *priv, u8 data, int reg)
37*5f119f29SThomas Bogendoerfer {
38*5f119f29SThomas Bogendoerfer 	__raw_writel(data, &priv->rtcregs[reg]);
39*5f119f29SThomas Bogendoerfer }
40*5f119f29SThomas Bogendoerfer 
41*5f119f29SThomas Bogendoerfer #ifdef CONFIG_RTC_INTF_DEV
42*5f119f29SThomas Bogendoerfer 
43*5f119f29SThomas Bogendoerfer static int ds1286_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
44*5f119f29SThomas Bogendoerfer {
45*5f119f29SThomas Bogendoerfer 	struct ds1286_priv *priv = dev_get_drvdata(dev);
46*5f119f29SThomas Bogendoerfer 	unsigned long flags;
47*5f119f29SThomas Bogendoerfer 	unsigned char val;
48*5f119f29SThomas Bogendoerfer 
49*5f119f29SThomas Bogendoerfer 	switch (cmd) {
50*5f119f29SThomas Bogendoerfer 	case RTC_AIE_OFF:
51*5f119f29SThomas Bogendoerfer 		/* Mask alarm int. enab. bit	*/
52*5f119f29SThomas Bogendoerfer 		spin_lock_irqsave(&priv->lock, flags);
53*5f119f29SThomas Bogendoerfer 		val = ds1286_rtc_read(priv, RTC_CMD);
54*5f119f29SThomas Bogendoerfer 		val |=  RTC_TDM;
55*5f119f29SThomas Bogendoerfer 		ds1286_rtc_write(priv, val, RTC_CMD);
56*5f119f29SThomas Bogendoerfer 		spin_unlock_irqrestore(&priv->lock, flags);
57*5f119f29SThomas Bogendoerfer 		break;
58*5f119f29SThomas Bogendoerfer 	case RTC_AIE_ON:
59*5f119f29SThomas Bogendoerfer 		/* Allow alarm interrupts.	*/
60*5f119f29SThomas Bogendoerfer 		spin_lock_irqsave(&priv->lock, flags);
61*5f119f29SThomas Bogendoerfer 		val = ds1286_rtc_read(priv, RTC_CMD);
62*5f119f29SThomas Bogendoerfer 		val &=  ~RTC_TDM;
63*5f119f29SThomas Bogendoerfer 		ds1286_rtc_write(priv, val, RTC_CMD);
64*5f119f29SThomas Bogendoerfer 		spin_unlock_irqrestore(&priv->lock, flags);
65*5f119f29SThomas Bogendoerfer 		break;
66*5f119f29SThomas Bogendoerfer 	case RTC_WIE_OFF:
67*5f119f29SThomas Bogendoerfer 		/* Mask watchdog int. enab. bit	*/
68*5f119f29SThomas Bogendoerfer 		spin_lock_irqsave(&priv->lock, flags);
69*5f119f29SThomas Bogendoerfer 		val = ds1286_rtc_read(priv, RTC_CMD);
70*5f119f29SThomas Bogendoerfer 		val |= RTC_WAM;
71*5f119f29SThomas Bogendoerfer 		ds1286_rtc_write(priv, val, RTC_CMD);
72*5f119f29SThomas Bogendoerfer 		spin_unlock_irqrestore(&priv->lock, flags);
73*5f119f29SThomas Bogendoerfer 		break;
74*5f119f29SThomas Bogendoerfer 	case RTC_WIE_ON:
75*5f119f29SThomas Bogendoerfer 		/* Allow watchdog interrupts.	*/
76*5f119f29SThomas Bogendoerfer 		spin_lock_irqsave(&priv->lock, flags);
77*5f119f29SThomas Bogendoerfer 		val = ds1286_rtc_read(priv, RTC_CMD);
78*5f119f29SThomas Bogendoerfer 		val &= ~RTC_WAM;
79*5f119f29SThomas Bogendoerfer 		ds1286_rtc_write(priv, val, RTC_CMD);
80*5f119f29SThomas Bogendoerfer 		spin_unlock_irqrestore(&priv->lock, flags);
81*5f119f29SThomas Bogendoerfer 		break;
82*5f119f29SThomas Bogendoerfer 	default:
83*5f119f29SThomas Bogendoerfer 		return -ENOIOCTLCMD;
84*5f119f29SThomas Bogendoerfer 	}
85*5f119f29SThomas Bogendoerfer 	return 0;
86*5f119f29SThomas Bogendoerfer }
87*5f119f29SThomas Bogendoerfer 
88*5f119f29SThomas Bogendoerfer #else
89*5f119f29SThomas Bogendoerfer #define ds1286_ioctl    NULL
90*5f119f29SThomas Bogendoerfer #endif
91*5f119f29SThomas Bogendoerfer 
92*5f119f29SThomas Bogendoerfer #ifdef CONFIG_PROC_FS
93*5f119f29SThomas Bogendoerfer 
94*5f119f29SThomas Bogendoerfer static int ds1286_proc(struct device *dev, struct seq_file *seq)
95*5f119f29SThomas Bogendoerfer {
96*5f119f29SThomas Bogendoerfer 	struct ds1286_priv *priv = dev_get_drvdata(dev);
97*5f119f29SThomas Bogendoerfer 	unsigned char month, cmd, amode;
98*5f119f29SThomas Bogendoerfer 	const char *s;
99*5f119f29SThomas Bogendoerfer 
100*5f119f29SThomas Bogendoerfer 	month = ds1286_rtc_read(priv, RTC_MONTH);
101*5f119f29SThomas Bogendoerfer 	seq_printf(seq,
102*5f119f29SThomas Bogendoerfer 		   "oscillator\t: %s\n"
103*5f119f29SThomas Bogendoerfer 		   "square_wave\t: %s\n",
104*5f119f29SThomas Bogendoerfer 		   (month & RTC_EOSC) ? "disabled" : "enabled",
105*5f119f29SThomas Bogendoerfer 		   (month & RTC_ESQW) ? "disabled" : "enabled");
106*5f119f29SThomas Bogendoerfer 
107*5f119f29SThomas Bogendoerfer 	amode = ((ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x80) >> 5) |
108*5f119f29SThomas Bogendoerfer 		((ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x80) >> 6) |
109*5f119f29SThomas Bogendoerfer 		((ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x80) >> 7);
110*5f119f29SThomas Bogendoerfer 	switch (amode) {
111*5f119f29SThomas Bogendoerfer 	case 7:
112*5f119f29SThomas Bogendoerfer 		s = "each minute";
113*5f119f29SThomas Bogendoerfer 		break;
114*5f119f29SThomas Bogendoerfer 	case 3:
115*5f119f29SThomas Bogendoerfer 		s = "minutes match";
116*5f119f29SThomas Bogendoerfer 		break;
117*5f119f29SThomas Bogendoerfer 	case 1:
118*5f119f29SThomas Bogendoerfer 		s = "hours and minutes match";
119*5f119f29SThomas Bogendoerfer 		break;
120*5f119f29SThomas Bogendoerfer 	case 0:
121*5f119f29SThomas Bogendoerfer 		s = "days, hours and minutes match";
122*5f119f29SThomas Bogendoerfer 		break;
123*5f119f29SThomas Bogendoerfer 	default:
124*5f119f29SThomas Bogendoerfer 		s = "invalid";
125*5f119f29SThomas Bogendoerfer 		break;
126*5f119f29SThomas Bogendoerfer 	}
127*5f119f29SThomas Bogendoerfer 	seq_printf(seq, "alarm_mode\t: %s\n", s);
128*5f119f29SThomas Bogendoerfer 
129*5f119f29SThomas Bogendoerfer 	cmd = ds1286_rtc_read(priv, RTC_CMD);
130*5f119f29SThomas Bogendoerfer 	seq_printf(seq,
131*5f119f29SThomas Bogendoerfer 		   "alarm_enable\t: %s\n"
132*5f119f29SThomas Bogendoerfer 		   "wdog_alarm\t: %s\n"
133*5f119f29SThomas Bogendoerfer 		   "alarm_mask\t: %s\n"
134*5f119f29SThomas Bogendoerfer 		   "wdog_alarm_mask\t: %s\n"
135*5f119f29SThomas Bogendoerfer 		   "interrupt_mode\t: %s\n"
136*5f119f29SThomas Bogendoerfer 		   "INTB_mode\t: %s_active\n"
137*5f119f29SThomas Bogendoerfer 		   "interrupt_pins\t: %s\n",
138*5f119f29SThomas Bogendoerfer 		   (cmd & RTC_TDF) ? "yes" : "no",
139*5f119f29SThomas Bogendoerfer 		   (cmd & RTC_WAF) ? "yes" : "no",
140*5f119f29SThomas Bogendoerfer 		   (cmd & RTC_TDM) ? "disabled" : "enabled",
141*5f119f29SThomas Bogendoerfer 		   (cmd & RTC_WAM) ? "disabled" : "enabled",
142*5f119f29SThomas Bogendoerfer 		   (cmd & RTC_PU_LVL) ? "pulse" : "level",
143*5f119f29SThomas Bogendoerfer 		   (cmd & RTC_IBH_LO) ? "low" : "high",
144*5f119f29SThomas Bogendoerfer 		   (cmd & RTC_IPSW) ? "unswapped" : "swapped");
145*5f119f29SThomas Bogendoerfer 	return 0;
146*5f119f29SThomas Bogendoerfer }
147*5f119f29SThomas Bogendoerfer 
148*5f119f29SThomas Bogendoerfer #else
149*5f119f29SThomas Bogendoerfer #define ds1286_proc     NULL
150*5f119f29SThomas Bogendoerfer #endif
151*5f119f29SThomas Bogendoerfer 
152*5f119f29SThomas Bogendoerfer static int ds1286_read_time(struct device *dev, struct rtc_time *tm)
153*5f119f29SThomas Bogendoerfer {
154*5f119f29SThomas Bogendoerfer 	struct ds1286_priv *priv = dev_get_drvdata(dev);
155*5f119f29SThomas Bogendoerfer 	unsigned char save_control;
156*5f119f29SThomas Bogendoerfer 	unsigned long flags;
157*5f119f29SThomas Bogendoerfer 	unsigned long uip_watchdog = jiffies;
158*5f119f29SThomas Bogendoerfer 
159*5f119f29SThomas Bogendoerfer 	/*
160*5f119f29SThomas Bogendoerfer 	 * read RTC once any update in progress is done. The update
161*5f119f29SThomas Bogendoerfer 	 * can take just over 2ms. We wait 10 to 20ms. There is no need to
162*5f119f29SThomas Bogendoerfer 	 * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
163*5f119f29SThomas Bogendoerfer 	 * If you need to know *exactly* when a second has started, enable
164*5f119f29SThomas Bogendoerfer 	 * periodic update complete interrupts, (via ioctl) and then
165*5f119f29SThomas Bogendoerfer 	 * immediately read /dev/rtc which will block until you get the IRQ.
166*5f119f29SThomas Bogendoerfer 	 * Once the read clears, read the RTC time (again via ioctl). Easy.
167*5f119f29SThomas Bogendoerfer 	 */
168*5f119f29SThomas Bogendoerfer 
169*5f119f29SThomas Bogendoerfer 	if (ds1286_rtc_read(priv, RTC_CMD) & RTC_TE)
170*5f119f29SThomas Bogendoerfer 		while (time_before(jiffies, uip_watchdog + 2*HZ/100))
171*5f119f29SThomas Bogendoerfer 			barrier();
172*5f119f29SThomas Bogendoerfer 
173*5f119f29SThomas Bogendoerfer 	/*
174*5f119f29SThomas Bogendoerfer 	 * Only the values that we read from the RTC are set. We leave
175*5f119f29SThomas Bogendoerfer 	 * tm_wday, tm_yday and tm_isdst untouched. Even though the
176*5f119f29SThomas Bogendoerfer 	 * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
177*5f119f29SThomas Bogendoerfer 	 * by the RTC when initially set to a non-zero value.
178*5f119f29SThomas Bogendoerfer 	 */
179*5f119f29SThomas Bogendoerfer 	spin_lock_irqsave(&priv->lock, flags);
180*5f119f29SThomas Bogendoerfer 	save_control = ds1286_rtc_read(priv, RTC_CMD);
181*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD);
182*5f119f29SThomas Bogendoerfer 
183*5f119f29SThomas Bogendoerfer 	tm->tm_sec = ds1286_rtc_read(priv, RTC_SECONDS);
184*5f119f29SThomas Bogendoerfer 	tm->tm_min = ds1286_rtc_read(priv, RTC_MINUTES);
185*5f119f29SThomas Bogendoerfer 	tm->tm_hour = ds1286_rtc_read(priv, RTC_HOURS) & 0x3f;
186*5f119f29SThomas Bogendoerfer 	tm->tm_mday = ds1286_rtc_read(priv, RTC_DATE);
187*5f119f29SThomas Bogendoerfer 	tm->tm_mon = ds1286_rtc_read(priv, RTC_MONTH) & 0x1f;
188*5f119f29SThomas Bogendoerfer 	tm->tm_year = ds1286_rtc_read(priv, RTC_YEAR);
189*5f119f29SThomas Bogendoerfer 
190*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, save_control, RTC_CMD);
191*5f119f29SThomas Bogendoerfer 	spin_unlock_irqrestore(&priv->lock, flags);
192*5f119f29SThomas Bogendoerfer 
193*5f119f29SThomas Bogendoerfer 	tm->tm_sec = bcd2bin(tm->tm_sec);
194*5f119f29SThomas Bogendoerfer 	tm->tm_min = bcd2bin(tm->tm_min);
195*5f119f29SThomas Bogendoerfer 	tm->tm_hour = bcd2bin(tm->tm_hour);
196*5f119f29SThomas Bogendoerfer 	tm->tm_mday = bcd2bin(tm->tm_mday);
197*5f119f29SThomas Bogendoerfer 	tm->tm_mon = bcd2bin(tm->tm_mon);
198*5f119f29SThomas Bogendoerfer 	tm->tm_year = bcd2bin(tm->tm_year);
199*5f119f29SThomas Bogendoerfer 
200*5f119f29SThomas Bogendoerfer 	/*
201*5f119f29SThomas Bogendoerfer 	 * Account for differences between how the RTC uses the values
202*5f119f29SThomas Bogendoerfer 	 * and how they are defined in a struct rtc_time;
203*5f119f29SThomas Bogendoerfer 	 */
204*5f119f29SThomas Bogendoerfer 	if (tm->tm_year < 45)
205*5f119f29SThomas Bogendoerfer 		tm->tm_year += 30;
206*5f119f29SThomas Bogendoerfer 	tm->tm_year += 40;
207*5f119f29SThomas Bogendoerfer 	if (tm->tm_year < 70)
208*5f119f29SThomas Bogendoerfer 		tm->tm_year += 100;
209*5f119f29SThomas Bogendoerfer 
210*5f119f29SThomas Bogendoerfer 	tm->tm_mon--;
211*5f119f29SThomas Bogendoerfer 
212*5f119f29SThomas Bogendoerfer 	return rtc_valid_tm(tm);
213*5f119f29SThomas Bogendoerfer }
214*5f119f29SThomas Bogendoerfer 
215*5f119f29SThomas Bogendoerfer static int ds1286_set_time(struct device *dev, struct rtc_time *tm)
216*5f119f29SThomas Bogendoerfer {
217*5f119f29SThomas Bogendoerfer 	struct ds1286_priv *priv = dev_get_drvdata(dev);
218*5f119f29SThomas Bogendoerfer 	unsigned char mon, day, hrs, min, sec;
219*5f119f29SThomas Bogendoerfer 	unsigned char save_control;
220*5f119f29SThomas Bogendoerfer 	unsigned int yrs;
221*5f119f29SThomas Bogendoerfer 	unsigned long flags;
222*5f119f29SThomas Bogendoerfer 
223*5f119f29SThomas Bogendoerfer 	yrs = tm->tm_year + 1900;
224*5f119f29SThomas Bogendoerfer 	mon = tm->tm_mon + 1;   /* tm_mon starts at zero */
225*5f119f29SThomas Bogendoerfer 	day = tm->tm_mday;
226*5f119f29SThomas Bogendoerfer 	hrs = tm->tm_hour;
227*5f119f29SThomas Bogendoerfer 	min = tm->tm_min;
228*5f119f29SThomas Bogendoerfer 	sec = tm->tm_sec;
229*5f119f29SThomas Bogendoerfer 
230*5f119f29SThomas Bogendoerfer 	if (yrs < 1970)
231*5f119f29SThomas Bogendoerfer 		return -EINVAL;
232*5f119f29SThomas Bogendoerfer 
233*5f119f29SThomas Bogendoerfer 	yrs -= 1940;
234*5f119f29SThomas Bogendoerfer 	if (yrs > 255)    /* They are unsigned */
235*5f119f29SThomas Bogendoerfer 		return -EINVAL;
236*5f119f29SThomas Bogendoerfer 
237*5f119f29SThomas Bogendoerfer 	if (yrs >= 100)
238*5f119f29SThomas Bogendoerfer 		yrs -= 100;
239*5f119f29SThomas Bogendoerfer 
240*5f119f29SThomas Bogendoerfer 	sec = bin2bcd(sec);
241*5f119f29SThomas Bogendoerfer 	min = bin2bcd(min);
242*5f119f29SThomas Bogendoerfer 	hrs = bin2bcd(hrs);
243*5f119f29SThomas Bogendoerfer 	day = bin2bcd(day);
244*5f119f29SThomas Bogendoerfer 	mon = bin2bcd(mon);
245*5f119f29SThomas Bogendoerfer 	yrs = bin2bcd(yrs);
246*5f119f29SThomas Bogendoerfer 
247*5f119f29SThomas Bogendoerfer 	spin_lock_irqsave(&priv->lock, flags);
248*5f119f29SThomas Bogendoerfer 	save_control = ds1286_rtc_read(priv, RTC_CMD);
249*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD);
250*5f119f29SThomas Bogendoerfer 
251*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, yrs, RTC_YEAR);
252*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, mon, RTC_MONTH);
253*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, day, RTC_DATE);
254*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, hrs, RTC_HOURS);
255*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, min, RTC_MINUTES);
256*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, sec, RTC_SECONDS);
257*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, 0, RTC_HUNDREDTH_SECOND);
258*5f119f29SThomas Bogendoerfer 
259*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, save_control, RTC_CMD);
260*5f119f29SThomas Bogendoerfer 	spin_unlock_irqrestore(&priv->lock, flags);
261*5f119f29SThomas Bogendoerfer 	return 0;
262*5f119f29SThomas Bogendoerfer }
263*5f119f29SThomas Bogendoerfer 
264*5f119f29SThomas Bogendoerfer static int ds1286_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
265*5f119f29SThomas Bogendoerfer {
266*5f119f29SThomas Bogendoerfer 	struct ds1286_priv *priv = dev_get_drvdata(dev);
267*5f119f29SThomas Bogendoerfer 	unsigned char cmd;
268*5f119f29SThomas Bogendoerfer 	unsigned long flags;
269*5f119f29SThomas Bogendoerfer 
270*5f119f29SThomas Bogendoerfer 	/*
271*5f119f29SThomas Bogendoerfer 	 * Only the values that we read from the RTC are set. That
272*5f119f29SThomas Bogendoerfer 	 * means only tm_wday, tm_hour, tm_min.
273*5f119f29SThomas Bogendoerfer 	 */
274*5f119f29SThomas Bogendoerfer 	spin_lock_irqsave(&priv->lock, flags);
275*5f119f29SThomas Bogendoerfer 	alm->time.tm_min = ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x7f;
276*5f119f29SThomas Bogendoerfer 	alm->time.tm_hour = ds1286_rtc_read(priv, RTC_HOURS_ALARM)  & 0x1f;
277*5f119f29SThomas Bogendoerfer 	alm->time.tm_wday = ds1286_rtc_read(priv, RTC_DAY_ALARM)    & 0x07;
278*5f119f29SThomas Bogendoerfer 	cmd = ds1286_rtc_read(priv, RTC_CMD);
279*5f119f29SThomas Bogendoerfer 	spin_unlock_irqrestore(&priv->lock, flags);
280*5f119f29SThomas Bogendoerfer 
281*5f119f29SThomas Bogendoerfer 	alm->time.tm_min = bcd2bin(alm->time.tm_min);
282*5f119f29SThomas Bogendoerfer 	alm->time.tm_hour = bcd2bin(alm->time.tm_hour);
283*5f119f29SThomas Bogendoerfer 	alm->time.tm_sec = 0;
284*5f119f29SThomas Bogendoerfer 	return 0;
285*5f119f29SThomas Bogendoerfer }
286*5f119f29SThomas Bogendoerfer 
287*5f119f29SThomas Bogendoerfer static int ds1286_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
288*5f119f29SThomas Bogendoerfer {
289*5f119f29SThomas Bogendoerfer 	struct ds1286_priv *priv = dev_get_drvdata(dev);
290*5f119f29SThomas Bogendoerfer 	unsigned char hrs, min, sec;
291*5f119f29SThomas Bogendoerfer 
292*5f119f29SThomas Bogendoerfer 	hrs = alm->time.tm_hour;
293*5f119f29SThomas Bogendoerfer 	min = alm->time.tm_min;
294*5f119f29SThomas Bogendoerfer 	sec = alm->time.tm_sec;
295*5f119f29SThomas Bogendoerfer 
296*5f119f29SThomas Bogendoerfer 	if (hrs >= 24)
297*5f119f29SThomas Bogendoerfer 		hrs = 0xff;
298*5f119f29SThomas Bogendoerfer 
299*5f119f29SThomas Bogendoerfer 	if (min >= 60)
300*5f119f29SThomas Bogendoerfer 		min = 0xff;
301*5f119f29SThomas Bogendoerfer 
302*5f119f29SThomas Bogendoerfer 	if (sec != 0)
303*5f119f29SThomas Bogendoerfer 		return -EINVAL;
304*5f119f29SThomas Bogendoerfer 
305*5f119f29SThomas Bogendoerfer 	min = bin2bcd(min);
306*5f119f29SThomas Bogendoerfer 	hrs = bin2bcd(hrs);
307*5f119f29SThomas Bogendoerfer 
308*5f119f29SThomas Bogendoerfer 	spin_lock(&priv->lock);
309*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, hrs, RTC_HOURS_ALARM);
310*5f119f29SThomas Bogendoerfer 	ds1286_rtc_write(priv, min, RTC_MINUTES_ALARM);
311*5f119f29SThomas Bogendoerfer 	spin_unlock(&priv->lock);
312*5f119f29SThomas Bogendoerfer 
313*5f119f29SThomas Bogendoerfer 	return 0;
314*5f119f29SThomas Bogendoerfer }
315*5f119f29SThomas Bogendoerfer 
316*5f119f29SThomas Bogendoerfer static const struct rtc_class_ops ds1286_ops = {
317*5f119f29SThomas Bogendoerfer 	.ioctl   	= ds1286_ioctl,
318*5f119f29SThomas Bogendoerfer 	.proc   	= ds1286_proc,
319*5f119f29SThomas Bogendoerfer 	.read_time	= ds1286_read_time,
320*5f119f29SThomas Bogendoerfer 	.set_time	= ds1286_set_time,
321*5f119f29SThomas Bogendoerfer 	.read_alarm	= ds1286_read_alarm,
322*5f119f29SThomas Bogendoerfer 	.set_alarm	= ds1286_set_alarm,
323*5f119f29SThomas Bogendoerfer };
324*5f119f29SThomas Bogendoerfer 
325*5f119f29SThomas Bogendoerfer static int __devinit ds1286_probe(struct platform_device *pdev)
326*5f119f29SThomas Bogendoerfer {
327*5f119f29SThomas Bogendoerfer 	struct rtc_device *rtc;
328*5f119f29SThomas Bogendoerfer 	struct resource *res;
329*5f119f29SThomas Bogendoerfer 	struct ds1286_priv *priv;
330*5f119f29SThomas Bogendoerfer 	int ret = 0;
331*5f119f29SThomas Bogendoerfer 
332*5f119f29SThomas Bogendoerfer 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
333*5f119f29SThomas Bogendoerfer 	if (!res)
334*5f119f29SThomas Bogendoerfer 		return -ENODEV;
335*5f119f29SThomas Bogendoerfer 	priv = kzalloc(sizeof(struct ds1286_priv), GFP_KERNEL);
336*5f119f29SThomas Bogendoerfer 	if (!priv)
337*5f119f29SThomas Bogendoerfer 		return -ENOMEM;
338*5f119f29SThomas Bogendoerfer 
339*5f119f29SThomas Bogendoerfer 	priv->size = res->end - res->start + 1;
340*5f119f29SThomas Bogendoerfer 	if (!request_mem_region(res->start, priv->size, pdev->name)) {
341*5f119f29SThomas Bogendoerfer 		ret = -EBUSY;
342*5f119f29SThomas Bogendoerfer 		goto out;
343*5f119f29SThomas Bogendoerfer 	}
344*5f119f29SThomas Bogendoerfer 	priv->baseaddr = res->start;
345*5f119f29SThomas Bogendoerfer 	priv->rtcregs = ioremap(priv->baseaddr, priv->size);
346*5f119f29SThomas Bogendoerfer 	if (!priv->rtcregs) {
347*5f119f29SThomas Bogendoerfer 		ret = -ENOMEM;
348*5f119f29SThomas Bogendoerfer 		goto out;
349*5f119f29SThomas Bogendoerfer 	}
350*5f119f29SThomas Bogendoerfer 	spin_lock_init(&priv->lock);
351*5f119f29SThomas Bogendoerfer 	rtc = rtc_device_register("ds1286", &pdev->dev,
352*5f119f29SThomas Bogendoerfer 				  &ds1286_ops, THIS_MODULE);
353*5f119f29SThomas Bogendoerfer 	if (IS_ERR(rtc)) {
354*5f119f29SThomas Bogendoerfer 		ret = PTR_ERR(rtc);
355*5f119f29SThomas Bogendoerfer 		goto out;
356*5f119f29SThomas Bogendoerfer 	}
357*5f119f29SThomas Bogendoerfer 	priv->rtc = rtc;
358*5f119f29SThomas Bogendoerfer 	platform_set_drvdata(pdev, priv);
359*5f119f29SThomas Bogendoerfer 	return 0;
360*5f119f29SThomas Bogendoerfer 
361*5f119f29SThomas Bogendoerfer out:
362*5f119f29SThomas Bogendoerfer 	if (priv->rtc)
363*5f119f29SThomas Bogendoerfer 		rtc_device_unregister(priv->rtc);
364*5f119f29SThomas Bogendoerfer 	if (priv->rtcregs)
365*5f119f29SThomas Bogendoerfer 		iounmap(priv->rtcregs);
366*5f119f29SThomas Bogendoerfer 	if (priv->baseaddr)
367*5f119f29SThomas Bogendoerfer 		release_mem_region(priv->baseaddr, priv->size);
368*5f119f29SThomas Bogendoerfer 	kfree(priv);
369*5f119f29SThomas Bogendoerfer 	return ret;
370*5f119f29SThomas Bogendoerfer }
371*5f119f29SThomas Bogendoerfer 
372*5f119f29SThomas Bogendoerfer static int __devexit ds1286_remove(struct platform_device *pdev)
373*5f119f29SThomas Bogendoerfer {
374*5f119f29SThomas Bogendoerfer 	struct ds1286_priv *priv = platform_get_drvdata(pdev);
375*5f119f29SThomas Bogendoerfer 
376*5f119f29SThomas Bogendoerfer 	rtc_device_unregister(priv->rtc);
377*5f119f29SThomas Bogendoerfer 	iounmap(priv->rtcregs);
378*5f119f29SThomas Bogendoerfer 	release_mem_region(priv->baseaddr, priv->size);
379*5f119f29SThomas Bogendoerfer 	kfree(priv);
380*5f119f29SThomas Bogendoerfer 	return 0;
381*5f119f29SThomas Bogendoerfer }
382*5f119f29SThomas Bogendoerfer 
383*5f119f29SThomas Bogendoerfer static struct platform_driver ds1286_platform_driver = {
384*5f119f29SThomas Bogendoerfer 	.driver		= {
385*5f119f29SThomas Bogendoerfer 		.name	= "rtc-ds1286",
386*5f119f29SThomas Bogendoerfer 		.owner	= THIS_MODULE,
387*5f119f29SThomas Bogendoerfer 	},
388*5f119f29SThomas Bogendoerfer 	.probe		= ds1286_probe,
389*5f119f29SThomas Bogendoerfer 	.remove		= __devexit_p(ds1286_remove),
390*5f119f29SThomas Bogendoerfer };
391*5f119f29SThomas Bogendoerfer 
392*5f119f29SThomas Bogendoerfer static int __init ds1286_init(void)
393*5f119f29SThomas Bogendoerfer {
394*5f119f29SThomas Bogendoerfer 	return platform_driver_register(&ds1286_platform_driver);
395*5f119f29SThomas Bogendoerfer }
396*5f119f29SThomas Bogendoerfer 
397*5f119f29SThomas Bogendoerfer static void __exit ds1286_exit(void)
398*5f119f29SThomas Bogendoerfer {
399*5f119f29SThomas Bogendoerfer 	platform_driver_unregister(&ds1286_platform_driver);
400*5f119f29SThomas Bogendoerfer }
401*5f119f29SThomas Bogendoerfer 
402*5f119f29SThomas Bogendoerfer MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
403*5f119f29SThomas Bogendoerfer MODULE_DESCRIPTION("DS1286 RTC driver");
404*5f119f29SThomas Bogendoerfer MODULE_LICENSE("GPL");
405*5f119f29SThomas Bogendoerfer MODULE_VERSION(DRV_VERSION);
406*5f119f29SThomas Bogendoerfer MODULE_ALIAS("platform:rtc-ds1286");
407*5f119f29SThomas Bogendoerfer 
408*5f119f29SThomas Bogendoerfer module_init(ds1286_init);
409*5f119f29SThomas Bogendoerfer module_exit(ds1286_exit);
410