2019-06-03 05:44:50 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2011-07-25 16:36:42 +00:00
|
|
|
/*
|
2012-07-11 20:13:16 +00:00
|
|
|
* Copyright (C) 2012 Altera Corporation
|
2011-07-25 16:36:42 +00:00
|
|
|
* Copyright (c) 2011 Picochip Ltd., Jamie Iles
|
|
|
|
*
|
2012-07-11 20:13:16 +00:00
|
|
|
* Modified from mach-picoxcell/time.c
|
2011-07-25 16:36:42 +00:00
|
|
|
*/
|
clocksource/drivers/dw_apb_timer_of: Implement ARM delay timer
Implement an ARM delay timer to be used for udelay(). This allows us to
skip the delay loop calibration at boot on Marvell BG2, BG2Q, BG2CD
platforms. And after this patch, udelay() will be unaffected by CPU
frequency changes.
Note: Although in case there are several possible delay timers, we may
not select the "best" delay timer. Take one Marvell Berlin platform for
example: we have arch timer and dw-apb timer. The arch timer freq is
25MHZ while the dw-apb timer freq is 100MHZ, current selection would
choose the dw-apb timer. But the dw apb timer is on the APB bus while
arch timer sits in CPU, the cost of accessing the apb timer is higher
than the arch timer. We could introduce "rating" concept to delay
timer, but this approach "brings a lot of complexity and workarounds
in the code for a small benefit" as pointed out by Daniel.
Later, Arnd pointed out "However, we could argue that this actually
doesn't matter at all, because the entire point of the ndelay()/
udelay()/mdelay() functions is to waste CPU cycles doing not much at
all, so we can just as well waste them reading the timer register
than spinning on the CPU reading the arch timer more often.", so we
just simply register the dw apb base delay timer.
Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
2015-11-05 02:32:06 +00:00
|
|
|
#include <linux/delay.h>
|
2011-07-25 16:36:42 +00:00
|
|
|
#include <linux/dw_apb_timer.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_address.h>
|
|
|
|
#include <linux/of_irq.h>
|
2013-06-04 09:37:36 +00:00
|
|
|
#include <linux/clk.h>
|
2018-09-17 14:52:14 +00:00
|
|
|
#include <linux/reset.h>
|
2013-06-02 06:39:40 +00:00
|
|
|
#include <linux/sched_clock.h>
|
2011-07-25 16:36:42 +00:00
|
|
|
|
2020-12-05 10:52:23 +00:00
|
|
|
static int __init timer_get_base_and_rate(struct device_node *np,
|
2011-07-25 16:36:42 +00:00
|
|
|
void __iomem **base, u32 *rate)
|
|
|
|
{
|
2013-06-04 09:37:36 +00:00
|
|
|
struct clk *timer_clk;
|
|
|
|
struct clk *pclk;
|
2018-09-17 14:52:14 +00:00
|
|
|
struct reset_control *rstc;
|
2020-12-05 10:52:23 +00:00
|
|
|
int ret;
|
2013-06-04 09:37:36 +00:00
|
|
|
|
2011-07-25 16:36:42 +00:00
|
|
|
*base = of_iomap(np, 0);
|
|
|
|
|
|
|
|
if (!*base)
|
2018-08-28 01:52:14 +00:00
|
|
|
panic("Unable to map regs for %pOFn", np);
|
2011-07-25 16:36:42 +00:00
|
|
|
|
2018-09-17 14:52:14 +00:00
|
|
|
/*
|
|
|
|
* Reset the timer if the reset control is available, wiping
|
|
|
|
* out the state the firmware may have left it
|
|
|
|
*/
|
|
|
|
rstc = of_reset_control_get(np, NULL);
|
|
|
|
if (!IS_ERR(rstc)) {
|
|
|
|
reset_control_assert(rstc);
|
|
|
|
reset_control_deassert(rstc);
|
|
|
|
}
|
|
|
|
|
2013-06-04 09:37:36 +00:00
|
|
|
/*
|
2021-03-22 21:39:03 +00:00
|
|
|
* Not all implementations use a peripheral clock, so don't panic
|
2013-06-04 09:37:36 +00:00
|
|
|
* if it's not present
|
|
|
|
*/
|
|
|
|
pclk = of_clk_get_by_name(np, "pclk");
|
|
|
|
if (!IS_ERR(pclk))
|
|
|
|
if (clk_prepare_enable(pclk))
|
2018-08-28 01:52:14 +00:00
|
|
|
pr_warn("pclk for %pOFn is present, but could not be activated\n",
|
|
|
|
np);
|
2013-06-04 09:37:36 +00:00
|
|
|
|
2020-12-05 10:52:23 +00:00
|
|
|
if (!of_property_read_u32(np, "clock-freq", rate) &&
|
|
|
|
!of_property_read_u32(np, "clock-frequency", rate))
|
|
|
|
return 0;
|
|
|
|
|
2013-06-04 09:37:36 +00:00
|
|
|
timer_clk = of_clk_get_by_name(np, "timer");
|
2021-03-22 12:18:44 +00:00
|
|
|
if (IS_ERR(timer_clk)) {
|
|
|
|
ret = PTR_ERR(timer_clk);
|
|
|
|
goto out_pclk_disable;
|
|
|
|
}
|
2013-06-04 09:37:36 +00:00
|
|
|
|
2020-12-05 10:52:23 +00:00
|
|
|
ret = clk_prepare_enable(timer_clk);
|
|
|
|
if (ret)
|
2021-03-22 12:18:44 +00:00
|
|
|
goto out_timer_clk_put;
|
2020-12-05 10:52:23 +00:00
|
|
|
|
|
|
|
*rate = clk_get_rate(timer_clk);
|
2021-03-22 12:18:44 +00:00
|
|
|
if (!(*rate)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out_timer_clk_disable;
|
|
|
|
}
|
2013-06-04 09:37:36 +00:00
|
|
|
|
2020-12-05 10:52:23 +00:00
|
|
|
return 0;
|
2021-03-22 12:18:44 +00:00
|
|
|
|
|
|
|
out_timer_clk_disable:
|
|
|
|
clk_disable_unprepare(timer_clk);
|
|
|
|
out_timer_clk_put:
|
|
|
|
clk_put(timer_clk);
|
|
|
|
out_pclk_disable:
|
|
|
|
if (!IS_ERR(pclk)) {
|
|
|
|
clk_disable_unprepare(pclk);
|
|
|
|
clk_put(pclk);
|
|
|
|
}
|
|
|
|
iounmap(*base);
|
|
|
|
return ret;
|
2011-07-25 16:36:42 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 10:52:23 +00:00
|
|
|
static int __init add_clockevent(struct device_node *event_timer)
|
2011-07-25 16:36:42 +00:00
|
|
|
{
|
|
|
|
void __iomem *iobase;
|
|
|
|
struct dw_apb_clock_event_device *ced;
|
|
|
|
u32 irq, rate;
|
2020-12-05 10:52:23 +00:00
|
|
|
int ret = 0;
|
2011-07-25 16:36:42 +00:00
|
|
|
|
|
|
|
irq = irq_of_parse_and_map(event_timer, 0);
|
2013-05-29 08:11:17 +00:00
|
|
|
if (irq == 0)
|
2011-07-25 16:36:42 +00:00
|
|
|
panic("No IRQ for clock event timer");
|
|
|
|
|
2020-12-05 10:52:23 +00:00
|
|
|
ret = timer_get_base_and_rate(event_timer, &iobase, &rate);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2011-07-25 16:36:42 +00:00
|
|
|
|
2020-05-21 20:48:14 +00:00
|
|
|
ced = dw_apb_clockevent_init(-1, event_timer->name, 300, iobase, irq,
|
2011-07-25 16:36:42 +00:00
|
|
|
rate);
|
|
|
|
if (!ced)
|
2020-12-05 10:52:23 +00:00
|
|
|
return -EINVAL;
|
2011-07-25 16:36:42 +00:00
|
|
|
|
|
|
|
dw_apb_clockevent_register(ced);
|
2020-12-05 10:52:23 +00:00
|
|
|
|
|
|
|
return 0;
|
2011-07-25 16:36:42 +00:00
|
|
|
}
|
|
|
|
|
2013-06-04 09:37:02 +00:00
|
|
|
static void __iomem *sched_io_base;
|
|
|
|
static u32 sched_rate;
|
|
|
|
|
2020-12-05 10:52:23 +00:00
|
|
|
static int __init add_clocksource(struct device_node *source_timer)
|
2011-07-25 16:36:42 +00:00
|
|
|
{
|
|
|
|
void __iomem *iobase;
|
|
|
|
struct dw_apb_clocksource *cs;
|
|
|
|
u32 rate;
|
2020-12-05 10:52:23 +00:00
|
|
|
int ret;
|
2011-07-25 16:36:42 +00:00
|
|
|
|
2020-12-05 10:52:23 +00:00
|
|
|
ret = timer_get_base_and_rate(source_timer, &iobase, &rate);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2011-07-25 16:36:42 +00:00
|
|
|
|
|
|
|
cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate);
|
|
|
|
if (!cs)
|
2020-12-05 10:52:23 +00:00
|
|
|
return -EINVAL;
|
2011-07-25 16:36:42 +00:00
|
|
|
|
|
|
|
dw_apb_clocksource_start(cs);
|
|
|
|
dw_apb_clocksource_register(cs);
|
|
|
|
|
2013-06-04 09:37:02 +00:00
|
|
|
/*
|
|
|
|
* Fallback to use the clocksource as sched_clock if no separate
|
|
|
|
* timer is found. sched_io_base then points to the current_value
|
|
|
|
* register of the clocksource timer.
|
|
|
|
*/
|
|
|
|
sched_io_base = iobase + 0x04;
|
|
|
|
sched_rate = rate;
|
2020-12-05 10:52:23 +00:00
|
|
|
|
|
|
|
return 0;
|
2013-06-04 09:37:02 +00:00
|
|
|
}
|
2011-07-25 16:36:42 +00:00
|
|
|
|
2014-05-13 03:10:08 +00:00
|
|
|
static u64 notrace read_sched_clock(void)
|
2011-07-25 16:36:42 +00:00
|
|
|
{
|
2015-03-30 20:17:12 +00:00
|
|
|
return ~readl_relaxed(sched_io_base);
|
2011-07-25 16:36:42 +00:00
|
|
|
}
|
|
|
|
|
2012-07-11 20:13:16 +00:00
|
|
|
static const struct of_device_id sptimer_ids[] __initconst = {
|
2011-07-25 16:36:42 +00:00
|
|
|
{ .compatible = "picochip,pc3x2-rtc" },
|
|
|
|
{ /* Sentinel */ },
|
|
|
|
};
|
|
|
|
|
2013-10-01 08:38:12 +00:00
|
|
|
static void __init init_sched_clock(void)
|
2011-07-25 16:36:42 +00:00
|
|
|
{
|
|
|
|
struct device_node *sched_timer;
|
|
|
|
|
2012-07-11 20:13:16 +00:00
|
|
|
sched_timer = of_find_matching_node(NULL, sptimer_ids);
|
2013-06-04 09:37:02 +00:00
|
|
|
if (sched_timer) {
|
|
|
|
timer_get_base_and_rate(sched_timer, &sched_io_base,
|
|
|
|
&sched_rate);
|
|
|
|
of_node_put(sched_timer);
|
|
|
|
}
|
2011-07-25 16:36:42 +00:00
|
|
|
|
2013-07-18 23:21:22 +00:00
|
|
|
sched_clock_register(read_sched_clock, 32, sched_rate);
|
2011-07-25 16:36:42 +00:00
|
|
|
}
|
|
|
|
|
clocksource/drivers/dw_apb_timer_of: Implement ARM delay timer
Implement an ARM delay timer to be used for udelay(). This allows us to
skip the delay loop calibration at boot on Marvell BG2, BG2Q, BG2CD
platforms. And after this patch, udelay() will be unaffected by CPU
frequency changes.
Note: Although in case there are several possible delay timers, we may
not select the "best" delay timer. Take one Marvell Berlin platform for
example: we have arch timer and dw-apb timer. The arch timer freq is
25MHZ while the dw-apb timer freq is 100MHZ, current selection would
choose the dw-apb timer. But the dw apb timer is on the APB bus while
arch timer sits in CPU, the cost of accessing the apb timer is higher
than the arch timer. We could introduce "rating" concept to delay
timer, but this approach "brings a lot of complexity and workarounds
in the code for a small benefit" as pointed out by Daniel.
Later, Arnd pointed out "However, we could argue that this actually
doesn't matter at all, because the entire point of the ndelay()/
udelay()/mdelay() functions is to waste CPU cycles doing not much at
all, so we can just as well waste them reading the timer register
than spinning on the CPU reading the arch timer more often.", so we
just simply register the dw apb base delay timer.
Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
2015-11-05 02:32:06 +00:00
|
|
|
#ifdef CONFIG_ARM
|
|
|
|
static unsigned long dw_apb_delay_timer_read(void)
|
|
|
|
{
|
|
|
|
return ~readl_relaxed(sched_io_base);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct delay_timer dw_apb_delay_timer = {
|
|
|
|
.read_current_timer = dw_apb_delay_timer_read,
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2013-06-04 09:38:42 +00:00
|
|
|
static int num_called;
|
2016-06-01 06:55:46 +00:00
|
|
|
static int __init dw_apb_timer_init(struct device_node *timer)
|
2011-07-25 16:36:42 +00:00
|
|
|
{
|
2020-12-05 10:52:23 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2013-06-04 09:38:42 +00:00
|
|
|
switch (num_called) {
|
|
|
|
case 1:
|
|
|
|
pr_debug("%s: found clocksource timer\n", __func__);
|
2020-12-05 10:52:23 +00:00
|
|
|
ret = add_clocksource(timer);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2013-06-04 09:38:42 +00:00
|
|
|
init_sched_clock();
|
clocksource/drivers/dw_apb_timer_of: Implement ARM delay timer
Implement an ARM delay timer to be used for udelay(). This allows us to
skip the delay loop calibration at boot on Marvell BG2, BG2Q, BG2CD
platforms. And after this patch, udelay() will be unaffected by CPU
frequency changes.
Note: Although in case there are several possible delay timers, we may
not select the "best" delay timer. Take one Marvell Berlin platform for
example: we have arch timer and dw-apb timer. The arch timer freq is
25MHZ while the dw-apb timer freq is 100MHZ, current selection would
choose the dw-apb timer. But the dw apb timer is on the APB bus while
arch timer sits in CPU, the cost of accessing the apb timer is higher
than the arch timer. We could introduce "rating" concept to delay
timer, but this approach "brings a lot of complexity and workarounds
in the code for a small benefit" as pointed out by Daniel.
Later, Arnd pointed out "However, we could argue that this actually
doesn't matter at all, because the entire point of the ndelay()/
udelay()/mdelay() functions is to waste CPU cycles doing not much at
all, so we can just as well waste them reading the timer register
than spinning on the CPU reading the arch timer more often.", so we
just simply register the dw apb base delay timer.
Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
2015-11-05 02:32:06 +00:00
|
|
|
#ifdef CONFIG_ARM
|
|
|
|
dw_apb_delay_timer.freq = sched_rate;
|
|
|
|
register_current_timer_delay(&dw_apb_delay_timer);
|
|
|
|
#endif
|
2013-06-04 09:38:42 +00:00
|
|
|
break;
|
|
|
|
default:
|
2020-05-21 20:48:15 +00:00
|
|
|
pr_debug("%s: found clockevent timer\n", __func__);
|
2020-12-05 10:52:23 +00:00
|
|
|
ret = add_clockevent(timer);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2013-06-04 09:38:42 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-07-25 16:36:42 +00:00
|
|
|
|
2013-06-04 09:38:42 +00:00
|
|
|
num_called++;
|
2016-06-01 06:55:46 +00:00
|
|
|
|
|
|
|
return 0;
|
2011-07-25 16:36:42 +00:00
|
|
|
}
|
2017-05-26 14:56:11 +00:00
|
|
|
TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
|
|
|
|
TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
|
|
|
|
TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
|
|
|
|
TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
|