| 1 | |
| 2 | |
| 3 | * linux/drivers/clocksource/timer-sp.c
|
| 4 | *
|
| 5 | * Copyright (C) 1999 - 2003 ARM Limited
|
| 6 | * Copyright (C) 2000 Deep Blue Solutions Ltd
|
| 7 |
|
| 8 |
|
| 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 10 |
|
| 11 | #include <linux/clk.h>
|
| 12 | #include <linux/clocksource.h>
|
| 13 | #include <linux/clockchips.h>
|
| 14 | #include <linux/err.h>
|
| 15 | #include <linux/interrupt.h>
|
| 16 | #include <linux/irq.h>
|
| 17 | #include <linux/io.h>
|
| 18 | #include <linux/of.h>
|
| 19 | #include <linux/of_address.h>
|
| 20 | #include <linux/of_clk.h>
|
| 21 | #include <linux/of_irq.h>
|
| 22 | #include <linux/sched_clock.h>
|
| 23 |
|
| 24 | #ifdef CONFIG_ARM
|
| 25 | #include <linux/delay.h>
|
| 26 | #endif
|
| 27 |
|
| 28 | #include "timer-sp.h"
|
| 29 |
|
| 30 |
|
| 31 | #define HISI_TIMER_1_BASE 0x00
|
| 32 | #define HISI_TIMER_2_BASE 0x40
|
| 33 | #define HISI_TIMER_LOAD 0x00
|
| 34 | #define HISI_TIMER_LOAD_H 0x04
|
| 35 | #define HISI_TIMER_VALUE 0x08
|
| 36 | #define HISI_TIMER_VALUE_H 0x0c
|
| 37 | #define HISI_TIMER_CTRL 0x10
|
| 38 | #define HISI_TIMER_INTCLR 0x14
|
| 39 | #define HISI_TIMER_RIS 0x18
|
| 40 | #define HISI_TIMER_MIS 0x1c
|
| 41 | #define HISI_TIMER_BGLOAD 0x20
|
| 42 | #define HISI_TIMER_BGLOAD_H 0x24
|
| 43 |
|
| 44 | static struct sp804_timer arm_sp804_timer __initdata = {
|
| 45 | .load = TIMER_LOAD,
|
| 46 | .value = TIMER_VALUE,
|
| 47 | .ctrl = TIMER_CTRL,
|
| 48 | .intclr = TIMER_INTCLR,
|
| 49 | .timer_base = {TIMER_1_BASE, TIMER_2_BASE},
|
| 50 | .width = 32,
|
| 51 | };
|
| 52 |
|
| 53 | static struct sp804_timer hisi_sp804_timer __initdata = {
|
| 54 | .load = HISI_TIMER_LOAD,
|
| 55 | .load_h = HISI_TIMER_LOAD_H,
|
| 56 | .value = HISI_TIMER_VALUE,
|
| 57 | .value_h = HISI_TIMER_VALUE_H,
|
| 58 | .ctrl = HISI_TIMER_CTRL,
|
| 59 | .intclr = HISI_TIMER_INTCLR,
|
| 60 | .timer_base = {HISI_TIMER_1_BASE, HISI_TIMER_2_BASE},
|
| 61 | .width = 64,
|
| 62 | };
|
| 63 |
|
| 64 | static struct sp804_clkevt sp804_clkevt[NR_TIMERS];
|
| 65 |
|
| 66 | static long __init sp804_get_clock_rate(struct clk *clk, const char *name)
|
| 67 | {
|
| 68 | int err;
|
| 69 |
|
| 70 | if (!clk)
|
| 71 | clk = clk_get_sys("sp804", name);
|
| 72 | if (IS_ERR(clk)) {
|
| 73 | pr_err("%s clock not found: %ld\n", name, PTR_ERR(clk));
|
| 74 | return PTR_ERR(clk);
|
| 75 | }
|
| 76 |
|
| 77 | err = clk_prepare_enable(clk);
|
| 78 | if (err) {
|
| 79 | pr_err("clock failed to enable: %d\n", err);
|
| 80 | clk_put(clk);
|
| 81 | return err;
|
| 82 | }
|
| 83 |
|
| 84 | return clk_get_rate(clk);
|
| 85 | }
|
| 86 |
|
| 87 | static struct sp804_clkevt * __init sp804_clkevt_get(void __iomem *base)
|
| 88 | {
|
| 89 | int i;
|
| 90 |
|
| 91 | for (i = 0; i < NR_TIMERS; i++) {
|
| 92 | if (sp804_clkevt[i].base == base)
|
| 93 | return &sp804_clkevt[i];
|
| 94 | }
|
| 95 |
|
| 96 |
|
| 97 | WARN_ON(1);
|
| 98 |
|
| 99 | return NULL;
|
| 100 | }
|
| 101 |
|
| 102 | static struct sp804_clkevt *sched_clkevt;
|
| 103 |
|
| 104 | static u64 notrace sp804_read(void)
|
| 105 | {
|
| 106 | return ~readl_relaxed(sched_clkevt->value);
|
| 107 | }
|
| 108 |
|
| 109 |
|
| 110 | #ifdef CONFIG_ARM
|
| 111 | static struct delay_timer delay;
|
| 112 | static struct sp804_clkevt *delay_clkevt;
|
| 113 |
|
| 114 | static unsigned long sp804_read_delay_timer_read(void)
|
| 115 | {
|
| 116 | return ~readl_relaxed(delay_clkevt->value);
|
| 117 | }
|
| 118 |
|
| 119 | static void sp804_register_delay_timer(struct sp804_clkevt *clk, int freq)
|
| 120 | {
|
| 121 | delay_clkevt = clk;
|
| 122 | delay.freq = freq;
|
| 123 | delay.read_current_timer = sp804_read_delay_timer_read;
|
| 124 | register_current_timer_delay(&delay);
|
| 125 | }
|
| 126 | #else
|
| 127 | static inline void sp804_register_delay_timer(struct sp804_clkevt *clk, int freq) {}
|
| 128 | #endif
|
| 129 |
|
| 130 | static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
|
| 131 | const char *name,
|
| 132 | struct clk *clk,
|
| 133 | int use_sched_clock)
|
| 134 | {
|
| 135 | long rate;
|
| 136 | struct sp804_clkevt *clkevt;
|
| 137 |
|
| 138 | rate = sp804_get_clock_rate(clk, name);
|
| 139 | if (rate < 0)
|
| 140 | return -EINVAL;
|
| 141 |
|
| 142 | clkevt = sp804_clkevt_get(base);
|
| 143 |
|
| 144 | writel(0, clkevt->ctrl);
|
| 145 | writel(0xffffffff, clkevt->load);
|
| 146 | writel(0xffffffff, clkevt->value);
|
| 147 | if (clkevt->width == 64) {
|
| 148 | writel(0xffffffff, clkevt->load_h);
|
| 149 | writel(0xffffffff, clkevt->value_h);
|
| 150 | }
|
| 151 | writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
|
| 152 | clkevt->ctrl);
|
| 153 |
|
| 154 | clocksource_mmio_init(clkevt->value, name,
|
| 155 | rate, 200, 32, clocksource_mmio_readl_down);
|
| 156 |
|
| 157 | sp804_register_delay_timer(clkevt, rate);
|
| 158 |
|
| 159 | if (use_sched_clock) {
|
| 160 | sched_clkevt = clkevt;
|
| 161 | sched_clock_register(sp804_read, 32, rate);
|
| 162 | }
|
| 163 |
|
| 164 | return 0;
|
| 165 | }
|
| 166 |
|
| 167 |
|
| 168 | static struct sp804_clkevt *common_clkevt;
|
| 169 |
|
| 170 | |
| 171 | * IRQ handler for the timer
|
| 172 |
|
| 173 | static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id)
|
| 174 | {
|
| 175 | struct clock_event_device *evt = dev_id;
|
| 176 |
|
| 177 |
|
| 178 | writel(1, common_clkevt->intclr);
|
| 179 |
|
| 180 | evt->event_handler(evt);
|
| 181 |
|
| 182 | return IRQ_HANDLED;
|
| 183 | }
|
| 184 |
|
| 185 | static inline void evt_timer_shutdown(struct clock_event_device *evt)
|
| 186 | {
|
| 187 | writel(0, common_clkevt->ctrl);
|
| 188 | }
|
| 189 |
|
| 190 | static int sp804_shutdown(struct clock_event_device *evt)
|
| 191 | {
|
| 192 | evt_timer_shutdown(evt);
|
| 193 | return 0;
|
| 194 | }
|
| 195 |
|
| 196 | static int sp804_set_periodic(struct clock_event_device *evt)
|
| 197 | {
|
| 198 | unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE |
|
| 199 | TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
|
| 200 |
|
| 201 | evt_timer_shutdown(evt);
|
| 202 | writel(common_clkevt->reload, common_clkevt->load);
|
| 203 | writel(ctrl, common_clkevt->ctrl);
|
| 204 | return 0;
|
| 205 | }
|
| 206 |
|
| 207 | static int sp804_set_next_event(unsigned long next,
|
| 208 | struct clock_event_device *evt)
|
| 209 | {
|
| 210 | unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE |
|
| 211 | TIMER_CTRL_ONESHOT | TIMER_CTRL_ENABLE;
|
| 212 |
|
| 213 | writel(next, common_clkevt->load);
|
| 214 | writel(ctrl, common_clkevt->ctrl);
|
| 215 |
|
| 216 | return 0;
|
| 217 | }
|
| 218 |
|
| 219 | static struct clock_event_device sp804_clockevent = {
|
| 220 | .features = CLOCK_EVT_FEAT_PERIODIC |
|
| 221 | CLOCK_EVT_FEAT_ONESHOT |
|
| 222 | CLOCK_EVT_FEAT_DYNIRQ,
|
| 223 | .set_state_shutdown = sp804_shutdown,
|
| 224 | .set_state_periodic = sp804_set_periodic,
|
| 225 | .set_state_oneshot = sp804_shutdown,
|
| 226 | .tick_resume = sp804_shutdown,
|
| 227 | .set_next_event = sp804_set_next_event,
|
| 228 | .rating = 300,
|
| 229 | };
|
| 230 |
|
| 231 | static int __init sp804_clockevents_init(void __iomem *base, unsigned int irq,
|
| 232 | struct clk *clk, const char *name)
|
| 233 | {
|
| 234 | struct clock_event_device *evt = &sp804_clockevent;
|
| 235 | long rate;
|
| 236 |
|
| 237 | rate = sp804_get_clock_rate(clk, name);
|
| 238 | if (rate < 0)
|
| 239 | return -EINVAL;
|
| 240 |
|
| 241 | common_clkevt = sp804_clkevt_get(base);
|
| 242 | common_clkevt->reload = DIV_ROUND_CLOSEST(rate, HZ);
|
| 243 | evt->name = name;
|
| 244 | evt->irq = irq;
|
| 245 | evt->cpumask = cpu_possible_mask;
|
| 246 |
|
| 247 | writel(0, common_clkevt->ctrl);
|
| 248 |
|
| 249 | if (request_irq(irq, sp804_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
|
| 250 | "timer", &sp804_clockevent))
|
| 251 | pr_err("request_irq() failed\n");
|
| 252 | clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
|
| 253 |
|
| 254 | return 0;
|
| 255 | }
|
| 256 |
|
| 257 | static void __init sp804_clkevt_init(struct sp804_timer *timer, void __iomem *base)
|
| 258 | {
|
| 259 | int i;
|
| 260 |
|
| 261 | for (i = 0; i < NR_TIMERS; i++) {
|
| 262 | void __iomem *timer_base;
|
| 263 | struct sp804_clkevt *clkevt;
|
| 264 |
|
| 265 | timer_base = base + timer->timer_base[i];
|
| 266 | clkevt = &sp804_clkevt[i];
|
| 267 | clkevt->base = timer_base;
|
| 268 | clkevt->load = timer_base + timer->load;
|
| 269 | clkevt->load_h = timer_base + timer->load_h;
|
| 270 | clkevt->value = timer_base + timer->value;
|
| 271 | clkevt->value_h = timer_base + timer->value_h;
|
| 272 | clkevt->ctrl = timer_base + timer->ctrl;
|
| 273 | clkevt->intclr = timer_base + timer->intclr;
|
| 274 | clkevt->width = timer->width;
|
| 275 | }
|
| 276 | }
|
| 277 |
|
| 278 | static int __init sp804_of_init(struct device_node *np, struct sp804_timer *timer)
|
| 279 | {
|
| 280 | static bool initialized = false;
|
| 281 | void __iomem *base;
|
| 282 | void __iomem *timer1_base;
|
| 283 | void __iomem *timer2_base;
|
| 284 | int irq, ret = -EINVAL;
|
| 285 | u32 irq_num = 0;
|
| 286 | struct clk *clk1, *clk2;
|
| 287 | const char *name = of_get_property(np, "compatible", NULL);
|
| 288 |
|
| 289 | if (initialized) {
|
| 290 | pr_debug("%pOF: skipping further SP804 timer device\n", np);
|
| 291 | return 0;
|
| 292 | }
|
| 293 |
|
| 294 | base = of_iomap(np, 0);
|
| 295 | if (!base)
|
| 296 | return -ENXIO;
|
| 297 |
|
| 298 | timer1_base = base + timer->timer_base[0];
|
| 299 | timer2_base = base + timer->timer_base[1];
|
| 300 |
|
| 301 |
|
| 302 | writel(0, timer1_base + timer->ctrl);
|
| 303 | writel(0, timer2_base + timer->ctrl);
|
| 304 |
|
| 305 | clk1 = of_clk_get(np, 0);
|
| 306 | if (IS_ERR(clk1))
|
| 307 | clk1 = NULL;
|
| 308 |
|
| 309 |
|
| 310 | if (of_clk_get_parent_count(np) == 3) {
|
| 311 | clk2 = of_clk_get(np, 1);
|
| 312 | if (IS_ERR(clk2)) {
|
| 313 | pr_err("%pOFn clock not found: %d\n", np,
|
| 314 | (int)PTR_ERR(clk2));
|
| 315 | clk2 = NULL;
|
| 316 | }
|
| 317 | } else
|
| 318 | clk2 = clk1;
|
| 319 |
|
| 320 | irq = irq_of_parse_and_map(np, 0);
|
| 321 | if (irq <= 0)
|
| 322 | goto err;
|
| 323 |
|
| 324 | sp804_clkevt_init(timer, base);
|
| 325 |
|
| 326 | of_property_read_u32(np, "arm,sp804-has-irq", &irq_num);
|
| 327 | if (irq_num == 2) {
|
| 328 |
|
| 329 | ret = sp804_clockevents_init(timer2_base, irq, clk2, name);
|
| 330 | if (ret)
|
| 331 | goto err;
|
| 332 |
|
| 333 | ret = sp804_clocksource_and_sched_clock_init(timer1_base,
|
| 334 | name, clk1, 1);
|
| 335 | if (ret)
|
| 336 | goto err;
|
| 337 | } else {
|
| 338 |
|
| 339 | ret = sp804_clockevents_init(timer1_base, irq, clk1, name);
|
| 340 | if (ret)
|
| 341 | goto err;
|
| 342 |
|
| 343 | ret = sp804_clocksource_and_sched_clock_init(timer2_base,
|
| 344 | name, clk2, 1);
|
| 345 | if (ret)
|
| 346 | goto err;
|
| 347 | }
|
| 348 |
|
| 349 | initialized = true;
|
| 350 |
|
| 351 | return 0;
|
| 352 | err:
|
| 353 | iounmap(base);
|
| 354 | return ret;
|
| 355 | }
|
| 356 |
|
| 357 | static int __init arm_sp804_of_init(struct device_node *np)
|
| 358 | {
|
| 359 | return sp804_of_init(np, &arm_sp804_timer);
|
| 360 | }
|
| 361 | TIMER_OF_DECLARE(sp804, "arm,sp804", arm_sp804_of_init);
|
| 362 |
|
| 363 | static int __init hisi_sp804_of_init(struct device_node *np)
|
| 364 | {
|
| 365 | return sp804_of_init(np, &hisi_sp804_timer);
|
| 366 | }
|
| 367 | TIMER_OF_DECLARE(hisi_sp804, "hisilicon,sp804", hisi_sp804_of_init);
|
| 368 |
|
| 369 | static int __init integrator_cp_of_init(struct device_node *np)
|
| 370 | {
|
| 371 | static int init_count = 0;
|
| 372 | void __iomem *base;
|
| 373 | int irq, ret = -EINVAL;
|
| 374 | const char *name = of_get_property(np, "compatible", NULL);
|
| 375 | struct clk *clk;
|
| 376 |
|
| 377 | base = of_iomap(np, 0);
|
| 378 | if (!base) {
|
| 379 | pr_err("Failed to iomap\n");
|
| 380 | return -ENXIO;
|
| 381 | }
|
| 382 |
|
| 383 | clk = of_clk_get(np, 0);
|
| 384 | if (IS_ERR(clk)) {
|
| 385 | pr_err("Failed to get clock\n");
|
| 386 | return PTR_ERR(clk);
|
| 387 | }
|
| 388 |
|
| 389 |
|
| 390 | writel(0, base + arm_sp804_timer.ctrl);
|
| 391 |
|
| 392 | if (init_count == 2 || !of_device_is_available(np))
|
| 393 | goto err;
|
| 394 |
|
| 395 | sp804_clkevt_init(&arm_sp804_timer, base);
|
| 396 |
|
| 397 | if (!init_count) {
|
| 398 | ret = sp804_clocksource_and_sched_clock_init(base,
|
| 399 | name, clk, 0);
|
| 400 | if (ret)
|
| 401 | goto err;
|
| 402 | } else {
|
| 403 | irq = irq_of_parse_and_map(np, 0);
|
| 404 | if (irq <= 0)
|
| 405 | goto err;
|
| 406 |
|
| 407 | ret = sp804_clockevents_init(base, irq, clk, name);
|
| 408 | if (ret)
|
| 409 | goto err;
|
| 410 | }
|
| 411 |
|
| 412 | init_count++;
|
| 413 | return 0;
|
| 414 | err:
|
| 415 | iounmap(base);
|
| 416 | return ret;
|
| 417 | }
|
| 418 | TIMER_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
|