1caab277bSThomas 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 175d9814dfSDinh Nguyen static int __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; 235d9814dfSDinh Nguyen int ret; 24a8b447f2SHeiko Stuebner 25cfda5901SDinh Nguyen *base = of_iomap(np, 0); 26cfda5901SDinh Nguyen 27cfda5901SDinh Nguyen if (!*base) 282a4849d2SRob Herring panic("Unable to map regs for %pOFn", np); 29cfda5901SDinh Nguyen 30a8b447f2SHeiko Stuebner /* 311f174a1aSDinh Nguyen * Reset the timer if the reset control is available, wiping 321f174a1aSDinh Nguyen * out the state the firmware may have left it 331f174a1aSDinh Nguyen */ 341f174a1aSDinh Nguyen rstc = of_reset_control_get(np, NULL); 351f174a1aSDinh Nguyen if (!IS_ERR(rstc)) { 361f174a1aSDinh Nguyen reset_control_assert(rstc); 371f174a1aSDinh Nguyen reset_control_deassert(rstc); 381f174a1aSDinh Nguyen } 391f174a1aSDinh Nguyen 401f174a1aSDinh Nguyen /* 414bf07f65SIngo Molnar * Not all implementations use a peripheral clock, so don't panic 42a8b447f2SHeiko Stuebner * if it's not present 43a8b447f2SHeiko Stuebner */ 44a8b447f2SHeiko Stuebner pclk = of_clk_get_by_name(np, "pclk"); 45a8b447f2SHeiko Stuebner if (!IS_ERR(pclk)) 46a8b447f2SHeiko Stuebner if (clk_prepare_enable(pclk)) 472a4849d2SRob Herring pr_warn("pclk for %pOFn is present, but could not be activated\n", 482a4849d2SRob Herring np); 49a8b447f2SHeiko Stuebner 505d9814dfSDinh Nguyen if (!of_property_read_u32(np, "clock-freq", rate) && 515d9814dfSDinh Nguyen !of_property_read_u32(np, "clock-frequency", rate)) 525d9814dfSDinh Nguyen return 0; 535d9814dfSDinh Nguyen 54a8b447f2SHeiko Stuebner timer_clk = of_clk_get_by_name(np, "timer"); 55*397dc6f7SDinh Nguyen if (IS_ERR(timer_clk)) { 56*397dc6f7SDinh Nguyen ret = PTR_ERR(timer_clk); 57*397dc6f7SDinh Nguyen goto out_pclk_disable; 58*397dc6f7SDinh Nguyen } 59a8b447f2SHeiko Stuebner 605d9814dfSDinh Nguyen ret = clk_prepare_enable(timer_clk); 615d9814dfSDinh Nguyen if (ret) 62*397dc6f7SDinh Nguyen goto out_timer_clk_put; 635d9814dfSDinh Nguyen 64a8b447f2SHeiko Stuebner *rate = clk_get_rate(timer_clk); 65*397dc6f7SDinh Nguyen if (!(*rate)) { 66*397dc6f7SDinh Nguyen ret = -EINVAL; 67*397dc6f7SDinh Nguyen goto out_timer_clk_disable; 68*397dc6f7SDinh Nguyen } 695d9814dfSDinh Nguyen 705d9814dfSDinh Nguyen return 0; 71*397dc6f7SDinh Nguyen 72*397dc6f7SDinh Nguyen out_timer_clk_disable: 73*397dc6f7SDinh Nguyen clk_disable_unprepare(timer_clk); 74*397dc6f7SDinh Nguyen out_timer_clk_put: 75*397dc6f7SDinh Nguyen clk_put(timer_clk); 76*397dc6f7SDinh Nguyen out_pclk_disable: 77*397dc6f7SDinh Nguyen if (!IS_ERR(pclk)) { 78*397dc6f7SDinh Nguyen clk_disable_unprepare(pclk); 79*397dc6f7SDinh Nguyen clk_put(pclk); 80*397dc6f7SDinh Nguyen } 81*397dc6f7SDinh Nguyen iounmap(*base); 82*397dc6f7SDinh Nguyen return ret; 83a8b447f2SHeiko Stuebner } 84a8b447f2SHeiko Stuebner 855d9814dfSDinh Nguyen static int __init add_clockevent(struct device_node *event_timer) 86cfda5901SDinh Nguyen { 87cfda5901SDinh Nguyen void __iomem *iobase; 88cfda5901SDinh Nguyen struct dw_apb_clock_event_device *ced; 89cfda5901SDinh Nguyen u32 irq, rate; 905d9814dfSDinh Nguyen int ret = 0; 91cfda5901SDinh Nguyen 92cfda5901SDinh Nguyen irq = irq_of_parse_and_map(event_timer, 0); 931a33bd2bSBaruch Siach if (irq == 0) 94cfda5901SDinh Nguyen panic("No IRQ for clock event timer"); 95cfda5901SDinh Nguyen 965d9814dfSDinh Nguyen ret = timer_get_base_and_rate(event_timer, &iobase, &rate); 975d9814dfSDinh Nguyen if (ret) 985d9814dfSDinh Nguyen return ret; 99cfda5901SDinh Nguyen 10065e0f876SSerge Semin ced = dw_apb_clockevent_init(-1, event_timer->name, 300, iobase, irq, 101cfda5901SDinh Nguyen rate); 102cfda5901SDinh Nguyen if (!ced) 1035d9814dfSDinh Nguyen return -EINVAL; 104cfda5901SDinh Nguyen 105cfda5901SDinh Nguyen dw_apb_clockevent_register(ced); 1065d9814dfSDinh Nguyen 1075d9814dfSDinh Nguyen return 0; 108cfda5901SDinh Nguyen } 109cfda5901SDinh Nguyen 110a1198f83SHeiko Stuebner static void __iomem *sched_io_base; 111a1198f83SHeiko Stuebner static u32 sched_rate; 112a1198f83SHeiko Stuebner 1135d9814dfSDinh Nguyen static int __init add_clocksource(struct device_node *source_timer) 114cfda5901SDinh Nguyen { 115cfda5901SDinh Nguyen void __iomem *iobase; 116cfda5901SDinh Nguyen struct dw_apb_clocksource *cs; 117cfda5901SDinh Nguyen u32 rate; 1185d9814dfSDinh Nguyen int ret; 119cfda5901SDinh Nguyen 1205d9814dfSDinh Nguyen ret = timer_get_base_and_rate(source_timer, &iobase, &rate); 1215d9814dfSDinh Nguyen if (ret) 1225d9814dfSDinh Nguyen return ret; 123cfda5901SDinh Nguyen 124cfda5901SDinh Nguyen cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate); 125cfda5901SDinh Nguyen if (!cs) 1265d9814dfSDinh Nguyen return -EINVAL; 127cfda5901SDinh Nguyen 128cfda5901SDinh Nguyen dw_apb_clocksource_start(cs); 129cfda5901SDinh Nguyen dw_apb_clocksource_register(cs); 130cfda5901SDinh Nguyen 131a1198f83SHeiko Stuebner /* 132a1198f83SHeiko Stuebner * Fallback to use the clocksource as sched_clock if no separate 133a1198f83SHeiko Stuebner * timer is found. sched_io_base then points to the current_value 134a1198f83SHeiko Stuebner * register of the clocksource timer. 135a1198f83SHeiko Stuebner */ 136a1198f83SHeiko Stuebner sched_io_base = iobase + 0x04; 137a1198f83SHeiko Stuebner sched_rate = rate; 1385d9814dfSDinh Nguyen 1395d9814dfSDinh Nguyen return 0; 140a1198f83SHeiko Stuebner } 141cfda5901SDinh Nguyen 1420d24d1f2SYang Wei static u64 notrace read_sched_clock(void) 143cfda5901SDinh Nguyen { 1443a10013bSBen Dooks return ~readl_relaxed(sched_io_base); 145cfda5901SDinh Nguyen } 146cfda5901SDinh Nguyen 147cfda5901SDinh Nguyen static const struct of_device_id sptimer_ids[] __initconst = { 148cfda5901SDinh Nguyen { .compatible = "picochip,pc3x2-rtc" }, 149cfda5901SDinh Nguyen { /* Sentinel */ }, 150cfda5901SDinh Nguyen }; 151cfda5901SDinh Nguyen 1521cf0203aSUwe Kleine-König static void __init init_sched_clock(void) 153cfda5901SDinh Nguyen { 154cfda5901SDinh Nguyen struct device_node *sched_timer; 155cfda5901SDinh Nguyen 156cfda5901SDinh Nguyen sched_timer = of_find_matching_node(NULL, sptimer_ids); 157a1198f83SHeiko Stuebner if (sched_timer) { 158a1198f83SHeiko Stuebner timer_get_base_and_rate(sched_timer, &sched_io_base, 159a1198f83SHeiko Stuebner &sched_rate); 160cfda5901SDinh Nguyen of_node_put(sched_timer); 161a1198f83SHeiko Stuebner } 162cfda5901SDinh Nguyen 163fa8296aeSStephen Boyd sched_clock_register(read_sched_clock, 32, sched_rate); 164cfda5901SDinh Nguyen } 165cfda5901SDinh Nguyen 1669115df89SJisheng Zhang #ifdef CONFIG_ARM 1679115df89SJisheng Zhang static unsigned long dw_apb_delay_timer_read(void) 1689115df89SJisheng Zhang { 1699115df89SJisheng Zhang return ~readl_relaxed(sched_io_base); 1709115df89SJisheng Zhang } 1719115df89SJisheng Zhang 1729115df89SJisheng Zhang static struct delay_timer dw_apb_delay_timer = { 1739115df89SJisheng Zhang .read_current_timer = dw_apb_delay_timer_read, 1749115df89SJisheng Zhang }; 1759115df89SJisheng Zhang #endif 1769115df89SJisheng Zhang 17710021488SHeiko Stuebner static int num_called; 1782e1773f8SDaniel Lezcano static int __init dw_apb_timer_init(struct device_node *timer) 179cfda5901SDinh Nguyen { 1805d9814dfSDinh Nguyen int ret = 0; 1815d9814dfSDinh Nguyen 18210021488SHeiko Stuebner switch (num_called) { 18310021488SHeiko Stuebner case 1: 18410021488SHeiko Stuebner pr_debug("%s: found clocksource timer\n", __func__); 1855d9814dfSDinh Nguyen ret = add_clocksource(timer); 1865d9814dfSDinh Nguyen if (ret) 1875d9814dfSDinh Nguyen return ret; 188cfda5901SDinh Nguyen init_sched_clock(); 1899115df89SJisheng Zhang #ifdef CONFIG_ARM 1909115df89SJisheng Zhang dw_apb_delay_timer.freq = sched_rate; 1919115df89SJisheng Zhang register_current_timer_delay(&dw_apb_delay_timer); 1929115df89SJisheng Zhang #endif 19310021488SHeiko Stuebner break; 19410021488SHeiko Stuebner default: 1956d2e16a3SSerge Semin pr_debug("%s: found clockevent timer\n", __func__); 1965d9814dfSDinh Nguyen ret = add_clockevent(timer); 1975d9814dfSDinh Nguyen if (ret) 1985d9814dfSDinh Nguyen return ret; 19910021488SHeiko Stuebner break; 200cfda5901SDinh Nguyen } 20110021488SHeiko Stuebner 20210021488SHeiko Stuebner num_called++; 2032e1773f8SDaniel Lezcano 2042e1773f8SDaniel Lezcano return 0; 20510021488SHeiko Stuebner } 20617273395SDaniel Lezcano TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); 20717273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init); 20817273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init); 20917273395SDaniel Lezcano TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init); 210