Blame view
drivers/clocksource/dw_apb_timer_of.c
4.17 KB
caab277b1
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
af75655c0
|
2 |
/* |
cfda59017
|
3 |
* Copyright (C) 2012 Altera Corporation |
af75655c0
|
4 5 |
* Copyright (c) 2011 Picochip Ltd., Jamie Iles * |
cfda59017
|
6 |
* Modified from mach-picoxcell/time.c |
af75655c0
|
7 |
*/ |
9115df89d
|
8 |
#include <linux/delay.h> |
af75655c0
|
9 10 11 12 |
#include <linux/dw_apb_timer.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> |
a8b447f2b
|
13 |
#include <linux/clk.h> |
1f174a1a2
|
14 |
#include <linux/reset.h> |
38ff87f77
|
15 |
#include <linux/sched_clock.h> |
af75655c0
|
16 |
|
1cf0203ac
|
17 |
static void __init timer_get_base_and_rate(struct device_node *np, |
af75655c0
|
18 19 |
void __iomem **base, u32 *rate) { |
a8b447f2b
|
20 21 |
struct clk *timer_clk; struct clk *pclk; |
1f174a1a2
|
22 |
struct reset_control *rstc; |
a8b447f2b
|
23 |
|
af75655c0
|
24 25 26 |
*base = of_iomap(np, 0); if (!*base) |
2a4849d26
|
27 |
panic("Unable to map regs for %pOFn", np); |
af75655c0
|
28 |
|
a8b447f2b
|
29 |
/* |
1f174a1a2
|
30 31 32 33 34 35 36 37 38 39 |
* Reset the timer if the reset control is available, wiping * out the state the firmware may have left it */ rstc = of_reset_control_get(np, NULL); if (!IS_ERR(rstc)) { reset_control_assert(rstc); reset_control_deassert(rstc); } /* |
a8b447f2b
|
40 41 42 43 44 45 |
* Not all implementations use a periphal clock, so don't panic * if it's not present */ pclk = of_clk_get_by_name(np, "pclk"); if (!IS_ERR(pclk)) if (clk_prepare_enable(pclk)) |
2a4849d26
|
46 47 48 |
pr_warn("pclk for %pOFn is present, but could not be activated ", np); |
a8b447f2b
|
49 50 51 52 53 54 55 56 57 58 59 |
timer_clk = of_clk_get_by_name(np, "timer"); if (IS_ERR(timer_clk)) goto try_clock_freq; if (!clk_prepare_enable(timer_clk)) { *rate = clk_get_rate(timer_clk); return; } try_clock_freq: |
cfda59017
|
60 |
if (of_property_read_u32(np, "clock-freq", rate) && |
1cf0203ac
|
61 |
of_property_read_u32(np, "clock-frequency", rate)) |
2a4849d26
|
62 |
panic("No clock nor clock-frequency property for %pOFn", np); |
af75655c0
|
63 |
} |
1cf0203ac
|
64 |
static void __init add_clockevent(struct device_node *event_timer) |
af75655c0
|
65 66 67 68 69 70 |
{ void __iomem *iobase; struct dw_apb_clock_event_device *ced; u32 irq, rate; irq = irq_of_parse_and_map(event_timer, 0); |
1a33bd2be
|
71 |
if (irq == 0) |
af75655c0
|
72 73 74 75 76 77 78 79 80 81 82 |
panic("No IRQ for clock event timer"); timer_get_base_and_rate(event_timer, &iobase, &rate); ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq, rate); if (!ced) panic("Unable to initialise clockevent device"); dw_apb_clockevent_register(ced); } |
a1198f834
|
83 84 |
static void __iomem *sched_io_base; static u32 sched_rate; |
1cf0203ac
|
85 |
static void __init add_clocksource(struct device_node *source_timer) |
af75655c0
|
86 87 88 89 90 91 92 93 94 95 96 97 98 |
{ void __iomem *iobase; struct dw_apb_clocksource *cs; u32 rate; timer_get_base_and_rate(source_timer, &iobase, &rate); cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate); if (!cs) panic("Unable to initialise clocksource device"); dw_apb_clocksource_start(cs); dw_apb_clocksource_register(cs); |
af75655c0
|
99 |
|
a1198f834
|
100 101 102 103 104 105 106 107 |
/* * Fallback to use the clocksource as sched_clock if no separate * timer is found. sched_io_base then points to the current_value * register of the clocksource timer. */ sched_io_base = iobase + 0x04; sched_rate = rate; } |
af75655c0
|
108 |
|
0d24d1f24
|
109 |
static u64 notrace read_sched_clock(void) |
af75655c0
|
110 |
{ |
3a10013b6
|
111 |
return ~readl_relaxed(sched_io_base); |
af75655c0
|
112 |
} |
cfda59017
|
113 |
static const struct of_device_id sptimer_ids[] __initconst = { |
af75655c0
|
114 115 116 |
{ .compatible = "picochip,pc3x2-rtc" }, { /* Sentinel */ }, }; |
1cf0203ac
|
117 |
static void __init init_sched_clock(void) |
af75655c0
|
118 119 |
{ struct device_node *sched_timer; |
af75655c0
|
120 |
|
cfda59017
|
121 |
sched_timer = of_find_matching_node(NULL, sptimer_ids); |
a1198f834
|
122 123 124 125 126 |
if (sched_timer) { timer_get_base_and_rate(sched_timer, &sched_io_base, &sched_rate); of_node_put(sched_timer); } |
af75655c0
|
127 |
|
fa8296ae6
|
128 |
sched_clock_register(read_sched_clock, 32, sched_rate); |
af75655c0
|
129 |
} |
9115df89d
|
130 131 132 133 134 135 136 137 138 139 |
#ifdef CONFIG_ARM static unsigned long dw_apb_delay_timer_read(void) { return ~readl_relaxed(sched_io_base); } static struct delay_timer dw_apb_delay_timer = { .read_current_timer = dw_apb_delay_timer_read, }; #endif |
100214889
|
140 |
static int num_called; |
2e1773f8c
|
141 |
static int __init dw_apb_timer_init(struct device_node *timer) |
af75655c0
|
142 |
{ |
100214889
|
143 |
switch (num_called) { |
100214889
|
144 145 146 147 |
case 1: pr_debug("%s: found clocksource timer ", __func__); add_clocksource(timer); |
100214889
|
148 |
init_sched_clock(); |
9115df89d
|
149 150 151 152 |
#ifdef CONFIG_ARM dw_apb_delay_timer.freq = sched_rate; register_current_timer_delay(&dw_apb_delay_timer); #endif |
100214889
|
153 154 |
break; default: |
bffe2c8e2
|
155 156 157 |
pr_debug("%s: found clockevent timer ", __func__); add_clockevent(timer); |
100214889
|
158 159 |
break; } |
af75655c0
|
160 |
|
100214889
|
161 |
num_called++; |
2e1773f8c
|
162 163 |
return 0; |
af75655c0
|
164 |
} |
172733959
|
165 166 167 168 |
TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init); TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init); TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init); |