diff options
Diffstat (limited to 'drivers/rtc/rtc-ds3232.c')
-rw-r--r-- | drivers/rtc/rtc-ds3232.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 9de8516e3531..57063552d3b7 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c | |||
@@ -2,6 +2,7 @@ | |||
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-2010 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 |
@@ -175,6 +176,182 @@ static int ds3232_set_time(struct device *dev, struct rtc_time *time) | |||
175 | DS3232_REG_SECONDS, 7, buf); | 176 | DS3232_REG_SECONDS, 7, buf); |
176 | } | 177 | } |
177 | 178 | ||
179 | /* | ||
180 | * DS3232 has two alarm, we only use alarm1 | ||
181 | * According to linux specification, only support one-shot alarm | ||
182 | * no periodic alarm mode | ||
183 | */ | ||
184 | static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
185 | { | ||
186 | struct i2c_client *client = to_i2c_client(dev); | ||
187 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | ||
188 | int control, stat; | ||
189 | int ret; | ||
190 | u8 buf[4]; | ||
191 | |||
192 | mutex_lock(&ds3232->mutex); | ||
193 | |||
194 | ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR); | ||
195 | if (ret < 0) | ||
196 | goto out; | ||
197 | stat = ret; | ||
198 | ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR); | ||
199 | if (ret < 0) | ||
200 | goto out; | ||
201 | control = ret; | ||
202 | ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); | ||
203 | if (ret < 0) | ||
204 | goto out; | ||
205 | |||
206 | alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F); | ||
207 | alarm->time.tm_min = bcd2bin(buf[1] & 0x7F); | ||
208 | alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F); | ||
209 | alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F); | ||
210 | |||
211 | alarm->time.tm_mon = -1; | ||
212 | alarm->time.tm_year = -1; | ||
213 | alarm->time.tm_wday = -1; | ||
214 | alarm->time.tm_yday = -1; | ||
215 | alarm->time.tm_isdst = -1; | ||
216 | |||
217 | alarm->enabled = !!(control & DS3232_REG_CR_A1IE); | ||
218 | alarm->pending = !!(stat & DS3232_REG_SR_A1F); | ||
219 | |||
220 | ret = 0; | ||
221 | out: | ||
222 | mutex_unlock(&ds3232->mutex); | ||
223 | return ret; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * linux rtc-module does not support wday alarm | ||
228 | * and only 24h time mode supported indeed | ||
229 | */ | ||
230 | static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
231 | { | ||
232 | struct i2c_client *client = to_i2c_client(dev); | ||
233 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | ||
234 | int control, stat; | ||
235 | int ret; | ||
236 | u8 buf[4]; | ||
237 | |||
238 | if (client->irq <= 0) | ||
239 | return -EINVAL; | ||
240 | |||
241 | mutex_lock(&ds3232->mutex); | ||
242 | |||
243 | buf[0] = bin2bcd(alarm->time.tm_sec); | ||
244 | buf[1] = bin2bcd(alarm->time.tm_min); | ||
245 | buf[2] = bin2bcd(alarm->time.tm_hour); | ||
246 | buf[3] = bin2bcd(alarm->time.tm_mday); | ||
247 | |||
248 | /* clear alarm interrupt enable bit */ | ||
249 | ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR); | ||
250 | if (ret < 0) | ||
251 | goto out; | ||
252 | control = ret; | ||
253 | control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE); | ||
254 | ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); | ||
255 | if (ret < 0) | ||
256 | goto out; | ||
257 | |||
258 | /* clear any pending alarm flag */ | ||
259 | ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR); | ||
260 | if (ret < 0) | ||
261 | goto out; | ||
262 | stat = ret; | ||
263 | stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F); | ||
264 | ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); | ||
265 | if (ret < 0) | ||
266 | goto out; | ||
267 | |||
268 | ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); | ||
269 | |||
270 | if (alarm->enabled) { | ||
271 | control |= DS3232_REG_CR_A1IE; | ||
272 | ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); | ||
273 | } | ||
274 | out: | ||
275 | mutex_unlock(&ds3232->mutex); | ||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | static void ds3232_update_alarm(struct i2c_client *client) | ||
280 | { | ||
281 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | ||
282 | int control; | ||
283 | int ret; | ||
284 | u8 buf[4]; | ||
285 | |||
286 | mutex_lock(&ds3232->mutex); | ||
287 | |||
288 | ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); | ||
289 | if (ret < 0) | ||
290 | goto unlock; | ||
291 | |||
292 | buf[0] = bcd2bin(buf[0]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? | ||
293 | 0x80 : buf[0]; | ||
294 | buf[1] = bcd2bin(buf[1]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? | ||
295 | 0x80 : buf[1]; | ||
296 | buf[2] = bcd2bin(buf[2]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? | ||
297 | 0x80 : buf[2]; | ||
298 | buf[3] = bcd2bin(buf[3]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? | ||
299 | 0x80 : buf[3]; | ||
300 | |||
301 | ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); | ||
302 | if (ret < 0) | ||
303 | goto unlock; | ||
304 | |||
305 | control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); | ||
306 | if (control < 0) | ||
307 | goto unlock; | ||
308 | |||
309 | if (ds3232->rtc->irq_data & (RTC_AF | RTC_UF)) | ||
310 | /* enable alarm1 interrupt */ | ||
311 | control |= DS3232_REG_CR_A1IE; | ||
312 | else | ||
313 | /* disable alarm1 interrupt */ | ||
314 | control &= ~(DS3232_REG_CR_A1IE); | ||
315 | i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); | ||
316 | |||
317 | unlock: | ||
318 | mutex_unlock(&ds3232->mutex); | ||
319 | } | ||
320 | |||
321 | static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
322 | { | ||
323 | struct i2c_client *client = to_i2c_client(dev); | ||
324 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | ||
325 | |||
326 | if (client->irq <= 0) | ||
327 | return -EINVAL; | ||
328 | |||
329 | if (enabled) | ||
330 | ds3232->rtc->irq_data |= RTC_AF; | ||
331 | else | ||
332 | ds3232->rtc->irq_data &= ~RTC_AF; | ||
333 | |||
334 | ds3232_update_alarm(client); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static int ds3232_update_irq_enable(struct device *dev, unsigned int enabled) | ||
339 | { | ||
340 | struct i2c_client *client = to_i2c_client(dev); | ||
341 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | ||
342 | |||
343 | if (client->irq <= 0) | ||
344 | return -EINVAL; | ||
345 | |||
346 | if (enabled) | ||
347 | ds3232->rtc->irq_data |= RTC_UF; | ||
348 | else | ||
349 | ds3232->rtc->irq_data &= ~RTC_UF; | ||
350 | |||
351 | ds3232_update_alarm(client); | ||
352 | return 0; | ||
353 | } | ||
354 | |||
178 | static irqreturn_t ds3232_irq(int irq, void *dev_id) | 355 | static irqreturn_t ds3232_irq(int irq, void *dev_id) |
179 | { | 356 | { |
180 | struct i2c_client *client = dev_id; | 357 | struct i2c_client *client = dev_id; |
@@ -222,6 +399,10 @@ unlock: | |||
222 | static const struct rtc_class_ops ds3232_rtc_ops = { | 399 | static const struct rtc_class_ops ds3232_rtc_ops = { |
223 | .read_time = ds3232_read_time, | 400 | .read_time = ds3232_read_time, |
224 | .set_time = ds3232_set_time, | 401 | .set_time = ds3232_set_time, |
402 | .read_alarm = ds3232_read_alarm, | ||
403 | .set_alarm = ds3232_set_alarm, | ||
404 | .alarm_irq_enable = ds3232_alarm_irq_enable, | ||
405 | .update_irq_enable = ds3232_update_irq_enable, | ||
225 | }; | 406 | }; |
226 | 407 | ||
227 | static int __devinit ds3232_probe(struct i2c_client *client, | 408 | static int __devinit ds3232_probe(struct i2c_client *client, |