diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-04-01 09:37:18 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-04-01 09:37:18 -0700 |
commit | 6a34fdcca452457a530980be2561dab06da3627f (patch) | |
tree | c721750ae6e7b17b518a1b6d83dd158da54e5a87 /drivers/rtc | |
parent | e8b767f5e04097aaedcd6e06e2270f9fe5282696 (diff) | |
parent | 915593a7a663b2ad08b895a5f3ba8b19d89d4ebf (diff) | |
download | lwn-6a34fdcca452457a530980be2561dab06da3627f.tar.gz lwn-6a34fdcca452457a530980be2561dab06da3627f.zip |
Merge tag 'rtc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
Pull RTC updates from Alexandre Belloni:
"The bulk of the patches are about replacing the uie_unsupported struct
rtc_device member by a feature bit.
Subsystem:
- remove uie_unsupported, all users have been converted to clear
RTC_FEATURE_UPDATE_INTERRUPT and provide a reason
- RTCs with an alarm with a resolution of a minute are now letting
the core handle rounding down the alarm time
- fix use-after-free on device removal
New driver:
- OP-TEE RTC PTA
Drivers:
- sun6i: Add H616 support
- cmos: Fix the AltCentury for AMD platforms
- spear: set range"
* tag 'rtc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (56 commits)
rtc: check if __rtc_read_time was successful
rtc: gamecube: Fix refcount leak in gamecube_rtc_read_offset_from_sram
rtc: mc146818-lib: Fix the AltCentury for AMD platforms
rtc: optee: add RTC driver for OP-TEE RTC PTA
rtc: pm8xxx: Return -ENODEV if set_time disallowed
rtc: pm8xxx: Attach wake irq to device
clk: sunxi-ng: sun6i-rtc: include clk/sunxi-ng.h
rtc: remove uie_unsupported
rtc: xgene: stop using uie_unsupported
rtc: hym8563: switch to RTC_FEATURE_UPDATE_INTERRUPT
rtc: hym8563: let the core handle the alarm resolution
rtc: hym8563: switch to devm_rtc_allocate_device
rtc: efi: switch to RTC_FEATURE_UPDATE_INTERRUPT
rtc: efi: switch to devm_rtc_allocate_device
rtc: add new RTC_FEATURE_ALARM_WAKEUP_ONLY feature
rtc: spear: fix spear_rtc_read_time
rtc: spear: drop uie_unsupported
rtc: spear: set range
rtc: spear: switch to devm_rtc_allocate_device
rtc: pcf8563: switch to RTC_FEATURE_UPDATE_INTERRUPT
...
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/class.c | 12 | ||||
-rw-r--r-- | drivers/rtc/interface.c | 7 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1685.c | 16 | ||||
-rw-r--r-- | drivers/rtc/rtc-efi.c | 10 | ||||
-rw-r--r-- | drivers/rtc/rtc-gamecube.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-hym8563.c | 34 | ||||
-rw-r--r-- | drivers/rtc/rtc-m41t80.c | 6 | ||||
-rw-r--r-- | drivers/rtc/rtc-mc146818-lib.c | 22 | ||||
-rw-r--r-- | drivers/rtc/rtc-mpc5121.c | 17 | ||||
-rw-r--r-- | drivers/rtc/rtc-opal.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-optee.c | 362 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf2123.c | 3 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf2127.c | 19 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf85063.c | 3 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf8523.c | 15 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 16 | ||||
-rw-r--r-- | drivers/rtc/rtc-pl031.c | 6 | ||||
-rw-r--r-- | drivers/rtc/rtc-pm8xxx.c | 33 | ||||
-rw-r--r-- | drivers/rtc/rtc-spear.c | 25 | ||||
-rw-r--r-- | drivers/rtc/rtc-sun6i.c | 184 | ||||
-rw-r--r-- | drivers/rtc/rtc-wm8350.c | 11 | ||||
-rw-r--r-- | drivers/rtc/rtc-xgene.c | 2 |
25 files changed, 617 insertions, 202 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f6d6d4c26361..41c65b4d2baf 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1293,6 +1293,16 @@ config RTC_DRV_OPAL This driver can also be built as a module. If so, the module will be called rtc-opal. +config RTC_DRV_OPTEE + tristate "OP-TEE based RTC driver" + depends on OPTEE + help + Select this to get support for OP-TEE based RTC control on SoCs where + RTC are not accessible to the normal world (Linux). + + This driver can also be built as a module. If so, the module + will be called rtc-optee. + config RTC_DRV_ZYNQMP tristate "Xilinx Zynq Ultrascale+ MPSoC RTC" depends on OF && HAS_IOMEM diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index e92f3e943245..2d827d8261d5 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -115,6 +115,7 @@ obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o +obj-$(CONFIG_RTC_DRV_OPTEE) += rtc-optee.o obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 4b460c61f1d8..3c8eec2218df 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -26,6 +26,15 @@ struct class *rtc_class; static void rtc_device_release(struct device *dev) { struct rtc_device *rtc = to_rtc_device(dev); + struct timerqueue_head *head = &rtc->timerqueue; + struct timerqueue_node *node; + + mutex_lock(&rtc->ops_lock); + while ((node = timerqueue_getnext(head))) + timerqueue_del(head, node); + mutex_unlock(&rtc->ops_lock); + + cancel_work_sync(&rtc->irqwork); ida_simple_remove(&rtc_ida, rtc->id); mutex_destroy(&rtc->ops_lock); @@ -390,9 +399,6 @@ int __devm_rtc_register_device(struct module *owner, struct rtc_device *rtc) if (!rtc->ops->set_alarm) clear_bit(RTC_FEATURE_ALARM, rtc->features); - if (rtc->uie_unsupported) - clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); - if (rtc->ops->set_offset) set_bit(RTC_FEATURE_CORRECTION, rtc->features); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index d8e835798153..9edd662c69ac 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -804,9 +804,13 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); struct rtc_time tm; ktime_t now; + int err; + + err = __rtc_read_time(rtc, &tm); + if (err) + return err; timer->enabled = 1; - __rtc_read_time(rtc, &tm); now = rtc_tm_to_ktime(tm); /* Skip over expired timers */ @@ -820,7 +824,6 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) trace_rtc_timer_enqueue(timer); if (!next || ktime_before(timer->node.expires, next->expires)) { struct rtc_wkalrm alarm; - int err; alarm.time = rtc_ktime_to_tm(timer->node.expires); alarm.enabled = 1; diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 336cb9aa5e33..d51565bcc189 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -1955,7 +1955,7 @@ static int ds1307_probe(struct i2c_client *client, dev_info(ds1307->dev, "'wakeup-source' is set, request for an IRQ is disabled!\n"); /* We cannot support UIE mode if we do not have an IRQ line */ - ds1307->rtc->uie_unsupported = 1; + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, ds1307->rtc->features); } if (want_irq) { diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c index 75db7ab654a5..a24331ba8a5f 100644 --- a/drivers/rtc/rtc-ds1685.c +++ b/drivers/rtc/rtc-ds1685.c @@ -1273,7 +1273,7 @@ ds1685_rtc_probe(struct platform_device *pdev) /* See if the platform doesn't support UIE. */ if (pdata->uie_unsupported) - rtc_dev->uie_unsupported = 1; + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc_dev->features); rtc->dev = rtc_dev; @@ -1285,13 +1285,10 @@ ds1685_rtc_probe(struct platform_device *pdev) * there won't be an automatic way of notifying the kernel about it, * unless ctrlc is explicitly polled. */ - if (!pdata->no_irq) { - ret = platform_get_irq(pdev, 0); - if (ret <= 0) - return ret; - - rtc->irq_num = ret; - + rtc->irq_num = platform_get_irq(pdev, 0); + if (rtc->irq_num <= 0) { + clear_bit(RTC_FEATURE_ALARM, rtc_dev->features); + } else { /* Request an IRQ. */ ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_num, NULL, ds1685_rtc_irq_handler, @@ -1305,7 +1302,6 @@ ds1685_rtc_probe(struct platform_device *pdev) rtc->irq_num = 0; } } - rtc->no_irq = pdata->no_irq; /* Setup complete. */ ds1685_rtc_switch_to_bank0(rtc); @@ -1394,7 +1390,7 @@ ds1685_rtc_poweroff(struct platform_device *pdev) * have been taken care of by the shutdown scripts and this * is the final function call. */ - if (!rtc->no_irq) + if (rtc->irq_num) disable_irq_nosync(rtc->irq_num); /* Oscillator must be on and the countdown chain enabled. */ diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index 138c5e0046c8..11850c2880ad 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -261,15 +261,17 @@ static int __init efi_rtc_probe(struct platform_device *dev) if (efi.get_time(&eft, &cap) != EFI_SUCCESS) return -ENODEV; - rtc = devm_rtc_device_register(&dev->dev, "rtc-efi", &efi_rtc_ops, - THIS_MODULE); + rtc = devm_rtc_allocate_device(&dev->dev); if (IS_ERR(rtc)) return PTR_ERR(rtc); - rtc->uie_unsupported = 1; platform_set_drvdata(dev, rtc); - return 0; + rtc->ops = &efi_rtc_ops; + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->features); + + return devm_rtc_register_device(rtc); } static struct platform_driver efi_rtc_driver = { diff --git a/drivers/rtc/rtc-gamecube.c b/drivers/rtc/rtc-gamecube.c index f717b36f4738..18ca3b38b2d0 100644 --- a/drivers/rtc/rtc-gamecube.c +++ b/drivers/rtc/rtc-gamecube.c @@ -235,6 +235,7 @@ static int gamecube_rtc_read_offset_from_sram(struct priv *d) } ret = of_address_to_resource(np, 0, &res); + of_node_put(np); if (ret) { pr_err("no io memory range found\n"); return -1; diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index 0751cae27285..90e602e99d03 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -220,24 +220,6 @@ static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) u8 buf[4]; int ret; - /* - * The alarm has no seconds so deal with it - */ - if (alm_tm->tm_sec) { - alm_tm->tm_sec = 0; - alm_tm->tm_min++; - if (alm_tm->tm_min >= 60) { - alm_tm->tm_min = 0; - alm_tm->tm_hour++; - if (alm_tm->tm_hour >= 24) { - alm_tm->tm_hour = 0; - alm_tm->tm_mday++; - if (alm_tm->tm_mday > 31) - alm_tm->tm_mday = 0; - } - } - } - ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2); if (ret < 0) return ret; @@ -523,6 +505,10 @@ static int hym8563_probe(struct i2c_client *client, if (!hym8563) return -ENOMEM; + hym8563->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(hym8563->rtc)) + return PTR_ERR(hym8563->rtc); + hym8563->client = client; i2c_set_clientdata(client, hym8563); @@ -557,19 +543,15 @@ static int hym8563_probe(struct i2c_client *client, dev_dbg(&client->dev, "rtc information is %s\n", (ret & HYM8563_SEC_VL) ? "invalid" : "valid"); - hym8563->rtc = devm_rtc_device_register(&client->dev, client->name, - &hym8563_rtc_ops, THIS_MODULE); - if (IS_ERR(hym8563->rtc)) - return PTR_ERR(hym8563->rtc); - - /* the hym8563 alarm only supports a minute accuracy */ - hym8563->rtc->uie_unsupported = 1; + hym8563->rtc->ops = &hym8563_rtc_ops; + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, hym8563->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, hym8563->rtc->features); #ifdef CONFIG_COMMON_CLK hym8563_clkout_register_clk(hym8563); #endif - return 0; + return devm_rtc_register_device(hym8563->rtc); } static const struct i2c_device_id hym8563_id[] = { diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 6d383b629d20..d868458cd40e 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -932,10 +932,8 @@ static int m41t80_probe(struct i2c_client *client, m41t80_data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; m41t80_data->rtc->range_max = RTC_TIMESTAMP_END_2099; - if (client->irq <= 0) { - /* We cannot support UIE mode if we do not have an IRQ line */ - m41t80_data->rtc->uie_unsupported = 1; - } + if (client->irq <= 0) + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, m41t80_data->rtc->features); /* Make sure HT (Halt Update) bit is cleared */ rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR); diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c index ae9f131b43c0..522449b25921 100644 --- a/drivers/rtc/rtc-mc146818-lib.c +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -176,6 +176,17 @@ int mc146818_get_time(struct rtc_time *time) } EXPORT_SYMBOL_GPL(mc146818_get_time); +/* AMD systems don't allow access to AltCentury with DV1 */ +static bool apply_amd_register_a_behavior(void) +{ +#ifdef CONFIG_X86 + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD || + boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) + return true; +#endif + return false; +} + /* Set the current date and time in the real time clock. */ int mc146818_set_time(struct rtc_time *time) { @@ -232,8 +243,10 @@ int mc146818_set_time(struct rtc_time *time) if (yrs >= 100) yrs -= 100; - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) - || RTC_ALWAYS_BCD) { + spin_lock_irqsave(&rtc_lock, flags); + save_control = CMOS_READ(RTC_CONTROL); + spin_unlock_irqrestore(&rtc_lock, flags); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { sec = bin2bcd(sec); min = bin2bcd(min); hrs = bin2bcd(hrs); @@ -247,7 +260,10 @@ int mc146818_set_time(struct rtc_time *time) save_control = CMOS_READ(RTC_CONTROL); CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); save_freq_select = CMOS_READ(RTC_FREQ_SELECT); - CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + if (apply_amd_register_a_behavior()) + CMOS_WRITE((save_freq_select & ~RTC_AMD_BANK_SELECT), RTC_FREQ_SELECT); + else + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); #ifdef CONFIG_MACH_DECSTATION CMOS_WRITE(real_yrs, RTC_DEC_YEAR); diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index bb2ea9bc56f2..6d7656a75cae 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -210,20 +210,6 @@ static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); struct mpc5121_rtc_regs __iomem *regs = rtc->regs; - /* - * the alarm has no seconds so deal with it - */ - if (alarm->time.tm_sec) { - alarm->time.tm_sec = 0; - alarm->time.tm_min++; - if (alarm->time.tm_min >= 60) { - alarm->time.tm_min = 0; - alarm->time.tm_hour++; - if (alarm->time.tm_hour >= 24) - alarm->time.tm_hour = 0; - } - } - alarm->time.tm_mday = -1; alarm->time.tm_mon = -1; alarm->time.tm_year = -1; @@ -349,7 +335,8 @@ static int mpc5121_rtc_probe(struct platform_device *op) } rtc->rtc->ops = &mpc5200_rtc_ops; - rtc->rtc->uie_unsupported = 1; + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtc->features); rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; rtc->rtc->range_max = 65733206399ULL; /* 4052-12-31 23:59:59 */ diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c index f8f49a969c23..ad41aaf8a17f 100644 --- a/drivers/rtc/rtc-opal.c +++ b/drivers/rtc/rtc-opal.c @@ -250,7 +250,7 @@ static int opal_rtc_probe(struct platform_device *pdev) rtc->ops = &opal_rtc_ops; rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; rtc->range_max = RTC_TIMESTAMP_END_9999; - rtc->uie_unsupported = 1; + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); return devm_rtc_register_device(rtc); } diff --git a/drivers/rtc/rtc-optee.c b/drivers/rtc/rtc-optee.c new file mode 100644 index 000000000000..9f8b5d4a8f6b --- /dev/null +++ b/drivers/rtc/rtc-optee.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Microchip. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/tee_drv.h> + +#define RTC_INFO_VERSION 0x1 + +#define TA_CMD_RTC_GET_INFO 0x0 +#define TA_CMD_RTC_GET_TIME 0x1 +#define TA_CMD_RTC_SET_TIME 0x2 +#define TA_CMD_RTC_GET_OFFSET 0x3 +#define TA_CMD_RTC_SET_OFFSET 0x4 + +#define TA_RTC_FEATURE_CORRECTION BIT(0) + +struct optee_rtc_time { + u32 tm_sec; + u32 tm_min; + u32 tm_hour; + u32 tm_mday; + u32 tm_mon; + u32 tm_year; + u32 tm_wday; +}; + +struct optee_rtc_info { + u64 version; + u64 features; + struct optee_rtc_time range_min; + struct optee_rtc_time range_max; +}; + +/** + * struct optee_rtc - OP-TEE RTC private data + * @dev: OP-TEE based RTC device. + * @ctx: OP-TEE context handler. + * @session_id: RTC TA session identifier. + * @shm: Memory pool shared with RTC device. + * @features: Bitfield of RTC features + */ +struct optee_rtc { + struct device *dev; + struct tee_context *ctx; + u32 session_id; + struct tee_shm *shm; + u64 features; +}; + +static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct optee_rtc_time *optee_tm; + struct tee_param param[4] = {0}; + int ret; + + inv_arg.func = TA_CMD_RTC_GET_TIME; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + /* Fill invoke cmd params */ + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[0].u.memref.shm = priv->shm; + param[0].u.memref.size = sizeof(struct optee_rtc_time); + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + optee_tm = tee_shm_get_va(priv->shm, 0); + if (IS_ERR(optee_tm)) + return PTR_ERR(optee_tm); + + if (param[0].u.memref.size != sizeof(*optee_tm)) + return -EPROTO; + + tm->tm_sec = optee_tm->tm_sec; + tm->tm_min = optee_tm->tm_min; + tm->tm_hour = optee_tm->tm_hour; + tm->tm_mday = optee_tm->tm_mday; + tm->tm_mon = optee_tm->tm_mon; + tm->tm_year = optee_tm->tm_year - 1900; + tm->tm_wday = optee_tm->tm_wday; + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + + return 0; +} + +static int optee_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[4] = {0}; + struct optee_rtc_time optee_tm; + void *rtc_data; + int ret; + + optee_tm.tm_sec = tm->tm_sec; + optee_tm.tm_min = tm->tm_min; + optee_tm.tm_hour = tm->tm_hour; + optee_tm.tm_mday = tm->tm_mday; + optee_tm.tm_mon = tm->tm_mon; + optee_tm.tm_year = tm->tm_year + 1900; + optee_tm.tm_wday = tm->tm_wday; + + inv_arg.func = TA_CMD_RTC_SET_TIME; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[0].u.memref.shm = priv->shm; + param[0].u.memref.size = sizeof(struct optee_rtc_time); + + rtc_data = tee_shm_get_va(priv->shm, 0); + if (IS_ERR(rtc_data)) + return PTR_ERR(rtc_data); + + memcpy(rtc_data, &optee_tm, sizeof(struct optee_rtc_time)); + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + return 0; +} + +static int optee_rtc_readoffset(struct device *dev, long *offset) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[4] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_CORRECTION)) + return -EOPNOTSUPP; + + inv_arg.func = TA_CMD_RTC_GET_OFFSET; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + *offset = param[0].u.value.a; + + return 0; +} + +static int optee_rtc_setoffset(struct device *dev, long offset) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[4] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_CORRECTION)) + return -EOPNOTSUPP; + + inv_arg.func = TA_CMD_RTC_SET_OFFSET; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = offset; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + return 0; +} + +static const struct rtc_class_ops optee_rtc_ops = { + .read_time = optee_rtc_readtime, + .set_time = optee_rtc_settime, + .set_offset = optee_rtc_setoffset, + .read_offset = optee_rtc_readoffset, +}; + +static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc, + u64 *features) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[4] = {0}; + struct optee_rtc_info *info; + struct optee_rtc_time *tm; + int ret; + + inv_arg.func = TA_CMD_RTC_GET_INFO; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[0].u.memref.shm = priv->shm; + param[0].u.memref.size = sizeof(*info); + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + info = tee_shm_get_va(priv->shm, 0); + if (IS_ERR(info)) + return PTR_ERR(info); + + if (param[0].u.memref.size != sizeof(*info)) + return -EPROTO; + + if (info->version != RTC_INFO_VERSION) + return -EPROTO; + + *features = info->features; + + tm = &info->range_min; + rtc->range_min = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + tm = &info->range_max; + rtc->range_max = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + + return 0; +} + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + if (ver->impl_id == TEE_IMPL_ID_OPTEE) + return 1; + else + return 0; +} + +static int optee_rtc_probe(struct device *dev) +{ + struct tee_client_device *rtc_device = to_tee_client_device(dev); + struct tee_ioctl_open_session_arg sess_arg; + struct optee_rtc *priv; + struct rtc_device *rtc; + struct tee_shm *shm; + int ret, err; + + memset(&sess_arg, 0, sizeof(sess_arg)); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + /* Open context with TEE driver */ + priv->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); + if (IS_ERR(priv->ctx)) + return -ENODEV; + + /* Open session with rtc Trusted App */ + export_uuid(sess_arg.uuid, &rtc_device->id.uuid); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + + ret = tee_client_open_session(priv->ctx, &sess_arg, NULL); + if (ret < 0 || sess_arg.ret != 0) { + dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret); + err = -EINVAL; + goto out_ctx; + } + priv->session_id = sess_arg.session; + + shm = tee_shm_alloc_kernel_buf(priv->ctx, sizeof(struct optee_rtc_info)); + if (IS_ERR(shm)) { + dev_err(priv->dev, "tee_shm_alloc_kernel_buf failed\n"); + err = PTR_ERR(shm); + goto out_sess; + } + + priv->shm = shm; + priv->dev = dev; + dev_set_drvdata(dev, priv); + + rtc->ops = &optee_rtc_ops; + + err = optee_rtc_read_info(dev, rtc, &priv->features); + if (err) { + dev_err(dev, "Failed to get RTC features from OP-TEE\n"); + goto out_shm; + } + + err = devm_rtc_register_device(rtc); + if (err) + goto out_shm; + + /* + * We must clear this bit after registering because rtc_register_device + * will set it if it sees that .set_offset is provided. + */ + if (!(priv->features & TA_RTC_FEATURE_CORRECTION)) + clear_bit(RTC_FEATURE_CORRECTION, rtc->features); + + return 0; + +out_shm: + tee_shm_free(priv->shm); +out_sess: + tee_client_close_session(priv->ctx, priv->session_id); +out_ctx: + tee_client_close_context(priv->ctx); + + return err; +} + +static int optee_rtc_remove(struct device *dev) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + + tee_client_close_session(priv->ctx, priv->session_id); + tee_client_close_context(priv->ctx); + + return 0; +} + +static const struct tee_client_device_id optee_rtc_id_table[] = { + {UUID_INIT(0xf389f8c8, 0x845f, 0x496c, + 0x8b, 0xbe, 0xd6, 0x4b, 0xd2, 0x4c, 0x92, 0xfd)}, + {} +}; + +MODULE_DEVICE_TABLE(tee, optee_rtc_id_table); + +static struct tee_client_driver optee_rtc_driver = { + .id_table = optee_rtc_id_table, + .driver = { + .name = "optee_rtc", + .bus = &tee_bus_type, + .probe = optee_rtc_probe, + .remove = optee_rtc_remove, + }, +}; + +static int __init optee_rtc_mod_init(void) +{ + return driver_register(&optee_rtc_driver.driver); +} + +static void __exit optee_rtc_mod_exit(void) +{ + driver_unregister(&optee_rtc_driver.driver); +} + +module_init(optee_rtc_mod_init); +module_exit(optee_rtc_mod_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>"); +MODULE_DESCRIPTION("OP-TEE based RTC driver"); diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c index 7473e6c8a183..e13b5e695d06 100644 --- a/drivers/rtc/rtc-pcf2123.c +++ b/drivers/rtc/rtc-pcf2123.c @@ -427,7 +427,8 @@ static int pcf2123_probe(struct spi_device *spi) * support to this driver to generate interrupts more than once * per minute. */ - rtc->uie_unsupported = 1; + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); rtc->ops = &pcf2123_rtc_ops; rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; rtc->range_max = RTC_TIMESTAMP_END_2099; diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c index 81a5b1f2e68c..63b275b014bd 100644 --- a/drivers/rtc/rtc-pcf2127.c +++ b/drivers/rtc/rtc-pcf2127.c @@ -374,7 +374,8 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127) static int pcf2127_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct pcf2127 *pcf2127 = dev_get_drvdata(dev); - unsigned int buf[5], ctrl2; + u8 buf[5]; + unsigned int ctrl2; int ret; ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2); @@ -655,13 +656,25 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap, pcf2127->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; pcf2127->rtc->range_max = RTC_TIMESTAMP_END_2099; pcf2127->rtc->set_start_time = true; /* Sets actual start to 1970 */ - pcf2127->rtc->uie_unsupported = 1; + set_bit(RTC_FEATURE_ALARM_RES_2S, pcf2127->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf2127->rtc->features); clear_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features); if (alarm_irq > 0) { + unsigned long flags; + + /* + * If flags = 0, devm_request_threaded_irq() will use IRQ flags + * obtained from device tree. + */ + if (dev_fwnode(dev)) + flags = 0; + else + flags = IRQF_TRIGGER_LOW; + ret = devm_request_threaded_irq(dev, alarm_irq, NULL, pcf2127_rtc_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, + flags | IRQF_ONESHOT, dev_name(dev), dev); if (ret) { dev_err(dev, "failed to request alarm irq\n"); diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c index df2b072c394d..9760824ec199 100644 --- a/drivers/rtc/rtc-pcf85063.c +++ b/drivers/rtc/rtc-pcf85063.c @@ -616,7 +616,8 @@ static int pcf85063_probe(struct i2c_client *client) pcf85063->rtc->ops = &pcf85063_rtc_ops; pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099; - pcf85063->rtc->uie_unsupported = 1; + set_bit(RTC_FEATURE_ALARM_RES_2S, pcf85063->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf85063->rtc->features); clear_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features); if (config->has_alarms && client->irq > 0) { diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index c93acade7205..b1b1943de844 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -212,14 +212,6 @@ static int pcf8523_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) if (err < 0) return err; - /* The alarm has no seconds, round up to nearest minute */ - if (tm->time.tm_sec) { - time64_t alarm_time = rtc_tm_to_time64(&tm->time); - - alarm_time += 60 - tm->time.tm_sec; - rtc_time64_to_tm(alarm_time, &tm->time); - } - regs[0] = bin2bcd(tm->time.tm_min); regs[1] = bin2bcd(tm->time.tm_hour); regs[2] = bin2bcd(tm->time.tm_mday); @@ -240,9 +232,9 @@ static int pcf8523_param_get(struct device *dev, struct rtc_param *param) { struct pcf8523 *pcf8523 = dev_get_drvdata(dev); int ret; + u32 value; switch(param->param) { - u32 value; case RTC_PARAM_BACKUP_SWITCH_MODE: ret = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL3, &value); @@ -279,9 +271,9 @@ static int pcf8523_param_get(struct device *dev, struct rtc_param *param) static int pcf8523_param_set(struct device *dev, struct rtc_param *param) { struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + u8 mode; switch(param->param) { - u8 mode; case RTC_PARAM_BACKUP_SWITCH_MODE: switch (param->uvalue) { case RTC_BSM_DISABLED: @@ -450,7 +442,8 @@ static int pcf8523_probe(struct i2c_client *client, rtc->ops = &pcf8523_rtc_ops; rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; rtc->range_max = RTC_TIMESTAMP_END_2099; - rtc->uie_unsupported = 1; + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); if (client->irq > 0) { err = regmap_write(pcf8523->regmap, PCF8523_TMR_CLKOUT_CTRL, 0x38); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index c8bddfb94129..9d06813e2e6d 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -330,19 +330,6 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) unsigned char buf[4]; int err; - /* The alarm has no seconds, round up to nearest minute */ - if (tm->time.tm_sec) { - time64_t alarm_time = rtc_tm_to_time64(&tm->time); - - alarm_time += 60 - tm->time.tm_sec; - rtc_time64_to_tm(alarm_time, &tm->time); - } - - 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); @@ -565,7 +552,8 @@ static int pcf8563_probe(struct i2c_client *client, pcf8563->rtc->ops = &pcf8563_rtc_ops; /* the pcf8563 alarm only supports a minute accuracy */ - pcf8563->rtc->uie_unsupported = 1; + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, pcf8563->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf8563->rtc->features); pcf8563->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; pcf8563->rtc->range_max = RTC_TIMESTAMP_END_2099; pcf8563->rtc->set_start_time = true; diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index e38ee8848385..bad6a5d9c683 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -350,9 +350,6 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) } } - if (!adev->irq[0]) - clear_bit(RTC_FEATURE_ALARM, ldata->rtc->features); - device_init_wakeup(&adev->dev, true); ldata->rtc = devm_rtc_allocate_device(&adev->dev); if (IS_ERR(ldata->rtc)) { @@ -360,6 +357,9 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) goto out; } + if (!adev->irq[0]) + clear_bit(RTC_FEATURE_ALARM, ldata->rtc->features); + ldata->rtc->ops = ops; ldata->rtc->range_min = vendor->range_min; ldata->rtc->range_max = vendor->range_max; diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index 29a1c65661e9..dc6d1476baa5 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -7,6 +7,7 @@ #include <linux/rtc.h> #include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/pm_wakeirq.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -83,7 +84,7 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; if (!rtc_dd->allow_set_time) - return -EACCES; + return -ENODEV; secs = rtc_tm_to_time64(tm); @@ -527,40 +528,28 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) return rc; } - return devm_rtc_register_device(rtc_dd->rtc); -} - -#ifdef CONFIG_PM_SLEEP -static int pm8xxx_rtc_resume(struct device *dev) -{ - struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + rc = devm_rtc_register_device(rtc_dd->rtc); + if (rc) + return rc; - if (device_may_wakeup(dev)) - disable_irq_wake(rtc_dd->rtc_alarm_irq); + rc = dev_pm_set_wake_irq(&pdev->dev, rtc_dd->rtc_alarm_irq); + if (rc) + return rc; return 0; } -static int pm8xxx_rtc_suspend(struct device *dev) +static int pm8xxx_remove(struct platform_device *pdev) { - struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); - - if (device_may_wakeup(dev)) - enable_irq_wake(rtc_dd->rtc_alarm_irq); - + dev_pm_clear_wake_irq(&pdev->dev); return 0; } -#endif - -static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, - pm8xxx_rtc_suspend, - pm8xxx_rtc_resume); static struct platform_driver pm8xxx_rtc_driver = { .probe = pm8xxx_rtc_probe, + .remove = pm8xxx_remove, .driver = { .name = "rtc-pm8xxx", - .pm = &pm8xxx_rtc_pm_ops, .of_match_table = pm8xxx_id_table, }, }; diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index b4a520056b1a..d4777b01ab22 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -204,8 +204,10 @@ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) /* we don't report wday/yday/isdst ... */ rtc_wait_not_busy(config); - time = readl(config->ioaddr + TIME_REG); - date = readl(config->ioaddr + DATE_REG); + do { + time = readl(config->ioaddr + TIME_REG); + date = readl(config->ioaddr + DATE_REG); + } while (time == readl(config->ioaddr + TIME_REG)); tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; @@ -352,6 +354,10 @@ static int spear_rtc_probe(struct platform_device *pdev) if (!config) return -ENOMEM; + config->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(config->rtc)) + return PTR_ERR(config->rtc); + /* alarm irqs */ irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -380,16 +386,13 @@ static int spear_rtc_probe(struct platform_device *pdev) spin_lock_init(&config->lock); platform_set_drvdata(pdev, config); - config->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &spear_rtc_ops, THIS_MODULE); - if (IS_ERR(config->rtc)) { - dev_err(&pdev->dev, "can't register RTC device, err %ld\n", - PTR_ERR(config->rtc)); - status = PTR_ERR(config->rtc); - goto err_disable_clock; - } + config->rtc->ops = &spear_rtc_ops; + config->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; + config->rtc->range_min = RTC_TIMESTAMP_END_9999; - config->rtc->uie_unsupported = 1; + status = devm_rtc_register_device(config->rtc); + if (status) + goto err_disable_clock; if (!device_can_wakeup(&pdev->dev)) device_init_wakeup(&pdev->dev, 1); diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 711832c758ae..5b3e4da63406 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -13,6 +13,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/clk/sunxi-ng.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/fs.h> @@ -48,7 +49,8 @@ /* Alarm 0 (counter) */ #define SUN6I_ALRM_COUNTER 0x0020 -#define SUN6I_ALRM_CUR_VAL 0x0024 +/* This holds the remaining alarm seconds on older SoCs (current value) */ +#define SUN6I_ALRM_COUNTER_HMS 0x0024 #define SUN6I_ALRM_EN 0x0028 #define SUN6I_ALRM_EN_CNT_EN BIT(0) #define SUN6I_ALRM_IRQ_EN 0x002c @@ -110,6 +112,8 @@ #define SUN6I_YEAR_MIN 1970 #define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900) +#define SECS_PER_DAY (24 * 3600ULL) + /* * There are other differences between models, including: * @@ -133,12 +137,15 @@ struct sun6i_rtc_clk_data { unsigned int has_auto_swt : 1; }; +#define RTC_LINEAR_DAY BIT(0) + struct sun6i_rtc_dev { struct rtc_device *rtc; const struct sun6i_rtc_clk_data *data; void __iomem *base; int irq; - unsigned long alarm; + time64_t alarm; + unsigned long flags; struct clk_hw hw; struct clk_hw *int_osc; @@ -363,23 +370,6 @@ CLK_OF_DECLARE_DRIVER(sun8i_h3_rtc_clk, "allwinner,sun8i-h3-rtc", CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc", sun8i_h3_rtc_clk_init); -static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = { - .rc_osc_rate = 16000000, - .fixed_prescaler = 32, - .has_prescaler = 1, - .has_out_clk = 1, - .export_iosc = 1, - .has_losc_en = 1, - .has_auto_swt = 1, -}; - -static void __init sun50i_h6_rtc_clk_init(struct device_node *node) -{ - sun6i_rtc_clk_init(node, &sun50i_h6_rtc_data); -} -CLK_OF_DECLARE_DRIVER(sun50i_h6_rtc_clk, "allwinner,sun50i-h6-rtc", - sun50i_h6_rtc_clk_init); - /* * The R40 user manual is self-conflicting on whether the prescaler is * fixed or configurable. The clock diagram shows it as fixed, but there @@ -467,22 +457,30 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) } while ((date != readl(chip->base + SUN6I_RTC_YMD)) || (time != readl(chip->base + SUN6I_RTC_HMS))); + if (chip->flags & RTC_LINEAR_DAY) { + /* + * Newer chips store a linear day number, the manual + * does not mandate any epoch base. The BSP driver uses + * the UNIX epoch, let's just copy that, as it's the + * easiest anyway. + */ + rtc_time64_to_tm((date & 0xffff) * SECS_PER_DAY, rtc_tm); + } else { + rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date); + rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date) - 1; + rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date); + + /* + * switch from (data_year->min)-relative offset to + * a (1900)-relative one + */ + rtc_tm->tm_year += SUN6I_YEAR_OFF; + } + 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 0; } @@ -510,36 +508,54 @@ 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; - } + time64_t time_set; + u32 counter_val, counter_val_hms; + int ret; time_set = rtc_tm_to_time64(alrm_tm); - time_now = rtc_tm_to_time64(&tm_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; + if (chip->flags & RTC_LINEAR_DAY) { + /* + * The alarm registers hold the actual alarm time, encoded + * in the same way (linear day + HMS) as the current time. + */ + counter_val_hms = SUN6I_TIME_SET_SEC_VALUE(alrm_tm->tm_sec) | + SUN6I_TIME_SET_MIN_VALUE(alrm_tm->tm_min) | + SUN6I_TIME_SET_HOUR_VALUE(alrm_tm->tm_hour); + /* The division will cut off the H:M:S part of alrm_tm. */ + counter_val = div_u64(rtc_tm_to_time64(alrm_tm), SECS_PER_DAY); + } else { + /* The alarm register holds the number of seconds left. */ + time64_t time_now; + + ret = sun6i_rtc_gettime(dev, &tm_now); + if (ret < 0) { + dev_err(dev, "Error in getting time\n"); + return -EINVAL; + } + + time_now = rtc_tm_to_time64(&tm_now); + if (time_set <= time_now) { + dev_err(dev, "Date to set in the past\n"); + return -EINVAL; + } + if ((time_set - time_now) > U32_MAX) { + dev_err(dev, "Date too far in the future\n"); + return -EINVAL; + } + + counter_val = time_set - time_now; } sun6i_rtc_setaie(0, chip); writel(0, chip->base + SUN6I_ALRM_COUNTER); + if (chip->flags & RTC_LINEAR_DAY) + writel(0, chip->base + SUN6I_ALRM_COUNTER_HMS); usleep_range(100, 300); - writel(time_gap, chip->base + SUN6I_ALRM_COUNTER); + writel(counter_val, chip->base + SUN6I_ALRM_COUNTER); + if (chip->flags & RTC_LINEAR_DAY) + writel(counter_val_hms, chip->base + SUN6I_ALRM_COUNTER_HMS); chip->alarm = time_set; sun6i_rtc_setaie(wkalrm->enabled, chip); @@ -571,20 +587,25 @@ static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) u32 date = 0; u32 time = 0; - 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(rtc_tm->tm_year + SUN6I_YEAR_MIN)) - 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); + if (chip->flags & RTC_LINEAR_DAY) { + /* The division will cut off the H:M:S part of rtc_tm. */ + date = div_u64(rtc_tm_to_time64(rtc_tm), SECS_PER_DAY); + } else { + 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(rtc_tm->tm_year + SUN6I_YEAR_MIN)) + date |= SUN6I_LEAP_SET_VALUE(1); + } + /* Check whether registers are writable */ if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, SUN6I_LOSC_CTRL_ACC_MASK, 50)) { @@ -668,11 +689,35 @@ static int sun6i_rtc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(sun6i_rtc_pm_ops, sun6i_rtc_suspend, sun6i_rtc_resume); +static void sun6i_rtc_bus_clk_cleanup(void *data) +{ + struct clk *bus_clk = data; + + clk_disable_unprepare(bus_clk); +} + static int sun6i_rtc_probe(struct platform_device *pdev) { struct sun6i_rtc_dev *chip = sun6i_rtc; + struct device *dev = &pdev->dev; + struct clk *bus_clk; int ret; + bus_clk = devm_clk_get_optional(dev, "bus"); + if (IS_ERR(bus_clk)) + return PTR_ERR(bus_clk); + + if (bus_clk) { + ret = clk_prepare_enable(bus_clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, sun6i_rtc_bus_clk_cleanup, + bus_clk); + if (ret) + return ret; + } + if (!chip) { chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -683,10 +728,18 @@ static int sun6i_rtc_probe(struct platform_device *pdev) chip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(chip->base)) return PTR_ERR(chip->base); + + if (IS_REACHABLE(CONFIG_SUN6I_RTC_CCU)) { + ret = sun6i_rtc_ccu_probe(dev, chip->base); + if (ret) + return ret; + } } platform_set_drvdata(pdev, chip); + chip->flags = (unsigned long)of_device_get_match_data(&pdev->dev); + chip->irq = platform_get_irq(pdev, 0); if (chip->irq < 0) return chip->irq; @@ -733,7 +786,10 @@ static int sun6i_rtc_probe(struct platform_device *pdev) return PTR_ERR(chip->rtc); chip->rtc->ops = &sun6i_rtc_ops; - chip->rtc->range_max = 2019686399LL; /* 2033-12-31 23:59:59 */ + if (chip->flags & RTC_LINEAR_DAY) + chip->rtc->range_max = (65536 * SECS_PER_DAY) - 1; + else + chip->rtc->range_max = 2019686399LL; /* 2033-12-31 23:59:59 */ ret = devm_rtc_register_device(chip->rtc); if (ret) @@ -758,6 +814,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = { { .compatible = "allwinner,sun8i-v3-rtc" }, { .compatible = "allwinner,sun50i-h5-rtc" }, { .compatible = "allwinner,sun50i-h6-rtc" }, + { .compatible = "allwinner,sun50i-h616-rtc", + .data = (void *)RTC_LINEAR_DAY }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c index 2018614f258f..6eaa9321c074 100644 --- a/drivers/rtc/rtc-wm8350.c +++ b/drivers/rtc/rtc-wm8350.c @@ -432,14 +432,21 @@ static int wm8350_rtc_probe(struct platform_device *pdev) return ret; } - wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC, + ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350_rtc_update_handler, 0, "RTC Seconds", wm8350); + if (ret) + return ret; + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); - wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM, + ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM, wm8350_rtc_alarm_handler, 0, "RTC Alarm", wm8350); + if (ret) { + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350); + return ret; + } return 0; } diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c index cf68a9b1c9eb..d3d0054e21fd 100644 --- a/drivers/rtc/rtc-xgene.c +++ b/drivers/rtc/rtc-xgene.c @@ -180,8 +180,6 @@ static int xgene_rtc_probe(struct platform_device *pdev) return ret; } - /* HW does not support update faster than 1 seconds */ - pdata->rtc->uie_unsupported = 1; pdata->rtc->ops = &xgene_rtc_ops; pdata->rtc->range_max = U32_MAX; |