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