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> 25*1f174a1aSDinh Nguyen #include <linux/reset.h> 2638ff87f7SStephen Boyd #include <linux/sched_clock.h> 27cfda5901SDinh Nguyen 281cf0203aSUwe Kleine-König static void __init timer_get_base_and_rate(struct device_node *np, 29cfda5901SDinh Nguyen void __iomem **base, u32 *rate) 30cfda5901SDinh Nguyen { 31a8b447f2SHeiko Stuebner struct clk *timer_clk; 32a8b447f2SHeiko Stuebner struct clk *pclk; 33*1f174a1aSDinh Nguyen struct reset_control *rstc; 34a8b447f2SHeiko Stuebner 35cfda5901SDinh Nguyen *base = of_iomap(np, 0); 36cfda5901SDinh Nguyen 37cfda5901SDinh Nguyen if (!*base) 382a4849d2SRob Herring panic("Unable to map regs for %pOFn", np); 39cfda5901SDinh Nguyen 40a8b447f2SHeiko Stuebner /* 41*1f174a1aSDinh Nguyen * Reset the timer if the reset control is available, wiping 42*1f174a1aSDinh Nguyen * out the state the firmware may have left it 43*1f174a1aSDinh Nguyen */ 44*1f174a1aSDinh Nguyen rstc = of_reset_control_get(np, NULL); 45*1f174a1aSDinh Nguyen if (!IS_ERR(rstc)) { 46*1f174a1aSDinh Nguyen reset_control_assert(rstc); 47*1f174a1aSDinh Nguyen reset_control_deassert(rstc); 48*1f174a1aSDinh Nguyen } 49*1f174a1aSDinh Nguyen 50*1f174a1aSDinh Nguyen /* 51a8b447f2SHeiko Stuebner * Not all implementations use a periphal clock, so don't panic 52a8b447f2SHeiko Stuebner * if it's not present 53a8b447f2SHeiko Stuebner */ 54a8b447f2SHeiko Stuebner pclk = of_clk_get_by_name(np, "pclk"); 55a8b447f2SHeiko Stuebner if (!IS_ERR(pclk)) 56a8b447f2SHeiko Stuebner if (clk_prepare_enable(pclk)) 572a4849d2SRob Herring pr_warn("pclk for %pOFn is present, but could not be activated\n", 582a4849d2SRob Herring np); 59a8b447f2SHeiko Stuebner 60a8b447f2SHeiko Stuebner timer_clk = of_clk_get_by_name(np, "timer"); 61a8b447f2SHeiko Stuebner if (IS_ERR(timer_clk)) 62a8b447f2SHeiko Stuebner goto try_clock_freq; 63a8b447f2SHeiko Stuebner 64a8b447f2SHeiko Stuebner if (!clk_prepare_enable(timer_clk)) { 65a8b447f2SHeiko Stuebner *rate = clk_get_rate(timer_clk); 66a8b447f2SHeiko Stuebner return; 67a8b447f2SHeiko Stuebner } 68a8b447f2SHeiko Stuebner 69a8b447f2SHeiko Stuebner try_clock_freq: 70cfda5901SDinh Nguyen if (of_property_read_u32(np, "clock-freq", rate) && 71cfda5901SDinh Nguyen of_property_read_u32(np, "clock-frequency", rate)) 722a4849d2SRob Herring panic("No clock nor clock-frequency property for %pOFn", np); 73cfda5901SDinh Nguyen } 74cfda5901SDinh Nguyen 751cf0203aSUwe Kleine-König static void __init add_clockevent(struct device_node *event_timer) 76cfda5901SDinh Nguyen { 77cfda5901SDinh Nguyen void __iomem *iobase; 78cfda5901SDinh Nguyen struct dw_apb_clock_event_device *ced; 79cfda5901SDinh Nguyen u32 irq, rate; 80cfda5901SDinh Nguyen 81cfda5901SDinh Nguyen irq = irq_of_parse_and_map(event_timer, 0); 821a33bd2bSBaruch Siach if (irq == 0) 83cfda5901SDinh Nguyen panic("No IRQ for clock event timer"); 84cfda5901SDinh Nguyen 85cfda5901SDinh Nguyen timer_get_base_and_rate(event_timer, &iobase, &rate); 86cfda5901SDinh Nguyen 87cfda5901SDinh Nguyen ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq, 88cfda5901SDinh Nguyen rate); 89cfda5901SDinh Nguyen if (!ced) 90cfda5901SDinh Nguyen panic("Unable to initialise clockevent device"); 91cfda5901SDinh Nguyen 92cfda5901SDinh Nguyen dw_apb_clockevent_register(ced); 93cfda5901SDinh Nguyen } 94cfda5901SDinh Nguyen 95a1198f83SHeiko Stuebner static void __iomem *sched_io_base; 96a1198f83SHeiko Stuebner static u32 sched_rate; 97a1198f83SHeiko Stuebner 981cf0203aSUwe Kleine-König static void __init add_clocksource(struct device_node *source_timer) 99cfda5901SDinh Nguyen { 100cfda5901SDinh Nguyen void __iomem *iobase; 101cfda5901SDinh Nguyen struct dw_apb_clocksource *cs; 102cfda5901SDinh Nguyen u32 rate; 103cfda5901SDinh Nguyen 104cfda5901SDinh Nguyen timer_get_base_and_rate(source_timer, &iobase, &rate); 105cfda5901SDinh Nguyen 106cfda5901SDinh Nguyen cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate); 107cfda5901SDinh Nguyen if (!cs) 108cfda5901SDinh Nguyen panic("Unable to initialise clocksource device"); 109cfda5901SDinh Nguyen 110cfda5901SDinh Nguyen dw_apb_clocksource_start(cs); 111cfda5901SDinh Nguyen dw_apb_clocksource_register(cs); 112cfda5901SDinh Nguyen 113a1198f83SHeiko Stuebner /* 114a1198f83SHeiko Stuebner * Fallback to use the clocksource as sched_clock if no separate 115a1198f83SHeiko Stuebner * timer is found. sched_io_base then points to the current_value 116a1198f83SHeiko Stuebner * register of the clocksource timer. 117a1198f83SHeiko Stuebner */ 118a1198f83SHeiko Stuebner sched_io_base = iobase + 0x04; 119a1198f83SHeiko Stuebner sched_rate = rate; 120a1198f83SHeiko Stuebner } 121cfda5901SDinh Nguyen 1220d24d1f2SYang Wei static u64 notrace read_sched_clock(void) 123cfda5901SDinh Nguyen { 1243a10013bSBen Dooks return ~readl_relaxed(sched_io_base); 125cfda5901SDinh Nguyen } 126cfda5901SDinh Nguyen 127cfda5901SDinh Nguyen static const struct of_device_id sptimer_ids[] __initconst = { 128cfda5901SDinh Nguyen { .compatible = "picochip,pc3x2-rtc" }, 129cfda5901SDinh Nguyen { /* Sentinel */ }, 130cfda5901SDinh Nguyen }; 131cfda5901SDinh Nguyen 1321cf0203aSUwe Kleine-König static void __init init_sched_clock(void) 133cfda5901SDinh Nguyen { 134cfda5901SDinh Nguyen struct device_node *sched_timer; 135cfda5901SDinh Nguyen 136cfda5901SDinh Nguyen sched_timer = of_find_matching_node(NULL, sptimer_ids); 137a1198f83SHeiko Stuebner if (sched_timer) { 138a1198f83SHeiko Stuebner timer_get_base_and_rate(sched_timer, &sched_io_base, 139a1198f83SHeiko Stuebner &sched_rate); 140cfda5901SDinh Nguyen of_node_put(sched_timer); 141a1198f83SHeiko Stuebner } 142cfda5901SDinh Nguyen 143fa8296aeSStephen Boyd sched_clock_register(read_sched_clock, 32, sched_rate); 144cfda5901SDinh Nguyen } 145cfda5901SDinh Nguyen 1469115df89SJisheng Zhang #ifdef CONFIG_ARM 1479115df89SJisheng Zhang static unsigned long dw_apb_delay_timer_read(void) 1489115df89SJisheng Zhang { 1499115df89SJisheng Zhang return ~readl_relaxed(sched_io_base); 1509115df89SJisheng Zhang } 1519115df89SJisheng Zhang 1529115df89SJisheng Zhang static struct delay_timer dw_apb_delay_timer = { 1539115df89SJisheng Zhang .read_current_timer = dw_apb_delay_timer_read, 1549115df89SJisheng Zhang }; 1559115df89SJisheng Zhang #endif 1569115df89SJisheng Zhang 15710021488SHeiko Stuebner static int num_called; 1582e1773f8SDaniel Lezcano static int __init dw_apb_timer_init(struct device_node *timer) 159cfda5901SDinh Nguyen { 16010021488SHeiko Stuebner switch (num_called) { 16110021488SHeiko Stuebner case 0: 16210021488SHeiko Stuebner pr_debug("%s: found clockevent timer\n", __func__); 16310021488SHeiko Stuebner add_clockevent(timer); 16410021488SHeiko Stuebner break; 16510021488SHeiko Stuebner case 1: 16610021488SHeiko Stuebner pr_debug("%s: found clocksource timer\n", __func__); 16710021488SHeiko Stuebner add_clocksource(timer); 168cfda5901SDinh Nguyen init_sched_clock(); 1699115df89SJisheng Zhang #ifdef CONFIG_ARM 1709115df89SJisheng Zhang dw_apb_delay_timer.freq = sched_rate; 1719115df89SJisheng Zhang register_current_timer_delay(&dw_apb_delay_timer); 1729115df89SJisheng Zhang #endif 17310021488SHeiko Stuebner break; 17410021488SHeiko Stuebner default: 17510021488SHeiko Stuebner break; 176cfda5901SDinh Nguyen } 17710021488SHeiko Stuebner 17810021488SHeiko Stuebner num_called++; 1792e1773f8SDaniel Lezcano 1802e1773f8SDaniel Lezcano return 0; 18110021488SHeiko Stuebner } 18217273395SDaniel Lezcano TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); 18317273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init); 18417273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init); 18517273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init); 186