linux-mips.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Introduce SMP support for CI20 (based on JZ4780) v8.
@ 2020-05-19 14:35 周琰杰 (Zhou Yanjie)
  2020-05-19 14:35 ` [PATCH v8 0/6] Introduce SMP support for CI20 (based on JZ4780) 周琰杰 (Zhou Yanjie)
                   ` (6 more replies)
  0 siblings, 7 replies; 31+ messages in thread
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
	chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
	ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin

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


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v8 0/6] Introduce SMP support for CI20 (based on JZ4780).
  2020-05-19 14:35 Introduce SMP support for CI20 (based on JZ4780) v8 周琰杰 (Zhou Yanjie)
@ 2020-05-19 14:35 ` 周琰杰 (Zhou Yanjie)
  2020-05-19 14:35 ` [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support 周琰杰 (Zhou Yanjie)
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 31+ messages in thread
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
	chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
	ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin

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

周琰杰 (Zhou Yanjie) (6):
  MIPS: JZ4780: Introduce SMP support.
  MIPS: CI20: Modify DTS to support high resolution timer for SMP.
  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: Update defconfig to support SMP.

 .../bindings/mips/ingenic/ingenic,cpu.yaml         |  57 +++++
 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/smp.h            |  87 +++++++
 arch/mips/jz4740/Kconfig                           |   2 +
 arch/mips/jz4740/Makefile                          |   5 +
 arch/mips/jz4740/prom.c                            |   4 +
 arch/mips/jz4740/smp-entry.S                       |  57 +++++
 arch/mips/jz4740/smp.c                             | 258 +++++++++++++++++++++
 arch/mips/kernel/idle.c                            |  35 ++-
 drivers/clocksource/ingenic-timer.c                | 103 ++++++--
 15 files changed, 661 insertions(+), 26 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
 create mode 100644 arch/mips/include/asm/mach-jz4740/smp.h
 create mode 100644 arch/mips/jz4740/smp-entry.S
 create mode 100644 arch/mips/jz4740/smp.c

-- 
2.7.4


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
  2020-05-19 14:35 Introduce SMP support for CI20 (based on JZ4780) v8 周琰杰 (Zhou Yanjie)
  2020-05-19 14:35 ` [PATCH v8 0/6] Introduce SMP support for CI20 (based on JZ4780) 周琰杰 (Zhou Yanjie)
@ 2020-05-19 14:35 ` 周琰杰 (Zhou Yanjie)
  2020-05-19 16:09   ` Paul Cercueil
                     ` (2 more replies)
  2020-05-19 14:35 ` [PATCH v8 2/6] MIPS: CI20: Modify DTS to support high resolution timer for SMP 周琰杰 (Zhou Yanjie)
                   ` (4 subsequent siblings)
  6 siblings, 3 replies; 31+ messages in thread
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
	chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
	ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin

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

Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---

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.
    
    v4->v5:
    1.Splitting changes involving "jz4780-cgu.c" into separate commit.
    2.Use "request_irq()" replace "setup_irq()".
    
    v5->v6:
    In order to have a kernel that works on multiple SoCs at the same
    time, use "IS_ENABLED()" replace "#ifdef".
    
    v6->v7:
    1.SMP has be decoupled from the SoC version.
    2.Add mailboxes 3 and 4 for XBurst.
    3.Adjust code in "jz4780_smp_prepare_cpus()".
    4."jz4780_smp_init()" has be marked "__init".
    
    v7->v8:
    No change.

 arch/mips/include/asm/mach-jz4740/smp.h |  87 +++++++++++
 arch/mips/jz4740/Kconfig                |   2 +
 arch/mips/jz4740/Makefile               |   5 +
 arch/mips/jz4740/prom.c                 |   4 +
 arch/mips/jz4740/smp-entry.S            |  57 +++++++
 arch/mips/jz4740/smp.c                  | 258 ++++++++++++++++++++++++++++++++
 arch/mips/kernel/idle.c                 |  35 ++++-
 7 files changed, 447 insertions(+), 1 deletion(-)
 create mode 100644 arch/mips/include/asm/mach-jz4740/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/smp.h b/arch/mips/include/asm/mach-jz4740/smp.h
new file mode 100644
index 00000000..86f660f
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/smp.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
+ *  JZ4780 SMP definitions
+ */
+
+#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
+#define __MIPS_ASM_MACH_JZ4740_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 read_c0_mailbox2()		__read_32bit_c0_register($20, 2)
+#define write_c0_mailbox2(val)		__write_32bit_c0_register($20, 2, val)
+
+#define read_c0_mailbox3()		__read_32bit_c0_register($20, 3)
+#define write_c0_mailbox3(val)		__write_32bit_c0_register($20, 3, 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)
+
+extern void jz4780_smp_init(void);
+extern void jz4780_secondary_cpu_entry(void);
+
+#endif /* __MIPS_ASM_MACH_JZ4740_SMP_H__ */
diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
index 412d2fa..2b88557 100644
--- a/arch/mips/jz4740/Kconfig
+++ b/arch/mips/jz4740/Kconfig
@@ -34,9 +34,11 @@ config MACH_JZ4770
 
 config MACH_JZ4780
 	bool
+	select GENERIC_CLOCKEVENTS_BROADCAST if SMP
 	select MIPS_CPU_SCACHE
 	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..4acf5c2c 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/smp.h>
 
 void __init prom_init(void)
 {
 	fw_init_cmdline();
+
+	if (IS_ENABLED(CONFIG_SMP))
+		jz4780_smp_init();
 }
 
 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 <paul.burton@imgtec.com>
+ *  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..d95d22a
--- /dev/null
+++ b/arch/mips/jz4740/smp.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
+ *  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/smp.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);
+
+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;
+	case 2:
+		action = read_c0_mailbox2();
+		write_c0_mailbox2(0);
+		break;
+	case 3:
+		action = read_c0_mailbox3();
+		write_c0_mailbox3(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 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 */
+	err = request_irq(MIPS_CPU_IRQ_BASE + 3, mbox_handler,
+			IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL);
+	if (err)
+		pr_err("request_irq() on core mailbox failed\n");
+
+	ctrl = read_c0_corectrl();
+
+	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;
+		}
+
+		/* use reset entry point from REIM register */
+		ctrl |= CORECTRL_RPC0 << cpu;
+
+		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 __init 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..d33f2d4 100644
--- a/arch/mips/kernel/idle.c
+++ b/arch/mips/kernel/idle.c
@@ -18,6 +18,7 @@
 #include <asm/cpu-type.h>
 #include <asm/idle.h>
 #include <asm/mipsregs.h>
+#include <asm/r4kcache.h>
 
 /*
  * Not all of the MIPS CPUs have the "wait" instruction available. Moreover,
@@ -88,6 +89,34 @@ static void __cpuidle rm7k_wait_irqoff(void)
 }
 
 /*
+ * 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();
+}
+
+/*
  * Au1 'wait' is only useful when the 32kHz counter is used as timer,
  * since coreclock (and the cp0 counter) stops upon executing it. Only an
  * interrupt can wake it, so they must be enabled before entering idle modes.
@@ -172,7 +201,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 +274,11 @@ void __init check_wait(void)
 		   cpu_wait = r4k_wait;
 		 */
 		break;
+	case CPU_XBURST:
+		if (IS_ENABLED(CONFIG_SMP))
+			cpu_wait = jz4780_smp_wait_irqoff;
+		else
+			cpu_wait = r4k_wait;
 	default:
 		break;
 	}
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v8 2/6] MIPS: CI20: Modify DTS to support high resolution timer for SMP.
  2020-05-19 14:35 Introduce SMP support for CI20 (based on JZ4780) v8 周琰杰 (Zhou Yanjie)
  2020-05-19 14:35 ` [PATCH v8 0/6] Introduce SMP support for CI20 (based on JZ4780) 周琰杰 (Zhou Yanjie)
  2020-05-19 14:35 ` [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support 周琰杰 (Zhou Yanjie)
@ 2020-05-19 14:35 ` 周琰杰 (Zhou Yanjie)
  2020-05-19 14:35 ` [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support " 周琰杰 (Zhou Yanjie)
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 31+ messages in thread
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
	chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
	ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin

Modify DTS, change tcu channel from 2 to 3, channel #0 and #1 for
per core local timer, #2 for clocksource.

Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---

Notes:
    v1->v2:
    No change.
    
    v2->v3:
    No change.
    
    v3->v4:
    Rebase on top of kernel 5.6-rc1.
    
    v4->v5:
    Move [5/6] in v4 to this patch, to ensure that we can
    git-bisect without ending up with a broken kernel.
    
    v5->v6:
    No change.
    
    v6->v7:
    Remove unnecessary "ingenic,pwm-channels-mask".
    
    v7->v8:
    No change.

 arch/mips/boot/dts/ingenic/ci20.dts | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts
index db0ca25..06e3186 100644
--- a/arch/mips/boot/dts/ingenic/ci20.dts
+++ b/arch/mips/boot/dts/ingenic/ci20.dts
@@ -486,7 +486,12 @@
 };
 
 &tcu {
-	/* 3 MHz for the system timer and clocksource */
-	assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>;
-	assigned-clock-rates = <3000000>, <3000000>;
+	/*
+	 * 750 kHz for the system timers and clocksource,
+	 * use channel #0 and #1 for the per cpu system timers,
+	 * and use channel #2 for the clocksource.
+	 */
+	assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>,
+					  <&tcu TCU_CLK_TIMER2>;
+	assigned-clock-rates = <750000>, <750000>, <750000>;
 };
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support for SMP.
  2020-05-19 14:35 Introduce SMP support for CI20 (based on JZ4780) v8 周琰杰 (Zhou Yanjie)
                   ` (2 preceding siblings ...)
  2020-05-19 14:35 ` [PATCH v8 2/6] MIPS: CI20: Modify DTS to support high resolution timer for SMP 周琰杰 (Zhou Yanjie)
@ 2020-05-19 14:35 ` 周琰杰 (Zhou Yanjie)
  2020-05-19 17:42   ` Paul Cercueil
  2020-05-19 20:11   ` [PATCH] " Paul Cercueil
  2020-05-19 14:35 ` [PATCH v8 4/6] dt-bindings: MIPS: Document Ingenic SoCs binding 周琰杰 (Zhou Yanjie)
                   ` (2 subsequent siblings)
  6 siblings, 2 replies; 31+ messages in thread
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
	chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
	ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin

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 <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---

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.
    
    v4->v5:
    Move the check for (evt->event_handler) from "ingenic_per_cpu_event_handler"
    to "ingenic_tcu_cevt_cb".
    
    v5->v6:
    No change.
    
    v6->v7:
    Remove unnecessary check for "NR_CPUS > 1".
    
    v7->v8:
    Use "num_possible_cpus()" instead "NR_CPUS".
    Reported-by: kbuild test robot <lkp@intel.com>

 drivers/clocksource/ingenic-timer.c | 103 ++++++++++++++++++++++++++++--------
 1 file changed, 82 insertions(+), 21 deletions(-)

diff --git a/drivers/clocksource/ingenic-timer.c b/drivers/clocksource/ingenic-timer.c
index 4963336..230e996 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 <paul@crapouillou.net>
+ * Copyright (C) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
  */
 
 #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[8];
 	unsigned long pwm_channels_mask;
+	int cpu;
 };
 
 static struct ingenic_tcu *ingenic_tcu;
@@ -81,6 +87,24 @@ 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;
+
+	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;
@@ -89,7 +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,21 @@ 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 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 +170,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%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,6 +199,25 @@ static int __init ingenic_tcu_timer_init(struct device_node *np,
 	return err;
 }
 
+static int ingenic_tcu_setup_cevt(unsigned int cpu)
+{
+	int ret;
+
+	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_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 int __init ingenic_tcu_clocksource_init(struct device_node *np,
 					       struct ingenic_tcu *tcu)
 {
@@ -240,6 +292,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,13 +306,18 @@ static int __init ingenic_tcu_init(struct device_node *np)
 	if (!tcu)
 		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);
+	/*
+	 * Enable all TCU channels for PWM use by default except channels 0/1,
+	 * and channel 2 if target CPU is JZ4780 and SMP is selected.
+	 */
+	tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1,
+								num_possible_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) {
+	/* Verify that we have at least num_possible_cpus() + 1 free channels */
+	if (hweight8(tcu->pwm_channels_mask) >
+			soc_info->num_channels - num_possible_cpus() + 1) {
 		pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__,
 			tcu->pwm_channels_mask);
 		ret = -EINVAL;
@@ -267,13 +325,19 @@ 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);
+
+	for (cpu = 1; cpu < num_possible_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_channel + 1);
+			soc_info->num_channels, tcu->timer_local[cpu - 1] + 1);
 
 	ret = ingenic_tcu_clocksource_init(np, tcu);
 	if (ret) {
@@ -281,9 +345,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);
@@ -291,10 +356,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


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v8 4/6] dt-bindings: MIPS: Document Ingenic SoCs binding.
  2020-05-19 14:35 Introduce SMP support for CI20 (based on JZ4780) v8 周琰杰 (Zhou Yanjie)
                   ` (3 preceding siblings ...)
  2020-05-19 14:35 ` [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support " 周琰杰 (Zhou Yanjie)
@ 2020-05-19 14:35 ` 周琰杰 (Zhou Yanjie)
  2020-05-26 19:29   ` Rob Herring
  2020-05-19 14:35 ` [PATCH v8 5/6] MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs 周琰杰 (Zhou Yanjie)
  2020-05-19 14:35 ` [PATCH v8 6/6] MIPS: CI20: Update defconfig to support SMP 周琰杰 (Zhou Yanjie)
  6 siblings, 1 reply; 31+ messages in thread
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
	chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
	ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin

Document the available properties for the SoC root node and the
CPU nodes of the devicetree for the Ingenic XBurst SoCs.

Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---

Notes:
    v1->v2:
    Change the two Document from txt to yaml.
    
    v2->v3:
    Fix formatting errors.
    
    v3->v4:
    Fix bugs in the two yaml files.
    
    v4->v5:
    No change.
    
    v5->v6:
    Rewrite the two yaml files.
    
    v6->v7:
    1.Update compatible strings in "ingenic,cpu.yaml".
    2.Fix formatting errors, and enum for compatible strings.
    3.Remove unnecessary "ingenic,soc.yaml".
    
    v7->v8:
    No change.

 .../bindings/mips/ingenic/ingenic,cpu.yaml         | 57 ++++++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml

diff --git a/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml b/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
new file mode 100644
index 00000000..afb0207
--- /dev/null
+++ b/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mips/ingenic/ingenic,cpu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Bindings for Ingenic XBurst family CPUs
+
+maintainers:
+  - 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
+
+description:
+  Ingenic XBurst family CPUs shall have the following properties.
+
+properties:
+  compatible:
+    oneOf:
+
+      - description: Ingenic XBurst®1 CPU Cores
+        items:
+          enum:
+            - ingenic,xburst-mxu1.0
+            - ingenic,xburst-fpu1.0-mxu1.1
+            - ingenic,xburst-fpu2.0-mxu2.0
+
+      - description: Ingenic XBurst®2 CPU Cores
+        items:
+          enum:
+            - ingenic,xburst2-fpu2.1-mxu2.1-smt
+
+  reg:
+    maxItems: 1
+
+required:
+  - device_type
+  - compatible
+  - reg
+
+examples:
+  - |
+    cpus {
+    	#address-cells = <1>;
+    	#size-cells = <0>;
+
+    	cpu0: cpu@0 {
+    		device_type = "cpu";
+    		compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+    		reg = <0>;
+    	};
+
+    	cpu1: cpu@1 {
+    		device_type = "cpu";
+    		compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+    		reg = <1>;
+    	};
+    };
+...
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v8 5/6] MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs.
  2020-05-19 14:35 Introduce SMP support for CI20 (based on JZ4780) v8 周琰杰 (Zhou Yanjie)
                   ` (4 preceding siblings ...)
  2020-05-19 14:35 ` [PATCH v8 4/6] dt-bindings: MIPS: Document Ingenic SoCs binding 周琰杰 (Zhou Yanjie)
@ 2020-05-19 14:35 ` 周琰杰 (Zhou Yanjie)
  2020-09-10  7:52   ` H. Nikolaus Schaller
  2020-05-19 14:35 ` [PATCH v8 6/6] MIPS: CI20: Update defconfig to support SMP 周琰杰 (Zhou Yanjie)
  6 siblings, 1 reply; 31+ messages in thread
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
	chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
	ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin

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

Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---

Notes:
    v1->v2:
    No change.
    
    v2->v3:
    No change.
    
    v3->v4:
    Rebase on top of kernel 5.6-rc1.
    
    v4->v5:
    No change.
    
    v5->v6:
    No change.
    
    v6->v7:
    Update compatible strings.
    
    v7->v8:
    No change.

 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 a3301ba..1f2f896 100644
--- a/arch/mips/boot/dts/ingenic/jz4740.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4740.dtsi
@@ -7,6 +7,20 @@
 	#size-cells = <1>;
 	compatible = "ingenic,jz4740";
 
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			compatible = "ingenic,xburst-mxu1.0";
+			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..12c7101 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-fpu1.0-mxu1.1";
+			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 bb89653..03aeeff 100644
--- a/arch/mips/boot/dts/ingenic/jz4780.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi
@@ -8,6 +8,29 @@
 	#size-cells = <1>;
 	compatible = "ingenic,jz4780";
 
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+			reg = <0>;
+
+			clocks = <&cgu JZ4780_CLK_CPU>;
+			clock-names = "cpu";
+		};
+
+		cpu1: cpu@1 {
+			device_type = "cpu";
+			compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+			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 147f7d5..2205e1b 100644
--- a/arch/mips/boot/dts/ingenic/x1000.dtsi
+++ b/arch/mips/boot/dts/ingenic/x1000.dtsi
@@ -8,6 +8,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-fpu1.0-mxu1.1";
+			reg = <0>;
+
+			clocks = <&cgu X1000_CLK_CPU>;
+			clock-names = "cpu";
+		};
+	};
+
 	cpuintc: interrupt-controller {
 		#address-cells = <0>;
 		#interrupt-cells = <1>;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v8 6/6] MIPS: CI20: Update defconfig to support SMP.
  2020-05-19 14:35 Introduce SMP support for CI20 (based on JZ4780) v8 周琰杰 (Zhou Yanjie)
                   ` (5 preceding siblings ...)
  2020-05-19 14:35 ` [PATCH v8 5/6] MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs 周琰杰 (Zhou Yanjie)
@ 2020-05-19 14:35 ` 周琰杰 (Zhou Yanjie)
  6 siblings, 0 replies; 31+ messages in thread
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
	chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
	ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin

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

Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---

Notes:
    v1->v2:
    No change.
    
    v2->v3:
    No change.
    
    v3->v4:
    Rebase on top of kernel 5.6-rc1.
    
    v4->v5:
    No change.
    
    v5->v6:
    No change.
    
    v6->v7:
    No change.
    
    v7->v8:
    No change.

 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 0db0088..c8dd136 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_MODULES=y
 CONFIG_KERNEL_XZ=y
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
  2020-05-19 14:35 ` [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support 周琰杰 (Zhou Yanjie)
@ 2020-05-19 16:09   ` Paul Cercueil
  2020-05-20  7:24     ` Zhou Yanjie
  2020-05-19 18:21   ` kbuild test robot
  2020-05-19 19:41   ` Paul Cercueil
  2 siblings, 1 reply; 31+ messages in thread
From: Paul Cercueil @ 2020-05-19 16:09 UTC (permalink / raw)
  To: 周琰杰
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
	krzk, hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
	sernia.zhou, zhenwenjin

Hi Zhou,

Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
<zhouyanjie@wanyeetech.com> a écrit :
> Forward port smp support from kernel 3.18.3 of CI20_linux
> to upstream kernel 5.6.
> 
> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
> Tested-by: Paul Boddie <paul@boddie.org.uk>
> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
> 
> 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.
> 
>     v4->v5:
>     1.Splitting changes involving "jz4780-cgu.c" into separate commit.
>     2.Use "request_irq()" replace "setup_irq()".
> 
>     v5->v6:
>     In order to have a kernel that works on multiple SoCs at the same
>     time, use "IS_ENABLED()" replace "#ifdef".
> 
>     v6->v7:
>     1.SMP has be decoupled from the SoC version.
>     2.Add mailboxes 3 and 4 for XBurst.
>     3.Adjust code in "jz4780_smp_prepare_cpus()".
>     4."jz4780_smp_init()" has be marked "__init".
> 
>     v7->v8:
>     No change.
> 
>  arch/mips/include/asm/mach-jz4740/smp.h |  87 +++++++++++
>  arch/mips/jz4740/Kconfig                |   2 +
>  arch/mips/jz4740/Makefile               |   5 +
>  arch/mips/jz4740/prom.c                 |   4 +
>  arch/mips/jz4740/smp-entry.S            |  57 +++++++
>  arch/mips/jz4740/smp.c                  | 258 
> ++++++++++++++++++++++++++++++++
>  arch/mips/kernel/idle.c                 |  35 ++++-
>  7 files changed, 447 insertions(+), 1 deletion(-)
>  create mode 100644 arch/mips/include/asm/mach-jz4740/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/smp.h 
> b/arch/mips/include/asm/mach-jz4740/smp.h
> new file mode 100644
> index 00000000..86f660f
> --- /dev/null
> +++ b/arch/mips/include/asm/mach-jz4740/smp.h
> @@ -0,0 +1,87 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
> + *  JZ4780 SMP definitions
> + */
> +
> +#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
> +#define __MIPS_ASM_MACH_JZ4740_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 read_c0_mailbox2()		__read_32bit_c0_register($20, 2)
> +#define write_c0_mailbox2(val)		__write_32bit_c0_register($20, 2, 
> val)
> +
> +#define read_c0_mailbox3()		__read_32bit_c0_register($20, 3)
> +#define write_c0_mailbox3(val)		__write_32bit_c0_register($20, 3, 
> 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)
> +
> +extern void jz4780_smp_init(void);
> +extern void jz4780_secondary_cpu_entry(void);
> +
> +#endif /* __MIPS_ASM_MACH_JZ4740_SMP_H__ */
> diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
> index 412d2fa..2b88557 100644
> --- a/arch/mips/jz4740/Kconfig
> +++ b/arch/mips/jz4740/Kconfig
> @@ -34,9 +34,11 @@ config MACH_JZ4770
> 
>  config MACH_JZ4780
>  	bool
> +	select GENERIC_CLOCKEVENTS_BROADCAST if SMP
>  	select MIPS_CPU_SCACHE
>  	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..4acf5c2c 100644
> --- a/arch/mips/jz4740/prom.c
> +++ b/arch/mips/jz4740/prom.c

