Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f46418c5 authored by Lan Chunhe-B25806's avatar Lan Chunhe-B25806 Committed by Linus Torvalds
Browse files

drivers/rtc/rtc-ds3232.c: add alarm function



The DS3232 RTC driver only has the tick function.  Add an alarm function
so the driver is complete.

Signed-off-by: default avatarLan Chunhe-B25806 <b25806@freescale.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Wan ZongShun <mcuos.com@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 5b3ffddd
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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.
+181 −0
Original line number Diff line number Diff line
@@ -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,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;
@@ -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,