* [PATCH v2 1/2] clocksource: meson6_timer: use register names from the datasheet
2018-11-15 22:46 [PATCH v2 0/2] clocksource/meson6_timer: implement ARM delay timer Martin Blumenstingl
@ 2018-11-15 22:46 ` Martin Blumenstingl
2018-11-15 22:46 ` [PATCH v2 2/2] clocksource: meson6_timer: implement ARM delay timer Martin Blumenstingl
2018-11-18 1:27 ` [PATCH v2 0/2] clocksource/meson6_timer: " Daniel Lezcano
2 siblings, 0 replies; 7+ messages in thread
From: Martin Blumenstingl @ 2018-11-15 22:46 UTC (permalink / raw)
To: linux-amlogic, linux-kernel, tglx, daniel.lezcano
Cc: linux-arm-kernel, Martin Blumenstingl
This makes the driver use the names from S805 datasheet for the
preprocessor #defines. This makes it easier to spot that the driver
currently only supports Timer A (as clockevent with interrupt support)
and Timer E (as clocksource without interrupts). Timer B, C and D (which
are similar to Timer A) are currently not supported by the driver.
While here, this also removes the internal "CED_ID" and "CSD_ID" defines
which are used to identify the timer. These IDs are not described in the
datasheet and thus make it harder to compare the code to what's written
in the datasheet.
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
drivers/clocksource/meson6_timer.c | 108 +++++++++++++++++------------
1 file changed, 64 insertions(+), 44 deletions(-)
diff --git a/drivers/clocksource/meson6_timer.c b/drivers/clocksource/meson6_timer.c
index 92f20991a937..23c7638e2bb3 100644
--- a/drivers/clocksource/meson6_timer.c
+++ b/drivers/clocksource/meson6_timer.c
@@ -10,6 +10,8 @@
* warranty of any kind, whether express or implied.
*/
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
@@ -20,80 +22,96 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#define CED_ID 0
-#define CSD_ID 4
-
-#define TIMER_ISA_MUX 0
-#define TIMER_ISA_VAL(t) (((t) + 1) << 2)
-
-#define TIMER_INPUT_BIT(t) (2 * (t))
-#define TIMER_ENABLE_BIT(t) (16 + (t))
-#define TIMER_PERIODIC_BIT(t) (12 + (t))
-
-#define TIMER_CED_INPUT_MASK (3UL << TIMER_INPUT_BIT(CED_ID))
-#define TIMER_CSD_INPUT_MASK (7UL << TIMER_INPUT_BIT(CSD_ID))
-
-#define TIMER_CED_UNIT_1US 0
-#define TIMER_CSD_UNIT_1US 1
+#define MESON_ISA_TIMER_MUX 0x00
+#define MESON_ISA_TIMER_MUX_TIMERD_EN BIT(19)
+#define MESON_ISA_TIMER_MUX_TIMERC_EN BIT(18)
+#define MESON_ISA_TIMER_MUX_TIMERB_EN BIT(17)
+#define MESON_ISA_TIMER_MUX_TIMERA_EN BIT(16)
+#define MESON_ISA_TIMER_MUX_TIMERD_MODE BIT(15)
+#define MESON_ISA_TIMER_MUX_TIMERC_MODE BIT(14)
+#define MESON_ISA_TIMER_MUX_TIMERB_MODE BIT(13)
+#define MESON_ISA_TIMER_MUX_TIMERA_MODE BIT(12)
+#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK GENMASK(10, 8)
+#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_SYSTEM_CLOCK 0x0
+#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1US 0x1
+#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_10US 0x2
+#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_100US 0x3
+#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1MS 0x4
+#define MESON_ISA_TIMER_MUX_TIMERD_INPUT_CLOCK_MASK GENMASK(7, 6)
+#define MESON_ISA_TIMER_MUX_TIMERC_INPUT_CLOCK_MASK GENMASK(5, 4)
+#define MESON_ISA_TIMER_MUX_TIMERB_INPUT_CLOCK_MASK GENMASK(3, 2)
+#define MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK GENMASK(1, 0)
+#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1US 0x0
+#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_10US 0x1
+#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_100US 0x0
+#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1MS 0x3
+
+#define MESON_ISA_TIMERA 0x04
+#define MESON_ISA_TIMERB 0x08
+#define MESON_ISA_TIMERC 0x0c
+#define MESON_ISA_TIMERD 0x10
+#define MESON_ISA_TIMERE 0x14
static void __iomem *timer_base;
static u64 notrace meson6_timer_sched_read(void)
{
- return (u64)readl(timer_base + TIMER_ISA_VAL(CSD_ID));
+ return (u64)readl(timer_base + MESON_ISA_TIMERE);
}
-static void meson6_clkevt_time_stop(unsigned char timer)
+static void meson6_clkevt_time_stop(void)
{
- u32 val = readl(timer_base + TIMER_ISA_MUX);
+ u32 val = readl(timer_base + MESON_ISA_TIMER_MUX);
- writel(val & ~TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
+ writel(val & ~MESON_ISA_TIMER_MUX_TIMERA_EN,
+ timer_base + MESON_ISA_TIMER_MUX);
}
-static void meson6_clkevt_time_setup(unsigned char timer, unsigned long delay)
+static void meson6_clkevt_time_setup(unsigned long delay)
{
- writel(delay, timer_base + TIMER_ISA_VAL(timer));
+ writel(delay, timer_base + MESON_ISA_TIMERA);
}
-static void meson6_clkevt_time_start(unsigned char timer, bool periodic)
+static void meson6_clkevt_time_start(bool periodic)
{
- u32 val = readl(timer_base + TIMER_ISA_MUX);
+ u32 val = readl(timer_base + MESON_ISA_TIMER_MUX);
if (periodic)
- val |= TIMER_PERIODIC_BIT(timer);
+ val |= MESON_ISA_TIMER_MUX_TIMERA_MODE;
else
- val &= ~TIMER_PERIODIC_BIT(timer);
+ val &= ~MESON_ISA_TIMER_MUX_TIMERA_MODE;
- writel(val | TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
+ writel(val | MESON_ISA_TIMER_MUX_TIMERA_EN,
+ timer_base + MESON_ISA_TIMER_MUX);
}
static int meson6_shutdown(struct clock_event_device *evt)
{
- meson6_clkevt_time_stop(CED_ID);
+ meson6_clkevt_time_stop();
return 0;
}
static int meson6_set_oneshot(struct clock_event_device *evt)
{
- meson6_clkevt_time_stop(CED_ID);
- meson6_clkevt_time_start(CED_ID, false);
+ meson6_clkevt_time_stop();
+ meson6_clkevt_time_start(false);
return 0;
}
static int meson6_set_periodic(struct clock_event_device *evt)
{
- meson6_clkevt_time_stop(CED_ID);
- meson6_clkevt_time_setup(CED_ID, USEC_PER_SEC / HZ - 1);
- meson6_clkevt_time_start(CED_ID, true);
+ meson6_clkevt_time_stop();
+ meson6_clkevt_time_setup(USEC_PER_SEC / HZ - 1);
+ meson6_clkevt_time_start(true);
return 0;
}
static int meson6_clkevt_next_event(unsigned long evt,
struct clock_event_device *unused)
{
- meson6_clkevt_time_stop(CED_ID);
- meson6_clkevt_time_setup(CED_ID, evt);
- meson6_clkevt_time_start(CED_ID, false);
+ meson6_clkevt_time_stop();
+ meson6_clkevt_time_setup(evt);
+ meson6_clkevt_time_start(false);
return 0;
}
@@ -144,22 +162,24 @@ static int __init meson6_timer_init(struct device_node *node)
}
/* Set 1us for timer E */
- val = readl(timer_base + TIMER_ISA_MUX);
- val &= ~TIMER_CSD_INPUT_MASK;
- val |= TIMER_CSD_UNIT_1US << TIMER_INPUT_BIT(CSD_ID);
- writel(val, timer_base + TIMER_ISA_MUX);
+ val = readl(timer_base + MESON_ISA_TIMER_MUX);
+ val &= ~MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK;
+ val |= FIELD_PREP(MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK,
+ MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1US);
+ writel(val, timer_base + MESON_ISA_TIMER_MUX);
sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC);
- clocksource_mmio_init(timer_base + TIMER_ISA_VAL(CSD_ID), node->name,
+ clocksource_mmio_init(timer_base + MESON_ISA_TIMERE, node->name,
1000 * 1000, 300, 32, clocksource_mmio_readl_up);
/* Timer A base 1us */
- val &= ~TIMER_CED_INPUT_MASK;
- val |= TIMER_CED_UNIT_1US << TIMER_INPUT_BIT(CED_ID);
- writel(val, timer_base + TIMER_ISA_MUX);
+ val &= ~MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK;
+ val |= FIELD_PREP(MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK,
+ MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1US);
+ writel(val, timer_base + MESON_ISA_TIMER_MUX);
/* Stop the timer A */
- meson6_clkevt_time_stop(CED_ID);
+ meson6_clkevt_time_stop();
ret = setup_irq(irq, &meson6_timer_irq);
if (ret) {
--
2.19.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/2] clocksource: meson6_timer: implement ARM delay timer
2018-11-15 22:46 [PATCH v2 0/2] clocksource/meson6_timer: implement ARM delay timer Martin Blumenstingl
2018-11-15 22:46 ` [PATCH v2 1/2] clocksource: meson6_timer: use register names from the datasheet Martin Blumenstingl
@ 2018-11-15 22:46 ` Martin Blumenstingl
2018-11-18 1:27 ` [PATCH v2 0/2] clocksource/meson6_timer: " Daniel Lezcano
2 siblings, 0 replies; 7+ messages in thread
From: Martin Blumenstingl @ 2018-11-15 22:46 UTC (permalink / raw)
To: linux-amlogic, linux-kernel, tglx, daniel.lezcano
Cc: linux-arm-kernel, Martin Blumenstingl
Implement an ARM delay timer to be used for udelay(). This allows us to
skip the delay loop calibration at boot.
With this patch udelay() is now independent of CPU frequency changes.
This is a good thing on Meson8, Meson8b and Meson8m2 because changing
the CPU frequency requires running the CPU clock off the XTAL while
changing the PLL or it's dividers. After changing the CPU clocks we need
to wait a few usecs for the clock to become stable. So having an
udelay() implementation that doesn't depend on the CPU frequency is
beneficial.
Suggested-by: Jianxin Pan <jianxin.pan@amlogic.com>
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
drivers/clocksource/meson6_timer.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/drivers/clocksource/meson6_timer.c b/drivers/clocksource/meson6_timer.c
index 23c7638e2bb3..84bd9479c3f8 100644
--- a/drivers/clocksource/meson6_timer.c
+++ b/drivers/clocksource/meson6_timer.c
@@ -22,6 +22,10 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#ifdef CONFIG_ARM
+#include <linux/delay.h>
+#endif
+
#define MESON_ISA_TIMER_MUX 0x00
#define MESON_ISA_TIMER_MUX_TIMERD_EN BIT(19)
#define MESON_ISA_TIMER_MUX_TIMERC_EN BIT(18)
@@ -54,6 +58,18 @@
static void __iomem *timer_base;
+#ifdef CONFIG_ARM
+static unsigned long meson6_read_current_timer(void)
+{
+ return readl_relaxed(timer_base + MESON_ISA_TIMERE);
+}
+
+static struct delay_timer meson6_delay_timer = {
+ .read_current_timer = meson6_read_current_timer,
+ .freq = 1000 * 1000,
+};
+#endif
+
static u64 notrace meson6_timer_sched_read(void)
{
return (u64)readl(timer_base + MESON_ISA_TIMERE);
@@ -192,6 +208,12 @@ static int __init meson6_timer_init(struct device_node *node)
clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC,
1, 0xfffe);
+
+#ifdef CONFIG_ARM
+ /* Also use MESON_ISA_TIMERE for delays */
+ register_current_timer_delay(&meson6_delay_timer);
+#endif
+
return 0;
}
TIMER_OF_DECLARE(meson6, "amlogic,meson6-timer",
--
2.19.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 0/2] clocksource/meson6_timer: implement ARM delay timer
2018-11-15 22:46 [PATCH v2 0/2] clocksource/meson6_timer: implement ARM delay timer Martin Blumenstingl
2018-11-15 22:46 ` [PATCH v2 1/2] clocksource: meson6_timer: use register names from the datasheet Martin Blumenstingl
2018-11-15 22:46 ` [PATCH v2 2/2] clocksource: meson6_timer: implement ARM delay timer Martin Blumenstingl
@ 2018-11-18 1:27 ` Daniel Lezcano
2018-11-22 22:12 ` Martin Blumenstingl
2 siblings, 1 reply; 7+ messages in thread
From: Daniel Lezcano @ 2018-11-18 1:27 UTC (permalink / raw)
To: Martin Blumenstingl, linux-amlogic, linux-kernel, tglx; +Cc: linux-arm-kernel
On 15/11/2018 23:46, Martin Blumenstingl wrote:
> While trying to add support for the ARM TWD Timer and the ARM Global
> Timer on Meson8, Meson8b and Meson8m2 (ARM Cortex-A5 and Cortex-A9 SoCs)
> I did a review of the existing driver.
> Unfortunately I found it hard to review because the pre-processor
> #defines did not match the names from the public S805 datasheet. Thus
> patch #1 adjusts these. No functional changes here, this is just
> preparation work for patch #2.
>
> Using the ARM Global Timer (drivers/clocksource/arm_global_timer.c)
> would have given us a timer-based delay implementation (so udelay() and
> friends would use the timer instead of using a loop-based delay
> implementation). Unfortunately we can't use the ARM Global Timer yet
> because it's input clock is derived from the CPU clock (which can change
> once we enable CPU frequency scaling on these SoCs, for which I will be
> sending patches in the near future).
> Amlogic's 3.10 kernel uses Timer E as delay timer which (with the
> current configuration) has a resolution of 1us. So patch #2 uses
> register_current_timer_delay() to register Timer E as ARM delay timer
> (which will be especially useful as we have to use udelay() when
> changing the CPU clocks during DVFS).
>
>
> Changes since v1 at [0]:
> - convert the enums for the input clock (meson6_timera_input_clock and
> meson6_timere_input_clock) to simple #defines as these are register
> values and not something driver-internal. All other register values
> are #defines so it makes sense that these are #defines as well.
>
>
> [0] https://patchwork.kernel.org/cover/10658591/
>
>
> Martin Blumenstingl (2):
> clocksource: meson6_timer: use register names from the datasheet
> clocksource: meson6_timer: implement ARM delay timer
>
> drivers/clocksource/meson6_timer.c | 128 +++++++++++++++++++----------
> 1 file changed, 85 insertions(+), 43 deletions(-)
Both applied, thanks!
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
^ permalink raw reply [flat|nested] 7+ messages in thread