That file is gone in mips-next. You should rebase your patchset on top 
of mips-next.

Cheers,
-Paul

> @@ -8,10 +8,14 @@
> 
>  #include <asm/bootinfo.h>
>  #include <asm/fw/fw.h>
> +#include <asm/mach-jz4740/smp.h>
> 
>  void __init prom_init(void)
>  {
>  	fw_init_cmdline();
> +
> +	if (IS_ENABLED(CONFIG_SMP))
> +		jz4780_smp_init();
>  }
> 
>  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 <paul.burton@imgtec.com>
> + *  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..d95d22a
> --- /dev/null
> +++ b/arch/mips/jz4740/smp.c
> @@ -0,0 +1,258 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
> + *  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/smp.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);
> +
> +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;
> +	case 2:
> +		action = read_c0_mailbox2();
> +		write_c0_mailbox2(0);
> +		break;
> +	case 3:
> +		action = read_c0_mailbox3();
> +		write_c0_mailbox3(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 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 */
> +	err = request_irq(MIPS_CPU_IRQ_BASE + 3, mbox_handler,
> +			IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL);
> +	if (err)
> +		pr_err("request_irq() on core mailbox failed\n");
> +
> +	ctrl = read_c0_corectrl();
> +
> +	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;
> +		}
> +
> +		/* use reset entry point from REIM register */
> +		ctrl |= CORECTRL_RPC0 << cpu;
> +
> +		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 __init 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..d33f2d4 100644
> --- a/arch/mips/kernel/idle.c
> +++ b/arch/mips/kernel/idle.c
> @@ -18,6 +18,7 @@
>  #include <asm/cpu-type.h>
>  #include <asm/idle.h>
>  #include <asm/mipsregs.h>
> +#include <asm/r4kcache.h>
> 
>  /*
>   * Not all of the MIPS CPUs have the "wait" instruction available. 
> Moreover,
> @@ -88,6 +89,34 @@ static void __cpuidle rm7k_wait_irqoff(void)
>  }
> 
>  /*
> + * 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();
> +}
> +
> +/*
>   * Au1 'wait' is only useful when the 32kHz counter is used as timer,
>   * since coreclock (and the cp0 counter) stops upon executing it. 
> Only an
>   * interrupt can wake it, so they must be enabled before entering 
> idle modes.
> @@ -172,7 +201,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 +274,11 @@ void __init check_wait(void)
>  		   cpu_wait = r4k_wait;
>  		 */
>  		break;
> +	case CPU_XBURST:
> +		if (IS_ENABLED(CONFIG_SMP))
> +			cpu_wait = jz4780_smp_wait_irqoff;
> +		else
> +			cpu_wait = r4k_wait;
>  	default:
>  		break;
>  	}
> --
> 2.7.4
> 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support for SMP.
  2020-05-19 14:35 ` [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support " 周琰杰 (Zhou Yanjie)
@ 2020-05-19 17:42   ` Paul Cercueil
  2020-05-19 20:11   ` [PATCH] " Paul Cercueil
  1 sibling, 0 replies; 31+ messages in thread
From: Paul Cercueil @ 2020-05-19 17:42 UTC (permalink / raw)
  To: 周琰杰
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
	krzk, hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
	sernia.zhou, zhenwenjin

Hi Zhou,

Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
<zhouyanjie@wanyeetech.com> 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 <hns@goldelico.com>
> Tested-by: Paul Boddie <paul@boddie.org.uk>
> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
> ---
> 
> 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.
> 
>     v4->v5:
>     Move the check for (evt->event_handler) from 
> "ingenic_per_cpu_event_handler"
>     to "ingenic_tcu_cevt_cb".
> 
>     v5->v6:
>     No change.
> 
>     v6->v7:
>     Remove unnecessary check for "NR_CPUS > 1".
> 
>     v7->v8:
>     Use "num_possible_cpus()" instead "NR_CPUS".
>     Reported-by: kbuild test robot <lkp@intel.com>
> 
>  drivers/clocksource/ingenic-timer.c | 103 
> ++++++++++++++++++++++++++++--------
>  1 file changed, 82 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/clocksource/ingenic-timer.c 
> b/drivers/clocksource/ingenic-timer.c
> index 4963336..230e996 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

If you want to get rid of the JZ47xx, then just write 'Ingenic SoCs TCU 
IRQ driver', since XBurst is only the name of the CPU. Also, this 
belongs to a separate patch.

>   * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
> + * Copyright (C) 2020 周琰杰 (Zhou Yanjie) 
> <zhouyanjie@wanyeetech.com>
>   */
> 
>  #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];

NR_CPUS can be very big, so this would make the struct ingenic_tcu very 
large. What you could do, is have a variable array at the end of the 
struct:

unsigned int timer_local[];

Then you can create the ingenic_tcu struct using struct_size() from 
<linux/overflow.h>:

tcu = kzalloc(struct_size(tcu, timer_local, num_possible_cpus()), 
GFP_KERNEL);

-Paul

>  	unsigned int timer_channel, cs_channel;
>  	struct clock_event_device cevt;
>  	struct clocksource cs;
> -	char name[4];
> +	char name[8];
>  	unsigned long pwm_channels_mask;
> +	int cpu;
>  };
> 
>  static struct ingenic_tcu *ingenic_tcu;
> @@ -81,6 +87,24 @@ 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;
> +
> +	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;
> @@ -89,7 +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,21 @@ 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 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;

I have no idea why you're doing that, but this is really wrong and 
highly confusing. The ingenic_tcu instance is already created in the 
probe. If you need per-timer data, then create another structure.

-Paul

> +
> +	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 +170,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%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,6 +199,25 @@ static int __init ingenic_tcu_timer_init(struct 
> device_node *np,
>  	return err;
>  }
> 
> +static int ingenic_tcu_setup_cevt(unsigned int cpu)
> +{
> +	int ret;
> +
> +	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_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 int __init ingenic_tcu_clocksource_init(struct device_node 
> *np,
>  					       struct ingenic_tcu *tcu)
>  {
> @@ -240,6 +292,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,13 +306,18 @@ static int __init ingenic_tcu_init(struct 
> device_node *np)
>  	if (!tcu)
>  		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);
> +	/*
> +	 * Enable all TCU channels for PWM use by default except channels 
> 0/1,
> +	 * and channel 2 if target CPU is JZ4780 and SMP is selected.
> +	 */
> +	tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1,
> +								num_possible_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) {
> +	/* Verify that we have at least num_possible_cpus() + 1 free 
> channels */
> +	if (hweight8(tcu->pwm_channels_mask) >
> +			soc_info->num_channels - num_possible_cpus() + 1) {
>  		pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__,
>  			tcu->pwm_channels_mask);
>  		ret = -EINVAL;
> @@ -267,13 +325,19 @@ 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);
> +
> +	for (cpu = 1; cpu < num_possible_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_channel + 1);
> +			soc_info->num_channels, tcu->timer_local[cpu - 1] + 1);
> 
>  	ret = ingenic_tcu_clocksource_init(np, tcu);
>  	if (ret) {
> @@ -281,9 +345,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);
> @@ -291,10 +356,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
> 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
  2020-05-19 14:35 ` [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support 周琰杰 (Zhou Yanjie)
  2020-05-19 16:09   ` Paul Cercueil
@ 2020-05-19 18:21   ` kbuild test robot
  2020-05-19 19:41   ` Paul Cercueil
  2 siblings, 0 replies; 31+ messages in thread
From: kbuild test robot @ 2020-05-19 18:21 UTC (permalink / raw)
  To: 周琰杰 (Zhou Yanjie), linux-mips
  Cc: kbuild-all, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano

[-- Attachment #1: Type: text/plain, Size: 2668 bytes --]

Hi "周琰杰,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on robh/for-next]
[also build test WARNING on tip/timers/core linus/master v5.7-rc6]
[cannot apply to linux/master next-20200518]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Zhou-Yanjie/Introduce-SMP-support-for-CI20-based-on-JZ4780/20200519-224008
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: mips-allyesconfig (attached as .config)
compiler: mips-linux-gcc (GCC) 9.3.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=mips 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kbuild test robot <lkp@intel.com>

All warnings (new ones prefixed by >>, old ones prefixed by <<):

>> arch/mips/kernel/idle.c:97:6: warning: no previous prototype for 'jz4780_smp_wait_irqoff' [-Wmissing-prototypes]
97 | void jz4780_smp_wait_irqoff(void)
|      ^~~~~~~~~~~~~~~~~~~~~~
arch/mips/kernel/idle.c:155:13: warning: no previous prototype for 'check_wait' [-Wmissing-prototypes]
155 | void __init check_wait(void)
|             ^~~~~~~~~~

vim +/jz4780_smp_wait_irqoff +97 arch/mips/kernel/idle.c

    90	
    91	/*
    92	 * The Ingenic jz4780 SMP variant has to write back dirty cache lines before
    93	 * executing wait. The CPU & cache clock will be gated until we return from
    94	 * the wait, and if another core attempts to access data from our data cache
    95	 * during this time then it will lock up.
    96	 */
  > 97	void jz4780_smp_wait_irqoff(void)
    98	{
    99		unsigned long pending = read_c0_cause() & read_c0_status() & CAUSEF_IP;
   100	
   101		/*
   102		 * Going to idle has a significant overhead due to the cache flush so
   103		 * try to avoid it if we'll immediately be woken again due to an IRQ.
   104		 */
   105		if (!need_resched() && !pending) {
   106			r4k_blast_dcache();
   107	
   108			__asm__(
   109			"	.set push	\n"
   110			"	.set mips3	\n"
   111			"	sync		\n"
   112			"	wait		\n"
   113			"	.set pop	\n");
   114		}
   115	
   116		local_irq_enable();
   117	}
   118	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 65515 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
  2020-05-19 14:35 ` [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support 周琰杰 (Zhou Yanjie)
  2020-05-19 16:09   ` Paul Cercueil
  2020-05-19 18:21   ` kbuild test robot
@ 2020-05-19 19:41   ` Paul Cercueil
  2020-05-20  7:23     ` Zhou Yanjie
  2 siblings, 1 reply; 31+ messages in thread
From: Paul Cercueil @ 2020-05-19 19:41 UTC (permalink / raw)
  To: 周琰杰
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
	krzk, hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
	sernia.zhou, zhenwenjin

Hi Zhou,

Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
<zhouyanjie@wanyeetech.com> a écrit :
> Forward port smp support from kernel 3.18.3 of CI20_linux
> to upstream kernel 5.6.
> 
> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
> Tested-by: Paul Boddie <paul@boddie.org.uk>
> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
> 
> 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.
> 
>     v4->v5:
>     1.Splitting changes involving "jz4780-cgu.c" into separate commit.
>     2.Use "request_irq()" replace "setup_irq()".
> 
>     v5->v6:
>     In order to have a kernel that works on multiple SoCs at the same
>     time, use "IS_ENABLED()" replace "#ifdef".
> 
>     v6->v7:
>     1.SMP has be decoupled from the SoC version.
>     2.Add mailboxes 3 and 4 for XBurst.
>     3.Adjust code in "jz4780_smp_prepare_cpus()".
>     4."jz4780_smp_init()" has be marked "__init".
> 
>     v7->v8:
>     No change.
> 
>  arch/mips/include/asm/mach-jz4740/smp.h |  87 +++++++++++
>  arch/mips/jz4740/Kconfig                |   2 +
>  arch/mips/jz4740/Makefile               |   5 +
>  arch/mips/jz4740/prom.c                 |   4 +
>  arch/mips/jz4740/smp-entry.S            |  57 +++++++
>  arch/mips/jz4740/smp.c                  | 258 
> ++++++++++++++++++++++++++++++++
>  arch/mips/kernel/idle.c                 |  35 ++++-
>  7 files changed, 447 insertions(+), 1 deletion(-)
>  create mode 100644 arch/mips/include/asm/mach-jz4740/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/smp.h 
> b/arch/mips/include/asm/mach-jz4740/smp.h
> new file mode 100644
> index 00000000..86f660f
> --- /dev/null
> +++ b/arch/mips/include/asm/mach-jz4740/smp.h
> @@ -0,0 +1,87 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
> + *  JZ4780 SMP definitions
> + */
> +
> +#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
> +#define __MIPS_ASM_MACH_JZ4740_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 read_c0_mailbox2()		__read_32bit_c0_register($20, 2)
> +#define write_c0_mailbox2(val)		__write_32bit_c0_register($20, 2, 
> val)
> +
> +#define read_c0_mailbox3()		__read_32bit_c0_register($20, 3)
> +#define write_c0_mailbox3(val)		__write_32bit_c0_register($20, 3, 
> 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)
> +
> +extern void jz4780_smp_init(void);
> +extern void jz4780_secondary_cpu_entry(void);
> +
> +#endif /* __MIPS_ASM_MACH_JZ4740_SMP_H__ */
> diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
> index 412d2fa..2b88557 100644
> --- a/arch/mips/jz4740/Kconfig
> +++ b/arch/mips/jz4740/Kconfig
> @@ -34,9 +34,11 @@ config MACH_JZ4770
> 
>  config MACH_JZ4780
>  	bool
> +	select GENERIC_CLOCKEVENTS_BROADCAST if SMP
>  	select MIPS_CPU_SCACHE
>  	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..4acf5c2c 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/smp.h>
> 
>  void __init prom_init(void)
>  {
>  	fw_init_cmdline();
> +
> +	if (IS_ENABLED(CONFIG_SMP))
> +		jz4780_smp_init();
>  }
> 
>  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 <paul.burton@imgtec.com>
> + *  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..d95d22a
> --- /dev/null
> +++ b/arch/mips/jz4740/smp.c
> @@ -0,0 +1,258 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
> + *  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/smp.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;

This cpumask is written, but never read anywhere. Since it's static, I 
believe it's dead code.

> +
> +static DEFINE_SPINLOCK(smp_lock);
> +
> +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;
> +	case 2:
> +		action = read_c0_mailbox2();
> +		write_c0_mailbox2(0);
> +		break;
> +	case 3:
> +		action = read_c0_mailbox3();
> +		write_c0_mailbox3(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 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);

I assume if you do that, you will have num_possible_cpus() == NR_CPUS, 
which is not what you want.

Correct me if I'm wrong, but I think you would need to call 
set_cpu_possible() for each CPU node found.

> +	}
> +
> +	/* 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 mailbox2/3 too.

> +	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 */
> +	err = request_irq(MIPS_CPU_IRQ_BASE + 3, mbox_handler,
> +			IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL);

Please don't hardcode the IRQ number. Instead, it should be read from 
devicetree, maybe from the 'cpus' node (not sure).

> +	if (err)
> +		pr_err("request_irq() on core mailbox failed\n");
> +
> +	ctrl = read_c0_corectrl();
> +
> +	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;
> +		}
> +
> +		/* use reset entry point from REIM register */
> +		ctrl |= CORECTRL_RPC0 << cpu;
> +
> +		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");

I'd suggest to call clk_prepare() in jz4780_boot_secondary(), since you 
can't handle errors here. That would also avoid the static 
cpu_clock_gates[] array which can grow quite big since its size is 
given by NR_CPUS.

> +	}
> +
> +	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]);

You should check the return value of clk_enable().

+		break;
> +
> +	/* 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);

Handle mailboxes 2/3 too here.

> +	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 __init 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..d33f2d4 100644
> --- a/arch/mips/kernel/idle.c
> +++ b/arch/mips/kernel/idle.c
> @@ -18,6 +18,7 @@
>  #include <asm/cpu-type.h>
>  #include <asm/idle.h>
>  #include <asm/mipsregs.h>
> +#include <asm/r4kcache.h>
> 
>  /*
>   * Not all of the MIPS CPUs have the "wait" instruction available. 
> Moreover,
> @@ -88,6 +89,34 @@ static void __cpuidle rm7k_wait_irqoff(void)
>  }
> 
>  /*
> + * 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.
> +	 */

You could add a fast path here where you just call r4k_wait() if 
num_online_cpus() < 2.

-Paul

