Blame view
drivers/clocksource/timer-sun4i.c
6.24 KB
b2ac5d754 clocksource: sunx... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* * Allwinner A1X SoCs timer handling. * * Copyright (C) 2012 Maxime Ripard * * Maxime Ripard <maxime.ripard@free-electrons.com> * * Based on code from * Allwinner Technology Co., Ltd. <www.allwinnertech.com> * Benn Huang <benn@allwinnertech.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include <linux/clk.h> #include <linux/clockchips.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqreturn.h> |
137c6b3c7 clocksource: sun4... |
22 |
#include <linux/sched_clock.h> |
b2ac5d754 clocksource: sunx... |
23 24 25 |
#include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> |
b2ac5d754 clocksource: sunx... |
26 |
|
239751eda clocksource/drive... |
27 |
#include "timer-of.h" |
049817319 clocksource: sunx... |
28 |
#define TIMER_IRQ_EN_REG 0x00 |
40777645e clocksource: sun4... |
29 |
#define TIMER_IRQ_EN(val) BIT(val) |
b2ac5d754 clocksource: sunx... |
30 |
#define TIMER_IRQ_ST_REG 0x04 |
049817319 clocksource: sunx... |
31 |
#define TIMER_CTL_REG(val) (0x10 * val + 0x10) |
40777645e clocksource: sun4... |
32 |
#define TIMER_CTL_ENABLE BIT(0) |
9eded2321 clocksource: sun4... |
33 |
#define TIMER_CTL_RELOAD BIT(1) |
a2c49e7b5 clocksource: sun4... |
34 35 36 |
#define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2) #define TIMER_CTL_CLK_SRC_OSC24M (1) #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) |
40777645e clocksource: sun4... |
37 |
#define TIMER_CTL_ONESHOT BIT(7) |
bb008b9e8 clocksource: sun4... |
38 39 |
#define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14) #define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18) |
b2ac5d754 clocksource: sunx... |
40 |
|
12e1480bc clocksource: sun4... |
41 |
#define TIMER_SYNC_TICKS 3 |
63d88f1ac clocksource: sun4... |
42 43 44 45 46 47 |
/* * When we disable a timer, we need to wait at least for 2 cycles of * the timer source clock. We will use for that the clocksource timer * that is already setup and runs at the same frequency than the other * timers, and we never will be disabled. */ |
239751eda clocksource/drive... |
48 |
static void sun4i_clkevt_sync(void __iomem *base) |
63d88f1ac clocksource: sun4... |
49 |
{ |
239751eda clocksource/drive... |
50 |
u32 old = readl(base + TIMER_CNTVAL_REG(1)); |
63d88f1ac clocksource: sun4... |
51 |
|
239751eda clocksource/drive... |
52 |
while ((old - readl(base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS) |
63d88f1ac clocksource: sun4... |
53 54 |
cpu_relax(); } |
239751eda clocksource/drive... |
55 |
static void sun4i_clkevt_time_stop(void __iomem *base, u8 timer) |
96651a073 clocksource: sun4... |
56 |
{ |
239751eda clocksource/drive... |
57 58 59 |
u32 val = readl(base + TIMER_CTL_REG(timer)); writel(val & ~TIMER_CTL_ENABLE, base + TIMER_CTL_REG(timer)); sun4i_clkevt_sync(base); |
96651a073 clocksource: sun4... |
60 |
} |
239751eda clocksource/drive... |
61 62 |
static void sun4i_clkevt_time_setup(void __iomem *base, u8 timer, unsigned long delay) |
96651a073 clocksource: sun4... |
63 |
{ |
239751eda clocksource/drive... |
64 |
writel(delay, base + TIMER_INTVAL_REG(timer)); |
96651a073 clocksource: sun4... |
65 |
} |
239751eda clocksource/drive... |
66 67 |
static void sun4i_clkevt_time_start(void __iomem *base, u8 timer, bool periodic) |
96651a073 clocksource: sun4... |
68 |
{ |
239751eda clocksource/drive... |
69 |
u32 val = readl(base + TIMER_CTL_REG(timer)); |
96651a073 clocksource: sun4... |
70 71 72 73 74 |
if (periodic) val &= ~TIMER_CTL_ONESHOT; else val |= TIMER_CTL_ONESHOT; |
7e1418346 clocksource: sun4... |
75 |
writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, |
239751eda clocksource/drive... |
76 |
base + TIMER_CTL_REG(timer)); |
96651a073 clocksource: sun4... |
77 |
} |
6de6c977b clockevents/drive... |
78 |
static int sun4i_clkevt_shutdown(struct clock_event_device *evt) |
b2ac5d754 clocksource: sunx... |
79 |
{ |
239751eda clocksource/drive... |
80 81 82 |
struct timer_of *to = to_timer_of(evt); sun4i_clkevt_time_stop(timer_of_base(to), 0); |
6de6c977b clockevents/drive... |
83 84 85 86 87 |
return 0; } static int sun4i_clkevt_set_oneshot(struct clock_event_device *evt) { |
239751eda clocksource/drive... |
88 89 90 91 |
struct timer_of *to = to_timer_of(evt); sun4i_clkevt_time_stop(timer_of_base(to), 0); sun4i_clkevt_time_start(timer_of_base(to), 0, false); |
6de6c977b clockevents/drive... |
92 93 94 95 96 |
return 0; } static int sun4i_clkevt_set_periodic(struct clock_event_device *evt) { |
239751eda clocksource/drive... |
97 98 99 100 101 |
struct timer_of *to = to_timer_of(evt); sun4i_clkevt_time_stop(timer_of_base(to), 0); sun4i_clkevt_time_setup(timer_of_base(to), 0, timer_of_period(to)); sun4i_clkevt_time_start(timer_of_base(to), 0, true); |
6de6c977b clockevents/drive... |
102 |
return 0; |
b2ac5d754 clocksource: sunx... |
103 |
} |
119fd635e clocksource: sunx... |
104 |
static int sun4i_clkevt_next_event(unsigned long evt, |
239751eda clocksource/drive... |
105 |
struct clock_event_device *clkevt) |
b2ac5d754 clocksource: sunx... |
106 |
{ |
239751eda clocksource/drive... |
107 108 109 110 111 |
struct timer_of *to = to_timer_of(clkevt); sun4i_clkevt_time_stop(timer_of_base(to), 0); sun4i_clkevt_time_setup(timer_of_base(to), 0, evt - TIMER_SYNC_TICKS); sun4i_clkevt_time_start(timer_of_base(to), 0, false); |
b2ac5d754 clocksource: sunx... |
112 113 114 |
return 0; } |
239751eda clocksource/drive... |
115 |
static void sun4i_timer_clear_interrupt(void __iomem *base) |
b53e7d000 clocksource/drive... |
116 |
{ |
239751eda clocksource/drive... |
117 |
writel(TIMER_IRQ_EN(0), base + TIMER_IRQ_ST_REG); |
b53e7d000 clocksource/drive... |
118 |
} |
b2ac5d754 clocksource: sunx... |
119 |
|
119fd635e clocksource: sunx... |
120 |
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) |
b2ac5d754 clocksource: sunx... |
121 122 |
{ struct clock_event_device *evt = (struct clock_event_device *)dev_id; |
239751eda clocksource/drive... |
123 |
struct timer_of *to = to_timer_of(evt); |
b2ac5d754 clocksource: sunx... |
124 |
|
239751eda clocksource/drive... |
125 |
sun4i_timer_clear_interrupt(timer_of_base(to)); |
b2ac5d754 clocksource: sunx... |
126 127 128 129 |
evt->event_handler(evt); return IRQ_HANDLED; } |
239751eda clocksource/drive... |
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
static struct timer_of to = { .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE, .clkevt = { .name = "sun4i_tick", .rating = 350, .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .set_state_shutdown = sun4i_clkevt_shutdown, .set_state_periodic = sun4i_clkevt_set_periodic, .set_state_oneshot = sun4i_clkevt_set_oneshot, .tick_resume = sun4i_clkevt_shutdown, .set_next_event = sun4i_clkevt_next_event, .cpumask = cpu_possible_mask, }, .of_irq = { .handler = sun4i_timer_interrupt, .flags = IRQF_TIMER | IRQF_IRQPOLL, }, |
b2ac5d754 clocksource: sunx... |
149 |
}; |
662e7230e clocksource: sun4... |
150 |
static u64 notrace sun4i_timer_sched_read(void) |
137c6b3c7 clocksource: sun4... |
151 |
{ |
239751eda clocksource/drive... |
152 |
return ~readl(timer_of_base(&to) + TIMER_CNTVAL_REG(1)); |
137c6b3c7 clocksource: sun4... |
153 |
} |
ce5dc743f clocksource/drive... |
154 |
static int __init sun4i_timer_init(struct device_node *node) |
b2ac5d754 clocksource: sunx... |
155 |
{ |
239751eda clocksource/drive... |
156 |
int ret; |
b2ac5d754 clocksource: sunx... |
157 |
u32 val; |
239751eda clocksource/drive... |
158 159 |
ret = timer_of_init(node, &to); if (ret) |
ce5dc743f clocksource/drive... |
160 |
return ret; |
b2ac5d754 clocksource: sunx... |
161 |
|
239751eda clocksource/drive... |
162 |
writel(~0, timer_of_base(&to) + TIMER_INTVAL_REG(1)); |
137c6b3c7 clocksource: sun4... |
163 164 |
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD | TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), |
239751eda clocksource/drive... |
165 |
timer_of_base(&to) + TIMER_CTL_REG(1)); |
137c6b3c7 clocksource: sun4... |
166 |
|
37b8b003c clocksource/drive... |
167 168 169 170 171 172 |
/* * sched_clock_register does not have priorities, and on sun6i and * later there is a better sched_clock registered by arm_arch_timer.c */ if (of_machine_is_compatible("allwinner,sun4i-a10") || of_machine_is_compatible("allwinner,sun5i-a13") || |
0113ab803 clocksource/drive... |
173 174 |
of_machine_is_compatible("allwinner,sun5i-a10s") || of_machine_is_compatible("allwinner,suniv-f1c100s")) |
239751eda clocksource/drive... |
175 176 |
sched_clock_register(sun4i_timer_sched_read, 32, timer_of_rate(&to)); |
37b8b003c clocksource/drive... |
177 |
|
239751eda clocksource/drive... |
178 179 180 |
ret = clocksource_mmio_init(timer_of_base(&to) + TIMER_CNTVAL_REG(1), node->name, timer_of_rate(&to), 350, 32, clocksource_mmio_readl_down); |
ce5dc743f clocksource/drive... |
181 |
if (ret) { |
ac9ce6d1a clocksource: Add ... |
182 183 |
pr_err("Failed to register clocksource "); |
ce5dc743f clocksource/drive... |
184 185 |
return ret; } |
137c6b3c7 clocksource: sun4... |
186 |
|
7e1418346 clocksource: sun4... |
187 |
writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), |
239751eda clocksource/drive... |
188 |
timer_of_base(&to) + TIMER_CTL_REG(0)); |
b2ac5d754 clocksource: sunx... |
189 |
|
6db50bb67 clocksource: sunx... |
190 |
/* Make sure timer is stopped before playing with interrupts */ |
239751eda clocksource/drive... |
191 |
sun4i_clkevt_time_stop(timer_of_base(&to), 0); |
6db50bb67 clocksource: sunx... |
192 |
|
b53e7d000 clocksource/drive... |
193 |
/* clear timer0 interrupt */ |
239751eda clocksource/drive... |
194 |
sun4i_timer_clear_interrupt(timer_of_base(&to)); |
6bab4a8a1 clockevent: sun4i... |
195 |
|
239751eda clocksource/drive... |
196 |
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), |
6bab4a8a1 clockevent: sun4i... |
197 |
TIMER_SYNC_TICKS, 0xffffffff); |
b2ac5d754 clocksource: sunx... |
198 |
/* Enable timer0 interrupt */ |
239751eda clocksource/drive... |
199 200 |
val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG); writel(val | TIMER_IRQ_EN(0), timer_of_base(&to) + TIMER_IRQ_EN_REG); |
ce5dc743f clocksource/drive... |
201 202 |
return ret; |
b2ac5d754 clocksource: sunx... |
203 |
} |
172733959 clocksource/drive... |
204 |
TIMER_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer", |
119fd635e clocksource: sunx... |
205 |
sun4i_timer_init); |
bca4e0842 clocksource: sun4... |
206 207 208 209 |
TIMER_OF_DECLARE(sun8i_a23, "allwinner,sun8i-a23-timer", sun4i_timer_init); TIMER_OF_DECLARE(sun8i_v3s, "allwinner,sun8i-v3s-timer", sun4i_timer_init); |
0113ab803 clocksource/drive... |
210 211 |
TIMER_OF_DECLARE(suniv, "allwinner,suniv-f1c100s-timer", sun4i_timer_init); |