Message ID | 20240412084543.7243-3-ziv.xu@starfivetech.com (mailing list archive) |
---|---|
State | Handled Elsewhere |
Headers | show |
Series | Add timer driver for StarFive JH7110 RISC-V SoC | expand |
> -----邮件原件----- > 发件人: Ziv Xu > 发送时间: 2024年4月12日 16:46 > 收件人: Daniel Lezcano <daniel.lezcano@linaro.org>; Thomas Gleixner > <tglx@linutronix.de>; Emil Renner Berthing > <emil.renner.berthing@canonical.com>; Christophe JAILLET > <christophe.jaillet@wanadoo.fr> > 抄送: linux-riscv@lists.infradead.org; devicetree@vger.kernel.org; Rob Herring > <robh+dt@kernel.org>; Krzysztof Kozlowski > <krzysztof.kozlowski+dt@linaro.org>; Paul Walmsley > <paul.walmsley@sifive.com>; Palmer Dabbelt <palmer@dabbelt.com>; Albert > Ou <aou@eecs.berkeley.edu>; Philipp Zabel <p.zabel@pengutronix.de>; Walker > Chen <walker.chen@starfivetech.com>; Xingyu Wu > <xingyu.wu@starfivetech.com>; linux-kernel@vger.kernel.org; Conor Dooley > <conor@kernel.org> > 主题: [PATCH v10 2/3] clocksource: Add JH7110 timer driver > > From: Xingyu Wu <xingyu.wu@starfivetech.com> > > Add timer driver for the StarFive JH7110 SoC. > > This timer has four free-running and independent 32-bit counters. > Each channel(counter) can trigger an interrupt when timeout even CPU is > sleeping. So this timer is used as global timer and register clockevent for each > CPU core after riscv-timer registration on the StarFive JH7110 SoC. > > Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com> > Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com> > --- > MAINTAINERS | 7 + > drivers/clocksource/Kconfig | 11 + > drivers/clocksource/Makefile | 1 + > drivers/clocksource/timer-jh7110.c | 345 +++++++++++++++++++++++++++++ > include/linux/cpuhotplug.h | 1 + > 5 files changed, 365 insertions(+) > create mode 100644 drivers/clocksource/timer-jh7110.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 7c121493f43d..ef9b5f5bad9e 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -21043,6 +21043,13 @@ S: Maintained > F: Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml > F: sound/soc/starfive/jh7110_tdm.c > > +STARFIVE JH7110 TIMER DRIVER > +M: Samin Guo <samin.guo@starfivetech.com> > +M: Xingyu Wu <xingyu.wu@starfivetech.com> > +S: Supported > +F: Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml > +F: drivers/clocksource/timer-jh7110.c > + > STARFIVE JH71X0 CLOCK DRIVERS > M: Emil Renner Berthing <kernel@esmil.dk> > M: Hal Feng <hal.feng@starfivetech.com> > diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index > 34faa0320ece..2dc97201dee1 100644 > --- a/drivers/clocksource/Kconfig > +++ b/drivers/clocksource/Kconfig > @@ -641,6 +641,17 @@ config RISCV_TIMER > is accessed via both the SBI and the rdcycle instruction. This is > required for all RISC-V systems. > > +config STARFIVE_JH7110_TIMER > + bool "Timer for the STARFIVE JH7110 SoC" > + depends on ARCH_STARFIVE || COMPILE_TEST > + select TIMER_OF > + select CLKSRC_MMIO > + default ARCH_STARFIVE > + help > + This enables the timer for StarFive JH7110 SoC. On RISC-V platform, > + the system has started RISCV_TIMER, but you can also use this timer > + which can provide four channels to do a lot more things on JH7110 SoC. > + > config CLINT_TIMER > bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST > depends on GENERIC_SCHED_CLOCK && RISCV diff --git > a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index > 4bb856e4df55..8dc2f0ea2d0f 100644 > --- a/drivers/clocksource/Makefile > +++ b/drivers/clocksource/Makefile > @@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o > obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o > obj-$(CONFIG_X86_NUMACHIP) += numachip.o > obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o > +obj-$(CONFIG_STARFIVE_JH7110_TIMER) += timer-jh7110.o > obj-$(CONFIG_CLINT_TIMER) += timer-clint.o > obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o > obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o > diff --git a/drivers/clocksource/timer-jh7110.c > b/drivers/clocksource/timer-jh7110.c > new file mode 100644 > index 000000000000..dc770507f209 > --- /dev/null > +++ b/drivers/clocksource/timer-jh7110.c > @@ -0,0 +1,345 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Starfive JH7110 Timer driver > + * > + * Copyright (C) 2023 StarFive Technology Co., Ltd. > + * > + * This timer has four free-running and independent 32-bit counters and > +runs in 24MHz > + * clock on the StarFive JH7110 SoC. Each channel(counter) can trigger > +an interrupt > + * when timeout even CPU is sleeping. They support one-shot mode and > continuous-run mode. > + * > + * Each channel is used as a global timer that serves each cpu core: > + * JH7110 Timer Channel 0 -- CPU 0 > + * JH7110 Timer Channel 1 -- CPU 1 > + * JH7110 Timer Channel 2 -- CPU 2 > + * JH7110 Timer Channel 3 -- CPU 3 > + */ > + > +#include <linux/clk.h> > +#include <linux/clockchips.h> > +#include <linux/cpu.h> > +#include <linux/iopoll.h> > +#include <linux/irq.h> > +#include <linux/platform_device.h> > +#include <linux/reset.h> > + > +/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ > +#define JH7110_TIMER_CH_LEN 0x40 > +#define JH7110_TIMER_CH_BASE(x) ((x) * JH7110_TIMER_CH_LEN) > +#define JH7110_TIMER_CH_MAX 4 > + > +#define JH7110_DELAY_US 0 > +#define JH7110_TIMEOUT_US 10000 > +#define JH7110_CLOCKEVENT_RATING 300 > +#define JH7110_TIMER_MAX_TICKS 0xffffffff > +#define JH7110_TIMER_MIN_TICKS 0xf > + > +#define JH7110_TIMER_INT_STATUS 0x00 /* RO[0:4]: Interrupt Status > for channel0~4 */ > +#define JH7110_TIMER_CTL 0x04 /* RW[0]: 0-continuous run, 1-single run > */ > +#define JH7110_TIMER_LOAD 0x08 /* RW: load value to counter */ > +#define JH7110_TIMER_ENABLE 0x10 /* RW[0]: timer enable register */ > +#define JH7110_TIMER_RELOAD 0x14 /* RW: write 1 or 0 both reload > counter */ > +#define JH7110_TIMER_VALUE 0x18 /* RO: timer value register */ > +#define JH7110_TIMER_INT_CLR 0x20 /* RW: timer interrupt clear > register */ > +#define JH7110_TIMER_INT_MASK 0x24 /* RW[0]: timer interrupt > mask register */ > + > +#define JH7110_TIMER_INT_CLR_ENA BIT(0) > +#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1) > + > +#define JH7110_PERCPU_GET_CLKEVT > (&jh7110_timer_info.clkevt[smp_processor_id()]) > + > +/** > + * struct jh7110_clkevt - Description of each timer channel > + * @clk: Clock of each timer channel > + * @rst: Reset of each timer channel > + * @base: Virtual address of each timer channel > + * @irq: Interrupt number of each timer channel > + * @timer_enabled: Enabled flag for each timer channel > + * @name: Name of each timer channel > + */ > +struct jh7110_clkevt { > + struct clk *clk; > + struct reset_control *rst; > + void __iomem *base; > + int irq; > + bool timer_enabled; > + char name[sizeof("jh7110-timer.chX")]; > +}; > + > +struct jh7110_timer_priv { > + struct clk *pclk; > + struct reset_control *prst; > + struct device *dev; > + struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX]; > +}; > + > +static struct jh7110_timer_priv jh7110_timer_info; > + > +/* 0:continuous-run mode, 1:single-run mode */ enum jh7110_timer_mode { > + JH7110_TIMER_MODE_CONTIN, > + JH7110_TIMER_MODE_SINGLE, > +}; > + > +/* Interrupt Mask, 0:Unmask, 1:Mask */ > +enum jh7110_timer_int_mask { > + JH7110_TIMER_INT_ENA, > + JH7110_TIMER_INT_DIS, > +}; > + > +enum jh7110_timer_enable { > + JH7110_TIMER_DIS, > + JH7110_TIMER_ENA, > +}; > + > +/* > + * BIT(0): Read value represent channel int status. > + * Write 1 to this bit to clear interrupt. Write 0 has no effects. > + * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. > + */ > +static inline int jh7110_timer_int_clear(struct jh7110_clkevt *clkevt) > +{ > + u32 value; > + int ret; > + > + /* Waiting interrupt can be cleared */ > + ret = readl_poll_timeout_atomic(clkevt->base + JH7110_TIMER_INT_CLR, > value, > + !(value & JH7110_TIMER_INT_CLR_AVA_MASK), > + JH7110_DELAY_US, JH7110_TIMEOUT_US); > + if (!ret) > + writel(JH7110_TIMER_INT_CLR_ENA, clkevt->base + > +JH7110_TIMER_INT_CLR); > + > + return ret; > +} > + > +static int jh7110_timer_start(struct jh7110_clkevt *clkevt) { > + int ret; > + > + /* Disable and clear interrupt first */ > + writel(JH7110_TIMER_INT_DIS, clkevt->base + > JH7110_TIMER_INT_MASK); > + ret = jh7110_timer_int_clear(clkevt); > + > + writel(JH7110_TIMER_INT_ENA, clkevt->base + > JH7110_TIMER_INT_MASK); > + writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE); > + > + return ret; > +} > + > +static int jh7110_timer_shutdown(struct clock_event_device *evt) { > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > + > + writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); > + return jh7110_timer_int_clear(clkevt); } > + > +/* IRQ handler for the timer */ > +static irqreturn_t jh7110_timer_interrupt(int irq, void *data) { > + struct clock_event_device *evt = (struct clock_event_device *)data; > + struct jh7110_clkevt *clkevt = &jh7110_timer_info.clkevt[0]; > + u32 reg = readl(clkevt->base + JH7110_TIMER_INT_STATUS); > + u8 cpu_id = smp_processor_id(); > + > + /* Check interrupt status and channel(cpu) ID */ > + if (!(reg & BIT(cpu_id))) > + return IRQ_NONE; > + > + clkevt = &jh7110_timer_info.clkevt[cpu_id]; > + writel(JH7110_TIMER_INT_CLR_ENA, (clkevt->base + > +JH7110_TIMER_INT_CLR)); > + > + if (evt->event_handler) > + evt->event_handler(evt); > + > + return IRQ_HANDLED; > +} > + > +static int jh7110_timer_set_periodic(struct clock_event_device *evt) { > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > + > + writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + > JH7110_TIMER_CTL); > + return 0; > +} > + > +static int jh7110_timer_set_oneshot(struct clock_event_device *evt) { > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > + > + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + > JH7110_TIMER_CTL); > + return 0; > +} > + > +static int jh7110_timer_set_next_event(unsigned long next, > + struct clock_event_device *evt) { > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > + > + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + > JH7110_TIMER_CTL); > + writel(next, clkevt->base + JH7110_TIMER_LOAD); > + > + return jh7110_timer_start(clkevt); > +} > + > +static DEFINE_PER_CPU(struct clock_event_device, jh7110_clock_event) = { > + .features = CLOCK_EVT_FEAT_PERIODIC | > + CLOCK_EVT_FEAT_ONESHOT, > + .rating = JH7110_CLOCKEVENT_RATING, > + .set_state_shutdown = jh7110_timer_shutdown, > + .set_state_periodic = jh7110_timer_set_periodic, > + .set_state_oneshot = jh7110_timer_set_oneshot, > + .set_state_oneshot_stopped = jh7110_timer_shutdown, > + .set_next_event = jh7110_timer_set_next_event, > +}; > + > +static int jh7110_timer_dying_cpu(unsigned int cpu) { > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > + > + if (!priv->clkevt[cpu].timer_enabled) > + return 0; > + > + writel(JH7110_TIMER_DIS, priv->clkevt[cpu].base + > JH7110_TIMER_ENABLE); > + jh7110_timer_int_clear(&priv->clkevt[cpu]); > + reset_control_assert(priv->clkevt[cpu].rst); > + clk_disable_unprepare(priv->clkevt[cpu].clk); > + > + return 0; > +} > + > +static int jh7110_timer_starting_cpu(unsigned int cpu) { > + struct clock_event_device *evt = per_cpu_ptr(&jh7110_clock_event, cpu); > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > + int err; > + u32 rate; > + > + if (cpu >= JH7110_TIMER_CH_MAX) > + return -ENOMEM; > + > + err = clk_prepare_enable(priv->clkevt[cpu].clk); > + if (err) > + goto err_starting_cpu; > + > + err = reset_control_deassert(priv->clkevt[cpu].rst); > + if (err) > + goto err_soft_reset; > + > + rate = clk_get_rate(priv->clkevt[cpu].clk); > + evt->cpumask = cpumask_of(cpu); > + evt->irq = priv->clkevt[cpu].irq; > + > + err = irq_force_affinity(evt->irq, cpumask_of(cpu)); > + if (err) > + goto err_affinity; > + > + clockevents_config_and_register(evt, rate, JH7110_TIMER_MIN_TICKS, > + JH7110_TIMER_MAX_TICKS); > + > + /* Use one-shot mode */ > + writel(JH7110_TIMER_MODE_SINGLE, (priv->clkevt[cpu].base + > +JH7110_TIMER_CTL)); > + > + priv->clkevt[cpu].timer_enabled = true; > + > + err = jh7110_timer_start(&priv->clkevt[cpu]); > + if (err) > + goto err_affinity; > + return 0; > + > +err_affinity: > + reset_control_assert(priv->clkevt[cpu].rst); > +err_soft_reset: > + clk_disable_unprepare(priv->clkevt[cpu].clk); > +err_starting_cpu: > + free_irq(evt->irq, evt); > + return err; > +} > + > +static int jh7110_timer_probe(struct platform_device *pdev) { > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > + struct clock_event_device *evt; > + struct jh7110_clkevt *clkevt; > + char name[sizeof("chX")]; > + int ch; > + int ret; > + void __iomem *base; > + > + base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(base)) > + return dev_err_probe(&pdev->dev, PTR_ERR(base), > + "failed to map registers\n"); > + > + priv->prst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); > + if (IS_ERR(priv->prst)) > + return dev_err_probe(&pdev->dev, PTR_ERR(priv->prst), > + "failed to get apb reset\n"); > + > + priv->pclk = devm_clk_get_enabled(&pdev->dev, "apb"); > + if (IS_ERR(priv->pclk)) > + return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), > + "failed to get & enable apb clock\n"); > + > + ret = reset_control_deassert(priv->prst); > + if (ret) > + return dev_err_probe(&pdev->dev, ret, "failed to deassert apb > +reset\n"); > + > + for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) { > + evt = per_cpu_ptr(&jh7110_clock_event, ch); > + clkevt = &priv->clkevt[ch]; > + snprintf(name, sizeof(name), "ch%d", ch); > + > + clkevt->base = base + JH7110_TIMER_CH_BASE(ch); > + /* Ensure timer is disabled */ > + writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); > + ret = jh7110_timer_int_clear(clkevt); > + if (ret) > + return ret; > + > + clkevt->rst = devm_reset_control_get_exclusive(&pdev->dev, name); > + if (IS_ERR(clkevt->rst)) > + return PTR_ERR(clkevt->rst); > + > + clkevt->clk = devm_clk_get(&pdev->dev, name); > + if (IS_ERR(clkevt->clk)) > + return PTR_ERR(clkevt->clk); > + > + clkevt->irq = platform_get_irq(pdev, ch); > + if (clkevt->irq < 0) > + return clkevt->irq; > + > + snprintf(clkevt->name, sizeof(clkevt->name), "jh7110-timer.ch%d", > ch); > + ret = devm_request_irq(&pdev->dev, clkevt->irq, > jh7110_timer_interrupt, > + IRQF_TIMER | IRQF_IRQPOLL, > + clkevt->name, evt); > + > + if (ret) > + return ret; > + > + clkevt->timer_enabled = false; > + } > + > + return cpuhp_setup_state(CPUHP_AP_JH7110_TIMER_STARTING, > + "clockevents/jh7110/timer:starting", > + jh7110_timer_starting_cpu, jh7110_timer_dying_cpu); } > + > +static const struct of_device_id jh7110_timer_match[] = { > + { .compatible = "starfive,jh7110-timer", }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, jh7110_timer_match); > + > +static struct platform_driver jh7110_timer_driver = { > + .probe = jh7110_timer_probe, > + .driver = { > + .name = "jh7110-timer", > + .of_match_table = jh7110_timer_match, > + }, > +}; > +module_platform_driver(jh7110_timer_driver); > + > +MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>"); > +MODULE_DESCRIPTION("StarFive JH7110 timer driver"); > +MODULE_LICENSE("GPL"); > diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index > 35e78ddb2b37..4a8b487c327e 100644 > --- a/include/linux/cpuhotplug.h > +++ b/include/linux/cpuhotplug.h > @@ -175,6 +175,7 @@ enum cpuhp_state { > CPUHP_AP_CSKY_TIMER_STARTING, > CPUHP_AP_TI_GP_TIMER_STARTING, > CPUHP_AP_HYPERV_TIMER_STARTING, > + CPUHP_AP_JH7110_TIMER_STARTING, > /* Must be the last timer callback */ > CPUHP_AP_DUMMY_TIMER_STARTING, > CPUHP_AP_ARM_XEN_STARTING, > -- > 2.17.1 Hi Daniel / Thomas I have submitted new version of patch for jh7110 timer driver. Could you please help to review and give your comments? Thanks a lot! Best regards, Ziv.Xu
Ziv Xu wrote: > > > > -----邮件原件----- > > 发件人: Ziv Xu > > 发送时间: 2024年4月12日 16:46 > > 收件人: Daniel Lezcano <daniel.lezcano@linaro.org>; Thomas Gleixner > > <tglx@linutronix.de>; Emil Renner Berthing > > <emil.renner.berthing@canonical.com>; Christophe JAILLET > > <christophe.jaillet@wanadoo.fr> > > 抄送: linux-riscv@lists.infradead.org; devicetree@vger.kernel.org; Rob Herring > > <robh+dt@kernel.org>; Krzysztof Kozlowski > > <krzysztof.kozlowski+dt@linaro.org>; Paul Walmsley > > <paul.walmsley@sifive.com>; Palmer Dabbelt <palmer@dabbelt.com>; Albert > > Ou <aou@eecs.berkeley.edu>; Philipp Zabel <p.zabel@pengutronix.de>; Walker > > Chen <walker.chen@starfivetech.com>; Xingyu Wu > > <xingyu.wu@starfivetech.com>; linux-kernel@vger.kernel.org; Conor Dooley > > <conor@kernel.org> > > 主题: [PATCH v10 2/3] clocksource: Add JH7110 timer driver > > > > From: Xingyu Wu <xingyu.wu@starfivetech.com> > > > > Add timer driver for the StarFive JH7110 SoC. > > > > This timer has four free-running and independent 32-bit counters. > > Each channel(counter) can trigger an interrupt when timeout even CPU is > > sleeping. So this timer is used as global timer and register clockevent for each > > CPU core after riscv-timer registration on the StarFive JH7110 SoC. > > > > Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com> > > Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com> > > --- > > MAINTAINERS | 7 + > > drivers/clocksource/Kconfig | 11 + > > drivers/clocksource/Makefile | 1 + > > drivers/clocksource/timer-jh7110.c | 345 +++++++++++++++++++++++++++++ > > include/linux/cpuhotplug.h | 1 + > > 5 files changed, 365 insertions(+) > > create mode 100644 drivers/clocksource/timer-jh7110.c > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 7c121493f43d..ef9b5f5bad9e 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -21043,6 +21043,13 @@ S: Maintained > > F: Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml > > F: sound/soc/starfive/jh7110_tdm.c > > > > +STARFIVE JH7110 TIMER DRIVER > > +M: Samin Guo <samin.guo@starfivetech.com> > > +M: Xingyu Wu <xingyu.wu@starfivetech.com> > > +S: Supported > > +F: Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml > > +F: drivers/clocksource/timer-jh7110.c > > + > > STARFIVE JH71X0 CLOCK DRIVERS > > M: Emil Renner Berthing <kernel@esmil.dk> > > M: Hal Feng <hal.feng@starfivetech.com> > > diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index > > 34faa0320ece..2dc97201dee1 100644 > > --- a/drivers/clocksource/Kconfig > > +++ b/drivers/clocksource/Kconfig > > @@ -641,6 +641,17 @@ config RISCV_TIMER > > is accessed via both the SBI and the rdcycle instruction. This is > > required for all RISC-V systems. > > > > +config STARFIVE_JH7110_TIMER > > + bool "Timer for the STARFIVE JH7110 SoC" > > + depends on ARCH_STARFIVE || COMPILE_TEST > > + select TIMER_OF > > + select CLKSRC_MMIO > > + default ARCH_STARFIVE > > + help > > + This enables the timer for StarFive JH7110 SoC. On RISC-V platform, > > + the system has started RISCV_TIMER, but you can also use this timer > > + which can provide four channels to do a lot more things on JH7110 SoC. > > + > > config CLINT_TIMER > > bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST > > depends on GENERIC_SCHED_CLOCK && RISCV diff --git > > a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index > > 4bb856e4df55..8dc2f0ea2d0f 100644 > > --- a/drivers/clocksource/Makefile > > +++ b/drivers/clocksource/Makefile > > @@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o > > obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o > > obj-$(CONFIG_X86_NUMACHIP) += numachip.o > > obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o > > +obj-$(CONFIG_STARFIVE_JH7110_TIMER) += timer-jh7110.o > > obj-$(CONFIG_CLINT_TIMER) += timer-clint.o > > obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o > > obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o > > diff --git a/drivers/clocksource/timer-jh7110.c > > b/drivers/clocksource/timer-jh7110.c > > new file mode 100644 > > index 000000000000..dc770507f209 > > --- /dev/null > > +++ b/drivers/clocksource/timer-jh7110.c > > @@ -0,0 +1,345 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Starfive JH7110 Timer driver > > + * > > + * Copyright (C) 2023 StarFive Technology Co., Ltd. > > + * > > + * This timer has four free-running and independent 32-bit counters and > > +runs in 24MHz > > + * clock on the StarFive JH7110 SoC. Each channel(counter) can trigger > > +an interrupt > > + * when timeout even CPU is sleeping. They support one-shot mode and > > continuous-run mode. > > + * > > + * Each channel is used as a global timer that serves each cpu core: > > + * JH7110 Timer Channel 0 -- CPU 0 > > + * JH7110 Timer Channel 1 -- CPU 1 > > + * JH7110 Timer Channel 2 -- CPU 2 > > + * JH7110 Timer Channel 3 -- CPU 3 > > + */ > > + > > +#include <linux/clk.h> > > +#include <linux/clockchips.h> > > +#include <linux/cpu.h> > > +#include <linux/iopoll.h> > > +#include <linux/irq.h> > > +#include <linux/platform_device.h> > > +#include <linux/reset.h> > > + > > +/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ > > +#define JH7110_TIMER_CH_LEN 0x40 > > +#define JH7110_TIMER_CH_BASE(x) ((x) * JH7110_TIMER_CH_LEN) > > +#define JH7110_TIMER_CH_MAX 4 > > + > > +#define JH7110_DELAY_US 0 > > +#define JH7110_TIMEOUT_US 10000 > > +#define JH7110_CLOCKEVENT_RATING 300 > > +#define JH7110_TIMER_MAX_TICKS 0xffffffff > > +#define JH7110_TIMER_MIN_TICKS 0xf > > + > > +#define JH7110_TIMER_INT_STATUS 0x00 /* RO[0:4]: Interrupt Status > > for channel0~4 */ > > +#define JH7110_TIMER_CTL 0x04 /* RW[0]: 0-continuous run, 1-single run > > */ > > +#define JH7110_TIMER_LOAD 0x08 /* RW: load value to counter */ > > +#define JH7110_TIMER_ENABLE 0x10 /* RW[0]: timer enable register */ > > +#define JH7110_TIMER_RELOAD 0x14 /* RW: write 1 or 0 both reload > > counter */ > > +#define JH7110_TIMER_VALUE 0x18 /* RO: timer value register */ > > +#define JH7110_TIMER_INT_CLR 0x20 /* RW: timer interrupt clear > > register */ > > +#define JH7110_TIMER_INT_MASK 0x24 /* RW[0]: timer interrupt > > mask register */ > > + > > +#define JH7110_TIMER_INT_CLR_ENA BIT(0) > > +#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1) > > + > > +#define JH7110_PERCPU_GET_CLKEVT > > (&jh7110_timer_info.clkevt[smp_processor_id()]) > > + > > +/** > > + * struct jh7110_clkevt - Description of each timer channel > > + * @clk: Clock of each timer channel > > + * @rst: Reset of each timer channel > > + * @base: Virtual address of each timer channel > > + * @irq: Interrupt number of each timer channel > > + * @timer_enabled: Enabled flag for each timer channel > > + * @name: Name of each timer channel > > + */ > > +struct jh7110_clkevt { > > + struct clk *clk; > > + struct reset_control *rst; > > + void __iomem *base; > > + int irq; > > + bool timer_enabled; > > + char name[sizeof("jh7110-timer.chX")]; > > +}; > > + > > +struct jh7110_timer_priv { > > + struct clk *pclk; > > + struct reset_control *prst; > > + struct device *dev; > > + struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX]; > > +}; > > + > > +static struct jh7110_timer_priv jh7110_timer_info; > > + > > +/* 0:continuous-run mode, 1:single-run mode */ enum jh7110_timer_mode { > > + JH7110_TIMER_MODE_CONTIN, > > + JH7110_TIMER_MODE_SINGLE, > > +}; > > + > > +/* Interrupt Mask, 0:Unmask, 1:Mask */ > > +enum jh7110_timer_int_mask { > > + JH7110_TIMER_INT_ENA, > > + JH7110_TIMER_INT_DIS, > > +}; > > + > > +enum jh7110_timer_enable { > > + JH7110_TIMER_DIS, > > + JH7110_TIMER_ENA, > > +}; > > + > > +/* > > + * BIT(0): Read value represent channel int status. > > + * Write 1 to this bit to clear interrupt. Write 0 has no effects. > > + * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. > > + */ > > +static inline int jh7110_timer_int_clear(struct jh7110_clkevt *clkevt) > > +{ > > + u32 value; > > + int ret; > > + > > + /* Waiting interrupt can be cleared */ > > + ret = readl_poll_timeout_atomic(clkevt->base + JH7110_TIMER_INT_CLR, > > value, > > + !(value & JH7110_TIMER_INT_CLR_AVA_MASK), > > + JH7110_DELAY_US, JH7110_TIMEOUT_US); > > + if (!ret) > > + writel(JH7110_TIMER_INT_CLR_ENA, clkevt->base + > > +JH7110_TIMER_INT_CLR); > > + > > + return ret; > > +} > > + > > +static int jh7110_timer_start(struct jh7110_clkevt *clkevt) { > > + int ret; > > + > > + /* Disable and clear interrupt first */ > > + writel(JH7110_TIMER_INT_DIS, clkevt->base + > > JH7110_TIMER_INT_MASK); > > + ret = jh7110_timer_int_clear(clkevt); > > + > > + writel(JH7110_TIMER_INT_ENA, clkevt->base + > > JH7110_TIMER_INT_MASK); > > + writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE); > > + > > + return ret; > > +} > > + > > +static int jh7110_timer_shutdown(struct clock_event_device *evt) { > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > + > > + writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); > > + return jh7110_timer_int_clear(clkevt); } > > + > > +/* IRQ handler for the timer */ > > +static irqreturn_t jh7110_timer_interrupt(int irq, void *data) { > > + struct clock_event_device *evt = (struct clock_event_device *)data; > > + struct jh7110_clkevt *clkevt = &jh7110_timer_info.clkevt[0]; > > + u32 reg = readl(clkevt->base + JH7110_TIMER_INT_STATUS); > > + u8 cpu_id = smp_processor_id(); > > + > > + /* Check interrupt status and channel(cpu) ID */ > > + if (!(reg & BIT(cpu_id))) > > + return IRQ_NONE; > > + > > + clkevt = &jh7110_timer_info.clkevt[cpu_id]; > > + writel(JH7110_TIMER_INT_CLR_ENA, (clkevt->base + > > +JH7110_TIMER_INT_CLR)); > > + > > + if (evt->event_handler) > > + evt->event_handler(evt); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static int jh7110_timer_set_periodic(struct clock_event_device *evt) { > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > + > > + writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + > > JH7110_TIMER_CTL); > > + return 0; > > +} > > + > > +static int jh7110_timer_set_oneshot(struct clock_event_device *evt) { > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > + > > + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + > > JH7110_TIMER_CTL); > > + return 0; > > +} > > + > > +static int jh7110_timer_set_next_event(unsigned long next, > > + struct clock_event_device *evt) { > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > + > > + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + > > JH7110_TIMER_CTL); > > + writel(next, clkevt->base + JH7110_TIMER_LOAD); > > + > > + return jh7110_timer_start(clkevt); > > +} > > + > > +static DEFINE_PER_CPU(struct clock_event_device, jh7110_clock_event) = { > > + .features = CLOCK_EVT_FEAT_PERIODIC | > > + CLOCK_EVT_FEAT_ONESHOT, > > + .rating = JH7110_CLOCKEVENT_RATING, > > + .set_state_shutdown = jh7110_timer_shutdown, > > + .set_state_periodic = jh7110_timer_set_periodic, > > + .set_state_oneshot = jh7110_timer_set_oneshot, > > + .set_state_oneshot_stopped = jh7110_timer_shutdown, > > + .set_next_event = jh7110_timer_set_next_event, > > +}; > > + > > +static int jh7110_timer_dying_cpu(unsigned int cpu) { > > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > > + > > + if (!priv->clkevt[cpu].timer_enabled) > > + return 0; > > + > > + writel(JH7110_TIMER_DIS, priv->clkevt[cpu].base + > > JH7110_TIMER_ENABLE); > > + jh7110_timer_int_clear(&priv->clkevt[cpu]); > > + reset_control_assert(priv->clkevt[cpu].rst); > > + clk_disable_unprepare(priv->clkevt[cpu].clk); > > + > > + return 0; > > +} > > + > > +static int jh7110_timer_starting_cpu(unsigned int cpu) { > > + struct clock_event_device *evt = per_cpu_ptr(&jh7110_clock_event, cpu); > > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > > + int err; > > + u32 rate; > > + > > + if (cpu >= JH7110_TIMER_CH_MAX) > > + return -ENOMEM; > > + > > + err = clk_prepare_enable(priv->clkevt[cpu].clk); > > + if (err) > > + goto err_starting_cpu; > > + > > + err = reset_control_deassert(priv->clkevt[cpu].rst); > > + if (err) > > + goto err_soft_reset; > > + > > + rate = clk_get_rate(priv->clkevt[cpu].clk); > > + evt->cpumask = cpumask_of(cpu); > > + evt->irq = priv->clkevt[cpu].irq; > > + > > + err = irq_force_affinity(evt->irq, cpumask_of(cpu)); > > + if (err) > > + goto err_affinity; > > + > > + clockevents_config_and_register(evt, rate, JH7110_TIMER_MIN_TICKS, > > + JH7110_TIMER_MAX_TICKS); > > + > > + /* Use one-shot mode */ > > + writel(JH7110_TIMER_MODE_SINGLE, (priv->clkevt[cpu].base + > > +JH7110_TIMER_CTL)); > > + > > + priv->clkevt[cpu].timer_enabled = true; > > + > > + err = jh7110_timer_start(&priv->clkevt[cpu]); > > + if (err) > > + goto err_affinity; > > + return 0; > > + > > +err_affinity: > > + reset_control_assert(priv->clkevt[cpu].rst); > > +err_soft_reset: > > + clk_disable_unprepare(priv->clkevt[cpu].clk); > > +err_starting_cpu: > > + free_irq(evt->irq, evt); > > + return err; > > +} > > + > > +static int jh7110_timer_probe(struct platform_device *pdev) { > > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > > + struct clock_event_device *evt; > > + struct jh7110_clkevt *clkevt; > > + char name[sizeof("chX")]; > > + int ch; > > + int ret; > > + void __iomem *base; > > + > > + base = devm_platform_ioremap_resource(pdev, 0); > > + if (IS_ERR(base)) > > + return dev_err_probe(&pdev->dev, PTR_ERR(base), > > + "failed to map registers\n"); > > + > > + priv->prst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); > > + if (IS_ERR(priv->prst)) > > + return dev_err_probe(&pdev->dev, PTR_ERR(priv->prst), > > + "failed to get apb reset\n"); > > + > > + priv->pclk = devm_clk_get_enabled(&pdev->dev, "apb"); > > + if (IS_ERR(priv->pclk)) > > + return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), > > + "failed to get & enable apb clock\n"); > > + > > + ret = reset_control_deassert(priv->prst); > > + if (ret) > > + return dev_err_probe(&pdev->dev, ret, "failed to deassert apb > > +reset\n"); > > + > > + for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) { > > + evt = per_cpu_ptr(&jh7110_clock_event, ch); > > + clkevt = &priv->clkevt[ch]; > > + snprintf(name, sizeof(name), "ch%d", ch); > > + > > + clkevt->base = base + JH7110_TIMER_CH_BASE(ch); > > + /* Ensure timer is disabled */ > > + writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); > > + ret = jh7110_timer_int_clear(clkevt); > > + if (ret) > > + return ret; > > + > > + clkevt->rst = devm_reset_control_get_exclusive(&pdev->dev, name); > > + if (IS_ERR(clkevt->rst)) > > + return PTR_ERR(clkevt->rst); > > + > > + clkevt->clk = devm_clk_get(&pdev->dev, name); > > + if (IS_ERR(clkevt->clk)) > > + return PTR_ERR(clkevt->clk); > > + > > + clkevt->irq = platform_get_irq(pdev, ch); > > + if (clkevt->irq < 0) > > + return clkevt->irq; > > + > > + snprintf(clkevt->name, sizeof(clkevt->name), "jh7110-timer.ch%d", > > ch); > > + ret = devm_request_irq(&pdev->dev, clkevt->irq, > > jh7110_timer_interrupt, > > + IRQF_TIMER | IRQF_IRQPOLL, > > + clkevt->name, evt); > > + > > + if (ret) > > + return ret; > > + > > + clkevt->timer_enabled = false; > > + } > > + > > + return cpuhp_setup_state(CPUHP_AP_JH7110_TIMER_STARTING, > > + "clockevents/jh7110/timer:starting", > > + jh7110_timer_starting_cpu, jh7110_timer_dying_cpu); } > > + > > +static const struct of_device_id jh7110_timer_match[] = { > > + { .compatible = "starfive,jh7110-timer", }, > > + { /* sentinel */ } > > +}; > > +MODULE_DEVICE_TABLE(of, jh7110_timer_match); > > + > > +static struct platform_driver jh7110_timer_driver = { > > + .probe = jh7110_timer_probe, > > + .driver = { > > + .name = "jh7110-timer", > > + .of_match_table = jh7110_timer_match, > > + }, > > +}; > > +module_platform_driver(jh7110_timer_driver); > > + > > +MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>"); > > +MODULE_DESCRIPTION("StarFive JH7110 timer driver"); > > +MODULE_LICENSE("GPL"); > > diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index > > 35e78ddb2b37..4a8b487c327e 100644 > > --- a/include/linux/cpuhotplug.h > > +++ b/include/linux/cpuhotplug.h > > @@ -175,6 +175,7 @@ enum cpuhp_state { > > CPUHP_AP_CSKY_TIMER_STARTING, > > CPUHP_AP_TI_GP_TIMER_STARTING, > > CPUHP_AP_HYPERV_TIMER_STARTING, > > + CPUHP_AP_JH7110_TIMER_STARTING, > > /* Must be the last timer callback */ > > CPUHP_AP_DUMMY_TIMER_STARTING, > > CPUHP_AP_ARM_XEN_STARTING, > > -- > > 2.17.1 > > Hi Daniel / Thomas > > I have submitted new version of patch for jh7110 timer driver. Could you please help to review and give your comments? > Thanks a lot! Hi Ziv I tried this on 6.9-rc6 on my VF2. It boots, but very slowly and "choppy". That is it repeatedly runs for <1s and then hangs for about 4s. Does this patch work for you? /Emil
> -----邮件原件----- > 发件人: Emil Renner Berthing <emil.renner.berthing@canonical.com> > 发送时间: 2024年4月30日 18:19 > 收件人: Ziv Xu <ziv.xu@starfivetech.com>; Daniel Lezcano > <daniel.lezcano@linaro.org>; Thomas Gleixner <tglx@linutronix.de> > 抄送: linux-riscv@lists.infradead.org; devicetree@vger.kernel.org; Rob Herring > <robh+dt@kernel.org>; Krzysztof Kozlowski > <krzysztof.kozlowski+dt@linaro.org>; Paul Walmsley > <paul.walmsley@sifive.com>; Palmer Dabbelt <palmer@dabbelt.com>; Albert > Ou <aou@eecs.berkeley.edu>; Philipp Zabel <p.zabel@pengutronix.de>; Walker > Chen <walker.chen@starfivetech.com>; Xingyu Wu > <xingyu.wu@starfivetech.com>; linux-kernel@vger.kernel.org; Conor Dooley > <conor@kernel.org> > 主题: Re: 回复: [PATCH v10 2/3] clocksource: Add JH7110 timer driver > > Ziv Xu wrote: > > > > > > > -----邮件原件----- > > > 发件人: Ziv Xu > > > 发送时间: 2024年4月12日 16:46 > > > 收件人: Daniel Lezcano <daniel.lezcano@linaro.org>; Thomas Gleixner > > > <tglx@linutronix.de>; Emil Renner Berthing > > > <emil.renner.berthing@canonical.com>; Christophe JAILLET > > > <christophe.jaillet@wanadoo.fr> > > > 抄送: linux-riscv@lists.infradead.org; devicetree@vger.kernel.org; Rob > > > Herring <robh+dt@kernel.org>; Krzysztof Kozlowski > > > <krzysztof.kozlowski+dt@linaro.org>; Paul Walmsley > > > <paul.walmsley@sifive.com>; Palmer Dabbelt <palmer@dabbelt.com>; > > > Albert Ou <aou@eecs.berkeley.edu>; Philipp Zabel > > > <p.zabel@pengutronix.de>; Walker Chen > > > <walker.chen@starfivetech.com>; Xingyu Wu > > > <xingyu.wu@starfivetech.com>; linux-kernel@vger.kernel.org; Conor > > > Dooley <conor@kernel.org> > > > 主题: [PATCH v10 2/3] clocksource: Add JH7110 timer driver > > > > > > From: Xingyu Wu <xingyu.wu@starfivetech.com> > > > > > > Add timer driver for the StarFive JH7110 SoC. > > > > > > This timer has four free-running and independent 32-bit counters. > > > Each channel(counter) can trigger an interrupt when timeout even CPU > > > is sleeping. So this timer is used as global timer and register > > > clockevent for each CPU core after riscv-timer registration on the StarFive > JH7110 SoC. > > > > > > Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com> > > > Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com> > > > --- > > > MAINTAINERS | 7 + > > > drivers/clocksource/Kconfig | 11 + > > > drivers/clocksource/Makefile | 1 + > > > drivers/clocksource/timer-jh7110.c | 345 > +++++++++++++++++++++++++++++ > > > include/linux/cpuhotplug.h | 1 + > > > 5 files changed, 365 insertions(+) > > > create mode 100644 drivers/clocksource/timer-jh7110.c > > > > > > diff --git a/MAINTAINERS b/MAINTAINERS index > > > 7c121493f43d..ef9b5f5bad9e 100644 > > > --- a/MAINTAINERS > > > +++ b/MAINTAINERS > > > @@ -21043,6 +21043,13 @@ S: Maintained > > > F: Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml > > > F: sound/soc/starfive/jh7110_tdm.c > > > > > > +STARFIVE JH7110 TIMER DRIVER > > > +M: Samin Guo <samin.guo@starfivetech.com> > > > +M: Xingyu Wu <xingyu.wu@starfivetech.com> > > > +S: Supported > > > +F: Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml > > > +F: drivers/clocksource/timer-jh7110.c > > > + > > > STARFIVE JH71X0 CLOCK DRIVERS > > > M: Emil Renner Berthing <kernel@esmil.dk> > > > M: Hal Feng <hal.feng@starfivetech.com> > > > diff --git a/drivers/clocksource/Kconfig > > > b/drivers/clocksource/Kconfig index > > > 34faa0320ece..2dc97201dee1 100644 > > > --- a/drivers/clocksource/Kconfig > > > +++ b/drivers/clocksource/Kconfig > > > @@ -641,6 +641,17 @@ config RISCV_TIMER > > > is accessed via both the SBI and the rdcycle instruction. This is > > > required for all RISC-V systems. > > > > > > +config STARFIVE_JH7110_TIMER > > > + bool "Timer for the STARFIVE JH7110 SoC" > > > + depends on ARCH_STARFIVE || COMPILE_TEST > > > + select TIMER_OF > > > + select CLKSRC_MMIO > > > + default ARCH_STARFIVE > > > + help > > > + This enables the timer for StarFive JH7110 SoC. On RISC-V platform, > > > + the system has started RISCV_TIMER, but you can also use this > timer > > > + which can provide four channels to do a lot more things on JH7110 > SoC. > > > + > > > config CLINT_TIMER > > > bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST > > > depends on GENERIC_SCHED_CLOCK && RISCV diff --git > > > a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index > > > 4bb856e4df55..8dc2f0ea2d0f 100644 > > > --- a/drivers/clocksource/Makefile > > > +++ b/drivers/clocksource/Makefile > > > @@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += > ingenic-timer.o > > > obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o > > > obj-$(CONFIG_X86_NUMACHIP) += numachip.o > > > obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o > > > +obj-$(CONFIG_STARFIVE_JH7110_TIMER) += timer-jh7110.o > > > obj-$(CONFIG_CLINT_TIMER) += timer-clint.o > > > obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o > > > obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o > > > diff --git a/drivers/clocksource/timer-jh7110.c > > > b/drivers/clocksource/timer-jh7110.c > > > new file mode 100644 > > > index 000000000000..dc770507f209 > > > --- /dev/null > > > +++ b/drivers/clocksource/timer-jh7110.c > > > @@ -0,0 +1,345 @@ > > > +// SPDX-License-Identifier: GPL-2.0 > > > +/* > > > + * Starfive JH7110 Timer driver > > > + * > > > + * Copyright (C) 2023 StarFive Technology Co., Ltd. > > > + * > > > + * This timer has four free-running and independent 32-bit counters > > > +and runs in 24MHz > > > + * clock on the StarFive JH7110 SoC. Each channel(counter) can > > > +trigger an interrupt > > > + * when timeout even CPU is sleeping. They support one-shot mode > > > +and > > > continuous-run mode. > > > + * > > > + * Each channel is used as a global timer that serves each cpu core: > > > + * JH7110 Timer Channel 0 -- CPU 0 > > > + * JH7110 Timer Channel 1 -- CPU 1 > > > + * JH7110 Timer Channel 2 -- CPU 2 > > > + * JH7110 Timer Channel 3 -- CPU 3 > > > + */ > > > + > > > +#include <linux/clk.h> > > > +#include <linux/clockchips.h> > > > +#include <linux/cpu.h> > > > +#include <linux/iopoll.h> > > > +#include <linux/irq.h> > > > +#include <linux/platform_device.h> > > > +#include <linux/reset.h> > > > + > > > +/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ > > > +#define JH7110_TIMER_CH_LEN 0x40 > > > +#define JH7110_TIMER_CH_BASE(x) ((x) * > JH7110_TIMER_CH_LEN) > > > +#define JH7110_TIMER_CH_MAX 4 > > > + > > > +#define JH7110_DELAY_US 0 > > > +#define JH7110_TIMEOUT_US 10000 > > > +#define JH7110_CLOCKEVENT_RATING 300 > > > +#define JH7110_TIMER_MAX_TICKS 0xffffffff > > > +#define JH7110_TIMER_MIN_TICKS 0xf > > > + > > > +#define JH7110_TIMER_INT_STATUS 0x00 /* RO[0:4]: Interrupt > Status > > > for channel0~4 */ > > > +#define JH7110_TIMER_CTL 0x04 /* RW[0]: 0-continuous run, > 1-single run > > > */ > > > +#define JH7110_TIMER_LOAD 0x08 /* RW: load value to counter > */ > > > +#define JH7110_TIMER_ENABLE 0x10 /* RW[0]: timer enable > register */ > > > +#define JH7110_TIMER_RELOAD 0x14 /* RW: write 1 or 0 both > reload > > > counter */ > > > +#define JH7110_TIMER_VALUE 0x18 /* RO: timer value register */ > > > +#define JH7110_TIMER_INT_CLR 0x20 /* RW: timer interrupt clear > > > register */ > > > +#define JH7110_TIMER_INT_MASK 0x24 /* RW[0]: timer > interrupt > > > mask register */ > > > + > > > +#define JH7110_TIMER_INT_CLR_ENA BIT(0) > > > +#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1) > > > + > > > +#define JH7110_PERCPU_GET_CLKEVT > > > (&jh7110_timer_info.clkevt[smp_processor_id()]) > > > + > > > +/** > > > + * struct jh7110_clkevt - Description of each timer channel > > > + * @clk: Clock of each timer channel > > > + * @rst: Reset of each timer channel > > > + * @base: Virtual address of each timer channel > > > + * @irq: Interrupt number of each timer channel > > > + * @timer_enabled: Enabled flag for each timer channel > > > + * @name: Name of each timer channel > > > + */ > > > +struct jh7110_clkevt { > > > + struct clk *clk; > > > + struct reset_control *rst; > > > + void __iomem *base; > > > + int irq; > > > + bool timer_enabled; > > > + char name[sizeof("jh7110-timer.chX")]; > > > +}; > > > + > > > +struct jh7110_timer_priv { > > > + struct clk *pclk; > > > + struct reset_control *prst; > > > + struct device *dev; > > > + struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX]; > > > +}; > > > + > > > +static struct jh7110_timer_priv jh7110_timer_info; > > > + > > > +/* 0:continuous-run mode, 1:single-run mode */ enum jh7110_timer_mode > { > > > + JH7110_TIMER_MODE_CONTIN, > > > + JH7110_TIMER_MODE_SINGLE, > > > +}; > > > + > > > +/* Interrupt Mask, 0:Unmask, 1:Mask */ enum jh7110_timer_int_mask { > > > + JH7110_TIMER_INT_ENA, > > > + JH7110_TIMER_INT_DIS, > > > +}; > > > + > > > +enum jh7110_timer_enable { > > > + JH7110_TIMER_DIS, > > > + JH7110_TIMER_ENA, > > > +}; > > > + > > > +/* > > > + * BIT(0): Read value represent channel int status. > > > + * Write 1 to this bit to clear interrupt. Write 0 has no effects. > > > + * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. > > > + */ > > > +static inline int jh7110_timer_int_clear(struct jh7110_clkevt > > > +*clkevt) { > > > + u32 value; > > > + int ret; > > > + > > > + /* Waiting interrupt can be cleared */ > > > + ret = readl_poll_timeout_atomic(clkevt->base + > > > +JH7110_TIMER_INT_CLR, > > > value, > > > + !(value & JH7110_TIMER_INT_CLR_AVA_MASK), > > > + JH7110_DELAY_US, JH7110_TIMEOUT_US); > > > + if (!ret) > > > + writel(JH7110_TIMER_INT_CLR_ENA, clkevt->base + > > > +JH7110_TIMER_INT_CLR); > > > + > > > + return ret; > > > +} > > > + > > > +static int jh7110_timer_start(struct jh7110_clkevt *clkevt) { > > > + int ret; > > > + > > > + /* Disable and clear interrupt first */ > > > + writel(JH7110_TIMER_INT_DIS, clkevt->base + > > > JH7110_TIMER_INT_MASK); > > > + ret = jh7110_timer_int_clear(clkevt); > > > + > > > + writel(JH7110_TIMER_INT_ENA, clkevt->base + > > > JH7110_TIMER_INT_MASK); > > > + writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE); > > > + > > > + return ret; > > > +} > > > + > > > +static int jh7110_timer_shutdown(struct clock_event_device *evt) { > > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > > + > > > + writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); > > > + return jh7110_timer_int_clear(clkevt); } > > > + > > > +/* IRQ handler for the timer */ > > > +static irqreturn_t jh7110_timer_interrupt(int irq, void *data) { > > > + struct clock_event_device *evt = (struct clock_event_device *)data; > > > + struct jh7110_clkevt *clkevt = &jh7110_timer_info.clkevt[0]; > > > + u32 reg = readl(clkevt->base + JH7110_TIMER_INT_STATUS); > > > + u8 cpu_id = smp_processor_id(); > > > + > > > + /* Check interrupt status and channel(cpu) ID */ > > > + if (!(reg & BIT(cpu_id))) > > > + return IRQ_NONE; > > > + > > > + clkevt = &jh7110_timer_info.clkevt[cpu_id]; > > > + writel(JH7110_TIMER_INT_CLR_ENA, (clkevt->base + > > > +JH7110_TIMER_INT_CLR)); > > > + > > > + if (evt->event_handler) > > > + evt->event_handler(evt); > > > + > > > + return IRQ_HANDLED; > > > +} > > > + > > > +static int jh7110_timer_set_periodic(struct clock_event_device *evt) { > > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > > + > > > + writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + > > > JH7110_TIMER_CTL); > > > + return 0; > > > +} > > > + > > > +static int jh7110_timer_set_oneshot(struct clock_event_device *evt) { > > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > > + > > > + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + > > > JH7110_TIMER_CTL); > > > + return 0; > > > +} > > > + > > > +static int jh7110_timer_set_next_event(unsigned long next, > > > + struct clock_event_device *evt) { > > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > > + > > > + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + > > > JH7110_TIMER_CTL); > > > + writel(next, clkevt->base + JH7110_TIMER_LOAD); > > > + > > > + return jh7110_timer_start(clkevt); } > > > + > > > +static DEFINE_PER_CPU(struct clock_event_device, jh7110_clock_event) = > { > > > + .features = CLOCK_EVT_FEAT_PERIODIC | > > > + CLOCK_EVT_FEAT_ONESHOT, > > > + .rating = JH7110_CLOCKEVENT_RATING, > > > + .set_state_shutdown = jh7110_timer_shutdown, > > > + .set_state_periodic = jh7110_timer_set_periodic, > > > + .set_state_oneshot = jh7110_timer_set_oneshot, > > > + .set_state_oneshot_stopped = jh7110_timer_shutdown, > > > + .set_next_event = jh7110_timer_set_next_event, > > > +}; > > > + > > > +static int jh7110_timer_dying_cpu(unsigned int cpu) { > > > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > > > + > > > + if (!priv->clkevt[cpu].timer_enabled) > > > + return 0; > > > + > > > + writel(JH7110_TIMER_DIS, priv->clkevt[cpu].base + > > > JH7110_TIMER_ENABLE); > > > + jh7110_timer_int_clear(&priv->clkevt[cpu]); > > > + reset_control_assert(priv->clkevt[cpu].rst); > > > + clk_disable_unprepare(priv->clkevt[cpu].clk); > > > + > > > + return 0; > > > +} > > > + > > > +static int jh7110_timer_starting_cpu(unsigned int cpu) { > > > + struct clock_event_device *evt = per_cpu_ptr(&jh7110_clock_event, > cpu); > > > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > > > + int err; > > > + u32 rate; > > > + > > > + if (cpu >= JH7110_TIMER_CH_MAX) > > > + return -ENOMEM; > > > + > > > + err = clk_prepare_enable(priv->clkevt[cpu].clk); > > > + if (err) > > > + goto err_starting_cpu; > > > + > > > + err = reset_control_deassert(priv->clkevt[cpu].rst); > > > + if (err) > > > + goto err_soft_reset; > > > + > > > + rate = clk_get_rate(priv->clkevt[cpu].clk); > > > + evt->cpumask = cpumask_of(cpu); > > > + evt->irq = priv->clkevt[cpu].irq; > > > + > > > + err = irq_force_affinity(evt->irq, cpumask_of(cpu)); > > > + if (err) > > > + goto err_affinity; > > > + > > > + clockevents_config_and_register(evt, rate, > JH7110_TIMER_MIN_TICKS, > > > + JH7110_TIMER_MAX_TICKS); > > > + > > > + /* Use one-shot mode */ > > > + writel(JH7110_TIMER_MODE_SINGLE, (priv->clkevt[cpu].base + > > > +JH7110_TIMER_CTL)); > > > + > > > + priv->clkevt[cpu].timer_enabled = true; > > > + > > > + err = jh7110_timer_start(&priv->clkevt[cpu]); > > > + if (err) > > > + goto err_affinity; > > > + return 0; > > > + > > > +err_affinity: > > > + reset_control_assert(priv->clkevt[cpu].rst); > > > +err_soft_reset: > > > + clk_disable_unprepare(priv->clkevt[cpu].clk); > > > +err_starting_cpu: > > > + free_irq(evt->irq, evt); > > > + return err; > > > +} > > > + > > > +static int jh7110_timer_probe(struct platform_device *pdev) { > > > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > > > + struct clock_event_device *evt; > > > + struct jh7110_clkevt *clkevt; > > > + char name[sizeof("chX")]; > > > + int ch; > > > + int ret; > > > + void __iomem *base; > > > + > > > + base = devm_platform_ioremap_resource(pdev, 0); > > > + if (IS_ERR(base)) > > > + return dev_err_probe(&pdev->dev, PTR_ERR(base), > > > + "failed to map registers\n"); > > > + > > > + priv->prst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); > > > + if (IS_ERR(priv->prst)) > > > + return dev_err_probe(&pdev->dev, PTR_ERR(priv->prst), > > > + "failed to get apb reset\n"); > > > + > > > + priv->pclk = devm_clk_get_enabled(&pdev->dev, "apb"); > > > + if (IS_ERR(priv->pclk)) > > > + return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), > > > + "failed to get & enable apb clock\n"); > > > + > > > + ret = reset_control_deassert(priv->prst); > > > + if (ret) > > > + return dev_err_probe(&pdev->dev, ret, "failed to deassert apb > > > +reset\n"); > > > + > > > + for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) { > > > + evt = per_cpu_ptr(&jh7110_clock_event, ch); > > > + clkevt = &priv->clkevt[ch]; > > > + snprintf(name, sizeof(name), "ch%d", ch); > > > + > > > + clkevt->base = base + JH7110_TIMER_CH_BASE(ch); > > > + /* Ensure timer is disabled */ > > > + writel(JH7110_TIMER_DIS, clkevt->base + > JH7110_TIMER_ENABLE); > > > + ret = jh7110_timer_int_clear(clkevt); > > > + if (ret) > > > + return ret; > > > + > > > + clkevt->rst = devm_reset_control_get_exclusive(&pdev->dev, > name); > > > + if (IS_ERR(clkevt->rst)) > > > + return PTR_ERR(clkevt->rst); > > > + > > > + clkevt->clk = devm_clk_get(&pdev->dev, name); > > > + if (IS_ERR(clkevt->clk)) > > > + return PTR_ERR(clkevt->clk); > > > + > > > + clkevt->irq = platform_get_irq(pdev, ch); > > > + if (clkevt->irq < 0) > > > + return clkevt->irq; > > > + > > > + snprintf(clkevt->name, sizeof(clkevt->name), > "jh7110-timer.ch%d", > > > ch); > > > + ret = devm_request_irq(&pdev->dev, clkevt->irq, > > > jh7110_timer_interrupt, > > > + IRQF_TIMER | IRQF_IRQPOLL, > > > + clkevt->name, evt); > > > + > > > + if (ret) > > > + return ret; > > > + > > > + clkevt->timer_enabled = false; > > > + } > > > + > > > + return cpuhp_setup_state(CPUHP_AP_JH7110_TIMER_STARTING, > > > + "clockevents/jh7110/timer:starting", > > > + jh7110_timer_starting_cpu, > jh7110_timer_dying_cpu); } > > > + > > > +static const struct of_device_id jh7110_timer_match[] = { > > > + { .compatible = "starfive,jh7110-timer", }, > > > + { /* sentinel */ } > > > +}; > > > +MODULE_DEVICE_TABLE(of, jh7110_timer_match); > > > + > > > +static struct platform_driver jh7110_timer_driver = { > > > + .probe = jh7110_timer_probe, > > > + .driver = { > > > + .name = "jh7110-timer", > > > + .of_match_table = jh7110_timer_match, > > > + }, > > > +}; > > > +module_platform_driver(jh7110_timer_driver); > > > + > > > +MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>"); > > > +MODULE_DESCRIPTION("StarFive JH7110 timer driver"); > > > +MODULE_LICENSE("GPL"); > > > diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h > > > index 35e78ddb2b37..4a8b487c327e 100644 > > > --- a/include/linux/cpuhotplug.h > > > +++ b/include/linux/cpuhotplug.h > > > @@ -175,6 +175,7 @@ enum cpuhp_state { > > > CPUHP_AP_CSKY_TIMER_STARTING, > > > CPUHP_AP_TI_GP_TIMER_STARTING, > > > CPUHP_AP_HYPERV_TIMER_STARTING, > > > + CPUHP_AP_JH7110_TIMER_STARTING, > > > /* Must be the last timer callback */ > > > CPUHP_AP_DUMMY_TIMER_STARTING, > > > CPUHP_AP_ARM_XEN_STARTING, > > > -- > > > 2.17.1 > > > > Hi Daniel / Thomas > > > > I have submitted new version of patch for jh7110 timer driver. Could you please > help to review and give your comments? > > Thanks a lot! > > Hi Ziv > > I tried this on 6.9-rc6 on my VF2. It boots, but very slowly and "choppy". That is > it repeatedly runs for <1s and then hangs for about 4s. > > Does this patch work for you? > > /Emil Hi, Emil I tried this on 6.9-rc7 and 6.9-rc7, but it doesn't reproduce the phenomenon you said. The attachment is the log for 6.9-rc6. Could you please share your config file (starfive_visionfive2_defconfig or .config) with me? Best Regards Ziv Xu StarFive # setenv bootfile vmlinuz; setenv fdt_addr_r 0x48000000; setenv fdt_high 0xffffffffffffffff; setenv fdtcontroladdr 0xffffffffffffffff; setenv initrd_high 0xffffffffffffffff; setenv kernel_addr_r 0x44000000; setenv fileaddr a0000000;setenv kernel_comp_addr_r 0xb0000000; setenv kernel_comp_size 0x10000000;setenv ipaddr 192.168.125.96; setenv serverip 192.168.125.171;setenv ramdisk_addr_r 0x48100000;setenv kernel_comp_addr_r 0xb0000000;setenv kernel_comp_size 0x10000000; StarFive # tftpboot ${fdt_addr_r} ziv/upstream/linux/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dtb; tftpboot ${kernel_addr_r} ziv/upstream/linux/arch/riscv/boot/Image.gz; tftpboot ${ramdisk_addr_r} ziv/visionfive/initramfs.cpio.gz;booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}; ethernet@16030000 Waiting for PHY auto negotiation to complete...... done Using ethernet@16030000 device TFTP from server 192.168.125.171; our IP address is 192.168.125.96 Filename 'ziv/upstream/linux/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dtb'. Load address: 0x48000000 Loading: ### 66.4 KiB/s done Bytes transferred = 36396 (8e2c hex) Using ethernet@16030000 device TFTP from server 192.168.125.171; our IP address is 192.168.125.96 Filename 'ziv/upstream/linux/arch/riscv/boot/Image.gz'. Load address: 0x44000000 Loading: ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ########## 5.9 MiB/s done Bytes transferred = 6819280 (680dd0 hex) Using ethernet@16030000 device TFTP from server 192.168.125.171; our IP address is 192.168.125.96 Filename 'ziv/visionfive/initramfs.cpio.gz'. Load address: 0x48100000 Loading: ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ############################################## 7.2 MiB/s done Bytes transferred = 114215585 (6cecaa1 hex) Uncompressing Kernel Image Moving Image from 0x44000000 to 0x40200000, end=41727000 ## Flattened Device Tree blob at 48000000 Booting using the fdt blob at 0x48000000 Using Device Tree in place at 0000000048000000, end 000000004800be2b Starting kernel ... clk u2_dw_i2c_clk_core already disabled clk u2_dw_i2c_clk_apb already disabled clk u5_dw_i2c_clk_core already disabled clk u5_dw_i2c_clk_apb already disabled Linux version 6.9.0-rc6-00003-g9385b82135ff (ziv@SD-Server) (riscv64-linux-gnu-gcc (Ubuntu 8.4.0-1ubuntu1~18.04) 8.4.0, GNU ld (GNU Binutils for Ubuntu) 2.30) #17 SMP Tue May 7 16:29:45 CST 2024 Machine model: StarFive VisionFive 2 v1.3B SBI specification v1.0 detected SBI implementation ID=0x1 Version=0x10002 SBI TIME extension detected SBI IPI extension detected SBI RFENCE extension detected SBI SRST extension detected efi: UEFI not found. OF: reserved mem: 0x0000000040000000..0x000000004007ffff (512 KiB) nomap non-reusable opensbi@40000000 Zone ranges: DMA32 [mem 0x0000000040000000-0x00000000ffffffff] Normal [mem 0x0000000100000000-0x000000013fffffff] Movable zone start for each node Early memory node ranges node 0: [mem 0x0000000040000000-0x000000004007ffff] node 0: [mem 0x0000000040080000-0x000000013fffffff] Initmem setup node 0 [mem 0x0000000040000000-0x000000013fffffff] SBI HSM extension detected CPU with hartid=0 is not available riscv: base ISA extensions acdfim riscv: ELF capabilities acdfim percpu: Embedded 20 pages/cpu s42664 r8192 d31064 u81920 pcpu-alloc: s42664 r8192 d31064 u81920 alloc=20*4096 pcpu-alloc: [0] 0 [0] 1 [0] 2 [0] 3 Kernel command line: console=tty1 console=ttyS0,115200 debug rootwait earlycon=sbi Dentry cache hash table entries: 524288 (order: 10, 4194304 bytes, linear) Inode-cache hash table entries: 262144 (order: 9, 2097152 bytes, linear) Built 1 zonelists, mobility grouping on. Total pages: 1034240 mem auto-init: stack:off, heap alloc:off, heap free:off software IO TLB: area num 4. software IO TLB: mapped [mem 0x00000000fbfff000-0x00000000fffff000] (64MB) Memory: 3929352K/4194304K available (8442K kernel code, 4781K rwdata, 4096K rodata, 2199K init, 339K bss, 264952K reserved, 0K cma-reserved) SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1 rcu: Hierarchical RCU implementation. rcu: RCU restricting CPUs from NR_CPUS=64 to nr_cpu_ids=4. rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies. rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=4 NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0 riscv-intc: 64 local interrupts mapped riscv: providing IPIs using SBI IPI extension rcu: srcu_init: Setting srcu_struct sizes based on contention. clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x1d854df40, max_idle_ns: 881590404240 ns sched_clock: 64 bits at 4MHz, resolution 250ns, wraps every 2199023255500ns Calibrating delay loop (skipped), value calculated using timer frequency.. 8.00 BogoMIPS (lpj=16000) pid_max: default: 32768 minimum: 301 Mount-cache hash table entries: 8192 (order: 4, 65536 bytes, linear) Mountpoint-cache hash table entries: 8192 (order: 4, 65536 bytes, linear) CPU node for /cpus/cpu@0 exist but the possible cpu range is :0-3 ASID allocator disabled (0 bits) rcu: Hierarchical SRCU implementation. rcu: Max phase no-delay instances is 1000. EFI services will not be available. smp: Bringing up secondary CPUs ... smp: Brought up 1 node, 4 CPUs devtmpfs: initialized clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns futex hash table entries: 1024 (order: 4, 65536 bytes, linear) pinctrl core: initialized pinctrl subsystem NET: Registered PF_NETLINK/PF_ROUTE protocol family DMA: preallocated 512 KiB GFP_KERNEL pool for atomic allocations DMA: preallocated 512 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations thermal_sys: Registered thermal governor 'step_wise' cpuidle: using governor menu cpu3: Ratio of byte access time to unaligned word access is 0.01, unaligned accesses are slow cpu1: Ratio of byte access time to unaligned word access is 0.01, unaligned accesses are slow cpu2: Ratio of byte access time to unaligned word access is 0.01, unaligned accesses are slow cpu0: Ratio of byte access time to unaligned word access is 0.01, unaligned accesses are slow CCACHE: 8 banks, 16 ways, sets/bank=256, bytes/block=64 CCACHE: Index of the largest way enabled: 15 platform soc: Fixed dependency cycle(s) with /soc/interrupt-controller@c000000 platform soc: Fixed dependency cycle(s) with /soc/interrupt-controller@c000000 platform 19800000.csi: Fixed dependency cycle(s) with /soc/isp@19840000 platform 19800000.csi: Fixed dependency cycle(s) with /soc/isp@19840000 platform 19840000.isp: Fixed dependency cycle(s) with /soc/csi@19800000 raid6: skipped pq benchmark and selected int64x8 raid6: using intx1 recovery algorithm SCSI subsystem initialized usbcore: registered new interface driver usbfs usbcore: registered new interface driver hub usbcore: registered new device driver usb mc: Linux media interface: v0.10 videodev: Linux video capture interface: v2.00 Advanced Linux Sound Architecture Driver Initialized. vgaarb: loaded clocksource: Switched to clocksource riscv_clocksource NET: Registered PF_INET protocol family IP idents hash table entries: 65536 (order: 7, 524288 bytes, linear) tcp_listen_portaddr_hash hash table entries: 2048 (order: 3, 32768 bytes, linear) Table-perturb hash table entries: 65536 (order: 6, 262144 bytes, linear) TCP established hash table entries: 32768 (order: 6, 262144 bytes, linear) TCP bind hash table entries: 32768 (order: 8, 1048576 bytes, linear) TCP: Hash tables configured (established 32768 bind 32768) UDP hash table entries: 2048 (order: 4, 65536 bytes, linear) UDP-Lite hash table entries: 2048 (order: 4, 65536 bytes, linear) NET: Registered PF_UNIX/PF_LOCAL protocol family RPC: Registered named UNIX socket transport module. RPC: Registered udp transport module. RPC: Registered tcp transport module. RPC: Registered tcp-with-tls transport module. RPC: Registered tcp NFSv4.1 backchannel transport module. PCI: CLS 0 bytes, default 64 Unpacking initramfs... workingset: timestamp_bits=62 max_order=20 bucket_order=0 NFS: Registering the id_resolver key type Key type id_resolver registered Key type id_legacy registered nfs4filelayout_init: NFSv4 File Layout Driver Registering... nfs4flexfilelayout_init: NFSv4 Flexfile Layout Driver Registering... ntfs3: Max link count 4000 jffs2: version 2.2. (NAND) © 2001-2006 Red Hat, Inc. xor: measuring software checksum speed 8regs : 2340 MB/sec 8regs_prefetch : 2336 MB/sec 32regs : 2340 MB/sec 32regs_prefetch : 2333 MB/sec xor: using function: 32regs (2340 MB/sec) Block layer SCSI generic (bsg) driver version 0.4 loaded (major 251) io scheduler bfq registered riscv-plic c000000.interrupt-controller: mapped 136 interrupts with 4 handlers for 9 contexts. Serial: 8250/16550 driver, 6 ports, IRQ sharing disabled random: crng init done loop: module loaded zram: Added device: zram0 Freeing initrd memory: 111536K 4 fixed-partitions partitions found on MTD device 13010000.spi.0 Creating 4 MTD partitions on "13010000.spi.0": 0x000000000000-0x000000080000 : "spl" 0x0000000f0000-0x000000100000 : "uboot-env" 0x000000100000-0x000000500000 : "uboot" 0x000000600000-0x000001000000 : "reserved-data" starfive-dwmac 16030000.ethernet: IRQ sfty not found starfive-dwmac 16030000.ethernet: User ID: 0x41, Synopsys ID: 0x52 starfive-dwmac 16030000.ethernet: DWMAC4/5 starfive-dwmac 16030000.ethernet: DMA HW capability register supported starfive-dwmac 16030000.ethernet: RX Checksum Offload Engine supported starfive-dwmac 16030000.ethernet: Wake-Up On Lan supported starfive-dwmac 16030000.ethernet: TSO supported starfive-dwmac 16030000.ethernet: Enable RX Mitigation via HW Watchdog Timer starfive-dwmac 16030000.ethernet: Enabled L3L4 Flow TC (entries=1) starfive-dwmac 16030000.ethernet: Enabled RFS Flow TC (entries=10) starfive-dwmac 16030000.ethernet: TSO feature enabled starfive-dwmac 16030000.ethernet: Using 40/40 bits DMA host/device width starfive-dwmac 16040000.ethernet: IRQ sfty not found starfive-dwmac 16040000.ethernet: User ID: 0x41, Synopsys ID: 0x52 starfive-dwmac 16040000.ethernet: DWMAC4/5 starfive-dwmac 16040000.ethernet: DMA HW capability register supported starfive-dwmac 16040000.ethernet: RX Checksum Offload Engine supported starfive-dwmac 16040000.ethernet: Wake-Up On Lan supported starfive-dwmac 16040000.ethernet: TSO supported starfive-dwmac 16040000.ethernet: Enable RX Mitigation via HW Watchdog Timer starfive-dwmac 16040000.ethernet: Enabled L3L4 Flow TC (entries=1) starfive-dwmac 16040000.ethernet: Enabled RFS Flow TC (entries=10) starfive-dwmac 16040000.ethernet: TSO feature enabled starfive-dwmac 16040000.ethernet: Using 40/40 bits DMA host/device width usbcore: registered new interface driver uas usbcore: registered new interface driver usb-storage i2c_dev: i2c /dev entries driver sdhci: Secure Digital Host Controller Interface driver sdhci: Copyright(c) Pierre Ossman Synopsys Designware Multimedia Card Interface Driver sdhci-pltfm: SDHCI platform and OF driver helper usbcore: registered new interface driver usbhid usbhid: USB HID core driver riscv-pmu-sbi: SBI PMU extension is available riscv-pmu-sbi: 16 firmware and 4 hardware counters riscv-pmu-sbi: Perf sampling/filtering is not supported as sscof extension is not available NET: Registered PF_INET6 protocol family Segment Routing with IPv6 In-situ OAM (IOAM) with IPv6 NET: Registered PF_PACKET protocol family Key type dns_resolver registered Timer migration: 1 hierarchy levels; 8 children per group; 1 crossnode level Btrfs loaded, zoned=no, fsverity=no CCACHE: DataError @ 0x00000000.08040270 CCACHE: DataFail @ 0x00000000.08040074 pl08xdmac 16008000.dma-controller: initialized 8 virtual memcpy channels pl08xdmac 16008000.dma-controller: initialized 16 virtual slave channels debugfs: Directory '16008000.dma-controller' with parent 'dmaengine' already present! pl08xdmac 16008000.dma-controller: DMA: PL080 rev0 at 0x16008000 irq 30 gpio gpiochip0: Static allocation of GPIO base is deprecated, use dynamic allocation. starfive-jh7110-sys-pinctrl 13040000.pinctrl: StarFive GPIO chip registered 64 GPIOs gpio gpiochip1: Static allocation of GPIO base is deprecated, use dynamic allocation. starfive-jh7110-aon-pinctrl 17020000.pinctrl: StarFive GPIO chip registered 4 GPIOs dw_axi_dmac_platform 16050000.dma-controller: DesignWare AXI DMA Controller, 4 channels printk: legacy console [ttyS0] disabled 10000000.serial: ttyS0 at MMIO 0x10000000 (irq = 33, base_baud = 1500000) is a 16550A printk: legacy console [ttyS0] enabled dwmmc_starfive 16020000.mmc: IDMAC supports 32-bit address mode. designware-i2s 100e0000.i2s: probe with driver designware-i2s failed with error -110 dwmmc_starfive 16020000.mmc: Using internal DMA controller. dwmmc_starfive 16020000.mmc: Version ID is 290a dwmmc_starfive 16020000.mmc: DW MMC controller at irq 40,32 bit host data width,32 deep fifo mmc_host mmc1: card is polling. mmc_host mmc1: Bus speed (slot 0) = 49500000Hz (slot req 400000Hz, actual 399193HZ div = 62) designware-i2s 120c0000.i2s: probe with driver designware-i2s failed with error -110 ssp-pl022 10060000.spi: ARM PL022 driver, device ID: 0x00041022 ssp-pl022 10060000.spi: mapped registers from 0x0000000010060000 to 00000000c0024811 axp20x-i2c 5-0036: AXP20x variant AXP15060 found axp20x-i2c 5-0036: AXP20X driver loaded starfive-dphy-rx 19820000.phy: supply mipi_0p9 not found, using dummy regulator cdns-csi2rx 19800000.csi: probe with driver cdns-csi2rx failed with error -22 cpufreq: cpufreq_online: CPU0: Running at unlisted initial frequency: 1000000 KHz, changing to: 1500000 KHz clk: Disabling unused clocks PM: genpd: Disabling unused power domains ALSA device list: #0: StarFive-PWMDAC-Sound-Card dwmmc_starfive 16010000.mmc: IDMAC supports 32-bit address mode. dwmmc_starfive 16010000.mmc: Using internal DMA controller. dwmmc_starfive 16010000.mmc: Version ID is 290a dwmmc_starfive 16010000.mmc: DW MMC controller at irq 62,32 bit host data width,32 deep fifo mmc_host mmc0: card is non-removable. mmc_host mmc1: Bus speed (slot 0) = 49500000Hz (slot req 300000Hz, actual 298192HZ div = 83) mmc_host mmc1: Bus speed (slot 0) = 49500000Hz (slot req 200000Hz, actual 199596HZ div = 124) mmc_host mmc1: Bus speed (slot 0) = 49500000Hz (slot req 100000Hz, actual 99798HZ div = 248) mmc_host mmc0: Bus speed (slot 0) = 49500000Hz (slot req 400000Hz, actual 399193HZ div = 62) Freeing unused kernel image (initmem) memory: 2196K Checked W+X mappings: passed, no W+X pages found rodata_test: all tests were successful Run /init as init process with arguments: /init with environment: HOME=/ TERM=linux Starting syslogd: OK Starting klogd: OK Running sysctl: OK Populating /dev using udev: udevd[125]: starting version 3.2.10 udevd[126]: starting eudev-3.2.10 mmc_host mmc0: Bus speed (slot 0) = 49500000Hz (slot req 52000000Hz, actual 49500000HZ div = 0) mmc_host mmc0: Bus speed (slot 0) = 49500000Hz (slot req 100000000Hz, actual 49500000HZ div = 0) mmc0: new HS200 MMC card at address 0001 mmcblk0: mmc0:0001 DG4032 29.1 GiB GPT:Primary header thinks Alt. header is not at the end of the disk. GPT:8191999 != 61071359 GPT:Alternate GPT header not at the end of the disk. GPT:8191999 != 61071359 GPT: Use GNU Parted to correct GPT errors. mmcblk0: p1 p2 p3 p4 mmcblk0boot0: mmc0:0001 DG4032 4.00 MiB mmcblk0boot1: mmc0:0001 DG4032 4.00 MiB mmcblk0rpmb: mmc0:0001 DG4032 4.00 MiB, chardev (248:0) done Saving random seed: OK Starting rngd: OK Starting system message bus: dbus[171]: Unknown username "pulse" in message bus configuration file done Starting rpcbind: OK Starting iptables: OK Starting bluetoothd: OK Starting network: OK Starting Network Interface Plugging Daemon:starfive-dwmac 16030000.ethernet eth0: Register MEM_TYPE_PAGE_POOL RxQ-0 starfive-dwmac 16030000.ethernet eth0: PHY [stmmac-0:00] driver [YT8531 Gigabit Ethernet] (irq=POLL) dwmac4: Master AXI performs fixed burst length starfive-dwmac 16030000.ethernet eth0: No Safety Features support found starfive-dwmac 16030000.ethernet eth0: IEEE 1588-2008 Advanced Timestamp supported eth0starfive-dwmac 16030000.ethernet eth0: configuring for phy/rgmii-id link mode eth1. Starting ofono ... done. Starting ntpd: starfive-dwmac 16040000.ethernet eth1: Register MEM_TYPE_PAGE_POOL RxQ-0 starfive-dwmac 16040000.ethernet eth1: PHY [stmmac-1:00] driver [YT8531 Gigabit Ethernet] (irq=POLL) starfive-dwmac 16040000.ethernet: Failed to reset the dma starfive-dwmac 16040000.ethernet eth1: stmmac_hw_setup: DMA engine initialization failed starfive-dwmac 16040000.ethernet eth1: __stmmac_open: Hw setup failed starfive-dwmac 16040000.ethernet eth1: Register MEM_TYPE_PAGE_POOL RxQ-0 starfive-dwmac 16040000.ethernet eth1: PHY [stmmac-1:00] driver [YT8531 Gigabit Ethernet] (irq=POLL) dwmac4: Master AXI performs fixed burst length starfive-dwmac 16040000.ethernet eth1: No Safety Features support found starfive-dwmac 16040000.ethernet eth1: IEEE 1588-2008 Advanced Timestamp supported starfive-dwmac 16040000.ethernet eth1: configuring for phy/rgmii-id link mode OK Starting dropbear sshd: OK Starting multipastarfive-dwmac 16030000.ethernet eth0: Link is Up - 1Gbps/Full - flow control rx/tx thd: OK Starting NFS statd: OK Starting NFS services: OK Starting NFS daemon: rpc.nfsd: Unable to access /proc/fs/nfsd errno 2 (No such file or directory). Please try, as root, 'mount -t nfsd nfsd /proc/fs/nfsd' and then restart rpc.nfsd to correct the problem FAIL Starting NFS mountd: OK Starting isp_ctrl_daemon.sh: OK Starting DHCP server: FAIL No PARTLABEL=hibernation! Welcome to Buildroot buildroot login: Welcome to Buildroot buildroot login: root Password: # cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 10: 1049 87 28 619 RISC-V INTC 5 Edge riscv-timer 12: 0 0 0 0 SiFive PLIC 73 Edge dw_axi_dmac_platform 13: 3 0 0 0 SiFive PLIC 111 Edge 17030000.power-controller 14: 90 0 0 0 SiFive PLIC 30 Edge 1600c000.rng 15: 6 0 0 0 SiFive PLIC 25 Edge 13010000.spi 16: 17 0 0 0 SiFive PLIC 7 Edge eth0 17: 0 0 0 0 SiFive PLIC 6 Edge eth0 18: 0 0 0 0 SiFive PLIC 5 Edge eth0 19: 0 0 0 0 SiFive PLIC 78 Edge eth1 20: 0 0 0 0 SiFive PLIC 77 Edge eth1 21: 0 0 0 0 SiFive PLIC 76 Edge eth1 22: 4664 0 0 0 SiFive PLIC 69 Edge jh7110-timer.ch0 23: 0 5602 0 0 SiFive PLIC 70 Edge jh7110-timer.ch1 24: 0 0 5717 0 SiFive PLIC 71 Edge jh7110-timer.ch2 25: 0 0 0 5385 SiFive PLIC 72 Edge jh7110-timer.ch3 26: 0 0 0 0 SiFive PLIC 1 Edge ccache_ecc 27: 1 0 0 0 SiFive PLIC 3 Edge ccache_ecc 28: 1 0 0 0 SiFive PLIC 4 Edge ccache_ecc 29: 0 0 0 0 SiFive PLIC 2 Edge ccache_ecc 30: 0 0 0 0 SiFive PLIC 29 Edge pl08xdmac 33: 164 0 0 0 SiFive PLIC 32 Edge ttyS0 35: 0 0 0 0 SiFive PLIC 108 Edge 10100000.usb 36: 0 0 0 0 SiFive PLIC 110 Edge 10100000.usb 37: 0 0 0 0 SiFive PLIC 92 Edge wr_irq 38: 0 0 0 0 SiFive PLIC 87 Edge isp_irq 39: 0 0 0 0 SiFive PLIC 90 Edge line_irq 40: 383 0 0 0 SiFive PLIC 75 Edge dw-mci 41: 0 0 0 0 SiFive PLIC 38 Edge pl022 42: 0 0 0 0 SiFive PLIC 35 Edge 10030000.i2c 43: 1 0 0 0 SiFive PLIC 37 Edge 10050000.i2c 44: 90 0 0 0 SiFive PLIC 50 Edge 12050000.i2c 45: 0 0 0 0 SiFive PLIC 0 Edge axp15060 61: 0 0 0 0 SiFive PLIC 51 Edge 12060000.i2c 62: 1009 0 0 0 SiFive PLIC 74 Edge dw-mci IPI0: 49 59 73 82 Rescheduling interrupts IPI1: 623 743 810 952 Function call interrupts IPI2: 0 0 0 0 CPU stop interrupts IPI3: 0 0 0 0 CPU stop (for crash dump) interrupts IPI4: 39 61 40 27 IRQ work interrupts IPI5: 0 0 0 0 Timer broadcast interrupts #
Ziv Xu wrote: > > > > -----邮件原件----- > > 发件人: Emil Renner Berthing <emil.renner.berthing@canonical.com> > > 发送时间: 2024年4月30日 18:19 > > 收件人: Ziv Xu <ziv.xu@starfivetech.com>; Daniel Lezcano > > <daniel.lezcano@linaro.org>; Thomas Gleixner <tglx@linutronix.de> > > 抄送: linux-riscv@lists.infradead.org; devicetree@vger.kernel.org; Rob Herring > > <robh+dt@kernel.org>; Krzysztof Kozlowski > > <krzysztof.kozlowski+dt@linaro.org>; Paul Walmsley > > <paul.walmsley@sifive.com>; Palmer Dabbelt <palmer@dabbelt.com>; Albert > > Ou <aou@eecs.berkeley.edu>; Philipp Zabel <p.zabel@pengutronix.de>; Walker > > Chen <walker.chen@starfivetech.com>; Xingyu Wu > > <xingyu.wu@starfivetech.com>; linux-kernel@vger.kernel.org; Conor Dooley > > <conor@kernel.org> > > 主题: Re: 回复: [PATCH v10 2/3] clocksource: Add JH7110 timer driver > > > > Ziv Xu wrote: > > > > > > > > > > -----邮件原件----- > > > > 发件人: Ziv Xu > > > > 发送时间: 2024年4月12日 16:46 > > > > 收件人: Daniel Lezcano <daniel.lezcano@linaro.org>; Thomas Gleixner > > > > <tglx@linutronix.de>; Emil Renner Berthing > > > > <emil.renner.berthing@canonical.com>; Christophe JAILLET > > > > <christophe.jaillet@wanadoo.fr> > > > > 抄送: linux-riscv@lists.infradead.org; devicetree@vger.kernel.org; Rob > > > > Herring <robh+dt@kernel.org>; Krzysztof Kozlowski > > > > <krzysztof.kozlowski+dt@linaro.org>; Paul Walmsley > > > > <paul.walmsley@sifive.com>; Palmer Dabbelt <palmer@dabbelt.com>; > > > > Albert Ou <aou@eecs.berkeley.edu>; Philipp Zabel > > > > <p.zabel@pengutronix.de>; Walker Chen > > > > <walker.chen@starfivetech.com>; Xingyu Wu > > > > <xingyu.wu@starfivetech.com>; linux-kernel@vger.kernel.org; Conor > > > > Dooley <conor@kernel.org> > > > > 主题: [PATCH v10 2/3] clocksource: Add JH7110 timer driver > > > > > > > > From: Xingyu Wu <xingyu.wu@starfivetech.com> > > > > > > > > Add timer driver for the StarFive JH7110 SoC. > > > > > > > > This timer has four free-running and independent 32-bit counters. > > > > Each channel(counter) can trigger an interrupt when timeout even CPU > > > > is sleeping. So this timer is used as global timer and register > > > > clockevent for each CPU core after riscv-timer registration on the StarFive > > JH7110 SoC. > > > > > > > > Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com> > > > > Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com> > > > > --- > > > > MAINTAINERS | 7 + > > > > drivers/clocksource/Kconfig | 11 + > > > > drivers/clocksource/Makefile | 1 + > > > > drivers/clocksource/timer-jh7110.c | 345 > > +++++++++++++++++++++++++++++ > > > > include/linux/cpuhotplug.h | 1 + > > > > 5 files changed, 365 insertions(+) > > > > create mode 100644 drivers/clocksource/timer-jh7110.c > > > > > > > > diff --git a/MAINTAINERS b/MAINTAINERS index > > > > 7c121493f43d..ef9b5f5bad9e 100644 > > > > --- a/MAINTAINERS > > > > +++ b/MAINTAINERS > > > > @@ -21043,6 +21043,13 @@ S: Maintained > > > > F: Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml > > > > F: sound/soc/starfive/jh7110_tdm.c > > > > > > > > +STARFIVE JH7110 TIMER DRIVER > > > > +M: Samin Guo <samin.guo@starfivetech.com> > > > > +M: Xingyu Wu <xingyu.wu@starfivetech.com> > > > > +S: Supported > > > > +F: Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml > > > > +F: drivers/clocksource/timer-jh7110.c > > > > + > > > > STARFIVE JH71X0 CLOCK DRIVERS > > > > M: Emil Renner Berthing <kernel@esmil.dk> > > > > M: Hal Feng <hal.feng@starfivetech.com> > > > > diff --git a/drivers/clocksource/Kconfig > > > > b/drivers/clocksource/Kconfig index > > > > 34faa0320ece..2dc97201dee1 100644 > > > > --- a/drivers/clocksource/Kconfig > > > > +++ b/drivers/clocksource/Kconfig > > > > @@ -641,6 +641,17 @@ config RISCV_TIMER > > > > is accessed via both the SBI and the rdcycle instruction. This is > > > > required for all RISC-V systems. > > > > > > > > +config STARFIVE_JH7110_TIMER > > > > + bool "Timer for the STARFIVE JH7110 SoC" > > > > + depends on ARCH_STARFIVE || COMPILE_TEST > > > > + select TIMER_OF > > > > + select CLKSRC_MMIO > > > > + default ARCH_STARFIVE > > > > + help > > > > + This enables the timer for StarFive JH7110 SoC. On RISC-V platform, > > > > + the system has started RISCV_TIMER, but you can also use this > > timer > > > > + which can provide four channels to do a lot more things on JH7110 > > SoC. > > > > + > > > > config CLINT_TIMER > > > > bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST > > > > depends on GENERIC_SCHED_CLOCK && RISCV diff --git > > > > a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index > > > > 4bb856e4df55..8dc2f0ea2d0f 100644 > > > > --- a/drivers/clocksource/Makefile > > > > +++ b/drivers/clocksource/Makefile > > > > @@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += > > ingenic-timer.o > > > > obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o > > > > obj-$(CONFIG_X86_NUMACHIP) += numachip.o > > > > obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o > > > > +obj-$(CONFIG_STARFIVE_JH7110_TIMER) += timer-jh7110.o > > > > obj-$(CONFIG_CLINT_TIMER) += timer-clint.o > > > > obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o > > > > obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o > > > > diff --git a/drivers/clocksource/timer-jh7110.c > > > > b/drivers/clocksource/timer-jh7110.c > > > > new file mode 100644 > > > > index 000000000000..dc770507f209 > > > > --- /dev/null > > > > +++ b/drivers/clocksource/timer-jh7110.c > > > > @@ -0,0 +1,345 @@ > > > > +// SPDX-License-Identifier: GPL-2.0 > > > > +/* > > > > + * Starfive JH7110 Timer driver > > > > + * > > > > + * Copyright (C) 2023 StarFive Technology Co., Ltd. > > > > + * > > > > + * This timer has four free-running and independent 32-bit counters > > > > +and runs in 24MHz > > > > + * clock on the StarFive JH7110 SoC. Each channel(counter) can > > > > +trigger an interrupt > > > > + * when timeout even CPU is sleeping. They support one-shot mode > > > > +and > > > > continuous-run mode. > > > > + * > > > > + * Each channel is used as a global timer that serves each cpu core: > > > > + * JH7110 Timer Channel 0 -- CPU 0 > > > > + * JH7110 Timer Channel 1 -- CPU 1 > > > > + * JH7110 Timer Channel 2 -- CPU 2 > > > > + * JH7110 Timer Channel 3 -- CPU 3 > > > > + */ > > > > + > > > > +#include <linux/clk.h> > > > > +#include <linux/clockchips.h> > > > > +#include <linux/cpu.h> > > > > +#include <linux/iopoll.h> > > > > +#include <linux/irq.h> > > > > +#include <linux/platform_device.h> > > > > +#include <linux/reset.h> > > > > + > > > > +/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ > > > > +#define JH7110_TIMER_CH_LEN 0x40 > > > > +#define JH7110_TIMER_CH_BASE(x) ((x) * > > JH7110_TIMER_CH_LEN) > > > > +#define JH7110_TIMER_CH_MAX 4 > > > > + > > > > +#define JH7110_DELAY_US 0 > > > > +#define JH7110_TIMEOUT_US 10000 > > > > +#define JH7110_CLOCKEVENT_RATING 300 > > > > +#define JH7110_TIMER_MAX_TICKS 0xffffffff > > > > +#define JH7110_TIMER_MIN_TICKS 0xf > > > > + > > > > +#define JH7110_TIMER_INT_STATUS 0x00 /* RO[0:4]: Interrupt > > Status > > > > for channel0~4 */ > > > > +#define JH7110_TIMER_CTL 0x04 /* RW[0]: 0-continuous run, > > 1-single run > > > > */ > > > > +#define JH7110_TIMER_LOAD 0x08 /* RW: load value to counter > > */ > > > > +#define JH7110_TIMER_ENABLE 0x10 /* RW[0]: timer enable > > register */ > > > > +#define JH7110_TIMER_RELOAD 0x14 /* RW: write 1 or 0 both > > reload > > > > counter */ > > > > +#define JH7110_TIMER_VALUE 0x18 /* RO: timer value register */ > > > > +#define JH7110_TIMER_INT_CLR 0x20 /* RW: timer interrupt clear > > > > register */ > > > > +#define JH7110_TIMER_INT_MASK 0x24 /* RW[0]: timer > > interrupt > > > > mask register */ > > > > + > > > > +#define JH7110_TIMER_INT_CLR_ENA BIT(0) > > > > +#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1) > > > > + > > > > +#define JH7110_PERCPU_GET_CLKEVT > > > > (&jh7110_timer_info.clkevt[smp_processor_id()]) > > > > + > > > > +/** > > > > + * struct jh7110_clkevt - Description of each timer channel > > > > + * @clk: Clock of each timer channel > > > > + * @rst: Reset of each timer channel > > > > + * @base: Virtual address of each timer channel > > > > + * @irq: Interrupt number of each timer channel > > > > + * @timer_enabled: Enabled flag for each timer channel > > > > + * @name: Name of each timer channel > > > > + */ > > > > +struct jh7110_clkevt { > > > > + struct clk *clk; > > > > + struct reset_control *rst; > > > > + void __iomem *base; > > > > + int irq; > > > > + bool timer_enabled; > > > > + char name[sizeof("jh7110-timer.chX")]; > > > > +}; > > > > + > > > > +struct jh7110_timer_priv { > > > > + struct clk *pclk; > > > > + struct reset_control *prst; > > > > + struct device *dev; > > > > + struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX]; > > > > +}; > > > > + > > > > +static struct jh7110_timer_priv jh7110_timer_info; > > > > + > > > > +/* 0:continuous-run mode, 1:single-run mode */ enum jh7110_timer_mode > > { > > > > + JH7110_TIMER_MODE_CONTIN, > > > > + JH7110_TIMER_MODE_SINGLE, > > > > +}; > > > > + > > > > +/* Interrupt Mask, 0:Unmask, 1:Mask */ enum jh7110_timer_int_mask { > > > > + JH7110_TIMER_INT_ENA, > > > > + JH7110_TIMER_INT_DIS, > > > > +}; > > > > + > > > > +enum jh7110_timer_enable { > > > > + JH7110_TIMER_DIS, > > > > + JH7110_TIMER_ENA, > > > > +}; > > > > + > > > > +/* > > > > + * BIT(0): Read value represent channel int status. > > > > + * Write 1 to this bit to clear interrupt. Write 0 has no effects. > > > > + * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. > > > > + */ > > > > +static inline int jh7110_timer_int_clear(struct jh7110_clkevt > > > > +*clkevt) { > > > > + u32 value; > > > > + int ret; > > > > + > > > > + /* Waiting interrupt can be cleared */ > > > > + ret = readl_poll_timeout_atomic(clkevt->base + > > > > +JH7110_TIMER_INT_CLR, > > > > value, > > > > + !(value & JH7110_TIMER_INT_CLR_AVA_MASK), > > > > + JH7110_DELAY_US, JH7110_TIMEOUT_US); > > > > + if (!ret) > > > > + writel(JH7110_TIMER_INT_CLR_ENA, clkevt->base + > > > > +JH7110_TIMER_INT_CLR); > > > > + > > > > + return ret; > > > > +} > > > > + > > > > +static int jh7110_timer_start(struct jh7110_clkevt *clkevt) { > > > > + int ret; > > > > + > > > > + /* Disable and clear interrupt first */ > > > > + writel(JH7110_TIMER_INT_DIS, clkevt->base + > > > > JH7110_TIMER_INT_MASK); > > > > + ret = jh7110_timer_int_clear(clkevt); > > > > + > > > > + writel(JH7110_TIMER_INT_ENA, clkevt->base + > > > > JH7110_TIMER_INT_MASK); > > > > + writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE); > > > > + > > > > + return ret; > > > > +} > > > > + > > > > +static int jh7110_timer_shutdown(struct clock_event_device *evt) { > > > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > > > + > > > > + writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); > > > > + return jh7110_timer_int_clear(clkevt); } > > > > + > > > > +/* IRQ handler for the timer */ > > > > +static irqreturn_t jh7110_timer_interrupt(int irq, void *data) { > > > > + struct clock_event_device *evt = (struct clock_event_device *)data; > > > > + struct jh7110_clkevt *clkevt = &jh7110_timer_info.clkevt[0]; > > > > + u32 reg = readl(clkevt->base + JH7110_TIMER_INT_STATUS); > > > > + u8 cpu_id = smp_processor_id(); > > > > + > > > > + /* Check interrupt status and channel(cpu) ID */ > > > > + if (!(reg & BIT(cpu_id))) > > > > + return IRQ_NONE; > > > > + > > > > + clkevt = &jh7110_timer_info.clkevt[cpu_id]; > > > > + writel(JH7110_TIMER_INT_CLR_ENA, (clkevt->base + > > > > +JH7110_TIMER_INT_CLR)); > > > > + > > > > + if (evt->event_handler) > > > > + evt->event_handler(evt); > > > > + > > > > + return IRQ_HANDLED; > > > > +} > > > > + > > > > +static int jh7110_timer_set_periodic(struct clock_event_device *evt) { > > > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > > > + > > > > + writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + > > > > JH7110_TIMER_CTL); > > > > + return 0; > > > > +} > > > > + > > > > +static int jh7110_timer_set_oneshot(struct clock_event_device *evt) { > > > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > > > + > > > > + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + > > > > JH7110_TIMER_CTL); > > > > + return 0; > > > > +} > > > > + > > > > +static int jh7110_timer_set_next_event(unsigned long next, > > > > + struct clock_event_device *evt) { > > > > + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; > > > > + > > > > + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + > > > > JH7110_TIMER_CTL); > > > > + writel(next, clkevt->base + JH7110_TIMER_LOAD); > > > > + > > > > + return jh7110_timer_start(clkevt); } > > > > + > > > > +static DEFINE_PER_CPU(struct clock_event_device, jh7110_clock_event) = > > { > > > > + .features = CLOCK_EVT_FEAT_PERIODIC | > > > > + CLOCK_EVT_FEAT_ONESHOT, > > > > + .rating = JH7110_CLOCKEVENT_RATING, > > > > + .set_state_shutdown = jh7110_timer_shutdown, > > > > + .set_state_periodic = jh7110_timer_set_periodic, > > > > + .set_state_oneshot = jh7110_timer_set_oneshot, > > > > + .set_state_oneshot_stopped = jh7110_timer_shutdown, > > > > + .set_next_event = jh7110_timer_set_next_event, > > > > +}; > > > > + > > > > +static int jh7110_timer_dying_cpu(unsigned int cpu) { > > > > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > > > > + > > > > + if (!priv->clkevt[cpu].timer_enabled) > > > > + return 0; > > > > + > > > > + writel(JH7110_TIMER_DIS, priv->clkevt[cpu].base + > > > > JH7110_TIMER_ENABLE); > > > > + jh7110_timer_int_clear(&priv->clkevt[cpu]); > > > > + reset_control_assert(priv->clkevt[cpu].rst); > > > > + clk_disable_unprepare(priv->clkevt[cpu].clk); > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +static int jh7110_timer_starting_cpu(unsigned int cpu) { > > > > + struct clock_event_device *evt = per_cpu_ptr(&jh7110_clock_event, > > cpu); > > > > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > > > > + int err; > > > > + u32 rate; > > > > + > > > > + if (cpu >= JH7110_TIMER_CH_MAX) > > > > + return -ENOMEM; > > > > + > > > > + err = clk_prepare_enable(priv->clkevt[cpu].clk); > > > > + if (err) > > > > + goto err_starting_cpu; > > > > + > > > > + err = reset_control_deassert(priv->clkevt[cpu].rst); > > > > + if (err) > > > > + goto err_soft_reset; > > > > + > > > > + rate = clk_get_rate(priv->clkevt[cpu].clk); > > > > + evt->cpumask = cpumask_of(cpu); > > > > + evt->irq = priv->clkevt[cpu].irq; > > > > + > > > > + err = irq_force_affinity(evt->irq, cpumask_of(cpu)); > > > > + if (err) > > > > + goto err_affinity; > > > > + > > > > + clockevents_config_and_register(evt, rate, > > JH7110_TIMER_MIN_TICKS, > > > > + JH7110_TIMER_MAX_TICKS); > > > > + > > > > + /* Use one-shot mode */ > > > > + writel(JH7110_TIMER_MODE_SINGLE, (priv->clkevt[cpu].base + > > > > +JH7110_TIMER_CTL)); > > > > + > > > > + priv->clkevt[cpu].timer_enabled = true; > > > > + > > > > + err = jh7110_timer_start(&priv->clkevt[cpu]); > > > > + if (err) > > > > + goto err_affinity; > > > > + return 0; > > > > + > > > > +err_affinity: > > > > + reset_control_assert(priv->clkevt[cpu].rst); > > > > +err_soft_reset: > > > > + clk_disable_unprepare(priv->clkevt[cpu].clk); > > > > +err_starting_cpu: > > > > + free_irq(evt->irq, evt); > > > > + return err; > > > > +} > > > > + > > > > +static int jh7110_timer_probe(struct platform_device *pdev) { > > > > + struct jh7110_timer_priv *priv = &jh7110_timer_info; > > > > + struct clock_event_device *evt; > > > > + struct jh7110_clkevt *clkevt; > > > > + char name[sizeof("chX")]; > > > > + int ch; > > > > + int ret; > > > > + void __iomem *base; > > > > + > > > > + base = devm_platform_ioremap_resource(pdev, 0); > > > > + if (IS_ERR(base)) > > > > + return dev_err_probe(&pdev->dev, PTR_ERR(base), > > > > + "failed to map registers\n"); > > > > + > > > > + priv->prst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); > > > > + if (IS_ERR(priv->prst)) > > > > + return dev_err_probe(&pdev->dev, PTR_ERR(priv->prst), > > > > + "failed to get apb reset\n"); > > > > + > > > > + priv->pclk = devm_clk_get_enabled(&pdev->dev, "apb"); > > > > + if (IS_ERR(priv->pclk)) > > > > + return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), > > > > + "failed to get & enable apb clock\n"); > > > > + > > > > + ret = reset_control_deassert(priv->prst); > > > > + if (ret) > > > > + return dev_err_probe(&pdev->dev, ret, "failed to deassert apb > > > > +reset\n"); > > > > + > > > > + for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) { > > > > + evt = per_cpu_ptr(&jh7110_clock_event, ch); > > > > + clkevt = &priv->clkevt[ch]; > > > > + snprintf(name, sizeof(name), "ch%d", ch); > > > > + > > > > + clkevt->base = base + JH7110_TIMER_CH_BASE(ch); > > > > + /* Ensure timer is disabled */ > > > > + writel(JH7110_TIMER_DIS, clkevt->base + > > JH7110_TIMER_ENABLE); > > > > + ret = jh7110_timer_int_clear(clkevt); > > > > + if (ret) > > > > + return ret; > > > > + > > > > + clkevt->rst = devm_reset_control_get_exclusive(&pdev->dev, > > name); > > > > + if (IS_ERR(clkevt->rst)) > > > > + return PTR_ERR(clkevt->rst); > > > > + > > > > + clkevt->clk = devm_clk_get(&pdev->dev, name); > > > > + if (IS_ERR(clkevt->clk)) > > > > + return PTR_ERR(clkevt->clk); > > > > + > > > > + clkevt->irq = platform_get_irq(pdev, ch); > > > > + if (clkevt->irq < 0) > > > > + return clkevt->irq; > > > > + > > > > + snprintf(clkevt->name, sizeof(clkevt->name), > > "jh7110-timer.ch%d", > > > > ch); > > > > + ret = devm_request_irq(&pdev->dev, clkevt->irq, > > > > jh7110_timer_interrupt, > > > > + IRQF_TIMER | IRQF_IRQPOLL, > > > > + clkevt->name, evt); > > > > + > > > > + if (ret) > > > > + return ret; > > > > + > > > > + clkevt->timer_enabled = false; > > > > + } > > > > + > > > > + return cpuhp_setup_state(CPUHP_AP_JH7110_TIMER_STARTING, > > > > + "clockevents/jh7110/timer:starting", > > > > + jh7110_timer_starting_cpu, > > jh7110_timer_dying_cpu); } > > > > + > > > > +static const struct of_device_id jh7110_timer_match[] = { > > > > + { .compatible = "starfive,jh7110-timer", }, > > > > + { /* sentinel */ } > > > > +}; > > > > +MODULE_DEVICE_TABLE(of, jh7110_timer_match); > > > > + > > > > +static struct platform_driver jh7110_timer_driver = { > > > > + .probe = jh7110_timer_probe, > > > > + .driver = { > > > > + .name = "jh7110-timer", > > > > + .of_match_table = jh7110_timer_match, > > > > + }, > > > > +}; > > > > +module_platform_driver(jh7110_timer_driver); > > > > + > > > > +MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>"); > > > > +MODULE_DESCRIPTION("StarFive JH7110 timer driver"); > > > > +MODULE_LICENSE("GPL"); > > > > diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h > > > > index 35e78ddb2b37..4a8b487c327e 100644 > > > > --- a/include/linux/cpuhotplug.h > > > > +++ b/include/linux/cpuhotplug.h > > > > @@ -175,6 +175,7 @@ enum cpuhp_state { > > > > CPUHP_AP_CSKY_TIMER_STARTING, > > > > CPUHP_AP_TI_GP_TIMER_STARTING, > > > > CPUHP_AP_HYPERV_TIMER_STARTING, > > > > + CPUHP_AP_JH7110_TIMER_STARTING, > > > > /* Must be the last timer callback */ > > > > CPUHP_AP_DUMMY_TIMER_STARTING, > > > > CPUHP_AP_ARM_XEN_STARTING, > > > > -- > > > > 2.17.1 > > > > > > Hi Daniel / Thomas > > > > > > I have submitted new version of patch for jh7110 timer driver. Could you please > > help to review and give your comments? > > > Thanks a lot! > > > > Hi Ziv > > > > I tried this on 6.9-rc6 on my VF2. It boots, but very slowly and "choppy". That is > > it repeatedly runs for <1s and then hangs for about 4s. > > > > Does this patch work for you? > > > > /Emil > > Hi, Emil > > I tried this on 6.9-rc7 and 6.9-rc7, but it doesn't reproduce the phenomenon you said. > The attachment is the log for 6.9-rc6. Could you please share your config file (starfive_visionfive2_defconfig or .config) with me? Yeah, I just tried again on 6.9-rc7 (+ Conor's riscv-dt-for-next, Minda's PCIe patches and the PWM driver) with this config: https://sprunge.us/kiXXba I don't know if it makes a difference but my also board boots via EFI. /Emil
Emil wrote: > > > > > Hi Daniel / Thomas > > > > > > > > I have submitted new version of patch for jh7110 timer driver. > > > > Could you please > > > help to review and give your comments? > > > > Thanks a lot! > > > > > > Hi Ziv > > > > > > I tried this on 6.9-rc6 on my VF2. It boots, but very slowly and > > > "choppy". That is it repeatedly runs for <1s and then hangs for about 4s. > > > > > > Does this patch work for you? > > > > > > /Emil > > > > Hi, Emil > > > > I tried this on 6.9-rc7 and 6.9-rc7, but it doesn't reproduce the phenomenon > you said. > > The attachment is the log for 6.9-rc6. Could you please share your config file > (starfive_visionfive2_defconfig or .config) with me? > > Yeah, I just tried again on 6.9-rc7 (+ Conor's riscv-dt-for-next, Minda's PCIe > patches and the PWM driver) with this config: > > https://sprunge.us/kiXXba > > I don't know if it makes a difference but my also board boots via EFI. > > /Emil Hi, Emil The cause of booting slowly is that when JH7110-Timer is registered as clockevent device, the soft timer will be pending while the crng tries to generate entropy(try_to_generate_entropy funcition in linux/driver/char/random.c). This can be avoided with setting CONFIG_HW_RANDOM and CONFIG_HW_RANDOM_JH7110 as "y". But I can't find a root casue. Could you please give me some advice? Best Regards Ziv.Xu
diff --git a/MAINTAINERS b/MAINTAINERS index 7c121493f43d..ef9b5f5bad9e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21043,6 +21043,13 @@ S: Maintained F: Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml F: sound/soc/starfive/jh7110_tdm.c +STARFIVE JH7110 TIMER DRIVER +M: Samin Guo <samin.guo@starfivetech.com> +M: Xingyu Wu <xingyu.wu@starfivetech.com> +S: Supported +F: Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml +F: drivers/clocksource/timer-jh7110.c + STARFIVE JH71X0 CLOCK DRIVERS M: Emil Renner Berthing <kernel@esmil.dk> M: Hal Feng <hal.feng@starfivetech.com> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 34faa0320ece..2dc97201dee1 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -641,6 +641,17 @@ config RISCV_TIMER is accessed via both the SBI and the rdcycle instruction. This is required for all RISC-V systems. +config STARFIVE_JH7110_TIMER + bool "Timer for the STARFIVE JH7110 SoC" + depends on ARCH_STARFIVE || COMPILE_TEST + select TIMER_OF + select CLKSRC_MMIO + default ARCH_STARFIVE + help + This enables the timer for StarFive JH7110 SoC. On RISC-V platform, + the system has started RISCV_TIMER, but you can also use this timer + which can provide four channels to do a lot more things on JH7110 SoC. + config CLINT_TIMER bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST depends on GENERIC_SCHED_CLOCK && RISCV diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 4bb856e4df55..8dc2f0ea2d0f 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o obj-$(CONFIG_X86_NUMACHIP) += numachip.o obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o +obj-$(CONFIG_STARFIVE_JH7110_TIMER) += timer-jh7110.o obj-$(CONFIG_CLINT_TIMER) += timer-clint.o obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o diff --git a/drivers/clocksource/timer-jh7110.c b/drivers/clocksource/timer-jh7110.c new file mode 100644 index 000000000000..dc770507f209 --- /dev/null +++ b/drivers/clocksource/timer-jh7110.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Starfive JH7110 Timer driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * This timer has four free-running and independent 32-bit counters and runs in 24MHz + * clock on the StarFive JH7110 SoC. Each channel(counter) can trigger an interrupt + * when timeout even CPU is sleeping. They support one-shot mode and continuous-run mode. + * + * Each channel is used as a global timer that serves each cpu core: + * JH7110 Timer Channel 0 -- CPU 0 + * JH7110 Timer Channel 1 -- CPU 1 + * JH7110 Timer Channel 2 -- CPU 2 + * JH7110 Timer Channel 3 -- CPU 3 + */ + +#include <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/cpu.h> +#include <linux/iopoll.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ +#define JH7110_TIMER_CH_LEN 0x40 +#define JH7110_TIMER_CH_BASE(x) ((x) * JH7110_TIMER_CH_LEN) +#define JH7110_TIMER_CH_MAX 4 + +#define JH7110_DELAY_US 0 +#define JH7110_TIMEOUT_US 10000 +#define JH7110_CLOCKEVENT_RATING 300 +#define JH7110_TIMER_MAX_TICKS 0xffffffff +#define JH7110_TIMER_MIN_TICKS 0xf + +#define JH7110_TIMER_INT_STATUS 0x00 /* RO[0:4]: Interrupt Status for channel0~4 */ +#define JH7110_TIMER_CTL 0x04 /* RW[0]: 0-continuous run, 1-single run */ +#define JH7110_TIMER_LOAD 0x08 /* RW: load value to counter */ +#define JH7110_TIMER_ENABLE 0x10 /* RW[0]: timer enable register */ +#define JH7110_TIMER_RELOAD 0x14 /* RW: write 1 or 0 both reload counter */ +#define JH7110_TIMER_VALUE 0x18 /* RO: timer value register */ +#define JH7110_TIMER_INT_CLR 0x20 /* RW: timer interrupt clear register */ +#define JH7110_TIMER_INT_MASK 0x24 /* RW[0]: timer interrupt mask register */ + +#define JH7110_TIMER_INT_CLR_ENA BIT(0) +#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1) + +#define JH7110_PERCPU_GET_CLKEVT (&jh7110_timer_info.clkevt[smp_processor_id()]) + +/** + * struct jh7110_clkevt - Description of each timer channel + * @clk: Clock of each timer channel + * @rst: Reset of each timer channel + * @base: Virtual address of each timer channel + * @irq: Interrupt number of each timer channel + * @timer_enabled: Enabled flag for each timer channel + * @name: Name of each timer channel + */ +struct jh7110_clkevt { + struct clk *clk; + struct reset_control *rst; + void __iomem *base; + int irq; + bool timer_enabled; + char name[sizeof("jh7110-timer.chX")]; +}; + +struct jh7110_timer_priv { + struct clk *pclk; + struct reset_control *prst; + struct device *dev; + struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX]; +}; + +static struct jh7110_timer_priv jh7110_timer_info; + +/* 0:continuous-run mode, 1:single-run mode */ +enum jh7110_timer_mode { + JH7110_TIMER_MODE_CONTIN, + JH7110_TIMER_MODE_SINGLE, +}; + +/* Interrupt Mask, 0:Unmask, 1:Mask */ +enum jh7110_timer_int_mask { + JH7110_TIMER_INT_ENA, + JH7110_TIMER_INT_DIS, +}; + +enum jh7110_timer_enable { + JH7110_TIMER_DIS, + JH7110_TIMER_ENA, +}; + +/* + * BIT(0): Read value represent channel int status. + * Write 1 to this bit to clear interrupt. Write 0 has no effects. + * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. + */ +static inline int jh7110_timer_int_clear(struct jh7110_clkevt *clkevt) +{ + u32 value; + int ret; + + /* Waiting interrupt can be cleared */ + ret = readl_poll_timeout_atomic(clkevt->base + JH7110_TIMER_INT_CLR, value, + !(value & JH7110_TIMER_INT_CLR_AVA_MASK), + JH7110_DELAY_US, JH7110_TIMEOUT_US); + if (!ret) + writel(JH7110_TIMER_INT_CLR_ENA, clkevt->base + JH7110_TIMER_INT_CLR); + + return ret; +} + +static int jh7110_timer_start(struct jh7110_clkevt *clkevt) +{ + int ret; + + /* Disable and clear interrupt first */ + writel(JH7110_TIMER_INT_DIS, clkevt->base + JH7110_TIMER_INT_MASK); + ret = jh7110_timer_int_clear(clkevt); + + writel(JH7110_TIMER_INT_ENA, clkevt->base + JH7110_TIMER_INT_MASK); + writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE); + + return ret; +} + +static int jh7110_timer_shutdown(struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; + + writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); + return jh7110_timer_int_clear(clkevt); +} + +/* IRQ handler for the timer */ +static irqreturn_t jh7110_timer_interrupt(int irq, void *data) +{ + struct clock_event_device *evt = (struct clock_event_device *)data; + struct jh7110_clkevt *clkevt = &jh7110_timer_info.clkevt[0]; + u32 reg = readl(clkevt->base + JH7110_TIMER_INT_STATUS); + u8 cpu_id = smp_processor_id(); + + /* Check interrupt status and channel(cpu) ID */ + if (!(reg & BIT(cpu_id))) + return IRQ_NONE; + + clkevt = &jh7110_timer_info.clkevt[cpu_id]; + writel(JH7110_TIMER_INT_CLR_ENA, (clkevt->base + JH7110_TIMER_INT_CLR)); + + if (evt->event_handler) + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static int jh7110_timer_set_periodic(struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; + + writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + JH7110_TIMER_CTL); + return 0; +} + +static int jh7110_timer_set_oneshot(struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; + + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL); + return 0; +} + +static int jh7110_timer_set_next_event(unsigned long next, + struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = JH7110_PERCPU_GET_CLKEVT; + + writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL); + writel(next, clkevt->base + JH7110_TIMER_LOAD); + + return jh7110_timer_start(clkevt); +} + +static DEFINE_PER_CPU(struct clock_event_device, jh7110_clock_event) = { + .features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + .rating = JH7110_CLOCKEVENT_RATING, + .set_state_shutdown = jh7110_timer_shutdown, + .set_state_periodic = jh7110_timer_set_periodic, + .set_state_oneshot = jh7110_timer_set_oneshot, + .set_state_oneshot_stopped = jh7110_timer_shutdown, + .set_next_event = jh7110_timer_set_next_event, +}; + +static int jh7110_timer_dying_cpu(unsigned int cpu) +{ + struct jh7110_timer_priv *priv = &jh7110_timer_info; + + if (!priv->clkevt[cpu].timer_enabled) + return 0; + + writel(JH7110_TIMER_DIS, priv->clkevt[cpu].base + JH7110_TIMER_ENABLE); + jh7110_timer_int_clear(&priv->clkevt[cpu]); + reset_control_assert(priv->clkevt[cpu].rst); + clk_disable_unprepare(priv->clkevt[cpu].clk); + + return 0; +} + +static int jh7110_timer_starting_cpu(unsigned int cpu) +{ + struct clock_event_device *evt = per_cpu_ptr(&jh7110_clock_event, cpu); + struct jh7110_timer_priv *priv = &jh7110_timer_info; + int err; + u32 rate; + + if (cpu >= JH7110_TIMER_CH_MAX) + return -ENOMEM; + + err = clk_prepare_enable(priv->clkevt[cpu].clk); + if (err) + goto err_starting_cpu; + + err = reset_control_deassert(priv->clkevt[cpu].rst); + if (err) + goto err_soft_reset; + + rate = clk_get_rate(priv->clkevt[cpu].clk); + evt->cpumask = cpumask_of(cpu); + evt->irq = priv->clkevt[cpu].irq; + + err = irq_force_affinity(evt->irq, cpumask_of(cpu)); + if (err) + goto err_affinity; + + clockevents_config_and_register(evt, rate, JH7110_TIMER_MIN_TICKS, + JH7110_TIMER_MAX_TICKS); + + /* Use one-shot mode */ + writel(JH7110_TIMER_MODE_SINGLE, (priv->clkevt[cpu].base + JH7110_TIMER_CTL)); + + priv->clkevt[cpu].timer_enabled = true; + + err = jh7110_timer_start(&priv->clkevt[cpu]); + if (err) + goto err_affinity; + return 0; + +err_affinity: + reset_control_assert(priv->clkevt[cpu].rst); +err_soft_reset: + clk_disable_unprepare(priv->clkevt[cpu].clk); +err_starting_cpu: + free_irq(evt->irq, evt); + return err; +} + +static int jh7110_timer_probe(struct platform_device *pdev) +{ + struct jh7110_timer_priv *priv = &jh7110_timer_info; + struct clock_event_device *evt; + struct jh7110_clkevt *clkevt; + char name[sizeof("chX")]; + int ch; + int ret; + void __iomem *base; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(&pdev->dev, PTR_ERR(base), + "failed to map registers\n"); + + priv->prst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); + if (IS_ERR(priv->prst)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->prst), + "failed to get apb reset\n"); + + priv->pclk = devm_clk_get_enabled(&pdev->dev, "apb"); + if (IS_ERR(priv->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), + "failed to get & enable apb clock\n"); + + ret = reset_control_deassert(priv->prst); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to deassert apb reset\n"); + + for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) { + evt = per_cpu_ptr(&jh7110_clock_event, ch); + clkevt = &priv->clkevt[ch]; + snprintf(name, sizeof(name), "ch%d", ch); + + clkevt->base = base + JH7110_TIMER_CH_BASE(ch); + /* Ensure timer is disabled */ + writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); + ret = jh7110_timer_int_clear(clkevt); + if (ret) + return ret; + + clkevt->rst = devm_reset_control_get_exclusive(&pdev->dev, name); + if (IS_ERR(clkevt->rst)) + return PTR_ERR(clkevt->rst); + + clkevt->clk = devm_clk_get(&pdev->dev, name); + if (IS_ERR(clkevt->clk)) + return PTR_ERR(clkevt->clk); + + clkevt->irq = platform_get_irq(pdev, ch); + if (clkevt->irq < 0) + return clkevt->irq; + + snprintf(clkevt->name, sizeof(clkevt->name), "jh7110-timer.ch%d", ch); + ret = devm_request_irq(&pdev->dev, clkevt->irq, jh7110_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, + clkevt->name, evt); + + if (ret) + return ret; + + clkevt->timer_enabled = false; + } + + return cpuhp_setup_state(CPUHP_AP_JH7110_TIMER_STARTING, + "clockevents/jh7110/timer:starting", + jh7110_timer_starting_cpu, jh7110_timer_dying_cpu); +} + +static const struct of_device_id jh7110_timer_match[] = { + { .compatible = "starfive,jh7110-timer", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_timer_match); + +static struct platform_driver jh7110_timer_driver = { + .probe = jh7110_timer_probe, + .driver = { + .name = "jh7110-timer", + .of_match_table = jh7110_timer_match, + }, +}; +module_platform_driver(jh7110_timer_driver); + +MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>"); +MODULE_DESCRIPTION("StarFive JH7110 timer driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 35e78ddb2b37..4a8b487c327e 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -175,6 +175,7 @@ enum cpuhp_state { CPUHP_AP_CSKY_TIMER_STARTING, CPUHP_AP_TI_GP_TIMER_STARTING, CPUHP_AP_HYPERV_TIMER_STARTING, + CPUHP_AP_JH7110_TIMER_STARTING, /* Must be the last timer callback */ CPUHP_AP_DUMMY_TIMER_STARTING, CPUHP_AP_ARM_XEN_STARTING,