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> 23a8b447f2SHeiko Stuebner #include <linux/clk.h> 2438ff87f7SStephen Boyd #include <linux/sched_clock.h> 25cfda5901SDinh Nguyen 261cf0203aSUwe Kleine-König static void __init timer_get_base_and_rate(struct device_node *np, 27cfda5901SDinh Nguyen void __iomem **base, u32 *rate) 28cfda5901SDinh Nguyen { 29a8b447f2SHeiko Stuebner struct clk *timer_clk; 30a8b447f2SHeiko Stuebner struct clk *pclk; 31a8b447f2SHeiko Stuebner 32cfda5901SDinh Nguyen *base = of_iomap(np, 0); 33cfda5901SDinh Nguyen 34cfda5901SDinh Nguyen if (!*base) 35cfda5901SDinh Nguyen panic("Unable to map regs for %s", np->name); 36cfda5901SDinh Nguyen 37a8b447f2SHeiko Stuebner /* 38a8b447f2SHeiko Stuebner * Not all implementations use a periphal clock, so don't panic 39a8b447f2SHeiko Stuebner * if it's not present 40a8b447f2SHeiko Stuebner */ 41a8b447f2SHeiko Stuebner pclk = of_clk_get_by_name(np, "pclk"); 42a8b447f2SHeiko Stuebner if (!IS_ERR(pclk)) 43a8b447f2SHeiko Stuebner if (clk_prepare_enable(pclk)) 44a8b447f2SHeiko Stuebner pr_warn("pclk for %s is present, but could not be activated\n", 45a8b447f2SHeiko Stuebner np->name); 46a8b447f2SHeiko Stuebner 47a8b447f2SHeiko Stuebner timer_clk = of_clk_get_by_name(np, "timer"); 48a8b447f2SHeiko Stuebner if (IS_ERR(timer_clk)) 49a8b447f2SHeiko Stuebner goto try_clock_freq; 50a8b447f2SHeiko Stuebner 51a8b447f2SHeiko Stuebner if (!clk_prepare_enable(timer_clk)) { 52a8b447f2SHeiko Stuebner *rate = clk_get_rate(timer_clk); 53a8b447f2SHeiko Stuebner return; 54a8b447f2SHeiko Stuebner } 55a8b447f2SHeiko Stuebner 56a8b447f2SHeiko Stuebner try_clock_freq: 57cfda5901SDinh Nguyen if (of_property_read_u32(np, "clock-freq", rate) && 58cfda5901SDinh Nguyen of_property_read_u32(np, "clock-frequency", rate)) 59a8b447f2SHeiko Stuebner panic("No clock nor clock-frequency property for %s", np->name); 60cfda5901SDinh Nguyen } 61cfda5901SDinh Nguyen 621cf0203aSUwe Kleine-König static void __init add_clockevent(struct device_node *event_timer) 63cfda5901SDinh Nguyen { 64cfda5901SDinh Nguyen void __iomem *iobase; 65cfda5901SDinh Nguyen struct dw_apb_clock_event_device *ced; 66cfda5901SDinh Nguyen u32 irq, rate; 67cfda5901SDinh Nguyen 68cfda5901SDinh Nguyen irq = irq_of_parse_and_map(event_timer, 0); 691a33bd2bSBaruch Siach if (irq == 0) 70cfda5901SDinh Nguyen panic("No IRQ for clock event timer"); 71cfda5901SDinh Nguyen 72cfda5901SDinh Nguyen timer_get_base_and_rate(event_timer, &iobase, &rate); 73cfda5901SDinh Nguyen 74cfda5901SDinh Nguyen ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq, 75cfda5901SDinh Nguyen rate); 76cfda5901SDinh Nguyen if (!ced) 77cfda5901SDinh Nguyen panic("Unable to initialise clockevent device"); 78cfda5901SDinh Nguyen 79cfda5901SDinh Nguyen dw_apb_clockevent_register(ced); 80cfda5901SDinh Nguyen } 81cfda5901SDinh Nguyen 82a1198f83SHeiko Stuebner static void __iomem *sched_io_base; 83a1198f83SHeiko Stuebner static u32 sched_rate; 84a1198f83SHeiko Stuebner 851cf0203aSUwe Kleine-König static void __init add_clocksource(struct device_node *source_timer) 86cfda5901SDinh Nguyen { 87cfda5901SDinh Nguyen void __iomem *iobase; 88cfda5901SDinh Nguyen struct dw_apb_clocksource *cs; 89cfda5901SDinh Nguyen u32 rate; 90cfda5901SDinh Nguyen 91cfda5901SDinh Nguyen timer_get_base_and_rate(source_timer, &iobase, &rate); 92cfda5901SDinh Nguyen 93cfda5901SDinh Nguyen cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate); 94cfda5901SDinh Nguyen if (!cs) 95cfda5901SDinh Nguyen panic("Unable to initialise clocksource device"); 96cfda5901SDinh Nguyen 97cfda5901SDinh Nguyen dw_apb_clocksource_start(cs); 98cfda5901SDinh Nguyen dw_apb_clocksource_register(cs); 99cfda5901SDinh Nguyen 100a1198f83SHeiko Stuebner /* 101a1198f83SHeiko Stuebner * Fallback to use the clocksource as sched_clock if no separate 102a1198f83SHeiko Stuebner * timer is found. sched_io_base then points to the current_value 103a1198f83SHeiko Stuebner * register of the clocksource timer. 104a1198f83SHeiko Stuebner */ 105a1198f83SHeiko Stuebner sched_io_base = iobase + 0x04; 106a1198f83SHeiko Stuebner sched_rate = rate; 107a1198f83SHeiko Stuebner } 108cfda5901SDinh Nguyen 1090d24d1f2SYang Wei static u64 notrace read_sched_clock(void) 110cfda5901SDinh Nguyen { 111*3a10013bSBen Dooks return ~readl_relaxed(sched_io_base); 112cfda5901SDinh Nguyen } 113cfda5901SDinh Nguyen 114cfda5901SDinh Nguyen static const struct of_device_id sptimer_ids[] __initconst = { 115cfda5901SDinh Nguyen { .compatible = "picochip,pc3x2-rtc" }, 116cfda5901SDinh Nguyen { /* Sentinel */ }, 117cfda5901SDinh Nguyen }; 118cfda5901SDinh Nguyen 1191cf0203aSUwe Kleine-König static void __init init_sched_clock(void) 120cfda5901SDinh Nguyen { 121cfda5901SDinh Nguyen struct device_node *sched_timer; 122cfda5901SDinh Nguyen 123cfda5901SDinh Nguyen sched_timer = of_find_matching_node(NULL, sptimer_ids); 124a1198f83SHeiko Stuebner if (sched_timer) { 125a1198f83SHeiko Stuebner timer_get_base_and_rate(sched_timer, &sched_io_base, 126a1198f83SHeiko Stuebner &sched_rate); 127cfda5901SDinh Nguyen of_node_put(sched_timer); 128a1198f83SHeiko Stuebner } 129cfda5901SDinh Nguyen 130fa8296aeSStephen Boyd sched_clock_register(read_sched_clock, 32, sched_rate); 131cfda5901SDinh Nguyen } 132cfda5901SDinh Nguyen 13310021488SHeiko Stuebner static int num_called; 13410021488SHeiko Stuebner static void __init dw_apb_timer_init(struct device_node *timer) 135cfda5901SDinh Nguyen { 13610021488SHeiko Stuebner switch (num_called) { 13710021488SHeiko Stuebner case 0: 13810021488SHeiko Stuebner pr_debug("%s: found clockevent timer\n", __func__); 13910021488SHeiko Stuebner add_clockevent(timer); 14010021488SHeiko Stuebner break; 14110021488SHeiko Stuebner case 1: 14210021488SHeiko Stuebner pr_debug("%s: found clocksource timer\n", __func__); 14310021488SHeiko Stuebner add_clocksource(timer); 144cfda5901SDinh Nguyen init_sched_clock(); 14510021488SHeiko Stuebner break; 14610021488SHeiko Stuebner default: 14710021488SHeiko Stuebner break; 148cfda5901SDinh Nguyen } 14910021488SHeiko Stuebner 15010021488SHeiko Stuebner num_called++; 15110021488SHeiko Stuebner } 15210021488SHeiko Stuebner CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); 1539ab4727cSDinh Nguyen CLOCKSOURCE_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init); 1549ab4727cSDinh Nguyen CLOCKSOURCE_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init); 1559ab4727cSDinh Nguyen CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init); 156