[U-Boot] [PATCH 2/2] rtc: Add rtc driver for stm32mp1
Benjamin Gaignard
benjamin.gaignard at linaro.org
Tue Jul 9 12:45:24 UTC 2019
Le mar. 9 juil. 2019 à 14:34, Patrick DELAUNAY
<patrick.delaunay at st.com> a écrit :
>
> Hi Benjamin,
>
> -----Original Message-----
> From: Benjamin GAIGNARD <benjamin.gaignard at st.com>
> Sent: mardi 25 juin 2019 11:24
> To: Patrick DELAUNAY <patrick.delaunay at st.com>; Patrice CHOTARD <patrice.chotard at st.com>; sjg at chromium.org
> Cc: uboot-stm32 at st-md-mailman.stormreply.com; u-boot at lists.denx.de; Benjamin GAIGNARD <benjamin.gaignard at st.com>
> Subject: [PATCH 2/2] rtc: Add rtc driver for stm32mp1
> Importance: High
>
> Add support of STM32MP1 rtc driver.
> Enable it for basic and trusted configurations.
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard at st.com>
> ---
> configs/stm32mp15_basic_defconfig | 2 +
> configs/stm32mp15_trusted_defconfig | 2 +
> drivers/rtc/Kconfig | 6 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/stm32_rtc.c | 321 ++++++++++++++++++++++++++++++++++++
> 5 files changed, 332 insertions(+)
> create mode 100644 drivers/rtc/stm32_rtc.c
>
> diff --git a/configs/stm32mp15_basic_defconfig b/configs/stm32mp15_basic_defconfig
> index 4aa184fb5b..6352075307 100644
> --- a/configs/stm32mp15_basic_defconfig
> +++ b/configs/stm32mp15_basic_defconfig
> @@ -94,6 +94,8 @@ CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_GPIO=y CONFIG_DM_REGULATOR_STM32_VREFBUF=y
> CONFIG_DM_REGULATOR_STPMIC1=y
> +CONFIG_DM_RTC=y
> +CONFIG_RTC_STM32=y
> CONFIG_SERIAL_RX_BUFFER=y
> CONFIG_STM32_SERIAL=y
> CONFIG_SPI=y
> diff --git a/configs/stm32mp15_trusted_defconfig b/configs/stm32mp15_trusted_defconfig
> index 66361c8715..e079adc3c4 100644
> --- a/configs/stm32mp15_trusted_defconfig
> +++ b/configs/stm32mp15_trusted_defconfig
> @@ -84,6 +84,8 @@ CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_GPIO=y CONFIG_DM_REGULATOR_STM32_VREFBUF=y
> CONFIG_DM_REGULATOR_STPMIC1=y
> +CONFIG_DM_RTC=y
> +CONFIG_RTC_STM32=y
> CONFIG_SERIAL_RX_BUFFER=y
> CONFIG_STM32_SERIAL=y
> CONFIG_SPI=y
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index fd0009b2e2..a383e632ad 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -110,4 +110,10 @@ config RTC_M41T62
> Enable driver for ST's M41T62 compatible RTC devices (like RV-4162).
> It is a serial (I2C) real-time clock (RTC) with alarm.
>
> +config RTC_STM32
> + bool "Enable STM32 RTC driver"
> + depends on DM_RTC
> + help
> + Enable STM32 RTC driver. This driver supports the rtc that is present
> + on some STM32 SoCs.
> endmenu
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 1724602f1c..90e79fb30d 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -50,5 +50,6 @@ obj-$(CONFIG_RTC_RX8025) += rx8025.o
> obj-$(CONFIG_RTC_RX8010SJ) += rx8010sj.o
> obj-$(CONFIG_RTC_S3C24X0) += s3c24x0_rtc.o
> obj-$(CONFIG_RTC_S35392A) += s35392a.o
> +obj-$(CONFIG_RTC_STM32) += stm32_rtc.o
> obj-$(CONFIG_SANDBOX) += sandbox_rtc.o
> obj-$(CONFIG_RTC_X1205) += x1205.o
> diff --git a/drivers/rtc/stm32_rtc.c b/drivers/rtc/stm32_rtc.c new file mode 100644 index 0000000000..5d0ffdbc9f
> --- /dev/null
> +++ b/drivers/rtc/stm32_rtc.c
> @@ -0,0 +1,321 @@
> +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
> +/*
> + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved */
> +#include <common.h> #include <clk.h> #include <dm.h> #include <rtc.h>
> +#include <asm/io.h> #include <linux/iopoll.h>
> +
> +#define STM32_RTC_TR 0x00
> +#define STM32_RTC_DR 0x04
> +#define STM32_RTC_ISR 0x0C
> +#define STM32_RTC_PRER 0x10
> +#define STM32_RTC_CR 0x18
> +#define STM32_RTC_WPR 0x24
> +
> +/* STM32_RTC_TR bit fields */
> +#define STM32_RTC_SEC_SHIFT 0
> +#define STM32_RTC_SEC GENMASK(6, 0)
> +#define STM32_RTC_MIN_SHIFT 8
> +#define STM32_RTC_MIN GENMASK(14, 8)
> +#define STM32_RTC_HOUR_SHIFT 16
> +#define STM32_RTC_HOUR GENMASK(21, 16)
> +
> +/* STM32_RTC_DR bit fields */
> +#define STM32_RTC_DATE_SHIFT 0
> +#define STM32_RTC_DATE GENMASK(5, 0)
> +#define STM32_RTC_MONTH_SHIFT 8
> +#define STM32_RTC_MONTH GENMASK(12, 8)
> +#define STM32_RTC_WDAY_SHIFT 13
> +#define STM32_RTC_WDAY GENMASK(15, 13)
> +#define STM32_RTC_YEAR_SHIFT 16
> +#define STM32_RTC_YEAR GENMASK(23, 16)
> +
> +/* STM32_RTC_CR bit fields */
> +#define STM32_RTC_CR_FMT BIT(6)
> +
> +/* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */
> +#define STM32_RTC_ISR_INITS BIT(4)
> +#define STM32_RTC_ISR_RSF BIT(5)
> +#define STM32_RTC_ISR_INITF BIT(6)
> +#define STM32_RTC_ISR_INIT BIT(7)
> +
> +/* STM32_RTC_PRER bit fields */
> +#define STM32_RTC_PRER_PRED_S_SHIFT 0
> +#define STM32_RTC_PRER_PRED_S GENMASK(14, 0)
> +#define STM32_RTC_PRER_PRED_A_SHIFT 16
> +#define STM32_RTC_PRER_PRED_A GENMASK(22, 16)
> +
> +/* STM32_RTC_WPR key constants */
> +#define RTC_WPR_1ST_KEY 0xCA
> +#define RTC_WPR_2ND_KEY 0x53
> +#define RTC_WPR_WRONG_KEY 0xFF
> +
> +struct stm32_rtc_priv {
> + fdt_addr_t base;
> +};
> +
> +static int stm32_rtc_get(struct udevice *dev, struct rtc_time *tm) {
> + struct stm32_rtc_priv *priv = dev_get_priv(dev);
> + u32 tr, dr;
> +
> + tr = readl(priv->base + STM32_RTC_TR);
> + dr = readl(priv->base + STM32_RTC_DR);
> +
> + tm->tm_sec = bcd2bin((tr & STM32_RTC_SEC) >> STM32_RTC_SEC_SHIFT);
> + tm->tm_min = bcd2bin((tr & STM32_RTC_MIN) >> STM32_RTC_MIN_SHIFT);
> + tm->tm_hour = bcd2bin((tr & STM32_RTC_HOUR) >> STM32_RTC_HOUR_SHIFT);
> +
> + tm->tm_mday = bcd2bin((dr & STM32_RTC_DATE) >> STM32_RTC_DATE_SHIFT);
> + tm->tm_mon = bcd2bin((dr & STM32_RTC_MONTH) >> STM32_RTC_MONTH_SHIFT);
> + tm->tm_year = bcd2bin((dr & STM32_RTC_YEAR) >> STM32_RTC_YEAR_SHIFT);
> + tm->tm_wday = bcd2bin((dr & STM32_RTC_WDAY) >> STM32_RTC_WDAY_SHIFT);
> + tm->tm_yday = 0;
> + tm->tm_isdst = 0;
> +
> + dev_dbg(dev, "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
> + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
> + tm->tm_hour, tm->tm_min, tm->tm_sec);
> +
> + return 0;
> +}
> +
> +static void stm32_rtc_unlock(struct udevice *dev) {
> + struct stm32_rtc_priv *priv = dev_get_priv(dev);
> +
> + writel(RTC_WPR_1ST_KEY, priv->base + STM32_RTC_WPR);
> + writel(RTC_WPR_2ND_KEY, priv->base + STM32_RTC_WPR); }
> +
> +static void stm32_rtc_lock(struct udevice *dev) {
> + struct stm32_rtc_priv *priv = dev_get_priv(dev);
> +
> + writel(RTC_WPR_WRONG_KEY, priv->base + STM32_RTC_WPR); }
> +
> +static int stm32_rtc_enter_init_mode(struct udevice *dev) {
> + struct stm32_rtc_priv *priv = dev_get_priv(dev);
> + u32 isr = readl(priv->base + STM32_RTC_ISR);
> +
> + if (!(isr & STM32_RTC_ISR_INITF)) {
> + isr |= STM32_RTC_ISR_INIT;
> + writel(isr, priv->base + STM32_RTC_ISR);
> +
> + return readl_poll_timeout(priv->base + STM32_RTC_ISR,
> + isr,
> + (isr & STM32_RTC_ISR_INITF),
> + 100000);
> + }
> +
> + return 0;
> +}
> +
> +static int stm32_rtc_wait_sync(struct udevice *dev) {
> + struct stm32_rtc_priv *priv = dev_get_priv(dev);
> + u32 isr = readl(priv->base + STM32_RTC_ISR);
> +
> + isr &= ~STM32_RTC_ISR_RSF;
> + writel(isr, priv->base + STM32_RTC_ISR);
> +
> + /*
> + * Wait for RSF to be set to ensure the calendar registers are
> + * synchronised, it takes around 2 rtc_ck clock cycles
> + */
> + return readl_poll_timeout(priv->base + STM32_RTC_ISR,
> + isr, (isr & STM32_RTC_ISR_RSF),
> + 100000);
> +}
> +
> +static void stm32_rtc_exit_init_mode(struct udevice *dev) {
> + struct stm32_rtc_priv *priv = dev_get_priv(dev);
> + u32 isr = readl(priv->base + STM32_RTC_ISR);
> +
> + isr &= ~STM32_RTC_ISR_INIT;
> + writel(isr, priv->base + STM32_RTC_ISR); }
> +
> +static int stm32_rtc_set_time(struct udevice *dev, u32 time, u32 date)
> +{
> + struct stm32_rtc_priv *priv = dev_get_priv(dev);
> + int ret;
> +
> + stm32_rtc_unlock(dev);
> +
> + ret = stm32_rtc_enter_init_mode(dev);
> + if (ret)
> + goto lock;
> +
> + writel(time, priv->base + STM32_RTC_TR);
> + writel(date, priv->base + STM32_RTC_DR);
> +
> + stm32_rtc_exit_init_mode(dev);
> +
> + ret = stm32_rtc_wait_sync(dev);
> +
> +lock:
> + stm32_rtc_lock(dev);
> + return ret;
> +}
> +
> +static int stm32_rtc_set(struct udevice *dev, const struct rtc_time
> +*tm) {
> + u32 t, d;
> +
> + dev_dbg(dev, "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
> + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
> + tm->tm_hour, tm->tm_min, tm->tm_sec);
> +
> + /* Time in BCD format */
> + t = (bin2bcd(tm->tm_sec) << STM32_RTC_SEC_SHIFT) & STM32_RTC_SEC;
> + t |= (bin2bcd(tm->tm_min) << STM32_RTC_MIN_SHIFT) & STM32_RTC_MIN;
> + t |= (bin2bcd(tm->tm_hour) << STM32_RTC_HOUR_SHIFT) & STM32_RTC_HOUR;
> +
> + /* Date in BCD format */
> + d = (bin2bcd(tm->tm_mday) << STM32_RTC_DATE_SHIFT) & STM32_RTC_DATE;
> + d |= (bin2bcd(tm->tm_mon) << STM32_RTC_MONTH_SHIFT) & STM32_RTC_MONTH;
> + d |= (bin2bcd(tm->tm_year) << STM32_RTC_YEAR_SHIFT) & STM32_RTC_YEAR;
> + d |= (bin2bcd(tm->tm_wday) << STM32_RTC_WDAY_SHIFT) & STM32_RTC_WDAY;
> +
> + return stm32_rtc_set_time(dev, t, d);
> +}
> +
> +static int stm32_rtc_reset(struct udevice *dev) {
> + dev_dbg(dev, "Reset DATE\n");
> +
> + return stm32_rtc_set_time(dev, 0, 0);
> +}
> +
> +static int stm32_rtc_init(struct udevice *dev) {
> + struct stm32_rtc_priv *priv = dev_get_priv(dev);
> + unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
> + unsigned int rate;
> + struct clk clk;
> + int ret;
> + u32 isr = readl(priv->base + STM32_RTC_ISR);
> +
> + if (isr & STM32_RTC_ISR_INITS)
> + return 0;
> +
> + ret = clk_get_by_index(dev, 1, &clk);
> + if (ret)
> + return ret;
> +
> + ret = clk_enable(&clk);
> + if (ret) {
> + clk_free(&clk);
> + return ret;
> + }
> +
> + rate = clk_get_rate(&clk);
> +
> + /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
> + pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
> + pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
> +
> + for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) {
> + pred_s = (rate / (pred_a + 1)) - 1;
> +
> + if (((pred_s + 1) * (pred_a + 1)) == rate)
> + break;
> + }
> +
> + /*
> + * Can't find a 1Hz, so give priority to RTC power consumption
> + * by choosing the higher possible value for prediv_a
> + */
> + if (pred_s > pred_s_max || pred_a > pred_a_max) {
> + pred_a = pred_a_max;
> + pred_s = (rate / (pred_a + 1)) - 1;
> + }
> +
> + stm32_rtc_unlock(dev);
> +
> + ret = stm32_rtc_enter_init_mode(dev);
> + if (ret) {
> + dev_err(dev,
> + "Can't enter in init mode. Prescaler config failed.\n");
> + goto unlock;
> + }
> +
> + prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S;
> + prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A;
> + writel(prer, priv->base + STM32_RTC_PRER);
> +
> + /* Force 24h time format */
> + cr = readl(priv->base + STM32_RTC_CR);
> + cr &= ~STM32_RTC_CR_FMT;
> + writel(cr, priv->base + STM32_RTC_CR);
> +
> + stm32_rtc_exit_init_mode(dev);
> +
> + ret = stm32_rtc_wait_sync(dev);
> +
> +unlock:
> + stm32_rtc_lock(dev);
> +
> + clk_disable(&clk);
> + clk_free(&clk);
>
>
> Clock free in all the case (even if ret = 0)
> => the next request failed (err = -110 for timeout)
>
> if (ret) {
> clk_disable(&clk);
> clk_free(&clk);
> }
>
My bad :-(
I will send a v2 for this patch.
Benjamin
> + return ret;
> +}
> +
> +static int stm32_rtc_probe(struct udevice *dev) {
> + struct stm32_rtc_priv *priv = dev_get_priv(dev);
> + struct clk clk;
> + int ret;
> +
> + priv->base = dev_read_addr(dev);
> + if (priv->base == FDT_ADDR_T_NONE)
> + return -EINVAL;
> +
> + ret = clk_get_by_index(dev, 0, &clk);
> + if (ret)
> + return ret;
> +
> + ret = clk_enable(&clk);
> + if (ret) {
> + clk_free(&clk);
> + return ret;
> + }
> +
> + ret = stm32_rtc_init(dev);
> +
> + if (ret) {
> + clk_disable(&clk);
> + clk_free(&clk);
> + }
> +
> + return ret;
> +}
> +
> +static const struct rtc_ops stm32_rtc_ops = {
> + .get = stm32_rtc_get,
> + .set = stm32_rtc_set,
> + .reset = stm32_rtc_reset,
> +};
> +
> +static const struct udevice_id stm32_rtc_ids[] = {
> + { .compatible = "st,stm32mp1-rtc" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(rtc_stm32) = {
> + .name = "rtc-stm32",
> + .id = UCLASS_RTC,
> + .probe = stm32_rtc_probe,
> + .of_match = stm32_rtc_ids,
> + .ops = &stm32_rtc_ops,
> + .priv_auto_alloc_size = sizeof(struct stm32_rtc_priv), };
> --
> 2.15.0
>
> Regards
>
> Patrick
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot
More information about the U-Boot
mailing list