From 6f1c1e71d933f58a6248f1681aededdd407f32a8 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 4 Jul 2014 22:24:04 +0200 Subject: mfd: max77686: Convert to use regmap_irq By using the generic IRQ support in the Register map API, it is possible to get rid max77686-irq.c and simplify the code. Suggested-by: Krzysztof Kozlowski Signed-off-by: Javier Martinez Canillas Reviewed-by: Doug Anderson Tested-by: Doug Anderson Signed-off-by: Lee Jones --- drivers/rtc/rtc-max77686.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 9efe118a28ba..d20a7f0786eb 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -492,16 +492,11 @@ static int max77686_rtc_init_reg(struct max77686_rtc_info *info) return ret; } -static struct regmap_config max77686_rtc_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; - static int max77686_rtc_probe(struct platform_device *pdev) { struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent); struct max77686_rtc_info *info; - int ret, virq; + int ret; dev_info(&pdev->dev, "%s\n", __func__); @@ -514,14 +509,7 @@ static int max77686_rtc_probe(struct platform_device *pdev) info->dev = &pdev->dev; info->max77686 = max77686; info->rtc = max77686->rtc; - info->max77686->rtc_regmap = devm_regmap_init_i2c(info->max77686->rtc, - &max77686_rtc_regmap_config); - if (IS_ERR(info->max77686->rtc_regmap)) { - ret = PTR_ERR(info->max77686->rtc_regmap); - dev_err(info->max77686->dev, "Failed to allocate register map: %d\n", - ret); - return ret; - } + platform_set_drvdata(pdev, info); ret = max77686_rtc_init_reg(info); @@ -550,15 +538,16 @@ static int max77686_rtc_probe(struct platform_device *pdev) ret = -EINVAL; goto err_rtc; } - virq = irq_create_mapping(max77686->irq_domain, MAX77686_RTCIRQ_RTCA1); - if (!virq) { + + info->virq = regmap_irq_get_virq(max77686->rtc_irq_data, + MAX77686_RTCIRQ_RTCA1); + if (!info->virq) { ret = -ENXIO; goto err_rtc; } - info->virq = virq; - ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, - max77686_rtc_alarm_irq, 0, "rtc-alarm0", info); + ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL, + max77686_rtc_alarm_irq, 0, "rtc-alarm1", info); if (ret < 0) dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", info->virq, ret); -- cgit v1.2.2 From 9cb42e2a8ed06e91ce9d2c59fbae8d1185ebe2f7 Mon Sep 17 00:00:00 2001 From: "Opensource [Steve Twiss]" Date: Mon, 21 Jul 2014 11:39:33 +0100 Subject: mfd: da9063: Add support for AD silicon variant Add register definitions for DA9063 AD (0x3) silicon variant ID the ability to choose the silicon variant at run-time using regmap configuration. This patch also adds RTC support for the AD silicon changes. It adds both BB and AD support as regmap ranges and then makes the distinction between the two tables at run-time. This allows both AD and BB silicon variants to be supported at the same time. Suggested-by: Philipp Zabel Signed-off-by: Opensource [Steve Twiss] Signed-off-by: Lee Jones --- drivers/rtc/rtc-da9063.c | 54 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 17 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c index 595393098b09..731ed1a97f59 100644 --- a/drivers/rtc/rtc-da9063.c +++ b/drivers/rtc/rtc-da9063.c @@ -29,6 +29,8 @@ #define YEARS_FROM_DA9063(year) ((year) + 100) #define MONTHS_FROM_DA9063(month) ((month) - 1) +#define RTC_ALARM_DATA_LEN (DA9063_AD_REG_ALARM_Y - DA9063_AD_REG_ALARM_MI + 1) + #define RTC_DATA_LEN (DA9063_REG_COUNT_Y - DA9063_REG_COUNT_S + 1) #define RTC_SEC 0 #define RTC_MIN 1 @@ -42,6 +44,10 @@ struct da9063_rtc { struct da9063 *hw; struct rtc_time alarm_time; bool rtc_sync; + int alarm_year; + int alarm_start; + int alarm_len; + int data_start; }; static void da9063_data_to_tm(u8 *data, struct rtc_time *tm) @@ -83,7 +89,7 @@ static int da9063_rtc_stop_alarm(struct device *dev) { struct da9063_rtc *rtc = dev_get_drvdata(dev); - return regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y, + return regmap_update_bits(rtc->hw->regmap, rtc->alarm_year, DA9063_ALARM_ON, 0); } @@ -91,7 +97,7 @@ static int da9063_rtc_start_alarm(struct device *dev) { struct da9063_rtc *rtc = dev_get_drvdata(dev); - return regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y, + return regmap_update_bits(rtc->hw->regmap, rtc->alarm_year, DA9063_ALARM_ON, DA9063_ALARM_ON); } @@ -151,8 +157,9 @@ static int da9063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) int ret; unsigned int val; - ret = regmap_bulk_read(rtc->hw->regmap, DA9063_REG_ALARM_S, - &data[RTC_SEC], RTC_DATA_LEN); + data[RTC_SEC] = 0; + ret = regmap_bulk_read(rtc->hw->regmap, rtc->alarm_start, + &data[rtc->data_start], rtc->alarm_len); if (ret < 0) return ret; @@ -186,14 +193,14 @@ static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } - ret = regmap_bulk_write(rtc->hw->regmap, DA9063_REG_ALARM_S, - data, RTC_DATA_LEN); + ret = regmap_bulk_write(rtc->hw->regmap, rtc->alarm_start, + &data[rtc->data_start], rtc->alarm_len); if (ret < 0) { dev_err(dev, "Failed to write alarm: %d\n", ret); return ret; } - rtc->alarm_time = alrm->time; + da9063_data_to_tm(data, &rtc->alarm_time); if (alrm->enabled) { ret = da9063_rtc_start_alarm(dev); @@ -218,7 +225,7 @@ static irqreturn_t da9063_alarm_event(int irq, void *data) { struct da9063_rtc *rtc = data; - regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y, + regmap_update_bits(rtc->hw->regmap, rtc->alarm_year, DA9063_ALARM_ON, 0); rtc->rtc_sync = true; @@ -257,7 +264,23 @@ static int da9063_rtc_probe(struct platform_device *pdev) goto err; } - ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_S, + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + if (da9063->variant_code == PMIC_DA9063_AD) { + rtc->alarm_year = DA9063_AD_REG_ALARM_Y; + rtc->alarm_start = DA9063_AD_REG_ALARM_MI; + rtc->alarm_len = RTC_ALARM_DATA_LEN; + rtc->data_start = RTC_MIN; + } else { + rtc->alarm_year = DA9063_BB_REG_ALARM_Y; + rtc->alarm_start = DA9063_BB_REG_ALARM_S; + rtc->alarm_len = RTC_DATA_LEN; + rtc->data_start = RTC_SEC; + } + + ret = regmap_update_bits(da9063->regmap, rtc->alarm_start, DA9063_ALARM_STATUS_TICK | DA9063_ALARM_STATUS_ALARM, 0); if (ret < 0) { @@ -265,7 +288,7 @@ static int da9063_rtc_probe(struct platform_device *pdev) goto err; } - ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_S, + ret = regmap_update_bits(da9063->regmap, rtc->alarm_start, DA9063_ALARM_STATUS_ALARM, DA9063_ALARM_STATUS_ALARM); if (ret < 0) { @@ -273,25 +296,22 @@ static int da9063_rtc_probe(struct platform_device *pdev) goto err; } - ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_Y, + ret = regmap_update_bits(da9063->regmap, rtc->alarm_year, DA9063_TICK_ON, 0); if (ret < 0) { dev_err(&pdev->dev, "Failed to disable TICKs\n"); goto err; } - ret = regmap_bulk_read(da9063->regmap, DA9063_REG_ALARM_S, - data, RTC_DATA_LEN); + data[RTC_SEC] = 0; + ret = regmap_bulk_read(da9063->regmap, rtc->alarm_start, + &data[rtc->data_start], rtc->alarm_len); if (ret < 0) { dev_err(&pdev->dev, "Failed to read initial alarm data: %d\n", ret); goto err; } - rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); - if (!rtc) - return -ENOMEM; - platform_set_drvdata(pdev, rtc); irq_alarm = platform_get_irq_byname(pdev, "ALARM"); -- cgit v1.2.2 From 1d09de7dc76ef96a9a2c7c0244e20f12d68e6ef8 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 23 Jul 2014 16:36:24 +0200 Subject: MIPS: Alchemy: introduce helpers to access SYS register block. This patch changes all absolute SYS_XY registers to offsets from the SYS block base, prefixes them with AU1000 to avoid silent failures due to changed addresses, and introduces helper functions to read/write them. No functional changes, comparing assembly of a few select functions shows no differences. Signed-off-by: Manuel Lauss Cc: Linux-MIPS Patchwork: https://patchwork.linux-mips.org/patch/7464/ Signed-off-by: Ralf Baechle --- drivers/rtc/rtc-au1xxx.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-au1xxx.c b/drivers/rtc/rtc-au1xxx.c index ed526a192ce0..fd25e2374d4e 100644 --- a/drivers/rtc/rtc-au1xxx.c +++ b/drivers/rtc/rtc-au1xxx.c @@ -32,7 +32,7 @@ static int au1xtoy_rtc_read_time(struct device *dev, struct rtc_time *tm) { unsigned long t; - t = au_readl(SYS_TOYREAD); + t = alchemy_rdsys(AU1000_SYS_TOYREAD); rtc_time_to_tm(t, tm); @@ -45,13 +45,12 @@ static int au1xtoy_rtc_set_time(struct device *dev, struct rtc_time *tm) rtc_tm_to_time(tm, &t); - au_writel(t, SYS_TOYWRITE); - au_sync(); + alchemy_wrsys(t, AU1000_SYS_TOYWRITE); /* wait for the pending register write to succeed. This can * take up to 6 seconds... */ - while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S) + while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S) msleep(1); return 0; @@ -68,7 +67,7 @@ static int au1xtoy_rtc_probe(struct platform_device *pdev) unsigned long t; int ret; - t = au_readl(SYS_COUNTER_CNTRL); + t = alchemy_rdsys(AU1000_SYS_CNTRCTRL); if (!(t & CNTR_OK)) { dev_err(&pdev->dev, "counters not working; aborting.\n"); ret = -ENODEV; @@ -78,10 +77,10 @@ static int au1xtoy_rtc_probe(struct platform_device *pdev) ret = -ETIMEDOUT; /* set counter0 tickrate to 1Hz if necessary */ - if (au_readl(SYS_TOYTRIM) != 32767) { + if (alchemy_rdsys(AU1000_SYS_TOYTRIM) != 32767) { /* wait until hardware gives access to TRIM register */ t = 0x00100000; - while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T0S) && --t) + while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_T0S) && --t) msleep(1); if (!t) { @@ -93,12 +92,11 @@ static int au1xtoy_rtc_probe(struct platform_device *pdev) } /* set 1Hz TOY tick rate */ - au_writel(32767, SYS_TOYTRIM); - au_sync(); + alchemy_wrsys(32767, AU1000_SYS_TOYTRIM); } /* wait until the hardware allows writes to the counter reg */ - while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S) + while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S) msleep(1); rtcdev = devm_rtc_device_register(&pdev->dev, "rtc-au1xxx", -- cgit v1.2.2 From 571eb88390182b0439f72326bb18aa10ebef5d88 Mon Sep 17 00:00:00 2001 From: Raghavendra Ganiga Date: Fri, 8 Aug 2014 14:20:01 -0700 Subject: drivers/rtc/rtc-ds1343.c: add support of nvram for maxim dallas rtc ds1343 This is a patch to add support of nvram for maxim dallas rtc ds1343. Signed-off-by: Raghavendra Chandra Ganiga Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1343.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c index c3719189dd96..ae9f997223b1 100644 --- a/drivers/rtc/rtc-ds1343.c +++ b/drivers/rtc/rtc-ds1343.c @@ -4,6 +4,7 @@ * Real Time Clock * * Author : Raghavendra Chandra Ganiga + * Ankur Srivastava : DS1343 Nvram Support * * 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 @@ -45,6 +46,9 @@ #define DS1343_CONTROL_REG 0x0F #define DS1343_STATUS_REG 0x10 #define DS1343_TRICKLE_REG 0x11 +#define DS1343_NVRAM 0x20 + +#define DS1343_NVRAM_LEN 96 /* DS1343 Control Registers bits */ #define DS1343_EOSC 0x80 @@ -149,6 +153,64 @@ static ssize_t ds1343_store_glitchfilter(struct device *dev, static DEVICE_ATTR(glitch_filter, S_IRUGO | S_IWUSR, ds1343_show_glitchfilter, ds1343_store_glitchfilter); +static ssize_t ds1343_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + int ret; + unsigned char address; + struct device *dev = kobj_to_dev(kobj); + struct ds1343_priv *priv = dev_get_drvdata(dev); + + if (unlikely(!count)) + return count; + + if ((count + off) > DS1343_NVRAM_LEN) + count = DS1343_NVRAM_LEN - off; + + address = DS1343_NVRAM + off; + + ret = regmap_bulk_write(priv->map, address, buf, count); + if (ret < 0) + dev_err(&priv->spi->dev, "Error in nvram write %d", ret); + + return (ret < 0) ? ret : count; +} + + +static ssize_t ds1343_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + int ret; + unsigned char address; + struct device *dev = kobj_to_dev(kobj); + struct ds1343_priv *priv = dev_get_drvdata(dev); + + if (unlikely(!count)) + return count; + + if ((count + off) > DS1343_NVRAM_LEN) + count = DS1343_NVRAM_LEN - off; + + address = DS1343_NVRAM + off; + + ret = regmap_bulk_read(priv->map, address, buf, count); + if (ret < 0) + dev_err(&priv->spi->dev, "Error in nvram read %d\n", ret); + + return (ret < 0) ? ret : count; +} + + +static struct bin_attribute nvram_attr = { + .attr.name = "nvram", + .attr.mode = S_IRUGO | S_IWUSR, + .read = ds1343_nvram_read, + .write = ds1343_nvram_write, + .size = DS1343_NVRAM_LEN, +}; + static ssize_t ds1343_show_alarmstatus(struct device *dev, struct device_attribute *attr, char *buf) { @@ -274,12 +336,16 @@ static int ds1343_sysfs_register(struct device *dev) if (err) goto error1; + err = device_create_bin_file(dev, &nvram_attr); + if (err) + goto error2; + if (priv->irq <= 0) return err; err = device_create_file(dev, &dev_attr_alarm_mode); if (err) - goto error2; + goto error3; err = device_create_file(dev, &dev_attr_alarm_status); if (!err) @@ -287,6 +353,9 @@ static int ds1343_sysfs_register(struct device *dev) device_remove_file(dev, &dev_attr_alarm_mode); +error3: + device_remove_bin_file(dev, &nvram_attr); + error2: device_remove_file(dev, &dev_attr_trickle_charger); @@ -302,6 +371,7 @@ static void ds1343_sysfs_unregister(struct device *dev) device_remove_file(dev, &dev_attr_glitch_filter); device_remove_file(dev, &dev_attr_trickle_charger); + device_remove_bin_file(dev, &nvram_attr); if (priv->irq <= 0) return; @@ -684,6 +754,7 @@ static struct spi_driver ds1343_driver = { module_spi_driver(ds1343_driver); MODULE_DESCRIPTION("DS1343 RTC SPI Driver"); -MODULE_AUTHOR("Raghavendra Chandra Ganiga "); +MODULE_AUTHOR("Raghavendra Chandra Ganiga ," + "Ankur Srivastava "); MODULE_LICENSE("GPL v2"); MODULE_VERSION(DS1343_DRV_VERSION); -- cgit v1.2.2 From ad0200f72d9875caa2023c59240ee677df66918e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 8 Aug 2014 14:20:03 -0700 Subject: drivers/rtc/Kconfig: move DS2404 entry where it belongs Move the DS2404 entry together with the other Dallas entries, and add Maxim so that it looks nicer (and more correct, too.) Signed-off-by: Jean Delvare Cc: Sven Schnelle Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 0754f5c7cb3b..2d99bf60db2f 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -760,6 +760,15 @@ config RTC_DRV_DS1742 This driver can also be built as a module. If so, the module will be called rtc-ds1742. +config RTC_DRV_DS2404 + tristate "Maxim/Dallas DS2404" + help + If you say yes here you get support for the + Dallas DS2404 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ds2404. + config RTC_DRV_DA9052 tristate "Dialog DA9052/DA9053 RTC" depends on PMIC_DA9052 @@ -873,15 +882,6 @@ config RTC_DRV_V3020 This driver can also be built as a module. If so, the module will be called rtc-v3020. -config RTC_DRV_DS2404 - tristate "Dallas DS2404" - help - If you say yes here you get support for the - Dallas DS2404 RTC chip. - - This driver can also be built as a module. If so, the module - will be called rtc-ds2404. - config RTC_DRV_WM831X tristate "Wolfson Microelectronics WM831x RTC" depends on MFD_WM831X -- cgit v1.2.2 From 441fb7684782be3553c67dc04defcf304b999bba Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 8 Aug 2014 14:20:05 -0700 Subject: drivers/rtc/Kconfig: add hardware dependency to rtc-moxart The rtc-moxart driver is only useful on MOXA ART systems so it should depend on ARCH_MOXART as all other MOXA ART drivers already do. Add COMPILE_TEST as an alternative to allow for build testing on other systems. Signed-off-by: Jean Delvare Cc: Alessandro Zummo Cc: Jonas Jensen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 2d99bf60db2f..a672dd16da62 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1349,6 +1349,7 @@ config RTC_DRV_SIRFSOC config RTC_DRV_MOXART tristate "MOXA ART RTC" + depends on ARCH_MOXART || COMPILE_TEST help If you say yes here you get support for the MOXA ART RTC module. -- cgit v1.2.2 From 3710f597aca5af4f605cda9793a8b2f0a42bc29a Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Fri, 8 Aug 2014 14:20:07 -0700 Subject: drivers/rtc/rtc-ds1742.c: revert "drivers/rtc/rtc-ds1742.c: remove redundant of_match_ptr() helper" Commit 5516f0971793 ("drivers/rtc/rtc-ds1742.c: remove redundant of_match_ptr() helper") has description as: "'ds1742_rtc_of_match' is always compiled in. Hence the helper macro is not needed". It is not true, of_match_ptr() macro makes of_device_id parameter unused and this constant is declared with __maybe_unused attribute, so normally this variable should be discarded by linker. This patch revers this commit, since there are no reason to compile of_device_id constant for non-DT systems. Signed-off-by: Alexander Shiyan Cc: Alessandro Zummo Cc: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1742.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index c6b2191a4128..9822715db8ba 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -231,7 +231,7 @@ static struct platform_driver ds1742_rtc_driver = { .driver = { .name = "rtc-ds1742", .owner = THIS_MODULE, - .of_match_table = ds1742_rtc_of_match, + .of_match_table = of_match_ptr(ds1742_rtc_of_match), }, }; -- cgit v1.2.2 From 6e85bab6bc1019f9b87c53b32da3ad7791e7ddf9 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 8 Aug 2014 14:20:09 -0700 Subject: drivers/rtc/rtc-efi.c: check for invalid data coming back from UEFI In particular seeing zero in eft->month is problematic, as it results in -1 (converted to unsigned int, i.e. yielding 0xffffffff) getting passed to rtc_year_days(), where the value gets used as an array index (normally resulting in a crash). This was observed with the driver enabled on x86 on some Fujitsu system (with possibly not up to date firmware, but anyway). Perhaps efi_read_alarm() should not fail if neither enabled nor pending are set, but the returned time is invalid? Signed-off-by: Jan Beulich Reported-by: Raymund Will Cc: Alessandro Zummo Cc: Jingoo Han Acked-by: Lee, Chun-Yi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-efi.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index c4c38431012e..8225b89de810 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -48,8 +49,8 @@ compute_wday(efi_time_t *eft) int y; int ndays = 0; - if (eft->year < 1998) { - pr_err("EFI year < 1998, invalid date\n"); + if (eft->year < EFI_RTC_EPOCH) { + pr_err("EFI year < " __stringify(EFI_RTC_EPOCH) ", invalid date\n"); return -1; } @@ -78,19 +79,36 @@ convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft) eft->timezone = EFI_UNSPECIFIED_TIMEZONE; } -static void +static bool convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) { memset(wtime, 0, sizeof(*wtime)); + + if (eft->second >= 60) + return false; wtime->tm_sec = eft->second; + + if (eft->minute >= 60) + return false; wtime->tm_min = eft->minute; + + if (eft->hour >= 24) + return false; wtime->tm_hour = eft->hour; + + if (!eft->day || eft->day > 31) + return false; wtime->tm_mday = eft->day; + + if (!eft->month || eft->month > 12) + return false; wtime->tm_mon = eft->month - 1; wtime->tm_year = eft->year - 1900; /* day of the week [0-6], Sunday=0 */ wtime->tm_wday = compute_wday(eft); + if (wtime->tm_wday < 0) + return false; /* day in the year [1-365]*/ wtime->tm_yday = compute_yday(eft); @@ -106,6 +124,8 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) default: wtime->tm_isdst = -1; } + + return true; } static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) @@ -122,7 +142,8 @@ static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) if (status != EFI_SUCCESS) return -EINVAL; - convert_from_efi_time(&eft, &wkalrm->time); + if (!convert_from_efi_time(&eft, &wkalrm->time)) + return -EIO; return rtc_valid_tm(&wkalrm->time); } @@ -163,7 +184,8 @@ static int efi_read_time(struct device *dev, struct rtc_time *tm) return -EINVAL; } - convert_from_efi_time(&eft, tm); + if (!convert_from_efi_time(&eft, tm)) + return -EIO; return rtc_valid_tm(tm); } -- cgit v1.2.2 From ca6dc2dab97133b874c2f6a76b6125497b67b429 Mon Sep 17 00:00:00 2001 From: Hyogi Gim Date: Fri, 8 Aug 2014 14:20:11 -0700 Subject: drivers/rtc/interface.c: check the error after __rtc_read_time() In __rtc_set_alarm(), the error after __rtc_read_time() is not checked. If rtc device fail to read time, we cannot guarantee the following process. Add the verification code for returned __rtc_read_time() error. Signed-off-by: Hyogi Gim Acked-by: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/interface.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 5813fa52c3d4..5b2717f5dafa 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -348,6 +348,8 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) /* Make sure we're not setting alarms in the past */ err = __rtc_read_time(rtc, &tm); + if (err) + return err; rtc_tm_to_time(&tm, &now); if (scheduled <= now) return -ETIME; -- cgit v1.2.2 From da167ad7638759adb811afa3c80ff4cb67608242 Mon Sep 17 00:00:00 2001 From: Mark Salter Date: Fri, 8 Aug 2014 14:20:14 -0700 Subject: rtc: ia64: allow other architectures to use EFI RTC Currently, the rtc-efi driver is restricted to ia64 only. Newer architectures with EFI support may want to also use that driver. This patch moves the platform device setup from ia64 into drivers/rtc and allow any architecture with CONFIG_EFI=y to use the rtc-efi driver. Signed-off-by: Mark Salter Cc: Alessandro Zummo Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 2 +- drivers/rtc/Makefile | 4 ++++ drivers/rtc/rtc-efi-platform.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 drivers/rtc/rtc-efi-platform.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a672dd16da62..83743a1a6731 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -798,7 +798,7 @@ config RTC_DRV_DA9063 config RTC_DRV_EFI tristate "EFI RTC" - depends on IA64 + depends on EFI help If you say yes here you will get support for the EFI Real Time Clock. diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 70347d041d10..f1dfc3648ee5 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -10,6 +10,10 @@ obj-$(CONFIG_RTC_SYSTOHC) += systohc.o obj-$(CONFIG_RTC_CLASS) += rtc-core.o rtc-core-y := class.o interface.o +ifdef CONFIG_RTC_DRV_EFI +rtc-core-y += rtc-efi-platform.o +endif + rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o diff --git a/drivers/rtc/rtc-efi-platform.c b/drivers/rtc/rtc-efi-platform.c new file mode 100644 index 000000000000..b40fbe332af4 --- /dev/null +++ b/drivers/rtc/rtc-efi-platform.c @@ -0,0 +1,31 @@ +/* + * Moved from arch/ia64/kernel/time.c + * + * Copyright (C) 1998-2003 Hewlett-Packard Co + * Stephane Eranian + * David Mosberger + * Copyright (C) 1999 Don Dugger + * Copyright (C) 1999-2000 VA Linux Systems + * Copyright (C) 1999-2000 Walt Drummond + */ +#include +#include +#include +#include +#include + +static struct platform_device rtc_efi_dev = { + .name = "rtc-efi", + .id = -1, +}; + +static int __init rtc_init(void) +{ + if (efi_enabled(EFI_RUNTIME_SERVICES)) + if (platform_device_register(&rtc_efi_dev) < 0) + pr_err("unable to register rtc device...\n"); + + /* not necessarily an error */ + return 0; +} +module_init(rtc_init); -- cgit v1.2.2 From 2784366c6797e6a3078abca2f303ebf7a36290be Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 8 Aug 2014 14:20:16 -0700 Subject: drivers/rtc/rtc-pcf8563.c: introduce read|write_block_data This functions allow to factorize I2C I/O operations. Signed-off-by: Vincent Donnefort Cc: Simon Guinot Cc: Jason Cooper Cc: Andrew Lunn Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8563.c | 74 ++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 26 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 63b558c48196..7cbbf50cd895 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -69,35 +69,66 @@ struct pcf8563 { int voltage_low; /* incicates if a low_voltage was detected */ }; -/* - * In the routines that deal directly with the pcf8563 hardware, we use - * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. - */ -static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm) +static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg, + unsigned char length, unsigned char *buf) { - struct pcf8563 *pcf8563 = i2c_get_clientdata(client); - unsigned char buf[13] = { PCF8563_REG_ST1 }; - struct i2c_msg msgs[] = { {/* setup read ptr */ .addr = client->addr, .len = 1, - .buf = buf + .buf = ®, }, - {/* read status + date */ + { .addr = client->addr, .flags = I2C_M_RD, - .len = 13, + .len = length, .buf = buf }, }; - /* read registers */ if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } + return 0; +} + +static int pcf8563_write_block_data(struct i2c_client *client, + unsigned char reg, unsigned char length, + unsigned char *buf) +{ + int i, err; + + for (i = 0; i < length; i++) { + unsigned char data[2] = { reg + i, buf[i] }; + + err = i2c_master_send(client, data, sizeof(data)); + if (err != sizeof(data)) { + dev_err(&client->dev, + "%s: err=%d addr=%02x, data=%02x\n", + __func__, err, data[0], data[1]); + return -EIO; + } + } + + return 0; +} + +/* + * In the routines that deal directly with the pcf8563 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + struct pcf8563 *pcf8563 = i2c_get_clientdata(client); + unsigned char buf[9]; + int err; + + err = pcf8563_read_block_data(client, PCF8563_REG_ST1, 9, buf); + if (err) + return err; + if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) { pcf8563->voltage_low = 1; dev_info(&client->dev, @@ -144,7 +175,7 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm) static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm) { struct pcf8563 *pcf8563 = i2c_get_clientdata(client); - int i, err; + int err; unsigned char buf[9]; dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " @@ -170,19 +201,10 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm) buf[PCF8563_REG_DW] = tm->tm_wday & 0x07; - /* write register's data */ - for (i = 0; i < 7; i++) { - unsigned char data[2] = { PCF8563_REG_SC + i, - buf[PCF8563_REG_SC + i] }; - - err = i2c_master_send(client, data, sizeof(data)); - if (err != sizeof(data)) { - dev_err(&client->dev, - "%s: err=%d addr=%02x, data=%02x\n", - __func__, err, data[0], data[1]); - return -EIO; - } - } + err = pcf8563_write_block_data(client, PCF8563_REG_SC, + 9 - PCF8563_REG_SC, buf + PCF8563_REG_SC); + if (err) + return err; return 0; } -- cgit v1.2.2 From ede3e9d47cca565d96613bea9213eccf91c7a2a7 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 8 Aug 2014 14:20:18 -0700 Subject: drivers/rtc/rtc-pcf8563.c: add alarm support This patch adds alarm support for the NXP PCF8563 chip. Signed-off-by: Vincent Donnefort Cc: Simon Guinot Cc: Jason Cooper Cc: Andrew Lunn Cc: Alessandro Zummo Cc: Dan Carpenter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8563.c | 157 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 153 insertions(+), 4 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 7cbbf50cd895..5a197d9dc7e7 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -26,6 +26,8 @@ #define PCF8563_REG_ST1 0x00 /* status */ #define PCF8563_REG_ST2 0x01 +#define PCF8563_BIT_AIE (1 << 1) +#define PCF8563_BIT_AF (1 << 3) #define PCF8563_REG_SC 0x02 /* datetime */ #define PCF8563_REG_MN 0x03 @@ -36,9 +38,6 @@ #define PCF8563_REG_YR 0x08 #define PCF8563_REG_AMN 0x09 /* alarm */ -#define PCF8563_REG_AHR 0x0A -#define PCF8563_REG_ADM 0x0B -#define PCF8563_REG_ADW 0x0C #define PCF8563_REG_CLKO 0x0D /* clock out */ #define PCF8563_REG_TMRC 0x0E /* timer control */ @@ -67,6 +66,8 @@ struct pcf8563 { */ int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */ int voltage_low; /* incicates if a low_voltage was detected */ + + struct i2c_client *client; }; static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg, @@ -115,6 +116,69 @@ static int pcf8563_write_block_data(struct i2c_client *client, return 0; } +static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on) +{ + unsigned char buf[2]; + int err; + + err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, buf + 1); + if (err < 0) + return err; + + if (on) + buf[1] |= PCF8563_BIT_AIE; + else + buf[1] &= ~PCF8563_BIT_AIE; + + buf[1] &= ~PCF8563_BIT_AF; + buf[0] = PCF8563_REG_ST2; + + err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, buf + 1); + if (err < 0) { + dev_err(&client->dev, "%s: write error\n", __func__); + return -EIO; + } + + return 0; +} + +static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en, + unsigned char *pen) +{ + unsigned char buf; + int err; + + err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf); + if (err) + return err; + + if (en) + *en = !!(buf & PCF8563_BIT_AIE); + if (pen) + *pen = !!(buf & PCF8563_BIT_AF); + + return 0; +} + +static irqreturn_t pcf8563_irq(int irq, void *dev_id) +{ + struct pcf8563 *pcf8563 = i2c_get_clientdata(dev_id); + int err; + char pending; + + err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending); + if (err < 0) + return err; + + if (pending) { + rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF); + pcf8563_set_alarm_mode(pcf8563->client, 1); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + /* * In the routines that deal directly with the pcf8563 hardware, we use * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. @@ -257,16 +321,83 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) return pcf8563_set_datetime(to_i2c_client(dev), tm); } +static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[4]; + int err; + + err = pcf8563_read_block_data(client, PCF8563_REG_AMN, 4, buf); + if (err) + return err; + + dev_dbg(&client->dev, + "%s: raw data is min=%02x, hr=%02x, mday=%02x, wday=%02x\n", + __func__, buf[0], buf[1], buf[2], buf[3]); + + tm->time.tm_min = bcd2bin(buf[0] & 0x7F); + tm->time.tm_hour = bcd2bin(buf[1] & 0x7F); + tm->time.tm_mday = bcd2bin(buf[2] & 0x1F); + tm->time.tm_wday = bcd2bin(buf[3] & 0x7); + tm->time.tm_mon = -1; + tm->time.tm_year = -1; + tm->time.tm_yday = -1; + tm->time.tm_isdst = -1; + + err = pcf8563_get_alarm_mode(client, &tm->enabled, &tm->pending); + if (err < 0) + return err; + + dev_dbg(&client->dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d," + " enabled=%d, pending=%d\n", __func__, tm->time.tm_min, + tm->time.tm_hour, tm->time.tm_mday, tm->time.tm_wday, + tm->enabled, tm->pending); + + return 0; +} + +static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[4]; + int err; + + dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d " + "enabled=%d pending=%d\n", __func__, + tm->time.tm_min, tm->time.tm_hour, tm->time.tm_wday, + tm->time.tm_mday, tm->enabled, tm->pending); + + buf[0] = bin2bcd(tm->time.tm_min); + buf[1] = bin2bcd(tm->time.tm_hour); + buf[2] = bin2bcd(tm->time.tm_mday); + buf[3] = tm->time.tm_wday & 0x07; + + err = pcf8563_write_block_data(client, PCF8563_REG_AMN, 4, buf); + if (err) + return err; + + return pcf8563_set_alarm_mode(client, 1); +} + +static int pcf8563_irq_enable(struct device *dev, unsigned int enabled) +{ + return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled); +} + static const struct rtc_class_ops pcf8563_rtc_ops = { .ioctl = pcf8563_rtc_ioctl, .read_time = pcf8563_rtc_read_time, .set_time = pcf8563_rtc_set_time, + .read_alarm = pcf8563_rtc_read_alarm, + .set_alarm = pcf8563_rtc_set_alarm, + .alarm_irq_enable = pcf8563_irq_enable, }; static int pcf8563_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pcf8563 *pcf8563; + int err; dev_dbg(&client->dev, "%s\n", __func__); @@ -281,12 +412,30 @@ static int pcf8563_probe(struct i2c_client *client, dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); i2c_set_clientdata(client, pcf8563); + pcf8563->client = client; + device_set_wakeup_capable(&client->dev, 1); pcf8563->rtc = devm_rtc_device_register(&client->dev, pcf8563_driver.driver.name, &pcf8563_rtc_ops, THIS_MODULE); - return PTR_ERR_OR_ZERO(pcf8563->rtc); + if (IS_ERR(pcf8563->rtc)) + return PTR_ERR(pcf8563->rtc); + + if (client->irq > 0) { + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pcf8563_irq, + IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING, + pcf8563->rtc->name, client); + if (err) { + dev_err(&client->dev, "unable to request IRQ %d\n", + client->irq); + return err; + } + + } + + return 0; } static const struct i2c_device_id pcf8563_id[] = { -- cgit v1.2.2 From db04d6284e2a626791e959dc71e922e8570440b6 Mon Sep 17 00:00:00 2001 From: Stuart Longland Date: Fri, 8 Aug 2014 14:20:20 -0700 Subject: drivers/rtc/rtc-isl12022.c: device tree support Add support for configuring the ISL12022 real-time clock via the Device tree framework. This is based on what I've seen in the related ISL12057 driver, it has been tested and works on a Technologic Systems TS-7670 device which uses a ISL12020 RTC device, my device tree looks like this: apbx@80040000 { i2c0: i2c@80058000 { pinctrl-names = "default"; pinctrl-0 = <&i2c0_pins_a>; clock-frequency = <400000>; status = "okay"; isl12022@0x6f { compatible = "isl,isl12022"; reg = <0x6f>; }; }; ... etc }; Signed-off-by: Stuart Longland Cc: Roman Fietze Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl12022.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index 03b891129428..aa55f081c505 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #define DRV_VERSION "0.1" @@ -271,6 +273,13 @@ static int isl12022_probe(struct i2c_client *client, return PTR_ERR_OR_ZERO(isl12022->rtc); } +#ifdef CONFIG_OF +static struct of_device_id isl12022_dt_match[] = { + { .compatible = "isl,isl12022" }, + { }, +}; +#endif + static const struct i2c_device_id isl12022_id[] = { { "isl12022", 0 }, { } @@ -280,6 +289,9 @@ MODULE_DEVICE_TABLE(i2c, isl12022_id); static struct i2c_driver isl12022_driver = { .driver = { .name = "rtc-isl12022", +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(isl12022_dt_match), +#endif }, .probe = isl12022_probe, .id_table = isl12022_id, -- cgit v1.2.2 From 796b7abb33cd78412897a9e927eb5a8f5a9c4fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Andersen?= Date: Fri, 8 Aug 2014 14:20:22 -0700 Subject: rtc: add pcf85063 support Add support for the pcf85063 rtc chip. [akpm@linux-foundation.org: fix comment typo, tweak conding style] Signed-off-by: Soeren Andersen Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 8 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-pcf85063.c | 204 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 drivers/rtc/rtc-pcf85063.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 83743a1a6731..a168e96142b9 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -373,6 +373,14 @@ config RTC_DRV_PCF8563 This driver can also be built as a module. If so, the module will be called rtc-pcf8563. +config RTC_DRV_PCF85063 + tristate "nxp PCF85063" + help + If you say yes here you get support for the PCF85063 RTC chip + + This driver can also be built as a module. If so, the module + will be called rtc-pcf85063. + config RTC_DRV_PCF8583 tristate "Philips PCF8583" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index f1dfc3648ee5..56f061c7c815 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o +obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c new file mode 100644 index 000000000000..6a12bf62c504 --- /dev/null +++ b/drivers/rtc/rtc-pcf85063.c @@ -0,0 +1,204 @@ +/* + * An I2C driver for the PCF85063 RTC + * Copyright 2014 Rose Technology + * + * Author: Søren Andersen + * Maintainers: http://www.nslu2-linux.org/ + * + * based on the other drivers in this same directory. + * + * 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 +#include +#include +#include + +#define DRV_VERSION "0.0.1" + +#define PCF85063_REG_CTRL1 0x00 /* status */ +#define PCF85063_REG_CTRL2 0x01 + +#define PCF85063_REG_SC 0x04 /* datetime */ +#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 + +#define PCF85063_MO_C 0x80 /* century */ + +static struct i2c_driver pcf85063_driver; + +struct pcf85063 { + struct rtc_device *rtc; + int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */ + int voltage_low; /* indicates if a low_voltage was detected */ +}; + +/* + * In the routines that deal directly with the pcf85063 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + struct pcf85063 *pcf85063 = i2c_get_clientdata(client); + unsigned char buf[13] = { PCF85063_REG_CTRL1 }; + struct i2c_msg msgs[] = { + {/* setup read ptr */ + .addr = client->addr, + .len = 1, + .buf = buf + }, + {/* read status + date */ + .addr = client->addr, + .flags = I2C_M_RD, + .len = 13, + .buf = buf + }, + }; + + /* read registers */ + if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + tm->tm_sec = bcd2bin(buf[PCF85063_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(buf[PCF85063_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(buf[PCF85063_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(buf[PCF85063_REG_DM] & 0x3F); + tm->tm_wday = buf[PCF85063_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(buf[PCF85063_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(buf[PCF85063_REG_YR]); + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + /* detect the polarity heuristically. see note above. */ + pcf85063->c_polarity = (buf[PCF85063_REG_MO] & PCF85063_MO_C) ? + (tm->tm_year >= 100) : (tm->tm_year < 100); + + /* the clock can give out invalid datetime, but we cannot return + * -EINVAL otherwise hwclock will refuse to set the time on bootup. + */ + if (rtc_valid_tm(tm) < 0) + dev_err(&client->dev, "retrieved date/time is not valid.\n"); + + return 0; +} + +static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + int i = 0, err = 0; + unsigned char buf[11]; + + /* Control & status */ + buf[PCF85063_REG_CTRL1] = 0; + buf[PCF85063_REG_CTRL2] = 5; + + /* hours, minutes and seconds */ + buf[PCF85063_REG_SC] = bin2bcd(tm->tm_sec) & 0x7F; + + buf[PCF85063_REG_MN] = bin2bcd(tm->tm_min); + buf[PCF85063_REG_HR] = bin2bcd(tm->tm_hour); + + /* Day of month, 1 - 31 */ + buf[PCF85063_REG_DM] = bin2bcd(tm->tm_mday); + + /* Day, 0 - 6 */ + buf[PCF85063_REG_DW] = tm->tm_wday & 0x07; + + /* month, 1 - 12 */ + buf[PCF85063_REG_MO] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[PCF85063_REG_YR] = bin2bcd(tm->tm_year % 100); + + /* write register's data */ + for (i = 0; i < sizeof(buf); i++) { + unsigned char data[2] = { i, buf[i] }; + + err = i2c_master_send(client, data, sizeof(data)); + if (err != sizeof(data)) { + dev_err(&client->dev, "%s: err=%d addr=%02x, data=%02x\n", + __func__, err, data[0], data[1]); + return -EIO; + } + } + + return 0; +} + +static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return pcf85063_get_datetime(to_i2c_client(dev), tm); +} + +static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return pcf85063_set_datetime(to_i2c_client(dev), tm); +} + +static const struct rtc_class_ops pcf85063_rtc_ops = { + .read_time = pcf85063_rtc_read_time, + .set_time = pcf85063_rtc_set_time +}; + +static int pcf85063_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pcf85063 *pcf85063; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063), + GFP_KERNEL); + if (!pcf85063) + return -ENOMEM; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + + i2c_set_clientdata(client, pcf85063); + + pcf85063->rtc = devm_rtc_device_register(&client->dev, + pcf85063_driver.driver.name, + &pcf85063_rtc_ops, THIS_MODULE); + + return PTR_ERR_OR_ZERO(pcf85063->rtc); +} + +static const struct i2c_device_id pcf85063_id[] = { + { "pcf85063", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf85063_id); + +#ifdef CONFIG_OF +static const struct of_device_id pcf85063_of_match[] = { + { .compatible = "nxp,pcf85063" }, + {} +}; +MODULE_DEVICE_TABLE(of, pcf85063_of_match); +#endif + +static struct i2c_driver pcf85063_driver = { + .driver = { + .name = "rtc-pcf85063", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pcf85063_of_match), + }, + .probe = pcf85063_probe, + .id_table = pcf85063_id, +}; + +module_i2c_driver(pcf85063_driver); + +MODULE_AUTHOR("Søren Andersen "); +MODULE_DESCRIPTION("PCF85063 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); -- cgit v1.2.2 From e1d60093ca7341e884578c41a29da7cd1714c80e Mon Sep 17 00:00:00 2001 From: Hyogi Gim Date: Fri, 8 Aug 2014 14:20:24 -0700 Subject: driver/rtc/class.c: check the error after rtc_read_time() In rtc_suspend() and rtc_resume(), the error after rtc_read_time() is not checked. If rtc device fail to read time, we cannot guarantee the following process. Add the verification code for returned rtc_read_time() error. Signed-off-by: Hyogi Gim Cc: Alessandro Zummo Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/class.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 589351ef75d0..38e26be705be 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -53,6 +53,7 @@ static int rtc_suspend(struct device *dev) struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; struct timespec delta, delta_delta; + int err; if (has_persistent_clock()) return 0; @@ -61,7 +62,12 @@ static int rtc_suspend(struct device *dev) return 0; /* snapshot the current RTC and system time at suspend*/ - rtc_read_time(rtc, &tm); + err = rtc_read_time(rtc, &tm); + if (err < 0) { + pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev)); + return 0; + } + getnstimeofday(&old_system); rtc_tm_to_time(&tm, &old_rtc.tv_sec); @@ -94,6 +100,7 @@ static int rtc_resume(struct device *dev) struct rtc_time tm; struct timespec new_system, new_rtc; struct timespec sleep_time; + int err; if (has_persistent_clock()) return 0; @@ -104,7 +111,12 @@ static int rtc_resume(struct device *dev) /* snapshot the current rtc and system time at resume */ getnstimeofday(&new_system); - rtc_read_time(rtc, &tm); + err = rtc_read_time(rtc, &tm); + if (err < 0) { + pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev)); + return 0; + } + if (rtc_valid_tm(&tm) != 0) { pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev)); return 0; -- cgit v1.2.2 From 9f7d7a1d0f36bed7f533807146483e8fdfe12a89 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 8 Aug 2014 14:20:26 -0700 Subject: drivers/rtc/rtc-tps65910.c: fix potential NULL-pointer dereference The interrupt handler gets the driver data associated with the RTC device and doesn't check it for validity. This can cause a NULL pointer being dereferenced when and interrupt fires before the driver data was properly set up. Fix this by setting the driver data earlier (before the interrupt is requested). Signed-off-by: Thierry Reding Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-tps65910.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c index 7af00208d637..2583349fbde5 100644 --- a/drivers/rtc/rtc-tps65910.c +++ b/drivers/rtc/rtc-tps65910.c @@ -258,6 +258,8 @@ static int tps65910_rtc_probe(struct platform_device *pdev) if (ret < 0) return ret; + platform_set_drvdata(pdev, tps_rtc); + irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n", @@ -283,8 +285,6 @@ static int tps65910_rtc_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, tps_rtc); - return 0; } -- cgit v1.2.2 From b7d5b9a9686674eedffe5b8745c85265f3fe3367 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 29 Aug 2014 15:18:53 -0700 Subject: drivers/rtc/rtc-s5m.c: re-add support for devices without irq specified The rtc-s5m driver used to support devices without irq specified in the past. Re-add this support. The patch fixes boot for Insignal's Exynos4412 based Origen board. Error messages before the patch: ... Unable to handle kernel NULL pointer dereference at virtual address 00000094 pgd = c0004000 [00000094] *pgd=00000000 Internal error: Oops: 5 [#1] PREEMPT SMP ARM Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 3.16.0-next-20140804-00008-ga59480f-dirty #701 task: ea80f000 ti: ea882000 task.ti: ea882000 PC is at regmap_irq_get_virq+0x0/0x28 LR is at s5m_rtc_probe+0xdc/0x310 pc : [] lr : [] psr: 80000153 sp : ea883e48 ip : 00000000 fp : 00000000 r10: 0000000c r9 : c05de7ac r8 : eaabc600 r7 : eaa6b4d0 r6 : c0439e8c r5 : eaabc610 r4 : eab30e50 r3 : 00000000 r2 : 00000000 r1 : 0000000c r0 : 00000000 Flags: Nzcv IRQs on FIQs off Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 4000404a DAC: 00000015 Process swapper/0 (pid: 1, stack limit = 0xea882240) Backtrace: regmap_irq_get_virq s5m_rtc_probe platform_drv_probe driver_probe_device __driver_attach bus_for_each_dev bus_add_driver driver_register do_one_initcall kernel_init_freeable kernel_init ---[ end trace a954d7f019122700 ]--- Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b ... Signed-off-by: Bartlomiej Zolnierkiewicz Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Acked-by: Kyungmin Park Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s5m.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c index 8f06250a0389..8754c33361e8 100644 --- a/drivers/rtc/rtc-s5m.c +++ b/drivers/rtc/rtc-s5m.c @@ -717,12 +717,14 @@ static int s5m_rtc_probe(struct platform_device *pdev) info->device_type = s5m87xx->device_type; info->wtsr_smpl = s5m87xx->wtsr_smpl; - info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq); - if (info->irq <= 0) { - ret = -EINVAL; - dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n", + if (s5m87xx->irq_data) { + info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq); + if (info->irq <= 0) { + ret = -EINVAL; + dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n", alarm_irq); - goto err; + goto err; + } } platform_set_drvdata(pdev, info); @@ -744,6 +746,11 @@ static int s5m_rtc_probe(struct platform_device *pdev) goto err; } + if (!info->irq) { + dev_info(&pdev->dev, "Alarm IRQ not available\n"); + return 0; + } + ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, s5m_rtc_alarm_irq, 0, "rtc-alarm0", info); @@ -802,7 +809,7 @@ static int s5m_rtc_resume(struct device *dev) struct s5m_rtc_info *info = dev_get_drvdata(dev); int ret = 0; - if (device_may_wakeup(dev)) + if (info->irq && device_may_wakeup(dev)) ret = disable_irq_wake(info->irq); return ret; @@ -813,7 +820,7 @@ static int s5m_rtc_suspend(struct device *dev) struct s5m_rtc_info *info = dev_get_drvdata(dev); int ret = 0; - if (device_may_wakeup(dev)) + if (info->irq && device_may_wakeup(dev)) ret = enable_irq_wake(info->irq); return ret; -- cgit v1.2.2 From ace2c0205b1bae2c3d1ea0932a1e45494938005a Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 30 Aug 2014 18:46:10 +0200 Subject: rtc: Remove ARCH_KIRKWOOD dependency mach-kirkwood has been removed, now that kirkwood lives in mach-mvebu. ARCH_MVEBU is sufficient. Signed-off-by: Andrew Lunn Cc: Alessandro Zummo Cc: rtc-linux@googlegroups.com Link: https://lkml.kernel.org/r/1409417172-6846-6-git-send-email-andrew@lunn.ch Signed-off-by: Jason Cooper --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a168e96142b9..fae9464eed9c 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1198,7 +1198,7 @@ config RTC_DRV_TX4939 config RTC_DRV_MV tristate "Marvell SoC RTC" - depends on ARCH_KIRKWOOD || ARCH_DOVE || ARCH_MVEBU + depends on ARCH_DOVE || ARCH_MVEBU help If you say yes here you will get support for the in-chip RTC that can be found in some of Marvell's SoC devices, such as -- cgit v1.2.2 From 9765d2d9430971ca89b09af076e7a8ff250df473 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 26 Aug 2014 11:54:55 +0800 Subject: rtc: sun6i: Add sun6i RTC driver This patch introduces the driver for the RTC in the Allwinner A31 and A23 SoCs. Unlike the RTC found in A10/A20 SoCs, which was part of the timer, the RTC in A31/A23 are a separate hardware block, which also contain a few controls for the RTC block hardware (a regulator and RTC block GPIO pin latches), while also having separate interrupts for the alarms. The hardware is different enough to make a different driver for it. Signed-off-by: Chen-Yu Tsai Reviewed-by: Varka Bhadram Signed-off-by: Maxime Ripard --- drivers/rtc/Kconfig | 7 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-sun6i.c | 447 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 455 insertions(+) create mode 100644 drivers/rtc/rtc-sun6i.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a168e96142b9..f2300b4e9b17 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1175,6 +1175,13 @@ config RTC_DRV_SUN4V If you say Y here you will get support for the Hypervisor based RTC on SUN4V systems. +config RTC_DRV_SUN6I + tristate "Allwinner A31 RTC" + depends on MACH_SUN6I || MACH_SUN8I + help + If you say Y here you will get support for the RTC found on + Allwinner A31. + config RTC_DRV_SUNXI tristate "Allwinner sun4i/sun7i RTC" depends on ARCH_SUNXI diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 56f061c7c815..9055b7dd3dc5 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -128,6 +128,7 @@ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o +obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c new file mode 100644 index 000000000000..c169a2cd4727 --- /dev/null +++ b/drivers/rtc/rtc-sun6i.c @@ -0,0 +1,447 @@ +/* + * An RTC driver for Allwinner A31/A23 + * + * Copyright (c) 2014, Chen-Yu Tsai + * + * based on rtc-sunxi.c + * + * An RTC driver for Allwinner A10/A20 + * + * Copyright (c) 2013, Carlo Caione + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Control register */ +#define SUN6I_LOSC_CTRL 0x0000 +#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) +#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) +#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) +#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) + +/* RTC */ +#define SUN6I_RTC_YMD 0x0010 +#define SUN6I_RTC_HMS 0x0014 + +/* Alarm 0 (counter) */ +#define SUN6I_ALRM_COUNTER 0x0020 +#define SUN6I_ALRM_CUR_VAL 0x0024 +#define SUN6I_ALRM_EN 0x0028 +#define SUN6I_ALRM_EN_CNT_EN BIT(0) +#define SUN6I_ALRM_IRQ_EN 0x002c +#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0) +#define SUN6I_ALRM_IRQ_STA 0x0030 +#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0) + +/* Alarm 1 (wall clock) */ +#define SUN6I_ALRM1_EN 0x0044 +#define SUN6I_ALRM1_IRQ_EN 0x0048 +#define SUN6I_ALRM1_IRQ_STA 0x004c +#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND BIT(0) + +/* Alarm config */ +#define SUN6I_ALARM_CONFIG 0x0050 +#define SUN6I_ALARM_CONFIG_WAKEUP BIT(0) + +/* + * Get date values + */ +#define SUN6I_DATE_GET_DAY_VALUE(x) ((x) & 0x0000001f) +#define SUN6I_DATE_GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8) +#define SUN6I_DATE_GET_YEAR_VALUE(x) (((x) & 0x003f0000) >> 16) +#define SUN6I_LEAP_GET_VALUE(x) (((x) & 0x00400000) >> 22) + +/* + * Get time values + */ +#define SUN6I_TIME_GET_SEC_VALUE(x) ((x) & 0x0000003f) +#define SUN6I_TIME_GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8) +#define SUN6I_TIME_GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16) + +/* + * Set date values + */ +#define SUN6I_DATE_SET_DAY_VALUE(x) ((x) & 0x0000001f) +#define SUN6I_DATE_SET_MON_VALUE(x) ((x) << 8 & 0x00000f00) +#define SUN6I_DATE_SET_YEAR_VALUE(x) ((x) << 16 & 0x003f0000) +#define SUN6I_LEAP_SET_VALUE(x) ((x) << 22 & 0x00400000) + +/* + * Set time values + */ +#define SUN6I_TIME_SET_SEC_VALUE(x) ((x) & 0x0000003f) +#define SUN6I_TIME_SET_MIN_VALUE(x) ((x) << 8 & 0x00003f00) +#define SUN6I_TIME_SET_HOUR_VALUE(x) ((x) << 16 & 0x001f0000) + +/* + * The year parameter passed to the driver is usually an offset relative to + * the year 1900. This macro is used to convert this offset to another one + * relative to the minimum year allowed by the hardware. + * + * The year range is 1970 - 2033. This range is selected to match Allwinner's + * driver, even though it is somewhat limited. + */ +#define SUN6I_YEAR_MIN 1970 +#define SUN6I_YEAR_MAX 2033 +#define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900) + +struct sun6i_rtc_dev { + struct rtc_device *rtc; + struct device *dev; + void __iomem *base; + int irq; + unsigned long alarm; +}; + +static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) +{ + struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; + u32 val; + + val = readl(chip->base + SUN6I_ALRM_IRQ_STA); + + if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { + val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND; + writel(val, chip->base + SUN6I_ALRM_IRQ_STA); + + rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) +{ + u32 alrm_val = 0; + u32 alrm_irq_val = 0; + u32 alrm_wake_val = 0; + + if (to) { + alrm_val = SUN6I_ALRM_EN_CNT_EN; + alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN; + alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP; + } else { + writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, + chip->base + SUN6I_ALRM_IRQ_STA); + } + + writel(alrm_val, chip->base + SUN6I_ALRM_EN); + writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); + writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); +} + +static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + u32 date, time; + + /* + * read again in case it changes + */ + do { + date = readl(chip->base + SUN6I_RTC_YMD); + time = readl(chip->base + SUN6I_RTC_HMS); + } while ((date != readl(chip->base + SUN6I_RTC_YMD)) || + (time != readl(chip->base + SUN6I_RTC_HMS))); + + rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time); + rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time); + rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time); + + rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date); + rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date); + rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date); + + rtc_tm->tm_mon -= 1; + + /* + * switch from (data_year->min)-relative offset to + * a (1900)-relative one + */ + rtc_tm->tm_year += SUN6I_YEAR_OFF; + + return rtc_valid_tm(rtc_tm); +} + +static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + u32 alrm_st; + u32 alrm_en; + + alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); + alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); + wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); + wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); + rtc_time_to_tm(chip->alarm, &wkalrm->time); + + return 0; +} + +static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + struct rtc_time tm_now; + unsigned long time_now = 0; + unsigned long time_set = 0; + unsigned long time_gap = 0; + int ret = 0; + + ret = sun6i_rtc_gettime(dev, &tm_now); + if (ret < 0) { + dev_err(dev, "Error in getting time\n"); + return -EINVAL; + } + + rtc_tm_to_time(alrm_tm, &time_set); + rtc_tm_to_time(&tm_now, &time_now); + if (time_set <= time_now) { + dev_err(dev, "Date to set in the past\n"); + return -EINVAL; + } + + time_gap = time_set - time_now; + + if (time_gap > U32_MAX) { + dev_err(dev, "Date too far in the future\n"); + return -EINVAL; + } + + sun6i_rtc_setaie(0, chip); + writel(0, chip->base + SUN6I_ALRM_COUNTER); + usleep_range(100, 300); + + writel(time_gap, chip->base + SUN6I_ALRM_COUNTER); + chip->alarm = time_set; + + sun6i_rtc_setaie(wkalrm->enabled, chip); + + return 0; +} + +static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset, + unsigned int mask, unsigned int ms_timeout) +{ + const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout); + u32 reg; + + do { + reg = readl(chip->base + offset); + reg &= mask; + + if (!reg) + return 0; + + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + u32 date = 0; + u32 time = 0; + int year; + + year = rtc_tm->tm_year + 1900; + if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) { + dev_err(dev, "rtc only supports year in range %d - %d\n", + SUN6I_YEAR_MIN, SUN6I_YEAR_MAX); + return -EINVAL; + } + + rtc_tm->tm_year -= SUN6I_YEAR_OFF; + rtc_tm->tm_mon += 1; + + date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | + SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | + SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year); + + if (is_leap_year(year)) + date |= SUN6I_LEAP_SET_VALUE(1); + + time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | + SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | + SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour); + + /* Check whether registers are writable */ + if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, + SUN6I_LOSC_CTRL_ACC_MASK, 50)) { + dev_err(dev, "rtc is still busy.\n"); + return -EBUSY; + } + + writel(time, chip->base + SUN6I_RTC_HMS); + + /* + * After writing the RTC HH-MM-SS register, the + * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not + * be cleared until the real writing operation is finished + */ + + if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, + SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) { + dev_err(dev, "Failed to set rtc time.\n"); + return -ETIMEDOUT; + } + + writel(date, chip->base + SUN6I_RTC_YMD); + + /* + * After writing the RTC YY-MM-DD register, the + * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not + * be cleared until the real writing operation is finished + */ + + if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, + SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) { + dev_err(dev, "Failed to set rtc time.\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + + if (!enabled) + sun6i_rtc_setaie(enabled, chip); + + return 0; +} + +static const struct rtc_class_ops sun6i_rtc_ops = { + .read_time = sun6i_rtc_gettime, + .set_time = sun6i_rtc_settime, + .read_alarm = sun6i_rtc_getalarm, + .set_alarm = sun6i_rtc_setalarm, + .alarm_irq_enable = sun6i_rtc_alarm_irq_enable +}; + +static int sun6i_rtc_probe(struct platform_device *pdev) +{ + struct sun6i_rtc_dev *chip; + struct resource *res; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + platform_set_drvdata(pdev, chip); + chip->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); + + chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return chip->irq; + } + + ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq, + 0, dev_name(&pdev->dev), chip); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + /* clear the alarm counter value */ + writel(0, chip->base + SUN6I_ALRM_COUNTER); + + /* disable counter alarm */ + writel(0, chip->base + SUN6I_ALRM_EN); + + /* disable counter alarm interrupt */ + writel(0, chip->base + SUN6I_ALRM_IRQ_EN); + + /* disable week alarm */ + writel(0, chip->base + SUN6I_ALRM1_EN); + + /* disable week alarm interrupt */ + writel(0, chip->base + SUN6I_ALRM1_IRQ_EN); + + /* clear counter alarm pending interrupts */ + writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, + chip->base + SUN6I_ALRM_IRQ_STA); + + /* clear week alarm pending interrupts */ + writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, + chip->base + SUN6I_ALRM1_IRQ_STA); + + /* disable alarm wakeup */ + writel(0, chip->base + SUN6I_ALARM_CONFIG); + + chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev, + &sun6i_rtc_ops, THIS_MODULE); + if (IS_ERR(chip->rtc)) { + dev_err(&pdev->dev, "unable to register device\n"); + return PTR_ERR(chip->rtc); + } + + dev_info(&pdev->dev, "RTC enabled\n"); + + return 0; +} + +static int sun6i_rtc_remove(struct platform_device *pdev) +{ + struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev); + + rtc_device_unregister(chip->rtc); + + return 0; +} + +static const struct of_device_id sun6i_rtc_dt_ids[] = { + { .compatible = "allwinner,sun6i-a31-rtc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); + +static struct platform_driver sun6i_rtc_driver = { + .probe = sun6i_rtc_probe, + .remove = sun6i_rtc_remove, + .driver = { + .name = "sun6i-rtc", + .of_match_table = sun6i_rtc_dt_ids, + }, +}; + +module_platform_driver(sun6i_rtc_driver); + +MODULE_DESCRIPTION("sun6i RTC driver"); +MODULE_AUTHOR("Chen-Yu Tsai "); +MODULE_LICENSE("GPL"); -- cgit v1.2.2 From 64a1925c00706253aa887431fc45f96db48aa727 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 26 Aug 2014 11:54:56 +0800 Subject: rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc Now that we have Kconfig options for individual sunxi platforms, let the rtc-sunxi driver depend on the platforms that actually have this hardware, sun4i and sun7i. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f2300b4e9b17..6b34bc94a3f6 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1184,7 +1184,7 @@ config RTC_DRV_SUN6I config RTC_DRV_SUNXI tristate "Allwinner sun4i/sun7i RTC" - depends on ARCH_SUNXI + depends on MACH_SUN4I || MACH_SUN7I help If you say Y here you will get support for the RTC found on Allwinner A10/A20. -- cgit v1.2.2 From 451ff6d40929c0d63e4fd22145160953b81a7dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Thu, 25 Sep 2014 16:05:22 -0700 Subject: drivers/rtc/rtc-efi.c: add missing module alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without proper alias kernel module is not loaded for rtc-efi driver. Signed-off-by: Pali Rohár Cc: dann frazier Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-efi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index 8225b89de810..c384fec6d173 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -232,6 +232,7 @@ static struct platform_driver efi_rtc_driver = { module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe); +MODULE_ALIAS("platform:rtc-efi"); MODULE_AUTHOR("dann frazier "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("EFI RTC driver"); -- cgit v1.2.2 From 3f71f6da7791a5feae0ff07e718431d1df01273a Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 11 Sep 2014 10:20:40 +0200 Subject: efi: rtc-efi: Export platform:rtc-efi as module alias When the rtc-efi driver is built as a module, we already register the EFI rtc as a platform device if UEFI Runtime Services are enabled. To wire it up to udev, and let the module be loaded automatically, we need to export the 'platform:rtc-efi' alias from the module. Signed-off-by: Ard Biesheuvel Cc: Alessandro Zummo Cc: Mark Salter Cc: Dave Young Signed-off-by: Matt Fleming --- drivers/rtc/rtc-efi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index 8225b89de810..59a55e7abf78 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -235,3 +235,4 @@ module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe); MODULE_AUTHOR("dann frazier "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("EFI RTC driver"); +MODULE_ALIAS("platform:rtc-efi"); -- cgit v1.2.2 From 7efe665903d0d963b0ebf4cab25cc3ae32c62600 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 3 Oct 2014 13:06:33 +0100 Subject: rtc: Disable EFI rtc for x86 commit da167ad7638759 ("rtc: ia64: allow other architectures to use EFI RTC") inadvertently introduced a regression for x86 because we've been careful not to enable the EFI rtc driver due to the generally buggy implementations of the time-related EFI runtime services. In fact, since the above commit was merged we've seen reports of crashes on 32-bit tablets, https://bugzilla.kernel.org/show_bug.cgi?id=84241#c21 Disable it explicitly for x86 so that we don't give users false hope that this driver will work - it won't, and your machine is likely to crash. Acked-by: Mark Salter Cc: Dave Young Cc: Alessandro Zummo Cc: # v3.17 Signed-off-by: Matt Fleming --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a168e96142b9..54ef393b0def 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -806,7 +806,7 @@ config RTC_DRV_DA9063 config RTC_DRV_EFI tristate "EFI RTC" - depends on EFI + depends on EFI && !X86 help If you say yes here you will get support for the EFI Real Time Clock. -- cgit v1.2.2 From 473b86451276d6d342ecd26d5e503163c30ea974 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 13 Oct 2014 15:52:26 -0700 Subject: rtc: use c99 initializers in structures Use c99 initializers for structures. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @decl@ identifier i1,fld; type T; field list[n] fs; @@ struct i1 { fs T fld; ...}; @bad@ identifier decl.i1,i2; expression e; initializer list[decl.n] is; @@ struct i1 i2 = { is, + .fld = e - e ,...}; // Signed-off-by: Julia Lawall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8583.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index c2639845186b..5911a6dca291 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -176,7 +176,11 @@ static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct i2c_client *client = to_i2c_client(dev); unsigned char ctrl, year[2]; - struct rtc_mem mem = { CMOS_YEAR, sizeof(year), year }; + struct rtc_mem mem = { + .loc = CMOS_YEAR, + .nr = sizeof(year), + .data = year + }; int real_year, year_offset, err; /* @@ -222,8 +226,16 @@ static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct i2c_client *client = to_i2c_client(dev); unsigned char year[2], chk; - struct rtc_mem cmos_year = { CMOS_YEAR, sizeof(year), year }; - struct rtc_mem cmos_check = { CMOS_CHECKSUM, 1, &chk }; + struct rtc_mem cmos_year = { + .loc = CMOS_YEAR, + .nr = sizeof(year), + .data = year + }; + struct rtc_mem cmos_check = { + .loc = CMOS_CHECKSUM, + .nr = 1, + .data = &chk + }; unsigned int proper_year = tm->tm_year + 1900; int ret; -- cgit v1.2.2 From 19be09f51d36101e4dc1577eb73ff8397bedd0a3 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 13 Oct 2014 15:52:28 -0700 Subject: rtc: s3c: define s3c_rtc structure to remove global variables. Define s3c_rtc structure including necessary variables for S3C RTC device instead of global variables. This patch improves the readability by removing global variables. Signed-off-by: Chanwoo Choi Acked-by: Kyungmin Park Cc: Alessandro Zummo Cc: Kukjin Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s3c.c | 431 +++++++++++++++++++++++++------------------------- 1 file changed, 216 insertions(+), 215 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 4958a363b2c7..2f71b13c00d6 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -43,125 +43,132 @@ struct s3c_rtc_drv_data { int cpu_type; }; -/* I have yet to find an S3C implementation with more than one - * of these rtc blocks in */ +struct s3c_rtc { + struct device *dev; + struct rtc_device *rtc; + + void __iomem *base; + struct clk *rtc_clk; + bool enabled; + + enum s3c_cpu_type cpu_type; -static struct clk *rtc_clk; -static void __iomem *s3c_rtc_base; -static int s3c_rtc_alarmno; -static int s3c_rtc_tickno; -static enum s3c_cpu_type s3c_rtc_cpu_type; + int irq_alarm; + int irq_tick; -static DEFINE_SPINLOCK(s3c_rtc_pie_lock); + spinlock_t pie_lock; + spinlock_t alarm_clk_lock; -static void s3c_rtc_alarm_clk_enable(bool enable) + int ticnt_save, ticnt_en_save; + bool wake_en; +}; + +static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) { - static DEFINE_SPINLOCK(s3c_rtc_alarm_clk_lock); - static bool alarm_clk_enabled; unsigned long irq_flags; - spin_lock_irqsave(&s3c_rtc_alarm_clk_lock, irq_flags); + spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); if (enable) { - if (!alarm_clk_enabled) { - clk_enable(rtc_clk); - alarm_clk_enabled = true; + if (!info->enabled) { + clk_enable(info->rtc_clk); + info->enabled = true; } } else { - if (alarm_clk_enabled) { - clk_disable(rtc_clk); - alarm_clk_enabled = false; + if (info->enabled) { + clk_disable(info->rtc_clk); + info->enabled = false; } } - spin_unlock_irqrestore(&s3c_rtc_alarm_clk_lock, irq_flags); + spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); } /* IRQ Handlers */ - static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) { - struct rtc_device *rdev = id; + struct s3c_rtc *info = (struct s3c_rtc *)id; - clk_enable(rtc_clk); - rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); + clk_enable(info->rtc_clk); + rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) - writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP); + if (info->cpu_type == TYPE_S3C64XX) + writeb(S3C2410_INTP_ALM, info->base + S3C2410_INTP); - clk_disable(rtc_clk); + clk_disable(info->rtc_clk); - s3c_rtc_alarm_clk_enable(false); + s3c_rtc_alarm_clk_enable(info, false); return IRQ_HANDLED; } static irqreturn_t s3c_rtc_tickirq(int irq, void *id) { - struct rtc_device *rdev = id; + struct s3c_rtc *info = (struct s3c_rtc *)id; - clk_enable(rtc_clk); - rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); + clk_enable(info->rtc_clk); + rtc_update_irq(info->rtc, 1, RTC_PF | RTC_IRQF); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) - writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP); + if (info->cpu_type == TYPE_S3C64XX) + writeb(S3C2410_INTP_TIC, info->base + S3C2410_INTP); + + clk_disable(info->rtc_clk); - clk_disable(rtc_clk); return IRQ_HANDLED; } /* Update control registers */ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) { + struct s3c_rtc *info = dev_get_drvdata(dev); unsigned int tmp; - dev_dbg(dev, "%s: aie=%d\n", __func__, enabled); + dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled); - clk_enable(rtc_clk); - tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; + clk_enable(info->rtc_clk); + tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; if (enabled) tmp |= S3C2410_RTCALM_ALMEN; - writeb(tmp, s3c_rtc_base + S3C2410_RTCALM); - clk_disable(rtc_clk); + writeb(tmp, info->base + S3C2410_RTCALM); + clk_disable(info->rtc_clk); - s3c_rtc_alarm_clk_enable(enabled); + s3c_rtc_alarm_clk_enable(info, enabled); return 0; } -static int s3c_rtc_setfreq(struct device *dev, int freq) +static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc_dev = platform_get_drvdata(pdev); unsigned int tmp = 0; int val; if (!is_power_of_2(freq)) return -EINVAL; - clk_enable(rtc_clk); - spin_lock_irq(&s3c_rtc_pie_lock); + clk_enable(info->rtc_clk); + spin_lock_irq(&info->pie_lock); - if (s3c_rtc_cpu_type != TYPE_S3C64XX) { - tmp = readb(s3c_rtc_base + S3C2410_TICNT); + if (info->cpu_type != TYPE_S3C64XX) { + tmp = readb(info->base + S3C2410_TICNT); tmp &= S3C2410_TICNT_ENABLE; } - val = (rtc_dev->max_user_freq / freq) - 1; + val = (info->rtc->max_user_freq / freq) - 1; - if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) { + if (info->cpu_type == TYPE_S3C2416 || info->cpu_type == TYPE_S3C2443) { tmp |= S3C2443_TICNT_PART(val); - writel(S3C2443_TICNT1_PART(val), s3c_rtc_base + S3C2443_TICNT1); + writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); - if (s3c_rtc_cpu_type == TYPE_S3C2416) - writel(S3C2416_TICNT2_PART(val), s3c_rtc_base + S3C2416_TICNT2); + if (info->cpu_type == TYPE_S3C2416) + writel(S3C2416_TICNT2_PART(val), + info->base + S3C2416_TICNT2); } else { tmp |= val; } - writel(tmp, s3c_rtc_base + S3C2410_TICNT); - spin_unlock_irq(&s3c_rtc_pie_lock); - clk_disable(rtc_clk); + writel(tmp, info->base + S3C2410_TICNT); + spin_unlock_irq(&info->pie_lock); + clk_disable(info->rtc_clk); return 0; } @@ -170,17 +177,17 @@ static int s3c_rtc_setfreq(struct device *dev, int freq) static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) { + struct s3c_rtc *info = dev_get_drvdata(dev); unsigned int have_retried = 0; - void __iomem *base = s3c_rtc_base; - clk_enable(rtc_clk); + clk_enable(info->rtc_clk); retry_get_time: - rtc_tm->tm_min = readb(base + S3C2410_RTCMIN); - rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR); - rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE); - rtc_tm->tm_mon = readb(base + S3C2410_RTCMON); - rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR); - rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC); + rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN); + rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); + rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE); + rtc_tm->tm_mon = readb(info->base + S3C2410_RTCMON); + rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR); + rtc_tm->tm_sec = readb(info->base + S3C2410_RTCSEC); /* the only way to work out whether the system was mid-update * when we read it is to check the second counter, and if it @@ -207,13 +214,14 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_mon -= 1; - clk_disable(rtc_clk); + clk_disable(info->rtc_clk); + return rtc_valid_tm(rtc_tm); } static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) { - void __iomem *base = s3c_rtc_base; + struct s3c_rtc *info = dev_get_drvdata(dev); int year = tm->tm_year - 100; dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n", @@ -227,33 +235,35 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) return -EINVAL; } - clk_enable(rtc_clk); - writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC); - writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN); - writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR); - writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE); - writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON); - writeb(bin2bcd(year), base + S3C2410_RTCYEAR); - clk_disable(rtc_clk); + clk_enable(info->rtc_clk); + + writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC); + writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN); + writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR); + writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE); + writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON); + writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR); + + clk_disable(info->rtc_clk); return 0; } static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) { + struct s3c_rtc *info = dev_get_drvdata(dev); struct rtc_time *alm_tm = &alrm->time; - void __iomem *base = s3c_rtc_base; unsigned int alm_en; - clk_enable(rtc_clk); - alm_tm->tm_sec = readb(base + S3C2410_ALMSEC); - alm_tm->tm_min = readb(base + S3C2410_ALMMIN); - alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR); - alm_tm->tm_mon = readb(base + S3C2410_ALMMON); - alm_tm->tm_mday = readb(base + S3C2410_ALMDATE); - alm_tm->tm_year = readb(base + S3C2410_ALMYEAR); + clk_enable(info->rtc_clk); + alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC); + alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN); + alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR); + alm_tm->tm_mon = readb(info->base + S3C2410_ALMMON); + alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE); + alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR); - alm_en = readb(base + S3C2410_RTCALM); + alm_en = readb(info->base + S3C2410_RTCALM); alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; @@ -297,65 +307,67 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) else alm_tm->tm_year = -1; - clk_disable(rtc_clk); + clk_disable(info->rtc_clk); return 0; } static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) { + struct s3c_rtc *info = dev_get_drvdata(dev); struct rtc_time *tm = &alrm->time; - void __iomem *base = s3c_rtc_base; unsigned int alrm_en; - clk_enable(rtc_clk); + clk_enable(info->rtc_clk); dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", alrm->enabled, 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; - writeb(0x00, base + S3C2410_RTCALM); + alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; + writeb(0x00, info->base + S3C2410_RTCALM); if (tm->tm_sec < 60 && tm->tm_sec >= 0) { alrm_en |= S3C2410_RTCALM_SECEN; - writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC); + writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC); } if (tm->tm_min < 60 && tm->tm_min >= 0) { alrm_en |= S3C2410_RTCALM_MINEN; - writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN); + writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN); } if (tm->tm_hour < 24 && tm->tm_hour >= 0) { alrm_en |= S3C2410_RTCALM_HOUREN; - writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR); + writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR); } dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en); - writeb(alrm_en, base + S3C2410_RTCALM); + writeb(alrm_en, info->base + S3C2410_RTCALM); s3c_rtc_setaie(dev, alrm->enabled); - clk_disable(rtc_clk); + clk_disable(info->rtc_clk); + return 0; } static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) { + struct s3c_rtc *info = dev_get_drvdata(dev); unsigned int ticnt; - clk_enable(rtc_clk); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - ticnt = readw(s3c_rtc_base + S3C2410_RTCCON); + clk_enable(info->rtc_clk); + if (info->cpu_type == TYPE_S3C64XX) { + ticnt = readw(info->base + S3C2410_RTCCON); ticnt &= S3C64XX_RTCCON_TICEN; } else { - ticnt = readb(s3c_rtc_base + S3C2410_TICNT); + ticnt = readb(info->base + S3C2410_TICNT); ticnt &= S3C2410_TICNT_ENABLE; } seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); - clk_disable(rtc_clk); + clk_disable(info->rtc_clk); return 0; } @@ -368,63 +380,61 @@ static const struct rtc_class_ops s3c_rtcops = { .alarm_irq_enable = s3c_rtc_setaie, }; -static void s3c_rtc_enable(struct platform_device *pdev, int en) +static void s3c_rtc_enable(struct s3c_rtc *info, int en) { - void __iomem *base = s3c_rtc_base; unsigned int tmp; - if (s3c_rtc_base == NULL) - return; - - clk_enable(rtc_clk); + clk_enable(info->rtc_clk); if (!en) { - tmp = readw(base + S3C2410_RTCCON); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) + tmp = readw(info->base + S3C2410_RTCCON); + if (info->cpu_type == TYPE_S3C64XX) tmp &= ~S3C64XX_RTCCON_TICEN; tmp &= ~S3C2410_RTCCON_RTCEN; - writew(tmp, base + S3C2410_RTCCON); + writew(tmp, info->base + S3C2410_RTCCON); - if (s3c_rtc_cpu_type != TYPE_S3C64XX) { - tmp = readb(base + S3C2410_TICNT); + if (info->cpu_type != TYPE_S3C64XX) { + tmp = readb(info->base + S3C2410_TICNT); tmp &= ~S3C2410_TICNT_ENABLE; - writeb(tmp, base + S3C2410_TICNT); + writeb(tmp, info->base + S3C2410_TICNT); } } else { /* re-enable the device, and check it is ok */ - if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { - dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); + if ((readw(info->base + S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { + dev_info(info->dev, "rtc disabled, re-enabling\n"); - tmp = readw(base + S3C2410_RTCCON); + tmp = readw(info->base + S3C2410_RTCCON); writew(tmp | S3C2410_RTCCON_RTCEN, - base + S3C2410_RTCCON); + info->base + S3C2410_RTCCON); } - if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) { - dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n"); + if ((readw(info->base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) { + dev_info(info->dev, "removing RTCCON_CNTSEL\n"); - tmp = readw(base + S3C2410_RTCCON); + tmp = readw(info->base + S3C2410_RTCCON); writew(tmp & ~S3C2410_RTCCON_CNTSEL, - base + S3C2410_RTCCON); + info->base + S3C2410_RTCCON); } - if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) { - dev_info(&pdev->dev, "removing RTCCON_CLKRST\n"); + if ((readw(info->base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) { + dev_info(info->dev, "removing RTCCON_CLKRST\n"); - tmp = readw(base + S3C2410_RTCCON); + tmp = readw(info->base + S3C2410_RTCCON); writew(tmp & ~S3C2410_RTCCON_CLKRST, - base + S3C2410_RTCCON); + info->base + S3C2410_RTCCON); } } - clk_disable(rtc_clk); + clk_disable(info->rtc_clk); } -static int s3c_rtc_remove(struct platform_device *dev) +static int s3c_rtc_remove(struct platform_device *pdev) { - s3c_rtc_setaie(&dev->dev, 0); + struct s3c_rtc *info = platform_get_drvdata(pdev); + + s3c_rtc_setaie(info->dev, 0); - clk_unprepare(rtc_clk); - rtc_clk = NULL; + clk_unprepare(info->rtc_clk); + info->rtc_clk = NULL; return 0; } @@ -447,73 +457,85 @@ static inline int s3c_rtc_get_driver_data(struct platform_device *pdev) static int s3c_rtc_probe(struct platform_device *pdev) { - struct rtc_device *rtc; + struct s3c_rtc *info = NULL; struct rtc_time rtc_tm; struct resource *res; int ret; int tmp; - dev_dbg(&pdev->dev, "%s: probe=%p\n", __func__, pdev); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; /* find the IRQs */ - - s3c_rtc_tickno = platform_get_irq(pdev, 1); - if (s3c_rtc_tickno < 0) { + info->irq_tick = platform_get_irq(pdev, 1); + if (info->irq_tick < 0) { dev_err(&pdev->dev, "no irq for rtc tick\n"); - return s3c_rtc_tickno; + return info->irq_tick; } - s3c_rtc_alarmno = platform_get_irq(pdev, 0); - if (s3c_rtc_alarmno < 0) { + info->dev = &pdev->dev; + info->cpu_type = s3c_rtc_get_driver_data(pdev); + spin_lock_init(&info->pie_lock); + spin_lock_init(&info->alarm_clk_lock); + + platform_set_drvdata(pdev, info); + + info->irq_alarm = platform_get_irq(pdev, 0); + if (info->irq_alarm < 0) { dev_err(&pdev->dev, "no irq for alarm\n"); - return s3c_rtc_alarmno; + return info->irq_alarm; } dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n", - s3c_rtc_tickno, s3c_rtc_alarmno); + info->irq_tick, info->irq_alarm); /* get the memory region */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - s3c_rtc_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(s3c_rtc_base)) - return PTR_ERR(s3c_rtc_base); + info->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->base)) + return PTR_ERR(info->base); - rtc_clk = devm_clk_get(&pdev->dev, "rtc"); - if (IS_ERR(rtc_clk)) { + info->rtc_clk = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(info->rtc_clk)) { dev_err(&pdev->dev, "failed to find rtc clock source\n"); - ret = PTR_ERR(rtc_clk); - rtc_clk = NULL; - return ret; + return PTR_ERR(info->rtc_clk); } - - clk_prepare_enable(rtc_clk); + clk_prepare_enable(info->rtc_clk); /* check to see if everything is setup correctly */ - - s3c_rtc_enable(pdev, 1); + s3c_rtc_enable(info, 1); dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", - readw(s3c_rtc_base + S3C2410_RTCCON)); + readw(info->base + S3C2410_RTCCON)); device_init_wakeup(&pdev->dev, 1); /* register RTC and exit */ - - rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops, + info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops, THIS_MODULE); - - if (IS_ERR(rtc)) { + if (IS_ERR(info->rtc)) { dev_err(&pdev->dev, "cannot attach rtc\n"); - ret = PTR_ERR(rtc); + ret = PTR_ERR(info->rtc); goto err_nortc; } - s3c_rtc_cpu_type = s3c_rtc_get_driver_data(pdev); + ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq, + 0, "s3c2410-rtc alarm", info); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret); + goto err_nortc; + } - /* Check RTC Time */ + ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq, + 0, "s3c2410-rtc tick", info); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret); + goto err_nortc; + } - s3c_rtc_gettime(NULL, &rtc_tm); + /* Check RTC Time */ + s3c_rtc_gettime(&pdev->dev, &rtc_tm); if (rtc_valid_tm(&rtc_tm)) { rtc_tm.tm_year = 100; @@ -523,111 +545,90 @@ static int s3c_rtc_probe(struct platform_device *pdev) rtc_tm.tm_min = 0; rtc_tm.tm_sec = 0; - s3c_rtc_settime(NULL, &rtc_tm); + s3c_rtc_settime(&pdev->dev, &rtc_tm); dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); } - if (s3c_rtc_cpu_type != TYPE_S3C2410) - rtc->max_user_freq = 32768; + if (info->cpu_type != TYPE_S3C2410) + info->rtc->max_user_freq = 32768; else - rtc->max_user_freq = 128; + info->rtc->max_user_freq = 128; - if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) { - tmp = readw(s3c_rtc_base + S3C2410_RTCCON); + if (info->cpu_type == TYPE_S3C2416 || info->cpu_type == TYPE_S3C2443) { + tmp = readw(info->base + S3C2410_RTCCON); tmp |= S3C2443_RTCCON_TICSEL; - writew(tmp, s3c_rtc_base + S3C2410_RTCCON); - } - - platform_set_drvdata(pdev, rtc); - - s3c_rtc_setfreq(&pdev->dev, 1); - - ret = devm_request_irq(&pdev->dev, s3c_rtc_alarmno, s3c_rtc_alarmirq, - 0, "s3c2410-rtc alarm", rtc); - if (ret) { - dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret); - goto err_nortc; + writew(tmp, info->base + S3C2410_RTCCON); } - ret = devm_request_irq(&pdev->dev, s3c_rtc_tickno, s3c_rtc_tickirq, - 0, "s3c2410-rtc tick", rtc); - if (ret) { - dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret); - goto err_nortc; - } + s3c_rtc_setfreq(info, 1); - clk_disable(rtc_clk); + clk_disable(info->rtc_clk); return 0; err_nortc: - s3c_rtc_enable(pdev, 0); - clk_disable_unprepare(rtc_clk); + s3c_rtc_enable(info, 0); + clk_disable_unprepare(info->rtc_clk); return ret; } #ifdef CONFIG_PM_SLEEP -/* RTC Power management control */ - -static int ticnt_save, ticnt_en_save; -static bool wake_en; static int s3c_rtc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); + struct s3c_rtc *info = dev_get_drvdata(dev); - clk_enable(rtc_clk); + clk_enable(info->rtc_clk); /* save TICNT for anyone using periodic interrupts */ - if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON); - ticnt_en_save &= S3C64XX_RTCCON_TICEN; - ticnt_save = readl(s3c_rtc_base + S3C2410_TICNT); + if (info->cpu_type == TYPE_S3C64XX) { + info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); + info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; + info->ticnt_save = readl(info->base + S3C2410_TICNT); } else { - ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); + info->ticnt_save = readb(info->base + S3C2410_TICNT); } - s3c_rtc_enable(pdev, 0); + s3c_rtc_enable(info, 0); - if (device_may_wakeup(dev) && !wake_en) { - if (enable_irq_wake(s3c_rtc_alarmno) == 0) - wake_en = true; + if (device_may_wakeup(dev) && !info->wake_en) { + if (enable_irq_wake(info->irq_alarm) == 0) + info->wake_en = true; else dev_err(dev, "enable_irq_wake failed\n"); } - clk_disable(rtc_clk); + clk_disable(info->rtc_clk); return 0; } static int s3c_rtc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); + struct s3c_rtc *info = dev_get_drvdata(dev); unsigned int tmp; - clk_enable(rtc_clk); - s3c_rtc_enable(pdev, 1); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - writel(ticnt_save, s3c_rtc_base + S3C2410_TICNT); - if (ticnt_en_save) { - tmp = readw(s3c_rtc_base + S3C2410_RTCCON); - writew(tmp | ticnt_en_save, - s3c_rtc_base + S3C2410_RTCCON); + clk_enable(info->rtc_clk); + s3c_rtc_enable(info, 1); + if (info->cpu_type == TYPE_S3C64XX) { + writel(info->ticnt_save, info->base + S3C2410_TICNT); + if (info->ticnt_en_save) { + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp | info->ticnt_en_save, + info->base + S3C2410_RTCCON); } } else { - writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); + writeb(info->ticnt_save, info->base + S3C2410_TICNT); } - if (device_may_wakeup(dev) && wake_en) { - disable_irq_wake(s3c_rtc_alarmno); - wake_en = false; + if (device_may_wakeup(dev) && info->wake_en) { + disable_irq_wake(info->irq_alarm); + info->wake_en = false; } - clk_disable(rtc_clk); + clk_disable(info->rtc_clk); return 0; } #endif - static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); #ifdef CONFIG_OF -- cgit v1.2.2 From d67288da51b782f54dd3ae1455b997131160fd41 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 13 Oct 2014 15:52:31 -0700 Subject: rtc: s3c: remove warning message when checking coding style with checkpatch script Remove warning message when checking codeing style with checkpatch script and reduce un-necessary i2c read operation on s3c_rtc_enable. WARNING: line over 80 characters #406: FILE: drivers/rtc/rtc-s3c.c:406: + if ((readw(info->base + S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { WARNING: line over 80 characters #414: FILE: drivers/rtc/rtc-s3c.c:414: + if ((readw(info->base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) { WARNING: line over 80 characters #422: FILE: drivers/rtc/rtc-s3c.c:422: + if ((readw(info->base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) { WARNING: Missing a blank line after declarations #451: FILE: drivers/rtc/rtc-s3c.c:451: + struct s3c_rtc_drv_data *data; + if (pdev->dev.of_node) { WARNING: Missing a blank line after declarations #453: FILE: drivers/rtc/rtc-s3c.c:453: + const struct of_device_id *match; + match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); WARNING: DT compatible string "samsung,s3c2416-rtc" appears un-documented -- check ./Documentation/devicetree/bindings/ #650: FILE: drivers/rtc/rtc-s3c.c:650: + .compatible = "samsung,s3c2416-rtc", WARNING: DT compatible string "samsung,s3c2443-rtc" appears un-documented -- check ./Documentation/devicetree/bindings/ #653: FILE: drivers/rtc/rtc-s3c.c:653: + .compatible = "samsung,s3c2443-rtc", Signed-off-by: Chanwoo Choi Acked-by: Kyungmin Park Cc: Alessandro Zummo Cc: Kukjin Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s3c.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 2f71b13c00d6..4e95cca7615c 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -382,25 +382,25 @@ static const struct rtc_class_ops s3c_rtcops = { static void s3c_rtc_enable(struct s3c_rtc *info, int en) { - unsigned int tmp; + unsigned int con, tmp; clk_enable(info->rtc_clk); + + con = readw(info->base + S3C2410_RTCCON); if (!en) { - tmp = readw(info->base + S3C2410_RTCCON); if (info->cpu_type == TYPE_S3C64XX) - tmp &= ~S3C64XX_RTCCON_TICEN; - tmp &= ~S3C2410_RTCCON_RTCEN; - writew(tmp, info->base + S3C2410_RTCCON); + con &= ~S3C64XX_RTCCON_TICEN; + con &= ~S3C2410_RTCCON_RTCEN; + writew(con, info->base + S3C2410_RTCCON); if (info->cpu_type != TYPE_S3C64XX) { - tmp = readb(info->base + S3C2410_TICNT); - tmp &= ~S3C2410_TICNT_ENABLE; - writeb(tmp, info->base + S3C2410_TICNT); + con = readb(info->base + S3C2410_TICNT); + con &= ~S3C2410_TICNT_ENABLE; + writeb(con, info->base + S3C2410_TICNT); } } else { /* re-enable the device, and check it is ok */ - - if ((readw(info->base + S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { + if ((con & S3C2410_RTCCON_RTCEN) == 0) { dev_info(info->dev, "rtc disabled, re-enabling\n"); tmp = readw(info->base + S3C2410_RTCCON); @@ -408,7 +408,7 @@ static void s3c_rtc_enable(struct s3c_rtc *info, int en) info->base + S3C2410_RTCCON); } - if ((readw(info->base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) { + if (con & S3C2410_RTCCON_CNTSEL) { dev_info(info->dev, "removing RTCCON_CNTSEL\n"); tmp = readw(info->base + S3C2410_RTCCON); @@ -416,7 +416,7 @@ static void s3c_rtc_enable(struct s3c_rtc *info, int en) info->base + S3C2410_RTCCON); } - if ((readw(info->base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) { + if (con & S3C2410_RTCCON_CLKRST) { dev_info(info->dev, "removing RTCCON_CLKRST\n"); tmp = readw(info->base + S3C2410_RTCCON); @@ -445,8 +445,10 @@ static inline int s3c_rtc_get_driver_data(struct platform_device *pdev) { #ifdef CONFIG_OF struct s3c_rtc_drv_data *data; + if (pdev->dev.of_node) { const struct of_device_id *match; + match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); data = (struct s3c_rtc_drv_data *) match->data; return data->cpu_type; -- cgit v1.2.2 From ae05c95074e0ead8a8fda4aca066e10270086e3f Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 13 Oct 2014 15:52:33 -0700 Subject: rtc: s3c: add s3c_rtc_data structure to use variant data instead of s3c_cpu_type Add s3c_rtc_data structure to variant data according to SoC type. The s3c_rtc_data structure includes some functions to control RTC operation and specific data dependent on SoC type. Signed-off-by: Chanwoo Choi Acked-by: Kyungmin Park Cc: Alessandro Zummo Cc: Kukjin Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s3c.c | 461 +++++++++++++++++++++++++++++++------------------- 1 file changed, 289 insertions(+), 172 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 4e95cca7615c..0d9089228bb0 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -32,17 +32,6 @@ #include #include "rtc-s3c.h" -enum s3c_cpu_type { - TYPE_S3C2410, - TYPE_S3C2416, - TYPE_S3C2443, - TYPE_S3C64XX, -}; - -struct s3c_rtc_drv_data { - int cpu_type; -}; - struct s3c_rtc { struct device *dev; struct rtc_device *rtc; @@ -51,7 +40,7 @@ struct s3c_rtc { struct clk *rtc_clk; bool enabled; - enum s3c_cpu_type cpu_type; + struct s3c_rtc_data *data; int irq_alarm; int irq_tick; @@ -63,6 +52,19 @@ struct s3c_rtc { bool wake_en; }; +struct s3c_rtc_data { + int max_user_freq; + + void (*irq_handler) (struct s3c_rtc *info, int mask); + void (*set_freq) (struct s3c_rtc *info, int freq); + void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq); + void (*select_tick_clk) (struct s3c_rtc *info); + void (*save_tick_cnt) (struct s3c_rtc *info); + void (*restore_tick_cnt) (struct s3c_rtc *info); + void (*enable) (struct s3c_rtc *info); + void (*disable) (struct s3c_rtc *info); +}; + static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) { unsigned long irq_flags; @@ -83,34 +85,22 @@ static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) } /* IRQ Handlers */ -static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) +static irqreturn_t s3c_rtc_tickirq(int irq, void *id) { struct s3c_rtc *info = (struct s3c_rtc *)id; - clk_enable(info->rtc_clk); - rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); - - if (info->cpu_type == TYPE_S3C64XX) - writeb(S3C2410_INTP_ALM, info->base + S3C2410_INTP); - - clk_disable(info->rtc_clk); - - s3c_rtc_alarm_clk_enable(info, false); + if (info->data->irq_handler) + info->data->irq_handler(info, S3C2410_INTP_TIC); return IRQ_HANDLED; } -static irqreturn_t s3c_rtc_tickirq(int irq, void *id) +static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) { struct s3c_rtc *info = (struct s3c_rtc *)id; - clk_enable(info->rtc_clk); - rtc_update_irq(info->rtc, 1, RTC_PF | RTC_IRQF); - - if (info->cpu_type == TYPE_S3C64XX) - writeb(S3C2410_INTP_TIC, info->base + S3C2410_INTP); - - clk_disable(info->rtc_clk); + if (info->data->irq_handler) + info->data->irq_handler(info, S3C2410_INTP_ALM); return IRQ_HANDLED; } @@ -137,36 +127,18 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) return 0; } +/* Set RTC frequency */ static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) { - unsigned int tmp = 0; - int val; - if (!is_power_of_2(freq)) return -EINVAL; clk_enable(info->rtc_clk); spin_lock_irq(&info->pie_lock); - if (info->cpu_type != TYPE_S3C64XX) { - tmp = readb(info->base + S3C2410_TICNT); - tmp &= S3C2410_TICNT_ENABLE; - } - - val = (info->rtc->max_user_freq / freq) - 1; - - if (info->cpu_type == TYPE_S3C2416 || info->cpu_type == TYPE_S3C2443) { - tmp |= S3C2443_TICNT_PART(val); - writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); + if (info->data->set_freq) + info->data->set_freq(info, freq); - if (info->cpu_type == TYPE_S3C2416) - writel(S3C2416_TICNT2_PART(val), - info->base + S3C2416_TICNT2); - } else { - tmp |= val; - } - - writel(tmp, info->base + S3C2410_TICNT); spin_unlock_irq(&info->pie_lock); clk_disable(info->rtc_clk); @@ -174,7 +146,6 @@ static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) } /* Time read/write */ - static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) { struct s3c_rtc *info = dev_get_drvdata(dev); @@ -355,19 +326,14 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) { struct s3c_rtc *info = dev_get_drvdata(dev); - unsigned int ticnt; clk_enable(info->rtc_clk); - if (info->cpu_type == TYPE_S3C64XX) { - ticnt = readw(info->base + S3C2410_RTCCON); - ticnt &= S3C64XX_RTCCON_TICEN; - } else { - ticnt = readb(info->base + S3C2410_TICNT); - ticnt &= S3C2410_TICNT_ENABLE; - } - seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); + if (info->data->enable_tick) + info->data->enable_tick(info, seq); + clk_disable(info->rtc_clk); + return 0; } @@ -380,50 +346,69 @@ static const struct rtc_class_ops s3c_rtcops = { .alarm_irq_enable = s3c_rtc_setaie, }; -static void s3c_rtc_enable(struct s3c_rtc *info, int en) +static void s3c24xx_rtc_enable(struct s3c_rtc *info) { unsigned int con, tmp; clk_enable(info->rtc_clk); con = readw(info->base + S3C2410_RTCCON); - if (!en) { - if (info->cpu_type == TYPE_S3C64XX) - con &= ~S3C64XX_RTCCON_TICEN; - con &= ~S3C2410_RTCCON_RTCEN; - writew(con, info->base + S3C2410_RTCCON); - - if (info->cpu_type != TYPE_S3C64XX) { - con = readb(info->base + S3C2410_TICNT); - con &= ~S3C2410_TICNT_ENABLE; - writeb(con, info->base + S3C2410_TICNT); - } - } else { - /* re-enable the device, and check it is ok */ - if ((con & S3C2410_RTCCON_RTCEN) == 0) { - dev_info(info->dev, "rtc disabled, re-enabling\n"); + /* re-enable the device, and check it is ok */ + if ((con & S3C2410_RTCCON_RTCEN) == 0) { + dev_info(info->dev, "rtc disabled, re-enabling\n"); - tmp = readw(info->base + S3C2410_RTCCON); - writew(tmp | S3C2410_RTCCON_RTCEN, - info->base + S3C2410_RTCCON); - } + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp | S3C2410_RTCCON_RTCEN, + info->base + S3C2410_RTCCON); + } - if (con & S3C2410_RTCCON_CNTSEL) { - dev_info(info->dev, "removing RTCCON_CNTSEL\n"); + if (con & S3C2410_RTCCON_CNTSEL) { + dev_info(info->dev, "removing RTCCON_CNTSEL\n"); - tmp = readw(info->base + S3C2410_RTCCON); - writew(tmp & ~S3C2410_RTCCON_CNTSEL, - info->base + S3C2410_RTCCON); - } + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp & ~S3C2410_RTCCON_CNTSEL, + info->base + S3C2410_RTCCON); + } - if (con & S3C2410_RTCCON_CLKRST) { - dev_info(info->dev, "removing RTCCON_CLKRST\n"); + if (con & S3C2410_RTCCON_CLKRST) { + dev_info(info->dev, "removing RTCCON_CLKRST\n"); - tmp = readw(info->base + S3C2410_RTCCON); - writew(tmp & ~S3C2410_RTCCON_CLKRST, - info->base + S3C2410_RTCCON); - } + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp & ~S3C2410_RTCCON_CLKRST, + info->base + S3C2410_RTCCON); } + + clk_disable(info->rtc_clk); +} + +static void s3c24xx_rtc_disable(struct s3c_rtc *info) +{ + unsigned int con; + + clk_enable(info->rtc_clk); + + con = readw(info->base + S3C2410_RTCCON); + con &= ~S3C2410_RTCCON_RTCEN; + writew(con, info->base + S3C2410_RTCCON); + + con = readb(info->base + S3C2410_TICNT); + con &= ~S3C2410_TICNT_ENABLE; + writeb(con, info->base + S3C2410_TICNT); + + clk_disable(info->rtc_clk); +} + +static void s3c6410_rtc_disable(struct s3c_rtc *info) +{ + unsigned int con; + + clk_enable(info->rtc_clk); + + con = readw(info->base + S3C2410_RTCCON); + con &= ~S3C64XX_RTCCON_TICEN; + con &= ~S3C2410_RTCCON_RTCEN; + writew(con, info->base + S3C2410_RTCCON); + clk_disable(info->rtc_clk); } @@ -441,20 +426,12 @@ static int s3c_rtc_remove(struct platform_device *pdev) static const struct of_device_id s3c_rtc_dt_match[]; -static inline int s3c_rtc_get_driver_data(struct platform_device *pdev) +static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) { -#ifdef CONFIG_OF - struct s3c_rtc_drv_data *data; + const struct of_device_id *match; - if (pdev->dev.of_node) { - const struct of_device_id *match; - - match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); - data = (struct s3c_rtc_drv_data *) match->data; - return data->cpu_type; - } -#endif - return platform_get_device_id(pdev)->driver_data; + match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); + return (struct s3c_rtc_data *)match->data; } static int s3c_rtc_probe(struct platform_device *pdev) @@ -463,7 +440,6 @@ static int s3c_rtc_probe(struct platform_device *pdev) struct rtc_time rtc_tm; struct resource *res; int ret; - int tmp; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -477,7 +453,11 @@ static int s3c_rtc_probe(struct platform_device *pdev) } info->dev = &pdev->dev; - info->cpu_type = s3c_rtc_get_driver_data(pdev); + info->data = s3c_rtc_get_data(pdev); + if (!info->data) { + dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); + return -EINVAL; + } spin_lock_init(&info->pie_lock); spin_lock_init(&info->alarm_clk_lock); @@ -506,7 +486,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) clk_prepare_enable(info->rtc_clk); /* check to see if everything is setup correctly */ - s3c_rtc_enable(info, 1); + if (info->data->enable) + info->data->enable(info); dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", readw(info->base + S3C2410_RTCCON)); @@ -552,16 +533,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); } - if (info->cpu_type != TYPE_S3C2410) - info->rtc->max_user_freq = 32768; - else - info->rtc->max_user_freq = 128; - - if (info->cpu_type == TYPE_S3C2416 || info->cpu_type == TYPE_S3C2443) { - tmp = readw(info->base + S3C2410_RTCCON); - tmp |= S3C2443_RTCCON_TICSEL; - writew(tmp, info->base + S3C2410_RTCCON); - } + if (info->data->select_tick_clk) + info->data->select_tick_clk(info); s3c_rtc_setfreq(info, 1); @@ -570,7 +543,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) return 0; err_nortc: - s3c_rtc_enable(info, 0); + if (info->data->disable) + info->data->disable(info); clk_disable_unprepare(info->rtc_clk); return ret; @@ -583,15 +557,13 @@ static int s3c_rtc_suspend(struct device *dev) struct s3c_rtc *info = dev_get_drvdata(dev); clk_enable(info->rtc_clk); + /* save TICNT for anyone using periodic interrupts */ - if (info->cpu_type == TYPE_S3C64XX) { - info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); - info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; - info->ticnt_save = readl(info->base + S3C2410_TICNT); - } else { - info->ticnt_save = readb(info->base + S3C2410_TICNT); - } - s3c_rtc_enable(info, 0); + if (info->data->save_tick_cnt) + info->data->save_tick_cnt(info); + + if (info->data->disable) + info->data->disable(info); if (device_may_wakeup(dev) && !info->wake_en) { if (enable_irq_wake(info->irq_alarm) == 0) @@ -599,6 +571,7 @@ static int s3c_rtc_suspend(struct device *dev) else dev_err(dev, "enable_irq_wake failed\n"); } + clk_disable(info->rtc_clk); return 0; @@ -607,25 +580,20 @@ static int s3c_rtc_suspend(struct device *dev) static int s3c_rtc_resume(struct device *dev) { struct s3c_rtc *info = dev_get_drvdata(dev); - unsigned int tmp; clk_enable(info->rtc_clk); - s3c_rtc_enable(info, 1); - if (info->cpu_type == TYPE_S3C64XX) { - writel(info->ticnt_save, info->base + S3C2410_TICNT); - if (info->ticnt_en_save) { - tmp = readw(info->base + S3C2410_RTCCON); - writew(tmp | info->ticnt_en_save, - info->base + S3C2410_RTCCON); - } - } else { - writeb(info->ticnt_save, info->base + S3C2410_TICNT); - } + + if (info->data->enable) + info->data->enable(info); + + if (info->data->restore_tick_cnt) + info->data->restore_tick_cnt(info); if (device_may_wakeup(dev) && info->wake_en) { disable_irq_wake(info->irq_alarm); info->wake_en = false; } + clk_disable(info->rtc_clk); return 0; @@ -633,56 +601,206 @@ static int s3c_rtc_resume(struct device *dev) #endif static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); -#ifdef CONFIG_OF -static struct s3c_rtc_drv_data s3c_rtc_drv_data_array[] = { - [TYPE_S3C2410] = { TYPE_S3C2410 }, - [TYPE_S3C2416] = { TYPE_S3C2416 }, - [TYPE_S3C2443] = { TYPE_S3C2443 }, - [TYPE_S3C64XX] = { TYPE_S3C64XX }, +static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) +{ + clk_enable(info->rtc_clk); + rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); + clk_disable(info->rtc_clk); + + s3c_rtc_alarm_clk_enable(info, false); +} + +static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) +{ + clk_enable(info->rtc_clk); + rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); + writeb(mask, info->base + S3C2410_INTP); + clk_disable(info->rtc_clk); + + s3c_rtc_alarm_clk_enable(info, false); +} + +static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq) +{ + unsigned int tmp = 0; + int val; + + tmp = readb(info->base + S3C2410_TICNT); + tmp &= S3C2410_TICNT_ENABLE; + + val = (info->rtc->max_user_freq / freq) - 1; + tmp |= val; + + writel(tmp, info->base + S3C2410_TICNT); +} + +static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq) +{ + unsigned int tmp = 0; + int val; + + tmp = readb(info->base + S3C2410_TICNT); + tmp &= S3C2410_TICNT_ENABLE; + + val = (info->rtc->max_user_freq / freq) - 1; + + tmp |= S3C2443_TICNT_PART(val); + writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); + + writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2); + + writel(tmp, info->base + S3C2410_TICNT); +} + +static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq) +{ + unsigned int tmp = 0; + int val; + + tmp = readb(info->base + S3C2410_TICNT); + tmp &= S3C2410_TICNT_ENABLE; + + val = (info->rtc->max_user_freq / freq) - 1; + + tmp |= S3C2443_TICNT_PART(val); + writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); + + writel(tmp, info->base + S3C2410_TICNT); +} + +static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq) +{ + int val; + + val = (info->rtc->max_user_freq / freq) - 1; + writel(val, info->base + S3C2410_TICNT); +} + +static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) +{ + unsigned int ticnt; + + ticnt = readb(info->base + S3C2410_TICNT); + ticnt &= S3C2410_TICNT_ENABLE; + + seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); +} + +static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info) +{ + unsigned int con; + + con = readw(info->base + S3C2410_RTCCON); + con |= S3C2443_RTCCON_TICSEL; + writew(con, info->base + S3C2410_RTCCON); +} + +static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) +{ + unsigned int ticnt; + + ticnt = readw(info->base + S3C2410_RTCCON); + ticnt &= S3C64XX_RTCCON_TICEN; + + seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); +} + +static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info) +{ + info->ticnt_save = readb(info->base + S3C2410_TICNT); +} + +static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info) +{ + writeb(info->ticnt_save, info->base + S3C2410_TICNT); +} + +static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info) +{ + info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); + info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; + info->ticnt_save = readl(info->base + S3C2410_TICNT); +} + +static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info) +{ + unsigned int con; + + writel(info->ticnt_save, info->base + S3C2410_TICNT); + if (info->ticnt_en_save) { + con = readw(info->base + S3C2410_RTCCON); + writew(con | info->ticnt_en_save, + info->base + S3C2410_RTCCON); + } +} + +static struct s3c_rtc_data const s3c2410_rtc_data = { + .max_user_freq = 128, + .irq_handler = s3c24xx_rtc_irq, + .set_freq = s3c2410_rtc_setfreq, + .enable_tick = s3c24xx_rtc_enable_tick, + .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, + .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c24xx_rtc_disable, +}; + +static struct s3c_rtc_data const s3c2416_rtc_data = { + .max_user_freq = 32768, + .irq_handler = s3c24xx_rtc_irq, + .set_freq = s3c2416_rtc_setfreq, + .enable_tick = s3c24xx_rtc_enable_tick, + .select_tick_clk = s3c2416_rtc_select_tick_clk, + .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, + .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c24xx_rtc_disable, +}; + +static struct s3c_rtc_data const s3c2443_rtc_data = { + .max_user_freq = 32768, + .irq_handler = s3c24xx_rtc_irq, + .set_freq = s3c2443_rtc_setfreq, + .enable_tick = s3c24xx_rtc_enable_tick, + .select_tick_clk = s3c2416_rtc_select_tick_clk, + .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, + .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c24xx_rtc_disable, +}; + +static struct s3c_rtc_data const s3c6410_rtc_data = { + .max_user_freq = 32768, + .irq_handler = s3c6410_rtc_irq, + .set_freq = s3c6410_rtc_setfreq, + .enable_tick = s3c6410_rtc_enable_tick, + .save_tick_cnt = s3c6410_rtc_save_tick_cnt, + .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c6410_rtc_disable, }; static const struct of_device_id s3c_rtc_dt_match[] = { { .compatible = "samsung,s3c2410-rtc", - .data = &s3c_rtc_drv_data_array[TYPE_S3C2410], + .data = (void *)&s3c2410_rtc_data, }, { .compatible = "samsung,s3c2416-rtc", - .data = &s3c_rtc_drv_data_array[TYPE_S3C2416], + .data = (void *)&s3c2416_rtc_data, }, { .compatible = "samsung,s3c2443-rtc", - .data = &s3c_rtc_drv_data_array[TYPE_S3C2443], + .data = (void *)&s3c2443_rtc_data, }, { .compatible = "samsung,s3c6410-rtc", - .data = &s3c_rtc_drv_data_array[TYPE_S3C64XX], + .data = (void *)&s3c6410_rtc_data, }, - {}, + { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); -#endif - -static struct platform_device_id s3c_rtc_driver_ids[] = { - { - .name = "s3c2410-rtc", - .driver_data = TYPE_S3C2410, - }, { - .name = "s3c2416-rtc", - .driver_data = TYPE_S3C2416, - }, { - .name = "s3c2443-rtc", - .driver_data = TYPE_S3C2443, - }, { - .name = "s3c64xx-rtc", - .driver_data = TYPE_S3C64XX, - }, - { } -}; - -MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids); static struct platform_driver s3c_rtc_driver = { .probe = s3c_rtc_probe, .remove = s3c_rtc_remove, - .id_table = s3c_rtc_driver_ids, .driver = { .name = "s3c-rtc", .owner = THIS_MODULE, @@ -690,7 +808,6 @@ static struct platform_driver s3c_rtc_driver = { .of_match_table = of_match_ptr(s3c_rtc_dt_match), }, }; - module_platform_driver(s3c_rtc_driver); MODULE_DESCRIPTION("Samsung S3C RTC Driver"); -- cgit v1.2.2 From df9e26d093d33a097c5558aab017dd2f540ccfe5 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 13 Oct 2014 15:52:35 -0700 Subject: rtc: s3c: add support for RTC of Exynos3250 SoC Add support for RTC of Exynos3250 SoC. The Exynos3250 needs source clock(32.768KHz) for RTC block. If source clock of RTC is registerd on clock list of common clk framework, Exynos RTC drvier have to control this clock. Clock list for s3c-rtc device: - rtc : CLK_RTC of CLK_GATE_IP_PERIR is gate clock for RTC. - rtc_src : XrtcXTI is 32.768.kHz source clock for RTC. (XRTCXTI: Specifies a clock from 32.768 kHz crystal pad with XRTCXTI and XRTCXTO pins. RTC uses this clock as the source of a real-time clock.) Signed-off-by: Chanwoo Choi Acked-by: Kyungmin Park Cc: Alessandro Zummo Cc: Kukjin Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s3c.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 0d9089228bb0..a6b1252c9941 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -38,6 +38,7 @@ struct s3c_rtc { void __iomem *base; struct clk *rtc_clk; + struct clk *rtc_src_clk; bool enabled; struct s3c_rtc_data *data; @@ -54,6 +55,7 @@ struct s3c_rtc { struct s3c_rtc_data { int max_user_freq; + bool needs_src_clk; void (*irq_handler) (struct s3c_rtc *info, int mask); void (*set_freq) (struct s3c_rtc *info, int freq); @@ -73,10 +75,14 @@ static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) if (enable) { if (!info->enabled) { clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); info->enabled = true; } } else { if (info->enabled) { + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); info->enabled = false; } @@ -114,12 +120,16 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled); clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; if (enabled) tmp |= S3C2410_RTCALM_ALMEN; writeb(tmp, info->base + S3C2410_RTCALM); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); s3c_rtc_alarm_clk_enable(info, enabled); @@ -134,12 +144,16 @@ static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) return -EINVAL; clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); spin_lock_irq(&info->pie_lock); if (info->data->set_freq) info->data->set_freq(info, freq); spin_unlock_irq(&info->pie_lock); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); return 0; @@ -152,6 +166,9 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) unsigned int have_retried = 0; clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + retry_get_time: rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN); rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); @@ -185,6 +202,8 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_mon -= 1; + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); return rtc_valid_tm(rtc_tm); @@ -207,6 +226,8 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) } clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC); writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN); @@ -215,6 +236,8 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON); writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); return 0; @@ -227,6 +250,9 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) unsigned int alm_en; clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC); alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN); alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR); @@ -278,7 +304,10 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) else alm_tm->tm_year = -1; + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); + return 0; } @@ -289,6 +318,9 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) unsigned int alrm_en; clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", alrm->enabled, 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, @@ -318,6 +350,8 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) s3c_rtc_setaie(dev, alrm->enabled); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); return 0; @@ -328,10 +362,14 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) struct s3c_rtc *info = dev_get_drvdata(dev); clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); if (info->data->enable_tick) info->data->enable_tick(info, seq); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); return 0; @@ -351,6 +389,8 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info) unsigned int con, tmp; clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); con = readw(info->base + S3C2410_RTCCON); /* re-enable the device, and check it is ok */ @@ -378,6 +418,8 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info) info->base + S3C2410_RTCCON); } + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); } @@ -386,6 +428,8 @@ static void s3c24xx_rtc_disable(struct s3c_rtc *info) unsigned int con; clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); con = readw(info->base + S3C2410_RTCCON); con &= ~S3C2410_RTCCON_RTCEN; @@ -395,6 +439,8 @@ static void s3c24xx_rtc_disable(struct s3c_rtc *info) con &= ~S3C2410_TICNT_ENABLE; writeb(con, info->base + S3C2410_TICNT); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); } @@ -403,12 +449,16 @@ static void s3c6410_rtc_disable(struct s3c_rtc *info) unsigned int con; clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); con = readw(info->base + S3C2410_RTCCON); con &= ~S3C64XX_RTCCON_TICEN; con &= ~S3C2410_RTCCON_RTCEN; writew(con, info->base + S3C2410_RTCCON); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); } @@ -480,11 +530,19 @@ static int s3c_rtc_probe(struct platform_device *pdev) info->rtc_clk = devm_clk_get(&pdev->dev, "rtc"); if (IS_ERR(info->rtc_clk)) { - dev_err(&pdev->dev, "failed to find rtc clock source\n"); + dev_err(&pdev->dev, "failed to find rtc clock\n"); return PTR_ERR(info->rtc_clk); } clk_prepare_enable(info->rtc_clk); + info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); + if (IS_ERR(info->rtc_src_clk)) { + dev_err(&pdev->dev, "failed to find rtc source clock\n"); + return PTR_ERR(info->rtc_src_clk); + } + clk_prepare_enable(info->rtc_src_clk); + + /* check to see if everything is setup correctly */ if (info->data->enable) info->data->enable(info); @@ -538,6 +596,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) s3c_rtc_setfreq(info, 1); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); return 0; @@ -557,6 +617,8 @@ static int s3c_rtc_suspend(struct device *dev) struct s3c_rtc *info = dev_get_drvdata(dev); clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); /* save TICNT for anyone using periodic interrupts */ if (info->data->save_tick_cnt) @@ -572,6 +634,8 @@ static int s3c_rtc_suspend(struct device *dev) dev_err(dev, "enable_irq_wake failed\n"); } + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); return 0; @@ -582,6 +646,8 @@ static int s3c_rtc_resume(struct device *dev) struct s3c_rtc *info = dev_get_drvdata(dev); clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); if (info->data->enable) info->data->enable(info); @@ -594,6 +660,8 @@ static int s3c_rtc_resume(struct device *dev) info->wake_en = false; } + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); return 0; @@ -604,7 +672,11 @@ static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) { clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); s3c_rtc_alarm_clk_enable(info, false); @@ -613,8 +685,12 @@ static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) { clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); writeb(mask, info->base + S3C2410_INTP); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); clk_disable(info->rtc_clk); s3c_rtc_alarm_clk_enable(info, false); @@ -780,6 +856,18 @@ static struct s3c_rtc_data const s3c6410_rtc_data = { .disable = s3c6410_rtc_disable, }; +static struct s3c_rtc_data const exynos3250_rtc_data = { + .max_user_freq = 32768, + .needs_src_clk = true, + .irq_handler = s3c6410_rtc_irq, + .set_freq = s3c6410_rtc_setfreq, + .enable_tick = s3c6410_rtc_enable_tick, + .save_tick_cnt = s3c6410_rtc_save_tick_cnt, + .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c6410_rtc_disable, +}; + static const struct of_device_id s3c_rtc_dt_match[] = { { .compatible = "samsung,s3c2410-rtc", @@ -793,6 +881,9 @@ static const struct of_device_id s3c_rtc_dt_match[] = { }, { .compatible = "samsung,s3c6410-rtc", .data = (void *)&s3c6410_rtc_data, + }, { + .compatible = "samsung,exynos3250-rtc", + .data = (void *)&exynos3250_rtc_data, }, { /* sentinel */ }, }; -- cgit v1.2.2 From a28885bc75da0aac61b975eb9c103407dbdf5cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 13 Oct 2014 15:52:39 -0700 Subject: rtc: make of_device_ids const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit of_device_ids (i.e. compatible strings and the respective data) are not supposed to change at runtime. All functions working with of_device_ids provided by work with const of_device_ids. This allows to mark all struct of_device_id below drivers/rtc const, too. Signed-off-by: Uwe Kleine-König Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl12022.c | 2 +- drivers/rtc/rtc-mpc5121.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index aa55f081c505..ee3ba7e6b45e 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -274,7 +274,7 @@ static int isl12022_probe(struct i2c_client *client, } #ifdef CONFIG_OF -static struct of_device_id isl12022_dt_match[] = { +static const struct of_device_id isl12022_dt_match[] = { { .compatible = "isl,isl12022" }, { }, }; diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index dc4f14255cc3..3b965ad6f4d5 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -401,7 +401,7 @@ static int mpc5121_rtc_remove(struct platform_device *op) } #ifdef CONFIG_OF -static struct of_device_id mpc5121_rtc_match[] = { +static const struct of_device_id mpc5121_rtc_match[] = { { .compatible = "fsl,mpc5121-rtc", }, { .compatible = "fsl,mpc5200-rtc", }, {}, -- cgit v1.2.2 From 3ca1e326f5952f3acca320b8a91d92393a188c15 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Mon, 13 Oct 2014 15:52:42 -0700 Subject: RTC: RK808: add RTC driver for RK808 This is the initial version of the RK808 PMIC. This is a power management IC for multimedia products. It provides regulators that are able to supply power to processor cores and other components. The chip provides other modules including RTC, Clockout. Add RTC driver for supporting RTC device present inside RK808 PMIC. [akpm@linux-foundation.org: make tm_def static] Signed-off-by: Chris Zhong Signed-off-by: Zhang Qing Tested-by: Heiko Stuebner Reviewed-by: Doug Anderson Tested-by: Doug Anderson Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Samuel Ortiz says: Cc: Alessandro Zummo Cc: Olof Johansson Cc: Dmitry Torokhov Cc: Javier Martinez Canillas Cc: Kever Yang Cc: Li Zhong Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-rk808.c | 414 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 425 insertions(+) create mode 100644 drivers/rtc/rtc-rk808.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 1bea0fc43464..943afaf8f448 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -288,6 +288,16 @@ config RTC_DRV_MAX77686 This driver can also be built as a module. If so, the module will be called rtc-max77686. +config RTC_DRV_RK808 + tristate "Rockchip RK808 RTC" + depends on MFD_RK808 + help + If you say yes here you will get support for the + RTC of RK808 PMIC. + + This driver can also be built as a module. If so, the module + will be called rk808-rtc. + config RTC_DRV_RS5C372 tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 9055b7dd3dc5..7dec7fd90f90 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -109,6 +109,7 @@ obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.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 obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c new file mode 100644 index 000000000000..df42257668ac --- /dev/null +++ b/drivers/rtc/rtc-rk808.c @@ -0,0 +1,414 @@ +/* + * RTC driver for Rockchip RK808 + * + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * + * Author: Chris Zhong + * Author: Zhang Qing + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include +#include +#include +#include + +/* RTC_CTRL_REG bitfields */ +#define BIT_RTC_CTRL_REG_STOP_RTC_M BIT(0) + +/* RK808 has a shadowed register for saving a "frozen" RTC time. + * When user setting "GET_TIME" to 1, the time will save in this shadowed + * register. If set "READSEL" to 1, user read rtc time register, actually + * get the time of that moment. If we need the real time, clr this bit. + */ +#define BIT_RTC_CTRL_REG_RTC_GET_TIME BIT(6) +#define BIT_RTC_CTRL_REG_RTC_READSEL_M BIT(7) +#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M BIT(3) +#define RTC_STATUS_MASK 0xFE + +#define SECONDS_REG_MSK 0x7F +#define MINUTES_REG_MAK 0x7F +#define HOURS_REG_MSK 0x3F +#define DAYS_REG_MSK 0x3F +#define MONTHS_REG_MSK 0x1F +#define YEARS_REG_MSK 0xFF +#define WEEKS_REG_MSK 0x7 + +/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */ + +#define NUM_TIME_REGS (RK808_WEEKS_REG - RK808_SECONDS_REG + 1) +#define NUM_ALARM_REGS (RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1) + +struct rk808_rtc { + struct rk808 *rk808; + struct rtc_device *rtc; + int irq; +}; + +/* Read current time and date in RTC */ +static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + u8 rtc_data[NUM_TIME_REGS]; + int ret; + + /* Force an update of the shadowed registers right now */ + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_RTC_GET_TIME, + 0); + if (ret) { + dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_RTC_GET_TIME, + BIT_RTC_CTRL_REG_RTC_GET_TIME); + if (ret) { + dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret); + return ret; + } + + ret = regmap_bulk_read(rk808->regmap, RK808_SECONDS_REG, + rtc_data, NUM_TIME_REGS); + if (ret) { + dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0] & SECONDS_REG_MSK); + tm->tm_min = bcd2bin(rtc_data[1] & MINUTES_REG_MAK); + tm->tm_hour = bcd2bin(rtc_data[2] & HOURS_REG_MSK); + tm->tm_mday = bcd2bin(rtc_data[3] & DAYS_REG_MSK); + tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1; + tm->tm_year = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100; + tm->tm_wday = bcd2bin(rtc_data[6] & WEEKS_REG_MSK); + dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, + tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec); + + return ret; +} + +/* Set current time and date in RTC */ +static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + u8 rtc_data[NUM_TIME_REGS]; + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + rtc_data[6] = bin2bcd(tm->tm_wday); + dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, + tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec); + + /* Stop RTC while updating the RTC registers */ + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_STOP_RTC_M, + BIT_RTC_CTRL_REG_STOP_RTC_M); + if (ret) { + dev_err(dev, "Failed to update RTC control: %d\n", ret); + return ret; + } + + ret = regmap_bulk_write(rk808->regmap, RK808_SECONDS_REG, + rtc_data, NUM_TIME_REGS); + if (ret) { + dev_err(dev, "Failed to bull write rtc_data: %d\n", ret); + return ret; + } + /* Start RTC again */ + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_STOP_RTC_M, 0); + if (ret) { + dev_err(dev, "Failed to update RTC control: %d\n", ret); + return ret; + } + return 0; +} + +/* Read alarm time and date in RTC */ +static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + u8 alrm_data[NUM_ALARM_REGS]; + uint32_t int_reg; + int ret; + + ret = regmap_bulk_read(rk808->regmap, RK808_ALARM_SECONDS_REG, + alrm_data, NUM_ALARM_REGS); + + alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK); + alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK); + alrm->time.tm_hour = bcd2bin(alrm_data[2] & HOURS_REG_MSK); + alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK); + alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1; + alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100; + + ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg); + if (ret) { + dev_err(dev, "Failed to read RTC INT REG: %d\n", ret); + return ret; + } + + dev_dbg(dev, "alrm read RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, + alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, + alrm->time.tm_min, alrm->time.tm_sec); + + alrm->enabled = (int_reg & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) ? 1 : 0; + + return 0; +} + +static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc) +{ + struct rk808 *rk808 = rk808_rtc->rk808; + int ret; + + ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0); + + return ret; +} + +static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc) +{ + struct rk808 *rk808 = rk808_rtc->rk808; + int ret; + + ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + + return ret; +} + +static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + u8 alrm_data[NUM_ALARM_REGS]; + int ret; + + ret = rk808_rtc_stop_alarm(rk808_rtc); + if (ret) { + dev_err(dev, "Failed to stop alarm: %d\n", ret); + return ret; + } + dev_dbg(dev, "alrm set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, + alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, + alrm->time.tm_min, alrm->time.tm_sec); + + alrm_data[0] = bin2bcd(alrm->time.tm_sec); + alrm_data[1] = bin2bcd(alrm->time.tm_min); + alrm_data[2] = bin2bcd(alrm->time.tm_hour); + alrm_data[3] = bin2bcd(alrm->time.tm_mday); + alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1); + alrm_data[5] = bin2bcd(alrm->time.tm_year - 100); + + ret = regmap_bulk_write(rk808->regmap, RK808_ALARM_SECONDS_REG, + alrm_data, NUM_ALARM_REGS); + if (ret) { + dev_err(dev, "Failed to bulk write: %d\n", ret); + return ret; + } + if (alrm->enabled) { + ret = rk808_rtc_start_alarm(rk808_rtc); + if (ret) { + dev_err(dev, "Failed to start alarm: %d\n", ret); + return ret; + } + } + return 0; +} + +static int rk808_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + + if (enabled) + return rk808_rtc_start_alarm(rk808_rtc); + + return rk808_rtc_stop_alarm(rk808_rtc); +} + +/* + * We will just handle setting the frequency and make use the framework for + * reading the periodic interupts. + * + * @freq: Current periodic IRQ freq: + * bit 0: every second + * bit 1: every minute + * bit 2: every hour + * bit 3: every day + */ +static irqreturn_t rk808_alarm_irq(int irq, void *data) +{ + struct rk808_rtc *rk808_rtc = data; + struct rk808 *rk808 = rk808_rtc->rk808; + struct i2c_client *client = rk808->i2c; + int ret; + + ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG, + RTC_STATUS_MASK); + if (ret) { + dev_err(&client->dev, + "%s:Failed to update RTC status: %d\n", __func__, ret); + return ret; + } + + rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_AF); + dev_dbg(&client->dev, + "%s:irq=%d\n", __func__, irq); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops rk808_rtc_ops = { + .read_time = rk808_rtc_readtime, + .set_time = rk808_rtc_set_time, + .read_alarm = rk808_rtc_readalarm, + .set_alarm = rk808_rtc_setalarm, + .alarm_irq_enable = rk808_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM_SLEEP +/* Turn off the alarm if it should not be a wake source. */ +static int rk808_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rk808_rtc->irq); + + return 0; +} + +/* Enable the alarm if it should be enabled (in case it was disabled to + * prevent use as a wake source). + */ +static int rk808_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rk808_rtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops, + rk808_rtc_suspend, rk808_rtc_resume); + +static int rk808_rtc_probe(struct platform_device *pdev) +{ + struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); + struct rk808_rtc *rk808_rtc; + struct rtc_time tm; + int ret; + + rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL); + if (rk808_rtc == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, rk808_rtc); + rk808_rtc->rk808 = rk808; + + /* start rtc running by default, and use shadowed timer. */ + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_STOP_RTC_M | + BIT_RTC_CTRL_REG_RTC_READSEL_M, + BIT_RTC_CTRL_REG_RTC_READSEL_M); + if (ret) { + dev_err(&pdev->dev, + "Failed to update RTC control: %d\n", ret); + return ret; + } + + ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG, + RTC_STATUS_MASK); + if (ret) { + dev_err(&pdev->dev, + "Failed to write RTC status: %d\n", ret); + return ret; + } + + /* set init time */ + ret = rk808_rtc_readtime(&pdev->dev, &tm); + if (ret) { + dev_err(&pdev->dev, "Failed to read RTC time\n"); + return ret; + } + ret = rtc_valid_tm(&tm); + if (ret) + dev_warn(&pdev->dev, "invalid date/time\n"); + + device_init_wakeup(&pdev->dev, 1); + + rk808_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rk808-rtc", + &rk808_rtc_ops, THIS_MODULE); + if (IS_ERR(rk808_rtc->rtc)) { + ret = PTR_ERR(rk808_rtc->rtc); + return ret; + } + + rk808_rtc->irq = platform_get_irq(pdev, 0); + if (rk808_rtc->irq < 0) { + if (rk808_rtc->irq != -EPROBE_DEFER) + dev_err(&pdev->dev, "Wake up is not possible as irq = %d\n", + rk808_rtc->irq); + return rk808_rtc->irq; + } + + /* request alarm irq of rk808 */ + ret = devm_request_threaded_irq(&pdev->dev, rk808_rtc->irq, NULL, + rk808_alarm_irq, 0, + "RTC alarm", rk808_rtc); + if (ret) { + dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", + rk808_rtc->irq, ret); + } + + return ret; +} + +static struct platform_driver rk808_rtc_driver = { + .probe = rk808_rtc_probe, + .driver = { + .name = "rk808-rtc", + .pm = &rk808_rtc_pm_ops, + }, +}; + +module_platform_driver(rk808_rtc_driver); + +MODULE_DESCRIPTION("RTC driver for the rk808 series PMICs"); +MODULE_AUTHOR("Chris Zhong "); +MODULE_AUTHOR("Zhang Qing "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rk808-rtc"); -- cgit v1.2.2 From 33b04b7b7c03d04584be3f91ebc1eb40eb1ed33c Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Mon, 13 Oct 2014 15:52:48 -0700 Subject: rtc: ds1307: add trickle charger device tree binding Some DS13XX devices have "trickle chargers". Introduce a device tree binding for specifying the trickle charger configuration for ds1339. Only ds1339 dt binding is supported because this is the only chip I have. I _assume_ the code would have worked on other allready supported chips. However I cannot check the resistor values for the other chips or test them. For other chips the driver code works as earlier Eg. it does not check the dt bindings at all Signed-off-by: Matti Vaittinen Cc: Rob Herring Cc: Pawel Moll Cc: Jason Cooper Cc: Guenter Roeck Cc: Alessandro Zummo Cc: Mark Rutland Cc: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1307.c | 67 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index f03d5ba96db1..bb43cf703efc 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -126,9 +126,14 @@ struct chip_desc { u16 nvram_offset; u16 nvram_size; u16 trickle_charger_reg; + u8 trickle_charger_setup; + u8 (*do_trickle_setup)(struct i2c_client *, uint32_t, bool); }; -static const struct chip_desc chips[last_ds_type] = { +static u8 do_trickle_setup_ds1339(struct i2c_client *, + uint32_t ohms, bool diode); + +static struct chip_desc chips[last_ds_type] = { [ds_1307] = { .nvram_offset = 8, .nvram_size = 56, @@ -143,6 +148,7 @@ static const struct chip_desc chips[last_ds_type] = { [ds_1339] = { .alarm = 1, .trickle_charger_reg = 0x10, + .do_trickle_setup = &do_trickle_setup_ds1339, }, [ds_1340] = { .trickle_charger_reg = 0x08, @@ -833,15 +839,58 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj, return count; } + /*----------------------------------------------------------------------*/ +static u8 do_trickle_setup_ds1339(struct i2c_client *client, + uint32_t 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(&client->dev, + "Unsupported ohm value %u in dt\n", ohms); + return 0; + } + return setup; +} + +static void ds1307_trickle_of_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)) + goto out; + if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable")) + diode = false; + chip->trickle_charger_setup = chip->do_trickle_setup(client, + ohms, diode); +out: + return; +} + static int ds1307_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds1307 *ds1307; int err = -ENODEV; int tmp; - const struct chip_desc *chip = &chips[id->driver_data]; + struct chip_desc *chip = &chips[id->driver_data]; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); bool want_irq = false; unsigned char *buf; @@ -866,9 +915,19 @@ static int ds1307_probe(struct i2c_client *client, ds1307->client = client; ds1307->type = id->driver_data; - if (pdata && pdata->trickle_charger_setup && chip->trickle_charger_reg) + if (!pdata && client->dev.of_node) + ds1307_trickle_of_init(client, chip); + else if (pdata && pdata->trickle_charger_setup) + chip->trickle_charger_setup = pdata->trickle_charger_setup; + + if (chip->trickle_charger_setup && chip->trickle_charger_reg) { + dev_dbg(&client->dev, "writing trickle charger info 0x%x to 0x%x\n", + DS13XX_TRICKLE_CHARGER_MAGIC | chip->trickle_charger_setup, + chip->trickle_charger_reg); i2c_smbus_write_byte_data(client, chip->trickle_charger_reg, - DS13XX_TRICKLE_CHARGER_MAGIC | pdata->trickle_charger_setup); + DS13XX_TRICKLE_CHARGER_MAGIC | + chip->trickle_charger_setup); + } buf = ds1307->regs; if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { -- cgit v1.2.2 From 765a98a6b9e71702fd8e3822b654f9041a206b47 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Mon, 13 Oct 2014 15:52:50 -0700 Subject: rtc: bq32000: add trickle charger option, with device tree binding BQ32000 devices have "trickle chargers". Introduce a code to enable the charger, based on device tree. Without charger, RTC does not keep time after power off. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Pavel Machek Cc: Jason Cooper Cc: Matti Vaittinen Cc: Rob Herring Cc: Ian Campbell Cc: Alessandro Zummo Cc: Guenter Roeck Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-bq32k.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index c74bf0dc52cc..314129e66d6e 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -2,10 +2,14 @@ * Driver for TI BQ32000 RTC. * * Copyright (C) 2009 Semihalf. + * Copyright (C) 2014 Pavel Machek * * 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. + * + * You can get hardware description at + * http://www.ti.com/lit/ds/symlink/bq32000.pdf */ #include @@ -27,6 +31,10 @@ #define BQ32K_CENT 0x40 /* Century flag */ #define BQ32K_CENT_EN 0x80 /* Century flag enable bit */ +#define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */ +#define BQ32K_TCH2 0x08 /* Trickle charge enable */ +#define BQ32K_CFG2 0x09 /* Trickle charger control */ + struct bq32k_regs { uint8_t seconds; uint8_t minutes; @@ -122,6 +130,57 @@ static const struct rtc_class_ops bq32k_rtc_ops = { .set_time = bq32k_rtc_set_time, }; +static int trickle_charger_of_init(struct device *dev, struct device_node *node) +{ + unsigned char reg; + int error; + u32 ohms = 0; + + if (of_property_read_u32(node, "trickle-resistor-ohms" , &ohms)) + return 0; + + switch (ohms) { + case 180+940: + /* + * TCHE[3:0] == 0x05, TCH2 == 1, TCFE == 0 (charging + * over diode and 940ohm resistor) + */ + + if (of_property_read_bool(node, "trickle-diode-disable")) { + dev_err(dev, "diode and resistor mismatch\n"); + return -EINVAL; + } + reg = 0x05; + break; + + case 180+20000: + /* diode disabled */ + + if (!of_property_read_bool(node, "trickle-diode-disable")) { + dev_err(dev, "bq32k: diode and resistor mismatch\n"); + return -EINVAL; + } + reg = 0x25; + break; + + default: + dev_err(dev, "invalid resistor value (%d)\n", ohms); + return -EINVAL; + } + + error = bq32k_write(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + reg = 0x20; + error = bq32k_write(dev, ®, BQ32K_TCH2, 1); + if (error) + return error; + + dev_info(dev, "Enabled trickle RTC battery charge.\n"); + return 0; +} + static int bq32k_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -153,6 +212,9 @@ static int bq32k_probe(struct i2c_client *client, if (error) return error; + if (client && client->dev.of_node) + trickle_charger_of_init(dev, client->dev.of_node); + rtc = devm_rtc_device_register(&client->dev, bq32k_driver.driver.name, &bq32k_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) -- cgit v1.2.2 From e7f7fc73693e0a9de693f261d63aa681f7979c33 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 13 Oct 2014 15:52:55 -0700 Subject: rtc: max77686: Allow the max77686 rtc to wakeup the system This series add support for the Real Time clock present in the Maxim 77802 Power Managment IC. The version number is quite high because it previously was part of a bigger series [0] that aimed to add support for all the devices in the max77802 PMIC. But now that the max77802 dependencies were already merged for 3.17, the series were split but I kept the version numbering. While working on the max77802 rtc support a lot of feedback was given and the issues pointed out also apply to a driver for a similar PMIC RTC (max77686). So patches 01/06 to 05/06 in the series are cleanups for the max77686 driver and patch 06/06 adds the support for the max77802 RTC. The series were tested on an Exynos5250 Snow (max77686) and Exynos5420 Peach Pit (max77802) machines. This patch (of 6): The max77686 includes an RTC that keeps power during suspend. It's convenient to be able to use it as a wakeup source. Signed-off-by: Doug Anderson Signed-off-by: Javier Martinez Canillas Reviewed-by: Krzysztof Kozlowski Cc: Alessandro Zummo Cc: Olof Johansson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max77686.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index d20a7f0786eb..c1c6055ccb71 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -583,6 +583,33 @@ static void max77686_rtc_shutdown(struct platform_device *pdev) #endif /* MAX77686_RTC_WTSR_SMPL */ } +#ifdef CONFIG_PM_SLEEP +static int max77686_rtc_suspend(struct device *dev) +{ + if (device_may_wakeup(dev)) { + struct max77686_rtc_info *info = dev_get_drvdata(dev); + + return enable_irq_wake(info->virq); + } + + return 0; +} + +static int max77686_rtc_resume(struct device *dev) +{ + if (device_may_wakeup(dev)) { + struct max77686_rtc_info *info = dev_get_drvdata(dev); + + return disable_irq_wake(info->virq); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops, + max77686_rtc_suspend, max77686_rtc_resume); + static const struct platform_device_id rtc_id[] = { { "max77686-rtc", 0 }, {}, @@ -592,6 +619,7 @@ static struct platform_driver max77686_rtc_driver = { .driver = { .name = "max77686-rtc", .owner = THIS_MODULE, + .pm = &max77686_rtc_pm_ops, }, .probe = max77686_rtc_probe, .shutdown = max77686_rtc_shutdown, -- cgit v1.2.2 From 6b50fac5ddb9748487aab3c5f8d323da01a6648f Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 13 Oct 2014 15:52:57 -0700 Subject: rtc: max77686: remove dead code for SMPL and WTSR The MAX77686 RTC chip has two features called SMPL (Sudden Momentary Power Loss) and WTSR (Watchdog Timeout and Software Resets). Support for these features seems to be implemented in the driver but compilation is disabled using a C pre-processor conditional. This code has been disabled since the driver was original merged in commit fca1dd031a28 ("rtc: max77686: add Maxim 77686 driver"). So, since this code has never been built, let's just remove it. Signed-off-by: Javier Martinez Canillas Reviewed-by: Krzysztof Kozlowski Cc: Doug Anderson Cc: Alessandro Zummo Cc: Olof Johansson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max77686.c | 101 --------------------------------------------- 1 file changed, 101 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index c1c6055ccb71..7bb543344369 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -32,15 +32,6 @@ #define RTC_UDR_MASK (1 << RTC_UDR_SHIFT) #define RTC_RBUDR_SHIFT 4 #define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT) -/* WTSR and SMPL Register */ -#define WTSRT_SHIFT 0 -#define SMPLT_SHIFT 2 -#define WTSR_EN_SHIFT 6 -#define SMPL_EN_SHIFT 7 -#define WTSRT_MASK (3 << WTSRT_SHIFT) -#define SMPLT_MASK (3 << SMPLT_SHIFT) -#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT) -#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT) /* RTC Hour register */ #define HOUR_PM_SHIFT 6 #define HOUR_PM_MASK (1 << HOUR_PM_SHIFT) @@ -49,7 +40,6 @@ #define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT) #define MAX77686_RTC_UPDATE_DELAY 16 -#undef MAX77686_RTC_WTSR_SMPL enum { RTC_SEC = 0, @@ -412,64 +402,6 @@ static const struct rtc_class_ops max77686_rtc_ops = { .alarm_irq_enable = max77686_rtc_alarm_irq_enable, }; -#ifdef MAX77686_RTC_WTSR_SMPL -static void max77686_rtc_enable_wtsr(struct max77686_rtc_info *info, bool enable) -{ - int ret; - unsigned int val, mask; - - if (enable) - val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT); - else - val = 0; - - mask = WTSR_EN_MASK | WTSRT_MASK; - - dev_info(info->dev, "%s: %s WTSR\n", __func__, - enable ? "enable" : "disable"); - - ret = regmap_update_bits(info->max77686->rtc_regmap, - MAX77686_WTSR_SMPL_CNTL, mask, val); - if (ret < 0) { - dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n", - __func__, ret); - return; - } - - max77686_rtc_update(info, MAX77686_RTC_WRITE); -} - -static void max77686_rtc_enable_smpl(struct max77686_rtc_info *info, bool enable) -{ - int ret; - unsigned int val, mask; - - if (enable) - val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT); - else - val = 0; - - mask = SMPL_EN_MASK | SMPLT_MASK; - - dev_info(info->dev, "%s: %s SMPL\n", __func__, - enable ? "enable" : "disable"); - - ret = regmap_update_bits(info->max77686->rtc_regmap, - MAX77686_WTSR_SMPL_CNTL, mask, val); - if (ret < 0) { - dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n", - __func__, ret); - return; - } - - max77686_rtc_update(info, MAX77686_RTC_WRITE); - - val = 0; - regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val); - dev_info(info->dev, "%s: WTSR_SMPL(0x%02x)\n", __func__, val); -} -#endif /* MAX77686_RTC_WTSR_SMPL */ - static int max77686_rtc_init_reg(struct max77686_rtc_info *info) { u8 data[2]; @@ -519,11 +451,6 @@ static int max77686_rtc_probe(struct platform_device *pdev) goto err_rtc; } -#ifdef MAX77686_RTC_WTSR_SMPL - max77686_rtc_enable_wtsr(info, true); - max77686_rtc_enable_smpl(info, true); -#endif - device_init_wakeup(&pdev->dev, 1); info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77686-rtc", @@ -556,33 +483,6 @@ err_rtc: return ret; } -static void max77686_rtc_shutdown(struct platform_device *pdev) -{ -#ifdef MAX77686_RTC_WTSR_SMPL - struct max77686_rtc_info *info = platform_get_drvdata(pdev); - int i; - u8 val = 0; - - for (i = 0; i < 3; i++) { - max77686_rtc_enable_wtsr(info, false); - regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val); - dev_info(info->dev, "%s: WTSR_SMPL reg(0x%02x)\n", __func__, - val); - if (val & WTSR_EN_MASK) { - dev_emerg(info->dev, "%s: fail to disable WTSR\n", - __func__); - } else { - dev_info(info->dev, "%s: success to disable WTSR\n", - __func__); - break; - } - } - - /* Disable SMPL when power off */ - max77686_rtc_enable_smpl(info, false); -#endif /* MAX77686_RTC_WTSR_SMPL */ -} - #ifdef CONFIG_PM_SLEEP static int max77686_rtc_suspend(struct device *dev) { @@ -622,7 +522,6 @@ static struct platform_driver max77686_rtc_driver = { .pm = &max77686_rtc_pm_ops, }, .probe = max77686_rtc_probe, - .shutdown = max77686_rtc_shutdown, .id_table = rtc_id, }; -- cgit v1.2.2 From 1745d6d3bc181800ba2e0930ab15432b3e2755ff Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 13 Oct 2014 15:52:59 -0700 Subject: rtc: max77686: fail to probe if no RTC regmap irqchip is set The max77686 mfd driver adds a regmap IRQ chip which creates an IRQ domain that is used to map the virtual RTC alarm1 interrupt. The RTC driver assumes that this will always be true since the PMIC IRQ is a required property according to the max77686 DT binding doc. If an "interrupts" property is not defined for a max77686 PMIC, then the mfd probe function will fail and the RTC platform driver will never be probed. But even when it is not possible to probe the rtc-max77686 driver without a regmap IRQ chip, it's better to explicitly check if the IRQ chip data is not NULL and gracefully fail instead of getting an OOPS. Signed-off-by: Javier Martinez Canillas Reported-by: Krzysztof Kozlowski Cc: Doug Anderson Cc: Alessandro Zummo Cc: Olof Johansson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max77686.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 7bb543344369..55396bb3de21 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -466,6 +466,12 @@ static int max77686_rtc_probe(struct platform_device *pdev) goto err_rtc; } + if (!max77686->rtc_irq_data) { + ret = -EINVAL; + dev_err(&pdev->dev, "%s: no RTC regmap IRQ chip\n", __func__); + goto err_rtc; + } + info->virq = regmap_irq_get_virq(max77686->rtc_irq_data, MAX77686_RTCIRQ_RTCA1); if (!info->virq) { -- cgit v1.2.2 From ea33c31b621e296356ab74ea0237ba7761f2287e Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 13 Oct 2014 15:53:01 -0700 Subject: rtc: max77686: remove unneeded info log If devm_rtc_device_register() fails a dev_err() is already reported so there is no need to do an additional dev_info(). Signed-off-by: Javier Martinez Canillas Cc: Doug Anderson Cc: Krzysztof Kozlowski Cc: Alessandro Zummo Cc: Olof Johansson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max77686.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 55396bb3de21..b177ba199a3d 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -457,8 +457,6 @@ static int max77686_rtc_probe(struct platform_device *pdev) &max77686_rtc_ops, THIS_MODULE); if (IS_ERR(info->rtc_dev)) { - dev_info(&pdev->dev, "%s: fail\n", __func__); - ret = PTR_ERR(info->rtc_dev); dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); if (ret == 0) -- cgit v1.2.2 From a20cd88e20e59ce11ebca84fac769654193c51e0 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 13 Oct 2014 15:53:03 -0700 Subject: rtc: max77686: Use ffs() to calculate tm_wday max77686_rtc_calculate_wday() is used to calculate the day of the week to be filled in struct rtc_time but that function only calculates the number of bits shifted. So the ffs() function can be used to find the first bit set instead of a special function. [akpm@linux-foundation.org: add comment clarifying ffs() use] Signed-off-by: Javier Martinez Canillas Cc: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max77686.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index b177ba199a3d..cf73e969c8cc 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -70,16 +70,6 @@ enum MAX77686_RTC_OP { MAX77686_RTC_READ, }; -static inline int max77686_rtc_calculate_wday(u8 shifted) -{ - int counter = -1; - while (shifted) { - shifted >>= 1; - counter++; - } - return counter; -} - static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, int rtc_24hr_mode) { @@ -93,7 +83,8 @@ static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, tm->tm_hour += 12; } - tm->tm_wday = max77686_rtc_calculate_wday(data[RTC_WEEKDAY] & 0x7f); + /* Only a single bit is set in data[], so fls() would be equivalent */ + tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f) - 1; tm->tm_mday = data[RTC_DATE] & 0x1f; tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100; -- cgit v1.2.2 From a4d4121ba753737c89e42a8df22e4859069fcf25 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 13 Oct 2014 15:53:05 -0700 Subject: rtc: add driver for Maxim 77802 PMIC Real-Time-Clock The MAX7802 PMIC has a Real-Time-Clock (RTC) with two alarms. This patch adds support for the RTC and is based on a driver added by Simon Glass to the Chrome OS kernel 3.8 tree. [akpm@linux-foundation.org: add comment clarifying ffs() use] Signed-off-by: Javier Martinez Canillas Reviewed-by: Krzysztof Kozlowski Cc: Doug Anderson Cc: Alessandro Zummo Cc: Olof Johansson Cc: Simon Glass Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-max77802.c | 502 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 513 insertions(+) create mode 100644 drivers/rtc/rtc-max77802.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 943afaf8f448..d59c365788a1 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -298,6 +298,16 @@ config RTC_DRV_RK808 This driver can also be built as a module. If so, the module will be called rk808-rtc. +config RTC_DRV_MAX77802 + tristate "Maxim 77802 RTC" + depends on MFD_MAX77686 + help + If you say yes here you will get support for the + RTC of Maxim MAX77802 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-max77802. + config RTC_DRV_RS5C372 tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 7dec7fd90f90..b188323c096a 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o +obj-$(CONFIG_RTC_DRV_MAX77802) += rtc-max77802.o obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o diff --git a/drivers/rtc/rtc-max77802.c b/drivers/rtc/rtc-max77802.c new file mode 100644 index 000000000000..566471335b33 --- /dev/null +++ b/drivers/rtc/rtc-max77802.c @@ -0,0 +1,502 @@ +/* + * RTC driver for Maxim MAX77802 + * + * Copyright (C) 2013 Google, Inc + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * + * based on rtc-max8997.c + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* RTC Control Register */ +#define BCD_EN_SHIFT 0 +#define BCD_EN_MASK (1 << BCD_EN_SHIFT) +#define MODEL24_SHIFT 1 +#define MODEL24_MASK (1 << MODEL24_SHIFT) +/* RTC Update Register1 */ +#define RTC_UDR_SHIFT 0 +#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT) +#define RTC_RBUDR_SHIFT 4 +#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT) +/* RTC Hour register */ +#define HOUR_PM_SHIFT 6 +#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT) +/* RTC Alarm Enable */ +#define ALARM_ENABLE_SHIFT 7 +#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT) + +/* For the RTCAE1 register, we write this value to enable the alarm */ +#define ALARM_ENABLE_VALUE 0x77 + +#define MAX77802_RTC_UPDATE_DELAY_US 200 + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_MONTH, + RTC_YEAR, + RTC_DATE, + RTC_NR_TIME +}; + +struct max77802_rtc_info { + struct device *dev; + struct max77686_dev *max77802; + struct i2c_client *rtc; + struct rtc_device *rtc_dev; + struct mutex lock; + + struct regmap *regmap; + + int virq; + int rtc_24hr_mode; +}; + +enum MAX77802_RTC_OP { + MAX77802_RTC_WRITE, + MAX77802_RTC_READ, +}; + +static void max77802_rtc_data_to_tm(u8 *data, struct rtc_time *tm, + int rtc_24hr_mode) +{ + tm->tm_sec = data[RTC_SEC] & 0xff; + tm->tm_min = data[RTC_MIN] & 0xff; + if (rtc_24hr_mode) + tm->tm_hour = data[RTC_HOUR] & 0x1f; + else { + tm->tm_hour = data[RTC_HOUR] & 0x0f; + if (data[RTC_HOUR] & HOUR_PM_MASK) + tm->tm_hour += 12; + } + + /* Only a single bit is set in data[], so fls() would be equivalent */ + tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0xff) - 1; + tm->tm_mday = data[RTC_DATE] & 0x1f; + tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; + + tm->tm_year = data[RTC_YEAR] & 0xff; + tm->tm_yday = 0; + tm->tm_isdst = 0; +} + +static int max77802_rtc_tm_to_data(struct rtc_time *tm, u8 *data) +{ + data[RTC_SEC] = tm->tm_sec; + data[RTC_MIN] = tm->tm_min; + data[RTC_HOUR] = tm->tm_hour; + data[RTC_WEEKDAY] = 1 << tm->tm_wday; + data[RTC_DATE] = tm->tm_mday; + data[RTC_MONTH] = tm->tm_mon + 1; + data[RTC_YEAR] = tm->tm_year; + + return 0; +} + +static int max77802_rtc_update(struct max77802_rtc_info *info, + enum MAX77802_RTC_OP op) +{ + int ret; + unsigned int data; + + if (op == MAX77802_RTC_WRITE) + data = 1 << RTC_UDR_SHIFT; + else + data = 1 << RTC_RBUDR_SHIFT; + + ret = regmap_update_bits(info->max77802->regmap, + MAX77802_RTC_UPDATE0, data, data); + if (ret < 0) + dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n", + __func__, ret, data); + else { + /* Minimum delay required before RTC update. */ + usleep_range(MAX77802_RTC_UPDATE_DELAY_US, + MAX77802_RTC_UPDATE_DELAY_US * 2); + } + + return ret; +} + +static int max77802_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + mutex_lock(&info->lock); + + ret = max77802_rtc_update(info, MAX77802_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->max77802->regmap, + MAX77802_RTC_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__, + ret); + goto out; + } + + max77802_rtc_data_to_tm(data, tm, info->rtc_24hr_mode); + + ret = rtc_valid_tm(tm); + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77802_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max77802_rtc_tm_to_data(tm, data); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = regmap_bulk_write(info->max77802->regmap, + MAX77802_RTC_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__, + ret); + goto out; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77802_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + unsigned int val; + int ret; + + mutex_lock(&info->lock); + + ret = max77802_rtc_update(info, MAX77802_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->max77802->regmap, + MAX77802_ALARM1_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n", + __func__, __LINE__, ret); + goto out; + } + + max77802_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); + + alrm->enabled = 0; + ret = regmap_read(info->max77802->regmap, + MAX77802_RTC_AE1, &val); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read alarm enable(%d)\n", + __func__, __LINE__, ret); + goto out; + } + if (val) + alrm->enabled = 1; + + alrm->pending = 0; + ret = regmap_read(info->max77802->regmap, MAX77802_REG_STATUS2, &val); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n", + __func__, __LINE__, ret); + goto out; + } + + if (val & (1 << 2)) /* RTCA1 */ + alrm->pending = 1; + +out: + mutex_unlock(&info->lock); + return 0; +} + +static int max77802_rtc_stop_alarm(struct max77802_rtc_info *info) +{ + int ret; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = max77802_rtc_update(info, MAX77802_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_write(info->max77802->regmap, + MAX77802_RTC_AE1, 0); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); +out: + return ret; +} + +static int max77802_rtc_start_alarm(struct max77802_rtc_info *info) +{ + int ret; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", + __func__); + + ret = max77802_rtc_update(info, MAX77802_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_write(info->max77802->regmap, + MAX77802_RTC_AE1, + ALARM_ENABLE_VALUE); + + if (ret < 0) { + dev_err(info->dev, "%s: fail to read alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); +out: + return ret; +} + +static int max77802_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max77802_rtc_tm_to_data(&alrm->time, data); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = max77802_rtc_stop_alarm(info); + if (ret < 0) + goto out; + + ret = regmap_bulk_write(info->max77802->regmap, + MAX77802_ALARM1_SEC, data, RTC_NR_TIME); + + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); + if (ret < 0) + goto out; + + if (alrm->enabled) + ret = max77802_rtc_start_alarm(info); +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77802_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + int ret; + + mutex_lock(&info->lock); + if (enabled) + ret = max77802_rtc_start_alarm(info); + else + ret = max77802_rtc_stop_alarm(info); + mutex_unlock(&info->lock); + + return ret; +} + +static irqreturn_t max77802_rtc_alarm_irq(int irq, void *data) +{ + struct max77802_rtc_info *info = data; + + dev_dbg(info->dev, "%s:irq(%d)\n", __func__, irq); + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops max77802_rtc_ops = { + .read_time = max77802_rtc_read_time, + .set_time = max77802_rtc_set_time, + .read_alarm = max77802_rtc_read_alarm, + .set_alarm = max77802_rtc_set_alarm, + .alarm_irq_enable = max77802_rtc_alarm_irq_enable, +}; + +static int max77802_rtc_init_reg(struct max77802_rtc_info *info) +{ + u8 data[2]; + int ret; + + max77802_rtc_update(info, MAX77802_RTC_READ); + + /* Set RTC control register : Binary mode, 24hour mdoe */ + data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + + info->rtc_24hr_mode = 1; + + ret = regmap_bulk_write(info->max77802->regmap, + MAX77802_RTC_CONTROLM, data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write controlm reg(%d)\n", + __func__, ret); + return ret; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); + return ret; +} + +static int max77802_rtc_probe(struct platform_device *pdev) +{ + struct max77686_dev *max77802 = dev_get_drvdata(pdev->dev.parent); + struct max77802_rtc_info *info; + int ret; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + info = devm_kzalloc(&pdev->dev, sizeof(struct max77802_rtc_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + mutex_init(&info->lock); + info->dev = &pdev->dev; + info->max77802 = max77802; + info->rtc = max77802->i2c; + + platform_set_drvdata(pdev, info); + + ret = max77802_rtc_init_reg(info); + + if (ret < 0) { + dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret); + return ret; + } + + device_init_wakeup(&pdev->dev, 1); + + info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77802-rtc", + &max77802_rtc_ops, THIS_MODULE); + + if (IS_ERR(info->rtc_dev)) { + ret = PTR_ERR(info->rtc_dev); + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + if (ret == 0) + ret = -EINVAL; + return ret; + } + + if (!max77802->rtc_irq_data) { + dev_err(&pdev->dev, "No RTC regmap IRQ chip\n"); + return -EINVAL; + } + + info->virq = regmap_irq_get_virq(max77802->rtc_irq_data, + MAX77686_RTCIRQ_RTCA1); + + if (info->virq <= 0) { + dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n", + MAX77686_RTCIRQ_RTCA1); + return -EINVAL; + } + + ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL, + max77802_rtc_alarm_irq, 0, "rtc-alarm1", + info); + if (ret < 0) + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + info->virq, ret); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int max77802_rtc_suspend(struct device *dev) +{ + if (device_may_wakeup(dev)) { + struct max77802_rtc_info *info = dev_get_drvdata(dev); + + return enable_irq_wake(info->virq); + } + + return 0; +} + +static int max77802_rtc_resume(struct device *dev) +{ + if (device_may_wakeup(dev)) { + struct max77802_rtc_info *info = dev_get_drvdata(dev); + + return disable_irq_wake(info->virq); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max77802_rtc_pm_ops, + max77802_rtc_suspend, max77802_rtc_resume); + +static const struct platform_device_id rtc_id[] = { + { "max77802-rtc", 0 }, + {}, +}; + +static struct platform_driver max77802_rtc_driver = { + .driver = { + .name = "max77802-rtc", + .owner = THIS_MODULE, + .pm = &max77802_rtc_pm_ops, + }, + .probe = max77802_rtc_probe, + .id_table = rtc_id, +}; + +module_platform_driver(max77802_rtc_driver); + +MODULE_DESCRIPTION("Maxim MAX77802 RTC driver"); +MODULE_AUTHOR("Simon Glass "); +MODULE_LICENSE("GPL"); -- cgit v1.2.2 From e698a51239f26c370247d759da9ea016f5841fc3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 13 Oct 2014 15:53:07 -0700 Subject: drivers/rtc/rtc-pcf8563.c: fix uninitialized use warning gcc-4.9 found a potential condition under which the 'pending' variable may be used uninitialized: drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_irq': drivers/rtc/rtc-pcf8563.c:173:5: warning: 'pending' may be used uninitialized in this function [-Wmaybe-uninitialized] This is because in the pcf8563_get_alarm_mode() function, we check any nonzero return of pcf8563_read_block_data, but in the irq function we only check for negative values, so a possible positive value does not get detected if the compiler chooses not to inline the entire call chain. Checking for any non-zero value in the interrupt handler as well is just as correct and lets the compiler know what we are doing, without needing a bogus initialization. Signed-off-by: Arnd Bergmann Cc: Sergei Shtylyov Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8563.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 5a197d9dc7e7..3a6f994c4da8 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -167,7 +167,7 @@ static irqreturn_t pcf8563_irq(int irq, void *dev_id) char pending; err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending); - if (err < 0) + if (err) return err; if (pending) { -- cgit v1.2.2 From 3ff38237f183ecd8a190318e0046138b92ee5e35 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 13 Oct 2014 15:53:10 -0700 Subject: drivers/rtc/rtc-pcf8563.c: fix pcf8563_irq() error return value As pointed out by Sergei Shtylyov, the pcf8563_irq function contains a bug in the error handling: an interrupt handler is not supposed to return an errno value but an 'enum irqreturn'. Let's fix this by returning IRQ_NONE in case of a communication error. Signed-off-by: Arnd Bergmann Cc: Sergei Shtylyov Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8563.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 3a6f994c4da8..c2ef0a22ee94 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -168,7 +168,7 @@ static irqreturn_t pcf8563_irq(int irq, void *dev_id) err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending); if (err) - return err; + return IRQ_NONE; if (pending) { rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF); -- cgit v1.2.2 From b513e522cb8bda32560fb6b7f0475aa56e486ab9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 13 Oct 2014 15:53:12 -0700 Subject: drivers/rtc/rtc-rs5c372.c: use %*ph to dump small buffers Instead of pushing each byte let's reduce stack usage by using %*ph specifier. Signed-off-by: Andy Shevchenko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rs5c372.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index ccf54f06396b..28871cd7e3b5 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -142,12 +142,11 @@ static int rs5c_get_regs(struct rs5c372 *rs5c) } dev_dbg(&client->dev, - "%02x %02x %02x (%02x) %02x %02x %02x (%02x), " - "%02x %02x %02x, %02x %02x %02x; %02x %02x\n", - rs5c->regs[0], rs5c->regs[1], rs5c->regs[2], rs5c->regs[3], - rs5c->regs[4], rs5c->regs[5], rs5c->regs[6], rs5c->regs[7], - rs5c->regs[8], rs5c->regs[9], rs5c->regs[10], rs5c->regs[11], - rs5c->regs[12], rs5c->regs[13], rs5c->regs[14], rs5c->regs[15]); + "%3ph (%02x) %3ph (%02x), %3ph, %3ph; %02x %02x\n", + rs5c->regs + 0, rs5c->regs[3], + rs5c->regs + 4, rs5c->regs[7], + rs5c->regs + 8, rs5c->regs + 11, + rs5c->regs[14], rs5c->regs[15]); return 0; } -- cgit v1.2.2 From 706b632d042c87a288f8b9adc8b6c83207e6d452 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 13 Oct 2014 15:53:14 -0700 Subject: drivers/rtc/Kconfig: Let several drivers depend on HAS_IOMEM to avoid compiling issue Some drivers need 'devm_ioremap_resource' or 'devm_ioremap' which need HAS_IOMEM, so let them depend on it. The related error (with allmodconfig under score): MODPOST 1365 modules ERROR: "devm_ioremap_resource" [drivers/rtc/rtc-xgene.ko] undefined! ERROR: "devm_ioremap_resource" [drivers/rtc/rtc-stk17ta8.ko] undefined! ERROR: "devm_ioremap_resource" [drivers/rtc/rtc-ds1742.ko] undefined! ERROR: "devm_ioremap_resource" [drivers/rtc/rtc-ds1553.ko] undefined! ERROR: "devm_ioremap_resource" [drivers/rtc/rtc-ds1511.ko] undefined! ERROR: "devm_ioremap_resource" [drivers/rtc/rtc-ds1286.ko] undefined! ERROR: "devm_ioremap" [drivers/rtc/rtc-rp5c01.ko] undefined! ERROR: "devm_ioremap" [drivers/rtc/rtc-msm6242.ko] undefined! ERROR: "devm_ioremap" [drivers/rtc/rtc-m48t59.ko] undefined! ERROR: "devm_ioremap" [drivers/rtc/rtc-m48t35.ko] undefined! ERROR: "devm_ioremap" [drivers/rtc/rtc-bq4802.ko] undefined! Signed-off-by: Chen Gang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index d59c365788a1..8cd0beebdc3f 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -752,6 +752,7 @@ config RTC_DRV_DS1216 config RTC_DRV_DS1286 tristate "Dallas DS1286" + depends on HAS_IOMEM help If you say yes here you get support for the Dallas DS1286 RTC chips. @@ -763,6 +764,7 @@ config RTC_DRV_DS1302 config RTC_DRV_DS1511 tristate "Dallas DS1511" + depends on HAS_IOMEM help If you say yes here you get support for the Dallas DS1511 timekeeping/watchdog chip. @@ -772,6 +774,7 @@ config RTC_DRV_DS1511 config RTC_DRV_DS1553 tristate "Maxim/Dallas DS1553" + depends on HAS_IOMEM help If you say yes here you get support for the Maxim/Dallas DS1553 timekeeping chip. @@ -781,6 +784,7 @@ config RTC_DRV_DS1553 config RTC_DRV_DS1742 tristate "Maxim/Dallas DS1742/1743" + depends on HAS_IOMEM help If you say yes here you get support for the Maxim/Dallas DS1742/1743 timekeeping chip. @@ -836,6 +840,7 @@ config RTC_DRV_EFI config RTC_DRV_STK17TA8 tristate "Simtek STK17TA8" + depends on HAS_IOMEM help If you say yes here you get support for the Simtek STK17TA8 timekeeping chip. @@ -854,6 +859,7 @@ config RTC_DRV_M48T86 config RTC_DRV_M48T35 tristate "ST M48T35" + depends on HAS_IOMEM help If you say Y here you will get support for the ST M48T35 RTC chip. @@ -863,6 +869,7 @@ config RTC_DRV_M48T35 config RTC_DRV_M48T59 tristate "ST M48T59/M48T08/M48T02" + depends on HAS_IOMEM help If you say Y here you will get support for the ST M48T59 RTC chip and compatible ST M48T08 and M48T02. @@ -875,6 +882,7 @@ config RTC_DRV_M48T59 config RTC_DRV_MSM6242 tristate "Oki MSM6242" + depends on HAS_IOMEM help If you say yes here you get support for the Oki MSM6242 timekeeping chip. It is used in some Amiga models (e.g. A2000). @@ -884,6 +892,7 @@ config RTC_DRV_MSM6242 config RTC_DRV_BQ4802 tristate "TI BQ4802" + depends on HAS_IOMEM help If you say Y here you will get support for the TI BQ4802 RTC chip. @@ -893,6 +902,7 @@ config RTC_DRV_BQ4802 config RTC_DRV_RP5C01 tristate "Ricoh RP5C01" + depends on HAS_IOMEM help If you say yes here you get support for the Ricoh RP5C01 timekeeping chip. It is used in some Amiga models (e.g. A3000 @@ -1394,6 +1404,7 @@ config RTC_DRV_MOXART config RTC_DRV_XGENE tristate "APM X-Gene RTC" + depends on HAS_IOMEM help If you say yes here you get support for the APM X-Gene SoC real time clock. -- cgit v1.2.2 From a882b14fe84951e236cd074e93575adc8a4be32e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gl=C3=B6ckner?= Date: Mon, 13 Oct 2014 15:53:16 -0700 Subject: rtc-cmos: fix wakeup from S5 without CONFIG_PM_SLEEP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b5ada4600dfd ("drivers/rtc/rtc-cmos.c: fix compilation warning when !CONFIG_PM_SLEEP") broke wakeup from S5 by making cmos_poweroff a nop unless CONFIG_PM_SLEEP was defined. Fix this by restricting the #ifdef to cmos_resume and restoring the old dependency on CONFIG_PM for cmos_suspend and cmos_poweroff. Signed-off-by: Daniel Glöckner Cc: Mika Westerberg Cc: Alessandro Zummo Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-cmos.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index b0e4a3eb33c7..5b2e76159b41 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -856,7 +856,7 @@ static void __exit cmos_do_remove(struct device *dev) cmos->dev = NULL; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM static int cmos_suspend(struct device *dev) { @@ -907,6 +907,8 @@ static inline int cmos_poweroff(struct device *dev) return cmos_suspend(dev); } +#ifdef CONFIG_PM_SLEEP + static int cmos_resume(struct device *dev) { struct cmos_rtc *cmos = dev_get_drvdata(dev); @@ -954,6 +956,7 @@ static int cmos_resume(struct device *dev) return 0; } +#endif #else static inline int cmos_poweroff(struct device *dev) -- cgit v1.2.2 From c8d523a4b053d1678adb92976b5ef84d9bc481e8 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 29 Oct 2014 14:50:33 -0700 Subject: drivers/rtc/rtc-pm8xxx.c: rework to support pm8941 rtc Adds support for RTC device inside PM8941 PMIC. The RTC in this PMIC have two register spaces. Thus the rtc-pm8xxx is slightly reworked to reflect these differences. The register set for different PMIC chips are selected on DT compatible string base. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: simplify and fix locking in pm8xxx_rtc_set_time()] Signed-off-by: Stanimir Varbanov Cc: Alessandro Zummo Cc: Stephen Boyd Cc: Josh Cartwright Cc: Stanimir Varbanov Cc: Dan Carpenter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 2 +- drivers/rtc/rtc-pm8xxx.c | 222 ++++++++++++++++++++++++++++------------------- 2 files changed, 135 insertions(+), 89 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 94ae1798d48a..6dd12ddbabc6 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1320,7 +1320,7 @@ config RTC_DRV_LPC32XX config RTC_DRV_PM8XXX tristate "Qualcomm PMIC8XXX RTC" - depends on MFD_PM8XXX + depends on MFD_PM8XXX || MFD_SPMI_PMIC help If you say yes here you get support for the Qualcomm PMIC8XXX RTC. diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index 197699f358c7..5adcf111fc14 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -27,21 +27,36 @@ /* RTC_CTRL register bit fields */ #define PM8xxx_RTC_ENABLE BIT(7) -#define PM8xxx_RTC_ALARM_ENABLE BIT(1) #define PM8xxx_RTC_ALARM_CLEAR BIT(0) #define NUM_8_BIT_RTC_REGS 0x4 +/** + * struct pm8xxx_rtc_regs - describe RTC registers per PMIC versions + * @ctrl: base address of control register + * @write: base address of write register + * @read: base address of read register + * @alarm_ctrl: base address of alarm control register + * @alarm_ctrl2: base address of alarm control2 register + * @alarm_rw: base address of alarm read-write register + * @alarm_en: alarm enable mask + */ +struct pm8xxx_rtc_regs { + unsigned int ctrl; + unsigned int write; + unsigned int read; + unsigned int alarm_ctrl; + unsigned int alarm_ctrl2; + unsigned int alarm_rw; + unsigned int alarm_en; +}; + /** * struct pm8xxx_rtc - rtc driver internal structure * @rtc: rtc device for this driver. * @regmap: regmap used to access RTC registers * @allow_set_time: indicates whether writing to the RTC is allowed * @rtc_alarm_irq: rtc alarm irq number. - * @rtc_base: address of rtc control register. - * @rtc_read_base: base address of read registers. - * @rtc_write_base: base address of write registers. - * @alarm_rw_base: base address of alarm registers. * @ctrl_reg: rtc control register. * @rtc_dev: device structure. * @ctrl_reg_lock: spinlock protecting access to ctrl_reg. @@ -51,11 +66,7 @@ struct pm8xxx_rtc { struct regmap *regmap; bool allow_set_time; int rtc_alarm_irq; - int rtc_base; - int rtc_read_base; - int rtc_write_base; - int alarm_rw_base; - u8 ctrl_reg; + const struct pm8xxx_rtc_regs *regs; struct device *rtc_dev; spinlock_t ctrl_reg_lock; }; @@ -71,8 +82,10 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) { int rc, i; unsigned long secs, irq_flags; - u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0, ctrl_reg; + u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0; + unsigned int ctrl_reg; struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; if (!rtc_dd->allow_set_time) return -EACCES; @@ -87,30 +100,30 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs); spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); - ctrl_reg = rtc_dd->ctrl_reg; - if (ctrl_reg & PM8xxx_RTC_ALARM_ENABLE) { + rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg); + if (rc) + goto rtc_rw_fail; + + if (ctrl_reg & regs->alarm_en) { alarm_enabled = 1; - ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; - rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + ctrl_reg &= ~regs->alarm_en; + rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg); if (rc) { dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; } - rtc_dd->ctrl_reg = ctrl_reg; - } else { - spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); } /* Write 0 to Byte[0] */ - rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_write_base, 0); + rc = regmap_write(rtc_dd->regmap, regs->write, 0); if (rc) { dev_err(dev, "Write to RTC write data register failed\n"); goto rtc_rw_fail; } /* Write Byte[1], Byte[2], Byte[3] */ - rc = regmap_bulk_write(rtc_dd->regmap, rtc_dd->rtc_write_base + 1, + rc = regmap_bulk_write(rtc_dd->regmap, regs->write + 1, &value[1], sizeof(value) - 1); if (rc) { dev_err(dev, "Write to RTC write data register failed\n"); @@ -118,25 +131,23 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) } /* Write Byte[0] */ - rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_write_base, value[0]); + rc = regmap_write(rtc_dd->regmap, regs->write, value[0]); if (rc) { dev_err(dev, "Write to RTC write data register failed\n"); goto rtc_rw_fail; } if (alarm_enabled) { - ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE; - rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + ctrl_reg |= regs->alarm_en; + rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg); if (rc) { dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; } - rtc_dd->ctrl_reg = ctrl_reg; } rtc_rw_fail: - if (alarm_enabled) - spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); + spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); return rc; } @@ -148,9 +159,9 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) unsigned long secs; unsigned int reg; struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; - rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->rtc_read_base, - value, sizeof(value)); + rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value)); if (rc) { dev_err(dev, "RTC read data register failed\n"); return rc; @@ -160,14 +171,14 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) * Read the LSB again and check if there has been a carry over. * If there is, redo the read operation. */ - rc = regmap_read(rtc_dd->regmap, rtc_dd->rtc_read_base, ®); + rc = regmap_read(rtc_dd->regmap, regs->read, ®); if (rc < 0) { dev_err(dev, "RTC read data register failed\n"); return rc; } if (unlikely(reg < value[0])) { - rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->rtc_read_base, + rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value)); if (rc) { dev_err(dev, "RTC read data register failed\n"); @@ -195,9 +206,11 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { int rc, i; - u8 value[NUM_8_BIT_RTC_REGS], ctrl_reg; + u8 value[NUM_8_BIT_RTC_REGS]; + unsigned int ctrl_reg; unsigned long secs, irq_flags; struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; rtc_tm_to_time(&alarm->time, &secs); @@ -208,28 +221,28 @@ static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); - rc = regmap_bulk_write(rtc_dd->regmap, rtc_dd->alarm_rw_base, value, + rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value, sizeof(value)); if (rc) { dev_err(dev, "Write to RTC ALARM register failed\n"); goto rtc_rw_fail; } - ctrl_reg = rtc_dd->ctrl_reg; + rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg); + if (rc) + goto rtc_rw_fail; if (alarm->enabled) - ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE; + ctrl_reg |= regs->alarm_en; else - ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; + ctrl_reg &= ~regs->alarm_en; - rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg); if (rc) { - dev_err(dev, "Write to RTC control register failed\n"); + dev_err(dev, "Write to RTC alarm control register failed\n"); goto rtc_rw_fail; } - rtc_dd->ctrl_reg = ctrl_reg; - dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n", alarm->time.tm_hour, alarm->time.tm_min, alarm->time.tm_sec, alarm->time.tm_mday, @@ -245,8 +258,9 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) u8 value[NUM_8_BIT_RTC_REGS]; unsigned long secs; struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; - rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->alarm_rw_base, value, + rc = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value, sizeof(value)); if (rc) { dev_err(dev, "RTC alarm time read failed\n"); @@ -276,25 +290,26 @@ static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) int rc; unsigned long irq_flags; struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); - u8 ctrl_reg; + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; + unsigned int ctrl_reg; spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); - ctrl_reg = rtc_dd->ctrl_reg; + rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg); + if (rc) + goto rtc_rw_fail; if (enable) - ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE; + ctrl_reg |= regs->alarm_en; else - ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; + ctrl_reg &= ~regs->alarm_en; - rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg); if (rc) { dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; } - rtc_dd->ctrl_reg = ctrl_reg; - rtc_rw_fail: spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); return rc; @@ -311,6 +326,7 @@ static const struct rtc_class_ops pm8xxx_rtc_ops = { static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) { struct pm8xxx_rtc *rtc_dd = dev_id; + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; unsigned int ctrl_reg; int rc; unsigned long irq_flags; @@ -320,48 +336,100 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); /* Clear the alarm enable bit */ - ctrl_reg = rtc_dd->ctrl_reg; - ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; + rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg); + if (rc) { + spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); + goto rtc_alarm_handled; + } + + ctrl_reg &= ~regs->alarm_en; - rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg); if (rc) { spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); dev_err(rtc_dd->rtc_dev, - "Write to RTC control register failed\n"); + "Write to alarm control register failed\n"); goto rtc_alarm_handled; } - rtc_dd->ctrl_reg = ctrl_reg; spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); /* Clear RTC alarm register */ - rc = regmap_read(rtc_dd->regmap, - rtc_dd->rtc_base + PM8XXX_ALARM_CTRL_OFFSET, - &ctrl_reg); + rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl2, &ctrl_reg); if (rc) { dev_err(rtc_dd->rtc_dev, - "RTC Alarm control register read failed\n"); + "RTC Alarm control2 register read failed\n"); goto rtc_alarm_handled; } - ctrl_reg &= ~PM8xxx_RTC_ALARM_CLEAR; - rc = regmap_write(rtc_dd->regmap, - rtc_dd->rtc_base + PM8XXX_ALARM_CTRL_OFFSET, - ctrl_reg); + ctrl_reg |= PM8xxx_RTC_ALARM_CLEAR; + rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl2, ctrl_reg); if (rc) dev_err(rtc_dd->rtc_dev, - "Write to RTC Alarm control register failed\n"); + "Write to RTC Alarm control2 register failed\n"); rtc_alarm_handled: return IRQ_HANDLED; } +static int pm8xxx_rtc_enable(struct pm8xxx_rtc *rtc_dd) +{ + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; + unsigned int ctrl_reg; + int rc; + + /* Check if the RTC is on, else turn it on */ + rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg); + if (rc) + return rc; + + if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) { + ctrl_reg |= PM8xxx_RTC_ENABLE; + rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg); + if (rc) + return rc; + } + + return 0; +} + +static const struct pm8xxx_rtc_regs pm8921_regs = { + .ctrl = 0x11d, + .write = 0x11f, + .read = 0x123, + .alarm_rw = 0x127, + .alarm_ctrl = 0x11d, + .alarm_ctrl2 = 0x11e, + .alarm_en = BIT(1), +}; + +static const struct pm8xxx_rtc_regs pm8058_regs = { + .ctrl = 0x1e8, + .write = 0x1ea, + .read = 0x1ee, + .alarm_rw = 0x1f2, + .alarm_ctrl = 0x1e8, + .alarm_ctrl2 = 0x1e9, + .alarm_en = BIT(1), +}; + +static const struct pm8xxx_rtc_regs pm8941_regs = { + .ctrl = 0x6046, + .write = 0x6040, + .read = 0x6048, + .alarm_rw = 0x6140, + .alarm_ctrl = 0x6146, + .alarm_ctrl2 = 0x6148, + .alarm_en = BIT(7), +}; + /* * Hardcoded RTC bases until IORESOURCE_REG mapping is figured out */ static const struct of_device_id pm8xxx_id_table[] = { - { .compatible = "qcom,pm8921-rtc", .data = (void *) 0x11D }, - { .compatible = "qcom,pm8058-rtc", .data = (void *) 0x1E8 }, + { .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs }, + { .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs }, + { .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs }, { }, }; MODULE_DEVICE_TABLE(of, pm8xxx_id_table); @@ -369,7 +437,6 @@ MODULE_DEVICE_TABLE(of, pm8xxx_id_table); static int pm8xxx_rtc_probe(struct platform_device *pdev) { int rc; - unsigned int ctrl_reg; struct pm8xxx_rtc *rtc_dd; const struct of_device_id *match; @@ -399,33 +466,12 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node, "allow-set-time"); - rtc_dd->rtc_base = (long) match->data; - - /* Setup RTC register addresses */ - rtc_dd->rtc_write_base = rtc_dd->rtc_base + PM8XXX_RTC_WRITE_OFFSET; - rtc_dd->rtc_read_base = rtc_dd->rtc_base + PM8XXX_RTC_READ_OFFSET; - rtc_dd->alarm_rw_base = rtc_dd->rtc_base + PM8XXX_ALARM_RW_OFFSET; - + rtc_dd->regs = match->data; rtc_dd->rtc_dev = &pdev->dev; - /* Check if the RTC is on, else turn it on */ - rc = regmap_read(rtc_dd->regmap, rtc_dd->rtc_base, &ctrl_reg); - if (rc) { - dev_err(&pdev->dev, "RTC control register read failed!\n"); + rc = pm8xxx_rtc_enable(rtc_dd); + if (rc) return rc; - } - - if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) { - ctrl_reg |= PM8xxx_RTC_ENABLE; - rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); - if (rc) { - dev_err(&pdev->dev, - "Write to RTC control register failed\n"); - return rc; - } - } - - rtc_dd->ctrl_reg = ctrl_reg; platform_set_drvdata(pdev, rtc_dd); -- cgit v1.2.2 From eaf3a659086e1d1d85dc8fbce4007e3c9076e0b3 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 29 Oct 2014 14:50:38 -0700 Subject: drivers/rtc/rtc-s3c.c: fix initialization failure without rtc source clock Fix unconditional initialization failure on non-exynos3250 SoCs. Commit df9e26d093d3 ("rtc: s3c: add support for RTC of Exynos3250 SoC") introduced rtc source clock support, but also added initialization failure on SoCs, which doesn't need such clock. Signed-off-by: Marek Szyprowski Reviewed-by: Chanwoo Choi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s3c.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index a6b1252c9941..806072238c00 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -535,13 +535,15 @@ static int s3c_rtc_probe(struct platform_device *pdev) } clk_prepare_enable(info->rtc_clk); - info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); - if (IS_ERR(info->rtc_src_clk)) { - dev_err(&pdev->dev, "failed to find rtc source clock\n"); - return PTR_ERR(info->rtc_src_clk); + if (info->data->needs_src_clk) { + info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); + if (IS_ERR(info->rtc_src_clk)) { + dev_err(&pdev->dev, + "failed to find rtc source clock\n"); + return PTR_ERR(info->rtc_src_clk); + } + clk_prepare_enable(info->rtc_src_clk); } - clk_prepare_enable(info->rtc_src_clk); - /* check to see if everything is setup correctly */ if (info->data->enable) -- cgit v1.2.2 From 5a6e7599d3f8000496068b12276492311efad5ea Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Wed, 29 Oct 2014 14:50:42 -0700 Subject: drivers/rtc/rtc-bq32k.c: fix register value Fix register value in bq32000 trickle charging. Mike reported that I'm using wrong value in one trickle-charging case, and after checking docs, I must admit he's right. Signed-off-by: Pavel Machek Reported-by: Mike Bremford Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-bq32k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index 314129e66d6e..92679df6d6e2 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -160,7 +160,7 @@ static int trickle_charger_of_init(struct device *dev, struct device_node *node) dev_err(dev, "bq32k: diode and resistor mismatch\n"); return -EINVAL; } - reg = 0x25; + reg = 0x45; break; default: -- cgit v1.2.2