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
next prev parent 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