mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Antony Pavlov <antonynpavlov@gmail.com>
To: Antony Pavlov <antonynpavlov@gmail.com>
Cc: barebox@lists.infradead.org
Subject: Re: [RFC v2 3/5] rtc: import ds1307 driver from linux-3.15
Date: Mon, 14 Jul 2014 16:48:44 +0400	[thread overview]
Message-ID: <20140714164844.4650208b0577aec2f59eb588@gmail.com> (raw)
In-Reply-To: <1405335380-17996-4-git-send-email-antonynpavlov@gmail.com>

On Mon, 14 Jul 2014 14:56:18 +0400
Antony Pavlov <antonynpavlov@gmail.com> wrote:

> Current ds1307 rtc driver supports only ds1307 and ds1338 chips;
> it has no nvram support at the moment.
> 
> Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
> ---
>  drivers/rtc/Kconfig      |  29 ++++
>  drivers/rtc/Makefile     |   4 +
>  drivers/rtc/rtc-ds1307.c | 345 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 378 insertions(+)
> 
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index f148266..8771933 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -14,3 +14,32 @@ menuconfig RTC_CLASS
>            Generic RTC class support. If you say yes here, you will
>            be allowed to plug one or more RTCs to your system. You will
>            probably want to enable one or more of the interfaces below.
> +
> +if RTC_CLASS
> +
> +comment "I2C RTC drivers"
> +        depends on I2C
> +
> +if I2C
> +
> +config RTC_DRV_DS1307
> +        tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
> +        help
> +          If you say yes here you get support for various compatible RTC
> +          chips (often with battery backup) connected with I2C. This driver
> +          should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00,
> +          EPSON RX-8025 and probably other chips. In some cases the RTC
> +          must already have been initialized (by manufacturing or a
> +          bootloader).
> +
> +          The first seven registers on these chips hold an RTC, and other
> +          registers may add features such as NVRAM, a trickle charger for
> +          the RTC/NVRAM backup power, and alarms. NVRAM is visible in
> +          sysfs, but other chip features may not be available.
> +
> +          This driver can also be built as a module. If so, the module
> +          will be called rtc-ds1307.

I should thin out this help text.

