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