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 */ 199115df89SJisheng Zhang #include <linux/delay.h> 20cfda5901SDinh Nguyen #include <linux/dw_apb_timer.h> 21cfda5901SDinh Nguyen #include <linux/of.h> 22cfda5901SDinh Nguyen #include <linux/of_address.h> 23cfda5901SDinh Nguyen #include <linux/of_irq.h> 24a8b447f2SHeiko Stuebner #include <linux/clk.h> 2538ff87f7SStephen Boyd #include <linux/sched_clock.h> 26cfda5901SDinh Nguyen 271cf0203aSUwe Kleine-König static void __init timer_get_base_and_rate(struct device_node *np, 28cfda5901SDinh Nguyen void __iomem **base, u32 *rate) 29cfda5901SDinh Nguyen { 30a8b447f2SHeiko Stuebner struct clk *timer_clk; 31a8b447f2SHeiko Stuebner struct clk *pclk; 32a8b447f2SHeiko Stuebner 33cfda5901SDinh Nguyen *base = of_iomap(np, 0); 34cfda5901SDinh Nguyen 35cfda5901SDinh Nguyen if (!*base) 36*2a4849d2SRob Herring panic("Unable to map regs for %pOFn", np); 37cfda5901SDinh Nguyen 38a8b447f2SHeiko Stuebner /* 39a8b447f2SHeiko Stuebner * Not all implementations use a periphal clock, so don't panic 40a8b447f2SHeiko Stuebner * if it's not present 41a8b447f2SHeiko Stuebner */ 42a8b447f2SHeiko Stuebner pclk = of_clk_get_by_name(np, "pclk"); 43a8b447f2SHeiko Stuebner if (!IS_ERR(pclk)) 44a8b447f2SHeiko Stuebner if (clk_prepare_enable(pclk)) 45*2a4849d2SRob Herring pr_warn("pclk for %pOFn is present, but could not be activated\n", 46*2a4849d2SRob Herring np); 47a8b447f2SHeiko Stuebner 48a8b447f2SHeiko Stuebner timer_clk = of_clk_get_by_name(np, "timer"); 49a8b447f2SHeiko Stuebner if (IS_ERR(timer_clk)) 50a8b447f2SHeiko Stuebner goto try_clock_freq; 51a8b447f2SHeiko Stuebner 52a8b447f2SHeiko Stuebner if (!clk_prepare_enable(timer_clk)) { 53a8b447f2SHeiko Stuebner *rate = clk_get_rate(timer_clk); 54a8b447f2SHeiko Stuebner return; 55a8b447f2SHeiko Stuebner } 56a8b447f2SHeiko Stuebner 57a8b447f2SHeiko Stuebner try_clock_freq: 58cfda5901SDinh Nguyen if (of_property_read_u32(np, "clock-freq", rate) && 59cfda5901SDinh Nguyen of_property_read_u32(np, "clock-frequency", rate)) 60*2a4849d2SRob Herring panic("No clock nor clock-frequency property for %pOFn", np); 61cfda5901SDinh Nguyen } 62cfda5901SDinh Nguyen 631cf0203aSUwe Kleine-König static void __init add_clockevent(struct device_node *event_timer) 64cfda5901SDinh Nguyen { 65cfda5901SDinh Nguyen void __iomem *iobase; 66cfda5901SDinh Nguyen struct dw_apb_clock_event_device *ced; 67cfda5901SDinh Nguyen u32 irq, rate; 68cfda5901SDinh Nguyen 69cfda5901SDinh Nguyen irq = irq_of_parse_and_map(event_timer, 0); 701a33bd2bSBaruch Siach if (irq == 0) 71cfda5901SDinh Nguyen panic("No IRQ for clock event timer"); 72cfda5901SDinh Nguyen 73cfda5901SDinh Nguyen timer_get_base_and_rate(event_timer, &iobase, &rate); 74cfda5901SDinh Nguyen 75cfda5901SDinh Nguyen ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq, 76cfda5901SDinh Nguyen rate); 77cfda5901SDinh Nguyen if (!ced) 78cfda5901SDinh Nguyen panic("Unable to initialise clockevent device"); 79cfda5901SDinh Nguyen 80cfda5901SDinh Nguyen dw_apb_clockevent_register(ced); 81cfda5901SDinh Nguyen } 82cfda5901SDinh Nguyen 83a1198f83SHeiko Stuebner static void __iomem *sched_io_base; 84a1198f83SHeiko Stuebner static u32 sched_rate; 85a1198f83SHeiko Stuebner 861cf0203aSUwe Kleine-König static void __init add_clocksource(struct device_node *source_timer) 87cfda5901SDinh Nguyen { 88cfda5901SDinh Nguyen void __iomem *iobase; 89cfda5901SDinh Nguyen struct dw_apb_clocksource *cs; 90cfda5901SDinh Nguyen u32 rate; 91cfda5901SDinh Nguyen 92cfda5901SDinh Nguyen timer_get_base_and_rate(source_timer, &iobase, &rate); 93cfda5901SDinh Nguyen 94cfda5901SDinh Nguyen cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate); 95cfda5901SDinh Nguyen if (!cs) 96cfda5901SDinh Nguyen panic("Unable to initialise clocksource device"); 97cfda5901SDinh Nguyen 98cfda5901SDinh Nguyen dw_apb_clocksource_start(cs); 99cfda5901SDinh Nguyen dw_apb_clocksource_register(cs); 100cfda5901SDinh Nguyen 101a1198f83SHeiko Stuebner /* 102a1198f83SHeiko Stuebner * Fallback to use the clocksource as sched_clock if no separate 103a1198f83SHeiko Stuebner * timer is found. sched_io_base then points to the current_value 104a1198f83SHeiko Stuebner * register of the clocksource timer. 105a1198f83SHeiko Stuebner */ 106a1198f83SHeiko Stuebner sched_io_base = iobase + 0x04; 107a1198f83SHeiko Stuebner sched_rate = rate; 108a1198f83SHeiko Stuebner } 109cfda5901SDinh Nguyen 1100d24d1f2SYang Wei static u64 notrace read_sched_clock(void) 111cfda5901SDinh Nguyen { 1123a10013bSBen Dooks return ~readl_relaxed(sched_io_base); 113cfda5901SDinh Nguyen } 114cfda5901SDinh Nguyen 115cfda5901SDinh Nguyen static const struct of_device_id sptimer_ids[] __initconst = { 116cfda5901SDinh Nguyen { .compatible = "picochip,pc3x2-rtc" }, 117cfda5901SDinh Nguyen { /* Sentinel */ }, 118cfda5901SDinh Nguyen }; 119cfda5901SDinh Nguyen 1201cf0203aSUwe Kleine-König static void __init init_sched_clock(void) 121cfda5901SDinh Nguyen { 122cfda5901SDinh Nguyen struct device_node *sched_timer; 123cfda5901SDinh Nguyen 124cfda5901SDinh Nguyen sched_timer = of_find_matching_node(NULL, sptimer_ids); 125a1198f83SHeiko Stuebner if (sched_timer) { 126a1198f83SHeiko Stuebner timer_get_base_and_rate(sched_timer, &sched_io_base, 127a1198f83SHeiko Stuebner &sched_rate); 128cfda5901SDinh Nguyen of_node_put(sched_timer); 129a1198f83SHeiko Stuebner } 130cfda5901SDinh Nguyen 131fa8296aeSStephen Boyd sched_clock_register(read_sched_clock, 32, sched_rate); 132cfda5901SDinh Nguyen } 133cfda5901SDinh Nguyen 1349115df89SJisheng Zhang #ifdef CONFIG_ARM 1359115df89SJisheng Zhang static unsigned long dw_apb_delay_timer_read(void) 1369115df89SJisheng Zhang { 1379115df89SJisheng Zhang return ~readl_relaxed(sched_io_base); 1389115df89SJisheng Zhang } 1399115df89SJisheng Zhang 1409115df89SJisheng Zhang static struct delay_timer dw_apb_delay_timer = { 1419115df89SJisheng Zhang .read_current_timer = dw_apb_delay_timer_read, 1429115df89SJisheng Zhang }; 1439115df89SJisheng Zhang #endif 1449115df89SJisheng Zhang 14510021488SHeiko Stuebner static int num_called; 1462e1773f8SDaniel Lezcano static int __init dw_apb_timer_init(struct device_node *timer) 147cfda5901SDinh Nguyen { 14810021488SHeiko Stuebner switch (num_called) { 14910021488SHeiko Stuebner case 0: 15010021488SHeiko Stuebner pr_debug("%s: found clockevent timer\n", __func__); 15110021488SHeiko Stuebner add_clockevent(timer); 15210021488SHeiko Stuebner break; 15310021488SHeiko Stuebner case 1: 15410021488SHeiko Stuebner pr_debug("%s: found clocksource timer\n", __func__); 15510021488SHeiko Stuebner add_clocksource(timer); 156cfda5901SDinh Nguyen init_sched_clock(); 1579115df89SJisheng Zhang #ifdef CONFIG_ARM 1589115df89SJisheng Zhang dw_apb_delay_timer.freq = sched_rate; 1599115df89SJisheng Zhang register_current_timer_delay(&dw_apb_delay_timer); 1609115df89SJisheng Zhang #endif 16110021488SHeiko Stuebner break; 16210021488SHeiko Stuebner default: 16310021488SHeiko Stuebner break; 164cfda5901SDinh Nguyen } 16510021488SHeiko Stuebner 16610021488SHeiko Stuebner num_called++; 1672e1773f8SDaniel Lezcano 1682e1773f8SDaniel Lezcano return 0; 16910021488SHeiko Stuebner } 17017273395SDaniel Lezcano TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); 17117273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init); 17217273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init); 17317273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init); 174