dw_apb_timer_of.c revision 0a0a7e66fa269de78975ea8d4e825a66d92b8d70
1/* 2 * Copyright (C) 2012 Altera Corporation 3 * Copyright (c) 2011 Picochip Ltd., Jamie Iles 4 * 5 * Modified from mach-picoxcell/time.c 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19#include <linux/dw_apb_timer.h> 20#include <linux/of.h> 21#include <linux/of_address.h> 22#include <linux/of_irq.h> 23 24#include <asm/sched_clock.h> 25 26static void timer_get_base_and_rate(struct device_node *np, 27 void __iomem **base, u32 *rate) 28{ 29 *base = of_iomap(np, 0); 30 31 if (!*base) 32 panic("Unable to map regs for %s", np->name); 33 34 if (of_property_read_u32(np, "clock-freq", rate) && 35 of_property_read_u32(np, "clock-frequency", rate)) 36 panic("No clock-frequency property for %s", np->name); 37} 38 39static void add_clockevent(struct device_node *event_timer) 40{ 41 void __iomem *iobase; 42 struct dw_apb_clock_event_device *ced; 43 u32 irq, rate; 44 45 irq = irq_of_parse_and_map(event_timer, 0); 46 if (irq == NO_IRQ) 47 panic("No IRQ for clock event timer"); 48 49 timer_get_base_and_rate(event_timer, &iobase, &rate); 50 51 ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq, 52 rate); 53 if (!ced) 54 panic("Unable to initialise clockevent device"); 55 56 dw_apb_clockevent_register(ced); 57} 58 59static void __iomem *sched_io_base; 60 61/* This is actually same as __apbt_read_clocksource(), but with 62 different interface */ 63static u32 read_sched_clock_sptimer(void) 64{ 65 return ~__raw_readl(sched_io_base + APBTMR_N_CURRENT_VALUE); 66} 67 68static void add_clocksource(struct device_node *source_timer) 69{ 70 void __iomem *iobase; 71 struct dw_apb_clocksource *cs; 72 u32 rate; 73 74 timer_get_base_and_rate(source_timer, &iobase, &rate); 75 76 cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate); 77 if (!cs) 78 panic("Unable to initialise clocksource device"); 79 80 dw_apb_clocksource_start(cs); 81 dw_apb_clocksource_register(cs); 82 83 sched_io_base = iobase; 84 setup_sched_clock(read_sched_clock_sptimer, 32, rate); 85} 86 87static const struct of_device_id osctimer_ids[] __initconst = { 88 { .compatible = "picochip,pc3x2-timer" }, 89 { .compatible = "snps,dw-apb-timer-osc" }, 90 { .compatible = "snps,dw-apb-timer-sp" }, 91 { /* Sentinel */ }, 92}; 93 94/* 95 You don't have to use dw_apb_timer for scheduler clock, 96 this should also work fine on arm: 97 98 twd_local_timer_of_register(); 99 arch_timer_of_register(); 100 arch_timer_sched_clock_init(); 101*/ 102 103 104void __init dw_apb_timer_init(void) 105{ 106 struct device_node *event_timer, *source_timer; 107 108 event_timer = of_find_matching_node(NULL, osctimer_ids); 109 if (!event_timer) 110 panic("No timer for clockevent"); 111 add_clockevent(event_timer); 112 113 source_timer = of_find_matching_node(event_timer, osctimer_ids); 114 if (!source_timer) 115 panic("No timer for clocksource"); 116 add_clocksource(source_timer); 117 118 of_node_put(event_timer); 119 of_node_put(source_timer); 120} 121