Blame view
drivers/clocksource/dw_apb_timer_of.c
4.45 KB
af75655c0
|
1 |
/* |
cfda59017
|
2 |
* Copyright (C) 2012 Altera Corporation |
af75655c0
|
3 4 |
* Copyright (c) 2011 Picochip Ltd., Jamie Iles * |
cfda59017
|
5 6 |
* Modified from mach-picoxcell/time.c * |
af75655c0
|
7 8 9 10 |
* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * |
cfda59017
|
11 12 13 14 15 16 17 |
* This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. |
af75655c0
|
18 |
*/ |
9115df89d
|
19 |
#include <linux/delay.h> |
af75655c0
|
20 21 22 23 |
#include <linux/dw_apb_timer.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> |
a8b447f2b
|
24 |
#include <linux/clk.h> |
38ff87f77
|
25 |
#include <linux/sched_clock.h> |
af75655c0
|
26 |
|
1cf0203ac
|
27 |
static void __init timer_get_base_and_rate(struct device_node *np, |
af75655c0
|
28 29 |
void __iomem **base, u32 *rate) { |
a8b447f2b
|
30 31 |
struct clk *timer_clk; struct clk *pclk; |
af75655c0
|
32 33 34 35 |
*base = of_iomap(np, 0); if (!*base) panic("Unable to map regs for %s", np->name); |
a8b447f2b
|
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
/* * 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)) pr_warn("pclk for %s is present, but could not be activated ", np->name); 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
|
57 |
if (of_property_read_u32(np, "clock-freq", rate) && |
1cf0203ac
|
58 |
of_property_read_u32(np, "clock-frequency", rate)) |
a8b447f2b
|
59 |
panic("No clock nor clock-frequency property for %s", np->name); |
af75655c0
|
60 |
} |
1cf0203ac
|
61 |
static void __init add_clockevent(struct device_node *event_timer) |
af75655c0
|
62 63 64 65 66 67 |
{ void __iomem *iobase; struct dw_apb_clock_event_device *ced; u32 irq, rate; irq = irq_of_parse_and_map(event_timer, 0); |
1a33bd2be
|
68 |
if (irq == 0) |
af75655c0
|
69 70 71 72 73 74 75 76 77 78 79 |
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
|
80 81 |
static void __iomem *sched_io_base; static u32 sched_rate; |
1cf0203ac
|
82 |
static void __init add_clocksource(struct device_node *source_timer) |
af75655c0
|
83 84 85 86 87 88 89 90 91 92 93 94 95 |
{ 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
|
96 |
|
a1198f834
|
97 98 99 100 101 102 103 104 |
/* * 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
|
105 |
|
0d24d1f24
|
106 |
static u64 notrace read_sched_clock(void) |
af75655c0
|
107 |
{ |
3a10013b6
|
108 |
return ~readl_relaxed(sched_io_base); |
af75655c0
|
109 |
} |
cfda59017
|
110 |
static const struct of_device_id sptimer_ids[] __initconst = { |
af75655c0
|
111 112 113 |
{ .compatible = "picochip,pc3x2-rtc" }, { /* Sentinel */ }, }; |
1cf0203ac
|
114 |
static void __init init_sched_clock(void) |
af75655c0
|
115 116 |
{ struct device_node *sched_timer; |
af75655c0
|
117 |
|
cfda59017
|
118 |
sched_timer = of_find_matching_node(NULL, sptimer_ids); |
a1198f834
|
119 120 121 122 123 |
if (sched_timer) { timer_get_base_and_rate(sched_timer, &sched_io_base, &sched_rate); of_node_put(sched_timer); } |
af75655c0
|
124 |
|
fa8296ae6
|
125 |
sched_clock_register(read_sched_clock, 32, sched_rate); |
af75655c0
|
126 |
} |
9115df89d
|
127 128 129 130 131 132 133 134 135 136 |
#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
|
137 138 |
static int num_called; static void __init dw_apb_timer_init(struct device_node *timer) |
af75655c0
|
139 |
{ |
100214889
|
140 141 142 143 144 |
switch (num_called) { case 0: pr_debug("%s: found clockevent timer ", __func__); add_clockevent(timer); |
100214889
|
145 146 147 148 149 |
break; case 1: pr_debug("%s: found clocksource timer ", __func__); add_clocksource(timer); |
100214889
|
150 |
init_sched_clock(); |
9115df89d
|
151 152 153 154 |
#ifdef CONFIG_ARM dw_apb_delay_timer.freq = sched_rate; register_current_timer_delay(&dw_apb_delay_timer); #endif |
100214889
|
155 156 157 158 |
break; default: break; } |
af75655c0
|
159 |
|
100214889
|
160 |
num_called++; |
af75655c0
|
161 |
} |
100214889
|
162 |
CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); |
9ab4727c1
|
163 164 165 |
CLOCKSOURCE_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init); CLOCKSOURCE_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init); CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init); |