From 0113ab803a8aee00bc136507bcc9b16466f3c3aa Mon Sep 17 00:00:00 2001 From: Mesih Kilinc Date: Mon, 11 Feb 2019 12:21:08 +0300 Subject: clocksource/drivers/sun4i: Add a compatible for suniv The suniv (new F-series) chip has a timer with less functionality than the A10 timer, e.g. it has only 3 channels. Add a new compatible for it. As we didn't use the extra channels on A10 either now, the code needn't to be changed. The suniv chip is based on ARM926EJ-S CPU, thus it has no architecture timer. Register sun4i_timer as sched_clock on it. Signed-off-by: Mesih Kilinc Acked-by: Maxime Ripard Acked-by: Daniel Lezcano Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-sun4i.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/timer-sun4i.c b/drivers/clocksource/timer-sun4i.c index 6e0180aaf784..65f38f6ca714 100644 --- a/drivers/clocksource/timer-sun4i.c +++ b/drivers/clocksource/timer-sun4i.c @@ -186,7 +186,8 @@ static int __init sun4i_timer_init(struct device_node *node) */ if (of_machine_is_compatible("allwinner,sun4i-a10") || of_machine_is_compatible("allwinner,sun5i-a13") || - of_machine_is_compatible("allwinner,sun5i-a10s")) + of_machine_is_compatible("allwinner,sun5i-a10s") || + of_machine_is_compatible("allwinner,suniv-f1c100s")) sched_clock_register(sun4i_timer_sched_read, 32, timer_of_rate(&to)); @@ -218,3 +219,5 @@ static int __init sun4i_timer_init(struct device_node *node) } TIMER_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer", sun4i_timer_init); +TIMER_OF_DECLARE(suniv, "allwinner,suniv-f1c100s-timer", + sun4i_timer_init); -- cgit v1.2.3 From dfc82faad72520769ca146f857e65c23632eed5a Mon Sep 17 00:00:00 2001 From: David Abdurachmanov Date: Mon, 8 Apr 2019 10:07:45 +0200 Subject: clocksource/drivers/sp804: Add COMPILE_TEST to CONFIG_ARM_TIMER_SP804 This is only used on arm and arm64 platforms. Add COMPILE_TEST option. Tested with 5.1-rc3+ on Fedora/RISCV. CONFIG_ARM_TIMER_SP804 no more shows up in riscv config. Signed-off-by: David Abdurachmanov Signed-off-by: Daniel Lezcano --- drivers/clocksource/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 171502a356aa..ede5d20299b9 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -379,7 +379,7 @@ config ARM_GLOBAL_TIMER This options enables support for the ARM global timer unit config ARM_TIMER_SP804 - bool "Support for Dual Timer SP804 module" + bool "Support for Dual Timer SP804 module" if COMPILE_TEST depends on GENERIC_SCHED_CLOCK && CLKDEV_LOOKUP select CLKSRC_MMIO select TIMER_OF if OF -- cgit v1.2.3 From 95170f0708f2f499da2594b29d39ac07ea221d7a Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Tue, 2 Apr 2019 11:02:34 +0800 Subject: clocksource/drivers/tegra: Rework for compensation of suspend time Since the clocksource framework has the support for suspend time compensation. Re-work the driver to use that, so we can reduce the duplicate code. Suggested-by: Daniel Lezcano Signed-off-by: Joseph Lo Acked-by: Thierry Reding Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-tegra20.c | 63 ++++++++++++------------------------- 1 file changed, 20 insertions(+), 43 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/timer-tegra20.c b/drivers/clocksource/timer-tegra20.c index fdb3d795a409..919b3568c495 100644 --- a/drivers/clocksource/timer-tegra20.c +++ b/drivers/clocksource/timer-tegra20.c @@ -60,9 +60,6 @@ static u32 usec_config; static void __iomem *timer_reg_base; #ifdef CONFIG_ARM -static void __iomem *rtc_base; -static struct timespec64 persistent_ts; -static u64 persistent_ms, last_persistent_ms; static struct delay_timer tegra_delay_timer; #endif @@ -199,40 +196,30 @@ static unsigned long tegra_delay_timer_read_counter_long(void) return readl(timer_reg_base + TIMERUS_CNTR_1US); } +static struct timer_of suspend_rtc_to = { + .flags = TIMER_OF_BASE | TIMER_OF_CLOCK, +}; + /* * tegra_rtc_read - Reads the Tegra RTC registers * Care must be taken that this funciton is not called while the * tegra_rtc driver could be executing to avoid race conditions * on the RTC shadow register */ -static u64 tegra_rtc_read_ms(void) +static u64 tegra_rtc_read_ms(struct clocksource *cs) { - u32 ms = readl(rtc_base + RTC_MILLISECONDS); - u32 s = readl(rtc_base + RTC_SHADOW_SECONDS); + u32 ms = readl(timer_of_base(&suspend_rtc_to) + RTC_MILLISECONDS); + u32 s = readl(timer_of_base(&suspend_rtc_to) + RTC_SHADOW_SECONDS); return (u64)s * MSEC_PER_SEC + ms; } -/* - * tegra_read_persistent_clock64 - Return time from a persistent clock. - * - * Reads the time from a source which isn't disabled during PM, the - * 32k sync timer. Convert the cycles elapsed since last read into - * nsecs and adds to a monotonically increasing timespec64. - * Care must be taken that this funciton is not called while the - * tegra_rtc driver could be executing to avoid race conditions - * on the RTC shadow register - */ -static void tegra_read_persistent_clock64(struct timespec64 *ts) -{ - u64 delta; - - last_persistent_ms = persistent_ms; - persistent_ms = tegra_rtc_read_ms(); - delta = persistent_ms - last_persistent_ms; - - timespec64_add_ns(&persistent_ts, delta * NSEC_PER_MSEC); - *ts = persistent_ts; -} +static struct clocksource suspend_rtc_clocksource = { + .name = "tegra_suspend_timer", + .rating = 200, + .read = tegra_rtc_read_ms, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP, +}; #endif static int tegra_timer_common_init(struct device_node *np, struct timer_of *to) @@ -385,25 +372,15 @@ out: static int __init tegra20_init_rtc(struct device_node *np) { - struct clk *clk; + int ret; - rtc_base = of_iomap(np, 0); - if (!rtc_base) { - pr_err("Can't map RTC registers\n"); - return -ENXIO; - } + ret = timer_of_init(np, &suspend_rtc_to); + if (ret) + return ret; - /* - * rtc registers are used by read_persistent_clock, keep the rtc clock - * enabled - */ - clk = of_clk_get(np, 0); - if (IS_ERR(clk)) - pr_warn("Unable to get rtc-tegra clock\n"); - else - clk_prepare_enable(clk); + clocksource_register_hz(&suspend_rtc_clocksource, 1000); - return register_persistent_clock(tegra_read_persistent_clock64); + return 0; } TIMER_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc); #endif -- cgit v1.2.3 From f0b1ca623d8df5dad977f22a1142a81a886b0eb8 Mon Sep 17 00:00:00 2001 From: Sugaya Taichi Date: Fri, 12 Apr 2019 16:36:49 +0900 Subject: clocksource/drivers/timer-milbeaut: Fix to enable one-shot timer Fix mlb_set_oneshot_state() to enable one-shot timer. The function should stop and start a timer, but "start" statement was dropped. Kick the register to start one-shot timer. Fixes: b58f28f306db ("clocksource/drivers/timer-milbeaut: Introduce timer for Milbeaut SoCs") Signed-off-by: Sugaya Taichi Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-milbeaut.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/timer-milbeaut.c b/drivers/clocksource/timer-milbeaut.c index f2019a88e3ee..9fd5d081fac4 100644 --- a/drivers/clocksource/timer-milbeaut.c +++ b/drivers/clocksource/timer-milbeaut.c @@ -79,6 +79,8 @@ static int mlb_set_state_oneshot(struct clock_event_device *clk) struct timer_of *to = to_timer_of(clk); u32 val = MLB_TMR_TMCSR_CSL_DIV2; + writel_relaxed(val, timer_of_base(to) + MLB_TMR_EVT_TMCSR_OFS); + val |= MLB_TMR_TMCSR_CNTE | MLB_TMR_TMCSR_TRG | MLB_TMR_TMCSR_INTE; writel_relaxed(val, timer_of_base(to) + MLB_TMR_EVT_TMCSR_OFS); return 0; } -- cgit v1.2.3 From 95d5dc712634901e6707d3178e6170aa6e0a43ce Mon Sep 17 00:00:00 2001 From: Sugaya Taichi Date: Fri, 12 Apr 2019 16:36:50 +0900 Subject: clocksource/drivers/timer-milbeaut: Add shutdown function Add a shutdown operation to support shutdown timer. Signed-off-by: Sugaya Taichi Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-milbeaut.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/timer-milbeaut.c b/drivers/clocksource/timer-milbeaut.c index 9fd5d081fac4..f4780619dbaf 100644 --- a/drivers/clocksource/timer-milbeaut.c +++ b/drivers/clocksource/timer-milbeaut.c @@ -85,6 +85,15 @@ static int mlb_set_state_oneshot(struct clock_event_device *clk) return 0; } +static int mlb_set_state_shutdown(struct clock_event_device *clk) +{ + struct timer_of *to = to_timer_of(clk); + u32 val = MLB_TMR_TMCSR_CSL_DIV2; + + writel_relaxed(val, timer_of_base(to) + MLB_TMR_EVT_TMCSR_OFS); + return 0; +} + static int mlb_clkevt_next_event(unsigned long event, struct clock_event_device *clk) { @@ -125,6 +134,7 @@ static struct timer_of to = { .features = CLOCK_EVT_FEAT_DYNIRQ | CLOCK_EVT_FEAT_ONESHOT, .set_state_oneshot = mlb_set_state_oneshot, .set_state_periodic = mlb_set_state_periodic, + .set_state_shutdown = mlb_set_state_shutdown, .set_next_event = mlb_clkevt_next_event, }, -- cgit v1.2.3 From 37dbf65c6a1fb7978ebc818c6fa9a4cf54ded2f9 Mon Sep 17 00:00:00 2001 From: Sugaya Taichi Date: Fri, 12 Apr 2019 16:36:51 +0900 Subject: clocksource/drivers/timer-milbeaut: Cleanup common register accesses Aggregate common register accesses into shared functions for maintainability. Signed-off-by: Sugaya Taichi Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-milbeaut.c | 62 +++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 23 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/timer-milbeaut.c b/drivers/clocksource/timer-milbeaut.c index f4780619dbaf..fa9fb4eacade 100644 --- a/drivers/clocksource/timer-milbeaut.c +++ b/drivers/clocksource/timer-milbeaut.c @@ -26,8 +26,8 @@ #define MLB_TMR_TMCSR_CSL_DIV2 0 #define MLB_TMR_DIV_CNT 2 -#define MLB_TMR_SRC_CH (1) -#define MLB_TMR_EVT_CH (0) +#define MLB_TMR_SRC_CH 1 +#define MLB_TMR_EVT_CH 0 #define MLB_TMR_SRC_CH_OFS (MLB_TMR_REGSZPCH * MLB_TMR_SRC_CH) #define MLB_TMR_EVT_CH_OFS (MLB_TMR_REGSZPCH * MLB_TMR_EVT_CH) @@ -43,6 +43,8 @@ #define MLB_TMR_EVT_TMRLR2_OFS (MLB_TMR_EVT_CH_OFS + MLB_TMR_TMRLR2_OFS) #define MLB_TIMER_RATING 500 +#define MLB_TIMER_ONESHOT 0 +#define MLB_TIMER_PERIODIC 1 static irqreturn_t mlb_timer_interrupt(int irq, void *dev_id) { @@ -59,38 +61,53 @@ static irqreturn_t mlb_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int mlb_set_state_periodic(struct clock_event_device *clk) +static void mlb_evt_timer_start(struct timer_of *to, bool periodic) { - struct timer_of *to = to_timer_of(clk); u32 val = MLB_TMR_TMCSR_CSL_DIV2; + val |= MLB_TMR_TMCSR_CNTE | MLB_TMR_TMCSR_TRG | MLB_TMR_TMCSR_INTE; + if (periodic) + val |= MLB_TMR_TMCSR_RELD; writel_relaxed(val, timer_of_base(to) + MLB_TMR_EVT_TMCSR_OFS); +} + +static void mlb_evt_timer_stop(struct timer_of *to) +{ + u32 val = readl_relaxed(timer_of_base(to) + MLB_TMR_EVT_TMCSR_OFS); - writel_relaxed(to->of_clk.period, timer_of_base(to) + - MLB_TMR_EVT_TMRLR1_OFS); - val |= MLB_TMR_TMCSR_RELD | MLB_TMR_TMCSR_CNTE | - MLB_TMR_TMCSR_TRG | MLB_TMR_TMCSR_INTE; + val &= ~MLB_TMR_TMCSR_CNTE; writel_relaxed(val, timer_of_base(to) + MLB_TMR_EVT_TMCSR_OFS); +} + +static void mlb_evt_timer_register_count(struct timer_of *to, unsigned long cnt) +{ + writel_relaxed(cnt, timer_of_base(to) + MLB_TMR_EVT_TMRLR1_OFS); +} + +static int mlb_set_state_periodic(struct clock_event_device *clk) +{ + struct timer_of *to = to_timer_of(clk); + + mlb_evt_timer_stop(to); + mlb_evt_timer_register_count(to, to->of_clk.period); + mlb_evt_timer_start(to, MLB_TIMER_PERIODIC); return 0; } static int mlb_set_state_oneshot(struct clock_event_device *clk) { struct timer_of *to = to_timer_of(clk); - u32 val = MLB_TMR_TMCSR_CSL_DIV2; - writel_relaxed(val, timer_of_base(to) + MLB_TMR_EVT_TMCSR_OFS); - val |= MLB_TMR_TMCSR_CNTE | MLB_TMR_TMCSR_TRG | MLB_TMR_TMCSR_INTE; - writel_relaxed(val, timer_of_base(to) + MLB_TMR_EVT_TMCSR_OFS); + mlb_evt_timer_stop(to); + mlb_evt_timer_start(to, MLB_TIMER_ONESHOT); return 0; } static int mlb_set_state_shutdown(struct clock_event_device *clk) { struct timer_of *to = to_timer_of(clk); - u32 val = MLB_TMR_TMCSR_CSL_DIV2; - writel_relaxed(val, timer_of_base(to) + MLB_TMR_EVT_TMCSR_OFS); + mlb_evt_timer_stop(to); return 0; } @@ -99,22 +116,21 @@ static int mlb_clkevt_next_event(unsigned long event, { struct timer_of *to = to_timer_of(clk); - writel_relaxed(event, timer_of_base(to) + MLB_TMR_EVT_TMRLR1_OFS); - writel_relaxed(MLB_TMR_TMCSR_CSL_DIV2 | - MLB_TMR_TMCSR_CNTE | MLB_TMR_TMCSR_INTE | - MLB_TMR_TMCSR_TRG, timer_of_base(to) + - MLB_TMR_EVT_TMCSR_OFS); + mlb_evt_timer_stop(to); + mlb_evt_timer_register_count(to, event); + mlb_evt_timer_start(to, MLB_TIMER_ONESHOT); return 0; } static int mlb_config_clock_source(struct timer_of *to) { - writel_relaxed(0, timer_of_base(to) + MLB_TMR_SRC_TMCSR_OFS); - writel_relaxed(~0, timer_of_base(to) + MLB_TMR_SRC_TMR_OFS); + u32 val = MLB_TMR_TMCSR_CSL_DIV2; + + writel_relaxed(val, timer_of_base(to) + MLB_TMR_SRC_TMCSR_OFS); writel_relaxed(~0, timer_of_base(to) + MLB_TMR_SRC_TMRLR1_OFS); writel_relaxed(~0, timer_of_base(to) + MLB_TMR_SRC_TMRLR2_OFS); - writel_relaxed(BIT(4) | BIT(1) | BIT(0), timer_of_base(to) + - MLB_TMR_SRC_TMCSR_OFS); + val |= MLB_TMR_TMCSR_RELD | MLB_TMR_TMCSR_CNTE | MLB_TMR_TMCSR_TRG; + writel_relaxed(val, timer_of_base(to) + MLB_TMR_SRC_TMCSR_OFS); return 0; } -- cgit v1.2.3 From c2c9136b7096f0583117d7d0486600feec387865 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 26 Apr 2019 23:47:10 +0200 Subject: ARM: at91: move SoC specific definitions to SoC folder Move linux/atmel_tc.h to the SoC specific folder include/soc/at91. Signed-off-by: Alexandre Belloni Acked-by: Thierry Reding Acked-by: Arnd Bergmann Signed-off-by: Daniel Lezcano --- drivers/clocksource/tcb_clksrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 43f4d5c4d6fa..138a12090149 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include /* -- cgit v1.2.3 From 86232bfd28d71db4a50562cf81ff88ef58a6d401 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 26 Apr 2019 23:47:11 +0200 Subject: clocksource/drivers/tcb_clksrc: Stop depending on atmel_tclib atmel_tclib is probed too late in the boot process to be able to use the TCB as the boot clocksource. This is an issue for SoCs without the PIT (sams70, samv70 and samv71 families) as they simply currently can't boot. Get rid of the atmel_tclib dependency and probe everything on our own using the correct device tree binding. This also allows getting rid of ATMEL_TCB_CLKSRC_BLOCK and makes the driver a bit more flexible as the TCB is not hardcoded in the kernel anymore. Signed-off-by: Alexandre Belloni Signed-off-by: Daniel Lezcano --- drivers/clocksource/tcb_clksrc.c | 108 ++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 40 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 138a12090149..bf68504da94a 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -9,7 +9,8 @@ #include #include #include -#include +#include +#include #include #include @@ -28,13 +29,6 @@ * source, used in either periodic or oneshot mode. This runs * at 32 KiHZ, and can handle delays of up to two seconds. * - * A boot clocksource and clockevent source are also currently needed, - * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so - * this code can be used when init_timers() is called, well before most - * devices are set up. (Some low end AT91 parts, which can run uClinux, - * have only the timers in one TC block... they currently don't support - * the tclib code, because of that initialization issue.) - * * REVISIT behavior during system suspend states... we should disable * all clocks and save the power. Easily done for clockevent devices, * but clocksources won't necessarily get the needed notifications. @@ -112,7 +106,6 @@ void tc_clksrc_resume(struct clocksource *cs) } static struct clocksource clksrc = { - .name = "tcb_clksrc", .rating = 200, .read = tc_get_cycles, .mask = CLOCKSOURCE_MASK(32), @@ -214,7 +207,6 @@ static int tc_next_event(unsigned long delta, struct clock_event_device *d) static struct tc_clkevt_device clkevt = { .clkevt = { - .name = "tc_clkevt", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, /* Should be lower than at91rm9200's system timer */ @@ -330,39 +322,73 @@ static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_id writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); } -static int __init tcb_clksrc_init(void) -{ - static char bootinfo[] __initdata - = KERN_DEBUG "%s: tc%d at %d.%03d MHz\n"; +static const u8 atmel_tcb_divisors[5] = { 2, 8, 32, 128, 0, }; + +static const struct of_device_id atmel_tcb_of_match[] = { + { .compatible = "atmel,at91rm9200-tcb", .data = (void *)16, }, + { .compatible = "atmel,at91sam9x5-tcb", .data = (void *)32, }, + { /* sentinel */ } +}; - struct platform_device *pdev; - struct atmel_tc *tc; +static int __init tcb_clksrc_init(struct device_node *node) +{ + struct atmel_tc tc; struct clk *t0_clk; + const struct of_device_id *match; u32 rate, divided_rate = 0; int best_divisor_idx = -1; int clk32k_divisor_idx = -1; + int bits; int i; int ret; - tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK); - if (!tc) { - pr_debug("can't alloc TC for clocksource\n"); - return -ENODEV; + /* Protect against multiple calls */ + if (tcaddr) + return 0; + + tc.regs = of_iomap(node->parent, 0); + if (!tc.regs) + return -ENXIO; + + t0_clk = of_clk_get_by_name(node->parent, "t0_clk"); + if (IS_ERR(t0_clk)) + return PTR_ERR(t0_clk); + + tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk"); + if (IS_ERR(tc.slow_clk)) + return PTR_ERR(tc.slow_clk); + + tc.clk[0] = t0_clk; + tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk"); + if (IS_ERR(tc.clk[1])) + tc.clk[1] = t0_clk; + tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk"); + if (IS_ERR(tc.clk[2])) + tc.clk[2] = t0_clk; + + tc.irq[2] = of_irq_get(node->parent, 2); + if (tc.irq[2] <= 0) { + tc.irq[2] = of_irq_get(node->parent, 0); + if (tc.irq[2] <= 0) + return -EINVAL; } - tcaddr = tc->regs; - pdev = tc->pdev; - t0_clk = tc->clk[0]; + match = of_match_node(atmel_tcb_of_match, node->parent); + bits = (uintptr_t)match->data; + + for (i = 0; i < ARRAY_SIZE(tc.irq); i++) + writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR)); + ret = clk_prepare_enable(t0_clk); if (ret) { pr_debug("can't enable T0 clk\n"); - goto err_free_tc; + return ret; } /* How fast will we be counting? Pick something over 5 MHz. */ rate = (u32) clk_get_rate(t0_clk); - for (i = 0; i < 5; i++) { - unsigned divisor = atmel_tc_divisors[i]; + for (i = 0; i < ARRAY_SIZE(atmel_tcb_divisors); i++) { + unsigned divisor = atmel_tcb_divisors[i]; unsigned tmp; /* remember 32 KiHz clock for later */ @@ -381,27 +407,29 @@ static int __init tcb_clksrc_init(void) best_divisor_idx = i; } - - printk(bootinfo, clksrc.name, CONFIG_ATMEL_TCB_CLKSRC_BLOCK, - divided_rate / 1000000, + clksrc.name = kbasename(node->parent->full_name); + clkevt.clkevt.name = kbasename(node->parent->full_name); + pr_debug("%s at %d.%03d MHz\n", clksrc.name, divided_rate / 1000000, ((divided_rate % 1000000) + 500) / 1000); - if (tc->tcb_config && tc->tcb_config->counter_width == 32) { + tcaddr = tc.regs; + + if (bits == 32) { /* use apropriate function to read 32 bit counter */ clksrc.read = tc_get_cycles32; /* setup ony channel 0 */ - tcb_setup_single_chan(tc, best_divisor_idx); + tcb_setup_single_chan(&tc, best_divisor_idx); } else { - /* tclib will give us three clocks no matter what the + /* we have three clocks no matter what the * underlying platform supports. */ - ret = clk_prepare_enable(tc->clk[1]); + ret = clk_prepare_enable(tc.clk[1]); if (ret) { pr_debug("can't enable T1 clk\n"); goto err_disable_t0; } /* setup both channel 0 & 1 */ - tcb_setup_dual_chan(tc, best_divisor_idx); + tcb_setup_dual_chan(&tc, best_divisor_idx); } /* and away we go! */ @@ -410,7 +438,7 @@ static int __init tcb_clksrc_init(void) goto err_disable_t1; /* channel 2: periodic and oneshot timer support */ - ret = setup_clkevents(tc, clk32k_divisor_idx); + ret = setup_clkevents(&tc, clk32k_divisor_idx); if (ret) goto err_unregister_clksrc; @@ -420,14 +448,14 @@ err_unregister_clksrc: clocksource_unregister(&clksrc); err_disable_t1: - if (!tc->tcb_config || tc->tcb_config->counter_width != 32) - clk_disable_unprepare(tc->clk[1]); + if (bits != 32) + clk_disable_unprepare(tc.clk[1]); err_disable_t0: clk_disable_unprepare(t0_clk); -err_free_tc: - atmel_tc_free(tc); + tcaddr = NULL; + return ret; } -arch_initcall(tcb_clksrc_init); +TIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init); -- cgit v1.2.3 From f712a1e8e59e8f42b5a9ec3887a4ad079bb18175 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 26 Apr 2019 23:47:12 +0200 Subject: clocksource/drivers/tcb_clksrc: Use tcb as sched_clock Now that the driver is registered early enough, use the TCB as the sched_clock which is much more accurate than the jiffies implementation. Signed-off-by: Alexandre Belloni Signed-off-by: Daniel Lezcano --- drivers/clocksource/tcb_clksrc.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index bf68504da94a..9de8c10ab546 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -114,6 +115,16 @@ static struct clocksource clksrc = { .resume = tc_clksrc_resume, }; +static u64 notrace tc_sched_clock_read(void) +{ + return tc_get_cycles(&clksrc); +} + +static u64 notrace tc_sched_clock_read32(void) +{ + return tc_get_cycles32(&clksrc); +} + #ifdef CONFIG_GENERIC_CLOCKEVENTS struct tc_clkevt_device { @@ -335,6 +346,7 @@ static int __init tcb_clksrc_init(struct device_node *node) struct atmel_tc tc; struct clk *t0_clk; const struct of_device_id *match; + u64 (*tc_sched_clock)(void); u32 rate, divided_rate = 0; int best_divisor_idx = -1; int clk32k_divisor_idx = -1; @@ -419,6 +431,7 @@ static int __init tcb_clksrc_init(struct device_node *node) clksrc.read = tc_get_cycles32; /* setup ony channel 0 */ tcb_setup_single_chan(&tc, best_divisor_idx); + tc_sched_clock = tc_sched_clock_read32; } else { /* we have three clocks no matter what the * underlying platform supports. @@ -430,6 +443,7 @@ static int __init tcb_clksrc_init(struct device_node *node) } /* setup both channel 0 & 1 */ tcb_setup_dual_chan(&tc, best_divisor_idx); + tc_sched_clock = tc_sched_clock_read; } /* and away we go! */ @@ -442,6 +456,8 @@ static int __init tcb_clksrc_init(struct device_node *node) if (ret) goto err_unregister_clksrc; + sched_clock_register(tc_sched_clock, 32, divided_rate); + return 0; err_unregister_clksrc: -- cgit v1.2.3 From 6275f4947e2f13a13a509d9fec4e1e5b15152680 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 26 Apr 2019 23:47:14 +0200 Subject: clocksource/drivers/tcb_clksrc: Move Kconfig option Move the ATMEL_TCB_CLKSRC option to drivers/clocksource and make it silent if COMPILE_TEST is not selected. Cc: Arnd Bergmann Signed-off-by: Alexandre Belloni Signed-off-by: Daniel Lezcano --- drivers/clocksource/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index ede5d20299b9..eb1560187434 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -410,6 +410,13 @@ config ATMEL_ST help Support for the Atmel ST timer. +config ATMEL_TCB_CLKSRC + bool "Atmel TC Block timer driver" if COMPILE_TEST + depends on HAS_IOMEM + select TIMER_OF if OF + help + Support for Timer Counter Blocks on Atmel SoCs. + config CLKSRC_EXYNOS_MCT bool "Exynos multi core timer driver" if COMPILE_TEST depends on ARM || ARM64 -- cgit v1.2.3 From 2f982703bd00245af8455efad491ca45990bc0d8 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 26 Apr 2019 23:47:15 +0200 Subject: clocksource/drivers/timer-atmel-pit: Rework Kconfig option Allow building the PIT driver when COMPILE_TEST is enabled. Also remove its default value so it can be disabled. Signed-off-by: Alexandre Belloni Signed-off-by: Daniel Lezcano --- drivers/clocksource/Kconfig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index eb1560187434..2137f672a12f 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -399,8 +399,11 @@ config ARMV7M_SYSTICK This options enables support for the ARMv7M system timer unit config ATMEL_PIT + bool "Atmel PIT support" if COMPILE_TEST + depends on HAS_IOMEM select TIMER_OF if OF - def_bool SOC_AT91SAM9 || SOC_SAMA5 + help + Support for the Periodic Interval Timer found on Atmel SoCs. config ATMEL_ST bool "Atmel ST timer support" if COMPILE_TEST -- cgit v1.2.3 From a7aae768166e1fff342cfe3ab506e657b7ea8044 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 26 Apr 2019 23:47:16 +0200 Subject: clocksource/drivers/tcb_clksrc: Rename the file for consistency For the sake of consistency, let's rename the file to a name similar to other file names in this directory. Signed-off-by: Alexandre Belloni Signed-off-by: Daniel Lezcano --- drivers/clocksource/Makefile | 2 +- drivers/clocksource/tcb_clksrc.c | 477 ---------------------------------- drivers/clocksource/timer-atmel-tcb.c | 477 ++++++++++++++++++++++++++++++++++ 3 files changed, 478 insertions(+), 478 deletions(-) delete mode 100644 drivers/clocksource/tcb_clksrc.c create mode 100644 drivers/clocksource/timer-atmel-tcb.c (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index be6e0fbc7489..923b9b60c909 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_TIMER_OF) += timer-of.o obj-$(CONFIG_TIMER_PROBE) += timer-probe.o obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o obj-$(CONFIG_ATMEL_ST) += timer-atmel-st.o -obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o +obj-$(CONFIG_ATMEL_TCB_CLKSRC) += timer-atmel-tcb.o obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o obj-$(CONFIG_CS5535_CLOCK_EVENT_SRC) += timer-cs5535.o diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c deleted file mode 100644 index 9de8c10ab546..000000000000 --- a/drivers/clocksource/tcb_clksrc.c +++ /dev/null @@ -1,477 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* - * We're configured to use a specific TC block, one that's not hooked - * up to external hardware, to provide a time solution: - * - * - Two channels combine to create a free-running 32 bit counter - * with a base rate of 5+ MHz, packaged as a clocksource (with - * resolution better than 200 nsec). - * - Some chips support 32 bit counter. A single channel is used for - * this 32 bit free-running counter. the second channel is not used. - * - * - The third channel may be used to provide a 16-bit clockevent - * source, used in either periodic or oneshot mode. This runs - * at 32 KiHZ, and can handle delays of up to two seconds. - * - * REVISIT behavior during system suspend states... we should disable - * all clocks and save the power. Easily done for clockevent devices, - * but clocksources won't necessarily get the needed notifications. - * For deeper system sleep states, this will be mandatory... - */ - -static void __iomem *tcaddr; -static struct -{ - u32 cmr; - u32 imr; - u32 rc; - bool clken; -} tcb_cache[3]; -static u32 bmr_cache; - -static u64 tc_get_cycles(struct clocksource *cs) -{ - unsigned long flags; - u32 lower, upper; - - raw_local_irq_save(flags); - do { - upper = readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV)); - lower = readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); - } while (upper != readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV))); - - raw_local_irq_restore(flags); - return (upper << 16) | lower; -} - -static u64 tc_get_cycles32(struct clocksource *cs) -{ - return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); -} - -void tc_clksrc_suspend(struct clocksource *cs) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { - tcb_cache[i].cmr = readl(tcaddr + ATMEL_TC_REG(i, CMR)); - tcb_cache[i].imr = readl(tcaddr + ATMEL_TC_REG(i, IMR)); - tcb_cache[i].rc = readl(tcaddr + ATMEL_TC_REG(i, RC)); - tcb_cache[i].clken = !!(readl(tcaddr + ATMEL_TC_REG(i, SR)) & - ATMEL_TC_CLKSTA); - } - - bmr_cache = readl(tcaddr + ATMEL_TC_BMR); -} - -void tc_clksrc_resume(struct clocksource *cs) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { - /* Restore registers for the channel, RA and RB are not used */ - writel(tcb_cache[i].cmr, tcaddr + ATMEL_TC_REG(i, CMR)); - writel(tcb_cache[i].rc, tcaddr + ATMEL_TC_REG(i, RC)); - writel(0, tcaddr + ATMEL_TC_REG(i, RA)); - writel(0, tcaddr + ATMEL_TC_REG(i, RB)); - /* Disable all the interrupts */ - writel(0xff, tcaddr + ATMEL_TC_REG(i, IDR)); - /* Reenable interrupts that were enabled before suspending */ - writel(tcb_cache[i].imr, tcaddr + ATMEL_TC_REG(i, IER)); - /* Start the clock if it was used */ - if (tcb_cache[i].clken) - writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(i, CCR)); - } - - /* Dual channel, chain channels */ - writel(bmr_cache, tcaddr + ATMEL_TC_BMR); - /* Finally, trigger all the channels*/ - writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); -} - -static struct clocksource clksrc = { - .rating = 200, - .read = tc_get_cycles, - .mask = CLOCKSOURCE_MASK(32), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, - .suspend = tc_clksrc_suspend, - .resume = tc_clksrc_resume, -}; - -static u64 notrace tc_sched_clock_read(void) -{ - return tc_get_cycles(&clksrc); -} - -static u64 notrace tc_sched_clock_read32(void) -{ - return tc_get_cycles32(&clksrc); -} - -#ifdef CONFIG_GENERIC_CLOCKEVENTS - -struct tc_clkevt_device { - struct clock_event_device clkevt; - struct clk *clk; - void __iomem *regs; -}; - -static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt) -{ - return container_of(clkevt, struct tc_clkevt_device, clkevt); -} - -/* For now, we always use the 32K clock ... this optimizes for NO_HZ, - * because using one of the divided clocks would usually mean the - * tick rate can never be less than several dozen Hz (vs 0.5 Hz). - * - * A divided clock could be good for high resolution timers, since - * 30.5 usec resolution can seem "low". - */ -static u32 timer_clock; - -static int tc_shutdown(struct clock_event_device *d) -{ - struct tc_clkevt_device *tcd = to_tc_clkevt(d); - void __iomem *regs = tcd->regs; - - writel(0xff, regs + ATMEL_TC_REG(2, IDR)); - writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR)); - if (!clockevent_state_detached(d)) - clk_disable(tcd->clk); - - return 0; -} - -static int tc_set_oneshot(struct clock_event_device *d) -{ - struct tc_clkevt_device *tcd = to_tc_clkevt(d); - void __iomem *regs = tcd->regs; - - if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) - tc_shutdown(d); - - clk_enable(tcd->clk); - - /* slow clock, count up to RC, then irq and stop */ - writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE | - ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR)); - writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); - - /* set_next_event() configures and starts the timer */ - return 0; -} - -static int tc_set_periodic(struct clock_event_device *d) -{ - struct tc_clkevt_device *tcd = to_tc_clkevt(d); - void __iomem *regs = tcd->regs; - - if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) - tc_shutdown(d); - - /* By not making the gentime core emulate periodic mode on top - * of oneshot, we get lower overhead and improved accuracy. - */ - clk_enable(tcd->clk); - - /* slow clock, count up to RC, then irq and restart */ - writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, - regs + ATMEL_TC_REG(2, CMR)); - writel((32768 + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); - - /* Enable clock and interrupts on RC compare */ - writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); - - /* go go gadget! */ - writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs + - ATMEL_TC_REG(2, CCR)); - return 0; -} - -static int tc_next_event(unsigned long delta, struct clock_event_device *d) -{ - writel_relaxed(delta, tcaddr + ATMEL_TC_REG(2, RC)); - - /* go go gadget! */ - writel_relaxed(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, - tcaddr + ATMEL_TC_REG(2, CCR)); - return 0; -} - -static struct tc_clkevt_device clkevt = { - .clkevt = { - .features = CLOCK_EVT_FEAT_PERIODIC | - CLOCK_EVT_FEAT_ONESHOT, - /* Should be lower than at91rm9200's system timer */ - .rating = 125, - .set_next_event = tc_next_event, - .set_state_shutdown = tc_shutdown, - .set_state_periodic = tc_set_periodic, - .set_state_oneshot = tc_set_oneshot, - }, -}; - -static irqreturn_t ch2_irq(int irq, void *handle) -{ - struct tc_clkevt_device *dev = handle; - unsigned int sr; - - sr = readl_relaxed(dev->regs + ATMEL_TC_REG(2, SR)); - if (sr & ATMEL_TC_CPCS) { - dev->clkevt.event_handler(&dev->clkevt); - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) -{ - int ret; - struct clk *t2_clk = tc->clk[2]; - int irq = tc->irq[2]; - - ret = clk_prepare_enable(tc->slow_clk); - if (ret) - return ret; - - /* try to enable t2 clk to avoid future errors in mode change */ - ret = clk_prepare_enable(t2_clk); - if (ret) { - clk_disable_unprepare(tc->slow_clk); - return ret; - } - - clk_disable(t2_clk); - - clkevt.regs = tc->regs; - clkevt.clk = t2_clk; - - timer_clock = clk32k_divisor_idx; - - clkevt.clkevt.cpumask = cpumask_of(0); - - ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt); - if (ret) { - clk_unprepare(t2_clk); - clk_disable_unprepare(tc->slow_clk); - return ret; - } - - clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); - - return ret; -} - -#else /* !CONFIG_GENERIC_CLOCKEVENTS */ - -static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) -{ - /* NOTHING */ - return 0; -} - -#endif - -static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) -{ - /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ - writel(mck_divisor_idx /* likely divide-by-8 */ - | ATMEL_TC_WAVE - | ATMEL_TC_WAVESEL_UP /* free-run */ - | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ - | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ - tcaddr + ATMEL_TC_REG(0, CMR)); - writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); - writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); - writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ - writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); - - /* channel 1: waveform mode, input TIOA0 */ - writel(ATMEL_TC_XC1 /* input: TIOA0 */ - | ATMEL_TC_WAVE - | ATMEL_TC_WAVESEL_UP, /* free-run */ - tcaddr + ATMEL_TC_REG(1, CMR)); - writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ - writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); - - /* chain channel 0 to channel 1*/ - writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); - /* then reset all the timers */ - writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); -} - -static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx) -{ - /* channel 0: waveform mode, input mclk/8 */ - writel(mck_divisor_idx /* likely divide-by-8 */ - | ATMEL_TC_WAVE - | ATMEL_TC_WAVESEL_UP, /* free-run */ - tcaddr + ATMEL_TC_REG(0, CMR)); - writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ - writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); - - /* then reset all the timers */ - writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); -} - -static const u8 atmel_tcb_divisors[5] = { 2, 8, 32, 128, 0, }; - -static const struct of_device_id atmel_tcb_of_match[] = { - { .compatible = "atmel,at91rm9200-tcb", .data = (void *)16, }, - { .compatible = "atmel,at91sam9x5-tcb", .data = (void *)32, }, - { /* sentinel */ } -}; - -static int __init tcb_clksrc_init(struct device_node *node) -{ - struct atmel_tc tc; - struct clk *t0_clk; - const struct of_device_id *match; - u64 (*tc_sched_clock)(void); - u32 rate, divided_rate = 0; - int best_divisor_idx = -1; - int clk32k_divisor_idx = -1; - int bits; - int i; - int ret; - - /* Protect against multiple calls */ - if (tcaddr) - return 0; - - tc.regs = of_iomap(node->parent, 0); - if (!tc.regs) - return -ENXIO; - - t0_clk = of_clk_get_by_name(node->parent, "t0_clk"); - if (IS_ERR(t0_clk)) - return PTR_ERR(t0_clk); - - tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk"); - if (IS_ERR(tc.slow_clk)) - return PTR_ERR(tc.slow_clk); - - tc.clk[0] = t0_clk; - tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk"); - if (IS_ERR(tc.clk[1])) - tc.clk[1] = t0_clk; - tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk"); - if (IS_ERR(tc.clk[2])) - tc.clk[2] = t0_clk; - - tc.irq[2] = of_irq_get(node->parent, 2); - if (tc.irq[2] <= 0) { - tc.irq[2] = of_irq_get(node->parent, 0); - if (tc.irq[2] <= 0) - return -EINVAL; - } - - match = of_match_node(atmel_tcb_of_match, node->parent); - bits = (uintptr_t)match->data; - - for (i = 0; i < ARRAY_SIZE(tc.irq); i++) - writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR)); - - ret = clk_prepare_enable(t0_clk); - if (ret) { - pr_debug("can't enable T0 clk\n"); - return ret; - } - - /* How fast will we be counting? Pick something over 5 MHz. */ - rate = (u32) clk_get_rate(t0_clk); - for (i = 0; i < ARRAY_SIZE(atmel_tcb_divisors); i++) { - unsigned divisor = atmel_tcb_divisors[i]; - unsigned tmp; - - /* remember 32 KiHz clock for later */ - if (!divisor) { - clk32k_divisor_idx = i; - continue; - } - - tmp = rate / divisor; - pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp); - if (best_divisor_idx > 0) { - if (tmp < 5 * 1000 * 1000) - continue; - } - divided_rate = tmp; - best_divisor_idx = i; - } - - clksrc.name = kbasename(node->parent->full_name); - clkevt.clkevt.name = kbasename(node->parent->full_name); - pr_debug("%s at %d.%03d MHz\n", clksrc.name, divided_rate / 1000000, - ((divided_rate % 1000000) + 500) / 1000); - - tcaddr = tc.regs; - - if (bits == 32) { - /* use apropriate function to read 32 bit counter */ - clksrc.read = tc_get_cycles32; - /* setup ony channel 0 */ - tcb_setup_single_chan(&tc, best_divisor_idx); - tc_sched_clock = tc_sched_clock_read32; - } else { - /* we have three clocks no matter what the - * underlying platform supports. - */ - ret = clk_prepare_enable(tc.clk[1]); - if (ret) { - pr_debug("can't enable T1 clk\n"); - goto err_disable_t0; - } - /* setup both channel 0 & 1 */ - tcb_setup_dual_chan(&tc, best_divisor_idx); - tc_sched_clock = tc_sched_clock_read; - } - - /* and away we go! */ - ret = clocksource_register_hz(&clksrc, divided_rate); - if (ret) - goto err_disable_t1; - - /* channel 2: periodic and oneshot timer support */ - ret = setup_clkevents(&tc, clk32k_divisor_idx); - if (ret) - goto err_unregister_clksrc; - - sched_clock_register(tc_sched_clock, 32, divided_rate); - - return 0; - -err_unregister_clksrc: - clocksource_unregister(&clksrc); - -err_disable_t1: - if (bits != 32) - clk_disable_unprepare(tc.clk[1]); - -err_disable_t0: - clk_disable_unprepare(t0_clk); - - tcaddr = NULL; - - return ret; -} -TIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init); diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c new file mode 100644 index 000000000000..9de8c10ab546 --- /dev/null +++ b/drivers/clocksource/timer-atmel-tcb.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * We're configured to use a specific TC block, one that's not hooked + * up to external hardware, to provide a time solution: + * + * - Two channels combine to create a free-running 32 bit counter + * with a base rate of 5+ MHz, packaged as a clocksource (with + * resolution better than 200 nsec). + * - Some chips support 32 bit counter. A single channel is used for + * this 32 bit free-running counter. the second channel is not used. + * + * - The third channel may be used to provide a 16-bit clockevent + * source, used in either periodic or oneshot mode. This runs + * at 32 KiHZ, and can handle delays of up to two seconds. + * + * REVISIT behavior during system suspend states... we should disable + * all clocks and save the power. Easily done for clockevent devices, + * but clocksources won't necessarily get the needed notifications. + * For deeper system sleep states, this will be mandatory... + */ + +static void __iomem *tcaddr; +static struct +{ + u32 cmr; + u32 imr; + u32 rc; + bool clken; +} tcb_cache[3]; +static u32 bmr_cache; + +static u64 tc_get_cycles(struct clocksource *cs) +{ + unsigned long flags; + u32 lower, upper; + + raw_local_irq_save(flags); + do { + upper = readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV)); + lower = readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); + } while (upper != readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV))); + + raw_local_irq_restore(flags); + return (upper << 16) | lower; +} + +static u64 tc_get_cycles32(struct clocksource *cs) +{ + return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); +} + +void tc_clksrc_suspend(struct clocksource *cs) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { + tcb_cache[i].cmr = readl(tcaddr + ATMEL_TC_REG(i, CMR)); + tcb_cache[i].imr = readl(tcaddr + ATMEL_TC_REG(i, IMR)); + tcb_cache[i].rc = readl(tcaddr + ATMEL_TC_REG(i, RC)); + tcb_cache[i].clken = !!(readl(tcaddr + ATMEL_TC_REG(i, SR)) & + ATMEL_TC_CLKSTA); + } + + bmr_cache = readl(tcaddr + ATMEL_TC_BMR); +} + +void tc_clksrc_resume(struct clocksource *cs) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { + /* Restore registers for the channel, RA and RB are not used */ + writel(tcb_cache[i].cmr, tcaddr + ATMEL_TC_REG(i, CMR)); + writel(tcb_cache[i].rc, tcaddr + ATMEL_TC_REG(i, RC)); + writel(0, tcaddr + ATMEL_TC_REG(i, RA)); + writel(0, tcaddr + ATMEL_TC_REG(i, RB)); + /* Disable all the interrupts */ + writel(0xff, tcaddr + ATMEL_TC_REG(i, IDR)); + /* Reenable interrupts that were enabled before suspending */ + writel(tcb_cache[i].imr, tcaddr + ATMEL_TC_REG(i, IER)); + /* Start the clock if it was used */ + if (tcb_cache[i].clken) + writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(i, CCR)); + } + + /* Dual channel, chain channels */ + writel(bmr_cache, tcaddr + ATMEL_TC_BMR); + /* Finally, trigger all the channels*/ + writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); +} + +static struct clocksource clksrc = { + .rating = 200, + .read = tc_get_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .suspend = tc_clksrc_suspend, + .resume = tc_clksrc_resume, +}; + +static u64 notrace tc_sched_clock_read(void) +{ + return tc_get_cycles(&clksrc); +} + +static u64 notrace tc_sched_clock_read32(void) +{ + return tc_get_cycles32(&clksrc); +} + +#ifdef CONFIG_GENERIC_CLOCKEVENTS + +struct tc_clkevt_device { + struct clock_event_device clkevt; + struct clk *clk; + void __iomem *regs; +}; + +static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt) +{ + return container_of(clkevt, struct tc_clkevt_device, clkevt); +} + +/* For now, we always use the 32K clock ... this optimizes for NO_HZ, + * because using one of the divided clocks would usually mean the + * tick rate can never be less than several dozen Hz (vs 0.5 Hz). + * + * A divided clock could be good for high resolution timers, since + * 30.5 usec resolution can seem "low". + */ +static u32 timer_clock; + +static int tc_shutdown(struct clock_event_device *d) +{ + struct tc_clkevt_device *tcd = to_tc_clkevt(d); + void __iomem *regs = tcd->regs; + + writel(0xff, regs + ATMEL_TC_REG(2, IDR)); + writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR)); + if (!clockevent_state_detached(d)) + clk_disable(tcd->clk); + + return 0; +} + +static int tc_set_oneshot(struct clock_event_device *d) +{ + struct tc_clkevt_device *tcd = to_tc_clkevt(d); + void __iomem *regs = tcd->regs; + + if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) + tc_shutdown(d); + + clk_enable(tcd->clk); + + /* slow clock, count up to RC, then irq and stop */ + writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE | + ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR)); + writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); + + /* set_next_event() configures and starts the timer */ + return 0; +} + +static int tc_set_periodic(struct clock_event_device *d) +{ + struct tc_clkevt_device *tcd = to_tc_clkevt(d); + void __iomem *regs = tcd->regs; + + if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) + tc_shutdown(d); + + /* By not making the gentime core emulate periodic mode on top + * of oneshot, we get lower overhead and improved accuracy. + */ + clk_enable(tcd->clk); + + /* slow clock, count up to RC, then irq and restart */ + writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, + regs + ATMEL_TC_REG(2, CMR)); + writel((32768 + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); + + /* Enable clock and interrupts on RC compare */ + writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); + + /* go go gadget! */ + writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs + + ATMEL_TC_REG(2, CCR)); + return 0; +} + +static int tc_next_event(unsigned long delta, struct clock_event_device *d) +{ + writel_relaxed(delta, tcaddr + ATMEL_TC_REG(2, RC)); + + /* go go gadget! */ + writel_relaxed(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, + tcaddr + ATMEL_TC_REG(2, CCR)); + return 0; +} + +static struct tc_clkevt_device clkevt = { + .clkevt = { + .features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + /* Should be lower than at91rm9200's system timer */ + .rating = 125, + .set_next_event = tc_next_event, + .set_state_shutdown = tc_shutdown, + .set_state_periodic = tc_set_periodic, + .set_state_oneshot = tc_set_oneshot, + }, +}; + +static irqreturn_t ch2_irq(int irq, void *handle) +{ + struct tc_clkevt_device *dev = handle; + unsigned int sr; + + sr = readl_relaxed(dev->regs + ATMEL_TC_REG(2, SR)); + if (sr & ATMEL_TC_CPCS) { + dev->clkevt.event_handler(&dev->clkevt); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) +{ + int ret; + struct clk *t2_clk = tc->clk[2]; + int irq = tc->irq[2]; + + ret = clk_prepare_enable(tc->slow_clk); + if (ret) + return ret; + + /* try to enable t2 clk to avoid future errors in mode change */ + ret = clk_prepare_enable(t2_clk); + if (ret) { + clk_disable_unprepare(tc->slow_clk); + return ret; + } + + clk_disable(t2_clk); + + clkevt.regs = tc->regs; + clkevt.clk = t2_clk; + + timer_clock = clk32k_divisor_idx; + + clkevt.clkevt.cpumask = cpumask_of(0); + + ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt); + if (ret) { + clk_unprepare(t2_clk); + clk_disable_unprepare(tc->slow_clk); + return ret; + } + + clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); + + return ret; +} + +#else /* !CONFIG_GENERIC_CLOCKEVENTS */ + +static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) +{ + /* NOTHING */ + return 0; +} + +#endif + +static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) +{ + /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ + writel(mck_divisor_idx /* likely divide-by-8 */ + | ATMEL_TC_WAVE + | ATMEL_TC_WAVESEL_UP /* free-run */ + | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ + | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ + tcaddr + ATMEL_TC_REG(0, CMR)); + writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); + writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); + writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ + writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); + + /* channel 1: waveform mode, input TIOA0 */ + writel(ATMEL_TC_XC1 /* input: TIOA0 */ + | ATMEL_TC_WAVE + | ATMEL_TC_WAVESEL_UP, /* free-run */ + tcaddr + ATMEL_TC_REG(1, CMR)); + writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ + writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); + + /* chain channel 0 to channel 1*/ + writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); + /* then reset all the timers */ + writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); +} + +static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx) +{ + /* channel 0: waveform mode, input mclk/8 */ + writel(mck_divisor_idx /* likely divide-by-8 */ + | ATMEL_TC_WAVE + | ATMEL_TC_WAVESEL_UP, /* free-run */ + tcaddr + ATMEL_TC_REG(0, CMR)); + writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ + writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); + + /* then reset all the timers */ + writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); +} + +static const u8 atmel_tcb_divisors[5] = { 2, 8, 32, 128, 0, }; + +static const struct of_device_id atmel_tcb_of_match[] = { + { .compatible = "atmel,at91rm9200-tcb", .data = (void *)16, }, + { .compatible = "atmel,at91sam9x5-tcb", .data = (void *)32, }, + { /* sentinel */ } +}; + +static int __init tcb_clksrc_init(struct device_node *node) +{ + struct atmel_tc tc; + struct clk *t0_clk; + const struct of_device_id *match; + u64 (*tc_sched_clock)(void); + u32 rate, divided_rate = 0; + int best_divisor_idx = -1; + int clk32k_divisor_idx = -1; + int bits; + int i; + int ret; + + /* Protect against multiple calls */ + if (tcaddr) + return 0; + + tc.regs = of_iomap(node->parent, 0); + if (!tc.regs) + return -ENXIO; + + t0_clk = of_clk_get_by_name(node->parent, "t0_clk"); + if (IS_ERR(t0_clk)) + return PTR_ERR(t0_clk); + + tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk"); + if (IS_ERR(tc.slow_clk)) + return PTR_ERR(tc.slow_clk); + + tc.clk[0] = t0_clk; + tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk"); + if (IS_ERR(tc.clk[1])) + tc.clk[1] = t0_clk; + tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk"); + if (IS_ERR(tc.clk[2])) + tc.clk[2] = t0_clk; + + tc.irq[2] = of_irq_get(node->parent, 2); + if (tc.irq[2] <= 0) { + tc.irq[2] = of_irq_get(node->parent, 0); + if (tc.irq[2] <= 0) + return -EINVAL; + } + + match = of_match_node(atmel_tcb_of_match, node->parent); + bits = (uintptr_t)match->data; + + for (i = 0; i < ARRAY_SIZE(tc.irq); i++) + writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR)); + + ret = clk_prepare_enable(t0_clk); + if (ret) { + pr_debug("can't enable T0 clk\n"); + return ret; + } + + /* How fast will we be counting? Pick something over 5 MHz. */ + rate = (u32) clk_get_rate(t0_clk); + for (i = 0; i < ARRAY_SIZE(atmel_tcb_divisors); i++) { + unsigned divisor = atmel_tcb_divisors[i]; + unsigned tmp; + + /* remember 32 KiHz clock for later */ + if (!divisor) { + clk32k_divisor_idx = i; + continue; + } + + tmp = rate / divisor; + pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp); + if (best_divisor_idx > 0) { + if (tmp < 5 * 1000 * 1000) + continue; + } + divided_rate = tmp; + best_divisor_idx = i; + } + + clksrc.name = kbasename(node->parent->full_name); + clkevt.clkevt.name = kbasename(node->parent->full_name); + pr_debug("%s at %d.%03d MHz\n", clksrc.name, divided_rate / 1000000, + ((divided_rate % 1000000) + 500) / 1000); + + tcaddr = tc.regs; + + if (bits == 32) { + /* use apropriate function to read 32 bit counter */ + clksrc.read = tc_get_cycles32; + /* setup ony channel 0 */ + tcb_setup_single_chan(&tc, best_divisor_idx); + tc_sched_clock = tc_sched_clock_read32; + } else { + /* we have three clocks no matter what the + * underlying platform supports. + */ + ret = clk_prepare_enable(tc.clk[1]); + if (ret) { + pr_debug("can't enable T1 clk\n"); + goto err_disable_t0; + } + /* setup both channel 0 & 1 */ + tcb_setup_dual_chan(&tc, best_divisor_idx); + tc_sched_clock = tc_sched_clock_read; + } + + /* and away we go! */ + ret = clocksource_register_hz(&clksrc, divided_rate); + if (ret) + goto err_disable_t1; + + /* channel 2: periodic and oneshot timer support */ + ret = setup_clkevents(&tc, clk32k_divisor_idx); + if (ret) + goto err_unregister_clksrc; + + sched_clock_register(tc_sched_clock, 32, divided_rate); + + return 0; + +err_unregister_clksrc: + clocksource_unregister(&clksrc); + +err_disable_t1: + if (bits != 32) + clk_disable_unprepare(tc.clk[1]); + +err_disable_t0: + clk_disable_unprepare(t0_clk); + + tcaddr = NULL; + + return ret; +} +TIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init); -- cgit v1.2.3 From 7ebe68100418bd196cd4d638655e60d17c412d56 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 26 Apr 2019 23:47:17 +0200 Subject: clocksource/drivers/timer-atmel-tcb: Convert tc_clksrc_suspend|resume() to static Statisticize tc_clksrc_suspend and tc_clksrc_resume. Signed-off-by: kbuild test robot Signed-off-by: Alexandre Belloni Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-atmel-tcb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c index 9de8c10ab546..6ed31f9def7e 100644 --- a/drivers/clocksource/timer-atmel-tcb.c +++ b/drivers/clocksource/timer-atmel-tcb.c @@ -66,7 +66,7 @@ static u64 tc_get_cycles32(struct clocksource *cs) return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); } -void tc_clksrc_suspend(struct clocksource *cs) +static void tc_clksrc_suspend(struct clocksource *cs) { int i; @@ -81,7 +81,7 @@ void tc_clksrc_suspend(struct clocksource *cs) bmr_cache = readl(tcaddr + ATMEL_TC_BMR); } -void tc_clksrc_resume(struct clocksource *cs) +static void tc_clksrc_resume(struct clocksource *cs) { int i; -- cgit v1.2.3