Blame view
drivers/clocksource/timer-atmel-pit.c
6.92 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
1a0ed732a [ARM] 3949/2: AT9... |
2 |
/* |
ad48ce74f [ARM] 4989/1: [AT... |
3 |
* at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x |
1a0ed732a [ARM] 3949/2: AT9... |
4 5 6 |
* * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France * Revision 2005 M. Nicolas Diremdjian, ATMEL Rousset, France |
ad48ce74f [ARM] 4989/1: [AT... |
7 |
* Converted to ClockSource/ClockEvents by David Brownell. |
1a0ed732a [ARM] 3949/2: AT9... |
8 |
*/ |
52c3ffb01 ARM: at91: PIT: F... |
9 |
|
cffbfe63e ARM: at91: PIT: U... |
10 |
#define pr_fmt(fmt) "AT91: PIT: " fmt |
52c3ffb01 ARM: at91: PIT: F... |
11 12 |
#include <linux/clk.h> #include <linux/clockchips.h> |
1a0ed732a [ARM] 3949/2: AT9... |
13 14 15 |
#include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel.h> |
23fa648fd ARM: at91: pit ad... |
16 17 18 |
#include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> |
64568d1db ARM: at91: PIT: (... |
19 |
#include <linux/slab.h> |
1a0ed732a [ARM] 3949/2: AT9... |
20 |
|
ffe5cd8e3 arm: at91: move p... |
21 |
#define AT91_PIT_MR 0x00 /* Mode Register */ |
52c3ffb01 ARM: at91: PIT: F... |
22 23 24 |
#define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */ #define AT91_PIT_PITEN BIT(24) /* Timer Enabled */ #define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */ |
ffe5cd8e3 arm: at91: move p... |
25 26 |
#define AT91_PIT_SR 0x04 /* Status Register */ |
52c3ffb01 ARM: at91: PIT: F... |
27 |
#define AT91_PIT_PITS BIT(0) /* Timer Status */ |
ffe5cd8e3 arm: at91: move p... |
28 29 30 |
#define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */ #define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */ |
52c3ffb01 ARM: at91: PIT: F... |
31 32 |
#define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */ #define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */ |
1a0ed732a [ARM] 3949/2: AT9... |
33 34 35 |
#define PIT_CPIV(x) ((x) & AT91_PIT_CPIV) #define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20) |
64568d1db ARM: at91: PIT: (... |
36 37 38 |
struct pit_data { struct clock_event_device clkevt; struct clocksource clksrc; |
ad48ce74f [ARM] 4989/1: [AT... |
39 |
|
64568d1db ARM: at91: PIT: (... |
40 41 42 43 44 45 46 47 |
void __iomem *base; u32 cycle; u32 cnt; unsigned int irq; struct clk *mck; }; static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc) |
4ab0c5998 ARM: at91: make p... |
48 |
{ |
64568d1db ARM: at91: PIT: (... |
49 |
return container_of(clksrc, struct pit_data, clksrc); |
4ab0c5998 ARM: at91: make p... |
50 |
} |
64568d1db ARM: at91: PIT: (... |
51 |
static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt) |
4ab0c5998 ARM: at91: make p... |
52 |
{ |
64568d1db ARM: at91: PIT: (... |
53 54 55 56 57 |
return container_of(clkevt, struct pit_data, clkevt); } static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset) { |
4806c87f0 clocksource/drive... |
58 |
return readl_relaxed(base + reg_offset); |
64568d1db ARM: at91: PIT: (... |
59 60 61 62 |
} static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value) { |
4806c87f0 clocksource/drive... |
63 |
writel_relaxed(value, base + reg_offset); |
4ab0c5998 ARM: at91: make p... |
64 |
} |
ad48ce74f [ARM] 4989/1: [AT... |
65 |
|
1a0ed732a [ARM] 3949/2: AT9... |
66 |
/* |
ad48ce74f [ARM] 4989/1: [AT... |
67 68 |
* Clocksource: just a monotonic counter of MCK/16 cycles. * We don't care whether or not PIT irqs are enabled. |
1a0ed732a [ARM] 3949/2: AT9... |
69 |
*/ |
a5a1d1c29 clocksource: Use ... |
70 |
static u64 read_pit_clk(struct clocksource *cs) |
1a0ed732a [ARM] 3949/2: AT9... |
71 |
{ |
64568d1db ARM: at91: PIT: (... |
72 |
struct pit_data *data = clksrc_to_pit_data(cs); |
ad48ce74f [ARM] 4989/1: [AT... |
73 74 75 76 77 |
unsigned long flags; u32 elapsed; u32 t; raw_local_irq_save(flags); |
64568d1db ARM: at91: PIT: (... |
78 79 |
elapsed = data->cnt; t = pit_read(data->base, AT91_PIT_PIIR); |
ad48ce74f [ARM] 4989/1: [AT... |
80 |
raw_local_irq_restore(flags); |
64568d1db ARM: at91: PIT: (... |
81 |
elapsed += PIT_PICNT(t) * data->cycle; |
ad48ce74f [ARM] 4989/1: [AT... |
82 83 84 |
elapsed += PIT_CPIV(t); return elapsed; } |
85250fb89 clockevents/drive... |
85 86 87 88 89 90 91 92 |
static int pit_clkevt_shutdown(struct clock_event_device *dev) { struct pit_data *data = clkevt_to_pit_data(dev); /* disable irq, leaving the clocksource active */ pit_write(data->base, AT91_PIT_MR, (data->cycle - 1) | AT91_PIT_PITEN); return 0; } |
ad48ce74f [ARM] 4989/1: [AT... |
93 94 95 |
/* * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16) */ |
85250fb89 clockevents/drive... |
96 |
static int pit_clkevt_set_periodic(struct clock_event_device *dev) |
ad48ce74f [ARM] 4989/1: [AT... |
97 |
{ |
64568d1db ARM: at91: PIT: (... |
98 |
struct pit_data *data = clkevt_to_pit_data(dev); |
85250fb89 clockevents/drive... |
99 100 101 102 103 |
/* update clocksource counter */ data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); pit_write(data->base, AT91_PIT_MR, (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN); return 0; |
1a0ed732a [ARM] 3949/2: AT9... |
104 |
} |
49356ae94 ARM: at91: conver... |
105 106 |
static void at91sam926x_pit_suspend(struct clock_event_device *cedev) { |
64568d1db ARM: at91: PIT: (... |
107 |
struct pit_data *data = clkevt_to_pit_data(cedev); |
49356ae94 ARM: at91: conver... |
108 |
/* Disable timer */ |
64568d1db ARM: at91: PIT: (... |
109 |
pit_write(data->base, AT91_PIT_MR, 0); |
49356ae94 ARM: at91: conver... |
110 |
} |
64568d1db ARM: at91: PIT: (... |
111 |
static void at91sam926x_pit_reset(struct pit_data *data) |
49356ae94 ARM: at91: conver... |
112 113 |
{ /* Disable timer and irqs */ |
64568d1db ARM: at91: PIT: (... |
114 |
pit_write(data->base, AT91_PIT_MR, 0); |
49356ae94 ARM: at91: conver... |
115 116 |
/* Clear any pending interrupts, wait for PIT to stop counting */ |
64568d1db ARM: at91: PIT: (... |
117 |
while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0) |
49356ae94 ARM: at91: conver... |
118 119 120 |
cpu_relax(); /* Start PIT but don't enable IRQ */ |
64568d1db ARM: at91: PIT: (... |
121 122 |
pit_write(data->base, AT91_PIT_MR, (data->cycle - 1) | AT91_PIT_PITEN); |
49356ae94 ARM: at91: conver... |
123 124 125 126 |
} static void at91sam926x_pit_resume(struct clock_event_device *cedev) { |
64568d1db ARM: at91: PIT: (... |
127 |
struct pit_data *data = clkevt_to_pit_data(cedev); |
ad48ce74f [ARM] 4989/1: [AT... |
128 |
|
64568d1db ARM: at91: PIT: (... |
129 130 |
at91sam926x_pit_reset(data); } |
ad48ce74f [ARM] 4989/1: [AT... |
131 |
|
1a0ed732a [ARM] 3949/2: AT9... |
132 133 134 |
/* * IRQ handler for the timer. */ |
ad48ce74f [ARM] 4989/1: [AT... |
135 |
static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) |
1a0ed732a [ARM] 3949/2: AT9... |
136 |
{ |
64568d1db ARM: at91: PIT: (... |
137 |
struct pit_data *data = dev_id; |
ad48ce74f [ARM] 4989/1: [AT... |
138 |
/* The PIT interrupt may be disabled, and is shared */ |
85250fb89 clockevents/drive... |
139 |
if (clockevent_state_periodic(&data->clkevt) && |
64568d1db ARM: at91: PIT: (... |
140 |
(pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) { |
ad48ce74f [ARM] 4989/1: [AT... |
141 |
/* Get number of ticks performed before irq, and ack it */ |
2783e5d63 clocksource/drive... |
142 143 144 |
data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); data->clkevt.event_handler(&data->clkevt); |
1a0ed732a [ARM] 3949/2: AT9... |
145 |
|
1a0ed732a [ARM] 3949/2: AT9... |
146 |
return IRQ_HANDLED; |
ad48ce74f [ARM] 4989/1: [AT... |
147 148 149 |
} return IRQ_NONE; |
1a0ed732a [ARM] 3949/2: AT9... |
150 |
} |
1a0ed732a [ARM] 3949/2: AT9... |
151 |
/* |
ad48ce74f [ARM] 4989/1: [AT... |
152 |
* Set up both clocksource and clockevent support. |
1a0ed732a [ARM] 3949/2: AT9... |
153 |
*/ |
a17686c46 clocksource/drive... |
154 |
static int __init at91sam926x_pit_dt_init(struct device_node *node) |
1a0ed732a [ARM] 3949/2: AT9... |
155 |
{ |
a17686c46 clocksource/drive... |
156 157 158 159 160 161 162 163 164 165 166 167 168 |
unsigned long pit_rate; unsigned bits; int ret; struct pit_data *data; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->base = of_iomap(node, 0); if (!data->base) { pr_err("Could not map PIT address "); |
52bf4a900 clocksource/drive... |
169 170 |
ret = -ENXIO; goto exit; |
a17686c46 clocksource/drive... |
171 172 173 174 175 176 |
} data->mck = of_clk_get(node, 0); if (IS_ERR(data->mck)) { pr_err("Unable to get mck clk "); |
52bf4a900 clocksource/drive... |
177 178 |
ret = PTR_ERR(data->mck); goto exit; |
a17686c46 clocksource/drive... |
179 180 181 182 183 184 |
} ret = clk_prepare_enable(data->mck); if (ret) { pr_err("Unable to enable mck "); |
52bf4a900 clocksource/drive... |
185 |
goto exit; |
a17686c46 clocksource/drive... |
186 187 188 189 190 191 192 |
} /* Get the interrupts property */ data->irq = irq_of_parse_and_map(node, 0); if (!data->irq) { pr_err("Unable to get IRQ from DT "); |
52bf4a900 clocksource/drive... |
193 194 |
ret = -EINVAL; goto exit; |
a17686c46 clocksource/drive... |
195 |
} |
ad48ce74f [ARM] 4989/1: [AT... |
196 197 198 199 200 |
/* * Use our actual MCK to figure out how many MCK/16 ticks per * 1/HZ period (instead of a compile-time constant LATCH). */ |
64568d1db ARM: at91: PIT: (... |
201 202 203 |
pit_rate = clk_get_rate(data->mck) / 16; data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ); WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0); |
1a0ed732a [ARM] 3949/2: AT9... |
204 |
|
ad48ce74f [ARM] 4989/1: [AT... |
205 |
/* Initialize and enable the timer */ |
64568d1db ARM: at91: PIT: (... |
206 |
at91sam926x_pit_reset(data); |
ad48ce74f [ARM] 4989/1: [AT... |
207 208 209 210 211 |
/* * Register clocksource. The high order bits of PIV are unused, * so this isn't a 32-bit counter unless we get clockevent irqs. */ |
64568d1db ARM: at91: PIT: (... |
212 213 214 215 |
bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */; data->clksrc.mask = CLOCKSOURCE_MASK(bits); data->clksrc.name = "pit"; data->clksrc.rating = 175; |
005e56272 clockevents/drive... |
216 217 |
data->clksrc.read = read_pit_clk; data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; |
504f34c9e clocksource/drive... |
218 219 220 |
ret = clocksource_register_hz(&data->clksrc, pit_rate); if (ret) { |
ac9ce6d1a clocksource: Add ... |
221 222 |
pr_err("Failed to register clocksource "); |
52bf4a900 clocksource/drive... |
223 |
goto exit; |
504f34c9e clocksource/drive... |
224 |
} |
ad48ce74f [ARM] 4989/1: [AT... |
225 226 |
/* Set up irq handler */ |
64568d1db ARM: at91: PIT: (... |
227 |
ret = request_irq(data->irq, at91sam926x_pit_interrupt, |
7f282e013 ARM: at91: PIT: u... |
228 |
IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, |
64568d1db ARM: at91: PIT: (... |
229 |
"at91_tick", data); |
504f34c9e clocksource/drive... |
230 231 232 |
if (ret) { pr_err("Unable to setup IRQ "); |
52bf4a900 clocksource/drive... |
233 234 |
clocksource_unregister(&data->clksrc); goto exit; |
504f34c9e clocksource/drive... |
235 |
} |
ad48ce74f [ARM] 4989/1: [AT... |
236 237 |
/* Set up and register clockevents */ |
64568d1db ARM: at91: PIT: (... |
238 239 240 241 242 243 |
data->clkevt.name = "pit"; data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC; data->clkevt.shift = 32; data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift); data->clkevt.rating = 100; data->clkevt.cpumask = cpumask_of(0); |
85250fb89 clockevents/drive... |
244 245 |
data->clkevt.set_state_shutdown = pit_clkevt_shutdown; data->clkevt.set_state_periodic = pit_clkevt_set_periodic; |
64568d1db ARM: at91: PIT: (... |
246 247 248 |
data->clkevt.resume = at91sam926x_pit_resume; data->clkevt.suspend = at91sam926x_pit_suspend; clockevents_register_device(&data->clkevt); |
504f34c9e clocksource/drive... |
249 250 |
return 0; |
52bf4a900 clocksource/drive... |
251 252 253 254 |
exit: kfree(data); return ret; |
1a0ed732a [ARM] 3949/2: AT9... |
255 |
} |
172733959 clocksource/drive... |
256 |
TIMER_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", |
f807a89cf ARM: at91: PIT: R... |
257 |
at91sam926x_pit_dt_init); |