2020-02-14 18:06:33

by Zhou Yanjie

[permalink] [raw]
Subject: Introduce SMP support for CI20 (based on JZ4780) v4.

Introduce SMP support for MIPS Creator CI20, which is
based on Ingenic JZ4780 SoC.


2020-02-14 18:06:36

by Zhou Yanjie

[permalink] [raw]
Subject: [PATCH v4 1/6] MIPS: JZ4780: Introduce SMP support.

Forward port smp support from kernel 3.18.3 of CI20_linux
to upstream kernel 5.5.

Tested-by: H. Nikolaus Schaller <[email protected]>
Tested-by: Paul Boddie <[email protected]>
Signed-off-by: 周琰杰 (Zhou Yanjie) <[email protected]>
---

Notes:
v1->v2:
1.Remove unnecessary "plat_irq_dispatch(void)" in irq-ingenic.c.
2.Add a timeout check for "jz4780_boot_secondary()" to avoid a dead loop.
3.Replace hard code in smp.c with macro.

v2->v3:
1.Remove unnecessary "extern void (*r4k_blast_dcache)(void)" in smp.c.
2.Use "for_each_of_cpu_node" instead "for_each_compatible_node" in smp.c.
3.Use "of_cpu_node_to_id" instead "of_property_read_u32_index" in smp.c.
4.Move LCR related operations to jz4780-cgu.c.

v3->v4:
Rebase on top of kernel 5.6-rc1.

arch/mips/include/asm/mach-jz4740/jz4780-smp.h | 91 ++++++++
arch/mips/jz4740/Kconfig | 3 +
arch/mips/jz4740/Makefile | 5 +
arch/mips/jz4740/prom.c | 4 +
arch/mips/jz4740/smp-entry.S | 57 +++++
arch/mips/jz4740/smp.c | 286 +++++++++++++++++++++++++
arch/mips/kernel/idle.c | 14 +-
drivers/clk/ingenic/jz4780-cgu.c | 58 ++++-
8 files changed, 512 insertions(+), 6 deletions(-)
create mode 100644 arch/mips/include/asm/mach-jz4740/jz4780-smp.h
create mode 100644 arch/mips/jz4740/smp-entry.S
create mode 100644 arch/mips/jz4740/smp.c

diff --git a/arch/mips/include/asm/mach-jz4740/jz4780-smp.h b/arch/mips/include/asm/mach-jz4740/jz4780-smp.h
new file mode 100644
index 00000000..3f592ce
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/jz4780-smp.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2013, Paul Burton <[email protected]>
+ * JZ4780 SMP definitions
+ */
+
+#ifndef __MIPS_ASM_MACH_JZ4740_JZ4780_SMP_H__
+#define __MIPS_ASM_MACH_JZ4740_JZ4780_SMP_H__
+
+#define read_c0_corectrl() __read_32bit_c0_register($12, 2)
+#define write_c0_corectrl(val) __write_32bit_c0_register($12, 2, val)
+
+#define read_c0_corestatus() __read_32bit_c0_register($12, 3)
+#define write_c0_corestatus(val) __write_32bit_c0_register($12, 3, val)
+
+#define read_c0_reim() __read_32bit_c0_register($12, 4)
+#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val)
+
+#define read_c0_mailbox0() __read_32bit_c0_register($20, 0)
+#define write_c0_mailbox0(val) __write_32bit_c0_register($20, 0, val)
+
+#define read_c0_mailbox1() __read_32bit_c0_register($20, 1)
+#define write_c0_mailbox1(val) __write_32bit_c0_register($20, 1, val)
+
+#define smp_clr_pending(mask) do { \
+ unsigned int stat; \
+ stat = read_c0_corestatus(); \
+ stat &= ~((mask) & 0xff); \
+ write_c0_corestatus(stat); \
+ } while (0)
+
+/*
+ * Core Control register
+ */
+#define CORECTRL_SLEEP1M_SHIFT 17
+#define CORECTRL_SLEEP1M (_ULCAST_(0x1) << CORECTRL_SLEEP1M_SHIFT)
+#define CORECTRL_SLEEP0M_SHIFT 16
+#define CORECTRL_SLEEP0M (_ULCAST_(0x1) << CORECTRL_SLEEP0M_SHIFT)
+#define CORECTRL_RPC1_SHIFT 9
+#define CORECTRL_RPC1 (_ULCAST_(0x1) << CORECTRL_RPC1_SHIFT)
+#define CORECTRL_RPC0_SHIFT 8
+#define CORECTRL_RPC0 (_ULCAST_(0x1) << CORECTRL_RPC0_SHIFT)
+#define CORECTRL_SWRST1_SHIFT 1
+#define CORECTRL_SWRST1 (_ULCAST_(0x1) << CORECTRL_SWRST1_SHIFT)
+#define CORECTRL_SWRST0_SHIFT 0
+#define CORECTRL_SWRST0 (_ULCAST_(0x1) << CORECTRL_SWRST0_SHIFT)
+
+/*
+ * Core Status register
+ */
+#define CORESTATUS_SLEEP1_SHIFT 17
+#define CORESTATUS_SLEEP1 (_ULCAST_(0x1) << CORESTATUS_SLEEP1_SHIFT)
+#define CORESTATUS_SLEEP0_SHIFT 16
+#define CORESTATUS_SLEEP0 (_ULCAST_(0x1) << CORESTATUS_SLEEP0_SHIFT)
+#define CORESTATUS_IRQ1P_SHIFT 9
+#define CORESTATUS_IRQ1P (_ULCAST_(0x1) << CORESTATUS_IRQ1P_SHIFT)
+#define CORESTATUS_IRQ0P_SHIFT 8
+#define CORESTATUS_IRQ0P (_ULCAST_(0x1) << CORESTATUS_IRQ8P_SHIFT)
+#define CORESTATUS_MIRQ1P_SHIFT 1
+#define CORESTATUS_MIRQ1P (_ULCAST_(0x1) << CORESTATUS_MIRQ1P_SHIFT)
+#define CORESTATUS_MIRQ0P_SHIFT 0
+#define CORESTATUS_MIRQ0P (_ULCAST_(0x1) << CORESTATUS_MIRQ0P_SHIFT)
+
+/*
+ * Reset Entry & IRQ Mask register
+ */
+#define REIM_ENTRY_SHIFT 16
+#define REIM_ENTRY (_ULCAST_(0xffff) << REIM_ENTRY_SHIFT)
+#define REIM_IRQ1M_SHIFT 9
+#define REIM_IRQ1M (_ULCAST_(0x1) << REIM_IRQ1M_SHIFT)
+#define REIM_IRQ0M_SHIFT 8
+#define REIM_IRQ0M (_ULCAST_(0x1) << REIM_IRQ0M_SHIFT)
+#define REIM_MBOXIRQ1M_SHIFT 1
+#define REIM_MBOXIRQ1M (_ULCAST_(0x1) << REIM_MBOXIRQ1M_SHIFT)
+#define REIM_MBOXIRQ0M_SHIFT 0
+#define REIM_MBOXIRQ0M (_ULCAST_(0x1) << REIM_MBOXIRQ0M_SHIFT)
+
+#ifdef CONFIG_SMP
+
+extern void jz4780_smp_wait_irqoff(void);
+
+extern void jz4780_smp_init(void);
+extern void jz4780_secondary_cpu_entry(void);
+
+#else /* !CONFIG_SMP */
+
+static inline void jz4780_smp_init(void) { }
+
+#endif /* !CONFIG_SMP */
+
+#endif /* __MIPS_ASM_MACH_JZ4740_JZ4780_SMP_H__ */
diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
index 412d2fa..0239597 100644
--- a/arch/mips/jz4740/Kconfig
+++ b/arch/mips/jz4740/Kconfig
@@ -34,9 +34,12 @@ config MACH_JZ4770

config MACH_JZ4780
bool
+ select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select MIPS_CPU_SCACHE
+ select NR_CPUS_DEFAULT_2
select SYS_HAS_CPU_MIPS32_R2
select SYS_SUPPORTS_HIGHMEM
+ select SYS_SUPPORTS_SMP

config MACH_X1000
bool
diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
index 6de14c0..0a0f024 100644
--- a/arch/mips/jz4740/Makefile
+++ b/arch/mips/jz4740/Makefile
@@ -12,3 +12,8 @@ CFLAGS_setup.o = -I$(src)/../../../scripts/dtc/libfdt
# PM support

obj-$(CONFIG_PM) += pm.o
+
+# SMP support
+
+obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_SMP) += smp-entry.o
diff --git a/arch/mips/jz4740/prom.c b/arch/mips/jz4740/prom.c
index ff4555c..a79159e 100644
--- a/arch/mips/jz4740/prom.c
+++ b/arch/mips/jz4740/prom.c
@@ -8,10 +8,14 @@

#include <asm/bootinfo.h>
#include <asm/fw/fw.h>
+#include <asm/mach-jz4740/jz4780-smp.h>

void __init prom_init(void)
{
fw_init_cmdline();
+#if defined(CONFIG_MACH_JZ4780) && defined(CONFIG_SMP)
+ jz4780_smp_init();
+#endif
}

void __init prom_free_prom_memory(void)
diff --git a/arch/mips/jz4740/smp-entry.S b/arch/mips/jz4740/smp-entry.S
new file mode 100644
index 00000000..20049a3
--- /dev/null
+++ b/arch/mips/jz4740/smp-entry.S
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2013, Paul Burton <[email protected]>
+ * JZ4780 SMP entry point
+ */
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/cacheops.h>
+#include <asm/mipsregs.h>
+
+#define CACHE_SIZE (32 * 1024)
+#define CACHE_LINESIZE 32
+
+.extern jz4780_cpu_entry_sp
+.extern jz4780_cpu_entry_gp
+
+.section .text.smp-entry
+.balign 0x10000
+.set noreorder
+LEAF(jz4780_secondary_cpu_entry)
+ mtc0 zero, CP0_CAUSE
+
+ li t0, ST0_CU0
+ mtc0 t0, CP0_STATUS
+
+ /* cache setup */
+ li t0, KSEG0
+ ori t1, t0, CACHE_SIZE
+ mtc0 zero, CP0_TAGLO, 0
+1: cache Index_Store_Tag_I, 0(t0)
+ cache Index_Store_Tag_D, 0(t0)
+ bne t0, t1, 1b
+ addiu t0, t0, CACHE_LINESIZE
+
+ /* kseg0 cache attribute */
+ mfc0 t0, CP0_CONFIG, 0
+ ori t0, t0, CONF_CM_CACHABLE_NONCOHERENT
+ mtc0 t0, CP0_CONFIG, 0
+
+ /* pagemask */
+ mtc0 zero, CP0_PAGEMASK, 0
+
+ /* retrieve sp */
+ la t0, jz4780_cpu_entry_sp
+ lw sp, 0(t0)
+
+ /* retrieve gp */
+ la t0, jz4780_cpu_entry_gp
+ lw gp, 0(t0)
+
+ /* jump to the kernel in kseg0 */
+ la t0, smp_bootstrap
+ jr t0
+ nop
+ END(jz4780_secondary_cpu_entry)
diff --git a/arch/mips/jz4740/smp.c b/arch/mips/jz4740/smp.c
new file mode 100644
index 00000000..19b75c2
--- /dev/null
+++ b/arch/mips/jz4740/smp.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2013, Paul Burton <[email protected]>
+ * JZ4780 SMP
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/sched/task_stack.h>
+#include <linux/smp.h>
+#include <linux/tick.h>
+#include <asm/mach-jz4740/jz4780-smp.h>
+#include <asm/r4kcache.h>
+#include <asm/smp-ops.h>
+
+static struct clk *cpu_clock_gates[CONFIG_NR_CPUS] = { NULL };
+
+u32 jz4780_cpu_entry_sp;
+u32 jz4780_cpu_entry_gp;
+
+static struct cpumask cpu_running;
+
+static DEFINE_SPINLOCK(smp_lock);
+
+/*
+ * The Ingenic jz4780 SMP variant has to write back dirty cache lines before
+ * executing wait. The CPU & cache clock will be gated until we return from
+ * the wait, and if another core attempts to access data from our data cache
+ * during this time then it will lock up.
+ */
+void jz4780_smp_wait_irqoff(void)
+{
+ unsigned long pending = read_c0_cause() & read_c0_status() & CAUSEF_IP;
+
+ /*
+ * Going to idle has a significant overhead due to the cache flush so
+ * try to avoid it if we'll immediately be woken again due to an IRQ.
+ */
+ if (!need_resched() && !pending) {
+ r4k_blast_dcache();
+
+ __asm__(
+ " .set push \n"
+ " .set mips3 \n"
+ " sync \n"
+ " wait \n"
+ " .set pop \n");
+ }
+
+ local_irq_enable();
+}
+
+static irqreturn_t mbox_handler(int irq, void *dev_id)
+{
+ int cpu = smp_processor_id();
+ u32 action, status;
+
+ spin_lock(&smp_lock);
+
+ switch (cpu) {
+ case 0:
+ action = read_c0_mailbox0();
+ write_c0_mailbox0(0);
+ break;
+ case 1:
+ action = read_c0_mailbox1();
+ write_c0_mailbox1(0);
+ break;
+ default:
+ panic("unhandled cpu %d!", cpu);
+ }
+
+ /* clear pending mailbox interrupt */
+ status = read_c0_corestatus();
+ status &= ~(CORESTATUS_MIRQ0P << cpu);
+ write_c0_corestatus(status);
+
+ spin_unlock(&smp_lock);
+
+ if (action & SMP_RESCHEDULE_YOURSELF)
+ scheduler_ipi();
+ if (action & SMP_CALL_FUNCTION)
+ generic_smp_call_function_interrupt();
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction mbox_action = {
+ .handler = mbox_handler,
+ .name = "core mailbox",
+ .flags = IRQF_PERCPU | IRQF_NO_THREAD,
+};
+
+static void jz4780_smp_setup(void)
+{
+ u32 addr, reim;
+ int cpu;
+
+ reim = read_c0_reim();
+
+ for (cpu = 0; cpu < NR_CPUS; cpu++) {
+ __cpu_number_map[cpu] = cpu;
+ __cpu_logical_map[cpu] = cpu;
+ set_cpu_possible(cpu, true);
+ }
+
+ /* mask mailbox interrupts for this core */
+ reim &= ~REIM_MBOXIRQ0M;
+ write_c0_reim(reim);
+
+ /* clear mailboxes & pending mailbox IRQs */
+ write_c0_mailbox0(0);
+ write_c0_mailbox1(0);
+ write_c0_corestatus(0);
+
+ /* set reset entry point */
+ addr = KSEG1ADDR((u32)&jz4780_secondary_cpu_entry);
+ WARN_ON(addr & ~REIM_ENTRY);
+ reim &= ~REIM_ENTRY;
+ reim |= addr & REIM_ENTRY;
+
+ /* unmask mailbox interrupts for this core */
+ reim |= REIM_MBOXIRQ0M;
+ write_c0_reim(reim);
+ set_c0_status(STATUSF_IP3);
+ irq_enable_hazard();
+
+ cpumask_set_cpu(cpu, &cpu_running);
+}
+
+static void jz4780_smp_prepare_cpus(unsigned int max_cpus)
+{
+ struct device_node *cpu_node;
+ unsigned cpu, ctrl;
+ int err;
+
+ /* setup the mailbox IRQ */
+ setup_irq(MIPS_CPU_IRQ_BASE + 3, &mbox_action);
+
+ init_cpu_present(cpu_possible_mask);
+
+ ctrl = read_c0_corectrl();
+
+ for (cpu = 0; cpu < max_cpus; cpu++) {
+ /* use reset entry point from REIM register */
+ ctrl |= CORECTRL_RPC0 << cpu;
+ }
+
+ for_each_of_cpu_node(cpu_node) {
+ cpu = of_cpu_node_to_id(cpu_node);
+ if (cpu < 0) {
+ pr_err("Failed to read index of %s\n",
+ cpu_node->full_name);
+ continue;
+ }
+
+ cpu_clock_gates[cpu] = of_clk_get(cpu_node, 0);
+ if (IS_ERR(cpu_clock_gates[cpu])) {
+ cpu_clock_gates[cpu] = NULL;
+ continue;
+ }
+
+ err = clk_prepare(cpu_clock_gates[cpu]);
+ if (err)
+ pr_err("Failed to prepare CPU clock gate\n");
+ }
+
+ write_c0_corectrl(ctrl);
+}
+
+static int jz4780_boot_secondary(int cpu, struct task_struct *idle)
+{
+ unsigned long flags;
+ u32 ctrl;
+
+ spin_lock_irqsave(&smp_lock, flags);
+
+ /* ensure the core is in reset */
+ ctrl = read_c0_corectrl();
+ ctrl |= CORECTRL_SWRST0 << cpu;
+ write_c0_corectrl(ctrl);
+
+ /* ungate core clock */
+ if (cpu_clock_gates[cpu])
+ clk_enable(cpu_clock_gates[cpu]);
+
+ /* set entry sp/gp register values */
+ jz4780_cpu_entry_sp = __KSTK_TOS(idle);
+ jz4780_cpu_entry_gp = (u32)task_thread_info(idle);
+ smp_wmb();
+
+ /* take the core out of reset */
+ ctrl &= ~(CORECTRL_SWRST0 << cpu);
+ write_c0_corectrl(ctrl);
+
+ cpumask_set_cpu(cpu, &cpu_running);
+
+ spin_unlock_irqrestore(&smp_lock, flags);
+
+ return 0;
+}
+
+static void jz4780_init_secondary(void)
+{
+}
+
+static void jz4780_smp_finish(void)
+{
+ u32 reim;
+
+ spin_lock(&smp_lock);
+
+ /* unmask mailbox interrupts for this core */
+ reim = read_c0_reim();
+ reim |= REIM_MBOXIRQ0M << smp_processor_id();
+ write_c0_reim(reim);
+
+ spin_unlock(&smp_lock);
+
+ /* unmask interrupts for this core */
+ change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP2 |
+ STATUSF_IP1 | STATUSF_IP0);
+ irq_enable_hazard();
+
+ /* force broadcast timer */
+ tick_broadcast_force();
+}
+
+static void jz4780_send_ipi_single_locked(int cpu, unsigned int action)
+{
+ u32 mbox;
+
+ switch (cpu) {
+ case 0:
+ mbox = read_c0_mailbox0();
+ write_c0_mailbox0(mbox | action);
+ break;
+ case 1:
+ mbox = read_c0_mailbox1();
+ write_c0_mailbox1(mbox | action);
+ break;
+ default:
+ panic("unhandled cpu %d!", cpu);
+ }
+}
+
+static void jz4780_send_ipi_single(int cpu, unsigned int action)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&smp_lock, flags);
+ jz4780_send_ipi_single_locked(cpu, action);
+ spin_unlock_irqrestore(&smp_lock, flags);
+}
+
+static void jz4780_send_ipi_mask(const struct cpumask *mask,
+ unsigned int action)
+{
+ unsigned long flags;
+ int cpu;
+
+ spin_lock_irqsave(&smp_lock, flags);
+
+ for_each_cpu(cpu, mask)
+ jz4780_send_ipi_single_locked(cpu, action);
+
+ spin_unlock_irqrestore(&smp_lock, flags);
+}
+
+static struct plat_smp_ops jz4780_smp_ops = {
+ .send_ipi_single = jz4780_send_ipi_single,
+ .send_ipi_mask = jz4780_send_ipi_mask,
+ .init_secondary = jz4780_init_secondary,
+ .smp_finish = jz4780_smp_finish,
+ .boot_secondary = jz4780_boot_secondary,
+ .smp_setup = jz4780_smp_setup,
+ .prepare_cpus = jz4780_smp_prepare_cpus,
+};
+
+void jz4780_smp_init(void)
+{
+ register_smp_ops(&jz4780_smp_ops);
+}
diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c
index 37f8e78..a406de3 100644
--- a/arch/mips/kernel/idle.c
+++ b/arch/mips/kernel/idle.c
@@ -19,6 +19,10 @@
#include <asm/idle.h>
#include <asm/mipsregs.h>

