clocksource/drivers/exynos_mct: Support local-timers property
authorVincent Whitchurch <vincent.whitchurch@axis.com>
Thu, 9 Jun 2022 11:27:37 +0000 (13:27 +0200)
committerDaniel Lezcano <daniel.lezcano@linaro.org>
Tue, 20 Sep 2022 08:49:45 +0000 (10:49 +0200)
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>
drivers/clocksource/exynos_mct.c

index 1202383..bfd6009 100644 (file)
@@ -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;