sunxi_mc_smp.c revision 1.3
1/* $NetBSD: sunxi_mc_smp.c,v 1.3 2019/01/03 14:44:21 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.3 2019/01/03 14:44:21 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 A80_PRCM_BASE 0x08001400 50#define A80_PRCM_SIZE 0x200 51 52#define A83T_PRCM_BASE 0x01f01400 53#define A83T_PRCM_SIZE 0x800 54 55#define PRCM_CL_RST_CTRL(cluster) (0x4 + (cluster) * 0x4) 56#define PRCM_CL_PWROFF(cluster) (0x100 + (cluster) * 0x4) 57#define PRCM_CL_PWR_CLAMP(cluster, cpu) (0x140 + (cluster) * 0x10 + (cpu) * 0x4) 58#define PRCM_CPU_SOFT_ENTRY 0x164 59 60#define CPUCFG_BASE 0x01f01c00 61#define CPUCFG_SIZE 0x400 62 63#define CPUCFG_CL_RST(cluster) (0x30 + (cluster) * 0x4) 64#define CPUCFG_P_REG0 0x1a4 65 66#define CPUXCFG_BASE 0x01700000 67#define CPUXCFG_SIZE 0x400 68 69#define CPUXCFG_CL_RST(cluster) (0x80 + (cluster) * 0x4) 70#define CPUXCFG_CL_RST_SOC_DBG_RST __BIT(24) 71#define CPUXCFG_CL_RST_ETM_RST(cpu) __BIT(20 + (cpu)) 72#define CPUXCFG_CL_RST_DBG_RST(cpu) __BIT(16 + (cpu)) 73#define CPUXCFG_CL_RST_H_RST __BIT(12) 74#define CPUXCFG_CL_RST_L2_RST __BIT(8) 75#define CPUXCFG_CL_RST_CX_RST(cpu) __BIT(4 + (cpu)) 76#define CPUXCFG_CL_CTRL0(cluster) (0x0 + (cluster) * 0x10) 77#define CPUXCFG_CL_CTRL1(cluster) (0x4 + (cluster) * 0x10) 78#define CPUXCFG_CL_CTRL1_ACINACTM __BIT(0) 79 80#define A80_CCI_BASE 0x01c90000 81#define A83T_CCI_BASE 0x01790000 82 83#define CCI_SLAVEIF3_OFFSET 0x4000 84#define CCI_SLAVEIF4_OFFSET 0x5000 85 86extern struct bus_space arm_generic_bs_tag; 87 88enum sunxi_mc_soc { 89 MC_SOC_A80, 90 MC_SOC_A83T 91}; 92 93enum sunxi_mc_cpu { 94 MC_CORE_CA7, 95 MC_CORE_CA15 96}; 97 98uint32_t sunxi_mc_cci_port[MAXCPUS]; 99 100static uint32_t 101sunxi_mc_smp_pa(void) 102{ 103 extern void sunxi_mc_mpstart(void); 104 bool ok __diagused; 105 paddr_t pa; 106 107 ok = pmap_extract(pmap_kernel(), (vaddr_t)sunxi_mc_mpstart, &pa); 108 KASSERT(ok); 109 110 return pa; 111} 112 113static int 114sunxi_mc_smp_start(bus_space_tag_t bst, bus_space_handle_t prcm, bus_space_handle_t cpucfg, 115 bus_space_handle_t cpuxcfg, u_int cluster, u_int cpu, enum sunxi_mc_soc soc, 116 enum sunxi_mc_cpu core) 117{ 118 uint32_t val; 119 int i; 120 121 /* Assert core reset */ 122 val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster)); 123 val &= ~__BIT(cpu); 124 bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val); 125 126 if (soc == MC_SOC_A83T) { 127 /* Assert power-on reset */ 128 val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster)); 129 val &= ~__BIT(cpu); 130 bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val); 131 } 132 133 if (core == MC_CORE_CA7) { 134 /* Disable automatic L1 cache invalidate at reset */ 135 val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL0(cluster)); 136 val &= ~__BIT(cpu); 137 bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL0(cluster), val); 138 } 139 140 /* Release power clamp */ 141 for (i = 0; i <= 8; i++) { 142 bus_space_write_4(bst, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu), 0xff >> i); 143 delay(10); 144 } 145 for (i = 100000; i > 0; i--) { 146 if (bus_space_read_4(bst, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu)) == 0) 147 break; 148 } 149 if (i == 0) { 150 printf("CPU %#llx failed to start\n", __SHIFTIN(cluster, MPIDR_AFF1) | __SHIFTIN(cpu, MPIDR_AFF0)); 151 return ETIMEDOUT; 152 } 153 154 /* Clear power-off gating */ 155 val = bus_space_read_4(bst, prcm, PRCM_CL_PWROFF(cluster)); 156 if (soc == MC_SOC_A83T) { 157 if (cpu == 0) 158 val &= ~__BIT(4); 159 val &= ~__BIT(0); /* cluster power gate */ 160 } else { 161 val &= ~__BIT(cpu); 162 val &= ~__BIT(4); /* cluster power gate */ 163 } 164 bus_space_write_4(bst, prcm, PRCM_CL_PWROFF(cluster), val); 165 166 /* De-assert power-on reset */ 167 val = bus_space_read_4(bst, prcm, PRCM_CL_RST_CTRL(cluster)); 168 val |= __BIT(cpu); 169 bus_space_write_4(bst, prcm, PRCM_CL_RST_CTRL(cluster), val); 170 171 if (soc == MC_SOC_A83T) { 172 val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster)); 173 val |= __BIT(cpu); 174 bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val); 175 delay(10); 176 } 177 178 /* De-assert core reset */ 179 val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster)); 180 val |= __BIT(cpu); 181 val |= CPUXCFG_CL_RST_SOC_DBG_RST; 182 if (core == MC_CORE_CA7) 183 val |= CPUXCFG_CL_RST_ETM_RST(cpu); 184 else 185 val |= CPUXCFG_CL_RST_CX_RST(cpu); 186 val |= CPUXCFG_CL_RST_DBG_RST(cpu); 187 val |= CPUXCFG_CL_RST_L2_RST; 188 val |= CPUXCFG_CL_RST_H_RST; 189 bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val); 190 191 /* De-assert ACINACTM */ 192 val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster)); 193 val &= ~CPUXCFG_CL_CTRL1_ACINACTM; 194 bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster), val); 195 196 return 0; 197} 198 199int 200sun8i_a83t_smp_enable(u_int mpidr) 201{ 202 bus_space_tag_t bst = &arm_generic_bs_tag; 203 bus_space_handle_t prcm, cpucfg, cpuxcfg; 204 int error; 205 206 const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1); 207 const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0); 208 209 if (bus_space_map(bst, A83T_PRCM_BASE, A83T_PRCM_SIZE, 0, &prcm) != 0 || 210 bus_space_map(bst, CPUCFG_BASE, CPUCFG_SIZE, 0, &cpucfg) != 0 || 211 bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0) 212 return ENOMEM; 213 214 for (int i = 0; i < 4; i++) 215 sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF3_OFFSET; 216 for (int i = 4; i < 8; i++) 217 sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF4_OFFSET; 218 219 /* Set start vector */ 220 bus_space_write_4(bst, cpucfg, CPUCFG_P_REG0, sunxi_mc_smp_pa()); 221 cpu_idcache_wbinv_all(); 222 223 error = sunxi_mc_smp_start(bst, prcm, cpucfg, cpuxcfg, cluster, cpu, 224 MC_SOC_A83T, MC_CORE_CA7); 225 226 bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE); 227 bus_space_unmap(bst, cpucfg, CPUCFG_SIZE); 228 bus_space_unmap(bst, prcm, A83T_PRCM_SIZE); 229 230 return error; 231} 232 233int 234sun9i_a80_smp_enable(u_int mpidr) 235{ 236 bus_space_tag_t bst = &arm_generic_bs_tag; 237 bus_space_handle_t prcm, cpuxcfg; 238 int error; 239 240 const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1); 241 const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0); 242 243 if (bus_space_map(bst, A80_PRCM_BASE, A80_PRCM_SIZE, 0, &prcm) != 0 || 244 bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0) 245 return ENOMEM; 246 247 for (int i = 0; i < 4; i++) 248 sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF3_OFFSET; 249 for (int i = 4; i < 8; i++) 250 sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF4_OFFSET; 251 252 /* Set start vector */ 253 bus_space_write_4(bst, prcm, PRCM_CPU_SOFT_ENTRY, sunxi_mc_smp_pa()); 254 cpu_idcache_wbinv_all(); 255 256 error = sunxi_mc_smp_start(bst, prcm, 0, cpuxcfg, cluster, cpu, 257 MC_SOC_A80, cluster == 0 ? MC_CORE_CA7 : MC_CORE_CA15); 258 259 bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE); 260 bus_space_unmap(bst, prcm, A80_PRCM_SIZE); 261 262 return error; 263} 264