summaryrefslogtreecommitdiff
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorVincent Whitchurch <vincent.whitchurch@axis.com>2022-06-09 13:27:37 +0200
committerDaniel Lezcano <daniel.lezcano@linaro.org>2022-09-20 10:49:45 +0200
commit47dbe4eb9822208be2b7ec901c7e0c15536f9c92 (patch)
tree2067b83042c0d1c42c9ba906570acab6ac858f63 /drivers/clocksource
parente8550f0e7bde9bd31697e3c534d386f7f3b5787b (diff)
clocksource/drivers/exynos_mct: Support local-timers property
If the device tree indicates that the hardware requires that the processor only use certain local timers, respect that. Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Link: https://lore.kernel.org/r/20220609112738.359385-4-vincent.whitchurch@axis.com Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/exynos_mct.c62
1 files changed, 56 insertions, 6 deletions
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index 12023831dedf..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,
@@ -456,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;
@@ -527,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;
@@ -561,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,
@@ -583,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",
@@ -613,13 +642,34 @@ 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;