Skip to content

Commit

Permalink
drivers/rtc/rtc-ds3232.c: add alarm function
Browse files Browse the repository at this point in the history
The DS3232 RTC driver only has the tick function.  Add an alarm function
so the driver is complete.

Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Wan ZongShun <mcuos.com@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Lan Chunhe-B25806 authored and Linus Torvalds committed Oct 28, 2010
1 parent 5b3ffdd commit f46418c
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 1 deletion.
3 changes: 2 additions & 1 deletion drivers/rtc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ config RTC_DRV_DS3232
depends on RTC_CLASS && I2C
help
If you say yes here you get support for Dallas Semiconductor
DS3232 real-time clock chips.
DS3232 real-time clock chips. If an interrupt is associated
with the device, the alarm functionality is supported.

This driver can also be built as a module. If so, the module
will be called rtc-ds3232.
Expand Down
181 changes: 181 additions & 0 deletions drivers/rtc/rtc-ds3232.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C
*
* Copyright (C) 2009-2010 Freescale Semiconductor.
* Author: Jack Lan <jack.lan@freescale.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Expand Down Expand Up @@ -175,6 +176,182 @@ static int ds3232_set_time(struct device *dev, struct rtc_time *time)
DS3232_REG_SECONDS, 7, buf);
}

/*
* DS3232 has two alarm, we only use alarm1
* According to linux specification, only support one-shot alarm
* no periodic alarm mode
*/
static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds3232 *ds3232 = i2c_get_clientdata(client);
int control, stat;
int ret;
u8 buf[4];

mutex_lock(&ds3232->mutex);

ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
if (ret < 0)
goto out;
stat = ret;
ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
if (ret < 0)
goto out;
control = ret;
ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
if (ret < 0)
goto out;

alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
alarm->time.tm_min = bcd2bin(buf[1] & 0x7F);
alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F);
alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F);

alarm->time.tm_mon = -1;
alarm->time.tm_year = -1;
alarm->time.tm_wday = -1;
alarm->time.tm_yday = -1;
alarm->time.tm_isdst = -1;

alarm->enabled = !!(control & DS3232_REG_CR_A1IE);
alarm->pending = !!(stat & DS3232_REG_SR_A1F);

ret = 0;
out:
mutex_unlock(&ds3232->mutex);
return ret;
}

/*
* linux rtc-module does not support wday alarm
* and only 24h time mode supported indeed
*/
static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds3232 *ds3232 = i2c_get_clientdata(client);
int control, stat;
int ret;
u8 buf[4];

if (client->irq <= 0)
return -EINVAL;

mutex_lock(&ds3232->mutex);

buf[0] = bin2bcd(alarm->time.tm_sec);
buf[1] = bin2bcd(alarm->time.tm_min);
buf[2] = bin2bcd(alarm->time.tm_hour);
buf[3] = bin2bcd(alarm->time.tm_mday);

/* clear alarm interrupt enable bit */
ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
if (ret < 0)
goto out;
control = ret;
control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
if (ret < 0)
goto out;

/* clear any pending alarm flag */
ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
if (ret < 0)
goto out;
stat = ret;
stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
if (ret < 0)
goto out;

ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);

if (alarm->enabled) {
control |= DS3232_REG_CR_A1IE;
ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
}
out:
mutex_unlock(&ds3232->mutex);
return ret;
}

static void ds3232_update_alarm(struct i2c_client *client)
{
struct ds3232 *ds3232 = i2c_get_clientdata(client);
int control;
int ret;
u8 buf[4];

mutex_lock(&ds3232->mutex);

ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
if (ret < 0)
goto unlock;

buf[0] = bcd2bin(buf[0]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
0x80 : buf[0];
buf[1] = bcd2bin(buf[1]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
0x80 : buf[1];
buf[2] = bcd2bin(buf[2]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
0x80 : buf[2];
buf[3] = bcd2bin(buf[3]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
0x80 : buf[3];

ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
if (ret < 0)
goto unlock;

control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
if (control < 0)
goto unlock;

if (ds3232->rtc->irq_data & (RTC_AF | RTC_UF))
/* enable alarm1 interrupt */
control |= DS3232_REG_CR_A1IE;
else
/* disable alarm1 interrupt */
control &= ~(DS3232_REG_CR_A1IE);
i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);

unlock:
mutex_unlock(&ds3232->mutex);
}

static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds3232 *ds3232 = i2c_get_clientdata(client);

if (client->irq <= 0)
return -EINVAL;

if (enabled)
ds3232->rtc->irq_data |= RTC_AF;
else
ds3232->rtc->irq_data &= ~RTC_AF;

ds3232_update_alarm(client);
return 0;
}

static int ds3232_update_irq_enable(struct device *dev, unsigned int enabled)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds3232 *ds3232 = i2c_get_clientdata(client);

if (client->irq <= 0)
return -EINVAL;

if (enabled)
ds3232->rtc->irq_data |= RTC_UF;
else
ds3232->rtc->irq_data &= ~RTC_UF;

ds3232_update_alarm(client);
return 0;
}

static irqreturn_t ds3232_irq(int irq, void *dev_id)
{
struct i2c_client *client = dev_id;
Expand Down Expand Up @@ -222,6 +399,10 @@ static void ds3232_work(struct work_struct *work)
static const struct rtc_class_ops ds3232_rtc_ops = {
.read_time = ds3232_read_time,
.set_time = ds3232_set_time,
.read_alarm = ds3232_read_alarm,
.set_alarm = ds3232_set_alarm,
.alarm_irq_enable = ds3232_alarm_irq_enable,
.update_irq_enable = ds3232_update_irq_enable,
};

static int __devinit ds3232_probe(struct i2c_client *client,
Expand Down

0 comments on commit f46418c

Please sign in to comment.