Linux Audio

Check our new training course

Linux kernel drivers training

May 6-19, 2025
Register
Loading...
Note: File does not exist in v3.15.
  1/*
  2 * Copyright 2014 Linaro Ltd.
  3 * Copyright (C) 2014 ZTE Corporation.
  4 *
  5 * This program is free software; you can redistribute it and/or modify
  6 * it under the terms of the GNU General Public License version 2 as
  7 * published by the Free Software Foundation.
  8 */
  9
 10#include <linux/delay.h>
 11#include <linux/errno.h>
 12#include <linux/init.h>
 13#include <linux/io.h>
 14#include <linux/jiffies.h>
 15#include <linux/of.h>
 16#include <linux/of_address.h>
 17#include <linux/smp.h>
 18
 19#include <asm/cacheflush.h>
 20#include <asm/cp15.h>
 21#include <asm/fncpy.h>
 22#include <asm/proc-fns.h>
 23#include <asm/smp_scu.h>
 24#include <asm/smp_plat.h>
 25
 26#include "core.h"
 27
 28#define AON_SYS_CTRL_RESERVED1		0xa8
 29
 30#define BUS_MATRIX_REMAP_CONFIG		0x00
 31
 32#define PCU_CPU0_CTRL			0x00
 33#define PCU_CPU1_CTRL			0x04
 34#define PCU_CPU1_ST			0x0c
 35#define PCU_GLOBAL_CTRL			0x14
 36#define PCU_EXPEND_CONTROL		0x34
 37
 38#define ZX_IRAM_BASE			0x00200000
 39
 40static void __iomem *pcu_base;
 41static void __iomem *matrix_base;
 42static void __iomem *scu_base;
 43
 44void __init zx_smp_prepare_cpus(unsigned int max_cpus)
 45{
 46	struct device_node *np;
 47	unsigned long base = 0;
 48	void __iomem *aonsysctrl_base;
 49	void __iomem *sys_iram;
 50
 51	base = scu_a9_get_base();
 52	scu_base = ioremap(base, SZ_256);
 53	if (!scu_base) {
 54		pr_err("%s: failed to map scu\n", __func__);
 55		return;
 56	}
 57
 58	scu_enable(scu_base);
 59
 60	np = of_find_compatible_node(NULL, NULL, "zte,sysctrl");
 61	if (!np) {
 62		pr_err("%s: failed to find sysctrl node\n", __func__);
 63		return;
 64	}
 65
 66	aonsysctrl_base = of_iomap(np, 0);
 67	if (!aonsysctrl_base) {
 68		pr_err("%s: failed to map aonsysctrl\n", __func__);
 69		of_node_put(np);
 70		return;
 71	}
 72
 73	/*
 74	 * Write the address of secondary startup into the
 75	 * system-wide flags register. The BootMonitor waits
 76	 * until it receives a soft interrupt, and then the
 77	 * secondary CPU branches to this address.
 78	 */
 79	__raw_writel(__pa_symbol(zx_secondary_startup),
 80		     aonsysctrl_base + AON_SYS_CTRL_RESERVED1);
 81
 82	iounmap(aonsysctrl_base);
 83	of_node_put(np);
 84
 85	np = of_find_compatible_node(NULL, NULL, "zte,zx296702-pcu");
 86	pcu_base = of_iomap(np, 0);
 87	of_node_put(np);
 88	WARN_ON(!pcu_base);
 89
 90	np = of_find_compatible_node(NULL, NULL, "zte,zx-bus-matrix");
 91	matrix_base = of_iomap(np, 0);
 92	of_node_put(np);
 93	WARN_ON(!matrix_base);
 94
 95	/* Map the first 4 KB IRAM for suspend usage */
 96	sys_iram = __arm_ioremap_exec(ZX_IRAM_BASE, PAGE_SIZE, false);
 97	zx_secondary_startup_pa = __pa_symbol(zx_secondary_startup);
 98	fncpy(sys_iram, &zx_resume_jump, zx_suspend_iram_sz);
 99}
100
101static int zx_boot_secondary(unsigned int cpu, struct task_struct *idle)
102{
103	static bool first_boot = true;
104
105	if (first_boot) {
106		arch_send_wakeup_ipi_mask(cpumask_of(cpu));
107		first_boot = false;
108		return 0;
109	}
110
111	/* Swap the base address mapping between IRAM and IROM */
112	writel_relaxed(0x1, matrix_base + BUS_MATRIX_REMAP_CONFIG);
113
114	/* Power on CPU1 */
115	writel_relaxed(0x0, pcu_base + PCU_CPU1_CTRL);
116
117	/* Wait for power on ack */
118	while (readl_relaxed(pcu_base + PCU_CPU1_ST) & 0x4)
119		cpu_relax();
120
121	/* Swap back the mapping of IRAM and IROM */
122	writel_relaxed(0x0, matrix_base + BUS_MATRIX_REMAP_CONFIG);
123
124	return 0;
125}
126
127#ifdef CONFIG_HOTPLUG_CPU
128static inline void cpu_enter_lowpower(void)
129{
130	unsigned int v;
131
132	asm volatile(
133		"mcr	p15, 0, %1, c7, c5, 0\n"
134	"	mcr	p15, 0, %1, c7, c10, 4\n"
135	/*
136	 * Turn off coherency
137	 */
138	"	mrc	p15, 0, %0, c1, c0, 1\n"
139	"	bic	%0, %0, %3\n"
140	"	mcr	p15, 0, %0, c1, c0, 1\n"
141	"	mrc	p15, 0, %0, c1, c0, 0\n"
142	"	bic	%0, %0, %2\n"
143	"	mcr	p15, 0, %0, c1, c0, 0\n"
144	  : "=&r" (v)
145	  : "r" (0), "Ir" (CR_C), "Ir" (0x40)
146	  : "cc");
147}
148
149static int zx_cpu_kill(unsigned int cpu)
150{
151	unsigned long timeout = jiffies + msecs_to_jiffies(2000);
152
153	writel_relaxed(0x2, pcu_base + PCU_CPU1_CTRL);
154
155	while ((readl_relaxed(pcu_base + PCU_CPU1_ST) & 0x3) != 0x0) {
156		if (time_after(jiffies, timeout)) {
157			pr_err("*** cpu1 poweroff timeout\n");
158			break;
159		}
160	}
161	return 1;
162}
163
164static void zx_cpu_die(unsigned int cpu)
165{
166	scu_power_mode(scu_base, SCU_PM_POWEROFF);
167	cpu_enter_lowpower();
168
169	while (1)
170		cpu_do_idle();
171}
172#endif
173
174static void zx_secondary_init(unsigned int cpu)
175{
176	scu_power_mode(scu_base, SCU_PM_NORMAL);
177}
178
179static const struct smp_operations zx_smp_ops __initconst = {
180	.smp_prepare_cpus	= zx_smp_prepare_cpus,
181	.smp_secondary_init	= zx_secondary_init,
182	.smp_boot_secondary	= zx_boot_secondary,
183#ifdef CONFIG_HOTPLUG_CPU
184	.cpu_kill		= zx_cpu_kill,
185	.cpu_die		= zx_cpu_die,
186#endif
187};
188
189CPU_METHOD_OF_DECLARE(zx_smp, "zte,zx296702-smp", &zx_smp_ops);