> +	if (!need_resched() && !pending) {
> +		r4k_blast_dcache();
> +
> +		__asm__(
> +		"	.set push	\n"
> +		"	.set mips3	\n"
> +		"	sync		\n"
> +		"	wait		\n"
> +		"	.set pop	\n");
> +	}
> +
> +	local_irq_enable();
> +}
> +
> +/*
>   * Au1 'wait' is only useful when the 32kHz counter is used as timer,
>   * since coreclock (and the cp0 counter) stops upon executing it. 
> Only an
>   * interrupt can wake it, so they must be enabled before entering 
> idle modes.
> @@ -172,7 +201,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 +274,11 @@ void __init check_wait(void)
>  		   cpu_wait = r4k_wait;
>  		 */
>  		break;
> +	case CPU_XBURST:
> +		if (IS_ENABLED(CONFIG_SMP))
> +			cpu_wait = jz4780_smp_wait_irqoff;
> +		else
> +			cpu_wait = r4k_wait;
>  	default:
>  		break;
>  	}
> --
> 2.7.4
> 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.
  2020-05-19 14:35 ` [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support " 周琰杰 (Zhou Yanjie)
  2020-05-19 17:42   ` Paul Cercueil
@ 2020-05-19 20:11   ` Paul Cercueil
  2020-05-20 22:14     ` Paul Boddie
  1 sibling, 1 reply; 31+ messages in thread
From: Paul Cercueil @ 2020-05-19 20:11 UTC (permalink / raw)
  To: 周琰杰
  Cc: linux-mips, linux-kernel, H . Nikolaus Schaller, Paul Boddie,
	Paul Cercueil

From: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>

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 <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---

Zhou:

I took the liberty to clean your patch so that it doesn't create a
struct ingenic_tcu per CPU timer.

Tested, and fully working on the JZ4770 with CONFIG_SMP disabled, and
also with CONFIG_SMP enabled (even though JZ4770 has only one CPU) with
a fixed smp.c and USB disabled (otherwise it crashes at boot).

Cheers,
-Paul

 drivers/clocksource/ingenic-timer.c | 180 +++++++++++++++++++---------
 1 file changed, 123 insertions(+), 57 deletions(-)

diff --git a/drivers/clocksource/ingenic-timer.c b/drivers/clocksource/ingenic-timer.c
index 496333650de2..a068e4620c44 100644
--- a/drivers/clocksource/ingenic-timer.c
+++ b/drivers/clocksource/ingenic-timer.c
@@ -2,6 +2,7 @@
 /*
  * JZ47xx SoCs TCU IRQ driver
  * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
  */
 
 #include <linux/bitops.h>
@@ -15,24 +16,35 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
+#include <linux/overflow.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/sched_clock.h>
 
 #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_timer {
+	unsigned int cpu;
+	unsigned int channel;
+	struct clock_event_device cevt;
+	struct clk *clk;
+	char name[8];
+};
+
 struct ingenic_tcu {
 	struct regmap *map;
-	struct clk *timer_clk, *cs_clk;
-	unsigned int timer_channel, cs_channel;
-	struct clock_event_device cevt;
+	struct device_node *np;
+	struct clk *cs_clk;
+	unsigned int cs_channel;
 	struct clocksource cs;
-	char name[4];
 	unsigned long pwm_channels_mask;
+	struct ingenic_tcu_timer timers[];
 };
 
 static struct ingenic_tcu *ingenic_tcu;
@@ -52,16 +64,24 @@ static u64 notrace ingenic_tcu_timer_cs_read(struct clocksource *cs)
 	return ingenic_tcu_timer_read();
 }
 
-static inline struct ingenic_tcu *to_ingenic_tcu(struct clock_event_device *evt)
+static inline struct ingenic_tcu *
+to_ingenic_tcu(struct ingenic_tcu_timer *timer)
+{
+	return container_of(timer, struct ingenic_tcu, timers[timer->cpu]);
+}
+
+static inline struct ingenic_tcu_timer *
+to_ingenic_tcu_timer(struct clock_event_device *evt)
 {
-	return container_of(evt, struct ingenic_tcu, cevt);
+	return container_of(evt, struct ingenic_tcu_timer, cevt);
 }
 
 static int ingenic_tcu_cevt_set_state_shutdown(struct clock_event_device *evt)
 {
-	struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
+	struct ingenic_tcu_timer *timer = to_ingenic_tcu_timer(evt);
+	struct ingenic_tcu *tcu = to_ingenic_tcu(timer);
 
-	regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
+	regmap_write(tcu->map, TCU_REG_TECR, BIT(timer->channel));
 
 	return 0;
 }
@@ -69,27 +89,40 @@ static int ingenic_tcu_cevt_set_state_shutdown(struct clock_event_device *evt)
 static int ingenic_tcu_cevt_set_next(unsigned long next,
 				     struct clock_event_device *evt)
 {
-	struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
+	struct ingenic_tcu_timer *timer = to_ingenic_tcu_timer(evt);
+	struct ingenic_tcu *tcu = to_ingenic_tcu(timer);
 
 	if (next > 0xffff)
 		return -EINVAL;
 
-	regmap_write(tcu->map, TCU_REG_TDFRc(tcu->timer_channel), next);
-	regmap_write(tcu->map, TCU_REG_TCNTc(tcu->timer_channel), 0);
-	regmap_write(tcu->map, TCU_REG_TESR, BIT(tcu->timer_channel));
+	regmap_write(tcu->map, TCU_REG_TDFRc(timer->channel), next);
+	regmap_write(tcu->map, TCU_REG_TCNTc(timer->channel), 0);
+	regmap_write(tcu->map, TCU_REG_TESR, BIT(timer->channel));
 
 	return 0;
 }
 
+static void ingenic_per_cpu_event_handler(void *info)
+{
+	struct clock_event_device *cevt = (struct clock_event_device *) info;
+
+	cevt->event_handler(cevt);
+}
+
 static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
 {
-	struct clock_event_device *evt = dev_id;
-	struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
+	struct ingenic_tcu_timer *timer = dev_id;
+	struct ingenic_tcu *tcu = to_ingenic_tcu(timer);
+	call_single_data_t *csd;
 
-	regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
+	regmap_write(tcu->map, TCU_REG_TECR, BIT(timer->channel));
 
-	if (evt->event_handler)
-		evt->event_handler(evt);
+	if (timer->cevt.event_handler) {
+		csd = &per_cpu(ingenic_cevt_csd, timer->cpu);
+		csd->info = (void *) &timer->cevt;
+		csd->func = ingenic_per_cpu_event_handler;
+		smp_call_function_single_async(timer->cpu, csd);
+	}
 
 	return IRQ_HANDLED;
 }
@@ -105,64 +138,66 @@ 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 ingenic_tcu_setup_cevt(unsigned int cpu)
 {
-	unsigned int timer_virq, channel = tcu->timer_channel;
+	struct ingenic_tcu *tcu = ingenic_tcu;
+	struct ingenic_tcu_timer *timer = &tcu->timers[cpu];
+	unsigned int timer_virq;
 	struct irq_domain *domain;
 	unsigned long rate;
 	int err;
 
-	tcu->timer_clk = ingenic_tcu_get_clock(np, channel);
-	if (IS_ERR(tcu->timer_clk))
-		return PTR_ERR(tcu->timer_clk);
+	timer->clk = ingenic_tcu_get_clock(tcu->np, timer->channel);
+	if (IS_ERR(timer->clk))
+		return PTR_ERR(timer->clk);
 
-	err = clk_prepare_enable(tcu->timer_clk);
+	err = clk_prepare_enable(timer->clk);
 	if (err)
 		goto err_clk_put;
 
-	rate = clk_get_rate(tcu->timer_clk);
+	rate = clk_get_rate(timer->clk);
 	if (!rate) {
 		err = -EINVAL;
 		goto err_clk_disable;
 	}
 
-	domain = irq_find_host(np);
+	domain = irq_find_host(tcu->np);
 	if (!domain) {
 		err = -ENODEV;
 		goto err_clk_disable;
 	}
 
-	timer_virq = irq_create_mapping(domain, channel);
+	timer_virq = irq_create_mapping(domain, timer->channel);
 	if (!timer_virq) {
 		err = -EINVAL;
 		goto err_clk_disable;
 	}
 
-	snprintf(tcu->name, sizeof(tcu->name), "TCU");
+	snprintf(timer->name, sizeof(timer->name), "TCU%u", timer->channel);
 
 	err = request_irq(timer_virq, ingenic_tcu_cevt_cb, IRQF_TIMER,
-			  tcu->name, &tcu->cevt);
+			  timer->name, timer);
 	if (err)
 		goto err_irq_dispose_mapping;
 
-	tcu->cevt.cpumask = cpumask_of(smp_processor_id());
-	tcu->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
-	tcu->cevt.name = tcu->name;
-	tcu->cevt.rating = 200;
-	tcu->cevt.set_state_shutdown = ingenic_tcu_cevt_set_state_shutdown;
-	tcu->cevt.set_next_event = ingenic_tcu_cevt_set_next;
+	timer->cpu = smp_processor_id();
+	timer->cevt.cpumask = cpumask_of(smp_processor_id());
+	timer->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
+	timer->cevt.name = timer->name;
+	timer->cevt.rating = 200;
+	timer->cevt.set_state_shutdown = ingenic_tcu_cevt_set_state_shutdown;
+	timer->cevt.set_next_event = ingenic_tcu_cevt_set_next;
 
-	clockevents_config_and_register(&tcu->cevt, rate, 10, 0xffff);
+	clockevents_config_and_register(&timer->cevt, rate, 10, 0xffff);
 
 	return 0;
 
 err_irq_dispose_mapping:
 	irq_dispose_mapping(timer_virq);
 err_clk_disable:
-	clk_disable_unprepare(tcu->timer_clk);
+	clk_disable_unprepare(timer->clk);
 err_clk_put:
-	clk_put(tcu->timer_clk);
+	clk_put(timer->clk);
 	return err;
 }
 
@@ -238,10 +273,12 @@ static int __init ingenic_tcu_init(struct device_node *np)
 {
 	const struct of_device_id *id = of_match_node(ingenic_tcu_of_match, np);
 	const struct ingenic_soc_info *soc_info = id->data;
+	struct ingenic_tcu_timer *timer;
 	struct ingenic_tcu *tcu;
 	struct regmap *map;
+	unsigned int cpu;
+	int ret, last_bit = -1;
 	long rate;
-	int ret;
 
 	of_node_clear_flag(np, OF_POPULATED);
 
@@ -249,17 +286,23 @@ static int __init ingenic_tcu_init(struct device_node *np)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
-	tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);
+	tcu = kzalloc(struct_size(tcu, timers, num_possible_cpus()),
+		      GFP_KERNEL);
 	if (!tcu)
 		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);
+	/*
+	 * Enable all TCU channels for PWM use by default except channels 0/1,
+	 * and channel 2 if target CPU is JZ4780 and SMP is selected.
+	 */
+	tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1,
+					 num_possible_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) {
+	/* Verify that we have at least num_possible_cpus() + 1 free channels */
+	if (hweight8(tcu->pwm_channels_mask) >
+			soc_info->num_channels - num_possible_cpus() + 1) {
 		pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__,
 			tcu->pwm_channels_mask);
 		ret = -EINVAL;
@@ -267,13 +310,22 @@ 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,
-						 soc_info->num_channels);
+	for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
+		timer = &tcu->timers[cpu];
+
+		timer->cpu = cpu;
+		timer->channel = find_next_zero_bit(&tcu->pwm_channels_mask,
+						  soc_info->num_channels,
+						  last_bit + 1);
+		last_bit = timer->channel;
+	}
+
 	tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
 					     soc_info->num_channels,
-					     tcu->timer_channel + 1);
+					     last_bit + 1);
 
 	ret = ingenic_tcu_clocksource_init(np, tcu);
 	if (ret) {
@@ -281,9 +333,13 @@ static int __init ingenic_tcu_init(struct device_node *np)
 		goto err_free_ingenic_tcu;
 	}
 
-	ret = ingenic_tcu_timer_init(np, tcu);
-	if (ret)
+	/* Setup clock events on each CPU core */
+	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Ingenic XBurst: online",
+				ingenic_tcu_setup_cevt, NULL);
+	if (ret < 0) {
+		pr_crit("%s: Unable to start CPU timers: %d\n", __func__, ret);
 		goto err_tcu_clocksource_cleanup;
+	}
 
 	/* Register the sched_clock at the end as there's no way to undo it */
 	rate = clk_get_rate(tcu->cs_clk);
