ATCPIT100 is often used on the Andes architecture,
This timer provide 4 PIT channels. Each PIT channel is a
multi-function timer, can be configured as 32,16,8 bit timers
or PWM as well.
For system timer it will set channel 1 32-bit timer0 as clock
source and count downwards until underflow and restart again.
It also set channel 0 32-bit timer0 as clock event and count
downwards until condition match. It will generate an interrupt
for handling periodically.
Changes in v6:
- To select TIMER_OF in drivers/clocksource/Kconfig instead of arch/nds32/Kconfig
- Refine Kconfig
- Update license format to SPDX-License-Identifier
Rick Chen (3):
clocksource/drivers/atcpit100: Add andestech atcpit100 timer
clocksource/drivers/atcpit100: VDSO support
dt-bindings: timer: Add andestech atcpit100 timer binding doc
.../bindings/timer/andestech,atcpit100-timer.txt | 33 +++
drivers/clocksource/Kconfig | 9 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-atcpit100.c | 262 ++++++++++++++++++++
4 files changed, 305 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/andestech,atcpit100-timer.txt
create mode 100644 drivers/clocksource/timer-atcpit100.c
--
1.7.9.5
From: Rick Chen <[email protected]>
ATCPIT100 is often used on the Andes architecture,
This timer provide 4 PIT channels. Each PIT channel is a
multi-function timer, can be configured as 32,16,8 bit timers
or PWM as well.
For system timer it will set channel 1 32-bit timer0 as clock
source and count downwards until underflow and restart again.
It also set channel 0 32-bit timer0 as clock event and count
downwards until condition match. It will generate an interrupt
for handling periodically.
Signed-off-by: Rick Chen <[email protected]>
Signed-off-by: Greentime Hu <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
---
drivers/clocksource/Kconfig | 9 ++
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-atcpit100.c | 244 +++++++++++++++++++++++++++++++++
3 files changed, 254 insertions(+)
create mode 100644 drivers/clocksource/timer-atcpit100.c
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index cc60620..5014949 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -615,4 +615,13 @@ config CLKSRC_ST_LPC
Enable this option to use the Low Power controller timer
as clocksource.
+config ATCPIT100_TIMER
+ bool "ATCPIT100 timer driver"
+ depends on NDS32 || COMPILE_TEST
+ depends on HAS_IOMEM
+ select TIMER_OF
+ default NDS32
+ help
+ This option enables support for the Andestech ATCPIT100 timers.
+
endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 72711f1..7403a19 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -75,3 +75,4 @@ obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
obj-$(CONFIG_X86_NUMACHIP) += numachip.o
+obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o
diff --git a/drivers/clocksource/timer-atcpit100.c b/drivers/clocksource/timer-atcpit100.c
new file mode 100644
index 0000000..e34b2fe
--- /dev/null
+++ b/drivers/clocksource/timer-atcpit100.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2005-2017 Andes Technology Corporation
+/*
+ * Andestech ATCPIT100 Timer Device Driver Implementation
+ * Rick Chen, Andes Technology Corporation <[email protected]>
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/sched.h>
+#include <linux/sched_clock.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include "timer-of.h"
+
+/*
+ * Definition of register offsets
+ */
+
+/* ID and Revision Register */
+#define ID_REV 0x0
+
+/* Configuration Register */
+#define CFG 0x10
+
+/* Interrupt Enable Register */
+#define INT_EN 0x14
+#define CH_INT_EN(c, i) ((1<<i)<<(4*c))
+#define CH0INT0EN 0x01
+
+/* Interrupt Status Register */
+#define INT_STA 0x18
+#define CH0INT0 0x01
+
+/* Channel Enable Register */
+#define CH_EN 0x1C
+#define CH0TMR0EN 0x1
+#define CH1TMR0EN 0x10
+
+/* Channel 0 , 1 Control Register */
+#define CH0_CTL (0x20)
+#define CH1_CTL (0x20 + 0x10)
+
+/* Channel clock source , bit 3 , 0:External clock , 1:APB clock */
+#define APB_CLK BIT(3)
+
+/* Channel mode , bit 0~2 */
+#define TMR_32 0x1
+#define TMR_16 0x2
+#define TMR_8 0x3
+
+/* Channel 0 , 1 Reload Register */
+#define CH0_REL (0x24)
+#define CH1_REL (0x24 + 0x10)
+
+/* Channel 0 , 1 Counter Register */
+#define CH0_CNT (0x28)
+#define CH1_CNT (0x28 + 0x10)
+
+#define TIMER_SYNC_TICKS 3
+
+static void atcpit100_ch1_tmr0_en(void __iomem *base)
+{
+ writel(~0, base + CH1_REL);
+ writel(APB_CLK|TMR_32, base + CH1_CTL);
+}
+
+static void atcpit100_ch0_tmr0_en(void __iomem *base)
+{
+ writel(APB_CLK|TMR_32, base + CH0_CTL);
+}
+
+static void atcpit100_clkevt_time_setup(void __iomem *base, unsigned long delay)
+{
+ writel(delay, base + CH0_CNT);
+ writel(delay, base + CH0_REL);
+}
+
+static void atcpit100_timer_clear_interrupt(void __iomem *base)
+{
+ u32 val;
+
+ val = readl(base + INT_STA);
+ writel(val | CH0INT0, base + INT_STA);
+}
+
+static void atcpit100_clocksource_start(void __iomem *base)
+{
+ u32 val;
+
+ val = readl(base + CH_EN);
+ writel(val | CH1TMR0EN, base + CH_EN);
+}
+
+static void atcpit100_clkevt_time_start(void __iomem *base)
+{
+ u32 val;
+
+ val = readl(base + CH_EN);
+ writel(val | CH0TMR0EN, base + CH_EN);
+}
+
+static void atcpit100_clkevt_time_stop(void __iomem *base)
+{
+ u32 val;
+
+ atcpit100_timer_clear_interrupt(base);
+ val = readl(base + CH_EN);
+ writel(val & ~CH0TMR0EN, base + CH_EN);
+}
+
+static int atcpit100_clkevt_next_event(unsigned long evt,
+ struct clock_event_device *clkevt)
+{
+ struct timer_of *to = to_timer_of(clkevt);
+
+ writel(evt, timer_of_base(to) + CH0_REL);
+
+ return 0;
+}
+
+static int atcpit100_clkevt_set_periodic(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+
+ atcpit100_clkevt_time_setup(timer_of_base(to), timer_of_period(to));
+ atcpit100_clkevt_time_start(timer_of_base(to));
+
+ return 0;
+}
+static int atcpit100_clkevt_shutdown(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+
+ atcpit100_clkevt_time_stop(timer_of_base(to));
+
+ return 0;
+}
+static int atcpit100_clkevt_set_oneshot(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+ u32 val;
+
+ writel(~0x0, timer_of_base(to) + CH0_REL);
+ val = readl(timer_of_base(to) + CH_EN);
+ writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN);
+
+ return 0;
+}
+
+static irqreturn_t atcpit100_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = (struct clock_event_device *)dev_id;
+ struct timer_of *to = to_timer_of(evt);
+
+ atcpit100_timer_clear_interrupt(timer_of_base(to));
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct timer_of to = {
+ .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
+
+ .clkevt = {
+ .name = "atcpit100_tick",
+ .rating = 300,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_state_shutdown = atcpit100_clkevt_shutdown,
+ .set_state_periodic = atcpit100_clkevt_set_periodic,
+ .set_state_oneshot = atcpit100_clkevt_set_oneshot,
+ .tick_resume = atcpit100_clkevt_shutdown,
+ .set_next_event = atcpit100_clkevt_next_event,
+ .cpumask = cpu_all_mask,
+ },
+
+ .of_irq = {
+ .handler = atcpit100_timer_interrupt,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
+ },
+
+ /*
+ * FIXME: we currently only support clocking using PCLK
+ * and using EXTCLK is not supported in the driver.
+ */
+ .of_clk = {
+ .name = "PCLK",
+ }
+};
+
+static u64 notrace atcpit100_timer_sched_read(void)
+{
+ return ~readl(timer_of_base(&to) + CH1_CNT);
+}
+
+static int __init atcpit100_timer_init(struct device_node *node)
+{
+ int ret;
+ u32 val;
+ void __iomem *base;
+
+ ret = timer_of_init(node, &to);
+ if (ret)
+ return ret;
+
+ base = timer_of_base(&to);
+
+ sched_clock_register(atcpit100_timer_sched_read, 32,
+ timer_of_rate(&to));
+
+ ret = clocksource_mmio_init(base + CH1_CNT,
+ node->name, timer_of_rate(&to), 300, 32,
+ clocksource_mmio_readl_down);
+
+ if (ret) {
+ pr_err("Failed to register clocksource\n");
+ return ret;
+ }
+
+ /* clear channel 0 timer0 interrupt */
+ atcpit100_timer_clear_interrupt(base);
+
+ clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
+ TIMER_SYNC_TICKS, 0xffffffff);
+ atcpit100_ch0_tmr0_en(base);
+ atcpit100_ch1_tmr0_en(base);
+ atcpit100_clocksource_start(base);
+ atcpit100_clkevt_time_start(base);
+
+ /* Enable channel 0 timer0 interrupt */
+ val = readl(base + INT_EN);
+ writel(val | CH0INT0EN, base + INT_EN);
+
+ return ret;
+}
+
+TIMER_OF_DECLARE(atcpit100, "andestech,atcpit100", atcpit100_timer_init);
--
1.7.9.5
From: Rick Chen <[email protected]>
VDSO needs real-time cycle count to ensure the time accuracy.
Unlike others, nds32 architecture does not define clock source,
hence VDSO needs atcpit100 offering real-time cycle count
to derive the correct time.
Signed-off-by: Vincent Chen <[email protected]>
Signed-off-by: Rick Chen <[email protected]>
Signed-off-by: Greentime Hu <[email protected]>
---
drivers/clocksource/timer-atcpit100.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/clocksource/timer-atcpit100.c b/drivers/clocksource/timer-atcpit100.c
index e34b2fe..4cf69e2 100644
--- a/drivers/clocksource/timer-atcpit100.c
+++ b/drivers/clocksource/timer-atcpit100.c
@@ -18,6 +18,9 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include "timer-of.h"
+#ifdef CONFIG_NDS32
+#include <asm/vdso_timer_info.h>
+#endif
/*
* Definition of register offsets
@@ -200,6 +203,17 @@ static u64 notrace atcpit100_timer_sched_read(void)
return ~readl(timer_of_base(&to) + CH1_CNT);
}
+#ifdef CONFIG_NDS32
+static void fill_vdso_need_info(struct device_node *node)
+{
+ struct resource timer_res;
+ of_address_to_resource(node, 0, &timer_res);
+ timer_info.mapping_base = (unsigned long)timer_res.start;
+ timer_info.cycle_count_down = true;
+ timer_info.cycle_count_reg_offset = CH1_CNT;
+}
+#endif
+
static int __init atcpit100_timer_init(struct device_node *node)
{
int ret;
@@ -238,6 +252,10 @@ static int __init atcpit100_timer_init(struct device_node *node)
val = readl(base + INT_EN);
writel(val | CH0INT0EN, base + INT_EN);
+#ifdef CONFIG_NDS32
+ fill_vdso_need_info(node);
+#endif
+
return ret;
}
--
1.7.9.5
From: Rick Chen <[email protected]>
Add a document to describe Andestech atcpit100 timer and
binding information.
Signed-off-by: Rick Chen <[email protected]>
Signed-off-by: Greentime Hu <[email protected]>
Acked-by: Rob Herring <[email protected]>
---
.../bindings/timer/andestech,atcpit100-timer.txt | 33 ++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/andestech,atcpit100-timer.txt
diff --git a/Documentation/devicetree/bindings/timer/andestech,atcpit100-timer.txt b/Documentation/devicetree/bindings/timer/andestech,atcpit100-timer.txt
new file mode 100644
index 0000000..4c9ea59
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/andestech,atcpit100-timer.txt
@@ -0,0 +1,33 @@
+Andestech ATCPIT100 timer
+------------------------------------------------------------------
+ATCPIT100 is a generic IP block from Andes Technology, embedded in
+Andestech AE3XX platforms and other designs.
+
+This timer is a set of compact multi-function timers, which can be
+used as pulse width modulators (PWM) as well as simple timers.
+
+It supports up to 4 PIT channels. Each PIT channel is a
+multi-function timer and provide the following usage scenarios:
+One 32-bit timer
+Two 16-bit timers
+Four 8-bit timers
+One 16-bit PWM
+One 16-bit timer and one 8-bit PWM
+Two 8-bit timer and one 8-bit PWM
+
+Required properties:
+- compatible : Should be "andestech,atcpit100"
+- reg : Address and length of the register set
+- interrupts : Reference to the timer interrupt
+- clocks : a clock to provide the tick rate for "andestech,atcpit100"
+- clock-names : should be "PCLK" for the peripheral clock source.
+
+Examples:
+
+timer0: timer@f0400000 {
+ compatible = "andestech,atcpit100";
+ reg = <0xf0400000 0x1000>;
+ interrupts = <2>;
+ clocks = <&apb>;
+ clock-names = "PCLK";
+};
--
1.7.9.5
On Mon, Jan 15, 2018 at 6:57 AM, Greentime Hu <[email protected]> wrote:
> From: Rick Chen <[email protected]>
>
> Add a document to describe Andestech atcpit100 timer and
> binding information.
>
> Signed-off-by: Rick Chen <[email protected]>
> Signed-off-by: Greentime Hu <[email protected]>
> Acked-by: Rob Herring <[email protected]>
Acked-by: Arnd Bergmann <[email protected]>
On Mon, Jan 15, 2018 at 6:57 AM, Greentime Hu <[email protected]> wrote:
> From: Rick Chen <[email protected]>
>
> VDSO needs real-time cycle count to ensure the time accuracy.
> Unlike others, nds32 architecture does not define clock source,
> hence VDSO needs atcpit100 offering real-time cycle count
> to derive the correct time.
>
> Signed-off-by: Vincent Chen <[email protected]>
> Signed-off-by: Rick Chen <[email protected]>
> Signed-off-by: Greentime Hu <[email protected]>
I'm a bit puzzled by this patch, can you explain how the vdso actually
manages to access the clock hardware? It looks like you make the
physical address and the register offset available to user space, but
how does it end up accessing it?
Arnd
2018-01-18 19:08 GMT+08:00 Arnd Bergmann <[email protected]>:
> On Mon, Jan 15, 2018 at 6:57 AM, Greentime Hu <[email protected]> wrote:
>> From: Rick Chen <[email protected]>
>>
>> VDSO needs real-time cycle count to ensure the time accuracy.
>> Unlike others, nds32 architecture does not define clock source,
>> hence VDSO needs atcpit100 offering real-time cycle count
>> to derive the correct time.
>>
>> Signed-off-by: Vincent Chen <[email protected]>
>> Signed-off-by: Rick Chen <[email protected]>
>> Signed-off-by: Greentime Hu <[email protected]>
>
> I'm a bit puzzled by this patch, can you explain how the vdso actually
> manages to access the clock hardware? It looks like you make the
> physical address and the register offset available to user space, but
> how does it end up accessing it?
>
> Arnd
Dear Arnd:
Accessing clock hardware in vdso can be divided to 2 step.
1. Setup an additional memory mapping for clock hardware in user space
when establishing
vdso-needed memory mapping
In arch_setup_additional_pages(), kernel establishes memory
mapping for vdso's text and vdata page
in user space. In order to make clock hardware be accessible in
user space, we try to establish an
additional memory mapping for clock hardware here based on clock
information from driver. This page is
located between vdata page and vdso text page. For safety, this
region for clock accessing is read-only.
2. Accessing clock hardware in vdso
After step 1, clock hardware is accessible in user space
through memory-mapped IO. However, it is not
enough to access a specific register. Therefore, we store register
offset information in vdata page to make it
visible in user space. Now, vdso can derive the address of counter
register by summation of __get_timerpage()
and counter register offset where __get_timerpage() is used to
derive the virtual address of memory-mapped
clock.
Vincent
Correct the composition
Dear Arnd:
1. Setup an additional memory mapping for clock hardware in user space
when establishing vdso-needed memory mapping
In arch_setup_additional_pages(), kernel establishes memory
mapping for vdso's text and vdata page in user space. In order to make
clock hardware be accessible in user space, we try to establish an
additional memory mapping for clock hardware here based on clock
information from driver. This page is located between vdata page and
vdso text page. For safety, this region for clock accessing is
read-only.
2. Accessing clock hardware in vdso
After step 1, clock hardware is accessible in user space through
memory-mapped IO. However, it is not enough to access a specific
register. Therefore, we store register offset information in vdata
page to make it visible in user space. Now, vdso can derive the
address of counter register by summation of __get_timerpage() and
counter register offset where __get_timerpage() is used to derive the
virtual address of memory-mapped clock.
2018-01-20 19:11 GMT+08:00 Vincent Chen <[email protected]>:
> 2018-01-18 19:08 GMT+08:00 Arnd Bergmann <[email protected]>:
>> On Mon, Jan 15, 2018 at 6:57 AM, Greentime Hu <[email protected]> wrote:
>>> From: Rick Chen <[email protected]>
>>>
>>> VDSO needs real-time cycle count to ensure the time accuracy.
>>> Unlike others, nds32 architecture does not define clock source,
>>> hence VDSO needs atcpit100 offering real-time cycle count
>>> to derive the correct time.
>>>
>>> Signed-off-by: Vincent Chen <[email protected]>
>>> Signed-off-by: Rick Chen <[email protected]>
>>> Signed-off-by: Greentime Hu <[email protected]>
>>
>> I'm a bit puzzled by this patch, can you explain how the vdso actually
>> manages to access the clock hardware? It looks like you make the
>> physical address and the register offset available to user space, but
>> how does it end up accessing it?
>>
>> Arnd
>
> Dear Arnd:
>
> Accessing clock hardware in vdso can be divided to 2 step.
>
> 1. Setup an additional memory mapping for clock hardware in user space
> when establishing
> vdso-needed memory mapping
>
> In arch_setup_additional_pages(), kernel establishes memory
> mapping for vdso's text and vdata page
> in user space. In order to make clock hardware be accessible in
> user space, we try to establish an
> additional memory mapping for clock hardware here based on clock
> information from driver. This page is
> located between vdata page and vdso text page. For safety, this
> region for clock accessing is read-only.
>
> 2. Accessing clock hardware in vdso
>
> After step 1, clock hardware is accessible in user space
> through memory-mapped IO. However, it is not
> enough to access a specific register. Therefore, we store register
> offset information in vdata page to make it
> visible in user space. Now, vdso can derive the address of counter
> register by summation of __get_timerpage()
> and counter register offset where __get_timerpage() is used to
> derive the virtual address of memory-mapped
> clock.
>
>
> Vincent