From f8f5fe86f7e10ecc784b53537edbe3fcf0c00884 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 11 Jul 2018 12:24:22 +0100 Subject: [PATCH 1/7] clocksource/drivers: Set clockevent device cpumask to cpu_possible_mask Currently, quite a few clockevent devices have cpumask set to cpu_all_mask which should be fine. However, cpu_possible_mask is more accurate and if there are any other clockevent devices in the system which have cpumask set to cpu_possible_mask, then having cpu_all_mask may result in issues (mostly boot hang with forever loops in clockevents_notify_released). So, lets replace all the clockevent device cpu_all_mask to cpu_possible_mask in order to prevent above mentioned possible issue. Cc: Daniel Lezcano Cc: Thomas Gleixner Cc: Thierry Reding Cc: Jonathan Hunter Cc: Santosh Shilimkar Signed-off-by: Sudeep Holla Signed-off-by: Daniel Lezcano --- drivers/clocksource/tegra20_timer.c | 2 +- drivers/clocksource/timer-atcpit100.c | 2 +- drivers/clocksource/timer-keystone.c | 2 +- drivers/clocksource/zevio-timer.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index c337a8100a7b..dabf0a103567 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -230,7 +230,7 @@ static int __init tegra20_init_timer(struct device_node *np) return ret; } - tegra_clockevent.cpumask = cpu_all_mask; + tegra_clockevent.cpumask = cpu_possible_mask; tegra_clockevent.irq = tegra_timer_irq.irq; clockevents_config_and_register(&tegra_clockevent, 1000000, 0x1, 0x1fffffff); diff --git a/drivers/clocksource/timer-atcpit100.c b/drivers/clocksource/timer-atcpit100.c index 5e23d7b4a722..b4bd2f5b801d 100644 --- a/drivers/clocksource/timer-atcpit100.c +++ b/drivers/clocksource/timer-atcpit100.c @@ -185,7 +185,7 @@ static struct timer_of to = { .set_state_oneshot = atcpit100_clkevt_set_oneshot, .tick_resume = atcpit100_clkevt_shutdown, .set_next_event = atcpit100_clkevt_next_event, - .cpumask = cpu_all_mask, + .cpumask = cpu_possible_mask, }, .of_irq = { diff --git a/drivers/clocksource/timer-keystone.c b/drivers/clocksource/timer-keystone.c index 0eee03250cfc..f5b2eda30bf3 100644 --- a/drivers/clocksource/timer-keystone.c +++ b/drivers/clocksource/timer-keystone.c @@ -211,7 +211,7 @@ static int __init keystone_timer_init(struct device_node *np) event_dev->set_state_shutdown = keystone_shutdown; event_dev->set_state_periodic = keystone_set_periodic; event_dev->set_state_oneshot = keystone_shutdown; - event_dev->cpumask = cpu_all_mask; + event_dev->cpumask = cpu_possible_mask; event_dev->owner = THIS_MODULE; event_dev->name = TIMER_NAME; event_dev->irq = irq; diff --git a/drivers/clocksource/zevio-timer.c b/drivers/clocksource/zevio-timer.c index a6a0338eea77..f74689334f7c 100644 --- a/drivers/clocksource/zevio-timer.c +++ b/drivers/clocksource/zevio-timer.c @@ -162,7 +162,7 @@ static int __init zevio_timer_add(struct device_node *node) timer->clkevt.set_state_oneshot = zevio_timer_set_oneshot; timer->clkevt.tick_resume = zevio_timer_set_oneshot; timer->clkevt.rating = 200; - timer->clkevt.cpumask = cpu_all_mask; + timer->clkevt.cpumask = cpu_possible_mask; timer->clkevt.features = CLOCK_EVT_FEAT_ONESHOT; timer->clkevt.irq = irqnr; From 59311b19d7f632397a98e8194de8e871867069db Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Fri, 6 Jul 2018 07:11:24 +0800 Subject: [PATCH 2/7] clocksource/drivers/timer-mediatek: Add system timer bindings This patch adds bindings of new "System Timer" on Mediatek SoCs. Remove RTC clock in the same time because it is not used by both "General Purpose Timer" and "System Timer" now. Signed-off-by: Stanley Chu Reviewed-by: Rob Herring Signed-off-by: Daniel Lezcano --- .../bindings/timer/mediatek,mtk-timer.txt | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt b/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt index b1fe7e9de1b4..18d4d0166c76 100644 --- a/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt +++ b/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt @@ -1,19 +1,25 @@ -Mediatek MT6577, MT6572 and MT6589 Timers ---------------------------------------- +Mediatek Timers +--------------- + +Mediatek SoCs have two different timers on different platforms, +- GPT (General Purpose Timer) +- SYST (System Timer) + +The proper timer will be selected automatically by driver. Required properties: - compatible should contain: - * "mediatek,mt2701-timer" for MT2701 compatible timers - * "mediatek,mt6580-timer" for MT6580 compatible timers - * "mediatek,mt6589-timer" for MT6589 compatible timers - * "mediatek,mt7623-timer" for MT7623 compatible timers - * "mediatek,mt8127-timer" for MT8127 compatible timers - * "mediatek,mt8135-timer" for MT8135 compatible timers - * "mediatek,mt8173-timer" for MT8173 compatible timers - * "mediatek,mt6577-timer" for MT6577 and all above compatible timers -- reg: Should contain location and length for timers register. -- clocks: Clocks driving the timer hardware. This list should include two - clocks. The order is system clock and as second clock the RTC clock. + * "mediatek,mt2701-timer" for MT2701 compatible timers (GPT) + * "mediatek,mt6580-timer" for MT6580 compatible timers (GPT) + * "mediatek,mt6589-timer" for MT6589 compatible timers (GPT) + * "mediatek,mt7623-timer" for MT7623 compatible timers (GPT) + * "mediatek,mt8127-timer" for MT8127 compatible timers (GPT) + * "mediatek,mt8135-timer" for MT8135 compatible timers (GPT) + * "mediatek,mt8173-timer" for MT8173 compatible timers (GPT) + * "mediatek,mt6577-timer" for MT6577 and all above compatible timers (GPT) + * "mediatek,mt6765-timer" for MT6765 compatible timers (SYST) +- reg: Should contain location and length for timer register. +- clocks: Should contain system clock. Examples: @@ -21,5 +27,5 @@ Examples: compatible = "mediatek,mt6577-timer"; reg = <0x10008000 0x80>; interrupts = ; - clocks = <&system_clk>, <&rtc_clk>; + clocks = <&system_clk>; }; From 7ec58e5244b7a7aa41f792968208239a43e5ade7 Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Fri, 6 Jul 2018 07:11:25 +0800 Subject: [PATCH 3/7] clocksource/drivers/timer-mediatek: Rename mtk_timer to timer-mediatek Rename mtk_timer to timer-mediatek to apply new naming convention in clocksource folder. Signed-off-by: Stanley Chu Signed-off-by: Daniel Lezcano --- drivers/clocksource/Makefile | 2 +- drivers/clocksource/{mtk_timer.c => timer-mediatek.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/clocksource/{mtk_timer.c => timer-mediatek.c} (100%) diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 00caf37e52f9..c070cc7992e9 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -49,7 +49,7 @@ obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o -obj-$(CONFIG_MTK_TIMER) += mtk_timer.o +obj-$(CONFIG_MTK_TIMER) += timer-mediatek.o obj-$(CONFIG_CLKSRC_PISTACHIO) += time-pistachio.o obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o obj-$(CONFIG_CLKSRC_NPS) += timer-nps.o diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/timer-mediatek.c similarity index 100% rename from drivers/clocksource/mtk_timer.c rename to drivers/clocksource/timer-mediatek.c From 56d52d3f56192049221105efa6ee76251a949c0e Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Fri, 6 Jul 2018 07:11:26 +0800 Subject: [PATCH 4/7] clocksource/drivers/timer-mediatek: Use specific prefix for GPT Use specific prefix to specify the name of supported timer hardware: "General Purpose Timer (GPT)". Signed-off-by: Stanley Chu Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-mediatek.c | 149 ++++++++++++++------------- 1 file changed, 76 insertions(+), 73 deletions(-) diff --git a/drivers/clocksource/timer-mediatek.c b/drivers/clocksource/timer-mediatek.c index f9b724fd9950..e3657d29e765 100644 --- a/drivers/clocksource/timer-mediatek.c +++ b/drivers/clocksource/timer-mediatek.c @@ -29,32 +29,35 @@ #include #include -#define GPT_IRQ_EN_REG 0x00 -#define GPT_IRQ_ENABLE(val) BIT((val) - 1) -#define GPT_IRQ_ACK_REG 0x08 -#define GPT_IRQ_ACK(val) BIT((val) - 1) +#define TIMER_CLK_EVT (1) +#define TIMER_CLK_SRC (2) -#define TIMER_CTRL_REG(val) (0x10 * (val)) -#define TIMER_CTRL_OP(val) (((val) & 0x3) << 4) -#define TIMER_CTRL_OP_ONESHOT (0) -#define TIMER_CTRL_OP_REPEAT (1) -#define TIMER_CTRL_OP_FREERUN (3) -#define TIMER_CTRL_CLEAR (2) -#define TIMER_CTRL_ENABLE (1) -#define TIMER_CTRL_DISABLE (0) +#define TIMER_SYNC_TICKS (3) -#define TIMER_CLK_REG(val) (0x04 + (0x10 * (val))) -#define TIMER_CLK_SRC(val) (((val) & 0x1) << 4) -#define TIMER_CLK_SRC_SYS13M (0) -#define TIMER_CLK_SRC_RTC32K (1) -#define TIMER_CLK_DIV1 (0x0) -#define TIMER_CLK_DIV2 (0x1) +/* gpt */ +#define GPT_IRQ_EN_REG 0x00 +#define GPT_IRQ_ENABLE(val) BIT((val) - 1) +#define GPT_IRQ_ACK_REG 0x08 +#define GPT_IRQ_ACK(val) BIT((val) - 1) -#define TIMER_CNT_REG(val) (0x08 + (0x10 * (val))) -#define TIMER_CMP_REG(val) (0x0C + (0x10 * (val))) +#define GPT_CTRL_REG(val) (0x10 * (val)) +#define GPT_CTRL_OP(val) (((val) & 0x3) << 4) +#define GPT_CTRL_OP_ONESHOT (0) +#define GPT_CTRL_OP_REPEAT (1) +#define GPT_CTRL_OP_FREERUN (3) +#define GPT_CTRL_CLEAR (2) +#define GPT_CTRL_ENABLE (1) +#define GPT_CTRL_DISABLE (0) -#define GPT_CLK_EVT 1 -#define GPT_CLK_SRC 2 +#define GPT_CLK_REG(val) (0x04 + (0x10 * (val))) +#define GPT_CLK_SRC(val) (((val) & 0x1) << 4) +#define GPT_CLK_SRC_SYS13M (0) +#define GPT_CLK_SRC_RTC32K (1) +#define GPT_CLK_DIV1 (0x0) +#define GPT_CLK_DIV2 (0x1) + +#define GPT_CNT_REG(val) (0x08 + (0x10 * (val))) +#define GPT_CMP_REG(val) (0x0C + (0x10 * (val))) struct mtk_clock_event_device { void __iomem *gpt_base; @@ -64,7 +67,7 @@ struct mtk_clock_event_device { static void __iomem *gpt_sched_reg __read_mostly; -static u64 notrace mtk_read_sched_clock(void) +static u64 notrace mtk_gpt_read_sched_clock(void) { return readl_relaxed(gpt_sched_reg); } @@ -75,22 +78,22 @@ static inline struct mtk_clock_event_device *to_mtk_clk( return container_of(c, struct mtk_clock_event_device, dev); } -static void mtk_clkevt_time_stop(struct mtk_clock_event_device *evt, u8 timer) +static void mtk_gpt_clkevt_time_stop(struct mtk_clock_event_device *evt, u8 timer) { u32 val; - val = readl(evt->gpt_base + TIMER_CTRL_REG(timer)); - writel(val & ~TIMER_CTRL_ENABLE, evt->gpt_base + - TIMER_CTRL_REG(timer)); + val = readl(evt->gpt_base + GPT_CTRL_REG(timer)); + writel(val & ~GPT_CTRL_ENABLE, evt->gpt_base + + GPT_CTRL_REG(timer)); } -static void mtk_clkevt_time_setup(struct mtk_clock_event_device *evt, +static void mtk_gpt_clkevt_time_setup(struct mtk_clock_event_device *evt, unsigned long delay, u8 timer) { - writel(delay, evt->gpt_base + TIMER_CMP_REG(timer)); + writel(delay, evt->gpt_base + GPT_CMP_REG(timer)); } -static void mtk_clkevt_time_start(struct mtk_clock_event_device *evt, +static void mtk_gpt_clkevt_time_start(struct mtk_clock_event_device *evt, bool periodic, u8 timer) { u32 val; @@ -98,75 +101,75 @@ static void mtk_clkevt_time_start(struct mtk_clock_event_device *evt, /* Acknowledge interrupt */ writel(GPT_IRQ_ACK(timer), evt->gpt_base + GPT_IRQ_ACK_REG); - val = readl(evt->gpt_base + TIMER_CTRL_REG(timer)); + val = readl(evt->gpt_base + GPT_CTRL_REG(timer)); /* Clear 2 bit timer operation mode field */ - val &= ~TIMER_CTRL_OP(0x3); + val &= ~GPT_CTRL_OP(0x3); if (periodic) - val |= TIMER_CTRL_OP(TIMER_CTRL_OP_REPEAT); + val |= GPT_CTRL_OP(GPT_CTRL_OP_REPEAT); else - val |= TIMER_CTRL_OP(TIMER_CTRL_OP_ONESHOT); + val |= GPT_CTRL_OP(GPT_CTRL_OP_ONESHOT); - writel(val | TIMER_CTRL_ENABLE | TIMER_CTRL_CLEAR, - evt->gpt_base + TIMER_CTRL_REG(timer)); + writel(val | GPT_CTRL_ENABLE | GPT_CTRL_CLEAR, + evt->gpt_base + GPT_CTRL_REG(timer)); } -static int mtk_clkevt_shutdown(struct clock_event_device *clk) +static int mtk_gpt_clkevt_shutdown(struct clock_event_device *clk) { - mtk_clkevt_time_stop(to_mtk_clk(clk), GPT_CLK_EVT); + mtk_gpt_clkevt_time_stop(to_mtk_clk(clk), TIMER_CLK_EVT); return 0; } -static int mtk_clkevt_set_periodic(struct clock_event_device *clk) +static int mtk_gpt_clkevt_set_periodic(struct clock_event_device *clk) { struct mtk_clock_event_device *evt = to_mtk_clk(clk); - mtk_clkevt_time_stop(evt, GPT_CLK_EVT); - mtk_clkevt_time_setup(evt, evt->ticks_per_jiffy, GPT_CLK_EVT); - mtk_clkevt_time_start(evt, true, GPT_CLK_EVT); + mtk_gpt_clkevt_time_stop(evt, TIMER_CLK_EVT); + mtk_gpt_clkevt_time_setup(evt, evt->ticks_per_jiffy, TIMER_CLK_EVT); + mtk_gpt_clkevt_time_start(evt, true, TIMER_CLK_EVT); return 0; } -static int mtk_clkevt_next_event(unsigned long event, +static int mtk_gpt_clkevt_next_event(unsigned long event, struct clock_event_device *clk) { struct mtk_clock_event_device *evt = to_mtk_clk(clk); - mtk_clkevt_time_stop(evt, GPT_CLK_EVT); - mtk_clkevt_time_setup(evt, event, GPT_CLK_EVT); - mtk_clkevt_time_start(evt, false, GPT_CLK_EVT); + mtk_gpt_clkevt_time_stop(evt, TIMER_CLK_EVT); + mtk_gpt_clkevt_time_setup(evt, event, TIMER_CLK_EVT); + mtk_gpt_clkevt_time_start(evt, false, TIMER_CLK_EVT); return 0; } -static irqreturn_t mtk_timer_interrupt(int irq, void *dev_id) +static irqreturn_t mtk_gpt_interrupt(int irq, void *dev_id) { struct mtk_clock_event_device *evt = dev_id; /* Acknowledge timer0 irq */ - writel(GPT_IRQ_ACK(GPT_CLK_EVT), evt->gpt_base + GPT_IRQ_ACK_REG); + writel(GPT_IRQ_ACK(TIMER_CLK_EVT), evt->gpt_base + GPT_IRQ_ACK_REG); evt->dev.event_handler(&evt->dev); return IRQ_HANDLED; } static void -__init mtk_timer_setup(struct mtk_clock_event_device *evt, u8 timer, u8 option) +__init mtk_gpt_setup(struct mtk_clock_event_device *evt, u8 timer, u8 option) { - writel(TIMER_CTRL_CLEAR | TIMER_CTRL_DISABLE, - evt->gpt_base + TIMER_CTRL_REG(timer)); + writel(GPT_CTRL_CLEAR | GPT_CTRL_DISABLE, + evt->gpt_base + GPT_CTRL_REG(timer)); - writel(TIMER_CLK_SRC(TIMER_CLK_SRC_SYS13M) | TIMER_CLK_DIV1, - evt->gpt_base + TIMER_CLK_REG(timer)); + writel(GPT_CLK_SRC(GPT_CLK_SRC_SYS13M) | GPT_CLK_DIV1, + evt->gpt_base + GPT_CLK_REG(timer)); - writel(0x0, evt->gpt_base + TIMER_CMP_REG(timer)); + writel(0x0, evt->gpt_base + GPT_CMP_REG(timer)); - writel(TIMER_CTRL_OP(option) | TIMER_CTRL_ENABLE, - evt->gpt_base + TIMER_CTRL_REG(timer)); + writel(GPT_CTRL_OP(option) | GPT_CTRL_ENABLE, + evt->gpt_base + GPT_CTRL_REG(timer)); } -static void mtk_timer_enable_irq(struct mtk_clock_event_device *evt, u8 timer) +static void mtk_gpt_enable_irq(struct mtk_clock_event_device *evt, u8 timer) { u32 val; @@ -181,7 +184,7 @@ static void mtk_timer_enable_irq(struct mtk_clock_event_device *evt, u8 timer) evt->gpt_base + GPT_IRQ_EN_REG); } -static int __init mtk_timer_init(struct device_node *node) +static int __init mtk_gpt_init(struct device_node *node) { struct mtk_clock_event_device *evt; struct resource res; @@ -195,14 +198,14 @@ static int __init mtk_timer_init(struct device_node *node) evt->dev.name = "mtk_tick"; evt->dev.rating = 300; evt->dev.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; - evt->dev.set_state_shutdown = mtk_clkevt_shutdown; - evt->dev.set_state_periodic = mtk_clkevt_set_periodic; - evt->dev.set_state_oneshot = mtk_clkevt_shutdown; - evt->dev.tick_resume = mtk_clkevt_shutdown; - evt->dev.set_next_event = mtk_clkevt_next_event; + evt->dev.set_state_shutdown = mtk_gpt_clkevt_shutdown; + evt->dev.set_state_periodic = mtk_gpt_clkevt_set_periodic; + evt->dev.set_state_oneshot = mtk_gpt_clkevt_shutdown; + evt->dev.tick_resume = mtk_gpt_clkevt_shutdown; + evt->dev.set_next_event = mtk_gpt_clkevt_next_event; evt->dev.cpumask = cpu_possible_mask; - evt->gpt_base = of_io_request_and_map(node, 0, "mtk-timer"); + evt->gpt_base = of_io_request_and_map(node, 0, "mtk-timer-gpt"); if (IS_ERR(evt->gpt_base)) { pr_err("Can't get resource\n"); goto err_kzalloc; @@ -226,7 +229,7 @@ static int __init mtk_timer_init(struct device_node *node) } rate = clk_get_rate(clk); - if (request_irq(evt->dev.irq, mtk_timer_interrupt, + if (request_irq(evt->dev.irq, mtk_gpt_interrupt, IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) { pr_err("failed to setup irq %d\n", evt->dev.irq); goto err_clk_disable; @@ -235,18 +238,18 @@ static int __init mtk_timer_init(struct device_node *node) evt->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); /* Configure clock source */ - mtk_timer_setup(evt, GPT_CLK_SRC, TIMER_CTRL_OP_FREERUN); - clocksource_mmio_init(evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC), + mtk_gpt_setup(evt, TIMER_CLK_SRC, GPT_CTRL_OP_FREERUN); + clocksource_mmio_init(evt->gpt_base + GPT_CNT_REG(TIMER_CLK_SRC), node->name, rate, 300, 32, clocksource_mmio_readl_up); - gpt_sched_reg = evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC); - sched_clock_register(mtk_read_sched_clock, 32, rate); + gpt_sched_reg = evt->gpt_base + GPT_CNT_REG(TIMER_CLK_SRC); + sched_clock_register(mtk_gpt_read_sched_clock, 32, rate); /* Configure clock event */ - mtk_timer_setup(evt, GPT_CLK_EVT, TIMER_CTRL_OP_REPEAT); - clockevents_config_and_register(&evt->dev, rate, 0x3, + mtk_gpt_setup(evt, TIMER_CLK_EVT, GPT_CTRL_OP_REPEAT); + clockevents_config_and_register(&evt->dev, rate, TIMER_SYNC_TICKS, 0xffffffff); - mtk_timer_enable_irq(evt, GPT_CLK_EVT); + mtk_gpt_enable_irq(evt, TIMER_CLK_EVT); return 0; @@ -265,4 +268,4 @@ err_kzalloc: return -EINVAL; } -TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init); +TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init); From a0858f937960d24e05cedf43d5cbcb95cf088434 Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Fri, 6 Jul 2018 07:11:27 +0800 Subject: [PATCH 5/7] clocksource/drivers/timer-mediatek: Convert the driver to timer-of Convert the driver to use the timer_of helpers. This allows to remove custom proprietary structure, factors out and simplifies the code. Signed-off-by: Stanley Chu Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-mediatek.c | 201 +++++++++++---------------- 1 file changed, 78 insertions(+), 123 deletions(-) diff --git a/drivers/clocksource/timer-mediatek.c b/drivers/clocksource/timer-mediatek.c index e3657d29e765..e57c4d7414a4 100644 --- a/drivers/clocksource/timer-mediatek.c +++ b/drivers/clocksource/timer-mediatek.c @@ -18,16 +18,13 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include +#include #include -#include #include -#include -#include -#include #include #include +#include "timer-of.h" #define TIMER_CLK_EVT (1) #define TIMER_CLK_SRC (2) @@ -59,12 +56,6 @@ #define GPT_CNT_REG(val) (0x08 + (0x10 * (val))) #define GPT_CMP_REG(val) (0x0C + (0x10 * (val))) -struct mtk_clock_event_device { - void __iomem *gpt_base; - u32 ticks_per_jiffy; - struct clock_event_device dev; -}; - static void __iomem *gpt_sched_reg __read_mostly; static u64 notrace mtk_gpt_read_sched_clock(void) @@ -72,36 +63,30 @@ static u64 notrace mtk_gpt_read_sched_clock(void) return readl_relaxed(gpt_sched_reg); } -static inline struct mtk_clock_event_device *to_mtk_clk( - struct clock_event_device *c) -{ - return container_of(c, struct mtk_clock_event_device, dev); -} - -static void mtk_gpt_clkevt_time_stop(struct mtk_clock_event_device *evt, u8 timer) +static void mtk_gpt_clkevt_time_stop(struct timer_of *to, u8 timer) { u32 val; - val = readl(evt->gpt_base + GPT_CTRL_REG(timer)); - writel(val & ~GPT_CTRL_ENABLE, evt->gpt_base + - GPT_CTRL_REG(timer)); + val = readl(timer_of_base(to) + GPT_CTRL_REG(timer)); + writel(val & ~GPT_CTRL_ENABLE, timer_of_base(to) + + GPT_CTRL_REG(timer)); } -static void mtk_gpt_clkevt_time_setup(struct mtk_clock_event_device *evt, - unsigned long delay, u8 timer) +static void mtk_gpt_clkevt_time_setup(struct timer_of *to, + unsigned long delay, u8 timer) { - writel(delay, evt->gpt_base + GPT_CMP_REG(timer)); + writel(delay, timer_of_base(to) + GPT_CMP_REG(timer)); } -static void mtk_gpt_clkevt_time_start(struct mtk_clock_event_device *evt, - bool periodic, u8 timer) +static void mtk_gpt_clkevt_time_start(struct timer_of *to, + bool periodic, u8 timer) { u32 val; /* Acknowledge interrupt */ - writel(GPT_IRQ_ACK(timer), evt->gpt_base + GPT_IRQ_ACK_REG); + writel(GPT_IRQ_ACK(timer), timer_of_base(to) + GPT_IRQ_ACK_REG); - val = readl(evt->gpt_base + GPT_CTRL_REG(timer)); + val = readl(timer_of_base(to) + GPT_CTRL_REG(timer)); /* Clear 2 bit timer operation mode field */ val &= ~GPT_CTRL_OP(0x3); @@ -112,160 +97,130 @@ static void mtk_gpt_clkevt_time_start(struct mtk_clock_event_device *evt, val |= GPT_CTRL_OP(GPT_CTRL_OP_ONESHOT); writel(val | GPT_CTRL_ENABLE | GPT_CTRL_CLEAR, - evt->gpt_base + GPT_CTRL_REG(timer)); + timer_of_base(to) + GPT_CTRL_REG(timer)); } static int mtk_gpt_clkevt_shutdown(struct clock_event_device *clk) { - mtk_gpt_clkevt_time_stop(to_mtk_clk(clk), TIMER_CLK_EVT); + mtk_gpt_clkevt_time_stop(to_timer_of(clk), TIMER_CLK_EVT); + return 0; } static int mtk_gpt_clkevt_set_periodic(struct clock_event_device *clk) { - struct mtk_clock_event_device *evt = to_mtk_clk(clk); + struct timer_of *to = to_timer_of(clk); + + mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT); + mtk_gpt_clkevt_time_setup(to, to->of_clk.period, TIMER_CLK_EVT); + mtk_gpt_clkevt_time_start(to, true, TIMER_CLK_EVT); - mtk_gpt_clkevt_time_stop(evt, TIMER_CLK_EVT); - mtk_gpt_clkevt_time_setup(evt, evt->ticks_per_jiffy, TIMER_CLK_EVT); - mtk_gpt_clkevt_time_start(evt, true, TIMER_CLK_EVT); return 0; } static int mtk_gpt_clkevt_next_event(unsigned long event, - struct clock_event_device *clk) + struct clock_event_device *clk) { - struct mtk_clock_event_device *evt = to_mtk_clk(clk); + struct timer_of *to = to_timer_of(clk); - mtk_gpt_clkevt_time_stop(evt, TIMER_CLK_EVT); - mtk_gpt_clkevt_time_setup(evt, event, TIMER_CLK_EVT); - mtk_gpt_clkevt_time_start(evt, false, TIMER_CLK_EVT); + mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT); + mtk_gpt_clkevt_time_setup(to, event, TIMER_CLK_EVT); + mtk_gpt_clkevt_time_start(to, false, TIMER_CLK_EVT); return 0; } static irqreturn_t mtk_gpt_interrupt(int irq, void *dev_id) { - struct mtk_clock_event_device *evt = dev_id; + struct clock_event_device *clkevt = (struct clock_event_device *)dev_id; + struct timer_of *to = to_timer_of(clkevt); /* Acknowledge timer0 irq */ - writel(GPT_IRQ_ACK(TIMER_CLK_EVT), evt->gpt_base + GPT_IRQ_ACK_REG); - evt->dev.event_handler(&evt->dev); + writel(GPT_IRQ_ACK(TIMER_CLK_EVT), timer_of_base(to) + GPT_IRQ_ACK_REG); + clkevt->event_handler(clkevt); return IRQ_HANDLED; } static void -__init mtk_gpt_setup(struct mtk_clock_event_device *evt, u8 timer, u8 option) +__init mtk_gpt_setup(struct timer_of *to, u8 timer, u8 option) { writel(GPT_CTRL_CLEAR | GPT_CTRL_DISABLE, - evt->gpt_base + GPT_CTRL_REG(timer)); + timer_of_base(to) + GPT_CTRL_REG(timer)); writel(GPT_CLK_SRC(GPT_CLK_SRC_SYS13M) | GPT_CLK_DIV1, - evt->gpt_base + GPT_CLK_REG(timer)); + timer_of_base(to) + GPT_CLK_REG(timer)); - writel(0x0, evt->gpt_base + GPT_CMP_REG(timer)); + writel(0x0, timer_of_base(to) + GPT_CMP_REG(timer)); writel(GPT_CTRL_OP(option) | GPT_CTRL_ENABLE, - evt->gpt_base + GPT_CTRL_REG(timer)); + timer_of_base(to) + GPT_CTRL_REG(timer)); } -static void mtk_gpt_enable_irq(struct mtk_clock_event_device *evt, u8 timer) +static void mtk_gpt_enable_irq(struct timer_of *to, u8 timer) { u32 val; /* Disable all interrupts */ - writel(0x0, evt->gpt_base + GPT_IRQ_EN_REG); + writel(0x0, timer_of_base(to) + GPT_IRQ_EN_REG); /* Acknowledge all spurious pending interrupts */ - writel(0x3f, evt->gpt_base + GPT_IRQ_ACK_REG); + writel(0x3f, timer_of_base(to) + GPT_IRQ_ACK_REG); - val = readl(evt->gpt_base + GPT_IRQ_EN_REG); + val = readl(timer_of_base(to) + GPT_IRQ_EN_REG); writel(val | GPT_IRQ_ENABLE(timer), - evt->gpt_base + GPT_IRQ_EN_REG); + timer_of_base(to) + GPT_IRQ_EN_REG); } +static struct timer_of to = { + .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, + + .clkevt = { + .name = "mtk-clkevt", + .rating = 300, + .cpumask = cpu_possible_mask, + }, + + .of_irq = { + .flags = IRQF_TIMER | IRQF_IRQPOLL, + }, +}; + static int __init mtk_gpt_init(struct device_node *node) { - struct mtk_clock_event_device *evt; - struct resource res; - unsigned long rate = 0; - struct clk *clk; + int ret; - evt = kzalloc(sizeof(*evt), GFP_KERNEL); - if (!evt) - return -ENOMEM; + to.clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + to.clkevt.set_state_shutdown = mtk_gpt_clkevt_shutdown; + to.clkevt.set_state_periodic = mtk_gpt_clkevt_set_periodic; + to.clkevt.set_state_oneshot = mtk_gpt_clkevt_shutdown; + to.clkevt.tick_resume = mtk_gpt_clkevt_shutdown; + to.clkevt.set_next_event = mtk_gpt_clkevt_next_event; + to.of_irq.handler = mtk_gpt_interrupt; - evt->dev.name = "mtk_tick"; - evt->dev.rating = 300; - evt->dev.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; - evt->dev.set_state_shutdown = mtk_gpt_clkevt_shutdown; - evt->dev.set_state_periodic = mtk_gpt_clkevt_set_periodic; - evt->dev.set_state_oneshot = mtk_gpt_clkevt_shutdown; - evt->dev.tick_resume = mtk_gpt_clkevt_shutdown; - evt->dev.set_next_event = mtk_gpt_clkevt_next_event; - evt->dev.cpumask = cpu_possible_mask; - - evt->gpt_base = of_io_request_and_map(node, 0, "mtk-timer-gpt"); - if (IS_ERR(evt->gpt_base)) { - pr_err("Can't get resource\n"); - goto err_kzalloc; - } - - evt->dev.irq = irq_of_parse_and_map(node, 0); - if (evt->dev.irq <= 0) { - pr_err("Can't parse IRQ\n"); - goto err_mem; - } - - clk = of_clk_get(node, 0); - if (IS_ERR(clk)) { - pr_err("Can't get timer clock\n"); - goto err_irq; - } - - if (clk_prepare_enable(clk)) { - pr_err("Can't prepare clock\n"); - goto err_clk_put; - } - rate = clk_get_rate(clk); - - if (request_irq(evt->dev.irq, mtk_gpt_interrupt, - IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) { - pr_err("failed to setup irq %d\n", evt->dev.irq); - goto err_clk_disable; - } - - evt->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); + ret = timer_of_init(node, &to); + if (ret) + goto err; /* Configure clock source */ - mtk_gpt_setup(evt, TIMER_CLK_SRC, GPT_CTRL_OP_FREERUN); - clocksource_mmio_init(evt->gpt_base + GPT_CNT_REG(TIMER_CLK_SRC), - node->name, rate, 300, 32, clocksource_mmio_readl_up); - gpt_sched_reg = evt->gpt_base + GPT_CNT_REG(TIMER_CLK_SRC); - sched_clock_register(mtk_gpt_read_sched_clock, 32, rate); + mtk_gpt_setup(&to, TIMER_CLK_SRC, GPT_CTRL_OP_FREERUN); + clocksource_mmio_init(timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC), + node->name, timer_of_rate(&to), 300, 32, + clocksource_mmio_readl_up); + gpt_sched_reg = timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC); + sched_clock_register(mtk_gpt_read_sched_clock, 32, timer_of_rate(&to)); /* Configure clock event */ - mtk_gpt_setup(evt, TIMER_CLK_EVT, GPT_CTRL_OP_REPEAT); - clockevents_config_and_register(&evt->dev, rate, TIMER_SYNC_TICKS, - 0xffffffff); + mtk_gpt_setup(&to, TIMER_CLK_EVT, GPT_CTRL_OP_REPEAT); + clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), + TIMER_SYNC_TICKS, 0xffffffff); - mtk_gpt_enable_irq(evt, TIMER_CLK_EVT); + mtk_gpt_enable_irq(&to, TIMER_CLK_EVT); return 0; -err_clk_disable: - clk_disable_unprepare(clk); -err_clk_put: - clk_put(clk); -err_irq: - irq_dispose_mapping(evt->dev.irq); -err_mem: - iounmap(evt->gpt_base); - of_address_to_resource(node, 0, &res); - release_mem_region(res.start, resource_size(&res)); -err_kzalloc: - kfree(evt); - - return -EINVAL; +err: + timer_of_cleanup(&to); + return ret; } TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init); From e3af677607d9daca9825bc98edfb5cedda5718a9 Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Fri, 6 Jul 2018 07:11:28 +0800 Subject: [PATCH 6/7] clocksource/drivers/timer-mediatek: Add support for system timer This patch adds a new "System Timer" on the Mediatek SoCs. The System Timer is introduced as an always-on timer being clockevent device for tick-broadcasting. For clock, it is driven by 13 MHz system clock. The implementation uses the system clock with no clock source divider. For interrupt, the clock event timer can be used by all cores. Signed-off-by: Stanley Chu Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-mediatek.c | 104 ++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-mediatek.c b/drivers/clocksource/timer-mediatek.c index e57c4d7414a4..eb10321f8517 100644 --- a/drivers/clocksource/timer-mediatek.c +++ b/drivers/clocksource/timer-mediatek.c @@ -56,8 +56,86 @@ #define GPT_CNT_REG(val) (0x08 + (0x10 * (val))) #define GPT_CMP_REG(val) (0x0C + (0x10 * (val))) +/* system timer */ +#define SYST_BASE (0x40) + +#define SYST_CON (SYST_BASE + 0x0) +#define SYST_VAL (SYST_BASE + 0x4) + +#define SYST_CON_REG(to) (timer_of_base(to) + SYST_CON) +#define SYST_VAL_REG(to) (timer_of_base(to) + SYST_VAL) + +/* + * SYST_CON_EN: Clock enable. Shall be set to + * - Start timer countdown. + * - Allow timeout ticks being updated. + * - Allow changing interrupt functions. + * + * SYST_CON_IRQ_EN: Set to allow interrupt. + * + * SYST_CON_IRQ_CLR: Set to clear interrupt. + */ +#define SYST_CON_EN BIT(0) +#define SYST_CON_IRQ_EN BIT(1) +#define SYST_CON_IRQ_CLR BIT(4) + static void __iomem *gpt_sched_reg __read_mostly; +static void mtk_syst_ack_irq(struct timer_of *to) +{ + /* Clear and disable interrupt */ + writel(SYST_CON_IRQ_CLR | SYST_CON_EN, SYST_CON_REG(to)); +} + +static irqreturn_t mtk_syst_handler(int irq, void *dev_id) +{ + struct clock_event_device *clkevt = dev_id; + struct timer_of *to = to_timer_of(clkevt); + + mtk_syst_ack_irq(to); + clkevt->event_handler(clkevt); + + return IRQ_HANDLED; +} + +static int mtk_syst_clkevt_next_event(unsigned long ticks, + struct clock_event_device *clkevt) +{ + struct timer_of *to = to_timer_of(clkevt); + + /* Enable clock to allow timeout tick update later */ + writel(SYST_CON_EN, SYST_CON_REG(to)); + + /* + * Write new timeout ticks. Timer shall start countdown + * after timeout ticks are updated. + */ + writel(ticks, SYST_VAL_REG(to)); + + /* Enable interrupt */ + writel(SYST_CON_EN | SYST_CON_IRQ_EN, SYST_CON_REG(to)); + + return 0; +} + +static int mtk_syst_clkevt_shutdown(struct clock_event_device *clkevt) +{ + /* Disable timer */ + writel(0, SYST_CON_REG(to_timer_of(clkevt))); + + return 0; +} + +static int mtk_syst_clkevt_resume(struct clock_event_device *clkevt) +{ + return mtk_syst_clkevt_shutdown(clkevt); +} + +static int mtk_syst_clkevt_oneshot(struct clock_event_device *clkevt) +{ + return 0; +} + static u64 notrace mtk_gpt_read_sched_clock(void) { return readl_relaxed(gpt_sched_reg); @@ -186,6 +264,30 @@ static struct timer_of to = { }, }; +static int __init mtk_syst_init(struct device_node *node) +{ + int ret; + + to.clkevt.features = CLOCK_EVT_FEAT_DYNIRQ | CLOCK_EVT_FEAT_ONESHOT; + to.clkevt.set_state_shutdown = mtk_syst_clkevt_shutdown; + to.clkevt.set_state_oneshot = mtk_syst_clkevt_oneshot; + to.clkevt.tick_resume = mtk_syst_clkevt_resume; + to.clkevt.set_next_event = mtk_syst_clkevt_next_event; + to.of_irq.handler = mtk_syst_handler; + + ret = timer_of_init(node, &to); + if (ret) + goto err; + + clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), + TIMER_SYNC_TICKS, 0xffffffff); + + return 0; +err: + timer_of_cleanup(&to); + return ret; +} + static int __init mtk_gpt_init(struct device_node *node) { int ret; @@ -218,9 +320,9 @@ static int __init mtk_gpt_init(struct device_node *node) mtk_gpt_enable_irq(&to, TIMER_CLK_EVT); return 0; - err: timer_of_cleanup(&to); return ret; } TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init); +TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init); From 1569557549697207e52310cc5b7dbc8e64bef504 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Tue, 17 Jul 2018 15:55:17 +0800 Subject: [PATCH 7/7] clocksource/drivers/sprd: Register one always-on timer to compensate suspend time Since the clocksource framework has introduced one suspend clocksource to compensate the suspend time, this patch registers one always-on timer as the suspend clocksource. Signed-off-by: Baolin Wang Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-sprd.c | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/clocksource/timer-sprd.c b/drivers/clocksource/timer-sprd.c index ef9ebeafb3ed..430cb99d8d79 100644 --- a/drivers/clocksource/timer-sprd.c +++ b/drivers/clocksource/timer-sprd.c @@ -156,4 +156,54 @@ static int __init sprd_timer_init(struct device_node *np) return 0; } +static struct timer_of suspend_to = { + .flags = TIMER_OF_BASE | TIMER_OF_CLOCK, +}; + +static u64 sprd_suspend_timer_read(struct clocksource *cs) +{ + return ~(u64)readl_relaxed(timer_of_base(&suspend_to) + + TIMER_VALUE_SHDW_LO) & cs->mask; +} + +static int sprd_suspend_timer_enable(struct clocksource *cs) +{ + sprd_timer_update_counter(timer_of_base(&suspend_to), + TIMER_VALUE_LO_MASK); + sprd_timer_enable(timer_of_base(&suspend_to), TIMER_CTL_PERIOD_MODE); + + return 0; +} + +static void sprd_suspend_timer_disable(struct clocksource *cs) +{ + sprd_timer_disable(timer_of_base(&suspend_to)); +} + +static struct clocksource suspend_clocksource = { + .name = "sprd_suspend_timer", + .rating = 200, + .read = sprd_suspend_timer_read, + .enable = sprd_suspend_timer_enable, + .disable = sprd_suspend_timer_disable, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP, +}; + +static int __init sprd_suspend_timer_init(struct device_node *np) +{ + int ret; + + ret = timer_of_init(np, &suspend_to); + if (ret) + return ret; + + clocksource_register_hz(&suspend_clocksource, + timer_of_rate(&suspend_to)); + + return 0; +} + TIMER_OF_DECLARE(sc9860_timer, "sprd,sc9860-timer", sprd_timer_init); +TIMER_OF_DECLARE(sc9860_persistent_timer, "sprd,sc9860-suspend-timer", + sprd_suspend_timer_init);