18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Pistachio clocksource based on general-purpose timers 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Imagination Technologies 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 78c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 88c2ecf20Sopenharmony_ci * for more details. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 158c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <linux/of_address.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/regmap.h> 258c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 268c2ecf20Sopenharmony_ci#include <linux/time.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Top level reg */ 298c2ecf20Sopenharmony_ci#define CR_TIMER_CTRL_CFG 0x00 308c2ecf20Sopenharmony_ci#define TIMER_ME_GLOBAL BIT(0) 318c2ecf20Sopenharmony_ci#define CR_TIMER_REV 0x10 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* Timer specific registers */ 348c2ecf20Sopenharmony_ci#define TIMER_CFG 0x20 358c2ecf20Sopenharmony_ci#define TIMER_ME_LOCAL BIT(0) 368c2ecf20Sopenharmony_ci#define TIMER_RELOAD_VALUE 0x24 378c2ecf20Sopenharmony_ci#define TIMER_CURRENT_VALUE 0x28 388c2ecf20Sopenharmony_ci#define TIMER_CURRENT_OVERFLOW_VALUE 0x2C 398c2ecf20Sopenharmony_ci#define TIMER_IRQ_STATUS 0x30 408c2ecf20Sopenharmony_ci#define TIMER_IRQ_CLEAR 0x34 418c2ecf20Sopenharmony_ci#define TIMER_IRQ_MASK 0x38 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define PERIP_TIMER_CONTROL 0x90 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Timer specific configuration Values */ 468c2ecf20Sopenharmony_ci#define RELOAD_VALUE 0xffffffff 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct pistachio_clocksource { 498c2ecf20Sopenharmony_ci void __iomem *base; 508c2ecf20Sopenharmony_ci raw_spinlock_t lock; 518c2ecf20Sopenharmony_ci struct clocksource cs; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic struct pistachio_clocksource pcs_gpt; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define to_pistachio_clocksource(cs) \ 578c2ecf20Sopenharmony_ci container_of(cs, struct pistachio_clocksource, cs) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline u32 gpt_readl(void __iomem *base, u32 offset, u32 gpt_id) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return readl(base + 0x20 * gpt_id + offset); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline void gpt_writel(void __iomem *base, u32 value, u32 offset, 658c2ecf20Sopenharmony_ci u32 gpt_id) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci writel(value, base + 0x20 * gpt_id + offset); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic u64 notrace 718c2ecf20Sopenharmony_cipistachio_clocksource_read_cycles(struct clocksource *cs) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs); 748c2ecf20Sopenharmony_ci u32 counter, overflw; 758c2ecf20Sopenharmony_ci unsigned long flags; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * The counter value is only refreshed after the overflow value is read. 798c2ecf20Sopenharmony_ci * And they must be read in strict order, hence raw spin lock added. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pcs->lock, flags); 838c2ecf20Sopenharmony_ci overflw = gpt_readl(pcs->base, TIMER_CURRENT_OVERFLOW_VALUE, 0); 848c2ecf20Sopenharmony_ci counter = gpt_readl(pcs->base, TIMER_CURRENT_VALUE, 0); 858c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pcs->lock, flags); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return (u64)~counter; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic u64 notrace pistachio_read_sched_clock(void) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci return pistachio_clocksource_read_cycles(&pcs_gpt.cs); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void pistachio_clksrc_set_mode(struct clocksource *cs, int timeridx, 968c2ecf20Sopenharmony_ci int enable) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs); 998c2ecf20Sopenharmony_ci u32 val; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci val = gpt_readl(pcs->base, TIMER_CFG, timeridx); 1028c2ecf20Sopenharmony_ci if (enable) 1038c2ecf20Sopenharmony_ci val |= TIMER_ME_LOCAL; 1048c2ecf20Sopenharmony_ci else 1058c2ecf20Sopenharmony_ci val &= ~TIMER_ME_LOCAL; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci gpt_writel(pcs->base, val, TIMER_CFG, timeridx); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void pistachio_clksrc_enable(struct clocksource *cs, int timeridx) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Disable GPT local before loading reload value */ 1158c2ecf20Sopenharmony_ci pistachio_clksrc_set_mode(cs, timeridx, false); 1168c2ecf20Sopenharmony_ci gpt_writel(pcs->base, RELOAD_VALUE, TIMER_RELOAD_VALUE, timeridx); 1178c2ecf20Sopenharmony_ci pistachio_clksrc_set_mode(cs, timeridx, true); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void pistachio_clksrc_disable(struct clocksource *cs, int timeridx) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci /* Disable GPT local */ 1238c2ecf20Sopenharmony_ci pistachio_clksrc_set_mode(cs, timeridx, false); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int pistachio_clocksource_enable(struct clocksource *cs) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci pistachio_clksrc_enable(cs, 0); 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void pistachio_clocksource_disable(struct clocksource *cs) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci pistachio_clksrc_disable(cs, 0); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* Desirable clock source for pistachio platform */ 1388c2ecf20Sopenharmony_cistatic struct pistachio_clocksource pcs_gpt = { 1398c2ecf20Sopenharmony_ci .cs = { 1408c2ecf20Sopenharmony_ci .name = "gptimer", 1418c2ecf20Sopenharmony_ci .rating = 300, 1428c2ecf20Sopenharmony_ci .enable = pistachio_clocksource_enable, 1438c2ecf20Sopenharmony_ci .disable = pistachio_clocksource_disable, 1448c2ecf20Sopenharmony_ci .read = pistachio_clocksource_read_cycles, 1458c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 1468c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS | 1478c2ecf20Sopenharmony_ci CLOCK_SOURCE_SUSPEND_NONSTOP, 1488c2ecf20Sopenharmony_ci }, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int __init pistachio_clksrc_of_init(struct device_node *node) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct clk *sys_clk, *fast_clk; 1548c2ecf20Sopenharmony_ci struct regmap *periph_regs; 1558c2ecf20Sopenharmony_ci unsigned long rate; 1568c2ecf20Sopenharmony_ci int ret; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci pcs_gpt.base = of_iomap(node, 0); 1598c2ecf20Sopenharmony_ci if (!pcs_gpt.base) { 1608c2ecf20Sopenharmony_ci pr_err("cannot iomap\n"); 1618c2ecf20Sopenharmony_ci return -ENXIO; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci periph_regs = syscon_regmap_lookup_by_phandle(node, "img,cr-periph"); 1658c2ecf20Sopenharmony_ci if (IS_ERR(periph_regs)) { 1668c2ecf20Sopenharmony_ci pr_err("cannot get peripheral regmap (%ld)\n", 1678c2ecf20Sopenharmony_ci PTR_ERR(periph_regs)); 1688c2ecf20Sopenharmony_ci return PTR_ERR(periph_regs); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Switch to using the fast counter clock */ 1728c2ecf20Sopenharmony_ci ret = regmap_update_bits(periph_regs, PERIP_TIMER_CONTROL, 1738c2ecf20Sopenharmony_ci 0xf, 0x0); 1748c2ecf20Sopenharmony_ci if (ret) 1758c2ecf20Sopenharmony_ci return ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci sys_clk = of_clk_get_by_name(node, "sys"); 1788c2ecf20Sopenharmony_ci if (IS_ERR(sys_clk)) { 1798c2ecf20Sopenharmony_ci pr_err("clock get failed (%ld)\n", PTR_ERR(sys_clk)); 1808c2ecf20Sopenharmony_ci return PTR_ERR(sys_clk); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci fast_clk = of_clk_get_by_name(node, "fast"); 1848c2ecf20Sopenharmony_ci if (IS_ERR(fast_clk)) { 1858c2ecf20Sopenharmony_ci pr_err("clock get failed (%lu)\n", PTR_ERR(fast_clk)); 1868c2ecf20Sopenharmony_ci return PTR_ERR(fast_clk); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sys_clk); 1908c2ecf20Sopenharmony_ci if (ret < 0) { 1918c2ecf20Sopenharmony_ci pr_err("failed to enable clock (%d)\n", ret); 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fast_clk); 1968c2ecf20Sopenharmony_ci if (ret < 0) { 1978c2ecf20Sopenharmony_ci pr_err("failed to enable clock (%d)\n", ret); 1988c2ecf20Sopenharmony_ci clk_disable_unprepare(sys_clk); 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci rate = clk_get_rate(fast_clk); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Disable irq's for clocksource usage */ 2058c2ecf20Sopenharmony_ci gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 0); 2068c2ecf20Sopenharmony_ci gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 1); 2078c2ecf20Sopenharmony_ci gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 2); 2088c2ecf20Sopenharmony_ci gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 3); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Enable timer block */ 2118c2ecf20Sopenharmony_ci writel(TIMER_ME_GLOBAL, pcs_gpt.base); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci raw_spin_lock_init(&pcs_gpt.lock); 2148c2ecf20Sopenharmony_ci sched_clock_register(pistachio_read_sched_clock, 32, rate); 2158c2ecf20Sopenharmony_ci return clocksource_register_hz(&pcs_gpt.cs, rate); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(pistachio_gptimer, "img,pistachio-gptimer", 2188c2ecf20Sopenharmony_ci pistachio_clksrc_of_init); 219