1*caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2cfda5901SDinh Nguyen /* 3cfda5901SDinh Nguyen * Copyright (C) 2012 Altera Corporation 4cfda5901SDinh Nguyen * Copyright (c) 2011 Picochip Ltd., Jamie Iles 5cfda5901SDinh Nguyen * 6cfda5901SDinh Nguyen * Modified from mach-picoxcell/time.c 7cfda5901SDinh Nguyen */ 89115df89SJisheng Zhang #include <linux/delay.h> 9cfda5901SDinh Nguyen #include <linux/dw_apb_timer.h> 10cfda5901SDinh Nguyen #include <linux/of.h> 11cfda5901SDinh Nguyen #include <linux/of_address.h> 12cfda5901SDinh Nguyen #include <linux/of_irq.h> 13a8b447f2SHeiko Stuebner #include <linux/clk.h> 141f174a1aSDinh Nguyen #include <linux/reset.h> 1538ff87f7SStephen Boyd #include <linux/sched_clock.h> 16cfda5901SDinh Nguyen 171cf0203aSUwe Kleine-König static void __init timer_get_base_and_rate(struct device_node *np, 18cfda5901SDinh Nguyen void __iomem **base, u32 *rate) 19cfda5901SDinh Nguyen { 20a8b447f2SHeiko Stuebner struct clk *timer_clk; 21a8b447f2SHeiko Stuebner struct clk *pclk; 221f174a1aSDinh Nguyen struct reset_control *rstc; 23a8b447f2SHeiko Stuebner 24cfda5901SDinh Nguyen *base = of_iomap(np, 0); 25cfda5901SDinh Nguyen 26cfda5901SDinh Nguyen if (!*base) 272a4849d2SRob Herring panic("Unable to map regs for %pOFn", np); 28cfda5901SDinh Nguyen 29a8b447f2SHeiko Stuebner /* 301f174a1aSDinh Nguyen * Reset the timer if the reset control is available, wiping 311f174a1aSDinh Nguyen * out the state the firmware may have left it 321f174a1aSDinh Nguyen */ 331f174a1aSDinh Nguyen rstc = of_reset_control_get(np, NULL); 341f174a1aSDinh Nguyen if (!IS_ERR(rstc)) { 351f174a1aSDinh Nguyen reset_control_assert(rstc); 361f174a1aSDinh Nguyen reset_control_deassert(rstc); 371f174a1aSDinh Nguyen } 381f174a1aSDinh Nguyen 391f174a1aSDinh Nguyen /* 40a8b447f2SHeiko Stuebner * Not all implementations use a periphal clock, so don't panic 41a8b447f2SHeiko Stuebner * if it's not present 42a8b447f2SHeiko Stuebner */ 43a8b447f2SHeiko Stuebner pclk = of_clk_get_by_name(np, "pclk"); 44a8b447f2SHeiko Stuebner if (!IS_ERR(pclk)) 45a8b447f2SHeiko Stuebner if (clk_prepare_enable(pclk)) 462a4849d2SRob Herring pr_warn("pclk for %pOFn is present, but could not be activated\n", 472a4849d2SRob Herring np); 48a8b447f2SHeiko Stuebner 49a8b447f2SHeiko Stuebner timer_clk = of_clk_get_by_name(np, "timer"); 50a8b447f2SHeiko Stuebner if (IS_ERR(timer_clk)) 51a8b447f2SHeiko Stuebner goto try_clock_freq; 52a8b447f2SHeiko Stuebner 53a8b447f2SHeiko Stuebner if (!clk_prepare_enable(timer_clk)) { 54a8b447f2SHeiko Stuebner *rate = clk_get_rate(timer_clk); 55a8b447f2SHeiko Stuebner return; 56a8b447f2SHeiko Stuebner } 57a8b447f2SHeiko Stuebner 58a8b447f2SHeiko Stuebner try_clock_freq: 59cfda5901SDinh Nguyen if (of_property_read_u32(np, "clock-freq", rate) && 60cfda5901SDinh Nguyen of_property_read_u32(np, "clock-frequency", rate)) 612a4849d2SRob Herring panic("No clock nor clock-frequency property for %pOFn", np); 62cfda5901SDinh Nguyen } 63cfda5901SDinh Nguyen 641cf0203aSUwe Kleine-König static void __init add_clockevent(struct device_node *event_timer) 65cfda5901SDinh Nguyen { 66cfda5901SDinh Nguyen void __iomem *iobase; 67cfda5901SDinh Nguyen struct dw_apb_clock_event_device *ced; 68cfda5901SDinh Nguyen u32 irq, rate; 69cfda5901SDinh Nguyen 70cfda5901SDinh Nguyen irq = irq_of_parse_and_map(event_timer, 0); 711a33bd2bSBaruch Siach if (irq == 0) 72cfda5901SDinh Nguyen panic("No IRQ for clock event timer"); 73cfda5901SDinh Nguyen 74cfda5901SDinh Nguyen timer_get_base_and_rate(event_timer, &iobase, &rate); 75cfda5901SDinh Nguyen 76cfda5901SDinh Nguyen ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq, 77cfda5901SDinh Nguyen rate); 78cfda5901SDinh Nguyen if (!ced) 79cfda5901SDinh Nguyen panic("Unable to initialise clockevent device"); 80cfda5901SDinh Nguyen 81cfda5901SDinh Nguyen dw_apb_clockevent_register(ced); 82cfda5901SDinh Nguyen } 83cfda5901SDinh Nguyen 84a1198f83SHeiko Stuebner static void __iomem *sched_io_base; 85a1198f83SHeiko Stuebner static u32 sched_rate; 86a1198f83SHeiko Stuebner 871cf0203aSUwe Kleine-König static void __init add_clocksource(struct device_node *source_timer) 88cfda5901SDinh Nguyen { 89cfda5901SDinh Nguyen void __iomem *iobase; 90cfda5901SDinh Nguyen struct dw_apb_clocksource *cs; 91cfda5901SDinh Nguyen u32 rate; 92cfda5901SDinh Nguyen 93cfda5901SDinh Nguyen timer_get_base_and_rate(source_timer, &iobase, &rate); 94cfda5901SDinh Nguyen 95cfda5901SDinh Nguyen cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate); 96cfda5901SDinh Nguyen if (!cs) 97cfda5901SDinh Nguyen panic("Unable to initialise clocksource device"); 98cfda5901SDinh Nguyen 99cfda5901SDinh Nguyen dw_apb_clocksource_start(cs); 100cfda5901SDinh Nguyen dw_apb_clocksource_register(cs); 101cfda5901SDinh Nguyen 102a1198f83SHeiko Stuebner /* 103a1198f83SHeiko Stuebner * Fallback to use the clocksource as sched_clock if no separate 104a1198f83SHeiko Stuebner * timer is found. sched_io_base then points to the current_value 105a1198f83SHeiko Stuebner * register of the clocksource timer. 106a1198f83SHeiko Stuebner */ 107a1198f83SHeiko Stuebner sched_io_base = iobase + 0x04; 108a1198f83SHeiko Stuebner sched_rate = rate; 109a1198f83SHeiko Stuebner } 110cfda5901SDinh Nguyen 1110d24d1f2SYang Wei static u64 notrace read_sched_clock(void) 112cfda5901SDinh Nguyen { 1133a10013bSBen Dooks return ~readl_relaxed(sched_io_base); 114cfda5901SDinh Nguyen } 115cfda5901SDinh Nguyen 116cfda5901SDinh Nguyen static const struct of_device_id sptimer_ids[] __initconst = { 117cfda5901SDinh Nguyen { .compatible = "picochip,pc3x2-rtc" }, 118cfda5901SDinh Nguyen { /* Sentinel */ }, 119cfda5901SDinh Nguyen }; 120cfda5901SDinh Nguyen 1211cf0203aSUwe Kleine-König static void __init init_sched_clock(void) 122cfda5901SDinh Nguyen { 123cfda5901SDinh Nguyen struct device_node *sched_timer; 124cfda5901SDinh Nguyen 125cfda5901SDinh Nguyen sched_timer = of_find_matching_node(NULL, sptimer_ids); 126a1198f83SHeiko Stuebner if (sched_timer) { 127a1198f83SHeiko Stuebner timer_get_base_and_rate(sched_timer, &sched_io_base, 128a1198f83SHeiko Stuebner &sched_rate); 129cfda5901SDinh Nguyen of_node_put(sched_timer); 130a1198f83SHeiko Stuebner } 131cfda5901SDinh Nguyen 132fa8296aeSStephen Boyd sched_clock_register(read_sched_clock, 32, sched_rate); 133cfda5901SDinh Nguyen } 134cfda5901SDinh Nguyen 1359115df89SJisheng Zhang #ifdef CONFIG_ARM 1369115df89SJisheng Zhang static unsigned long dw_apb_delay_timer_read(void) 1379115df89SJisheng Zhang { 1389115df89SJisheng Zhang return ~readl_relaxed(sched_io_base); 1399115df89SJisheng Zhang } 1409115df89SJisheng Zhang 1419115df89SJisheng Zhang static struct delay_timer dw_apb_delay_timer = { 1429115df89SJisheng Zhang .read_current_timer = dw_apb_delay_timer_read, 1439115df89SJisheng Zhang }; 1449115df89SJisheng Zhang #endif 1459115df89SJisheng Zhang 14610021488SHeiko Stuebner static int num_called; 1472e1773f8SDaniel Lezcano static int __init dw_apb_timer_init(struct device_node *timer) 148cfda5901SDinh Nguyen { 14910021488SHeiko Stuebner switch (num_called) { 15010021488SHeiko Stuebner case 0: 15110021488SHeiko Stuebner pr_debug("%s: found clockevent timer\n", __func__); 15210021488SHeiko Stuebner add_clockevent(timer); 15310021488SHeiko Stuebner break; 15410021488SHeiko Stuebner case 1: 15510021488SHeiko Stuebner pr_debug("%s: found clocksource timer\n", __func__); 15610021488SHeiko Stuebner add_clocksource(timer); 157cfda5901SDinh Nguyen init_sched_clock(); 1589115df89SJisheng Zhang #ifdef CONFIG_ARM 1599115df89SJisheng Zhang dw_apb_delay_timer.freq = sched_rate; 1609115df89SJisheng Zhang register_current_timer_delay(&dw_apb_delay_timer); 1619115df89SJisheng Zhang #endif 16210021488SHeiko Stuebner break; 16310021488SHeiko Stuebner default: 16410021488SHeiko Stuebner break; 165cfda5901SDinh Nguyen } 16610021488SHeiko Stuebner 16710021488SHeiko Stuebner num_called++; 1682e1773f8SDaniel Lezcano 1692e1773f8SDaniel Lezcano return 0; 17010021488SHeiko Stuebner } 17117273395SDaniel Lezcano TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); 17217273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init); 17317273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init); 17417273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init); 175