+#ifdef CONFIG_MACH_JZ4780
+# include <asm/mach-jz4740/jz4780-smp.h>
+#endif
+
/*
* Not all of the MIPS CPUs have the "wait" instruction available. Moreover,
* the implementation of the "wait" feature differs between CPU families. This
@@ -172,7 +176,6 @@ void __init check_wait(void)
case CPU_CAVIUM_OCTEON_PLUS:
case CPU_CAVIUM_OCTEON2:
case CPU_CAVIUM_OCTEON3:
- case CPU_XBURST:
case CPU_LOONGSON32:
case CPU_XLR:
case CPU_XLP:
@@ -246,6 +249,15 @@ void __init check_wait(void)
cpu_wait = r4k_wait;
*/
break;
+ case CPU_XBURST:
+#if defined(CONFIG_MACH_JZ4780) && defined(CONFIG_SMP)
+ if (NR_CPUS > 1)
+ cpu_wait = jz4780_smp_wait_irqoff;
+ else
+ cpu_wait = r4k_wait;
+#else
+ cpu_wait = r4k_wait;
+#endif
default:
break;
}
diff --git a/drivers/clk/ingenic/jz4780-cgu.c b/drivers/clk/ingenic/jz4780-cgu.c
index d07fff1..4f81819 100644
--- a/drivers/clk/ingenic/jz4780-cgu.c
+++ b/drivers/clk/ingenic/jz4780-cgu.c
@@ -16,7 +16,7 @@

/* CGU register offsets */
#define CGU_REG_CLOCKCONTROL 0x00
-#define CGU_REG_PLLCONTROL 0x0c
+#define CGU_REG_LCR 0x04
#define CGU_REG_APLL 0x10
#define CGU_REG_MPLL 0x14
#define CGU_REG_EPLL 0x18
@@ -46,8 +46,8 @@
#define CGU_REG_CLOCKSTATUS 0xd4

/* bits within the OPCR register */
-#define OPCR_SPENDN0 (1 << 7)
-#define OPCR_SPENDN1 (1 << 6)
+#define OPCR_SPENDN0 BIT(7)
+#define OPCR_SPENDN1 BIT(6)

/* bits within the USBPCR register */
#define USBPCR_USB_MODE BIT(31)
@@ -88,6 +88,13 @@
#define USBVBFIL_IDDIGFIL_MASK (0xffff << USBVBFIL_IDDIGFIL_SHIFT)
#define USBVBFIL_USBVBFIL_MASK (0xffff)

+/* bits within the LCR register */
+#define LCR_PD_SCPU BIT(31)
+#define LCR_SCPUS BIT(27)
+
+/* bits within the CLKGR1 register */
+#define CLKGR1_CORE1 BIT(15)
+
static struct ingenic_cgu *cgu;

static u8 jz4780_otg_phy_get_parent(struct clk_hw *hw)
@@ -205,6 +212,47 @@ static const struct clk_ops jz4780_otg_phy_ops = {
.set_rate = jz4780_otg_phy_set_rate,
};

+static int jz4780_core1_enable(struct clk_hw *hw)
+{
+ struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+ struct ingenic_cgu *cgu = ingenic_clk->cgu;
+ const unsigned int timeout = 100;
+ unsigned long flags;
+ unsigned int i;
+ u32 lcr, clkgr1;
+
+ spin_lock_irqsave(&cgu->lock, flags);
+
+ lcr = readl(cgu->base + CGU_REG_LCR);
+ lcr &= ~LCR_PD_SCPU;
+ writel(lcr, cgu->base + CGU_REG_LCR);
+
+ clkgr1 = readl(cgu->base + CGU_REG_CLKGR1);
+ clkgr1 &= ~CLKGR1_CORE1;
+ writel(clkgr1, cgu->base + CGU_REG_CLKGR1);
+
+ spin_unlock_irqrestore(&cgu->lock, flags);
+
+ /* wait for the CPU to be powered up */
+ for (i = 0; i < timeout; i++) {
+ lcr = readl(cgu->base + CGU_REG_LCR);
+ if (!(lcr & LCR_SCPUS))
+ break;
+ mdelay(1);
+ }
+
+ if (i == timeout) {
+ pr_err("%s: Wait for power up core1 timeout\n", __func__);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static const struct clk_ops jz4780_core1_ops = {
+ .enable = jz4780_core1_enable,
+};
+
static const s8 pll_od_encoding[16] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
@@ -701,9 +749,9 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = {
},

[JZ4780_CLK_CORE1] = {
- "core1", CGU_CLK_GATE,
+ "core1", CGU_CLK_CUSTOM,
.parents = { JZ4780_CLK_CPU, -1, -1, -1 },
- .gate = { CGU_REG_CLKGR1, 15 },
+ .custom = { &jz4780_core1_ops },
},

};
--
2.7.4

2020-02-14 18:06:38

by Zhou Yanjie

[permalink] [raw]
Subject: [PATCH v4 4/6] MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs.

Add 'cpus' node to the jz4740.dtsi, jz4770.dtsi, jz4780.dtsi
and x1000.dtsi files.

Tested-by: H. Nikolaus Schaller <[email protected]>
Tested-by: Paul Boddie <[email protected]>
Signed-off-by: 周琰杰 (Zhou Yanjie) <[email protected]>
---

Notes:
v1->v2:
No change.

v2->v3:
No change.

v3->v4:
Rebase on top of kernel 5.6-rc1.

arch/mips/boot/dts/ingenic/jz4740.dtsi | 14 ++++++++++++++
arch/mips/boot/dts/ingenic/jz4770.dtsi | 15 ++++++++++++++-
arch/mips/boot/dts/ingenic/jz4780.dtsi | 23 +++++++++++++++++++++++
arch/mips/boot/dts/ingenic/x1000.dtsi | 14 ++++++++++++++
4 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/arch/mips/boot/dts/ingenic/jz4740.dtsi b/arch/mips/boot/dts/ingenic/jz4740.dtsi
index 5accda2..9627d95 100644
--- a/arch/mips/boot/dts/ingenic/jz4740.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4740.dtsi
@@ -6,6 +6,20 @@
#size-cells = <1>;
compatible = "ingenic,jz4740";

+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst";
+ reg = <0>;
+
+ clocks = <&cgu JZ4740_CLK_CCLK>;
+ clock-names = "cpu";
+ };
+ };
+
cpuintc: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
diff --git a/arch/mips/boot/dts/ingenic/jz4770.dtsi b/arch/mips/boot/dts/ingenic/jz4770.dtsi
index 0bfb9ed..1b8114d 100644
--- a/arch/mips/boot/dts/ingenic/jz4770.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4770.dtsi
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-
#include <dt-bindings/clock/jz4770-cgu.h>

/ {
@@ -7,6 +6,20 @@
#size-cells = <1>;
compatible = "ingenic,jz4770";

+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst";
+ reg = <0>;
+
+ clocks = <&cgu JZ4770_CLK_CCLK>;
+ clock-names = "cpu";
+ };
+ };
+
cpuintc: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi
index f928329..93a0604 100644
--- a/arch/mips/boot/dts/ingenic/jz4780.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi
@@ -7,6 +7,29 @@
#size-cells = <1>;
compatible = "ingenic,jz4780";

+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst";
+ reg = <0>;
+
+ clocks = <&cgu JZ4780_CLK_CPU>;
+ clock-names = "cpu";
+ };
+
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst";
+ reg = <1>;
+
+ clocks = <&cgu JZ4780_CLK_CORE1>;
+ clock-names = "cpu";
+ };
+ };
+
cpuintc: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
diff --git a/arch/mips/boot/dts/ingenic/x1000.dtsi b/arch/mips/boot/dts/ingenic/x1000.dtsi
index 4994c69..376df1b 100644
--- a/arch/mips/boot/dts/ingenic/x1000.dtsi
+++ b/arch/mips/boot/dts/ingenic/x1000.dtsi
@@ -7,6 +7,20 @@
#size-cells = <1>;
compatible = "ingenic,x1000", "ingenic,x1000e";

+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst";
+ reg = <0>;
+
+ clocks = <&cgu X1000_CLK_CPU>;
+ clock-names = "cpu";
+ };
+ };
+
cpuintc: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
--
2.7.4

2020-02-14 18:06:41

by Zhou Yanjie

[permalink] [raw]
Subject: [PATCH v4 6/6] MIPS: CI20: Update defconfig to support SMP.

Add "CONFIG_SMP=y" and "CONFIG_NR_CPUS=2" to support SMP.

Tested-by: H. Nikolaus Schaller <[email protected]>
Tested-by: Paul Boddie <[email protected]>
Signed-off-by: 周琰杰 (Zhou Yanjie) <[email protected]>
---

Notes:
v1->v2:
No change.

v2->v3:
No change.

v3->v4:
Rebase on top of kernel 5.6-rc1.

arch/mips/configs/ci20_defconfig | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/mips/configs/ci20_defconfig b/arch/mips/configs/ci20_defconfig
index be41df2..3aadb2e 100644
--- a/arch/mips/configs/ci20_defconfig
+++ b/arch/mips/configs/ci20_defconfig
@@ -1,3 +1,5 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_KERNEL_XZ=y
CONFIG_SYSVIPC=y
--
2.7.4

2020-02-14 18:06:42

by Zhou Yanjie

[permalink] [raw]
Subject: [PATCH v4 0/6] Introduce SMP support for JZ4780.

Introduce SMP support for MIPS Creator CI20, which is
based on Ingenic JZ4780 SoC.

周琰杰 (Zhou Yanjie) (6):
MIPS: JZ4780: Introduce SMP support.
clocksource: Ingenic: Add high resolution timer support for SMP.
dt-bindings: MIPS: Document Ingenic SoCs binding.
MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs.
MIPS: CI20: Modify DTS to support high resolution timer for SMP.
MIPS: CI20: Update defconfig to support SMP.

.../bindings/mips/ingenic/ingenic,cpu.yaml | 53 ++++
.../bindings/mips/ingenic/ingenic,soc.yaml | 35 +++
arch/mips/boot/dts/ingenic/ci20.dts | 11 +-
arch/mips/boot/dts/ingenic/jz4740.dtsi | 14 +
arch/mips/boot/dts/ingenic/jz4770.dtsi | 15 +-
arch/mips/boot/dts/ingenic/jz4780.dtsi | 23 ++
arch/mips/boot/dts/ingenic/x1000.dtsi | 14 +
arch/mips/configs/ci20_defconfig | 2 +
arch/mips/include/asm/mach-jz4740/jz4780-smp.h | 91 +++++++
arch/mips/jz4740/Kconfig | 3 +
arch/mips/jz4740/Makefile | 5 +
arch/mips/jz4740/prom.c | 4 +
arch/mips/jz4740/smp-entry.S | 57 ++++
arch/mips/jz4740/smp.c | 286 +++++++++++++++++++++
arch/mips/kernel/idle.c | 14 +-
drivers/clk/ingenic/jz4780-cgu.c | 58 ++++-
drivers/clocksource/ingenic-timer.c | 200 +++++++++-----
17 files changed, 810 insertions(+), 75 deletions(-)
create mode 100755 Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
create mode 100755 Documentation/devicetree/bindings/mips/ingenic/ingenic,soc.yaml
create mode 100644 arch/mips/include/asm/mach-jz4740/jz4780-smp.h
create mode 100644 arch/mips/jz4740/smp-entry.S
create mode 100644 arch/mips/jz4740/smp.c

--
2.7.4

2020-02-14 18:06:47

by Zhou Yanjie

[permalink] [raw]
Subject: [PATCH v4 2/6] clocksource: Ingenic: Add high resolution timer support for SMP.

Enable clock event handling on per CPU core basis.
Make sure that interrupts raised on the first core execute
event handlers on the correct CPU core.

Tested-by: H. Nikolaus Schaller <[email protected]>
Tested-by: Paul Boddie <[email protected]>
Signed-off-by: 周琰杰 (Zhou Yanjie) <[email protected]>
---

Notes:
v1->v2:
1.Adjust function naming to make it more reasonable.
2.Replace function smp_call_function_single() with
smp_call_function_single_async() in order to resolve
the warning below:

[ 0.350942] smp: Brought up 1 node, 2 CPUs
[ 0.365497] ------------[ cut here ]------------
[ 0.365522] WARNING: CPU: 0 PID: 1 at kernel/smp.c:300 smp_call_function_single+0x110/0x200
[ 0.365533] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.5.0-rc1+ #5
[ 0.365537] Stack : 00000000 59c73bcd 00000037 80074e80 80000000 80670000 805a0000 80620590
[ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000 80670000 00000001 8fc0dc20 59c73bcd
[ 0.365574] 00000000 00000000 806f0000 80670000 00000000 806dab00 00000000 2d302e35
[ 0.365591] 203a6d6d 806e0000 806e0000 70617773 80670000 00000000 00000000 00000009
[ 0.365610] 00000000 8fc94e20 8fc0de30 80690000 00000018 803592dc 00000000 806d0000
[ 0.365627] ...
[ 0.365634] Call Trace:
[ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
[ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
[ 0.365673] [<8003044c>] __warn+0xc4/0xe8
[ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
[ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
[ 0.365703] ---[ end trace 5785856ca39c79d5 ]---
[ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000 80670000 00000001 8fc0dc20 59c73bcd
[ 0.365574] 00000000 00000000 806f0000 80670000 00000000 806dab00 00000000 2d302e35
[ 0.365591] 203a6d6d 806e0000 806e0000 70617773 80670000 00000000 00000000 00000009
[ 0.365610] 00000000 8fc94e20 8fc0de30 80690000 00000018 803592dc 00000000 806d0000
[ 0.365627] ...
[ 0.365634] Call Trace:
[ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
[ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
[ 0.365673] [<8003044c>] __warn+0xc4/0xe8
[ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
[ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
[ 0.365703] ---[ end trace 5785856ca39c79d5 ]---

v2->v3:
No Change.

v3->v4:
Rebase on top of kernel 5.6-rc1.

drivers/clocksource/ingenic-timer.c | 200 ++++++++++++++++++++++++------------
1 file changed, 134 insertions(+), 66 deletions(-)

diff --git a/drivers/clocksource/ingenic-timer.c b/drivers/clocksource/ingenic-timer.c
index 4bbdb3d..9127c73 100644
--- a/drivers/clocksource/ingenic-timer.c
+++ b/drivers/clocksource/ingenic-timer.c
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * JZ47xx SoCs TCU IRQ driver
+ * XBurst SoCs TCU IRQ driver
* Copyright (C) 2019 Paul Cercueil <[email protected]>
+ * Copyright (C) 2020 周琰杰 (Zhou Yanjie) <[email protected]>
*/

#include <linux/bitops.h>
@@ -21,18 +22,23 @@

#include <dt-bindings/clock/ingenic,tcu.h>

+static DEFINE_PER_CPU(call_single_data_t, ingenic_cevt_csd);
+
struct ingenic_soc_info {
unsigned int num_channels;
};

struct ingenic_tcu {
struct regmap *map;
+ struct device_node *np;
struct clk *timer_clk, *cs_clk;
+ unsigned int timer_local[NR_CPUS];
unsigned int timer_channel, cs_channel;
struct clock_event_device cevt;
struct clocksource cs;
- char name[4];
+ char name[16];
unsigned long pwm_channels_mask;
+ int cpu;
};

static struct ingenic_tcu *ingenic_tcu;
@@ -81,6 +87,25 @@ static int ingenic_tcu_cevt_set_next(unsigned long next,
return 0;
}

+static void ingenic_per_cpu_event_handler(void *info)
+{
+ struct clock_event_device *cevt = (struct clock_event_device *) info;
+
+ if (cevt->event_handler)
+ cevt->event_handler(cevt);
+}
+
+static void ingenic_tcu_per_cpu_cb(struct clock_event_device *evt)
+{
+ struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
+ call_single_data_t *csd;
+
+ csd = &per_cpu(ingenic_cevt_csd, tcu->cpu);
+ csd->info = (void *) evt;
+ csd->func = ingenic_per_cpu_event_handler;
+ smp_call_function_single_async(tcu->cpu, csd);
+}
+
static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
@@ -88,8 +113,7 @@ static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)

regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));

- if (evt->event_handler)
- evt->event_handler(evt);
+ ingenic_tcu_per_cpu_cb(evt);

return IRQ_HANDLED;
}
@@ -105,14 +129,73 @@ static struct clk * __init ingenic_tcu_get_clock(struct device_node *np, int id)
return of_clk_get_from_provider(&args);
}

-static int __init ingenic_tcu_timer_init(struct device_node *np,
- struct ingenic_tcu *tcu)
+static int __init ingenic_tcu_clocksource_init(struct device_node *np,
+ struct ingenic_tcu *tcu)
+{
+ unsigned int channel = tcu->cs_channel;
+ struct clocksource *cs = &tcu->cs;
+ unsigned long rate;
+ int err;
+
+ tcu->cs_clk = ingenic_tcu_get_clock(np, channel);
+ if (IS_ERR(tcu->cs_clk))
+ return PTR_ERR(tcu->cs_clk);
+
+ err = clk_prepare_enable(tcu->cs_clk);
+ if (err)
+ goto err_clk_put;
+
+ rate = clk_get_rate(tcu->cs_clk);
+ if (!rate) {
+ err = -EINVAL;
+ goto err_clk_disable;
+ }
+
+ /* Reset channel */
+ regmap_update_bits(tcu->map, TCU_REG_TCSRc(channel),
+ 0xffff & ~TCU_TCSR_RESERVED_BITS, 0);
+
+ /* Reset counter */
+ regmap_write(tcu->map, TCU_REG_TDFRc(channel), 0xffff);
+ regmap_write(tcu->map, TCU_REG_TCNTc(channel), 0);
+
+ /* Enable channel */
+ regmap_write(tcu->map, TCU_REG_TESR, BIT(channel));
+
+ cs->name = "ingenic-timer";
+ cs->rating = 200;
+ cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
+ cs->mask = CLOCKSOURCE_MASK(16);
+ cs->read = ingenic_tcu_timer_cs_read;
+
+ err = clocksource_register_hz(cs, rate);
+ if (err)
+ goto err_clk_disable;
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(tcu->cs_clk);
+err_clk_put:
+ clk_put(tcu->cs_clk);
+ return err;
+}
+
+static int ingenic_tcu_setup_per_cpu_cevt(struct device_node *np,
+ unsigned int channel)
{
- unsigned int timer_virq, channel = tcu->timer_channel;
+ unsigned int timer_virq;
struct irq_domain *domain;
+ struct ingenic_tcu *tcu;
unsigned long rate;
int err;

+ tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);
+ if (!tcu)
+ return -ENOMEM;
+
+ tcu->map = ingenic_tcu->map;
+
tcu->timer_clk = ingenic_tcu_get_clock(np, channel);
if (IS_ERR(tcu->timer_clk))
return PTR_ERR(tcu->timer_clk);
@@ -139,13 +222,15 @@ static int __init ingenic_tcu_timer_init(struct device_node *np,
goto err_clk_disable;
}

- snprintf(tcu->name, sizeof(tcu->name), "TCU");
+ snprintf(tcu->name, sizeof(tcu->name), "TCU channel.%u", channel);

err = request_irq(timer_virq, ingenic_tcu_cevt_cb, IRQF_TIMER,
tcu->name, &tcu->cevt);
if (err)
goto err_irq_dispose_mapping;

+ tcu->cpu = smp_processor_id();
+ tcu->timer_channel = channel;
tcu->cevt.cpumask = cpumask_of(smp_processor_id());
tcu->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
tcu->cevt.name = tcu->name;
@@ -166,56 +251,23 @@ static int __init ingenic_tcu_timer_init(struct device_node *np,
return err;
}

-static int __init ingenic_tcu_clocksource_init(struct device_node *np,
- struct ingenic_tcu *tcu)
+static int ingenic_tcu_setup_cevt(unsigned int cpu)
{
- unsigned int channel = tcu->cs_channel;
- struct clocksource *cs = &tcu->cs;
- unsigned long rate;
- int err;
-
- tcu->cs_clk = ingenic_tcu_get_clock(np, channel);
- if (IS_ERR(tcu->cs_clk))
- return PTR_ERR(tcu->cs_clk);
-
- err = clk_prepare_enable(tcu->cs_clk);
- if (err)
- goto err_clk_put;
-
- rate = clk_get_rate(tcu->cs_clk);
- if (!rate) {
- err = -EINVAL;
- goto err_clk_disable;
- }
-
- /* Reset channel */
- regmap_update_bits(tcu->map, TCU_REG_TCSRc(channel),
- 0xffff & ~TCU_TCSR_RESERVED_BITS, 0);
-
- /* Reset counter */
- regmap_write(tcu->map, TCU_REG_TDFRc(channel), 0xffff);
- regmap_write(tcu->map, TCU_REG_TCNTc(channel), 0);
-
- /* Enable channel */
- regmap_write(tcu->map, TCU_REG_TESR, BIT(channel));
-
- cs->name = "ingenic-timer";
- cs->rating = 200;
- cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
- cs->mask = CLOCKSOURCE_MASK(16);
- cs->read = ingenic_tcu_timer_cs_read;
+ int ret;

- err = clocksource_register_hz(cs, rate);
- if (err)
- goto err_clk_disable;
+ ret = ingenic_tcu_setup_per_cpu_cevt(ingenic_tcu->np,
+ ingenic_tcu->timer_local[cpu]);
+ if (ret)
+ goto err_tcu_clocksource_cleanup;

return 0;

-err_clk_disable:
- clk_disable_unprepare(tcu->cs_clk);
-err_clk_put:
- clk_put(tcu->cs_clk);
- return err;
+err_tcu_clocksource_cleanup:
+ clocksource_unregister(&ingenic_tcu->cs);
+ clk_disable_unprepare(ingenic_tcu->cs_clk);
+ clk_put(ingenic_tcu->cs_clk);
+ kfree(ingenic_tcu);
+ return ret;
}

static const struct ingenic_soc_info jz4740_soc_info = {
@@ -239,6 +291,7 @@ static int __init ingenic_tcu_init(struct device_node *np)
const struct ingenic_soc_info *soc_info = id->data;
struct ingenic_tcu *tcu;
struct regmap *map;
+ unsigned cpu = 0;
long rate;
int ret;

@@ -253,12 +306,14 @@ static int __init ingenic_tcu_init(struct device_node *np)
return -ENOMEM;

/* Enable all TCU channels for PWM use by default except channels 0/1 */
- tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1, 2);
+ tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1,
+ NR_CPUS + 1);
of_property_read_u32(np, "ingenic,pwm-channels-mask",
(u32 *)&tcu->pwm_channels_mask);

/* Verify that we have at least two free channels */
- if (hweight8(tcu->pwm_channels_mask) > soc_info->num_channels - 2) {
+ if (hweight8(tcu->pwm_channels_mask) >
+ soc_info->num_channels - NR_CPUS + 1) {
pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__,
tcu->pwm_channels_mask);
ret = -EINVAL;
@@ -266,13 +321,29 @@ static int __init ingenic_tcu_init(struct device_node *np)
}

tcu->map = map;
+ tcu->np = np;
ingenic_tcu = tcu;

- tcu->timer_channel = find_first_zero_bit(&tcu->pwm_channels_mask,
+ tcu->timer_local[cpu] = find_first_zero_bit(&tcu->pwm_channels_mask,
soc_info->num_channels);
- tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
- soc_info->num_channels,
- tcu->timer_channel + 1);
+
+ if (NR_CPUS > 1) {
+ for (cpu = 1; cpu < NR_CPUS; cpu++)
+ tcu->timer_local[cpu] = find_next_zero_bit(
+ &tcu->pwm_channels_mask,
+ soc_info->num_channels,
+ tcu->timer_local[cpu - 1] + 1);
+
+ tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
+ soc_info->num_channels,
+ tcu->timer_local[cpu-1] + 1);
+ } else {
+ tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
+ soc_info->num_channels,
+ tcu->timer_local[cpu] + 1);
+ }
+
+

ret = ingenic_tcu_clocksource_init(np, tcu);
if (ret) {
@@ -280,9 +351,10 @@ static int __init ingenic_tcu_init(struct device_node *np)
goto err_free_ingenic_tcu;
}

- ret = ingenic_tcu_timer_init(np, tcu);
- if (ret)
- goto err_tcu_clocksource_cleanup;
+ /* Setup clock events on each CPU core */
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Ingenic XBurst: online",
+ ingenic_tcu_setup_cevt, NULL);
+ WARN_ON(ret < 0);

/* Register the sched_clock at the end as there's no way to undo it */
rate = clk_get_rate(tcu->cs_clk);
@@ -290,10 +362,6 @@ static int __init ingenic_tcu_init(struct device_node *np)

return 0;

-err_tcu_clocksource_cleanup:
- clocksource_unregister(&tcu->cs);
- clk_disable_unprepare(tcu->cs_clk);
- clk_put(tcu->cs_clk);
err_free_ingenic_tcu:
kfree(tcu);
return ret;
--
2.7.4

2020-02-14 18:22:48

by Paul Cercueil

[permalink] [raw]
Subject: Re: [PATCH v4 1/6] MIPS: JZ4780: Introduce SMP support.

Hi Zhou,

The changes to drivers/clk/ingenic/jz4780-cgu.c look sound but they
really don't belong in this patch.

Please split them to another patch.

-Paul


Le sam., févr. 15, 2020 at 02:02, 周琰杰 (Zhou Yanjie)
<[email protected]> a écrit :
> Forward port smp support from kernel 3.18.3 of CI20_linux
> to upstream kernel 5.5.
>
> Tested-by: H. Nikolaus Schaller <[email protected]>
> Tested-by: Paul Boddie <[email protected]>
> Signed-off-by: 周琰杰 (Zhou Yanjie) <[email protected]>
> ---
>
> Notes:
> v1->v2:
> 1.Remove unnecessary "plat_irq_dispatch(void)" in irq-ingenic.c.
> 2.Add a timeout check for "jz4780_boot_secondary()" to avoid a
> dead loop.
> 3.Replace hard code in smp.c with macro.
>
> v2->v3:
> 1.Remove unnecessary "extern void (*r4k_blast_dcache)(void)" in
> smp.c.
> 2.Use "for_each_of_cpu_node" instead "for_each_compatible_node"
> in smp.c.
> 3.Use "of_cpu_node_to_id" instead "of_property_read_u32_index" in
> smp.c.
> 4.Move LCR related operations to jz4780-cgu.c.
>
> v3->v4:
> Rebase on top of kernel 5.6-rc1.
>
> arch/mips/include/asm/mach-jz4740/jz4780-smp.h | 91 ++++++++
> arch/mips/jz4740/Kconfig | 3 +
> arch/mips/jz4740/Makefile | 5 +
> arch/mips/jz4740/prom.c | 4 +
> arch/mips/jz4740/smp-entry.S | 57 +++++
> arch/mips/jz4740/smp.c | 286
> +++++++++++++++++++++++++
> arch/mips/kernel/idle.c | 14 +-
> drivers/clk/ingenic/jz4780-cgu.c | 58 ++++-
> 8 files changed, 512 insertions(+), 6 deletions(-)
> create mode 100644 arch/mips/include/asm/mach-jz4740/jz4780-smp.h
> create mode 100644 arch/mips/jz4740/smp-entry.S
> create mode 100644 arch/mips/jz4740/smp.c
>
> diff --git a/arch/mips/include/asm/mach-jz4740/jz4780-smp.h
> b/arch/mips/include/asm/mach-jz4740/jz4780-smp.h
> new file mode 100644
> index 00000000..3f592ce
> --- /dev/null
> +++ b/arch/mips/include/asm/mach-jz4740/jz4780-smp.h
> @@ -0,0 +1,91 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2013, Paul Burton <[email protected]>
> + * JZ4780 SMP definitions
> + */
> +
> +#ifndef __MIPS_ASM_MACH_JZ4740_JZ4780_SMP_H__
> +#define __MIPS_ASM_MACH_JZ4740_JZ4780_SMP_H__
> +
> +#define read_c0_corectrl() __read_32bit_c0_register($12, 2)
> +#define write_c0_corectrl(val) __write_32bit_c0_register($12, 2,
> val)
> +
> +#define read_c0_corestatus() __read_32bit_c0_register($12, 3)
> +#define write_c0_corestatus(val) __write_32bit_c0_register($12, 3,
> val)
> +
> +#define read_c0_reim() __read_32bit_c0_register($12, 4)
> +#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val)
> +
> +#define read_c0_mailbox0() __read_32bit_c0_register($20, 0)
> +#define write_c0_mailbox0(val) __write_32bit_c0_register($20, 0,
> val)
> +
> +#define read_c0_mailbox1() __read_32bit_c0_register($20, 1)
> +#define write_c0_mailbox1(val) __write_32bit_c0_register($20, 1,
> val)
> +
> +#define smp_clr_pending(mask) do { \
> + unsigned int stat; \
> + stat = read_c0_corestatus(); \
> + stat &= ~((mask) & 0xff); \
> + write_c0_corestatus(stat); \
> + } while (0)
> +
> +/*
> + * Core Control register
> + */
> +#define CORECTRL_SLEEP1M_SHIFT 17
> +#define CORECTRL_SLEEP1M (_ULCAST_(0x1) << CORECTRL_SLEEP1M_SHIFT)
> +#define CORECTRL_SLEEP0M_SHIFT 16
> +#define CORECTRL_SLEEP0M (_ULCAST_(0x1) << CORECTRL_SLEEP0M_SHIFT)
> +#define CORECTRL_RPC1_SHIFT 9
> +#define CORECTRL_RPC1 (_ULCAST_(0x1) << CORECTRL_RPC1_SHIFT)
> +#define CORECTRL_RPC0_SHIFT 8
> +#define CORECTRL_RPC0 (_ULCAST_(0x1) << CORECTRL_RPC0_SHIFT)
> +#define CORECTRL_SWRST1_SHIFT 1
> +#define CORECTRL_SWRST1 (_ULCAST_(0x1) << CORECTRL_SWRST1_SHIFT)
> +#define CORECTRL_SWRST0_SHIFT 0
> +#define CORECTRL_SWRST0 (_ULCAST_(0x1) << CORECTRL_SWRST0_SHIFT)
> +
> +/*
> + * Core Status register
> + */
> +#define CORESTATUS_SLEEP1_SHIFT 17
> +#define CORESTATUS_SLEEP1 (_ULCAST_(0x1) << CORESTATUS_SLEEP1_SHIFT)
> +#define CORESTATUS_SLEEP0_SHIFT 16
> +#define CORESTATUS_SLEEP0 (_ULCAST_(0x1) << CORESTATUS_SLEEP0_SHIFT)
> +#define CORESTATUS_IRQ1P_SHIFT 9
> +#define CORESTATUS_IRQ1P (_ULCAST_(0x1) << CORESTATUS_IRQ1P_SHIFT)
> +#define CORESTATUS_IRQ0P_SHIFT 8
> +#define CORESTATUS_IRQ0P (_ULCAST_(0x1) << CORESTATUS_IRQ8P_SHIFT)
> +#define CORESTATUS_MIRQ1P_SHIFT 1
> +#define CORESTATUS_MIRQ1P (_ULCAST_(0x1) << CORESTATUS_MIRQ1P_SHIFT)
> +#define CORESTATUS_MIRQ0P_SHIFT 0
> +#define CORESTATUS_MIRQ0P (_ULCAST_(0x1) << CORESTATUS_MIRQ0P_SHIFT)
> +
> +/*
> + * Reset Entry & IRQ Mask register
> + */
> +#define REIM_ENTRY_SHIFT 16
> +#define REIM_ENTRY (_ULCAST_(0xffff) << REIM_ENTRY_SHIFT)
> +#define REIM_IRQ1M_SHIFT 9
> +#define REIM_IRQ1M (_ULCAST_(0x1) << REIM_IRQ1M_SHIFT)
> +#define REIM_IRQ0M_SHIFT 8
> +#define REIM_IRQ0M (_ULCAST_(0x1) << REIM_IRQ0M_SHIFT)
> +#define REIM_MBOXIRQ1M_SHIFT 1
> +#define REIM_MBOXIRQ1M (_ULCAST_(0x1) << REIM_MBOXIRQ1M_SHIFT)
> +#define REIM_MBOXIRQ0M_SHIFT 0
> +#define REIM_MBOXIRQ0M (_ULCAST_(0x1) << REIM_MBOXIRQ0M_SHIFT)
> +
> +#ifdef CONFIG_SMP
> +
> +extern void jz4780_smp_wait_irqoff(void);
> +
> +extern void jz4780_smp_init(void);
> +extern void jz4780_secondary_cpu_entry(void);
> +
> +#else /* !CONFIG_SMP */
> +
> +static inline void jz4780_smp_init(void) { }
> +
> +#endif /* !CONFIG_SMP */
> +
> +#endif /* __MIPS_ASM_MACH_JZ4740_JZ4780_SMP_H__ */
> diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
> index 412d2fa..0239597 100644
> --- a/arch/mips/jz4740/Kconfig
> +++ b/arch/mips/jz4740/Kconfig
> @@ -34,9 +34,12 @@ config MACH_JZ4770
>
> config MACH_JZ4780
> bool
> + select GENERIC_CLOCKEVENTS_BROADCAST if SMP
> select MIPS_CPU_SCACHE
> + select NR_CPUS_DEFAULT_2
> select SYS_HAS_CPU_MIPS32_R2
> select SYS_SUPPORTS_HIGHMEM
> + select SYS_SUPPORTS_SMP
>
> config MACH_X1000
> bool
> diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
> index 6de14c0..0a0f024 100644
> --- a/arch/mips/jz4740/Makefile
> +++ b/arch/mips/jz4740/Makefile
> @@ -12,3 +12,8 @@ CFLAGS_setup.o =
> -I$(src)/../../../scripts/dtc/libfdt
> # PM support
>
> obj-$(CONFIG_PM) += pm.o
> +
> +# SMP support
> +
> +obj-$(CONFIG_SMP) += smp.o
> +obj-$(CONFIG_SMP) += smp-entry.o
> diff --git a/arch/mips/jz4740/prom.c b/arch/mips/jz4740/prom.c
> index ff4555c..a79159e 100644
> --- a/arch/mips/jz4740/prom.c
> +++ b/arch/mips/jz4740/prom.c
> @@ -8,10 +8,14 @@
>
> #include <asm/bootinfo.h>
> #include <asm/fw/fw.h>
> +#include <asm/mach-jz4740/jz4780-smp.h>
>
> void __init prom_init(void)
> {
> fw_init_cmdline();
> +#if defined(CONFIG_MACH_JZ4780) && defined(CONFIG_SMP)
> + jz4780_smp_init();
> +#endif
> }
>
> void __init prom_free_prom_memory(void)
> diff --git a/arch/mips/jz4740/smp-entry.S
> b/arch/mips/jz4740/smp-entry.S
> new file mode 100644
> index 00000000..20049a3
> --- /dev/null
> +++ b/arch/mips/jz4740/smp-entry.S
> @@ -0,0 +1,57 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2013, Paul Burton <[email protected]>
> + * JZ4780 SMP entry point
> + */
> +
> +#include <asm/addrspace.h>
> +#include <asm/asm.h>
> +#include <asm/asmmacro.h>
> +#include <asm/cacheops.h>
> +#include <asm/mipsregs.h>
> +
> +#define CACHE_SIZE (32 * 1024)
> +#define CACHE_LINESIZE 32
> +
> +.extern jz4780_cpu_entry_sp
> +.extern jz4780_cpu_entry_gp
> +
> +.section .text.smp-entry
> +.balign 0x10000
> +.set noreorder
> +LEAF(jz4780_secondary_cpu_entry)
> + mtc0 zero, CP0_CAUSE
> +
> + li t0, ST0_CU0
> + mtc0 t0, CP0_STATUS
> +
> + /* cache setup */
> + li t0, KSEG0
> + ori t1, t0, CACHE_SIZE
> + mtc0 zero, CP0_TAGLO, 0
> +1: cache Index_Store_Tag_I, 0(t0)
> + cache Index_Store_Tag_D, 0(t0)
> + bne t0, t1, 1b
> + addiu t0, t0, CACHE_LINESIZE
> +
> + /* kseg0 cache attribute */
> + mfc0 t0, CP0_CONFIG, 0
> + ori t0, t0, CONF_CM_CACHABLE_NONCOHERENT
> + mtc0 t0, CP0_CONFIG, 0
> +
> + /* pagemask */
> + mtc0 zero, CP0_PAGEMASK, 0
> +
> + /* retrieve sp */
> + la t0, jz4780_cpu_entry_sp
> + lw sp, 0(t0)
> +
> + /* retrieve gp */
> + la t0, jz4780_cpu_entry_gp
> + lw gp, 0(t0)
> +
> + /* jump to the kernel in kseg0 */
> + la t0, smp_bootstrap
> + jr t0
> + nop
> + END(jz4780_secondary_cpu_entry)
> diff --git a/arch/mips/jz4740/smp.c b/arch/mips/jz4740/smp.c
> new file mode 100644
> index 00000000..19b75c2
> --- /dev/null
> +++ b/arch/mips/jz4740/smp.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2013, Paul Burton <[email protected]>
> + * JZ4780 SMP
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/of.h>
> +#include <linux/sched.h>
> +#include <linux/sched/task_stack.h>
> +#include <linux/smp.h>
> +#include <linux/tick.h>
> +#include <asm/mach-jz4740/jz4780-smp.h>
> +#include <asm/r4kcache.h>
> +#include <asm/smp-ops.h>
> +
> +static struct clk *cpu_clock_gates[CONFIG_NR_CPUS] = { NULL };
> +
> +u32 jz4780_cpu_entry_sp;
> +u32 jz4780_cpu_entry_gp;
> +
> +static struct cpumask cpu_running;
> +
> +static DEFINE_SPINLOCK(smp_lock);
> +
> +/*
> + * The Ingenic jz4780 SMP variant has to write back dirty cache
> lines before
> + * executing wait. The CPU & cache clock will be gated until we
> return from
> + * the wait, and if another core attempts to access data from our
> data cache
> + * during this time then it will lock up.
> + */
> +void jz4780_smp_wait_irqoff(void)
> +{
> + unsigned long pending = read_c0_cause() & read_c0_status() &
> CAUSEF_IP;
> +
> + /*
> + * Going to idle has a significant overhead due to the cache flush
> so
> + * try to avoid it if we'll immediately be woken again due to an
> IRQ.
> + */
> + if (!need_resched() && !pending) {
> + r4k_blast_dcache();
> +
> + __asm__(
> + " .set push \n"
> + " .set mips3 \n"
> + " sync \n"
> + " wait \n"
> + " .set pop \n");
> + }
> +
> + local_irq_enable();
> +}
> +
> +static irqreturn_t mbox_handler(int irq, void *dev_id)
> +{
> + int cpu = smp_processor_id();
> + u32 action, status;
> +
> + spin_lock(&smp_lock);
> +
> + switch (cpu) {
> + case 0:
> + action = read_c0_mailbox0();
> + write_c0_mailbox0(0);
> + break;
> + case 1:
> + action = read_c0_mailbox1();
> + write_c0_mailbox1(0);
> + break;
> + default:
> + panic("unhandled cpu %d!", cpu);
> + }
> +
> + /* clear pending mailbox interrupt */
> + status = read_c0_corestatus();
> + status &= ~(CORESTATUS_MIRQ0P << cpu);
> + write_c0_corestatus(status);
> +
> + spin_unlock(&smp_lock);
> +
> + if (action & SMP_RESCHEDULE_YOURSELF)
> + scheduler_ipi();
> + if (action & SMP_CALL_FUNCTION)
> + generic_smp_call_function_interrupt();
> +
> + return IRQ_HANDLED;
> +}
> +
> +static struct irqaction mbox_action = {
> + .handler = mbox_handler,
> + .name = "core mailbox",
> + .flags = IRQF_PERCPU | IRQF_NO_THREAD,
> +};
> +
> +static void jz4780_smp_setup(void)
> +{
> + u32 addr, reim;
> + int cpu;
> +
> + reim = read_c0_reim();
> +
> + for (cpu = 0; cpu < NR_CPUS; cpu++) {
> + __cpu_number_map[cpu] = cpu;
> + __cpu_logical_map[cpu] = cpu;
> + set_cpu_possible(cpu, true);
> + }
> +
> + /* mask mailbox interrupts for this core */
> + reim &= ~REIM_MBOXIRQ0M;
> + write_c0_reim(reim);
> +
> + /* clear mailboxes & pending mailbox IRQs */
> + write_c0_mailbox0(0);
> + write_c0_mailbox1(0);
> + write_c0_corestatus(0);
> +
> + /* set reset entry point */
> + addr = KSEG1ADDR((u32)&jz4780_secondary_cpu_entry);
> + WARN_ON(addr & ~REIM_ENTRY);
> + reim &= ~REIM_ENTRY;
> + reim |= addr & REIM_ENTRY;
> +
> + /* unmask mailbox interrupts for this core */
> + reim |= REIM_MBOXIRQ0M;
> + write_c0_reim(reim);
> + set_c0_status(STATUSF_IP3);
> + irq_enable_hazard();
> +
> + cpumask_set_cpu(cpu, &cpu_running);
> +}
> +
> +static void jz4780_smp_prepare_cpus(unsigned int max_cpus)
> +{
> + struct device_node *cpu_node;
> + unsigned cpu, ctrl;
> + int err;
> +
> + /* setup the mailbox IRQ */
> + setup_irq(MIPS_CPU_IRQ_BASE + 3, &mbox_action);
> +
> + init_cpu_present(cpu_possible_mask);
> +
> + ctrl = read_c0_corectrl();
> +
> + for (cpu = 0; cpu < max_cpus; cpu++) {
> + /* use reset entry point from REIM register */
> + ctrl |= CORECTRL_RPC0 << cpu;
> + }
> +
> + for_each_of_cpu_node(cpu_node) {
> + cpu = of_cpu_node_to_id(cpu_node);
> + if (cpu < 0) {
> + pr_err("Failed to read index of %s\n",
> + cpu_node->full_name);
> + continue;
> + }
> +
> + cpu_clock_gates[cpu] = of_clk_get(cpu_node, 0);
> + if (IS_ERR(cpu_clock_gates[cpu])) {
> + cpu_clock_gates[cpu] = NULL;
> + continue;
> + }
> +
> + err = clk_prepare(cpu_clock_gates[cpu]);
> + if (err)
> + pr_err("Failed to prepare CPU clock gate\n");
> + }
> +
> + write_c0_corectrl(ctrl);
> +}
> +
> +static int jz4780_boot_secondary(int cpu, struct task_struct *idle)
> +{
> + unsigned long flags;
> + u32 ctrl;
> +
> + spin_lock_irqsave(&smp_lock, flags);
> +
> + /* ensure the core is in reset */
> + ctrl = read_c0_corectrl();
> + ctrl |= CORECTRL_SWRST0 << cpu;
> + write_c0_corectrl(ctrl);
> +
> + /* ungate core clock */
> + if (cpu_clock_gates[cpu])
> + clk_enable(cpu_clock_gates[cpu]);
> +
> + /* set entry sp/gp register values */
> + jz4780_cpu_entry_sp = __KSTK_TOS(idle);
> + jz4780_cpu_entry_gp = (u32)task_thread_info(idle);
> + smp_wmb();
> +
> + /* take the core out of reset */
> + ctrl &= ~(CORECTRL_SWRST0 << cpu);
> + write_c0_corectrl(ctrl);
> +
> + cpumask_set_cpu(cpu, &cpu_running);
> +
> + spin_unlock_irqrestore(&smp_lock, flags);
> +
> + return 0;
> +}
> +
> +static void jz4780_init_secondary(void)
> +{
> +}
> +
> +static void jz4780_smp_finish(void)
> +{
> + u32 reim;
> +
> + spin_lock(&smp_lock);
> +
> + /* unmask mailbox interrupts for this core */
> + reim = read_c0_reim();
> + reim |= REIM_MBOXIRQ0M << smp_processor_id();
> + write_c0_reim(reim);
> +
> + spin_unlock(&smp_lock);
> +
> + /* unmask interrupts for this core */
> + change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP2 |
> + STATUSF_IP1 | STATUSF_IP0);
> + irq_enable_hazard();
> +
> + /* force broadcast timer */
> + tick_broadcast_force();
> +}
> +
> +static void jz4780_send_ipi_single_locked(int cpu, unsigned int
> action)
> +{
> + u32 mbox;
> +
> + switch (cpu) {
> + case 0:
> + mbox = read_c0_mailbox0();
> + write_c0_mailbox0(mbox | action);
> + break;
> + case 1:
> + mbox = read_c0_mailbox1();
> + write_c0_mailbox1(mbox | action);
> + break;
> + default:
> + panic("unhandled cpu %d!", cpu);
> + }
> +}
> +
> +static void jz4780_send_ipi_single(int cpu, unsigned int action)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&smp_lock, flags);
> + jz4780_send_ipi_single_locked(cpu, action);
> + spin_unlock_irqrestore(&smp_lock, flags);
> +}
> +
> +static void jz4780_send_ipi_mask(const struct cpumask *mask,
> + unsigned int action)
> +{
> + unsigned long flags;
> + int cpu;
> +
> + spin_lock_irqsave(&smp_lock, flags);
> +
> + for_each_cpu(cpu, mask)
> + jz4780_send_ipi_single_locked(cpu, action);
> +
> + spin_unlock_irqrestore(&smp_lock, flags);
> +}
> +
> +static struct plat_smp_ops jz4780_smp_ops = {
> + .send_ipi_single = jz4780_send_ipi_single,
> + .send_ipi_mask = jz4780_send_ipi_mask,
> + .init_secondary = jz4780_init_secondary,
> + .smp_finish = jz4780_smp_finish,
> + .boot_secondary = jz4780_boot_secondary,
> + .smp_setup = jz4780_smp_setup,
> + .prepare_cpus = jz4780_smp_prepare_cpus,
> +};
> +
> +void jz4780_smp_init(void)
> +{
> + register_smp_ops(&jz4780_smp_ops);
> +}
> diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c
> index 37f8e78..a406de3 100644
> --- a/arch/mips/kernel/idle.c
> +++ b/arch/mips/kernel/idle.c
> @@ -19,6 +19,10 @@
> #include <asm/idle.h>
> #include <asm/mipsregs.h>
>
> +#ifdef CONFIG_MACH_JZ4780
> +# include <asm/mach-jz4740/jz4780-smp.h>
> +#endif
> +
> /*
> * Not all of the MIPS CPUs have the "wait" instruction available.
> Moreover,
> * the implementation of the "wait" feature differs between CPU
> families. This
> @@ -172,7 +176,6 @@ void __init check_wait(void)
> case CPU_CAVIUM_OCTEON_PLUS:
> case CPU_CAVIUM_OCTEON2:
> case CPU_CAVIUM_OCTEON3:
> - case CPU_XBURST:
> case CPU_LOONGSON32:
> case CPU_XLR:
> case CPU_XLP:
> @@ -246,6 +249,15 @@ void __init check_wait(void)
> cpu_wait = r4k_wait;
> */
> break;
> + case CPU_XBURST:
> +#if defined(CONFIG_MACH_JZ4780) && defined(CONFIG_SMP)
> + if (NR_CPUS > 1)
> + cpu_wait = jz4780_smp_wait_irqoff;
> + else
> + cpu_wait = r4k_wait;
> +#else
> + cpu_wait = r4k_wait;
> +#endif
> default:
> break;
> }
> diff --git a/drivers/clk/ingenic/jz4780-cgu.c
> b/drivers/clk/ingenic/jz4780-cgu.c
> index d07fff1..4f81819 100644
> --- a/drivers/clk/ingenic/jz4780-cgu.c
> +++ b/drivers/clk/ingenic/jz4780-cgu.c
> @@ -16,7 +16,7 @@
>
> /* CGU register offsets */
> #define CGU_REG_CLOCKCONTROL 0x00
> -#define CGU_REG_PLLCONTROL 0x0c
> +#define CGU_REG_LCR 0x04
> #define CGU_REG_APLL 0x10
> #define CGU_REG_MPLL 0x14
> #define CGU_REG_EPLL 0x18
> @@ -46,8 +46,8 @@
> #define CGU_REG_CLOCKSTATUS 0xd4
>
> /* bits within the OPCR register */
> -#define OPCR_SPENDN0 (1 << 7)
> -#define OPCR_SPENDN1 (1 << 6)
> +#define OPCR_SPENDN0 BIT(7)
> +#define OPCR_SPENDN1 BIT(6)
>
> /* bits within the USBPCR register */
> #define USBPCR_USB_MODE BIT(31)
> @@ -88,6 +88,13 @@
> #define USBVBFIL_IDDIGFIL_MASK (0xffff << USBVBFIL_IDDIGFIL_SHIFT)
> #define USBVBFIL_USBVBFIL_MASK (0xffff)
>
> +/* bits within the LCR register */
> +#define LCR_PD_SCPU BIT(31)
> +#define LCR_SCPUS BIT(27)
> +
> +/* bits within the CLKGR1 register */
> +#define CLKGR1_CORE1 BIT(15)
> +
> static struct ingenic_cgu *cgu;
>
> static u8 jz4780_otg_phy_get_parent(struct clk_hw *hw)
> @@ -205,6 +212,47 @@ static const struct clk_ops jz4780_otg_phy_ops =
> {
> .set_rate = jz4780_otg_phy_set_rate,
> };
>
> +static int jz4780_core1_enable(struct clk_hw *hw)
> +{
> + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
> + struct ingenic_cgu *cgu = ingenic_clk->cgu;
> + const unsigned int timeout = 100;
> + unsigned long flags;
> + unsigned int i;
> + u32 lcr, clkgr1;
> +
> + spin_lock_irqsave(&cgu->lock, flags);
> +
> + lcr = readl(cgu->base + CGU_REG_LCR);
> + lcr &= ~LCR_PD_SCPU;
> + writel(lcr, cgu->base + CGU_REG_LCR);
> +
> + clkgr1 = readl(cgu->base + CGU_REG_CLKGR1);
> + clkgr1 &= ~CLKGR1_CORE1;
> + writel(clkgr1, cgu->base + CGU_REG_CLKGR1);
> +
> + spin_unlock_irqrestore(&cgu->lock, flags);
> +
> + /* wait for the CPU to be powered up */
> + for (i = 0; i < timeout; i++) {
> + lcr = readl(cgu->base + CGU_REG_LCR);
> + if (!(lcr & LCR_SCPUS))
> + break;
> + mdelay(1);
> + }
> +
> + if (i == timeout) {
> + pr_err("%s: Wait for power up core1 timeout\n", __func__);
> + return -EBUSY;
> + }
> +
> + return 0;
> +}
> +
> +static const struct clk_ops jz4780_core1_ops = {
> + .enable = jz4780_core1_enable,
> +};
> +
> static const s8 pll_od_encoding[16] = {
> 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
> 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
> @@ -701,9 +749,9 @@ static const struct ingenic_cgu_clk_info
> jz4780_cgu_clocks[] = {
> },
>
> [JZ4780_CLK_CORE1] = {
> - "core1", CGU_CLK_GATE,
> + "core1", CGU_CLK_CUSTOM,
> .parents = { JZ4780_CLK_CPU, -1, -1, -1 },
> - .gate = { CGU_REG_CLKGR1, 15 },
> + .custom = { &jz4780_core1_ops },
> },
>
> };
> --
> 2.7.4
>