> +endif # I2C
> +
> +endif # RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 7d1b5bc..2c5a50e 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -4,3 +4,7 @@
>  
>  obj-$(CONFIG_RTC_LIB)           += rtc-lib.o
>  obj-$(CONFIG_RTC_CLASS)         += class.o
> +
> +# Keep the list ordered.
> +
> +obj-$(CONFIG_RTC_DRV_DS1307)    += rtc-ds1307.o
> diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
> new file mode 100644
> index 0000000..acbb41e
> --- /dev/null
> +++ b/drivers/rtc/rtc-ds1307.c
> @@ -0,0 +1,345 @@
> +/*
> + * rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
> + *
> + *  Copyright (C) 2005 James Chapman (ds1337 core)
> + *  Copyright (C) 2006 David Brownell
> + *  Copyright (C) 2009 Matthias Fuchs (rx8025 support)
> + *  Copyright (C) 2012 Bertrand Achard (nvram access fixes)
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <common.h>
> +#include <init.h>
> +#include <driver.h>
> +#include <xfuncs.h>
> +#include <errno.h>
> +#include <i2c/i2c.h>
> +#include <rtc.h>
> +#include <linux/rtc.h>
> +#include <linux/bcd.h>
> +
> +/*
> + * We can't determine type by probing, but if we expect pre-Linux code
> + * to have set the chip up as a clock (turning on the oscillator and
> + * setting the date and time), Linux can ignore the non-clock features.
> + * That's a natural job for a factory or repair bench.
> + */
> +enum ds_type {
> +	ds_1307,
> +	ds_1338,
> +	last_ds_type /* always last */
> +};
> +
> +/* RTC registers don't differ much, except for the century flag */
> +#define DS1307_REG_SECS		0x00	/* 00-59 */
> +#	define DS1307_BIT_CH		0x80
> +#	define DS1340_BIT_nEOSC		0x80
> +#define DS1307_REG_MIN		0x01	/* 00-59 */
> +#define DS1307_REG_HOUR		0x02	/* 00-23, or 1-12{am,pm} */
> +#	define DS1307_BIT_12HR		0x40	/* in REG_HOUR */
> +#	define DS1307_BIT_PM		0x20	/* in REG_HOUR */
> +#	define DS1340_BIT_CENTURY_EN	0x80	/* in REG_HOUR */
> +#	define DS1340_BIT_CENTURY	0x40	/* in REG_HOUR */
> +#define DS1307_REG_WDAY		0x03	/* 01-07 */
> +#define DS1307_REG_MDAY		0x04	/* 01-31 */
> +#define DS1307_REG_MONTH	0x05	/* 01-12 */
> +#	define DS1337_BIT_CENTURY	0x80	/* in REG_MONTH */
> +#define DS1307_REG_YEAR		0x06	/* 00-99 */
> +
> +/*
> + * Other registers (control, status, alarms, trickle charge, NVRAM, etc)
> + * start at 7, and they differ a LOT. Only control and status matter for
> + * basic RTC date and time functionality; be careful using them.
> + */
> +#define DS1307_REG_CONTROL	0x07		/* or ds1338 */
> +#	define DS1307_BIT_OUT		0x80
> +#	define DS1338_BIT_OSF		0x20
> +#	define DS1307_BIT_SQWE		0x10
> +#	define DS1307_BIT_RS1		0x02
> +#	define DS1307_BIT_RS0		0x01
> +
> +struct ds1307 {
> +	struct rtc_device	rtc;
> +	u8			offset; /* register's offset */
> +	u8			regs[11];
> +	enum ds_type		type;
> +	unsigned long		flags;
> +	struct i2c_client	*client;
> +	s32 (*read_block_data)(const struct i2c_client *client, u8 command,
> +			       u8 length, u8 *values);
> +	s32 (*write_block_data)(const struct i2c_client *client, u8 command,
> +				u8 length, const u8 *values);
> +};
> +
> +static struct platform_device_id ds1307_id[] = {
> +	{ "ds1307", ds_1307 },
> +	{ "ds1338", ds_1338 },
> +	{ "pt7c4338", ds_1307 },
> +	{ }
> +};
> +
> +/*----------------------------------------------------------------------*/
> +
> +#define BLOCK_DATA_MAX_TRIES 10
> +
> +static s32 ds1307_read_block_data_once(const struct i2c_client *client,
> +				       u8 command, u8 length, u8 *values)
> +{
> +	s32 i, data;
> +
> +	for (i = 0; i < length; i++) {
> +		data = i2c_smbus_read_byte_data(client, command + i);
> +		if (data < 0)
> +			return data;
> +		values[i] = data;
> +	}
> +	return i;
> +}
> +
> +static s32 ds1307_read_block_data(const struct i2c_client *client, u8 command,
> +				  u8 length, u8 *values)
> +{
> +	u8 oldvalues[255];
> +	s32 ret;
> +	int tries = 0;
> +
> +	dev_dbg(&client->dev, "ds1307_read_block_data (length=%d)\n", length);
> +	ret = ds1307_read_block_data_once(client, command, length, values);
> +	if (ret < 0)
> +		return ret;
> +	do {
> +		if (++tries > BLOCK_DATA_MAX_TRIES) {
> +			dev_err(&client->dev,
> +				"ds1307_read_block_data failed\n");
> +			return -EIO;
> +		}
> +		memcpy(oldvalues, values, length);
> +		ret = ds1307_read_block_data_once(client, command, length,
> +						  values);
> +		if (ret < 0)
> +			return ret;
> +	} while (memcmp(oldvalues, values, length));
> +	return length;
> +}
> +
> +static s32 ds1307_write_block_data(const struct i2c_client *client, u8 command,
> +				   u8 length, const u8 *values)
> +{
> +	u8 currvalues[255];
> +	int tries = 0;
> +
> +	dev_dbg(&client->dev, "ds1307_write_block_data (length=%d)\n", length);
> +	do {
> +		s32 i, ret;
> +
> +		if (++tries > BLOCK_DATA_MAX_TRIES) {
> +			dev_err(&client->dev,
> +				"ds1307_write_block_data failed\n");
> +			return -EIO;
> +		}
> +		for (i = 0; i < length; i++) {
> +			ret = i2c_smbus_write_byte_data(client, command + i,
> +							values[i]);
> +			if (ret < 0)
> +				return ret;
> +		}
> +		ret = ds1307_read_block_data_once(client, command, length,
> +						  currvalues);
> +		if (ret < 0)
> +			return ret;
> +	} while (memcmp(currvalues, values, length));
> +	return length;
> +}
> +
> +static inline struct ds1307 *to_ds1307_priv(struct rtc_device *rtcdev)
> +{
> +	return container_of(rtcdev, struct ds1307, rtc);
> +}
> +
> +static int ds1307_get_time(struct rtc_device *rtcdev, struct rtc_time *t)
> +{
> +	struct device_d *dev = rtcdev->dev;
> +	struct ds1307 *ds1307 = to_ds1307_priv(rtcdev);
> +	int		tmp;
> +
> +	/* read the RTC date and time registers all at once */
> +	tmp = ds1307->read_block_data(ds1307->client,
> +		ds1307->offset, 7, ds1307->regs);
> +	if (tmp != 7) {
> +		dev_err(dev, "%s error %d\n", "read", tmp);
> +		return -EIO;
> +	}
> +
> +	dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs);
> +
> +	t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f);
> +	t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f);
> +	tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f;
> +	t->tm_hour = bcd2bin(tmp);
> +	t->tm_wday = bcd2bin(ds1307->regs[DS1307_REG_WDAY] & 0x07) - 1;
> +	t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
> +	tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f;
> +	t->tm_mon = bcd2bin(tmp) - 1;
> +
> +	/* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */
> +	t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100;
> +
> +	dev_dbg(dev, "%s secs=%d, mins=%d, "
> +		"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
> +		"read", t->tm_sec, t->tm_min,
> +		t->tm_hour, t->tm_mday,
> +		t->tm_mon, t->tm_year, t->tm_wday);
> +
> +	/* initial clock setting can be undefined */
> +	return rtc_valid_tm(t);
> +}
> +
> +static int ds1307_set_time(struct rtc_device *rtcdev, struct rtc_time *t)
> +{
> +	struct device_d *dev = rtcdev->dev;
> +	struct ds1307 *ds1307 = to_ds1307_priv(rtcdev);
> +	int		result;
> +	int		tmp;
> +	u8		*buf = ds1307->regs;
> +
> +	dev_dbg(dev, "%s secs=%d, mins=%d, "
> +		"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
> +		"write", t->tm_sec, t->tm_min,
> +		t->tm_hour, t->tm_mday,
> +		t->tm_mon, t->tm_year, t->tm_wday);
> +
> +	buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
> +	buf[DS1307_REG_MIN] = bin2bcd(t->tm_min);
> +	buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
> +	buf[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1);
> +	buf[DS1307_REG_MDAY] = bin2bcd(t->tm_mday);
> +	buf[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1);
> +
> +	/* assume 20YY not 19YY */
> +	tmp = t->tm_year - 100;
> +	buf[DS1307_REG_YEAR] = bin2bcd(tmp);
> +
> +	dev_dbg(dev, "%s: %7ph\n", "write", buf);
> +
> +	result = ds1307->write_block_data(ds1307->client,
> +		ds1307->offset, 7, buf);
> +	if (result < 0) {
> +		dev_err(dev, "%s error %d\n", "write", result);
> +		return result;
> +	}
> +	return 0;
> +}
> +
> +static const struct rtc_class_ops ds13xx_rtc_ops = {
> +	.read_time	= ds1307_get_time,
> +	.set_time	= ds1307_set_time,
> +};
> +
> +static int ds1307_probe(struct device_d *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct ds1307		*ds1307;
> +	int			err = -ENODEV;
> +	int			tmp;
> +	unsigned char		*buf;
> +	unsigned long driver_data;
> +
> +	ds1307 = xzalloc(sizeof(struct ds1307));
> +
> +	err = dev_get_drvdata(dev, &driver_data);
> +	if (err)
> +		goto exit;
> +
> +	ds1307->client = client;
> +	ds1307->type = driver_data;
> +
> +	buf = ds1307->regs;
> +
> +	ds1307->read_block_data = ds1307_read_block_data;
> +	ds1307->write_block_data = ds1307_write_block_data;
> +
> +read_rtc:
> +	/* read RTC registers */
> +	tmp = ds1307->read_block_data(client, ds1307->offset, 8, buf);
> +	if (tmp != 8) {
> +		dev_dbg(&client->dev, "read error %d\n", tmp);
> +		err = -EIO;
> +		goto exit;
> +	}
> +
> +	/*
> +	 * minimal sanity checking; some chips (like DS1340) don't
> +	 * specify the extra bits as must-be-zero, but there are
> +	 * still a few values that are clearly out-of-range.
> +	 */
> +	tmp = ds1307->regs[DS1307_REG_SECS];
> +	switch (ds1307->type) {
> +	case ds_1307:
> +		/* clock halted?  turn it on, so clock can tick. */
> +		if (tmp & DS1307_BIT_CH) {
> +			i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
> +			dev_warn(&client->dev, "SET TIME!\n");
> +			goto read_rtc;
> +		}
> +		break;
> +	case ds_1338:
> +		/* clock halted?  turn it on, so clock can tick. */
> +		if (tmp & DS1307_BIT_CH)
> +			i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
> +
> +		/* oscillator fault?  clear flag, and warn */
> +		if (ds1307->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) {
> +			i2c_smbus_write_byte_data(client, DS1307_REG_CONTROL,
> +					ds1307->regs[DS1307_REG_CONTROL]
> +					& ~DS1338_BIT_OSF);
> +			dev_warn(&client->dev, "SET TIME!\n");
> +			goto read_rtc;
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	tmp = ds1307->regs[DS1307_REG_HOUR];
> +	switch (ds1307->type) {
> +	default:
> +		if (!(tmp & DS1307_BIT_12HR))
> +			break;
> +
> +		/*
> +		 * Be sure we're in 24 hour mode.  Multi-master systems
> +		 * take note...
> +		 */
> +		tmp = bcd2bin(tmp & 0x1f);
> +		if (tmp == 12)
> +			tmp = 0;
> +		if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
> +			tmp += 12;
> +		i2c_smbus_write_byte_data(client,
> +				ds1307->offset + DS1307_REG_HOUR,
> +				bin2bcd(tmp));
> +	}
> +
> +	ds1307->rtc.ops = &ds13xx_rtc_ops;
> +	ds1307->rtc.dev = dev;
> +
> +	err = rtc_register(&ds1307->rtc);
> +
> +exit:
> +	return err;
> +}
> +
> +static struct driver_d ds1307_driver = {
> +	.name	= "rtc-ds1307",
> +	.probe		= ds1307_probe,
> +	.id_table	= ds1307_id,
> +};
> +
> +static int __init ds1307_init(void)
> +{
> +	return i2c_driver_register(&ds1307_driver);
> +}
> +device_initcall(ds1307_init);
> -- 
> 2.0.1
> 


-- 
-- 
Best regards,
  Antony Pavlov

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

  reply	other threads:[~2014-07-14 12:36 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-14 10:56 [RFC v2 0/5] add rtc support Antony Pavlov
2014-07-14 10:56 ` [RFC v2 1/5] lib: import 'bcd' from linux-3.15 Antony Pavlov
2014-07-14 10:56 ` [RFC v2 2/5] add rtc support Antony Pavlov
2014-07-14 12:40   ` Antony Pavlov
2014-07-14 10:56 ` [RFC v2 3/5] rtc: import ds1307 driver from linux-3.15 Antony Pavlov
2014-07-14 12:48   ` Antony Pavlov [this message]
2014-07-14 10:56 ` [RFC v2 4/5] commands: add hwclock Antony Pavlov
2014-07-14 10:56 ` [RFC v2 5/5] ARM: versatilepb_defconfig: enable RTC support Antony Pavlov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140714164844.4650208b0577aec2f59eb588@gmail.com \
    --to=antonynpavlov@gmail.com \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox