diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 25 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 2 | ||||
-rw-r--r-- | drivers/rtc/class.c | 2 | ||||
-rw-r--r-- | drivers/rtc/interface.c | 2 | ||||
-rw-r--r-- | drivers/rtc/lib.c | 24 | ||||
-rw-r--r-- | drivers/rtc/lib_test.c | 27 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-cpcap.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-cv1800.c | 218 | ||||
-rw-r--r-- | drivers/rtc/rtc-da9063.c | 31 | ||||
-rw-r--r-- | drivers/rtc/rtc-jz4740.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-loongson.c | 8 | ||||
-rw-r--r-- | drivers/rtc/rtc-m41t80.c | 78 | ||||
-rw-r--r-- | drivers/rtc/rtc-mt6397.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-pm8xxx.c | 18 | ||||
-rw-r--r-- | drivers/rtc/rtc-rzn1.c | 71 | ||||
-rw-r--r-- | drivers/rtc/rtc-s32g.c | 385 | ||||
-rw-r--r-- | drivers/rtc/rtc-s3c.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-sh.c | 285 | ||||
-rw-r--r-- | drivers/rtc/rtc-stm32.c | 1 |
21 files changed, 858 insertions, 326 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 838bdc138ffe..9aec922613ce 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1388,6 +1388,18 @@ config RTC_DRV_ASM9260 This driver can also be built as a module. If so, the module will be called rtc-asm9260. +config RTC_DRV_CV1800 + tristate "Sophgo CV1800 RTC" + depends on SOPHGO_CV1800_RTCSYS || COMPILE_TEST + select MFD_SYSCON + select REGMAP + help + If you say yes here you get support the RTC driver for Sophgo CV1800 + series SoC. + + This driver can also be built as a module. If so, the module will be + called rtc-cv1800. + config RTC_DRV_DIGICOLOR tristate "Conexant Digicolor RTC" depends on ARCH_DIGICOLOR || COMPILE_TEST @@ -2088,7 +2100,7 @@ config RTC_DRV_AMLOGIC_A4 tristate "Amlogic RTC" depends on ARCH_MESON || COMPILE_TEST select REGMAP_MMIO - default y + default ARCH_MESON help If you say yes here you get support for the RTC block on the Amlogic A113L2(A4) and A113X2(A5) SoCs. @@ -2096,4 +2108,15 @@ config RTC_DRV_AMLOGIC_A4 This driver can also be built as a module. If so, the module will be called "rtc-amlogic-a4". +config RTC_DRV_S32G + tristate "RTC driver for S32G2/S32G3 SoCs" + depends on ARCH_S32 || COMPILE_TEST + depends on COMMON_CLK + help + Say yes to enable RTC driver for platforms based on the + S32G2/S32G3 SoC family. + + This RTC module can be used as a wakeup source. + Please note that it is not battery-powered. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 31473b3276d9..4619aa2ac469 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_CADENCE) += rtc-cadence.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o obj-$(CONFIG_RTC_DRV_CROS_EC) += rtc-cros-ec.o +obj-$(CONFIG_RTC_DRV_CV1800) += rtc-cv1800.o obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o @@ -160,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index b88cd4fb295b..b1a2be1f9e3b 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -326,7 +326,7 @@ static void rtc_device_get_offset(struct rtc_device *rtc) * * Otherwise the offset seconds should be 0. */ - if (rtc->start_secs > rtc->range_max || + if ((rtc->start_secs >= 0 && rtc->start_secs > rtc->range_max) || rtc->start_secs + range_secs - 1 < rtc->range_min) rtc->offset_secs = rtc->start_secs - rtc->range_min; else if (rtc->start_secs > rtc->range_min) diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index aaf76406cd7d..dc741ba29fa3 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -205,7 +205,7 @@ static int rtc_read_alarm_internal(struct rtc_device *rtc, mutex_unlock(&rtc->ops_lock); - trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err); + trace_rtc_read_alarm(err?0:rtc_tm_to_time64(&alarm->time), err); return err; } diff --git a/drivers/rtc/lib.c b/drivers/rtc/lib.c index fe361652727a..13b5b1f20465 100644 --- a/drivers/rtc/lib.c +++ b/drivers/rtc/lib.c @@ -46,24 +46,38 @@ EXPORT_SYMBOL(rtc_year_days); * rtc_time64_to_tm - converts time64_t to rtc_time. * * @time: The number of seconds since 01-01-1970 00:00:00. - * (Must be positive.) + * Works for values since at least 1900 * @tm: Pointer to the struct rtc_time. */ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) { - unsigned int secs; - int days; + int days, secs; u64 u64tmp; u32 u32tmp, udays, century, day_of_century, year_of_century, year, day_of_year, month, day; bool is_Jan_or_Feb, is_leap_year; - /* time must be positive */ + /* + * Get days and seconds while preserving the sign to + * handle negative time values (dates before 1970-01-01) + */ days = div_s64_rem(time, 86400, &secs); + /* + * We need 0 <= secs < 86400 which isn't given for negative + * values of time. Fixup accordingly. + */ + if (secs < 0) { + days -= 1; + secs += 86400; + } + /* day of the week, 1970-01-01 was a Thursday */ tm->tm_wday = (days + 4) % 7; + /* Ensure tm_wday is always positive */ + if (tm->tm_wday < 0) + tm->tm_wday += 7; /* * The following algorithm is, basically, Proposition 6.3 of Neri @@ -93,7 +107,7 @@ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) * thus, is slightly different from [1]. */ - udays = ((u32) days) + 719468; + udays = days + 719468; u32tmp = 4 * udays + 3; century = u32tmp / 146097; diff --git a/drivers/rtc/lib_test.c b/drivers/rtc/lib_test.c index c30c759662e3..0eebad1fe2a0 100644 --- a/drivers/rtc/lib_test.c +++ b/drivers/rtc/lib_test.c @@ -6,8 +6,10 @@ /* * Advance a date by one day. */ -static void advance_date(int *year, int *month, int *mday, int *yday) +static void advance_date(int *year, int *month, int *mday, int *yday, int *wday) { + *wday = (*wday + 1) % 7; + if (*mday != rtc_month_days(*month - 1, *year)) { ++*mday; ++*yday; @@ -39,35 +41,38 @@ static void rtc_time64_to_tm_test_date_range(struct kunit *test, int years) */ time64_t total_secs = ((time64_t)years) / 400 * 146097 * 86400; - int year = 1970; + int year = 1900; int month = 1; int mday = 1; int yday = 1; + int wday = 1; /* Jan 1st 1900 was a Monday */ struct rtc_time result; time64_t secs; - s64 days; + const time64_t sec_offset = RTC_TIMESTAMP_BEGIN_1900 + ((1 * 60) + 2) * 60 + 3; for (secs = 0; secs <= total_secs; secs += 86400) { - rtc_time64_to_tm(secs, &result); - - days = div_s64(secs, 86400); + rtc_time64_to_tm(secs + sec_offset, &result); - #define FAIL_MSG "%d/%02d/%02d (%2d) : %lld", \ - year, month, mday, yday, days + #define FAIL_MSG "%d/%02d/%02d (%2d, %d) : %lld", \ + year, month, mday, yday, wday, secs + sec_offset KUNIT_ASSERT_EQ_MSG(test, year - 1900, result.tm_year, FAIL_MSG); KUNIT_ASSERT_EQ_MSG(test, month - 1, result.tm_mon, FAIL_MSG); KUNIT_ASSERT_EQ_MSG(test, mday, result.tm_mday, FAIL_MSG); KUNIT_ASSERT_EQ_MSG(test, yday, result.tm_yday, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, 1, result.tm_hour, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, 2, result.tm_min, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, 3, result.tm_sec, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, wday, result.tm_wday, FAIL_MSG); - advance_date(&year, &month, &mday, &yday); + advance_date(&year, &month, &mday, &yday, &wday); } } /* - * Checks every day in a 160000 years interval starting on 1970-01-01 + * Checks every day in a 160000 years interval starting on 1900-01-01 * against the expected result. */ static void rtc_time64_to_tm_test_date_range_160000(struct kunit *test) @@ -76,7 +81,7 @@ static void rtc_time64_to_tm_test_date_range_160000(struct kunit *test) } /* - * Checks every day in a 1000 years interval starting on 1970-01-01 + * Checks every day in a 1000 years interval starting on 1900-01-01 * against the expected result. */ static void rtc_time64_to_tm_test_date_range_1000(struct kunit *test) diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index f6b0102a843a..643734dbae33 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -654,4 +654,3 @@ module_platform_driver_probe(at91_rtc_driver, at91_rtc_probe); MODULE_AUTHOR("Rick Bronson"); MODULE_DESCRIPTION("RTC driver for Atmel AT91RM9200"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:at91_rtc"); diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c index 568a89e79c11..c170345ac076 100644 --- a/drivers/rtc/rtc-cpcap.c +++ b/drivers/rtc/rtc-cpcap.c @@ -320,7 +320,6 @@ static struct platform_driver cpcap_rtc_driver = { module_platform_driver(cpcap_rtc_driver); -MODULE_ALIAS("platform:cpcap-rtc"); MODULE_DESCRIPTION("CPCAP RTC driver"); MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-cv1800.c b/drivers/rtc/rtc-cv1800.c new file mode 100644 index 000000000000..678c2c10bf58 --- /dev/null +++ b/drivers/rtc/rtc-cv1800.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-cv1800.c: RTC driver for Sophgo cv1800 RTC + * + * Author: Jingbao Qiu <qiujingbao.dlmu@gmail.com> + */ + +#include <linux/clk.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/rtc.h> + +#define SEC_PULSE_GEN 0x1004 +#define ALARM_TIME 0x1008 +#define ALARM_ENABLE 0x100C +#define SET_SEC_CNTR_VAL 0x1010 +#define SET_SEC_CNTR_TRIG 0x1014 +#define SEC_CNTR_VAL 0x1018 + +/* + * When in VDDBKUP domain, this MACRO register + * does not power down + */ +#define MACRO_RO_T 0x14A8 +#define MACRO_RG_SET_T 0x1498 + +#define ALARM_ENABLE_MASK BIT(0) +#define SEL_SEC_PULSE BIT(31) + +struct cv1800_rtc_priv { + struct rtc_device *rtc_dev; + struct regmap *rtc_map; + struct clk *clk; + int irq; +}; + +static bool cv1800_rtc_enabled(struct device *dev) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + u32 reg; + + regmap_read(info->rtc_map, SEC_PULSE_GEN, ®); + + return (reg & SEL_SEC_PULSE) == 0; +} + +static void cv1800_rtc_enable(struct device *dev) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + + /* Sec pulse generated internally */ + regmap_update_bits(info->rtc_map, SEC_PULSE_GEN, SEL_SEC_PULSE, 0); +} + +static int cv1800_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + + regmap_write(info->rtc_map, ALARM_ENABLE, enabled); + + return 0; +} + +static int cv1800_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + unsigned long alarm_time; + + alarm_time = rtc_tm_to_time64(&alrm->time); + + cv1800_rtc_alarm_irq_enable(dev, 0); + + regmap_write(info->rtc_map, ALARM_TIME, alarm_time); + + cv1800_rtc_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static int cv1800_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + u32 enabled; + u32 time; + + if (!cv1800_rtc_enabled(dev)) { + alarm->enabled = 0; + return 0; + } + + regmap_read(info->rtc_map, ALARM_ENABLE, &enabled); + + alarm->enabled = enabled & ALARM_ENABLE_MASK; + + regmap_read(info->rtc_map, ALARM_TIME, &time); + + rtc_time64_to_tm(time, &alarm->time); + + return 0; +} + +static int cv1800_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + u32 sec; + + if (!cv1800_rtc_enabled(dev)) + return -EINVAL; + + regmap_read(info->rtc_map, SEC_CNTR_VAL, &sec); + + rtc_time64_to_tm(sec, tm); + + return 0; +} + +static int cv1800_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + unsigned long sec; + + sec = rtc_tm_to_time64(tm); + + regmap_write(info->rtc_map, SET_SEC_CNTR_VAL, sec); + regmap_write(info->rtc_map, SET_SEC_CNTR_TRIG, 1); + + regmap_write(info->rtc_map, MACRO_RG_SET_T, sec); + + cv1800_rtc_enable(dev); + + return 0; +} + +static irqreturn_t cv1800_rtc_irq_handler(int irq, void *dev_id) +{ + struct cv1800_rtc_priv *info = dev_id; + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + regmap_write(info->rtc_map, ALARM_ENABLE, 0); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops cv1800_rtc_ops = { + .read_time = cv1800_rtc_read_time, + .set_time = cv1800_rtc_set_time, + .read_alarm = cv1800_rtc_read_alarm, + .set_alarm = cv1800_rtc_set_alarm, + .alarm_irq_enable = cv1800_rtc_alarm_irq_enable, +}; + +static int cv1800_rtc_probe(struct platform_device *pdev) +{ + struct cv1800_rtc_priv *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc_map = device_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(rtc->rtc_map)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_map), + "cannot get parent regmap\n"); + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) + return rtc->irq; + + rtc->clk = devm_clk_get_enabled(pdev->dev.parent, "rtc"); + if (IS_ERR(rtc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->clk), + "rtc clk not found\n"); + + platform_set_drvdata(pdev, rtc); + + device_init_wakeup(&pdev->dev, 1); + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + rtc->rtc_dev->ops = &cv1800_rtc_ops; + rtc->rtc_dev->range_max = U32_MAX; + + ret = devm_request_irq(&pdev->dev, rtc->irq, cv1800_rtc_irq_handler, + IRQF_TRIGGER_HIGH, "rtc alarm", rtc); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "cannot register interrupt handler\n"); + + return devm_rtc_register_device(rtc->rtc_dev); +} + +static const struct platform_device_id cv1800_rtc_id[] = { + { .name = "cv1800b-rtc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, cv1800_rtc_id); + +static struct platform_driver cv1800_rtc_driver = { + .driver = { + .name = "sophgo-cv1800-rtc", + }, + .probe = cv1800_rtc_probe, + .id_table = cv1800_rtc_id, +}; + +module_platform_driver(cv1800_rtc_driver); +MODULE_AUTHOR("Jingbao Qiu"); +MODULE_DESCRIPTION("Sophgo cv1800 RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c index 859397541f29..557c9b29dcc1 100644 --- a/drivers/rtc/rtc-da9063.c +++ b/drivers/rtc/rtc-da9063.c @@ -194,26 +194,17 @@ static void da9063_tm_to_data(struct rtc_time *tm, u8 *data, config->rtc_count_year_mask; } -static int da9063_rtc_stop_alarm(struct device *dev) -{ - struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); - const struct da9063_compatible_rtc_regmap *config = rtc->config; - - return regmap_update_bits(rtc->regmap, - config->rtc_alarm_year_reg, - config->rtc_alarm_on_mask, - 0); -} - -static int da9063_rtc_start_alarm(struct device *dev) +static int da9063_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) { struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); const struct da9063_compatible_rtc_regmap *config = rtc->config; + u8 set_bit = enabled ? config->rtc_alarm_on_mask : 0; return regmap_update_bits(rtc->regmap, config->rtc_alarm_year_reg, config->rtc_alarm_on_mask, - config->rtc_alarm_on_mask); + set_bit); } static int da9063_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -312,7 +303,7 @@ static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) da9063_tm_to_data(&alrm->time, data, rtc); - ret = da9063_rtc_stop_alarm(dev); + ret = da9063_rtc_alarm_irq_enable(dev, 0); if (ret < 0) { dev_err(dev, "Failed to stop alarm: %d\n", ret); return ret; @@ -330,7 +321,7 @@ static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) da9063_data_to_tm(data, &rtc->alarm_time, rtc); if (alrm->enabled) { - ret = da9063_rtc_start_alarm(dev); + ret = da9063_rtc_alarm_irq_enable(dev, 1); if (ret < 0) { dev_err(dev, "Failed to start alarm: %d\n", ret); return ret; @@ -340,15 +331,6 @@ static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } -static int da9063_rtc_alarm_irq_enable(struct device *dev, - unsigned int enabled) -{ - if (enabled) - return da9063_rtc_start_alarm(dev); - else - return da9063_rtc_stop_alarm(dev); -} - static irqreturn_t da9063_alarm_event(int irq, void *data) { struct da9063_compatible_rtc *rtc = data; @@ -513,4 +495,3 @@ module_platform_driver(da9063_rtc_driver); MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); MODULE_DESCRIPTION("Real time clock device driver for Dialog DA9063"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DA9063_DRVNAME_RTC); diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 44bba356268c..11fce47be780 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -437,4 +437,3 @@ module_platform_driver(jz4740_rtc_driver); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n"); -MODULE_ALIAS("platform:jz4740-rtc"); diff --git a/drivers/rtc/rtc-loongson.c b/drivers/rtc/rtc-loongson.c index 97e5625c064c..2ca7ffd5d7a9 100644 --- a/drivers/rtc/rtc-loongson.c +++ b/drivers/rtc/rtc-loongson.c @@ -129,6 +129,14 @@ static u32 loongson_rtc_handler(void *id) { struct loongson_rtc_priv *priv = (struct loongson_rtc_priv *)id; + rtc_update_irq(priv->rtcdev, 1, RTC_AF | RTC_IRQF); + + /* + * The TOY_MATCH0_REG should be cleared 0 here, + * otherwise the interrupt cannot be cleared. + */ + regmap_write(priv->regmap, TOY_MATCH0_REG, 0); + spin_lock(&priv->lock); /* Disable RTC alarm wakeup and interrupt */ writel(readl(priv->pm_base + PM1_EN_REG) & ~RTC_EN, diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 1f58ae8b151e..c568639d2151 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/string.h> +#include <linux/delay.h> #ifdef CONFIG_RTC_DRV_M41T80_WDT #include <linux/fs.h> #include <linux/ioctl.h> @@ -204,14 +205,14 @@ static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm) return flags; if (flags & M41T80_FLAGS_OF) { - dev_err(&client->dev, "Oscillator failure, data is invalid.\n"); + dev_err(&client->dev, "Oscillator failure, time may not be accurate, write time to RTC to fix it.\n"); return -EINVAL; } err = i2c_smbus_read_i2c_block_data(client, M41T80_REG_SSEC, sizeof(buf), buf); if (err < 0) { - dev_err(&client->dev, "Unable to read date\n"); + dev_dbg(&client->dev, "Unable to read date\n"); return err; } @@ -227,21 +228,31 @@ static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm) return 0; } -static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm) +static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *in_tm) { struct i2c_client *client = to_i2c_client(dev); struct m41t80_data *clientdata = i2c_get_clientdata(client); + struct rtc_time tm = *in_tm; unsigned char buf[8]; int err, flags; + time64_t time = 0; + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) + return flags; + if (flags & M41T80_FLAGS_OF) { + /* add 4sec of oscillator stablize time otherwise we are behind 4sec */ + time = rtc_tm_to_time64(&tm); + rtc_time64_to_tm(time + 4, &tm); + } buf[M41T80_REG_SSEC] = 0; - buf[M41T80_REG_SEC] = bin2bcd(tm->tm_sec); - buf[M41T80_REG_MIN] = bin2bcd(tm->tm_min); - buf[M41T80_REG_HOUR] = bin2bcd(tm->tm_hour); - buf[M41T80_REG_DAY] = bin2bcd(tm->tm_mday); - buf[M41T80_REG_MON] = bin2bcd(tm->tm_mon + 1); - buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100); - buf[M41T80_REG_WDAY] = tm->tm_wday; + buf[M41T80_REG_SEC] = bin2bcd(tm.tm_sec); + buf[M41T80_REG_MIN] = bin2bcd(tm.tm_min); + buf[M41T80_REG_HOUR] = bin2bcd(tm.tm_hour); + buf[M41T80_REG_DAY] = bin2bcd(tm.tm_mday); + buf[M41T80_REG_MON] = bin2bcd(tm.tm_mon + 1); + buf[M41T80_REG_YEAR] = bin2bcd(tm.tm_year - 100); + buf[M41T80_REG_WDAY] = tm.tm_wday; /* If the square wave output is controlled in the weekday register */ if (clientdata->features & M41T80_FEATURE_SQ_ALT) { @@ -257,20 +268,37 @@ static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm) err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC, sizeof(buf), buf); if (err < 0) { - dev_err(&client->dev, "Unable to write to date registers\n"); + dev_dbg(&client->dev, "Unable to write to date registers\n"); return err; } - - /* Clear the OF bit of Flags Register */ - flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); - if (flags < 0) - return flags; - - err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, - flags & ~M41T80_FLAGS_OF); - if (err < 0) { - dev_err(&client->dev, "Unable to write flags register\n"); - return err; + if (flags & M41T80_FLAGS_OF) { + /* OF cannot be immediately reset: oscillator has to be restarted. */ + dev_warn(&client->dev, "OF bit is still set, kickstarting clock.\n"); + err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, M41T80_SEC_ST); + if (err < 0) { + dev_dbg(&client->dev, "Can't set ST bit\n"); + return err; + } + err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, flags & ~M41T80_SEC_ST); + if (err < 0) { + dev_dbg(&client->dev, "Can't clear ST bit\n"); + return err; + } + /* oscillator must run for 4sec before we attempt to reset OF bit */ + msleep(4000); + /* Clear the OF bit of Flags Register */ + err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, flags & ~M41T80_FLAGS_OF); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write flags register\n"); + return err; + } + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) { + return flags; + } else if (flags & M41T80_FLAGS_OF) { + dev_dbg(&client->dev, "Can't clear the OF bit check battery\n"); + return err; + } } return err; @@ -308,7 +336,7 @@ static int m41t80_alarm_irq_enable(struct device *dev, unsigned int enabled) retval = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, flags); if (retval < 0) { - dev_err(dev, "Unable to enable alarm IRQ %d\n", retval); + dev_dbg(dev, "Unable to enable alarm IRQ %d\n", retval); return retval; } return 0; @@ -333,7 +361,7 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) err = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret & ~(M41T80_ALMON_AFE)); if (err < 0) { - dev_err(dev, "Unable to clear AFE bit\n"); + dev_dbg(dev, "Unable to clear AFE bit\n"); return err; } @@ -347,7 +375,7 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, ret & ~(M41T80_FLAGS_AF)); if (err < 0) { - dev_err(dev, "Unable to clear AF bit\n"); + dev_dbg(dev, "Unable to clear AF bit\n"); return err; } diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c index 6979d225a78e..692c00ff544b 100644 --- a/drivers/rtc/rtc-mt6397.c +++ b/drivers/rtc/rtc-mt6397.c @@ -332,6 +332,7 @@ static const struct mtk_rtc_data mt6397_rtc_data = { static const struct of_device_id mt6397_rtc_of_match[] = { { .compatible = "mediatek,mt6323-rtc", .data = &mt6397_rtc_data }, + { .compatible = "mediatek,mt6357-rtc", .data = &mt6358_rtc_data }, { .compatible = "mediatek,mt6358-rtc", .data = &mt6358_rtc_data }, { .compatible = "mediatek,mt6397-rtc", .data = &mt6397_rtc_data }, { } diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 5a084d426e58..b2611697fa5e 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -285,7 +285,7 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) buf[2] = bin2bcd(tm->time.tm_mday); buf[3] = tm->time.tm_wday & 0x07; - err = regmap_bulk_write(pcf8563->regmap, PCF8563_REG_SC, buf, + err = regmap_bulk_write(pcf8563->regmap, PCF8563_REG_AMN, buf, sizeof(buf)); if (err) return err; diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index 3c1dddcc81df..e624f848c22b 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -576,13 +576,20 @@ static int pm8xxx_rtc_probe_offset(struct pm8xxx_rtc *rtc_dd) } /* Use UEFI storage as fallback if available */ - if (efivar_is_available()) { - rc = pm8xxx_rtc_read_uefi_offset(rtc_dd); - if (rc == 0) - rtc_dd->use_uefi = true; + rtc_dd->use_uefi = of_property_read_bool(rtc_dd->dev->of_node, + "qcom,uefi-rtc-info"); + if (!rtc_dd->use_uefi) + return 0; + + if (!efivar_is_available()) { + if (IS_ENABLED(CONFIG_EFI)) + return -EPROBE_DEFER; + + dev_warn(rtc_dd->dev, "efivars not available\n"); + rtc_dd->use_uefi = false; } - return 0; + return pm8xxx_rtc_read_uefi_offset(rtc_dd); } static int pm8xxx_rtc_probe(struct platform_device *pdev) @@ -676,7 +683,6 @@ static struct platform_driver pm8xxx_rtc_driver = { module_platform_driver(pm8xxx_rtc_driver); -MODULE_ALIAS("platform:rtc-pm8xxx"); MODULE_DESCRIPTION("PMIC8xxx RTC driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Anirudh Ghayal <aghayal@codeaurora.org>"); diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c index eeb9612a666f..c4ed43735457 100644 --- a/drivers/rtc/rtc-rzn1.c +++ b/drivers/rtc/rtc-rzn1.c @@ -12,6 +12,7 @@ */ #include <linux/bcd.h> +#include <linux/clk.h> #include <linux/init.h> #include <linux/iopoll.h> #include <linux/module.h> @@ -22,9 +23,9 @@ #include <linux/spinlock.h> #define RZN1_RTC_CTL0 0x00 -#define RZN1_RTC_CTL0_SLSB_SUBU 0 #define RZN1_RTC_CTL0_SLSB_SCMP BIT(4) #define RZN1_RTC_CTL0_AMPM BIT(5) +#define RZN1_RTC_CTL0_CEST BIT(6) #define RZN1_RTC_CTL0_CE BIT(7) #define RZN1_RTC_CTL1 0x04 @@ -49,6 +50,8 @@ #define RZN1_RTC_SUBU_DEV BIT(7) #define RZN1_RTC_SUBU_DECR BIT(6) +#define RZN1_RTC_SCMP 0x3c + #define RZN1_RTC_ALM 0x40 #define RZN1_RTC_ALH 0x44 #define RZN1_RTC_ALW 0x48 @@ -356,7 +359,7 @@ static int rzn1_rtc_set_offset(struct device *dev, long offset) return 0; } -static const struct rtc_class_ops rzn1_rtc_ops = { +static const struct rtc_class_ops rzn1_rtc_ops_subu = { .read_time = rzn1_rtc_read_time, .set_time = rzn1_rtc_set_time, .read_alarm = rzn1_rtc_read_alarm, @@ -366,11 +369,21 @@ static const struct rtc_class_ops rzn1_rtc_ops = { .set_offset = rzn1_rtc_set_offset, }; +static const struct rtc_class_ops rzn1_rtc_ops_scmp = { + .read_time = rzn1_rtc_read_time, + .set_time = rzn1_rtc_set_time, + .read_alarm = rzn1_rtc_read_alarm, + .set_alarm = rzn1_rtc_set_alarm, + .alarm_irq_enable = rzn1_rtc_alarm_irq_enable, +}; + static int rzn1_rtc_probe(struct platform_device *pdev) { struct rzn1_rtc *rtc; - int irq; - int ret; + u32 val, scmp_val = 0; + struct clk *xtal; + unsigned long rate; + int irq, ret; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) @@ -393,7 +406,6 @@ static int rzn1_rtc_probe(struct platform_device *pdev) rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099; rtc->rtcdev->alarm_offset_max = 7 * 86400; - rtc->rtcdev->ops = &rzn1_rtc_ops; ret = devm_pm_runtime_enable(&pdev->dev); if (ret < 0) @@ -402,12 +414,44 @@ static int rzn1_rtc_probe(struct platform_device *pdev) if (ret < 0) return ret; - /* - * Ensure the clock counter is enabled. - * Set 24-hour mode and possible oscillator offset compensation in SUBU mode. - */ - writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | RZN1_RTC_CTL0_SLSB_SUBU, - rtc->base + RZN1_RTC_CTL0); + /* Only switch to scmp if we have an xtal clock with a valid rate and != 32768 */ + xtal = devm_clk_get_optional(&pdev->dev, "xtal"); + if (IS_ERR(xtal)) { + ret = PTR_ERR(xtal); + goto dis_runtime_pm; + } else if (xtal) { + rate = clk_get_rate(xtal); + + if (rate < 32000 || rate > BIT(22)) { + ret = -EOPNOTSUPP; + goto dis_runtime_pm; + } + + if (rate != 32768) + scmp_val = RZN1_RTC_CTL0_SLSB_SCMP; + } + + /* Disable controller during SUBU/SCMP setup */ + val = readl(rtc->base + RZN1_RTC_CTL0) & ~RZN1_RTC_CTL0_CE; + writel(val, rtc->base + RZN1_RTC_CTL0); + /* Wait 2-4 32k clock cycles for the disabled controller */ + ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL0, val, + !(val & RZN1_RTC_CTL0_CEST), 62, 123); + if (ret) + goto dis_runtime_pm; + + /* Set desired modes leaving the controller disabled */ + writel(RZN1_RTC_CTL0_AMPM | scmp_val, rtc->base + RZN1_RTC_CTL0); + + if (scmp_val) { + writel(rate - 1, rtc->base + RZN1_RTC_SCMP); + rtc->rtcdev->ops = &rzn1_rtc_ops_scmp; + } else { + rtc->rtcdev->ops = &rzn1_rtc_ops_subu; + } + + /* Enable controller finally */ + writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | scmp_val, rtc->base + RZN1_RTC_CTL0); /* Disable all interrupts */ writel(0, rtc->base + RZN1_RTC_CTL1); @@ -444,6 +488,11 @@ dis_runtime_pm: static void rzn1_rtc_remove(struct platform_device *pdev) { + struct rzn1_rtc *rtc = platform_get_drvdata(pdev); + + /* Disable all interrupts */ + writel(0, rtc->base + RZN1_RTC_CTL1); + pm_runtime_put(&pdev->dev); } diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c new file mode 100644 index 000000000000..3a0818e972eb --- /dev/null +++ b/drivers/rtc/rtc-s32g.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2025 NXP + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#define RTCC_OFFSET 0x4ul +#define RTCS_OFFSET 0x8ul +#define APIVAL_OFFSET 0x10ul + +/* RTCC fields */ +#define RTCC_CNTEN BIT(31) +#define RTCC_APIEN BIT(15) +#define RTCC_APIIE BIT(14) +#define RTCC_CLKSEL_MASK GENMASK(13, 12) +#define RTCC_DIV512EN BIT(11) +#define RTCC_DIV32EN BIT(10) + +/* RTCS fields */ +#define RTCS_INV_API BIT(17) +#define RTCS_APIF BIT(13) + +#define APIVAL_MAX_VAL GENMASK(31, 0) +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) + +/* + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and + * should not be used. + */ +#define RTC_CLK_SRC1_RESERVED BIT(1) + +/* + * S32G RTC module has a 512 value and a 32 value hardware frequency + * divisors (DIV512 and DIV32) which could be used to achieve higher + * counter ranges by lowering the RTC frequency. + */ +enum { + DIV1 = 1, + DIV32 = 32, + DIV512 = 512, + DIV512_32 = 16384 +}; + +static const char *const rtc_clk_src[] = { + "source0", + "source1", + "source2", + "source3" +}; + +struct rtc_priv { + struct rtc_device *rdev; + void __iomem *rtc_base; + struct clk *ipg; + struct clk *clk_src; + const struct rtc_soc_data *rtc_data; + u64 rtc_hz; + time64_t sleep_sec; + int irq; + u32 clk_src_idx; +}; + +struct rtc_soc_data { + u32 clk_div; + u32 reserved_clk_mask; +}; + +static const struct rtc_soc_data rtc_s32g2_data = { + .clk_div = DIV512_32, + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, +}; + +static irqreturn_t s32g_rtc_handler(int irq, void *dev) +{ + struct rtc_priv *priv = platform_get_drvdata(dev); + u32 status; + + status = readl(priv->rtc_base + RTCS_OFFSET); + + if (status & RTCS_APIF) { + writel(0x0, priv->rtc_base + APIVAL_OFFSET); + writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET); + } + + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +/* + * The function is not really getting time from the RTC since the S32G RTC + * has several limitations. Thus, to setup alarm use system time. + */ +static int s32g_rtc_read_time(struct device *dev, + struct rtc_time *tm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + time64_t sec; + + if (check_add_overflow(ktime_get_real_seconds(), + priv->sleep_sec, &sec)) + return -ERANGE; + + rtc_time64_to_tm(sec, tm); + + return 0; +} + +static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 rtcc, rtcs; + + rtcc = readl(priv->rtc_base + RTCC_OFFSET); + rtcs = readl(priv->rtc_base + RTCS_OFFSET); + + alrm->enabled = rtcc & RTCC_APIIE; + if (alrm->enabled) + alrm->pending = !(rtcs & RTCS_APIF); + + return 0; +} + +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 rtcc; + + /* RTC API functionality is used both for triggering interrupts + * and as a wakeup event. Hence it should always be enabled. + */ + rtcc = readl(priv->rtc_base + RTCC_OFFSET); + rtcc |= RTCC_APIEN | RTCC_APIIE; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); + + return 0; +} + +static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + unsigned long long cycles; + long long t_offset; + time64_t alrm_time; + u32 rtcs; + int ret; + + alrm_time = rtc_tm_to_time64(&alrm->time); + t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec; + if (t_offset < 0) + return -ERANGE; + + cycles = t_offset * priv->rtc_hz; + if (cycles > APIVAL_MAX_VAL) + return -ERANGE; + + /* APIVAL could have been reset from the IRQ handler. + * Hence, we wait in case there is a synchronization process. + */ + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); + if (ret) + return ret; + + writel(cycles, priv->rtc_base + APIVAL_OFFSET); + + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); +} + +/* + * Disable the 32-bit free running counter. + * This allows Clock Source and Divisors selection + * to be performed without causing synchronization issues. + */ +static void s32g_rtc_disable(struct rtc_priv *priv) +{ + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); + + rtcc &= ~RTCC_CNTEN; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); +} + +static void s32g_rtc_enable(struct rtc_priv *priv) +{ + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); + + rtcc |= RTCC_CNTEN; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); +} + +static int rtc_clk_src_setup(struct rtc_priv *priv) +{ + u32 rtcc; + + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); + + switch (priv->rtc_data->clk_div) { + case DIV512_32: + rtcc |= RTCC_DIV512EN; + rtcc |= RTCC_DIV32EN; + break; + case DIV512: + rtcc |= RTCC_DIV512EN; + break; + case DIV32: + rtcc |= RTCC_DIV32EN; + break; + case DIV1: + break; + default: + return -EINVAL; + } + + rtcc |= RTCC_APIEN | RTCC_APIIE; + /* + * Make sure the CNTEN is 0 before we configure + * the clock source and dividers. + */ + s32g_rtc_disable(priv); + writel(rtcc, priv->rtc_base + RTCC_OFFSET); + s32g_rtc_enable(priv); + + return 0; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = s32g_rtc_read_time, + .read_alarm = s32g_rtc_read_alarm, + .set_alarm = s32g_rtc_set_alarm, + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, +}; + +static int rtc_clk_dts_setup(struct rtc_priv *priv, + struct device *dev) +{ + u32 i; + + priv->ipg = devm_clk_get_enabled(dev, "ipg"); + if (IS_ERR(priv->ipg)) + return dev_err_probe(dev, PTR_ERR(priv->ipg), + "Failed to get 'ipg' clock\n"); + + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { + if (priv->rtc_data->reserved_clk_mask & BIT(i)) + return -EOPNOTSUPP; + + priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]); + if (!IS_ERR(priv->clk_src)) { + priv->clk_src_idx = i; + break; + } + } + + if (IS_ERR(priv->clk_src)) + return dev_err_probe(dev, PTR_ERR(priv->clk_src), + "Failed to get rtc module clock source\n"); + + return 0; +} + +static int s32g_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtc_priv *priv; + unsigned long rtc_hz; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rtc_data = of_device_get_match_data(dev); + if (!priv->rtc_data) + return -ENODEV; + + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->rtc_base)) + return PTR_ERR(priv->rtc_base); + + device_init_wakeup(dev, true); + + ret = rtc_clk_dts_setup(priv, dev); + if (ret) + return ret; + + priv->rdev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rdev)) + return PTR_ERR(priv->rdev); + + ret = rtc_clk_src_setup(priv); + if (ret) + return ret; + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + ret = priv->irq; + goto disable_rtc; + } + + rtc_hz = clk_get_rate(priv->clk_src); + if (!rtc_hz) { + dev_err(dev, "Failed to get RTC frequency\n"); + ret = -EINVAL; + goto disable_rtc; + } + + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); + + platform_set_drvdata(pdev, priv); + priv->rdev->ops = &rtc_ops; + + ret = devm_request_irq(dev, priv->irq, + s32g_rtc_handler, 0, dev_name(dev), pdev); + if (ret) { + dev_err(dev, "Request interrupt %d failed, error: %d\n", + priv->irq, ret); + goto disable_rtc; + } + + ret = devm_rtc_register_device(priv->rdev); + if (ret) + goto disable_rtc; + + return 0; + +disable_rtc: + s32g_rtc_disable(priv); + return ret; +} + +static int s32g_rtc_suspend(struct device *dev) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); + + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz), + &priv->sleep_sec)) { + dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n"); + priv->sleep_sec = 0; + } + + return 0; +} + +static int s32g_rtc_resume(struct device *dev) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + + /* The transition from resume to run is a reset event. + * This leads to the RTC registers being reset after resume from + * suspend. It is uncommon, but this behaviour has been observed + * on S32G RTC after issuing a Suspend to RAM operation. + * Thus, reconfigure RTC registers on the resume path. + */ + return rtc_clk_src_setup(priv); +} + +static const struct of_device_id rtc_dt_ids[] = { + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, + { /* sentinel */ }, +}; + +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, + s32g_rtc_suspend, s32g_rtc_resume); + +static struct platform_driver s32g_rtc_driver = { + .driver = { + .name = "s32g-rtc", + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), + .of_match_table = rtc_dt_ids, + }, + .probe = s32g_rtc_probe, +}; +module_platform_driver(s32g_rtc_driver); + +MODULE_AUTHOR("NXP"); +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 58c957eb753d..5dd575865adf 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -609,4 +609,3 @@ module_platform_driver(s3c_rtc_driver); MODULE_DESCRIPTION("Samsung S3C RTC Driver"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c2410-rtc"); diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 9ea40f40188f..f15ef3aa82a0 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -5,6 +5,7 @@ * Copyright (C) 2006 - 2009 Paul Mundt * Copyright (C) 2006 Jamie Lenehan * Copyright (C) 2008 Angelo Castello + * Copyright (C) 2025 Wolfram Sang, Renesas Electronics Corporation * * Based on the old arch/sh/kernel/cpu/rtc.c by: * @@ -31,7 +32,7 @@ /* Default values for RZ/A RTC */ #define rtc_reg_size sizeof(u16) #define RTC_BIT_INVERTED 0 /* no chip bugs */ -#define RTC_CAP_4_DIGIT_YEAR (1 << 0) +#define RTC_CAP_4_DIGIT_YEAR BIT(0) #define RTC_DEF_CAPABILITIES RTC_CAP_4_DIGIT_YEAR #endif @@ -70,62 +71,35 @@ */ /* ALARM Bits - or with BCD encoded value */ -#define AR_ENB 0x80 /* Enable for alarm cmp */ - -/* Period Bits */ -#define PF_HP 0x100 /* Enable Half Period to support 8,32,128Hz */ -#define PF_COUNT 0x200 /* Half periodic counter */ -#define PF_OXS 0x400 /* Periodic One x Second */ -#define PF_KOU 0x800 /* Kernel or User periodic request 1=kernel */ -#define PF_MASK 0xf00 +#define AR_ENB BIT(7) /* Enable for alarm cmp */ /* RCR1 Bits */ -#define RCR1_CF 0x80 /* Carry Flag */ -#define RCR1_CIE 0x10 /* Carry Interrupt Enable */ -#define RCR1_AIE 0x08 /* Alarm Interrupt Enable */ -#define RCR1_AF 0x01 /* Alarm Flag */ +#define RCR1_CF BIT(7) /* Carry Flag */ +#define RCR1_CIE BIT(4) /* Carry Interrupt Enable */ +#define RCR1_AIE BIT(3) /* Alarm Interrupt Enable */ +#define RCR1_AF BIT(0) /* Alarm Flag */ /* RCR2 Bits */ -#define RCR2_PEF 0x80 /* PEriodic interrupt Flag */ -#define RCR2_PESMASK 0x70 /* Periodic interrupt Set */ -#define RCR2_RTCEN 0x08 /* ENable RTC */ -#define RCR2_ADJ 0x04 /* ADJustment (30-second) */ -#define RCR2_RESET 0x02 /* Reset bit */ -#define RCR2_START 0x01 /* Start bit */ +#define RCR2_RTCEN BIT(3) /* ENable RTC */ +#define RCR2_ADJ BIT(2) /* ADJustment (30-second) */ +#define RCR2_RESET BIT(1) /* Reset bit */ +#define RCR2_START BIT(0) /* Start bit */ struct sh_rtc { void __iomem *regbase; - unsigned long regsize; - struct resource *res; int alarm_irq; - int periodic_irq; - int carry_irq; struct clk *clk; struct rtc_device *rtc_dev; - spinlock_t lock; + spinlock_t lock; /* protecting register access */ unsigned long capabilities; /* See asm/rtc.h for cap bits */ - unsigned short periodic_freq; }; -static int __sh_rtc_interrupt(struct sh_rtc *rtc) +static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) { + struct sh_rtc *rtc = dev_id; unsigned int tmp, pending; - tmp = readb(rtc->regbase + RCR1); - pending = tmp & RCR1_CF; - tmp &= ~RCR1_CF; - writeb(tmp, rtc->regbase + RCR1); - - /* Users have requested One x Second IRQ */ - if (pending && rtc->periodic_freq & PF_OXS) - rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); - - return pending; -} - -static int __sh_rtc_alarm(struct sh_rtc *rtc) -{ - unsigned int tmp, pending; + spin_lock(&rtc->lock); tmp = readb(rtc->regbase + RCR1); pending = tmp & RCR1_AF; @@ -135,84 +109,12 @@ static int __sh_rtc_alarm(struct sh_rtc *rtc) if (pending) rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); - return pending; -} - -static int __sh_rtc_periodic(struct sh_rtc *rtc) -{ - unsigned int tmp, pending; - - tmp = readb(rtc->regbase + RCR2); - pending = tmp & RCR2_PEF; - tmp &= ~RCR2_PEF; - writeb(tmp, rtc->regbase + RCR2); - - if (!pending) - return 0; - - /* Half period enabled than one skipped and the next notified */ - if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT)) - rtc->periodic_freq &= ~PF_COUNT; - else { - if (rtc->periodic_freq & PF_HP) - rtc->periodic_freq |= PF_COUNT; - rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); - } - - return pending; -} - -static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) -{ - struct sh_rtc *rtc = dev_id; - int ret; - - spin_lock(&rtc->lock); - ret = __sh_rtc_interrupt(rtc); - spin_unlock(&rtc->lock); - - return IRQ_RETVAL(ret); -} - -static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) -{ - struct sh_rtc *rtc = dev_id; - int ret; - - spin_lock(&rtc->lock); - ret = __sh_rtc_alarm(rtc); - spin_unlock(&rtc->lock); - - return IRQ_RETVAL(ret); -} - -static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) -{ - struct sh_rtc *rtc = dev_id; - int ret; - - spin_lock(&rtc->lock); - ret = __sh_rtc_periodic(rtc); - spin_unlock(&rtc->lock); - - return IRQ_RETVAL(ret); -} - -static irqreturn_t sh_rtc_shared(int irq, void *dev_id) -{ - struct sh_rtc *rtc = dev_id; - int ret; - - spin_lock(&rtc->lock); - ret = __sh_rtc_interrupt(rtc); - ret |= __sh_rtc_alarm(rtc); - ret |= __sh_rtc_periodic(rtc); spin_unlock(&rtc->lock); - return IRQ_RETVAL(ret); + return IRQ_RETVAL(pending); } -static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) +static int sh_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) { struct sh_rtc *rtc = dev_get_drvdata(dev); unsigned int tmp; @@ -229,45 +131,7 @@ static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) writeb(tmp, rtc->regbase + RCR1); spin_unlock_irq(&rtc->lock); -} - -static int sh_rtc_proc(struct device *dev, struct seq_file *seq) -{ - struct sh_rtc *rtc = dev_get_drvdata(dev); - unsigned int tmp; - - tmp = readb(rtc->regbase + RCR1); - seq_printf(seq, "carry_IRQ\t: %s\n", (tmp & RCR1_CIE) ? "yes" : "no"); - - tmp = readb(rtc->regbase + RCR2); - seq_printf(seq, "periodic_IRQ\t: %s\n", - (tmp & RCR2_PESMASK) ? "yes" : "no"); - - return 0; -} - -static inline void sh_rtc_setcie(struct device *dev, unsigned int enable) -{ - struct sh_rtc *rtc = dev_get_drvdata(dev); - unsigned int tmp; - - spin_lock_irq(&rtc->lock); - - tmp = readb(rtc->regbase + RCR1); - - if (!enable) - tmp &= ~RCR1_CIE; - else - tmp |= RCR1_CIE; - - writeb(tmp, rtc->regbase + RCR1); - - spin_unlock_irq(&rtc->lock); -} -static int sh_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) -{ - sh_rtc_setaie(dev, enabled); return 0; } @@ -320,14 +184,8 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) tm->tm_sec--; #endif - /* only keep the carry interrupt enabled if UIE is on */ - if (!(rtc->periodic_freq & PF_OXS)) - sh_rtc_setcie(dev, 0); - - dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " - "mday=%d, mon=%d, year=%d, wday=%d\n", - __func__, - tm->tm_sec, tm->tm_min, tm->tm_hour, + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); return 0; @@ -461,16 +319,17 @@ static const struct rtc_class_ops sh_rtc_ops = { .set_time = sh_rtc_set_time, .read_alarm = sh_rtc_read_alarm, .set_alarm = sh_rtc_set_alarm, - .proc = sh_rtc_proc, .alarm_irq_enable = sh_rtc_alarm_irq_enable, }; static int __init sh_rtc_probe(struct platform_device *pdev) { struct sh_rtc *rtc; - struct resource *res; + struct resource *res, *req_res; char clk_name[14]; int clk_id, ret; + unsigned int tmp; + resource_size_t regsize; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (unlikely(!rtc)) @@ -478,34 +337,32 @@ static int __init sh_rtc_probe(struct platform_device *pdev) spin_lock_init(&rtc->lock); - /* get periodic/carry/alarm irqs */ ret = platform_get_irq(pdev, 0); if (unlikely(ret <= 0)) { dev_err(&pdev->dev, "No IRQ resource\n"); return -ENOENT; } - rtc->periodic_irq = ret; - rtc->carry_irq = platform_get_irq(pdev, 1); - rtc->alarm_irq = platform_get_irq(pdev, 2); + if (!pdev->dev.of_node) + rtc->alarm_irq = platform_get_irq(pdev, 2); + else + rtc->alarm_irq = ret; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(res == NULL)) { + if (!res) { dev_err(&pdev->dev, "No IO resource\n"); return -ENOENT; } - rtc->regsize = resource_size(res); - - rtc->res = devm_request_mem_region(&pdev->dev, res->start, - rtc->regsize, pdev->name); - if (unlikely(!rtc->res)) + regsize = resource_size(res); + req_res = devm_request_mem_region(&pdev->dev, res->start, regsize, pdev->name); + if (!req_res) return -EBUSY; - rtc->regbase = devm_ioremap(&pdev->dev, rtc->res->start, rtc->regsize); - if (unlikely(!rtc->regbase)) + rtc->regbase = devm_ioremap(&pdev->dev, req_res->start, regsize); + if (!rtc->regbase) return -EINVAL; if (!pdev->dev.of_node) { @@ -515,8 +372,9 @@ static int __init sh_rtc_probe(struct platform_device *pdev) clk_id = 0; snprintf(clk_name, sizeof(clk_name), "rtc%d", clk_id); - } else + } else { snprintf(clk_name, sizeof(clk_name), "fck"); + } rtc->clk = devm_clk_get(&pdev->dev, clk_name); if (IS_ERR(rtc->clk)) { @@ -550,51 +408,19 @@ static int __init sh_rtc_probe(struct platform_device *pdev) } #endif - if (rtc->carry_irq <= 0) { - /* register shared periodic/carry/alarm irq */ - ret = devm_request_irq(&pdev->dev, rtc->periodic_irq, - sh_rtc_shared, 0, "sh-rtc", rtc); - if (unlikely(ret)) { - dev_err(&pdev->dev, - "request IRQ failed with %d, IRQ %d\n", ret, - rtc->periodic_irq); - goto err_unmap; - } - } else { - /* register periodic/carry/alarm irqs */ - ret = devm_request_irq(&pdev->dev, rtc->periodic_irq, - sh_rtc_periodic, 0, "sh-rtc period", rtc); - if (unlikely(ret)) { - dev_err(&pdev->dev, - "request period IRQ failed with %d, IRQ %d\n", - ret, rtc->periodic_irq); - goto err_unmap; - } - - ret = devm_request_irq(&pdev->dev, rtc->carry_irq, - sh_rtc_interrupt, 0, "sh-rtc carry", rtc); - if (unlikely(ret)) { - dev_err(&pdev->dev, - "request carry IRQ failed with %d, IRQ %d\n", - ret, rtc->carry_irq); - goto err_unmap; - } - - ret = devm_request_irq(&pdev->dev, rtc->alarm_irq, - sh_rtc_alarm, 0, "sh-rtc alarm", rtc); - if (unlikely(ret)) { - dev_err(&pdev->dev, - "request alarm IRQ failed with %d, IRQ %d\n", - ret, rtc->alarm_irq); - goto err_unmap; - } + ret = devm_request_irq(&pdev->dev, rtc->alarm_irq, sh_rtc_alarm, 0, "sh-rtc", rtc); + if (ret) { + dev_err(&pdev->dev, "request alarm IRQ failed with %d, IRQ %d\n", + ret, rtc->alarm_irq); + goto err_unmap; } platform_set_drvdata(pdev, rtc); /* everything disabled by default */ - sh_rtc_setaie(&pdev->dev, 0); - sh_rtc_setcie(&pdev->dev, 0); + tmp = readb(rtc->regbase + RCR1); + tmp &= ~(RCR1_CIE | RCR1_AIE); + writeb(tmp, rtc->regbase + RCR1); rtc->rtc_dev->ops = &sh_rtc_ops; rtc->rtc_dev->max_user_freq = 256; @@ -624,36 +450,27 @@ static void __exit sh_rtc_remove(struct platform_device *pdev) { struct sh_rtc *rtc = platform_get_drvdata(pdev); - sh_rtc_setaie(&pdev->dev, 0); - sh_rtc_setcie(&pdev->dev, 0); + sh_rtc_alarm_irq_enable(&pdev->dev, 0); clk_disable(rtc->clk); } -static void sh_rtc_set_irq_wake(struct device *dev, int enabled) +static int __maybe_unused sh_rtc_suspend(struct device *dev) { struct sh_rtc *rtc = dev_get_drvdata(dev); - irq_set_irq_wake(rtc->periodic_irq, enabled); - - if (rtc->carry_irq > 0) { - irq_set_irq_wake(rtc->carry_irq, enabled); - irq_set_irq_wake(rtc->alarm_irq, enabled); - } -} - -static int __maybe_unused sh_rtc_suspend(struct device *dev) -{ if (device_may_wakeup(dev)) - sh_rtc_set_irq_wake(dev, 1); + irq_set_irq_wake(rtc->alarm_irq, 1); return 0; } static int __maybe_unused sh_rtc_resume(struct device *dev) { + struct sh_rtc *rtc = dev_get_drvdata(dev); + if (device_may_wakeup(dev)) - sh_rtc_set_irq_wake(dev, 0); + irq_set_irq_wake(rtc->alarm_irq, 0); return 0; } @@ -684,8 +501,8 @@ static struct platform_driver sh_rtc_platform_driver __refdata = { module_platform_driver_probe(sh_rtc_platform_driver, sh_rtc_probe); MODULE_DESCRIPTION("SuperH on-chip RTC driver"); -MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, " - "Jamie Lenehan <lenehan@twibble.org>, " - "Angelo Castello <angelo.castello@st.com>"); +MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); +MODULE_AUTHOR("Jamie Lenehan <lenehan@twibble.org>"); +MODULE_AUTHOR("Angelo Castello <angelo.castello@st.com>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c index 1b715db47160..ef8fb88aab48 100644 --- a/drivers/rtc/rtc-stm32.c +++ b/drivers/rtc/rtc-stm32.c @@ -1283,7 +1283,6 @@ static struct platform_driver stm32_rtc_driver = { module_platform_driver(stm32_rtc_driver); -MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>"); MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver"); MODULE_LICENSE("GPL v2"); |