2020-02-14 18:32:20

by Paul Cercueil

[permalink] [raw]
Subject: Re: [PATCH v4 2/6] clocksource: Ingenic: Add high resolution timer support for SMP.

Hi Zhou,


Le sam., févr. 15, 2020 at 02:02, 周琰杰 (Zhou Yanjie)
<[email protected]> a écrit :
> Enable clock event handling on per CPU core basis.
> Make sure that interrupts raised on the first core execute
> event handlers on the correct CPU core.
>
> Tested-by: H. Nikolaus Schaller <[email protected]>
> Tested-by: Paul Boddie <[email protected]>
> Signed-off-by: 周琰杰 (Zhou Yanjie) <[email protected]>
> ---
>
> Notes:
> v1->v2:
> 1.Adjust function naming to make it more reasonable.
> 2.Replace function smp_call_function_single() with
> smp_call_function_single_async() in order to resolve
> the warning below:
>
> [ 0.350942] smp: Brought up 1 node, 2 CPUs
> [ 0.365497] ------------[ cut here ]------------
> [ 0.365522] WARNING: CPU: 0 PID: 1 at kernel/smp.c:300
> smp_call_function_single+0x110/0x200
> [ 0.365533] CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> 5.5.0-rc1+ #5
> [ 0.365537] Stack : 00000000 59c73bcd 00000037 80074e80
> 80000000 80670000 805a0000 80620590
> [ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000
> 80670000 00000001 8fc0dc20 59c73bcd
> [ 0.365574] 00000000 00000000 806f0000 80670000
> 00000000 806dab00 00000000 2d302e35
> [ 0.365591] 203a6d6d 806e0000 806e0000 70617773
> 80670000 00000000 00000000 00000009
> [ 0.365610] 00000000 8fc94e20 8fc0de30 80690000
> 00000018 803592dc 00000000 806d0000
> [ 0.365627] ...
> [ 0.365634] Call Trace:
> [ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
> [ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
> [ 0.365673] [<8003044c>] __warn+0xc4/0xe8
> [ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
> [ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
> [ 0.365703] ---[ end trace 5785856ca39c79d5 ]---
> [ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000
> 80670000 00000001 8fc0dc20 59c73bcd
> [ 0.365574] 00000000 00000000 806f0000 80670000
> 00000000 806dab00 00000000 2d302e35
> [ 0.365591] 203a6d6d 806e0000 806e0000 70617773
> 80670000 00000000 00000000 00000009
> [ 0.365610] 00000000 8fc94e20 8fc0de30 80690000
> 00000018 803592dc 00000000 806d0000
> [ 0.365627] ...
> [ 0.365634] Call Trace:
> [ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
> [ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
> [ 0.365673] [<8003044c>] __warn+0xc4/0xe8
> [ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
> [ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
> [ 0.365703] ---[ end trace 5785856ca39c79d5 ]---
>
> v2->v3:
> No Change.
>
> v3->v4:
> Rebase on top of kernel 5.6-rc1.
>
> drivers/clocksource/ingenic-timer.c | 200
> ++++++++++++++++++++++++------------
> 1 file changed, 134 insertions(+), 66 deletions(-)
>
> diff --git a/drivers/clocksource/ingenic-timer.c
> b/drivers/clocksource/ingenic-timer.c
> index 4bbdb3d..9127c73 100644
> --- a/drivers/clocksource/ingenic-timer.c
> +++ b/drivers/clocksource/ingenic-timer.c
> @@ -1,7 +1,8 @@
> // SPDX-License-Identifier: GPL-2.0
> /*
> - * JZ47xx SoCs TCU IRQ driver
> + * XBurst SoCs TCU IRQ driver
> * Copyright (C) 2019 Paul Cercueil <[email protected]>
> + * Copyright (C) 2020 周琰杰 (Zhou Yanjie)
> <[email protected]>
> */
>
> #include <linux/bitops.h>
> @@ -21,18 +22,23 @@
>
> #include <dt-bindings/clock/ingenic,tcu.h>
>
> +static DEFINE_PER_CPU(call_single_data_t, ingenic_cevt_csd);
> +
> struct ingenic_soc_info {
> unsigned int num_channels;
> };
>
> struct ingenic_tcu {
> struct regmap *map;
> + struct device_node *np;
> struct clk *timer_clk, *cs_clk;
> + unsigned int timer_local[NR_CPUS];
> unsigned int timer_channel, cs_channel;
> struct clock_event_device cevt;
> struct clocksource cs;
> - char name[4];
> + char name[16];
> unsigned long pwm_channels_mask;
> + int cpu;
> };
>
> static struct ingenic_tcu *ingenic_tcu;
> @@ -81,6 +87,25 @@ static int ingenic_tcu_cevt_set_next(unsigned long
> next,
> return 0;
> }
>
> +static void ingenic_per_cpu_event_handler(void *info)
> +{
> + struct clock_event_device *cevt = (struct clock_event_device *)
> info;
> +
> + if (cevt->event_handler)
> + cevt->event_handler(cevt);
> +}
> +
> +static void ingenic_tcu_per_cpu_cb(struct clock_event_device *evt)
> +{
> + struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
> + call_single_data_t *csd;
> +
> + csd = &per_cpu(ingenic_cevt_csd, tcu->cpu);
> + csd->info = (void *) evt;
> + csd->func = ingenic_per_cpu_event_handler;
> + smp_call_function_single_async(tcu->cpu, csd);
> +}
> +
> static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
> {
> struct clock_event_device *evt = dev_id;
> @@ -88,8 +113,7 @@ static irqreturn_t ingenic_tcu_cevt_cb(int irq,
> void *dev_id)
>
> regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
>
> - if (evt->event_handler)
> - evt->event_handler(evt);
> + ingenic_tcu_per_cpu_cb(evt);

I guess the check for (evt->event_handler) is still valid here. There's
no point in calling ingenic_tcu_per_cpu_cb() if in the end the handler
won't do anything.

>
> return IRQ_HANDLED;
> }
> @@ -105,14 +129,73 @@ static struct clk * __init
> ingenic_tcu_get_clock(struct device_node *np, int id)
> return of_clk_get_from_provider(&args);
> }
>
> -static int __init ingenic_tcu_timer_init(struct device_node *np,
> - struct ingenic_tcu *tcu)
> +static int __init ingenic_tcu_clocksource_init(struct device_node
> *np,
> + struct ingenic_tcu *tcu)

Please - don't move functions around. It makes the patch extremely hard
to review. Just add a forward declaration for
ingenic_tcu_clocksource_init() at the top of the file.

> +{
> + unsigned int channel = tcu->cs_channel;
> + struct clocksource *cs = &tcu->cs;
> + unsigned long rate;
> + int err;
> +
> + tcu->cs_clk = ingenic_tcu_get_clock(np, channel);
> + if (IS_ERR(tcu->cs_clk))
> + return PTR_ERR(tcu->cs_clk);
> +
> + err = clk_prepare_enable(tcu->cs_clk);
> + if (err)
> + goto err_clk_put;
> +
> + rate = clk_get_rate(tcu->cs_clk);
> + if (!rate) {
> + err = -EINVAL;
> + goto err_clk_disable;
> + }
> +
> + /* Reset channel */
> + regmap_update_bits(tcu->map, TCU_REG_TCSRc(channel),
> + 0xffff & ~TCU_TCSR_RESERVED_BITS, 0);
> +
> + /* Reset counter */
> + regmap_write(tcu->map, TCU_REG_TDFRc(channel), 0xffff);
> + regmap_write(tcu->map, TCU_REG_TCNTc(channel), 0);
> +
> + /* Enable channel */
> + regmap_write(tcu->map, TCU_REG_TESR, BIT(channel));
> +
> + cs->name = "ingenic-timer";
> + cs->rating = 200;
> + cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
> + cs->mask = CLOCKSOURCE_MASK(16);
> + cs->read = ingenic_tcu_timer_cs_read;
> +
> + err = clocksource_register_hz(cs, rate);
> + if (err)
> + goto err_clk_disable;
> +
> + return 0;
> +
> +err_clk_disable:
> + clk_disable_unprepare(tcu->cs_clk);
> +err_clk_put:
> + clk_put(tcu->cs_clk);
> + return err;
> +}
> +
> +static int ingenic_tcu_setup_per_cpu_cevt(struct device_node *np,
> + unsigned int channel)
> {
> - unsigned int timer_virq, channel = tcu->timer_channel;
> + unsigned int timer_virq;
> struct irq_domain *domain;
> + struct ingenic_tcu *tcu;
> unsigned long rate;
> int err;
>
> + tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);
> + if (!tcu)
> + return -ENOMEM;
> +
> + tcu->map = ingenic_tcu->map;
> +
> tcu->timer_clk = ingenic_tcu_get_clock(np, channel);
> if (IS_ERR(tcu->timer_clk))
> return PTR_ERR(tcu->timer_clk);
> @@ -139,13 +222,15 @@ static int __init ingenic_tcu_timer_init(struct
> device_node *np,
> goto err_clk_disable;
> }
>
> - snprintf(tcu->name, sizeof(tcu->name), "TCU");
> + snprintf(tcu->name, sizeof(tcu->name), "TCU channel.%u", channel);

Make it TCU%u

>
> err = request_irq(timer_virq, ingenic_tcu_cevt_cb, IRQF_TIMER,
> tcu->name, &tcu->cevt);
> if (err)
> goto err_irq_dispose_mapping;
>
> + tcu->cpu = smp_processor_id();
> + tcu->timer_channel = channel;
> tcu->cevt.cpumask = cpumask_of(smp_processor_id());
> tcu->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
> tcu->cevt.name = tcu->name;
> @@ -166,56 +251,23 @@ static int __init ingenic_tcu_timer_init(struct
> device_node *np,
> return err;
> }
>
> -static int __init ingenic_tcu_clocksource_init(struct device_node
> *np,
> - struct ingenic_tcu *tcu)
> +static int ingenic_tcu_setup_cevt(unsigned int cpu)
> {
> - unsigned int channel = tcu->cs_channel;
> - struct clocksource *cs = &tcu->cs;
> - unsigned long rate;
> - int err;
> -
> - tcu->cs_clk = ingenic_tcu_get_clock(np, channel);
> - if (IS_ERR(tcu->cs_clk))
> - return PTR_ERR(tcu->cs_clk);
> -
> - err = clk_prepare_enable(tcu->cs_clk);
> - if (err)
> - goto err_clk_put;
> -
> - rate = clk_get_rate(tcu->cs_clk);
> - if (!rate) {
> - err = -EINVAL;
> - goto err_clk_disable;
> - }
> -
> - /* Reset channel */
> - regmap_update_bits(tcu->map, TCU_REG_TCSRc(channel),
> - 0xffff & ~TCU_TCSR_RESERVED_BITS, 0);
> -
> - /* Reset counter */
> - regmap_write(tcu->map, TCU_REG_TDFRc(channel), 0xffff);
> - regmap_write(tcu->map, TCU_REG_TCNTc(channel), 0);
> -
> - /* Enable channel */
> - regmap_write(tcu->map, TCU_REG_TESR, BIT(channel));
> -
> - cs->name = "ingenic-timer";
> - cs->rating = 200;
> - cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
> - cs->mask = CLOCKSOURCE_MASK(16);
> - cs->read = ingenic_tcu_timer_cs_read;
> + int ret;
>
> - err = clocksource_register_hz(cs, rate);
> - if (err)
> - goto err_clk_disable;
> + ret = ingenic_tcu_setup_per_cpu_cevt(ingenic_tcu->np,
> + ingenic_tcu->timer_local[cpu]);
> + if (ret)
> + goto err_tcu_clocksource_cleanup;
>
> return 0;
>
> -err_clk_disable:
> - clk_disable_unprepare(tcu->cs_clk);
> -err_clk_put:
> - clk_put(tcu->cs_clk);
> - return err;
> +err_tcu_clocksource_cleanup:
> + clocksource_unregister(&ingenic_tcu->cs);
> + clk_disable_unprepare(ingenic_tcu->cs_clk);
> + clk_put(ingenic_tcu->cs_clk);
> + kfree(ingenic_tcu);
> + return ret;
> }
>
> static const struct ingenic_soc_info jz4740_soc_info = {
> @@ -239,6 +291,7 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
> const struct ingenic_soc_info *soc_info = id->data;
> struct ingenic_tcu *tcu;
> struct regmap *map;
> + unsigned cpu = 0;
> long rate;
> int ret;
>
> @@ -253,12 +306,14 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
> return -ENOMEM;
>
> /* Enable all TCU channels for PWM use by default except channels
> 0/1 */
> - tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1, 2);
> + tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1,
> + NR_CPUS + 1);
> of_property_read_u32(np, "ingenic,pwm-channels-mask",
> (u32 *)&tcu->pwm_channels_mask);
>
> /* Verify that we have at least two free channels */
> - if (hweight8(tcu->pwm_channels_mask) > soc_info->num_channels - 2) {
> + if (hweight8(tcu->pwm_channels_mask) >
> + soc_info->num_channels - NR_CPUS + 1) {
> pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__,
> tcu->pwm_channels_mask);
> ret = -EINVAL;
> @@ -266,13 +321,29 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
> }
>
> tcu->map = map;
> + tcu->np = np;
> ingenic_tcu = tcu;
>
> - tcu->timer_channel = find_first_zero_bit(&tcu->pwm_channels_mask,
> + tcu->timer_local[cpu] = find_first_zero_bit(&tcu->pwm_channels_mask,
> soc_info->num_channels);
> - tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
> - soc_info->num_channels,
> - tcu->timer_channel + 1);
> +
> + if (NR_CPUS > 1) {
> + for (cpu = 1; cpu < NR_CPUS; cpu++)
> + tcu->timer_local[cpu] = find_next_zero_bit(
> + &tcu->pwm_channels_mask,
> + soc_info->num_channels,
> + tcu->timer_local[cpu - 1] + 1);
> +
> + tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
> + soc_info->num_channels,
> + tcu->timer_local[cpu-1] + 1);
> + } else {
> + tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
> + soc_info->num_channels,
> + tcu->timer_local[cpu] + 1);
> + }
> +
> +
>
> ret = ingenic_tcu_clocksource_init(np, tcu);
> if (ret) {
> @@ -280,9 +351,10 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
> goto err_free_ingenic_tcu;
> }
>
> - ret = ingenic_tcu_timer_init(np, tcu);
> - if (ret)
> - goto err_tcu_clocksource_cleanup;
> + /* Setup clock events on each CPU core */
> + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Ingenic XBurst:
> online",
> + ingenic_tcu_setup_cevt, NULL);
> + WARN_ON(ret < 0);
>
> /* Register the sched_clock at the end as there's no way to undo it
> */
> rate = clk_get_rate(tcu->cs_clk);
> @@ -290,10 +362,6 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
>
> return 0;
>
> -err_tcu_clocksource_cleanup:
> - clocksource_unregister(&tcu->cs);
> - clk_disable_unprepare(tcu->cs_clk);
> - clk_put(tcu->cs_clk);
> err_free_ingenic_tcu:
> kfree(tcu);
> return ret;
> --
> 2.7.4
>


2020-02-15 14:06:13

by Zhou Yanjie

[permalink] [raw]
Subject: Re: [PATCH v4 2/6] clocksource: Ingenic: Add high resolution timer support for SMP.

Hi Paul,

On 2020年02月15日 02:31, Paul Cercueil wrote:
> Hi Zhou,
>
>
> Le sam., févr. 15, 2020 at 02:02, 周琰杰 (Zhou Yanjie)
> <[email protected]> a écrit :
>> Enable clock event handling on per CPU core basis.
>> Make sure that interrupts raised on the first core execute
>> event handlers on the correct CPU core.
>>
>> Tested-by: H. Nikolaus Schaller <[email protected]>
>> Tested-by: Paul Boddie <[email protected]>
>> Signed-off-by: 周琰杰 (Zhou Yanjie) <[email protected]>
>> ---
>>
>> Notes:
>> v1->v2:
>> 1.Adjust function naming to make it more reasonable.
>> 2.Replace function smp_call_function_single() with
>> smp_call_function_single_async() in order to resolve
>> the warning below:
>>
>> [ 0.350942] smp: Brought up 1 node, 2 CPUs
>> [ 0.365497] ------------[ cut here ]------------
>> [ 0.365522] WARNING: CPU: 0 PID: 1 at kernel/smp.c:300
>> smp_call_function_single+0x110/0x200
>> [ 0.365533] CPU: 0 PID: 1 Comm: swapper/0 Not tainted
>> 5.5.0-rc1+ #5
>> [ 0.365537] Stack : 00000000 59c73bcd 00000037 80074e80
>> 80000000 80670000 805a0000 80620590
>> [ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000
>> 80670000 00000001 8fc0dc20 59c73bcd
>> [ 0.365574] 00000000 00000000 806f0000 80670000
>> 00000000 806dab00 00000000 2d302e35
>> [ 0.365591] 203a6d6d 806e0000 806e0000 70617773
>> 80670000 00000000 00000000 00000009
>> [ 0.365610] 00000000 8fc94e20 8fc0de30 80690000
>> 00000018 803592dc 00000000 806d0000
>> [ 0.365627] ...
>> [ 0.365634] Call Trace:
>> [ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
>> [ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
>> [ 0.365673] [<8003044c>] __warn+0xc4/0xe8
>> [ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
>> [ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
>> [ 0.365703] ---[ end trace 5785856ca39c79d5 ]---
>> [ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000
>> 80670000 00000001 8fc0dc20 59c73bcd
>> [ 0.365574] 00000000 00000000 806f0000 80670000
>> 00000000 806dab00 00000000 2d302e35
>> [ 0.365591] 203a6d6d 806e0000 806e0000 70617773
>> 80670000 00000000 00000000 00000009
>> [ 0.365610] 00000000 8fc94e20 8fc0de30 80690000
>> 00000018 803592dc 00000000 806d0000
>> [ 0.365627] ...
>> [ 0.365634] Call Trace:
>> [ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
>> [ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
>> [ 0.365673] [<8003044c>] __warn+0xc4/0xe8
>> [ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
>> [ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
>> [ 0.365703] ---[ end trace 5785856ca39c79d5 ]---
>>
>> v2->v3:
>> No Change.
>>
>> v3->v4:
>> Rebase on top of kernel 5.6-rc1.
>>
>> drivers/clocksource/ingenic-timer.c | 200
>> ++++++++++++++++++++++++------------
>> 1 file changed, 134 insertions(+), 66 deletions(-)
>>
>> diff --git a/drivers/clocksource/ingenic-timer.c
>> b/drivers/clocksource/ingenic-timer.c
>> index 4bbdb3d..9127c73 100644
>> --- a/drivers/clocksource/ingenic-timer.c
>> +++ b/drivers/clocksource/ingenic-timer.c
>> @@ -1,7 +1,8 @@
>> // SPDX-License-Identifier: GPL-2.0
>> /*
>> - * JZ47xx SoCs TCU IRQ driver
>> + * XBurst SoCs TCU IRQ driver
>> * Copyright (C) 2019 Paul Cercueil <[email protected]>
>> + * Copyright (C) 2020 周琰杰 (Zhou Yanjie) <[email protected]>
>> */
>>
>> #include <linux/bitops.h>
>> @@ -21,18 +22,23 @@
>>
>> #include <dt-bindings/clock/ingenic,tcu.h>
>>
>> +static DEFINE_PER_CPU(call_single_data_t, ingenic_cevt_csd);
>> +
>> struct ingenic_soc_info {
>> unsigned int num_channels;
>> };
>>
>> struct ingenic_tcu {
>> struct regmap *map;
>> + struct device_node *np;
>> struct clk *timer_clk, *cs_clk;
>> + unsigned int timer_local[NR_CPUS];
>> unsigned int timer_channel, cs_channel;
>> struct clock_event_device cevt;
>> struct clocksource cs;
>> - char name[4];
>> + char name[16];
>> unsigned long pwm_channels_mask;
>> + int cpu;
>> };
>>
>> static struct ingenic_tcu *ingenic_tcu;
>> @@ -81,6 +87,25 @@ static int ingenic_tcu_cevt_set_next(unsigned long
>> next,
>> return 0;
>> }
>>
>> +static void ingenic_per_cpu_event_handler(void *info)
>> +{
>> + struct clock_event_device *cevt = (struct clock_event_device *)
>> info;
>> +
>> + if (cevt->event_handler)
>> + cevt->event_handler(cevt);
>> +}
>> +
>> +static void ingenic_tcu_per_cpu_cb(struct clock_event_device *evt)
>> +{
>> + struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
>> + call_single_data_t *csd;
>> +
>> + csd = &per_cpu(ingenic_cevt_csd, tcu->cpu);
>> + csd->info = (void *) evt;
>> + csd->func = ingenic_per_cpu_event_handler;
>> + smp_call_function_single_async(tcu->cpu, csd);
>> +}
>> +
>> static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
>> {
>> struct clock_event_device *evt = dev_id;
>> @@ -88,8 +113,7 @@ static irqreturn_t ingenic_tcu_cevt_cb(int irq,
>> void *dev_id)
>>
>> regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
>>
>> - if (evt->event_handler)
>> - evt->event_handler(evt);
>> + ingenic_tcu_per_cpu_cb(evt);
>
> I guess the check for (evt->event_handler) is still valid here.
> There's no point in calling ingenic_tcu_per_cpu_cb() if in the end the
> handler won't do anything.
>

Sure, I will fix it in v5.

>>
>> return IRQ_HANDLED;
>> }
>> @@ -105,14 +129,73 @@ static struct clk * __init
>> ingenic_tcu_get_clock(struct device_node *np, int id)
>> return of_clk_get_from_provider(&args);
>> }
>>
>> -static int __init ingenic_tcu_timer_init(struct device_node *np,
>> - struct ingenic_tcu *tcu)
>> +static int __init ingenic_tcu_clocksource_init(struct device_node *np,
>> + struct ingenic_tcu *tcu)
>
> Please - don't move functions around. It makes the patch extremely
> hard to review. Just add a forward declaration for
> ingenic_tcu_clocksource_init() at the top of the file.

Sure.

>
>> +{
>> + unsigned int channel = tcu->cs_channel;
>> + struct clocksource *cs = &tcu->cs;
>> + unsigned long rate;
>> + int err;
>> +
>> + tcu->cs_clk = ingenic_tcu_get_clock(np, channel);
>> + if (IS_ERR(tcu->cs_clk))
>> + return PTR_ERR(tcu->cs_clk);
>> +
>> + err = clk_prepare_enable(tcu->cs_clk);
>> + if (err)
>> + goto err_clk_put;
>> +
>> + rate = clk_get_rate(tcu->cs_clk);
>> + if (!rate) {
>> + err = -EINVAL;
>> + goto err_clk_disable;
>> + }
>> +
>> + /* Reset channel */
>> + regmap_update_bits(tcu->map, TCU_REG_TCSRc(channel),
>> + 0xffff & ~TCU_TCSR_RESERVED_BITS, 0);
>> +
>> + /* Reset counter */
>> + regmap_write(tcu->map, TCU_REG_TDFRc(channel), 0xffff);
>> + regmap_write(tcu->map, TCU_REG_TCNTc(channel), 0);
>> +
>> + /* Enable channel */
>> + regmap_write(tcu->map, TCU_REG_TESR, BIT(channel));
>> +
>> + cs->name = "ingenic-timer";
>> + cs->rating = 200;
>> + cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
>> + cs->mask = CLOCKSOURCE_MASK(16);
>> + cs->read = ingenic_tcu_timer_cs_read;
>> +
>> + err = clocksource_register_hz(cs, rate);
>> + if (err)
>> + goto err_clk_disable;
>> +
>> + return 0;
>> +
>> +err_clk_disable:
>> + clk_disable_unprepare(tcu->cs_clk);
>> +err_clk_put:
>> + clk_put(tcu->cs_clk);
>> + return err;
>> +}
>> +
>> +static int ingenic_tcu_setup_per_cpu_cevt(struct device_node *np,
>> + unsigned int channel)
>> {
>> - unsigned int timer_virq, channel = tcu->timer_channel;
>> + unsigned int timer_virq;
>> struct irq_domain *domain;
>> + struct ingenic_tcu *tcu;
>> unsigned long rate;
>> int err;
>>
>> + tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);
>> + if (!tcu)
>> + return -ENOMEM;
>> +
>> + tcu->map = ingenic_tcu->map;
>> +
>> tcu->timer_clk = ingenic_tcu_get_clock(np, channel);
>> if (IS_ERR(tcu->timer_clk))
>> return PTR_ERR(tcu->timer_clk);
>> @@ -139,13 +222,15 @@ static int __init ingenic_tcu_timer_init(struct
>> device_node *np,
>> goto err_clk_disable;
>> }
>>
>> - snprintf(tcu->name, sizeof(tcu->name), "TCU");
>> + snprintf(tcu->name, sizeof(tcu->name), "TCU channel.%u", channel);
>
> Make it TCU%u
>

OK.

>>
>> err = request_irq(timer_virq, ingenic_tcu_cevt_cb, IRQF_TIMER,
>> tcu->name, &tcu->cevt);
>> if (err)
>> goto err_irq_dispose_mapping;
>>
>> + tcu->cpu = smp_processor_id();
>> + tcu->timer_channel = channel;
>> tcu->cevt.cpumask = cpumask_of(smp_processor_id());
>> tcu->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
>> tcu->cevt.name = tcu->name;
>> @@ -166,56 +251,23 @@ static int __init ingenic_tcu_timer_init(struct
>> device_node *np,
>> return err;
>> }
>>
>> -static int __init ingenic_tcu_clocksource_init(struct device_node *np,
>> - struct ingenic_tcu *tcu)
>> +static int ingenic_tcu_setup_cevt(unsigned int cpu)
>> {
>> - unsigned int channel = tcu->cs_channel;
>> - struct clocksource *cs = &tcu->cs;
>> - unsigned long rate;
>> - int err;
>> -
>> - tcu->cs_clk = ingenic_tcu_get_clock(np, channel);
>> - if (IS_ERR(tcu->cs_clk))
>> - return PTR_ERR(tcu->cs_clk);
>> -
>> - err = clk_prepare_enable(tcu->cs_clk);
>> - if (err)
>> - goto err_clk_put;
>> -
>> - rate = clk_get_rate(tcu->cs_clk);
>> - if (!rate) {
>> - err = -EINVAL;
>> - goto err_clk_disable;
>> - }
>> -
>> - /* Reset channel */
>> - regmap_update_bits(tcu->map, TCU_REG_TCSRc(channel),
>> - 0xffff & ~TCU_TCSR_RESERVED_BITS, 0);
>> -
>> - /* Reset counter */
>> - regmap_write(tcu->map, TCU_REG_TDFRc(channel), 0xffff);
>> - regmap_write(tcu->map, TCU_REG_TCNTc(channel), 0);
>> -
>> - /* Enable channel */
>> - regmap_write(tcu->map, TCU_REG_TESR, BIT(channel));
>> -
>> - cs->name = "ingenic-timer";
>> - cs->rating = 200;
>> - cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
>> - cs->mask = CLOCKSOURCE_MASK(16);
>> - cs->read = ingenic_tcu_timer_cs_read;
>> + int ret;
>>
>> - err = clocksource_register_hz(cs, rate);
>> - if (err)
>> - goto err_clk_disable;
>> + ret = ingenic_tcu_setup_per_cpu_cevt(ingenic_tcu->np,
>> + ingenic_tcu->timer_local[cpu]);
>> + if (ret)
>> + goto err_tcu_clocksource_cleanup;
>>
>> return 0;
>>
>> -err_clk_disable:
>> - clk_disable_unprepare(tcu->cs_clk);
>> -err_clk_put:
>> - clk_put(tcu->cs_clk);
>> - return err;
>> +err_tcu_clocksource_cleanup:
>> + clocksource_unregister(&ingenic_tcu->cs);
>> + clk_disable_unprepare(ingenic_tcu->cs_clk);
>> + clk_put(ingenic_tcu->cs_clk);
>> + kfree(ingenic_tcu);
>> + return ret;
>> }
>>
>> static const struct ingenic_soc_info jz4740_soc_info = {
>> @@ -239,6 +291,7 @@ static int __init ingenic_tcu_init(struct
>> device_node *np)
>> const struct ingenic_soc_info *soc_info = id->data;
>> struct ingenic_tcu *tcu;
>> struct regmap *map;
>> + unsigned cpu = 0;
>> long rate;
>> int ret;
>>
>> @@ -253,12 +306,14 @@ static int __init ingenic_tcu_init(struct
>> device_node *np)
>> return -ENOMEM;
>>
>> /* Enable all TCU channels for PWM use by default except
>> channels 0/1 */
>> - tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1, 2);
>> + tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1,
>> + NR_CPUS + 1);
>> of_property_read_u32(np, "ingenic,pwm-channels-mask",
>> (u32 *)&tcu->pwm_channels_mask);
>>
>> /* Verify that we have at least two free channels */
>> - if (hweight8(tcu->pwm_channels_mask) > soc_info->num_channels -
>> 2) {
>> + if (hweight8(tcu->pwm_channels_mask) >
>> + soc_info->num_channels - NR_CPUS + 1) {
>> pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__,
>> tcu->pwm_channels_mask);
>> ret = -EINVAL;
>> @@ -266,13 +321,29 @@ static int __init ingenic_tcu_init(struct
>> device_node *np)
>> }
>>
>> tcu->map = map;
>> + tcu->np = np;
>> ingenic_tcu = tcu;
>>
>> - tcu->timer_channel = find_first_zero_bit(&tcu->pwm_channels_mask,
>> + tcu->timer_local[cpu] =
>> find_first_zero_bit(&tcu->pwm_channels_mask,
>> soc_info->num_channels);
>> - tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
>> - soc_info->num_channels,
>> - tcu->timer_channel + 1);
>> +
>> + if (NR_CPUS > 1) {
>> + for (cpu = 1; cpu < NR_CPUS; cpu++)
>> + tcu->timer_local[cpu] = find_next_zero_bit(
>> + &tcu->pwm_channels_mask,
>> + soc_info->num_channels,
>> + tcu->timer_local[cpu - 1] + 1);
>> +
>> + tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
>> + soc_info->num_channels,
>> + tcu->timer_local[cpu-1] + 1);
>> + } else {
>> + tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
>> + soc_info->num_channels,
>> + tcu->timer_local[cpu] + 1);
>> + }
>> +
>> +
>>
>> ret = ingenic_tcu_clocksource_init(np, tcu);
>> if (ret) {
>> @@ -280,9 +351,10 @@ static int __init ingenic_tcu_init(struct
>> device_node *np)
>> goto err_free_ingenic_tcu;
>> }
>>
>> - ret = ingenic_tcu_timer_init(np, tcu);
>> - if (ret)
>> - goto err_tcu_clocksource_cleanup;
>> + /* Setup clock events on each CPU core */
>> + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Ingenic XBurst:
>> online",
>> + ingenic_tcu_setup_cevt, NULL);
>> + WARN_ON(ret < 0);
>>
>> /* Register the sched_clock at the end as there's no way to undo
>> it */
>> rate = clk_get_rate(tcu->cs_clk);
>> @@ -290,10 +362,6 @@ static int __init ingenic_tcu_init(struct
>> device_node *np)
>>
>> return 0;
>>
>> -err_tcu_clocksource_cleanup:
>> - clocksource_unregister(&tcu->cs);
>> - clk_disable_unprepare(tcu->cs_clk);
>> - clk_put(tcu->cs_clk);
>> err_free_ingenic_tcu:
>> kfree(tcu);
>> return ret;
>> --
>> 2.7.4
>>
>

2020-02-15 14:12:23

by Zhou Yanjie

[permalink] [raw]
Subject: Re: [PATCH v4 1/6] MIPS: JZ4780: Introduce SMP support.

Hi Paul,

On 2020年02月15日 02:21, Paul Cercueil wrote:
> Hi Zhou,
>
> The changes to drivers/clk/ingenic/jz4780-cgu.c look sound but they
> really don't belong in this patch.
>
> Please split them to another patch.

Sure., I will split them.

>
> -Paul
>
>
> Le sam., févr. 15, 2020 at 02:02, 周琰杰 (Zhou Yanjie)
> <[email protected]> a écrit :
>> Forward port smp support from kernel 3.18.3 of CI20_linux
>> to upstream kernel 5.5.
>>
>> Tested-by: H. Nikolaus Schaller <[email protected]>
>> Tested-by: Paul Boddie <[email protected]>
>> Signed-off-by: 周琰杰 (Zhou Yanjie) <[email protected]>
>> ---
>>
>> Notes:
>> v1->v2:
>> 1.Remove unnecessary "plat_irq_dispatch(void)" in irq-ingenic.c.
>> 2.Add a timeout check for "jz4780_boot_secondary()" to avoid a
>> dead loop.
>> 3.Replace hard code in smp.c with macro.
>>
>> v2->v3:
>> 1.Remove unnecessary "extern void (*r4k_blast_dcache)(void)" in
>> smp.c.
>> 2.Use "for_each_of_cpu_node" instead "for_each_compatible_node"
>> in smp.c.
>> 3.Use "of_cpu_node_to_id" instead "of_property_read_u32_index" in
>> smp.c.
>> 4.Move LCR related operations to jz4780-cgu.c.
>>
>> v3->v4:
>> Rebase on top of kernel 5.6-rc1.
>>
>> arch/mips/include/asm/mach-jz4740/jz4780-smp.h | 91 ++++++++
>> arch/mips/jz4740/Kconfig | 3 +
>> arch/mips/jz4740/Makefile | 5 +
>> arch/mips/jz4740/prom.c | 4 +
>> arch/mips/jz4740/smp-entry.S | 57 +++++
>> arch/mips/jz4740/smp.c | 286
>> +++++++++++++++++++++++++
>> arch/mips/kernel/idle.c | 14 +-
>> drivers/clk/ingenic/jz4780-cgu.c | 58 ++++-
>> 8 files changed, 512 insertions(+), 6 deletions(-)
>> create mode 100644 arch/mips/include/asm/mach-jz4740/jz4780-smp.h
>> create mode 100644 arch/mips/jz4740/smp-entry.S
>> create mode 100644 arch/mips/jz4740/smp.c
>>
>> diff --git a/arch/mips/include/asm/mach-jz4740/jz4780-smp.h
>> b/arch/mips/include/asm/mach-jz4740/jz4780-smp.h
>> new file mode 100644
>> index 00000000..3f592ce
>> --- /dev/null
>> +++ b/arch/mips/include/asm/mach-jz4740/jz4780-smp.h
>> @@ -0,0 +1,91 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright (C) 2013, Paul Burton <[email protected]>
>> + * JZ4780 SMP definitions
>> + */
>> +
>> +#ifndef __MIPS_ASM_MACH_JZ4740_JZ4780_SMP_H__
>> +#define __MIPS_ASM_MACH_JZ4740_JZ4780_SMP_H__
>> +
>> +#define read_c0_corectrl() __read_32bit_c0_register($12, 2)
>> +#define write_c0_corectrl(val) __write_32bit_c0_register($12, 2, val)
>> +
>> +#define read_c0_corestatus() __read_32bit_c0_register($12, 3)
>> +#define write_c0_corestatus(val) __write_32bit_c0_register($12, 3, val)
>> +
>> +#define read_c0_reim() __read_32bit_c0_register($12, 4)
>> +#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val)
>> +
>> +#define read_c0_mailbox0() __read_32bit_c0_register($20, 0)
>> +#define write_c0_mailbox0(val) __write_32bit_c0_register($20, 0, val)
>> +
>> +#define read_c0_mailbox1() __read_32bit_c0_register($20, 1)
>> +#define write_c0_mailbox1(val) __write_32bit_c0_register($20, 1, val)
>> +
>> +#define smp_clr_pending(mask) do { \
>> + unsigned int stat; \
>> + stat = read_c0_corestatus(); \
>> + stat &= ~((mask) & 0xff); \
>> + write_c0_corestatus(stat); \
>> + } while (0)
>> +
>> +/*
>> + * Core Control register
>> + */
>> +#define CORECTRL_SLEEP1M_SHIFT 17
>> +#define CORECTRL_SLEEP1M (_ULCAST_(0x1) << CORECTRL_SLEEP1M_SHIFT)
>> +#define CORECTRL_SLEEP0M_SHIFT 16
>> +#define CORECTRL_SLEEP0M (_ULCAST_(0x1) << CORECTRL_SLEEP0M_SHIFT)
>> +#define CORECTRL_RPC1_SHIFT 9
>> +#define CORECTRL_RPC1 (_ULCAST_(0x1) << CORECTRL_RPC1_SHIFT)
>> +#define CORECTRL_RPC0_SHIFT 8
>> +#define CORECTRL_RPC0 (_ULCAST_(0x1) << CORECTRL_RPC0_SHIFT)
>> +#define CORECTRL_SWRST1_SHIFT 1
>> +#define CORECTRL_SWRST1 (_ULCAST_(0x1) << CORECTRL_SWRST1_SHIFT)
>> +#define CORECTRL_SWRST0_SHIFT 0
>> +#define CORECTRL_SWRST0 (_ULCAST_(0x1) << CORECTRL_SWRST0_SHIFT)
>> +
>> +/*
>> + * Core Status register
>> + */
>> +#define CORESTATUS_SLEEP1_SHIFT 17
>> +#define CORESTATUS_SLEEP1 (_ULCAST_(0x1) << CORESTATUS_SLEEP1_SHIFT)
>> +#define CORESTATUS_SLEEP0_SHIFT 16
>> +#define CORESTATUS_SLEEP0 (_ULCAST_(0x1) << CORESTATUS_SLEEP0_SHIFT)
>> +#define CORESTATUS_IRQ1P_SHIFT 9
>> +#define CORESTATUS_IRQ1P (_ULCAST_(0x1) << CORESTATUS_IRQ1P_SHIFT)
>> +#define CORESTATUS_IRQ0P_SHIFT 8
>> +#define CORESTATUS_IRQ0P (_ULCAST_(0x1) << CORESTATUS_IRQ8P_SHIFT)
>> +#define CORESTATUS_MIRQ1P_SHIFT 1
>> +#define CORESTATUS_MIRQ1P (_ULCAST_(0x1) << CORESTATUS_MIRQ1P_SHIFT)
>> +#define CORESTATUS_MIRQ0P_SHIFT 0
>> +#define CORESTATUS_MIRQ0P (_ULCAST_(0x1) << CORESTATUS_MIRQ0P_SHIFT)
>> +
>> +/*
>> + * Reset Entry & IRQ Mask register
>> + */
>> +#define REIM_ENTRY_SHIFT 16
>> +#define REIM_ENTRY (_ULCAST_(0xffff) << REIM_ENTRY_SHIFT)
>> +#define REIM_IRQ1M_SHIFT 9
>> +#define REIM_IRQ1M (_ULCAST_(0x1) << REIM_IRQ1M_SHIFT)
>> +#define REIM_IRQ0M_SHIFT 8
>> +#define REIM_IRQ0M (_ULCAST_(0x1) << REIM_IRQ0M_SHIFT)
>> +#define REIM_MBOXIRQ1M_SHIFT 1
>> +#define REIM_MBOXIRQ1M (_ULCAST_(0x1) << REIM_MBOXIRQ1M_SHIFT)
>> +#define REIM_MBOXIRQ0M_SHIFT 0
>> +#define REIM_MBOXIRQ0M (_ULCAST_(0x1) << REIM_MBOXIRQ0M_SHIFT)
>> +
>> +#ifdef CONFIG_SMP
>> +
>> +extern void jz4780_smp_wait_irqoff(void);
>> +
>> +extern void jz4780_smp_init(void);
>> +extern void jz4780_secondary_cpu_entry(void);
>> +
>> +#else /* !CONFIG_SMP */
>> +
>> +static inline void jz4780_smp_init(void) { }
>> +
>> +#endif /* !CONFIG_SMP */
>> +
>> +#endif /* __MIPS_ASM_MACH_JZ4740_JZ4780_SMP_H__ */
>> diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
>> index 412d2fa..0239597 100644
>> --- a/arch/mips/jz4740/Kconfig
>> +++ b/arch/mips/jz4740/Kconfig
>> @@ -34,9 +34,12 @@ config MACH_JZ4770
>>
>> config MACH_JZ4780
>> bool
>> + select GENERIC_CLOCKEVENTS_BROADCAST if SMP
>> select MIPS_CPU_SCACHE
>> + select NR_CPUS_DEFAULT_2
>> select SYS_HAS_CPU_MIPS32_R2
>> select SYS_SUPPORTS_HIGHMEM
>> + select SYS_SUPPORTS_SMP
>>
>> config MACH_X1000
>> bool
>> diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
>> index 6de14c0..0a0f024 100644
>> --- a/arch/mips/jz4740/Makefile
>> +++ b/arch/mips/jz4740/Makefile
>> @@ -12,3 +12,8 @@ CFLAGS_setup.o = -I$(src)/../../../scripts/dtc/libfdt
>> # PM support
>>
>> obj-$(CONFIG_PM) += pm.o
>> +
>> +# SMP support
>> +
>> +obj-$(CONFIG_SMP) += smp.o
>> +obj-$(CONFIG_SMP) += smp-entry.o
>> diff --git a/arch/mips/jz4740/prom.c b/arch/mips/jz4740/prom.c
>> index ff4555c..a79159e 100644
>> --- a/arch/mips/jz4740/prom.c
>> +++ b/arch/mips/jz4740/prom.c
>> @@ -8,10 +8,14 @@
>>
>> #include <asm/bootinfo.h>
>> #include <asm/fw/fw.h>
>> +#include <asm/mach-jz4740/jz4780-smp.h>
>>
>> void __init prom_init(void)
>> {
>> fw_init_cmdline();
>> +#if defined(CONFIG_MACH_JZ4780) && defined(CONFIG_SMP)
>> + jz4780_smp_init();
>> +#endif
>> }
>>
>> void __init prom_free_prom_memory(void)
>> diff --git a/arch/mips/jz4740/smp-entry.S b/arch/mips/jz4740/smp-entry.S
>> new file mode 100644
>> index 00000000..20049a3
>> --- /dev/null
>> +++ b/arch/mips/jz4740/smp-entry.S
>> @@ -0,0 +1,57 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright (C) 2013, Paul Burton <[email protected]>
>> + * JZ4780 SMP entry point
>> + */
>> +
>> +#include <asm/addrspace.h>
>> +#include <asm/asm.h>
>> +#include <asm/asmmacro.h>
>> +#include <asm/cacheops.h>
>> +#include <asm/mipsregs.h>
>> +
>> +#define CACHE_SIZE (32 * 1024)
>> +#define CACHE_LINESIZE 32
>> +
>> +.extern jz4780_cpu_entry_sp
>> +.extern jz4780_cpu_entry_gp
>> +
>> +.section .text.smp-entry
>> +.balign 0x10000
>> +.set noreorder
>> +LEAF(jz4780_secondary_cpu_entry)
>> + mtc0 zero, CP0_CAUSE
>> +
>> + li t0, ST0_CU0
>> + mtc0 t0, CP0_STATUS
>> +
>> + /* cache setup */
>> + li t0, KSEG0
>> + ori t1, t0, CACHE_SIZE
>> + mtc0 zero, CP0_TAGLO, 0
>> +1: cache Index_Store_Tag_I, 0(t0)
>> + cache Index_Store_Tag_D, 0(t0)
>> + bne t0, t1, 1b
>> + addiu t0, t0, CACHE_LINESIZE
>> +
>> + /* kseg0 cache attribute */
>> + mfc0 t0, CP0_CONFIG, 0
>> + ori t0, t0, CONF_CM_CACHABLE_NONCOHERENT
>> + mtc0 t0, CP0_CONFIG, 0
>> +
>> + /* pagemask */
>> + mtc0 zero, CP0_PAGEMASK, 0
>> +
>> + /* retrieve sp */
>> + la t0, jz4780_cpu_entry_sp
>> + lw sp, 0(t0)
>> +
>> + /* retrieve gp */
>> + la t0, jz4780_cpu_entry_gp
>> + lw gp, 0(t0)
>> +
>> + /* jump to the kernel in kseg0 */
>> + la t0, smp_bootstrap
>> + jr t0
>> + nop
>> + END(jz4780_secondary_cpu_entry)
>> diff --git a/arch/mips/jz4740/smp.c b/arch/mips/jz4740/smp.c
>> new file mode 100644
>> index 00000000..19b75c2
>> --- /dev/null
>> +++ b/arch/mips/jz4740/smp.c
>> @@ -0,0 +1,286 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2013, Paul Burton <[email protected]>
>> + * JZ4780 SMP
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/of.h>
>> +#include <linux/sched.h>
>> +#include <linux/sched/task_stack.h>
>> +#include <linux/smp.h>
>> +#include <linux/tick.h>
>> +#include <asm/mach-jz4740/jz4780-smp.h>
>> +#include <asm/r4kcache.h>
>> +#include <asm/smp-ops.h>
>> +
>> +static struct clk *cpu_clock_gates[CONFIG_NR_CPUS] = { NULL };
>> +
>> +u32 jz4780_cpu_entry_sp;
>> +u32 jz4780_cpu_entry_gp;
>> +
>> +static struct cpumask cpu_running;
>> +
>> +static DEFINE_SPINLOCK(smp_lock);
>> +
>> +/*
>> + * The Ingenic jz4780 SMP variant has to write back dirty cache
>> lines before
>> + * executing wait. The CPU & cache clock will be gated until we
>> return from
>> + * the wait, and if another core attempts to access data from our
>> data cache
>> + * during this time then it will lock up.
>> + */
>> +void jz4780_smp_wait_irqoff(void)
>> +{
>> + unsigned long pending = read_c0_cause() & read_c0_status() &
>> CAUSEF_IP;
>> +
>> + /*
>> + * Going to idle has a significant overhead due to the cache
>> flush so
>> + * try to avoid it if we'll immediately be woken again due to an
>> IRQ.
>> + */
>> + if (!need_resched() && !pending) {
>> + r4k_blast_dcache();
>> +
>> + __asm__(
>> + " .set push \n"
>> + " .set mips3 \n"
>> + " sync \n"
>> + " wait \n"
>> + " .set pop \n");
>> + }
>> +
>> + local_irq_enable();
>> +}
>> +
>> +static irqreturn_t mbox_handler(int irq, void *dev_id)
>> +{
>> + int cpu = smp_processor_id();
>> + u32 action, status;
>> +
>> + spin_lock(&smp_lock);
>> +
>> + switch (cpu) {
>> + case 0:
>> + action = read_c0_mailbox0();
>> + write_c0_mailbox0(0);
>> + break;
>> + case 1:
>> + action = read_c0_mailbox1();
>> + write_c0_mailbox1(0);
>> + break;
>> + default:
>> + panic("unhandled cpu %d!", cpu);
>> + }
>> +
>> + /* clear pending mailbox interrupt */
>> + status = read_c0_corestatus();
>> + status &= ~(CORESTATUS_MIRQ0P << cpu);
>> + write_c0_corestatus(status);
>> +
>> + spin_unlock(&smp_lock);
>> +
>> + if (action & SMP_RESCHEDULE_YOURSELF)
>> + scheduler_ipi();
>> + if (action & SMP_CALL_FUNCTION)
>> + generic_smp_call_function_interrupt();
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static struct irqaction mbox_action = {
>> + .handler = mbox_handler,
>> + .name = "core mailbox",
>> + .flags = IRQF_PERCPU | IRQF_NO_THREAD,
>> +};
>> +
>> +static void jz4780_smp_setup(void)
>> +{
>> + u32 addr, reim;
>> + int cpu;
>> +
>> + reim = read_c0_reim();
>> +
>> + for (cpu = 0; cpu < NR_CPUS; cpu++) {
>> + __cpu_number_map[cpu] = cpu;
>> + __cpu_logical_map[cpu] = cpu;
>> + set_cpu_possible(cpu, true);
>> + }
>> +
>> + /* mask mailbox interrupts for this core */
>> + reim &= ~REIM_MBOXIRQ0M;
>> + write_c0_reim(reim);
>> +
>> + /* clear mailboxes & pending mailbox IRQs */
>> + write_c0_mailbox0(0);
>> + write_c0_mailbox1(0);
>> + write_c0_corestatus(0);
>> +
>> + /* set reset entry point */
>> + addr = KSEG1ADDR((u32)&jz4780_secondary_cpu_entry);
>> + WARN_ON(addr & ~REIM_ENTRY);
>> + reim &= ~REIM_ENTRY;
>> + reim |= addr & REIM_ENTRY;
>> +
>> + /* unmask mailbox interrupts for this core */
>> + reim |= REIM_MBOXIRQ0M;
>> + write_c0_reim(reim);
>> + set_c0_status(STATUSF_IP3);
>> + irq_enable_hazard();
>> +
>> + cpumask_set_cpu(cpu, &cpu_running);
>> +}
>> +
>> +static void jz4780_smp_prepare_cpus(unsigned int max_cpus)
>> +{
>> + struct device_node *cpu_node;
>> + unsigned cpu, ctrl;
>> + int err;
>> +
>> + /* setup the mailbox IRQ */
>> + setup_irq(MIPS_CPU_IRQ_BASE + 3, &mbox_action);
>> +
>> + init_cpu_present(cpu_possible_mask);
>> +
>> + ctrl = read_c0_corectrl();
>> +
>> + for (cpu = 0; cpu < max_cpus; cpu++) {
>> + /* use reset entry point from REIM register */
>> + ctrl |= CORECTRL_RPC0 << cpu;
>> + }
>> +
>> + for_each_of_cpu_node(cpu_node) {
>> + cpu = of_cpu_node_to_id(cpu_node);
>> + if (cpu < 0) {
>> + pr_err("Failed to read index of %s\n",
>> + cpu_node->full_name);
>> + continue;
>> + }
>> +
>> + cpu_clock_gates[cpu] = of_clk_get(cpu_node, 0);
>> + if (IS_ERR(cpu_clock_gates[cpu])) {
>> + cpu_clock_gates[cpu] = NULL;
>> + continue;
>> + }
>> +
>> + err = clk_prepare(cpu_clock_gates[cpu]);
>> + if (err)
>> + pr_err("Failed to prepare CPU clock gate\n");
>> + }
>> +
>> + write_c0_corectrl(ctrl);
>> +}
>> +
>> +static int jz4780_boot_secondary(int cpu, struct task_struct *idle)
>> +{
>> + unsigned long flags;
>> + u32 ctrl;
>> +
>> + spin_lock_irqsave(&smp_lock, flags);
>> +
>> + /* ensure the core is in reset */
>> + ctrl = read_c0_corectrl();
>> + ctrl |= CORECTRL_SWRST0 << cpu;
>> + write_c0_corectrl(ctrl);
>> +
>> + /* ungate core clock */
>> + if (cpu_clock_gates[cpu])
>> + clk_enable(cpu_clock_gates[cpu]);
>> +
>> + /* set entry sp/gp register values */
>> + jz4780_cpu_entry_sp = __KSTK_TOS(idle);
>> + jz4780_cpu_entry_gp = (u32)task_thread_info(idle);
>> + smp_wmb();
>> +
>> + /* take the core out of reset */
>> + ctrl &= ~(CORECTRL_SWRST0 << cpu);
>> + write_c0_corectrl(ctrl);
>> +
>> + cpumask_set_cpu(cpu, &cpu_running);
>> +
>> + spin_unlock_irqrestore(&smp_lock, flags);
>> +
>> + return 0;
>> +}
>> +
>> +static void jz4780_init_secondary(void)
>> +{
>> +}
>> +
>> +static void jz4780_smp_finish(void)
>> +{
>> + u32 reim;
>> +
>> + spin_lock(&smp_lock);
>> +
>> + /* unmask mailbox interrupts for this core */
>> + reim = read_c0_reim();
>> + reim |= REIM_MBOXIRQ0M << smp_processor_id();
>> + write_c0_reim(reim);
>> +
>> + spin_unlock(&smp_lock);
>> +
>> + /* unmask interrupts for this core */
>> + change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP2 |
>> + STATUSF_IP1 | STATUSF_IP0);
>> + irq_enable_hazard();
>> +
>> + /* force broadcast timer */
>> + tick_broadcast_force();
>> +}
>> +
>> +static void jz4780_send_ipi_single_locked(int cpu, unsigned int action)
>> +{
>> + u32 mbox;
>> +
>> + switch (cpu) {
>> + case 0:
>> + mbox = read_c0_mailbox0();
>> + write_c0_mailbox0(mbox | action);
>> + break;
>> + case 1:
>> + mbox = read_c0_mailbox1();
>> + write_c0_mailbox1(mbox | action);
>> + break;
>> + default:
>> + panic("unhandled cpu %d!", cpu);
>> + }
>> +}
>> +
>> +static void jz4780_send_ipi_single(int cpu, unsigned int action)
>> +{
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&smp_lock, flags);
>> + jz4780_send_ipi_single_locked(cpu, action);
>> + spin_unlock_irqrestore(&smp_lock, flags);
>> +}
>> +
>> +static void jz4780_send_ipi_mask(const struct cpumask *mask,
>> + unsigned int action)
>> +{
>> + unsigned long flags;
>> + int cpu;
>> +
>> + spin_lock_irqsave(&smp_lock, flags);
>> +
>> + for_each_cpu(cpu, mask)
>> + jz4780_send_ipi_single_locked(cpu, action);
>> +
>> + spin_unlock_irqrestore(&smp_lock, flags);
>> +}
>> +
>> +static struct plat_smp_ops jz4780_smp_ops = {
>> + .send_ipi_single = jz4780_send_ipi_single,
>> + .send_ipi_mask = jz4780_send_ipi_mask,
>> + .init_secondary = jz4780_init_secondary,
>> + .smp_finish = jz4780_smp_finish,
>> + .boot_secondary = jz4780_boot_secondary,
>> + .smp_setup = jz4780_smp_setup,
>> + .prepare_cpus = jz4780_smp_prepare_cpus,
>> +};
>> +
>> +void jz4780_smp_init(void)
>> +{
>> + register_smp_ops(&jz4780_smp_ops);
>> +}
>> diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c
>> index 37f8e78..a406de3 100644
>> --- a/arch/mips/kernel/idle.c
>> +++ b/arch/mips/kernel/idle.c
>> @@ -19,6 +19,10 @@
>> #include <asm/idle.h>
>> #include <asm/mipsregs.h>
>>
>> +#ifdef CONFIG_MACH_JZ4780
>> +# include <asm/mach-jz4740/jz4780-smp.h>
>> +#endif
>> +
>> /*
>> * Not all of the MIPS CPUs have the "wait" instruction available.
>> Moreover,
>> * the implementation of the "wait" feature differs between CPU
>> families. This
>> @@ -172,7 +176,6 @@ void __init check_wait(void)
>> case CPU_CAVIUM_OCTEON_PLUS:
>> case CPU_CAVIUM_OCTEON2:
>> case CPU_CAVIUM_OCTEON3:
>> - case CPU_XBURST:
>> case CPU_LOONGSON32:
>> case CPU_XLR:
>> case CPU_XLP:
>> @@ -246,6 +249,15 @@ void __init check_wait(void)
>> cpu_wait = r4k_wait;
>> */
>> break;
>> + case CPU_XBURST:
>> +#if defined(CONFIG_MACH_JZ4780) && defined(CONFIG_SMP)
>> + if (NR_CPUS > 1)
>> + cpu_wait = jz4780_smp_wait_irqoff;
>> + else
>> + cpu_wait = r4k_wait;
>> +#else
>> + cpu_wait = r4k_wait;
>> +#endif
>> default:
>> break;
>> }
>> diff --git a/drivers/clk/ingenic/jz4780-cgu.c
>> b/drivers/clk/ingenic/jz4780-cgu.c
>> index d07fff1..4f81819 100644
>> --- a/drivers/clk/ingenic/jz4780-cgu.c
>> +++ b/drivers/clk/ingenic/jz4780-cgu.c
>> @@ -16,7 +16,7 @@
>>
>> /* CGU register offsets */
>> #define CGU_REG_CLOCKCONTROL 0x00
>> -#define CGU_REG_PLLCONTROL 0x0c
>> +#define CGU_REG_LCR 0x04
>> #define CGU_REG_APLL 0x10
>> #define CGU_REG_MPLL 0x14
>> #define CGU_REG_EPLL 0x18
>> @@ -46,8 +46,8 @@
>> #define CGU_REG_CLOCKSTATUS 0xd4
>>
>> /* bits within the OPCR register */
>> -#define OPCR_SPENDN0 (1 << 7)
>> -#define OPCR_SPENDN1 (1 << 6)
>> +#define OPCR_SPENDN0 BIT(7)
>> +#define OPCR_SPENDN1 BIT(6)
>>
>> /* bits within the USBPCR register */
>> #define USBPCR_USB_MODE BIT(31)
>> @@ -88,6 +88,13 @@
>> #define USBVBFIL_IDDIGFIL_MASK (0xffff << USBVBFIL_IDDIGFIL_SHIFT)
>> #define USBVBFIL_USBVBFIL_MASK (0xffff)
>>
>> +/* bits within the LCR register */
>> +#define LCR_PD_SCPU BIT(31)
>> +#define LCR_SCPUS BIT(27)
>> +
>> +/* bits within the CLKGR1 register */
>> +#define CLKGR1_CORE1 BIT(15)
>> +
>> static struct ingenic_cgu *cgu;
>>
>> static u8 jz4780_otg_phy_get_parent(struct clk_hw *hw)
>> @@ -205,6 +212,47 @@ static const struct clk_ops jz4780_otg_phy_ops = {
>> .set_rate = jz4780_otg_phy_set_rate,
>> };
>>
>> +static int jz4780_core1_enable(struct clk_hw *hw)
>> +{
>> + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
>> + struct ingenic_cgu *cgu = ingenic_clk->cgu;
>> + const unsigned int timeout = 100;
>> + unsigned long flags;
>> + unsigned int i;
>> + u32 lcr, clkgr1;
>> +
>> + spin_lock_irqsave(&cgu->lock, flags);
>> +
>> + lcr = readl(cgu->base + CGU_REG_LCR);
>> + lcr &= ~LCR_PD_SCPU;
>> + writel(lcr, cgu->base + CGU_REG_LCR);
>> +
>> + clkgr1 = readl(cgu->base + CGU_REG_CLKGR1);
>> + clkgr1 &= ~CLKGR1_CORE1;
>> + writel(clkgr1, cgu->base + CGU_REG_CLKGR1);
>> +
>> + spin_unlock_irqrestore(&cgu->lock, flags);
>> +
>> + /* wait for the CPU to be powered up */
>> + for (i = 0; i < timeout; i++) {
>> + lcr = readl(cgu->base + CGU_REG_LCR);
>> + if (!(lcr & LCR_SCPUS))
>> + break;
>> + mdelay(1);
>> + }
>> +
>> + if (i == timeout) {
>> + pr_err("%s: Wait for power up core1 timeout\n", __func__);
>> + return -EBUSY;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static const struct clk_ops jz4780_core1_ops = {
>> + .enable = jz4780_core1_enable,
>> +};
>> +
>> static const s8 pll_od_encoding[16] = {
>> 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
>> 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
>> @@ -701,9 +749,9 @@ static const struct ingenic_cgu_clk_info
>> jz4780_cgu_clocks[] = {
>> },
>>
>> [JZ4780_CLK_CORE1] = {
>> - "core1", CGU_CLK_GATE,
>> + "core1", CGU_CLK_CUSTOM,
>> .parents = { JZ4780_CLK_CPU, -1, -1, -1 },
>> - .gate = { CGU_REG_CLKGR1, 15 },
>> + .custom = { &jz4780_core1_ops },
>> },
>>
>> };
>> --
>> 2.7.4
>>
>

2020-02-15 15:08:48

by afzal mohammed

[permalink] [raw]
Subject: Re: [PATCH v4 1/6] MIPS: JZ4780: Introduce SMP support.

Hi,

On Sat, Feb 15, 2020 at 02:02:35AM +0800, 周琰杰 (Zhou Yanjie) wrote:

> + /* setup the mailbox IRQ */
> + setup_irq(MIPS_CPU_IRQ_BASE + 3, &mbox_action);

s/setup_irq/request_irq, see,

https://lkml.kernel.org/r/alpine.DEB.2.20.1710191609480.1971@nanos
https://lkml.kernel.org/r/[email protected]

Regards
afzal

2020-02-15 16:17:53

by Zhou Yanjie

[permalink] [raw]
Subject: Re: [PATCH v4 1/6] MIPS: JZ4780: Introduce SMP support.

Hi Afzal,

On 2020年02月15日 23:03, afzal mohammed wrote:
> Hi,
>
> On Sat, Feb 15, 2020 at 02:02:35AM +0800, 周琰杰 (Zhou Yanjie) wrote:
>
>> + /* setup the mailbox IRQ */
>> + setup_irq(MIPS_CPU_IRQ_BASE + 3, &mbox_action);
> s/setup_irq/request_irq, see,
>
> https://lkml.kernel.org/r/alpine.DEB.2.20.1710191609480.1971@nanos
> https://lkml.kernel.org/r/[email protected]

Thanks for your advice, will change in v5.

>
> Regards
> afzal

Thanks and best regards!