1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Marvell Orion SoC timer handling. |
4 | * |
5 | * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> |
6 | * |
7 | * Timer 0 is used as free-running clocksource, while timer 1 is |
8 | * used as clock_event_device. |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/bitops.h> |
13 | #include <linux/clk.h> |
14 | #include <linux/clockchips.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/of_address.h> |
18 | #include <linux/of_irq.h> |
19 | #include <linux/spinlock.h> |
20 | #include <linux/sched_clock.h> |
21 | |
22 | #define TIMER_CTRL 0x00 |
23 | #define TIMER0_EN BIT(0) |
24 | #define TIMER0_RELOAD_EN BIT(1) |
25 | #define TIMER1_EN BIT(2) |
26 | #define TIMER1_RELOAD_EN BIT(3) |
27 | #define TIMER0_RELOAD 0x10 |
28 | #define TIMER0_VAL 0x14 |
29 | #define TIMER1_RELOAD 0x18 |
30 | #define TIMER1_VAL 0x1c |
31 | |
32 | #define ORION_ONESHOT_MIN 1 |
33 | #define ORION_ONESHOT_MAX 0xfffffffe |
34 | |
35 | static void __iomem *timer_base; |
36 | |
37 | static unsigned long notrace orion_read_timer(void) |
38 | { |
39 | return ~readl(addr: timer_base + TIMER0_VAL); |
40 | } |
41 | |
42 | static struct delay_timer orion_delay_timer = { |
43 | .read_current_timer = orion_read_timer, |
44 | }; |
45 | |
46 | static void orion_delay_timer_init(unsigned long rate) |
47 | { |
48 | orion_delay_timer.freq = rate; |
49 | register_current_timer_delay(&orion_delay_timer); |
50 | } |
51 | |
52 | /* |
53 | * Free-running clocksource handling. |
54 | */ |
55 | static u64 notrace orion_read_sched_clock(void) |
56 | { |
57 | return ~readl(addr: timer_base + TIMER0_VAL); |
58 | } |
59 | |
60 | /* |
61 | * Clockevent handling. |
62 | */ |
63 | static u32 ticks_per_jiffy; |
64 | |
65 | static int orion_clkevt_next_event(unsigned long delta, |
66 | struct clock_event_device *dev) |
67 | { |
68 | /* setup and enable one-shot timer */ |
69 | writel(val: delta, addr: timer_base + TIMER1_VAL); |
70 | atomic_io_modify(timer_base + TIMER_CTRL, |
71 | TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN); |
72 | |
73 | return 0; |
74 | } |
75 | |
76 | static int orion_clkevt_shutdown(struct clock_event_device *dev) |
77 | { |
78 | /* disable timer */ |
79 | atomic_io_modify(timer_base + TIMER_CTRL, |
80 | TIMER1_RELOAD_EN | TIMER1_EN, 0); |
81 | return 0; |
82 | } |
83 | |
84 | static int orion_clkevt_set_periodic(struct clock_event_device *dev) |
85 | { |
86 | /* setup and enable periodic timer at 1/HZ intervals */ |
87 | writel(val: ticks_per_jiffy - 1, addr: timer_base + TIMER1_RELOAD); |
88 | writel(val: ticks_per_jiffy - 1, addr: timer_base + TIMER1_VAL); |
89 | atomic_io_modify(timer_base + TIMER_CTRL, |
90 | TIMER1_RELOAD_EN | TIMER1_EN, |
91 | TIMER1_RELOAD_EN | TIMER1_EN); |
92 | return 0; |
93 | } |
94 | |
95 | static struct clock_event_device orion_clkevt = { |
96 | .name = "orion_event" , |
97 | .features = CLOCK_EVT_FEAT_ONESHOT | |
98 | CLOCK_EVT_FEAT_PERIODIC, |
99 | .shift = 32, |
100 | .rating = 300, |
101 | .set_next_event = orion_clkevt_next_event, |
102 | .set_state_shutdown = orion_clkevt_shutdown, |
103 | .set_state_periodic = orion_clkevt_set_periodic, |
104 | .set_state_oneshot = orion_clkevt_shutdown, |
105 | .tick_resume = orion_clkevt_shutdown, |
106 | }; |
107 | |
108 | static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id) |
109 | { |
110 | orion_clkevt.event_handler(&orion_clkevt); |
111 | return IRQ_HANDLED; |
112 | } |
113 | |
114 | static int __init orion_timer_init(struct device_node *np) |
115 | { |
116 | unsigned long rate; |
117 | struct clk *clk; |
118 | int irq, ret; |
119 | |
120 | /* timer registers are shared with watchdog timer */ |
121 | timer_base = of_iomap(node: np, index: 0); |
122 | if (!timer_base) { |
123 | pr_err("%pOFn: unable to map resource\n" , np); |
124 | return -ENXIO; |
125 | } |
126 | |
127 | clk = of_clk_get(np, index: 0); |
128 | if (IS_ERR(ptr: clk)) { |
129 | pr_err("%pOFn: unable to get clk\n" , np); |
130 | return PTR_ERR(ptr: clk); |
131 | } |
132 | |
133 | ret = clk_prepare_enable(clk); |
134 | if (ret) { |
135 | pr_err("Failed to prepare clock\n" ); |
136 | return ret; |
137 | } |
138 | |
139 | /* we are only interested in timer1 irq */ |
140 | irq = irq_of_parse_and_map(node: np, index: 1); |
141 | if (irq <= 0) { |
142 | pr_err("%pOFn: unable to parse timer1 irq\n" , np); |
143 | ret = -EINVAL; |
144 | goto out_unprep_clk; |
145 | } |
146 | |
147 | rate = clk_get_rate(clk); |
148 | |
149 | /* setup timer0 as free-running clocksource */ |
150 | writel(val: ~0, addr: timer_base + TIMER0_VAL); |
151 | writel(val: ~0, addr: timer_base + TIMER0_RELOAD); |
152 | atomic_io_modify(timer_base + TIMER_CTRL, |
153 | TIMER0_RELOAD_EN | TIMER0_EN, |
154 | TIMER0_RELOAD_EN | TIMER0_EN); |
155 | |
156 | ret = clocksource_mmio_init(timer_base + TIMER0_VAL, |
157 | "orion_clocksource" , rate, 300, 32, |
158 | clocksource_mmio_readl_down); |
159 | if (ret) { |
160 | pr_err("Failed to initialize mmio timer\n" ); |
161 | goto out_unprep_clk; |
162 | } |
163 | |
164 | sched_clock_register(read: orion_read_sched_clock, bits: 32, rate); |
165 | |
166 | /* setup timer1 as clockevent timer */ |
167 | ret = request_irq(irq, handler: orion_clkevt_irq_handler, IRQF_TIMER, |
168 | name: "orion_event" , NULL); |
169 | if (ret) { |
170 | pr_err("%pOFn: unable to setup irq\n" , np); |
171 | goto out_unprep_clk; |
172 | } |
173 | |
174 | ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ; |
175 | orion_clkevt.cpumask = cpumask_of(0); |
176 | orion_clkevt.irq = irq; |
177 | clockevents_config_and_register(dev: &orion_clkevt, freq: rate, |
178 | ORION_ONESHOT_MIN, ORION_ONESHOT_MAX); |
179 | |
180 | |
181 | orion_delay_timer_init(rate); |
182 | |
183 | return 0; |
184 | |
185 | out_unprep_clk: |
186 | clk_disable_unprepare(clk); |
187 | return ret; |
188 | } |
189 | TIMER_OF_DECLARE(orion_timer, "marvell,orion-timer" , orion_timer_init); |
190 | |