From mboxrd@z Thu Jan 1 00:00:00 1970 From: Dario Binacchi Date: Fri, 7 May 2021 06:15:07 +0200 Subject: [PATCH 6/7] rtc: davinci: add driver model support In-Reply-To: <20210507041509.4928-1-dariobin@libero.it> References: <20210507041509.4928-1-dariobin@libero.it> Message-ID: <20210507041509.4928-7-dariobin@libero.it> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Update the driver to support the device tree and the driver model. The read / write helpers in rtc_ops allow access to scratch registers only. The offset parameter is added to the address of the scratch0 register. Signed-off-by: Dario Binacchi --- drivers/rtc/davinci.c | 373 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 363 insertions(+), 10 deletions(-) diff --git a/drivers/rtc/davinci.c b/drivers/rtc/davinci.c index 82e5eb3b43..b0a077cba7 100644 --- a/drivers/rtc/davinci.c +++ b/drivers/rtc/davinci.c @@ -2,20 +2,20 @@ /* * (C) Copyright 2011 DENX Software Engineering GmbH * Heiko Schocher + * Copyright (C) 2021 Dario Binacchi */ #include #include +#include +#include #include #include #include #include #include +#include #include -#if !defined(RTC_BASE) && defined(DAVINCI_RTC_BASE) -#define RTC_BASE DAVINCI_RTC_BASE -#endif - static void davinci_rtc_lock(struct davinci_rtc *rtc) { writel(0, &rtc->kick0r); @@ -52,9 +52,8 @@ static int davinci_rtc_wait_not_busy(struct davinci_rtc *rtc) return 0; } -int rtc_get(struct rtc_time *tmp) +static int davinci_rtc_get(struct davinci_rtc *rtc, struct rtc_time *tmp) { - struct davinci_rtc *rtc = (struct davinci_rtc *)RTC_BASE; unsigned long sec, min, hour, mday, wday, mon_cent, year; int ret; @@ -92,9 +91,8 @@ int rtc_get(struct rtc_time *tmp) return 0; } -int rtc_set(struct rtc_time *tmp) +static int davinci_rtc_set(struct davinci_rtc *rtc, const struct rtc_time *tmp) { - struct davinci_rtc *rtc = (struct davinci_rtc *)RTC_BASE; int ret; ret = davinci_rtc_wait_not_busy(rtc); @@ -119,10 +117,365 @@ int rtc_set(struct rtc_time *tmp) return 0; } +static void davinci_rtc_reset(struct davinci_rtc *rtc) +{ + /* run RTC counter */ + writeb(0x01, &rtc->ctrl); +} + +#if !CONFIG_IS_ENABLED(DM_RTC) + +#if !defined(RTC_BASE) && defined(DAVINCI_RTC_BASE) +#define RTC_BASE DAVINCI_RTC_BASE +#endif + +int rtc_get(struct rtc_time *tmp) +{ + struct davinci_rtc *rtc = (struct davinci_rtc *)RTC_BASE; + + return davinci_rtc_get(rtc, tmp); +} + +int rtc_set(struct rtc_time *tmp) +{ + struct davinci_rtc *rtc = (struct davinci_rtc *)RTC_BASE; + + return davinci_rtc_set(rtc, tmp); +} + void rtc_reset(void) { struct davinci_rtc *rtc = (struct davinci_rtc *)RTC_BASE; - /* run RTC counter */ - writeb(0x01, &rtc->ctrl); + davinci_rtc_reset(rtc); +} + +#else /* CONFIG_DM_RTC */ + +#define OMAP_RTC_CTRL_REG 0x40 +#define OMAP_RTC_STATUS_REG 0x44 +#define OMAP_RTC_INTERRUPTS_REG 0x48 + +#define OMAP_RTC_OSC_REG 0x54 + +#define OMAP_RTC_SCRATCH0_REG 0x60 +#define OMAP_RTC_SCRATCH1_REG 0x64 +#define OMAP_RTC_SCRATCH2_REG 0x68 + +#define OMAP_RTC_KICK0_REG 0x6c +#define OMAP_RTC_KICK1_REG 0x70 + +#define OMAP_RTC_PMIC_REG 0x98 + +/* OMAP_RTC_CTRL_REG bit fields: */ +#define OMAP_RTC_CTRL_SPLIT BIT(7) +#define OMAP_RTC_CTRL_DISABLE BIT(6) +#define OMAP_RTC_CTRL_SET_32_COUNTER BIT(5) +#define OMAP_RTC_CTRL_TEST BIT(4) +#define OMAP_RTC_CTRL_MODE_12_24 BIT(3) +#define OMAP_RTC_CTRL_AUTO_COMP BIT(2) +#define OMAP_RTC_CTRL_ROUND_30S BIT(1) +#define OMAP_RTC_CTRL_STOP BIT(0) + +/* OMAP_RTC_STATUS_REG bit fields */ +#define OMAP_RTC_STATUS_POWER_UP BIT(7) +#define OMAP_RTC_STATUS_ALARM2 BIT(7) +#define OMAP_RTC_STATUS_ALARM BIT(6) +#define OMAP_RTC_STATUS_1D_EVENT BIT(5) +#define OMAP_RTC_STATUS_1H_EVENT BIT(4) +#define OMAP_RTC_STATUS_1M_EVENT BIT(3) +#define OMAP_RTC_STATUS_1S_EVENT BIT(2) +#define OMAP_RTC_STATUS_RUN BIT(1) +#define OMAP_RTC_STATUS_BUSY BIT(0) + +/* OMAP_RTC_OSC_REG bit fields */ +#define OMAP_RTC_OSC_32KCLK_EN BIT(6) +#define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3) +#define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4) + +struct omap_rtc_device_type { + bool has_32kclk_en; + bool has_irqwakeen; + bool has_pmic_mode; + bool has_power_up_reset; +}; + +struct omap_rtc_priv { + fdt_addr_t base; + u8 max_reg; + struct udevice *dev; + struct clk clk; + bool has_ext_clk; + const struct omap_rtc_device_type *type; +}; + +static inline u8 omap_rtc_readb(struct omap_rtc_priv *priv, unsigned int reg) +{ + return readb(priv->base + reg); +} + +static inline u32 omap_rtc_readl(struct omap_rtc_priv *priv, unsigned int reg) +{ + return readl(priv->base + reg); +} + +static inline void omap_rtc_writeb(struct omap_rtc_priv *priv, unsigned int reg, + u8 val) +{ + writeb(val, priv->base + reg); +} + +static inline void omap_rtc_writel(struct omap_rtc_priv *priv, unsigned int reg, + u32 val) +{ + writel(val, priv->base + reg); +} + +static inline void omap_rtc_unlock(struct omap_rtc_priv *priv) +{ + davinci_rtc_unlock((struct davinci_rtc *)priv->base); +} + +static inline void omap_rtc_lock(struct omap_rtc_priv *priv) +{ + davinci_rtc_lock((struct davinci_rtc *)priv->base); +} + +static int omap_rtc_reset(struct udevice *dev) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + + davinci_rtc_reset((struct davinci_rtc *)priv->base); + return 0; +} + +static int omap_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + + return davinci_rtc_set((struct davinci_rtc *)priv->base, tm); } + +static int omap_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + + return davinci_rtc_get((struct davinci_rtc *)priv->base, tm); +} + +static int omap_rtc_scratch_read(struct udevice *dev, uint offset, + u8 *buffer, uint len) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + u32 *val = (u32 *)buffer; + unsigned int reg; + int i; + + if (len & 3) + return -EFAULT; + + for (i = 0; i < len / 4; i++) { + reg = OMAP_RTC_SCRATCH0_REG + offset + (i * 4); + if (reg >= OMAP_RTC_KICK0_REG) + return -EFAULT; + + val[i] = omap_rtc_readl(priv, reg); + } + + return 0; +} + +static int omap_rtc_scratch_write(struct udevice *dev, uint offset, + const u8 *buffer, uint len) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + u32 *val = (u32 *)buffer; + unsigned int reg; + int i; + + if (len & 3) + return -EFAULT; + + omap_rtc_unlock(priv); + for (i = 0; i < len / 4; i++) { + reg = OMAP_RTC_SCRATCH0_REG + offset + (i * 4); + if (reg >= OMAP_RTC_KICK0_REG) + return -EFAULT; + + omap_rtc_writel(priv, reg, val[i]); + } + omap_rtc_lock(priv); + + return 0; +} + +static int omap_rtc_remove(struct udevice *dev) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + u8 reg; + + if (priv->clk.dev) + clk_disable(&priv->clk); + + omap_rtc_unlock(priv); + + /* leave rtc running, but disable irqs */ + omap_rtc_writeb(priv, OMAP_RTC_INTERRUPTS_REG, 0); + + if (priv->has_ext_clk) { + reg = omap_rtc_readb(priv, OMAP_RTC_OSC_REG); + reg &= ~OMAP_RTC_OSC_SEL_32KCLK_SRC; + omap_rtc_writeb(priv, OMAP_RTC_OSC_REG, reg); + } + + omap_rtc_lock(priv); + return 0; +} + +static int omap_rtc_probe(struct udevice *dev) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + u8 reg, mask, new_ctrl; + + priv->dev = dev; + priv->type = (struct omap_rtc_device_type *)dev_get_driver_data(dev); + priv->max_reg = OMAP_RTC_PMIC_REG; + + if (!clk_get_by_name(dev, "ext-clk", &priv->clk)) + priv->has_ext_clk = true; + else + clk_get_by_name(dev, "int-clk", &priv->clk); + + if (priv->clk.dev) + clk_enable(&priv->clk); + else + dev_warn(dev, "missing clock\n"); + + omap_rtc_unlock(priv); + + /* + * disable interrupts + * + * NOTE: ALARM2 is not cleared on AM3352 if rtc_write (writeb) is used + */ + omap_rtc_writel(priv, OMAP_RTC_INTERRUPTS_REG, 0); + + if (priv->type->has_32kclk_en) { + reg = omap_rtc_readb(priv, OMAP_RTC_OSC_REG); + omap_rtc_writeb(priv, OMAP_RTC_OSC_REG, + reg | OMAP_RTC_OSC_32KCLK_EN); + } + + /* clear old status */ + reg = omap_rtc_readb(priv, OMAP_RTC_STATUS_REG); + + mask = OMAP_RTC_STATUS_ALARM; + + if (priv->type->has_pmic_mode) + mask |= OMAP_RTC_STATUS_ALARM2; + + if (priv->type->has_power_up_reset) { + mask |= OMAP_RTC_STATUS_POWER_UP; + if (reg & OMAP_RTC_STATUS_POWER_UP) + dev_info(dev, "RTC power up reset detected\n"); + } + + if (reg & mask) + omap_rtc_writeb(priv, OMAP_RTC_STATUS_REG, reg & mask); + + /* On boards with split power, RTC_ON_NOFF won't reset the RTC */ + reg = omap_rtc_readb(priv, OMAP_RTC_CTRL_REG); + if (reg & OMAP_RTC_CTRL_STOP) + dev_info(dev, "already running\n"); + + /* force to 24 hour mode */ + new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP); + new_ctrl |= OMAP_RTC_CTRL_STOP; + + /* + * BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: + * + * - Device wake-up capability setting should come through chip + * init logic. OMAP1 boards should initialize the "wakeup capable" + * flag in the platform device if the board is wired right for + * being woken up by RTC alarm. For OMAP-L138, this capability + * is built into the SoC by the "Deep Sleep" capability. + * + * - Boards wired so RTC_ON_nOFF is used as the reset signal, + * rather than nPWRON_RESET, should forcibly enable split + * power mode. (Some chip errata report that RTC_CTRL_SPLIT + * is write-only, and always reads as zero...) + */ + + if (new_ctrl & OMAP_RTC_CTRL_SPLIT) + dev_info(dev, "split power mode\n"); + + if (reg != new_ctrl) + omap_rtc_writeb(priv, OMAP_RTC_CTRL_REG, new_ctrl); + + /* + * If we have the external clock then switch to it so we can keep + * ticking across suspend. + */ + if (priv->has_ext_clk) { + reg = omap_rtc_readb(priv, OMAP_RTC_OSC_REG); + reg &= ~OMAP_RTC_OSC_OSC32K_GZ_DISABLE; + reg |= OMAP_RTC_OSC_32KCLK_EN | OMAP_RTC_OSC_SEL_32KCLK_SRC; + omap_rtc_writeb(priv, OMAP_RTC_OSC_REG, reg); + } + + omap_rtc_lock(priv); + return 0; +} + +static int omap_rtc_of_to_plat(struct udevice *dev) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) { + dev_err(dev, "invalid address\n"); + return -EINVAL; + } + + dev_dbg(dev, "base=%pa\n", &priv->base); + return 0; +} + +static const struct rtc_ops omap_rtc_ops = { + .get = omap_rtc_get, + .set = omap_rtc_set, + .reset = omap_rtc_reset, + .read = omap_rtc_scratch_read, + .write = omap_rtc_scratch_write, +}; + +static const struct omap_rtc_device_type omap_rtc_am3352_type = { + .has_32kclk_en = true, + .has_irqwakeen = true, + .has_pmic_mode = true, +}; + +static const struct omap_rtc_device_type omap_rtc_da830_type = { + .has_32kclk_en = false, + .has_irqwakeen = false, + .has_pmic_mode = false, +}; + +static const struct udevice_id omap_rtc_ids[] = { + {.compatible = "ti,am3352-rtc", .data = (ulong)&omap_rtc_am3352_type}, + {.compatible = "ti,da830-rtc", .data = (ulong)&omap_rtc_da830_type } +}; + +U_BOOT_DRIVER(omap_rtc) = { + .name = "omap_rtc", + .id = UCLASS_RTC, + .of_match = omap_rtc_ids, + .ops = &omap_rtc_ops, + .of_to_plat = omap_rtc_of_to_plat, + .probe = omap_rtc_probe, + .remove = omap_rtc_remove, + .priv_auto = sizeof(struct omap_rtc_priv), +}; + +#endif /* CONFIG_DM_RTC */ -- 2.17.1