aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/sun4i_timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clocksource/sun4i_timer.c')
-rw-r--r--drivers/clocksource/sun4i_timer.c110
1 files changed, 78 insertions, 32 deletions
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index d4674e78ef35..8ead0258740a 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -19,42 +19,83 @@
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/of.h> 23#include <linux/of.h>
23#include <linux/of_address.h> 24#include <linux/of_address.h>
24#include <linux/of_irq.h> 25#include <linux/of_irq.h>
25 26
26#define TIMER_IRQ_EN_REG 0x00 27#define TIMER_IRQ_EN_REG 0x00
27#define TIMER_IRQ_EN(val) (1 << val) 28#define TIMER_IRQ_EN(val) BIT(val)
28#define TIMER_IRQ_ST_REG 0x04 29#define TIMER_IRQ_ST_REG 0x04
29#define TIMER_CTL_REG(val) (0x10 * val + 0x10) 30#define TIMER_CTL_REG(val) (0x10 * val + 0x10)
30#define TIMER_CTL_ENABLE (1 << 0) 31#define TIMER_CTL_ENABLE BIT(0)
31#define TIMER_CTL_AUTORELOAD (1 << 1) 32#define TIMER_CTL_RELOAD BIT(1)
32#define TIMER_CTL_ONESHOT (1 << 7) 33#define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2)
33#define TIMER_INTVAL_REG(val) (0x10 * val + 0x14) 34#define TIMER_CTL_CLK_SRC_OSC24M (1)
34#define TIMER_CNTVAL_REG(val) (0x10 * val + 0x18) 35#define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4)
35 36#define TIMER_CTL_ONESHOT BIT(7)
36#define TIMER_SCAL 16 37#define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14)
38#define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18)
37 39
38static void __iomem *timer_base; 40static void __iomem *timer_base;
41static u32 ticks_per_jiffy;
42
43/*
44 * When we disable a timer, we need to wait at least for 2 cycles of
45 * the timer source clock. We will use for that the clocksource timer
46 * that is already setup and runs at the same frequency than the other
47 * timers, and we never will be disabled.
48 */
49static void sun4i_clkevt_sync(void)
50{
51 u32 old = readl(timer_base + TIMER_CNTVAL_REG(1));
52
53 while ((old - readl(timer_base + TIMER_CNTVAL_REG(1))) < 3)
54 cpu_relax();
55}
56
57static void sun4i_clkevt_time_stop(u8 timer)
58{
59 u32 val = readl(timer_base + TIMER_CTL_REG(timer));
60 writel(val & ~TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(timer));
61 sun4i_clkevt_sync();
62}
63
64static void sun4i_clkevt_time_setup(u8 timer, unsigned long delay)
65{
66 writel(delay, timer_base + TIMER_INTVAL_REG(timer));
67}
68
69static void sun4i_clkevt_time_start(u8 timer, bool periodic)
70{
71 u32 val = readl(timer_base + TIMER_CTL_REG(timer));
72
73 if (periodic)
74 val &= ~TIMER_CTL_ONESHOT;
75 else
76 val |= TIMER_CTL_ONESHOT;
77
78 writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
79 timer_base + TIMER_CTL_REG(timer));
80}
39 81
40static void sun4i_clkevt_mode(enum clock_event_mode mode, 82static void sun4i_clkevt_mode(enum clock_event_mode mode,
41 struct clock_event_device *clk) 83 struct clock_event_device *clk)
42{ 84{
43 u32 u = readl(timer_base + TIMER_CTL_REG(0));
44
45 switch (mode) { 85 switch (mode) {
46 case CLOCK_EVT_MODE_PERIODIC: 86 case CLOCK_EVT_MODE_PERIODIC:
47 u &= ~(TIMER_CTL_ONESHOT); 87 sun4i_clkevt_time_stop(0);
48 writel(u | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(0)); 88 sun4i_clkevt_time_setup(0, ticks_per_jiffy);
89 sun4i_clkevt_time_start(0, true);
49 break; 90 break;
50
51 case CLOCK_EVT_MODE_ONESHOT: 91 case CLOCK_EVT_MODE_ONESHOT:
52 writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0)); 92 sun4i_clkevt_time_stop(0);
93 sun4i_clkevt_time_start(0, false);
53 break; 94 break;
54 case CLOCK_EVT_MODE_UNUSED: 95 case CLOCK_EVT_MODE_UNUSED:
55 case CLOCK_EVT_MODE_SHUTDOWN: 96 case CLOCK_EVT_MODE_SHUTDOWN:
56 default: 97 default:
57 writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0)); 98 sun4i_clkevt_time_stop(0);
58 break; 99 break;
59 } 100 }
60} 101}
@@ -62,10 +103,9 @@ static void sun4i_clkevt_mode(enum clock_event_mode mode,
62static int sun4i_clkevt_next_event(unsigned long evt, 103static int sun4i_clkevt_next_event(unsigned long evt,
63 struct clock_event_device *unused) 104 struct clock_event_device *unused)
64{ 105{
65 u32 u = readl(timer_base + TIMER_CTL_REG(0)); 106 sun4i_clkevt_time_stop(0);
66 writel(evt, timer_base + TIMER_CNTVAL_REG(0)); 107 sun4i_clkevt_time_setup(0, evt);
67 writel(u | TIMER_CTL_ENABLE | TIMER_CTL_AUTORELOAD, 108 sun4i_clkevt_time_start(0, false);
68 timer_base + TIMER_CTL_REG(0));
69 109
70 return 0; 110 return 0;
71} 111}
@@ -96,6 +136,11 @@ static struct irqaction sun4i_timer_irq = {
96 .dev_id = &sun4i_clockevent, 136 .dev_id = &sun4i_clockevent,
97}; 137};
98 138
139static u32 sun4i_timer_sched_read(void)
140{
141 return ~readl(timer_base + TIMER_CNTVAL_REG(1));
142}
143
99static void __init sun4i_timer_init(struct device_node *node) 144static void __init sun4i_timer_init(struct device_node *node)
100{ 145{
101 unsigned long rate = 0; 146 unsigned long rate = 0;
@@ -114,22 +159,23 @@ static void __init sun4i_timer_init(struct device_node *node)
114 clk = of_clk_get(node, 0); 159 clk = of_clk_get(node, 0);
115 if (IS_ERR(clk)) 160 if (IS_ERR(clk))
116 panic("Can't get timer clock"); 161 panic("Can't get timer clock");
162 clk_prepare_enable(clk);
117 163
118 rate = clk_get_rate(clk); 164 rate = clk_get_rate(clk);
119 165
120 writel(rate / (TIMER_SCAL * HZ), 166 writel(~0, timer_base + TIMER_INTVAL_REG(1));
121 timer_base + TIMER_INTVAL_REG(0)); 167 writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
168 TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
169 timer_base + TIMER_CTL_REG(1));
170
171 setup_sched_clock(sun4i_timer_sched_read, 32, rate);
172 clocksource_mmio_init(timer_base + TIMER_CNTVAL_REG(1), node->name,
173 rate, 300, 32, clocksource_mmio_readl_down);
122 174
123 /* set clock source to HOSC, 16 pre-division */ 175 ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
124 val = readl(timer_base + TIMER_CTL_REG(0));
125 val &= ~(0x07 << 4);
126 val &= ~(0x03 << 2);
127 val |= (4 << 4) | (1 << 2);
128 writel(val, timer_base + TIMER_CTL_REG(0));
129 176
130 /* set mode to auto reload */ 177 writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
131 val = readl(timer_base + TIMER_CTL_REG(0)); 178 timer_base + TIMER_CTL_REG(0));
132 writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0));
133 179
134 ret = setup_irq(irq, &sun4i_timer_irq); 180 ret = setup_irq(irq, &sun4i_timer_irq);
135 if (ret) 181 if (ret)
@@ -141,8 +187,8 @@ static void __init sun4i_timer_init(struct device_node *node)
141 187
142 sun4i_clockevent.cpumask = cpumask_of(0); 188 sun4i_clockevent.cpumask = cpumask_of(0);
143 189
144 clockevents_config_and_register(&sun4i_clockevent, rate / TIMER_SCAL, 190 clockevents_config_and_register(&sun4i_clockevent, rate, 0x1,
145 0x1, 0xff); 191 0xffffffff);
146} 192}
147CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer", 193CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer",
148 sun4i_timer_init); 194 sun4i_timer_init);