From 6a6af3d04435adfdaab363624ec569a9b5d3973c Mon Sep 17 00:00:00 2001 From: Gabriele Mazzotta Date: Tue, 4 Oct 2016 00:50:28 +0200 Subject: rtc: cmos: Reject unsupported alarm values Some platforms allows to specify the month and day of the month in which an alarm should go off, some others the day of the month and some others just the time. Currently any given value is accepted by the driver and only the supported fields are used to program the hardware. As consequence, alarms are potentially programmed to go off in the wrong moment. Fix this by rejecting any unsupported value. Signed-off-by: Gabriele Mazzotta Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-cmos.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index dd3d59806ffa..903a82ae83f0 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -325,14 +325,86 @@ static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask) cmos_checkintr(cmos, rtc_control); } +static int cmos_validate_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct rtc_time now; + + cmos_read_time(dev, &now); + + if (!cmos->day_alrm) { + time64_t t_max_date; + time64_t t_alrm; + + t_max_date = rtc_tm_to_time64(&now); + t_max_date += 24 * 60 * 60 - 1; + t_alrm = rtc_tm_to_time64(&t->time); + if (t_alrm > t_max_date) { + dev_err(dev, + "Alarms can be up to one day in the future\n"); + return -EINVAL; + } + } else if (!cmos->mon_alrm) { + struct rtc_time max_date = now; + time64_t t_max_date; + time64_t t_alrm; + int max_mday; + + if (max_date.tm_mon == 11) { + max_date.tm_mon = 0; + max_date.tm_year += 1; + } else { + max_date.tm_mon += 1; + } + max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year); + if (max_date.tm_mday > max_mday) + max_date.tm_mday = max_mday; + + t_max_date = rtc_tm_to_time64(&max_date); + t_max_date -= 1; + t_alrm = rtc_tm_to_time64(&t->time); + if (t_alrm > t_max_date) { + dev_err(dev, + "Alarms can be up to one month in the future\n"); + return -EINVAL; + } + } else { + struct rtc_time max_date = now; + time64_t t_max_date; + time64_t t_alrm; + int max_mday; + + max_date.tm_year += 1; + max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year); + if (max_date.tm_mday > max_mday) + max_date.tm_mday = max_mday; + + t_max_date = rtc_tm_to_time64(&max_date); + t_max_date -= 1; + t_alrm = rtc_tm_to_time64(&t->time); + if (t_alrm > t_max_date) { + dev_err(dev, + "Alarms can be up to one year in the future\n"); + return -EINVAL; + } + } + + return 0; +} + static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct cmos_rtc *cmos = dev_get_drvdata(dev); unsigned char mon, mday, hrs, min, sec, rtc_control; + int ret; if (!is_valid_irq(cmos->irq)) return -EIO; + ret = cmos_validate_alarm(dev, t); + if (ret < 0) + return ret; + mon = t->time.tm_mon + 1; mday = t->time.tm_mday; hrs = t->time.tm_hour; -- cgit v1.2.3 From 290cd0f07f0c55f4071aee47b5d4cc3c83da588c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Oct 2016 15:30:53 +0200 Subject: rtc: cmos: don't refer to asm-generic/rtc.h That header has been gone for a while. I've fixed up the Kconfig comment, but the one in rtc-cmos.c doesn't make any sense to me even looking at its history. Signed-off-by: Christoph Hellwig Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 4 ++-- drivers/rtc/rtc-cmos.c | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e859d148aba9..4cbea34e6003 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -820,8 +820,8 @@ config RTC_DRV_RV3029_HWMON comment "Platform RTC drivers" -# this 'CMOS' RTC driver is arch dependent because -# requires defining CMOS_READ/CMOS_WRITE, and a +# this 'CMOS' RTC driver is arch dependent because it requires +# defining CMOS_READ/CMOS_WRITE, and a # global rtc_lock ... it's not yet just another platform_device. config RTC_DRV_CMOS diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 903a82ae83f0..390172bac39b 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -772,9 +772,6 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) spin_unlock_irq(&rtc_lock); - /* FIXME: - * doesn't know 12-hour mode either. - */ if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) { dev_warn(dev, "only 24-hr supported\n"); retval = -ENXIO; -- cgit v1.2.3 From c18b4c52c755175dc62aed7b409dca24f028ce76 Mon Sep 17 00:00:00 2001 From: Mirza Krak Date: Mon, 17 Oct 2016 15:53:31 +0200 Subject: rtc: pcf85063: do not register a RTC device if chip is not present Add a sanity check to see if chip is present. If we can not communicate with the chip there is no point in registering a RTC device. Signed-off-by: Mirza Krak Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pcf85063.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c index efb0a08ac117..a06dff994c83 100644 --- a/drivers/rtc/rtc-pcf85063.c +++ b/drivers/rtc/rtc-pcf85063.c @@ -191,12 +191,19 @@ static int pcf85063_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct rtc_device *rtc; + int err; dev_dbg(&client->dev, "%s\n", __func__); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; + err = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1); + if (err < 0) { + dev_err(&client->dev, "RTC chip is not present\n"); + return err; + } + rtc = devm_rtc_device_register(&client->dev, pcf85063_driver.driver.name, &pcf85063_rtc_ops, THIS_MODULE); -- cgit v1.2.3 From 87ebfd6439c12df09c3b4e319e1fa9e1ebba3d55 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 31 Oct 2016 14:55:25 -0400 Subject: rtc: make rtc-lib explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/rtc/Kconfig:config RTC_LIB drivers/rtc/Kconfig: bool ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag since all that information is already contained at the top of the file in the comments. We don't replace module.h with init.h since the file doesn't need that. However we do add export.h since the file uses EXPORT_SYMBOL. Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: rtc-linux@googlegroups.com Signed-off-by: Paul Gortmaker Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-lib.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index e6bfb9c42a10..1ae7da5cfc60 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -11,7 +11,7 @@ * published by the Free Software Foundation. */ -#include +#include #include static const unsigned char rtc_days_in_month[] = { @@ -148,5 +148,3 @@ struct rtc_time rtc_ktime_to_tm(ktime_t kt) return ret; } EXPORT_SYMBOL_GPL(rtc_ktime_to_tm); - -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 683cec8a06a0f1e67ff8f00791471e3ff04ce0f0 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 31 Oct 2016 14:55:26 -0400 Subject: rtc: sparc: make starfire explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/rtc/Kconfig:config RTC_DRV_STARFIRE drivers/rtc/Kconfig: bool "Starfire RTC" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information was (or is now) contained at the top of the file in the comments. Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: "David S. Miller" Cc: sparclinux@vger.kernel.org Cc: rtc-linux@googlegroups.com Signed-off-by: Paul Gortmaker Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-starfire.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c index 83a057a03060..7fc36973fa33 100644 --- a/drivers/rtc/rtc-starfire.c +++ b/drivers/rtc/rtc-starfire.c @@ -1,20 +1,18 @@ /* rtc-starfire.c: Starfire platform RTC driver. + * + * Author: David S. Miller + * License: GPL * * Copyright (C) 2008 David S. Miller */ #include -#include #include #include #include #include -MODULE_AUTHOR("David S. Miller "); -MODULE_DESCRIPTION("Starfire RTC driver"); -MODULE_LICENSE("GPL"); - static u32 starfire_get_time(void) { static char obp_gettod[32]; @@ -57,4 +55,4 @@ static struct platform_driver starfire_rtc_driver = { }, }; -module_platform_driver_probe(starfire_rtc_driver, starfire_rtc_probe); +builtin_platform_driver_probe(starfire_rtc_driver, starfire_rtc_probe); -- cgit v1.2.3 From 8b6102534cef64624caca7eb0a15e83f5ab9864c Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 31 Oct 2016 14:55:27 -0400 Subject: rtc: sparc: make sun4v explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/rtc/Kconfig:config RTC_DRV_SUN4V drivers/rtc/Kconfig: bool "SUN4V Hypervisor RTC" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information was (or is now) contained at the top of the file in the comments. Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: "David S. Miller" Cc: sparclinux@vger.kernel.org Cc: rtc-linux@googlegroups.com Signed-off-by: Paul Gortmaker Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-sun4v.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c index 7c696c12f28f..11bc562eba5d 100644 --- a/drivers/rtc/rtc-sun4v.c +++ b/drivers/rtc/rtc-sun4v.c @@ -1,4 +1,7 @@ /* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems. + * + * Author: David S. Miller + * License: GPL * * Copyright (C) 2008 David S. Miller */ @@ -6,7 +9,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include #include #include @@ -98,8 +100,4 @@ static struct platform_driver sun4v_rtc_driver = { }, }; -module_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe); - -MODULE_AUTHOR("David S. Miller "); -MODULE_DESCRIPTION("SUN4V RTC driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe); -- cgit v1.2.3 From cd563200c090de628eb71c7478f272eaedc6fa4d Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 31 Oct 2016 21:39:45 +0100 Subject: rtc: jz4740: Add support for the RTC in the jz4780 SoC The RTC unit present in the JZ4780 works mostly the same as the one in the JZ4740. The major difference is that register writes need to be explicitly enabled, by writing a magic code (0xA55A) to a "write enable" register before each access. Signed-off-by: Paul Cercueil Acked-by: Maarten ter Huurne Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 6 +++--- drivers/rtc/rtc-jz4740.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4cbea34e6003..1d0ae30e9927 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1550,10 +1550,10 @@ config RTC_DRV_MPC5121 config RTC_DRV_JZ4740 tristate "Ingenic JZ4740 SoC" - depends on MACH_JZ4740 || COMPILE_TEST + depends on MACH_INGENIC || COMPILE_TEST help - If you say yes here you get support for the Ingenic JZ4740 SoC RTC - controller. + If you say yes here you get support for the Ingenic JZ47xx SoCs RTC + controllers. This driver can also be buillt as a module. If so, the module will be called rtc-jz4740. diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 5e14651b71a8..7ad2d7965b4e 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -29,6 +29,10 @@ #define JZ_REG_RTC_HIBERNATE 0x20 #define JZ_REG_RTC_SCRATCHPAD 0x34 +/* The following are present on the jz4780 */ +#define JZ_REG_RTC_WENR 0x3C +#define JZ_RTC_WENR_WEN BIT(31) + #define JZ_RTC_CTRL_WRDY BIT(7) #define JZ_RTC_CTRL_1HZ BIT(6) #define JZ_RTC_CTRL_1HZ_IRQ BIT(5) @@ -37,8 +41,17 @@ #define JZ_RTC_CTRL_AE BIT(2) #define JZ_RTC_CTRL_ENABLE BIT(0) +/* Magic value to enable writes on jz4780 */ +#define JZ_RTC_WENR_MAGIC 0xA55A + +enum jz4740_rtc_type { + ID_JZ4740, + ID_JZ4780, +}; + struct jz4740_rtc { void __iomem *base; + enum jz4740_rtc_type type; struct rtc_device *rtc; @@ -64,11 +77,33 @@ static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) return timeout ? 0 : -EIO; } +static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc) +{ + uint32_t ctrl; + int ret, timeout = 1000; + + ret = jz4740_rtc_wait_write_ready(rtc); + if (ret != 0) + return ret; + + writel(JZ_RTC_WENR_MAGIC, rtc->base + JZ_REG_RTC_WENR); + + do { + ctrl = readl(rtc->base + JZ_REG_RTC_WENR); + } while (!(ctrl & JZ_RTC_WENR_WEN) && --timeout); + + return timeout ? 0 : -EIO; +} + static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, uint32_t val) { - int ret; - ret = jz4740_rtc_wait_write_ready(rtc); + int ret = 0; + + if (rtc->type >= ID_JZ4780) + ret = jz4780_rtc_enable_write(rtc); + if (ret == 0) + ret = jz4740_rtc_wait_write_ready(rtc); if (ret == 0) writel(val, rtc->base + reg); @@ -216,11 +251,14 @@ static int jz4740_rtc_probe(struct platform_device *pdev) struct jz4740_rtc *rtc; uint32_t scratchpad; struct resource *mem; + const struct platform_device_id *id = platform_get_device_id(pdev); rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) return -ENOMEM; + rtc->type = id->driver_data; + rtc->irq = platform_get_irq(pdev, 0); if (rtc->irq < 0) { dev_err(&pdev->dev, "Failed to get platform irq\n"); @@ -295,12 +333,20 @@ static const struct dev_pm_ops jz4740_pm_ops = { #define JZ4740_RTC_PM_OPS NULL #endif /* CONFIG_PM */ +static const struct platform_device_id jz4740_rtc_ids[] = { + { "jz4740-rtc", ID_JZ4740 }, + { "jz4780-rtc", ID_JZ4780 }, + {} +}; +MODULE_DEVICE_TABLE(platform, jz4740_rtc_ids); + static struct platform_driver jz4740_rtc_driver = { .probe = jz4740_rtc_probe, .driver = { .name = "jz4740-rtc", .pm = JZ4740_RTC_PM_OPS, }, + .id_table = jz4740_rtc_ids, }; module_platform_driver(jz4740_rtc_driver); -- cgit v1.2.3 From c05229a893e79dc992abe0e561d7da0e3e84e1d8 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 31 Oct 2016 21:39:47 +0100 Subject: rtc: jz4740: Add support for devicetree See Documentation/devicetree/bindings/rtc/ingenic,jz4740-rtc.txt for a description of the bindings. Signed-off-by: Paul Cercueil Acked-by: Maarten ter Huurne Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-jz4740.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 7ad2d7965b4e..c2895cec56b4 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -245,6 +246,13 @@ void jz4740_rtc_poweroff(struct device *dev) } EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff); +static const struct of_device_id jz4740_rtc_of_match[] = { + { .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 }, + { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4740_rtc_of_match); + static int jz4740_rtc_probe(struct platform_device *pdev) { int ret; @@ -252,12 +260,17 @@ static int jz4740_rtc_probe(struct platform_device *pdev) uint32_t scratchpad; struct resource *mem; const struct platform_device_id *id = platform_get_device_id(pdev); + const struct of_device_id *of_id = of_match_device( + jz4740_rtc_of_match, &pdev->dev); rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) return -ENOMEM; - rtc->type = id->driver_data; + if (of_id) + rtc->type = (enum jz4740_rtc_type)of_id->data; + else + rtc->type = id->driver_data; rtc->irq = platform_get_irq(pdev, 0); if (rtc->irq < 0) { @@ -345,6 +358,7 @@ static struct platform_driver jz4740_rtc_driver = { .driver = { .name = "jz4740-rtc", .pm = JZ4740_RTC_PM_OPS, + .of_match_table = of_match_ptr(jz4740_rtc_of_match), }, .id_table = jz4740_rtc_ids, }; -- cgit v1.2.3 From f9eb69d1ae2f82b0e556852bf719f550c2a34193 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 31 Oct 2016 21:39:48 +0100 Subject: rtc: jz4740: Add support for acting as the system power controller The 'system-power-controller' singleton entry can be used in the devicetree node of the jz4740-rtc driver to specify that the driver is granted the right to power off the system through the registers of the RTC unit. See the documentation for more details: Documentation/devicetree/bindings/rtc/ingenic,jz4740-rtc.txt Signed-off-by: Paul Cercueil Acked-by: Maarten ter Huurne Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-jz4740.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index c2895cec56b4..33ccd73ee947 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -14,11 +14,13 @@ * */ +#include #include #include #include #include #include +#include #include #include #include @@ -28,6 +30,8 @@ #define JZ_REG_RTC_SEC_ALARM 0x08 #define JZ_REG_RTC_REGULATOR 0x0C #define JZ_REG_RTC_HIBERNATE 0x20 +#define JZ_REG_RTC_WAKEUP_FILTER 0x24 +#define JZ_REG_RTC_RESET_COUNTER 0x28 #define JZ_REG_RTC_SCRATCHPAD 0x34 /* The following are present on the jz4780 */ @@ -45,6 +49,9 @@ /* Magic value to enable writes on jz4780 */ #define JZ_RTC_WENR_MAGIC 0xA55A +#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0 +#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0 + enum jz4740_rtc_type { ID_JZ4740, ID_JZ4780, @@ -55,12 +62,18 @@ struct jz4740_rtc { enum jz4740_rtc_type type; struct rtc_device *rtc; + struct clk *clk; int irq; spinlock_t lock; + + unsigned int min_wakeup_pin_assert_time; + unsigned int reset_pin_assert_time; }; +static struct device *dev_for_power_off; + static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) { return readl(rtc->base + reg); @@ -246,6 +259,46 @@ void jz4740_rtc_poweroff(struct device *dev) } EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff); +static void jz4740_rtc_power_off(void) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev_for_power_off); + unsigned long rtc_rate; + unsigned long wakeup_filter_ticks; + unsigned long reset_counter_ticks; + + clk_prepare_enable(rtc->clk); + + rtc_rate = clk_get_rate(rtc->clk); + + /* + * Set minimum wakeup pin assertion time: 100 ms. + * Range is 0 to 2 sec if RTC is clocked at 32 kHz. + */ + wakeup_filter_ticks = + (rtc->min_wakeup_pin_assert_time * rtc_rate) / 1000; + if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK) + wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK; + else + wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK; + jz4740_rtc_reg_write(rtc, + JZ_REG_RTC_WAKEUP_FILTER, wakeup_filter_ticks); + + /* + * Set reset pin low-level assertion time after wakeup: 60 ms. + * Range is 0 to 125 ms if RTC is clocked at 32 kHz. + */ + reset_counter_ticks = (rtc->reset_pin_assert_time * rtc_rate) / 1000; + if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK) + reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK; + else + reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK; + jz4740_rtc_reg_write(rtc, + JZ_REG_RTC_RESET_COUNTER, reset_counter_ticks); + + jz4740_rtc_poweroff(dev_for_power_off); + machine_halt(); +} + static const struct of_device_id jz4740_rtc_of_match[] = { { .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 }, { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 }, @@ -262,6 +315,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev) const struct platform_device_id *id = platform_get_device_id(pdev); const struct of_device_id *of_id = of_match_device( jz4740_rtc_of_match, &pdev->dev); + struct device_node *np = pdev->dev.of_node; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) @@ -283,6 +337,12 @@ static int jz4740_rtc_probe(struct platform_device *pdev) if (IS_ERR(rtc->base)) return PTR_ERR(rtc->base); + rtc->clk = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(rtc->clk)) { + dev_err(&pdev->dev, "Failed to get RTC clock\n"); + return PTR_ERR(rtc->clk); + } + spin_lock_init(&rtc->lock); platform_set_drvdata(pdev, rtc); @@ -314,6 +374,27 @@ static int jz4740_rtc_probe(struct platform_device *pdev) } } + if (np && of_device_is_system_power_controller(np)) { + if (!pm_power_off) { + /* Default: 60ms */ + rtc->reset_pin_assert_time = 60; + of_property_read_u32(np, "reset-pin-assert-time-ms", + &rtc->reset_pin_assert_time); + + /* Default: 100ms */ + rtc->min_wakeup_pin_assert_time = 100; + of_property_read_u32(np, + "min-wakeup-pin-assert-time-ms", + &rtc->min_wakeup_pin_assert_time); + + dev_for_power_off = &pdev->dev; + pm_power_off = jz4740_rtc_power_off; + } else { + dev_warn(&pdev->dev, + "Poweroff handler already present!\n"); + } + } + return 0; } -- cgit v1.2.3 From 959df7778bbd3003fe2ade67e3b41ecb5e0ca25d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 2 Oct 2016 22:58:16 +0200 Subject: rtc: Enable compile testing for Maxim and Samsung drivers max8907, max77686 and s5m RTC drivers can be compile tested to increase build coverage. The s5m-rtc uses REGMAP_IRQ so add this as explicit dependency. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 1d0ae30e9927..4259ce61c31b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -303,7 +303,7 @@ config RTC_DRV_MAX6900 config RTC_DRV_MAX8907 tristate "Maxim MAX8907" - depends on MFD_MAX8907 + depends on MFD_MAX8907 || COMPILE_TEST help If you say yes here you will get support for the RTC of Maxim MAX8907 PMIC. @@ -343,7 +343,7 @@ config RTC_DRV_MAX8997 config RTC_DRV_MAX77686 tristate "Maxim MAX77686" - depends on MFD_MAX77686 || MFD_MAX77620 + depends on MFD_MAX77686 || MFD_MAX77620 || COMPILE_TEST help If you say yes here you will get support for the RTC of Maxim MAX77686/MAX77620/MAX77802 PMIC. @@ -602,7 +602,8 @@ config RTC_DRV_RV8803 config RTC_DRV_S5M tristate "Samsung S2M/S5M series" - depends on MFD_SEC_CORE + depends on MFD_SEC_CORE || COMPILE_TEST + select REGMAP_IRQ help If you say yes here you will get support for the RTC of Samsung S2MPS14 and S5M PMIC series. -- cgit v1.2.3 From 819c21785b498d38fbbe361458dc2f05d943cc46 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 8 Nov 2016 22:20:37 +0100 Subject: rtc: jz4740: remove unused EXPORT_SYMBOL jz4740_rtc_poweroff() is only called from the driver, stop exporting it. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-jz4740.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 33ccd73ee947..18feae6c4e95 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -252,12 +252,11 @@ static irqreturn_t jz4740_rtc_irq(int irq, void *data) return IRQ_HANDLED; } -void jz4740_rtc_poweroff(struct device *dev) +static void jz4740_rtc_poweroff(struct device *dev) { struct jz4740_rtc *rtc = dev_get_drvdata(dev); jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1); } -EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff); static void jz4740_rtc_power_off(void) { -- cgit v1.2.3 From b9168c539c0b2de756aaffd380384dbde8adbe07 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 8 Nov 2016 22:49:55 +0100 Subject: rtc: jz4740: make the driver builtin only Since the driver is now calling machine_halt() that is not exported, it has to be built in the kernel. Building it as a module will fail at linking time. Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 5 +---- drivers/rtc/rtc-jz4740.c | 10 +--------- 2 files changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4259ce61c31b..24b0778f6e28 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1550,15 +1550,12 @@ config RTC_DRV_MPC5121 will be called rtc-mpc5121. config RTC_DRV_JZ4740 - tristate "Ingenic JZ4740 SoC" + bool "Ingenic JZ4740 SoC" depends on MACH_INGENIC || COMPILE_TEST help If you say yes here you get support for the Ingenic JZ47xx SoCs RTC controllers. - This driver can also be buillt as a module. If so, the module - will be called rtc-jz4740. - config RTC_DRV_LPC24XX tristate "NXP RTC for LPC178x/18xx/408x/43xx" depends on ARCH_LPC18XX || COMPILE_TEST diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 18feae6c4e95..72918c1ba092 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -303,7 +302,6 @@ static const struct of_device_id jz4740_rtc_of_match[] = { { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 }, {}, }; -MODULE_DEVICE_TABLE(of, jz4740_rtc_of_match); static int jz4740_rtc_probe(struct platform_device *pdev) { @@ -431,7 +429,6 @@ static const struct platform_device_id jz4740_rtc_ids[] = { { "jz4780-rtc", ID_JZ4780 }, {} }; -MODULE_DEVICE_TABLE(platform, jz4740_rtc_ids); static struct platform_driver jz4740_rtc_driver = { .probe = jz4740_rtc_probe, @@ -443,9 +440,4 @@ static struct platform_driver jz4740_rtc_driver = { .id_table = jz4740_rtc_ids, }; -module_platform_driver(jz4740_rtc_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n"); -MODULE_ALIAS("platform:jz4740-rtc"); +builtin_platform_driver(jz4740_rtc_driver); -- cgit v1.2.3 From 72d3d79f8da9b17e1f47db567b8def0f9674555c Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 8 Nov 2016 22:51:45 +0100 Subject: rtc: fix typos in Kconfig s/buillt/built/g Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 24b0778f6e28..ab55f35019fc 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1565,7 +1565,7 @@ config RTC_DRV_LPC24XX NXP LPC178x/18xx/408x/43xx devices. If you have one of the devices above enable this driver to use - the hardware RTC. This driver can also be buillt as a module. If + the hardware RTC. This driver can also be built as a module. If so, the module will be called rtc-lpc24xx. config RTC_DRV_LPC32XX @@ -1574,7 +1574,7 @@ config RTC_DRV_LPC32XX help This enables support for the NXP RTC in the LPC32XX - This driver can also be buillt as a module. If so, the module + This driver can also be built as a module. If so, the module will be called rtc-lpc32xx. config RTC_DRV_PM8XXX -- cgit v1.2.3 From e3e7f95bca95c7d0172e6481066668a176dc4696 Mon Sep 17 00:00:00 2001 From: Nicolae Rosia Date: Wed, 23 Nov 2016 10:55:56 +0200 Subject: rtc: twl: kill static variables The current code uses static variables which prevent the use of multiple rtc twl instances. We also make it clear that this driver supports only TWL4030 and TWL6030 classes. Signed-off-by: Nicolae Rosia Tested-by: Tony Lindgren Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-twl.c | 190 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 76 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 176720b7b9e5..1e415268770e 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -33,6 +33,10 @@ #include +enum twl_class { + TWL_4030 = 0, + TWL_6030, +}; /* * RTC block register offsets (use TWL_MODULE_RTC) @@ -136,16 +140,30 @@ static const u8 twl6030_rtc_reg_map[] = { #define ALL_TIME_REGS 6 /*----------------------------------------------------------------------*/ -static u8 *rtc_reg_map; +struct twl_rtc { + struct device *dev; + struct rtc_device *rtc; + u8 *reg_map; + /* + * Cache the value for timer/alarm interrupts register; this is + * only changed by callers holding rtc ops lock (or resume). + */ + unsigned char rtc_irq_bits; + bool wake_enabled; +#ifdef CONFIG_PM_SLEEP + unsigned char irqstat; +#endif + enum twl_class class; +}; /* * Supports 1 byte read from TWL RTC register. */ -static int twl_rtc_read_u8(u8 *data, u8 reg) +static int twl_rtc_read_u8(struct twl_rtc *twl_rtc, u8 *data, u8 reg) { int ret; - ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg])); + ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (twl_rtc->reg_map[reg])); if (ret < 0) pr_err("Could not read TWL register %X - error %d\n", reg, ret); return ret; @@ -154,40 +172,34 @@ static int twl_rtc_read_u8(u8 *data, u8 reg) /* * Supports 1 byte write to TWL RTC registers. */ -static int twl_rtc_write_u8(u8 data, u8 reg) +static int twl_rtc_write_u8(struct twl_rtc *twl_rtc, u8 data, u8 reg) { int ret; - ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg])); + ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (twl_rtc->reg_map[reg])); if (ret < 0) pr_err("Could not write TWL register %X - error %d\n", reg, ret); return ret; } -/* - * Cache the value for timer/alarm interrupts register; this is - * only changed by callers holding rtc ops lock (or resume). - */ -static unsigned char rtc_irq_bits; - /* * Enable 1/second update and/or alarm interrupts. */ -static int set_rtc_irq_bit(unsigned char bit) +static int set_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit) { unsigned char val; int ret; /* if the bit is set, return from here */ - if (rtc_irq_bits & bit) + if (twl_rtc->rtc_irq_bits & bit) return 0; - val = rtc_irq_bits | bit; + val = twl_rtc->rtc_irq_bits | bit; val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M; - ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG); + ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG); if (ret == 0) - rtc_irq_bits = val; + twl_rtc->rtc_irq_bits = val; return ret; } @@ -195,19 +207,19 @@ static int set_rtc_irq_bit(unsigned char bit) /* * Disable update and/or alarm interrupts. */ -static int mask_rtc_irq_bit(unsigned char bit) +static int mask_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit) { unsigned char val; int ret; /* if the bit is clear, return from here */ - if (!(rtc_irq_bits & bit)) + if (!(twl_rtc->rtc_irq_bits & bit)) return 0; - val = rtc_irq_bits & ~bit; - ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG); + val = twl_rtc->rtc_irq_bits & ~bit; + ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG); if (ret == 0) - rtc_irq_bits = val; + twl_rtc->rtc_irq_bits = val; return ret; } @@ -215,21 +227,23 @@ static int mask_rtc_irq_bit(unsigned char bit) static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) { struct platform_device *pdev = to_platform_device(dev); + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); int irq = platform_get_irq(pdev, 0); - static bool twl_rtc_wake_enabled; int ret; if (enabled) { - ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); - if (device_can_wakeup(dev) && !twl_rtc_wake_enabled) { + ret = set_rtc_irq_bit(twl_rtc, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + if (device_can_wakeup(dev) && !twl_rtc->wake_enabled) { enable_irq_wake(irq); - twl_rtc_wake_enabled = true; + twl_rtc->wake_enabled = true; } } else { - ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); - if (twl_rtc_wake_enabled) { + ret = mask_rtc_irq_bit(twl_rtc, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + if (twl_rtc->wake_enabled) { disable_irq_wake(irq); - twl_rtc_wake_enabled = false; + twl_rtc->wake_enabled = false; } } @@ -247,21 +261,23 @@ static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) */ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm) { + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); unsigned char rtc_data[ALL_TIME_REGS]; int ret; u8 save_control; u8 rtc_control; - ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG); + ret = twl_rtc_read_u8(twl_rtc, &save_control, REG_RTC_CTRL_REG); if (ret < 0) { dev_err(dev, "%s: reading CTRL_REG, error %d\n", __func__, ret); return ret; } /* for twl6030/32 make sure BIT_RTC_CTRL_REG_GET_TIME_M is clear */ - if (twl_class_is_6030()) { + if (twl_rtc->class == TWL_6030) { if (save_control & BIT_RTC_CTRL_REG_GET_TIME_M) { save_control &= ~BIT_RTC_CTRL_REG_GET_TIME_M; - ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); + ret = twl_rtc_write_u8(twl_rtc, save_control, + REG_RTC_CTRL_REG); if (ret < 0) { dev_err(dev, "%s clr GET_TIME, error %d\n", __func__, ret); @@ -274,17 +290,17 @@ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm) rtc_control = save_control | BIT_RTC_CTRL_REG_GET_TIME_M; /* for twl6030/32 enable read access to static shadowed registers */ - if (twl_class_is_6030()) + if (twl_rtc->class == TWL_6030) rtc_control |= BIT_RTC_CTRL_REG_RTC_V_OPT; - ret = twl_rtc_write_u8(rtc_control, REG_RTC_CTRL_REG); + ret = twl_rtc_write_u8(twl_rtc, rtc_control, REG_RTC_CTRL_REG); if (ret < 0) { dev_err(dev, "%s: writing CTRL_REG, error %d\n", __func__, ret); return ret; } ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data, - (rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS); + (twl_rtc->reg_map[REG_SECONDS_REG]), ALL_TIME_REGS); if (ret < 0) { dev_err(dev, "%s: reading data, error %d\n", __func__, ret); @@ -292,8 +308,8 @@ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm) } /* for twl6030 restore original state of rtc control register */ - if (twl_class_is_6030()) { - ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); + if (twl_rtc->class == TWL_6030) { + ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); if (ret < 0) { dev_err(dev, "%s: restore CTRL_REG, error %d\n", __func__, ret); @@ -313,6 +329,7 @@ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm) static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm) { + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); unsigned char save_control; unsigned char rtc_data[ALL_TIME_REGS]; int ret; @@ -325,18 +342,18 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm) rtc_data[5] = bin2bcd(tm->tm_year - 100); /* Stop RTC while updating the TC registers */ - ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG); + ret = twl_rtc_read_u8(twl_rtc, &save_control, REG_RTC_CTRL_REG); if (ret < 0) goto out; save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M; - ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); + ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); if (ret < 0) goto out; /* update all the time registers in one shot */ ret = twl_i2c_write(TWL_MODULE_RTC, rtc_data, - (rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS); + (twl_rtc->reg_map[REG_SECONDS_REG]), ALL_TIME_REGS); if (ret < 0) { dev_err(dev, "rtc_set_time error %d\n", ret); goto out; @@ -344,7 +361,7 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm) /* Start back RTC */ save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M; - ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); + ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); out: return ret; @@ -355,11 +372,12 @@ out: */ static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); unsigned char rtc_data[ALL_TIME_REGS]; int ret; ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data, - (rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS); + twl_rtc->reg_map[REG_ALARM_SECONDS_REG], ALL_TIME_REGS); if (ret < 0) { dev_err(dev, "rtc_read_alarm error %d\n", ret); return ret; @@ -374,7 +392,7 @@ static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) alm->time.tm_year = bcd2bin(rtc_data[5]) + 100; /* report cached alarm enable state */ - if (rtc_irq_bits & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) + if (twl_rtc->rtc_irq_bits & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) alm->enabled = 1; return ret; @@ -382,6 +400,8 @@ static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) { + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + unsigned char alarm_data[ALL_TIME_REGS]; int ret; @@ -398,7 +418,7 @@ static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) /* update all the alarm registers in one shot */ ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data, - (rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS); + twl_rtc->reg_map[REG_ALARM_SECONDS_REG], ALL_TIME_REGS); if (ret) { dev_err(dev, "rtc_set_alarm error %d\n", ret); goto out; @@ -410,14 +430,15 @@ out: return ret; } -static irqreturn_t twl_rtc_interrupt(int irq, void *rtc) +static irqreturn_t twl_rtc_interrupt(int irq, void *data) { + struct twl_rtc *twl_rtc = data; unsigned long events; int ret = IRQ_NONE; int res; u8 rd_reg; - res = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG); + res = twl_rtc_read_u8(twl_rtc, &rd_reg, REG_RTC_STATUS_REG); if (res) goto out; /* @@ -431,12 +452,12 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc) else events = RTC_IRQF | RTC_PF; - res = twl_rtc_write_u8(BIT_RTC_STATUS_REG_ALARM_M, - REG_RTC_STATUS_REG); + res = twl_rtc_write_u8(twl_rtc, BIT_RTC_STATUS_REG_ALARM_M, + REG_RTC_STATUS_REG); if (res) goto out; - if (twl_class_is_4030()) { + if (twl_rtc->class == TWL_4030) { /* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1 * needs 2 reads to clear the interrupt. One read is done in * do_twl_pwrirq(). Doing the second read, to clear @@ -455,7 +476,7 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc) } /* Notify RTC core on event */ - rtc_update_irq(rtc, 1, events); + rtc_update_irq(twl_rtc->rtc, 1, events); ret = IRQ_HANDLED; out: @@ -474,7 +495,7 @@ static const struct rtc_class_ops twl_rtc_ops = { static int twl_rtc_probe(struct platform_device *pdev) { - struct rtc_device *rtc; + struct twl_rtc *twl_rtc; int ret = -EINVAL; int irq = platform_get_irq(pdev, 0); u8 rd_reg; @@ -482,13 +503,22 @@ static int twl_rtc_probe(struct platform_device *pdev) if (irq <= 0) return ret; - /* Initialize the register map */ - if (twl_class_is_4030()) - rtc_reg_map = (u8 *)twl4030_rtc_reg_map; - else - rtc_reg_map = (u8 *)twl6030_rtc_reg_map; + twl_rtc = devm_kzalloc(&pdev->dev, sizeof(*twl_rtc), GFP_KERNEL); + if (!twl_rtc) + return -ENOMEM; + + if (twl_class_is_4030()) { + twl_rtc->class = TWL_4030; + twl_rtc->reg_map = (u8 *)twl4030_rtc_reg_map; + } else if (twl_class_is_6030()) { + twl_rtc->class = TWL_6030; + twl_rtc->reg_map = (u8 *)twl6030_rtc_reg_map; + } else { + dev_err(&pdev->dev, "TWL Class not supported.\n"); + return -EINVAL; + } - ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG); + ret = twl_rtc_read_u8(twl_rtc, &rd_reg, REG_RTC_STATUS_REG); if (ret < 0) return ret; @@ -499,11 +529,11 @@ static int twl_rtc_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n"); /* Clear RTC Power up reset and pending alarm interrupts */ - ret = twl_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG); + ret = twl_rtc_write_u8(twl_rtc, rd_reg, REG_RTC_STATUS_REG); if (ret < 0) return ret; - if (twl_class_is_6030()) { + if (twl_rtc->class == TWL_6030) { twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK, REG_INT_MSK_LINE_A); twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK, @@ -511,40 +541,42 @@ static int twl_rtc_probe(struct platform_device *pdev) } dev_info(&pdev->dev, "Enabling TWL-RTC\n"); - ret = twl_rtc_write_u8(BIT_RTC_CTRL_REG_STOP_RTC_M, REG_RTC_CTRL_REG); + ret = twl_rtc_write_u8(twl_rtc, BIT_RTC_CTRL_REG_STOP_RTC_M, + REG_RTC_CTRL_REG); if (ret < 0) return ret; /* ensure interrupts are disabled, bootloaders can be strange */ - ret = twl_rtc_write_u8(0, REG_RTC_INTERRUPTS_REG); + ret = twl_rtc_write_u8(twl_rtc, 0, REG_RTC_INTERRUPTS_REG); if (ret < 0) dev_warn(&pdev->dev, "unable to disable interrupt\n"); /* init cached IRQ enable bits */ - ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG); + ret = twl_rtc_read_u8(twl_rtc, &twl_rtc->rtc_irq_bits, + REG_RTC_INTERRUPTS_REG); if (ret < 0) return ret; + platform_set_drvdata(pdev, twl_rtc); device_init_wakeup(&pdev->dev, 1); - rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + twl_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &twl_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { + if (IS_ERR(twl_rtc->rtc)) { dev_err(&pdev->dev, "can't register RTC device, err %ld\n", - PTR_ERR(rtc)); - return PTR_ERR(rtc); + PTR_ERR(twl_rtc->rtc)); + return PTR_ERR(twl_rtc->rtc); } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, twl_rtc_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT, - dev_name(&rtc->dev), rtc); + dev_name(&twl_rtc->rtc->dev), twl_rtc); if (ret < 0) { dev_err(&pdev->dev, "IRQ is not free.\n"); return ret; } - platform_set_drvdata(pdev, rtc); return 0; } @@ -554,10 +586,12 @@ static int twl_rtc_probe(struct platform_device *pdev) */ static int twl_rtc_remove(struct platform_device *pdev) { + struct twl_rtc *twl_rtc = platform_get_drvdata(pdev); + /* leave rtc running, but disable irqs */ - mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); - mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); - if (twl_class_is_6030()) { + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); + if (twl_rtc->class == TWL_6030) { twl6030_interrupt_mask(TWL6030_RTC_INT_MASK, REG_INT_MSK_LINE_A); twl6030_interrupt_mask(TWL6030_RTC_INT_MASK, @@ -569,25 +603,29 @@ static int twl_rtc_remove(struct platform_device *pdev) static void twl_rtc_shutdown(struct platform_device *pdev) { + struct twl_rtc *twl_rtc = platform_get_drvdata(pdev); + /* mask timer interrupts, but leave alarm interrupts on to enable power-on when alarm is triggered */ - mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); } #ifdef CONFIG_PM_SLEEP -static unsigned char irqstat; - static int twl_rtc_suspend(struct device *dev) { - irqstat = rtc_irq_bits; + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); - mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); + twl_rtc->irqstat = twl_rtc->rtc_irq_bits; + + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); return 0; } static int twl_rtc_resume(struct device *dev) { - set_rtc_irq_bit(irqstat); + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + + set_rtc_irq_bit(twl_rtc, twl_rtc->irqstat); return 0; } #endif -- cgit v1.2.3 From 1c02cbfec5c7754cb16456fcb26592a20781cdff Mon Sep 17 00:00:00 2001 From: Nicolae Rosia Date: Wed, 23 Nov 2016 10:55:57 +0200 Subject: rtc: twl: make driver DT only Since there are no platform based users and all users of this code are TI OMAP-based which is DT only, it makes sense to remove unused code. Signed-off-by: Nicolae Rosia Tested-by: Tony Lindgren Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 1 + drivers/rtc/rtc-twl.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index ab55f35019fc..a44f1f27ecfa 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -481,6 +481,7 @@ config RTC_DRV_TWL92330 config RTC_DRV_TWL4030 tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0" depends on TWL4030_CORE + depends on OF help If you say yes here you get support for the RTC on the TWL4030/TWL5030/TWL6030 family chips, used mostly with OMAP3 platforms. diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 1e415268770e..c18c39212ce6 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -496,10 +496,16 @@ static const struct rtc_class_ops twl_rtc_ops = { static int twl_rtc_probe(struct platform_device *pdev) { struct twl_rtc *twl_rtc; + struct device_node *np = pdev->dev.of_node; int ret = -EINVAL; int irq = platform_get_irq(pdev, 0); u8 rd_reg; + if (!np) { + dev_err(&pdev->dev, "no DT info\n"); + return -EINVAL; + } + if (irq <= 0) return ret; @@ -632,15 +638,11 @@ static int twl_rtc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(twl_rtc_pm_ops, twl_rtc_suspend, twl_rtc_resume); -#ifdef CONFIG_OF static const struct of_device_id twl_rtc_of_match[] = { {.compatible = "ti,twl4030-rtc", }, { }, }; MODULE_DEVICE_TABLE(of, twl_rtc_of_match); -#endif - -MODULE_ALIAS("platform:twl_rtc"); static struct platform_driver twl4030rtc_driver = { .probe = twl_rtc_probe, @@ -649,7 +651,7 @@ static struct platform_driver twl4030rtc_driver = { .driver = { .name = "twl_rtc", .pm = &twl_rtc_pm_ops, - .of_match_table = of_match_ptr(twl_rtc_of_match), + .of_match_table = twl_rtc_of_match, }, }; -- cgit v1.2.3 From 01835fadf5eda4afdca5c0aa7a89e210ee45bec6 Mon Sep 17 00:00:00 2001 From: Srikant Ritolia Date: Tue, 29 Nov 2016 11:04:37 +0000 Subject: rtc: ds1374: Merge conditional + WARN_ON() WARN_ON does both these things in one statement. Using a better pattern with WARN_ON(). Signed-off-by: Srikant Ritolia Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1374.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index 3b3049c8c9e0..52429f0a57cc 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -89,10 +89,8 @@ static int ds1374_read_rtc(struct i2c_client *client, u32 *time, int ret; int i; - if (nbytes > 4) { - WARN_ON(1); + if (WARN_ON(nbytes > 4)) return -EINVAL; - } ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf); -- cgit v1.2.3 From b88e0ae958267dc63bea661d66c15f61ba1dd93c Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Sat, 19 Nov 2016 14:03:33 +0100 Subject: rtc: imxdi: (trivial) fix a typo Fix a typo Signed-off-by: Martin Kaiser Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-imxdi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c index 8d8049bdfaf6..67b56b80dc70 100644 --- a/drivers/rtc/rtc-imxdi.c +++ b/drivers/rtc/rtc-imxdi.c @@ -67,7 +67,7 @@ #define DSR_ETAD (1 << 21) /* External tamper A detected */ #define DSR_EBD (1 << 20) /* External boot detected */ #define DSR_SAD (1 << 19) /* SCC alarm detected */ -#define DSR_TTD (1 << 18) /* Temperatur tamper detected */ +#define DSR_TTD (1 << 18) /* Temperature tamper detected */ #define DSR_CTD (1 << 17) /* Clock tamper detected */ #define DSR_VTD (1 << 16) /* Voltage tamper detected */ #define DSR_WBF (1 << 10) /* Write Busy Flag (synchronous) */ -- cgit v1.2.3 From 9c19b8930d2cf95f5dc5ec11610400a7c61845d1 Mon Sep 17 00:00:00 2001 From: Tin Huynh Date: Wed, 30 Nov 2016 09:57:31 +0700 Subject: rtc: ds1307: Add ACPI support This patch enables ACPI support for rtc-ds1307 driver. Signed-off-by: Tin Huynh Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 52 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 9 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 4e31036ee259..4ad97be48043 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -11,6 +11,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -191,6 +192,26 @@ static const struct i2c_device_id ds1307_id[] = { }; MODULE_DEVICE_TABLE(i2c, ds1307_id); +#ifdef CONFIG_ACPI +static const struct acpi_device_id ds1307_acpi_ids[] = { + { .id = "DS1307", .driver_data = ds_1307 }, + { .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 = "DS3231", .driver_data = ds_3231 }, + { .id = "M41T00", .driver_data = m41t00 }, + { .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 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids); +#endif + /*----------------------------------------------------------------------*/ #define BLOCK_DATA_MAX_TRIES 10 @@ -874,17 +895,17 @@ static u8 do_trickle_setup_ds1339(struct i2c_client *client, return setup; } -static void ds1307_trickle_of_init(struct i2c_client *client, - struct chip_desc *chip) +static void ds1307_trickle_init(struct i2c_client *client, + struct chip_desc *chip) { uint32_t ohms = 0; bool diode = true; if (!chip->do_trickle_setup) goto out; - if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms" , &ohms)) + if (device_property_read_u32(&client->dev, "trickle-resistor-ohms", &ohms)) goto out; - if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable")) + if (device_property_read_bool(&client->dev, "trickle-diode-disable")) diode = false; chip->trickle_charger_setup = chip->do_trickle_setup(client, ohms, diode); @@ -1268,7 +1289,7 @@ static int ds1307_probe(struct i2c_client *client, struct ds1307 *ds1307; int err = -ENODEV; int tmp, wday; - struct chip_desc *chip = &chips[id->driver_data]; + struct chip_desc *chip; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); bool want_irq = false; bool ds1307_can_wakeup_device = false; @@ -1297,11 +1318,23 @@ static int ds1307_probe(struct i2c_client *client, i2c_set_clientdata(client, ds1307); ds1307->client = client; - ds1307->type = id->driver_data; + if (id) { + chip = &chips[id->driver_data]; + ds1307->type = id->driver_data; + } else { + const struct acpi_device_id *acpi_id; + + acpi_id = acpi_match_device(ACPI_PTR(ds1307_acpi_ids), + &client->dev); + if (!acpi_id) + return -ENODEV; + chip = &chips[acpi_id->driver_data]; + ds1307->type = acpi_id->driver_data; + } - if (!pdata && client->dev.of_node) - ds1307_trickle_of_init(client, chip); - else if (pdata && pdata->trickle_charger_setup) + if (!pdata) + ds1307_trickle_init(client, chip); + else if (pdata->trickle_charger_setup) chip->trickle_charger_setup = pdata->trickle_charger_setup; if (chip->trickle_charger_setup && chip->trickle_charger_reg) { @@ -1678,6 +1711,7 @@ static int ds1307_remove(struct i2c_client *client) static struct i2c_driver ds1307_driver = { .driver = { .name = "rtc-ds1307", + .acpi_match_table = ACPI_PTR(ds1307_acpi_ids), }, .probe = ds1307_probe, .remove = ds1307_remove, -- cgit v1.2.3 From 0b6a8f5c9bebe51b95ff23939db570e8d298a36f Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 4 Dec 2016 23:04:39 +0900 Subject: rtc: add support for EPSON TOYOCOM RTC-7301SF/DG This adds support for EPSON TOYOCOM RTC-7301SF/DG which has parallel interface compatible with SRAM. This driver supports basic clock, calendar and alarm functionality. Tested with Microblaze linux running on Artix7 FPGA board with my own custom IP for RTC-7301. Signed-off-by: Akinobu Mita Acked-by: Rob Herring Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/epson,rtc7301.txt | 16 + drivers/rtc/Kconfig | 11 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-r7301.c | 453 +++++++++++++++++++++ 4 files changed, 481 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/epson,rtc7301.txt create mode 100644 drivers/rtc/rtc-r7301.c (limited to 'drivers/rtc') diff --git a/Documentation/devicetree/bindings/rtc/epson,rtc7301.txt b/Documentation/devicetree/bindings/rtc/epson,rtc7301.txt new file mode 100644 index 000000000000..5f9df3f1467c --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/epson,rtc7301.txt @@ -0,0 +1,16 @@ +EPSON TOYOCOM RTC-7301SF/DG + +Required properties: + +- compatible: Should be "epson,rtc7301sf" or "epson,rtc7301dg" +- reg: Specifies base physical address and size of the registers. +- interrupts: A single interrupt specifier. + +Example: + +rtc: rtc@44a00000 { + compatible = "epson,rtc7301dg"; + reg = <0x44a00000 0x10000>; + interrupt-parent = <&axi_intc_0>; + interrupts = <3 2>; +}; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a44f1f27ecfa..c93c5a8fba32 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1705,6 +1705,17 @@ config RTC_DRV_PIC32 This driver can also be built as a module. If so, the module will be called rtc-pic32 +config RTC_DRV_R7301 + tristate "EPSON TOYOCOM RTC-7301SF/DG" + select REGMAP_MMIO + depends on OF && HAS_IOMEM + help + If you say yes here you get support for the EPSON TOYOCOM + RTC-7301SF/DG chips. + + This driver can also be built as a module. If so, the module + will be called rtc-r7301. + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 1ac694a330c8..f13ab1c5c222 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -120,6 +120,7 @@ obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o +obj-$(CONFIG_RTC_DRV_R7301) += rtc-r7301.o obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o diff --git a/drivers/rtc/rtc-r7301.c b/drivers/rtc/rtc-r7301.c new file mode 100644 index 000000000000..28d540885f3d --- /dev/null +++ b/drivers/rtc/rtc-r7301.c @@ -0,0 +1,453 @@ +/* + * EPSON TOYOCOM RTC-7301SF/DG Driver + * + * Copyright (c) 2016 Akinobu Mita + * + * Based on rtc-rp5c01.c + * + * Datasheet: http://www5.epsondevice.com/en/products/parallel/rtc7301sf.html + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rtc-r7301" + +#define RTC7301_1_SEC 0x0 /* Bank 0 and Band 1 */ +#define RTC7301_10_SEC 0x1 /* Bank 0 and Band 1 */ +#define RTC7301_AE BIT(3) +#define RTC7301_1_MIN 0x2 /* Bank 0 and Band 1 */ +#define RTC7301_10_MIN 0x3 /* Bank 0 and Band 1 */ +#define RTC7301_1_HOUR 0x4 /* Bank 0 and Band 1 */ +#define RTC7301_10_HOUR 0x5 /* Bank 0 and Band 1 */ +#define RTC7301_DAY_OF_WEEK 0x6 /* Bank 0 and Band 1 */ +#define RTC7301_1_DAY 0x7 /* Bank 0 and Band 1 */ +#define RTC7301_10_DAY 0x8 /* Bank 0 and Band 1 */ +#define RTC7301_1_MONTH 0x9 /* Bank 0 */ +#define RTC7301_10_MONTH 0xa /* Bank 0 */ +#define RTC7301_1_YEAR 0xb /* Bank 0 */ +#define RTC7301_10_YEAR 0xc /* Bank 0 */ +#define RTC7301_100_YEAR 0xd /* Bank 0 */ +#define RTC7301_1000_YEAR 0xe /* Bank 0 */ +#define RTC7301_ALARM_CONTROL 0xe /* Bank 1 */ +#define RTC7301_ALARM_CONTROL_AIE BIT(0) +#define RTC7301_ALARM_CONTROL_AF BIT(1) +#define RTC7301_TIMER_CONTROL 0xe /* Bank 2 */ +#define RTC7301_TIMER_CONTROL_TIE BIT(0) +#define RTC7301_TIMER_CONTROL_TF BIT(1) +#define RTC7301_CONTROL 0xf /* All banks */ +#define RTC7301_CONTROL_BUSY BIT(0) +#define RTC7301_CONTROL_STOP BIT(1) +#define RTC7301_CONTROL_BANK_SEL_0 BIT(2) +#define RTC7301_CONTROL_BANK_SEL_1 BIT(3) + +struct rtc7301_priv { + struct regmap *regmap; + int irq; + spinlock_t lock; + u8 bank; +}; + +static const struct regmap_config rtc7301_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_stride = 4, +}; + +static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg) +{ + int reg_stride = regmap_get_reg_stride(priv->regmap); + unsigned int val; + + regmap_read(priv->regmap, reg_stride * reg, &val); + + return val & 0xf; +} + +static void rtc7301_write(struct rtc7301_priv *priv, u8 val, unsigned int reg) +{ + int reg_stride = regmap_get_reg_stride(priv->regmap); + + regmap_write(priv->regmap, reg_stride * reg, val); +} + +static void rtc7301_update_bits(struct rtc7301_priv *priv, unsigned int reg, + u8 mask, u8 val) +{ + int reg_stride = regmap_get_reg_stride(priv->regmap); + + regmap_update_bits(priv->regmap, reg_stride * reg, mask, val); +} + +static int rtc7301_wait_while_busy(struct rtc7301_priv *priv) +{ + int retries = 100; + + while (retries-- > 0) { + u8 val; + + val = rtc7301_read(priv, RTC7301_CONTROL); + if (!(val & RTC7301_CONTROL_BUSY)) + return 0; + + usleep_range(200, 300); + } + + return -ETIMEDOUT; +} + +static void rtc7301_stop(struct rtc7301_priv *priv) +{ + rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP, + RTC7301_CONTROL_STOP); +} + +static void rtc7301_start(struct rtc7301_priv *priv) +{ + rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP, 0); +} + +static void rtc7301_select_bank(struct rtc7301_priv *priv, u8 bank) +{ + u8 val = 0; + + if (bank == priv->bank) + return; + + if (bank & BIT(0)) + val |= RTC7301_CONTROL_BANK_SEL_0; + if (bank & BIT(1)) + val |= RTC7301_CONTROL_BANK_SEL_1; + + rtc7301_update_bits(priv, RTC7301_CONTROL, + RTC7301_CONTROL_BANK_SEL_0 | + RTC7301_CONTROL_BANK_SEL_1, val); + + priv->bank = bank; +} + +static void rtc7301_get_time(struct rtc7301_priv *priv, struct rtc_time *tm, + bool alarm) +{ + int year; + + tm->tm_sec = rtc7301_read(priv, RTC7301_1_SEC); + tm->tm_sec += (rtc7301_read(priv, RTC7301_10_SEC) & ~RTC7301_AE) * 10; + tm->tm_min = rtc7301_read(priv, RTC7301_1_MIN); + tm->tm_min += (rtc7301_read(priv, RTC7301_10_MIN) & ~RTC7301_AE) * 10; + tm->tm_hour = rtc7301_read(priv, RTC7301_1_HOUR); + tm->tm_hour += (rtc7301_read(priv, RTC7301_10_HOUR) & ~RTC7301_AE) * 10; + tm->tm_mday = rtc7301_read(priv, RTC7301_1_DAY); + tm->tm_mday += (rtc7301_read(priv, RTC7301_10_DAY) & ~RTC7301_AE) * 10; + + if (alarm) { + tm->tm_wday = -1; + tm->tm_mon = -1; + tm->tm_year = -1; + tm->tm_yday = -1; + tm->tm_isdst = -1; + return; + } + + tm->tm_wday = (rtc7301_read(priv, RTC7301_DAY_OF_WEEK) & ~RTC7301_AE); + tm->tm_mon = rtc7301_read(priv, RTC7301_10_MONTH) * 10 + + rtc7301_read(priv, RTC7301_1_MONTH) - 1; + year = rtc7301_read(priv, RTC7301_1000_YEAR) * 1000 + + rtc7301_read(priv, RTC7301_100_YEAR) * 100 + + rtc7301_read(priv, RTC7301_10_YEAR) * 10 + + rtc7301_read(priv, RTC7301_1_YEAR); + + tm->tm_year = year - 1900; +} + +static void rtc7301_write_time(struct rtc7301_priv *priv, struct rtc_time *tm, + bool alarm) +{ + int year; + + rtc7301_write(priv, tm->tm_sec % 10, RTC7301_1_SEC); + rtc7301_write(priv, tm->tm_sec / 10, RTC7301_10_SEC); + + rtc7301_write(priv, tm->tm_min % 10, RTC7301_1_MIN); + rtc7301_write(priv, tm->tm_min / 10, RTC7301_10_MIN); + + rtc7301_write(priv, tm->tm_hour % 10, RTC7301_1_HOUR); + rtc7301_write(priv, tm->tm_hour / 10, RTC7301_10_HOUR); + + rtc7301_write(priv, tm->tm_mday % 10, RTC7301_1_DAY); + rtc7301_write(priv, tm->tm_mday / 10, RTC7301_10_DAY); + + /* Don't care for alarm register */ + rtc7301_write(priv, alarm ? RTC7301_AE : tm->tm_wday, + RTC7301_DAY_OF_WEEK); + + if (alarm) + return; + + rtc7301_write(priv, (tm->tm_mon + 1) % 10, RTC7301_1_MONTH); + rtc7301_write(priv, (tm->tm_mon + 1) / 10, RTC7301_10_MONTH); + + year = tm->tm_year + 1900; + + rtc7301_write(priv, year % 10, RTC7301_1_YEAR); + rtc7301_write(priv, (year / 10) % 10, RTC7301_10_YEAR); + rtc7301_write(priv, (year / 100) % 10, RTC7301_100_YEAR); + rtc7301_write(priv, year / 1000, RTC7301_1000_YEAR); +} + +static void rtc7301_alarm_irq(struct rtc7301_priv *priv, unsigned int enabled) +{ + rtc7301_update_bits(priv, RTC7301_ALARM_CONTROL, + RTC7301_ALARM_CONTROL_AF | + RTC7301_ALARM_CONTROL_AIE, + enabled ? RTC7301_ALARM_CONTROL_AIE : 0); +} + +static int rtc7301_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + int err; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 0); + + err = rtc7301_wait_while_busy(priv); + if (!err) + rtc7301_get_time(priv, tm, false); + + spin_unlock_irqrestore(&priv->lock, flags); + + return err ? err : rtc_valid_tm(tm); +} + +static int rtc7301_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_stop(priv); + usleep_range(200, 300); + rtc7301_select_bank(priv, 0); + rtc7301_write_time(priv, tm, false); + rtc7301_start(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int rtc7301_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + u8 alrm_ctrl; + + if (priv->irq <= 0) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + rtc7301_get_time(priv, &alarm->time, true); + + alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL); + + alarm->enabled = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AIE); + alarm->pending = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AF); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int rtc7301_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + if (priv->irq <= 0) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + rtc7301_write_time(priv, &alarm->time, true); + rtc7301_alarm_irq(priv, alarm->enabled); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int rtc7301_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + if (priv->irq <= 0) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + rtc7301_alarm_irq(priv, enabled); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static const struct rtc_class_ops rtc7301_rtc_ops = { + .read_time = rtc7301_read_time, + .set_time = rtc7301_set_time, + .read_alarm = rtc7301_read_alarm, + .set_alarm = rtc7301_set_alarm, + .alarm_irq_enable = rtc7301_alarm_irq_enable, +}; + +static irqreturn_t rtc7301_irq_handler(int irq, void *dev_id) +{ + struct rtc_device *rtc = dev_id; + struct rtc7301_priv *priv = dev_get_drvdata(rtc->dev.parent); + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + u8 alrm_ctrl; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + + alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL); + if (alrm_ctrl & RTC7301_ALARM_CONTROL_AF) { + ret = IRQ_HANDLED; + rtc7301_alarm_irq(priv, false); + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static void rtc7301_init(struct rtc7301_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 2); + rtc7301_write(priv, 0, RTC7301_TIMER_CONTROL); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int __init rtc7301_rtc_probe(struct platform_device *dev) +{ + struct resource *res; + void __iomem *regs; + struct rtc7301_priv *priv; + struct rtc_device *rtc; + int ret; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + regs = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->regmap = devm_regmap_init_mmio(&dev->dev, regs, + &rtc7301_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->irq = platform_get_irq(dev, 0); + + spin_lock_init(&priv->lock); + priv->bank = -1; + + rtc7301_init(priv); + + platform_set_drvdata(dev, priv); + + rtc = devm_rtc_device_register(&dev->dev, DRV_NAME, &rtc7301_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + if (priv->irq > 0) { + ret = devm_request_irq(&dev->dev, priv->irq, + rtc7301_irq_handler, IRQF_SHARED, + dev_name(&dev->dev), rtc); + if (ret) { + priv->irq = 0; + dev_err(&dev->dev, "unable to request IRQ\n"); + } else { + device_set_wakeup_capable(&dev->dev, true); + } + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int rtc7301_suspend(struct device *dev) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(priv->irq); + + return 0; +} + +static int rtc7301_resume(struct device *dev) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(priv->irq); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(rtc7301_pm_ops, rtc7301_suspend, rtc7301_resume); + +static const struct of_device_id rtc7301_dt_match[] = { + { .compatible = "epson,rtc7301sf" }, + { .compatible = "epson,rtc7301dg" }, + {} +}; +MODULE_DEVICE_TABLE(of, rtc7301_dt_match); + +static struct platform_driver rtc7301_rtc_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = rtc7301_dt_match, + .pm = &rtc7301_pm_ops, + }, +}; + +module_platform_driver_probe(rtc7301_rtc_driver, rtc7301_rtc_probe); + +MODULE_AUTHOR("Akinobu Mita "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("EPSON TOYOCOM RTC-7301SF/DG Driver"); +MODULE_ALIAS("platform:rtc-r7301"); -- cgit v1.2.3 From bcf18d88ac16c1a86bac7e8f5f4b1de1f752865f Mon Sep 17 00:00:00 2001 From: Emil Bartczak Date: Thu, 8 Dec 2016 00:27:37 +0100 Subject: rtc: mcp795: use bcd2bin/bin2bcd. Change rtc-mcp795.c to use the bcd2bin/bin2bcd functions. This change fixes the wrong conversion of month value from binary to BCD (missing right shift operation for 10 month). Signed-off-by: Emil Bartczak Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mcp795.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c index 4021fd04cb0a..0389ee03c660 100644 --- a/drivers/rtc/rtc-mcp795.c +++ b/drivers/rtc/rtc-mcp795.c @@ -21,6 +21,7 @@ #include #include #include +#include /* MCP795 Instructions, see datasheet table 3-1 */ #define MCP795_EEREAD 0x03 @@ -104,16 +105,16 @@ static int mcp795_set_time(struct device *dev, struct rtc_time *tim) if (ret) return ret; - data[0] = (data[0] & 0x80) | ((tim->tm_sec / 10) << 4) | (tim->tm_sec % 10); - data[1] = (data[1] & 0x80) | ((tim->tm_min / 10) << 4) | (tim->tm_min % 10); - data[2] = ((tim->tm_hour / 10) << 4) | (tim->tm_hour % 10); - data[4] = ((tim->tm_mday / 10) << 4) | ((tim->tm_mday) % 10); - data[5] = (data[5] & 0x10) | (tim->tm_mon / 10) | (tim->tm_mon % 10); + data[0] = (data[0] & 0x80) | bin2bcd(tim->tm_sec); + data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min); + data[2] = bin2bcd(tim->tm_hour); + data[4] = bin2bcd(tim->tm_mday); + data[5] = (data[5] & 0x10) | bin2bcd(tim->tm_mon); if (tim->tm_year > 100) tim->tm_year -= 100; - data[6] = ((tim->tm_year / 10) << 4) | (tim->tm_year % 10); + data[6] = bin2bcd(tim->tm_year); ret = mcp795_rtcc_write(dev, 0x01, data, sizeof(data)); @@ -137,12 +138,12 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim) if (ret) return ret; - tim->tm_sec = ((data[0] & 0x70) >> 4) * 10 + (data[0] & 0x0f); - tim->tm_min = ((data[1] & 0x70) >> 4) * 10 + (data[1] & 0x0f); - tim->tm_hour = ((data[2] & 0x30) >> 4) * 10 + (data[2] & 0x0f); - tim->tm_mday = ((data[4] & 0x30) >> 4) * 10 + (data[4] & 0x0f); - tim->tm_mon = ((data[5] & 0x10) >> 4) * 10 + (data[5] & 0x0f); - tim->tm_year = ((data[6] & 0xf0) >> 4) * 10 + (data[6] & 0x0f) + 100; /* Assume we are in 20xx */ + tim->tm_sec = bcd2bin(data[0] & 0x7F); + tim->tm_min = bcd2bin(data[1] & 0x7F); + tim->tm_hour = bcd2bin(data[2] & 0x3F); + tim->tm_mday = bcd2bin(data[4] & 0x3F); + tim->tm_mon = bcd2bin(data[5] & 0x1F); + tim->tm_year = bcd2bin(data[6]) + 100; /* Assume we are in 20xx */ dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d %02d:%02d:%02d\n", tim->tm_year + 1900, tim->tm_mon, tim->tm_mday, -- cgit v1.2.3 From e72765c648a172f052486cba9688ddc28f23140b Mon Sep 17 00:00:00 2001 From: Emil Bartczak Date: Thu, 8 Dec 2016 00:27:38 +0100 Subject: rtc: mcp795: fix bitmask value for leap year (LP). According the datasheet the leap year is a fifth bit in month register. Signed-off-by: Emil Bartczak Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mcp795.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c index 0389ee03c660..5fbdb4c20a9b 100644 --- a/drivers/rtc/rtc-mcp795.c +++ b/drivers/rtc/rtc-mcp795.c @@ -40,6 +40,7 @@ #define MCP795_ST_BIT 0x80 #define MCP795_24_BIT 0x40 +#define MCP795_LP_BIT BIT(5) static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count) { @@ -109,7 +110,7 @@ static int mcp795_set_time(struct device *dev, struct rtc_time *tim) data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min); data[2] = bin2bcd(tim->tm_hour); data[4] = bin2bcd(tim->tm_mday); - data[5] = (data[5] & 0x10) | bin2bcd(tim->tm_mon); + data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon); if (tim->tm_year > 100) tim->tm_year -= 100; -- cgit v1.2.3 From 26eeefd5956449b03c87c49b996e012ffe3e59aa Mon Sep 17 00:00:00 2001 From: Emil Bartczak Date: Thu, 8 Dec 2016 00:27:39 +0100 Subject: rtc: mcp795: fix time range difference between linux and RTC chip. In linux rtc_time struct, tm_mon range is 0~11, while in RTC HW REG, month range is 1~12. This patch adjusts difference of them. Signed-off-by: Emil Bartczak Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mcp795.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c index 5fbdb4c20a9b..4618f4fa655f 100644 --- a/drivers/rtc/rtc-mcp795.c +++ b/drivers/rtc/rtc-mcp795.c @@ -110,7 +110,7 @@ static int mcp795_set_time(struct device *dev, struct rtc_time *tim) data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min); data[2] = bin2bcd(tim->tm_hour); data[4] = bin2bcd(tim->tm_mday); - data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon); + data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon + 1); if (tim->tm_year > 100) tim->tm_year -= 100; @@ -143,7 +143,7 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim) tim->tm_min = bcd2bin(data[1] & 0x7F); tim->tm_hour = bcd2bin(data[2] & 0x3F); tim->tm_mday = bcd2bin(data[4] & 0x3F); - tim->tm_mon = bcd2bin(data[5] & 0x1F); + tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1; tim->tm_year = bcd2bin(data[6]) + 100; /* Assume we are in 20xx */ dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d %02d:%02d:%02d\n", -- cgit v1.2.3 From 43d0b10f60c54667108729adb525bc1090d4238f Mon Sep 17 00:00:00 2001 From: Emil Bartczak Date: Thu, 8 Dec 2016 00:27:40 +0100 Subject: rtc: mcp795: fix month write resetting date to 1. According to Microchip errata some combinations of date and month values may result in the date being reset to 1, even if the date is also written with the month (for example 31-07 or 31-08). As a workaround avoid writing date and month values within the same Write command. Instead, terminate the Write command after loading the date and begin a new command to write the month. In addition, disable the oscillator before loading the new values. This is done by ensuring both the ST and EXTOSC bits are cleared and waiting for the OSCON bit to clear. Signed-off-by: Emil Bartczak Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mcp795.c | 86 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 5 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c index 4618f4fa655f..89b0ffa83064 100644 --- a/drivers/rtc/rtc-mcp795.c +++ b/drivers/rtc/rtc-mcp795.c @@ -22,6 +22,7 @@ #include #include #include +#include /* MCP795 Instructions, see datasheet table 3-1 */ #define MCP795_EEREAD 0x03 @@ -38,9 +39,17 @@ #define MCP795_CLRWDT 0x44 #define MCP795_CLRRAM 0x54 +/* MCP795 RTCC registers, see datasheet table 4-1 */ +#define MCP795_REG_SECONDS 0x01 +#define MCP795_REG_DAY 0x04 +#define MCP795_REG_MONTH 0x06 +#define MCP795_REG_CONTROL 0x08 + #define MCP795_ST_BIT 0x80 #define MCP795_24_BIT 0x40 #define MCP795_LP_BIT BIT(5) +#define MCP795_EXTOSC_BIT BIT(3) +#define MCP795_OSCON_BIT BIT(5) static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count) { @@ -95,13 +104,65 @@ static int mcp795_rtcc_set_bits(struct device *dev, u8 addr, u8 mask, u8 state) return ret; } +static int mcp795_stop_oscillator(struct device *dev, bool *extosc) +{ + int retries = 5; + int ret; + u8 data; + + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_SECONDS, MCP795_ST_BIT, 0); + if (ret) + return ret; + ret = mcp795_rtcc_read(dev, MCP795_REG_CONTROL, &data, 1); + if (ret) + return ret; + *extosc = !!(data & MCP795_EXTOSC_BIT); + ret = mcp795_rtcc_set_bits( + dev, MCP795_REG_CONTROL, MCP795_EXTOSC_BIT, 0); + if (ret) + return ret; + /* wait for the OSCON bit to clear */ + do { + usleep_range(700, 800); + ret = mcp795_rtcc_read(dev, MCP795_REG_DAY, &data, 1); + if (ret) + break; + if (!(data & MCP795_OSCON_BIT)) + break; + + } while (--retries); + + return !retries ? -EIO : ret; +} + +static int mcp795_start_oscillator(struct device *dev, bool *extosc) +{ + if (extosc) { + u8 data = *extosc ? MCP795_EXTOSC_BIT : 0; + int ret; + + ret = mcp795_rtcc_set_bits( + dev, MCP795_REG_CONTROL, MCP795_EXTOSC_BIT, data); + if (ret) + return ret; + } + return mcp795_rtcc_set_bits( + dev, MCP795_REG_SECONDS, MCP795_ST_BIT, MCP795_ST_BIT); +} + static int mcp795_set_time(struct device *dev, struct rtc_time *tim) { int ret; u8 data[7]; + bool extosc; + + /* Stop RTC and store current value of EXTOSC bit */ + ret = mcp795_stop_oscillator(dev, &extosc); + if (ret) + return ret; /* Read first, so we can leave config bits untouched */ - ret = mcp795_rtcc_read(dev, 0x01, data, sizeof(data)); + ret = mcp795_rtcc_read(dev, MCP795_REG_SECONDS, data, sizeof(data)); if (ret) return ret; @@ -117,8 +178,23 @@ static int mcp795_set_time(struct device *dev, struct rtc_time *tim) data[6] = bin2bcd(tim->tm_year); - ret = mcp795_rtcc_write(dev, 0x01, data, sizeof(data)); + /* Always write the date and month using a separate Write command. + * This is a workaround for a know silicon issue that some combinations + * of date and month values may result in the date being reset to 1. + */ + ret = mcp795_rtcc_write(dev, MCP795_REG_SECONDS, data, 5); + if (ret) + return ret; + + ret = mcp795_rtcc_write(dev, MCP795_REG_MONTH, &data[5], 2); + if (ret) + return ret; + /* Start back RTC and restore previous value of EXTOSC bit. + * There is no need to clear EXTOSC bit when the previous value was 0 + * because it was already cleared when stopping the RTC oscillator. + */ + ret = mcp795_start_oscillator(dev, extosc ? &extosc : NULL); if (ret) return ret; @@ -134,7 +210,7 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim) int ret; u8 data[7]; - ret = mcp795_rtcc_read(dev, 0x01, data, sizeof(data)); + ret = mcp795_rtcc_read(dev, MCP795_REG_SECONDS, data, sizeof(data)); if (ret) return ret; @@ -171,8 +247,8 @@ static int mcp795_probe(struct spi_device *spi) return ret; } - /* Start the oscillator */ - mcp795_rtcc_set_bits(&spi->dev, 0x01, MCP795_ST_BIT, MCP795_ST_BIT); + /* Start the oscillator but don't set the value of EXTOSC bit */ + mcp795_start_oscillator(&spi->dev, NULL); /* Clear the 12 hour mode flag*/ mcp795_rtcc_set_bits(&spi->dev, 0x03, MCP795_24_BIT, 0); -- cgit v1.2.3 From a2b42997513401903341cf96616839ad22f151b6 Mon Sep 17 00:00:00 2001 From: Emil Bartczak Date: Thu, 8 Dec 2016 00:27:41 +0100 Subject: rtc: mcp795: Prefer using the BIT() macro. This patch doesn't change the code but replaces all bitmask values with the BIT(x) macro. Signed-off-by: Emil Bartczak Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mcp795.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c index 89b0ffa83064..8ed55f6ff8ba 100644 --- a/drivers/rtc/rtc-mcp795.c +++ b/drivers/rtc/rtc-mcp795.c @@ -45,8 +45,8 @@ #define MCP795_REG_MONTH 0x06 #define MCP795_REG_CONTROL 0x08 -#define MCP795_ST_BIT 0x80 -#define MCP795_24_BIT 0x40 +#define MCP795_ST_BIT BIT(7) +#define MCP795_24_BIT BIT(6) #define MCP795_LP_BIT BIT(5) #define MCP795_EXTOSC_BIT BIT(3) #define MCP795_OSCON_BIT BIT(5) -- cgit v1.2.3 From d3e5925902dc0f639efc3641e07fca2bd7af5441 Mon Sep 17 00:00:00 2001 From: Emil Bartczak Date: Thu, 8 Dec 2016 00:27:42 +0100 Subject: rtc: mcp795: Fix whitespace and indentation. Fix whitespace and indentation errors and the following checkpatch warnings: - line 15: Block comments use a trailing */ on a separate line - line 256: Line over 80 characters No code change. Signed-off-by: Emil Bartczak Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mcp795.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c index 8ed55f6ff8ba..ce75e421ba00 100644 --- a/drivers/rtc/rtc-mcp795.c +++ b/drivers/rtc/rtc-mcp795.c @@ -12,7 +12,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * */ + */ #include #include @@ -31,7 +31,7 @@ #define MCP795_EEWREN 0x06 #define MCP795_SRREAD 0x05 #define MCP795_SRWRITE 0x01 -#define MCP795_READ 0x13 +#define MCP795_READ 0x13 #define MCP795_WRITE 0x12 #define MCP795_UNLOCK 0x14 #define MCP795_IDWRITE 0x32 @@ -45,9 +45,9 @@ #define MCP795_REG_MONTH 0x06 #define MCP795_REG_CONTROL 0x08 -#define MCP795_ST_BIT BIT(7) -#define MCP795_24_BIT BIT(6) -#define MCP795_LP_BIT BIT(5) +#define MCP795_ST_BIT BIT(7) +#define MCP795_24_BIT BIT(6) +#define MCP795_LP_BIT BIT(5) #define MCP795_EXTOSC_BIT BIT(3) #define MCP795_OSCON_BIT BIT(5) @@ -253,7 +253,7 @@ static int mcp795_probe(struct spi_device *spi) mcp795_rtcc_set_bits(&spi->dev, 0x03, MCP795_24_BIT, 0); rtc = devm_rtc_device_register(&spi->dev, "rtc-mcp795", - &mcp795_rtc_ops, THIS_MODULE); + &mcp795_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) return PTR_ERR(rtc); -- cgit v1.2.3