2019-05-27 06:55:05 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2010-01-14 13:30:16 +00:00
|
|
|
/*
|
2015-05-18 15:29:40 +00:00
|
|
|
* linux/drivers/clocksource/timer-sp.c
|
2010-01-14 13:30:16 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 1999 - 2003 ARM Limited
|
|
|
|
* Copyright (C) 2000 Deep Blue Solutions Ltd
|
|
|
|
*/
|
2020-10-29 12:33:17 +00:00
|
|
|
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2011-05-12 12:31:48 +00:00
|
|
|
#include <linux/clk.h>
|
2010-01-14 13:30:16 +00:00
|
|
|
#include <linux/clocksource.h>
|
|
|
|
#include <linux/clockchips.h>
|
2011-05-12 12:31:48 +00:00
|
|
|
#include <linux/err.h>
|
2010-01-14 13:30:16 +00:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/io.h>
|
2013-03-25 16:23:52 +00:00
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_address.h>
|
2018-04-18 14:50:02 +00:00
|
|
|
#include <linux/of_clk.h>
|
2013-03-25 16:23:52 +00:00
|
|
|
#include <linux/of_irq.h>
|
2013-06-02 06:39:40 +00:00
|
|
|
#include <linux/sched_clock.h>
|
2010-01-14 13:30:16 +00:00
|
|
|
|
2015-05-18 15:29:40 +00:00
|
|
|
#include "timer-sp.h"
|
2010-01-14 13:30:16 +00:00
|
|
|
|
2020-09-18 13:22:35 +00:00
|
|
|
/* Hisilicon 64-bit timer(a variant of ARM SP804) */
|
|
|
|
#define HISI_TIMER_1_BASE 0x00
|
|
|
|
#define HISI_TIMER_2_BASE 0x40
|
|
|
|
#define HISI_TIMER_LOAD 0x00
|
2020-09-18 13:22:36 +00:00
|
|
|
#define HISI_TIMER_LOAD_H 0x04
|
2020-09-18 13:22:35 +00:00
|
|
|
#define HISI_TIMER_VALUE 0x08
|
2020-09-18 13:22:36 +00:00
|
|
|
#define HISI_TIMER_VALUE_H 0x0c
|
2020-09-18 13:22:35 +00:00
|
|
|
#define HISI_TIMER_CTRL 0x10
|
|
|
|
#define HISI_TIMER_INTCLR 0x14
|
|
|
|
#define HISI_TIMER_RIS 0x18
|
|
|
|
#define HISI_TIMER_MIS 0x1c
|
|
|
|
#define HISI_TIMER_BGLOAD 0x20
|
2020-09-18 13:22:36 +00:00
|
|
|
#define HISI_TIMER_BGLOAD_H 0x24
|
2020-09-18 13:22:35 +00:00
|
|
|
|
2020-10-29 12:33:14 +00:00
|
|
|
static struct sp804_timer arm_sp804_timer __initdata = {
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
.load = TIMER_LOAD,
|
|
|
|
.value = TIMER_VALUE,
|
|
|
|
.ctrl = TIMER_CTRL,
|
|
|
|
.intclr = TIMER_INTCLR,
|
|
|
|
.timer_base = {TIMER_1_BASE, TIMER_2_BASE},
|
|
|
|
.width = 32,
|
|
|
|
};
|
|
|
|
|
2020-10-29 12:33:14 +00:00
|
|
|
static struct sp804_timer hisi_sp804_timer __initdata = {
|
2020-09-18 13:22:35 +00:00
|
|
|
.load = HISI_TIMER_LOAD,
|
2020-09-18 13:22:36 +00:00
|
|
|
.load_h = HISI_TIMER_LOAD_H,
|
2020-09-18 13:22:35 +00:00
|
|
|
.value = HISI_TIMER_VALUE,
|
2020-09-18 13:22:36 +00:00
|
|
|
.value_h = HISI_TIMER_VALUE_H,
|
2020-09-18 13:22:35 +00:00
|
|
|
.ctrl = HISI_TIMER_CTRL,
|
|
|
|
.intclr = HISI_TIMER_INTCLR,
|
|
|
|
.timer_base = {HISI_TIMER_1_BASE, HISI_TIMER_2_BASE},
|
|
|
|
.width = 64,
|
|
|
|
};
|
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
static struct sp804_clkevt sp804_clkevt[NR_TIMERS];
|
|
|
|
|
2020-09-18 13:22:29 +00:00
|
|
|
static long __init sp804_get_clock_rate(struct clk *clk, const char *name)
|
2011-05-12 12:31:48 +00:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2020-09-18 13:22:29 +00:00
|
|
|
if (!clk)
|
|
|
|
clk = clk_get_sys("sp804", name);
|
|
|
|
if (IS_ERR(clk)) {
|
2020-10-29 12:33:17 +00:00
|
|
|
pr_err("%s clock not found: %ld\n", name, PTR_ERR(clk));
|
2020-09-18 13:22:29 +00:00
|
|
|
return PTR_ERR(clk);
|
|
|
|
}
|
|
|
|
|
2020-10-29 12:33:15 +00:00
|
|
|
err = clk_prepare_enable(clk);
|
2011-05-12 12:31:48 +00:00
|
|
|
if (err) {
|
2020-10-29 12:33:17 +00:00
|
|
|
pr_err("clock failed to enable: %d\n", err);
|
2011-05-12 12:31:48 +00:00
|
|
|
clk_put(clk);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-10-29 12:33:16 +00:00
|
|
|
return clk_get_rate(clk);
|
2011-05-12 12:31:48 +00:00
|
|
|
}
|
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
static struct sp804_clkevt * __init sp804_clkevt_get(void __iomem *base)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NR_TIMERS; i++) {
|
|
|
|
if (sp804_clkevt[i].base == base)
|
|
|
|
return &sp804_clkevt[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It's impossible to reach here */
|
|
|
|
WARN_ON(1);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sp804_clkevt *sched_clkevt;
|
2011-12-12 21:29:08 +00:00
|
|
|
|
2013-11-15 23:26:09 +00:00
|
|
|
static u64 notrace sp804_read(void)
|
2011-12-12 21:29:08 +00:00
|
|
|
{
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
return ~readl_relaxed(sched_clkevt->value);
|
2011-12-12 21:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-10-21 01:22:59 +00:00
|
|
|
static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
|
|
|
|
const char *name,
|
|
|
|
struct clk *clk,
|
|
|
|
int use_sched_clock)
|
2010-01-14 13:30:16 +00:00
|
|
|
{
|
2013-03-25 16:23:52 +00:00
|
|
|
long rate;
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
struct sp804_clkevt *clkevt;
|
2013-03-25 16:23:52 +00:00
|
|
|
|
2020-09-18 13:22:29 +00:00
|
|
|
rate = sp804_get_clock_rate(clk, name);
|
2011-05-12 12:31:48 +00:00
|
|
|
if (rate < 0)
|
2016-06-06 21:28:01 +00:00
|
|
|
return -EINVAL;
|
2011-05-12 12:31:48 +00:00
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
clkevt = sp804_clkevt_get(base);
|
|
|
|
|
|
|
|
writel(0, clkevt->ctrl);
|
|
|
|
writel(0xffffffff, clkevt->load);
|
|
|
|
writel(0xffffffff, clkevt->value);
|
2020-09-18 13:22:36 +00:00
|
|
|
if (clkevt->width == 64) {
|
|
|
|
writel(0xffffffff, clkevt->load_h);
|
|
|
|
writel(0xffffffff, clkevt->value_h);
|
|
|
|
}
|
2010-01-14 13:30:16 +00:00
|
|
|
writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
clkevt->ctrl);
|
2010-01-14 13:30:16 +00:00
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
clocksource_mmio_init(clkevt->value, name,
|
2011-05-12 12:31:48 +00:00
|
|
|
rate, 200, 32, clocksource_mmio_readl_down);
|
2011-12-12 21:29:08 +00:00
|
|
|
|
|
|
|
if (use_sched_clock) {
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
sched_clkevt = clkevt;
|
2013-11-15 23:26:09 +00:00
|
|
|
sched_clock_register(sp804_read, 32, rate);
|
2011-12-12 21:29:08 +00:00
|
|
|
}
|
2016-06-06 21:28:01 +00:00
|
|
|
|
|
|
|
return 0;
|
2010-01-14 13:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
static struct sp804_clkevt *common_clkevt;
|
2010-01-14 13:30:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* IRQ handler for the timer
|
|
|
|
*/
|
|
|
|
static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct clock_event_device *evt = dev_id;
|
|
|
|
|
|
|
|
/* clear the interrupt */
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
writel(1, common_clkevt->intclr);
|
2010-01-14 13:30:16 +00:00
|
|
|
|
|
|
|
evt->event_handler(evt);
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2015-07-06 10:09:19 +00:00
|
|
|
static inline void timer_shutdown(struct clock_event_device *evt)
|
2010-01-14 13:30:16 +00:00
|
|
|
{
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
writel(0, common_clkevt->ctrl);
|
2015-07-06 10:09:19 +00:00
|
|
|
}
|
2010-01-14 13:30:16 +00:00
|
|
|
|
2015-07-06 10:09:19 +00:00
|
|
|
static int sp804_shutdown(struct clock_event_device *evt)
|
|
|
|
{
|
|
|
|
timer_shutdown(evt);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-01-14 13:30:16 +00:00
|
|
|
|
2015-07-06 10:09:19 +00:00
|
|
|
static int sp804_set_periodic(struct clock_event_device *evt)
|
|
|
|
{
|
|
|
|
unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE |
|
|
|
|
TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
|
2010-01-14 13:30:16 +00:00
|
|
|
|
2015-07-06 10:09:19 +00:00
|
|
|
timer_shutdown(evt);
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
writel(common_clkevt->reload, common_clkevt->load);
|
|
|
|
writel(ctrl, common_clkevt->ctrl);
|
2015-07-06 10:09:19 +00:00
|
|
|
return 0;
|
2010-01-14 13:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sp804_set_next_event(unsigned long next,
|
|
|
|
struct clock_event_device *evt)
|
|
|
|
{
|
2015-07-06 10:09:19 +00:00
|
|
|
unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE |
|
|
|
|
TIMER_CTRL_ONESHOT | TIMER_CTRL_ENABLE;
|
2010-01-14 13:30:16 +00:00
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
writel(next, common_clkevt->load);
|
|
|
|
writel(ctrl, common_clkevt->ctrl);
|
2010-01-14 13:30:16 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct clock_event_device sp804_clockevent = {
|
2015-07-06 10:09:19 +00:00
|
|
|
.features = CLOCK_EVT_FEAT_PERIODIC |
|
|
|
|
CLOCK_EVT_FEAT_ONESHOT |
|
|
|
|
CLOCK_EVT_FEAT_DYNIRQ,
|
|
|
|
.set_state_shutdown = sp804_shutdown,
|
|
|
|
.set_state_periodic = sp804_set_periodic,
|
|
|
|
.set_state_oneshot = sp804_shutdown,
|
|
|
|
.tick_resume = sp804_shutdown,
|
|
|
|
.set_next_event = sp804_set_next_event,
|
|
|
|
.rating = 300,
|
2010-01-14 13:30:16 +00:00
|
|
|
};
|
|
|
|
|
2020-10-21 01:22:59 +00:00
|
|
|
static int __init sp804_clockevents_init(void __iomem *base, unsigned int irq,
|
|
|
|
struct clk *clk, const char *name)
|
2010-01-14 13:30:16 +00:00
|
|
|
{
|
|
|
|
struct clock_event_device *evt = &sp804_clockevent;
|
2013-03-25 16:23:52 +00:00
|
|
|
long rate;
|
|
|
|
|
2020-09-18 13:22:29 +00:00
|
|
|
rate = sp804_get_clock_rate(clk, name);
|
2011-05-12 14:45:16 +00:00
|
|
|
if (rate < 0)
|
2016-06-06 21:28:01 +00:00
|
|
|
return -EINVAL;
|
2010-01-14 13:30:16 +00:00
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
common_clkevt = sp804_clkevt_get(base);
|
|
|
|
common_clkevt->reload = DIV_ROUND_CLOSEST(rate, HZ);
|
2011-05-12 14:31:13 +00:00
|
|
|
evt->name = name;
|
|
|
|
evt->irq = irq;
|
2012-11-23 17:55:30 +00:00
|
|
|
evt->cpumask = cpu_possible_mask;
|
2010-01-14 13:30:16 +00:00
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
writel(0, common_clkevt->ctrl);
|
2013-03-25 16:23:52 +00:00
|
|
|
|
2020-02-27 10:59:02 +00:00
|
|
|
if (request_irq(irq, sp804_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
|
|
|
|
"timer", &sp804_clockevent))
|
2020-10-29 12:33:17 +00:00
|
|
|
pr_err("request_irq() failed\n");
|
2011-12-21 12:25:34 +00:00
|
|
|
clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
|
2016-06-06 21:28:01 +00:00
|
|
|
|
|
|
|
return 0;
|
2010-01-14 13:30:16 +00:00
|
|
|
}
|
2013-03-25 16:23:52 +00:00
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
static void __init sp804_clkevt_init(struct sp804_timer *timer, void __iomem *base)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NR_TIMERS; i++) {
|
|
|
|
void __iomem *timer_base;
|
|
|
|
struct sp804_clkevt *clkevt;
|
|
|
|
|
|
|
|
timer_base = base + timer->timer_base[i];
|
|
|
|
clkevt = &sp804_clkevt[i];
|
|
|
|
clkevt->base = timer_base;
|
|
|
|
clkevt->load = timer_base + timer->load;
|
2020-09-18 13:22:36 +00:00
|
|
|
clkevt->load_h = timer_base + timer->load_h;
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
clkevt->value = timer_base + timer->value;
|
2020-09-18 13:22:36 +00:00
|
|
|
clkevt->value_h = timer_base + timer->value_h;
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
clkevt->ctrl = timer_base + timer->ctrl;
|
|
|
|
clkevt->intclr = timer_base + timer->intclr;
|
|
|
|
clkevt->width = timer->width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init sp804_of_init(struct device_node *np, struct sp804_timer *timer)
|
2013-03-25 16:23:52 +00:00
|
|
|
{
|
|
|
|
static bool initialized = false;
|
|
|
|
void __iomem *base;
|
2020-09-18 13:22:33 +00:00
|
|
|
void __iomem *timer1_base;
|
|
|
|
void __iomem *timer2_base;
|
2016-06-06 21:28:01 +00:00
|
|
|
int irq, ret = -EINVAL;
|
2013-03-25 16:23:52 +00:00
|
|
|
u32 irq_num = 0;
|
|
|
|
struct clk *clk1, *clk2;
|
|
|
|
const char *name = of_get_property(np, "compatible", NULL);
|
|
|
|
|
|
|
|
base = of_iomap(np, 0);
|
2016-06-06 21:28:01 +00:00
|
|
|
if (!base)
|
|
|
|
return -ENXIO;
|
2013-03-25 16:23:52 +00:00
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
timer1_base = base + timer->timer_base[0];
|
|
|
|
timer2_base = base + timer->timer_base[1];
|
2020-09-18 13:22:33 +00:00
|
|
|
|
2013-03-25 16:23:52 +00:00
|
|
|
/* Ensure timers are disabled */
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
writel(0, timer1_base + timer->ctrl);
|
|
|
|
writel(0, timer2_base + timer->ctrl);
|
2013-03-25 16:23:52 +00:00
|
|
|
|
2016-06-06 21:28:01 +00:00
|
|
|
if (initialized || !of_device_is_available(np)) {
|
|
|
|
ret = -EINVAL;
|
2013-03-25 16:23:52 +00:00
|
|
|
goto err;
|
2016-06-06 21:28:01 +00:00
|
|
|
}
|
2013-03-25 16:23:52 +00:00
|
|
|
|
|
|
|
clk1 = of_clk_get(np, 0);
|
|
|
|
if (IS_ERR(clk1))
|
|
|
|
clk1 = NULL;
|
|
|
|
|
2014-05-29 21:01:34 +00:00
|
|
|
/* Get the 2nd clock if the timer has 3 timer clocks */
|
2018-04-18 14:50:02 +00:00
|
|
|
if (of_clk_get_parent_count(np) == 3) {
|
2013-03-25 16:23:52 +00:00
|
|
|
clk2 = of_clk_get(np, 1);
|
|
|
|
if (IS_ERR(clk2)) {
|
2020-10-29 12:33:17 +00:00
|
|
|
pr_err("%pOFn clock not found: %d\n", np,
|
2013-03-25 16:23:52 +00:00
|
|
|
(int)PTR_ERR(clk2));
|
2014-05-29 21:01:34 +00:00
|
|
|
clk2 = NULL;
|
2013-03-25 16:23:52 +00:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
clk2 = clk1;
|
|
|
|
|
|
|
|
irq = irq_of_parse_and_map(np, 0);
|
|
|
|
if (irq <= 0)
|
|
|
|
goto err;
|
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
sp804_clkevt_init(timer, base);
|
|
|
|
|
2013-03-25 16:23:52 +00:00
|
|
|
of_property_read_u32(np, "arm,sp804-has-irq", &irq_num);
|
|
|
|
if (irq_num == 2) {
|
2016-06-06 21:28:01 +00:00
|
|
|
|
2020-09-18 13:22:33 +00:00
|
|
|
ret = sp804_clockevents_init(timer2_base, irq, clk2, name);
|
2016-06-06 21:28:01 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2020-09-18 13:22:33 +00:00
|
|
|
ret = sp804_clocksource_and_sched_clock_init(timer1_base,
|
2020-09-18 13:22:31 +00:00
|
|
|
name, clk1, 1);
|
2016-06-06 21:28:01 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2013-03-25 16:23:52 +00:00
|
|
|
} else {
|
2016-06-06 21:28:01 +00:00
|
|
|
|
2020-09-18 13:22:33 +00:00
|
|
|
ret = sp804_clockevents_init(timer1_base, irq, clk1, name);
|
2016-06-06 21:28:01 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2020-09-18 13:22:33 +00:00
|
|
|
ret = sp804_clocksource_and_sched_clock_init(timer2_base,
|
2020-09-18 13:22:31 +00:00
|
|
|
name, clk2, 1);
|
2016-06-06 21:28:01 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2013-03-25 16:23:52 +00:00
|
|
|
}
|
|
|
|
initialized = true;
|
|
|
|
|
2016-06-06 21:28:01 +00:00
|
|
|
return 0;
|
2013-03-25 16:23:52 +00:00
|
|
|
err:
|
|
|
|
iounmap(base);
|
2016-06-06 21:28:01 +00:00
|
|
|
return ret;
|
2013-03-25 16:23:52 +00:00
|
|
|
}
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
|
|
|
|
static int __init arm_sp804_of_init(struct device_node *np)
|
|
|
|
{
|
|
|
|
return sp804_of_init(np, &arm_sp804_timer);
|
|
|
|
}
|
|
|
|
TIMER_OF_DECLARE(sp804, "arm,sp804", arm_sp804_of_init);
|
2013-03-13 20:31:12 +00:00
|
|
|
|
2020-09-18 13:22:35 +00:00
|
|
|
static int __init hisi_sp804_of_init(struct device_node *np)
|
|
|
|
{
|
|
|
|
return sp804_of_init(np, &hisi_sp804_timer);
|
|
|
|
}
|
|
|
|
TIMER_OF_DECLARE(hisi_sp804, "hisilicon,sp804", hisi_sp804_of_init);
|
|
|
|
|
2016-06-06 21:28:01 +00:00
|
|
|
static int __init integrator_cp_of_init(struct device_node *np)
|
2013-03-13 20:31:12 +00:00
|
|
|
{
|
|
|
|
static int init_count = 0;
|
|
|
|
void __iomem *base;
|
2016-06-06 21:28:01 +00:00
|
|
|
int irq, ret = -EINVAL;
|
2013-03-13 20:31:12 +00:00
|
|
|
const char *name = of_get_property(np, "compatible", NULL);
|
2014-01-10 14:54:34 +00:00
|
|
|
struct clk *clk;
|
2013-03-13 20:31:12 +00:00
|
|
|
|
|
|
|
base = of_iomap(np, 0);
|
2016-06-06 21:28:01 +00:00
|
|
|
if (!base) {
|
2017-03-09 09:47:10 +00:00
|
|
|
pr_err("Failed to iomap\n");
|
2016-06-06 21:28:01 +00:00
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
2014-01-10 14:54:34 +00:00
|
|
|
clk = of_clk_get(np, 0);
|
2016-06-06 21:28:01 +00:00
|
|
|
if (IS_ERR(clk)) {
|
2017-03-09 09:47:10 +00:00
|
|
|
pr_err("Failed to get clock\n");
|
2016-06-06 21:28:01 +00:00
|
|
|
return PTR_ERR(clk);
|
|
|
|
}
|
2013-03-13 20:31:12 +00:00
|
|
|
|
|
|
|
/* Ensure timer is disabled */
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
writel(0, base + arm_sp804_timer.ctrl);
|
2013-03-13 20:31:12 +00:00
|
|
|
|
|
|
|
if (init_count == 2 || !of_device_is_available(np))
|
|
|
|
goto err;
|
|
|
|
|
clocksource/drivers/sp804: Support non-standard register offset
The ARM SP804 supports a maximum of 32-bit counter, but Hisilicon extends
it to 64-bit. That means, the registers: TimerXload, TimerXValue and
TimerXBGLoad are 64bits, all other registers are the same as those in the
SP804. The driver code can be completely reused except that the register
offset is different.
Currently, we get a timer register address by: add the constant register
offset to the timer base address. e.g. "base + TIMER_CTRL". It can not be
dynamically adjusted at run time.
So create a new structure "sp804_timer" to record the original registers
offset, and create a new structure "sp804_clkevt" to record the
calculated registers address. So the "base + TIMER_CTRL" is changed to
"clkevt->ctrl", this will faster than "base + timer->ctrl".
For example:
struct sp804_timer arm_sp804_timer = {
.ctrl = TIMER_CTRL,
};
struct sp804_clkevt clkevt;
clkevt.ctrl = base + arm_sp804_timer.ctrl.
- writel(0, base + TIMER_CTRL);
+ writel(0, clkevt->ctrl);
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200918132237.3552-7-thunder.leizhen@huawei.com
2020-09-18 13:22:34 +00:00
|
|
|
sp804_clkevt_init(&arm_sp804_timer, base);
|
|
|
|
|
2016-06-06 21:28:01 +00:00
|
|
|
if (!init_count) {
|
2020-09-18 13:22:31 +00:00
|
|
|
ret = sp804_clocksource_and_sched_clock_init(base,
|
|
|
|
name, clk, 0);
|
2016-06-06 21:28:01 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
} else {
|
2013-03-13 20:31:12 +00:00
|
|
|
irq = irq_of_parse_and_map(np, 0);
|
|
|
|
if (irq <= 0)
|
|
|
|
goto err;
|
|
|
|
|
2020-09-18 13:22:31 +00:00
|
|
|
ret = sp804_clockevents_init(base, irq, clk, name);
|
2016-06-06 21:28:01 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2013-03-13 20:31:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
init_count++;
|
2016-06-06 21:28:01 +00:00
|
|
|
return 0;
|
2013-03-13 20:31:12 +00:00
|
|
|
err:
|
|
|
|
iounmap(base);
|
2016-06-06 21:28:01 +00:00
|
|
|
return ret;
|
2013-03-13 20:31:12 +00:00
|
|
|
}
|
2017-05-26 14:56:11 +00:00
|
|
|
TIMER_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
|