]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
arm: Add support for dynamic IPI allocation
authorMichal Simek <michal.simek@xilinx.com>
Mon, 7 Aug 2017 09:05:44 +0000 (11:05 +0200)
committerMichal Simek <michal.simek@xilinx.com>
Mon, 1 Apr 2019 10:47:57 +0000 (12:47 +0200)
Inter processor interrupts are fixed in current implementation and can't
be dynamically assign by drivers. In OpenAMP case on Zynq there is a need
to use IPIs for communication between Linux and RTOS.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
arch/arm/include/asm/hardirq.h
arch/arm/include/asm/smp.h
arch/arm/kernel/smp.c

index cba23eaa607215aaaab9342c6a661b06ebd07db8..9089de368da840e54517b3de13b2d9a208bbb23e 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/threads.h>
 #include <asm/irq.h>
 
-#define NR_IPI 7
+#define NR_IPI 16
 
 typedef struct {
        unsigned int __softirq_pending;
index 709a55989cb0641f7006f5a1765898aa9fb972c3..93a62a15e3c3c66daa5b65de9c16caf472592c58 100644 (file)
@@ -124,4 +124,7 @@ struct of_cpu_method {
  */
 extern void smp_set_ops(const struct smp_operations *);
 
+extern int set_ipi_handler(int ipinr, void *handler, char *desc);
+extern void clear_ipi_handler(int ipinr);
+
 #endif /* ifndef __ASM_ARM_SMP_H */
index 0978282d5fc27a7c4a5e6b0e274da8bfc4c14c8d..aac78f365696e55a528d03a2985d5cf816e92a20 100644 (file)
@@ -481,20 +481,59 @@ void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
                __smp_cross_call = fn;
 }
 
-static const char *ipi_types[NR_IPI] __tracepoint_string = {
-#define S(x,s) [x] = s
-       S(IPI_WAKEUP, "CPU wakeup interrupts"),
-       S(IPI_TIMER, "Timer broadcast interrupts"),
-       S(IPI_RESCHEDULE, "Rescheduling interrupts"),
-       S(IPI_CALL_FUNC, "Function call interrupts"),
-       S(IPI_CPU_STOP, "CPU stop interrupts"),
-       S(IPI_IRQ_WORK, "IRQ work interrupts"),
-       S(IPI_COMPLETION, "completion interrupts"),
+struct ipi {
+       const char *desc;
+       void (*handler)(void);
+};
+
+static void ipi_cpu_stop(void);
+static void ipi_complete(void);
+
+#define IPI_DESC_STRING_IPI_WAKEUP "CPU wakeup interrupts"
+#define IPI_DESC_STRING_IPI_TIMER "Timer broadcast interrupts"
+#define IPI_DESC_STRING_IPI_RESCHEDULE "Rescheduling interrupts"
+#define IPI_DESC_STRING_IPI_CALL_FUNC "Function call interrupts"
+#define IPI_DESC_STRING_IPI_CPU_STOP "CPU stop interrupts"
+#define IPI_DESC_STRING_IPI_IRQ_WORK "IRQ work interrupts"
+#define IPI_DESC_STRING_IPI_COMPLETION "completion interrupts"
+
+#define IPI_DESC_STR(x) IPI_DESC_STRING_ ## x
+
+static const char* ipi_desc_strings[] __tracepoint_string =
+               {
+                       [IPI_WAKEUP] = IPI_DESC_STR(IPI_WAKEUP),
+                       [IPI_TIMER] = IPI_DESC_STR(IPI_TIMER),
+                       [IPI_RESCHEDULE] = IPI_DESC_STR(IPI_RESCHEDULE),
+                       [IPI_CALL_FUNC] = IPI_DESC_STR(IPI_CALL_FUNC),
+                       [IPI_CPU_STOP] = IPI_DESC_STR(IPI_CPU_STOP),
+                       [IPI_IRQ_WORK] = IPI_DESC_STR(IPI_IRQ_WORK),
+                       [IPI_COMPLETION] = IPI_DESC_STR(IPI_COMPLETION),
+               };
+
+
+static void tick_receive_broadcast_local(void)
+{
+       tick_receive_broadcast();
+}
+
+static struct ipi ipi_types[NR_IPI] = {
+#define S(x, f)        [x].desc = IPI_DESC_STR(x), [x].handler = f
+       S(IPI_WAKEUP, NULL),
+#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
+       S(IPI_TIMER, tick_receive_broadcast_local),
+#endif
+       S(IPI_RESCHEDULE, scheduler_ipi),
+       S(IPI_CALL_FUNC, generic_smp_call_function_interrupt),
+       S(IPI_CPU_STOP, ipi_cpu_stop),
+#ifdef CONFIG_IRQ_WORK
+       S(IPI_IRQ_WORK, irq_work_run),
+#endif
+       S(IPI_COMPLETION, ipi_complete),
 };
 
 static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
 {
-       trace_ipi_raise_rcuidle(target, ipi_types[ipinr]);
+       trace_ipi_raise_rcuidle(target, ipi_desc_strings[ipinr]);
        __smp_cross_call(target, ipinr);
 }
 
@@ -503,13 +542,13 @@ void show_ipi_list(struct seq_file *p, int prec)
        unsigned int cpu, i;
 
        for (i = 0; i < NR_IPI; i++) {
-               seq_printf(p, "%*s%u: ", prec - 1, "IPI", i);
-
-               for_each_online_cpu(cpu)
-                       seq_printf(p, "%10u ",
-                                  __get_irq_stat(cpu, ipi_irqs[i]));
-
-               seq_printf(p, " %s\n", ipi_types[i]);
+               if (ipi_types[i].handler) {
+                       seq_printf(p, "%*s%u: ", prec - 1, "IPI", i);
+                       for_each_present_cpu(cpu)
+                               seq_printf(p, "%10u ",
+                                       __get_irq_stat(cpu, ipi_irqs[i]));
+                       seq_printf(p, " %s\n", ipi_types[i].desc);
+               }
        }
 }
 
@@ -559,8 +598,10 @@ static DEFINE_RAW_SPINLOCK(stop_lock);
 /*
  * ipi_cpu_stop - handle IPI from smp_send_stop()
  */
-static void ipi_cpu_stop(unsigned int cpu)
+static void ipi_cpu_stop(void)
 {
+       unsigned int cpu = smp_processor_id();
+
        if (system_state <= SYSTEM_RUNNING) {
                raw_spin_lock(&stop_lock);
                pr_crit("CPU%u: stopping\n", cpu);
@@ -585,8 +626,10 @@ int register_ipi_completion(struct completion *completion, int cpu)
        return IPI_COMPLETION;
 }
 
-static void ipi_complete(unsigned int cpu)
+static void ipi_complete(void)
 {
+       unsigned int cpu = smp_processor_id();
+
        complete(per_cpu(cpu_completion, cpu));
 }
 
@@ -603,71 +646,48 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
        unsigned int cpu = smp_processor_id();
        struct pt_regs *old_regs = set_irq_regs(regs);
 
-       if ((unsigned)ipinr < NR_IPI) {
-               trace_ipi_entry_rcuidle(ipi_types[ipinr]);
+       if (ipi_types[ipinr].handler) {
                __inc_irq_stat(cpu, ipi_irqs[ipinr]);
-       }
-
-       switch (ipinr) {
-       case IPI_WAKEUP:
-               break;
-
-#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
-       case IPI_TIMER:
-               irq_enter();
-               tick_receive_broadcast();
-               irq_exit();
-               break;
-#endif
-
-       case IPI_RESCHEDULE:
-               scheduler_ipi();
-               break;
-
-       case IPI_CALL_FUNC:
                irq_enter();
-               generic_smp_call_function_interrupt();
+               (*ipi_types[ipinr].handler)();
                irq_exit();
-               break;
+       } else
+               pr_debug("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
 
-       case IPI_CPU_STOP:
-               irq_enter();
-               ipi_cpu_stop(cpu);
-               irq_exit();
-               break;
+       set_irq_regs(old_regs);
+}
 
-#ifdef CONFIG_IRQ_WORK
      case IPI_IRQ_WORK:
-               irq_enter();
-               irq_work_run();
-               irq_exit();
-               break;
-#endif
+/*
* set_ipi_handler:
+ * Interface provided for a kernel module to specify an IPI handler function.
+ */
+int set_ipi_handler(int ipinr, void *handler, char *desc)
+{
+       unsigned int cpu = smp_processor_id();
 
-       case IPI_COMPLETION:
-               irq_enter();
-               ipi_complete(cpu);
-               irq_exit();
-               break;
+       if (ipi_types[ipinr].handler) {
+               pr_crit("CPU%u: IPI handler 0x%x already registered to %pf\n",
+                                       cpu, ipinr, ipi_types[ipinr].handler);
+               return -1;
+       }
 
-       case IPI_CPU_BACKTRACE:
-               printk_nmi_enter();
-               irq_enter();
-               nmi_cpu_backtrace(regs);
-               irq_exit();
-               printk_nmi_exit();
-               break;
+       ipi_types[ipinr].handler = handler;
+       ipi_types[ipinr].desc = desc;
 
-       default:
-               pr_crit("CPU%u: Unknown IPI message 0x%x\n",
-                       cpu, ipinr);
-               break;
-       }
+       return 0;
+}
+EXPORT_SYMBOL(set_ipi_handler);
 
-       if ((unsigned)ipinr < NR_IPI)
-               trace_ipi_exit_rcuidle(ipi_types[ipinr]);
-       set_irq_regs(old_regs);
+/*
+ * clear_ipi_handler:
+ * Interface provided for a kernel module to clear an IPI handler function.
+ */
+void clear_ipi_handler(int ipinr)
+{
+       ipi_types[ipinr].handler = NULL;
+       ipi_types[ipinr].desc = NULL;
 }
+EXPORT_SYMBOL(clear_ipi_handler);
 
 void smp_send_reschedule(int cpu)
 {