1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 27ec58e52SStanley Chu /* 37ec58e52SStanley Chu * Mediatek SoCs General-Purpose Timer handling. 47ec58e52SStanley Chu * 57ec58e52SStanley Chu * Copyright (C) 2014 Matthias Brugger 67ec58e52SStanley Chu * 77ec58e52SStanley Chu * Matthias Brugger <matthias.bgg@gmail.com> 87ec58e52SStanley Chu */ 97ec58e52SStanley Chu 107ec58e52SStanley Chu #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 117ec58e52SStanley Chu 127ec58e52SStanley Chu #include <linux/clockchips.h> 13a0858f93SStanley Chu #include <linux/clocksource.h> 147ec58e52SStanley Chu #include <linux/interrupt.h> 157ec58e52SStanley Chu #include <linux/irqreturn.h> 167ec58e52SStanley Chu #include <linux/sched_clock.h> 177ec58e52SStanley Chu #include <linux/slab.h> 18a0858f93SStanley Chu #include "timer-of.h" 197ec58e52SStanley Chu 2056d52d3fSStanley Chu #define TIMER_CLK_EVT (1) 2156d52d3fSStanley Chu #define TIMER_CLK_SRC (2) 2256d52d3fSStanley Chu 2356d52d3fSStanley Chu #define TIMER_SYNC_TICKS (3) 2456d52d3fSStanley Chu 2556d52d3fSStanley Chu /* gpt */ 267ec58e52SStanley Chu #define GPT_IRQ_EN_REG 0x00 277ec58e52SStanley Chu #define GPT_IRQ_ENABLE(val) BIT((val) - 1) 287ec58e52SStanley Chu #define GPT_IRQ_ACK_REG 0x08 297ec58e52SStanley Chu #define GPT_IRQ_ACK(val) BIT((val) - 1) 307ec58e52SStanley Chu 3156d52d3fSStanley Chu #define GPT_CTRL_REG(val) (0x10 * (val)) 3256d52d3fSStanley Chu #define GPT_CTRL_OP(val) (((val) & 0x3) << 4) 3356d52d3fSStanley Chu #define GPT_CTRL_OP_ONESHOT (0) 3456d52d3fSStanley Chu #define GPT_CTRL_OP_REPEAT (1) 3556d52d3fSStanley Chu #define GPT_CTRL_OP_FREERUN (3) 3656d52d3fSStanley Chu #define GPT_CTRL_CLEAR (2) 3756d52d3fSStanley Chu #define GPT_CTRL_ENABLE (1) 3856d52d3fSStanley Chu #define GPT_CTRL_DISABLE (0) 397ec58e52SStanley Chu 4056d52d3fSStanley Chu #define GPT_CLK_REG(val) (0x04 + (0x10 * (val))) 4156d52d3fSStanley Chu #define GPT_CLK_SRC(val) (((val) & 0x1) << 4) 4256d52d3fSStanley Chu #define GPT_CLK_SRC_SYS13M (0) 4356d52d3fSStanley Chu #define GPT_CLK_SRC_RTC32K (1) 4456d52d3fSStanley Chu #define GPT_CLK_DIV1 (0x0) 4556d52d3fSStanley Chu #define GPT_CLK_DIV2 (0x1) 467ec58e52SStanley Chu 4756d52d3fSStanley Chu #define GPT_CNT_REG(val) (0x08 + (0x10 * (val))) 4856d52d3fSStanley Chu #define GPT_CMP_REG(val) (0x0C + (0x10 * (val))) 497ec58e52SStanley Chu 50e3af6776SStanley Chu /* system timer */ 51e3af6776SStanley Chu #define SYST_BASE (0x40) 52e3af6776SStanley Chu 53e3af6776SStanley Chu #define SYST_CON (SYST_BASE + 0x0) 54e3af6776SStanley Chu #define SYST_VAL (SYST_BASE + 0x4) 55e3af6776SStanley Chu 56e3af6776SStanley Chu #define SYST_CON_REG(to) (timer_of_base(to) + SYST_CON) 57e3af6776SStanley Chu #define SYST_VAL_REG(to) (timer_of_base(to) + SYST_VAL) 58e3af6776SStanley Chu 59e3af6776SStanley Chu /* 60e3af6776SStanley Chu * SYST_CON_EN: Clock enable. Shall be set to 61e3af6776SStanley Chu * - Start timer countdown. 62e3af6776SStanley Chu * - Allow timeout ticks being updated. 63e3af6776SStanley Chu * - Allow changing interrupt functions. 64e3af6776SStanley Chu * 65e3af6776SStanley Chu * SYST_CON_IRQ_EN: Set to allow interrupt. 66e3af6776SStanley Chu * 67e3af6776SStanley Chu * SYST_CON_IRQ_CLR: Set to clear interrupt. 68e3af6776SStanley Chu */ 69e3af6776SStanley Chu #define SYST_CON_EN BIT(0) 70e3af6776SStanley Chu #define SYST_CON_IRQ_EN BIT(1) 71e3af6776SStanley Chu #define SYST_CON_IRQ_CLR BIT(4) 72e3af6776SStanley Chu 737ec58e52SStanley Chu static void __iomem *gpt_sched_reg __read_mostly; 747ec58e52SStanley Chu 75e3af6776SStanley Chu static void mtk_syst_ack_irq(struct timer_of *to) 76e3af6776SStanley Chu { 77e3af6776SStanley Chu /* Clear and disable interrupt */ 78e3af6776SStanley Chu writel(SYST_CON_IRQ_CLR | SYST_CON_EN, SYST_CON_REG(to)); 79e3af6776SStanley Chu } 80e3af6776SStanley Chu 81e3af6776SStanley Chu static irqreturn_t mtk_syst_handler(int irq, void *dev_id) 82e3af6776SStanley Chu { 83e3af6776SStanley Chu struct clock_event_device *clkevt = dev_id; 84e3af6776SStanley Chu struct timer_of *to = to_timer_of(clkevt); 85e3af6776SStanley Chu 86e3af6776SStanley Chu mtk_syst_ack_irq(to); 87e3af6776SStanley Chu clkevt->event_handler(clkevt); 88e3af6776SStanley Chu 89e3af6776SStanley Chu return IRQ_HANDLED; 90e3af6776SStanley Chu } 91e3af6776SStanley Chu 92e3af6776SStanley Chu static int mtk_syst_clkevt_next_event(unsigned long ticks, 93e3af6776SStanley Chu struct clock_event_device *clkevt) 94e3af6776SStanley Chu { 95e3af6776SStanley Chu struct timer_of *to = to_timer_of(clkevt); 96e3af6776SStanley Chu 97e3af6776SStanley Chu /* Enable clock to allow timeout tick update later */ 98e3af6776SStanley Chu writel(SYST_CON_EN, SYST_CON_REG(to)); 99e3af6776SStanley Chu 100e3af6776SStanley Chu /* 101e3af6776SStanley Chu * Write new timeout ticks. Timer shall start countdown 102e3af6776SStanley Chu * after timeout ticks are updated. 103e3af6776SStanley Chu */ 104e3af6776SStanley Chu writel(ticks, SYST_VAL_REG(to)); 105e3af6776SStanley Chu 106e3af6776SStanley Chu /* Enable interrupt */ 107e3af6776SStanley Chu writel(SYST_CON_EN | SYST_CON_IRQ_EN, SYST_CON_REG(to)); 108e3af6776SStanley Chu 109e3af6776SStanley Chu return 0; 110e3af6776SStanley Chu } 111e3af6776SStanley Chu 112e3af6776SStanley Chu static int mtk_syst_clkevt_shutdown(struct clock_event_device *clkevt) 113e3af6776SStanley Chu { 114e3af6776SStanley Chu /* Disable timer */ 115e3af6776SStanley Chu writel(0, SYST_CON_REG(to_timer_of(clkevt))); 116e3af6776SStanley Chu 117e3af6776SStanley Chu return 0; 118e3af6776SStanley Chu } 119e3af6776SStanley Chu 120e3af6776SStanley Chu static int mtk_syst_clkevt_resume(struct clock_event_device *clkevt) 121e3af6776SStanley Chu { 122e3af6776SStanley Chu return mtk_syst_clkevt_shutdown(clkevt); 123e3af6776SStanley Chu } 124e3af6776SStanley Chu 125e3af6776SStanley Chu static int mtk_syst_clkevt_oneshot(struct clock_event_device *clkevt) 126e3af6776SStanley Chu { 127e3af6776SStanley Chu return 0; 128e3af6776SStanley Chu } 129e3af6776SStanley Chu 13056d52d3fSStanley Chu static u64 notrace mtk_gpt_read_sched_clock(void) 1317ec58e52SStanley Chu { 1327ec58e52SStanley Chu return readl_relaxed(gpt_sched_reg); 1337ec58e52SStanley Chu } 1347ec58e52SStanley Chu 135a0858f93SStanley Chu static void mtk_gpt_clkevt_time_stop(struct timer_of *to, u8 timer) 1367ec58e52SStanley Chu { 1377ec58e52SStanley Chu u32 val; 1387ec58e52SStanley Chu 139a0858f93SStanley Chu val = readl(timer_of_base(to) + GPT_CTRL_REG(timer)); 140a0858f93SStanley Chu writel(val & ~GPT_CTRL_ENABLE, timer_of_base(to) + 14156d52d3fSStanley Chu GPT_CTRL_REG(timer)); 1427ec58e52SStanley Chu } 1437ec58e52SStanley Chu 144a0858f93SStanley Chu static void mtk_gpt_clkevt_time_setup(struct timer_of *to, 1457ec58e52SStanley Chu unsigned long delay, u8 timer) 1467ec58e52SStanley Chu { 147a0858f93SStanley Chu writel(delay, timer_of_base(to) + GPT_CMP_REG(timer)); 1487ec58e52SStanley Chu } 1497ec58e52SStanley Chu 150a0858f93SStanley Chu static void mtk_gpt_clkevt_time_start(struct timer_of *to, 1517ec58e52SStanley Chu bool periodic, u8 timer) 1527ec58e52SStanley Chu { 1537ec58e52SStanley Chu u32 val; 1547ec58e52SStanley Chu 1557ec58e52SStanley Chu /* Acknowledge interrupt */ 156a0858f93SStanley Chu writel(GPT_IRQ_ACK(timer), timer_of_base(to) + GPT_IRQ_ACK_REG); 1577ec58e52SStanley Chu 158a0858f93SStanley Chu val = readl(timer_of_base(to) + GPT_CTRL_REG(timer)); 1597ec58e52SStanley Chu 1607ec58e52SStanley Chu /* Clear 2 bit timer operation mode field */ 16156d52d3fSStanley Chu val &= ~GPT_CTRL_OP(0x3); 1627ec58e52SStanley Chu 1637ec58e52SStanley Chu if (periodic) 16456d52d3fSStanley Chu val |= GPT_CTRL_OP(GPT_CTRL_OP_REPEAT); 1657ec58e52SStanley Chu else 16656d52d3fSStanley Chu val |= GPT_CTRL_OP(GPT_CTRL_OP_ONESHOT); 1677ec58e52SStanley Chu 16856d52d3fSStanley Chu writel(val | GPT_CTRL_ENABLE | GPT_CTRL_CLEAR, 169a0858f93SStanley Chu timer_of_base(to) + GPT_CTRL_REG(timer)); 1707ec58e52SStanley Chu } 1717ec58e52SStanley Chu 17256d52d3fSStanley Chu static int mtk_gpt_clkevt_shutdown(struct clock_event_device *clk) 1737ec58e52SStanley Chu { 174a0858f93SStanley Chu mtk_gpt_clkevt_time_stop(to_timer_of(clk), TIMER_CLK_EVT); 175a0858f93SStanley Chu 1767ec58e52SStanley Chu return 0; 1777ec58e52SStanley Chu } 1787ec58e52SStanley Chu 17956d52d3fSStanley Chu static int mtk_gpt_clkevt_set_periodic(struct clock_event_device *clk) 1807ec58e52SStanley Chu { 181a0858f93SStanley Chu struct timer_of *to = to_timer_of(clk); 1827ec58e52SStanley Chu 183a0858f93SStanley Chu mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT); 184a0858f93SStanley Chu mtk_gpt_clkevt_time_setup(to, to->of_clk.period, TIMER_CLK_EVT); 185a0858f93SStanley Chu mtk_gpt_clkevt_time_start(to, true, TIMER_CLK_EVT); 186a0858f93SStanley Chu 1877ec58e52SStanley Chu return 0; 1887ec58e52SStanley Chu } 1897ec58e52SStanley Chu 19056d52d3fSStanley Chu static int mtk_gpt_clkevt_next_event(unsigned long event, 1917ec58e52SStanley Chu struct clock_event_device *clk) 1927ec58e52SStanley Chu { 193a0858f93SStanley Chu struct timer_of *to = to_timer_of(clk); 1947ec58e52SStanley Chu 195a0858f93SStanley Chu mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT); 196a0858f93SStanley Chu mtk_gpt_clkevt_time_setup(to, event, TIMER_CLK_EVT); 197a0858f93SStanley Chu mtk_gpt_clkevt_time_start(to, false, TIMER_CLK_EVT); 1987ec58e52SStanley Chu 1997ec58e52SStanley Chu return 0; 2007ec58e52SStanley Chu } 2017ec58e52SStanley Chu 20256d52d3fSStanley Chu static irqreturn_t mtk_gpt_interrupt(int irq, void *dev_id) 2037ec58e52SStanley Chu { 204a0858f93SStanley Chu struct clock_event_device *clkevt = (struct clock_event_device *)dev_id; 205a0858f93SStanley Chu struct timer_of *to = to_timer_of(clkevt); 2067ec58e52SStanley Chu 2077ec58e52SStanley Chu /* Acknowledge timer0 irq */ 208a0858f93SStanley Chu writel(GPT_IRQ_ACK(TIMER_CLK_EVT), timer_of_base(to) + GPT_IRQ_ACK_REG); 209a0858f93SStanley Chu clkevt->event_handler(clkevt); 2107ec58e52SStanley Chu 2117ec58e52SStanley Chu return IRQ_HANDLED; 2127ec58e52SStanley Chu } 2137ec58e52SStanley Chu 2147ec58e52SStanley Chu static void 215a0858f93SStanley Chu __init mtk_gpt_setup(struct timer_of *to, u8 timer, u8 option) 2167ec58e52SStanley Chu { 21756d52d3fSStanley Chu writel(GPT_CTRL_CLEAR | GPT_CTRL_DISABLE, 218a0858f93SStanley Chu timer_of_base(to) + GPT_CTRL_REG(timer)); 2197ec58e52SStanley Chu 22056d52d3fSStanley Chu writel(GPT_CLK_SRC(GPT_CLK_SRC_SYS13M) | GPT_CLK_DIV1, 221a0858f93SStanley Chu timer_of_base(to) + GPT_CLK_REG(timer)); 2227ec58e52SStanley Chu 223a0858f93SStanley Chu writel(0x0, timer_of_base(to) + GPT_CMP_REG(timer)); 2247ec58e52SStanley Chu 22556d52d3fSStanley Chu writel(GPT_CTRL_OP(option) | GPT_CTRL_ENABLE, 226a0858f93SStanley Chu timer_of_base(to) + GPT_CTRL_REG(timer)); 2277ec58e52SStanley Chu } 2287ec58e52SStanley Chu 229a0858f93SStanley Chu static void mtk_gpt_enable_irq(struct timer_of *to, u8 timer) 2307ec58e52SStanley Chu { 2317ec58e52SStanley Chu u32 val; 2327ec58e52SStanley Chu 2337ec58e52SStanley Chu /* Disable all interrupts */ 234a0858f93SStanley Chu writel(0x0, timer_of_base(to) + GPT_IRQ_EN_REG); 2357ec58e52SStanley Chu 2367ec58e52SStanley Chu /* Acknowledge all spurious pending interrupts */ 237a0858f93SStanley Chu writel(0x3f, timer_of_base(to) + GPT_IRQ_ACK_REG); 2387ec58e52SStanley Chu 239a0858f93SStanley Chu val = readl(timer_of_base(to) + GPT_IRQ_EN_REG); 2407ec58e52SStanley Chu writel(val | GPT_IRQ_ENABLE(timer), 241a0858f93SStanley Chu timer_of_base(to) + GPT_IRQ_EN_REG); 2427ec58e52SStanley Chu } 2437ec58e52SStanley Chu 244*75ac5cc2SEvan Benn static void mtk_gpt_resume(struct clock_event_device *clk) 245*75ac5cc2SEvan Benn { 246*75ac5cc2SEvan Benn struct timer_of *to = to_timer_of(clk); 247*75ac5cc2SEvan Benn 248*75ac5cc2SEvan Benn mtk_gpt_enable_irq(to, TIMER_CLK_EVT); 249*75ac5cc2SEvan Benn } 250*75ac5cc2SEvan Benn 251*75ac5cc2SEvan Benn static void mtk_gpt_suspend(struct clock_event_device *clk) 252*75ac5cc2SEvan Benn { 253*75ac5cc2SEvan Benn struct timer_of *to = to_timer_of(clk); 254*75ac5cc2SEvan Benn 255*75ac5cc2SEvan Benn /* Disable all interrupts */ 256*75ac5cc2SEvan Benn writel(0x0, timer_of_base(to) + GPT_IRQ_EN_REG); 257*75ac5cc2SEvan Benn 258*75ac5cc2SEvan Benn /* 259*75ac5cc2SEvan Benn * This is called with interrupts disabled, 260*75ac5cc2SEvan Benn * so we need to ack any interrupt that is pending 261*75ac5cc2SEvan Benn * or for example ATF will prevent a suspend from completing. 262*75ac5cc2SEvan Benn */ 263*75ac5cc2SEvan Benn writel(0x3f, timer_of_base(to) + GPT_IRQ_ACK_REG); 264*75ac5cc2SEvan Benn } 265*75ac5cc2SEvan Benn 266a0858f93SStanley Chu static struct timer_of to = { 267a0858f93SStanley Chu .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, 268a0858f93SStanley Chu 269a0858f93SStanley Chu .clkevt = { 270a0858f93SStanley Chu .name = "mtk-clkevt", 271a0858f93SStanley Chu .rating = 300, 272a0858f93SStanley Chu .cpumask = cpu_possible_mask, 273a0858f93SStanley Chu }, 274a0858f93SStanley Chu 275a0858f93SStanley Chu .of_irq = { 276a0858f93SStanley Chu .flags = IRQF_TIMER | IRQF_IRQPOLL, 277a0858f93SStanley Chu }, 278a0858f93SStanley Chu }; 279a0858f93SStanley Chu 280e3af6776SStanley Chu static int __init mtk_syst_init(struct device_node *node) 281e3af6776SStanley Chu { 282e3af6776SStanley Chu int ret; 283e3af6776SStanley Chu 284e3af6776SStanley Chu to.clkevt.features = CLOCK_EVT_FEAT_DYNIRQ | CLOCK_EVT_FEAT_ONESHOT; 285e3af6776SStanley Chu to.clkevt.set_state_shutdown = mtk_syst_clkevt_shutdown; 286e3af6776SStanley Chu to.clkevt.set_state_oneshot = mtk_syst_clkevt_oneshot; 287e3af6776SStanley Chu to.clkevt.tick_resume = mtk_syst_clkevt_resume; 288e3af6776SStanley Chu to.clkevt.set_next_event = mtk_syst_clkevt_next_event; 289e3af6776SStanley Chu to.of_irq.handler = mtk_syst_handler; 290e3af6776SStanley Chu 291e3af6776SStanley Chu ret = timer_of_init(node, &to); 292e3af6776SStanley Chu if (ret) 29341d49e79SFabien Parent return ret; 294e3af6776SStanley Chu 295e3af6776SStanley Chu clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 296e3af6776SStanley Chu TIMER_SYNC_TICKS, 0xffffffff); 297e3af6776SStanley Chu 298e3af6776SStanley Chu return 0; 299e3af6776SStanley Chu } 300e3af6776SStanley Chu 30156d52d3fSStanley Chu static int __init mtk_gpt_init(struct device_node *node) 3027ec58e52SStanley Chu { 303a0858f93SStanley Chu int ret; 3047ec58e52SStanley Chu 305a0858f93SStanley Chu to.clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 306a0858f93SStanley Chu to.clkevt.set_state_shutdown = mtk_gpt_clkevt_shutdown; 307a0858f93SStanley Chu to.clkevt.set_state_periodic = mtk_gpt_clkevt_set_periodic; 308a0858f93SStanley Chu to.clkevt.set_state_oneshot = mtk_gpt_clkevt_shutdown; 309a0858f93SStanley Chu to.clkevt.tick_resume = mtk_gpt_clkevt_shutdown; 310a0858f93SStanley Chu to.clkevt.set_next_event = mtk_gpt_clkevt_next_event; 311*75ac5cc2SEvan Benn to.clkevt.suspend = mtk_gpt_suspend; 312*75ac5cc2SEvan Benn to.clkevt.resume = mtk_gpt_resume; 313a0858f93SStanley Chu to.of_irq.handler = mtk_gpt_interrupt; 3147ec58e52SStanley Chu 315a0858f93SStanley Chu ret = timer_of_init(node, &to); 316a0858f93SStanley Chu if (ret) 31741d49e79SFabien Parent return ret; 3187ec58e52SStanley Chu 3197ec58e52SStanley Chu /* Configure clock source */ 320a0858f93SStanley Chu mtk_gpt_setup(&to, TIMER_CLK_SRC, GPT_CTRL_OP_FREERUN); 321a0858f93SStanley Chu clocksource_mmio_init(timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC), 322a0858f93SStanley Chu node->name, timer_of_rate(&to), 300, 32, 323a0858f93SStanley Chu clocksource_mmio_readl_up); 324a0858f93SStanley Chu gpt_sched_reg = timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC); 325a0858f93SStanley Chu sched_clock_register(mtk_gpt_read_sched_clock, 32, timer_of_rate(&to)); 3267ec58e52SStanley Chu 3277ec58e52SStanley Chu /* Configure clock event */ 328a0858f93SStanley Chu mtk_gpt_setup(&to, TIMER_CLK_EVT, GPT_CTRL_OP_REPEAT); 329a0858f93SStanley Chu clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 330a0858f93SStanley Chu TIMER_SYNC_TICKS, 0xffffffff); 3317ec58e52SStanley Chu 332a0858f93SStanley Chu mtk_gpt_enable_irq(&to, TIMER_CLK_EVT); 3337ec58e52SStanley Chu 3347ec58e52SStanley Chu return 0; 3357ec58e52SStanley Chu } 33656d52d3fSStanley Chu TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init); 337e3af6776SStanley Chu TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init); 338