summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2022-10-04 11:22:43 +0200
committerThomas Gleixner <tglx@linutronix.de>2022-10-04 11:22:43 +0200
commit6cb5ce13357de06d376d300778eee2009f53d8cb (patch)
treeb12c8c4129ae8e062980a969a4114fad774ae0f9
parentcceeeb6a6d02e7b9a74ddd27a3225013b34174aa (diff)
parentaf246cc6d0ed11318223606128bb0b09866c4c08 (diff)
downloadlinux-6cb5ce13357de06d376d300778eee2009f53d8cb.tar.gz
linux-6cb5ce13357de06d376d300778eee2009f53d8cb.tar.bz2
linux-6cb5ce13357de06d376d300778eee2009f53d8cb.zip
Merge tag 'timers-v6.1-rc1' of https://git.linaro.org/people/daniel.lezcano/linux into timers/core
Pull clocksource/event updates from Daniel Lezcano: - Added DT bindings for Mediatek MT8188 (Johnson Wang) - Added DT bindings for Renesas r8a779f0 (Wolfram Sang) - Added support for RZ/V2L SoC (Lad Prabhakar) - Rename TIMER_IRQ_EN to TIMER_IRQ_CLEAR to prevent confusion on sun4i (Victor Hassan) - Added support for Exynos ARTPEC-8 MCT, including DT bindings (Vincent Whitchurch) - Fixed handling of ARM erratum 858921 on the ARM Arch timer (Kunkun Jiang) - Added missing call platform_device_put() in the error path on ther GXP timer (Lin Yujun) - Cleaned the timer TI DM driver by self-encapsulating the code, dropping dead code and simplifying some functions (Tony Lindgren) - Added a DT property to tell the driver the clock is no longer divided on recent NXP hardware (Peng Fan) - Fixed the CNTPCT_LO and CNTVCT_LO values in the ARM arch timer (Yang Guo) Link: https://lore.kernel.org/r/b28ac4b0-5745-b3a9-b7e7-cc86dcb1b023@linaro.org
-rw-r--r--Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt1
-rw-r--r--Documentation/devicetree/bindings/timer/nxp,sysctr-timer.yaml4
-rw-r--r--Documentation/devicetree/bindings/timer/renesas,tmu.yaml1
-rw-r--r--Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml26
-rw-r--r--drivers/clocksource/Kconfig2
-rw-r--r--drivers/clocksource/arm_arch_timer.c6
-rw-r--r--drivers/clocksource/exynos_mct.c83
-rw-r--r--drivers/clocksource/renesas-ostm.c2
-rw-r--r--drivers/clocksource/timer-gxp.c7
-rw-r--r--drivers/clocksource/timer-imx-sysctr.c6
-rw-r--r--drivers/clocksource/timer-sun4i.c3
-rw-r--r--drivers/clocksource/timer-ti-dm.c681
-rw-r--r--include/clocksource/timer-ti-dm.h112
13 files changed, 553 insertions, 381 deletions
diff --git a/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt b/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt
index f1c848af91d3..8bbb6e94508b 100644
--- a/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt
+++ b/Documentation/devicetree/bindings/timer/mediatek,mtk-timer.txt
@@ -25,6 +25,7 @@ Required properties:
For those SoCs that use SYST
* "mediatek,mt8183-timer" for MT8183 compatible timers (SYST)
* "mediatek,mt8186-timer" for MT8186 compatible timers (SYST)
+ * "mediatek,mt8188-timer" for MT8188 compatible timers (SYST)
* "mediatek,mt8192-timer" for MT8192 compatible timers (SYST)
* "mediatek,mt8195-timer" for MT8195 compatible timers (SYST)
* "mediatek,mt7629-timer" for MT7629 compatible timers (SYST)
diff --git a/Documentation/devicetree/bindings/timer/nxp,sysctr-timer.yaml b/Documentation/devicetree/bindings/timer/nxp,sysctr-timer.yaml
index 830211c55b4a..2b9653dafab8 100644
--- a/Documentation/devicetree/bindings/timer/nxp,sysctr-timer.yaml
+++ b/Documentation/devicetree/bindings/timer/nxp,sysctr-timer.yaml
@@ -32,6 +32,10 @@ properties:
clock-names:
const: per
+ nxp,no-divider:
+ description: if present, means there is no internal base clk divider.
+ type: boolean
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/timer/renesas,tmu.yaml b/Documentation/devicetree/bindings/timer/renesas,tmu.yaml
index c57169118b68..60f4c059bcff 100644
--- a/Documentation/devicetree/bindings/timer/renesas,tmu.yaml
+++ b/Documentation/devicetree/bindings/timer/renesas,tmu.yaml
@@ -37,6 +37,7 @@ properties:
- renesas,tmu-r8a77990 # R-Car E3
- renesas,tmu-r8a77995 # R-Car D3
- renesas,tmu-r8a779a0 # R-Car V3U
+ - renesas,tmu-r8a779f0 # R-Car S4-8
- const: renesas,tmu
reg:
diff --git a/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml b/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml
index 9c81d00b12e0..829bd2227f7c 100644
--- a/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml
+++ b/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml
@@ -25,6 +25,7 @@ properties:
- samsung,exynos4412-mct
- items:
- enum:
+ - axis,artpec8-mct
- samsung,exynos3250-mct
- samsung,exynos5250-mct
- samsung,exynos5260-mct
@@ -45,6 +46,19 @@ properties:
reg:
maxItems: 1
+ samsung,frc-shared:
+ type: boolean
+ description: |
+ Indicates that the hardware requires that this processor share the
+ free-running counter with a different (main) processor.
+
+ samsung,local-timers:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 16
+ description: |
+ List of indices of local timers usable from this processor.
+
interrupts:
description: |
Interrupts should be put in specific order. This is, the local timer
@@ -75,6 +89,17 @@ required:
allOf:
- if:
+ not:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - axis,artpec8-mct
+ then:
+ properties:
+ samsung,local-timers: false
+ samsung,frc-shared: false
+ - if:
properties:
compatible:
contains:
@@ -101,6 +126,7 @@ allOf:
compatible:
contains:
enum:
+ - axis,artpec8-mct
- samsung,exynos5260-mct
- samsung,exynos5420-mct
- samsung,exynos5433-mct
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 4f2bb7315b67..4469e7f555e9 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -434,7 +434,7 @@ config ATMEL_TCB_CLKSRC
config CLKSRC_EXYNOS_MCT
bool "Exynos multi core timer driver" if COMPILE_TEST
depends on ARM || ARM64
- depends on ARCH_EXYNOS || COMPILE_TEST
+ depends on ARCH_ARTPEC || ARCH_EXYNOS || COMPILE_TEST
help
Support for Multi Core Timer controller on Exynos SoCs.
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 9ab8221ee3c6..a7ff77550e17 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -44,8 +44,8 @@
#define CNTACR_RWVT BIT(4)
#define CNTACR_RWPT BIT(5)
-#define CNTVCT_LO 0x00
-#define CNTPCT_LO 0x08
+#define CNTPCT_LO 0x00
+#define CNTVCT_LO 0x08
#define CNTFRQ 0x10
#define CNTP_CVAL_LO 0x20
#define CNTP_CTL 0x2c
@@ -473,6 +473,8 @@ static const struct arch_timer_erratum_workaround ool_workarounds[] = {
.desc = "ARM erratum 858921",
.read_cntpct_el0 = arm64_858921_read_cntpct_el0,
.read_cntvct_el0 = arm64_858921_read_cntvct_el0,
+ .set_next_event_phys = erratum_set_next_event_phys,
+ .set_next_event_virt = erratum_set_next_event_virt,
},
#endif
#ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index f29c812b70c9..bfd60093ee1c 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -33,7 +33,7 @@
#define EXYNOS4_MCT_G_INT_ENB EXYNOS4_MCTREG(0x248)
#define EXYNOS4_MCT_G_WSTAT EXYNOS4_MCTREG(0x24C)
#define _EXYNOS4_MCT_L_BASE EXYNOS4_MCTREG(0x300)
-#define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * x))
+#define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * (x)))
#define EXYNOS4_MCT_L_MASK (0xffffff00)
#define MCT_L_TCNTB_OFFSET (0x00)
@@ -66,6 +66,8 @@
#define MCT_L0_IRQ 4
/* Max number of IRQ as per DT binding document */
#define MCT_NR_IRQS 20
+/* Max number of local timers */
+#define MCT_NR_LOCAL (MCT_NR_IRQS - MCT_L0_IRQ)
enum {
MCT_INT_SPI,
@@ -233,9 +235,16 @@ static cycles_t exynos4_read_current_timer(void)
}
#endif
-static int __init exynos4_clocksource_init(void)
+static int __init exynos4_clocksource_init(bool frc_shared)
{
- exynos4_mct_frc_start();
+ /*
+ * When the frc is shared, the main processer should have already
+ * turned it on and we shouldn't be writing to TCON.
+ */
+ if (frc_shared)
+ mct_frc.resume = NULL;
+ else
+ exynos4_mct_frc_start();
#if defined(CONFIG_ARM)
exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer;
@@ -449,7 +458,6 @@ static int exynos4_mct_starting_cpu(unsigned int cpu)
per_cpu_ptr(&percpu_mct_tick, cpu);
struct clock_event_device *evt = &mevt->evt;
- mevt->base = EXYNOS4_MCT_L_BASE(cpu);
snprintf(mevt->name, sizeof(mevt->name), "mct_tick%d", cpu);
evt->name = mevt->name;
@@ -520,8 +528,17 @@ static int __init exynos4_timer_resources(struct device_node *np)
return 0;
}
+/**
+ * exynos4_timer_interrupts - initialize MCT interrupts
+ * @np: device node for MCT
+ * @int_type: interrupt type, MCT_INT_PPI or MCT_INT_SPI
+ * @local_idx: array mapping CPU numbers to local timer indices
+ * @nr_local: size of @local_idx array
+ */
static int __init exynos4_timer_interrupts(struct device_node *np,
- unsigned int int_type)
+ unsigned int int_type,
+ const u32 *local_idx,
+ size_t nr_local)
{
int nr_irqs, i, err, cpu;
@@ -554,13 +571,21 @@ static int __init exynos4_timer_interrupts(struct device_node *np,
} else {
for_each_possible_cpu(cpu) {
int mct_irq;
+ unsigned int irq_idx;
struct mct_clock_event_device *pcpu_mevt =
per_cpu_ptr(&percpu_mct_tick, cpu);
+ if (cpu >= nr_local) {
+ err = -EINVAL;
+ goto out_irq;
+ }
+
+ irq_idx = MCT_L0_IRQ + local_idx[cpu];
+
pcpu_mevt->evt.irq = -1;
- if (MCT_L0_IRQ + cpu >= ARRAY_SIZE(mct_irqs))
+ if (irq_idx >= ARRAY_SIZE(mct_irqs))
break;
- mct_irq = mct_irqs[MCT_L0_IRQ + cpu];
+ mct_irq = mct_irqs[irq_idx];
irq_set_status_flags(mct_irq, IRQ_NOAUTOEN);
if (request_irq(mct_irq,
@@ -576,6 +601,17 @@ static int __init exynos4_timer_interrupts(struct device_node *np,
}
}
+ for_each_possible_cpu(cpu) {
+ struct mct_clock_event_device *mevt = per_cpu_ptr(&percpu_mct_tick, cpu);
+
+ if (cpu >= nr_local) {
+ err = -EINVAL;
+ goto out_irq;
+ }
+
+ mevt->base = EXYNOS4_MCT_L_BASE(local_idx[cpu]);
+ }
+
/* Install hotplug callbacks which configure the timer on this CPU */
err = cpuhp_setup_state(CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING,
"clockevents/exynos4/mct_timer:starting",
@@ -605,20 +641,49 @@ out_irq:
static int __init mct_init_dt(struct device_node *np, unsigned int int_type)
{
+ bool frc_shared = of_property_read_bool(np, "samsung,frc-shared");
+ u32 local_idx[MCT_NR_LOCAL] = {0};
+ int nr_local;
int ret;
+ nr_local = of_property_count_u32_elems(np, "samsung,local-timers");
+ if (nr_local == 0)
+ return -EINVAL;
+ if (nr_local > 0) {
+ if (nr_local > ARRAY_SIZE(local_idx))
+ return -EINVAL;
+
+ ret = of_property_read_u32_array(np, "samsung,local-timers",
+ local_idx, nr_local);
+ if (ret)
+ return ret;
+ } else {
+ int i;
+
+ nr_local = ARRAY_SIZE(local_idx);
+ for (i = 0; i < nr_local; i++)
+ local_idx[i] = i;
+ }
+
ret = exynos4_timer_resources(np);
if (ret)
return ret;
- ret = exynos4_timer_interrupts(np, int_type);
+ ret = exynos4_timer_interrupts(np, int_type, local_idx, nr_local);
if (ret)
return ret;
- ret = exynos4_clocksource_init();
+ ret = exynos4_clocksource_init(frc_shared);
if (ret)
return ret;
+ /*
+ * When the FRC is shared with a main processor, this secondary
+ * processor cannot use the global comparator.
+ */
+ if (frc_shared)
+ return ret;
+
return exynos4_clockevent_init();
}
diff --git a/drivers/clocksource/renesas-ostm.c b/drivers/clocksource/renesas-ostm.c
index 21d1392637b8..8da972dc1713 100644
--- a/drivers/clocksource/renesas-ostm.c
+++ b/drivers/clocksource/renesas-ostm.c
@@ -224,7 +224,7 @@ err_free:
TIMER_OF_DECLARE(ostm, "renesas,ostm", ostm_init);
-#ifdef CONFIG_ARCH_R9A07G044
+#ifdef CONFIG_ARCH_RZG2L
static int __init ostm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
diff --git a/drivers/clocksource/timer-gxp.c b/drivers/clocksource/timer-gxp.c
index 8b38b3212388..fe4fa8d7b3f1 100644
--- a/drivers/clocksource/timer-gxp.c
+++ b/drivers/clocksource/timer-gxp.c
@@ -171,6 +171,7 @@ static int gxp_timer_probe(struct platform_device *pdev)
{
struct platform_device *gxp_watchdog_device;
struct device *dev = &pdev->dev;
+ int ret;
if (!gxp_timer) {
pr_err("Gxp Timer not initialized, cannot create watchdog");
@@ -187,7 +188,11 @@ static int gxp_timer_probe(struct platform_device *pdev)
gxp_watchdog_device->dev.platform_data = gxp_timer->counter;
gxp_watchdog_device->dev.parent = dev;
- return platform_device_add(gxp_watchdog_device);
+ ret = platform_device_add(gxp_watchdog_device);
+ if (ret)
+ platform_device_put(gxp_watchdog_device);
+
+ return ret;
}
static const struct of_device_id gxp_timer_of_match[] = {
diff --git a/drivers/clocksource/timer-imx-sysctr.c b/drivers/clocksource/timer-imx-sysctr.c
index 523e37662a6e..5a7a951c4efc 100644
--- a/drivers/clocksource/timer-imx-sysctr.c
+++ b/drivers/clocksource/timer-imx-sysctr.c
@@ -134,8 +134,10 @@ static int __init sysctr_timer_init(struct device_node *np)
if (ret)
return ret;
- /* system counter clock is divided by 3 internally */
- to_sysctr.of_clk.rate /= SYS_CTR_CLK_DIV;
+ if (!of_property_read_bool(np, "nxp,no-divider")) {
+ /* system counter clock is divided by 3 internally */
+ to_sysctr.of_clk.rate /= SYS_CTR_CLK_DIV;
+ }
sys_ctr_base = timer_of_base(&to_sysctr);
cmpcr = readl(sys_ctr_base + CMPCR);
diff --git a/drivers/clocksource/timer-sun4i.c b/drivers/clocksource/timer-sun4i.c
index 94dc6e42e983..e5a70aa1deb4 100644
--- a/drivers/clocksource/timer-sun4i.c
+++ b/drivers/clocksource/timer-sun4i.c
@@ -26,6 +26,7 @@
#define TIMER_IRQ_EN_REG 0x00
#define TIMER_IRQ_EN(val) BIT(val)
#define TIMER_IRQ_ST_REG 0x04
+#define TIMER_IRQ_CLEAR(val) BIT(val)
#define TIMER_CTL_REG(val) (0x10 * val + 0x10)
#define TIMER_CTL_ENABLE BIT(0)
#define TIMER_CTL_RELOAD BIT(1)
@@ -123,7 +124,7 @@ static int sun4i_clkevt_next_event(unsigned long evt,
static void sun4i_timer_clear_interrupt(void __iomem *base)
{
- writel(TIMER_IRQ_EN(0), base + TIMER_IRQ_ST_REG);
+ writel(TIMER_IRQ_CLEAR(0), base + TIMER_IRQ_ST_REG);
}
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c
index 469f7c91564b..cad29ded3a48 100644
--- a/drivers/clocksource/timer-ti-dm.c
+++ b/drivers/clocksource/timer-ti-dm.c
@@ -33,6 +33,116 @@
#include <clocksource/timer-ti-dm.h>
+/*
+ * timer errata flags
+ *
+ * Errata i103/i767 impacts all OMAP3/4/5 devices including AM33xx. This
+ * errata prevents us from using posted mode on these devices, unless the
+ * timer counter register is never read. For more details please refer to
+ * the OMAP3/4/5 errata documents.
+ */
+#define OMAP_TIMER_ERRATA_I103_I767 0x80000000
+
+/* posted mode types */
+#define OMAP_TIMER_NONPOSTED 0x00
+#define OMAP_TIMER_POSTED 0x01
+
+/* register offsets with the write pending bit encoded */
+#define WPSHIFT 16
+
+#define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_CTRL_REG (_OMAP_TIMER_CTRL_OFFSET \
+ | (WP_TCLR << WPSHIFT))
+
+#define OMAP_TIMER_COUNTER_REG (_OMAP_TIMER_COUNTER_OFFSET \
+ | (WP_TCRR << WPSHIFT))
+
+#define OMAP_TIMER_LOAD_REG (_OMAP_TIMER_LOAD_OFFSET \
+ | (WP_TLDR << WPSHIFT))
+
+#define OMAP_TIMER_TRIGGER_REG (_OMAP_TIMER_TRIGGER_OFFSET \
+ | (WP_TTGR << WPSHIFT))
+
+#define OMAP_TIMER_WRITE_PEND_REG (_OMAP_TIMER_WRITE_PEND_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_MATCH_REG (_OMAP_TIMER_MATCH_OFFSET \
+ | (WP_TMAR << WPSHIFT))
+
+#define OMAP_TIMER_CAPTURE_REG (_OMAP_TIMER_CAPTURE_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_IF_CTRL_REG (_OMAP_TIMER_IF_CTRL_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_CAPTURE2_REG (_OMAP_TIMER_CAPTURE2_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_TICK_POS_REG (_OMAP_TIMER_TICK_POS_OFFSET \
+ | (WP_TPIR << WPSHIFT))
+
+#define OMAP_TIMER_TICK_NEG_REG (_OMAP_TIMER_TICK_NEG_OFFSET \
+ | (WP_TNIR << WPSHIFT))
+
+#define OMAP_TIMER_TICK_COUNT_REG (_OMAP_TIMER_TICK_COUNT_OFFSET \
+ | (WP_TCVR << WPSHIFT))
+
+#define OMAP_TIMER_TICK_INT_MASK_SET_REG \
+ (_OMAP_TIMER_TICK_INT_MASK_SET_OFFSET | (WP_TOCR << WPSHIFT))
+
+#define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \
+ (_OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | (WP_TOWR << WPSHIFT))
+
+struct timer_regs {
+ u32 ocp_cfg;
+ u32 tidr;
+ u32 tier;
+ u32 twer;
+ u32 tclr;
+ u32 tcrr;
+ u32 tldr;
+ u32 ttrg;
+ u32 twps;
+ u32 tmar;
+ u32 tcar1;
+ u32 tsicr;
+ u32 tcar2;
+ u32 tpir;
+ u32 tnir;
+ u32 tcvr;
+ u32 tocr;
+ u32 towr;
+};
+
+struct dmtimer {
+ struct omap_dm_timer cookie;
+ int id;
+ int irq;
+ struct clk *fclk;
+
+ void __iomem *io_base;
+ int irq_stat; /* TISR/IRQSTATUS interrupt status */
+ int irq_ena; /* irq enable */
+ int irq_dis; /* irq disable, only on v2 ip */
+ void __iomem *pend; /* write pending */
+ void __iomem *func_base; /* function register base */
+
+ atomic_t enabled;
+ unsigned long rate;
+ unsigned reserved:1;
+ unsigned posted:1;
+ unsigned omap1:1;
+ struct timer_regs context;
+ int revision;
+ u32 capability;
+ u32 errata;
+ struct platform_device *pdev;
+ struct list_head node;
+ struct notifier_block nb;
+};
+
static u32 omap_reserved_systimers;
static LIST_HEAD(omap_timer_list);
static DEFINE_SPINLOCK(dm_timer_lock);
@@ -44,27 +154,56 @@ enum {
REQUEST_BY_NODE,
};
-static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg,
- int posted)
+/**
+ * dmtimer_read - read timer registers in posted and non-posted mode
+ * @timer: timer pointer over which read operation to perform
+ * @reg: lowest byte holds the register offset
+ *
+ * The posted mode bit is encoded in reg. Note that in posted mode, write
+ * pending bit must be checked. Otherwise a read of a non completed write
+ * will produce an error.
+ */
+static inline u32 dmtimer_read(struct dmtimer *timer, u32 reg)
{
- if (posted)
- while (readl_relaxed(timer->pend) & (reg >> WPSHIFT))
+ u16 wp, offset;
+
+ wp = reg >> WPSHIFT;
+ offset = reg & 0xff;
+
+ /* Wait for a possible write pending bit in posted mode */
+ if (wp && timer->posted)
+ while (readl_relaxed(timer->pend) & wp)
cpu_relax();
- return readl_relaxed(timer->func_base + (reg & 0xff));
+ return readl_relaxed(timer->func_base + offset);
}
-static inline void __omap_dm_timer_write(struct omap_dm_timer *timer,
- u32 reg, u32 val, int posted)
+/**
+ * dmtimer_write - write timer registers in posted and non-posted mode
+ * @timer: timer pointer over which write operation is to perform
+ * @reg: lowest byte holds the register offset
+ * @value: data to write into the register
+ *
+ * The posted mode bit is encoded in reg. Note that in posted mode, the write
+ * pending bit must be checked. Otherwise a write on a register which has a
+ * pending write will be lost.
+ */
+static inline void dmtimer_write(struct dmtimer *timer, u32 reg, u32 val)
{
- if (posted)
- while (readl_relaxed(timer->pend) & (reg >> WPSHIFT))
+ u16 wp, offset;
+
+ wp = reg >> WPSHIFT;
+ offset = reg & 0xff;
+
+ /* Wait for a possible write pending bit in posted mode */
+ if (wp && timer->posted)
+ while (readl_relaxed(timer->pend) & wp)
cpu_relax();
- writel_relaxed(val, timer->func_base + (reg & 0xff));
+ writel_relaxed(val, timer->func_base + offset);
}
-static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
+static inline void __omap_dm_timer_init_regs(struct dmtimer *timer)
{
u32 tidr;
@@ -72,16 +211,16 @@ static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
tidr = readl_relaxed(timer->io_base);
if (!(tidr >> 16)) {
timer->revision = 1;
- timer->irq_stat = timer->io_base + OMAP_TIMER_V1_STAT_OFFSET;
- timer->irq_ena = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET;
- timer->irq_dis = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET;
+ timer->irq_stat = OMAP_TIMER_V1_STAT_OFFSET;
+ timer->irq_ena = OMAP_TIMER_V1_INT_EN_OFFSET;
+ timer->irq_dis = OMAP_TIMER_V1_INT_EN_OFFSET;
timer->pend = timer->io_base + _OMAP_TIMER_WRITE_PEND_OFFSET;
timer->func_base = timer->io_base;
} else {
timer->revision = 2;
- timer->irq_stat = timer->io_base + OMAP_TIMER_V2_IRQSTATUS;
- timer->irq_ena = timer->io_base + OMAP_TIMER_V2_IRQENABLE_SET;
- timer->irq_dis = timer->io_base + OMAP_TIMER_V2_IRQENABLE_CLR;
+ timer->irq_stat = OMAP_TIMER_V2_IRQSTATUS - OMAP_TIMER_V2_FUNC_OFFSET;
+ timer->irq_ena = OMAP_TIMER_V2_IRQENABLE_SET - OMAP_TIMER_V2_FUNC_OFFSET;
+ timer->irq_dis = OMAP_TIMER_V2_IRQENABLE_CLR - OMAP_TIMER_V2_FUNC_OFFSET;
timer->pend = timer->io_base +
_OMAP_TIMER_WRITE_PEND_OFFSET +
OMAP_TIMER_V2_FUNC_OFFSET;
@@ -99,35 +238,34 @@ static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
* complete. Enabling this feature can improve performance for writing to the
* timer registers.
*/
-static inline void __omap_dm_timer_enable_posted(struct omap_dm_timer *timer)
+static inline void __omap_dm_timer_enable_posted(struct dmtimer *timer)
{
if (timer->posted)
return;
if (timer->errata & OMAP_TIMER_ERRATA_I103_I767) {
timer->posted = OMAP_TIMER_NONPOSTED;
- __omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0, 0);
+ dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0);
return;
}
- __omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG,
- OMAP_TIMER_CTRL_POSTED, 0);
+ dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, OMAP_TIMER_CTRL_POSTED);
timer->context.tsicr = OMAP_TIMER_CTRL_POSTED;
timer->posted = OMAP_TIMER_POSTED;
}
-static inline void __omap_dm_timer_stop(struct omap_dm_timer *timer,
- int posted, unsigned long rate)
+static inline void __omap_dm_timer_stop(struct dmtimer *timer,
+ unsigned long rate)
{
u32 l;
- l = __omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
+ l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
if (l & OMAP_TIMER_CTRL_ST) {
l &= ~0x1;
- __omap_dm_timer_write(timer, OMAP_TIMER_CTRL_REG, l, posted);
+ dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l);
#ifdef CONFIG_ARCH_OMAP2PLUS
/* Readback to make sure write has completed */
- __omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
+ dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
/*
* Wait for functional clock period x 3.5 to make sure that
* timer is stopped
@@ -137,104 +275,59 @@ static inline void __omap_dm_timer_stop(struct omap_dm_timer *timer,
}
/* Ack possibly pending interrupt */
- writel_relaxed(OMAP_TIMER_INT_OVERFLOW, timer->irq_stat);
+ dmtimer_write(timer, timer->irq_stat, OMAP_TIMER_INT_OVERFLOW);
}
-static inline void __omap_dm_timer_int_enable(struct omap_dm_timer *timer,
- unsigned int value)
+static inline void __omap_dm_timer_int_enable(struct dmtimer *timer,
+ unsigned int value)
{
- writel_relaxed(value, timer->irq_ena);
- __omap_dm_timer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, value, 0);
+ dmtimer_write(timer, timer->irq_ena, value);
+ dmtimer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, value);
}
static inline unsigned int
-__omap_dm_timer_read_counter(struct omap_dm_timer *timer, int posted)
+__omap_dm_timer_read_counter(struct dmtimer *timer)
{
- return __omap_dm_timer_read(timer, OMAP_TIMER_COUNTER_REG, posted);
+ return dmtimer_read(timer, OMAP_TIMER_COUNTER_REG);
}
-static inline void __omap_dm_timer_write_status(struct omap_dm_timer *timer,
+static inline void __omap_dm_timer_write_status(struct dmtimer *timer,
unsigned int value)
{
- writel_relaxed(value, timer->irq_stat);
+ dmtimer_write(timer, timer->irq_stat, value);
}
-/**
- * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode
- * @timer: timer pointer over which read operation to perform
- * @reg: lowest byte holds the register offset
- *
- * The posted mode bit is encoded in reg. Note that in posted mode write
- * pending bit must be checked. Otherwise a read of a non completed write
- * will produce an error.
- */
-static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
+static void omap_timer_restore_context(struct dmtimer *timer)
{
- WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
- return __omap_dm_timer_read(timer, reg, timer->posted);
+ dmtimer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, timer->context.ocp_cfg);
+
+ dmtimer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, timer->context.twer);
+ dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, timer->context.tcrr);
+ dmtimer_write(timer, OMAP_TIMER_LOAD_REG, timer->context.tldr);
+ dmtimer_write(timer, OMAP_TIMER_MATCH_REG, timer->context.tmar);
+ dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, timer->context.tsicr);
+ dmtimer_write(timer, timer->irq_ena, timer->context.tier);
+ dmtimer_write(timer, OMAP_TIMER_CTRL_REG, timer->context.tclr);
}
-/**
- * omap_dm_timer_write_reg - write timer registers in posted and non-posted mode
- * @timer: timer pointer over which write operation is to perform
- * @reg: lowest byte holds the register offset
- * @value: data to write into the register
- *
- * The posted mode bit is encoded in reg. Note that in posted mode the write
- * pending bit must be checked. Otherwise a write on a register which has a
- * pending write will be lost.
- */
-static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
- u32 value)
+static void omap_timer_save_context(struct dmtimer *timer)
{
- WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
- __omap_dm_timer_write(timer, reg, value, timer->posted);
-}
-
-static void omap_timer_restore_context(struct omap_dm_timer *timer)
-{
- __omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET,
- timer->context.ocp_cfg, 0);
-
- omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG,
- timer->context.twer);
- omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG,
- timer->context.tcrr);
- omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG,
- timer->context.tldr);
- omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG,
- timer->context.tmar);
- omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG,
- timer->context.tsicr);
- writel_relaxed(timer->context.tier, timer->irq_ena);
- omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG,
- timer->context.tclr);
-}
-
-static void omap_timer_save_context(struct omap_dm_timer *timer)
-{
- timer->context.ocp_cfg =
- __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0);
-
- timer->context.tclr =
- omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- timer->context.twer =
- omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG);
- timer->context.tldr =
- omap_dm_timer_read_reg(timer, OMAP_TIMER_LOAD_REG);
- timer->context.tmar =
- omap_dm_timer_read_reg(timer, OMAP_TIMER_MATCH_REG);
- timer->context.tier = readl_relaxed(timer->irq_ena);
- timer->context.tsicr =
- omap_dm_timer_read_reg(timer, OMAP_TIMER_IF_CTRL_REG);
+ timer->context.ocp_cfg = dmtimer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET);
+
+ timer->context.tclr = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
+ timer->context.twer = dmtimer_read(timer, OMAP_TIMER_WAKEUP_EN_REG);
+ timer->context.tldr = dmtimer_read(timer, OMAP_TIMER_LOAD_REG);
+ timer->context.tmar = dmtimer_read(timer, OMAP_TIMER_MATCH_REG);
+ timer->context.tier = dmtimer_read(timer, timer->irq_ena);
+ timer->context.tsicr = dmtimer_read(timer, OMAP_TIMER_IF_CTRL_REG);
}
static int omap_timer_context_notifier(struct notifier_block *nb,
unsigned long cmd, void *v)
{
- struct omap_dm_timer *timer;
+ struct dmtimer *timer;
- timer = container_of(nb, struct omap_dm_timer, nb);
+ timer = container_of(nb, struct dmtimer, nb);
switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
@@ -256,18 +349,17 @@ static int omap_timer_context_notifier(struct notifier_block *nb,
return NOTIFY_OK;
}
-static int omap_dm_timer_reset(struct omap_dm_timer *timer)
+static int omap_dm_timer_reset(struct dmtimer *timer)
{
u32 l, timeout = 100000;
if (timer->revision != 1)
return -EINVAL;
- omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
+ dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
do {
- l = __omap_dm_timer_read(timer,
- OMAP_TIMER_V1_SYS_STAT_OFFSET, 0);
+ l = dmtimer_read(timer, OMAP_TIMER_V1_SYS_STAT_OFFSET);
} while (!l && timeout--);
if (!timeout) {
@@ -276,22 +368,38 @@ static int omap_dm_timer_reset(struct omap_dm_timer *timer)
}
/* Configure timer for smart-idle mode */
- l = __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0);
+ l = dmtimer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET);
l |= 0x2 << 0x3;
- __omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l, 0);
+ dmtimer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l);
timer->posted = 0;
return 0;
}
-static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
+/*
+ * Functions exposed to PWM and remoteproc drivers via platform_data.
+ * Do not use these in the driver, these will get deprecated and will
+ * will be replaced by Linux generic framework functions such as
+ * chained interrupts and clock framework.
+ */
+static struct dmtimer *to_dmtimer(struct omap_dm_timer *cookie)
+{
+ if (!cookie)
+ return NULL;
+
+ return container_of(cookie, struct dmtimer, cookie);
+}
+
+static int omap_dm_timer_set_source(struct omap_dm_timer *cookie, int source)
{
int ret;
const char *parent_name;
struct clk *parent;
struct dmtimer_platform_data *pdata;
+ struct dmtimer *timer;
+ timer = to_dmtimer(cookie);
if (unlikely(!timer) || IS_ERR(timer->fclk))
return -EINVAL;
@@ -316,7 +424,7 @@ static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
* use the clock framework to set the parent clock. To be removed
* once OMAP1 migrated to using clock framework for dmtimers
*/
- if (pdata && pdata->set_timer_src)
+ if (timer->omap1 && pdata && pdata->set_timer_src)
return pdata->set_timer_src(timer->pdev, source);
#if defined(CONFIG_COMMON_CLK)
@@ -341,44 +449,44 @@ static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
return ret;
}
-static void omap_dm_timer_enable(struct omap_dm_timer *timer)
+static void omap_dm_timer_enable(struct omap_dm_timer *cookie)
{
- pm_runtime_get_sync(&timer->pdev->dev);
+ struct dmtimer *timer = to_dmtimer(cookie);
+ struct device *dev = &timer->pdev->dev;
+ int rc;
+
+ rc = pm_runtime_resume_and_get(dev);
+ if (rc)
+ dev_err(dev, "could not enable timer\n");
}
-static void omap_dm_timer_disable(struct omap_dm_timer *timer)
+static void omap_dm_timer_disable(struct omap_dm_timer *cookie)
{
- pm_runtime_put_sync(&timer->pdev->dev);
+ struct dmtimer *timer = to_dmtimer(cookie);
+ struct device *dev = &timer->pdev->dev;
+