1*9d8d47eaSDaniel Lezcano /* 2*9d8d47eaSDaniel Lezcano * Pistachio clocksource based on general-purpose timers 3*9d8d47eaSDaniel Lezcano * 4*9d8d47eaSDaniel Lezcano * Copyright (C) 2015 Imagination Technologies 5*9d8d47eaSDaniel Lezcano * 6*9d8d47eaSDaniel Lezcano * This file is subject to the terms and conditions of the GNU General Public 7*9d8d47eaSDaniel Lezcano * License. See the file "COPYING" in the main directory of this archive 8*9d8d47eaSDaniel Lezcano * for more details. 9*9d8d47eaSDaniel Lezcano */ 10*9d8d47eaSDaniel Lezcano 11*9d8d47eaSDaniel Lezcano #define pr_fmt(fmt) "%s: " fmt, __func__ 12*9d8d47eaSDaniel Lezcano 13*9d8d47eaSDaniel Lezcano #include <linux/clk.h> 14*9d8d47eaSDaniel Lezcano #include <linux/clocksource.h> 15*9d8d47eaSDaniel Lezcano #include <linux/clockchips.h> 16*9d8d47eaSDaniel Lezcano #include <linux/delay.h> 17*9d8d47eaSDaniel Lezcano #include <linux/err.h> 18*9d8d47eaSDaniel Lezcano #include <linux/init.h> 19*9d8d47eaSDaniel Lezcano #include <linux/spinlock.h> 20*9d8d47eaSDaniel Lezcano #include <linux/mfd/syscon.h> 21*9d8d47eaSDaniel Lezcano #include <linux/of.h> 22*9d8d47eaSDaniel Lezcano #include <linux/of_address.h> 23*9d8d47eaSDaniel Lezcano #include <linux/platform_device.h> 24*9d8d47eaSDaniel Lezcano #include <linux/regmap.h> 25*9d8d47eaSDaniel Lezcano #include <linux/sched_clock.h> 26*9d8d47eaSDaniel Lezcano #include <linux/time.h> 27*9d8d47eaSDaniel Lezcano 28*9d8d47eaSDaniel Lezcano /* Top level reg */ 29*9d8d47eaSDaniel Lezcano #define CR_TIMER_CTRL_CFG 0x00 30*9d8d47eaSDaniel Lezcano #define TIMER_ME_GLOBAL BIT(0) 31*9d8d47eaSDaniel Lezcano #define CR_TIMER_REV 0x10 32*9d8d47eaSDaniel Lezcano 33*9d8d47eaSDaniel Lezcano /* Timer specific registers */ 34*9d8d47eaSDaniel Lezcano #define TIMER_CFG 0x20 35*9d8d47eaSDaniel Lezcano #define TIMER_ME_LOCAL BIT(0) 36*9d8d47eaSDaniel Lezcano #define TIMER_RELOAD_VALUE 0x24 37*9d8d47eaSDaniel Lezcano #define TIMER_CURRENT_VALUE 0x28 38*9d8d47eaSDaniel Lezcano #define TIMER_CURRENT_OVERFLOW_VALUE 0x2C 39*9d8d47eaSDaniel Lezcano #define TIMER_IRQ_STATUS 0x30 40*9d8d47eaSDaniel Lezcano #define TIMER_IRQ_CLEAR 0x34 41*9d8d47eaSDaniel Lezcano #define TIMER_IRQ_MASK 0x38 42*9d8d47eaSDaniel Lezcano 43*9d8d47eaSDaniel Lezcano #define PERIP_TIMER_CONTROL 0x90 44*9d8d47eaSDaniel Lezcano 45*9d8d47eaSDaniel Lezcano /* Timer specific configuration Values */ 46*9d8d47eaSDaniel Lezcano #define RELOAD_VALUE 0xffffffff 47*9d8d47eaSDaniel Lezcano 48*9d8d47eaSDaniel Lezcano struct pistachio_clocksource { 49*9d8d47eaSDaniel Lezcano void __iomem *base; 50*9d8d47eaSDaniel Lezcano raw_spinlock_t lock; 51*9d8d47eaSDaniel Lezcano struct clocksource cs; 52*9d8d47eaSDaniel Lezcano }; 53*9d8d47eaSDaniel Lezcano 54*9d8d47eaSDaniel Lezcano static struct pistachio_clocksource pcs_gpt; 55*9d8d47eaSDaniel Lezcano 56*9d8d47eaSDaniel Lezcano #define to_pistachio_clocksource(cs) \ 57*9d8d47eaSDaniel Lezcano container_of(cs, struct pistachio_clocksource, cs) 58*9d8d47eaSDaniel Lezcano 59*9d8d47eaSDaniel Lezcano static inline u32 gpt_readl(void __iomem *base, u32 offset, u32 gpt_id) 60*9d8d47eaSDaniel Lezcano { 61*9d8d47eaSDaniel Lezcano return readl(base + 0x20 * gpt_id + offset); 62*9d8d47eaSDaniel Lezcano } 63*9d8d47eaSDaniel Lezcano 64*9d8d47eaSDaniel Lezcano static inline void gpt_writel(void __iomem *base, u32 value, u32 offset, 65*9d8d47eaSDaniel Lezcano u32 gpt_id) 66*9d8d47eaSDaniel Lezcano { 67*9d8d47eaSDaniel Lezcano writel(value, base + 0x20 * gpt_id + offset); 68*9d8d47eaSDaniel Lezcano } 69*9d8d47eaSDaniel Lezcano 70*9d8d47eaSDaniel Lezcano static u64 notrace 71*9d8d47eaSDaniel Lezcano pistachio_clocksource_read_cycles(struct clocksource *cs) 72*9d8d47eaSDaniel Lezcano { 73*9d8d47eaSDaniel Lezcano struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs); 74*9d8d47eaSDaniel Lezcano u32 counter, overflw; 75*9d8d47eaSDaniel Lezcano unsigned long flags; 76*9d8d47eaSDaniel Lezcano 77*9d8d47eaSDaniel Lezcano /* 78*9d8d47eaSDaniel Lezcano * The counter value is only refreshed after the overflow value is read. 79*9d8d47eaSDaniel Lezcano * And they must be read in strict order, hence raw spin lock added. 80*9d8d47eaSDaniel Lezcano */ 81*9d8d47eaSDaniel Lezcano 82*9d8d47eaSDaniel Lezcano raw_spin_lock_irqsave(&pcs->lock, flags); 83*9d8d47eaSDaniel Lezcano overflw = gpt_readl(pcs->base, TIMER_CURRENT_OVERFLOW_VALUE, 0); 84*9d8d47eaSDaniel Lezcano counter = gpt_readl(pcs->base, TIMER_CURRENT_VALUE, 0); 85*9d8d47eaSDaniel Lezcano raw_spin_unlock_irqrestore(&pcs->lock, flags); 86*9d8d47eaSDaniel Lezcano 87*9d8d47eaSDaniel Lezcano return (u64)~counter; 88*9d8d47eaSDaniel Lezcano } 89*9d8d47eaSDaniel Lezcano 90*9d8d47eaSDaniel Lezcano static u64 notrace pistachio_read_sched_clock(void) 91*9d8d47eaSDaniel Lezcano { 92*9d8d47eaSDaniel Lezcano return pistachio_clocksource_read_cycles(&pcs_gpt.cs); 93*9d8d47eaSDaniel Lezcano } 94*9d8d47eaSDaniel Lezcano 95*9d8d47eaSDaniel Lezcano static void pistachio_clksrc_set_mode(struct clocksource *cs, int timeridx, 96*9d8d47eaSDaniel Lezcano int enable) 97*9d8d47eaSDaniel Lezcano { 98*9d8d47eaSDaniel Lezcano struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs); 99*9d8d47eaSDaniel Lezcano u32 val; 100*9d8d47eaSDaniel Lezcano 101*9d8d47eaSDaniel Lezcano val = gpt_readl(pcs->base, TIMER_CFG, timeridx); 102*9d8d47eaSDaniel Lezcano if (enable) 103*9d8d47eaSDaniel Lezcano val |= TIMER_ME_LOCAL; 104*9d8d47eaSDaniel Lezcano else 105*9d8d47eaSDaniel Lezcano val &= ~TIMER_ME_LOCAL; 106*9d8d47eaSDaniel Lezcano 107*9d8d47eaSDaniel Lezcano gpt_writel(pcs->base, val, TIMER_CFG, timeridx); 108*9d8d47eaSDaniel Lezcano } 109*9d8d47eaSDaniel Lezcano 110*9d8d47eaSDaniel Lezcano static void pistachio_clksrc_enable(struct clocksource *cs, int timeridx) 111*9d8d47eaSDaniel Lezcano { 112*9d8d47eaSDaniel Lezcano struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs); 113*9d8d47eaSDaniel Lezcano 114*9d8d47eaSDaniel Lezcano /* Disable GPT local before loading reload value */ 115*9d8d47eaSDaniel Lezcano pistachio_clksrc_set_mode(cs, timeridx, false); 116*9d8d47eaSDaniel Lezcano gpt_writel(pcs->base, RELOAD_VALUE, TIMER_RELOAD_VALUE, timeridx); 117*9d8d47eaSDaniel Lezcano pistachio_clksrc_set_mode(cs, timeridx, true); 118*9d8d47eaSDaniel Lezcano } 119*9d8d47eaSDaniel Lezcano 120*9d8d47eaSDaniel Lezcano static void pistachio_clksrc_disable(struct clocksource *cs, int timeridx) 121*9d8d47eaSDaniel Lezcano { 122*9d8d47eaSDaniel Lezcano /* Disable GPT local */ 123*9d8d47eaSDaniel Lezcano pistachio_clksrc_set_mode(cs, timeridx, false); 124*9d8d47eaSDaniel Lezcano } 125*9d8d47eaSDaniel Lezcano 126*9d8d47eaSDaniel Lezcano static int pistachio_clocksource_enable(struct clocksource *cs) 127*9d8d47eaSDaniel Lezcano { 128*9d8d47eaSDaniel Lezcano pistachio_clksrc_enable(cs, 0); 129*9d8d47eaSDaniel Lezcano return 0; 130*9d8d47eaSDaniel Lezcano } 131*9d8d47eaSDaniel Lezcano 132*9d8d47eaSDaniel Lezcano static void pistachio_clocksource_disable(struct clocksource *cs) 133*9d8d47eaSDaniel Lezcano { 134*9d8d47eaSDaniel Lezcano pistachio_clksrc_disable(cs, 0); 135*9d8d47eaSDaniel Lezcano } 136*9d8d47eaSDaniel Lezcano 137*9d8d47eaSDaniel Lezcano /* Desirable clock source for pistachio platform */ 138*9d8d47eaSDaniel Lezcano static struct pistachio_clocksource pcs_gpt = { 139*9d8d47eaSDaniel Lezcano .cs = { 140*9d8d47eaSDaniel Lezcano .name = "gptimer", 141*9d8d47eaSDaniel Lezcano .rating = 300, 142*9d8d47eaSDaniel Lezcano .enable = pistachio_clocksource_enable, 143*9d8d47eaSDaniel Lezcano .disable = pistachio_clocksource_disable, 144*9d8d47eaSDaniel Lezcano .read = pistachio_clocksource_read_cycles, 145*9d8d47eaSDaniel Lezcano .mask = CLOCKSOURCE_MASK(32), 146*9d8d47eaSDaniel Lezcano .flags = CLOCK_SOURCE_IS_CONTINUOUS | 147*9d8d47eaSDaniel Lezcano CLOCK_SOURCE_SUSPEND_NONSTOP, 148*9d8d47eaSDaniel Lezcano }, 149*9d8d47eaSDaniel Lezcano }; 150*9d8d47eaSDaniel Lezcano 151*9d8d47eaSDaniel Lezcano static int __init pistachio_clksrc_of_init(struct device_node *node) 152*9d8d47eaSDaniel Lezcano { 153*9d8d47eaSDaniel Lezcano struct clk *sys_clk, *fast_clk; 154*9d8d47eaSDaniel Lezcano struct regmap *periph_regs; 155*9d8d47eaSDaniel Lezcano unsigned long rate; 156*9d8d47eaSDaniel Lezcano int ret; 157*9d8d47eaSDaniel Lezcano 158*9d8d47eaSDaniel Lezcano pcs_gpt.base = of_iomap(node, 0); 159*9d8d47eaSDaniel Lezcano if (!pcs_gpt.base) { 160*9d8d47eaSDaniel Lezcano pr_err("cannot iomap\n"); 161*9d8d47eaSDaniel Lezcano return -ENXIO; 162*9d8d47eaSDaniel Lezcano } 163*9d8d47eaSDaniel Lezcano 164*9d8d47eaSDaniel Lezcano periph_regs = syscon_regmap_lookup_by_phandle(node, "img,cr-periph"); 165*9d8d47eaSDaniel Lezcano if (IS_ERR(periph_regs)) { 166*9d8d47eaSDaniel Lezcano pr_err("cannot get peripheral regmap (%ld)\n", 167*9d8d47eaSDaniel Lezcano PTR_ERR(periph_regs)); 168*9d8d47eaSDaniel Lezcano return PTR_ERR(periph_regs); 169*9d8d47eaSDaniel Lezcano } 170*9d8d47eaSDaniel Lezcano 171*9d8d47eaSDaniel Lezcano /* Switch to using the fast counter clock */ 172*9d8d47eaSDaniel Lezcano ret = regmap_update_bits(periph_regs, PERIP_TIMER_CONTROL, 173*9d8d47eaSDaniel Lezcano 0xf, 0x0); 174*9d8d47eaSDaniel Lezcano if (ret) 175*9d8d47eaSDaniel Lezcano return ret; 176*9d8d47eaSDaniel Lezcano 177*9d8d47eaSDaniel Lezcano sys_clk = of_clk_get_by_name(node, "sys"); 178*9d8d47eaSDaniel Lezcano if (IS_ERR(sys_clk)) { 179*9d8d47eaSDaniel Lezcano pr_err("clock get failed (%ld)\n", PTR_ERR(sys_clk)); 180*9d8d47eaSDaniel Lezcano return PTR_ERR(sys_clk); 181*9d8d47eaSDaniel Lezcano } 182*9d8d47eaSDaniel Lezcano 183*9d8d47eaSDaniel Lezcano fast_clk = of_clk_get_by_name(node, "fast"); 184*9d8d47eaSDaniel Lezcano if (IS_ERR(fast_clk)) { 185*9d8d47eaSDaniel Lezcano pr_err("clock get failed (%lu)\n", PTR_ERR(fast_clk)); 186*9d8d47eaSDaniel Lezcano return PTR_ERR(fast_clk); 187*9d8d47eaSDaniel Lezcano } 188*9d8d47eaSDaniel Lezcano 189*9d8d47eaSDaniel Lezcano ret = clk_prepare_enable(sys_clk); 190*9d8d47eaSDaniel Lezcano if (ret < 0) { 191*9d8d47eaSDaniel Lezcano pr_err("failed to enable clock (%d)\n", ret); 192*9d8d47eaSDaniel Lezcano return ret; 193*9d8d47eaSDaniel Lezcano } 194*9d8d47eaSDaniel Lezcano 195*9d8d47eaSDaniel Lezcano ret = clk_prepare_enable(fast_clk); 196*9d8d47eaSDaniel Lezcano if (ret < 0) { 197*9d8d47eaSDaniel Lezcano pr_err("failed to enable clock (%d)\n", ret); 198*9d8d47eaSDaniel Lezcano clk_disable_unprepare(sys_clk); 199*9d8d47eaSDaniel Lezcano return ret; 200*9d8d47eaSDaniel Lezcano } 201*9d8d47eaSDaniel Lezcano 202*9d8d47eaSDaniel Lezcano rate = clk_get_rate(fast_clk); 203*9d8d47eaSDaniel Lezcano 204*9d8d47eaSDaniel Lezcano /* Disable irq's for clocksource usage */ 205*9d8d47eaSDaniel Lezcano gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 0); 206*9d8d47eaSDaniel Lezcano gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 1); 207*9d8d47eaSDaniel Lezcano gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 2); 208*9d8d47eaSDaniel Lezcano gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 3); 209*9d8d47eaSDaniel Lezcano 210*9d8d47eaSDaniel Lezcano /* Enable timer block */ 211*9d8d47eaSDaniel Lezcano writel(TIMER_ME_GLOBAL, pcs_gpt.base); 212*9d8d47eaSDaniel Lezcano 213*9d8d47eaSDaniel Lezcano raw_spin_lock_init(&pcs_gpt.lock); 214*9d8d47eaSDaniel Lezcano sched_clock_register(pistachio_read_sched_clock, 32, rate); 215*9d8d47eaSDaniel Lezcano return clocksource_register_hz(&pcs_gpt.cs, rate); 216*9d8d47eaSDaniel Lezcano } 217*9d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(pistachio_gptimer, "img,pistachio-gptimer", 218*9d8d47eaSDaniel Lezcano pistachio_clksrc_of_init); 219