@@ -315,28 +371,38 @@ static int __init ingenic_tcu_probe(struct platform_device *pdev)
 static int __maybe_unused ingenic_tcu_suspend(struct device *dev)
 {
 	struct ingenic_tcu *tcu = dev_get_drvdata(dev);
+	unsigned int cpu;
 
 	clk_disable(tcu->cs_clk);
-	clk_disable(tcu->timer_clk);
+
+	for (cpu = 0; cpu < num_online_cpus(); cpu++)
+		clk_disable(tcu->timers[cpu].clk);
+
 	return 0;
 }
 
 static int __maybe_unused ingenic_tcu_resume(struct device *dev)
 {
 	struct ingenic_tcu *tcu = dev_get_drvdata(dev);
+	unsigned int cpu;
 	int ret;
 
-	ret = clk_enable(tcu->timer_clk);
-	if (ret)
-		return ret;
+	for (cpu = 0; cpu < num_online_cpus(); cpu++) {
+		ret = clk_enable(tcu->timers[cpu].clk);
+		if (ret)
+			goto err_timer_clk_disable;
+	}
 
 	ret = clk_enable(tcu->cs_clk);
-	if (ret) {
-		clk_disable(tcu->timer_clk);
-		return ret;
-	}
+	if (ret)
+		goto err_timer_clk_disable;
 
 	return 0;
+
+err_timer_clk_disable:
+	for (; cpu > 0; cpu--)
+		clk_disable(tcu->timers[cpu - 1].clk);
+	return ret;
 }
 
 static const struct dev_pm_ops __maybe_unused ingenic_tcu_pm_ops = {
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
  2020-05-19 19:41   ` Paul Cercueil
@ 2020-05-20  7:23     ` Zhou Yanjie
  2020-05-20 11:33       ` Paul Cercueil
  0 siblings, 1 reply; 31+ messages in thread
From: Zhou Yanjie @ 2020-05-20  7:23 UTC (permalink / raw)
  To: Paul Cercueil
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
	krzk, hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
	sernia.zhou, zhenwenjin

Hi Paul,

On 2020年05月20日 03:41, Paul Cercueil wrote:
> Hi Zhou,
>
> Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
> <zhouyanjie@wanyeetech.com> a écrit :
>> Forward port smp support from kernel 3.18.3 of CI20_linux
>> to upstream kernel 5.6.
>>
>> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
>> Tested-by: Paul Boddie <paul@boddie.org.uk>
>> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
>> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
>> ---
>>
>> 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.
>>
>>     v4->v5:
>>     1.Splitting changes involving "jz4780-cgu.c" into separate commit.
>>     2.Use "request_irq()" replace "setup_irq()".
>>
>>     v5->v6:
>>     In order to have a kernel that works on multiple SoCs at the same
>>     time, use "IS_ENABLED()" replace "#ifdef".
>>
>>     v6->v7:
>>     1.SMP has be decoupled from the SoC version.
>>     2.Add mailboxes 3 and 4 for XBurst.
>>     3.Adjust code in "jz4780_smp_prepare_cpus()".
>>     4."jz4780_smp_init()" has be marked "__init".
>>
>>     v7->v8:
>>     No change.
>>
>>  arch/mips/include/asm/mach-jz4740/smp.h |  87 +++++++++++
>>  arch/mips/jz4740/Kconfig                |   2 +
>>  arch/mips/jz4740/Makefile               |   5 +
>>  arch/mips/jz4740/prom.c                 |   4 +
>>  arch/mips/jz4740/smp-entry.S            |  57 +++++++
>>  arch/mips/jz4740/smp.c                  | 258 
>> ++++++++++++++++++++++++++++++++
>>  arch/mips/kernel/idle.c                 |  35 ++++-
>>  7 files changed, 447 insertions(+), 1 deletion(-)
>>  create mode 100644 arch/mips/include/asm/mach-jz4740/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/smp.h 
>> b/arch/mips/include/asm/mach-jz4740/smp.h
>> new file mode 100644
>> index 00000000..86f660f
>> --- /dev/null
>> +++ b/arch/mips/include/asm/mach-jz4740/smp.h
>> @@ -0,0 +1,87 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
>> + *  JZ4780 SMP definitions
>> + */
>> +
>> +#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
>> +#define __MIPS_ASM_MACH_JZ4740_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 read_c0_mailbox2()        __read_32bit_c0_register($20, 2)
>> +#define write_c0_mailbox2(val) __write_32bit_c0_register($20, 2, val)
>> +
>> +#define read_c0_mailbox3()        __read_32bit_c0_register($20, 3)
>> +#define write_c0_mailbox3(val) __write_32bit_c0_register($20, 3, 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)
>> +
>> +extern void jz4780_smp_init(void);
>> +extern void jz4780_secondary_cpu_entry(void);
>> +
>> +#endif /* __MIPS_ASM_MACH_JZ4740_SMP_H__ */
>> diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
>> index 412d2fa..2b88557 100644
>> --- a/arch/mips/jz4740/Kconfig
>> +++ b/arch/mips/jz4740/Kconfig
>> @@ -34,9 +34,11 @@ config MACH_JZ4770
>>
>>  config MACH_JZ4780
>>      bool
>> +    select GENERIC_CLOCKEVENTS_BROADCAST if SMP
>>      select MIPS_CPU_SCACHE
>>      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..4acf5c2c 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/smp.h>
>>
>>  void __init prom_init(void)
>>  {
>>      fw_init_cmdline();
>> +
>> +    if (IS_ENABLED(CONFIG_SMP))
>> +        jz4780_smp_init();
>>  }
>>
>>  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 <paul.burton@imgtec.com>
>> + *  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..d95d22a
>> --- /dev/null
>> +++ b/arch/mips/jz4740/smp.c
>> @@ -0,0 +1,258 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
>> + *  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/smp.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;
>
> This cpumask is written, but never read anywhere. Since it's static, I 
> believe it's dead code.
>

Sure, I will remove it.

>> +
>> +static DEFINE_SPINLOCK(smp_lock);
>> +
>> +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;
>> +    case 2:
>> +        action = read_c0_mailbox2();
>> +        write_c0_mailbox2(0);
>> +        break;
>> +    case 3:
>> +        action = read_c0_mailbox3();
>> +        write_c0_mailbox3(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 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);
>
> I assume if you do that, you will have num_possible_cpus() == NR_CPUS, 
> which is not what you want.
>
> Correct me if I'm wrong, but I think you would need to call 
> set_cpu_possible() for each CPU node found.
>

Yes, the current way is indeed a little problem, it will cause 
num_possible_cpus() == NR_CPUS, I will try to find a better way.

>> +    }
>> +
>> +    /* 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 mailbox2/3 too.
>

Although the XBurst1 architecture can have up to four cores, but JZ4780 
only has two. If we need to write all four mailboxes here, do we need 
change the function name to "xburst1_smp_setup" or other similar names? 
This seems more appropriate.

>> +    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 */
>> +    err = request_irq(MIPS_CPU_IRQ_BASE + 3, mbox_handler,
>> +            IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL);
>
> Please don't hardcode the IRQ number. Instead, it should be read from 
> devicetree, maybe from the 'cpus' node (not sure).
>

OK, I'll try to figure it out.

>> +    if (err)
>> +        pr_err("request_irq() on core mailbox failed\n");
>> +
>> +    ctrl = read_c0_corectrl();
>> +
>> +    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;
>> +        }
>> +
>> +        /* use reset entry point from REIM register */
>> +        ctrl |= CORECTRL_RPC0 << cpu;
>> +
>> +        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");
>
> I'd suggest to call clk_prepare() in jz4780_boot_secondary(), since 
> you can't handle errors here. That would also avoid the static 
> cpu_clock_gates[] array which can grow quite big since its size is 
> given by NR_CPUS.
>

Sure, I will move it to jz4780_boot_secondary().

>> +    }
>> +
>> +    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]);
>
> You should check the return value of clk_enable().
>
> +        break;

Sure.

>> +
>> +    /* 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);
>
> Handle mailboxes 2/3 too here.
>

Same to the above, do we need to change the function to a more 
appropriate name?

>> +    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 __init 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..d33f2d4 100644
>> --- a/arch/mips/kernel/idle.c
>> +++ b/arch/mips/kernel/idle.c
>> @@ -18,6 +18,7 @@
>>  #include <asm/cpu-type.h>
>>  #include <asm/idle.h>
>>  #include <asm/mipsregs.h>
>> +#include <asm/r4kcache.h>
>>
>>  /*
>>   * Not all of the MIPS CPUs have the "wait" instruction available. 
>> Moreover,
>> @@ -88,6 +89,34 @@ static void __cpuidle rm7k_wait_irqoff(void)
>>  }
>>
>>  /*
>> + * 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.
>> +     */
>
> You could add a fast path here where you just call r4k_wait() if 
> num_online_cpus() < 2.
>

Please correct me if I'm wrong, if we add it here, when the number of 
CPU cores is greater than 1 (which should be the case on most 
occasions), each call to "jz4780_smp_wait_irqoff" will generate 
additional overhead (judging the number of CPUs), is it better to change 
"if (IS_ENABLED(CONFIG_SMP))" in "case CPU_XBURST" below to "if 
(IS_ENABLED(CONFIG_SMP) && (num_possible_cpus() > 1))"?

Thanks and best regards!

> -Paul
>
>> +    if (!need_resched() && !pending) {
>> +        r4k_blast_dcache();
>> +
>> +        __asm__(
>> +        "    .set push    \n"
>> +        "    .set mips3    \n"
>> +        "    sync        \n"
>> +        "    wait        \n"
>> +        "    .set pop    \n");
>> +    }
>> +
>> +    local_irq_enable();
>> +}
>> +
>> +/*
>>   * Au1 'wait' is only useful when the 32kHz counter is used as timer,
>>   * since coreclock (and the cp0 counter) stops upon executing it. 
>> Only an
>>   * interrupt can wake it, so they must be enabled before entering 
>> idle modes.
>> @@ -172,7 +201,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 +274,11 @@ void __init check_wait(void)
>>             cpu_wait = r4k_wait;
>>           */
>>          break;
>> +    case CPU_XBURST:
>> +        if (IS_ENABLED(CONFIG_SMP))
>> +            cpu_wait = jz4780_smp_wait_irqoff;
>> +        else
>> +            cpu_wait = r4k_wait;
>>      default:
>>          break;
>>      }
>> -- 
>> 2.7.4
>>
>


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
  2020-05-19 16:09   ` Paul Cercueil
@ 2020-05-20  7:24     ` Zhou Yanjie
  0 siblings, 0 replies; 31+ messages in thread
From: Zhou Yanjie @ 2020-05-20  7:24 UTC (permalink / raw)
  To: Paul Cercueil
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
	krzk, hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
	sernia.zhou, zhenwenjin



On 2020年05月20日 00:09, Paul Cercueil wrote:
> Hi Zhou,
>
> Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
> <zhouyanjie@wanyeetech.com> a écrit :
>> Forward port smp support from kernel 3.18.3 of CI20_linux
>> to upstream kernel 5.6.
>>
>> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
>> Tested-by: Paul Boddie <paul@boddie.org.uk>
>> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
>> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
>> ---
>>
>> 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.
>>
>>     v4->v5:
>>     1.Splitting changes involving "jz4780-cgu.c" into separate commit.
>>     2.Use "request_irq()" replace "setup_irq()".
>>
>>     v5->v6:
>>     In order to have a kernel that works on multiple SoCs at the same
>>     time, use "IS_ENABLED()" replace "#ifdef".
>>
>>     v6->v7:
>>     1.SMP has be decoupled from the SoC version.
>>     2.Add mailboxes 3 and 4 for XBurst.
>>     3.Adjust code in "jz4780_smp_prepare_cpus()".
>>     4."jz4780_smp_init()" has be marked "__init".
>>
>>     v7->v8:
>>     No change.
>>
>>  arch/mips/include/asm/mach-jz4740/smp.h |  87 +++++++++++
>>  arch/mips/jz4740/Kconfig                |   2 +
>>  arch/mips/jz4740/Makefile               |   5 +
>>  arch/mips/jz4740/prom.c                 |   4 +
>>  arch/mips/jz4740/smp-entry.S            |  57 +++++++
>>  arch/mips/jz4740/smp.c                  | 258 
>> ++++++++++++++++++++++++++++++++
>>  arch/mips/kernel/idle.c                 |  35 ++++-
>>  7 files changed, 447 insertions(+), 1 deletion(-)
>>  create mode 100644 arch/mips/include/asm/mach-jz4740/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/smp.h 
>> b/arch/mips/include/asm/mach-jz4740/smp.h
>> new file mode 100644
>> index 00000000..86f660f
>> --- /dev/null
>> +++ b/arch/mips/include/asm/mach-jz4740/smp.h
>> @@ -0,0 +1,87 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
>> + *  JZ4780 SMP definitions
>> + */
>> +
>> +#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
>> +#define __MIPS_ASM_MACH_JZ4740_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 read_c0_mailbox2()        __read_32bit_c0_register($20, 2)
>> +#define write_c0_mailbox2(val) __write_32bit_c0_register($20, 2, val)
>> +
>> +#define read_c0_mailbox3()        __read_32bit_c0_register($20, 3)
>> +#define write_c0_mailbox3(val) __write_32bit_c0_register($20, 3, 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)
>> +
>> +extern void jz4780_smp_init(void);
>> +extern void jz4780_secondary_cpu_entry(void);
>> +
>> +#endif /* __MIPS_ASM_MACH_JZ4740_SMP_H__ */
>> diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
>> index 412d2fa..2b88557 100644
>> --- a/arch/mips/jz4740/Kconfig
>> +++ b/arch/mips/jz4740/Kconfig
>> @@ -34,9 +34,11 @@ config MACH_JZ4770
>>
>>  config MACH_JZ4780
>>      bool
>> +    select GENERIC_CLOCKEVENTS_BROADCAST if SMP
>>      select MIPS_CPU_SCACHE
>>      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..4acf5c2c 100644
>> --- a/arch/mips/jz4740/prom.c
>> +++ b/arch/mips/jz4740/prom.c
>
> That file is gone in mips-next. You should rebase your patchset on top 
> of mips-next.
>

OK, I will fix it in the next version.

> Cheers,
> -Paul
>
>> @@ -8,10 +8,14 @@
>>
>>  #include <asm/bootinfo.h>
>>  #include <asm/fw/fw.h>
>> +#include <asm/mach-jz4740/smp.h>
>>
>>  void __init prom_init(void)
>>  {
>>      fw_init_cmdline();
>> +
>> +    if (IS_ENABLED(CONFIG_SMP))
>> +        jz4780_smp_init();
>>  }
>>
>>  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 <paul.burton@imgtec.com>
>> + *  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..d95d22a
>> --- /dev/null
>> +++ b/arch/mips/jz4740/smp.c
>> @@ -0,0 +1,258 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
>> + *  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/smp.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);
>> +
>> +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;
>> +    case 2:
>> +        action = read_c0_mailbox2();
>> +        write_c0_mailbox2(0);
>> +        break;
>> +    case 3:
>> +        action = read_c0_mailbox3();
>> +        write_c0_mailbox3(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 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 */
>> +    err = request_irq(MIPS_CPU_IRQ_BASE + 3, mbox_handler,
>> +            IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL);
>> +    if (err)
>> +        pr_err("request_irq() on core mailbox failed\n");
>> +
>> +    ctrl = read_c0_corectrl();
>> +
>> +    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;
>> +        }
>> +
>> +        /* use reset entry point from REIM register */
>> +        ctrl |= CORECTRL_RPC0 << cpu;
>> +
>> +        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 __init 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..d33f2d4 100644
>> --- a/arch/mips/kernel/idle.c
>> +++ b/arch/mips/kernel/idle.c
>> @@ -18,6 +18,7 @@
>>  #include <asm/cpu-type.h>
>>  #include <asm/idle.h>
>>  #include <asm/mipsregs.h>
>> +#include <asm/r4kcache.h>
>>
>>  /*
>>   * Not all of the MIPS CPUs have the "wait" instruction available. 
>> Moreover,
>> @@ -88,6 +89,34 @@ static void __cpuidle rm7k_wait_irqoff(void)
>>  }
>>
>>  /*
>> + * 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();
>> +}
>> +
>> +/*
>>   * Au1 'wait' is only useful when the 32kHz counter is used as timer,
>>   * since coreclock (and the cp0 counter) stops upon executing it. 
>> Only an
>>   * interrupt can wake it, so they must be enabled before entering 
>> idle modes.
>> @@ -172,7 +201,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 +274,11 @@ void __init check_wait(void)
>>             cpu_wait = r4k_wait;
>>           */
>>          break;
>> +    case CPU_XBURST:
>> +        if (IS_ENABLED(CONFIG_SMP))
>> +            cpu_wait = jz4780_smp_wait_irqoff;
>> +        else
>> +            cpu_wait = r4k_wait;
>>      default:
>>          break;
>>      }
>> -- 
>> 2.7.4
>>
>


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
  2020-05-20  7:23     ` Zhou Yanjie
@ 2020-05-20 11:33       ` Paul Cercueil
  2020-05-20 12:32         ` Jiaxun Yang
  0 siblings, 1 reply; 31+ messages in thread
From: Paul Cercueil @ 2020-05-20 11:33 UTC (permalink / raw)
  To: Zhou Yanjie
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
	krzk, hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
	sernia.zhou, zhenwenjin

Hi Zhou,

Le mer. 20 mai 2020 à 15:23, Zhou Yanjie <zhouyanjie@wanyeetech.com> a 
écrit :
> Hi Paul,
> 
> On 2020年05月20日 03:41, Paul Cercueil wrote:
>> Hi Zhou,
>> 
>> Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
>> \x7f<zhouyanjie@wanyeetech.com> a écrit :
>>> Forward port smp support from kernel 3.18.3 of CI20_linux
>>> to upstream kernel 5.6.
>>> 
>>> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
>>> Tested-by: Paul Boddie <paul@boddie.org.uk>
>>> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
>>> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
>>> ---
>>> 
>>> 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 
>>> \x7f\x7fdead loop.
>>>     3.Replace hard code in smp.c with macro.
>>> 
>>>     v2->v3:
>>>     1.Remove unnecessary "extern void (*r4k_blast_dcache)(void)" in 
>>> \x7f\x7fsmp.c.
>>>     2.Use "for_each_of_cpu_node" instead "for_each_compatible_node" 
>>> \x7f\x7fin smp.c.
>>>     3.Use "of_cpu_node_to_id" instead "of_property_read_u32_index" 
>>> in \x7f\x7fsmp.c.
>>>     4.Move LCR related operations to jz4780-cgu.c.
>>> 
>>>     v3->v4:
>>>     Rebase on top of kernel 5.6-rc1.
>>> 
>>>     v4->v5:
>>>     1.Splitting changes involving "jz4780-cgu.c" into separate 
>>> commit.
>>>     2.Use "request_irq()" replace "setup_irq()".
>>> 
>>>     v5->v6:
>>>     In order to have a kernel that works on multiple SoCs at the 
>>> same
>>>     time, use "IS_ENABLED()" replace "#ifdef".
>>> 
>>>     v6->v7:
>>>     1.SMP has be decoupled from the SoC version.
>>>     2.Add mailboxes 3 and 4 for XBurst.
>>>     3.Adjust code in "jz4780_smp_prepare_cpus()".
>>>     4."jz4780_smp_init()" has be marked "__init".
>>> 
>>>     v7->v8:
>>>     No change.
>>> 
>>>  arch/mips/include/asm/mach-jz4740/smp.h |  87 +++++++++++
>>>  arch/mips/jz4740/Kconfig                |   2 +
>>>  arch/mips/jz4740/Makefile               |   5 +
>>>  arch/mips/jz4740/prom.c                 |   4 +
>>>  arch/mips/jz4740/smp-entry.S            |  57 +++++++
>>>  arch/mips/jz4740/smp.c                  | 258 
>>> \x7f\x7f++++++++++++++++++++++++++++++++
>>>  arch/mips/kernel/idle.c                 |  35 ++++-
>>>  7 files changed, 447 insertions(+), 1 deletion(-)
>>>  create mode 100644 arch/mips/include/asm/mach-jz4740/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/smp.h 
>>> \x7f\x7fb/arch/mips/include/asm/mach-jz4740/smp.h
>>> new file mode 100644
>>> index 00000000..86f660f
>>> --- /dev/null
>>> +++ b/arch/mips/include/asm/mach-jz4740/smp.h
>>> @@ -0,0 +1,87 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
>>> + *  JZ4780 SMP definitions
>>> + */
>>> +
>>> +#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
>>> +#define __MIPS_ASM_MACH_JZ4740_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 read_c0_mailbox2()        __read_32bit_c0_register($20, 2)
>>> +#define write_c0_mailbox2(val) __write_32bit_c0_register($20, 2, 
>>> val)
>>> +
>>> +#define read_c0_mailbox3()        __read_32bit_c0_register($20, 3)
>>> +#define write_c0_mailbox3(val) __write_32bit_c0_register($20, 3, 
>>> 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)
>>> +
>>> +extern void jz4780_smp_init(void);
>>> +extern void jz4780_secondary_cpu_entry(void);
>>> +
>>> +#endif /* __MIPS_ASM_MACH_JZ4740_SMP_H__ */
>>> diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
>>> index 412d2fa..2b88557 100644
>>> --- a/arch/mips/jz4740/Kconfig
>>> +++ b/arch/mips/jz4740/Kconfig
>>> @@ -34,9 +34,11 @@ config MACH_JZ4770
>>> 
>>>  config MACH_JZ4780
>>>      bool
>>> +    select GENERIC_CLOCKEVENTS_BROADCAST if SMP
>>>      select MIPS_CPU_SCACHE
>>>      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..4acf5c2c 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/smp.h>
>>> 
>>>  void __init prom_init(void)
>>>  {
>>>      fw_init_cmdline();
>>> +
>>> +    if (IS_ENABLED(CONFIG_SMP))
>>> +        jz4780_smp_init();
>>>  }
>>> 
>>>  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 <paul.burton@imgtec.com>
>>> + *  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..d95d22a
>>> --- /dev/null
>>> +++ b/arch/mips/jz4740/smp.c
>>> @@ -0,0 +1,258 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + *  Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
>>> + *  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/smp.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;
>> 
>> This cpumask is written, but never read anywhere. Since it's static, 
>> I \x7fbelieve it's dead code.
>> 
> 
> Sure, I will remove it.
> 
>>> +
>>> +static DEFINE_SPINLOCK(smp_lock);
>>> +
>>> +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;
>>> +    case 2:
>>> +        action = read_c0_mailbox2();
>>> +        write_c0_mailbox2(0);
>>> +        break;
>>> +    case 3:
>>> +        action = read_c0_mailbox3();
>>> +        write_c0_mailbox3(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 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);
>> 
>> I assume if you do that, you will have num_possible_cpus() == 
>> NR_CPUS, \x7fwhich is not what you want.
>> 
>> Correct me if I'm wrong, but I think you would need to call 
>> \x7fset_cpu_possible() for each CPU node found.
>> 
> 
> Yes, the current way is indeed a little problem, it will cause 
> num_possible_cpus() == NR_CPUS, I will try to find a better way.

You can do:

for_each_of_cpu_node(cpu_node) {
       cpu = of_cpu_node_to_id(cpu_node);
       __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 mailbox2/3 too.
>> 
> 
> Although the XBurst1 architecture can have up to four cores, but 
> JZ4780 only has two. If we need to write all four mailboxes here, do 
> we need change the function name to "xburst1_smp_setup" or other 
> similar names? This seems more appropriate.

Yes, these functions are not jz4780-specific, you can rename them all.

>>> +    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 */
>>> +    err = request_irq(MIPS_CPU_IRQ_BASE + 3, mbox_handler,
>>> +            IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL);
>> 
>> Please don't hardcode the IRQ number. Instead, it should be read 
>> from \x7fdevicetree, maybe from the 'cpus' node (not sure).
>> 
> 
> OK, I'll try to figure it out.
> 
>>> +    if (err)
>>> +        pr_err("request_irq() on core mailbox failed\n");
>>> +
>>> +    ctrl = read_c0_corectrl();
>>> +
>>> +    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;
>>> +        }
>>> +
>>> +        /* use reset entry point from REIM register */
>>> +        ctrl |= CORECTRL_RPC0 << cpu;
>>> +
>>> +        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");
>> 
>> I'd suggest to call clk_prepare() in jz4780_boot_secondary(), since 
>> \x7fyou can't handle errors here. That would also avoid the static 
>> \x7fcpu_clock_gates[] array which can grow quite big since its size is 
>> \x7fgiven by NR_CPUS.
>> 
> 
> Sure, I will move it to jz4780_boot_secondary().
> 
>>> +    }
>>> +
>>> +    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]);
>> 
>> You should check the return value of clk_enable().
>> 
>> +        break;
> 
> Sure.
> 
>>> +
>>> +    /* 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);
>> 
>> Handle mailboxes 2/3 too here.
>> 
> 
> Same to the above, do we need to change the function to a more 
> appropriate name?
> 
>>> +    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 __init 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..d33f2d4 100644
>>> --- a/arch/mips/kernel/idle.c
>>> +++ b/arch/mips/kernel/idle.c
>>> @@ -18,6 +18,7 @@
>>>  #include <asm/cpu-type.h>
>>>  #include <asm/idle.h>
>>>  #include <asm/mipsregs.h>
>>> +#include <asm/r4kcache.h>
>>> 
>>>  /*
>>>   * Not all of the MIPS CPUs have the "wait" instruction available. 
>>> \x7f\x7fMoreover,
>>> @@ -88,6 +89,34 @@ static void __cpuidle rm7k_wait_irqoff(void)
>>>  }
>>> 
>>>  /*
>>> + * The Ingenic jz4780 SMP variant has to write back dirty cache 
>>> \x7f\x7flines before
>>> + * executing wait. The CPU & cache clock will be gated until we 
>>> \x7f\x7freturn from
>>> + * the wait, and if another core attempts to access data from our 
>>> \x7f\x7fdata cache
>>> + * during this time then it will lock up.
>>> + */
>>> +void jz4780_smp_wait_irqoff(void)
>>> +{
>>> +    unsigned long pending = read_c0_cause() & read_c0_status() & 
>>> \x7f\x7fCAUSEF_IP;
>>> +
>>> +    /*
>>> +     * Going to idle has a significant overhead due to the cache 
>>> \x7f\x7fflush so
>>> +     * try to avoid it if we'll immediately be woken again due to 
>>> an \x7f\x7fIRQ.
>>> +     */
>> 
>> You could add a fast path here where you just call r4k_wait() if 
>> \x7fnum_online_cpus() < 2.
>> 
> 
> Please correct me if I'm wrong, if we add it here, when the number of 
> CPU cores is greater than 1 (which should be the case on most 
> occasions), each call to "jz4780_smp_wait_irqoff" will generate 
> additional overhead (judging the number of CPUs), is it better to 
> change "if (IS_ENABLED(CONFIG_SMP))" in "case CPU_XBURST" below to 
> "if (IS_ENABLED(CONFIG_SMP) && (num_possible_cpus() > 1))"?

Is the number of possible CPUs already known when cpu_wait is set?

Cheers,
-Paul

> Thanks and best regards!
> 
>> -Paul
>> 
>>> +    if (!need_resched() && !pending) {
>>> +        r4k_blast_dcache();
>>> +
>>> +        __asm__(
>>> +        "    .set push    \n"
>>> +        "    .set mips3    \n"
>>> +        "    sync        \n"
>>> +        "    wait        \n"
>>> +        "    .set pop    \n");
>>> +    }
>>> +
>>> +    local_irq_enable();
>>> +}
>>> +
>>> +/*
>>>   * Au1 'wait' is only useful when the 32kHz counter is used as 
>>> timer,
>>>   * since coreclock (and the cp0 counter) stops upon executing it. 
>>> \x7f\x7fOnly an
>>>   * interrupt can wake it, so they must be enabled before entering 
>>> \x7f\x7fidle modes.
>>> @@ -172,7 +201,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 +274,11 @@ void __init check_wait(void)
>>>             cpu_wait = r4k_wait;
>>>           */
>>>          break;
>>> +    case CPU_XBURST:
>>> +        if (IS_ENABLED(CONFIG_SMP))
>>> +            cpu_wait = jz4780_smp_wait_irqoff;
>>> +        else
>>> +            cpu_wait = r4k_wait;
>>>      default:
>>>          break;
>>>      }
>>> --
>>> 2.7.4
>>> 
>> 
> 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
  2020-05-20 11:33       ` Paul Cercueil
@ 2020-05-20 12:32         ` Jiaxun Yang
  0 siblings, 0 replies; 31+ messages in thread
