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