On Wed, May 01, 2024 at 01:03:58PM GMT, Jingbao Qiu wrote: > Hi, Inochi > > On Wed, May 1, 2024 at 10:19 AM Inochi Amaoto <inochiama@xxxxxxxxxxx> wrote: > > > > On Sun, Feb 04, 2024 at 12:41:43PM GMT, Jingbao Qiu wrote: > > > Implement the RTC driver for CV1800, which able to provide time alarm. > > > > > > Signed-off-by: Jingbao Qiu <qiujingbao.dlmu@xxxxxxxxx> > > > --- > > > drivers/rtc/Kconfig | 10 ++ > > > drivers/rtc/Makefile | 1 + > > > drivers/rtc/rtc-cv1800.c | 244 +++++++++++++++++++++++++++++++++++++++ > > > 3 files changed, 255 insertions(+) > > > create mode 100644 drivers/rtc/rtc-cv1800.c > > > > > > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig > > > index e37a4341f442..3c6ed45a3b03 100644 > > > --- a/drivers/rtc/Kconfig > > > +++ b/drivers/rtc/Kconfig > > > @@ -1128,6 +1128,16 @@ config RTC_DRV_DS2404 > > > This driver can also be built as a module. If so, the module > > > will be called rtc-ds2404. > > > > > > +config RTC_DRV_CV1800 > > > + tristate "Sophgo CV1800 RTC" > > > + depends on ARCH_SOPHGO || COMPILE_TEST > > > + help > > > + If you say yes here you get support the RTC driver > > > + for Sophgo CV1800 chip. > > > + > > > + This driver can also be built as a module.If so, the > > > + module will be called rtc-cv1800. > > > + > > > config RTC_DRV_DA9052 > > > tristate "Dialog DA9052/DA9053 RTC" > > > depends on PMIC_DA9052 > > > diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile > > > index 6efff381c484..4efdd2d1e963 100644 > > > --- a/drivers/rtc/Makefile > > > +++ b/drivers/rtc/Makefile > > > @@ -42,6 +42,7 @@ obj-$(CONFIG_RTC_DRV_CADENCE) += rtc-cadence.o > > > obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o > > > obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o > > > obj-$(CONFIG_RTC_DRV_CROS_EC) += rtc-cros-ec.o > > > +obj-$(CONFIG_RTC_DRV_CV1800) += rtc-cv1800.o > > > obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o > > > obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o > > > obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o > > > diff --git a/drivers/rtc/rtc-cv1800.c b/drivers/rtc/rtc-cv1800.c > > > new file mode 100644 > > > index 000000000000..60a7192659f5 > > > --- /dev/null > > > +++ b/drivers/rtc/rtc-cv1800.c > > > @@ -0,0 +1,244 @@ > > > +// SPDX-License-Identifier: GPL-2.0-only > > > +/* > > > + * rtc-cv1800.c: RTC driver for Sophgo cv1800 RTC > > > + * > > > + * Author: Jingbao Qiu <qiujingbao.dlmu@xxxxxxxxx> > > > + */ > > > + > > > +#include <linux/clk.h> > > > +#include <linux/irq.h> > > > +#include <linux/kernel.h> > > > +#include <linux/module.h> > > > +#include <linux/of.h> > > > +#include <linux/platform_device.h> > > > +#include <linux/regmap.h> > > > +#include <linux/rtc.h> > > > + > > > +#define CTRL 0x08 > > > +#define ANA_CALIB 0x1000 > > > +#define SEC_PULSE_GEN 0x1004 > > > +#define ALARM_TIME 0x1008 > > > +#define ALARM_ENABLE 0x100C > > > +#define SET_SEC_CNTR_VAL 0x1010 > > > +#define SET_SEC_CNTR_TRIG 0x1014 > > > +#define SEC_CNTR_VAL 0x1018 > > > +#define APB_RDATA_SEL 0x103C > > > +#define POR_DB_MAGIC_KEY 0x1068 > > > +#define EN_PWR_WAKEUP 0x10BC > > > + > > > +/* > > > + * When in VDDBKUP domain, this MACRO register > > > + * does not power down > > > + */ > > > +#define MACRO_DA_CLEAR_ALL 0x1480 > > > +#define MACRO_DA_SOC_READY 0x148C > > > +#define MACRO_RO_T 0x14A8 > > > +#define MACRO_RG_SET_T 0x1498 > > > + > > > +#define CTRL_MODE_MASK BIT(10) > > > +#define CTRL_MODE_OSC32K 0x00UL > > > +#define CTRL_MODE_XTAL32K BIT(0) > > > +#define REG_ENABLE_FUN BIT(0) > > > +#define REG_DISABLE_FUN 0x00UL > > > +#define ALARM_ENABLE_MASK BIT(0) > > > +#define SET_SEC_CNTR_VAL_INIT (BIT(28) || BIT(29)) > > > +#define SEC_PULSE_SEL_INNER BIT(31) > > > +#define SEC_PULSE_GEN_SEL_MASK GENMASK(30, 0) > > > +#define CALIB_SEL_FTUNE_MASK GENMASK(30, 0) > > > +#define CALIB_SEL_FTUNE_INNER 0x00UL > > > + > > > +struct cv1800_rtc_priv { > > > + struct rtc_device *rtc_dev; > > > + struct regmap *rtc_map; > > > + struct clk *clk; > > > + int irq; > > > +}; > > > + > > > +static const struct regmap_config cv1800_rtc_regmap_config = { > > > + .reg_bits = 32, > > > + .val_bits = 32, > > > + .reg_stride = 4, > > > +}; > > > + > > > +static int cv1800_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) > > > +{ > > > + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); > > > + > > > + regmap_write(info->rtc_map, ALARM_ENABLE, enabled); > > > + > > > + return 0; > > > +} > > > + > > > +static int cv1800_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) > > > +{ > > > + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); > > > + unsigned long alarm_time; > > > + > > > + alarm_time = rtc_tm_to_time64(&alrm->time); > > > + > > > + cv1800_rtc_alarm_irq_enable(dev, REG_DISABLE_FUN); > > > + > > > + regmap_write(info->rtc_map, ALARM_TIME, alarm_time); > > > + > > > + cv1800_rtc_alarm_irq_enable(dev, alrm->enabled); > > > + > > > + return 0; > > > +} > > > + > > > +static int cv1800_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) > > > +{ > > > + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); > > > + u32 enabled; > > > + u32 time; > > > + > > > + regmap_read(info->rtc_map, ALARM_ENABLE, &enabled); > > > + > > > + alarm->enabled = enabled & ALARM_ENABLE_MASK; > > > + > > > + regmap_read(info->rtc_map, ALARM_TIME, &time); > > > + > > > + rtc_time64_to_tm(time, &alarm->time); > > > + > > > + return 0; > > > +} > > > + > > > +static void rtc_enable_sec_counter(struct cv1800_rtc_priv *info) > > > +{ > > > + u32 sec_ro_t; > > > + u32 sec; > > > + > > > + /* select inner sec pulse */ > > > + regmap_update_bits(info->rtc_map, SEC_PULSE_GEN, > > > + (u32)(~SEC_PULSE_GEN_SEL_MASK), > > > + (u32)(~SEC_PULSE_SEL_INNER)); > > > + > > > + regmap_update_bits(info->rtc_map, ANA_CALIB, > > > + (u32)(~CALIB_SEL_FTUNE_MASK), > > > + CALIB_SEL_FTUNE_INNER); > > > + > > > + sec = SET_SEC_CNTR_VAL_INIT; > > > + > > > + /* load from MACRO register */ > > > + regmap_read(info->rtc_map, MACRO_RO_T, &sec_ro_t); > > > + if (sec_ro_t > (SET_SEC_CNTR_VAL_INIT)) > > > + sec = sec_ro_t; > > > + > > > + regmap_write(info->rtc_map, SET_SEC_CNTR_VAL, sec); > > > + regmap_write(info->rtc_map, SET_SEC_CNTR_TRIG, REG_ENABLE_FUN); > > > +} > > > + > > > +static int cv1800_rtc_read_time(struct device *dev, struct rtc_time *tm) > > > +{ > > > + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); > > > + u32 sec; > > > + > > > + regmap_read(info->rtc_map, SEC_CNTR_VAL, &sec); > > > + > > > + rtc_time64_to_tm(sec, tm); > > > + > > > + return 0; > > > +} > > > + > > > +static int cv1800_rtc_set_time(struct device *dev, struct rtc_time *tm) > > > +{ > > > + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); > > > + unsigned long sec; > > > + > > > + sec = rtc_tm_to_time64(tm); > > > + > > > + regmap_write(info->rtc_map, SET_SEC_CNTR_VAL, sec); > > > + regmap_write(info->rtc_map, SET_SEC_CNTR_TRIG, REG_ENABLE_FUN); > > > + > > > + regmap_write(info->rtc_map, MACRO_RG_SET_T, sec); > > > + > > > + return 0; > > > +} > > > + > > > +static irqreturn_t cv1800_rtc_irq_handler(int irq, void *dev_id) > > > +{ > > > + struct rtc_device *rtc = dev_id; > > > + > > > + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); > > > + > > > + return IRQ_HANDLED; > > > +} > > > + > > > +static const struct rtc_class_ops cv1800_rtc_ops = { > > > + .read_time = cv1800_rtc_read_time, > > > + .set_time = cv1800_rtc_set_time, > > > + .read_alarm = cv1800_rtc_read_alarm, > > > + .set_alarm = cv1800_rtc_set_alarm, > > > + .alarm_irq_enable = cv1800_rtc_alarm_irq_enable, > > > +}; > > > + > > > +static int cv1800_rtc_probe(struct platform_device *pdev) > > > +{ > > > + struct cv1800_rtc_priv *rtc; > > > + u32 ctrl_val; > > > + void __iomem *base; > > > + int ret; > > > + > > > + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); > > > + if (!rtc) > > > + return -ENOMEM; > > > + > > > + base = devm_platform_ioremap_resource(pdev, 0); > > > + if (IS_ERR(base)) > > > + return PTR_ERR(base); > > > + > > > + rtc->rtc_map = devm_regmap_init_mmio(&pdev->dev, base, > > > + &cv1800_rtc_regmap_config); > > > + if (IS_ERR(rtc->rtc_map)) > > > + return PTR_ERR(rtc->rtc_map); > > > + > > > + rtc->irq = platform_get_irq(pdev, 0); > > > + if (rtc->irq < 0) > > > + return rtc->irq; > > > + > > > + ret = devm_request_irq(&pdev->dev, rtc->irq, cv1800_rtc_irq_handler, > > > + IRQF_TRIGGER_HIGH, "alarm", &pdev->dev); > > > + if (ret) > > > + return dev_err_probe(&pdev->dev, ret, > > > + "cannot register interrupt handler\n"); > > > + > > > + rtc->clk = devm_clk_get_enabled(&pdev->dev, NULL); > > > + if (IS_ERR(rtc->clk)) > > > + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->clk), > > > + "clk not found\n"); > > > + > > > + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); > > > + if (IS_ERR(rtc->rtc_dev)) > > > + return PTR_ERR(rtc->rtc_dev); > > > + > > > + platform_set_drvdata(pdev, rtc); > > > + > > > + rtc->rtc_dev->ops = &cv1800_rtc_ops; > > > + rtc->rtc_dev->range_max = U32_MAX; > > > + > > > > > + regmap_read(rtc->rtc_map, CTRL, &ctrl_val); > > > + ctrl_val &= CTRL_MODE_MASK; > > > + > > > + if (ctrl_val == CTRL_MODE_OSC32K) > > > + rtc_enable_sec_counter(rtc); > > > + > > > > use (ctrl_val & CTRL_MODE_OSC32K). > > > > Another thing is that I do not think is a good way to let the > > rtc driver access RTC_CTRL area directly. You have already > > know there is a 8051 device in the 0x05025000. It is necessary > > to make some room for this device. Maybe you want to implement > > them all in the rtc driver? If so, I do think it is a bad idea. > > > Do you mean that RTC drivers should not directly access the 0x05025000 address? > Because there is an 8051 subsystem on this address. Yes. At least we need some mechanism to share these address between this devices. > Firstly, I do not intend to implement 8051 in the RTC driver, > but the 8051 subsystem is located within a module independently > powered by the RTC. > So if we want to implement the 8051 subsystem in the future, it can be > used as a node in RTC? I'm not sure. Yes, this is what I care about. > Then, Alexandre told me that there are operations related to PM in > RTC, such as the following files. > This matches the description of address 0x05025000. > > drivers/rtc/rtc jz4740. c > I do not think this is something related to the PM. 8051 is more like remoteproc. So it is necessary to arrange them carefully. > > > > > + return devm_rtc_register_device(rtc->rtc_dev); > > > +} > > > + > > > +static const struct of_device_id cv1800_dt_ids[] = { > > > + { .compatible = "sophgo,cv1800-rtc" }, > > > + { /* sentinel */ }, > > > +}; > > > +MODULE_DEVICE_TABLE(of, cv1800_dt_ids); > > > + > > > +static struct platform_driver cv1800_rtc_driver = { > > > + .driver = { > > > + .name = "sophgo-cv1800-rtc", > > > + .of_match_table = cv1800_dt_ids, > > > + }, > > > + .probe = cv1800_rtc_probe, > > > +}; > > > + > > > +module_platform_driver(cv1800_rtc_driver); > > > +MODULE_AUTHOR("Jingbao Qiu"); > > > +MODULE_DESCRIPTION("Sophgo cv1800 RTC Driver"); > > > +MODULE_LICENSE("GPL"); > > > -- > > > 2.25.1 > > >