From: Jiaxun Yang @ 2020-05-20 12:32 UTC (permalink / raw)
  To: Paul Cercueil, Zhou Yanjie
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	chenhc, tglx, robh+dt, daniel.lezcano, keescook, krzk, hns,
	ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin



于 2020年5月20日 GMT+08:00 下午7:33:22, Paul Cercueil <paul@crapouillou.net> 写到:
>> 
>> Yes, the current way is indeed a little problem, it will cause 
>> num_possible_cpus() == NR_CPUS, I will try to find a better way.
>
>You can do:
>
>for_each_of_cpu_node(cpu_node) {
>       cpu = of_cpu_node_to_id(cpu_node);
>       __cpu_number_map[cpu] = cpu;
>       __cpu_logical_map[cpu] = cpu;
>       set_cpu_possible(cpu, true);
>}
>

FYI: There is a abandoned DeviceTree[1], parser. You can take it.

I'm going to submit this topology clean-up for next release cycle
but you can pick it for now.

[...]

[1]: https://lkml.org/lkml/2020/4/11/1088
-- 
Jiaxun Yang

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.
  2020-05-19 20:11   ` [PATCH] " Paul Cercueil
@ 2020-05-20 22:14     ` Paul Boddie
  2020-05-22 12:26       ` Paul Cercueil
  0 siblings, 1 reply; 31+ messages in thread
From: Paul Boddie @ 2020-05-20 22:14 UTC (permalink / raw)
  To: Paul Cercueil, H . Nikolaus Schaller; +Cc: 周琰杰, linux-mips

On Tuesday 19. May 2020 22.11.10 Paul Cercueil wrote:
> 
> I took the liberty to clean your patch so that it doesn't create a
> struct ingenic_tcu per CPU timer.
> 
> Tested, and fully working on the JZ4770 with CONFIG_SMP disabled, and
> also with CONFIG_SMP enabled (even though JZ4770 has only one CPU) with
> a fixed smp.c and USB disabled (otherwise it crashes at boot).

Thanks for looking at this and also your continuing work with the DRM driver. 
I'll try and look a bit more at the DRM driver for the JZ4780 and see what I 
am doing wrong with regard to JZ4780-specific configuration. In principle, 
there should not be any, but I don't really have the full picture (literally 
and also in terms of documentation or understanding).

One thing that seems to be done with the Imagination/Ingenic drivers is dual 
DMA channel initialisation. Usually, this seems to be necessary only for dual-
panel configurations, and I wonder whether there is some kind of requirement 
for the same arrangement when using the HDMI output.

Another thing that is done involves setting a foreground, which might be 
relevant to your more recent contributions adding IPU support. Again, I don't 
really understand why this seems necessary, but the documentation hardly 
provides any details that might explain it.

Paul

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.
  2020-05-20 22:14     ` Paul Boddie
@ 2020-05-22 12:26       ` Paul Cercueil
  2020-05-22 19:16         ` Paul Boddie
  0 siblings, 1 reply; 31+ messages in thread
From: Paul Cercueil @ 2020-05-22 12:26 UTC (permalink / raw)
  To: Paul Boddie; +Cc: H . Nikolaus Schaller, 周琰杰, linux-mips

Hi Paul,

I think the ingenic-drm driver is fine, even though the old 3.8 kernel 
worked differently, the IP is backwards-compatible so it should work no 
problem. I think the problem is somewhere in the Synopsis HDMI code or 
the glue code. Because the LCDC does seem to send data, which is not 
encoded properly by the HDMI chip.

-Paul


Le jeu. 21 mai 2020 à 0:14, Paul Boddie <paul@boddie.org.uk> a écrit :
> On Tuesday 19. May 2020 22.11.10 Paul Cercueil wrote:
>> 
>>  I took the liberty to clean your patch so that it doesn't create a
>>  struct ingenic_tcu per CPU timer.
>> 
>>  Tested, and fully working on the JZ4770 with CONFIG_SMP disabled, 
>> and
>>  also with CONFIG_SMP enabled (even though JZ4770 has only one CPU) 
>> with
>>  a fixed smp.c and USB disabled (otherwise it crashes at boot).
> 
> Thanks for looking at this and also your continuing work with the DRM 
> driver.
> I'll try and look a bit more at the DRM driver for the JZ4780 and see 
> what I
> am doing wrong with regard to JZ4780-specific configuration. In 
> principle,
> there should not be any, but I don't really have the full picture 
> (literally
> and also in terms of documentation or understanding).
> 
> One thing that seems to be done with the Imagination/Ingenic drivers 
> is dual
> DMA channel initialisation. Usually, this seems to be necessary only 
> for dual-
> panel configurations, and I wonder whether there is some kind of 
> requirement
> for the same arrangement when using the HDMI output.
> 
> Another thing that is done involves setting a foreground, which might 
> be
> relevant to your more recent contributions adding IPU support. Again, 
> I don't
> really understand why this seems necessary, but the documentation 
> hardly
> provides any details that might explain it.
> 
> Paul



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.
  2020-05-22 12:26       ` Paul Cercueil
@ 2020-05-22 19:16         ` Paul Boddie
  2020-05-25 23:03           ` JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.) Paul Boddie
  0 siblings, 1 reply; 31+ messages in thread
From: Paul Boddie @ 2020-05-22 19:16 UTC (permalink / raw)
  To: Paul Cercueil; +Cc: H . Nikolaus Schaller, 周琰杰, linux-mips

On Friday 22. May 2020 14.26.15 Paul Cercueil wrote:
> Hi Paul,
> 
> I think the ingenic-drm driver is fine, even though the old 3.8 kernel
> worked differently, the IP is backwards-compatible so it should work no
> problem. I think the problem is somewhere in the Synopsis HDMI code or
> the glue code. Because the LCDC does seem to send data, which is not
> encoded properly by the HDMI chip.

There was one interesting insight related to vertical blank interrupts, where 
it would appear that the end-of-frame condition does not occur, with this 
failure then obstructing driver initialisation. I aim to look into that 
further.

Indeed, to approach this from another angle, I started to experiment with the 
HDMI peripheral in the L4 Runtime Environment, which has been helpful when 
investigating things like the RTC chip on the CI20 and its interaction with 
the power management system. I have previously demonstrated the LCD controller 
initialisation in this environment for other Ingenic devices, so I should have 
a chance of building on that success without too many added complications.

So far, I have managed to reproduce EDID retrieval using the HDMI peripheral's 
own I2C support, and I plan to reproduce the HDMI peripheral initialisation 
itself. However, it is perhaps more interesting to get the LCD controller 
working first and potentially delivering end-of-frame interrupts: this might 
help me understand whether this problem is a serious obstacle or not.

Paul

^ permalink raw reply	[flat|nested] 31+ messages in thread

* JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.)
  2020-05-22 19:16         ` Paul Boddie
@ 2020-05-25 23:03           ` Paul Boddie
  2020-05-26  4:48             ` H. Nikolaus Schaller
  2020-05-26 15:03             ` Paul Cercueil
  0 siblings, 2 replies; 31+ messages in thread
From: Paul Boddie @ 2020-05-25 23:03 UTC (permalink / raw)
  To: Paul Cercueil; +Cc: H . Nikolaus Schaller, 周琰杰, linux-mips

On Friday 22. May 2020 21.16.10 Paul Boddie wrote:
> On Friday 22. May 2020 14.26.15 Paul Cercueil wrote:
> > Hi Paul,
> > 
> > I think the ingenic-drm driver is fine, even though the old 3.8 kernel
> > worked differently, the IP is backwards-compatible so it should work no
> > problem. I think the problem is somewhere in the Synopsis HDMI code or
> > the glue code. Because the LCDC does seem to send data, which is not
> > encoded properly by the HDMI chip.
> 
> There was one interesting insight related to vertical blank interrupts,
> where it would appear that the end-of-frame condition does not occur, with
> this failure then obstructing driver initialisation. I aim to look into
> that further.

Some further experiments indicate that interrupt generation is indeed a 
problem...

[L4Re experimentation]

> So far, I have managed to reproduce EDID retrieval using the HDMI
> peripheral's own I2C support, and I plan to reproduce the HDMI peripheral
> initialisation itself. However, it is perhaps more interesting to get the
> LCD controller working first and potentially delivering end-of-frame
> interrupts: this might help me understand whether this problem is a serious
> obstacle or not.

First of all, I managed to get the HDMI connector hotplug detection working. 
This was a relatively simple matter of setting the appropriate flags, binding 
to an interrupt and then waiting for one to arrive. Consequently, booting 
without the connector inserted means that my program is halted until the 
interrupt arrives upon insertion; then, the EDID is read, which seems to work 
reliably.

However, I then found that the LCD controller could not be activated. The 
solution to this involves the TVE clock on the JZ4780 which appears to be 
necessary in addition to the LCD clock. Ingenic documentation being what it 
is, I suspect that the LCD clock in the block diagram is really the pixel 
clock(s), with the TVE clock not even appearing in the diagram. The 3.18 
kernel's device tree for the JZ4780 plus the CGU code provide the necessary 
hints, without any explanation, of course.

With the LCD controller now willing to retain values stored in its registers, 
I have been attempting to set up descriptors and do the usual general 
configuration exercise that works on the JZ4720 and JZ4730. However, I have 
never enabled interrupts on those devices, so I don't know what I need to do 
other than to set the appropriate control and command (descriptor) flags. 
Doing so doesn't manage to generate any interrupts, though.

The 3.18 kernel driver sets up the "new" 8-word descriptor and other new 
things. Initially, I ignored these things, but then I thought that they might 
actually be mandatory. Still, introducing practically the same details as seen 
in the 3.18 driver seems to have no effect. So, I imagine I am missing 
something pretty obvious: it took me a while to realise that I wasn't even 
enabling the LCD controller, after all.

One thing I would point out is that the operation of the JZ4780 is not exactly 
the same as earlier products. For example, various configuration register bits 
related to pixel depths are now read-only. Presumably, the idea is to set the 
pixel depth in the "new" descriptor fields instead, enabling some kind of 
mixing of different kinds of pixel data. I also wonder how well supported some 
of the older functions are in the newer hardware.

Anyway, I'm rather stuck with this at the moment. I don't know whether I 
should be reproducing the HDMI initialisation in my test environment under the 
assumption that the LCD controller isn't sending data without some kind of 
output path, or whether I should fake up some other output path (maybe a 
serial LCD) by setting GPIO alternate pin functions. But it turns out not to 
be entirely trivial to just have the LCD controller do its own thing and 
generate these interrupts all by itself.

But again, I may well be overlooking an obvious mistake of my own.

Paul

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.)
  2020-05-25 23:03           ` JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.) Paul Boddie
@ 2020-05-26  4:48             ` H. Nikolaus Schaller
  2020-05-26 15:03             ` Paul Cercueil
  1 sibling, 0 replies; 31+ messages in thread
From: H. Nikolaus Schaller @ 2020-05-26  4:48 UTC (permalink / raw)
  To: Paul Boddie; +Cc: Paul Cercueil, 周琰杰, linux-mips

Hi Paul,

> Am 26.05.2020 um 01:03 schrieb Paul Boddie <paul@boddie.org.uk>:
> 
> On Friday 22. May 2020 21.16.10 Paul Boddie wrote:
>> On Friday 22. May 2020 14.26.15 Paul Cercueil wrote:
>>> Hi Paul,
>>> 
>>> I think the ingenic-drm driver is fine, even though the old 3.8 kernel
>>> worked differently, the IP is backwards-compatible so it should work no
>>> problem. I think the problem is somewhere in the Synopsis HDMI code or
>>> the glue code. Because the LCDC does seem to send data, which is not
>>> encoded properly by the HDMI chip.
>> 
>> There was one interesting insight related to vertical blank interrupts,
>> where it would appear that the end-of-frame condition does not occur, with
>> this failure then obstructing driver initialisation. I aim to look into
>> that further.
> 
> Some further experiments indicate that interrupt generation is indeed a 
> problem...
> 
> [L4Re experimentation]
> 
>> So far, I have managed to reproduce EDID retrieval using the HDMI
>> peripheral's own I2C support, and I plan to reproduce the HDMI peripheral
>> initialisation itself. However, it is perhaps more interesting to get the
>> LCD controller working first and potentially delivering end-of-frame
>> interrupts: this might help me understand whether this problem is a serious
>> obstacle or not.
> 
> First of all, I managed to get the HDMI connector hotplug detection working. 
> This was a relatively simple matter of setting the appropriate flags, binding 
> to an interrupt and then waiting for one to arrive. Consequently, booting 
> without the connector inserted means that my program is halted until the 
> interrupt arrives upon insertion; then, the EDID is read, which seems to work 
> reliably.

Well, with Linux my hack to set dev->mode_config.poll_enabled = true;
in drm_probe_helper.c seems to make it works in all cases without halting.

And what I observed is that interrupts are generated and arriving. They are
just not properly delivered because dev->mode_config.poll_enabled = false
has some wrong side-effect.

So I think there must be a simpler solution (for Linux).

> 
> However, I then found that the LCD controller could not be activated.

In my tests the code to setup the LCDC was never called so I could never
debug anything there.

I still have to digest the mail "DRM interaction problems on Ingenic CI20 / jz4780 with dw-hdmi and ingenic-drm"
from Paul.

> The 
> solution to this involves the TVE clock on the JZ4780 which appears to be 
> necessary in addition to the LCD clock. Ingenic documentation being what it 
> is, I suspect that the LCD clock in the block diagram is really the pixel 
> clock(s), with the TVE clock not even appearing in the diagram. The 3.18 
> kernel's device tree for the JZ4780 plus the CGU code provide the necessary 
> hints, without any explanation, of course.
> 
> With the LCD controller now willing to retain values stored in its registers, 
> I have been attempting to set up descriptors and do the usual general 
> configuration exercise that works on the JZ4720 and JZ4730. However, I have 
> never enabled interrupts on those devices, so I don't know what I need to do 
> other than to set the appropriate control and command (descriptor) flags. 
> Doing so doesn't manage to generate any interrupts, though.
> 
> The 3.18 kernel driver sets up the "new" 8-word descriptor and other new 
> things. Initially, I ignored these things, but then I thought that they might 
> actually be mandatory. Still, introducing practically the same details as seen 
> in the 3.18 driver seems to have no effect. So, I imagine I am missing 
> something pretty obvious: it took me a while to realise that I wasn't even 
> enabling the LCD controller, after all.
> 
> One thing I would point out is that the operation of the JZ4780 is not exactly 
> the same as earlier products. For example, various configuration register bits 
> related to pixel depths are now read-only.

And what I observed is that there are additional registers compared to a
JZ4740 LCDC. My assumption would be that leaving them 0x00 would be ok.

> Presumably, the idea is to set the 
> pixel depth in the "new" descriptor fields instead, enabling some kind of 
> mixing of different kinds of pixel data. I also wonder how well supported some 
> of the older functions are in the newer hardware.
> 
> Anyway, I'm rather stuck with this at the moment. I don't know whether I 
> should be reproducing the HDMI initialisation in my test environment under the 
> assumption that the LCD controller isn't sending data without some kind of 
> output path, or whether I should fake up some other output path (maybe a 
> serial LCD) by setting GPIO alternate pin functions. But it turns out not to 
> be entirely trivial to just have the LCD controller do its own thing and 
> generate these interrupts all by itself.
> 
> But again, I may well be overlooking an obvious mistake of my own.
> 
> Paul

BR,
Nikolaus


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.)
  2020-05-25 23:03           ` JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.) Paul Boddie
  2020-05-26  4:48             ` H. Nikolaus Schaller
@ 2020-05-26 15:03             ` Paul Cercueil
  2020-05-26 22:44               ` Paul Boddie
  1 sibling, 1 reply; 31+ messages in thread
From: Paul Cercueil @ 2020-05-26 15:03 UTC (permalink / raw)
  To: Paul Boddie; +Cc: H . Nikolaus Schaller, 周琰杰, linux-mips

Hi Paul,

Le mar. 26 mai 2020 à 1:03, Paul Boddie <paul@boddie.org.uk> a écrit :
> On Friday 22. May 2020 21.16.10 Paul Boddie wrote:
>>  On Friday 22. May 2020 14.26.15 Paul Cercueil wrote:
>>  > Hi Paul,
>>  >
>>  > I think the ingenic-drm driver is fine, even though the old 3.8 
>> kernel
>>  > worked differently, the IP is backwards-compatible so it should 
>> work no
>>  > problem. I think the problem is somewhere in the Synopsis HDMI 
>> code or
>>  > the glue code. Because the LCDC does seem to send data, which is 
>> not
>>  > encoded properly by the HDMI chip.
>> 
>>  There was one interesting insight related to vertical blank 
>> interrupts,
>>  where it would appear that the end-of-frame condition does not 
>> occur, with
>>  this failure then obstructing driver initialisation. I aim to look 
>> into
>>  that further.
> 
> Some further experiments indicate that interrupt generation is indeed 
> a
> problem...
> 
> [L4Re experimentation]
> 
>>  So far, I have managed to reproduce EDID retrieval using the HDMI
>>  peripheral's own I2C support, and I plan to reproduce the HDMI 
>> peripheral
>>  initialisation itself. However, it is perhaps more interesting to 
>> get the
>>  LCD controller working first and potentially delivering end-of-frame
>>  interrupts: this might help me understand whether this problem is a 
>> serious
>>  obstacle or not.
> 
> First of all, I managed to get the HDMI connector hotplug detection 
> working.
> This was a relatively simple matter of setting the appropriate flags, 
> binding
> to an interrupt and then waiting for one to arrive. Consequently, 
> booting
> without the connector inserted means that my program is halted until 
> the
> interrupt arrives upon insertion; then, the EDID is read, which seems 
> to work
> reliably.
> 
> However, I then found that the LCD controller could not be activated. 
> The
> solution to this involves the TVE clock on the JZ4780 which appears 
> to be
> necessary in addition to the LCD clock. Ingenic documentation being 
> what it
> is, I suspect that the LCD clock in the block diagram is really the 
> pixel
> clock(s), with the TVE clock not even appearing in the diagram. The 
> 3.18
> kernel's device tree for the JZ4780 plus the CGU code provide the 
> necessary
> hints, without any explanation, of course.

