sunxi_mc_smp.c revision 1.2
1/* $NetBSD: sunxi_mc_smp.c,v 1.2 2019/01/03 12:52:40 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2019 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30
31__KERNEL_RCSID(0, "$NetBSD: sunxi_mc_smp.c,v 1.2 2019/01/03 12:52:40 jmcneill Exp $");
32
33#include <sys/param.h>
34#include <sys/bus.h>
35#include <sys/device.h>
36#include <sys/systm.h>
37
38#include <uvm/uvm_extern.h>
39
40#include <dev/fdt/fdtvar.h>
41
42#include <arm/armreg.h>
43#include <arm/cpu.h>
44#include <arm/cpufunc.h>
45#include <arm/locore.h>
46
47#include <arm/sunxi/sunxi_mc_smp.h>
48
49#define	A83T_SMP_ENABLE_METHOD	"allwinner,sun8i-a83t-smp"
50
51#define	PRCM_BASE		0x01f01400
52#define	PRCM_SIZE		0x800
53
54#define	 PRCM_CL_RST_CTRL(cluster)	(0x4 + (cluster) * 0x4)
55#define	 PRCM_CL_PWROFF(cluster)	(0x100 + (cluster) * 0x4)
56#define	 PRCM_CL_PWR_CLAMP(cluster, cpu) (0x140 + (cluster) * 0x10 + (cpu) * 0x4)
57
58#define	CPUCFG_BASE	0x01f01c00
59#define	CPUCFG_SIZE	0x400
60
61#define	 CPUCFG_CL_RST(cluster)		(0x30 + (cluster) * 0x4)
62#define	 CPUCFG_P_REG0			0x1a4
63
64#define	CPUXCFG_BASE	0x01700000
65#define	CPUXCFG_SIZE	0x400
66
67#define	 CPUXCFG_CL_RST(cluster)	(0x80 + (cluster) * 0x4)
68#define	  CPUXCFG_CL_RST_SOC_DBG_RST	__BIT(24)
69#define	  CPUXCFG_CL_RST_ETM_RST(cpu)	__BIT(20 + (cpu))
70#define	  CPUXCFG_CL_RST_DBG_RST(cpu)	__BIT(16 + (cpu))
71#define	  CPUXCFG_CL_RST_H_RST		__BIT(12)
72#define	  CPUXCFG_CL_RST_L2_RST		__BIT(8)
73#define	 CPUXCFG_CL_CTRL0(cluster)	(0x0 + (cluster) * 0x10)
74#define	 CPUXCFG_CL_CTRL1(cluster)	(0x4 + (cluster) * 0x10)
75#define	  CPUXCFG_CL_CTRL1_ACINACTM	__BIT(0)
76
77#define	CCI_BASE		0x01790000
78#define	CCI_SLAVEIF3_BASE	(CCI_BASE + 0x4000)
79#define	CCI_SLAVEIF4_BASE	(CCI_BASE + 0x5000)
80
81extern struct bus_space arm_generic_bs_tag;
82
83uint32_t sunxi_mc_cci_port[MAXCPUS] = {
84	CCI_SLAVEIF3_BASE,
85	CCI_SLAVEIF3_BASE,
86	CCI_SLAVEIF3_BASE,
87	CCI_SLAVEIF3_BASE,
88	CCI_SLAVEIF4_BASE,
89	CCI_SLAVEIF4_BASE,
90	CCI_SLAVEIF4_BASE,
91	CCI_SLAVEIF4_BASE,
92};
93
94static uint32_t
95sunxi_mc_smp_pa(void)
96{
97	extern void sunxi_mc_mpstart(void);
98	bool ok __diagused;
99	paddr_t pa;
100
101	ok = pmap_extract(pmap_kernel(), (vaddr_t)sunxi_mc_mpstart, &pa);
102	KASSERT(ok);
103
104	return pa;
105}
106
107static int
108sunxi_mc_smp_start(bus_space_tag_t bst, bus_space_handle_t prcm, bus_space_handle_t cpucfg,
109    bus_space_handle_t cpuxcfg, u_int cluster, u_int cpu)
110{
111	uint32_t val;
112	int i;
113
114	/* Set start vector */
115	bus_space_write_4(bst, cpucfg, CPUCFG_P_REG0, sunxi_mc_smp_pa());
116
117	/* Assert core reset */
118	val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster));
119	val &= ~__BIT(cpu);
120	bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val);
121
122	/* Assert power-on reset */
123	val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster));
124	val &= ~__BIT(cpu);
125	bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val);
126
127	/* Disable automatic L1 cache invalidate at reset */
128	val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL0(cluster));
129	val &= ~__BIT(cpu);
130	bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL0(cluster), val);
131
132	/* Release power clamp */
133	for (i = 0; i <= 8; i++) {
134		bus_space_write_4(bst, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu), 0xff >> i);
135		delay(10);
136	}
137	for (i = 100000; i > 0; i--) {
138		if (bus_space_read_4(bst, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu)) == 0)
139			break;
140	}
141	if (i == 0) {
142		printf("CPU %#llx failed to start\n", __SHIFTIN(cluster, MPIDR_AFF1) | __SHIFTIN(cpu, MPIDR_AFF0));
143		return ETIMEDOUT;
144	}
145
146	/* Clear power-off gating */
147	val = bus_space_read_4(bst, prcm, PRCM_CL_PWROFF(cluster));
148	if (cpu == 0)
149		val &= ~__BIT(4);
150	val &= ~__BIT(cpu);
151	bus_space_write_4(bst, prcm, PRCM_CL_PWROFF(cluster), val);
152
153	/* De-assert power-on reset */
154	val = bus_space_read_4(bst, prcm, PRCM_CL_RST_CTRL(cluster));
155	val |= __BIT(cpu);
156	bus_space_write_4(bst, prcm, PRCM_CL_RST_CTRL(cluster), val);
157
158	val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster));
159	val |= __BIT(cpu);
160	bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val);
161	delay(10);
162
163	/* De-assert core reset */
164	val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster));
165	val |= __BIT(cpu);
166	val |= CPUXCFG_CL_RST_SOC_DBG_RST;
167	val |= CPUXCFG_CL_RST_ETM_RST(cpu);
168	val |= CPUXCFG_CL_RST_DBG_RST(cpu);
169	val |= CPUXCFG_CL_RST_L2_RST;
170	val |= CPUXCFG_CL_RST_H_RST;
171	bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val);
172
173	/* De-assert ACINACTM */
174	val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster));
175	val &= ~CPUXCFG_CL_CTRL1_ACINACTM;
176	bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster), val);
177
178	return 0;
179}
180
181int
182sunxi_mc_smp_enable(u_int mpidr)
183{
184	bus_space_tag_t bst = &arm_generic_bs_tag;
185	bus_space_handle_t prcm, cpucfg, cpuxcfg;
186	int error;
187
188	const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1);
189	const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0);
190
191	if (bus_space_map(bst, PRCM_BASE, PRCM_SIZE, 0, &prcm) != 0 ||
192	    bus_space_map(bst, CPUCFG_BASE, CPUCFG_SIZE, 0, &cpucfg) != 0 ||
193	    bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0)
194		return ENOMEM;
195
196	error = sunxi_mc_smp_start(bst, prcm, cpucfg, cpuxcfg, cluster, cpu);
197
198	bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE);
199	bus_space_unmap(bst, cpucfg, CPUCFG_SIZE);
200	bus_space_unmap(bst, prcm, PRCM_SIZE);
201
202	return error;
203}
204