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> 22*a0858f93SStanley 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> 27*a0858f93SStanley 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 597ec58e52SStanley Chu static void __iomem *gpt_sched_reg __read_mostly; 607ec58e52SStanley Chu 6156d52d3fSStanley Chu static u64 notrace mtk_gpt_read_sched_clock(void) 627ec58e52SStanley Chu { 637ec58e52SStanley Chu return readl_relaxed(gpt_sched_reg); 647ec58e52SStanley Chu } 657ec58e52SStanley Chu 66*a0858f93SStanley Chu static void mtk_gpt_clkevt_time_stop(struct timer_of *to, u8 timer) 677ec58e52SStanley Chu { 687ec58e52SStanley Chu u32 val; 697ec58e52SStanley Chu 70*a0858f93SStanley Chu val = readl(timer_of_base(to) + GPT_CTRL_REG(timer)); 71*a0858f93SStanley Chu writel(val & ~GPT_CTRL_ENABLE, timer_of_base(to) + 7256d52d3fSStanley Chu GPT_CTRL_REG(timer)); 737ec58e52SStanley Chu } 747ec58e52SStanley Chu 75*a0858f93SStanley Chu static void mtk_gpt_clkevt_time_setup(struct timer_of *to, 767ec58e52SStanley Chu unsigned long delay, u8 timer) 777ec58e52SStanley Chu { 78*a0858f93SStanley Chu writel(delay, timer_of_base(to) + GPT_CMP_REG(timer)); 797ec58e52SStanley Chu } 807ec58e52SStanley Chu 81*a0858f93SStanley Chu static void mtk_gpt_clkevt_time_start(struct timer_of *to, 827ec58e52SStanley Chu bool periodic, u8 timer) 837ec58e52SStanley Chu { 847ec58e52SStanley Chu u32 val; 857ec58e52SStanley Chu 867ec58e52SStanley Chu /* Acknowledge interrupt */ 87*a0858f93SStanley Chu writel(GPT_IRQ_ACK(timer), timer_of_base(to) + GPT_IRQ_ACK_REG); 887ec58e52SStanley Chu 89*a0858f93SStanley Chu val = readl(timer_of_base(to) + GPT_CTRL_REG(timer)); 907ec58e52SStanley Chu 917ec58e52SStanley Chu /* Clear 2 bit timer operation mode field */ 9256d52d3fSStanley Chu val &= ~GPT_CTRL_OP(0x3); 937ec58e52SStanley Chu 947ec58e52SStanley Chu if (periodic) 9556d52d3fSStanley Chu val |= GPT_CTRL_OP(GPT_CTRL_OP_REPEAT); 967ec58e52SStanley Chu else 9756d52d3fSStanley Chu val |= GPT_CTRL_OP(GPT_CTRL_OP_ONESHOT); 987ec58e52SStanley Chu 9956d52d3fSStanley Chu writel(val | GPT_CTRL_ENABLE | GPT_CTRL_CLEAR, 100*a0858f93SStanley Chu timer_of_base(to) + GPT_CTRL_REG(timer)); 1017ec58e52SStanley Chu } 1027ec58e52SStanley Chu 10356d52d3fSStanley Chu static int mtk_gpt_clkevt_shutdown(struct clock_event_device *clk) 1047ec58e52SStanley Chu { 105*a0858f93SStanley Chu mtk_gpt_clkevt_time_stop(to_timer_of(clk), TIMER_CLK_EVT); 106*a0858f93SStanley Chu 1077ec58e52SStanley Chu return 0; 1087ec58e52SStanley Chu } 1097ec58e52SStanley Chu 11056d52d3fSStanley Chu static int mtk_gpt_clkevt_set_periodic(struct clock_event_device *clk) 1117ec58e52SStanley Chu { 112*a0858f93SStanley Chu struct timer_of *to = to_timer_of(clk); 1137ec58e52SStanley Chu 114*a0858f93SStanley Chu mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT); 115*a0858f93SStanley Chu mtk_gpt_clkevt_time_setup(to, to->of_clk.period, TIMER_CLK_EVT); 116*a0858f93SStanley Chu mtk_gpt_clkevt_time_start(to, true, TIMER_CLK_EVT); 117*a0858f93SStanley Chu 1187ec58e52SStanley Chu return 0; 1197ec58e52SStanley Chu } 1207ec58e52SStanley Chu 12156d52d3fSStanley Chu static int mtk_gpt_clkevt_next_event(unsigned long event, 1227ec58e52SStanley Chu struct clock_event_device *clk) 1237ec58e52SStanley Chu { 124*a0858f93SStanley Chu struct timer_of *to = to_timer_of(clk); 1257ec58e52SStanley Chu 126*a0858f93SStanley Chu mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT); 127*a0858f93SStanley Chu mtk_gpt_clkevt_time_setup(to, event, TIMER_CLK_EVT); 128*a0858f93SStanley Chu mtk_gpt_clkevt_time_start(to, false, TIMER_CLK_EVT); 1297ec58e52SStanley Chu 1307ec58e52SStanley Chu return 0; 1317ec58e52SStanley Chu } 1327ec58e52SStanley Chu 13356d52d3fSStanley Chu static irqreturn_t mtk_gpt_interrupt(int irq, void *dev_id) 1347ec58e52SStanley Chu { 135*a0858f93SStanley Chu struct clock_event_device *clkevt = (struct clock_event_device *)dev_id; 136*a0858f93SStanley Chu struct timer_of *to = to_timer_of(clkevt); 1377ec58e52SStanley Chu 1387ec58e52SStanley Chu /* Acknowledge timer0 irq */ 139*a0858f93SStanley Chu writel(GPT_IRQ_ACK(TIMER_CLK_EVT), timer_of_base(to) + GPT_IRQ_ACK_REG); 140*a0858f93SStanley Chu clkevt->event_handler(clkevt); 1417ec58e52SStanley Chu 1427ec58e52SStanley Chu return IRQ_HANDLED; 1437ec58e52SStanley Chu } 1447ec58e52SStanley Chu 1457ec58e52SStanley Chu static void 146*a0858f93SStanley Chu __init mtk_gpt_setup(struct timer_of *to, u8 timer, u8 option) 1477ec58e52SStanley Chu { 14856d52d3fSStanley Chu writel(GPT_CTRL_CLEAR | GPT_CTRL_DISABLE, 149*a0858f93SStanley Chu timer_of_base(to) + GPT_CTRL_REG(timer)); 1507ec58e52SStanley Chu 15156d52d3fSStanley Chu writel(GPT_CLK_SRC(GPT_CLK_SRC_SYS13M) | GPT_CLK_DIV1, 152*a0858f93SStanley Chu timer_of_base(to) + GPT_CLK_REG(timer)); 1537ec58e52SStanley Chu 154*a0858f93SStanley Chu writel(0x0, timer_of_base(to) + GPT_CMP_REG(timer)); 1557ec58e52SStanley Chu 15656d52d3fSStanley Chu writel(GPT_CTRL_OP(option) | GPT_CTRL_ENABLE, 157*a0858f93SStanley Chu timer_of_base(to) + GPT_CTRL_REG(timer)); 1587ec58e52SStanley Chu } 1597ec58e52SStanley Chu 160*a0858f93SStanley Chu static void mtk_gpt_enable_irq(struct timer_of *to, u8 timer) 1617ec58e52SStanley Chu { 1627ec58e52SStanley Chu u32 val; 1637ec58e52SStanley Chu 1647ec58e52SStanley Chu /* Disable all interrupts */ 165*a0858f93SStanley Chu writel(0x0, timer_of_base(to) + GPT_IRQ_EN_REG); 1667ec58e52SStanley Chu 1677ec58e52SStanley Chu /* Acknowledge all spurious pending interrupts */ 168*a0858f93SStanley Chu writel(0x3f, timer_of_base(to) + GPT_IRQ_ACK_REG); 1697ec58e52SStanley Chu 170*a0858f93SStanley Chu val = readl(timer_of_base(to) + GPT_IRQ_EN_REG); 1717ec58e52SStanley Chu writel(val | GPT_IRQ_ENABLE(timer), 172*a0858f93SStanley Chu timer_of_base(to) + GPT_IRQ_EN_REG); 1737ec58e52SStanley Chu } 1747ec58e52SStanley Chu 175*a0858f93SStanley Chu static struct timer_of to = { 176*a0858f93SStanley Chu .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, 177*a0858f93SStanley Chu 178*a0858f93SStanley Chu .clkevt = { 179*a0858f93SStanley Chu .name = "mtk-clkevt", 180*a0858f93SStanley Chu .rating = 300, 181*a0858f93SStanley Chu .cpumask = cpu_possible_mask, 182*a0858f93SStanley Chu }, 183*a0858f93SStanley Chu 184*a0858f93SStanley Chu .of_irq = { 185*a0858f93SStanley Chu .flags = IRQF_TIMER | IRQF_IRQPOLL, 186*a0858f93SStanley Chu }, 187*a0858f93SStanley Chu }; 188*a0858f93SStanley Chu 18956d52d3fSStanley Chu static int __init mtk_gpt_init(struct device_node *node) 1907ec58e52SStanley Chu { 191*a0858f93SStanley Chu int ret; 1927ec58e52SStanley Chu 193*a0858f93SStanley Chu to.clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 194*a0858f93SStanley Chu to.clkevt.set_state_shutdown = mtk_gpt_clkevt_shutdown; 195*a0858f93SStanley Chu to.clkevt.set_state_periodic = mtk_gpt_clkevt_set_periodic; 196*a0858f93SStanley Chu to.clkevt.set_state_oneshot = mtk_gpt_clkevt_shutdown; 197*a0858f93SStanley Chu to.clkevt.tick_resume = mtk_gpt_clkevt_shutdown; 198*a0858f93SStanley Chu to.clkevt.set_next_event = mtk_gpt_clkevt_next_event; 199*a0858f93SStanley Chu to.of_irq.handler = mtk_gpt_interrupt; 2007ec58e52SStanley Chu 201*a0858f93SStanley Chu ret = timer_of_init(node, &to); 202*a0858f93SStanley Chu if (ret) 203*a0858f93SStanley Chu goto err; 2047ec58e52SStanley Chu 2057ec58e52SStanley Chu /* Configure clock source */ 206*a0858f93SStanley Chu mtk_gpt_setup(&to, TIMER_CLK_SRC, GPT_CTRL_OP_FREERUN); 207*a0858f93SStanley Chu clocksource_mmio_init(timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC), 208*a0858f93SStanley Chu node->name, timer_of_rate(&to), 300, 32, 209*a0858f93SStanley Chu clocksource_mmio_readl_up); 210*a0858f93SStanley Chu gpt_sched_reg = timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC); 211*a0858f93SStanley Chu sched_clock_register(mtk_gpt_read_sched_clock, 32, timer_of_rate(&to)); 2127ec58e52SStanley Chu 2137ec58e52SStanley Chu /* Configure clock event */ 214*a0858f93SStanley Chu mtk_gpt_setup(&to, TIMER_CLK_EVT, GPT_CTRL_OP_REPEAT); 215*a0858f93SStanley Chu clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 216*a0858f93SStanley Chu TIMER_SYNC_TICKS, 0xffffffff); 2177ec58e52SStanley Chu 218*a0858f93SStanley Chu mtk_gpt_enable_irq(&to, TIMER_CLK_EVT); 2197ec58e52SStanley Chu 2207ec58e52SStanley Chu return 0; 2217ec58e52SStanley Chu 222*a0858f93SStanley Chu err: 223*a0858f93SStanley Chu timer_of_cleanup(&to); 224*a0858f93SStanley Chu return ret; 2257ec58e52SStanley Chu } 22656d52d3fSStanley Chu TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init); 227