17ec58e52SStanley Chu /* 27ec58e52SStanley Chu * Mediatek SoCs General-Purpose Timer handling. 37ec58e52SStanley Chu * 47ec58e52SStanley Chu * Copyright (C) 2014 Matthias Brugger 57ec58e52SStanley Chu * 67ec58e52SStanley Chu * Matthias Brugger <matthias.bgg@gmail.com> 77ec58e52SStanley Chu * 87ec58e52SStanley Chu * This program is free software; you can redistribute it and/or modify 97ec58e52SStanley Chu * it under the terms of the GNU General Public License as published by 107ec58e52SStanley Chu * the Free Software Foundation; either version 2 of the License, or 117ec58e52SStanley Chu * (at your option) any later version. 127ec58e52SStanley Chu * 137ec58e52SStanley Chu * This program is distributed in the hope that it will be useful, 147ec58e52SStanley Chu * but WITHOUT ANY WARRANTY; without even the implied warranty of 157ec58e52SStanley Chu * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 167ec58e52SStanley Chu * GNU General Public License for more details. 177ec58e52SStanley Chu */ 187ec58e52SStanley Chu 197ec58e52SStanley Chu #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 207ec58e52SStanley Chu 217ec58e52SStanley Chu #include <linux/clockchips.h> 22a0858f93SStanley Chu #include <linux/clocksource.h> 237ec58e52SStanley Chu #include <linux/interrupt.h> 247ec58e52SStanley Chu #include <linux/irqreturn.h> 257ec58e52SStanley Chu #include <linux/sched_clock.h> 267ec58e52SStanley Chu #include <linux/slab.h> 27a0858f93SStanley Chu #include "timer-of.h" 287ec58e52SStanley Chu 2956d52d3fSStanley Chu #define TIMER_CLK_EVT (1) 3056d52d3fSStanley Chu #define TIMER_CLK_SRC (2) 3156d52d3fSStanley Chu 3256d52d3fSStanley Chu #define TIMER_SYNC_TICKS (3) 3356d52d3fSStanley Chu 3456d52d3fSStanley Chu /* gpt */ 357ec58e52SStanley Chu #define GPT_IRQ_EN_REG 0x00 367ec58e52SStanley Chu #define GPT_IRQ_ENABLE(val) BIT((val) - 1) 377ec58e52SStanley Chu #define GPT_IRQ_ACK_REG 0x08 387ec58e52SStanley Chu #define GPT_IRQ_ACK(val) BIT((val) - 1) 397ec58e52SStanley Chu 4056d52d3fSStanley Chu #define GPT_CTRL_REG(val) (0x10 * (val)) 4156d52d3fSStanley Chu #define GPT_CTRL_OP(val) (((val) & 0x3) << 4) 4256d52d3fSStanley Chu #define GPT_CTRL_OP_ONESHOT (0) 4356d52d3fSStanley Chu #define GPT_CTRL_OP_REPEAT (1) 4456d52d3fSStanley Chu #define GPT_CTRL_OP_FREERUN (3) 4556d52d3fSStanley Chu #define GPT_CTRL_CLEAR (2) 4656d52d3fSStanley Chu #define GPT_CTRL_ENABLE (1) 4756d52d3fSStanley Chu #define GPT_CTRL_DISABLE (0) 487ec58e52SStanley Chu 4956d52d3fSStanley Chu #define GPT_CLK_REG(val) (0x04 + (0x10 * (val))) 5056d52d3fSStanley Chu #define GPT_CLK_SRC(val) (((val) & 0x1) << 4) 5156d52d3fSStanley Chu #define GPT_CLK_SRC_SYS13M (0) 5256d52d3fSStanley Chu #define GPT_CLK_SRC_RTC32K (1) 5356d52d3fSStanley Chu #define GPT_CLK_DIV1 (0x0) 5456d52d3fSStanley Chu #define GPT_CLK_DIV2 (0x1) 557ec58e52SStanley Chu 5656d52d3fSStanley Chu #define GPT_CNT_REG(val) (0x08 + (0x10 * (val))) 5756d52d3fSStanley Chu #define GPT_CMP_REG(val) (0x0C + (0x10 * (val))) 587ec58e52SStanley Chu 59*e3af6776SStanley Chu /* system timer */ 60*e3af6776SStanley Chu #define SYST_BASE (0x40) 61*e3af6776SStanley Chu 62*e3af6776SStanley Chu #define SYST_CON (SYST_BASE + 0x0) 63*e3af6776SStanley Chu #define SYST_VAL (SYST_BASE + 0x4) 64*e3af6776SStanley Chu 65*e3af6776SStanley Chu #define SYST_CON_REG(to) (timer_of_base(to) + SYST_CON) 66*e3af6776SStanley Chu #define SYST_VAL_REG(to) (timer_of_base(to) + SYST_VAL) 67*e3af6776SStanley Chu 68*e3af6776SStanley Chu /* 69*e3af6776SStanley Chu * SYST_CON_EN: Clock enable. Shall be set to 70*e3af6776SStanley Chu * - Start timer countdown. 71*e3af6776SStanley Chu * - Allow timeout ticks being updated. 72*e3af6776SStanley Chu * - Allow changing interrupt functions. 73*e3af6776SStanley Chu * 74*e3af6776SStanley Chu * SYST_CON_IRQ_EN: Set to allow interrupt. 75*e3af6776SStanley Chu * 76*e3af6776SStanley Chu * SYST_CON_IRQ_CLR: Set to clear interrupt. 77*e3af6776SStanley Chu */ 78*e3af6776SStanley Chu #define SYST_CON_EN BIT(0) 79*e3af6776SStanley Chu #define SYST_CON_IRQ_EN BIT(1) 80*e3af6776SStanley Chu #define SYST_CON_IRQ_CLR BIT(4) 81*e3af6776SStanley Chu 827ec58e52SStanley Chu static void __iomem *gpt_sched_reg __read_mostly; 837ec58e52SStanley Chu 84*e3af6776SStanley Chu static void mtk_syst_ack_irq(struct timer_of *to) 85*e3af6776SStanley Chu { 86*e3af6776SStanley Chu /* Clear and disable interrupt */ 87*e3af6776SStanley Chu writel(SYST_CON_IRQ_CLR | SYST_CON_EN, SYST_CON_REG(to)); 88*e3af6776SStanley Chu } 89*e3af6776SStanley Chu 90*e3af6776SStanley Chu static irqreturn_t mtk_syst_handler(int irq, void *dev_id) 91*e3af6776SStanley Chu { 92*e3af6776SStanley Chu struct clock_event_device *clkevt = dev_id; 93*e3af6776SStanley Chu struct timer_of *to = to_timer_of(clkevt); 94*e3af6776SStanley Chu 95*e3af6776SStanley Chu mtk_syst_ack_irq(to); 96*e3af6776SStanley Chu clkevt->event_handler(clkevt); 97*e3af6776SStanley Chu 98*e3af6776SStanley Chu return IRQ_HANDLED; 99*e3af6776SStanley Chu } 100*e3af6776SStanley Chu 101*e3af6776SStanley Chu static int mtk_syst_clkevt_next_event(unsigned long ticks, 102*e3af6776SStanley Chu struct clock_event_device *clkevt) 103*e3af6776SStanley Chu { 104*e3af6776SStanley Chu struct timer_of *to = to_timer_of(clkevt); 105*e3af6776SStanley Chu 106*e3af6776SStanley Chu /* Enable clock to allow timeout tick update later */ 107*e3af6776SStanley Chu writel(SYST_CON_EN, SYST_CON_REG(to)); 108*e3af6776SStanley Chu 109*e3af6776SStanley Chu /* 110*e3af6776SStanley Chu * Write new timeout ticks. Timer shall start countdown 111*e3af6776SStanley Chu * after timeout ticks are updated. 112*e3af6776SStanley Chu */ 113*e3af6776SStanley Chu writel(ticks, SYST_VAL_REG(to)); 114*e3af6776SStanley Chu 115*e3af6776SStanley Chu /* Enable interrupt */ 116*e3af6776SStanley Chu writel(SYST_CON_EN | SYST_CON_IRQ_EN, SYST_CON_REG(to)); 117*e3af6776SStanley Chu 118*e3af6776SStanley Chu return 0; 119*e3af6776SStanley Chu } 120*e3af6776SStanley Chu 121*e3af6776SStanley Chu static int mtk_syst_clkevt_shutdown(struct clock_event_device *clkevt) 122*e3af6776SStanley Chu { 123*e3af6776SStanley Chu /* Disable timer */ 124*e3af6776SStanley Chu writel(0, SYST_CON_REG(to_timer_of(clkevt))); 125*e3af6776SStanley Chu 126*e3af6776SStanley Chu return 0; 127*e3af6776SStanley Chu } 128*e3af6776SStanley Chu 129*e3af6776SStanley Chu static int mtk_syst_clkevt_resume(struct clock_event_device *clkevt) 130*e3af6776SStanley Chu { 131*e3af6776SStanley Chu return mtk_syst_clkevt_shutdown(clkevt); 132*e3af6776SStanley Chu } 133*e3af6776SStanley Chu 134*e3af6776SStanley Chu static int mtk_syst_clkevt_oneshot(struct clock_event_device *clkevt) 135*e3af6776SStanley Chu { 136*e3af6776SStanley Chu return 0; 137*e3af6776SStanley Chu } 138*e3af6776SStanley Chu 13956d52d3fSStanley Chu static u64 notrace mtk_gpt_read_sched_clock(void) 1407ec58e52SStanley Chu { 1417ec58e52SStanley Chu return readl_relaxed(gpt_sched_reg); 1427ec58e52SStanley Chu } 1437ec58e52SStanley Chu 144a0858f93SStanley Chu static void mtk_gpt_clkevt_time_stop(struct timer_of *to, u8 timer) 1457ec58e52SStanley Chu { 1467ec58e52SStanley Chu u32 val; 1477ec58e52SStanley Chu 148a0858f93SStanley Chu val = readl(timer_of_base(to) + GPT_CTRL_REG(timer)); 149a0858f93SStanley Chu writel(val & ~GPT_CTRL_ENABLE, timer_of_base(to) + 15056d52d3fSStanley Chu GPT_CTRL_REG(timer)); 1517ec58e52SStanley Chu } 1527ec58e52SStanley Chu 153a0858f93SStanley Chu static void mtk_gpt_clkevt_time_setup(struct timer_of *to, 1547ec58e52SStanley Chu unsigned long delay, u8 timer) 1557ec58e52SStanley Chu { 156a0858f93SStanley Chu writel(delay, timer_of_base(to) + GPT_CMP_REG(timer)); 1577ec58e52SStanley Chu } 1587ec58e52SStanley Chu 159a0858f93SStanley Chu static void mtk_gpt_clkevt_time_start(struct timer_of *to, 1607ec58e52SStanley Chu bool periodic, u8 timer) 1617ec58e52SStanley Chu { 1627ec58e52SStanley Chu u32 val; 1637ec58e52SStanley Chu 1647ec58e52SStanley Chu /* Acknowledge interrupt */ 165a0858f93SStanley Chu writel(GPT_IRQ_ACK(timer), timer_of_base(to) + GPT_IRQ_ACK_REG); 1667ec58e52SStanley Chu 167a0858f93SStanley Chu val = readl(timer_of_base(to) + GPT_CTRL_REG(timer)); 1687ec58e52SStanley Chu 1697ec58e52SStanley Chu /* Clear 2 bit timer operation mode field */ 17056d52d3fSStanley Chu val &= ~GPT_CTRL_OP(0x3); 1717ec58e52SStanley Chu 1727ec58e52SStanley Chu if (periodic) 17356d52d3fSStanley Chu val |= GPT_CTRL_OP(GPT_CTRL_OP_REPEAT); 1747ec58e52SStanley Chu else 17556d52d3fSStanley Chu val |= GPT_CTRL_OP(GPT_CTRL_OP_ONESHOT); 1767ec58e52SStanley Chu 17756d52d3fSStanley Chu writel(val | GPT_CTRL_ENABLE | GPT_CTRL_CLEAR, 178a0858f93SStanley Chu timer_of_base(to) + GPT_CTRL_REG(timer)); 1797ec58e52SStanley Chu } 1807ec58e52SStanley Chu 18156d52d3fSStanley Chu static int mtk_gpt_clkevt_shutdown(struct clock_event_device *clk) 1827ec58e52SStanley Chu { 183a0858f93SStanley Chu mtk_gpt_clkevt_time_stop(to_timer_of(clk), TIMER_CLK_EVT); 184a0858f93SStanley Chu 1857ec58e52SStanley Chu return 0; 1867ec58e52SStanley Chu } 1877ec58e52SStanley Chu 18856d52d3fSStanley Chu static int mtk_gpt_clkevt_set_periodic(struct clock_event_device *clk) 1897ec58e52SStanley Chu { 190a0858f93SStanley Chu struct timer_of *to = to_timer_of(clk); 1917ec58e52SStanley Chu 192a0858f93SStanley Chu mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT); 193a0858f93SStanley Chu mtk_gpt_clkevt_time_setup(to, to->of_clk.period, TIMER_CLK_EVT); 194a0858f93SStanley Chu mtk_gpt_clkevt_time_start(to, true, TIMER_CLK_EVT); 195a0858f93SStanley Chu 1967ec58e52SStanley Chu return 0; 1977ec58e52SStanley Chu } 1987ec58e52SStanley Chu 19956d52d3fSStanley Chu static int mtk_gpt_clkevt_next_event(unsigned long event, 2007ec58e52SStanley Chu struct clock_event_device *clk) 2017ec58e52SStanley Chu { 202a0858f93SStanley Chu struct timer_of *to = to_timer_of(clk); 2037ec58e52SStanley Chu 204a0858f93SStanley Chu mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT); 205a0858f93SStanley Chu mtk_gpt_clkevt_time_setup(to, event, TIMER_CLK_EVT); 206a0858f93SStanley Chu mtk_gpt_clkevt_time_start(to, false, TIMER_CLK_EVT); 2077ec58e52SStanley Chu 2087ec58e52SStanley Chu return 0; 2097ec58e52SStanley Chu } 2107ec58e52SStanley Chu 21156d52d3fSStanley Chu static irqreturn_t mtk_gpt_interrupt(int irq, void *dev_id) 2127ec58e52SStanley Chu { 213a0858f93SStanley Chu struct clock_event_device *clkevt = (struct clock_event_device *)dev_id; 214a0858f93SStanley Chu struct timer_of *to = to_timer_of(clkevt); 2157ec58e52SStanley Chu 2167ec58e52SStanley Chu /* Acknowledge timer0 irq */ 217a0858f93SStanley Chu writel(GPT_IRQ_ACK(TIMER_CLK_EVT), timer_of_base(to) + GPT_IRQ_ACK_REG); 218a0858f93SStanley Chu clkevt->event_handler(clkevt); 2197ec58e52SStanley Chu 2207ec58e52SStanley Chu return IRQ_HANDLED; 2217ec58e52SStanley Chu } 2227ec58e52SStanley Chu 2237ec58e52SStanley Chu static void 224a0858f93SStanley Chu __init mtk_gpt_setup(struct timer_of *to, u8 timer, u8 option) 2257ec58e52SStanley Chu { 22656d52d3fSStanley Chu writel(GPT_CTRL_CLEAR | GPT_CTRL_DISABLE, 227a0858f93SStanley Chu timer_of_base(to) + GPT_CTRL_REG(timer)); 2287ec58e52SStanley Chu 22956d52d3fSStanley Chu writel(GPT_CLK_SRC(GPT_CLK_SRC_SYS13M) | GPT_CLK_DIV1, 230a0858f93SStanley Chu timer_of_base(to) + GPT_CLK_REG(timer)); 2317ec58e52SStanley Chu 232a0858f93SStanley Chu writel(0x0, timer_of_base(to) + GPT_CMP_REG(timer)); 2337ec58e52SStanley Chu 23456d52d3fSStanley Chu writel(GPT_CTRL_OP(option) | GPT_CTRL_ENABLE, 235a0858f93SStanley Chu timer_of_base(to) + GPT_CTRL_REG(timer)); 2367ec58e52SStanley Chu } 2377ec58e52SStanley Chu 238a0858f93SStanley Chu static void mtk_gpt_enable_irq(struct timer_of *to, u8 timer) 2397ec58e52SStanley Chu { 2407ec58e52SStanley Chu u32 val; 2417ec58e52SStanley Chu 2427ec58e52SStanley Chu /* Disable all interrupts */ 243a0858f93SStanley Chu writel(0x0, timer_of_base(to) + GPT_IRQ_EN_REG); 2447ec58e52SStanley Chu 2457ec58e52SStanley Chu /* Acknowledge all spurious pending interrupts */ 246a0858f93SStanley Chu writel(0x3f, timer_of_base(to) + GPT_IRQ_ACK_REG); 2477ec58e52SStanley Chu 248a0858f93SStanley Chu val = readl(timer_of_base(to) + GPT_IRQ_EN_REG); 2497ec58e52SStanley Chu writel(val | GPT_IRQ_ENABLE(timer), 250a0858f93SStanley Chu timer_of_base(to) + GPT_IRQ_EN_REG); 2517ec58e52SStanley Chu } 2527ec58e52SStanley Chu 253a0858f93SStanley Chu static struct timer_of to = { 254a0858f93SStanley Chu .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, 255a0858f93SStanley Chu 256a0858f93SStanley Chu .clkevt = { 257a0858f93SStanley Chu .name = "mtk-clkevt", 258a0858f93SStanley Chu .rating = 300, 259a0858f93SStanley Chu .cpumask = cpu_possible_mask, 260a0858f93SStanley Chu }, 261a0858f93SStanley Chu 262a0858f93SStanley Chu .of_irq = { 263a0858f93SStanley Chu .flags = IRQF_TIMER | IRQF_IRQPOLL, 264a0858f93SStanley Chu }, 265a0858f93SStanley Chu }; 266a0858f93SStanley Chu 267*e3af6776SStanley Chu static int __init mtk_syst_init(struct device_node *node) 268*e3af6776SStanley Chu { 269*e3af6776SStanley Chu int ret; 270*e3af6776SStanley Chu 271*e3af6776SStanley Chu to.clkevt.features = CLOCK_EVT_FEAT_DYNIRQ | CLOCK_EVT_FEAT_ONESHOT; 272*e3af6776SStanley Chu to.clkevt.set_state_shutdown = mtk_syst_clkevt_shutdown; 273*e3af6776SStanley Chu to.clkevt.set_state_oneshot = mtk_syst_clkevt_oneshot; 274*e3af6776SStanley Chu to.clkevt.tick_resume = mtk_syst_clkevt_resume; 275*e3af6776SStanley Chu to.clkevt.set_next_event = mtk_syst_clkevt_next_event; 276*e3af6776SStanley Chu to.of_irq.handler = mtk_syst_handler; 277*e3af6776SStanley Chu 278*e3af6776SStanley Chu ret = timer_of_init(node, &to); 279*e3af6776SStanley Chu if (ret) 280*e3af6776SStanley Chu goto err; 281*e3af6776SStanley Chu 282*e3af6776SStanley Chu clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 283*e3af6776SStanley Chu TIMER_SYNC_TICKS, 0xffffffff); 284*e3af6776SStanley Chu 285*e3af6776SStanley Chu return 0; 286*e3af6776SStanley Chu err: 287*e3af6776SStanley Chu timer_of_cleanup(&to); 288*e3af6776SStanley Chu return ret; 289*e3af6776SStanley Chu } 290*e3af6776SStanley Chu 29156d52d3fSStanley Chu static int __init mtk_gpt_init(struct device_node *node) 2927ec58e52SStanley Chu { 293a0858f93SStanley Chu int ret; 2947ec58e52SStanley Chu 295a0858f93SStanley Chu to.clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 296a0858f93SStanley Chu to.clkevt.set_state_shutdown = mtk_gpt_clkevt_shutdown; 297a0858f93SStanley Chu to.clkevt.set_state_periodic = mtk_gpt_clkevt_set_periodic; 298a0858f93SStanley Chu to.clkevt.set_state_oneshot = mtk_gpt_clkevt_shutdown; 299a0858f93SStanley Chu to.clkevt.tick_resume = mtk_gpt_clkevt_shutdown; 300a0858f93SStanley Chu to.clkevt.set_next_event = mtk_gpt_clkevt_next_event; 301a0858f93SStanley Chu to.of_irq.handler = mtk_gpt_interrupt; 3027ec58e52SStanley Chu 303a0858f93SStanley Chu ret = timer_of_init(node, &to); 304a0858f93SStanley Chu if (ret) 305a0858f93SStanley Chu goto err; 3067ec58e52SStanley Chu 3077ec58e52SStanley Chu /* Configure clock source */ 308a0858f93SStanley Chu mtk_gpt_setup(&to, TIMER_CLK_SRC, GPT_CTRL_OP_FREERUN); 309a0858f93SStanley Chu clocksource_mmio_init(timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC), 310a0858f93SStanley Chu node->name, timer_of_rate(&to), 300, 32, 311a0858f93SStanley Chu clocksource_mmio_readl_up); 312a0858f93SStanley Chu gpt_sched_reg = timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC); 313a0858f93SStanley Chu sched_clock_register(mtk_gpt_read_sched_clock, 32, timer_of_rate(&to)); 3147ec58e52SStanley Chu 3157ec58e52SStanley Chu /* Configure clock event */ 316a0858f93SStanley Chu mtk_gpt_setup(&to, TIMER_CLK_EVT, GPT_CTRL_OP_REPEAT); 317a0858f93SStanley Chu clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 318a0858f93SStanley Chu TIMER_SYNC_TICKS, 0xffffffff); 3197ec58e52SStanley Chu 320a0858f93SStanley Chu mtk_gpt_enable_irq(&to, TIMER_CLK_EVT); 3217ec58e52SStanley Chu 3227ec58e52SStanley Chu return 0; 323a0858f93SStanley Chu err: 324a0858f93SStanley Chu timer_of_cleanup(&to); 325a0858f93SStanley Chu return ret; 3267ec58e52SStanley Chu } 32756d52d3fSStanley Chu TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init); 328*e3af6776SStanley Chu TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init); 329