diff options
Diffstat (limited to 'drivers/rtc/rtc-ds3232.c')
-rw-r--r-- | drivers/rtc/rtc-ds3232.c | 179 |
1 files changed, 173 insertions, 6 deletions
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 9de8516e3531..27b7bf672ac6 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C | 2 | * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C |
3 | * | 3 | * |
4 | * Copyright (C) 2009-2010 Freescale Semiconductor. | 4 | * Copyright (C) 2009-2011 Freescale Semiconductor. |
5 | * Author: Jack Lan <jack.lan@freescale.com> | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the | 8 | * under the terms of the GNU General Public License as published by the |
@@ -140,9 +141,11 @@ static int ds3232_read_time(struct device *dev, struct rtc_time *time) | |||
140 | time->tm_hour = bcd2bin(hour); | 141 | time->tm_hour = bcd2bin(hour); |
141 | } | 142 | } |
142 | 143 | ||
143 | time->tm_wday = bcd2bin(week); | 144 | /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */ |
145 | time->tm_wday = bcd2bin(week) - 1; | ||
144 | time->tm_mday = bcd2bin(day); | 146 | time->tm_mday = bcd2bin(day); |
145 | time->tm_mon = bcd2bin(month & 0x7F); | 147 | /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */ |
148 | time->tm_mon = bcd2bin(month & 0x7F) - 1; | ||
146 | if (century) | 149 | if (century) |
147 | add_century = 100; | 150 | add_century = 100; |
148 | 151 | ||
@@ -161,9 +164,11 @@ static int ds3232_set_time(struct device *dev, struct rtc_time *time) | |||
161 | buf[0] = bin2bcd(time->tm_sec); | 164 | buf[0] = bin2bcd(time->tm_sec); |
162 | buf[1] = bin2bcd(time->tm_min); | 165 | buf[1] = bin2bcd(time->tm_min); |
163 | buf[2] = bin2bcd(time->tm_hour); | 166 | buf[2] = bin2bcd(time->tm_hour); |
164 | buf[3] = bin2bcd(time->tm_wday); /* Day of the week */ | 167 | /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */ |
168 | buf[3] = bin2bcd(time->tm_wday + 1); | ||
165 | buf[4] = bin2bcd(time->tm_mday); /* Date */ | 169 | buf[4] = bin2bcd(time->tm_mday); /* Date */ |
166 | buf[5] = bin2bcd(time->tm_mon); | 170 | /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */ |
171 | buf[5] = bin2bcd(time->tm_mon + 1); | ||
167 | if (time->tm_year >= 100) { | 172 | if (time->tm_year >= 100) { |
168 | buf[5] |= 0x80; | 173 | buf[5] |= 0x80; |
169 | buf[6] = bin2bcd(time->tm_year - 100); | 174 | buf[6] = bin2bcd(time->tm_year - 100); |
@@ -175,6 +180,165 @@ static int ds3232_set_time(struct device *dev, struct rtc_time *time) | |||
175 | DS3232_REG_SECONDS, 7, buf); | 180 | DS3232_REG_SECONDS, 7, buf); |
176 | } | 181 | } |
177 | 182 | ||
183 | /* | ||
184 | * DS3232 has two alarm, we only use alarm1 | ||
185 | * According to linux specification, only support one-shot alarm | ||
186 | * no periodic alarm mode | ||
187 | */ | ||
188 | static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
189 | { | ||
190 | struct i2c_client *client = to_i2c_client(dev); | ||
191 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | ||
192 | int control, stat; | ||
193 | int ret; | ||
194 | u8 buf[4]; | ||
195 | |||
196 | mutex_lock(&ds3232->mutex); | ||
197 | |||
198 | ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR); | ||
199 | if (ret < 0) | ||
200 | goto out; | ||
201 | stat = ret; | ||
202 | ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR); | ||
203 | if (ret < 0) | ||
204 | goto out; | ||
205 | control = ret; | ||
206 | ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); | ||
207 | if (ret < 0) | ||
208 | goto out; | ||
209 | |||
210 | alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F); | ||
211 | alarm->time.tm_min = bcd2bin(buf[1] & 0x7F); | ||
212 | alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F); | ||
213 | alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F); | ||
214 | |||
215 | alarm->time.tm_mon = -1; | ||
216 | alarm->time.tm_year = -1; | ||
217 | alarm->time.tm_wday = -1; | ||
218 | alarm->time.tm_yday = -1; | ||
219 | alarm->time.tm_isdst = -1; | ||
220 | |||
221 | alarm->enabled = !!(control & DS3232_REG_CR_A1IE); | ||
222 | alarm->pending = !!(stat & DS3232_REG_SR_A1F); | ||
223 | |||
224 | ret = 0; | ||
225 | out: | ||
226 | mutex_unlock(&ds3232->mutex); | ||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * linux rtc-module does not support wday alarm | ||
232 | * and only 24h time mode supported indeed | ||
233 | */ | ||
234 | static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
235 | { | ||
236 | struct i2c_client *client = to_i2c_client(dev); | ||
237 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | ||
238 | int control, stat; | ||
239 | int ret; | ||
240 | u8 buf[4]; | ||
241 | |||
242 | if (client->irq <= 0) | ||
243 | return -EINVAL; | ||
244 | |||
245 | mutex_lock(&ds3232->mutex); | ||
246 | |||
247 | buf[0] = bin2bcd(alarm->time.tm_sec); | ||
248 | buf[1] = bin2bcd(alarm->time.tm_min); | ||
249 | buf[2] = bin2bcd(alarm->time.tm_hour); | ||
250 | buf[3] = bin2bcd(alarm->time.tm_mday); | ||
251 | |||
252 | /* clear alarm interrupt enable bit */ | ||
253 | ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR); | ||
254 | if (ret < 0) | ||
255 | goto out; | ||
256 | control = ret; | ||
257 | control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE); | ||
258 | ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); | ||
259 | if (ret < 0) | ||
260 | goto out; | ||
261 | |||
262 | /* clear any pending alarm flag */ | ||
263 | ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR); | ||
264 | if (ret < 0) | ||
265 | goto out; | ||
266 | stat = ret; | ||
267 | stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F); | ||
268 | ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); | ||
269 | if (ret < 0) | ||
270 | goto out; | ||
271 | |||
272 | ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); | ||
273 | |||
274 | if (alarm->enabled) { | ||
275 | control |= DS3232_REG_CR_A1IE; | ||
276 | ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); | ||
277 | } | ||
278 | out: | ||
279 | mutex_unlock(&ds3232->mutex); | ||
280 | return ret; | ||
281 | } | ||
282 | |||
283 | static void ds3232_update_alarm(struct i2c_client *client) | ||
284 | { | ||
285 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | ||
286 | int control; | ||
287 | int ret; | ||
288 | u8 buf[4]; | ||
289 | |||
290 | mutex_lock(&ds3232->mutex); | ||
291 | |||
292 | ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); | ||
293 | if (ret < 0) | ||
294 | goto unlock; | ||
295 | |||
296 | buf[0] = bcd2bin(buf[0]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? | ||
297 | 0x80 : buf[0]; | ||
298 | buf[1] = bcd2bin(buf[1]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? | ||
299 | 0x80 : buf[1]; | ||
300 | buf[2] = bcd2bin(buf[2]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? | ||
301 | 0x80 : buf[2]; | ||
302 | buf[3] = bcd2bin(buf[3]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? | ||
303 | 0x80 : buf[3]; | ||
304 | |||
305 | ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); | ||
306 | if (ret < 0) | ||
307 | goto unlock; | ||
308 | |||
309 | control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); | ||
310 | if (control < 0) | ||
311 | goto unlock; | ||
312 | |||
313 | if (ds3232->rtc->irq_data & (RTC_AF | RTC_UF)) | ||
314 | /* enable alarm1 interrupt */ | ||
315 | control |= DS3232_REG_CR_A1IE; | ||
316 | else | ||
317 | /* disable alarm1 interrupt */ | ||
318 | control &= ~(DS3232_REG_CR_A1IE); | ||
319 | i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); | ||
320 | |||
321 | unlock: | ||
322 | mutex_unlock(&ds3232->mutex); | ||
323 | } | ||
324 | |||
325 | static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
326 | { | ||
327 | struct i2c_client *client = to_i2c_client(dev); | ||
328 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | ||
329 | |||
330 | if (client->irq <= 0) | ||
331 | return -EINVAL; | ||
332 | |||
333 | if (enabled) | ||
334 | ds3232->rtc->irq_data |= RTC_AF; | ||
335 | else | ||
336 | ds3232->rtc->irq_data &= ~RTC_AF; | ||
337 | |||
338 | ds3232_update_alarm(client); | ||
339 | return 0; | ||
340 | } | ||
341 | |||
178 | static irqreturn_t ds3232_irq(int irq, void *dev_id) | 342 | static irqreturn_t ds3232_irq(int irq, void *dev_id) |
179 | { | 343 | { |
180 | struct i2c_client *client = dev_id; | 344 | struct i2c_client *client = dev_id; |
@@ -222,6 +386,9 @@ unlock: | |||
222 | static const struct rtc_class_ops ds3232_rtc_ops = { | 386 | static const struct rtc_class_ops ds3232_rtc_ops = { |
223 | .read_time = ds3232_read_time, | 387 | .read_time = ds3232_read_time, |
224 | .set_time = ds3232_set_time, | 388 | .set_time = ds3232_set_time, |
389 | .read_alarm = ds3232_read_alarm, | ||
390 | .set_alarm = ds3232_set_alarm, | ||
391 | .alarm_irq_enable = ds3232_alarm_irq_enable, | ||
225 | }; | 392 | }; |
226 | 393 | ||
227 | static int __devinit ds3232_probe(struct i2c_client *client, | 394 | static int __devinit ds3232_probe(struct i2c_client *client, |
@@ -282,7 +449,7 @@ static int __devexit ds3232_remove(struct i2c_client *client) | |||
282 | mutex_unlock(&ds3232->mutex); | 449 | mutex_unlock(&ds3232->mutex); |
283 | 450 | ||
284 | free_irq(client->irq, client); | 451 | free_irq(client->irq, client); |
285 | flush_scheduled_work(); | 452 | cancel_work_sync(&ds3232->work); |
286 | } | 453 | } |
287 | 454 | ||
288 | rtc_device_unregister(ds3232->rtc); | 455 | rtc_device_unregister(ds3232->rtc); |