diff options
Diffstat (limited to 'drivers/clocksource/timer-stm32.c')
-rw-r--r-- | drivers/clocksource/timer-stm32.c | 56 |
1 files changed, 44 insertions, 12 deletions
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c index 928ac281f937..882037f1d8d6 100644 --- a/drivers/clocksource/timer-stm32.c +++ b/drivers/clocksource/timer-stm32.c | |||
@@ -24,14 +24,18 @@ | |||
24 | #define TIM_DIER 0x0c | 24 | #define TIM_DIER 0x0c |
25 | #define TIM_SR 0x10 | 25 | #define TIM_SR 0x10 |
26 | #define TIM_EGR 0x14 | 26 | #define TIM_EGR 0x14 |
27 | #define TIM_CNT 0x24 | ||
27 | #define TIM_PSC 0x28 | 28 | #define TIM_PSC 0x28 |
28 | #define TIM_ARR 0x2c | 29 | #define TIM_ARR 0x2c |
30 | #define TIM_CCR1 0x34 | ||
29 | 31 | ||
30 | #define TIM_CR1_CEN BIT(0) | 32 | #define TIM_CR1_CEN BIT(0) |
33 | #define TIM_CR1_UDIS BIT(1) | ||
31 | #define TIM_CR1_OPM BIT(3) | 34 | #define TIM_CR1_OPM BIT(3) |
32 | #define TIM_CR1_ARPE BIT(7) | 35 | #define TIM_CR1_ARPE BIT(7) |
33 | 36 | ||
34 | #define TIM_DIER_UIE BIT(0) | 37 | #define TIM_DIER_UIE BIT(0) |
38 | #define TIM_DIER_CC1IE BIT(1) | ||
35 | 39 | ||
36 | #define TIM_SR_UIF BIT(0) | 40 | #define TIM_SR_UIF BIT(0) |
37 | 41 | ||
@@ -40,33 +44,57 @@ | |||
40 | #define TIM_PSC_MAX USHRT_MAX | 44 | #define TIM_PSC_MAX USHRT_MAX |
41 | #define TIM_PSC_CLKRATE 10000 | 45 | #define TIM_PSC_CLKRATE 10000 |
42 | 46 | ||
47 | static void stm32_clock_event_disable(struct timer_of *to) | ||
48 | { | ||
49 | writel_relaxed(0, timer_of_base(to) + TIM_DIER); | ||
50 | } | ||
51 | |||
52 | static void stm32_clock_event_enable(struct timer_of *to) | ||
53 | { | ||
54 | writel_relaxed(TIM_CR1_UDIS | TIM_CR1_CEN, timer_of_base(to) + TIM_CR1); | ||
55 | } | ||
56 | |||
43 | static int stm32_clock_event_shutdown(struct clock_event_device *clkevt) | 57 | static int stm32_clock_event_shutdown(struct clock_event_device *clkevt) |
44 | { | 58 | { |
45 | struct timer_of *to = to_timer_of(clkevt); | 59 | struct timer_of *to = to_timer_of(clkevt); |
46 | 60 | ||
47 | writel_relaxed(0, timer_of_base(to) + TIM_CR1); | 61 | stm32_clock_event_disable(to); |
48 | 62 | ||
49 | return 0; | 63 | return 0; |
50 | } | 64 | } |
51 | 65 | ||
52 | static int stm32_clock_event_set_periodic(struct clock_event_device *clkevt) | 66 | static int stm32_clock_event_set_next_event(unsigned long evt, |
67 | struct clock_event_device *clkevt) | ||
53 | { | 68 | { |
54 | struct timer_of *to = to_timer_of(clkevt); | 69 | struct timer_of *to = to_timer_of(clkevt); |
70 | unsigned long now, next; | ||
71 | |||
72 | next = readl_relaxed(timer_of_base(to) + TIM_CNT) + evt; | ||
73 | writel_relaxed(next, timer_of_base(to) + TIM_CCR1); | ||
74 | now = readl_relaxed(timer_of_base(to) + TIM_CNT); | ||
75 | |||
76 | if ((next - now) > evt) | ||
77 | return -ETIME; | ||
55 | 78 | ||
56 | writel_relaxed(timer_of_period(to), timer_of_base(to) + TIM_ARR); | 79 | writel_relaxed(TIM_DIER_CC1IE, timer_of_base(to) + TIM_DIER); |
57 | writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, timer_of_base(to) + TIM_CR1); | ||
58 | 80 | ||
59 | return 0; | 81 | return 0; |
60 | } | 82 | } |
61 | 83 | ||
62 | static int stm32_clock_event_set_next_event(unsigned long evt, | 84 | static int stm32_clock_event_set_periodic(struct clock_event_device *clkevt) |
63 | struct clock_event_device *clkevt) | 85 | { |
86 | struct timer_of *to = to_timer_of(clkevt); | ||
87 | |||
88 | stm32_clock_event_enable(to); | ||
89 | |||
90 | return stm32_clock_event_set_next_event(timer_of_period(to), clkevt); | ||
91 | } | ||
92 | |||
93 | static int stm32_clock_event_set_oneshot(struct clock_event_device *clkevt) | ||
64 | { | 94 | { |
65 | struct timer_of *to = to_timer_of(clkevt); | 95 | struct timer_of *to = to_timer_of(clkevt); |
66 | 96 | ||
67 | writel_relaxed(evt, timer_of_base(to) + TIM_ARR); | 97 | stm32_clock_event_enable(to); |
68 | writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN, | ||
69 | timer_of_base(to) + TIM_CR1); | ||
70 | 98 | ||
71 | return 0; | 99 | return 0; |
72 | } | 100 | } |
@@ -78,6 +106,11 @@ static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id) | |||
78 | 106 | ||
79 | writel_relaxed(0, timer_of_base(to) + TIM_SR); | 107 | writel_relaxed(0, timer_of_base(to) + TIM_SR); |
80 | 108 | ||
109 | if (clockevent_state_periodic(clkevt)) | ||
110 | stm32_clock_event_set_periodic(clkevt); | ||
111 | else | ||
112 | stm32_clock_event_shutdown(clkevt); | ||
113 | |||
81 | clkevt->event_handler(clkevt); | 114 | clkevt->event_handler(clkevt); |
82 | 115 | ||
83 | return IRQ_HANDLED; | 116 | return IRQ_HANDLED; |
@@ -108,9 +141,10 @@ static void __init stm32_clockevent_init(struct timer_of *to) | |||
108 | 141 | ||
109 | to->clkevt.name = to->np->full_name; | 142 | to->clkevt.name = to->np->full_name; |
110 | to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC; | 143 | to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC; |
144 | to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; | ||
111 | to->clkevt.set_state_shutdown = stm32_clock_event_shutdown; | 145 | to->clkevt.set_state_shutdown = stm32_clock_event_shutdown; |
112 | to->clkevt.set_state_periodic = stm32_clock_event_set_periodic; | 146 | to->clkevt.set_state_periodic = stm32_clock_event_set_periodic; |
113 | to->clkevt.set_state_oneshot = stm32_clock_event_shutdown; | 147 | to->clkevt.set_state_oneshot = stm32_clock_event_set_oneshot; |
114 | to->clkevt.tick_resume = stm32_clock_event_shutdown; | 148 | to->clkevt.tick_resume = stm32_clock_event_shutdown; |
115 | to->clkevt.set_next_event = stm32_clock_event_set_next_event; | 149 | to->clkevt.set_next_event = stm32_clock_event_set_next_event; |
116 | 150 | ||
@@ -129,12 +163,10 @@ static void __init stm32_clockevent_init(struct timer_of *to) | |||
129 | prescaler = prescaler < TIM_PSC_MAX ? prescaler : TIM_PSC_MAX; | 163 | prescaler = prescaler < TIM_PSC_MAX ? prescaler : TIM_PSC_MAX; |
130 | to->clkevt.rating = 100; | 164 | to->clkevt.rating = 100; |
131 | } | 165 | } |
132 | writel_relaxed(0, timer_of_base(to) + TIM_ARR); | ||
133 | 166 | ||
134 | writel_relaxed(prescaler - 1, timer_of_base(to) + TIM_PSC); | 167 | writel_relaxed(prescaler - 1, timer_of_base(to) + TIM_PSC); |
135 | writel_relaxed(TIM_EGR_UG, timer_of_base(to) + TIM_EGR); | 168 | writel_relaxed(TIM_EGR_UG, timer_of_base(to) + TIM_EGR); |
136 | writel_relaxed(0, timer_of_base(to) + TIM_SR); | 169 | writel_relaxed(0, timer_of_base(to) + TIM_SR); |
137 | writel_relaxed(TIM_DIER_UIE, timer_of_base(to) + TIM_DIER); | ||
138 | 170 | ||
139 | /* Adjust rate and period given the prescaler value */ | 171 | /* Adjust rate and period given the prescaler value */ |
140 | to->of_clk.rate = DIV_ROUND_CLOSEST(to->of_clk.rate, prescaler); | 172 | to->of_clk.rate = DIV_ROUND_CLOSEST(to->of_clk.rate, prescaler); |