diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2016-01-11 13:11:08 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2016-01-11 13:11:08 +0100 |
commit | 1b59b573e434b19b45b12b07a28d749d72aeb145 (patch) | |
tree | 7fa1a5006cb916555621e09571cdb5424edb76fe /drivers | |
parent | 70ef1cef5c1a03a4b130f3916d47c3076880b5d6 (diff) | |
parent | 4c9b2e72057ca3d1578b33ff75ad251bb8225a9c (diff) | |
download | barebox-1b59b573e434b19b45b12b07a28d749d72aeb145.tar.gz barebox-1b59b573e434b19b45b12b07a28d749d72aeb145.tar.xz |
Merge branch 'for-next/rtc'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/rtc/Kconfig | 3 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/class.c | 11 | ||||
-rw-r--r-- | drivers/rtc/rtc-abracon.c | 126 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 94 | ||||
-rw-r--r-- | drivers/rtc/rtc-lib.c | 2 |
6 files changed, 236 insertions, 1 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 191ad97fb8..7d181949ee 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -33,6 +33,9 @@ config RTC_DRV_DS1307 registers may add features such as NVRAM, a trickle charger for the RTC/NVRAM backup power, and alarms. +config RTC_DRV_ABRACON + tristate "Abracon RTCs" + endif # I2C config RTC_DRV_JZ4740 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 1cc9bb8b4b..68741c26a1 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -7,5 +7,6 @@ obj-$(CONFIG_RTC_CLASS) += class.o # Keep the list ordered. +obj-$(CONFIG_RTC_DRV_ABRACON) += rtc-abracon.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 356707be2f..8b047a638d 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -46,7 +46,16 @@ EXPORT_SYMBOL(rtc_read_time); int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) { - return rtc->ops->set_time(rtc, tm); + struct rtc_time time; + unsigned long secs; + + if (rtc_valid_tm(tm)) + return -EINVAL; + + rtc_tm_to_time(tm, &secs); + rtc_time_to_tm(secs, &time); + + return rtc->ops->set_time(rtc, &time); } EXPORT_SYMBOL(rtc_set_time); diff --git a/drivers/rtc/rtc-abracon.c b/drivers/rtc/rtc-abracon.c new file mode 100644 index 0000000000..b3af990b9c --- /dev/null +++ b/drivers/rtc/rtc-abracon.c @@ -0,0 +1,126 @@ +/* + * 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 Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#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> + +struct abracon { + struct rtc_device rtc; + struct i2c_client *client; +}; + +static inline struct abracon *to_abracon_priv(struct rtc_device *rtcdev) +{ + return container_of(rtcdev, struct abracon, rtc); +} + +static int abracon_get_time(struct rtc_device *rtcdev, struct rtc_time *t) +{ + struct abracon *abracon = to_abracon_priv(rtcdev); + struct i2c_client *client = abracon->client; + u8 cp[7] = {}; + u8 reg = 8; + struct i2c_msg msg[2] = {}; + int ret; + + msg[0].addr = client->addr; + msg[0].buf = ® + msg[0].len = 1; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = cp; + msg[1].len = 7; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != 2) + return -EIO; + + t->tm_sec = bcd2bin(cp[0]); + t->tm_min = bcd2bin(cp[1]); + t->tm_hour = bcd2bin(cp[2]); + t->tm_mday = bcd2bin(cp[3]); + t->tm_wday = bcd2bin(cp[4]); + t->tm_mon = bcd2bin(cp[5]); + t->tm_year = bcd2bin(cp[6]) + 100; + + return 0; +} + +static int abracon_set_time(struct rtc_device *rtcdev, struct rtc_time *t) +{ + struct abracon *abracon = to_abracon_priv(rtcdev); + struct i2c_client *client = abracon->client; + u8 cp[8] = {}; + int ret; + + cp[0] = 8; + cp[1] = bin2bcd(t->tm_sec); + cp[2] = bin2bcd(t->tm_min); + cp[3] = bin2bcd(t->tm_hour); + cp[4] = bin2bcd(t->tm_mday); + cp[5] = bin2bcd(t->tm_wday); + cp[6] = bin2bcd(t->tm_mon); + cp[7] = bin2bcd(t->tm_year - 100); + + ret = i2c_master_send(client, cp, 8); + if (ret != 8) + return -EIO; + + return 0; +} + +static const struct rtc_class_ops ds13xx_rtc_ops = { + .read_time = abracon_get_time, + .set_time = abracon_set_time, +}; + +static int abracon_probe(struct device_d *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct abracon *abracon; + int ret; + + abracon = xzalloc(sizeof(*abracon)); + + abracon->client = client; + + abracon->rtc.ops = &ds13xx_rtc_ops; + abracon->rtc.dev = dev; + + ret = rtc_register(&abracon->rtc); + + return ret; +}; + +static struct platform_device_id abracon_id[] = { + { "ab-rtcmc-32.768khz-eoz9-s3", 0 }, + { } +}; + +static struct driver_d abracon_driver = { + .name = "rtc-abracon", + .probe = abracon_probe, + .id_table = abracon_id, +}; + +static int __init abracon_init(void) +{ + return i2c_driver_register(&abracon_driver); +} +device_initcall(abracon_init);
\ No newline at end of file diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index d78faa892d..e2d561b96f 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -17,6 +17,7 @@ #include <init.h> #include <driver.h> #include <xfuncs.h> +#include <malloc.h> #include <errno.h> #include <i2c/i2c.h> #include <rtc.h> @@ -31,7 +32,9 @@ */ enum ds_type { ds_1307, + ds_1337, ds_1338, + ds_1341, last_ds_type /* always last */ }; @@ -62,6 +65,28 @@ enum ds_type { # define DS1307_BIT_SQWE 0x10 # define DS1307_BIT_RS1 0x02 # define DS1307_BIT_RS0 0x01 +#define DS1337_REG_CONTROL 0x0e +# define DS1337_BIT_nEOSC 0x80 +# define DS1339_BIT_BBSQI 0x20 +# define DS3231_BIT_BBSQW 0x40 /* same as BBSQI */ +# define DS1337_BIT_RS2 0x10 +# define DS1337_BIT_RS1 0x08 +# define DS1337_BIT_INTCN 0x04 +# define DS1337_BIT_A2IE 0x02 +# define DS1337_BIT_A1IE 0x01 +#define DS1340_REG_CONTROL 0x07 +# define DS1340_BIT_OUT 0x80 +# define DS1340_BIT_FT 0x40 +# define DS1340_BIT_CALIB_SIGN 0x20 +# define DS1340_M_CALIBRATION 0x1f +#define DS1340_REG_FLAG 0x09 +# define DS1340_BIT_OSF 0x80 +#define DS1337_REG_STATUS 0x0f +# define DS1337_BIT_OSF 0x80 +# define DS1341_BIT_ECLK 0x04 +# define DS1337_BIT_A2I 0x02 +# define DS1337_BIT_A1I 0x01 + struct ds1307 { struct rtc_device rtc; @@ -78,7 +103,9 @@ struct ds1307 { static struct platform_device_id ds1307_id[] = { { "ds1307", ds_1307 }, + { "ds1337", ds_1337 }, { "ds1338", ds_1338 }, + { "ds1341", ds_1341 }, { "pt7c4338", ds_1307 }, { } }; @@ -224,6 +251,16 @@ static int ds1307_set_time(struct rtc_device *rtcdev, struct rtc_time *t) tmp = t->tm_year - 100; buf[DS1307_REG_YEAR] = bin2bcd(tmp); + switch (ds1307->type) { + case ds_1337: + case ds_1341: + buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY; + break; + default: + break; + } + + dev_dbg(dev, "%s: %7ph\n", "write", buf); result = ds1307->write_block_data(ds1307->client, @@ -263,6 +300,61 @@ static int ds1307_probe(struct device_d *dev) ds1307->read_block_data = ds1307_read_block_data; ds1307->write_block_data = ds1307_write_block_data; + + switch (ds1307->type) { + case ds_1337: + case ds_1341: + /* get registers that the "rtc" read below won't read... */ + tmp = ds1307->read_block_data(ds1307->client, + DS1337_REG_CONTROL, 2, buf); + + if (tmp != 2) { + dev_dbg(&client->dev, "read error %d\n", tmp); + err = -EIO; + goto exit; + } + + /* oscillator off? turn it on, so clock can tick. */ + if (ds1307->regs[0] & DS1337_BIT_nEOSC) + ds1307->regs[0] &= ~DS1337_BIT_nEOSC; + + + /* + Make sure no alarm interrupts or square wave signals + are produced by the chip while we are in + bootloader. We do this by configuring the RTC to + generate alarm interrupts (thus disabling square + wave generation), but disabling each individual + alarm interrupt source + */ + ds1307->regs[0] |= DS1337_BIT_INTCN; + ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); + + i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, + ds1307->regs[0]); + + /* + For the above to be true, DS1341 also has to have + ECLK bit set to 0 + */ + if (ds1307->type == ds_1341) { + ds1307->regs[1] &= DS1341_BIT_ECLK; + i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, + ds1307->regs[1]); + } + + + /* oscillator fault? clear flag, and warn */ + if (ds1307->regs[1] & DS1337_BIT_OSF) { + i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, + ds1307->regs[1] & ~DS1337_BIT_OSF); + dev_warn(&client->dev, "SET TIME!\n"); + } + + default: + break; + } + read_rtc: /* read RTC registers */ tmp = ds1307->read_block_data(client, ds1307->offset, 8, buf); @@ -331,6 +423,8 @@ read_rtc: err = rtc_register(&ds1307->rtc); exit: + if (err) + free(ds1307); return err; } diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index 1b23458a57..83d80455da 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -90,6 +90,8 @@ int rtc_valid_tm(struct rtc_time *tm) { if (tm->tm_year < 70 || ((unsigned)tm->tm_mon) >= 12 + || tm->tm_wday < 0 + || tm->tm_wday > 6 || tm->tm_mday < 1 || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) || ((unsigned)tm->tm_hour) >= 24 |