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