From 67d06e6efa41c4135cfc4c4c5c9b55d5e3a9b6f7 Mon Sep 17 00:00:00 2001 From: Mesih Kilinc Date: Mon, 11 Feb 2019 12:21:07 +0300 Subject: dt-bindings: timer: Add Allwinner suniv timer Add compatible string for Allwinner suniv timer which is similar to sun4i timer. Signed-off-by: Mesih Kilinc Acked-by: Maxime Ripard Reviewed-by: Rob Herring Signed-off-by: Daniel Lezcano --- Documentation/devicetree/bindings/timer/allwinner,sun4i-timer.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/timer/allwinner,sun4i-timer.txt b/Documentation/devicetree/bindings/timer/allwinner,sun4i-timer.txt index 5c2e23574ca0..3da9d515c03a 100644 --- a/Documentation/devicetree/bindings/timer/allwinner,sun4i-timer.txt +++ b/Documentation/devicetree/bindings/timer/allwinner,sun4i-timer.txt @@ -2,7 +2,9 @@ Allwinner A1X SoCs Timer Controller Required properties: -- compatible : should be "allwinner,sun4i-a10-timer" +- compatible : should be one of the following: + "allwinner,sun4i-a10-timer" + "allwinner,suniv-f1c100s-timer" - reg : Specifies base physical address and size of the registers. - interrupts : The interrupt of the first timer - clocks: phandle to the source clock (usually a 24 MHz fixed clock) -- cgit v1.2.3-59-g8ed1b 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(-) 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-59-g8ed1b 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(-) 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-59-g8ed1b 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(-) 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-59-g8ed1b 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(+) 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-59-g8ed1b 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(+) 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-59-g8ed1b 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(-) 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-59-g8ed1b 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 +- drivers/misc/atmel_tclib.c | 2 +- drivers/pwm/pwm-atmel-tcb.c | 2 +- include/linux/atmel_tc.h | 270 --------------------------------------- include/soc/at91/atmel_tcb.h | 270 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 273 insertions(+), 273 deletions(-) delete mode 100644 include/linux/atmel_tc.h create mode 100644 include/soc/at91/atmel_tcb.h 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 /* diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index ac24a4bd63f7..194f774ab3a1 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -10,6 +9,7 @@ #include #include #include +#include /* * This is a thin library to solve the problem of how to portably allocate diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 0d0f8376bc35..7da1fdb4d269 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -17,10 +17,10 @@ #include #include #include -#include #include #include #include +#include #define NPWM 6 diff --git a/include/linux/atmel_tc.h b/include/linux/atmel_tc.h deleted file mode 100644 index 468fdfa643f0..000000000000 --- a/include/linux/atmel_tc.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Timer/Counter Unit (TC) registers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef ATMEL_TC_H -#define ATMEL_TC_H - -#include -#include - -/* - * Many 32-bit Atmel SOCs include one or more TC blocks, each of which holds - * three general-purpose 16-bit timers. These timers share one register bank. - * Depending on the SOC, each timer may have its own clock and IRQ, or those - * may be shared by the whole TC block. - * - * These TC blocks may have up to nine external pins: TCLK0..2 signals for - * clocks or clock gates, and per-timer TIOA and TIOB signals used for PWM - * or triggering. Those pins need to be set up for use with the TC block, - * else they will be used as GPIOs or for a different controller. - * - * Although we expect each TC block to have a platform_device node, those - * nodes are not what drivers bind to. Instead, they ask for a specific - * TC block, by number ... which is a common approach on systems with many - * timers. Then they use clk_get() and platform_get_irq() to get clock and - * IRQ resources. - */ - -struct clk; - -/** - * struct atmel_tcb_config - SoC data for a Timer/Counter Block - * @counter_width: size in bits of a timer counter register - */ -struct atmel_tcb_config { - size_t counter_width; -}; - -/** - * struct atmel_tc - information about a Timer/Counter Block - * @pdev: physical device - * @regs: mapping through which the I/O registers can be accessed - * @id: block id - * @tcb_config: configuration data from SoC - * @irq: irq for each of the three channels - * @clk: internal clock source for each of the three channels - * @node: list node, for tclib internal use - * @allocated: if already used, for tclib internal use - * - * On some platforms, each TC channel has its own clocks and IRQs, - * while on others, all TC channels share the same clock and IRQ. - * Drivers should clk_enable() all the clocks they need even though - * all the entries in @clk may point to the same physical clock. - * Likewise, drivers should request irqs independently for each - * channel, but they must use IRQF_SHARED in case some of the entries - * in @irq are actually the same IRQ. - */ -struct atmel_tc { - struct platform_device *pdev; - void __iomem *regs; - int id; - const struct atmel_tcb_config *tcb_config; - int irq[3]; - struct clk *clk[3]; - struct clk *slow_clk; - struct list_head node; - bool allocated; -}; - -extern struct atmel_tc *atmel_tc_alloc(unsigned block); -extern void atmel_tc_free(struct atmel_tc *tc); - -/* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */ -extern const u8 atmel_tc_divisors[5]; - - -/* - * Two registers have block-wide controls. These are: configuring the three - * "external" clocks (or event sources) used by the timer channels; and - * synchronizing the timers by resetting them all at once. - * - * "External" can mean "external to chip" using the TCLK0, TCLK1, or TCLK2 - * signals. Or, it can mean "external to timer", using the TIOA output from - * one of the other two timers that's being run in waveform mode. - */ - -#define ATMEL_TC_BCR 0xc0 /* TC Block Control Register */ -#define ATMEL_TC_SYNC (1 << 0) /* synchronize timers */ - -#define ATMEL_TC_BMR 0xc4 /* TC Block Mode Register */ -#define ATMEL_TC_TC0XC0S (3 << 0) /* external clock 0 source */ -#define ATMEL_TC_TC0XC0S_TCLK0 (0 << 0) -#define ATMEL_TC_TC0XC0S_NONE (1 << 0) -#define ATMEL_TC_TC0XC0S_TIOA1 (2 << 0) -#define ATMEL_TC_TC0XC0S_TIOA2 (3 << 0) -#define ATMEL_TC_TC1XC1S (3 << 2) /* external clock 1 source */ -#define ATMEL_TC_TC1XC1S_TCLK1 (0 << 2) -#define ATMEL_TC_TC1XC1S_NONE (1 << 2) -#define ATMEL_TC_TC1XC1S_TIOA0 (2 << 2) -#define ATMEL_TC_TC1XC1S_TIOA2 (3 << 2) -#define ATMEL_TC_TC2XC2S (3 << 4) /* external clock 2 source */ -#define ATMEL_TC_TC2XC2S_TCLK2 (0 << 4) -#define ATMEL_TC_TC2XC2S_NONE (1 << 4) -#define ATMEL_TC_TC2XC2S_TIOA0 (2 << 4) -#define ATMEL_TC_TC2XC2S_TIOA1 (3 << 4) - - -/* - * Each TC block has three "channels", each with one counter and controls. - * - * Note that the semantics of ATMEL_TC_TIMER_CLOCKx (input clock selection - * when it's not "external") is silicon-specific. AT91 platforms use one - * set of definitions; AVR32 platforms use a different set. Don't hard-wire - * such knowledge into your code, use the global "atmel_tc_divisors" ... - * where index N is the divisor for clock N+1, else zero to indicate it uses - * the 32 KiHz clock. - * - * The timers can be chained in various ways, and operated in "waveform" - * generation mode (including PWM) or "capture" mode (to time events). In - * both modes, behavior can be configured in many ways. - * - * Each timer has two I/O pins, TIOA and TIOB. Waveform mode uses TIOA as a - * PWM output, and TIOB as either another PWM or as a trigger. Capture mode - * uses them only as inputs. - */ -#define ATMEL_TC_CHAN(idx) ((idx)*0x40) -#define ATMEL_TC_REG(idx, reg) (ATMEL_TC_CHAN(idx) + ATMEL_TC_ ## reg) - -#define ATMEL_TC_CCR 0x00 /* Channel Control Register */ -#define ATMEL_TC_CLKEN (1 << 0) /* clock enable */ -#define ATMEL_TC_CLKDIS (1 << 1) /* clock disable */ -#define ATMEL_TC_SWTRG (1 << 2) /* software trigger */ - -#define ATMEL_TC_CMR 0x04 /* Channel Mode Register */ - -/* Both modes share some CMR bits */ -#define ATMEL_TC_TCCLKS (7 << 0) /* clock source */ -#define ATMEL_TC_TIMER_CLOCK1 (0 << 0) -#define ATMEL_TC_TIMER_CLOCK2 (1 << 0) -#define ATMEL_TC_TIMER_CLOCK3 (2 << 0) -#define ATMEL_TC_TIMER_CLOCK4 (3 << 0) -#define ATMEL_TC_TIMER_CLOCK5 (4 << 0) -#define ATMEL_TC_XC0 (5 << 0) -#define ATMEL_TC_XC1 (6 << 0) -#define ATMEL_TC_XC2 (7 << 0) -#define ATMEL_TC_CLKI (1 << 3) /* clock invert */ -#define ATMEL_TC_BURST (3 << 4) /* clock gating */ -#define ATMEL_TC_GATE_NONE (0 << 4) -#define ATMEL_TC_GATE_XC0 (1 << 4) -#define ATMEL_TC_GATE_XC1 (2 << 4) -#define ATMEL_TC_GATE_XC2 (3 << 4) -#define ATMEL_TC_WAVE (1 << 15) /* true = Waveform mode */ - -/* CAPTURE mode CMR bits */ -#define ATMEL_TC_LDBSTOP (1 << 6) /* counter stops on RB load */ -#define ATMEL_TC_LDBDIS (1 << 7) /* counter disable on RB load */ -#define ATMEL_TC_ETRGEDG (3 << 8) /* external trigger edge */ -#define ATMEL_TC_ETRGEDG_NONE (0 << 8) -#define ATMEL_TC_ETRGEDG_RISING (1 << 8) -#define ATMEL_TC_ETRGEDG_FALLING (2 << 8) -#define ATMEL_TC_ETRGEDG_BOTH (3 << 8) -#define ATMEL_TC_ABETRG (1 << 10) /* external trigger is TIOA? */ -#define ATMEL_TC_CPCTRG (1 << 14) /* RC compare trigger enable */ -#define ATMEL_TC_LDRA (3 << 16) /* RA loading edge (of TIOA) */ -#define ATMEL_TC_LDRA_NONE (0 << 16) -#define ATMEL_TC_LDRA_RISING (1 << 16) -#define ATMEL_TC_LDRA_FALLING (2 << 16) -#define ATMEL_TC_LDRA_BOTH (3 << 16) -#define ATMEL_TC_LDRB (3 << 18) /* RB loading edge (of TIOA) */ -#define ATMEL_TC_LDRB_NONE (0 << 18) -#define ATMEL_TC_LDRB_RISING (1 << 18) -#define ATMEL_TC_LDRB_FALLING (2 << 18) -#define ATMEL_TC_LDRB_BOTH (3 << 18) - -/* WAVEFORM mode CMR bits */ -#define ATMEL_TC_CPCSTOP (1 << 6) /* RC compare stops counter */ -#define ATMEL_TC_CPCDIS (1 << 7) /* RC compare disables counter */ -#define ATMEL_TC_EEVTEDG (3 << 8) /* external event edge */ -#define ATMEL_TC_EEVTEDG_NONE (0 << 8) -#define ATMEL_TC_EEVTEDG_RISING (1 << 8) -#define ATMEL_TC_EEVTEDG_FALLING (2 << 8) -#define ATMEL_TC_EEVTEDG_BOTH (3 << 8) -#define ATMEL_TC_EEVT (3 << 10) /* external event source */ -#define ATMEL_TC_EEVT_TIOB (0 << 10) -#define ATMEL_TC_EEVT_XC0 (1 << 10) -#define ATMEL_TC_EEVT_XC1 (2 << 10) -#define ATMEL_TC_EEVT_XC2 (3 << 10) -#define ATMEL_TC_ENETRG (1 << 12) /* external event is trigger */ -#define ATMEL_TC_WAVESEL (3 << 13) /* waveform type */ -#define ATMEL_TC_WAVESEL_UP (0 << 13) -#define ATMEL_TC_WAVESEL_UPDOWN (1 << 13) -#define ATMEL_TC_WAVESEL_UP_AUTO (2 << 13) -#define ATMEL_TC_WAVESEL_UPDOWN_AUTO (3 << 13) -#define ATMEL_TC_ACPA (3 << 16) /* RA compare changes TIOA */ -#define ATMEL_TC_ACPA_NONE (0 << 16) -#define ATMEL_TC_ACPA_SET (1 << 16) -#define ATMEL_TC_ACPA_CLEAR (2 << 16) -#define ATMEL_TC_ACPA_TOGGLE (3 << 16) -#define ATMEL_TC_ACPC (3 << 18) /* RC compare changes TIOA */ -#define ATMEL_TC_ACPC_NONE (0 << 18) -#define ATMEL_TC_ACPC_SET (1 << 18) -#define ATMEL_TC_ACPC_CLEAR (2 << 18) -#define ATMEL_TC_ACPC_TOGGLE (3 << 18) -#define ATMEL_TC_AEEVT (3 << 20) /* external event changes TIOA */ -#define ATMEL_TC_AEEVT_NONE (0 << 20) -#define ATMEL_TC_AEEVT_SET (1 << 20) -#define ATMEL_TC_AEEVT_CLEAR (2 << 20) -#define ATMEL_TC_AEEVT_TOGGLE (3 << 20) -#define ATMEL_TC_ASWTRG (3 << 22) /* software trigger changes TIOA */ -#define ATMEL_TC_ASWTRG_NONE (0 << 22) -#define ATMEL_TC_ASWTRG_SET (1 << 22) -#define ATMEL_TC_ASWTRG_CLEAR (2 << 22) -#define ATMEL_TC_ASWTRG_TOGGLE (3 << 22) -#define ATMEL_TC_BCPB (3 << 24) /* RB compare changes TIOB */ -#define ATMEL_TC_BCPB_NONE (0 << 24) -#define ATMEL_TC_BCPB_SET (1 << 24) -#define ATMEL_TC_BCPB_CLEAR (2 << 24) -#define ATMEL_TC_BCPB_TOGGLE (3 << 24) -#define ATMEL_TC_BCPC (3 << 26) /* RC compare changes TIOB */ -#define ATMEL_TC_BCPC_NONE (0 << 26) -#define ATMEL_TC_BCPC_SET (1 << 26) -#define ATMEL_TC_BCPC_CLEAR (2 << 26) -#define ATMEL_TC_BCPC_TOGGLE (3 << 26) -#define ATMEL_TC_BEEVT (3 << 28) /* external event changes TIOB */ -#define ATMEL_TC_BEEVT_NONE (0 << 28) -#define ATMEL_TC_BEEVT_SET (1 << 28) -#define ATMEL_TC_BEEVT_CLEAR (2 << 28) -#define ATMEL_TC_BEEVT_TOGGLE (3 << 28) -#define ATMEL_TC_BSWTRG (3 << 30) /* software trigger changes TIOB */ -#define ATMEL_TC_BSWTRG_NONE (0 << 30) -#define ATMEL_TC_BSWTRG_SET (1 << 30) -#define ATMEL_TC_BSWTRG_CLEAR (2 << 30) -#define ATMEL_TC_BSWTRG_TOGGLE (3 << 30) - -#define ATMEL_TC_CV 0x10 /* counter Value */ -#define ATMEL_TC_RA 0x14 /* register A */ -#define ATMEL_TC_RB 0x18 /* register B */ -#define ATMEL_TC_RC 0x1c /* register C */ - -#define ATMEL_TC_SR 0x20 /* status (read-only) */ -/* Status-only flags */ -#define ATMEL_TC_CLKSTA (1 << 16) /* clock enabled */ -#define ATMEL_TC_MTIOA (1 << 17) /* TIOA mirror */ -#define ATMEL_TC_MTIOB (1 << 18) /* TIOB mirror */ - -#define ATMEL_TC_IER 0x24 /* interrupt enable (write-only) */ -#define ATMEL_TC_IDR 0x28 /* interrupt disable (write-only) */ -#define ATMEL_TC_IMR 0x2c /* interrupt mask (read-only) */ - -/* Status and IRQ flags */ -#define ATMEL_TC_COVFS (1 << 0) /* counter overflow */ -#define ATMEL_TC_LOVRS (1 << 1) /* load overrun */ -#define ATMEL_TC_CPAS (1 << 2) /* RA compare */ -#define ATMEL_TC_CPBS (1 << 3) /* RB compare */ -#define ATMEL_TC_CPCS (1 << 4) /* RC compare */ -#define ATMEL_TC_LDRAS (1 << 5) /* RA loading */ -#define ATMEL_TC_LDRBS (1 << 6) /* RB loading */ -#define ATMEL_TC_ETRGS (1 << 7) /* external trigger */ -#define ATMEL_TC_ALL_IRQ (ATMEL_TC_COVFS | ATMEL_TC_LOVRS | \ - ATMEL_TC_CPAS | ATMEL_TC_CPBS | \ - ATMEL_TC_CPCS | ATMEL_TC_LDRAS | \ - ATMEL_TC_LDRBS | ATMEL_TC_ETRGS) \ - /* all IRQs */ - -#endif diff --git a/include/soc/at91/atmel_tcb.h b/include/soc/at91/atmel_tcb.h new file mode 100644 index 000000000000..c3c7200ce151 --- /dev/null +++ b/include/soc/at91/atmel_tcb.h @@ -0,0 +1,270 @@ +/* + * Timer/Counter Unit (TC) registers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __SOC_ATMEL_TCB_H +#define __SOC_ATMEL_TCB_H + +#include +#include + +/* + * Many 32-bit Atmel SOCs include one or more TC blocks, each of which holds + * three general-purpose 16-bit timers. These timers share one register bank. + * Depending on the SOC, each timer may have its own clock and IRQ, or those + * may be shared by the whole TC block. + * + * These TC blocks may have up to nine external pins: TCLK0..2 signals for + * clocks or clock gates, and per-timer TIOA and TIOB signals used for PWM + * or triggering. Those pins need to be set up for use with the TC block, + * else they will be used as GPIOs or for a different controller. + * + * Although we expect each TC block to have a platform_device node, those + * nodes are not what drivers bind to. Instead, they ask for a specific + * TC block, by number ... which is a common approach on systems with many + * timers. Then they use clk_get() and platform_get_irq() to get clock and + * IRQ resources. + */ + +struct clk; + +/** + * struct atmel_tcb_config - SoC data for a Timer/Counter Block + * @counter_width: size in bits of a timer counter register + */ +struct atmel_tcb_config { + size_t counter_width; +}; + +/** + * struct atmel_tc - information about a Timer/Counter Block + * @pdev: physical device + * @regs: mapping through which the I/O registers can be accessed + * @id: block id + * @tcb_config: configuration data from SoC + * @irq: irq for each of the three channels + * @clk: internal clock source for each of the three channels + * @node: list node, for tclib internal use + * @allocated: if already used, for tclib internal use + * + * On some platforms, each TC channel has its own clocks and IRQs, + * while on others, all TC channels share the same clock and IRQ. + * Drivers should clk_enable() all the clocks they need even though + * all the entries in @clk may point to the same physical clock. + * Likewise, drivers should request irqs independently for each + * channel, but they must use IRQF_SHARED in case some of the entries + * in @irq are actually the same IRQ. + */ +struct atmel_tc { + struct platform_device *pdev; + void __iomem *regs; + int id; + const struct atmel_tcb_config *tcb_config; + int irq[3]; + struct clk *clk[3]; + struct clk *slow_clk; + struct list_head node; + bool allocated; +}; + +extern struct atmel_tc *atmel_tc_alloc(unsigned block); +extern void atmel_tc_free(struct atmel_tc *tc); + +/* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */ +extern const u8 atmel_tc_divisors[5]; + + +/* + * Two registers have block-wide controls. These are: configuring the three + * "external" clocks (or event sources) used by the timer channels; and + * synchronizing the timers by resetting them all at once. + * + * "External" can mean "external to chip" using the TCLK0, TCLK1, or TCLK2 + * signals. Or, it can mean "external to timer", using the TIOA output from + * one of the other two timers that's being run in waveform mode. + */ + +#define ATMEL_TC_BCR 0xc0 /* TC Block Control Register */ +#define ATMEL_TC_SYNC (1 << 0) /* synchronize timers */ + +#define ATMEL_TC_BMR 0xc4 /* TC Block Mode Register */ +#define ATMEL_TC_TC0XC0S (3 << 0) /* external clock 0 source */ +#define ATMEL_TC_TC0XC0S_TCLK0 (0 << 0) +#define ATMEL_TC_TC0XC0S_NONE (1 << 0) +#define ATMEL_TC_TC0XC0S_TIOA1 (2 << 0) +#define ATMEL_TC_TC0XC0S_TIOA2 (3 << 0) +#define ATMEL_TC_TC1XC1S (3 << 2) /* external clock 1 source */ +#define ATMEL_TC_TC1XC1S_TCLK1 (0 << 2) +#define ATMEL_TC_TC1XC1S_NONE (1 << 2) +#define ATMEL_TC_TC1XC1S_TIOA0 (2 << 2) +#define ATMEL_TC_TC1XC1S_TIOA2 (3 << 2) +#define ATMEL_TC_TC2XC2S (3 << 4) /* external clock 2 source */ +#define ATMEL_TC_TC2XC2S_TCLK2 (0 << 4) +#define ATMEL_TC_TC2XC2S_NONE (1 << 4) +#define ATMEL_TC_TC2XC2S_TIOA0 (2 << 4) +#define ATMEL_TC_TC2XC2S_TIOA1 (3 << 4) + + +/* + * Each TC block has three "channels", each with one counter and controls. + * + * Note that the semantics of ATMEL_TC_TIMER_CLOCKx (input clock selection + * when it's not "external") is silicon-specific. AT91 platforms use one + * set of definitions; AVR32 platforms use a different set. Don't hard-wire + * such knowledge into your code, use the global "atmel_tc_divisors" ... + * where index N is the divisor for clock N+1, else zero to indicate it uses + * the 32 KiHz clock. + * + * The timers can be chained in various ways, and operated in "waveform" + * generation mode (including PWM) or "capture" mode (to time events). In + * both modes, behavior can be configured in many ways. + * + * Each timer has two I/O pins, TIOA and TIOB. Waveform mode uses TIOA as a + * PWM output, and TIOB as either another PWM or as a trigger. Capture mode + * uses them only as inputs. + */ +#define ATMEL_TC_CHAN(idx) ((idx)*0x40) +#define ATMEL_TC_REG(idx, reg) (ATMEL_TC_CHAN(idx) + ATMEL_TC_ ## reg) + +#define ATMEL_TC_CCR 0x00 /* Channel Control Register */ +#define ATMEL_TC_CLKEN (1 << 0) /* clock enable */ +#define ATMEL_TC_CLKDIS (1 << 1) /* clock disable */ +#define ATMEL_TC_SWTRG (1 << 2) /* software trigger */ + +#define ATMEL_TC_CMR 0x04 /* Channel Mode Register */ + +/* Both modes share some CMR bits */ +#define ATMEL_TC_TCCLKS (7 << 0) /* clock source */ +#define ATMEL_TC_TIMER_CLOCK1 (0 << 0) +#define ATMEL_TC_TIMER_CLOCK2 (1 << 0) +#define ATMEL_TC_TIMER_CLOCK3 (2 << 0) +#define ATMEL_TC_TIMER_CLOCK4 (3 << 0) +#define ATMEL_TC_TIMER_CLOCK5 (4 << 0) +#define ATMEL_TC_XC0 (5 << 0) +#define ATMEL_TC_XC1 (6 << 0) +#define ATMEL_TC_XC2 (7 << 0) +#define ATMEL_TC_CLKI (1 << 3) /* clock invert */ +#define ATMEL_TC_BURST (3 << 4) /* clock gating */ +#define ATMEL_TC_GATE_NONE (0 << 4) +#define ATMEL_TC_GATE_XC0 (1 << 4) +#define ATMEL_TC_GATE_XC1 (2 << 4) +#define ATMEL_TC_GATE_XC2 (3 << 4) +#define ATMEL_TC_WAVE (1 << 15) /* true = Waveform mode */ + +/* CAPTURE mode CMR bits */ +#define ATMEL_TC_LDBSTOP (1 << 6) /* counter stops on RB load */ +#define ATMEL_TC_LDBDIS (1 << 7) /* counter disable on RB load */ +#define ATMEL_TC_ETRGEDG (3 << 8) /* external trigger edge */ +#define ATMEL_TC_ETRGEDG_NONE (0 << 8) +#define ATMEL_TC_ETRGEDG_RISING (1 << 8) +#define ATMEL_TC_ETRGEDG_FALLING (2 << 8) +#define ATMEL_TC_ETRGEDG_BOTH (3 << 8) +#define ATMEL_TC_ABETRG (1 << 10) /* external trigger is TIOA? */ +#define ATMEL_TC_CPCTRG (1 << 14) /* RC compare trigger enable */ +#define ATMEL_TC_LDRA (3 << 16) /* RA loading edge (of TIOA) */ +#define ATMEL_TC_LDRA_NONE (0 << 16) +#define ATMEL_TC_LDRA_RISING (1 << 16) +#define ATMEL_TC_LDRA_FALLING (2 << 16) +#define ATMEL_TC_LDRA_BOTH (3 << 16) +#define ATMEL_TC_LDRB (3 << 18) /* RB loading edge (of TIOA) */ +#define ATMEL_TC_LDRB_NONE (0 << 18) +#define ATMEL_TC_LDRB_RISING (1 << 18) +#define ATMEL_TC_LDRB_FALLING (2 << 18) +#define ATMEL_TC_LDRB_BOTH (3 << 18) + +/* WAVEFORM mode CMR bits */ +#define ATMEL_TC_CPCSTOP (1 << 6) /* RC compare stops counter */ +#define ATMEL_TC_CPCDIS (1 << 7) /* RC compare disables counter */ +#define ATMEL_TC_EEVTEDG (3 << 8) /* external event edge */ +#define ATMEL_TC_EEVTEDG_NONE (0 << 8) +#define ATMEL_TC_EEVTEDG_RISING (1 << 8) +#define ATMEL_TC_EEVTEDG_FALLING (2 << 8) +#define ATMEL_TC_EEVTEDG_BOTH (3 << 8) +#define ATMEL_TC_EEVT (3 << 10) /* external event source */ +#define ATMEL_TC_EEVT_TIOB (0 << 10) +#define ATMEL_TC_EEVT_XC0 (1 << 10) +#define ATMEL_TC_EEVT_XC1 (2 << 10) +#define ATMEL_TC_EEVT_XC2 (3 << 10) +#define ATMEL_TC_ENETRG (1 << 12) /* external event is trigger */ +#define ATMEL_TC_WAVESEL (3 << 13) /* waveform type */ +#define ATMEL_TC_WAVESEL_UP (0 << 13) +#define ATMEL_TC_WAVESEL_UPDOWN (1 << 13) +#define ATMEL_TC_WAVESEL_UP_AUTO (2 << 13) +#define ATMEL_TC_WAVESEL_UPDOWN_AUTO (3 << 13) +#define ATMEL_TC_ACPA (3 << 16) /* RA compare changes TIOA */ +#define ATMEL_TC_ACPA_NONE (0 << 16) +#define ATMEL_TC_ACPA_SET (1 << 16) +#define ATMEL_TC_ACPA_CLEAR (2 << 16) +#define ATMEL_TC_ACPA_TOGGLE (3 << 16) +#define ATMEL_TC_ACPC (3 << 18) /* RC compare changes TIOA */ +#define ATMEL_TC_ACPC_NONE (0 << 18) +#define ATMEL_TC_ACPC_SET (1 << 18) +#define ATMEL_TC_ACPC_CLEAR (2 << 18) +#define ATMEL_TC_ACPC_TOGGLE (3 << 18) +#define ATMEL_TC_AEEVT (3 << 20) /* external event changes TIOA */ +#define ATMEL_TC_AEEVT_NONE (0 << 20) +#define ATMEL_TC_AEEVT_SET (1 << 20) +#define ATMEL_TC_AEEVT_CLEAR (2 << 20) +#define ATMEL_TC_AEEVT_TOGGLE (3 << 20) +#define ATMEL_TC_ASWTRG (3 << 22) /* software trigger changes TIOA */ +#define ATMEL_TC_ASWTRG_NONE (0 << 22) +#define ATMEL_TC_ASWTRG_SET (1 << 22) +#define ATMEL_TC_ASWTRG_CLEAR (2 << 22) +#define ATMEL_TC_ASWTRG_TOGGLE (3 << 22) +#define ATMEL_TC_BCPB (3 << 24) /* RB compare changes TIOB */ +#define ATMEL_TC_BCPB_NONE (0 << 24) +#define ATMEL_TC_BCPB_SET (1 << 24) +#define ATMEL_TC_BCPB_CLEAR (2 << 24) +#define ATMEL_TC_BCPB_TOGGLE (3 << 24) +#define ATMEL_TC_BCPC (3 << 26) /* RC compare changes TIOB */ +#define ATMEL_TC_BCPC_NONE (0 << 26) +#define ATMEL_TC_BCPC_SET (1 << 26) +#define ATMEL_TC_BCPC_CLEAR (2 << 26) +#define ATMEL_TC_BCPC_TOGGLE (3 << 26) +#define ATMEL_TC_BEEVT (3 << 28) /* external event changes TIOB */ +#define ATMEL_TC_BEEVT_NONE (0 << 28) +#define ATMEL_TC_BEEVT_SET (1 << 28) +#define ATMEL_TC_BEEVT_CLEAR (2 << 28) +#define ATMEL_TC_BEEVT_TOGGLE (3 << 28) +#define ATMEL_TC_BSWTRG (3 << 30) /* software trigger changes TIOB */ +#define ATMEL_TC_BSWTRG_NONE (0 << 30) +#define ATMEL_TC_BSWTRG_SET (1 << 30) +#define ATMEL_TC_BSWTRG_CLEAR (2 << 30) +#define ATMEL_TC_BSWTRG_TOGGLE (3 << 30) + +#define ATMEL_TC_CV 0x10 /* counter Value */ +#define ATMEL_TC_RA 0x14 /* register A */ +#define ATMEL_TC_RB 0x18 /* register B */ +#define ATMEL_TC_RC 0x1c /* register C */ + +#define ATMEL_TC_SR 0x20 /* status (read-only) */ +/* Status-only flags */ +#define ATMEL_TC_CLKSTA (1 << 16) /* clock enabled */ +#define ATMEL_TC_MTIOA (1 << 17) /* TIOA mirror */ +#define ATMEL_TC_MTIOB (1 << 18) /* TIOB mirror */ + +#define ATMEL_TC_IER 0x24 /* interrupt enable (write-only) */ +#define ATMEL_TC_IDR 0x28 /* interrupt disable (write-only) */ +#define ATMEL_TC_IMR 0x2c /* interrupt mask (read-only) */ + +/* Status and IRQ flags */ +#define ATMEL_TC_COVFS (1 << 0) /* counter overflow */ +#define ATMEL_TC_LOVRS (1 << 1) /* load overrun */ +#define ATMEL_TC_CPAS (1 << 2) /* RA compare */ +#define ATMEL_TC_CPBS (1 << 3) /* RB compare */ +#define ATMEL_TC_CPCS (1 << 4) /* RC compare */ +#define ATMEL_TC_LDRAS (1 << 5) /* RA loading */ +#define ATMEL_TC_LDRBS (1 << 6) /* RB loading */ +#define ATMEL_TC_ETRGS (1 << 7) /* external trigger */ +#define ATMEL_TC_ALL_IRQ (ATMEL_TC_COVFS | ATMEL_TC_LOVRS | \ + ATMEL_TC_CPAS | ATMEL_TC_CPBS | \ + ATMEL_TC_CPCS | ATMEL_TC_LDRAS | \ + ATMEL_TC_LDRBS | ATMEL_TC_ETRGS) \ + /* all IRQs */ + +#endif -- cgit v1.2.3-59-g8ed1b 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 ++++++++++++++++++++++++--------------- drivers/misc/Kconfig | 14 +---- 2 files changed, 70 insertions(+), 52 deletions(-) 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); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 42ab8ec92a04..268a01d3d6f3 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -61,7 +61,8 @@ config ATMEL_TCLIB config ATMEL_TCB_CLKSRC bool "TC Block Clocksource" - depends on ATMEL_TCLIB + depends on ARCH_AT91 + select TIMER_OF if OF default y help Select this to get a high precision clocksource based on a @@ -72,17 +73,6 @@ config ATMEL_TCB_CLKSRC may be used as a clock event device supporting oneshot mode (delays of up to two seconds) based on the 32 KiHz clock. -config ATMEL_TCB_CLKSRC_BLOCK - int - depends on ATMEL_TCB_CLKSRC - default 0 - range 0 1 - help - Some chips provide more than one TC block, so you have the - choice of which one to use for the clock framework. The other - TC can be used for other purposes, such as PWM generation and - interval timing. - config DUMMY_IRQ tristate "Dummy IRQ handler" default n -- cgit v1.2.3-59-g8ed1b 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(+) 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-59-g8ed1b From 7803dc865bd2963192c623546fb240d01e3e7aa0 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 26 Apr 2019 23:47:13 +0200 Subject: ARM: at91: Implement clocksource selection Allow selecting and unselecting the PIT clocksource driver so it doesn't have to be compiled when unused. Signed-off-by: Alexandre Belloni Signed-off-by: Daniel Lezcano --- arch/arm/mach-at91/Kconfig | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 903f23c309df..da1d97a06c53 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -107,6 +107,29 @@ config SOC_AT91SAM9 AT91SAM9X35 AT91SAM9XE +comment "Clocksource driver selection" + +config ATMEL_CLOCKSOURCE_PIT + bool "Periodic Interval Timer (PIT) support" + depends on SOC_AT91SAM9 || SOC_SAMA5 + default SOC_AT91SAM9 || SOC_SAMA5 + select ATMEL_PIT + help + Select this to get a clocksource based on the Atmel Periodic Interval + Timer. It has a relatively low resolution and the TC Block clocksource + should be preferred. + +config ATMEL_CLOCKSOURCE_TCB + bool "Timer Counter Blocks (TCB) support" + default SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAMA5 + select ATMEL_TCB_CLKSRC + help + Select this to get a high precision clocksource based on a + TC block with a 5+ MHz base clock rate. + On platforms with 16-bit counters, two timer channels are combined + to make a single 32-bit timer. + It can also be used as a clock event device supporting oneshot mode. + config HAVE_AT91_UTMI bool -- cgit v1.2.3-59-g8ed1b 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 +++++++ drivers/misc/Kconfig | 14 -------------- 2 files changed, 7 insertions(+), 14 deletions(-) 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 diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 268a01d3d6f3..c84033909395 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -59,20 +59,6 @@ config ATMEL_TCLIB blocks found on many Atmel processors. This facilitates using these blocks by different drivers despite processor differences. -config ATMEL_TCB_CLKSRC - bool "TC Block Clocksource" - depends on ARCH_AT91 - select TIMER_OF if OF - default y - help - Select this to get a high precision clocksource based on a - TC block with a 5+ MHz base clock rate. Two timer channels - are combined to make a single 32-bit timer. - - When GENERIC_CLOCKEVENTS is defined, the third timer channel - may be used as a clock event device supporting oneshot mode - (delays of up to two seconds) based on the 32 KiHz clock. - config DUMMY_IRQ tristate "Dummy IRQ handler" default n -- cgit v1.2.3-59-g8ed1b 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(-) 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-59-g8ed1b 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 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-59-g8ed1b 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(-) 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-59-g8ed1b From 8c9374068ef6dc3fdf244484b4711a6e10c84d1a Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 26 Apr 2019 23:47:18 +0200 Subject: misc: atmel_tclib: Do not probe already used TCBs The TCBs that have children are using the proper DT bindings and don't need to be handled by tclib. Acked-by: Greg Kroah-Hartman Signed-off-by: Alexandre Belloni Signed-off-by: Daniel Lezcano --- drivers/misc/atmel_tclib.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index 194f774ab3a1..2c6850ef0e9c 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -111,6 +111,9 @@ static int __init tc_probe(struct platform_device *pdev) struct resource *r; unsigned int i; + if (of_get_child_count(pdev->dev.of_node)) + return -EBUSY; + irq = platform_get_irq(pdev, 0); if (irq < 0) return -EINVAL; -- cgit v1.2.3-59-g8ed1b