Add a driver for the timer as part of LiteX SoC generator. By default, it's a 32-bit down counter with reload support. It has an optional uptime counter, however because it's noe defaultly enabled, it's not supported yet. Signed-off-by: Icenowy Zheng <uwu@xxxxxxxxxx> --- MAINTAINERS | 1 + drivers/clocksource/Kconfig | 10 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-litex.c | 163 ++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 drivers/clocksource/timer-litex.c diff --git a/MAINTAINERS b/MAINTAINERS index 1df62c469bd9..5892a0083531 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11973,6 +11973,7 @@ S: Maintained F: Documentation/devicetree/bindings/*/litex,*.yaml F: arch/openrisc/boot/dts/or1klitex.dts F: include/linux/litex.h +F: drivers/clocksource/timer-litex.c F: drivers/tty/serial/liteuart.c F: drivers/soc/litex/* F: drivers/net/ethernet/litex/* diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 4469e7f555e9..6936e09d1898 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -657,6 +657,16 @@ config GX6605S_TIMER help This option enables support for gx6605s SOC's timer. +config LITEX_TIMER + bool "LiteX SoC timer" + default LITEX + depends on OF + select TIMER_OF + help + Say yes here to enable LiteX SoC timer driver automatically + generated in a LiteX SoC. This timer could be useful when the + CPU core itself does not contain a supported timer. + config MILBEAUT_TIMER bool "Milbeaut timer driver" if COMPILE_TEST depends on OF diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 64ab547de97b..c7d3eda617a7 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_MILBEAUT_TIMER) += timer-milbeaut.o obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o obj-$(CONFIG_RDA_TIMER) += timer-rda.o +obj-$(CONFIG_LITEX_TIMER) += timer-litex.o obj-$(CONFIG_ARC_TIMERS) += arc_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o diff --git a/drivers/clocksource/timer-litex.c b/drivers/clocksource/timer-litex.c new file mode 100644 index 000000000000..609023403602 --- /dev/null +++ b/drivers/clocksource/timer-litex.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LiteX SoC builder timer handling. + * + * Copyright (C) 2022 Icenowy Zheng <uwu@xxxxxxxxxx> + */ + +#include <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqreturn.h> +#include <linux/sched_clock.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +#include "timer-of.h" + +/* + * CSRs definitions (base address offsets + width) + * + * The definitions below are true for LiteX SoC configured for 32-bit CSR Bus, + * 32-bit aligned, and the timer configured as 32-bit. + * + * Supporting other configurations might require new definitions or a more + * generic way of indexing the LiteX CSRs. + * + * For more details on how CSRs are defined and handled in LiteX, see comments + * in the LiteX SoC Driver: drivers/soc/litex/litex_soc_ctrl.c + */ +#define OFF_LOAD 0x00 +#define OFF_RELOAD 0x04 +#define OFF_EN 0x08 +#define OFF_UPDATE_VALUE 0x0c +#define OFF_VALUE 0x10 +#define OFF_EV_STATUS 0x14 +#define OFF_EV_PENDING 0x18 +#define OFF_EV_ENABLE 0x1c + +/* events */ +#define EV_ZERO BIT(0) + +static void litex_timer_enable(struct timer_of *to, bool enable) +{ + writel(enable ? EV_ZERO : 0, timer_of_base(to) + OFF_EV_ENABLE); + writel(enable ? 1 : 0, timer_of_base(to) + OFF_EN); +} + +static int litex_timer_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + struct timer_of *to = to_timer_of(evt); + + litex_timer_enable(to, false); + writel((uint32_t) delta, timer_of_base(to) + OFF_LOAD); + litex_timer_enable(to, true); + + return 0; +} + +static int litex_timer_state_oneshot(struct clock_event_device *evt) +{ + struct timer_of *to = to_timer_of(evt); + + litex_timer_enable(to, false); + writel(0, timer_of_base(to) + OFF_RELOAD); + litex_timer_enable(to, true); + + return 0; +} + +static int litex_timer_state_periodic(struct clock_event_device *evt) +{ + struct timer_of *to = to_timer_of(evt); + + litex_timer_enable(to, false); + writel((uint32_t) timer_of_period(to), timer_of_base(to) + OFF_RELOAD); + writel((uint32_t) timer_of_period(to), timer_of_base(to) + OFF_LOAD); + litex_timer_enable(to, true); + + return 0; +} + +static int litex_timer_state_shutdown(struct clock_event_device *evt) +{ + struct timer_of *to = to_timer_of(evt); + + litex_timer_enable(to, false); + + return 0; +} + +static irqreturn_t litex_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + struct timer_of *to = to_timer_of(evt); + u32 val; + + val = readl(timer_of_base(to) + OFF_EV_PENDING); + if (!(val & EV_ZERO)) + return IRQ_NONE; + + writel(EV_ZERO, timer_of_base(to) + OFF_EV_PENDING); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct timer_of to_litex = { + .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, + .clkevt = { + .name = "LiteX Timer", + .features = CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_PERIODIC, + .set_state_oneshot = litex_timer_state_oneshot, + .set_state_periodic = litex_timer_state_periodic, + .set_next_event = litex_timer_next_event, + .set_state_shutdown = litex_timer_state_shutdown, + .rating = 101, + }, + .of_irq = { + .handler = litex_timer_interrupt, + .flags = IRQF_TIMER, + }, +}; + +static void __init litex_clockevent_init(void) +{ + to_litex.clkevt.cpumask = cpu_possible_mask; + + clockevents_config_and_register(&to_litex.clkevt, + timer_of_rate(&to_litex), + 0x1, 0xffffffff); +} + +static int __init litex_timer_init(struct device_node *np) +{ + int ret = 0; + u32 width; + + ret = of_property_read_u32(np, "litex,width", &width); + if (ret) { + pr_err("Cannot retrieve width\n"); + return ret; + } + if (width != 32) { + pr_err("Unsupported width\n"); + return -ENOTSUPP; + } + + ret = timer_of_init(np, &to_litex); + if (ret) { + pr_err("Cannot parse DT for LiteX timer\n"); + return ret; + } + + litex_clockevent_init(); + + return 0; +} +TIMER_OF_DECLARE(litex, "litex,timer", litex_timer_init); -- 2.37.1