"lcd0pixclk" and "tve" are for LCD0, "lcd1pixclk" and "lcd" are for 
LCD1.

> With the LCD controller now willing to retain values stored in its 
> registers,
> I have been attempting to set up descriptors and do the usual general
> configuration exercise that works on the JZ4720 and JZ4730. However, 
> I have
> never enabled interrupts on those devices, so I don't know what I 
> need to do
> other than to set the appropriate control and command (descriptor) 
> flags.
> Doing so doesn't manage to generate any interrupts, though.
> 
> The 3.18 kernel driver sets up the "new" 8-word descriptor and other 
> new
> things. Initially, I ignored these things, but then I thought that 
> they might
> actually be mandatory. Still, introducing practically the same 
> details as seen
> in the 3.18 driver seems to have no effect. So, I imagine I am missing
> something pretty obvious: it took me a while to realise that I wasn't 
> even
> enabling the LCD controller, after all.
> 
> One thing I would point out is that the operation of the JZ4780 is 
> not exactly
> the same as earlier products. For example, various configuration 
> register bits
> related to pixel depths are now read-only. Presumably, the idea is to 
> set the
> pixel depth in the "new" descriptor fields instead, enabling some 
> kind of
> mixing of different kinds of pixel data. I also wonder how well 
> supported some
> of the older functions are in the newer hardware.

OK, indeed the BPP and OSD config is read-only, and it's not a doc 
typo. How annoying.

I tried to configure the LCD controller for a 8-byte descriptor without 
much success. No IRQs here either.

-Paul

> Anyway, I'm rather stuck with this at the moment. I don't know 
> whether I
> should be reproducing the HDMI initialisation in my test environment 
> under the
> assumption that the LCD controller isn't sending data without some 
> kind of
> output path, or whether I should fake up some other output path 
> (maybe a
> serial LCD) by setting GPIO alternate pin functions. But it turns out 
> not to
> be entirely trivial to just have the LCD controller do its own thing 
> and
> generate these interrupts all by itself.
> 
> But again, I may well be overlooking an obvious mistake of my own.
> 
> Paul



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 4/6] dt-bindings: MIPS: Document Ingenic SoCs binding.
  2020-05-19 14:35 ` [PATCH v8 4/6] dt-bindings: MIPS: Document Ingenic SoCs binding 周琰杰 (Zhou Yanjie)
@ 2020-05-26 19:29   ` Rob Herring
  2020-05-27  5:59     ` Zhou Yanjie
  0 siblings, 1 reply; 31+ messages in thread
From: Rob Herring @ 2020-05-26 19:29 UTC (permalink / raw)
  To: 周琰杰 (Zhou Yanjie)
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, daniel.lezcano, keescook, paul, krzk,
	hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin

On Tue, May 19, 2020 at 10:35:21PM +0800, 周琰杰 (Zhou Yanjie) wrote:
> Document the available properties for the SoC root node and the
> CPU nodes of the devicetree for the Ingenic XBurst SoCs.
> 
> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
> Tested-by: Paul Boddie <paul@boddie.org.uk>
> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
> ---
> 
> Notes:
>     v1->v2:
>     Change the two Document from txt to yaml.
>     
>     v2->v3:
>     Fix formatting errors.
>     
>     v3->v4:
>     Fix bugs in the two yaml files.
>     
>     v4->v5:
>     No change.
>     
>     v5->v6:
>     Rewrite the two yaml files.
>     
>     v6->v7:
>     1.Update compatible strings in "ingenic,cpu.yaml".
>     2.Fix formatting errors, and enum for compatible strings.
>     3.Remove unnecessary "ingenic,soc.yaml".
>     
>     v7->v8:
>     No change.
> 
>  .../bindings/mips/ingenic/ingenic,cpu.yaml         | 57 ++++++++++++++++++++++
>  1 file changed, 57 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
> 
> diff --git a/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml b/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
> new file mode 100644
> index 00000000..afb0207
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
> @@ -0,0 +1,57 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mips/ingenic/ingenic,cpu.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Bindings for Ingenic XBurst family CPUs
> +
> +maintainers:
> +  - 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
> +
> +description:
> +  Ingenic XBurst family CPUs shall have the following properties.
> +
> +properties:
> +  compatible:
> +    oneOf:
> +
> +      - description: Ingenic XBurst®1 CPU Cores
> +        items:

This is a single compatible string, right? If so, drop items. 

> +          enum:
> +            - ingenic,xburst-mxu1.0
> +            - ingenic,xburst-fpu1.0-mxu1.1
> +            - ingenic,xburst-fpu2.0-mxu2.0
> +
> +      - description: Ingenic XBurst®2 CPU Cores
> +        items:
> +          enum:
> +            - ingenic,xburst2-fpu2.1-mxu2.1-smt

Just: const: ingenic,xburst2-fpu2.1-mxu2.1-smt

Continuing to append CPU features isn't going to scale well. Does 
'xburst2' imply certain features? If so, not really any need to have 
them be explicit.

> +
> +  reg:
> +    maxItems: 1
> +
> +required:
> +  - device_type
> +  - compatible
> +  - reg
> +
> +examples:
> +  - |
> +    cpus {
> +    	#address-cells = <1>;
> +    	#size-cells = <0>;
> +
> +    	cpu0: cpu@0 {
> +    		device_type = "cpu";
> +    		compatible = "ingenic,xburst-fpu1.0-mxu1.1";
> +    		reg = <0>;
> +    	};
> +
> +    	cpu1: cpu@1 {
> +    		device_type = "cpu";
> +    		compatible = "ingenic,xburst-fpu1.0-mxu1.1";
> +    		reg = <1>;
> +    	};
> +    };
> +...
> -- 
> 2.7.4
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.)
  2020-05-26 15:03             ` Paul Cercueil
@ 2020-05-26 22:44               ` Paul Boddie
  2020-05-26 23:07                 ` Paul Cercueil
  0 siblings, 1 reply; 31+ messages in thread
From: Paul Boddie @ 2020-05-26 22:44 UTC (permalink / raw)
  To: Paul Cercueil; +Cc: H . Nikolaus Schaller, 周琰杰, linux-mips

Paul,

Thanks for the reply!

On Tuesday 26. May 2020 17.03.04 Paul Cercueil wrote:
> 
> "lcd0pixclk" and "tve" are for LCD0, "lcd1pixclk" and "lcd" are for
> LCD1.

The 3.0.8 kernel actually uses LCD0 for what the documentation and 3.18 kernel 
call TVE, and it uses LCD1 for what the others call LCD. That earlier kernel 
indicates that LCD1 is the parent clock of LCD0.

I actually found that you can enable LCD0 and not LCD1 and the LCD controller 
(LCDC0) still operates to an extent, but without LCD1 enabled I didn't see a 
DMA command value in the appropriate register, discussed below.

[...]

> OK, indeed the BPP and OSD config is read-only, and it's not a doc
> typo. How annoying.
> 
> I tried to configure the LCD controller for a 8-byte descriptor without
> much success. No IRQs here either.

I had a look at the interrupt controller registers to see whether I was 
missing anything obvious, but the mask was correctly configured to unmask LCD 
interrupts (bit 31 of ICMR0). I did wonder whether the PDMA interrupts might 
need unmasking, just in case there is some interaction between the peripherals 
and that part of the hardware, but unmasking LCD interrupts there (bit 31 of 
DMR0) didn't make any difference.

One observation I can make is that the length or size field of the LCD command 
register (LCDCMD0) does get initialised to the appropriate value as set in a 
descriptor. Since I don't set this register explicitly myself (unlike, I 
think, the current Ingenic DRM driver in the Linux kernel), the value must 
have been set up appropriately by a DMA transfer, as configured using the 
descriptor address register (LCDDA0). However, the command flags I also set in 
the descriptor are not reflected in the register. So, 0x44140000 becomes 
0x00140000.

I thought I should check the interrupt ID register (LCDIID) to see what it 
reveals. Despite setting a value in the appropriate descriptor field, the 
register contains only zero.

I think I must probably tackle the job of initialising the HDMI controller to 
see if that makes a difference. If the interrupts are not working but are also 
not necessary, then maybe I get a visual indication of success.

Thanks for the feedback!

Paul

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.)
  2020-05-26 22:44               ` Paul Boddie
@ 2020-05-26 23:07                 ` Paul Cercueil
  2020-06-01 20:06                   ` Paul Boddie
  2020-06-23 21:28                   ` Paul Boddie
  0 siblings, 2 replies; 31+ messages in thread
From: Paul Cercueil @ 2020-05-26 23:07 UTC (permalink / raw)
  To: Paul Boddie; +Cc: H . Nikolaus Schaller, 周琰杰, linux-mips

Hi,

Le mer. 27 mai 2020 à 0:44, Paul Boddie <paul@boddie.org.uk> a écrit :
> Paul,
> 
> Thanks for the reply!
> 
> On Tuesday 26. May 2020 17.03.04 Paul Cercueil wrote:
>> 
>>  "lcd0pixclk" and "tve" are for LCD0, "lcd1pixclk" and "lcd" are for
>>  LCD1.
> 
> The 3.0.8 kernel actually uses LCD0 for what the documentation and 
> 3.18 kernel
> call TVE, and it uses LCD1 for what the others call LCD. That earlier 
> kernel
> indicates that LCD1 is the parent clock of LCD0.
> 
> I actually found that you can enable LCD0 and not LCD1 and the LCD 
> controller
> (LCDC0) still operates to an extent, but without LCD1 enabled I 
> didn't see a
> DMA command value in the appropriate register, discussed below.

Yes, I was preparing a patch for the clock driver, then I noticed that 
too.

> [...]
> 
>>  OK, indeed the BPP and OSD config is read-only, and it's not a doc
>>  typo. How annoying.
>> 
>>  I tried to configure the LCD controller for a 8-byte descriptor 
>> without
>>  much success. No IRQs here either.
> 
> I had a look at the interrupt controller registers to see whether I 
> was
> missing anything obvious, but the mask was correctly configured to 
> unmask LCD
> interrupts (bit 31 of ICMR0). I did wonder whether the PDMA 
> interrupts might
> need unmasking, just in case there is some interaction between the 
> peripherals
> and that part of the hardware, but unmasking LCD interrupts there 
> (bit 31 of
> DMR0) didn't make any difference.
> 
> One observation I can make is that the length or size field of the 
> LCD command
> register (LCDCMD0) does get initialised to the appropriate value as 
> set in a
> descriptor. Since I don't set this register explicitly myself 
> (unlike, I
> think, the current Ingenic DRM driver in the Linux kernel), the value 
> must
> have been set up appropriately by a DMA transfer, as configured using 
> the
> descriptor address register (LCDDA0). However, the command flags I 
> also set in
> the descriptor are not reflected in the register. So, 0x44140000 
> becomes
> 0x00140000.
> 
> I thought I should check the interrupt ID register (LCDIID) to see 
> what it
> reveals. Despite setting a value in the appropriate descriptor field, 
> the
> register contains only zero.
> 
> I think I must probably tackle the job of initialising the HDMI 
> controller to
> see if that makes a difference. If the interrupts are not working but 
> are also
> not necessary, then maybe I get a visual indication of success.

Don't focus too much on interrupts right now. You don't get interrupts 
because the data is not flowing. Which in turns is caused either by the 
LCDC not being correctly configured, or the HDMI not sending 
hsync/vsync signals.

For now, what seems to be the problem is that the DMA descriptors 
aren't loaded properly. Whatever I do, the debug registers 
(LCDSAx/LCDIDx/etc) are still zero here.

-Paul



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 4/6] dt-bindings: MIPS: Document Ingenic SoCs binding.
  2020-05-26 19:29   ` Rob Herring
@ 2020-05-27  5:59     ` Zhou Yanjie
  0 siblings, 0 replies; 31+ messages in thread
From: Zhou Yanjie @ 2020-05-27  5:59 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, daniel.lezcano, keescook, paul, krzk,
	hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
	zhenwenjin, aric.pzqi


在 2020/5/27 上午3:29, Rob Herring 写道:
> On Tue, May 19, 2020 at 10:35:21PM +0800, 周琰杰 (Zhou Yanjie) wrote:
>> Document the available properties for the SoC root node and the
>> CPU nodes of the devicetree for the Ingenic XBurst SoCs.
>>
>> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
>> Tested-by: Paul Boddie <paul@boddie.org.uk>
>> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
>> ---
>>
>> Notes:
>>      v1->v2:
>>      Change the two Document from txt to yaml.
>>      
>>      v2->v3:
>>      Fix formatting errors.
>>      
>>      v3->v4:
>>      Fix bugs in the two yaml files.
>>      
>>      v4->v5:
>>      No change.
>>      
>>      v5->v6:
>>      Rewrite the two yaml files.
>>      
>>      v6->v7:
>>      1.Update compatible strings in "ingenic,cpu.yaml".
>>      2.Fix formatting errors, and enum for compatible strings.
>>      3.Remove unnecessary "ingenic,soc.yaml".
>>      
>>      v7->v8:
>>      No change.
>>
>>   .../bindings/mips/ingenic/ingenic,cpu.yaml         | 57 ++++++++++++++++++++++
>>   1 file changed, 57 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml b/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
>> new file mode 100644
>> index 00000000..afb0207
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
>> @@ -0,0 +1,57 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/mips/ingenic/ingenic,cpu.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Bindings for Ingenic XBurst family CPUs
>> +
>> +maintainers:
>> +  - 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
>> +
>> +description:
>> +  Ingenic XBurst family CPUs shall have the following properties.
>> +
>> +properties:
>> +  compatible:
>> +    oneOf:
>> +
>> +      - description: Ingenic XBurst®1 CPU Cores
>> +        items:
> This is a single compatible string, right? If so, drop items.


Sure, I'll drop it. And because the SMP driver has some other work that 
can't be completed in a short time, so I will send this patch separately.


>> +          enum:
>> +            - ingenic,xburst-mxu1.0
>> +            - ingenic,xburst-fpu1.0-mxu1.1
>> +            - ingenic,xburst-fpu2.0-mxu2.0
>> +
>> +      - description: Ingenic XBurst®2 CPU Cores
>> +        items:
>> +          enum:
>> +            - ingenic,xburst2-fpu2.1-mxu2.1-smt
> Just: const: ingenic,xburst2-fpu2.1-mxu2.1-smt
>
> Continuing to append CPU features isn't going to scale well. Does
> 'xburst2' imply certain features? If so, not really any need to have
> them be explicit.


XBurst (XBurst1) is the first generation CPU core of Ingenic, its basic 
property is single-issue in-order execution, XBurst2 is the second 
generation CPU core of Ingenic, its basic property is daul-issue limited 
out-of-order execution, and both CPU cores can cooperate with some 
extended propeties,  such as different versions of FPU, different 
versions of MXU instruction set, with or without simultaneous 
multi-threading.

Currently there is only one processor entity based on XBurst2 is 
produced, so there is  only one string for now.


Thanks and best regards!


>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +required:
>> +  - device_type
>> +  - compatible
>> +  - reg
>> +
>> +examples:
>> +  - |
>> +    cpus {
>> +    	#address-cells = <1>;
>> +    	#size-cells = <0>;
>> +
>> +    	cpu0: cpu@0 {
>> +    		device_type = "cpu";
>> +    		compatible = "ingenic,xburst-fpu1.0-mxu1.1";
>> +    		reg = <0>;
>> +    	};
>> +
>> +    	cpu1: cpu@1 {
>> +    		device_type = "cpu";
>> +    		compatible = "ingenic,xburst-fpu1.0-mxu1.1";
>> +    		reg = <1>;
>> +    	};
>> +    };
>> +...
>> -- 
>> 2.7.4
>>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.)
  2020-05-26 23:07                 ` Paul Cercueil
@ 2020-06-01 20:06                   ` Paul Boddie
  2020-06-23 21:28                   ` Paul Boddie
  1 sibling, 0 replies; 31+ messages in thread
From: Paul Boddie @ 2020-06-01 20:06 UTC (permalink / raw)
  To: Paul Cercueil; +Cc: H . Nikolaus Schaller, 周琰杰, linux-mips

On Wednesday 27. May 2020 01.07.41 Paul Cercueil wrote:
> 
> Don't focus too much on interrupts right now. You don't get interrupts
> because the data is not flowing. Which in turns is caused either by the
> LCDC not being correctly configured, or the HDMI not sending
> hsync/vsync signals.
> 
> For now, what seems to be the problem is that the DMA descriptors
> aren't loaded properly. Whatever I do, the debug registers
> (LCDSAx/LCDIDx/etc) are still zero here.

I checked the LCDSA0 (source address) and LCDFID0 (frame identifier) 
registers, and they get populated with what I put in the descriptor, so I am 
inclined to think that some kind of initialisation does happen. However, as 
previously noted, the LCDIID (interrupt identifier) remains zero.

What I have subsequently done is to introduce the HDMI driver functionality 
from Linux into my test environment (L4Re) to investigate the configuration 
process. Eventually, I managed to get the HDMI signal enabled, meaning that I 
can switch my monitor to the DVI input and be told what kind of picture it 
thinks it is getting (1280x1024 @ 62Hz, and although I asked for 60Hz, the 
111MHz pixel clock will probably generate something closer to 62Hz).

Where this gets interesting/infuriating is that the signal persists but there 
is no actual picture (just a black screen, but illuminated), but accompanying 
this is a "FIFO 0 under run" condition (IFU0 in LCDSTATE). Looking at the 
manual, there is no other mention of this "FIFO 0", but I did wonder whether 
it had anything to do with the OSD functionality because it seems to be the 
case that the OSD is always enabled (OSDEN is set in LCDOSDC) and cannot be 
unset just by clearing the OSDEN bit.

I thought that the "new" 8-word descriptor arrangement might be important in 
this regard. Although the manual doesn't make this explicit, it seems to be 
the case that the extra 4 words are configuring the OSD foreground planes. So, 
I enabled the populated extra words in a similar way to what the 3.18 driver 
does. But even with various other JZ4780-specific registers set up (RGB 
control, priority threshold), what actually happens is that the monitor fails 
to acknowledge a signal and switches back to its default VGA input.

I have tried a few things, such as enabling the IPU clock in the CPM unit 
(which is probably unnecessary since it is enabled by default) and enabling 
the IPU clock in the LCD controller (IPU_CLKEN in LCDOSDCTRL), but neither 
changes the situation.

I also noted a recent patch on the dri-devel list adding OSD support...

"[PATCH 09/12] gpu/drm: Ingenic: Add support for OSD mode"

What is interesting about that patch is that it attempts to enable the OSD 
foreground planes by setting F0EN and F1EN on LCDOSDC. However, these are 
marked as read-only in the JZ4780 and cannot actually be set.

So, I am currently looking for some more ideas about how to get this working. 
It is entirely possible that I have taken one too many shortcuts with the HDMI 
initialisation: I have only implemented "DVI mode" and have sought only to 
implement what is necessary for that and for RGB data. It is also likely that 
I have missed something from the LCD initialisation, but whatever it is 
escapes me at present.

Any suggestions would be appreciated at this point!

Thanks in advance,

Paul

P.S. I can easily imagine that the obstacle to getting things working in a 
modern Linux kernel is just the data format/encoding. The 3.18 driver supports 
only RGB data, which is presumably converted by the HDMI peripheral to 
whatever the preferred output actually is.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.)
  2020-05-26 23:07                 ` Paul Cercueil
  2020-06-01 20:06                   ` Paul Boddie
@ 2020-06-23 21:28                   ` Paul Boddie
  1 sibling, 0 replies; 31+ messages in thread
