19d8d47eaSDaniel Lezcano /* 29d8d47eaSDaniel Lezcano * Pistachio clocksource based on general-purpose timers 39d8d47eaSDaniel Lezcano * 49d8d47eaSDaniel Lezcano * Copyright (C) 2015 Imagination Technologies 59d8d47eaSDaniel Lezcano * 69d8d47eaSDaniel Lezcano * This file is subject to the terms and conditions of the GNU General Public 79d8d47eaSDaniel Lezcano * License. See the file "COPYING" in the main directory of this archive 89d8d47eaSDaniel Lezcano * for more details. 99d8d47eaSDaniel Lezcano */ 109d8d47eaSDaniel Lezcano 119d8d47eaSDaniel Lezcano #define pr_fmt(fmt) "%s: " fmt, __func__ 129d8d47eaSDaniel Lezcano 139d8d47eaSDaniel Lezcano #include <linux/clk.h> 149d8d47eaSDaniel Lezcano #include <linux/clocksource.h> 159d8d47eaSDaniel Lezcano #include <linux/clockchips.h> 169d8d47eaSDaniel Lezcano #include <linux/delay.h> 179d8d47eaSDaniel Lezcano #include <linux/err.h> 189d8d47eaSDaniel Lezcano #include <linux/init.h> 199d8d47eaSDaniel Lezcano #include <linux/spinlock.h> 209d8d47eaSDaniel Lezcano #include <linux/mfd/syscon.h> 219d8d47eaSDaniel Lezcano #include <linux/of.h> 229d8d47eaSDaniel Lezcano #include <linux/of_address.h> 239d8d47eaSDaniel Lezcano #include <linux/platform_device.h> 249d8d47eaSDaniel Lezcano #include <linux/regmap.h> 259d8d47eaSDaniel Lezcano #include <linux/sched_clock.h> 269d8d47eaSDaniel Lezcano #include <linux/time.h> 279d8d47eaSDaniel Lezcano 289d8d47eaSDaniel Lezcano /* Top level reg */ 299d8d47eaSDaniel Lezcano #define CR_TIMER_CTRL_CFG 0x00 309d8d47eaSDaniel Lezcano #define TIMER_ME_GLOBAL BIT(0) 319d8d47eaSDaniel Lezcano #define CR_TIMER_REV 0x10 329d8d47eaSDaniel Lezcano 339d8d47eaSDaniel Lezcano /* Timer specific registers */ 349d8d47eaSDaniel Lezcano #define TIMER_CFG 0x20 359d8d47eaSDaniel Lezcano #define TIMER_ME_LOCAL BIT(0) 369d8d47eaSDaniel Lezcano #define TIMER_RELOAD_VALUE 0x24 379d8d47eaSDaniel Lezcano #define TIMER_CURRENT_VALUE 0x28 389d8d47eaSDaniel Lezcano #define TIMER_CURRENT_OVERFLOW_VALUE 0x2C 399d8d47eaSDaniel Lezcano #define TIMER_IRQ_STATUS 0x30 409d8d47eaSDaniel Lezcano #define TIMER_IRQ_CLEAR 0x34 419d8d47eaSDaniel Lezcano #define TIMER_IRQ_MASK 0x38 429d8d47eaSDaniel Lezcano 439d8d47eaSDaniel Lezcano #define PERIP_TIMER_CONTROL 0x90 449d8d47eaSDaniel Lezcano 459d8d47eaSDaniel Lezcano /* Timer specific configuration Values */ 469d8d47eaSDaniel Lezcano #define RELOAD_VALUE 0xffffffff 479d8d47eaSDaniel Lezcano 489d8d47eaSDaniel Lezcano struct pistachio_clocksource { 499d8d47eaSDaniel Lezcano void __iomem *base; 509d8d47eaSDaniel Lezcano raw_spinlock_t lock; 519d8d47eaSDaniel Lezcano struct clocksource cs; 529d8d47eaSDaniel Lezcano }; 539d8d47eaSDaniel Lezcano 549d8d47eaSDaniel Lezcano static struct pistachio_clocksource pcs_gpt; 559d8d47eaSDaniel Lezcano 569d8d47eaSDaniel Lezcano #define to_pistachio_clocksource(cs) \ 579d8d47eaSDaniel Lezcano container_of(cs, struct pistachio_clocksource, cs) 589d8d47eaSDaniel Lezcano 599d8d47eaSDaniel Lezcano static inline u32 gpt_readl(void __iomem *base, u32 offset, u32 gpt_id) 609d8d47eaSDaniel Lezcano { 619d8d47eaSDaniel Lezcano return readl(base + 0x20 * gpt_id + offset); 629d8d47eaSDaniel Lezcano } 639d8d47eaSDaniel Lezcano 649d8d47eaSDaniel Lezcano static inline void gpt_writel(void __iomem *base, u32 value, u32 offset, 659d8d47eaSDaniel Lezcano u32 gpt_id) 669d8d47eaSDaniel Lezcano { 679d8d47eaSDaniel Lezcano writel(value, base + 0x20 * gpt_id + offset); 689d8d47eaSDaniel Lezcano } 699d8d47eaSDaniel Lezcano 709d8d47eaSDaniel Lezcano static u64 notrace 719d8d47eaSDaniel Lezcano pistachio_clocksource_read_cycles(struct clocksource *cs) 729d8d47eaSDaniel Lezcano { 739d8d47eaSDaniel Lezcano struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs); 74*a47d7ef4SDrew Fustini u32 counter, overflow; 759d8d47eaSDaniel Lezcano unsigned long flags; 769d8d47eaSDaniel Lezcano 779d8d47eaSDaniel Lezcano /* 789d8d47eaSDaniel Lezcano * The counter value is only refreshed after the overflow value is read. 799d8d47eaSDaniel Lezcano * And they must be read in strict order, hence raw spin lock added. 809d8d47eaSDaniel Lezcano */ 819d8d47eaSDaniel Lezcano 829d8d47eaSDaniel Lezcano raw_spin_lock_irqsave(&pcs->lock, flags); 83*a47d7ef4SDrew Fustini overflow = gpt_readl(pcs->base, TIMER_CURRENT_OVERFLOW_VALUE, 0); 849d8d47eaSDaniel Lezcano counter = gpt_readl(pcs->base, TIMER_CURRENT_VALUE, 0); 859d8d47eaSDaniel Lezcano raw_spin_unlock_irqrestore(&pcs->lock, flags); 869d8d47eaSDaniel Lezcano 879d8d47eaSDaniel Lezcano return (u64)~counter; 889d8d47eaSDaniel Lezcano } 899d8d47eaSDaniel Lezcano 909d8d47eaSDaniel Lezcano static u64 notrace pistachio_read_sched_clock(void) 919d8d47eaSDaniel Lezcano { 929d8d47eaSDaniel Lezcano return pistachio_clocksource_read_cycles(&pcs_gpt.cs); 939d8d47eaSDaniel Lezcano } 949d8d47eaSDaniel Lezcano 959d8d47eaSDaniel Lezcano static void pistachio_clksrc_set_mode(struct clocksource *cs, int timeridx, 969d8d47eaSDaniel Lezcano int enable) 979d8d47eaSDaniel Lezcano { 989d8d47eaSDaniel Lezcano struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs); 999d8d47eaSDaniel Lezcano u32 val; 1009d8d47eaSDaniel Lezcano 1019d8d47eaSDaniel Lezcano val = gpt_readl(pcs->base, TIMER_CFG, timeridx); 1029d8d47eaSDaniel Lezcano if (enable) 1039d8d47eaSDaniel Lezcano val |= TIMER_ME_LOCAL; 1049d8d47eaSDaniel Lezcano else 1059d8d47eaSDaniel Lezcano val &= ~TIMER_ME_LOCAL; 1069d8d47eaSDaniel Lezcano 1079d8d47eaSDaniel Lezcano gpt_writel(pcs->base, val, TIMER_CFG, timeridx); 1089d8d47eaSDaniel Lezcano } 1099d8d47eaSDaniel Lezcano 1109d8d47eaSDaniel Lezcano static void pistachio_clksrc_enable(struct clocksource *cs, int timeridx) 1119d8d47eaSDaniel Lezcano { 1129d8d47eaSDaniel Lezcano struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs); 1139d8d47eaSDaniel Lezcano 1149d8d47eaSDaniel Lezcano /* Disable GPT local before loading reload value */ 1159d8d47eaSDaniel Lezcano pistachio_clksrc_set_mode(cs, timeridx, false); 1169d8d47eaSDaniel Lezcano gpt_writel(pcs->base, RELOAD_VALUE, TIMER_RELOAD_VALUE, timeridx); 1179d8d47eaSDaniel Lezcano pistachio_clksrc_set_mode(cs, timeridx, true); 1189d8d47eaSDaniel Lezcano } 1199d8d47eaSDaniel Lezcano 1209d8d47eaSDaniel Lezcano static void pistachio_clksrc_disable(struct clocksource *cs, int timeridx) 1219d8d47eaSDaniel Lezcano { 1229d8d47eaSDaniel Lezcano /* Disable GPT local */ 1239d8d47eaSDaniel Lezcano pistachio_clksrc_set_mode(cs, timeridx, false); 1249d8d47eaSDaniel Lezcano } 1259d8d47eaSDaniel Lezcano 1269d8d47eaSDaniel Lezcano static int pistachio_clocksource_enable(struct clocksource *cs) 1279d8d47eaSDaniel Lezcano { 1289d8d47eaSDaniel Lezcano pistachio_clksrc_enable(cs, 0); 1299d8d47eaSDaniel Lezcano return 0; 1309d8d47eaSDaniel Lezcano } 1319d8d47eaSDaniel Lezcano 1329d8d47eaSDaniel Lezcano static void pistachio_clocksource_disable(struct clocksource *cs) 1339d8d47eaSDaniel Lezcano { 1349d8d47eaSDaniel Lezcano pistachio_clksrc_disable(cs, 0); 1359d8d47eaSDaniel Lezcano } 1369d8d47eaSDaniel Lezcano 1379d8d47eaSDaniel Lezcano /* Desirable clock source for pistachio platform */ 1389d8d47eaSDaniel Lezcano static struct pistachio_clocksource pcs_gpt = { 1399d8d47eaSDaniel Lezcano .cs = { 1409d8d47eaSDaniel Lezcano .name = "gptimer", 1419d8d47eaSDaniel Lezcano .rating = 300, 1429d8d47eaSDaniel Lezcano .enable = pistachio_clocksource_enable, 1439d8d47eaSDaniel Lezcano .disable = pistachio_clocksource_disable, 1449d8d47eaSDaniel Lezcano .read = pistachio_clocksource_read_cycles, 1459d8d47eaSDaniel Lezcano .mask = CLOCKSOURCE_MASK(32), 1469d8d47eaSDaniel Lezcano .flags = CLOCK_SOURCE_IS_CONTINUOUS | 1479d8d47eaSDaniel Lezcano CLOCK_SOURCE_SUSPEND_NONSTOP, 1489d8d47eaSDaniel Lezcano }, 1499d8d47eaSDaniel Lezcano }; 1509d8d47eaSDaniel Lezcano 1519d8d47eaSDaniel Lezcano static int __init pistachio_clksrc_of_init(struct device_node *node) 1529d8d47eaSDaniel Lezcano { 1539d8d47eaSDaniel Lezcano struct clk *sys_clk, *fast_clk; 1549d8d47eaSDaniel Lezcano struct regmap *periph_regs; 1559d8d47eaSDaniel Lezcano unsigned long rate; 1569d8d47eaSDaniel Lezcano int ret; 1579d8d47eaSDaniel Lezcano 1589d8d47eaSDaniel Lezcano pcs_gpt.base = of_iomap(node, 0); 1599d8d47eaSDaniel Lezcano if (!pcs_gpt.base) { 1609d8d47eaSDaniel Lezcano pr_err("cannot iomap\n"); 1619d8d47eaSDaniel Lezcano return -ENXIO; 1629d8d47eaSDaniel Lezcano } 1639d8d47eaSDaniel Lezcano 1649d8d47eaSDaniel Lezcano periph_regs = syscon_regmap_lookup_by_phandle(node, "img,cr-periph"); 1659d8d47eaSDaniel Lezcano if (IS_ERR(periph_regs)) { 1669d8d47eaSDaniel Lezcano pr_err("cannot get peripheral regmap (%ld)\n", 1679d8d47eaSDaniel Lezcano PTR_ERR(periph_regs)); 1689d8d47eaSDaniel Lezcano return PTR_ERR(periph_regs); 1699d8d47eaSDaniel Lezcano } 1709d8d47eaSDaniel Lezcano 1719d8d47eaSDaniel Lezcano /* Switch to using the fast counter clock */ 1729d8d47eaSDaniel Lezcano ret = regmap_update_bits(periph_regs, PERIP_TIMER_CONTROL, 1739d8d47eaSDaniel Lezcano 0xf, 0x0); 1749d8d47eaSDaniel Lezcano if (ret) 1759d8d47eaSDaniel Lezcano return ret; 1769d8d47eaSDaniel Lezcano 1779d8d47eaSDaniel Lezcano sys_clk = of_clk_get_by_name(node, "sys"); 1789d8d47eaSDaniel Lezcano if (IS_ERR(sys_clk)) { 1799d8d47eaSDaniel Lezcano pr_err("clock get failed (%ld)\n", PTR_ERR(sys_clk)); 1809d8d47eaSDaniel Lezcano return PTR_ERR(sys_clk); 1819d8d47eaSDaniel Lezcano } 1829d8d47eaSDaniel Lezcano 1839d8d47eaSDaniel Lezcano fast_clk = of_clk_get_by_name(node, "fast"); 1849d8d47eaSDaniel Lezcano if (IS_ERR(fast_clk)) { 1859d8d47eaSDaniel Lezcano pr_err("clock get failed (%lu)\n", PTR_ERR(fast_clk)); 1869d8d47eaSDaniel Lezcano return PTR_ERR(fast_clk); 1879d8d47eaSDaniel Lezcano } 1889d8d47eaSDaniel Lezcano 1899d8d47eaSDaniel Lezcano ret = clk_prepare_enable(sys_clk); 1909d8d47eaSDaniel Lezcano if (ret < 0) { 1919d8d47eaSDaniel Lezcano pr_err("failed to enable clock (%d)\n", ret); 1929d8d47eaSDaniel Lezcano return ret; 1939d8d47eaSDaniel Lezcano } 1949d8d47eaSDaniel Lezcano 1959d8d47eaSDaniel Lezcano ret = clk_prepare_enable(fast_clk); 1969d8d47eaSDaniel Lezcano if (ret < 0) { 1979d8d47eaSDaniel Lezcano pr_err("failed to enable clock (%d)\n", ret); 1989d8d47eaSDaniel Lezcano clk_disable_unprepare(sys_clk); 1999d8d47eaSDaniel Lezcano return ret; 2009d8d47eaSDaniel Lezcano } 2019d8d47eaSDaniel Lezcano 2029d8d47eaSDaniel Lezcano rate = clk_get_rate(fast_clk); 2039d8d47eaSDaniel Lezcano 2049d8d47eaSDaniel Lezcano /* Disable irq's for clocksource usage */ 2059d8d47eaSDaniel Lezcano gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 0); 2069d8d47eaSDaniel Lezcano gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 1); 2079d8d47eaSDaniel Lezcano gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 2); 2089d8d47eaSDaniel Lezcano gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 3); 2099d8d47eaSDaniel Lezcano 2109d8d47eaSDaniel Lezcano /* Enable timer block */ 2119d8d47eaSDaniel Lezcano writel(TIMER_ME_GLOBAL, pcs_gpt.base); 2129d8d47eaSDaniel Lezcano 2139d8d47eaSDaniel Lezcano raw_spin_lock_init(&pcs_gpt.lock); 2149d8d47eaSDaniel Lezcano sched_clock_register(pistachio_read_sched_clock, 32, rate); 2159d8d47eaSDaniel Lezcano return clocksource_register_hz(&pcs_gpt.cs, rate); 2169d8d47eaSDaniel Lezcano } 2179d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(pistachio_gptimer, "img,pistachio-gptimer", 2189d8d47eaSDaniel Lezcano pistachio_clksrc_of_init); 219