Commit cb95413971d605b0d152d3ceecc47ba8991d66fb
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
Merge branch 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer fix from Thomas Gleixner: "A single bugfix for an init order problem in the sun4i subarch clockevents code" * 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: clockevent: sun4i: Fix race condition in the probe code
Showing 1 changed file Inline Diff
drivers/clocksource/sun4i_timer.c
1 | /* | 1 | /* |
2 | * Allwinner A1X SoCs timer handling. | 2 | * Allwinner A1X SoCs timer handling. |
3 | * | 3 | * |
4 | * Copyright (C) 2012 Maxime Ripard | 4 | * Copyright (C) 2012 Maxime Ripard |
5 | * | 5 | * |
6 | * Maxime Ripard <maxime.ripard@free-electrons.com> | 6 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
7 | * | 7 | * |
8 | * Based on code from | 8 | * Based on code from |
9 | * Allwinner Technology Co., Ltd. <www.allwinnertech.com> | 9 | * Allwinner Technology Co., Ltd. <www.allwinnertech.com> |
10 | * Benn Huang <benn@allwinnertech.com> | 10 | * Benn Huang <benn@allwinnertech.com> |
11 | * | 11 | * |
12 | * This file is licensed under the terms of the GNU General Public | 12 | * This file is licensed under the terms of the GNU General Public |
13 | * License version 2. This program is licensed "as is" without any | 13 | * License version 2. This program is licensed "as is" without any |
14 | * warranty of any kind, whether express or implied. | 14 | * warranty of any kind, whether express or implied. |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <linux/clk.h> | 17 | #include <linux/clk.h> |
18 | #include <linux/clockchips.h> | 18 | #include <linux/clockchips.h> |
19 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
20 | #include <linux/irq.h> | 20 | #include <linux/irq.h> |
21 | #include <linux/irqreturn.h> | 21 | #include <linux/irqreturn.h> |
22 | #include <linux/sched_clock.h> | 22 | #include <linux/sched_clock.h> |
23 | #include <linux/of.h> | 23 | #include <linux/of.h> |
24 | #include <linux/of_address.h> | 24 | #include <linux/of_address.h> |
25 | #include <linux/of_irq.h> | 25 | #include <linux/of_irq.h> |
26 | 26 | ||
27 | #define TIMER_IRQ_EN_REG 0x00 | 27 | #define TIMER_IRQ_EN_REG 0x00 |
28 | #define TIMER_IRQ_EN(val) BIT(val) | 28 | #define TIMER_IRQ_EN(val) BIT(val) |
29 | #define TIMER_IRQ_ST_REG 0x04 | 29 | #define TIMER_IRQ_ST_REG 0x04 |
30 | #define TIMER_CTL_REG(val) (0x10 * val + 0x10) | 30 | #define TIMER_CTL_REG(val) (0x10 * val + 0x10) |
31 | #define TIMER_CTL_ENABLE BIT(0) | 31 | #define TIMER_CTL_ENABLE BIT(0) |
32 | #define TIMER_CTL_RELOAD BIT(1) | 32 | #define TIMER_CTL_RELOAD BIT(1) |
33 | #define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2) | 33 | #define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2) |
34 | #define TIMER_CTL_CLK_SRC_OSC24M (1) | 34 | #define TIMER_CTL_CLK_SRC_OSC24M (1) |
35 | #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) | 35 | #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) |
36 | #define TIMER_CTL_ONESHOT BIT(7) | 36 | #define TIMER_CTL_ONESHOT BIT(7) |
37 | #define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14) | 37 | #define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14) |
38 | #define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18) | 38 | #define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18) |
39 | 39 | ||
40 | #define TIMER_SYNC_TICKS 3 | 40 | #define TIMER_SYNC_TICKS 3 |
41 | 41 | ||
42 | static void __iomem *timer_base; | 42 | static void __iomem *timer_base; |
43 | static u32 ticks_per_jiffy; | 43 | static u32 ticks_per_jiffy; |
44 | 44 | ||
45 | /* | 45 | /* |
46 | * When we disable a timer, we need to wait at least for 2 cycles of | 46 | * When we disable a timer, we need to wait at least for 2 cycles of |
47 | * the timer source clock. We will use for that the clocksource timer | 47 | * the timer source clock. We will use for that the clocksource timer |
48 | * that is already setup and runs at the same frequency than the other | 48 | * that is already setup and runs at the same frequency than the other |
49 | * timers, and we never will be disabled. | 49 | * timers, and we never will be disabled. |
50 | */ | 50 | */ |
51 | static void sun4i_clkevt_sync(void) | 51 | static void sun4i_clkevt_sync(void) |
52 | { | 52 | { |
53 | u32 old = readl(timer_base + TIMER_CNTVAL_REG(1)); | 53 | u32 old = readl(timer_base + TIMER_CNTVAL_REG(1)); |
54 | 54 | ||
55 | while ((old - readl(timer_base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS) | 55 | while ((old - readl(timer_base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS) |
56 | cpu_relax(); | 56 | cpu_relax(); |
57 | } | 57 | } |
58 | 58 | ||
59 | static void sun4i_clkevt_time_stop(u8 timer) | 59 | static void sun4i_clkevt_time_stop(u8 timer) |
60 | { | 60 | { |
61 | u32 val = readl(timer_base + TIMER_CTL_REG(timer)); | 61 | u32 val = readl(timer_base + TIMER_CTL_REG(timer)); |
62 | writel(val & ~TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(timer)); | 62 | writel(val & ~TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(timer)); |
63 | sun4i_clkevt_sync(); | 63 | sun4i_clkevt_sync(); |
64 | } | 64 | } |
65 | 65 | ||
66 | static void sun4i_clkevt_time_setup(u8 timer, unsigned long delay) | 66 | static void sun4i_clkevt_time_setup(u8 timer, unsigned long delay) |
67 | { | 67 | { |
68 | writel(delay, timer_base + TIMER_INTVAL_REG(timer)); | 68 | writel(delay, timer_base + TIMER_INTVAL_REG(timer)); |
69 | } | 69 | } |
70 | 70 | ||
71 | static void sun4i_clkevt_time_start(u8 timer, bool periodic) | 71 | static void sun4i_clkevt_time_start(u8 timer, bool periodic) |
72 | { | 72 | { |
73 | u32 val = readl(timer_base + TIMER_CTL_REG(timer)); | 73 | u32 val = readl(timer_base + TIMER_CTL_REG(timer)); |
74 | 74 | ||
75 | if (periodic) | 75 | if (periodic) |
76 | val &= ~TIMER_CTL_ONESHOT; | 76 | val &= ~TIMER_CTL_ONESHOT; |
77 | else | 77 | else |
78 | val |= TIMER_CTL_ONESHOT; | 78 | val |= TIMER_CTL_ONESHOT; |
79 | 79 | ||
80 | writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, | 80 | writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, |
81 | timer_base + TIMER_CTL_REG(timer)); | 81 | timer_base + TIMER_CTL_REG(timer)); |
82 | } | 82 | } |
83 | 83 | ||
84 | static void sun4i_clkevt_mode(enum clock_event_mode mode, | 84 | static void sun4i_clkevt_mode(enum clock_event_mode mode, |
85 | struct clock_event_device *clk) | 85 | struct clock_event_device *clk) |
86 | { | 86 | { |
87 | switch (mode) { | 87 | switch (mode) { |
88 | case CLOCK_EVT_MODE_PERIODIC: | 88 | case CLOCK_EVT_MODE_PERIODIC: |
89 | sun4i_clkevt_time_stop(0); | 89 | sun4i_clkevt_time_stop(0); |
90 | sun4i_clkevt_time_setup(0, ticks_per_jiffy); | 90 | sun4i_clkevt_time_setup(0, ticks_per_jiffy); |
91 | sun4i_clkevt_time_start(0, true); | 91 | sun4i_clkevt_time_start(0, true); |
92 | break; | 92 | break; |
93 | case CLOCK_EVT_MODE_ONESHOT: | 93 | case CLOCK_EVT_MODE_ONESHOT: |
94 | sun4i_clkevt_time_stop(0); | 94 | sun4i_clkevt_time_stop(0); |
95 | sun4i_clkevt_time_start(0, false); | 95 | sun4i_clkevt_time_start(0, false); |
96 | break; | 96 | break; |
97 | case CLOCK_EVT_MODE_UNUSED: | 97 | case CLOCK_EVT_MODE_UNUSED: |
98 | case CLOCK_EVT_MODE_SHUTDOWN: | 98 | case CLOCK_EVT_MODE_SHUTDOWN: |
99 | default: | 99 | default: |
100 | sun4i_clkevt_time_stop(0); | 100 | sun4i_clkevt_time_stop(0); |
101 | break; | 101 | break; |
102 | } | 102 | } |
103 | } | 103 | } |
104 | 104 | ||
105 | static int sun4i_clkevt_next_event(unsigned long evt, | 105 | static int sun4i_clkevt_next_event(unsigned long evt, |
106 | struct clock_event_device *unused) | 106 | struct clock_event_device *unused) |
107 | { | 107 | { |
108 | sun4i_clkevt_time_stop(0); | 108 | sun4i_clkevt_time_stop(0); |
109 | sun4i_clkevt_time_setup(0, evt - TIMER_SYNC_TICKS); | 109 | sun4i_clkevt_time_setup(0, evt - TIMER_SYNC_TICKS); |
110 | sun4i_clkevt_time_start(0, false); | 110 | sun4i_clkevt_time_start(0, false); |
111 | 111 | ||
112 | return 0; | 112 | return 0; |
113 | } | 113 | } |
114 | 114 | ||
115 | static struct clock_event_device sun4i_clockevent = { | 115 | static struct clock_event_device sun4i_clockevent = { |
116 | .name = "sun4i_tick", | 116 | .name = "sun4i_tick", |
117 | .rating = 350, | 117 | .rating = 350, |
118 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | 118 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, |
119 | .set_mode = sun4i_clkevt_mode, | 119 | .set_mode = sun4i_clkevt_mode, |
120 | .set_next_event = sun4i_clkevt_next_event, | 120 | .set_next_event = sun4i_clkevt_next_event, |
121 | }; | 121 | }; |
122 | 122 | ||
123 | 123 | ||
124 | static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) | 124 | static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) |
125 | { | 125 | { |
126 | struct clock_event_device *evt = (struct clock_event_device *)dev_id; | 126 | struct clock_event_device *evt = (struct clock_event_device *)dev_id; |
127 | 127 | ||
128 | writel(0x1, timer_base + TIMER_IRQ_ST_REG); | 128 | writel(0x1, timer_base + TIMER_IRQ_ST_REG); |
129 | evt->event_handler(evt); | 129 | evt->event_handler(evt); |
130 | 130 | ||
131 | return IRQ_HANDLED; | 131 | return IRQ_HANDLED; |
132 | } | 132 | } |
133 | 133 | ||
134 | static struct irqaction sun4i_timer_irq = { | 134 | static struct irqaction sun4i_timer_irq = { |
135 | .name = "sun4i_timer0", | 135 | .name = "sun4i_timer0", |
136 | .flags = IRQF_TIMER | IRQF_IRQPOLL, | 136 | .flags = IRQF_TIMER | IRQF_IRQPOLL, |
137 | .handler = sun4i_timer_interrupt, | 137 | .handler = sun4i_timer_interrupt, |
138 | .dev_id = &sun4i_clockevent, | 138 | .dev_id = &sun4i_clockevent, |
139 | }; | 139 | }; |
140 | 140 | ||
141 | static u64 notrace sun4i_timer_sched_read(void) | 141 | static u64 notrace sun4i_timer_sched_read(void) |
142 | { | 142 | { |
143 | return ~readl(timer_base + TIMER_CNTVAL_REG(1)); | 143 | return ~readl(timer_base + TIMER_CNTVAL_REG(1)); |
144 | } | 144 | } |
145 | 145 | ||
146 | static void __init sun4i_timer_init(struct device_node *node) | 146 | static void __init sun4i_timer_init(struct device_node *node) |
147 | { | 147 | { |
148 | unsigned long rate = 0; | 148 | unsigned long rate = 0; |
149 | struct clk *clk; | 149 | struct clk *clk; |
150 | int ret, irq; | 150 | int ret, irq; |
151 | u32 val; | 151 | u32 val; |
152 | 152 | ||
153 | timer_base = of_iomap(node, 0); | 153 | timer_base = of_iomap(node, 0); |
154 | if (!timer_base) | 154 | if (!timer_base) |
155 | panic("Can't map registers"); | 155 | panic("Can't map registers"); |
156 | 156 | ||
157 | irq = irq_of_parse_and_map(node, 0); | 157 | irq = irq_of_parse_and_map(node, 0); |
158 | if (irq <= 0) | 158 | if (irq <= 0) |
159 | panic("Can't parse IRQ"); | 159 | panic("Can't parse IRQ"); |
160 | 160 | ||
161 | clk = of_clk_get(node, 0); | 161 | clk = of_clk_get(node, 0); |
162 | if (IS_ERR(clk)) | 162 | if (IS_ERR(clk)) |
163 | panic("Can't get timer clock"); | 163 | panic("Can't get timer clock"); |
164 | clk_prepare_enable(clk); | 164 | clk_prepare_enable(clk); |
165 | 165 | ||
166 | rate = clk_get_rate(clk); | 166 | rate = clk_get_rate(clk); |
167 | 167 | ||
168 | writel(~0, timer_base + TIMER_INTVAL_REG(1)); | 168 | writel(~0, timer_base + TIMER_INTVAL_REG(1)); |
169 | writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD | | 169 | writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD | |
170 | TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), | 170 | TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), |
171 | timer_base + TIMER_CTL_REG(1)); | 171 | timer_base + TIMER_CTL_REG(1)); |
172 | 172 | ||
173 | sched_clock_register(sun4i_timer_sched_read, 32, rate); | 173 | sched_clock_register(sun4i_timer_sched_read, 32, rate); |
174 | clocksource_mmio_init(timer_base + TIMER_CNTVAL_REG(1), node->name, | 174 | clocksource_mmio_init(timer_base + TIMER_CNTVAL_REG(1), node->name, |
175 | rate, 350, 32, clocksource_mmio_readl_down); | 175 | rate, 350, 32, clocksource_mmio_readl_down); |
176 | 176 | ||
177 | ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); | 177 | ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); |
178 | 178 | ||
179 | writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), | 179 | writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), |
180 | timer_base + TIMER_CTL_REG(0)); | 180 | timer_base + TIMER_CTL_REG(0)); |
181 | 181 | ||
182 | /* Make sure timer is stopped before playing with interrupts */ | 182 | /* Make sure timer is stopped before playing with interrupts */ |
183 | sun4i_clkevt_time_stop(0); | 183 | sun4i_clkevt_time_stop(0); |
184 | 184 | ||
185 | sun4i_clockevent.cpumask = cpu_possible_mask; | ||
186 | sun4i_clockevent.irq = irq; | ||
187 | |||
188 | clockevents_config_and_register(&sun4i_clockevent, rate, | ||
189 | TIMER_SYNC_TICKS, 0xffffffff); | ||
190 | |||
185 | ret = setup_irq(irq, &sun4i_timer_irq); | 191 | ret = setup_irq(irq, &sun4i_timer_irq); |
186 | if (ret) | 192 | if (ret) |
187 | pr_warn("failed to setup irq %d\n", irq); | 193 | pr_warn("failed to setup irq %d\n", irq); |
188 | 194 | ||
189 | /* Enable timer0 interrupt */ | 195 | /* Enable timer0 interrupt */ |
190 | val = readl(timer_base + TIMER_IRQ_EN_REG); | 196 | val = readl(timer_base + TIMER_IRQ_EN_REG); |
191 | writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); | 197 | writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); |
192 | |||
193 | sun4i_clockevent.cpumask = cpu_possible_mask; | ||
194 | sun4i_clockevent.irq = irq; | ||
195 |