From: Alexander Koch <[email protected]> Add simple driver for i.MX SNVS RTC IP present in NXP i.MX SoCs.
Signed-off-by: Alexander Koch <[email protected]> Signed-off-by: Marek Vasut <[email protected]> --- Cc: Alexander Feilke <[email protected]> Cc: Alexander Koch <[email protected]> Cc: Johan Jonker <[email protected]> Cc: Michael Walle <[email protected]> Cc: Tom Rini <[email protected]> Cc: [email protected] --- drivers/rtc/Kconfig | 7 +++ drivers/rtc/Makefile | 1 + drivers/rtc/snvs-rtc.c | 118 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 drivers/rtc/snvs-rtc.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 6fb3019a644..5b44cc99c97 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -296,6 +296,13 @@ config SPL_RTC_SANDBOX emulator and is used to test the RTC uclasses and associated code, as well as the I2C subsystem. +config RTC_SNVS + bool "Enable i.MX SNVS RTC driver" + depends on DM_RTC + help + Enable i.MX RTC driver. This driver supports the RTC that is present + on various NXP i.MX SoCs. + config RTC_STM32 bool "Enable STM32 RTC driver" depends on DM_RTC diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 782f5a3bc3d..742481ce185 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_RTC_RV8803) += rv8803.o obj-$(CONFIG_RTC_RX8025) += rx8025.o obj-$(CONFIG_RTC_RX8010SJ) += rx8010sj.o obj-$(CONFIG_RTC_S35392A) += s35392a.o +obj-$(CONFIG_RTC_SNVS) += snvs-rtc.o obj-$(CONFIG_RTC_STM32) += stm32_rtc.o obj-$(CONFIG_$(PHASE_)RTC_SANDBOX) += sandbox_rtc.o obj-$(CONFIG_RTC_ABX80X) += abx80x.o diff --git a/drivers/rtc/snvs-rtc.c b/drivers/rtc/snvs-rtc.c new file mode 100644 index 00000000000..c1b4fa0f8da --- /dev/null +++ b/drivers/rtc/snvs-rtc.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018-2026 INIT GmbH, Alexander Koch <[email protected]> + */ + +#include <asm/io.h> +#include <dm.h> +#include <linux/bitops.h> +#include <linux/iopoll.h> +#include <malloc.h> +#include <rtc.h> + +#define SNVS_LPSRTCMR 0x1c +#define SNVS_LPSRTCLR 0x20 +#define SNVS_LPCR 0x04 +#define SNVS_LPCR_SRTC_ENV BIT(0) +#define CNTR_TO_SECS_SH 15 + +struct snvs_rtc_priv { + fdt_addr_t base; +}; + +static int snvs_rtc_enable(struct udevice *dev) +{ + struct snvs_rtc_priv *priv = dev_get_priv(dev); + u32 val; + + setbits_le32(priv->base + SNVS_LPCR, SNVS_LPCR_SRTC_ENV); + + return readl_poll_timeout(priv->base + SNVS_LPCR, val, + (val & SNVS_LPCR_SRTC_ENV), 1000); +} + +static int snvs_rtc_disable(struct udevice *dev) +{ + struct snvs_rtc_priv *priv = dev_get_priv(dev); + u32 val; + + clrbits_le32(priv->base + SNVS_LPCR, SNVS_LPCR_SRTC_ENV); + + return readl_poll_timeout(priv->base + SNVS_LPCR, val, + !(val & SNVS_LPCR_SRTC_ENV), 1000); +} + +static int snvs_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + struct snvs_rtc_priv *priv = dev_get_priv(dev); + u64 read1, read2; + + /* Require two identical consecutive reads as stated in manual */ + do { + read1 = (u64)readl(priv->base + SNVS_LPSRTCMR) << 32ULL; + read1 |= readl(priv->base + SNVS_LPSRTCLR); + + read2 = (u64)readl(priv->base + SNVS_LPSRTCMR) << 32ULL; + read2 |= readl(priv->base + SNVS_LPSRTCLR); + } while (read1 != read2); + + /* Convert 47-bit counter to 32-bit raw second count */ + rtc_to_tm((time_t)(read1 >> CNTR_TO_SECS_SH), tm); + + return 0; +} + +static int snvs_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + struct snvs_rtc_priv *priv = dev_get_priv(dev); + u32 time = rtc_mktime(tm); + int ret; + + ret = snvs_rtc_disable(dev); + if (ret) + return ret; + + /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */ + writel(time << CNTR_TO_SECS_SH, priv->base + SNVS_LPSRTCLR); + writel(time >> (32 - CNTR_TO_SECS_SH), priv->base + SNVS_LPSRTCMR); + + return snvs_rtc_enable(dev); +} + +static const struct rtc_ops snvs_rtc_ops = { + .get = snvs_rtc_get, + .set = snvs_rtc_set, +}; + +static int snvs_rtc_probe(struct udevice *dev) +{ + struct snvs_rtc_priv *priv = dev_get_priv(dev); + u32 offset; + int ret; + + priv->base = dev_read_addr(dev->parent); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + ret = dev_read_u32(dev, "offset", &offset); + if (ret) + return ret; + + priv->base += offset; + + return snvs_rtc_enable(dev); +} + +static const struct udevice_id snvs_rtc_ids[] = { + { .compatible = "fsl,sec-v4.0-mon-rtc-lp" }, + { } +}; + +U_BOOT_DRIVER(rtc_snvs) = { + .name = "rtc-snvs", + .id = UCLASS_RTC, + .probe = snvs_rtc_probe, + .of_match = snvs_rtc_ids, + .ops = &snvs_rtc_ops, + .priv_auto = sizeof(struct snvs_rtc_priv), +}; -- 2.53.0

