MIPS: Loongson-3: Add CSR IPI support
authorHuacai Chen <chenhc@lemote.com>
Sat, 21 Sep 2019 13:50:28 +0000 (21:50 +0800)
committerPaul Burton <paul.burton@mips.com>
Mon, 7 Oct 2019 16:45:25 +0000 (09:45 -0700)
CSR IPI and legacy MMIO use the same infrastructure, but CSR IPI is
faster than legacy MMIO IPI. This patch enable CSR IPI if possible
(except for MailBox, because CSR IPI is too complicated for MailBox).

Signed-off-by: Huacai Chen <chenhc@lemote.com>
Signed-off-by: Paul Burton <paul.burton@mips.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: James Hogan <jhogan@kernel.org>
Cc: linux-mips@linux-mips.org
Cc: linux-mips@vger.kernel.org
Cc: Fuxin Zhang <zhangfx@lemote.com>
Cc: Zhangjin Wu <wuzhangjin@gmail.com>
Cc: Huacai Chen <chenhuacai@gmail.com>
arch/mips/loongson64/loongson-3/smp.c

index e999bb1..de8e074 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/tlbflush.h>
 #include <asm/cacheflush.h>
 #include <loongson.h>
+#include <loongson_regs.h>
 #include <workarounds.h>
 
 #include "smp.h"
@@ -48,6 +49,62 @@ static uint32_t core0_c0count[NR_CPUS];
                __wbflush();                    \
        } while (0)
 
+u32 (*ipi_read_clear)(int cpu);
+void (*ipi_write_action)(int cpu, u32 action);
+
+static u32 csr_ipi_read_clear(int cpu)
+{
+       u32 action;
+
+       /* Load the ipi register to figure out what we're supposed to do */
+       action = csr_readl(LOONGSON_CSR_IPI_STATUS);
+       /* Clear the ipi register to clear the interrupt */
+       csr_writel(action, LOONGSON_CSR_IPI_CLEAR);
+
+       return action;
+}
+
+static void csr_ipi_write_action(int cpu, u32 action)
+{
+       unsigned int irq = 0;
+
+       while ((irq = ffs(action))) {
+               uint32_t val = CSR_IPI_SEND_BLOCK;
+               val |= (irq - 1);
+               val |= (cpu << CSR_IPI_SEND_CPU_SHIFT);
+               csr_writel(val, LOONGSON_CSR_IPI_SEND);
+               action &= ~BIT(irq - 1);
+       }
+}
+
+static u32 legacy_ipi_read_clear(int cpu)
+{
+       u32 action;
+
+       /* Load the ipi register to figure out what we're supposed to do */
+       action = loongson3_ipi_read32(ipi_status0_regs[cpu_logical_map(cpu)]);
+       /* Clear the ipi register to clear the interrupt */
+       loongson3_ipi_write32(action, ipi_clear0_regs[cpu_logical_map(cpu)]);
+
+       return action;
+}
+
+static void legacy_ipi_write_action(int cpu, u32 action)
+{
+       loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu]);
+}
+
+static void csr_ipi_probe(void)
+{
+       if (cpu_has_csr() && csr_readl(LOONGSON_CSR_FEATURES) & LOONGSON_CSRF_IPI) {
+               ipi_read_clear = csr_ipi_read_clear;
+               ipi_write_action = csr_ipi_write_action;
+       } else {
+               ipi_read_clear = legacy_ipi_read_clear;
+               ipi_write_action = legacy_ipi_write_action;
+       }
+}
+
 static void ipi_set0_regs_init(void)
 {
        ipi_set0_regs[0] = (void *)
@@ -233,7 +290,7 @@ static void ipi_mailbox_buf_init(void)
  */
 static void loongson3_send_ipi_single(int cpu, unsigned int action)
 {
-       loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu_logical_map(cpu)]);
+       ipi_write_action(cpu_logical_map(cpu), (u32)action);
 }
 
 static void
@@ -242,14 +299,14 @@ loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
        unsigned int i;
 
        for_each_cpu(i, mask)
-               loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu_logical_map(i)]);
+               ipi_write_action(cpu_logical_map(i), (u32)action);
 }
 
 #define IPI_IRQ_OFFSET 6
 
 void loongson3_send_irq_by_ipi(int cpu, int irqs)
 {
-       loongson3_ipi_write32(irqs << IPI_IRQ_OFFSET, ipi_set0_regs[cpu_logical_map(cpu)]);
+       ipi_write_action(cpu_logical_map(cpu), irqs << IPI_IRQ_OFFSET);
 }
 
 void loongson3_ipi_interrupt(struct pt_regs *regs)
@@ -257,13 +314,9 @@ void loongson3_ipi_interrupt(struct pt_regs *regs)
        int i, cpu = smp_processor_id();
        unsigned int action, c0count, irqs;
 
-       /* Load the ipi register to figure out what we're supposed to do */
-       action = loongson3_ipi_read32(ipi_status0_regs[cpu_logical_map(cpu)]);
+       action = ipi_read_clear(cpu);
        irqs = action >> IPI_IRQ_OFFSET;
 
-       /* Clear the ipi register to clear the interrupt */
-       loongson3_ipi_write32((u32)action, ipi_clear0_regs[cpu_logical_map(cpu)]);
-
        if (action & SMP_RESCHEDULE_YOURSELF)
                scheduler_ipi();
 
@@ -372,6 +425,7 @@ static void __init loongson3_smp_setup(void)
                num++;
        }
 
+       csr_ipi_probe();
        ipi_set0_regs_init();
        ipi_clear0_regs_init();
        ipi_status0_regs_init();