From 47bc0dad56f5a93b28a4a644068fb9424cfc5975 Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Tue, 5 Feb 2019 02:59:49 +0800 Subject: [PATCH] rtc: add rk8xx rtc support support rtc alarm interrupt and alarm trigger power up. Change-Id: I7752f173d524f579b57b862d2788296ab1486c14 Signed-off-by: Elaine Zhang --- drivers/power/pmic/rk8xx.c | 9 ++ drivers/rtc/Kconfig | 6 + drivers/rtc/Makefile | 1 + drivers/rtc/rk8xx_rtc.c | 232 +++++++++++++++++++++++++++++++++++++ drivers/rtc/rtc-uclass.c | 10 ++ include/rtc.h | 14 +++ 6 files changed, 272 insertions(+) create mode 100644 drivers/rtc/rk8xx_rtc.c diff --git a/drivers/power/pmic/rk8xx.c b/drivers/power/pmic/rk8xx.c index d7ae6caa17..02ea363e1a 100644 --- a/drivers/power/pmic/rk8xx.c +++ b/drivers/power/pmic/rk8xx.c @@ -37,6 +37,11 @@ static const struct pmic_child_info power_key_info[] = { { }, }; +static const struct pmic_child_info rtc_info[] = { + { .prefix = "rtc", .driver = "rk8xx_rtc"}, + { }, +}; + static const struct pmic_child_info fuel_gauge_info[] = { { .prefix = "battery", .driver = "rk818_fg"}, { .prefix = "battery", .driver = "rk817_fg"}, @@ -147,6 +152,10 @@ static int rk8xx_bind(struct udevice *dev) if (!children) debug("%s: %s - no child found\n", __func__, dev->name); + children = pmic_bind_children(dev, dev->node, rtc_info); + if (!children) + debug("%s: %s - no child found\n", __func__, dev->name); + children = pmic_bind_children(dev, dev->node, fuel_gauge_info); if (!children) debug("%s: %s - no child found\n", __func__, dev->name); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index d06130c7a2..81f81e68be 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -30,4 +30,10 @@ config RTC_DS1307 Support for Dallas Semiconductor (now Maxim) DS1307 and DS1338/9 and compatible Real Time Clock devices. +config RTC_RK8XX + bool "Enable RK808/RK818/RK805/816/817 rtc support" + depends on DM_RTC && PMIC_RK8XX + help + This adds a driver for the RK808/RK818/RK805/816/817 rtc support. + endmenu diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 003e31aeba..c129d90ded 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_RTC_PCF8563) += pcf8563.o obj-$(CONFIG_RTC_PCF2127) += pcf2127.o obj-$(CONFIG_RTC_PL031) += pl031.o obj-$(CONFIG_RTC_PT7C4338) += pt7c4338.o +obj-$(CONFIG_RTC_RK8XX) += rk8xx_rtc.o obj-$(CONFIG_RTC_RS5C372A) += rs5c372.o obj-$(CONFIG_RTC_RV3029) += rv3029.o obj-$(CONFIG_RTC_RX8025) += rx8025.o diff --git a/drivers/rtc/rk8xx_rtc.c b/drivers/rtc/rk8xx_rtc.c new file mode 100644 index 0000000000..7d9152a889 --- /dev/null +++ b/drivers/rtc/rk8xx_rtc.c @@ -0,0 +1,232 @@ +/* + * (C) Copyright 2019 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK817_INT_STS_REG0 0xf8 +#define RK817_INT_MSK_REG0 0xf9 + +#define RK816_INT_STS_REG2 0x4c +#define RK816_INT_MSK_REG2 0x4d + +#define RK808_INT_STS_REG1 0x4c +#define RK808_INT_MSK_REG1 0x4d + +#define RK805_INT_STS_REG 0x4c +#define RK805_INT_MSK_REG 0x4d + +#define RK808_RTC_CTRL_REG 0x10 +#define RK808_RTC_STATUS_REG 0x11 +#define RK808_RTC_INT_REG 0x12 + +#define RK817_RTC_CTRL_REG 0x0d +#define RK817_RTC_STATUS_REG 0x0e +#define RK817_RTC_INT_REG 0x0f + +#define RTC_ALARM_EN 5 +#define RTC_ALARM_STATUS BIT(6) + +struct rk8xx_rtc_priv { + u8 rtc_int_sts_reg; + u8 rtc_int_msk_reg; + u8 int_sts_reg; + u8 int_msk_reg; + int rtc_alarm_trigger; + int irq_is_busy; +}; + +static void rtc_irq_handler(int irq, void *data) +{ + struct udevice *dev = data; + struct rk8xx_rtc_priv *priv = dev_get_priv(dev); + int ret, val; + + debug("%s: irq = %d\n", __func__, irq); + + if (priv->rtc_int_sts_reg) { + val = pmic_reg_read(dev->parent, priv->rtc_int_sts_reg); + if (val < 0) { + printf("%s: i2c read reg 0x%x failed, ret=%d\n", + __func__, priv->rtc_int_sts_reg, val); + return; + } + + if (val & RTC_ALARM_STATUS) { + priv->rtc_alarm_trigger = 1; + printf("RTC: alarm interrupt\n"); + } + + ret = pmic_reg_write(dev->parent, + priv->rtc_int_sts_reg, 0xfe); + if (ret < 0) { + printf("%s: i2c write reg 0x%x failed, ret=%d\n", + __func__, priv->rtc_int_sts_reg, ret); + return; + } + } + + ret = pmic_reg_write(dev->parent, + priv->int_sts_reg, 0xff); + if (ret < 0) { + printf("%s: i2c write reg 0x%x failed, ret=%d\n", + __func__, priv->int_sts_reg, ret); + return; + } + debug("%s: reg[0x%x] = 0x%x\n", __func__, priv->int_sts_reg, + pmic_reg_read(dev->parent, priv->int_sts_reg)); +} + +static int rtc_interrupt_init(struct udevice *dev) +{ + struct rk8xx_rtc_priv *priv = dev_get_priv(dev); + u32 interrupt[2], phandle; + int irq, ret; + + phandle = dev_read_u32_default(dev->parent, "interrupt-parent", -1); + if (phandle < 0) { + printf("failed get 'interrupt-parent', ret=%d\n", phandle); + return phandle; + } + + ret = dev_read_u32_array(dev->parent, "interrupts", interrupt, 2); + if (ret) { + printf("failed get 'interrupt', ret=%d\n", ret); + return ret; + } + + irq = phandle_gpio_to_irq(phandle, interrupt[0]); + if (irq < 0) { + if (irq == -EBUSY) { + priv->irq_is_busy = 1; + return 0; + } + return irq; + } + irq_install_handler(irq, rtc_irq_handler, dev); + irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING); + irq_handler_enable(irq); + + return 0; +} + +static int rk8xx_rtc_alarm_trigger(struct udevice *dev) +{ + struct rk8xx_rtc_priv *priv = dev_get_priv(dev); + int val, ret, alarm_trigger = 0; + + if (priv->irq_is_busy) { + val = pmic_reg_read(dev->parent, priv->rtc_int_sts_reg); + if (val < 0) { + printf("%s: i2c read reg 0x%x failed, ret=%d\n", + __func__, priv->rtc_int_sts_reg, val); + return val; + } + if (val & RTC_ALARM_STATUS) { + alarm_trigger = 1; + printf("rtc alarm interrupt\n"); + } + ret = pmic_reg_write(dev->parent, + priv->rtc_int_sts_reg, 0xfe); + if (ret < 0) { + printf("%s: i2c write reg 0x%x failed, ret=%d\n", + __func__, priv->rtc_int_sts_reg, ret); + return ret; + } + return alarm_trigger; + } else { + return priv->rtc_alarm_trigger; + } +} + +static struct rtc_ops rk8xx_rtc_ops = { + .alarm_trigger = rk8xx_rtc_alarm_trigger, +}; + +static int rk8xx_rtc_probe(struct udevice *dev) +{ + struct rk8xx_priv *rk8xx = dev_get_priv(dev->parent); + struct rk8xx_rtc_priv *priv = dev_get_priv(dev); + int ret, val; + + priv->rtc_int_sts_reg = RK808_RTC_STATUS_REG; + priv->rtc_int_msk_reg = RK808_RTC_INT_REG; + switch (rk8xx->variant) { + case RK808_ID: + case RK818_ID: + priv->int_msk_reg = RK808_INT_MSK_REG1; + priv->int_sts_reg = RK808_INT_STS_REG1; + break; + case RK805_ID: + priv->int_msk_reg = RK805_INT_MSK_REG; + priv->int_sts_reg = RK805_INT_STS_REG; + break; + case RK816_ID: + priv->int_msk_reg = RK816_INT_MSK_REG2; + priv->int_sts_reg = RK816_INT_STS_REG2; + break; + case RK809_ID: + case RK817_ID: + priv->rtc_int_sts_reg = RK817_RTC_STATUS_REG; + priv->rtc_int_msk_reg = RK817_RTC_INT_REG; + priv->int_msk_reg = RK817_INT_MSK_REG0; + priv->int_sts_reg = RK817_INT_STS_REG0; + break; + default: + return -EINVAL; + } + + priv->rtc_alarm_trigger = 0; + priv->irq_is_busy = 0; + /* mask and clear interrupt */ + val = pmic_reg_read(dev->parent, priv->int_msk_reg); + if (val < 0) { + printf("%s: i2c read reg 0x%x failed, ret=%d\n", + __func__, priv->int_msk_reg, val); + return val; + } + ret = pmic_reg_write(dev->parent, + priv->int_msk_reg, val | 0xc1); + if (ret < 0) { + printf("%s: i2c write reg 0x%x failed, ret=%d\n", + __func__, priv->int_msk_reg, ret); + return ret; + } + val = pmic_reg_read(dev->parent, priv->int_sts_reg); + if (val < 0) { + printf("%s: i2c read reg 0x%x failed, ret=%d\n", + __func__, priv->int_sts_reg, val); + return val; + } + ret = pmic_reg_write(dev->parent, + priv->int_sts_reg, + val | (1 << RTC_ALARM_EN)); + if (ret < 0) { + printf("%s: i2c write reg 0x%x failed, ret=%d\n", + __func__, priv->int_sts_reg, ret); + return ret; + } + debug("%s: reg[0x%x] = 0x%x\n", __func__, priv->int_msk_reg, + pmic_reg_read(dev->parent, priv->int_msk_reg)); + debug("%s: reg[0x%x] = 0x%x\n", __func__, priv->int_sts_reg, + pmic_reg_read(dev->parent, priv->int_sts_reg)); + + return rtc_interrupt_init(dev); +} + +U_BOOT_DRIVER(rk8xx_rtc) = { + .name = "rk8xx_rtc", + .id = UCLASS_RTC, + .probe = rk8xx_rtc_probe, + .ops = &rk8xx_rtc_ops, + .priv_auto_alloc_size = sizeof(struct rk8xx_rtc_priv), +}; diff --git a/drivers/rtc/rtc-uclass.c b/drivers/rtc/rtc-uclass.c index 89312c51ff..38e6b73839 100644 --- a/drivers/rtc/rtc-uclass.c +++ b/drivers/rtc/rtc-uclass.c @@ -120,6 +120,16 @@ int rtc_write32(struct udevice *dev, unsigned int reg, u32 value) return 0; } +int rtc_alarm_trigger(struct udevice *dev) +{ + const struct rtc_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->alarm_trigger) + return 0; + + return ops->alarm_trigger(dev); +} + UCLASS_DRIVER(rtc) = { .name = "rtc", .id = UCLASS_RTC, diff --git a/include/rtc.h b/include/rtc.h index 49142b6e18..e429c7899f 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -72,6 +72,13 @@ struct rtc_ops { * @return 0 if OK, -ve on error */ int (*write8)(struct udevice *dev, unsigned int reg, int val); + + /** + * alarm_trigger() + * @dev: Device to write to + * @return 1 if rtc alarm trigger boot on + */ + int (*alarm_trigger)(struct udevice *dev); }; /* Access the operations for an RTC device */ @@ -167,6 +174,13 @@ int rtc_read32(struct udevice *dev, unsigned int reg, u32 *valuep); */ int rtc_write32(struct udevice *dev, unsigned int reg, u32 value); +/** + * rtc_alarm_trigger() + * + * @dev: Device to write to + * @return 1 if rtc alarm trigger boot on + */ +int rtc_alarm_trigger(struct udevice *dev); #else int rtc_get (struct rtc_time *); int rtc_set (struct rtc_time *);