From: Paul Boddie @ 2020-06-23 21:28 UTC (permalink / raw)
  To: Paul Cercueil; +Cc: H . Nikolaus Schaller, 周琰杰, linux-mips

[-- Attachment #1: Type: text/plain, Size: 2353 bytes --]

On Wednesday, 27 May 2020 01:07:41 CEST Paul Cercueil wrote:
> 
> Don't focus too much on interrupts right now. You don't get interrupts
> because the data is not flowing. Which in turns is caused either by the
> LCDC not being correctly configured, or the HDMI not sending
> hsync/vsync signals.
> 
> For now, what seems to be the problem is that the DMA descriptors
> aren't loaded properly. Whatever I do, the debug registers
> (LCDSAx/LCDIDx/etc) are still zero here.

So, as I may have mentioned already, I got the LCD controller and HDMI 
peripheral working in a simple L4Re-based configuration, verifying a working 
configuration of the LCD controller. Since then, I have been trying to build 
working Linux drivers with this knowledge.

At the moment, I can indeed get the LCDC to start and to produce end-of-frame 
interrupts which are counted by the DRM framework. However, my current problem 
to solve is how to get anything other than "Input not supported" from my 
monitor.

I have pursuing the laborious process of verifying the HDMI register settings, 
which is rather perverse given that my L4Re code effectively takes the 
simplified logic of the HDMI driver (plus my JZ4780 specialisations) and works 
fine. So, I am trying to reproduce the configuration of something that should 
already work from my clone of that same thing.

One thing that was in dispute before was the matter of the input bus formats. 
It is definitely the case that the Ingenic driver will never see any format 
information based on what the Synopsys driver does. I don't know whether the 
DRM framework is supposed to magically propagate the information, but as of 
right now it just doesn't.

Currently, I hack the display information's input bus format in the bridge 
driver's atomic_check function along with bus flags that I also think are 
needed. Setting the format enables the Ingenic DRM driver to start up. For 
some reason, the signal over the cable cannot be handled by the monitor.

Anyway, I have included the Ingenic DRM driver patch along with the Synopsys 
driver hack and a patch adding the specialised driver, all of which apply 
against v5.8-rc1 of torvalds/linux, so that they might be inspected. I hope 
that some additional insight can move things forward. Debugging this in the 
Linux environment is particularly tiresome.

Regards,

Paul

[-- Attachment #2: ingenic-drm.c-jz4780.diff --]
[-- Type: text/x-patch, Size: 14194 bytes --]

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c
index 55b49a31729b..41974ab13e02 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -3,6 +3,7 @@
 // Ingenic JZ47xx KMS driver
 //
 // Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
+// Copyright (C) 2020 Paul Boddie <paul@boddie.org.uk>
 
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
@@ -53,8 +54,17 @@
 #define JZ_REG_LCD_SA1				0x54
 #define JZ_REG_LCD_FID1				0x58
 #define JZ_REG_LCD_CMD1				0x5C
+#define JZ_REG_LCD_RGBC				0x90
+#define JZ_REG_LCD_OSDC				0x100
+#define JZ_REG_LCD_OSDCTRL			0x104
+#define JZ_REG_LCD_OSDS				0x108
+#define JZ_REG_LCD_PCFG				0x2c0
 
 #define JZ_LCD_CFG_SLCD				BIT(31)
+#define JZ_LCD_CFG_TV_PAL_HALFLINE_ENABLE	BIT(30)
+#define JZ_LCD_CFG_DESCRIPTOR_8			BIT(28)
+#define JZ_LCD_CFG_TV_ENABLE			BIT(26)
+#define JZ_LCD_CFG_RECOVER_FIFO_UNDERRUN	BIT(25)
 #define JZ_LCD_CFG_PS_DISABLE			BIT(23)
 #define JZ_LCD_CFG_CLS_DISABLE			BIT(22)
 #define JZ_LCD_CFG_SPL_DISABLE			BIT(21)
@@ -72,6 +82,7 @@
 #define JZ_LCD_CFG_DE_ACTIVE_LOW		BIT(9)
 #define JZ_LCD_CFG_VSYNC_ACTIVE_LOW		BIT(8)
 #define JZ_LCD_CFG_18_BIT			BIT(7)
+#define JZ_LCD_CFG_24_BIT			BIT(6)
 #define JZ_LCD_CFG_PDW				(BIT(5) | BIT(4))
 
 #define JZ_LCD_CFG_MODE_GENERIC_16BIT		0
@@ -111,6 +122,9 @@
 #define JZ_LCD_CTRL_BURST_4			(0x0 << 28)
 #define JZ_LCD_CTRL_BURST_8			(0x1 << 28)
 #define JZ_LCD_CTRL_BURST_16			(0x2 << 28)
+#define JZ_LCD_CTRL_BURST_32			(0x3 << 28)
+#define JZ_LCD_CTRL_BURST_64			(0x4 << 28)
+#define JZ_LCD_CTRL_BURST_MASK			(0x7 << 28)
 #define JZ_LCD_CTRL_RGB555			BIT(27)
 #define JZ_LCD_CTRL_OFUP			BIT(26)
 #define JZ_LCD_CTRL_FRC_GRAYSCALE_16		(0x0 << 24)
@@ -139,6 +153,7 @@
 #define JZ_LCD_CMD_SOF_IRQ			BIT(31)
 #define JZ_LCD_CMD_EOF_IRQ			BIT(30)
 #define JZ_LCD_CMD_ENABLE_PAL			BIT(28)
+#define JZ_LCD_CMD_FRM_ENABLE			BIT(26)
 
 #define JZ_LCD_SYNC_MASK			0x3ff
 
@@ -146,6 +161,52 @@
 #define JZ_LCD_STATE_SOF_IRQ			BIT(4)
 #define JZ_LCD_STATE_DISABLED			BIT(0)
 
+#define JZ_LCD_DESSIZE_ALPHA_OFFSET		24
+#define JZ_LCD_DESSIZE_HEIGHT_OFFSET		12
+#define JZ_LCD_DESSIZE_WIDTH_OFFSET		0
+#define JZ_LCD_DESSIZE_HEIGHT_MASK		0xfff
+#define JZ_LCD_DESSIZE_WIDTH_MASK		0xfff
+
+#define JZ_LCD_CPOS_BPP_15_16			(4 << 27)
+#define JZ_LCD_CPOS_BPP_18_24			(5 << 27)
+#define JZ_LCD_CPOS_BPP_30			(7 << 27)
+#define JZ_LCD_CPOS_PREMULTIPLY_LCD		BIT(26)
+#define JZ_LCD_CPOS_COEFFICIENT_OFFSET		24
+
+#define JZ_LCD_RGBC_RGB_PADDING			BIT(15)
+#define JZ_LCD_RGBC_RGB_PADDING_FIRST		BIT(14)
+#define JZ_LCD_RGBC_422				BIT(8)
+#define JZ_LCD_RGBC_RGB_FORMAT_ENABLE		BIT(7)
+#define JZ_LCD_RGBC_ODD_LINE_MASK		(0x7 << 4)
+#define JZ_LCD_RGBC_ODD_LINE_RGB		(0 << 4)
+#define JZ_LCD_RGBC_ODD_LINE_RBG		(1 << 4)
+#define JZ_LCD_RGBC_ODD_LINE_GRB		(2 << 4)
+#define JZ_LCD_RGBC_ODD_LINE_GBR		(3 << 4)
+#define JZ_LCD_RGBC_ODD_LINE_BRG		(4 << 4)
+#define JZ_LCD_RGBC_ODD_LINE_BGR		(5 << 4)
+#define JZ_LCD_RGBC_EVEN_LINE_MASK		(0x7 << 0)
+#define JZ_LCD_RGBC_EVEN_LINE_RGB		0
+#define JZ_LCD_RGBC_EVEN_LINE_RBG		1
+#define JZ_LCD_RGBC_EVEN_LINE_GRB		2
+#define JZ_LCD_RGBC_EVEN_LINE_GBR		3
+#define JZ_LCD_RGBC_EVEN_LINE_BRG		4
+#define JZ_LCD_RGBC_EVEN_LINE_BGR		5
+
+#define JZ_REG_LCD_OSDC_ALPHA_ENABLE		BIT(2)
+#define JZ_REG_LCD_OSDC_ENABLE			BIT(0)
+
+#define JZ_LCD_PCFG_PRI_MODE			BIT(31)
+#define JZ_LCD_PCFG_HP_BST_4			(0 << 28)
+#define JZ_LCD_PCFG_HP_BST_8			(1 << 28)
+#define JZ_LCD_PCFG_HP_BST_16			(2 << 28)
+#define JZ_LCD_PCFG_HP_BST_32			(3 << 28)
+#define JZ_LCD_PCFG_HP_BST_64			(4 << 28)
+#define JZ_LCD_PCFG_HP_BST_16_CONT		(5 << 28)
+#define JZ_LCD_PCFG_HP_BST_DISABLE		(7 << 28)
+#define JZ_LCD_PCFG_THRESHOLD2_OFFSET		18
+#define JZ_LCD_PCFG_THRESHOLD1_OFFSET		9
+#define JZ_LCD_PCFG_THRESHOLD0_OFFSET		0
+
 struct ingenic_dma_hwdesc {
 	u32 next;
 	u32 addr;
@@ -153,8 +214,25 @@ struct ingenic_dma_hwdesc {
 	u32 cmd;
 } __packed;
 
+struct ingenic_dma_hwdesc_extended_fields {
+	u32 offsize;
+	u32 pagewidth;
+	u32 cpos;
+	u32 dessize;
+} __packed;
+
+struct ingenic_dma_hwdesc_extended {
+	struct ingenic_dma_hwdesc base;
+	struct ingenic_dma_hwdesc_extended_fields extra;
+} __packed;
+
 struct jz_soc_info {
 	bool needs_dev_clk;
+	bool has_pcfg;
+	bool has_recover;
+	bool has_rgbc;
+	u32 dma_hwdesc_size;
+	int num_descriptors;
 	unsigned int max_width, max_height;
 };
 
@@ -169,7 +247,7 @@ struct ingenic_drm {
 	struct clk *lcd_clk, *pix_clk;
 	const struct jz_soc_info *soc_info;
 
-	struct ingenic_dma_hwdesc *dma_hwdesc;
+	struct ingenic_dma_hwdesc *dma_hwdesc[2];
 	dma_addr_t dma_hwdesc_phys;
 
 	bool panel_is_sharp;
@@ -179,6 +257,7 @@ static const u32 ingenic_drm_primary_formats[] = {
 	DRM_FORMAT_XRGB1555,
 	DRM_FORMAT_RGB565,
 	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ABGR8888,
 };
 
 static bool ingenic_drm_writeable_reg(struct device *dev, unsigned int reg)
@@ -202,7 +281,7 @@ static const struct regmap_config ingenic_drm_regmap_config = {
 	.val_bits = 32,
 	.reg_stride = 4,
 
-	.max_register = JZ_REG_LCD_CMD1,
+	.max_register = JZ_REG_LCD_PCFG,
 	.writeable_reg = ingenic_drm_writeable_reg,
 };
 
@@ -304,6 +383,9 @@ static void ingenic_drm_crtc_update_ctrl(struct ingenic_drm *priv,
 {
 	unsigned int ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16;
 
+	/* NOTE: This does not have the intended effect on JZ4780 since the
+		 pixel depth of each frame is set in the descriptors. */
+
 	switch (finfo->format) {
 	case DRM_FORMAT_XRGB1555:
 		ctrl |= JZ_LCD_CTRL_RGB555;
@@ -319,6 +401,30 @@ static void ingenic_drm_crtc_update_ctrl(struct ingenic_drm *priv,
 	regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
 			   JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16 |
 			   JZ_LCD_CTRL_BPP_MASK, ctrl);
+
+	/* "Magic values" from the 3.18 kernel for the priority thresholds. */
+	if (priv->soc_info->has_pcfg)
+		regmap_write(priv->map, JZ_REG_LCD_PCFG,
+			     JZ_LCD_PCFG_PRI_MODE |
+			     JZ_LCD_PCFG_HP_BST_16 |
+			     (511 << JZ_LCD_PCFG_THRESHOLD2_OFFSET) |
+			     (400 << JZ_LCD_PCFG_THRESHOLD1_OFFSET) |
+			     (256 << JZ_LCD_PCFG_THRESHOLD0_OFFSET));
+
+	/* RGB output control may be superfluous. */
+	if (priv->soc_info->has_rgbc)
+		regmap_write(priv->map, JZ_REG_LCD_RGBC,
+			     JZ_LCD_RGBC_RGB_FORMAT_ENABLE |
+			     JZ_LCD_RGBC_ODD_LINE_RGB |
+			     JZ_LCD_RGBC_EVEN_LINE_RGB);
+
+	/* OSD settings for extended descriptor definitions. */
+	if (priv->soc_info->dma_hwdesc_size == sizeof(struct ingenic_dma_hwdesc_extended))
+	{
+		regmap_write(priv->map, JZ_REG_LCD_OSDC,
+			     JZ_REG_LCD_OSDC_ALPHA_ENABLE |
+			     JZ_REG_LCD_OSDC_ENABLE);
+	}
 }
 
 static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
@@ -350,6 +456,7 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc,
 	struct drm_pending_vblank_event *event = state->event;
 	struct drm_framebuffer *drm_fb = crtc->primary->state->fb;
 	const struct drm_format_info *finfo;
+	int num;
 
 	if (drm_atomic_crtc_needs_modeset(state)) {
 		finfo = drm_format_info(drm_fb->format->format);
@@ -359,7 +466,10 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc,
 
 		clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000);
 
-		regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc->next);
+		/* Initialise up to two descriptor address registers. */
+		for (num = 0; num < priv->soc_info->num_descriptors; num++)
+			regmap_write(priv->map, !num ? JZ_REG_LCD_DA0 : JZ_REG_LCD_DA1,
+				     priv->dma_hwdesc[num]->next);
 	}
 
 	if (event) {
@@ -374,6 +484,48 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc,
 	}
 }
 
+static void ingenic_drm_descriptor_init(struct ingenic_drm *priv, int num,
+					unsigned int width,
+					unsigned int height,
+					unsigned int cpp,
+					u32 addr)
+{
+	struct ingenic_dma_hwdesc *hwdesc = priv->dma_hwdesc[num];
+	struct ingenic_dma_hwdesc_extended *hwdesc_ext;
+
+	/* Chain the descriptor to itself. */
+	hwdesc->next = priv->dma_hwdesc_phys +
+		       num * priv->soc_info->dma_hwdesc_size;
+
+	hwdesc->id = 0xfeed0000 | num;
+	hwdesc->addr = addr;
+	hwdesc->cmd = width * height * cpp / 4;
+	hwdesc->cmd |= JZ_LCD_CMD_EOF_IRQ;
+
+	/* CI20 initialisation. */
+	if (priv->soc_info->dma_hwdesc_size == sizeof(struct ingenic_dma_hwdesc_extended))
+	{
+		/* Enable only the first descriptor's frame. */
+		if (!num)
+			hwdesc->cmd |= JZ_LCD_CMD_FRM_ENABLE;
+
+		hwdesc_ext = (struct ingenic_dma_hwdesc_extended *) hwdesc;
+		hwdesc_ext->extra.offsize = 0;
+		hwdesc_ext->extra.pagewidth = 0;
+
+		hwdesc_ext->extra.cpos = JZ_LCD_CPOS_BPP_18_24 |
+					 JZ_LCD_CPOS_PREMULTIPLY_LCD |
+					 (3 << JZ_LCD_CPOS_COEFFICIENT_OFFSET);
+
+		hwdesc_ext->extra.dessize =
+			(0xff << JZ_LCD_DESSIZE_ALPHA_OFFSET) |
+			(((height - 1) & JZ_LCD_DESSIZE_HEIGHT_MASK) <<
+					 JZ_LCD_DESSIZE_HEIGHT_OFFSET) |
+			(((width - 1) & JZ_LCD_DESSIZE_WIDTH_MASK) <<
+					JZ_LCD_DESSIZE_WIDTH_OFFSET);
+	}
+}
+
 static void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
 					    struct drm_plane_state *oldstate)
 {
@@ -381,6 +533,7 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
 	struct drm_plane_state *state = plane->state;
 	unsigned int width, height, cpp;
 	dma_addr_t addr;
+	int num;
 
 	if (state && state->fb) {
 		addr = drm_fb_cma_get_gem_addr(state->fb, state, 0);
@@ -388,9 +541,8 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
 		height = state->src_h >> 16;
 		cpp = state->fb->format->cpp[plane->index];
 
-		priv->dma_hwdesc->addr = addr;
-		priv->dma_hwdesc->cmd = width * height * cpp / 4;
-		priv->dma_hwdesc->cmd |= JZ_LCD_CMD_EOF_IRQ;
+		for (num = 0; num < priv->soc_info->num_descriptors; num++)
+			ingenic_drm_descriptor_init(priv, num, width, height, cpp, addr);
 	}
 }
 
@@ -413,6 +565,13 @@ static void ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,
 		    | JZ_LCD_CFG_SPL_DISABLE | JZ_LCD_CFG_REV_DISABLE;
 	}
 
+	if (priv->soc_info->has_recover)
+		cfg |= JZ_LCD_CFG_RECOVER_FIFO_UNDERRUN;
+
+	/* CI20: set use of the 8-word descriptor and OSD foreground usage. */
+	if (priv->soc_info->dma_hwdesc_size == sizeof(struct ingenic_dma_hwdesc_extended))
+		cfg |= JZ_LCD_CFG_DESCRIPTOR_8;
+
 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
 		cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
 	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
@@ -584,12 +743,33 @@ static const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = {
 	.atomic_commit		= drm_atomic_helper_commit,
 };
 
-static void ingenic_drm_free_dma_hwdesc(void *d)
+static int ingenic_drm_allocate_descriptors(struct ingenic_drm *priv)
+{
+	int num;
+
+	priv->dma_hwdesc[0] = dma_alloc_coherent(priv->dev,
+						 priv->soc_info->num_descriptors
+						 * priv->soc_info->dma_hwdesc_size,
+						 &priv->dma_hwdesc_phys,
+						 GFP_KERNEL);
+	if (!priv->dma_hwdesc[0])
+		return -ENOMEM;
+
+	for (num = 1; num < priv->soc_info->num_descriptors; num++)
+		priv->dma_hwdesc[num] = (struct ingenic_dma_hwdesc *)
+					((u32) priv->dma_hwdesc[0] +
+					num * priv->soc_info->dma_hwdesc_size);
+
+	return 0;
+}
+
+static void ingenic_drm_free_descriptors(void *d)
 {
 	struct ingenic_drm *priv = d;
 
-	dma_free_coherent(priv->dev, sizeof(*priv->dma_hwdesc),
-			  priv->dma_hwdesc, priv->dma_hwdesc_phys);
+	dma_free_coherent(priv->dev, priv->soc_info->num_descriptors
+				     * priv->soc_info->dma_hwdesc_size,
+			  priv->dma_hwdesc[0], priv->dma_hwdesc_phys);
 }
 
 static int ingenic_drm_probe(struct platform_device *pdev)
@@ -674,19 +854,13 @@ static int ingenic_drm_probe(struct platform_device *pdev)
 		bridge = devm_drm_panel_bridge_add_typed(dev, panel,
 							 DRM_MODE_CONNECTOR_DPI);
 
-	priv->dma_hwdesc = dma_alloc_coherent(dev, sizeof(*priv->dma_hwdesc),
-					      &priv->dma_hwdesc_phys,
-					      GFP_KERNEL);
-	if (!priv->dma_hwdesc)
+	if (ingenic_drm_allocate_descriptors(priv))
 		return -ENOMEM;
 
-	ret = devm_add_action_or_reset(dev, ingenic_drm_free_dma_hwdesc, priv);
+	ret = devm_add_action_or_reset(dev, ingenic_drm_free_descriptors, priv);
 	if (ret)
 		return ret;
 
-	priv->dma_hwdesc->next = priv->dma_hwdesc_phys;
-	priv->dma_hwdesc->id = 0xdeafbead;
-
 	drm_plane_helper_add(&priv->primary, &ingenic_drm_plane_helper_funcs);
 
 	ret = drm_universal_plane_init(drm, &priv->primary,
@@ -802,26 +976,53 @@ static int ingenic_drm_remove(struct platform_device *pdev)
 
 static const struct jz_soc_info jz4740_soc_info = {
 	.needs_dev_clk = true,
+	.has_pcfg = false,
+	.has_recover = false,
+	.has_rgbc = false,
+	.dma_hwdesc_size = sizeof(struct ingenic_dma_hwdesc),
+	.num_descriptors = 1,
 	.max_width = 800,
 	.max_height = 600,
 };
 
 static const struct jz_soc_info jz4725b_soc_info = {
 	.needs_dev_clk = false,
+	.has_pcfg = false,
+	.has_recover = false,
+	.has_rgbc = false,
+	.dma_hwdesc_size = sizeof(struct ingenic_dma_hwdesc),
+	.num_descriptors = 1,
 	.max_width = 800,
 	.max_height = 600,
 };
 
 static const struct jz_soc_info jz4770_soc_info = {
 	.needs_dev_clk = false,
+	.has_pcfg = false,
+	.has_recover = false,
+	.has_rgbc = false,
+	.dma_hwdesc_size = sizeof(struct ingenic_dma_hwdesc),
+	.num_descriptors = 1,
 	.max_width = 1280,
 	.max_height = 720,
 };
 
+static const struct jz_soc_info jz4780_soc_info = {
+	.needs_dev_clk = true,
+	.has_pcfg = true,
+	.has_recover = true,
+	.has_rgbc = true,
+	.dma_hwdesc_size = sizeof(struct ingenic_dma_hwdesc_extended),
+	.num_descriptors = 2,
+	.max_width = 4096,
+	.max_height = 4096,
+};
+
 static const struct of_device_id ingenic_drm_of_match[] = {
 	{ .compatible = "ingenic,jz4740-lcd", .data = &jz4740_soc_info },
 	{ .compatible = "ingenic,jz4725b-lcd", .data = &jz4725b_soc_info },
 	{ .compatible = "ingenic,jz4770-lcd", .data = &jz4770_soc_info },
+	{ .compatible = "ingenic,jz4780-lcd", .data = &jz4780_soc_info },
 	{ /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, ingenic_drm_of_match);
@@ -837,5 +1038,6 @@ static struct platform_driver ingenic_drm_driver = {
 module_platform_driver(ingenic_drm_driver);
 
 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_AUTHOR("Paul Boddie <paul@boddie.org.uk>");
 MODULE_DESCRIPTION("DRM driver for the Ingenic SoCs\n");
 MODULE_LICENSE("GPL v2");

[-- Attachment #3: dw-hdmi.c-bus_hack_jz4780.diff --]
[-- Type: text/x-patch, Size: 1027 bytes --]

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 30681398cfb0..089250c81fd5 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2690,6 +2690,7 @@ static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
 				       struct drm_connector_state *conn_state)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
+	struct drm_display_info *info = &conn_state->connector->display_info;
 
 	hdmi->hdmi_data.enc_out_bus_format =
 			bridge_state->output_bus_cfg.format;
@@ -2701,6 +2702,11 @@ static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
 		bridge_state->input_bus_cfg.format,
 		bridge_state->output_bus_cfg.format);
 
+	/* NOTE: Propagate the bus format to the display info. */
+	drm_display_info_set_bus_formats(info, &bridge_state->input_bus_cfg.format, 1);
+	/* NOTE: Set the bus flags for the LCD controller output. */
+	info->bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE;
+
 	return 0;
 }
 

[-- Attachment #4: dw_hdmi-jz4780.c-new_driver.diff --]
[-- Type: text/x-patch, Size: 4504 bytes --]

diff --git a/drivers/gpu/drm/ingenic/Kconfig b/drivers/gpu/drm/ingenic/Kconfig
index d82c3d37ec9c..44bfd0d35af1 100644
--- a/drivers/gpu/drm/ingenic/Kconfig
+++ b/drivers/gpu/drm/ingenic/Kconfig
@@ -14,3 +14,11 @@ config DRM_INGENIC
 	  Choose this option for DRM support for the Ingenic SoCs.
 
 	  If M is selected the module will be called ingenic-drm.
+
+config DRM_DW_HDMI_JZ4780
+	tristate "HDMI Support for Ingenic JZ4780"
+	depends on DRM_INGENIC
+	depends on OF
+	select DRM_DW_HDMI
+	help
+	  Choose this option for HDMI output from the Ingenic JZ4780.
diff --git a/drivers/gpu/drm/ingenic/Makefile b/drivers/gpu/drm/ingenic/Makefile
index 11cac42ce0bb..238383de63c7 100644
--- a/drivers/gpu/drm/ingenic/Makefile
+++ b/drivers/gpu/drm/ingenic/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o
+obj-$(CONFIG_DRM_DW_HDMI_JZ4780) += dw_hdmi-jz4780.o
diff --git a/drivers/gpu/drm/ingenic/dw_hdmi-jz4780.c b/drivers/gpu/drm/ingenic/dw_hdmi-jz4780.c
new file mode 100644
index 000000000000..8e4e316183ef
--- /dev/null
+++ b/drivers/gpu/drm/ingenic/dw_hdmi-jz4780.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2019, 2020 Paul Boddie <paul@boddie.org.uk>
+ *
+ * Derived from dw_hdmi-imx.c with i.MX portions removed.
+ * Probe and remove operations derived from rcar_dw_hdmi.c.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_of.h>
+
+static const struct dw_hdmi_mpll_config jz4780_mpll_cfg[] = {
+	{ 45250000,  { { 0x01e0, 0x0000 }, { 0x21e1, 0x0000 }, { 0x41e2, 0x0000 } } },
+	{ 92500000,  { { 0x0140, 0x0005 }, { 0x2141, 0x0005 }, { 0x4142, 0x0005 } } },
+	{ 148500000, { { 0x00a0, 0x000a }, { 0x20a1, 0x000a }, { 0x40a2, 0x000a } } },
+	{ 216000000, { { 0x00a0, 0x000a }, { 0x2001, 0x000f }, { 0x4002, 0x000f } } },
+	{ ~0UL,      { { 0x0000, 0x0000 }, { 0x0000, 0x0000 }, { 0x0000, 0x0000 } } }
+};
+
+static const struct dw_hdmi_curr_ctrl jz4780_cur_ctr[] = {
+	/*pixelclk     bpp8    bpp10   bpp12 */
+	{ 54000000,  { 0x091c, 0x091c, 0x06dc } },
+	{ 58400000,  { 0x091c, 0x06dc, 0x06dc } },
+	{ 72000000,  { 0x06dc, 0x06dc, 0x091c } },
+	{ 74250000,  { 0x06dc, 0x0b5c, 0x091c } },
+	{ 118800000, { 0x091c, 0x091c, 0x06dc } },
+	{ 216000000, { 0x06dc, 0x0b5c, 0x091c } },
+	{ ~0UL,      { 0x0000, 0x0000, 0x0000 } },
+};
+
+/*
+ * Resistance term 133Ohm Cfg
+ * PREEMP config 0.00
+ * TX/CK level 10
+ */
+static const struct dw_hdmi_phy_config jz4780_phy_config[] = {
+	/*pixelclk   symbol   term   vlev */
+	{ 216000000, 0x800d, 0x0005, 0x01ad},
+	{ ~0UL,      0x0000, 0x0000, 0x0000}
+};
+
+static enum drm_mode_status
+jz4780_hdmi_mode_valid(struct drm_connector *con,
+		       const struct drm_display_mode *mode)
+{
+	if (mode->clock < 13500)
+		return MODE_CLOCK_LOW;
+	/* FIXME: Hardware is capable of 270MHz, but setup data is missing. */
+	if (mode->clock > 216000)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static struct dw_hdmi_plat_data jz4780_dw_hdmi_plat_data = {
+	.mpll_cfg   = jz4780_mpll_cfg,
+	.cur_ctr    = jz4780_cur_ctr,
+	.phy_config = jz4780_phy_config,
+	.mode_valid = jz4780_hdmi_mode_valid,
+};
+
+static const struct of_device_id jz4780_dw_hdmi_dt_ids[] = {
+	{ .compatible = "ingenic,jz4780-dw-hdmi" },
+	{ /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, jz4780_dw_hdmi_dt_ids);
+
+static int jz4780_dw_hdmi_probe(struct platform_device *pdev)
+{
+        struct dw_hdmi *hdmi;
+
+        hdmi = dw_hdmi_probe(pdev, &jz4780_dw_hdmi_plat_data);
+        if (IS_ERR(hdmi))
+                return PTR_ERR(hdmi);
+
+        platform_set_drvdata(pdev, hdmi);
+
+        return 0;
+}
+
+static int jz4780_dw_hdmi_remove(struct platform_device *pdev)
+{
+        struct dw_hdmi *hdmi = platform_get_drvdata(pdev);
+
+        dw_hdmi_remove(hdmi);
+
+        return 0;
+}
+
+static struct platform_driver jz4780_dw_hdmi_platform_driver = {
+	.probe  = jz4780_dw_hdmi_probe,
+	.remove = jz4780_dw_hdmi_remove,
+	.driver = {
+		.name = "dw-hdmi-jz4780",
+		.of_match_table = jz4780_dw_hdmi_dt_ids,
+	},
+};
+
+module_platform_driver(jz4780_dw_hdmi_platform_driver);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
+MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
+MODULE_AUTHOR("Paul Boddie <paul@boddie.org.uk>");
+MODULE_DESCRIPTION("Ingenic JZ4780 DW-HDMI Driver Extension");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dw-hdmi-jz4780");

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 5/6] MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs.
  2020-05-19 14:35 ` [PATCH v8 5/6] MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs 周琰杰 (Zhou Yanjie)
@ 2020-09-10  7:52   ` H. Nikolaus Schaller
  2020-09-12  6:17     ` Zhou Yanjie
  0 siblings, 1 reply; 31+ messages in thread
From: H. Nikolaus Schaller @ 2020-09-10  7:52 UTC (permalink / raw)
  To: "周琰杰 (Zhou Yanjie)"
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
	paul, krzk, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
	sernia.zhou, zhenwenjin

Hi Zhou Yanjie,
what is the status of this series? It does not seem to have arrived in linux-next for v5.10-rc1.

BR and thanks,
Nikolaus


> Am 19.05.2020 um 16:35 schrieb 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>:
> 
> Add 'cpus' node to the jz4740.dtsi, jz4770.dtsi, jz4780.dtsi
> and x1000.dtsi files.
> 
> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
> Tested-by: Paul Boddie <paul@boddie.org.uk>
> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
> ---
> 
> Notes:
>    v1->v2:
>    No change.
> 
>    v2->v3:
>    No change.
> 
>    v3->v4:
>    Rebase on top of kernel 5.6-rc1.
> 
>    v4->v5:
>    No change.
> 
>    v5->v6:
>    No change.
> 
>    v6->v7:
>    Update compatible strings.
> 
>    v7->v8:
>    No change.
> 
> 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 a3301ba..1f2f896 100644
> --- a/arch/mips/boot/dts/ingenic/jz4740.dtsi
> +++ b/arch/mips/boot/dts/ingenic/jz4740.dtsi
> @@ -7,6 +7,20 @@
> 	#size-cells = <1>;
> 	compatible = "ingenic,jz4740";
> 
> +	cpus {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		cpu0: cpu@0 {
> +			device_type = "cpu";
> +			compatible = "ingenic,xburst-mxu1.0";
> +			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..12c7101 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-fpu1.0-mxu1.1";
> +			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 bb89653..03aeeff 100644
> --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi
> +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi
> @@ -8,6 +8,29 @@
> 	#size-cells = <1>;
> 	compatible = "ingenic,jz4780";
> 
> +	cpus {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		cpu0: cpu@0 {
> +			device_type = "cpu";
> +			compatible = "ingenic,xburst-fpu1.0-mxu1.1";
> +			reg = <0>;
> +
> +			clocks = <&cgu JZ4780_CLK_CPU>;
> +			clock-names = "cpu";
> +		};
> +
> +		cpu1: cpu@1 {
> +			device_type = "cpu";
> +			compatible = "ingenic,xburst-fpu1.0-mxu1.1";
> +			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 147f7d5..2205e1b 100644
> --- a/arch/mips/boot/dts/ingenic/x1000.dtsi
> +++ b/arch/mips/boot/dts/ingenic/x1000.dtsi
> @@ -8,6 +8,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-fpu1.0-mxu1.1";
> +			reg = <0>;
> +
> +			clocks = <&cgu X1000_CLK_CPU>;
> +			clock-names = "cpu";
> +		};
> +	};
> +
> 	cpuintc: interrupt-controller {
> 		#address-cells = <0>;
> 		#interrupt-cells = <1>;
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v8 5/6] MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs.
  2020-09-10  7:52   ` H. Nikolaus Schaller
@ 2020-09-12  6:17     ` Zhou Yanjie
  0 siblings, 0 replies; 31+ messages in thread
From: Zhou Yanjie @ 2020-09-12  6:17 UTC (permalink / raw)
  To: H. Nikolaus Schaller
  Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
	jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
	paul, krzk, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
	sernia.zhou, zhenwenjin

Hi Nikolaus,

This series was temporarily shelved, because now it is necessary to add 
support for the new X2000 SoC, and it will continue to advance after all 
the functions related to X2000 are completed.

Thanks and best regards!

在 2020/9/10 下午3:52, H. Nikolaus Schaller 写道:
> Hi Zhou Yanjie,
> what is the status of this series? It does not seem to have arrived in linux-next for v5.10-rc1.
>
> BR and thanks,
> Nikolaus
>
>
>> Am 19.05.2020 um 16:35 schrieb 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>:
>>
>> Add 'cpus' node to the jz4740.dtsi, jz4770.dtsi, jz4780.dtsi
>> and x1000.dtsi files.
>>
>> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
>> Tested-by: Paul Boddie <paul@boddie.org.uk>
>> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
>> ---
>>
>> Notes:
>>     v1->v2:
>>     No change.
>>
>>     v2->v3:
>>     No change.
>>
>>     v3->v4:
>>     Rebase on top of kernel 5.6-rc1.
>>
>>     v4->v5:
>>     No change.
>>
>>     v5->v6:
>>     No change.
>>
>>     v6->v7:
>>     Update compatible strings.
>>
>>     v7->v8:
>>     No change.
>>
>> 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 a3301ba..1f2f896 100644
>> --- a/arch/mips/boot/dts/ingenic/jz4740.dtsi
>> +++ b/arch/mips/boot/dts/ingenic/jz4740.dtsi
>> @@ -7,6 +7,20 @@
>> 	#size-cells = <1>;
>> 	compatible = "ingenic,jz4740";
>>
>> +	cpus {
>> +		#address-cells = <1>;
>> +		#size-cells = <0>;
>> +
>> +		cpu0: cpu@0 {
>> +			device_type = "cpu";
>> +			compatible = "ingenic,xburst-mxu1.0";
>> +			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..12c7101 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-fpu1.0-mxu1.1";
>> +			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 bb89653..03aeeff 100644
>> --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi
>> +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi
>> @@ -8,6 +8,29 @@
>> 	#size-cells = <1>;
>> 	compatible = "ingenic,jz4780";
>>
>> +	cpus {
>> +		#address-cells = <1>;
>> +		#size-cells = <0>;
>> +
>> +		cpu0: cpu@0 {
>> +			device_type = "cpu";
>> +			compatible = "ingenic,xburst-fpu1.0-mxu1.1";
>> +			reg = <0>;
>> +
>> +			clocks = <&cgu JZ4780_CLK_CPU>;
>> +			clock-names = "cpu";
>> +		};
>> +
>> +		cpu1: cpu@1 {
>> +			device_type = "cpu";
>> +			compatible = "ingenic,xburst-fpu1.0-mxu1.1";
>> +			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 147f7d5..2205e1b 100644
>> --- a/arch/mips/boot/dts/ingenic/x1000.dtsi
>> +++ b/arch/mips/boot/dts/ingenic/x1000.dtsi
>> @@ -8,6 +8,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-fpu1.0-mxu1.1";
>> +			reg = <0>;
>> +
>> +			clocks = <&cgu X1000_CLK_CPU>;
>> +			clock-names = "cpu";
>> +		};
>> +	};
>> +
>> 	cpuintc: interrupt-controller {
>> 		#address-cells = <0>;
>> 		#interrupt-cells = <1>;
>> -- 
>> 2.7.4
>>

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2020-09-12  6:17 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-19 14:35 Introduce SMP support for CI20 (based on JZ4780) v8 周琰杰 (Zhou Yanjie)
2020-05-19 14:35 ` [PATCH v8 0/6] Introduce SMP support for CI20 (based on JZ4780) 周琰杰 (Zhou Yanjie)
2020-05-19 14:35 ` [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support 周琰杰 (Zhou Yanjie)
2020-05-19 16:09   ` Paul Cercueil
2020-05-20  7:24     ` Zhou Yanjie
2020-05-19 18:21   ` kbuild test robot
2020-05-19 19:41   ` Paul Cercueil
2020-05-20  7:23     ` Zhou Yanjie
2020-05-20 11:33       ` Paul Cercueil
2020-05-20 12:32         ` Jiaxun Yang
2020-05-19 14:35 ` [PATCH v8 2/6] MIPS: CI20: Modify DTS to support high resolution timer for SMP 周琰杰 (Zhou Yanjie)
2020-05-19 14:35 ` [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support " 周琰杰 (Zhou Yanjie)
2020-05-19 17:42   ` Paul Cercueil
2020-05-19 20:11   ` [PATCH] " Paul Cercueil
2020-05-20 22:14     ` Paul Boddie
2020-05-22 12:26       ` Paul Cercueil
2020-05-22 19:16         ` Paul Boddie
2020-05-25 23:03           ` JZ4780 LCD controller initialisation (was Re: [PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.) Paul Boddie
2020-05-26  4:48             ` H. Nikolaus Schaller
2020-05-26 15:03             ` Paul Cercueil
2020-05-26 22:44               ` Paul Boddie
2020-05-26 23:07                 ` Paul Cercueil
2020-06-01 20:06                   ` Paul Boddie
2020-06-23 21:28                   ` Paul Boddie
2020-05-19 14:35 ` [PATCH v8 4/6] dt-bindings: MIPS: Document Ingenic SoCs binding 周琰杰 (Zhou Yanjie)
2020-05-26 19:29   ` Rob Herring
2020-05-27  5:59     ` Zhou Yanjie
2020-05-19 14:35 ` [PATCH v8 5/6] MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs 周琰杰 (Zhou Yanjie)
2020-09-10  7:52   ` H. Nikolaus Schaller
2020-09-12  6:17     ` Zhou Yanjie
2020-05-19 14:35 ` [PATCH v8 6/6] MIPS: CI20: Update defconfig to support SMP 周琰杰 (Zhou Yanjie)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).