Path: blob/master/drivers/clocksource/dw_apb_timer_of.c
26278 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2012 Altera Corporation3* Copyright (c) 2011 Picochip Ltd., Jamie Iles4*5* Modified from mach-picoxcell/time.c6*/7#include <linux/delay.h>8#include <linux/dw_apb_timer.h>9#include <linux/of.h>10#include <linux/of_address.h>11#include <linux/of_irq.h>12#include <linux/clk.h>13#include <linux/reset.h>14#include <linux/sched_clock.h>1516static int __init timer_get_base_and_rate(struct device_node *np,17void __iomem **base, u32 *rate)18{19struct clk *timer_clk;20struct clk *pclk;21struct reset_control *rstc;22int ret;2324*base = of_iomap(np, 0);2526if (!*base)27panic("Unable to map regs for %pOFn", np);2829/*30* Reset the timer if the reset control is available, wiping31* out the state the firmware may have left it32*/33rstc = of_reset_control_get(np, NULL);34if (!IS_ERR(rstc)) {35reset_control_assert(rstc);36reset_control_deassert(rstc);37}3839/*40* Not all implementations use a peripheral clock, so don't panic41* if it's not present42*/43pclk = of_clk_get_by_name(np, "pclk");44if (!IS_ERR(pclk))45if (clk_prepare_enable(pclk))46pr_warn("pclk for %pOFn is present, but could not be activated\n",47np);4849if (!of_property_read_u32(np, "clock-freq", rate) ||50!of_property_read_u32(np, "clock-frequency", rate))51return 0;5253timer_clk = of_clk_get_by_name(np, "timer");54if (IS_ERR(timer_clk)) {55ret = PTR_ERR(timer_clk);56goto out_pclk_disable;57}5859ret = clk_prepare_enable(timer_clk);60if (ret)61goto out_timer_clk_put;6263*rate = clk_get_rate(timer_clk);64if (!(*rate)) {65ret = -EINVAL;66goto out_timer_clk_disable;67}6869return 0;7071out_timer_clk_disable:72clk_disable_unprepare(timer_clk);73out_timer_clk_put:74clk_put(timer_clk);75out_pclk_disable:76if (!IS_ERR(pclk)) {77clk_disable_unprepare(pclk);78clk_put(pclk);79}80iounmap(*base);81return ret;82}8384static int __init add_clockevent(struct device_node *event_timer)85{86void __iomem *iobase;87struct dw_apb_clock_event_device *ced;88u32 irq, rate;89int ret = 0;9091irq = irq_of_parse_and_map(event_timer, 0);92if (irq == 0)93panic("No IRQ for clock event timer");9495ret = timer_get_base_and_rate(event_timer, &iobase, &rate);96if (ret)97return ret;9899ced = dw_apb_clockevent_init(-1, event_timer->name, 300, iobase, irq,100rate);101if (!ced)102return -EINVAL;103104dw_apb_clockevent_register(ced);105106return 0;107}108109static void __iomem *sched_io_base;110static u32 sched_rate;111112static int __init add_clocksource(struct device_node *source_timer)113{114void __iomem *iobase;115struct dw_apb_clocksource *cs;116u32 rate;117int ret;118119ret = timer_get_base_and_rate(source_timer, &iobase, &rate);120if (ret)121return ret;122123cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate);124if (!cs)125return -EINVAL;126127dw_apb_clocksource_start(cs);128dw_apb_clocksource_register(cs);129130/*131* Fallback to use the clocksource as sched_clock if no separate132* timer is found. sched_io_base then points to the current_value133* register of the clocksource timer.134*/135sched_io_base = iobase + 0x04;136sched_rate = rate;137138return 0;139}140141static u64 notrace read_sched_clock(void)142{143return ~readl_relaxed(sched_io_base);144}145146static const struct of_device_id sptimer_ids[] __initconst = {147{ .compatible = "picochip,pc3x2-rtc" },148{ /* Sentinel */ },149};150151static void __init init_sched_clock(void)152{153struct device_node *sched_timer;154155sched_timer = of_find_matching_node(NULL, sptimer_ids);156if (sched_timer) {157timer_get_base_and_rate(sched_timer, &sched_io_base,158&sched_rate);159of_node_put(sched_timer);160}161162sched_clock_register(read_sched_clock, 32, sched_rate);163}164165#ifdef CONFIG_ARM166static unsigned long dw_apb_delay_timer_read(void)167{168return ~readl_relaxed(sched_io_base);169}170171static struct delay_timer dw_apb_delay_timer = {172.read_current_timer = dw_apb_delay_timer_read,173};174#endif175176static int num_called;177static int __init dw_apb_timer_init(struct device_node *timer)178{179int ret = 0;180181switch (num_called) {182case 1:183pr_debug("%s: found clocksource timer\n", __func__);184ret = add_clocksource(timer);185if (ret)186return ret;187init_sched_clock();188#ifdef CONFIG_ARM189dw_apb_delay_timer.freq = sched_rate;190register_current_timer_delay(&dw_apb_delay_timer);191#endif192break;193default:194pr_debug("%s: found clockevent timer\n", __func__);195ret = add_clockevent(timer);196if (ret)197return ret;198break;199}200201num_called++;202203return 0;204}205TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);206TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);207TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);208TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);209210211