Patchwork Add the alarm function for driver of DS3232 RTC

login
register
mail settings
Submitter Lan Chunhe-B25806
Date Oct. 15, 2010, 11:12 a.m.
Message ID <1287141145-3217-1-git-send-email-b25806@freescale.com>
Download mbox | patch
Permalink /patch/67935/
State New
Headers show

Comments

Lan Chunhe-B25806 - Oct. 15, 2010, 11:12 a.m.
The former driver of DS3232 RTC only has the tick function,
and now add the alarm function for driver of DS3232 RTC.
So, the driver of DS3232 RTC is complete.

Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
---
 drivers/rtc/rtc-ds3232.c |  182 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 182 insertions(+), 0 deletions(-)
Wan ZongShun - Oct. 15, 2010, 1:42 p.m.
Hi Lan,

I just have a little of proposal.

2010/10/15 Lan Chunhe-B25806 <b25806@freescale.com>:
> The former driver of DS3232 RTC only has the tick function,
> and now add the alarm function for driver of DS3232 RTC.
> So, the driver of DS3232 RTC is complete.
>
> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
> ---
>  drivers/rtc/rtc-ds3232.c |  182 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 182 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
> index 9daed8d..a9eda7a 100644
> --- a/drivers/rtc/rtc-ds3232.c
> +++ b/drivers/rtc/rtc-ds3232.c
> @@ -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
> @@ -175,6 +176,183 @@ 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 = 0;
> +       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);
> +
> +       mutex_unlock(&ds3232->mutex);
> +       return 0;

To use 'ret = 0;' to avoid writing twice mutex unlock, Is it better?

> +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;
> @@ -222,6 +400,10 @@ unlock:
>  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,
>  };
>

Others look good to me, thanks!

>  static int __devinit ds3232_probe(struct i2c_client *client,
> --
> 1.5.4.5
>
>
> --
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
Tiefei zang - Oct. 16, 2010, 12:07 a.m.
On Fri, Oct 15, 2010 at 7:12 PM, Lan Chunhe-B25806 <b25806@freescale.com>wrote:

> The former driver of DS3232 RTC only has the tick function,
> and now add the alarm function for driver of DS3232 RTC.
> So, the driver of DS3232 RTC is complete.
>
> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
> ---
>  drivers/rtc/rtc-ds3232.c |  182
> ++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 182 insertions(+), 0 deletions(-)
>
> Thanks for the patch, please also help to add the alarm function
explanation in the help information in Kconfig.
Roy
Lan Chunhe-B25806 - Oct. 18, 2010, 3 a.m.
On Fri, 15 Oct 2010 21:42:22 +0800, Wan ZongShun <mcuos.com@gmail.com>  
wrote:

> Hi Lan,
>
> I just have a little of proposal.
>
> 2010/10/15 Lan Chunhe-B25806 <b25806@freescale.com>:
>> The former driver of DS3232 RTC only has the tick function,
>> and now add the alarm function for driver of DS3232 RTC.
>> So, the driver of DS3232 RTC is complete.
>>
>> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
>> ---
>>  drivers/rtc/rtc-ds3232.c |  182  
>> ++++++++++++++++++++++++++++++++++++++++++++++
>>  1 files changed, 182 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
>> index 9daed8d..a9eda7a 100644
>> --- a/drivers/rtc/rtc-ds3232.c
>> +++ b/drivers/rtc/rtc-ds3232.c
>> @@ -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
>> @@ -175,6 +176,183 @@ 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 = 0;
>> +       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);

	The function of i2c_smbus_read_i2c_block_data returns the
	number of read bytes, and is not zero.

>> +       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);
>> +
>> +       mutex_unlock(&ds3232->mutex);
>> +       return 0;
>
> To use 'ret = 0;' to avoid writing twice mutex unlock, Is it better?
>

	Thanks.

                 Jack Lan
Lan Chunhe-B25806 - Oct. 18, 2010, 3:04 a.m.
On Sat, 16 Oct 2010 08:07:30 +0800, Tiefei zang <tiefei.zang@gmail.com>  
wrote:

> On Fri, Oct 15, 2010 at 7:12 PM, Lan Chunhe-B25806  
> <b25806@freescale.com>wrote:
>
>> The former driver of DS3232 RTC only has the tick function,
>> and now add the alarm function for driver of DS3232 RTC.
>> So, the driver of DS3232 RTC is complete.
>>
>> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
>> ---
>>  drivers/rtc/rtc-ds3232.c |  182
>> ++++++++++++++++++++++++++++++++++++++++++++++
>>  1 files changed, 182 insertions(+), 0 deletions(-)
>>
>> Thanks for the patch, please also help to add the alarm function
> explanation in the help information in Kconfig.

      I will add the alarm function explanation in the revision two of  
patch.

      Thanks.

      Jack Lan

Patch

diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
index 9daed8d..a9eda7a 100644
--- a/drivers/rtc/rtc-ds3232.c
+++ b/drivers/rtc/rtc-ds3232.c
@@ -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
@@ -175,6 +176,183 @@  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 = 0;
+	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);
+
+	mutex_unlock(&ds3232->mutex);
+	return 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;
@@ -222,6 +400,10 @@  unlock:
 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,