Signed-off-by: Antony Pavlov <antonynpavlov@xxxxxxxxx> --- drivers/rtc/Kconfig | 7 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-pcf85363.c | 195 +++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 drivers/rtc/rtc-pcf85363.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 9d2c6e614b..50f352f422 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -36,6 +36,13 @@ config RTC_DRV_DS1307 config RTC_DRV_ABRACON tristate "Abracon RTCs" +config RTC_DRV_PCF85363 + tristate "NXP PCF85363" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the PCF85363 RTC chip. + endif # I2C config RTC_DRV_IMXDI diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 1308beff38..163264a8c6 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_RTC_DRV_ABRACON) += rtc-abracon.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o +obj-$(CONFIG_RTC_DRV_PCF85363) += rtc-pcf85363.o diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c new file mode 100644 index 0000000000..3c4da5a536 --- /dev/null +++ b/drivers/rtc/rtc-pcf85363.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/rtc/rtc-pcf85363.c + * + * Driver for NXP PCF85363 real-time clock. + * + * Copyright (C) 2017 Eric Nelson + */ + +#include <common.h> +#include <driver.h> +#include <xfuncs.h> +#include <malloc.h> +#include <errno.h> +#include <i2c/i2c.h> +#include <regmap.h> +#include <rtc.h> +#include <linux/rtc.h> +#include <linux/bcd.h> + +/* + * Date/Time registers + */ +#define DT_100THS 0x00 +#define DT_SECS 0x01 +#define DT_MINUTES 0x02 +#define DT_HOURS 0x03 +#define DT_DAYS 0x04 +#define DT_WEEKDAYS 0x05 +#define DT_MONTHS 0x06 +#define DT_YEARS 0x07 + +/* + * control registers + */ +#define CTRL_STOP_EN 0x2e + +#define STOP_EN_STOP BIT(0) + +#define RESET_CPR 0xa4 + +struct pcf85363 { + struct rtc_device rtc; + struct regmap *regmap; + + struct i2c_client *client; +}; + + +static int pcf85363_i2c_reg_read(void *ctx, unsigned int reg, unsigned int *val) +{ + struct pcf85363 *pcf85363 = ctx; + u8 buf[1]; + int ret; + + ret = i2c_read_reg(pcf85363->client, reg, buf, 1); + *val = buf[0]; + + return ret == 1 ? 0 : ret; +} + +static int pcf85363_i2c_reg_write(void *ctx, unsigned int reg, unsigned int val) +{ + struct pcf85363 *pcf85363 = ctx; + u8 buf[] = { + val & 0xff, + }; + int ret; + + ret = i2c_write_reg(pcf85363->client, reg, buf, 1); + + return ret == 1 ? 0 : ret; +} + +static struct regmap_bus regmap_pcf85363_i2c_bus = { + .reg_write = pcf85363_i2c_reg_write, + .reg_read = pcf85363_i2c_reg_read, +}; + +static inline struct pcf85363 *to_pcf85363_priv(struct rtc_device *rtcdev) +{ + return container_of(rtcdev, struct pcf85363, rtc); +} + +static int pcf85363_rtc_read_time(struct rtc_device *rtcdev, struct rtc_time *tm) +{ + struct device_d *dev = rtcdev->dev; + struct pcf85363 *pcf85363 = to_pcf85363_priv(rtcdev); + unsigned char buf[DT_YEARS + 1]; + int ret, len = sizeof(buf); + + /* read the RTC date and time registers all at once */ + ret = regmap_bulk_read(pcf85363->regmap, DT_100THS, buf, len); + if (ret) { + dev_err(dev, "%s: error %d\n", __func__, ret); + return ret; + } + + tm->tm_year = bcd2bin(buf[DT_YEARS]); + /* adjust for 1900 base of rtc_time */ + tm->tm_year += 100; + + tm->tm_wday = buf[DT_WEEKDAYS] & 7; + buf[DT_SECS] &= 0x7F; + tm->tm_sec = bcd2bin(buf[DT_SECS]); + buf[DT_MINUTES] &= 0x7F; + tm->tm_min = bcd2bin(buf[DT_MINUTES]); + tm->tm_hour = bcd2bin(buf[DT_HOURS]); + tm->tm_mday = bcd2bin(buf[DT_DAYS]); + tm->tm_mon = bcd2bin(buf[DT_MONTHS]) - 1; + + return 0; +} + +static int pcf85363_rtc_set_time(struct rtc_device *rtcdev, struct rtc_time *tm) +{ + struct pcf85363 *pcf85363 = to_pcf85363_priv(rtcdev); + unsigned char tmp[11]; + unsigned char *buf = &tmp[2]; + int ret; + + tmp[0] = STOP_EN_STOP; + tmp[1] = RESET_CPR; + + buf[DT_100THS] = 0; + buf[DT_SECS] = bin2bcd(tm->tm_sec); + buf[DT_MINUTES] = bin2bcd(tm->tm_min); + buf[DT_HOURS] = bin2bcd(tm->tm_hour); + buf[DT_DAYS] = bin2bcd(tm->tm_mday); + buf[DT_WEEKDAYS] = tm->tm_wday; + buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1); + buf[DT_YEARS] = bin2bcd(tm->tm_year % 100); + + ret = regmap_bulk_write(pcf85363->regmap, CTRL_STOP_EN, + tmp, 2); + if (ret) + return ret; + + ret = regmap_bulk_write(pcf85363->regmap, DT_100THS, + buf, sizeof(tmp) - 2); + if (ret) + return ret; + + return regmap_write(pcf85363->regmap, CTRL_STOP_EN, 0); +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = pcf85363_rtc_read_time, + .set_time = pcf85363_rtc_set_time, +}; + +static const struct regmap_config pcf85363_regmap_i2c_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7f, +}; + + +static int pcf85363_probe(struct device_d *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pcf85363 *pcf85363; + int ret; + + pcf85363 = xzalloc(sizeof(struct pcf85363)); + + pcf85363->regmap = regmap_init(dev, ®map_pcf85363_i2c_bus, + pcf85363, &pcf85363_regmap_i2c_config); + + pcf85363->client = client; + i2c_set_clientdata(client, pcf85363); + + pcf85363->rtc.ops = &rtc_ops; + pcf85363->rtc.dev = dev; + + ret = rtc_register(&pcf85363->rtc); + + return ret; +} + +static struct platform_device_id dev_ids[] = { + { .name = "pcf85363" }, + { /* sentinel */ } +}; + +static struct driver_d pcf85363_driver = { + .name = "pcf85363", + .probe = pcf85363_probe, + .id_table = dev_ids, +}; +device_i2c_driver(pcf85363_driver); + +MODULE_AUTHOR("Eric Nelson"); +MODULE_DESCRIPTION("pcf85363 I2C RTC driver"); +MODULE_LICENSE("GPL"); -- 2.33.0 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox