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