From a9074e1e3d78298dd085c84299c8de27766f3884 Mon Sep 17 00:00:00 2001 From: Dianlong Li Date: Thu, 13 Dec 2018 18:13:48 +0800 Subject: dt-bindings: define vendor prefix for whwave, Inc. Introduce vendor prefix for whwave, Inc. for SD3078 rtc device. Signed-off-by: Dianlong Li Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 389508584f48..475f90687c05 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -430,6 +430,7 @@ vot Vision Optical Technology Co., Ltd. wd Western Digital Corp. wetek WeTek Electronics, limited. wexler Wexler +whwave Shenzhen whwave Electronics, Inc. wi2wi Wi2Wi, Inc. winbond Winbond Electronics corp. winstar Winstar Display Corp. -- cgit v1.2.3-59-g8ed1b From a1c1eae4690aacd3a7ce9df97b47194ac2bf44e2 Mon Sep 17 00:00:00 2001 From: Dianlong Li Date: Thu, 13 Dec 2018 18:13:49 +0800 Subject: dt-bindings: rtc: sd3078: add device tree documentation The devicetree documentation for the SD3078 device tree. Signed-off-by: Dianlong Li Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index 7c8da6926095..e46edab31403 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -62,3 +62,4 @@ ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC sii,s35390a 2-wire CMOS real-time clock +whwave,sd3078 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC -- cgit v1.2.3-59-g8ed1b From 1d67a232105b4dec2b10b2689f9cb89cc366d62f Mon Sep 17 00:00:00 2001 From: Dianlong Li Date: Thu, 13 Dec 2018 18:13:50 +0800 Subject: rtc: sd3078: new driver. The sd3078 is a combination RTC and SRAM device with I2C interface. Signed-off-by: Dianlong Li Signed-off-by: Alexandre Belloni --- MAINTAINERS | 6 ++ drivers/rtc/Kconfig | 9 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-sd3078.c | 232 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 drivers/rtc/rtc-sd3078.c diff --git a/MAINTAINERS b/MAINTAINERS index 32d444476a90..5b04149b2adf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16458,6 +16458,12 @@ L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-wcove.c +WHWAVE RTC DRIVER +M: Dianlong Li +L: linux-rtc@vger.kernel.org +S: Maintained +F: drivers/rtc/rtc-sd3078.c + WIIMOTE HID DRIVER M: David Herrmann L: linux-input@vger.kernel.org diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 225b0b8516f3..a24fe8b7b98a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -646,6 +646,15 @@ config RTC_DRV_S5M This driver can also be built as a module. If so, the module will be called rtc-s5m. +config RTC_DRV_SD3078 + tristate "ZXW Crystal SD3078" + help + If you say yes here you get support for the ZXW Crystal + SD3078 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-sd3078 + endif # I2C comment "SPI RTC drivers" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index df022d820bee..39eb6de4ad74 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -149,6 +149,7 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o +obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c new file mode 100644 index 000000000000..a70d7311d039 --- /dev/null +++ b/drivers/rtc/rtc-sd3078.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock (RTC) Driver for sd3078 + * Copyright (C) 2018 Zoro Li + */ + +#include +#include +#include +#include +#include +#include + +#define SD3078_REG_SC 0x00 +#define SD3078_REG_MN 0x01 +#define SD3078_REG_HR 0x02 +#define SD3078_REG_DW 0x03 +#define SD3078_REG_DM 0x04 +#define SD3078_REG_MO 0x05 +#define SD3078_REG_YR 0x06 + +#define SD3078_REG_CTRL1 0x0f +#define SD3078_REG_CTRL2 0x10 +#define SD3078_REG_CTRL3 0x11 + +#define KEY_WRITE1 0x80 +#define KEY_WRITE2 0x04 +#define KEY_WRITE3 0x80 + +#define NUM_TIME_REGS (SD3078_REG_YR - SD3078_REG_SC + 1) + +/* + * The sd3078 has write protection + * and we can choose whether or not to use it. + * Write protection is turned off by default. + */ +#define WRITE_PROTECT_EN 0 + +struct sd3078 { + struct rtc_device *rtc; + struct regmap *regmap; +}; + +/* + * In order to prevent arbitrary modification of the time register, + * when modification of the register, + * the "write" bit needs to be written in a certain order. + * 1. set WRITE1 bit + * 2. set WRITE2 bit + * 3. set WRITE3 bit + */ +static void sd3078_enable_reg_write(struct sd3078 *sd3078) +{ + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2, + KEY_WRITE1, KEY_WRITE1); + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, + KEY_WRITE2, KEY_WRITE2); + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, + KEY_WRITE3, KEY_WRITE3); +} + +#if WRITE_PROTECT_EN +/* + * In order to prevent arbitrary modification of the time register, + * we should disable the write function. + * when disable write, + * the "write" bit needs to be clear in a certain order. + * 1. clear WRITE2 bit + * 2. clear WRITE3 bit + * 3. clear WRITE1 bit + */ +static void sd3078_disable_reg_write(struct sd3078 *sd3078) +{ + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, + KEY_WRITE2, 0); + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, + KEY_WRITE3, 0); + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2, + KEY_WRITE1, 0); +} +#endif + +static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char hour; + unsigned char rtc_data[NUM_TIME_REGS] = {0}; + struct i2c_client *client = to_i2c_client(dev); + struct sd3078 *sd3078 = i2c_get_clientdata(client); + int ret; + + ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "reading from RTC failed with err:%d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F); + + /* + * The sd3078 supports 12/24 hour mode. + * When getting time, + * we need to convert the 12 hour mode to the 24 hour mode. + */ + hour = rtc_data[SD3078_REG_HR]; + if (hour & 0x80) /* 24H MODE */ + tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F); + else if (hour & 0x20) /* 12H MODE PM */ + tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12; + else /* 12H MODE AM */ + tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F); + + tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F); + tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1; + tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100; + + return 0; +} + +static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct i2c_client *client = to_i2c_client(dev); + struct sd3078 *sd3078 = i2c_get_clientdata(client); + int ret; + + rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec); + rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min); + rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80; + rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday); + rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07; + rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1; + rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100); + +#if WRITE_PROTECT_EN + sd3078_enable_reg_write(sd3078); +#endif + + ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "writing to RTC failed with err:%d\n", ret); + return ret; + } + +#if WRITE_PROTECT_EN + sd3078_disable_reg_write(sd3078); +#endif + + return 0; +} + +static const struct rtc_class_ops sd3078_rtc_ops = { + .read_time = sd3078_rtc_read_time, + .set_time = sd3078_rtc_set_time, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x11, +}; + +static int sd3078_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct sd3078 *sd3078; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL); + if (!sd3078) + return -ENOMEM; + + sd3078->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(sd3078->regmap)) { + dev_err(&client->dev, "regmap allocation failed\n"); + return PTR_ERR(sd3078->regmap); + } + + i2c_set_clientdata(client, sd3078); + + sd3078->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(sd3078->rtc)) + return PTR_ERR(sd3078->rtc); + + sd3078->rtc->ops = &sd3078_rtc_ops; + sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = rtc_register_device(sd3078->rtc); + if (ret) { + dev_err(&client->dev, "failed to register rtc device\n"); + return ret; + } + + sd3078_enable_reg_write(sd3078); + + return 0; +} + +static const struct i2c_device_id sd3078_id[] = { + {"sd3078", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, sd3078_id); + +static const struct of_device_id rtc_dt_match[] = { + { .compatible = "whwave,sd3078" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rtc_dt_match); + +struct i2c_driver sd3078_driver = { + .driver = { + .name = "sd3078", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rtc_dt_match), + }, + .probe = sd3078_probe, + .id_table = sd3078_id, +}; + +module_i2c_driver(sd3078_driver); + +MODULE_AUTHOR("Dianlong Li "); +MODULE_DESCRIPTION("SD3078 RTC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 7c617e0c5f37d2f681b3a568c3642115a32b7427 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 18 Dec 2018 22:52:12 +0100 Subject: rtc: pcf8523: Add rv8523 compatible The Microcrystal RV-8523 is compatible with the PCF8523. Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 1 + drivers/rtc/rtc-pcf8523.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index e46edab31403..b79a4794a928 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -49,6 +49,7 @@ isil,isl1208 Intersil ISL1208 Low Power RTC with Battery Backed SRAM isil,isl1218 Intersil ISL1218 Low Power RTC with Battery Backed SRAM isil,isl12022 Intersil ISL12022 Real-time Clock microcrystal,rv3029 Real Time Clock Module with I2C-Bus +microcrystal,rv8523 Real Time Clock nxp,pcf2127 Real-time clock nxp,pcf2129 Real-time clock nxp,pcf8523 Real-time Clock diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 3fcd2cbafc84..fb13933f4006 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -374,6 +374,7 @@ MODULE_DEVICE_TABLE(i2c, pcf8523_id); #ifdef CONFIG_OF static const struct of_device_id pcf8523_of_match[] = { { .compatible = "nxp,pcf8523" }, + { .compatible = "microcrystal,rv8523" }, { } }; MODULE_DEVICE_TABLE(of, pcf8523_of_match); -- cgit v1.2.3-59-g8ed1b From 074b01a51d058f4216b7ce541e96b778aa6af60d Mon Sep 17 00:00:00 2001 From: ZhangXiaoxu Date: Thu, 20 Dec 2018 17:36:56 +0800 Subject: rtc: Fix UBSAN overflow warning Users may call 'ioctl' and pass a very big value on 'tm->tm_year'. It can be overflowed in 'int' after add 1900. In function 'rtc_month_days' and 'mktime64', also treated it as an 'unsigned' parameter. UBSAN: Undefined behaviour in drivers/rtc/rtc-lib.c:103:59 signed integer overflow: 2147483647 + 1900 cannot be represented in type 'int' UBSAN: Undefined behaviour in drivers/rtc/rtc-lib.c:119:30 signed integer overflow: 2147483647 + 1900 cannot be represented in type 'int' So, covert it to 'unsigned' explicitly. Signed-off-by: ZhangXiaoxu Signed-off-by: Alexandre Belloni --- drivers/rtc/lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/lib.c b/drivers/rtc/lib.c index ef160da84220..9714cb3d1e29 100644 --- a/drivers/rtc/lib.c +++ b/drivers/rtc/lib.c @@ -100,7 +100,7 @@ int rtc_valid_tm(struct rtc_time *tm) if (tm->tm_year < 70 || ((unsigned)tm->tm_mon) >= 12 || tm->tm_mday < 1 - || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) + || tm->tm_mday > rtc_month_days(tm->tm_mon, ((unsigned)tm->tm_year + 1900)) || ((unsigned)tm->tm_hour) >= 24 || ((unsigned)tm->tm_min) >= 60 || ((unsigned)tm->tm_sec) >= 60) @@ -116,8 +116,8 @@ EXPORT_SYMBOL(rtc_valid_tm); */ time64_t rtc_tm_to_time64(struct rtc_time *tm) { - return mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + return mktime64(((unsigned)tm->tm_year + 1900), tm->tm_mon + 1, + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } EXPORT_SYMBOL(rtc_tm_to_time64); -- cgit v1.2.3-59-g8ed1b From c8c97a4fb52c526be7a78c4c1e61a95cf1c915cc Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Wed, 2 Jan 2019 16:00:17 +0000 Subject: rtc: isl1208: fix negative digital trim reporting isl1208_i2c_get_dtr() was returning the dtr value directly, but could also return a negative error code. Negative trimming values, e.g. -20, would get interpreted as an error code, e.g. -ENOTDIR. This patch offsets the dtr value by 100 so it's positive and won't alias an error code. Also fix check that considered a return value of -1 to be success. Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-isl1208.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 37ab3e1d25f5..263af3d8cd9f 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -161,6 +161,7 @@ isl1208_i2c_get_atr(struct i2c_client *client) return atr; } +/* returns adjustment value + 100 */ static int isl1208_i2c_get_dtr(struct i2c_client *client) { @@ -171,7 +172,7 @@ isl1208_i2c_get_dtr(struct i2c_client *client) /* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */ dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1); - return dtr; + return dtr + 100; } static int @@ -248,8 +249,8 @@ isl1208_rtc_proc(struct device *dev, struct seq_file *seq) (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay"); dtr = isl1208_i2c_get_dtr(client); - if (dtr >= 0 - 1) - seq_printf(seq, "digital_trim\t: %d ppm\n", dtr); + if (dtr >= 0) + seq_printf(seq, "digital_trim\t: %d ppm\n", dtr - 100); atr = isl1208_i2c_get_atr(client); if (atr >= 0) @@ -637,7 +638,7 @@ isl1208_sysfs_show_dtrim(struct device *dev, if (dtr < 0) return dtr; - return sprintf(buf, "%d ppm\n", dtr); + return sprintf(buf, "%d ppm\n", dtr - 100); } static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL); -- cgit v1.2.3-59-g8ed1b From f724c6bee162be3a9cd3b9168414ca106e0cdfe2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 16 Jan 2019 03:01:21 +0000 Subject: rtc: sd3078: make symbol 'sd3078_driver' static Fixes the following sparse warning: drivers/rtc/rtc-sd3078.c:218:19: warning: symbol 'sd3078_driver' was not declared. Should it be static? Fixes: 1d67a232105b ("rtc: sd3078: new driver.") Signed-off-by: Wei Yongjun Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-sd3078.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c index a70d7311d039..704844f32b45 100644 --- a/drivers/rtc/rtc-sd3078.c +++ b/drivers/rtc/rtc-sd3078.c @@ -215,7 +215,7 @@ static const struct of_device_id rtc_dt_match[] = { }; MODULE_DEVICE_TABLE(of, rtc_dt_match); -struct i2c_driver sd3078_driver = { +static struct i2c_driver sd3078_driver = { .driver = { .name = "sd3078", .owner = THIS_MODULE, -- cgit v1.2.3-59-g8ed1b From 5a5b614ba61cc2a89ad0dffc63d913a1a6ba1f9f Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 21 Jan 2019 12:09:30 +0100 Subject: rtc: s3c: Rewrite clock handling s3c_rtc_enable/disable_clk() functions were designed to be called multiple times without reference counting, because they were initially only used in alarm setting/clearing functions, which can be called both when alarm is already set or not. Later however, calls to those functions have been added to other places in the driver - like time and /proc reading callbacks, what results in broken alarm if any of such events happens after the alarm has been set. Fix this by simplifying s3c_rtc_enable/disable_clk() functions to rely on proper reference counting in clock core and move alarm enable counter to s3c_rtc_setaie() function. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 74 +++++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 04c68178c42d..ea82efba6f94 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -39,7 +39,7 @@ struct s3c_rtc { void __iomem *base; struct clk *rtc_clk; struct clk *rtc_src_clk; - bool clk_disabled; + bool alarm_enabled; const struct s3c_rtc_data *data; @@ -47,7 +47,7 @@ struct s3c_rtc { int irq_tick; spinlock_t pie_lock; - spinlock_t alarm_clk_lock; + spinlock_t alarm_lock; int ticnt_save; int ticnt_en_save; @@ -70,44 +70,27 @@ struct s3c_rtc_data { static int s3c_rtc_enable_clk(struct s3c_rtc *info) { - unsigned long irq_flags; - int ret = 0; + int ret; - spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); + ret = clk_enable(info->rtc_clk); + if (ret) + return ret; - if (info->clk_disabled) { - ret = clk_enable(info->rtc_clk); - if (ret) - goto out; - - if (info->data->needs_src_clk) { - ret = clk_enable(info->rtc_src_clk); - if (ret) { - clk_disable(info->rtc_clk); - goto out; - } + if (info->data->needs_src_clk) { + ret = clk_enable(info->rtc_src_clk); + if (ret) { + clk_disable(info->rtc_clk); + return ret; } - info->clk_disabled = false; } - -out: - spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); - - return ret; + return 0; } static void s3c_rtc_disable_clk(struct s3c_rtc *info) { - unsigned long irq_flags; - - spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); - if (!info->clk_disabled) { - if (info->data->needs_src_clk) - clk_disable(info->rtc_src_clk); - clk_disable(info->rtc_clk); - info->clk_disabled = true; - } - spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); } /* IRQ Handlers */ @@ -135,6 +118,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) { struct s3c_rtc *info = dev_get_drvdata(dev); + unsigned long flags; unsigned int tmp; int ret; @@ -151,17 +135,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) writeb(tmp, info->base + S3C2410_RTCALM); - s3c_rtc_disable_clk(info); + spin_lock_irqsave(&info->alarm_lock, flags); - if (enabled) { - ret = s3c_rtc_enable_clk(info); - if (ret) - return ret; - } else { + if (info->alarm_enabled && !enabled) s3c_rtc_disable_clk(info); - } + else if (!info->alarm_enabled && enabled) + ret = s3c_rtc_enable_clk(info); - return 0; + info->alarm_enabled = enabled; + spin_unlock_irqrestore(&info->alarm_lock, flags); + + s3c_rtc_disable_clk(info); + + return ret; } /* Set RTC frequency */ @@ -357,10 +343,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) writeb(alrm_en, info->base + S3C2410_RTCALM); - s3c_rtc_disable_clk(info); - s3c_rtc_setaie(dev, alrm->enabled); + s3c_rtc_disable_clk(info); + return 0; } @@ -491,7 +477,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) return -EINVAL; } spin_lock_init(&info->pie_lock); - spin_lock_init(&info->alarm_clk_lock); + spin_lock_init(&info->alarm_lock); platform_set_drvdata(pdev, info); @@ -591,6 +577,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) s3c_rtc_setfreq(info, 1); + s3c_rtc_disable_clk(info); + return 0; err_nortc: -- cgit v1.2.3-59-g8ed1b From 64704c92fd19c599f20433aae1372a7ccab79a57 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 18 Jan 2019 14:28:37 +0100 Subject: rtc: s3c: Use generic helper to get driver data Replace of_match_node() with of_device_get_match_data(), which removes a few lines of code from the driver. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index ea82efba6f94..e81a2b22a5c3 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -442,16 +443,6 @@ static int s3c_rtc_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id s3c_rtc_dt_match[]; - -static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) -{ - const struct of_device_id *match; - - match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); - return match->data; -} - static int s3c_rtc_probe(struct platform_device *pdev) { struct s3c_rtc *info = NULL; @@ -471,7 +462,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) } info->dev = &pdev->dev; - info->data = s3c_rtc_get_data(pdev); + info->data = of_device_get_match_data(&pdev->dev); if (!info->data) { dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 5eca99917cb73086908cdb262ca54dff1fce4f6e Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:26 +0100 Subject: devicetree: property-units: Add femtofarads unit When dealing with capacitance of 0.5 pF then a smaller unit is preferred. Add femtofarads to deal with this. Signed-off-by: Sam Ravnborg Reviewed-by: Rob Herring Cc: Mark Rutland Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/property-units.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/property-units.txt b/Documentation/devicetree/bindings/property-units.txt index 45ce054d844d..bfd33734faca 100644 --- a/Documentation/devicetree/bindings/property-units.txt +++ b/Documentation/devicetree/bindings/property-units.txt @@ -31,6 +31,7 @@ Electricity -microwatt-hours: micro Watt-hours -microvolt : micro volts -picofarads : picofarads +-femtofarads : femtofarads Temperature ---------------------------------------- -- cgit v1.2.3-59-g8ed1b From d08fd7c1b9bd78b10345d2f9b115c5fd362b75cd Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:27 +0100 Subject: dt-bindings: rtc: Add quartz-load-femtofarads property The quartz-load-femtofarads are relevant for several users. Add it as a common property in rtc. Note that valid values and default values must be documented. Signed-off-by: Sam Ravnborg Suggested-by: Rob Herring Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Rob Herring Cc: Mark Rutland Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index b79a4794a928..0cacbc0edd20 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -21,12 +21,16 @@ Optional properties The following properties may not be supported by all drivers. However, if a driver wants to support one of the below features, it should adapt the bindings below. -- trickle-resistor-ohms : Selected resistor for trickle charger. Should be given - if trickle charger should be enabled -- trickle-diode-disable : Do not use internal trickle charger diode Should be - given if internal trickle charger diode should be - disabled -- wakeup-source : Enables wake up of host system on alarm +- trickle-resistor-ohms : Selected resistor for trickle charger. Should be given + if trickle charger should be enabled +- trickle-diode-disable : Do not use internal trickle charger diode Should be + given if internal trickle charger diode should be + disabled +- wakeup-source : Enables wake up of host system on alarm +- quartz-load-femtofarads : The capacitive load of the quartz(x-tal), + expressed in femto Farad (fF). + The default value shall be listed (if optional), + and likewise all valid values. Trivial RTCs ------------ -- cgit v1.2.3-59-g8ed1b From 8c12af15e7340a14e2978814ef28df58962283d6 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:28 +0100 Subject: dt-binding: pcf8523: add xtal load capacitance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NXP pcf8523 supports two different xtal load capacitance - 7000fF (7pF) HW default - 12500fF (12.5pF) Minimum power consumption, driver default To obtain a precise RTC the pcf8523 must be configured with the correct capacitance load of the xtal. Add a property to specify the xtal capacitance load. The default value matches that of the current Linux driver. With a dedicated binding remove the entry in rtc.txt Signed-off-by: Søren Andersen Signed-off-by: Sam Ravnborg Reviewed-by: Rob Herring Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Mark Rutland Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt | 18 ++++++++++++++++++ Documentation/devicetree/bindings/rtc/rtc.txt | 1 - 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt b/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt new file mode 100644 index 000000000000..0b1080c60f63 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt @@ -0,0 +1,18 @@ +* NXP PCF8523 Real Time Clock + +Required properties: +- compatible: Should contain "nxp,pcf8523". +- reg: I2C address for chip. + +Optional property: +- quartz-load-femtofarads: The capacitive load of the quartz(x-tal), + expressed in femto Farad (fF). Valid values are 7000 and 12500. + Default value (if no value is specified) is 12500fF. + +Example: + +pcf8523: rtc@68 { + compatible = "nxp,pcf8523"; + reg = <0x68>; + quartz-load-femtofarads = <7000>; +}; diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index 0cacbc0edd20..27a7c1578f6e 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -56,7 +56,6 @@ microcrystal,rv3029 Real Time Clock Module with I2C-Bus microcrystal,rv8523 Real Time Clock nxp,pcf2127 Real-time clock nxp,pcf2129 Real-time clock -nxp,pcf8523 Real-time Clock nxp,pcf8563 Real-time clock/calendar nxp,pcf85063 Tiny Real-Time Clock pericom,pt7c4338 Real-time Clock Module -- cgit v1.2.3-59-g8ed1b From e4a604cbee5470fc116ac9a4f852152070984286 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:29 +0100 Subject: dt-binding: pcf85063: add xtal load capacitance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NXP pcf85063 supports two different xtal load capacitance - 7000fF (7pF) HW default, Linux driver default - 12500fF (12.5pF) Minimum power consumption To obtain a precise RTC the pcf85063 must be configured with the correct capacitance load of the xtal. Add a property to specify the xtal capacitance load. The default value matches that of the current Linux driver. With a dedicated binding remove the entry in rtc.txt Signed-off-by: Søren Andersen Signed-off-by: Sam Ravnborg Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Rob Herring Cc: Mark Rutland Cc: Urs Fässler Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt | 18 ++++++++++++++++++ Documentation/devicetree/bindings/rtc/rtc.txt | 1 - 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt new file mode 100644 index 000000000000..d3e380ad712d --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt @@ -0,0 +1,18 @@ +* NXP PCF85063 Real Time Clock + +Required properties: +- compatible: Should contain "nxp,pcf85063". +- reg: I2C address for chip. + +Optional property: +- quartz-load-femtofarads: The capacitive load of the quartz(x-tal), + expressed in femto Farad (fF). Valid values are 7000 and 12500. + Default value (if no value is specified) is 7000fF. + +Example: + +pcf85063: rtc@51 { + compatible = "nxp,pcf85063"; + reg = <0x51>; + quartz-load-femtofarads = <12500>; +}; diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index 27a7c1578f6e..d86e2850fe1c 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -57,7 +57,6 @@ microcrystal,rv8523 Real Time Clock nxp,pcf2127 Real-time clock nxp,pcf2129 Real-time clock nxp,pcf8563 Real-time clock/calendar -nxp,pcf85063 Tiny Real-Time Clock pericom,pt7c4338 Real-time Clock Module ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC -- cgit v1.2.3-59-g8ed1b From 189927e719e36ceefbb8037f23d3849e47833aef Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:30 +0100 Subject: rtc: pcf8523: set xtal load capacitance from DT Add support for specifying the xtal load capacitance in the DT node. The pcf8523 supports xtal load capacitance of 7pF or 12.5pF. If the rtc has the wrong configuration the time will drift several hours/week. The driver use the default value 12.5pF. The DT may specify either 7000fF or 12500fF. (The DT uses femto Farad to avoid decimal numbers). Other values are warned and the driver uses the default value. Signed-off-by: Sam Ravnborg Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pcf8523.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index fb13933f4006..b5c61a70b5df 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -97,8 +97,9 @@ static int pcf8523_voltage_low(struct i2c_client *client) return !!(value & REG_CONTROL3_BLF); } -static int pcf8523_select_capacitance(struct i2c_client *client, bool high) +static int pcf8523_load_capacitance(struct i2c_client *client) { + u32 load; u8 value; int err; @@ -106,14 +107,24 @@ static int pcf8523_select_capacitance(struct i2c_client *client, bool high) if (err < 0) return err; - if (!high) - value &= ~REG_CONTROL1_CAP_SEL; - else + load = 12500; + of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads", + &load); + + switch (load) { + default: + dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 12500", + load); + /* fall through */ + case 12500: value |= REG_CONTROL1_CAP_SEL; + break; + case 7000: + value &= ~REG_CONTROL1_CAP_SEL; + break; + } err = pcf8523_write(client, REG_CONTROL1, value); - if (err < 0) - return err; return err; } @@ -347,9 +358,10 @@ static int pcf8523_probe(struct i2c_client *client, if (!pcf) return -ENOMEM; - err = pcf8523_select_capacitance(client, true); + err = pcf8523_load_capacitance(client); if (err < 0) - return err; + dev_warn(&client->dev, "failed to set xtal load capacitance: %d", + err); err = pcf8523_set_pm(client, 0); if (err < 0) -- cgit v1.2.3-59-g8ed1b From bbb4383826b20cdc9efbd8ffdd19a5fa58320175 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:31 +0100 Subject: rtc: pcf85063: set xtal load capacitance from DT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for specifying the xtal load capacitance in the DT node. The pcf85063 supports xtal load capacitance of 7pF or 12.5pF. If the rtc has the wrong configuration the time will drift several hours/week. The driver use the default value 7pF. The DT may specify either 7000fF or 12500fF. (The DT uses femto Farad to avoid decimal numbers). Other values are warned and the driver uses the default value. Signed-off-by: Sam Ravnborg Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Urs Fässler Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pcf85063.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c index 283c2335b01b..b79af6e96292 100644 --- a/drivers/rtc/rtc-pcf85063.c +++ b/drivers/rtc/rtc-pcf85063.c @@ -27,6 +27,7 @@ */ #define PCF85063_REG_CTRL1 0x00 /* status */ +#define PCF85063_REG_CTRL1_CAP_SEL BIT(0) #define PCF85063_REG_CTRL1_STOP BIT(5) #define PCF85063_REG_CTRL2 0x01 @@ -180,6 +181,39 @@ static const struct rtc_class_ops pcf85063_rtc_ops = { .set_time = pcf85063_rtc_set_time }; +static int pcf85063_load_capacitance(struct i2c_client *client) +{ + u32 load; + int rc; + u8 reg; + + rc = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1); + if (rc < 0) + return rc; + + reg = rc; + load = 7000; + of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads", + &load); + + switch (load) { + default: + dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000", + load); + /* fall through */ + case 7000: + reg &= ~PCF85063_REG_CTRL1_CAP_SEL; + break; + case 12500: + reg |= PCF85063_REG_CTRL1_CAP_SEL; + break; + } + + rc = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, reg); + + return rc; +} + static int pcf85063_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -197,6 +231,11 @@ static int pcf85063_probe(struct i2c_client *client, return err; } + err = pcf85063_load_capacitance(client); + if (err < 0) + dev_warn(&client->dev, "failed to set xtal load capacitance: %d", + err); + rtc = devm_rtc_device_register(&client->dev, pcf85063_driver.driver.name, &pcf85063_rtc_ops, THIS_MODULE); -- cgit v1.2.3-59-g8ed1b From 5910fa0d0d9809c4d8fbf1fb905a8c86fbf2eccb Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 25 Dec 2018 20:43:33 -0600 Subject: rtc: coh901331: fix a missing check of clk_prepare clk_prepare() could fail, so let's check its status and if it fails return its error code upstream. Signed-off-by: Kangjie Lu Acked-by: Linus Walleij Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-coh901331.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c index fc5cf5c44ae7..0b232c84f674 100644 --- a/drivers/rtc/rtc-coh901331.c +++ b/drivers/rtc/rtc-coh901331.c @@ -235,9 +235,13 @@ static int coh901331_suspend(struct device *dev) static int coh901331_resume(struct device *dev) { + int ret; struct coh901331_port *rtap = dev_get_drvdata(dev); - clk_prepare(rtap->clk); + ret = clk_prepare(rtap->clk); + if (ret) + return ret; + if (device_may_wakeup(dev)) { disable_irq_wake(rtap->irq); } else { -- cgit v1.2.3-59-g8ed1b From 9a20b5e35a536d6bb4b2d4a3b14a0457e205356c Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 25 Dec 2018 21:09:11 -0600 Subject: rtc: hym8563: fix a missing check of block data read When i2c_smbus_read_i2c_block_data() fails, the read data in "buf" could be incorrect and should not be used. The fix checks if i2c_smbus_read_i2c_block_data fails, and if so, return its error code upstream. Signed-off-by: Kangjie Lu Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-hym8563.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index e5ad527cb75e..d03f5d212eea 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -109,6 +109,8 @@ static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm) } ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf); + if (ret < 0) + return ret; tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK); tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK); -- cgit v1.2.3-59-g8ed1b From 2a4daadd4d3e507138f8937926e6a4df49c6bfdc Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 6 Jan 2019 08:21:03 +0000 Subject: rtc: cmos: ignore bogus century byte Older versions of Libreboot and Coreboot had an invalid value (`3' in my case) in the century byte affecting the GM45 in the Thinkpad X200. Not everybody's updated their firmwares, and Linux <= 4.2 was able to read the RTC without problems, so workaround this by ignoring invalid values. Fixes: 3c217e51d8a272b9 ("rtc: cmos: century support") Cc: Alexandre Belloni Cc: Alessandro Zummo Cc: Sylvain Chouleur Cc: Patrick McDermott Cc: linux-rtc@vger.kernel.org Signed-off-by: Eric Wong Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mc146818-lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c index 2f1772a358ca..18a6f15e313d 100644 --- a/drivers/rtc/rtc-mc146818-lib.c +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -82,7 +82,7 @@ unsigned int mc146818_get_time(struct rtc_time *time) time->tm_year += real_year - 72; #endif - if (century) + if (century > 20) time->tm_year += (century - 19) * 100; /* -- cgit v1.2.3-59-g8ed1b From 1e47704e15f0907d2d8482ec939e3cb500bae558 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 22 Jan 2019 10:42:15 +0000 Subject: dt-bindings: rtc: Add bindings for Cadence RTC This patch adds a DT binding documentation for Cadence RTC controller. Signed-off-by: Jan Kotas Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/cdns,rtc.txt | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/cdns,rtc.txt diff --git a/Documentation/devicetree/bindings/rtc/cdns,rtc.txt b/Documentation/devicetree/bindings/rtc/cdns,rtc.txt new file mode 100644 index 000000000000..14a04487b432 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/cdns,rtc.txt @@ -0,0 +1,25 @@ +Cadence Real Time Clock + +The Cadence RTC controller with date, time and alarm capabilities. +The alarm may wake the system from low-power state. + +Required properties: +- compatible: Should be "cdns,rtc-r109v3" +- reg: Specifies base physical address and size of the register area. +- interrupts: A single interrupt specifier. +- clocks: Must contain two entries: + - pclk: APB registers clock + - ref_clk: reference 1Hz or 100Hz clock, depending on IP configuration + See ../clocks/clock-bindings.txt for details. + +Example: + rtc0: rtc@fd080000 { + compatible = "cdns,rtc-r109v3"; + reg = <0xfd080000 0x1000>; + + clock-names = "pclk", "ref_clk"; + clocks = <&sysclock>, <&refclock>; + + interrupt-parent = <&gic>; + interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>; + }; -- cgit v1.2.3-59-g8ed1b From 42986fb8119928d32e585c73181f39506937a3b2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 22 Jan 2019 10:42:16 +0000 Subject: rtc: Add Cadence RTC driver This patch adds a driver for Cadence RTC controller. It can be enabled with RTC_DRV_CADENCE Kconfig option. It supports waking system from sleep modes. Signed-off-by: Jan Kotas Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-cadence.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 434 insertions(+) create mode 100644 drivers/rtc/rtc-cadence.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a24fe8b7b98a..faa9ae1a3062 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1517,6 +1517,16 @@ config RTC_DRV_ARMADA38X This driver can also be built as a module. If so, the module will be called armada38x-rtc. +config RTC_DRV_CADENCE + tristate "Cadence RTC driver" + depends on OF && HAS_IOMEM + help + If you say Y here you will get access to Cadence RTC IP + found on certain SOCs. + + To compile this driver as a module, choose M here: the + module will be called rtc-cadence. + config RTC_DRV_FTRTC010 tristate "Faraday Technology FTRTC010 RTC" depends on HAS_IOMEM diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 39eb6de4ad74..faca02109aaa 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o +obj-$(CONFIG_RTC_DRV_CADENCE) += rtc-cadence.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o diff --git a/drivers/rtc/rtc-cadence.c b/drivers/rtc/rtc-cadence.c new file mode 100644 index 000000000000..3b7d643c8a63 --- /dev/null +++ b/drivers/rtc/rtc-cadence.c @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2019 Cadence + * + * Authors: + * Jan Kotas + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define CDNS_RTC_CTLR 0x00 +#define CDNS_RTC_HMR 0x04 +#define CDNS_RTC_TIMR 0x08 +#define CDNS_RTC_CALR 0x0C +#define CDNS_RTC_TIMAR 0x10 +#define CDNS_RTC_CALAR 0x14 +#define CDNS_RTC_AENR 0x18 +#define CDNS_RTC_EFLR 0x1C +#define CDNS_RTC_IENR 0x20 +#define CDNS_RTC_IDISR 0x24 +#define CDNS_RTC_IMSKR 0x28 +#define CDNS_RTC_STSR 0x2C +#define CDNS_RTC_KRTCR 0x30 + +/* Control */ +#define CDNS_RTC_CTLR_TIME BIT(0) +#define CDNS_RTC_CTLR_CAL BIT(1) +#define CDNS_RTC_CTLR_TIME_CAL (CDNS_RTC_CTLR_TIME | CDNS_RTC_CTLR_CAL) + +/* Status */ +#define CDNS_RTC_STSR_VT BIT(0) +#define CDNS_RTC_STSR_VC BIT(1) +#define CDNS_RTC_STSR_VTA BIT(2) +#define CDNS_RTC_STSR_VCA BIT(3) +#define CDNS_RTC_STSR_VT_VC (CDNS_RTC_STSR_VT | CDNS_RTC_STSR_VC) +#define CDNS_RTC_STSR_VTA_VCA (CDNS_RTC_STSR_VTA | CDNS_RTC_STSR_VCA) + +/* Keep RTC */ +#define CDNS_RTC_KRTCR_KRTC BIT(0) + +/* Alarm, Event, Interrupt */ +#define CDNS_RTC_AEI_HOS BIT(0) +#define CDNS_RTC_AEI_SEC BIT(1) +#define CDNS_RTC_AEI_MIN BIT(2) +#define CDNS_RTC_AEI_HOUR BIT(3) +#define CDNS_RTC_AEI_DATE BIT(4) +#define CDNS_RTC_AEI_MNTH BIT(5) +#define CDNS_RTC_AEI_ALRM BIT(6) + +/* Time */ +#define CDNS_RTC_TIME_H GENMASK(7, 0) +#define CDNS_RTC_TIME_S GENMASK(14, 8) +#define CDNS_RTC_TIME_M GENMASK(22, 16) +#define CDNS_RTC_TIME_HR GENMASK(29, 24) +#define CDNS_RTC_TIME_PM BIT(30) +#define CDNS_RTC_TIME_CH BIT(31) + +/* Calendar */ +#define CDNS_RTC_CAL_DAY GENMASK(2, 0) +#define CDNS_RTC_CAL_M GENMASK(7, 3) +#define CDNS_RTC_CAL_D GENMASK(13, 8) +#define CDNS_RTC_CAL_Y GENMASK(23, 16) +#define CDNS_RTC_CAL_C GENMASK(29, 24) +#define CDNS_RTC_CAL_CH BIT(31) + +#define CDNS_RTC_MAX_REGS_TRIES 3 + +struct cdns_rtc { + struct rtc_device *rtc_dev; + struct clk *pclk; + struct clk *ref_clk; + void __iomem *regs; + int irq; +}; + +static void cdns_rtc_set_enabled(struct cdns_rtc *crtc, bool enabled) +{ + u32 reg = enabled ? 0x0 : CDNS_RTC_CTLR_TIME_CAL; + + writel(reg, crtc->regs + CDNS_RTC_CTLR); +} + +static bool cdns_rtc_get_enabled(struct cdns_rtc *crtc) +{ + return !(readl(crtc->regs + CDNS_RTC_CTLR) & CDNS_RTC_CTLR_TIME_CAL); +} + +static irqreturn_t cdns_rtc_irq_handler(int irq, void *id) +{ + struct device *dev = id; + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + /* Reading the register clears it */ + if (!(readl(crtc->regs + CDNS_RTC_EFLR) & CDNS_RTC_AEI_ALRM)) + return IRQ_NONE; + + rtc_update_irq(crtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static u32 cdns_rtc_time2reg(struct rtc_time *tm) +{ + return FIELD_PREP(CDNS_RTC_TIME_S, bin2bcd(tm->tm_sec)) + | FIELD_PREP(CDNS_RTC_TIME_M, bin2bcd(tm->tm_min)) + | FIELD_PREP(CDNS_RTC_TIME_HR, bin2bcd(tm->tm_hour)); +} + +static void cdns_rtc_reg2time(u32 reg, struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(FIELD_GET(CDNS_RTC_TIME_S, reg)); + tm->tm_min = bcd2bin(FIELD_GET(CDNS_RTC_TIME_M, reg)); + tm->tm_hour = bcd2bin(FIELD_GET(CDNS_RTC_TIME_HR, reg)); +} + +static int cdns_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + u32 reg; + + /* If the RTC is disabled, assume the values are invalid */ + if (!cdns_rtc_get_enabled(crtc)) + return -EINVAL; + + cdns_rtc_set_enabled(crtc, false); + + reg = readl(crtc->regs + CDNS_RTC_TIMR); + cdns_rtc_reg2time(reg, tm); + + reg = readl(crtc->regs + CDNS_RTC_CALR); + tm->tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg)); + tm->tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1; + tm->tm_year = bcd2bin(FIELD_GET(CDNS_RTC_CAL_Y, reg)) + + bcd2bin(FIELD_GET(CDNS_RTC_CAL_C, reg)) * 100 - 1900; + tm->tm_wday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_DAY, reg)) - 1; + + cdns_rtc_set_enabled(crtc, true); + return 0; +} + +static int cdns_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + u32 timr, calr, stsr; + int ret = -EIO; + int year = tm->tm_year + 1900; + int tries; + + cdns_rtc_set_enabled(crtc, false); + + timr = cdns_rtc_time2reg(tm); + + calr = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(tm->tm_mday)) + | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(tm->tm_mon + 1)) + | FIELD_PREP(CDNS_RTC_CAL_Y, bin2bcd(year % 100)) + | FIELD_PREP(CDNS_RTC_CAL_C, bin2bcd(year / 100)) + | FIELD_PREP(CDNS_RTC_CAL_DAY, tm->tm_wday + 1); + + /* Update registers, check valid flags */ + for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) { + writel(timr, crtc->regs + CDNS_RTC_TIMR); + writel(calr, crtc->regs + CDNS_RTC_CALR); + stsr = readl(crtc->regs + CDNS_RTC_STSR); + + if ((stsr & CDNS_RTC_STSR_VT_VC) == CDNS_RTC_STSR_VT_VC) { + ret = 0; + break; + } + } + + cdns_rtc_set_enabled(crtc, true); + return ret; +} + +static int cdns_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + if (enabled) { + writel((CDNS_RTC_AEI_SEC | CDNS_RTC_AEI_MIN | CDNS_RTC_AEI_HOUR + | CDNS_RTC_AEI_DATE | CDNS_RTC_AEI_MNTH), + crtc->regs + CDNS_RTC_AENR); + writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IENR); + } else { + writel(0, crtc->regs + CDNS_RTC_AENR); + writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IDISR); + } + + return 0; +} + +static int cdns_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + u32 reg; + + reg = readl(crtc->regs + CDNS_RTC_TIMAR); + cdns_rtc_reg2time(reg, &alarm->time); + + reg = readl(crtc->regs + CDNS_RTC_CALAR); + alarm->time.tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg)); + alarm->time.tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1; + + return 0; +} + +static int cdns_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + int ret = -EIO; + int tries; + u32 timar, calar, stsr; + + cdns_rtc_alarm_irq_enable(dev, 0); + + timar = cdns_rtc_time2reg(&alarm->time); + calar = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(alarm->time.tm_mday)) + | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(alarm->time.tm_mon + 1)); + + /* Update registers, check valid alarm flags */ + for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) { + writel(timar, crtc->regs + CDNS_RTC_TIMAR); + writel(calar, crtc->regs + CDNS_RTC_CALAR); + stsr = readl(crtc->regs + CDNS_RTC_STSR); + + if ((stsr & CDNS_RTC_STSR_VTA_VCA) == CDNS_RTC_STSR_VTA_VCA) { + ret = 0; + break; + } + } + + if (!ret) + cdns_rtc_alarm_irq_enable(dev, alarm->enabled); + return ret; +} + +static const struct rtc_class_ops cdns_rtc_ops = { + .read_time = cdns_rtc_read_time, + .set_time = cdns_rtc_set_time, + .read_alarm = cdns_rtc_read_alarm, + .set_alarm = cdns_rtc_set_alarm, + .alarm_irq_enable = cdns_rtc_alarm_irq_enable, +}; + +static int cdns_rtc_probe(struct platform_device *pdev) +{ + struct cdns_rtc *crtc; + struct resource *res; + int ret; + unsigned long ref_clk_freq; + + crtc = devm_kzalloc(&pdev->dev, sizeof(*crtc), GFP_KERNEL); + if (!crtc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + crtc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(crtc->regs)) + return PTR_ERR(crtc->regs); + + crtc->irq = platform_get_irq(pdev, 0); + if (crtc->irq < 0) + return -EINVAL; + + crtc->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(crtc->pclk)) { + ret = PTR_ERR(crtc->pclk); + dev_err(&pdev->dev, + "Failed to retrieve the peripheral clock, %d\n", ret); + return ret; + } + + crtc->ref_clk = devm_clk_get(&pdev->dev, "ref_clk"); + if (IS_ERR(crtc->ref_clk)) { + ret = PTR_ERR(crtc->ref_clk); + dev_err(&pdev->dev, + "Failed to retrieve the reference clock, %d\n", ret); + return ret; + } + + crtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(crtc->rtc_dev)) { + ret = PTR_ERR(crtc->rtc_dev); + dev_err(&pdev->dev, + "Failed to allocate the RTC device, %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, crtc); + + ret = clk_prepare_enable(crtc->pclk); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable the peripheral clock, %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(crtc->ref_clk); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable the reference clock, %d\n", ret); + goto err_disable_pclk; + } + + ref_clk_freq = clk_get_rate(crtc->ref_clk); + if ((ref_clk_freq != 1) && (ref_clk_freq != 100)) { + dev_err(&pdev->dev, + "Invalid reference clock frequency %lu Hz.\n", + ref_clk_freq); + ret = -EINVAL; + goto err_disable_ref_clk; + } + + ret = devm_request_irq(&pdev->dev, crtc->irq, + cdns_rtc_irq_handler, 0, + dev_name(&pdev->dev), &pdev->dev); + if (ret) { + dev_err(&pdev->dev, + "Failed to request interrupt for the device, %d\n", + ret); + goto err_disable_ref_clk; + } + + /* The RTC supports 01.01.1900 - 31.12.2999 */ + crtc->rtc_dev->range_min = mktime64(1900, 1, 1, 0, 0, 0); + crtc->rtc_dev->range_max = mktime64(2999, 12, 31, 23, 59, 59); + + crtc->rtc_dev->ops = &cdns_rtc_ops; + device_init_wakeup(&pdev->dev, true); + + /* Always use 24-hour mode and keep the RTC values */ + writel(0, crtc->regs + CDNS_RTC_HMR); + writel(CDNS_RTC_KRTCR_KRTC, crtc->regs + CDNS_RTC_KRTCR); + + ret = rtc_register_device(crtc->rtc_dev); + if (ret) { + dev_err(&pdev->dev, + "Failed to register the RTC device, %d\n", ret); + goto err_disable_wakeup; + } + + return 0; + +err_disable_wakeup: + device_init_wakeup(&pdev->dev, false); + +err_disable_ref_clk: + clk_disable_unprepare(crtc->ref_clk); + +err_disable_pclk: + clk_disable_unprepare(crtc->pclk); + + return ret; +} + +static int cdns_rtc_remove(struct platform_device *pdev) +{ + struct cdns_rtc *crtc = platform_get_drvdata(pdev); + + cdns_rtc_alarm_irq_enable(&pdev->dev, 0); + device_init_wakeup(&pdev->dev, 0); + + clk_disable_unprepare(crtc->pclk); + clk_disable_unprepare(crtc->ref_clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cdns_rtc_suspend(struct device *dev) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(crtc->irq); + + return 0; +} + +static int cdns_rtc_resume(struct device *dev) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(crtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(cdns_rtc_pm_ops, cdns_rtc_suspend, cdns_rtc_resume); + +static const struct of_device_id cdns_rtc_of_match[] = { + { .compatible = "cdns,rtc-r109v3" }, + { }, +}; +MODULE_DEVICE_TABLE(of, cdns_rtc_of_match); + +static struct platform_driver cdns_rtc_driver = { + .driver = { + .name = "cdns-rtc", + .of_match_table = cdns_rtc_of_match, + .pm = &cdns_rtc_pm_ops, + }, + .probe = cdns_rtc_probe, + .remove = cdns_rtc_remove, +}; +module_platform_driver(cdns_rtc_driver); + +MODULE_AUTHOR("Jan Kotas "); +MODULE_DESCRIPTION("Cadence RTC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cdns-rtc"); -- cgit v1.2.3-59-g8ed1b From a3b8bcbafa45b6cdcbca6beb8684c862a5a2ca61 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 23 Jan 2019 12:44:32 +0000 Subject: rtc: sd3078: fix platform_no_drv_owner.cocci warnings Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: YueHaibing Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-sd3078.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c index 704844f32b45..42cb90db7f94 100644 --- a/drivers/rtc/rtc-sd3078.c +++ b/drivers/rtc/rtc-sd3078.c @@ -218,7 +218,6 @@ MODULE_DEVICE_TABLE(of, rtc_dt_match); static struct i2c_driver sd3078_driver = { .driver = { .name = "sd3078", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(rtc_dt_match), }, .probe = sd3078_probe, -- cgit v1.2.3-59-g8ed1b From a26ec8857da387303c6121a8aff7b7544a981690 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Jan 2019 12:39:50 +0100 Subject: dt-bindings: rtc: Add RV1805 to abracon,abx80x bindings Add compatible string for Microcrystal RV1805 RTC, which is compatible with AB1805. The RV1805 uses smaller 10 pin package, while the AB1805 uses larger 16pin package, however the die inside the chip is the same. Signed-off-by: Marek Vasut Cc: Alexandre Belloni Cc: Rob Herring To: devicetree@vger.kernel.org Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/abracon,abx80x.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/rtc/abracon,abx80x.txt b/Documentation/devicetree/bindings/rtc/abracon,abx80x.txt index 18b892d010d8..2405e35a1bc0 100644 --- a/Documentation/devicetree/bindings/rtc/abracon,abx80x.txt +++ b/Documentation/devicetree/bindings/rtc/abracon,abx80x.txt @@ -16,6 +16,7 @@ Required properties: "abracon,ab1803" "abracon,ab1804" "abracon,ab1805" + "microcrystal,rv1805" Using "abracon,abx80x" will enable chip autodetection. - "reg": I2C bus address of the device -- cgit v1.2.3-59-g8ed1b From 75455e258ea2b3ea08f08edc41735a447c3e7711 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Jan 2019 12:40:28 +0100 Subject: rtc: abx80x: Configure reserved bits in RV1805 The RV1805 uses smaller package than the AB1805, discern those two chips based on the compatible value and configure reserved bits in the RV1805 to prevent current leakage and accidental test mode entry. Signed-off-by: Marek Vasut Cc: Alexandre Belloni To: linux-rtc@vger.kernel.org Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-abx80x.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 4d24f7288ad7..b76f65983658 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -46,6 +46,9 @@ #define ABX8XX_CTRL_ARST BIT(2) #define ABX8XX_CTRL_12_24 BIT(6) +#define ABX8XX_REG_CTRL2 0x11 +#define ABX8XX_CTRL2_RSVD BIT(5) + #define ABX8XX_REG_IRQ 0x12 #define ABX8XX_IRQ_AIE BIT(2) #define ABX8XX_IRQ_IM_1_4 (0x3 << 5) @@ -78,6 +81,9 @@ #define ABX8XX_REG_ID0 0x28 +#define ABX8XX_REG_OUT_CTRL 0x30 +#define ABX8XX_OUT_CTRL_EXDS BIT(4) + #define ABX8XX_REG_TRICKLE 0x20 #define ABX8XX_TRICKLE_CHARGE_ENABLE 0xa0 #define ABX8XX_TRICKLE_STANDARD_DIODE 0x8 @@ -86,7 +92,7 @@ static u8 trickle_resistors[] = {0, 3, 6, 11}; enum abx80x_chip {AB0801, AB0803, AB0804, AB0805, - AB1801, AB1803, AB1804, AB1805, ABX80X}; + AB1801, AB1803, AB1804, AB1805, RV1805, ABX80X}; struct abx80x_cap { u16 pn; @@ -103,6 +109,7 @@ static struct abx80x_cap abx80x_caps[] = { [AB1803] = {.pn = 0x1803}, [AB1804] = {.pn = 0x1804, .has_tc = true, .has_wdog = true}, [AB1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true}, + [RV1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true}, [ABX80X] = {.pn = 0} }; @@ -723,6 +730,62 @@ static int abx80x_probe(struct i2c_client *client, return -EIO; } + /* Configure RV1805 specifics */ + if (part == RV1805) { + /* + * Avoid accidentally entering test mode. This can happen + * on the RV1805 in case the reserved bit 5 in control2 + * register is set. RV-1805-C3 datasheet indicates that + * the bit should be cleared in section 11h - Control2. + */ + data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL2); + if (data < 0) { + dev_err(&client->dev, + "Unable to read control2 register\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL2, + data & ~ABX8XX_CTRL2_RSVD); + if (err < 0) { + dev_err(&client->dev, + "Unable to write control2 register\n"); + return -EIO; + } + + /* + * Avoid extra power leakage. The RV1805 uses smaller + * 10pin package and the EXTI input is not present. + * Disable it to avoid leakage. + */ + data = i2c_smbus_read_byte_data(client, ABX8XX_REG_OUT_CTRL); + if (data < 0) { + dev_err(&client->dev, + "Unable to read output control register\n"); + return -EIO; + } + + /* + * Write the configuration key register to enable access to + * the config2 register + */ + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY, + ABX8XX_CFG_KEY_MISC); + if (err < 0) { + dev_err(&client->dev, + "Unable to write configuration key\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OUT_CTRL, + data | ABX8XX_OUT_CTRL_EXDS); + if (err < 0) { + dev_err(&client->dev, + "Unable to write output control register\n"); + return -EIO; + } + } + /* part autodetection */ if (part == ABX80X) { for (i = 0; abx80x_caps[i].pn; i++) @@ -826,7 +889,7 @@ static const struct i2c_device_id abx80x_id[] = { { "ab1803", AB1803 }, { "ab1804", AB1804 }, { "ab1805", AB1805 }, - { "rv1805", AB1805 }, + { "rv1805", RV1805 }, { } }; MODULE_DEVICE_TABLE(i2c, abx80x_id); -- cgit v1.2.3-59-g8ed1b From f0c04c276739ed8acbb41b4868e942a55b128dca Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 5 Feb 2019 18:04:49 +0000 Subject: rtc: ds1672: fix unintended sign extension Shifting a u8 by 24 will cause the value to be promoted to an integer. If the top bit of the u8 is set then the following conversion to an unsigned long will sign extend the value causing the upper 32 bits to be set in the result. Fix this by casting the u8 value to an unsigned long before the shift. Detected by CoverityScan, CID#138801 ("Unintended sign extension") Fixes: edf1aaa31fc5 ("[PATCH] RTC subsystem: DS1672 driver") Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1672.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 9caaccccaa57..b1ebca099b0d 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -58,7 +58,8 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) "%s: raw read data - counters=%02x,%02x,%02x,%02x\n", __func__, buf[0], buf[1], buf[2], buf[3]); - time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + time = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; rtc_time_to_tm(time, tm); -- cgit v1.2.3-59-g8ed1b From edb190cb173416b9d7f59b3887ecc43f6d94a9ad Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 11 Jan 2019 07:09:02 +0000 Subject: rtc: snvs: make sure clock is enabled for interrupt handle During system suspend, the SNVS RTC's clock will be disabled in noirq suspend phase, but SNVS RTC's alarm interrupt could still arrive, system will hang if SNVS RTC driver tries to access register without clock enabled, this patch fixes the issue of this scenario. Signed-off-by: Anson Huang Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-snvs.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index b2483a749ac4..0b9eff19149b 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -239,6 +239,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) u32 lpsr; u32 events = 0; + if (data->clk) + clk_enable(data->clk); + regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr); if (lpsr & SNVS_LPSR_LPTA) { @@ -253,6 +256,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) /* clear interrupt status */ regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr); + if (data->clk) + clk_disable(data->clk); + return events ? IRQ_HANDLED : IRQ_NONE; } -- cgit v1.2.3-59-g8ed1b From 761acdda5c2cc89208a94941f69579923420c8e9 Mon Sep 17 00:00:00 2001 From: "Oliver.Rohe@wago.com" Date: Wed, 9 Jan 2019 10:59:40 +0000 Subject: rtc: rs5c372: r2221: fix to use the correct XSTP bit The Ricoh chips have slightly different register layouts and the r2221 chip uses bit 5 as the oscillator halt sensor bit. Signed-off-by: Olive Rohe Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rs5c372.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index c5038329058c..ff35dcef338b 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -52,8 +52,8 @@ # define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */ #define RS5C_REG_CTRL2 15 # define RS5C372_CTRL2_24 (1 << 5) -# define R2025_CTRL2_XST (1 << 5) -# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2025S/D */ +# define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */ +# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2x2x */ # define RS5C_CTRL2_CTFG (1 << 2) # define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */ # define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */ @@ -519,20 +519,30 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) unsigned char buf[2]; int addr, i, ret = 0; - if (rs5c372->type == rtc_r2025sd) { - if (rs5c372->regs[RS5C_REG_CTRL2] & R2025_CTRL2_XST) + addr = RS5C_ADDR(RS5C_REG_CTRL1); + buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; + buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; + + /* handle xstp bit */ + switch (rs5c372->type) { + case rtc_r2025sd: + if (buf[1] & R2x2x_CTRL2_XSTP) return ret; - rs5c372->regs[RS5C_REG_CTRL2] |= R2025_CTRL2_XST; - } else { - if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP)) + rs5c372->regs[RS5C_REG_CTRL2] |= R2x2x_CTRL2_XSTP; + break; + case rtc_r2221tl: + if (!(buf[1] & R2x2x_CTRL2_XSTP)) + return ret; + rs5c372->regs[RS5C_REG_CTRL2] &= ~R2x2x_CTRL2_XSTP; + break; + + default: + if (!(buf[1] & RS5C_CTRL2_XSTP)) return ret; rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; + break; } - addr = RS5C_ADDR(RS5C_REG_CTRL1); - buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; - buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; - /* use 24hr mode */ switch (rs5c372->type) { case rtc_rs5c372a: -- cgit v1.2.3-59-g8ed1b From 13a929f38ad742200270389cedc78c1859726bd8 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 22 Jan 2019 03:29:24 +0000 Subject: rtc: imx-sc: add rtc set time support This patch adds i.MX system controller RTC set time support, the RTC set time is implemented via SIP(silicon provider) runtime service call and ARM-Trusted-Firmware will communicate with system controller via MU(message unit) IPC to set RTC time. Signed-off-by: Anson Huang Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-imx-sc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/rtc/rtc-imx-sc.c b/drivers/rtc/rtc-imx-sc.c index 7ff08544532a..60570a278bb0 100644 --- a/drivers/rtc/rtc-imx-sc.c +++ b/drivers/rtc/rtc-imx-sc.c @@ -3,6 +3,7 @@ * Copyright 2018 NXP. */ +#include #include #include #include @@ -12,6 +13,9 @@ #define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 9 #define IMX_SC_TIMER_FUNC_SET_RTC_TIME 6 +#define IMX_SIP_SRTC 0xC2000002 +#define IMX_SIP_SRTC_SET_TIME 0x0 + static struct imx_sc_ipc *rtc_ipc_handle; static struct rtc_device *imx_sc_rtc; @@ -42,8 +46,23 @@ static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm) return 0; } +static int imx_sc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct arm_smccc_res res; + + /* pack 2 time parameters into 1 register, 16 bits for each */ + arm_smccc_smc(IMX_SIP_SRTC, IMX_SIP_SRTC_SET_TIME, + ((tm->tm_year + 1900) << 16) | (tm->tm_mon + 1), + (tm->tm_mday << 16) | tm->tm_hour, + (tm->tm_min << 16) | tm->tm_sec, + 0, 0, 0, &res); + + return res.a0; +} + static const struct rtc_class_ops imx_sc_rtc_ops = { .read_time = imx_sc_rtc_read_time, + .set_time = imx_sc_rtc_set_time, }; static int imx_sc_rtc_probe(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 92cbf12fc200595611511fb3c5ab17d2627fa43c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 25 Jan 2019 15:35:54 +0100 Subject: rtc: ds1307: Move register definitions to start of file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to use the register offsets in all functions which is needed in one of the next patches. Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 52 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 74b31dce484f..f7a47ee5cdf6 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -114,6 +114,32 @@ enum ds_type { # define RX8025_BIT_VDET 0x40 # define RX8025_BIT_XST 0x20 +#define RX8130_REG_ALARM_MIN 0x07 +#define RX8130_REG_ALARM_HOUR 0x08 +#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 +#define RX8130_REG_EXTENSION 0x0c +#define RX8130_REG_EXTENSION_WADA BIT(3) +#define RX8130_REG_FLAG 0x0d +#define RX8130_REG_FLAG_AF BIT(3) +#define RX8130_REG_CONTROL0 0x0e +#define RX8130_REG_CONTROL0_AIE BIT(3) + +#define MCP794XX_REG_CONTROL 0x07 +# define MCP794XX_BIT_ALM0_EN 0x10 +# define MCP794XX_BIT_ALM1_EN 0x20 +#define MCP794XX_REG_ALARM0_BASE 0x0a +#define MCP794XX_REG_ALARM0_CTRL 0x0d +#define MCP794XX_REG_ALARM1_BASE 0x11 +#define MCP794XX_REG_ALARM1_CTRL 0x14 +# define MCP794XX_BIT_ALMX_IF BIT(3) +# define MCP794XX_BIT_ALMX_C0 BIT(4) +# define MCP794XX_BIT_ALMX_C1 BIT(5) +# define MCP794XX_BIT_ALMX_C2 BIT(6) +# define MCP794XX_BIT_ALMX_POL BIT(7) +# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \ + MCP794XX_BIT_ALMX_C1 | \ + MCP794XX_BIT_ALMX_C2) + #define M41TXX_REG_CONTROL 0x07 # define M41TXX_BIT_OUT BIT(7) # define M41TXX_BIT_FT BIT(6) @@ -680,16 +706,6 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { * Alarm support for rx8130 devices. */ -#define RX8130_REG_ALARM_MIN 0x07 -#define RX8130_REG_ALARM_HOUR 0x08 -#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 -#define RX8130_REG_EXTENSION 0x0c -#define RX8130_REG_EXTENSION_WADA BIT(3) -#define RX8130_REG_FLAG 0x0d -#define RX8130_REG_FLAG_AF BIT(3) -#define RX8130_REG_CONTROL0 0x0e -#define RX8130_REG_CONTROL0_AIE BIT(3) - static irqreturn_t rx8130_irq(int irq, void *dev_id) { struct ds1307 *ds1307 = dev_id; @@ -839,22 +855,6 @@ static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) * Alarm support for mcp794xx devices. */ -#define MCP794XX_REG_CONTROL 0x07 -# define MCP794XX_BIT_ALM0_EN 0x10 -# define MCP794XX_BIT_ALM1_EN 0x20 -#define MCP794XX_REG_ALARM0_BASE 0x0a -#define MCP794XX_REG_ALARM0_CTRL 0x0d -#define MCP794XX_REG_ALARM1_BASE 0x11 -#define MCP794XX_REG_ALARM1_CTRL 0x14 -# define MCP794XX_BIT_ALMX_IF BIT(3) -# define MCP794XX_BIT_ALMX_C0 BIT(4) -# define MCP794XX_BIT_ALMX_C1 BIT(5) -# define MCP794XX_BIT_ALMX_C2 BIT(6) -# define MCP794XX_BIT_ALMX_POL BIT(7) -# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \ - MCP794XX_BIT_ALMX_C1 | \ - MCP794XX_BIT_ALMX_C2) - static irqreturn_t mcp794xx_irq(int irq, void *dev_id) { struct ds1307 *ds1307 = dev_id; -- cgit v1.2.3-59-g8ed1b From d0e3f61b08e222d3c6895085487cbb8b54448abd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 25 Jan 2019 15:35:55 +0100 Subject: rtc: ds1307: forward declare chips array instead of a bunch of functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There used to be 16 declarations for static functions. By just adding a declaration for the chips array and reordering the functions the 16 function declarations can be dropped. Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 992 +++++++++++++++++++++++------------------------ 1 file changed, 482 insertions(+), 510 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index f7a47ee5cdf6..a4883d59d88d 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -184,289 +184,7 @@ struct chip_desc { bool); }; -static int ds1307_get_time(struct device *dev, struct rtc_time *t); -static int ds1307_set_time(struct device *dev, struct rtc_time *t); -static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t); -static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t); -static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled); -static u8 do_trickle_setup_ds1339(struct ds1307 *, u32 ohms, bool diode); -static irqreturn_t rx8130_irq(int irq, void *dev_id); -static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t); -static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t); -static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled); -static irqreturn_t mcp794xx_irq(int irq, void *dev_id); -static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t); -static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t); -static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled); -static int m41txx_rtc_read_offset(struct device *dev, long *offset); -static int m41txx_rtc_set_offset(struct device *dev, long offset); - -static const struct rtc_class_ops rx8130_rtc_ops = { - .read_time = ds1307_get_time, - .set_time = ds1307_set_time, - .read_alarm = rx8130_read_alarm, - .set_alarm = rx8130_set_alarm, - .alarm_irq_enable = rx8130_alarm_irq_enable, -}; - -static const struct rtc_class_ops mcp794xx_rtc_ops = { - .read_time = ds1307_get_time, - .set_time = ds1307_set_time, - .read_alarm = mcp794xx_read_alarm, - .set_alarm = mcp794xx_set_alarm, - .alarm_irq_enable = mcp794xx_alarm_irq_enable, -}; - -static const struct rtc_class_ops m41txx_rtc_ops = { - .read_time = ds1307_get_time, - .set_time = ds1307_set_time, - .read_alarm = ds1337_read_alarm, - .set_alarm = ds1337_set_alarm, - .alarm_irq_enable = ds1307_alarm_irq_enable, - .read_offset = m41txx_rtc_read_offset, - .set_offset = m41txx_rtc_set_offset, -}; - -static const struct chip_desc chips[last_ds_type] = { - [ds_1307] = { - .nvram_offset = 8, - .nvram_size = 56, - }, - [ds_1308] = { - .nvram_offset = 8, - .nvram_size = 56, - }, - [ds_1337] = { - .alarm = 1, - .century_reg = DS1307_REG_MONTH, - .century_bit = DS1337_BIT_CENTURY, - }, - [ds_1338] = { - .nvram_offset = 8, - .nvram_size = 56, - }, - [ds_1339] = { - .alarm = 1, - .century_reg = DS1307_REG_MONTH, - .century_bit = DS1337_BIT_CENTURY, - .bbsqi_bit = DS1339_BIT_BBSQI, - .trickle_charger_reg = 0x10, - .do_trickle_setup = &do_trickle_setup_ds1339, - }, - [ds_1340] = { - .century_reg = DS1307_REG_HOUR, - .century_enable_bit = DS1340_BIT_CENTURY_EN, - .century_bit = DS1340_BIT_CENTURY, - .do_trickle_setup = &do_trickle_setup_ds1339, - .trickle_charger_reg = 0x08, - }, - [ds_1341] = { - .century_reg = DS1307_REG_MONTH, - .century_bit = DS1337_BIT_CENTURY, - }, - [ds_1388] = { - .offset = 1, - .trickle_charger_reg = 0x0a, - }, - [ds_3231] = { - .alarm = 1, - .century_reg = DS1307_REG_MONTH, - .century_bit = DS1337_BIT_CENTURY, - .bbsqi_bit = DS3231_BIT_BBSQW, - }, - [rx_8130] = { - .alarm = 1, - /* this is battery backed SRAM */ - .nvram_offset = 0x20, - .nvram_size = 4, /* 32bit (4 word x 8 bit) */ - .offset = 0x10, - .irq_handler = rx8130_irq, - .rtc_ops = &rx8130_rtc_ops, - }, - [m41t0] = { - .rtc_ops = &m41txx_rtc_ops, - }, - [m41t00] = { - .rtc_ops = &m41txx_rtc_ops, - }, - [m41t11] = { - /* this is battery backed SRAM */ - .nvram_offset = 8, - .nvram_size = 56, - .rtc_ops = &m41txx_rtc_ops, - }, - [mcp794xx] = { - .alarm = 1, - /* this is battery backed SRAM */ - .nvram_offset = 0x20, - .nvram_size = 0x40, - .irq_handler = mcp794xx_irq, - .rtc_ops = &mcp794xx_rtc_ops, - }, -}; - -static const struct i2c_device_id ds1307_id[] = { - { "ds1307", ds_1307 }, - { "ds1308", ds_1308 }, - { "ds1337", ds_1337 }, - { "ds1338", ds_1338 }, - { "ds1339", ds_1339 }, - { "ds1388", ds_1388 }, - { "ds1340", ds_1340 }, - { "ds1341", ds_1341 }, - { "ds3231", ds_3231 }, - { "m41t0", m41t0 }, - { "m41t00", m41t00 }, - { "m41t11", m41t11 }, - { "mcp7940x", mcp794xx }, - { "mcp7941x", mcp794xx }, - { "pt7c4338", ds_1307 }, - { "rx8025", rx_8025 }, - { "isl12057", ds_1337 }, - { "rx8130", rx_8130 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ds1307_id); - -#ifdef CONFIG_OF -static const struct of_device_id ds1307_of_match[] = { - { - .compatible = "dallas,ds1307", - .data = (void *)ds_1307 - }, - { - .compatible = "dallas,ds1308", - .data = (void *)ds_1308 - }, - { - .compatible = "dallas,ds1337", - .data = (void *)ds_1337 - }, - { - .compatible = "dallas,ds1338", - .data = (void *)ds_1338 - }, - { - .compatible = "dallas,ds1339", - .data = (void *)ds_1339 - }, - { - .compatible = "dallas,ds1388", - .data = (void *)ds_1388 - }, - { - .compatible = "dallas,ds1340", - .data = (void *)ds_1340 - }, - { - .compatible = "dallas,ds1341", - .data = (void *)ds_1341 - }, - { - .compatible = "maxim,ds3231", - .data = (void *)ds_3231 - }, - { - .compatible = "st,m41t0", - .data = (void *)m41t0 - }, - { - .compatible = "st,m41t00", - .data = (void *)m41t00 - }, - { - .compatible = "st,m41t11", - .data = (void *)m41t11 - }, - { - .compatible = "microchip,mcp7940x", - .data = (void *)mcp794xx - }, - { - .compatible = "microchip,mcp7941x", - .data = (void *)mcp794xx - }, - { - .compatible = "pericom,pt7c4338", - .data = (void *)ds_1307 - }, - { - .compatible = "epson,rx8025", - .data = (void *)rx_8025 - }, - { - .compatible = "isil,isl12057", - .data = (void *)ds_1337 - }, - { - .compatible = "epson,rx8130", - .data = (void *)rx_8130 - }, - { } -}; -MODULE_DEVICE_TABLE(of, ds1307_of_match); -#endif - -#ifdef CONFIG_ACPI -static const struct acpi_device_id ds1307_acpi_ids[] = { - { .id = "DS1307", .driver_data = ds_1307 }, - { .id = "DS1308", .driver_data = ds_1308 }, - { .id = "DS1337", .driver_data = ds_1337 }, - { .id = "DS1338", .driver_data = ds_1338 }, - { .id = "DS1339", .driver_data = ds_1339 }, - { .id = "DS1388", .driver_data = ds_1388 }, - { .id = "DS1340", .driver_data = ds_1340 }, - { .id = "DS1341", .driver_data = ds_1341 }, - { .id = "DS3231", .driver_data = ds_3231 }, - { .id = "M41T0", .driver_data = m41t0 }, - { .id = "M41T00", .driver_data = m41t00 }, - { .id = "M41T11", .driver_data = m41t11 }, - { .id = "MCP7940X", .driver_data = mcp794xx }, - { .id = "MCP7941X", .driver_data = mcp794xx }, - { .id = "PT7C4338", .driver_data = ds_1307 }, - { .id = "RX8025", .driver_data = rx_8025 }, - { .id = "ISL12057", .driver_data = ds_1337 }, - { .id = "RX8130", .driver_data = rx_8130 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids); -#endif - -/* - * The ds1337 and ds1339 both have two alarms, but we only use the first - * one (with a "seconds" field). For ds1337 we expect nINTA is our alarm - * signal; ds1339 chips have only one alarm signal. - */ -static irqreturn_t ds1307_irq(int irq, void *dev_id) -{ - struct ds1307 *ds1307 = dev_id; - struct mutex *lock = &ds1307->rtc->ops_lock; - int stat, ret; - - mutex_lock(lock); - ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat); - if (ret) - goto out; - - if (stat & DS1337_BIT_A1I) { - stat &= ~DS1337_BIT_A1I; - regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat); - - ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, - DS1337_BIT_A1IE, 0); - if (ret) - goto out; - - rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); - } - -out: - mutex_unlock(lock); - - return IRQ_HANDLED; -} - -/*----------------------------------------------------------------------*/ +static const struct chip_desc chips[last_ds_type]; static int ds1307_get_time(struct device *dev, struct rtc_time *t) { @@ -692,19 +410,28 @@ static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled) enabled ? DS1337_BIT_A1IE : 0); } -static const struct rtc_class_ops ds13xx_rtc_ops = { - .read_time = ds1307_get_time, - .set_time = ds1307_set_time, - .read_alarm = ds1337_read_alarm, - .set_alarm = ds1337_set_alarm, - .alarm_irq_enable = ds1307_alarm_irq_enable, -}; - -/*----------------------------------------------------------------------*/ +static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, u32 ohms, bool diode) +{ + u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE : + DS1307_TRICKLE_CHARGER_NO_DIODE; -/* - * Alarm support for rx8130 devices. - */ + switch (ohms) { + case 250: + setup |= DS1307_TRICKLE_CHARGER_250_OHM; + break; + case 2000: + setup |= DS1307_TRICKLE_CHARGER_2K_OHM; + break; + case 4000: + setup |= DS1307_TRICKLE_CHARGER_4K_OHM; + break; + default: + dev_warn(ds1307->dev, + "Unsupported ohm value %u in dt\n", ohms); + return 0; + } + return setup; +} static irqreturn_t rx8130_irq(int irq, void *dev_id) { @@ -829,58 +556,479 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) sizeof(ctl)); } -static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) +static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int ret, reg; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®); + if (ret < 0) + return ret; + + if (enabled) + reg |= RX8130_REG_CONTROL0_AIE; + else + reg &= ~RX8130_REG_CONTROL0_AIE; + + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg); +} + +static irqreturn_t mcp794xx_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + int reg, ret; + + mutex_lock(lock); + + /* Check and clear alarm 0 interrupt flag. */ + ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, ®); + if (ret) + goto out; + if (!(reg & MCP794XX_BIT_ALMX_IF)) + goto out; + reg &= ~MCP794XX_BIT_ALMX_IF; + ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg); + if (ret) + goto out; + + /* Disable alarm 0. */ + ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, 0); + if (ret) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 regs[10]; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* Read control and alarm 0 registers. */ + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN); + + /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ + t->time.tm_sec = bcd2bin(regs[3] & 0x7f); + t->time.tm_min = bcd2bin(regs[4] & 0x7f); + t->time.tm_hour = bcd2bin(regs[5] & 0x3f); + t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1; + t->time.tm_mday = bcd2bin(regs[7] & 0x3f); + t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1; + t->time.tm_year = -1; + t->time.tm_yday = -1; + t->time.tm_isdst = -1; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d polarity=%d irq=%d match=%lu\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled, + !!(regs[6] & MCP794XX_BIT_ALMX_POL), + !!(regs[6] & MCP794XX_BIT_ALMX_IF), + (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); + + return 0; +} + +/* + * We may have a random RTC weekday, therefore calculate alarm weekday based + * on current weekday we read from the RTC timekeeping regs + */ +static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm) +{ + struct rtc_time tm_now; + int days_now, days_alarm, ret; + + ret = ds1307_get_time(dev, &tm_now); + if (ret) + return ret; + + days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60); + days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60); + + return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1; +} + +static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned char regs[10]; + int wday, ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + wday = mcp794xx_alm_weekday(dev, &t->time); + if (wday < 0) + return wday; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d pending=%d\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, + t->enabled, t->pending); + + /* Read control and alarm 0 registers. */ + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + /* Set alarm 0, using 24-hour and day-of-month modes. */ + regs[3] = bin2bcd(t->time.tm_sec); + regs[4] = bin2bcd(t->time.tm_min); + regs[5] = bin2bcd(t->time.tm_hour); + regs[6] = wday; + regs[7] = bin2bcd(t->time.tm_mday); + regs[8] = bin2bcd(t->time.tm_mon + 1); + + /* Clear the alarm 0 interrupt flag. */ + regs[6] &= ~MCP794XX_BIT_ALMX_IF; + /* Set alarm match: second, minute, hour, day, date, month. */ + regs[6] |= MCP794XX_MSK_ALMX_MATCH; + /* Disable interrupt. We will not enable until completely programmed */ + regs[0] &= ~MCP794XX_BIT_ALM0_EN; + + ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + if (!t->enabled) + return 0; + regs[0] |= MCP794XX_BIT_ALM0_EN; + return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]); +} + +static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - int ret, reg; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; - ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®); - if (ret < 0) - return ret; + return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, + enabled ? MCP794XX_BIT_ALM0_EN : 0); +} - if (enabled) - reg |= RX8130_REG_CONTROL0_AIE; +static int m41txx_rtc_read_offset(struct device *dev, long *offset) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned int ctrl_reg; + u8 val; + + regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg); + + val = ctrl_reg & M41TXX_M_CALIBRATION; + + /* check if positive */ + if (ctrl_reg & M41TXX_BIT_CALIB_SIGN) + *offset = (val * M41TXX_POS_OFFSET_STEP_PPB); else - reg &= ~RX8130_REG_CONTROL0_AIE; + *offset = -(val * M41TXX_NEG_OFFSET_STEP_PPB); - return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg); + return 0; +} + +static int m41txx_rtc_set_offset(struct device *dev, long offset) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned int ctrl_reg; + + if ((offset < M41TXX_MIN_OFFSET) || (offset > M41TXX_MAX_OFFSET)) + return -ERANGE; + + if (offset >= 0) { + ctrl_reg = DIV_ROUND_CLOSEST(offset, + M41TXX_POS_OFFSET_STEP_PPB); + ctrl_reg |= M41TXX_BIT_CALIB_SIGN; + } else { + ctrl_reg = DIV_ROUND_CLOSEST(abs(offset), + M41TXX_NEG_OFFSET_STEP_PPB); + } + + return regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL, + M41TXX_M_CALIBRATION | M41TXX_BIT_CALIB_SIGN, + ctrl_reg); } -/*----------------------------------------------------------------------*/ +static const struct rtc_class_ops rx8130_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = rx8130_read_alarm, + .set_alarm = rx8130_set_alarm, + .alarm_irq_enable = rx8130_alarm_irq_enable, +}; + +static const struct rtc_class_ops mcp794xx_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = mcp794xx_read_alarm, + .set_alarm = mcp794xx_set_alarm, + .alarm_irq_enable = mcp794xx_alarm_irq_enable, +}; + +static const struct rtc_class_ops m41txx_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = ds1337_read_alarm, + .set_alarm = ds1337_set_alarm, + .alarm_irq_enable = ds1307_alarm_irq_enable, + .read_offset = m41txx_rtc_read_offset, + .set_offset = m41txx_rtc_set_offset, +}; + +static const struct chip_desc chips[last_ds_type] = { + [ds_1307] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1308] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1337] = { + .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + }, + [ds_1338] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1339] = { + .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS1339_BIT_BBSQI, + .trickle_charger_reg = 0x10, + .do_trickle_setup = &do_trickle_setup_ds1339, + }, + [ds_1340] = { + .century_reg = DS1307_REG_HOUR, + .century_enable_bit = DS1340_BIT_CENTURY_EN, + .century_bit = DS1340_BIT_CENTURY, + .do_trickle_setup = &do_trickle_setup_ds1339, + .trickle_charger_reg = 0x08, + }, + [ds_1341] = { + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + }, + [ds_1388] = { + .offset = 1, + .trickle_charger_reg = 0x0a, + }, + [ds_3231] = { + .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS3231_BIT_BBSQW, + }, + [rx_8130] = { + .alarm = 1, + /* this is battery backed SRAM */ + .nvram_offset = 0x20, + .nvram_size = 4, /* 32bit (4 word x 8 bit) */ + .offset = 0x10, + .irq_handler = rx8130_irq, + .rtc_ops = &rx8130_rtc_ops, + }, + [m41t0] = { + .rtc_ops = &m41txx_rtc_ops, + }, + [m41t00] = { + .rtc_ops = &m41txx_rtc_ops, + }, + [m41t11] = { + /* this is battery backed SRAM */ + .nvram_offset = 8, + .nvram_size = 56, + .rtc_ops = &m41txx_rtc_ops, + }, + [mcp794xx] = { + .alarm = 1, + /* this is battery backed SRAM */ + .nvram_offset = 0x20, + .nvram_size = 0x40, + .irq_handler = mcp794xx_irq, + .rtc_ops = &mcp794xx_rtc_ops, + }, +}; + +static const struct i2c_device_id ds1307_id[] = { + { "ds1307", ds_1307 }, + { "ds1308", ds_1308 }, + { "ds1337", ds_1337 }, + { "ds1338", ds_1338 }, + { "ds1339", ds_1339 }, + { "ds1388", ds_1388 }, + { "ds1340", ds_1340 }, + { "ds1341", ds_1341 }, + { "ds3231", ds_3231 }, + { "m41t0", m41t0 }, + { "m41t00", m41t00 }, + { "m41t11", m41t11 }, + { "mcp7940x", mcp794xx }, + { "mcp7941x", mcp794xx }, + { "pt7c4338", ds_1307 }, + { "rx8025", rx_8025 }, + { "isl12057", ds_1337 }, + { "rx8130", rx_8130 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds1307_id); + +#ifdef CONFIG_OF +static const struct of_device_id ds1307_of_match[] = { + { + .compatible = "dallas,ds1307", + .data = (void *)ds_1307 + }, + { + .compatible = "dallas,ds1308", + .data = (void *)ds_1308 + }, + { + .compatible = "dallas,ds1337", + .data = (void *)ds_1337 + }, + { + .compatible = "dallas,ds1338", + .data = (void *)ds_1338 + }, + { + .compatible = "dallas,ds1339", + .data = (void *)ds_1339 + }, + { + .compatible = "dallas,ds1388", + .data = (void *)ds_1388 + }, + { + .compatible = "dallas,ds1340", + .data = (void *)ds_1340 + }, + { + .compatible = "dallas,ds1341", + .data = (void *)ds_1341 + }, + { + .compatible = "maxim,ds3231", + .data = (void *)ds_3231 + }, + { + .compatible = "st,m41t0", + .data = (void *)m41t0 + }, + { + .compatible = "st,m41t00", + .data = (void *)m41t00 + }, + { + .compatible = "st,m41t11", + .data = (void *)m41t11 + }, + { + .compatible = "microchip,mcp7940x", + .data = (void *)mcp794xx + }, + { + .compatible = "microchip,mcp7941x", + .data = (void *)mcp794xx + }, + { + .compatible = "pericom,pt7c4338", + .data = (void *)ds_1307 + }, + { + .compatible = "epson,rx8025", + .data = (void *)rx_8025 + }, + { + .compatible = "isil,isl12057", + .data = (void *)ds_1337 + }, + { + .compatible = "epson,rx8130", + .data = (void *)rx_8130 + }, + { } +}; +MODULE_DEVICE_TABLE(of, ds1307_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ds1307_acpi_ids[] = { + { .id = "DS1307", .driver_data = ds_1307 }, + { .id = "DS1308", .driver_data = ds_1308 }, + { .id = "DS1337", .driver_data = ds_1337 }, + { .id = "DS1338", .driver_data = ds_1338 }, + { .id = "DS1339", .driver_data = ds_1339 }, + { .id = "DS1388", .driver_data = ds_1388 }, + { .id = "DS1340", .driver_data = ds_1340 }, + { .id = "DS1341", .driver_data = ds_1341 }, + { .id = "DS3231", .driver_data = ds_3231 }, + { .id = "M41T0", .driver_data = m41t0 }, + { .id = "M41T00", .driver_data = m41t00 }, + { .id = "M41T11", .driver_data = m41t11 }, + { .id = "MCP7940X", .driver_data = mcp794xx }, + { .id = "MCP7941X", .driver_data = mcp794xx }, + { .id = "PT7C4338", .driver_data = ds_1307 }, + { .id = "RX8025", .driver_data = rx_8025 }, + { .id = "ISL12057", .driver_data = ds_1337 }, + { .id = "RX8130", .driver_data = rx_8130 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids); +#endif /* - * Alarm support for mcp794xx devices. + * The ds1337 and ds1339 both have two alarms, but we only use the first + * one (with a "seconds" field). For ds1337 we expect nINTA is our alarm + * signal; ds1339 chips have only one alarm signal. */ - -static irqreturn_t mcp794xx_irq(int irq, void *dev_id) +static irqreturn_t ds1307_irq(int irq, void *dev_id) { - struct ds1307 *ds1307 = dev_id; - struct mutex *lock = &ds1307->rtc->ops_lock; - int reg, ret; + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + int stat, ret; mutex_lock(lock); - - /* Check and clear alarm 0 interrupt flag. */ - ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, ®); - if (ret) - goto out; - if (!(reg & MCP794XX_BIT_ALMX_IF)) - goto out; - reg &= ~MCP794XX_BIT_ALMX_IF; - ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg); + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat); if (ret) goto out; - /* Disable alarm 0. */ - ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, - MCP794XX_BIT_ALM0_EN, 0); - if (ret) - goto out; + if (stat & DS1337_BIT_A1I) { + stat &= ~DS1337_BIT_A1I; + regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat); - rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + DS1337_BIT_A1IE, 0); + if (ret) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + } out: mutex_unlock(lock); @@ -888,167 +1036,15 @@ out: return IRQ_HANDLED; } -static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - u8 regs[10]; - int ret; - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - /* Read control and alarm 0 registers. */ - ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, - sizeof(regs)); - if (ret) - return ret; - - t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN); - - /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ - t->time.tm_sec = bcd2bin(regs[3] & 0x7f); - t->time.tm_min = bcd2bin(regs[4] & 0x7f); - t->time.tm_hour = bcd2bin(regs[5] & 0x3f); - t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1; - t->time.tm_mday = bcd2bin(regs[7] & 0x3f); - t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1; - t->time.tm_year = -1; - t->time.tm_yday = -1; - t->time.tm_isdst = -1; - - dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " - "enabled=%d polarity=%d irq=%d match=%lu\n", __func__, - t->time.tm_sec, t->time.tm_min, t->time.tm_hour, - t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled, - !!(regs[6] & MCP794XX_BIT_ALMX_POL), - !!(regs[6] & MCP794XX_BIT_ALMX_IF), - (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); - - return 0; -} - -/* - * We may have a random RTC weekday, therefore calculate alarm weekday based - * on current weekday we read from the RTC timekeeping regs - */ -static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm) -{ - struct rtc_time tm_now; - int days_now, days_alarm, ret; - - ret = ds1307_get_time(dev, &tm_now); - if (ret) - return ret; - - days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60); - days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60); - - return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1; -} - -static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned char regs[10]; - int wday, ret; - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - wday = mcp794xx_alm_weekday(dev, &t->time); - if (wday < 0) - return wday; - - dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " - "enabled=%d pending=%d\n", __func__, - t->time.tm_sec, t->time.tm_min, t->time.tm_hour, - t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, - t->enabled, t->pending); - - /* Read control and alarm 0 registers. */ - ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, - sizeof(regs)); - if (ret) - return ret; - - /* Set alarm 0, using 24-hour and day-of-month modes. */ - regs[3] = bin2bcd(t->time.tm_sec); - regs[4] = bin2bcd(t->time.tm_min); - regs[5] = bin2bcd(t->time.tm_hour); - regs[6] = wday; - regs[7] = bin2bcd(t->time.tm_mday); - regs[8] = bin2bcd(t->time.tm_mon + 1); - - /* Clear the alarm 0 interrupt flag. */ - regs[6] &= ~MCP794XX_BIT_ALMX_IF; - /* Set alarm match: second, minute, hour, day, date, month. */ - regs[6] |= MCP794XX_MSK_ALMX_MATCH; - /* Disable interrupt. We will not enable until completely programmed */ - regs[0] &= ~MCP794XX_BIT_ALM0_EN; - - ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, - sizeof(regs)); - if (ret) - return ret; - - if (!t->enabled) - return 0; - regs[0] |= MCP794XX_BIT_ALM0_EN; - return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]); -} - -static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, - MCP794XX_BIT_ALM0_EN, - enabled ? MCP794XX_BIT_ALM0_EN : 0); -} - -static int m41txx_rtc_read_offset(struct device *dev, long *offset) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned int ctrl_reg; - u8 val; - - regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg); - - val = ctrl_reg & M41TXX_M_CALIBRATION; - - /* check if positive */ - if (ctrl_reg & M41TXX_BIT_CALIB_SIGN) - *offset = (val * M41TXX_POS_OFFSET_STEP_PPB); - else - *offset = -(val * M41TXX_NEG_OFFSET_STEP_PPB); - - return 0; -} - -static int m41txx_rtc_set_offset(struct device *dev, long offset) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned int ctrl_reg; - - if ((offset < M41TXX_MIN_OFFSET) || (offset > M41TXX_MAX_OFFSET)) - return -ERANGE; - - if (offset >= 0) { - ctrl_reg = DIV_ROUND_CLOSEST(offset, - M41TXX_POS_OFFSET_STEP_PPB); - ctrl_reg |= M41TXX_BIT_CALIB_SIGN; - } else { - ctrl_reg = DIV_ROUND_CLOSEST(abs(offset), - M41TXX_NEG_OFFSET_STEP_PPB); - } +/*----------------------------------------------------------------------*/ - return regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL, - M41TXX_M_CALIBRATION | M41TXX_BIT_CALIB_SIGN, - ctrl_reg); -} +static const struct rtc_class_ops ds13xx_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = ds1337_read_alarm, + .set_alarm = ds1337_set_alarm, + .alarm_irq_enable = ds1307_alarm_irq_enable, +}; static ssize_t frequency_test_store(struct device *dev, struct device_attribute *attr, @@ -1137,30 +1133,6 @@ static int ds1307_nvram_write(void *priv, unsigned int offset, void *val, /*----------------------------------------------------------------------*/ -static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, - u32 ohms, bool diode) -{ - u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE : - DS1307_TRICKLE_CHARGER_NO_DIODE; - - switch (ohms) { - case 250: - setup |= DS1307_TRICKLE_CHARGER_250_OHM; - break; - case 2000: - setup |= DS1307_TRICKLE_CHARGER_2K_OHM; - break; - case 4000: - setup |= DS1307_TRICKLE_CHARGER_4K_OHM; - break; - default: - dev_warn(ds1307->dev, - "Unsupported ohm value %u in dt\n", ohms); - return 0; - } - return setup; -} - static u8 ds1307_trickle_init(struct ds1307 *ds1307, const struct chip_desc *chip) { -- cgit v1.2.3-59-g8ed1b From 3ffd4a2fcf52c1170654c68459fff40773a729ea Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 25 Jan 2019 15:35:56 +0100 Subject: rtc: ds1307: correct register offset for rx8130 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While rx8130 has a register offset of 0x10 in its chip_desc, this isn't used when regmap accesses are done. So add 0x10 to access the right locations. Fixes: ee0981be7704 ("rtc: ds1307: Add support for Epson RX8130CE") Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index a4883d59d88d..81e20653700d 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -114,14 +114,15 @@ enum ds_type { # define RX8025_BIT_VDET 0x40 # define RX8025_BIT_XST 0x20 -#define RX8130_REG_ALARM_MIN 0x07 -#define RX8130_REG_ALARM_HOUR 0x08 -#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 -#define RX8130_REG_EXTENSION 0x0c +#define RX8130_REG_ALARM_MIN 0x17 +#define RX8130_REG_ALARM_HOUR 0x18 +#define RX8130_REG_ALARM_WEEK_OR_DAY 0x19 +#define RX8130_REG_EXTENSION 0x1c #define RX8130_REG_EXTENSION_WADA BIT(3) -#define RX8130_REG_FLAG 0x0d +#define RX8130_REG_FLAG 0x1d +#define RX8130_REG_FLAG_VLF BIT(1) #define RX8130_REG_FLAG_AF BIT(3) -#define RX8130_REG_CONTROL0 0x0e +#define RX8130_REG_CONTROL0 0x1e #define RX8130_REG_CONTROL0_AIE BIT(3) #define MCP794XX_REG_CONTROL 0x07 -- cgit v1.2.3-59-g8ed1b From 501f98266caddd2b76cdf10093105aa2318d1421 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 25 Jan 2019 15:35:57 +0100 Subject: rtc: ds1307: rx8130: honor Voltage Loss Flag when reading the time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When voltage dropped since the RTC was last set the reported time is not reliable. In this case return an error indicator instead of a bogus time. Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 81e20653700d..974eafda9de4 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -194,6 +194,20 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) const struct chip_desc *chip = &chips[ds1307->type]; u8 regs[7]; + if (ds1307->type == rx_8130) { + unsigned int regflag; + ret = regmap_read(ds1307->regmap, RX8130_REG_FLAG, ®flag); + if (ret) { + dev_err(dev, "%s error %d\n", "read", ret); + return ret; + } + + if (regflag & RX8130_REG_FLAG_VLF) { + dev_warn_once(dev, "oscillator failed, set time!\n"); + return -EINVAL; + } + } + /* read the RTC date and time registers all at once */ ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs, sizeof(regs)); @@ -293,6 +307,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) dev_err(dev, "%s error %d\n", "write", result); return result; } + + if (ds1307->type == rx_8130) { + /* clear Voltage Loss Flag as data is available now */ + result = regmap_write(ds1307->regmap, RX8130_REG_FLAG, + ~(u8)RX8130_REG_FLAG_VLF); + if (result) { + dev_err(dev, "%s error %d\n", "write", result); + return result; + } + } + return 0; } -- cgit v1.2.3-59-g8ed1b From 3f929cad943380370b6db31fcb7a38d898d91089 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 25 Jan 2019 15:35:58 +0100 Subject: rtc: ds1307: rx8130: Fix alarm handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the EXTENSION.WADA bit is set, register 0x19 contains a bitmap of week days, not a day of month. As Linux only handles a single alarm without repetition using day of month is more flexible, so clear this bit. (Otherwise a value depending on time.tm_wday would have to be written to register 0x19.) Also optimize setting the AIE bit to use a single register write instead of a bulk write of three registers. Fixes: ee0981be7704 ("rtc: ds1307: Add support for Epson RX8130CE") Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 974eafda9de4..07530fe1da2a 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -554,8 +554,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) if (ret < 0) return ret; - ctl[0] &= ~RX8130_REG_EXTENSION_WADA; - ctl[1] |= RX8130_REG_FLAG_AF; + ctl[0] &= RX8130_REG_EXTENSION_WADA; + ctl[1] &= ~RX8130_REG_FLAG_AF; ctl[2] &= ~RX8130_REG_CONTROL0_AIE; ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, @@ -578,8 +578,7 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) ctl[2] |= RX8130_REG_CONTROL0_AIE; - return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, - sizeof(ctl)); + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, ctl[2]); } static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) -- cgit v1.2.3-59-g8ed1b From dc9e47160626cdb58d5c39a4f43dcfdb27a5c004 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 6 Feb 2019 09:50:53 +0000 Subject: rtc: 88pm860x: fix unintended sign extension Shifting a u8 by 24 will cause the value to be promoted to an integer. If the top bit of the u8 is set then the following conversion to an unsigned long will sign extend the value causing the upper 32 bits to be set in the result. Fix this by casting the u8 value to an unsigned long before the shift. Detected by CoverityScan, CID#144925-144928 ("Unintended sign extension") Fixes: 008b30408c40 ("mfd: Add rtc support to 88pm860x") Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-88pm860x.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index 01ffc0ef8033..d25282b4a7dd 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -115,11 +115,13 @@ static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm) pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; /* load 32-bit read-only counter */ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -145,7 +147,8 @@ static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm) /* load 32-bit read-only counter */ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; base = ticks - data; dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -170,10 +173,12 @@ static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -198,11 +203,13 @@ static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; /* load 32-bit read-only counter */ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); -- cgit v1.2.3-59-g8ed1b From fb0b322537a831b5b0cb948c56f8f958ce493d3a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 6 Feb 2019 10:08:11 +0000 Subject: rtc: 88pm80x: fix unintended sign extension Shifting a u8 by 24 will cause the value to be promoted to an integer. If the top bit of the u8 is set then the following conversion to an unsigned long will sign extend the value causing the upper 32 bits to be set in the result. Fix this by casting the u8 value to an unsigned long before the shift. Detected by CoverityScan, CID#714646-714649 ("Unintended sign extension") Fixes: 2985c29c1964 ("rtc: Add rtc support to 88PM80X PMIC") Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-88pm80x.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c index cab293cb2bf0..1fc48ebd3cd0 100644 --- a/drivers/rtc/rtc-88pm80x.c +++ b/drivers/rtc/rtc-88pm80x.c @@ -114,12 +114,14 @@ static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm) unsigned char buf[4]; unsigned long ticks, base, data; regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); - base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); /* load 32-bit read-only counter */ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -137,7 +139,8 @@ static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm) /* load 32-bit read-only counter */ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; base = ticks - data; dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -158,11 +161,13 @@ static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) int ret; regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); - base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -185,12 +190,14 @@ static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0); regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); - base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); /* load 32-bit read-only counter */ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); -- cgit v1.2.3-59-g8ed1b From e42280886018c6f77f0a90190f7cba344b0df3e0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 6 Feb 2019 10:31:02 +0000 Subject: rtc: pm8xxx: fix unintended sign extension Shifting a u8 by 24 will cause the value to be promoted to an integer. If the top bit of the u8 is set then the following conversion to an unsigned long will sign extend the value causing the upper 32 bits to be set in the result. Fix this by casting the u8 value to an unsigned long before the shift. Detected by CoverityScan, CID#1309693 ("Unintended sign extension") Fixes: 9a9a54ad7aa2 ("drivers/rtc: add support for Qualcomm PMIC8xxx RTC") Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pm8xxx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index 1074e3dbfc1d..cda020700744 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -213,7 +213,8 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) } } - secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + secs = value[0] | (value[1] << 8) | (value[2] << 16) | + ((unsigned long)value[3] << 24); rtc_time_to_tm(secs, tm); @@ -284,7 +285,8 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) return rc; } - secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + secs = value[0] | (value[1] << 8) | (value[2] << 16) | + ((unsigned long)value[3] << 24); rtc_time_to_tm(secs, &alarm->time); -- cgit v1.2.3-59-g8ed1b From 7dd63ec076cfc1a7c46742e2d820554a6fb42fc6 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 8 Feb 2019 11:01:03 +0100 Subject: rtc: imx-sc: depend on HAVE_ARM_SMCCC Fix the following build issue: drivers/rtc/rtc-imx-sc.o: In function `imx_sc_rtc_set_time': >> rtc-imx-sc.c:(.text+0x212): undefined reference to `__arm_smccc_smc' Fixes: 13a929f38ad7 ("rtc: imx-sc: add rtc set time support") Reviewed-by: Anson Huang Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index faa9ae1a3062..8f6735e0ff14 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1698,6 +1698,7 @@ config RTC_DRV_SNVS config RTC_DRV_IMX_SC depends on IMX_SCU + depends on HAVE_ARM_SMCCC tristate "NXP i.MX System Controller RTC support" help If you say yes here you get support for the NXP i.MX System -- cgit v1.2.3-59-g8ed1b From 09832dc0594ee264b658b7239d3d963b111e8faf Mon Sep 17 00:00:00 2001 From: "Oliver.Rohe@wago.com" Date: Fri, 8 Feb 2019 13:20:44 +0000 Subject: rtc: rs5c372: Fix reading from rtc when the oscillator got interrupted. When the oscillator of the rtc gets interrupted, e.g. due to an empty battery, reading from the rtc will now return an error and the oscillator bit will be cleared, once the rtc is successfully reset. Signed-off-by: Oliver Rohe Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rs5c372.c | 54 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index ff35dcef338b..66a473a3c3fe 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -52,8 +52,10 @@ # define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */ #define RS5C_REG_CTRL2 15 # define RS5C372_CTRL2_24 (1 << 5) -# define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */ # define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2x2x */ +# define R2x2x_CTRL2_VDET (1 << 6) /* only if R2x2x */ +# define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */ +# define R2x2x_CTRL2_PON (1 << 4) /* only if R2x2x */ # define RS5C_CTRL2_CTFG (1 << 2) # define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */ # define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */ @@ -212,10 +214,27 @@ static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm) struct i2c_client *client = to_i2c_client(dev); struct rs5c372 *rs5c = i2c_get_clientdata(client); int status = rs5c_get_regs(rs5c); + unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2]; if (status < 0) return status; + switch (rs5c->type) { + case rtc_r2025sd: + case rtc_r2221tl: + if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) || + (rs5c->type == rtc_r2221tl && (ctrl2 & R2x2x_CTRL2_XSTP))) { + dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n"); + return -EINVAL; + } + break; + default: + if (ctrl2 & RS5C_CTRL2_XSTP) { + dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n"); + return -EINVAL; + } + } + tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f); tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f); tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]); @@ -243,6 +262,7 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) struct i2c_client *client = to_i2c_client(dev); struct rs5c372 *rs5c = i2c_get_clientdata(client); unsigned char buf[7]; + unsigned char ctrl2; int addr; dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d " @@ -261,7 +281,32 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) buf[6] = bin2bcd(tm->tm_year - 100); if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) { - dev_err(&client->dev, "%s: write error\n", __func__); + dev_dbg(&client->dev, "%s: write error in line %i\n", + __func__, __LINE__); + return -EIO; + } + + addr = RS5C_ADDR(RS5C_REG_CTRL2); + ctrl2 = i2c_smbus_read_byte_data(client, addr); + + /* clear rtc warning bits */ + switch (rs5c->type) { + case rtc_r2025sd: + case rtc_r2221tl: + ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON); + if (rs5c->type == rtc_r2025sd) + ctrl2 |= R2x2x_CTRL2_XSTP; + else + ctrl2 &= ~R2x2x_CTRL2_XSTP; + break; + default: + ctrl2 &= ~RS5C_CTRL2_XSTP; + break; + } + + if (i2c_smbus_write_byte_data(client, addr, ctrl2) < 0) { + dev_dbg(&client->dev, "%s: write error in line %i\n", + __func__, __LINE__); return -EIO; } @@ -523,23 +568,18 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; - /* handle xstp bit */ switch (rs5c372->type) { case rtc_r2025sd: if (buf[1] & R2x2x_CTRL2_XSTP) return ret; - rs5c372->regs[RS5C_REG_CTRL2] |= R2x2x_CTRL2_XSTP; break; case rtc_r2221tl: if (!(buf[1] & R2x2x_CTRL2_XSTP)) return ret; - rs5c372->regs[RS5C_REG_CTRL2] &= ~R2x2x_CTRL2_XSTP; break; - default: if (!(buf[1] & RS5C_CTRL2_XSTP)) return ret; - rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; break; } -- cgit v1.2.3-59-g8ed1b From 1366e010ade86bcc8226fbdd9c0cb71cfd2aa173 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 9 Feb 2019 01:18:12 +0100 Subject: dt-bindings: rtc: add device-tree bindings for the Amlogic Meson RTC The 32-bit Amlogic Meson SoCs (Meson6, Meson8, Meson8b and Meson8m2) have a built-in RTC block. It has the following inputs: - an 32.768kHz crystal oscillator - an interrupt line - a reset line - 0.9V voltage input Signed-off-by: Ben Dooks [resurrected patches from Ben after 2 years] Signed-off-by: Martin Blumenstingl Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/rtc-meson.txt | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/rtc-meson.txt diff --git a/Documentation/devicetree/bindings/rtc/rtc-meson.txt b/Documentation/devicetree/bindings/rtc/rtc-meson.txt new file mode 100644 index 000000000000..e921fe66a362 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/rtc-meson.txt @@ -0,0 +1,35 @@ +* Amlogic Meson6, Meson8, Meson8b and Meson8m2 RTC + +Required properties: +- compatible: should be one of the following describing the hardware: + * "amlogic,meson6-rtc" + * "amlogic,meson8-rtc" + * "amlogic,meson8b-rtc" + * "amlogic,meson8m2-rtc" + +- reg: physical register space for the controller's memory mapped registers. +- interrupts: the interrupt line of the RTC block. +- clocks: reference to the external 32.768kHz crystal oscillator. +- vdd-supply: reference to the power supply of the RTC block. +- resets: reset controller reference to allow reset of the controller + +Optional properties for the battery-backed non-volatile memory: +- #address-cells: should be 1 to address the battery-backed non-volatile memory +- #size-cells: should be 1 to reference the battery-backed non-volatile memory + +Optional child nodes: +- see ../nvmem/nvmem.txt + +Example: + + rtc: rtc@740 { + compatible = "amlogic,meson6-rtc"; + reg = <0x740 0x14>; + interrupts = ; + clocks = <&rtc32k_xtal>; + vdd-supply = <&rtc_vdd>; + resets = <&reset RESET_RTC>; + + #address-cells = <1>; + #size-cells = <1>; + }; -- cgit v1.2.3-59-g8ed1b From d8fe6009aa3ecbeeab3a4ec1a8bce68959a885be Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 9 Feb 2019 01:18:13 +0100 Subject: rtc: support for the Amlogic Meson RTC Add support for the RTC block on the 32-bit Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs. The RTC is split in to two parts, which are both managed by this driver: - the AHB front end - and a simple serial connection to the actual registers The RTC_COUNTER register which holds the time is 32-bits wide. There are four 32-bit wide (in total: 16 bytes) "regmem" registers which are exposed using nvmem. On Amlogic's 3.10 kernel this is used to store data which needs to survive a suspend / resume cycle. Signed-off-by: Ben Dooks [resurrected Ben's patches after 2 years] Signed-off-by: Martin Blumenstingl Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 11 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-meson.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 422 insertions(+) create mode 100644 drivers/rtc/rtc-meson.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8f6735e0ff14..7d61bb382cf3 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1294,6 +1294,17 @@ config RTC_DRV_IMXDI This driver can also be built as a module, if so, the module will be called "rtc-imxdi". +config RTC_DRV_MESON + tristate "Amlogic Meson RTC" + depends on (ARM && ARCH_MESON) || COMPILE_TEST + select REGMAP_MMIO + help + Support for the RTC block on the Amlogic Meson6, Meson8, Meson8b + and Meson8m2 SoCs. + + This driver can also be built as a module, if so, the module + will be called "rtc-meson". + config RTC_DRV_OMAP tristate "TI OMAP Real Time Clock" depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index faca02109aaa..b994323942bd 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o +obj-$(CONFIG_RTC_DRV_MESON) += rtc-meson.o obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o diff --git a/drivers/rtc/rtc-meson.c b/drivers/rtc/rtc-meson.c new file mode 100644 index 000000000000..6fc4b09e7ebd --- /dev/null +++ b/drivers/rtc/rtc-meson.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the interal RTC block in the Amlogic Meson6, Meson8, + * Meson8b and Meson8m2 SoCs. + * + * The RTC is split in to two parts, the AHB front end and a simple serial + * connection to the actual registers. This driver manages both parts. + * + * Copyright (c) 2018 Martin Blumenstingl + * Copyright (c) 2015 Ben Dooks for Codethink Ltd + * Based on origin by Carlo Caione + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers accessed from cpu bus */ +#define RTC_ADDR0 0x00 + #define RTC_ADDR0_LINE_SCLK BIT(0) + #define RTC_ADDR0_LINE_SEN BIT(1) + #define RTC_ADDR0_LINE_SDI BIT(2) + #define RTC_ADDR0_START_SER BIT(17) + #define RTC_ADDR0_WAIT_SER BIT(22) + #define RTC_ADDR0_DATA GENMASK(31, 24) + +#define RTC_ADDR1 0x04 + #define RTC_ADDR1_SDO BIT(0) + #define RTC_ADDR1_S_READY BIT(1) + +#define RTC_ADDR2 0x08 +#define RTC_ADDR3 0x0c + +#define RTC_REG4 0x10 + #define RTC_REG4_STATIC_VALUE GENMASK(7, 0) + +/* rtc registers accessed via rtc-serial interface */ +#define RTC_COUNTER (0) +#define RTC_SEC_ADJ (2) +#define RTC_REGMEM_0 (4) +#define RTC_REGMEM_1 (5) +#define RTC_REGMEM_2 (6) +#define RTC_REGMEM_3 (7) + +#define RTC_ADDR_BITS (3) /* number of address bits to send */ +#define RTC_DATA_BITS (32) /* number of data bits to tx/rx */ + +#define MESON_STATIC_BIAS_CUR (0x5 << 1) +#define MESON_STATIC_VOLTAGE (0x3 << 11) +#define MESON_STATIC_DEFAULT (MESON_STATIC_BIAS_CUR | MESON_STATIC_VOLTAGE) + +struct meson_rtc { + struct rtc_device *rtc; /* rtc device we created */ + struct device *dev; /* device we bound from */ + struct reset_control *reset; /* reset source */ + struct regulator *vdd; /* voltage input */ + struct regmap *peripheral; /* peripheral registers */ + struct regmap *serial; /* serial registers */ +}; + +static const struct regmap_config meson_rtc_peripheral_regmap_config = { + .name = "peripheral-registers", + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = RTC_REG4, + .fast_io = true, +}; + +/* RTC front-end serialiser controls */ + +static void meson_rtc_sclk_pulse(struct meson_rtc *rtc) +{ + udelay(5); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, 0); + udelay(5); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, + RTC_ADDR0_LINE_SCLK); +} + +static void meson_rtc_send_bit(struct meson_rtc *rtc, unsigned int bit) +{ + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, + bit ? RTC_ADDR0_LINE_SDI : 0); + meson_rtc_sclk_pulse(rtc); +} + +static void meson_rtc_send_bits(struct meson_rtc *rtc, u32 data, + unsigned int nr) +{ + u32 bit = 1 << (nr - 1); + + while (bit) { + meson_rtc_send_bit(rtc, data & bit); + bit >>= 1; + } +} + +static void meson_rtc_set_dir(struct meson_rtc *rtc, u32 mode) +{ + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, 0); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0); + meson_rtc_send_bit(rtc, mode); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0); +} + +static u32 meson_rtc_get_data(struct meson_rtc *rtc) +{ + u32 tmp, val = 0; + int bit; + + for (bit = 0; bit < RTC_DATA_BITS; bit++) { + meson_rtc_sclk_pulse(rtc); + val <<= 1; + + regmap_read(rtc->peripheral, RTC_ADDR1, &tmp); + val |= tmp & RTC_ADDR1_SDO; + } + + return val; +} + +static int meson_rtc_get_bus(struct meson_rtc *rtc) +{ + int ret, retries = 3; + u32 val; + + /* prepare bus for transfers, set all lines low */ + val = RTC_ADDR0_LINE_SDI | RTC_ADDR0_LINE_SEN | RTC_ADDR0_LINE_SCLK; + regmap_update_bits(rtc->peripheral, RTC_ADDR0, val, 0); + + for (retries = 0; retries < 3; retries++) { + /* wait for the bus to be ready */ + if (!regmap_read_poll_timeout(rtc->peripheral, RTC_ADDR1, val, + val & RTC_ADDR1_S_READY, 10, + 10000)) + return 0; + + dev_warn(rtc->dev, "failed to get bus, resetting RTC\n"); + + ret = reset_control_reset(rtc->reset); + if (ret) + return ret; + } + + dev_err(rtc->dev, "bus is not ready\n"); + return -ETIMEDOUT; +} + +static int meson_rtc_serial_bus_reg_read(void *context, unsigned int reg, + unsigned int *data) +{ + struct meson_rtc *rtc = context; + int ret; + + ret = meson_rtc_get_bus(rtc); + if (ret) + return ret; + + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, + RTC_ADDR0_LINE_SEN); + meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS); + meson_rtc_set_dir(rtc, 0); + *data = meson_rtc_get_data(rtc); + + return 0; +} + +static int meson_rtc_serial_bus_reg_write(void *context, unsigned int reg, + unsigned int data) +{ + struct meson_rtc *rtc = context; + int ret; + + ret = meson_rtc_get_bus(rtc); + if (ret) + return ret; + + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, + RTC_ADDR0_LINE_SEN); + meson_rtc_send_bits(rtc, data, RTC_DATA_BITS); + meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS); + meson_rtc_set_dir(rtc, 1); + + return 0; +} + +static const struct regmap_bus meson_rtc_serial_bus = { + .reg_read = meson_rtc_serial_bus_reg_read, + .reg_write = meson_rtc_serial_bus_reg_write, +}; + +static const struct regmap_config meson_rtc_serial_regmap_config = { + .name = "serial-registers", + .reg_bits = 4, + .reg_stride = 1, + .val_bits = 32, + .max_register = RTC_REGMEM_3, + .fast_io = false, +}; + +static int meson_rtc_write_static(struct meson_rtc *rtc, u32 data) +{ + u32 tmp; + + regmap_write(rtc->peripheral, RTC_REG4, + FIELD_PREP(RTC_REG4_STATIC_VALUE, (data >> 8))); + + /* write the static value and start the auto serializer */ + tmp = FIELD_PREP(RTC_ADDR0_DATA, (data & 0xff)) | RTC_ADDR0_START_SER; + regmap_update_bits(rtc->peripheral, RTC_ADDR0, + RTC_ADDR0_DATA | RTC_ADDR0_START_SER, tmp); + + /* wait for the auto serializer to complete */ + return regmap_read_poll_timeout(rtc->peripheral, RTC_REG4, tmp, + !(tmp & RTC_ADDR0_WAIT_SER), 10, + 10000); +} + +/* RTC interface layer functions */ + +static int meson_rtc_gettime(struct device *dev, struct rtc_time *tm) +{ + struct meson_rtc *rtc = dev_get_drvdata(dev); + u32 time; + int ret; + + ret = regmap_read(rtc->serial, RTC_COUNTER, &time); + if (!ret) + rtc_time64_to_tm(time, tm); + + return ret; +} + +static int meson_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct meson_rtc *rtc = dev_get_drvdata(dev); + + return regmap_write(rtc->serial, RTC_COUNTER, rtc_tm_to_time64(tm)); +} + +static const struct rtc_class_ops meson_rtc_ops = { + .read_time = meson_rtc_gettime, + .set_time = meson_rtc_settime, +}; + +/* NVMEM interface layer functions */ + +static int meson_rtc_regmem_read(void *context, unsigned int offset, + void *buf, size_t bytes) +{ + struct meson_rtc *rtc = context; + unsigned int read_offset, read_size; + + read_offset = RTC_REGMEM_0 + (offset / 4); + read_size = bytes / 4; + + return regmap_bulk_read(rtc->serial, read_offset, buf, read_size); +} + +static int meson_rtc_regmem_write(void *context, unsigned int offset, + void *buf, size_t bytes) +{ + struct meson_rtc *rtc = context; + unsigned int write_offset, write_size; + + write_offset = RTC_REGMEM_0 + (offset / 4); + write_size = bytes / 4; + + return regmap_bulk_write(rtc->serial, write_offset, buf, write_size); +} + +static int meson_rtc_probe(struct platform_device *pdev) +{ + struct nvmem_config meson_rtc_nvmem_config = { + .name = "meson-rtc-regmem", + .type = NVMEM_TYPE_BATTERY_BACKED, + .word_size = 4, + .stride = 4, + .size = 4 * 4, + .reg_read = meson_rtc_regmem_read, + .reg_write = meson_rtc_regmem_write, + }; + struct device *dev = &pdev->dev; + struct meson_rtc *rtc; + struct resource *res; + void __iomem *base; + int ret; + u32 tm; + + rtc = devm_kzalloc(dev, sizeof(struct meson_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc->rtc)) + return PTR_ERR(rtc->rtc); + + platform_set_drvdata(pdev, rtc); + + rtc->dev = dev; + + rtc->rtc->ops = &meson_rtc_ops; + rtc->rtc->range_max = U32_MAX; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + rtc->peripheral = devm_regmap_init_mmio(dev, base, + &meson_rtc_peripheral_regmap_config); + if (IS_ERR(rtc->peripheral)) { + dev_err(dev, "failed to create peripheral regmap\n"); + return PTR_ERR(rtc->peripheral); + } + + rtc->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(rtc->reset)) { + dev_err(dev, "missing reset line\n"); + return PTR_ERR(rtc->reset); + } + + rtc->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(rtc->vdd)) { + dev_err(dev, "failed to get the vdd-supply\n"); + return PTR_ERR(rtc->vdd); + } + + ret = regulator_enable(rtc->vdd); + if (ret) { + dev_err(dev, "failed to enable vdd-supply\n"); + return ret; + } + + ret = meson_rtc_write_static(rtc, MESON_STATIC_DEFAULT); + if (ret) { + dev_err(dev, "failed to set static values\n"); + goto out_disable_vdd; + } + + rtc->serial = devm_regmap_init(dev, &meson_rtc_serial_bus, rtc, + &meson_rtc_serial_regmap_config); + if (IS_ERR(rtc->serial)) { + dev_err(dev, "failed to create serial regmap\n"); + ret = PTR_ERR(rtc->serial); + goto out_disable_vdd; + } + + /* + * check if we can read RTC counter, if not then the RTC is probably + * not functional. If it isn't probably best to not bind. + */ + ret = regmap_read(rtc->serial, RTC_COUNTER, &tm); + if (ret) { + dev_err(dev, "cannot read RTC counter, RTC not functional\n"); + goto out_disable_vdd; + } + + meson_rtc_nvmem_config.priv = rtc; + ret = rtc_nvmem_register(rtc->rtc, &meson_rtc_nvmem_config); + if (ret) + goto out_disable_vdd; + + ret = rtc_register_device(rtc->rtc); + if (ret) + goto out_unregister_nvmem; + + return 0; + +out_unregister_nvmem: + rtc_nvmem_unregister(rtc->rtc); + +out_disable_vdd: + regulator_disable(rtc->vdd); + return ret; +} + +static const struct of_device_id meson_rtc_dt_match[] = { + { .compatible = "amlogic,meson6-rtc", }, + { .compatible = "amlogic,meson8-rtc", }, + { .compatible = "amlogic,meson8b-rtc", }, + { .compatible = "amlogic,meson8m2-rtc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, meson_rtc_dt_match); + +static struct platform_driver meson_rtc_driver = { + .probe = meson_rtc_probe, + .driver = { + .name = "meson-rtc", + .of_match_table = of_match_ptr(meson_rtc_dt_match), + }, +}; +module_platform_driver(meson_rtc_driver); + +MODULE_DESCRIPTION("Amlogic Meson RTC Driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:meson-rtc"); -- cgit v1.2.3-59-g8ed1b From 5736610ab61622ca4ca09a5778a8b18fd3220b1b Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 11 Feb 2019 10:46:46 +0100 Subject: rtc: meson: remove useless rtc_nvmem_unregister call rtc_nvmem_unregister() is called on rtc_device release so it is not necessary to call it from the driver. Reviewed-by: Martin Blumenstingl Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-meson.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-meson.c b/drivers/rtc/rtc-meson.c index 6fc4b09e7ebd..e08b981dfc21 100644 --- a/drivers/rtc/rtc-meson.c +++ b/drivers/rtc/rtc-meson.c @@ -373,13 +373,10 @@ static int meson_rtc_probe(struct platform_device *pdev) ret = rtc_register_device(rtc->rtc); if (ret) - goto out_unregister_nvmem; + goto out_disable_vdd; return 0; -out_unregister_nvmem: - rtc_nvmem_unregister(rtc->rtc); - out_disable_vdd: regulator_disable(rtc->vdd); return ret; -- cgit v1.2.3-59-g8ed1b From ed3c52a075e9488cc672c5658414b2f157fc5688 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 12 Feb 2019 02:34:03 +0000 Subject: rtc: isl1208: Introduce driver state struct This driver has no state of its own, depending entirely on what is in the generic rtc device. Intoduce a state struct. For now it only contains a pointer to the rtc device struct, but future patches will add more data. Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-isl1208.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 263af3d8cd9f..d8e0670e12fc 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -79,6 +79,11 @@ enum { TYPE_ISL1219, }; +/* Device state */ +struct isl1208_state { + struct rtc_device *rtc; +}; + /* block read */ static int isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], @@ -557,7 +562,7 @@ isl1208_rtc_interrupt(int irq, void *data) { unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct i2c_client *client = data; - struct rtc_device *rtc = i2c_get_clientdata(client); + struct isl1208_state *isl1208 = i2c_get_clientdata(client); int handled = 0, sr, err; /* @@ -580,7 +585,7 @@ isl1208_rtc_interrupt(int irq, void *data) if (sr & ISL1208_REG_SR_ALM) { dev_dbg(&client->dev, "alarm!\n"); - rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF); /* Clear the alarm */ sr &= ~ISL1208_REG_SR_ALM; @@ -598,7 +603,7 @@ isl1208_rtc_interrupt(int irq, void *data) } if (sr & ISL1208_REG_SR_EVT) { - sysfs_notify(&rtc->dev.kobj, NULL, + sysfs_notify(&isl1208->rtc->dev.kobj, NULL, dev_attr_timestamp0.attr.name); dev_warn(&client->dev, "event detected"); handled = 1; @@ -723,7 +728,7 @@ static int isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc = 0; - struct rtc_device *rtc; + struct isl1208_state *isl1208; int evdet_irq = -1; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) @@ -732,13 +737,17 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) if (isl1208_i2c_validate_client(client) < 0) return -ENODEV; - rtc = devm_rtc_allocate_device(&client->dev); - if (IS_ERR(rtc)) - return PTR_ERR(rtc); + /* Allocate driver state, point i2c client data to it */ + isl1208 = devm_kzalloc(&client->dev, sizeof(*isl1208), GFP_KERNEL); + if (!isl1208) + return -ENOMEM; + i2c_set_clientdata(client, isl1208); - rtc->ops = &isl1208_rtc_ops; + isl1208->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(isl1208->rtc)) + return PTR_ERR(isl1208->rtc); - i2c_set_clientdata(client, rtc); + isl1208->rtc->ops = &isl1208_rtc_ops; rc = isl1208_i2c_get_sr(client); if (rc < 0) { @@ -771,13 +780,13 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_err(&client->dev, "could not enable tamper detection\n"); return rc; } - rc = rtc_add_group(rtc, &isl1219_rtc_sysfs_files); + rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files); if (rc) return rc; evdet_irq = of_irq_get_byname(np, "evdet"); } - rc = rtc_add_group(rtc, &isl1208_rtc_sysfs_files); + rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files); if (rc) return rc; @@ -791,7 +800,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) if (rc) return rc; - return rtc_register_device(rtc); + return rtc_register_device(isl1208->rtc); } static const struct i2c_device_id isl1208_id[] = { -- cgit v1.2.3-59-g8ed1b From 5909b87db8ef0df4e11363afc53ed788b7ea17cb Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 12 Feb 2019 02:34:04 +0000 Subject: rtc: isl1208: Support more chip variations Add more support in the driver for dealing with differences in is1208 compatible chips. Put the 1208, 1209, 1218, and 1219 in the list and encode information about nvram size, tamper, and timestamp features. This adds support for the isl1209, which has a tamper detect but no timestamp feature. Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-isl1208.c | 77 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index d8e0670e12fc..036e5fc148d4 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -73,15 +74,49 @@ static struct i2c_driver isl1208_driver; /* ISL1208 various variants */ -enum { +enum isl1208_id { TYPE_ISL1208 = 0, + TYPE_ISL1209, TYPE_ISL1218, TYPE_ISL1219, + ISL_LAST_ID }; +/* Chip capabilities table */ +static const struct isl1208_config { + const char name[8]; + unsigned int nvmem_length; + unsigned has_tamper:1; + unsigned has_timestamp:1; +} isl1208_configs[] = { + [TYPE_ISL1208] = { "isl1208", 2, false, false }, + [TYPE_ISL1209] = { "isl1209", 2, true, false }, + [TYPE_ISL1218] = { "isl1218", 8, false, false }, + [TYPE_ISL1219] = { "isl1219", 2, true, true }, +}; + +static const struct i2c_device_id isl1208_id[] = { + { "isl1208", TYPE_ISL1208 }, + { "isl1209", TYPE_ISL1209 }, + { "isl1218", TYPE_ISL1218 }, + { "isl1219", TYPE_ISL1219 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isl1208_id); + +static const struct of_device_id isl1208_of_match[] = { + { .compatible = "isil,isl1208", .data = &isl1208_configs[TYPE_ISL1208] }, + { .compatible = "isil,isl1209", .data = &isl1208_configs[TYPE_ISL1209] }, + { .compatible = "isil,isl1218", .data = &isl1208_configs[TYPE_ISL1218] }, + { .compatible = "isil,isl1219", .data = &isl1208_configs[TYPE_ISL1219] }, + { } +}; +MODULE_DEVICE_TABLE(of, isl1208_of_match); + /* Device state */ struct isl1208_state { struct rtc_device *rtc; + const struct isl1208_config *config; }; /* block read */ @@ -602,11 +637,12 @@ isl1208_rtc_interrupt(int irq, void *data) return err; } - if (sr & ISL1208_REG_SR_EVT) { - sysfs_notify(&isl1208->rtc->dev.kobj, NULL, - dev_attr_timestamp0.attr.name); + if (isl1208->config->has_tamper && (sr & ISL1208_REG_SR_EVT)) { dev_warn(&client->dev, "event detected"); handled = 1; + if (isl1208->config->has_timestamp) + sysfs_notify(&isl1208->rtc->dev.kobj, NULL, + dev_attr_timestamp0.attr.name); } return handled ? IRQ_HANDLED : IRQ_NONE; @@ -743,6 +779,17 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) return -ENOMEM; i2c_set_clientdata(client, isl1208); + /* Determine which chip we have */ + if (client->dev.of_node) { + isl1208->config = of_device_get_match_data(&client->dev); + if (!isl1208->config) + return -ENODEV; + } else { + if (id->driver_data >= ISL_LAST_ID) + return -ENODEV; + isl1208->config = &isl1208_configs[id->driver_data]; + } + isl1208->rtc = devm_rtc_allocate_device(&client->dev); if (IS_ERR(isl1208->rtc)) return PTR_ERR(isl1208->rtc); @@ -759,7 +806,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_warn(&client->dev, "rtc power failure detected, " "please set clock.\n"); - if (id->driver_data == TYPE_ISL1219) { + if (isl1208->config->has_tamper) { struct device_node *np = client->dev.of_node; u32 evienb; @@ -780,10 +827,12 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_err(&client->dev, "could not enable tamper detection\n"); return rc; } + evdet_irq = of_irq_get_byname(np, "evdet"); + } + if (isl1208->config->has_timestamp) { rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files); if (rc) return rc; - evdet_irq = of_irq_get_byname(np, "evdet"); } rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files); @@ -803,22 +852,6 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) return rtc_register_device(isl1208->rtc); } -static const struct i2c_device_id isl1208_id[] = { - { "isl1208", TYPE_ISL1208 }, - { "isl1218", TYPE_ISL1218 }, - { "isl1219", TYPE_ISL1219 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, isl1208_id); - -static const struct of_device_id isl1208_of_match[] = { - { .compatible = "isil,isl1208" }, - { .compatible = "isil,isl1218" }, - { .compatible = "isil,isl1219" }, - { } -}; -MODULE_DEVICE_TABLE(of, isl1208_of_match); - static struct i2c_driver isl1208_driver = { .driver = { .name = "rtc-isl1208", -- cgit v1.2.3-59-g8ed1b From c3544f6f51edcac4040dd78b9e9d2ece48e2ac0b Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 12 Feb 2019 02:34:05 +0000 Subject: rtc: isl1208: Add new style nvmem support to driver Add support for the RTC's NVRAM using the standard nvmem support that is part of the Linux RTC framework. This driver already has a sysfs attribute that provides access to the RTC's NVRAM as a single 16-bit value. Some chips have more than two bytes of NVRAM, so this will not work for them. It's also non-standard. This sysfs attribute is left in for backward compatibility, but will only be able to read the first two bytes of NVRAM. The nvmem interface will allow access to all NVRAM, e.g. eight bytes on the isl1218. Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-isl1208.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 036e5fc148d4..471e395b20db 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -115,6 +115,7 @@ MODULE_DEVICE_TABLE(of, isl1208_of_match); /* Device state */ struct isl1208_state { + struct nvmem_config nvmem_config; struct rtc_device *rtc; const struct isl1208_config *config; }; @@ -742,6 +743,46 @@ static const struct attribute_group isl1219_rtc_sysfs_files = { .attrs = isl1219_rtc_attrs, }; +static int isl1208_nvmem_read(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct isl1208_state *isl1208 = priv; + struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); + int ret; + + /* nvmem sanitizes offset/count for us, but count==0 is possible */ + if (!count) + return count; + ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf, + count); + return ret == 0 ? count : ret; +} + +static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct isl1208_state *isl1208 = priv; + struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); + int ret; + + /* nvmem sanitizes off/count for us, but count==0 is possible */ + if (!count) + return count; + ret = isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf, + count); + + return ret == 0 ? count : ret; +} + +static const struct nvmem_config isl1208_nvmem_config = { + .name = "isl1208_nvram", + .word_size = 1, + .stride = 1, + /* .size from chip specific config */ + .reg_read = isl1208_nvmem_read, + .reg_write = isl1208_nvmem_write, +}; + static int isl1208_setup_irq(struct i2c_client *client, int irq) { int rc = devm_request_threaded_irq(&client->dev, irq, NULL, @@ -796,6 +837,11 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) isl1208->rtc->ops = &isl1208_rtc_ops; + /* Setup nvmem configuration in driver state struct */ + isl1208->nvmem_config = isl1208_nvmem_config; + isl1208->nvmem_config.size = isl1208->config->nvmem_length; + isl1208->nvmem_config.priv = isl1208; + rc = isl1208_i2c_get_sr(client); if (rc < 0) { dev_err(&client->dev, "reading status failed\n"); @@ -849,6 +895,10 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) if (rc) return rc; + rc = rtc_nvmem_register(isl1208->rtc, &isl1208->nvmem_config); + if (rc) + return rc; + return rtc_register_device(isl1208->rtc); } -- cgit v1.2.3-59-g8ed1b From 10e7122107bd426a6ac33bcb17392978a72ca13b Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 12 Feb 2019 02:34:05 +0000 Subject: dt-bindings: rtc: Update for new chip in isl1208 series Since this documents multiple chips controlled by the isl1208 driver, name it isil,isl1208.txt, as the convention is to use the base driver name in the bindings document for drivers with multiple devices. Include all chips supported by this driver in the docs. Make it clear which properties apply to which chips. Expand documentation for existing isil,evienb property to explain operation in more detail. Existing docs just describe it in terms of setting a bit in a register. CC: Alessandro Zummo CC: Alexandre Belloni CC: Rob Herring CC: Mark Rutland CC: devicetree@vger.kernel.org Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/isil,isl1208.txt | 38 ++++++++++++++++++++++ .../devicetree/bindings/rtc/isil,isl1219.txt | 29 ----------------- 2 files changed, 38 insertions(+), 29 deletions(-) create mode 100644 Documentation/devicetree/bindings/rtc/isil,isl1208.txt delete mode 100644 Documentation/devicetree/bindings/rtc/isil,isl1219.txt diff --git a/Documentation/devicetree/bindings/rtc/isil,isl1208.txt b/Documentation/devicetree/bindings/rtc/isil,isl1208.txt new file mode 100644 index 000000000000..51f003006f04 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/isil,isl1208.txt @@ -0,0 +1,38 @@ +Intersil ISL1209/19 I2C RTC/Alarm chip with event in + +ISL12X9 have additional pins EVIN and #EVDET for tamper detection, while the +ISL1208 and ISL1218 do not. They are all use the same driver with the bindings +described here, with chip specific properties as noted. + +Required properties supported by the device: + - "compatible": Should be one of the following: + - "isil,isl1208" + - "isil,isl1209" + - "isil,isl1218" + - "isil,isl1219" + - "reg": I2C bus address of the device + +Optional properties: + - "interrupt-names": list which may contains "irq" and "evdet" + evdet applies to isl1209 and isl1219 only + - "interrupts": list of interrupts for "irq" and "evdet" + evdet applies to isl1209 and isl1219 only + - "isil,ev-evienb": Enable or disable internal pull on EVIN pin + Applies to isl1209 and isl1219 only + Possible values are 0 and 1 + Value 0 enables internal pull-up on evin pin, 1 disables it. + Default will leave the non-volatile configuration of the pullup + as is. + +Example isl1219 node with #IRQ pin connected to SoC gpio1 pin12 and #EVDET pin +connected to SoC gpio2 pin 24 and internal pull-up enabled in EVIN pin. + + isl1219: rtc@68 { + compatible = "isil,isl1219"; + reg = <0x68>; + interrupt-names = "irq", "evdet"; + interrupts-extended = <&gpio1 12 IRQ_TYPE_EDGE_FALLING>, + <&gpio2 24 IRQ_TYPE_EDGE_FALLING>; + isil,ev-evienb = <1>; + }; + diff --git a/Documentation/devicetree/bindings/rtc/isil,isl1219.txt b/Documentation/devicetree/bindings/rtc/isil,isl1219.txt deleted file mode 100644 index c3efd48e91c2..000000000000 --- a/Documentation/devicetree/bindings/rtc/isil,isl1219.txt +++ /dev/null @@ -1,29 +0,0 @@ -Intersil ISL1219 I2C RTC/Alarm chip with event in - -ISL1219 has additional pins EVIN and #EVDET for tamper detection. - -Required properties supported by the device: - - - "compatible": must be "isil,isl1219" - - "reg": I2C bus address of the device - -Optional properties: - - - "interrupt-names": list which may contains "irq" and "evdet" - - "interrupts": list of interrupts for "irq" and "evdet" - - "isil,ev-evienb": if present EV.EVIENB bit is set to the specified - value for proper operation. - - -Example isl1219 node with #IRQ pin connected to SoC gpio1 pin12 - and #EVDET pin connected to SoC gpio2 pin 24: - - isl1219: rtc@68 { - compatible = "isil,isl1219"; - reg = <0x68>; - interrupt-names = "irq", "evdet"; - interrupts-extended = <&gpio1 12 IRQ_TYPE_EDGE_FALLING>, - <&gpio2 24 IRQ_TYPE_EDGE_FALLING>; - isil,ev-evienb = <1>; - }; - -- cgit v1.2.3-59-g8ed1b From e6e7376cfd7b3f9b63de3a22792f64d9bfb2ab53 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 13 Feb 2019 00:21:36 +0100 Subject: rtc: rv3028: add new driver Add a driver for the MicroCrystal RV-3028. It is a SMT Real-Time Clock Module that incorporates an integrated CMOS circuit together with an XTAL. It has an i2c interface. The driver handles date/time, alarms, trickle charging, timestamping, frequency offset correction, EEPROM and NVRAM. Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 1 + drivers/rtc/Kconfig | 9 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-rv3028.c | 732 ++++++++++++++++++++++++++ 4 files changed, 743 insertions(+) create mode 100644 drivers/rtc/rtc-rv3028.c diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index d86e2850fe1c..3e6a215e7304 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -52,6 +52,7 @@ emmicro,em3027 EM Microelectronic EM3027 Real-time Clock isil,isl1208 Intersil ISL1208 Low Power RTC with Battery Backed SRAM isil,isl1218 Intersil ISL1218 Low Power RTC with Battery Backed SRAM isil,isl12022 Intersil ISL12022 Real-time Clock +microcrystal,rv3028 Real Time Clock Module with I2C-Bus microcrystal,rv3029 Real Time Clock Module with I2C-Bus microcrystal,rv8523 Real Time Clock nxp,pcf2127 Real-time clock diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 7d61bb382cf3..a08785ac8903 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -626,6 +626,15 @@ config RTC_DRV_EM3027 This driver can also be built as a module. If so, the module will be called rtc-em3027. +config RTC_DRV_RV3028 + tristate "Micro Crystal RV3028" + help + If you say yes here you get support for the Micro Crystal + RV3028. + + This driver can also be built as a module. If so, the module + will be called rtc-rv3028. + config RTC_DRV_RV8803 tristate "Micro Crystal RV8803, Epson RX8900" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index b994323942bd..6dc1e561f533 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -139,6 +139,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o obj-$(CONFIG_RTC_DRV_RTD119X) += rtc-rtd119x.o +obj-$(CONFIG_RTC_DRV_RV3028) += rtc-rv3028.o obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c new file mode 100644 index 000000000000..06884ebb7a61 --- /dev/null +++ b/drivers/rtc/rtc-rv3028.c @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the Micro Crystal RV3028 + * + * Copyright (C) 2019 Micro Crystal SA + * + * Alexandre Belloni + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RV3028_SEC 0x00 +#define RV3028_MIN 0x01 +#define RV3028_HOUR 0x02 +#define RV3028_WDAY 0x03 +#define RV3028_DAY 0x04 +#define RV3028_MONTH 0x05 +#define RV3028_YEAR 0x06 +#define RV3028_ALARM_MIN 0x07 +#define RV3028_ALARM_HOUR 0x08 +#define RV3028_ALARM_DAY 0x09 +#define RV3028_STATUS 0x0E +#define RV3028_CTRL1 0x0F +#define RV3028_CTRL2 0x10 +#define RV3028_EVT_CTRL 0x13 +#define RV3028_TS_COUNT 0x14 +#define RV3028_TS_SEC 0x15 +#define RV3028_RAM1 0x1F +#define RV3028_EEPROM_ADDR 0x25 +#define RV3028_EEPROM_DATA 0x26 +#define RV3028_EEPROM_CMD 0x27 +#define RV3028_CLKOUT 0x35 +#define RV3028_OFFSET 0x36 +#define RV3028_BACKUP 0x37 + +#define RV3028_STATUS_PORF BIT(0) +#define RV3028_STATUS_EVF BIT(1) +#define RV3028_STATUS_AF BIT(2) +#define RV3028_STATUS_TF BIT(3) +#define RV3028_STATUS_UF BIT(4) +#define RV3028_STATUS_BSF BIT(5) +#define RV3028_STATUS_CLKF BIT(6) +#define RV3028_STATUS_EEBUSY BIT(7) + +#define RV3028_CTRL1_EERD BIT(3) +#define RV3028_CTRL1_WADA BIT(5) + +#define RV3028_CTRL2_RESET BIT(0) +#define RV3028_CTRL2_12_24 BIT(1) +#define RV3028_CTRL2_EIE BIT(2) +#define RV3028_CTRL2_AIE BIT(3) +#define RV3028_CTRL2_TIE BIT(4) +#define RV3028_CTRL2_UIE BIT(5) +#define RV3028_CTRL2_TSE BIT(7) + +#define RV3028_EVT_CTRL_TSR BIT(2) + +#define RV3028_EEPROM_CMD_WRITE 0x21 +#define RV3028_EEPROM_CMD_READ 0x22 + +#define RV3028_EEBUSY_POLL 10000 +#define RV3028_EEBUSY_TIMEOUT 100000 + +#define RV3028_BACKUP_TCE BIT(5) +#define RV3028_BACKUP_TCR_MASK GENMASK(1,0) + +#define OFFSET_STEP_PPT 953674 + +enum rv3028_type { + rv_3028, +}; + +struct rv3028_data { + struct regmap *regmap; + struct rtc_device *rtc; + enum rv3028_type type; +}; + +static u16 rv3028_trickle_resistors[] = {1000, 3000, 6000, 11000}; + +static ssize_t timestamp0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); + + regmap_update_bits(rv3028->regmap, RV3028_EVT_CTRL, RV3028_EVT_CTRL_TSR, + RV3028_EVT_CTRL_TSR); + + return count; +}; + +static ssize_t timestamp0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); + struct rtc_time tm; + int ret, count; + u8 date[6]; + + ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); + if (ret) + return ret; + + if (!count) + return 0; + + ret = regmap_bulk_read(rv3028->regmap, RV3028_TS_SEC, date, + sizeof(date)); + if (ret) + return ret; + + tm.tm_sec = bcd2bin(date[0]); + tm.tm_min = bcd2bin(date[1]); + tm.tm_hour = bcd2bin(date[2]); + tm.tm_mday = bcd2bin(date[3]); + tm.tm_mon = bcd2bin(date[4]) - 1; + tm.tm_year = bcd2bin(date[5]) + 100; + + ret = rtc_valid_tm(&tm); + if (ret) + return ret; + + return sprintf(buf, "%llu\n", + (unsigned long long)rtc_tm_to_time64(&tm)); +}; + +static DEVICE_ATTR_RW(timestamp0); + +static ssize_t timestamp0_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); + int ret, count; + + ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); + if (ret) + return ret; + + return sprintf(buf, "%u\n", count); +}; + +static DEVICE_ATTR_RO(timestamp0_count); + +static struct attribute *rv3028_attrs[] = { + &dev_attr_timestamp0.attr, + &dev_attr_timestamp0_count.attr, + NULL +}; + +static const struct attribute_group rv3028_attr_group = { + .attrs = rv3028_attrs, +}; + +static irqreturn_t rv3028_handle_irq(int irq, void *dev_id) +{ + struct rv3028_data *rv3028 = dev_id; + unsigned long events = 0; + u32 status = 0, ctrl = 0; + + if (regmap_read(rv3028->regmap, RV3028_STATUS, &status) < 0 || + status == 0) { + return IRQ_NONE; + } + + if (status & RV3028_STATUS_PORF) + dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n"); + + if (status & RV3028_STATUS_TF) { + status |= RV3028_STATUS_TF; + ctrl |= RV3028_CTRL2_TIE; + events |= RTC_PF; + } + + if (status & RV3028_STATUS_AF) { + status |= RV3028_STATUS_AF; + ctrl |= RV3028_CTRL2_AIE; + events |= RTC_AF; + } + + if (status & RV3028_STATUS_UF) { + status |= RV3028_STATUS_UF; + ctrl |= RV3028_CTRL2_UIE; + events |= RTC_UF; + } + + if (events) { + rtc_update_irq(rv3028->rtc, 1, events); + regmap_update_bits(rv3028->regmap, RV3028_STATUS, status, 0); + regmap_update_bits(rv3028->regmap, RV3028_CTRL2, ctrl, 0); + } + + if (status & RV3028_STATUS_EVF) { + sysfs_notify(&rv3028->rtc->dev.kobj, NULL, + dev_attr_timestamp0.attr.name); + dev_warn(&rv3028->rtc->dev, "event detected"); + } + + return IRQ_HANDLED; +} + +static int rv3028_get_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 date[7]; + int ret, status; + + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + if (status & RV3028_STATUS_PORF) { + dev_warn(dev, "Voltage low, data is invalid.\n"); + return -EINVAL; + } + + ret = regmap_bulk_read(rv3028->regmap, RV3028_SEC, date, sizeof(date)); + if (ret) + return ret; + + tm->tm_sec = bcd2bin(date[RV3028_SEC] & 0x7f); + tm->tm_min = bcd2bin(date[RV3028_MIN] & 0x7f); + tm->tm_hour = bcd2bin(date[RV3028_HOUR] & 0x3f); + tm->tm_wday = ilog2(date[RV3028_WDAY] & 0x7f); + tm->tm_mday = bcd2bin(date[RV3028_DAY] & 0x3f); + tm->tm_mon = bcd2bin(date[RV3028_MONTH] & 0x1f) - 1; + tm->tm_year = bcd2bin(date[RV3028_YEAR]) + 100; + + return 0; +} + +static int rv3028_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 date[7]; + int ret; + + date[RV3028_SEC] = bin2bcd(tm->tm_sec); + date[RV3028_MIN] = bin2bcd(tm->tm_min); + date[RV3028_HOUR] = bin2bcd(tm->tm_hour); + date[RV3028_WDAY] = 1 << (tm->tm_wday); + date[RV3028_DAY] = bin2bcd(tm->tm_mday); + date[RV3028_MONTH] = bin2bcd(tm->tm_mon + 1); + date[RV3028_YEAR] = bin2bcd(tm->tm_year - 100); + + /* + * Writing to the Seconds register has the same effect as setting RESET + * bit to 1 + */ + ret = regmap_bulk_write(rv3028->regmap, RV3028_SEC, date, + sizeof(date)); + if (ret) + return ret; + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_PORF, 0); + + return ret; +} + +static int rv3028_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 alarmvals[3]; + int status, ctrl, ret; + + ret = regmap_bulk_read(rv3028->regmap, RV3028_ALARM_MIN, alarmvals, + sizeof(alarmvals)); + if (ret) + return ret; + + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + ret = regmap_read(rv3028->regmap, RV3028_CTRL2, &ctrl); + if (ret < 0) + return ret; + + alrm->time.tm_sec = 0; + alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); + alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); + alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f); + + alrm->enabled = !!(ctrl & RV3028_CTRL2_AIE); + alrm->pending = (status & RV3028_STATUS_AF) && alrm->enabled; + + return 0; +} + +static int rv3028_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 alarmvals[3]; + u8 ctrl = 0; + int ret; + + /* The alarm has no seconds, round up to nearest minute */ + if (alrm->time.tm_sec) { + time64_t alarm_time = rtc_tm_to_time64(&alrm->time); + + alarm_time += 60 - alrm->time.tm_sec; + rtc_time64_to_tm(alarm_time, &alrm->time); + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_AIE | RV3028_CTRL2_UIE, 0); + if (ret) + return ret; + + alarmvals[0] = bin2bcd(alrm->time.tm_min); + alarmvals[1] = bin2bcd(alrm->time.tm_hour); + alarmvals[2] = bin2bcd(alrm->time.tm_mday); + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_AF, 0); + if (ret) + return ret; + + ret = regmap_bulk_write(rv3028->regmap, RV3028_ALARM_MIN, alarmvals, + sizeof(alarmvals)); + if (ret) + return ret; + + if (alrm->enabled) { + if (rv3028->rtc->uie_rtctimer.enabled) + ctrl |= RV3028_CTRL2_UIE; + if (rv3028->rtc->aie_timer.enabled) + ctrl |= RV3028_CTRL2_AIE; + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl); + + return ret; +} + +static int rv3028_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int ctrl = 0, ret; + + if (enabled) { + if (rv3028->rtc->uie_rtctimer.enabled) + ctrl |= RV3028_CTRL2_UIE; + if (rv3028->rtc->aie_timer.enabled) + ctrl |= RV3028_CTRL2_AIE; + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_AF | RV3028_STATUS_UF, 0); + if (ret) + return ret; + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl); + if (ret) + return ret; + + return 0; +} + +static int rv3028_read_offset(struct device *dev, long *offset) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int ret, value, steps; + + ret = regmap_read(rv3028->regmap, RV3028_OFFSET, &value); + if (ret < 0) + return ret; + + steps = sign_extend32(value << 1, 8); + + ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value); + if (ret < 0) + return ret; + + steps += value >> 7; + + *offset = DIV_ROUND_CLOSEST(steps * OFFSET_STEP_PPT, 1000); + + return 0; +} + +static int rv3028_set_offset(struct device *dev, long offset) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int ret; + + offset = clamp(offset, -244141L, 243187L) * 1000; + offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT); + + ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1); + if (ret < 0) + return ret; + + return regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7), + offset << 7); +} + +static int rv3028_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int status, ret = 0; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + if (status & RV3028_STATUS_PORF) + dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n"); + + status &= RV3028_STATUS_PORF; + + if (copy_to_user((void __user *)arg, &status, sizeof(int))) + return -EFAULT; + + return 0; + + case RTC_VL_CLR: + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_PORF, 0); + + return ret; + + default: + return -ENOIOCTLCMD; + } +} + +static int rv3028_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return regmap_bulk_write(priv, RV3028_RAM1 + offset, val, bytes); +} + +static int rv3028_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return regmap_bulk_read(priv, RV3028_RAM1 + offset, val, bytes); +} + +static int rv3028_eeprom_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + u32 status, ctrl1; + int i, ret, err; + u8 *buf = val; + + ret = regmap_read(priv, RV3028_CTRL1, &ctrl1); + if (ret) + return ret; + + if (!(ctrl1 & RV3028_CTRL1_EERD)) { + ret = regmap_update_bits(priv, RV3028_CTRL1, + RV3028_CTRL1_EERD, RV3028_CTRL1_EERD); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + } + + for (i = 0; i < bytes; i++) { + ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_DATA, buf[i]); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_CMD, + RV3028_EEPROM_CMD_WRITE); + if (ret) + goto restore_eerd; + + usleep_range(RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); + + ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + } + +restore_eerd: + if (!(ctrl1 & RV3028_CTRL1_EERD)) + { + err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD, + 0); + if (err && !ret) + ret = err; + } + + return ret; +} + +static int rv3028_eeprom_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + u32 status, ctrl1, data; + int i, ret, err; + u8 *buf = val; + + ret = regmap_read(priv, RV3028_CTRL1, &ctrl1); + if (ret) + return ret; + + if (!(ctrl1 & RV3028_CTRL1_EERD)) { + ret = regmap_update_bits(priv, RV3028_CTRL1, + RV3028_CTRL1_EERD, RV3028_CTRL1_EERD); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + } + + for (i = 0; i < bytes; i++) { + ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_CMD, + RV3028_EEPROM_CMD_READ); + if (ret) + goto restore_eerd; + + ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + + ret = regmap_read(priv, RV3028_EEPROM_DATA, &data); + if (ret) + goto restore_eerd; + buf[i] = data; + } + +restore_eerd: + if (!(ctrl1 & RV3028_CTRL1_EERD)) + { + err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD, + 0); + if (err && !ret) + ret = err; + } + + return ret; +} + +static struct rtc_class_ops rv3028_rtc_ops = { + .read_time = rv3028_get_time, + .set_time = rv3028_set_time, + .read_offset = rv3028_read_offset, + .set_offset = rv3028_set_offset, + .ioctl = rv3028_ioctl, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x37, +}; + +static int rv3028_probe(struct i2c_client *client) +{ + struct rv3028_data *rv3028; + int ret, status; + u32 ohms; + struct nvmem_config nvmem_cfg = { + .name = "rv3028_nvram", + .word_size = 1, + .stride = 1, + .size = 2, + .type = NVMEM_TYPE_BATTERY_BACKED, + .reg_read = rv3028_nvram_read, + .reg_write = rv3028_nvram_write, + }; + struct nvmem_config eeprom_cfg = { + .name = "rv3028_eeprom", + .word_size = 1, + .stride = 1, + .size = 43, + .type = NVMEM_TYPE_EEPROM, + .reg_read = rv3028_eeprom_read, + .reg_write = rv3028_eeprom_write, + }; + + rv3028 = devm_kzalloc(&client->dev, sizeof(struct rv3028_data), + GFP_KERNEL); + if (!rv3028) + return -ENOMEM; + + rv3028->regmap = devm_regmap_init_i2c(client, ®map_config); + + i2c_set_clientdata(client, rv3028); + + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + if (status & RV3028_STATUS_PORF) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + if (status & RV3028_STATUS_AF) + dev_warn(&client->dev, "An alarm may have been missed.\n"); + + rv3028->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rv3028->rtc)) { + return PTR_ERR(rv3028->rtc); + } + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, rv3028_handle_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "rv3028", rv3028); + if (ret) { + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } else { + rv3028_rtc_ops.read_alarm = rv3028_get_alarm; + rv3028_rtc_ops.set_alarm = rv3028_set_alarm; + rv3028_rtc_ops.alarm_irq_enable = rv3028_alarm_irq_enable; + } + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1, + RV3028_CTRL1_WADA, RV3028_CTRL1_WADA); + if (ret) + return ret; + + /* setup timestamping */ + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_EIE | RV3028_CTRL2_TSE, + RV3028_CTRL2_EIE | RV3028_CTRL2_TSE); + if (ret) + return ret; + + /* setup trickle charger */ + if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms", + &ohms)) { + int i; + + for (i = 0; i < ARRAY_SIZE(rv3028_trickle_resistors); i++) + if (ohms == rv3028_trickle_resistors[i]) + break; + + if (i < ARRAY_SIZE(rv3028_trickle_resistors)) { + ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, + RV3028_BACKUP_TCE | + RV3028_BACKUP_TCR_MASK, + RV3028_BACKUP_TCE | i); + if (ret) + return ret; + } else { + dev_warn(&client->dev, "invalid trickle resistor value\n"); + } + } + + ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group); + if (ret) + return ret; + + rv3028->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rv3028->rtc->range_max = RTC_TIMESTAMP_END_2099; + rv3028->rtc->ops = &rv3028_rtc_ops; + ret = rtc_register_device(rv3028->rtc); + if (ret) + return ret; + + nvmem_cfg.priv = rv3028->regmap; + rtc_nvmem_register(rv3028->rtc, &nvmem_cfg); + eeprom_cfg.priv = rv3028->regmap; + rtc_nvmem_register(rv3028->rtc, &eeprom_cfg); + + rv3028->rtc->max_user_freq = 1; + + return 0; +} + +static const struct of_device_id rv3028_of_match[] = { + { .compatible = "microcrystal,rv3028", }, + { } +}; +MODULE_DEVICE_TABLE(of, rv3028_of_match); + +static struct i2c_driver rv3028_driver = { + .driver = { + .name = "rtc-rv3028", + .of_match_table = of_match_ptr(rv3028_of_match), + }, + .probe_new = rv3028_probe, +}; +module_i2c_driver(rv3028_driver); + +MODULE_AUTHOR("Alexandre Belloni "); +MODULE_DESCRIPTION("Micro Crystal RV3028 RTC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 3cbb7b638fd1b1adaf35911a8aa812290727df4f Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 13 Feb 2019 14:38:33 +0100 Subject: rtc: hid-sensor-time: stop selecting IIO Make RTC_DRV_HID_SENSOR_TIME depend on IIO and HID_SENSOR_HUB to remove possible circular dependencies as this is the only symbol selecting those. Suggested-by: Florian Lohoff Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a08785ac8903..5d155ce99517 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1835,8 +1835,7 @@ comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME tristate "HID Sensor Time" depends on USB_HID - select IIO - select HID_SENSOR_HUB + depends on HID_SENSOR_HUB && IIO select HID_SENSOR_IIO_COMMON help Say yes here to build support for the HID Sensors of type Time. -- cgit v1.2.3-59-g8ed1b From 243b44e157598b34cbc62f925cb4b8f725e69b68 Mon Sep 17 00:00:00 2001 From: Artem Panfilov Date: Sun, 17 Feb 2019 21:53:21 +0300 Subject: dt-bindings: rtc: add ABEOZ9 Add ABEOZ9 to the list of trivial RTCs Signed-off-by: Artem Panfilov Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index 3e6a215e7304..fc79d7133218 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -43,6 +43,7 @@ possibly an interrupt line. Compatible Vendor / Chip ========== ============= abracon,abb5zes3 AB-RTCMC-32.768kHz-B5ZE-S3: Real Time Clock/Calendar Module with I2C Interface +abracon,abeoz9 AB-RTCMC-32.768kHz-EOZ9: Real Time Clock/Calendar Module with I2C Interface dallas,ds1374 I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output dallas,ds1672 Dallas DS1672 Real-time Clock dallas,ds3232 Extremely Accurate I²C RTC with Integrated Crystal and SRAM -- cgit v1.2.3-59-g8ed1b From 67075b63cce26c9a235986f163ce890e7bb035a7 Mon Sep 17 00:00:00 2001 From: Artem Panfilov Date: Sun, 17 Feb 2019 21:52:56 +0300 Subject: rtc: add AB-RTCMC-32.768kHz-EOZ9 RTC support This patch adds support for AB-RTCMC-32.768kHz-EOZ9 RTC/Calendar module with I2C interface. Signed-off-by: Artem Panfilov Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-ab-eoz9.c | 465 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 476 insertions(+) create mode 100644 drivers/rtc/rtc-ab-eoz9.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 5d155ce99517..29e8974dc77f 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -185,6 +185,16 @@ config RTC_DRV_ABB5ZES3 This driver can also be built as a module. If so, the module will be called rtc-ab-b5ze-s3. +config RTC_DRV_ABEOZ9 + select REGMAP_I2C + tristate "Abracon AB-RTCMC-32.768kHz-EOZ9" + help + If you say yes here you get support for the Abracon + AB-RTCMC-32.768kHz-EOA9 I2C RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ab-e0z9. + config RTC_DRV_ABX80X tristate "Abracon ABx80x" select WATCHDOG_CORE if WATCHDOG diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6dc1e561f533..1bf9f75a7865 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o +obj-$(CONFIG_RTC_DRV_ABEOZ9) += rtc-ab-eoz9.o obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c new file mode 100644 index 000000000000..e4f6e0061ccf --- /dev/null +++ b/drivers/rtc/rtc-ab-eoz9.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock driver for AB-RTCMC-32.768kHz-EOZ9 chip. + * Copyright (C) 2019 Orolia + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ABEOZ9_REG_CTRL1 0x00 +#define ABEOZ9_REG_CTRL1_MASK GENMASK(7, 0) +#define ABEOZ9_REG_CTRL1_WE BIT(0) +#define ABEOZ9_REG_CTRL1_TE BIT(1) +#define ABEOZ9_REG_CTRL1_TAR BIT(2) +#define ABEOZ9_REG_CTRL1_EERE BIT(3) +#define ABEOZ9_REG_CTRL1_SRON BIT(4) +#define ABEOZ9_REG_CTRL1_TD0 BIT(5) +#define ABEOZ9_REG_CTRL1_TD1 BIT(6) +#define ABEOZ9_REG_CTRL1_CLKINT BIT(7) + +#define ABEOZ9_REG_CTRL_INT 0x01 +#define ABEOZ9_REG_CTRL_INT_AIE BIT(0) +#define ABEOZ9_REG_CTRL_INT_TIE BIT(1) +#define ABEOZ9_REG_CTRL_INT_V1IE BIT(2) +#define ABEOZ9_REG_CTRL_INT_V2IE BIT(3) +#define ABEOZ9_REG_CTRL_INT_SRIE BIT(4) + +#define ABEOZ9_REG_CTRL_INT_FLAG 0x02 +#define ABEOZ9_REG_CTRL_INT_FLAG_AF BIT(0) +#define ABEOZ9_REG_CTRL_INT_FLAG_TF BIT(1) +#define ABEOZ9_REG_CTRL_INT_FLAG_V1IF BIT(2) +#define ABEOZ9_REG_CTRL_INT_FLAG_V2IF BIT(3) +#define ABEOZ9_REG_CTRL_INT_FLAG_SRF BIT(4) + +#define ABEOZ9_REG_CTRL_STATUS 0x03 +#define ABEOZ9_REG_CTRL_STATUS_V1F BIT(2) +#define ABEOZ9_REG_CTRL_STATUS_V2F BIT(3) +#define ABEOZ9_REG_CTRL_STATUS_SR BIT(4) +#define ABEOZ9_REG_CTRL_STATUS_PON BIT(5) +#define ABEOZ9_REG_CTRL_STATUS_EEBUSY BIT(7) + +#define ABEOZ9_REG_SEC 0x08 +#define ABEOZ9_REG_MIN 0x09 +#define ABEOZ9_REG_HOURS 0x0A +#define ABEOZ9_HOURS_PM BIT(6) +#define ABEOZ9_REG_DAYS 0x0B +#define ABEOZ9_REG_WEEKDAYS 0x0C +#define ABEOZ9_REG_MONTHS 0x0D +#define ABEOZ9_REG_YEARS 0x0E + +#define ABEOZ9_SEC_LEN 7 + +#define ABEOZ9_REG_REG_TEMP 0x20 +#define ABEOZ953_TEMP_MAX 120 +#define ABEOZ953_TEMP_MIN -60 + +#define ABEOZ9_REG_EEPROM 0x30 +#define ABEOZ9_REG_EEPROM_MASK GENMASK(8, 0) +#define ABEOZ9_REG_EEPROM_THP BIT(0) +#define ABEOZ9_REG_EEPROM_THE BIT(1) +#define ABEOZ9_REG_EEPROM_FD0 BIT(2) +#define ABEOZ9_REG_EEPROM_FD1 BIT(3) +#define ABEOZ9_REG_EEPROM_R1K BIT(4) +#define ABEOZ9_REG_EEPROM_R5K BIT(5) +#define ABEOZ9_REG_EEPROM_R20K BIT(6) +#define ABEOZ9_REG_EEPROM_R80K BIT(7) + +struct abeoz9_rtc_data { + struct rtc_device *rtc; + struct regmap *regmap; + struct device *hwmon_dev; +}; + +static int abeoz9_check_validity(struct device *dev) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int ret; + int val; + + ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val); + if (ret < 0) { + dev_err(dev, + "unable to get CTRL_STATUS register (%d)\n", ret); + return ret; + } + + if (val & ABEOZ9_REG_CTRL_STATUS_PON) { + dev_warn(dev, "power-on reset detected, date is invalid\n"); + return -EINVAL; + } + + if (val & ABEOZ9_REG_CTRL_STATUS_V1F) { + dev_warn(dev, + "voltage drops below VLOW1 threshold, date is invalid\n"); + return -EINVAL; + } + + if ((val & ABEOZ9_REG_CTRL_STATUS_V2F)) { + dev_warn(dev, + "voltage drops below VLOW2 threshold, date is invalid\n"); + return -EINVAL; + } + + return 0; +} + +static int abeoz9_reset_validity(struct regmap *regmap) +{ + return regmap_update_bits(regmap, ABEOZ9_REG_CTRL_STATUS, + ABEOZ9_REG_CTRL_STATUS_V1F | + ABEOZ9_REG_CTRL_STATUS_V2F | + ABEOZ9_REG_CTRL_STATUS_PON, + 0); +} + +static int abeoz9_rtc_get_time(struct device *dev, struct rtc_time *tm) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + u8 regs[ABEOZ9_SEC_LEN]; + int ret; + + ret = abeoz9_check_validity(dev); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, ABEOZ9_REG_SEC, + regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "reading RTC time failed (%d)\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] & 0x7F); + tm->tm_min = bcd2bin(regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] & 0x7F); + + if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM) { + tm->tm_hour = + bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & 0x1f); + if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM) + tm->tm_hour += 12; + } else { + tm->tm_hour = bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC]); + } + + tm->tm_mday = bcd2bin(regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC]); + tm->tm_wday = bcd2bin(regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC]); + tm->tm_mon = bcd2bin(regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC]) - 1; + tm->tm_year = bcd2bin(regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC]) + 100; + + return ret; +} + +static int abeoz9_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + u8 regs[ABEOZ9_SEC_LEN]; + int ret; + + regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_sec); + regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_min); + regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_hour); + regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mday); + regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_wday); + regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mon + 1); + regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_year - 100); + + ret = regmap_bulk_write(data->regmap, ABEOZ9_REG_SEC, + regs, + sizeof(regs)); + + if (ret) { + dev_err(dev, "set RTC time failed (%d)\n", ret); + return ret; + } + + return abeoz9_reset_validity(regmap); +} + +static int abeoz9_trickle_parse_dt(struct device_node *node) +{ + u32 ohms = 0; + + if (of_property_read_u32(node, "trickle-resistor-ohms", &ohms)) + return 0; + + switch (ohms) { + case 1000: + return ABEOZ9_REG_EEPROM_R1K; + case 5000: + return ABEOZ9_REG_EEPROM_R5K; + case 20000: + return ABEOZ9_REG_EEPROM_R20K; + case 80000: + return ABEOZ9_REG_EEPROM_R80K; + default: + return 0; + } +} + +static int abeoz9_rtc_setup(struct device *dev, struct device_node *node) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int ret; + + /* Enable Self Recovery, Clock for Watch and EEPROM refresh functions */ + ret = regmap_update_bits(regmap, ABEOZ9_REG_CTRL1, + ABEOZ9_REG_CTRL1_MASK, + ABEOZ9_REG_CTRL1_WE | + ABEOZ9_REG_CTRL1_EERE | + ABEOZ9_REG_CTRL1_SRON); + if (ret < 0) { + dev_err(dev, "unable to set CTRL_1 register (%d)\n", ret); + return ret; + } + + ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT, 0); + if (ret < 0) { + dev_err(dev, + "unable to set control CTRL_INT register (%d)\n", + ret); + return ret; + } + + ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT_FLAG, 0); + if (ret < 0) { + dev_err(dev, + "unable to set control CTRL_INT_FLAG register (%d)\n", + ret); + return ret; + } + + ret = abeoz9_trickle_parse_dt(node); + + /* Enable built-in termometer */ + ret |= ABEOZ9_REG_EEPROM_THE; + + ret = regmap_update_bits(regmap, ABEOZ9_REG_EEPROM, + ABEOZ9_REG_EEPROM_MASK, + ret); + if (ret < 0) { + dev_err(dev, "unable to set EEPROM register (%d)\n", ret); + return ret; + } + + return ret; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = abeoz9_rtc_get_time, + .set_time = abeoz9_rtc_set_time, +}; + +static const struct regmap_config abeoz9_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +#if IS_REACHABLE(CONFIG_HWMON) + +static int abeoz9z3_temp_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int ret; + unsigned int val; + + ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val); + if (ret < 0) + return ret; + + if ((val & ABEOZ9_REG_CTRL_STATUS_V1F) || + (val & ABEOZ9_REG_CTRL_STATUS_V2F)) { + dev_err(dev, + "thermometer might be disabled due to low voltage\n"); + return -EINVAL; + } + + switch (attr) { + case hwmon_temp_input: + ret = regmap_read(regmap, ABEOZ9_REG_REG_TEMP, &val); + if (ret < 0) + return ret; + *temp = 1000 * (val + ABEOZ953_TEMP_MIN); + return 0; + case hwmon_temp_max: + *temp = 1000 * ABEOZ953_TEMP_MAX; + return 0; + case hwmon_temp_min: + *temp = 1000 * ABEOZ953_TEMP_MIN; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static umode_t abeoz9_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max: + case hwmon_temp_min: + return 0444; + default: + return 0; + } +} + +static const u32 abeoz9_chip_config[] = { + HWMON_C_REGISTER_TZ, + 0 +}; + +static const struct hwmon_channel_info abeoz9_chip = { + .type = hwmon_chip, + .config = abeoz9_chip_config, +}; + +static const u32 abeoz9_temp_config[] = { + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN, + 0 +}; + +static const struct hwmon_channel_info abeoz9_temp = { + .type = hwmon_temp, + .config = abeoz9_temp_config, +}; + +static const struct hwmon_channel_info *abeoz9_info[] = { + &abeoz9_chip, + &abeoz9_temp, + NULL +}; + +static const struct hwmon_ops abeoz9_hwmon_ops = { + .is_visible = abeoz9_is_visible, + .read = abeoz9z3_temp_read, +}; + +static const struct hwmon_chip_info abeoz9_chip_info = { + .ops = &abeoz9_hwmon_ops, + .info = abeoz9_info, +}; + +static void abeoz9_hwmon_register(struct device *dev, + struct abeoz9_rtc_data *data) +{ + data->hwmon_dev = + devm_hwmon_device_register_with_info(dev, + "abeoz9", + data, + &abeoz9_chip_info, + NULL); + if (IS_ERR(data->hwmon_dev)) { + dev_warn(dev, "unable to register hwmon device %ld\n", + PTR_ERR(data->hwmon_dev)); + } +} + +#else + +static void abeoz9_hwmon_register(struct device *dev, + struct abeoz9_rtc_data *data) +{ +} + +#endif + +static int abeoz9_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct abeoz9_rtc_data *data = NULL; + struct device *dev = &client->dev; + struct regmap *regmap; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + ret = -ENODEV; + goto err; + } + + regmap = devm_regmap_init_i2c(client, &abeoz9_rtc_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "regmap allocation failed: %d\n", ret); + goto err; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + + data->regmap = regmap; + dev_set_drvdata(dev, data); + + ret = abeoz9_rtc_setup(dev, client->dev.of_node); + if (ret) + goto err; + + data->rtc = devm_rtc_allocate_device(dev); + ret = PTR_ERR_OR_ZERO(data->rtc); + if (ret) + goto err; + + data->rtc->ops = &rtc_ops; + data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + data->rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = rtc_register_device(data->rtc); + if (ret) + goto err; + + abeoz9_hwmon_register(dev, data); + return 0; + +err: + dev_err(dev, "unable to register RTC device (%d)\n", ret); + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id abeoz9_dt_match[] = { + { .compatible = "abracon,abeoz9" }, + { }, +}; +MODULE_DEVICE_TABLE(of, abeoz9_dt_match); +#endif + +static const struct i2c_device_id abeoz9_id[] = { + { "abeoz9", 0 }, + { } +}; + +static struct i2c_driver abeoz9_driver = { + .driver = { + .name = "rtc-ab-eoz9", + .of_match_table = of_match_ptr(abeoz9_dt_match), + }, + .probe = abeoz9_probe, + .id_table = abeoz9_id, +}; + +module_i2c_driver(abeoz9_driver); + +MODULE_AUTHOR("Artem Panfilov "); +MODULE_DESCRIPTION("Abracon AB-RTCMC-32.768kHz-EOZ9 RTC driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From ad5ea5b9d513107869acd460f0180d8fb94856b9 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 25 Feb 2019 21:20:45 +0100 Subject: rtc: remove rtc_class_ops.read_callback Since commit 416f0e8056f7 ("RTC: sa1100: Update the sa1100 RTC driver."), the last user of .read_callback is gone. It has been 8 years and now new user appeared. Simply remove it. Signed-off-by: Alexandre Belloni --- drivers/rtc/dev.c | 5 ----- include/linux/rtc.h | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index 43d962a9c210..1d006ef4bb57 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -178,11 +178,6 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) remove_wait_queue(&rtc->irq_queue, &wait); if (ret == 0) { - /* Check for any data updates */ - if (rtc->ops->read_callback) - data = rtc->ops->read_callback(rtc->dev.parent, - data); - if (sizeof(int) != sizeof(long) && count == sizeof(unsigned int)) ret = put_user(data, (unsigned int __user *)buf) ?: diff --git a/include/linux/rtc.h b/include/linux/rtc.h index c1089fe5344a..f89bfbb54902 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -67,7 +67,7 @@ extern struct class *rtc_class; * * The (current) exceptions are mostly filesystem hooks: * - the proc() hook for procfs - * - non-ioctl() chardev hooks: open(), release(), read_callback() + * - non-ioctl() chardev hooks: open(), release() * * REVISIT those periodic irq calls *do* have ops_lock when they're * issued through ioctl() ... @@ -81,7 +81,6 @@ struct rtc_class_ops { int (*proc)(struct device *, struct seq_file *); int (*set_mmss64)(struct device *, time64_t secs); int (*set_mmss)(struct device *, unsigned long secs); - int (*read_callback)(struct device *, int data); int (*alarm_irq_enable)(struct device *, unsigned int enabled); int (*read_offset)(struct device *, long *offset); int (*set_offset)(struct device *, long offset); -- cgit v1.2.3-59-g8ed1b From 069e28e5715e519118dc5a17aa79887c16be8575 Mon Sep 17 00:00:00 2001 From: Alexey Roslyakov Date: Tue, 19 Feb 2019 15:39:29 +0700 Subject: rtc: pcf85063: remove dead code Some of defines are not in use since 7b5768486a910532885f01b9d2dad4818c8b3be1. Remove it to make the code easier to read and understand. Signed-off-by: Alexey Roslyakov Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pcf85063.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c index b79af6e96292..f6ce63c443a0 100644 --- a/drivers/rtc/rtc-pcf85063.c +++ b/drivers/rtc/rtc-pcf85063.c @@ -29,16 +29,9 @@ #define PCF85063_REG_CTRL1 0x00 /* status */ #define PCF85063_REG_CTRL1_CAP_SEL BIT(0) #define PCF85063_REG_CTRL1_STOP BIT(5) -#define PCF85063_REG_CTRL2 0x01 #define PCF85063_REG_SC 0x04 /* datetime */ #define PCF85063_REG_SC_OS 0x80 -#define PCF85063_REG_MN 0x05 -#define PCF85063_REG_HR 0x06 -#define PCF85063_REG_DM 0x07 -#define PCF85063_REG_DW 0x08 -#define PCF85063_REG_MO 0x09 -#define PCF85063_REG_YR 0x0A static struct i2c_driver pcf85063_driver; -- cgit v1.2.3-59-g8ed1b From 57e883b576dc056f59a5612713b679432db36bfa Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 21 Feb 2019 09:40:44 +0000 Subject: dt-bindings: rtc: add rx8571 compatible The Epson rx8571 is compatible with the Epson rx8581 RTC. The difference between the rx8571 and rx8581 RTC is that the former has 16 bytes more RAM. This renders them incompatible from a DT point of view. Signed-off-by: Biju Das Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index fc79d7133218..f4687c68c08c 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -48,6 +48,7 @@ dallas,ds1374 I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and dallas,ds1672 Dallas DS1672 Real-time Clock dallas,ds3232 Extremely Accurate I²C RTC with Integrated Crystal and SRAM epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE +epson,rx8571 I2C-BUS INTERFACE REAL TIME CLOCK MODULE with Battery Backed RAM epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE emmicro,em3027 EM Microelectronic EM3027 Real-time Clock isil,isl1208 Intersil ISL1208 Low Power RTC with Battery Backed SRAM -- cgit v1.2.3-59-g8ed1b From 51f896ffd1a5aacbda82ed82552c4077e3cc3b68 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 21 Feb 2019 09:40:45 +0000 Subject: rtc: rx8581: Add support for Epson rx8571 RTC Add support for Epson rx8571 real-time clock. rx8571 rtc is compatible with rx8581,except that rx8571 has additional 16 bytes of RAM. 16 bytes of nvmem is supported and exposed in sysfs (# is the instance number,starting with 0): /sys/bus/nvmem/devices/rx8571-#/nvmem Signed-off-by: Biju Das Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 5 ++- drivers/rtc/rtc-rx8581.c | 114 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 108 insertions(+), 11 deletions(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 29e8974dc77f..e1a1f2b1cbba 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -611,9 +611,10 @@ config RTC_DRV_RX8010 will be called rtc-rx8010. config RTC_DRV_RX8581 - tristate "Epson RX-8581" + tristate "Epson RX-8571/RX-8581" help - If you say yes here you will get support for the Epson RX-8581. + If you say yes here you will get support for the Epson RX-8571/ + RX-8581. This driver can also be built as a module. If so the module will be called rtc-rx8581. diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c index eac882169744..776e3a2b89e8 100644 --- a/drivers/rtc/rtc-rx8581.c +++ b/drivers/rtc/rtc-rx8581.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -51,11 +53,19 @@ #define RX8581_CTRL_STOP 0x02 /* STOP bit */ #define RX8581_CTRL_RESET 0x01 /* RESET bit */ +#define RX8571_USER_RAM 0x10 +#define RX8571_NVRAM_SIZE 0x10 + struct rx8581 { struct regmap *regmap; struct rtc_device *rtc; }; +struct rx85x1_config { + struct regmap_config regmap; + unsigned int num_nvram; +}; + /* * In the routines that deal directly with the rx8581 hardware, we use * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. @@ -181,25 +191,103 @@ static const struct rtc_class_ops rx8581_rtc_ops = { .set_time = rx8581_rtc_set_time, }; -static int rx8581_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rx8571_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) { - struct rx8581 *rx8581; - static const struct regmap_config config = { + struct rx8581 *rx8581 = priv; + + return regmap_bulk_read(rx8581->regmap, RX8571_USER_RAM + offset, + val, bytes); +} + +static int rx8571_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct rx8581 *rx8581 = priv; + + return regmap_bulk_write(rx8581->regmap, RX8571_USER_RAM + offset, + val, bytes); +} + +static int rx85x1_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct rx8581 *rx8581 = priv; + unsigned int tmp_val; + int ret; + + ret = regmap_read(rx8581->regmap, RX8581_REG_RAM, &tmp_val); + (*(unsigned char *)val) = (unsigned char) tmp_val; + + return ret; +} + +static int rx85x1_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct rx8581 *rx8581 = priv; + unsigned char tmp_val; + + tmp_val = *((unsigned char *)val); + return regmap_write(rx8581->regmap, RX8581_REG_RAM, + (unsigned int)tmp_val); +} + +static const struct rx85x1_config rx8581_config = { + .regmap = { .reg_bits = 8, .val_bits = 8, .max_register = 0xf, + }, + .num_nvram = 1 +}; + +static const struct rx85x1_config rx8571_config = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x1f, + }, + .num_nvram = 2 +}; + +static int rx8581_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rx8581 *rx8581; + const struct rx85x1_config *config = &rx8581_config; + const void *data = of_device_get_match_data(&client->dev); + static struct nvmem_config nvmem_cfg[] = { + { + .name = "rx85x1-", + .word_size = 1, + .stride = 1, + .size = 1, + .reg_read = rx85x1_nvram_read, + .reg_write = rx85x1_nvram_write, + }, { + .name = "rx8571-", + .word_size = 1, + .stride = 1, + .size = RX8571_NVRAM_SIZE, + .reg_read = rx8571_nvram_read, + .reg_write = rx8571_nvram_write, + }, }; + int ret, i; dev_dbg(&client->dev, "%s\n", __func__); + if (data) + config = data; + rx8581 = devm_kzalloc(&client->dev, sizeof(struct rx8581), GFP_KERNEL); if (!rx8581) return -ENOMEM; i2c_set_clientdata(client, rx8581); - rx8581->regmap = devm_regmap_init_i2c(client, &config); + rx8581->regmap = devm_regmap_init_i2c(client, &config->regmap); if (IS_ERR(rx8581->regmap)) return PTR_ERR(rx8581->regmap); @@ -213,7 +301,14 @@ static int rx8581_probe(struct i2c_client *client, rx8581->rtc->start_secs = 0; rx8581->rtc->set_start_time = true; - return rtc_register_device(rx8581->rtc); + ret = rtc_register_device(rx8581->rtc); + + for (i = 0; i < config->num_nvram; i++) { + nvmem_cfg[i].priv = rx8581; + rtc_nvmem_register(rx8581->rtc, &nvmem_cfg[i]); + } + + return ret; } static const struct i2c_device_id rx8581_id[] = { @@ -223,8 +318,9 @@ static const struct i2c_device_id rx8581_id[] = { MODULE_DEVICE_TABLE(i2c, rx8581_id); static const struct of_device_id rx8581_of_match[] = { - { .compatible = "epson,rx8581" }, - { } + { .compatible = "epson,rx8571", .data = &rx8571_config }, + { .compatible = "epson,rx8581", .data = &rx8581_config }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rx8581_of_match); @@ -240,5 +336,5 @@ static struct i2c_driver rx8581_driver = { module_i2c_driver(rx8581_driver); MODULE_AUTHOR("Martyn Welch "); -MODULE_DESCRIPTION("Epson RX-8581 RTC driver"); +MODULE_DESCRIPTION("Epson RX-8571/RX-8581 RTC driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 30adde6b181104249fb6c17b72a9bf12332bc053 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sun, 3 Mar 2019 22:12:38 +0100 Subject: rtc: imx-sc: use rtc_time64_to_tm The imx-sc driver properly sets range_max, use rtc_time64_to_tm() instead of the deprecated rtc_time_to_tm() Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-imx-sc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-imx-sc.c b/drivers/rtc/rtc-imx-sc.c index 60570a278bb0..19642bfd913a 100644 --- a/drivers/rtc/rtc-imx-sc.c +++ b/drivers/rtc/rtc-imx-sc.c @@ -41,7 +41,7 @@ static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm) return ret; } - rtc_time_to_tm(msg.time, tm); + rtc_time64_to_tm(msg.time, tm); return 0; } -- cgit v1.2.3-59-g8ed1b From b8541798a8a6a288cb2f5c540bf5270e118c0b0c Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sun, 3 Mar 2019 22:24:44 +0100 Subject: rtc: zynqmp: fix possible race condition The IRQ is requested before the struct rtc is allocated and registered, but this struct is used in the IRQ handler. This may lead to a NULL pointer dereference. Switch to devm_rtc_allocate_device/rtc_register_device to allocate the rtc struct before requesting the IRQ. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-zynqmp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c index c532bd13fbe5..f0dd20785b54 100644 --- a/drivers/rtc/rtc-zynqmp.c +++ b/drivers/rtc/rtc-zynqmp.c @@ -222,6 +222,12 @@ static int xlnx_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, xrtcdev); + xrtcdev->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(xrtcdev->rtc)) + return PTR_ERR(xrtcdev->rtc); + + xrtcdev->rtc->ops = &xlnx_rtc_ops; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); xrtcdev->reg_base = devm_ioremap_resource(&pdev->dev, res); @@ -263,9 +269,7 @@ static int xlnx_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); - xrtcdev->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &xlnx_rtc_ops, THIS_MODULE); - return PTR_ERR_OR_ZERO(xrtcdev->rtc); + return rtc_register_device(xrtcdev->rtc); } static int xlnx_rtc_remove(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 3199fc3d7417282f60387086d26c60d3b32bc4dc Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sun, 3 Mar 2019 22:38:34 +0100 Subject: rtc: zynqmp: let the core handle range Let the core handle the RTC range instead of open coding it. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-zynqmp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c index f0dd20785b54..bb950945ec7f 100644 --- a/drivers/rtc/rtc-zynqmp.c +++ b/drivers/rtc/rtc-zynqmp.c @@ -49,7 +49,6 @@ #define RTC_CALIB_DEF 0x198233 #define RTC_CALIB_MASK 0x1FFFFF -#define RTC_SEC_MAX_VAL 0xFFFFFFFF struct xlnx_rtc_dev { struct rtc_device *rtc; @@ -71,9 +70,6 @@ static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) */ new_time = rtc_tm_to_time64(tm) + 1; - if (new_time > RTC_SEC_MAX_VAL) - return -EINVAL; - /* * Writing into calibration register will clear the Tick Counter and * force the next second to be signaled exactly in 1 second period @@ -154,9 +150,6 @@ static int xlnx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) alarm_time = rtc_tm_to_time64(&alrm->time); - if (alarm_time > RTC_SEC_MAX_VAL) - return -EINVAL; - writel((u32)alarm_time, (xrtcdev->reg_base + RTC_ALRM)); xlnx_rtc_alarm_irq_enable(dev, alrm->enabled); @@ -227,6 +220,7 @@ static int xlnx_rtc_probe(struct platform_device *pdev) return PTR_ERR(xrtcdev->rtc); xrtcdev->rtc->ops = &xlnx_rtc_ops; + xrtcdev->rtc->range_max = U32_MAX; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- cgit v1.2.3-59-g8ed1b From 72d381974403018562ca7fa0fbc34d07202e7e1c Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sun, 3 Mar 2019 22:54:49 +0100 Subject: rtc: tx4939: remove useless test The tested condition will never happen as the core always passes a fully set struct tm (using rtc_ktime_to_tm) to the .set_alarm callback. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-tx4939.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index 61c110b2045f..8497fceea27f 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -123,13 +123,6 @@ static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) unsigned long sec; unsigned char buf[6]; - if (alrm->time.tm_sec < 0 || - alrm->time.tm_min < 0 || - alrm->time.tm_hour < 0 || - alrm->time.tm_mday < 0 || - alrm->time.tm_mon < 0 || - alrm->time.tm_year < 0) - return -EINVAL; rtc_tm_to_time(&alrm->time, &sec); buf[0] = 0; buf[1] = 0; -- cgit v1.2.3-59-g8ed1b From e866cbdf6e775d14e0c7c3528b2d03ccc29647c1 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sun, 3 Mar 2019 23:08:09 +0100 Subject: rtc: tx4939: set range The TX4939 RTC is a 48bit counter that counts two on every clock edge of 32.768 KHz oscillator clock so it counts 32bit seconds. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-tx4939.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index 8497fceea27f..b405818933fb 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -276,6 +276,7 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev) rtc->ops = &tx4939_rtc_ops; rtc->nvram_old_abi = true; + rtc->range_max = U32_MAX; pdata->rtc = rtc; -- cgit v1.2.3-59-g8ed1b From e230b2c26dbad465f9e8a11a33be7b67b0be242e Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sun, 3 Mar 2019 23:14:35 +0100 Subject: rtc: tx4939: switch to rtc_time64_to_tm/rtc_tm_to_time64 Call the 64bit versions of rtc_time_to_tm now that the range is enforced by the core. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-tx4939.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index b405818933fb..8acb8adc11d7 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -111,7 +111,7 @@ static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm) spin_unlock_irq(&pdata->lock); sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2]; - rtc_time_to_tm(sec, tm); + rtc_time64_to_tm(sec, tm); return 0; } @@ -123,7 +123,7 @@ static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) unsigned long sec; unsigned char buf[6]; - rtc_tm_to_time(&alrm->time, &sec); + sec = rtc_tm_to_time64(&alrm->time); buf[0] = 0; buf[1] = 0; buf[2] = sec; @@ -166,7 +166,7 @@ static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) spin_unlock_irq(&pdata->lock); sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2]; - rtc_time_to_tm(sec, &alrm->time); + rtc_time64_to_tm(sec, &alrm->time); return rtc_valid_tm(&alrm->time); } -- cgit v1.2.3-59-g8ed1b From 1ebb909d5aa2c53973cbdcde7629fa50c0a56c21 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sun, 3 Mar 2019 23:19:30 +0100 Subject: rtc: tx4939: use .set_time Switch from .set_mmss to .set_time as the former is deprecated. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-tx4939.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index 8acb8adc11d7..404de455409b 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -65,10 +65,11 @@ static int tx4939_rtc_cmd(struct tx4939_rtc_reg __iomem *rtcreg, int cmd) return 0; } -static int tx4939_rtc_set_mmss(struct device *dev, unsigned long secs) +static int tx4939_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev); struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg; + unsigned long secs = rtc_tm_to_time64(tm); int i, ret; unsigned char buf[6]; @@ -203,7 +204,7 @@ static const struct rtc_class_ops tx4939_rtc_ops = { .read_time = tx4939_rtc_read_time, .read_alarm = tx4939_rtc_read_alarm, .set_alarm = tx4939_rtc_set_alarm, - .set_mmss = tx4939_rtc_set_mmss, + .set_time = tx4939_rtc_set_time, .alarm_irq_enable = tx4939_rtc_alarm_irq_enable, }; -- cgit v1.2.3-59-g8ed1b From 1a9de2100d1c3ef7cb685845726438767e44c24e Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 4 Mar 2019 20:02:05 +0100 Subject: rtc: tx4939: convert to SPDX identifier Use SPDX-License-Identifier instead of a verbose license text. The original text refers to the COPYING file in the main directory which is GPL v2 only so also update MODULE_LICENSE() to "GPL v2" instead of "GPL". Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-tx4939.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index 404de455409b..2d24babc4057 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * TX4939 internal RTC driver * Based on RBTX49xx patch from CELF patch archive. * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * * (C) Copyright TOSHIBA CORPORATION 2005-2007 */ #include @@ -310,5 +307,5 @@ module_platform_driver_probe(tx4939_rtc_driver, tx4939_rtc_probe); MODULE_AUTHOR("Atsushi Nemoto "); MODULE_DESCRIPTION("TX4939 internal RTC driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:tx4939rtc"); -- cgit v1.2.3-59-g8ed1b From 2e17f8b982c38719b3e19e33ef85e8188df7cfd0 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 4 Mar 2019 11:03:46 +0100 Subject: rtc: rv8803: let the core handle range Let the core handle the RTC range instead of open coding it. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rv8803.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 450a0b831a2d..df000a68d6b1 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -236,9 +236,6 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm) u8 date[7]; int ctrl, flags, ret; - if ((tm->tm_year < 100) || (tm->tm_year > 199)) - return -EINVAL; - ctrl = rv8803_read_reg(rv8803->client, RV8803_CTRL); if (ctrl < 0) return ctrl; @@ -602,6 +599,8 @@ static int rv8803_probe(struct i2c_client *client, rv8803->rtc->ops = &rv8803_rtc_ops; rv8803->rtc->nvram_old_abi = true; + rv8803->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rv8803->rtc->range_max = RTC_TIMESTAMP_END_2099; err = rtc_register_device(rv8803->rtc); if (err) return err; -- cgit v1.2.3-59-g8ed1b From 182ae2bb640b9202cc29ad6fba92057aefcbe719 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 4 Mar 2019 20:12:17 +0100 Subject: rtc: rv8803: convert to SPDX identifier Use SPDX-License-Identifier instead of a verbose license text. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rv8803.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index df000a68d6b1..7ffd18ab0cdd 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * RTC driver for the Micro Crystal RV8803 * * Copyright (C) 2015 Micro Crystal SA - * * Alexandre Belloni * - * 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 -- cgit v1.2.3-59-g8ed1b From 7d1e5bfe17a69cbe60b46bea8a1be51520d514d3 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 4 Mar 2019 20:17:38 +0100 Subject: rtc: update my email address Use my current email address. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-abx80x.c | 4 ++-- drivers/rtc/rtc-rv8803.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index b76f65983658..6ddcad642d1e 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -5,7 +5,7 @@ * Copyright 2014-2015 Macq S.A. * * Author: Philippe De Muyter - * Author: Alexandre Belloni + * Author: Alexandre Belloni * * 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 @@ -906,6 +906,6 @@ static struct i2c_driver abx80x_driver = { module_i2c_driver(abx80x_driver); MODULE_AUTHOR("Philippe De Muyter "); -MODULE_AUTHOR("Alexandre Belloni "); +MODULE_AUTHOR("Alexandre Belloni "); MODULE_DESCRIPTION("Abracon ABX80X RTC driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 7ffd18ab0cdd..0b102c3cf5a4 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -3,7 +3,7 @@ * RTC driver for the Micro Crystal RV8803 * * Copyright (C) 2015 Micro Crystal SA - * Alexandre Belloni + * Alexandre Belloni * */ @@ -643,6 +643,6 @@ static struct i2c_driver rv8803_driver = { }; module_i2c_driver(rv8803_driver); -MODULE_AUTHOR("Alexandre Belloni "); +MODULE_AUTHOR("Alexandre Belloni "); MODULE_DESCRIPTION("Micro Crystal RV8803 RTC driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 6515e23b9fdeb9a69caee79f6a71dd6d82125f66 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 4 Mar 2019 11:16:30 +0100 Subject: rtc: pic32: convert to devm_rtc_allocate_device This allows further improvement of the driver. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pic32.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/rtc/rtc-pic32.c b/drivers/rtc/rtc-pic32.c index d7ef0a6f8931..43bd701daef2 100644 --- a/drivers/rtc/rtc-pic32.c +++ b/drivers/rtc/rtc-pic32.c @@ -348,13 +348,15 @@ static int pic32_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); - pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &pic32_rtcops, - THIS_MODULE); - if (IS_ERR(pdata->rtc)) { - ret = PTR_ERR(pdata->rtc); + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + pdata->rtc->ops = &pic32_rtcops; + + ret = rtc_register_device(pdata->rtc); + if (ret) goto err_nortc; - } pdata->rtc->max_user_freq = 128; -- cgit v1.2.3-59-g8ed1b From c145e5f4a4b76971d42098775a2443bdd4884de4 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 4 Mar 2019 11:19:02 +0100 Subject: rtc: pic32: let the core handle range Let the core handle the RTC range instead of open coding it. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pic32.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-pic32.c b/drivers/rtc/rtc-pic32.c index 43bd701daef2..7824acc18b6e 100644 --- a/drivers/rtc/rtc-pic32.c +++ b/drivers/rtc/rtc-pic32.c @@ -180,22 +180,16 @@ static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm) { struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); void __iomem *base = pdata->reg_base; - int year = tm->tm_year - 100; dev_dbg(dev, "set time %ptR\n", tm); - if (year < 0 || year >= 100) { - dev_err(dev, "rtc only supports 100 years\n"); - return -EINVAL; - } - clk_enable(pdata->clk); writeb(bin2bcd(tm->tm_sec), base + PIC32_RTCSEC); writeb(bin2bcd(tm->tm_min), base + PIC32_RTCMIN); writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR); writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY); writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON); - writeb(bin2bcd(year), base + PIC32_RTCYEAR); + writeb(bin2bcd(tm->tm_year - 100), base + PIC32_RTCYEAR); clk_disable(pdata->clk); return 0; @@ -353,6 +347,8 @@ static int pic32_rtc_probe(struct platform_device *pdev) return PTR_ERR(pdata->rtc); pdata->rtc->ops = &pic32_rtcops; + pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + pdata->rtc->range_max = RTC_TIMESTAMP_END_2099; ret = rtc_register_device(pdata->rtc); if (ret) -- cgit v1.2.3-59-g8ed1b From e91b94fd2bc411b5926031496bbc3de9a96bb1c6 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 4 Mar 2019 20:23:15 +0100 Subject: rtc: pic32: convert to SPDX identifier Use SPDX-License-Identifier instead of a verbose license text. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pic32.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/rtc/rtc-pic32.c b/drivers/rtc/rtc-pic32.c index 7824acc18b6e..1c4de6e90da0 100644 --- a/drivers/rtc/rtc-pic32.c +++ b/drivers/rtc/rtc-pic32.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * PIC32 RTC driver * * Joshua Henderson * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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 #include -- cgit v1.2.3-59-g8ed1b