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