Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 218502
b: refs/heads/master
c: f46418c
h: refs/heads/master
v: v3
  • Loading branch information
Lan Chunhe-B25806 authored and Linus Torvalds committed Oct 28, 2010
1 parent 67bcd37 commit a7cf5a2
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 5b3ffddd8ddb4712dfe14ad3e23eb5494f11bf61
refs/heads/master: f46418c5cadfe76b15c630ff746ca859a8207d71
3 changes: 2 additions & 1 deletion trunk/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 trunk/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 a7cf5a2

Please sign in to comment.