1 /* $NetBSD: sunxi_mc_smp.c,v 1.4 2019/03/03 17:00:22 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.4 2019/03/03 17:00:22 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 86 extern struct bus_space arm_generic_bs_tag; 87 88 enum sunxi_mc_soc { 89 MC_SOC_A80, 90 MC_SOC_A83T 91 }; 92 93 enum sunxi_mc_cpu { 94 MC_CORE_CA7, 95 MC_CORE_CA15 96 }; 97 98 uint32_t sunxi_mc_cci_port[MAXCPUS]; 99 100 static uint32_t 101 sunxi_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 113 static int 114 sunxi_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 else 160 val &= ~__BIT(cpu); 161 val &= ~__BIT(0); /* cluster power gate */ 162 } else { 163 val &= ~__BIT(cpu); 164 val &= ~__BIT(4); /* cluster power gate */ 165 } 166 bus_space_write_4(bst, prcm, PRCM_CL_PWROFF(cluster), val); 167 168 /* De-assert power-on reset */ 169 val = bus_space_read_4(bst, prcm, PRCM_CL_RST_CTRL(cluster)); 170 val |= __BIT(cpu); 171 bus_space_write_4(bst, prcm, PRCM_CL_RST_CTRL(cluster), val); 172 173 if (soc == MC_SOC_A83T) { 174 val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster)); 175 val |= __BIT(cpu); 176 bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val); 177 delay(10); 178 } 179 180 /* De-assert core reset */ 181 val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster)); 182 val |= __BIT(cpu); 183 val |= CPUXCFG_CL_RST_SOC_DBG_RST; 184 if (core == MC_CORE_CA7) 185 val |= CPUXCFG_CL_RST_ETM_RST(cpu); 186 else 187 val |= CPUXCFG_CL_RST_CX_RST(cpu); 188 val |= CPUXCFG_CL_RST_DBG_RST(cpu); 189 val |= CPUXCFG_CL_RST_L2_RST; 190 val |= CPUXCFG_CL_RST_H_RST; 191 bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val); 192 193 /* De-assert ACINACTM */ 194 val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster)); 195 val &= ~CPUXCFG_CL_CTRL1_ACINACTM; 196 bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster), val); 197 198 return 0; 199 } 200 201 int 202 sun8i_a83t_smp_enable(u_int mpidr) 203 { 204 bus_space_tag_t bst = &arm_generic_bs_tag; 205 bus_space_handle_t prcm, cpucfg, cpuxcfg; 206 int error; 207 208 const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1); 209 const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0); 210 211 if (bus_space_map(bst, A83T_PRCM_BASE, A83T_PRCM_SIZE, 0, &prcm) != 0 || 212 bus_space_map(bst, CPUCFG_BASE, CPUCFG_SIZE, 0, &cpucfg) != 0 || 213 bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0) 214 return ENOMEM; 215 216 for (int i = 0; i < 4; i++) 217 sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF3_OFFSET; 218 for (int i = 4; i < 8; i++) 219 sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF4_OFFSET; 220 221 /* Set start vector */ 222 bus_space_write_4(bst, cpucfg, CPUCFG_P_REG0, sunxi_mc_smp_pa()); 223 cpu_idcache_wbinv_all(); 224 225 error = sunxi_mc_smp_start(bst, prcm, cpucfg, cpuxcfg, cluster, cpu, 226 MC_SOC_A83T, MC_CORE_CA7); 227 228 bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE); 229 bus_space_unmap(bst, cpucfg, CPUCFG_SIZE); 230 bus_space_unmap(bst, prcm, A83T_PRCM_SIZE); 231 232 return error; 233 } 234 235 int 236 sun9i_a80_smp_enable(u_int mpidr) 237 { 238 bus_space_tag_t bst = &arm_generic_bs_tag; 239 bus_space_handle_t prcm, cpuxcfg; 240 int error; 241 242 const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1); 243 const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0); 244 245 if (bus_space_map(bst, A80_PRCM_BASE, A80_PRCM_SIZE, 0, &prcm) != 0 || 246 bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0) 247 return ENOMEM; 248 249 for (int i = 0; i < 4; i++) 250 sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF3_OFFSET; 251 for (int i = 4; i < 8; i++) 252 sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF4_OFFSET; 253 254 /* Set start vector */ 255 bus_space_write_4(bst, prcm, PRCM_CPU_SOFT_ENTRY, sunxi_mc_smp_pa()); 256 cpu_idcache_wbinv_all(); 257 258 error = sunxi_mc_smp_start(bst, prcm, 0, cpuxcfg, cluster, cpu, 259 MC_SOC_A80, cluster == 0 ? MC_CORE_CA7 : MC_CORE_CA15); 260 261 bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE); 262 bus_space_unmap(bst, prcm, A80_PRCM_SIZE); 263 264 return error; 265 } 266