1 /*- 2 * Copyright (c) 2015 Landon Fuller <landon@landonf.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/bus.h> 35 #include <sys/kernel.h> 36 #include <sys/limits.h> 37 #include <sys/systm.h> 38 39 #include <machine/bus.h> 40 #include <machine/resource.h> 41 42 #include <dev/bhnd/bhndvar.h> 43 44 #include "bcma_dmp.h" 45 46 #include "bcmavar.h" 47 48 /* Return the resource ID for a device's agent register allocation */ 49 #define BCMA_AGENT_RID(_dinfo) \ 50 (BCMA_AGENT_RID_BASE + BCMA_DINFO_COREIDX(_dinfo)) 51 52 /** 53 * Allocate and initialize new core config structure. 54 * 55 * @param core_index Core index on the bus. 56 * @param core_unit Core unit number. 57 * @param vendor Core designer. 58 * @param device Core identifier (e.g. part number). 59 * @param hwrev Core revision. 60 */ 61 struct bcma_corecfg * 62 bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor, 63 uint16_t device, uint8_t hwrev) 64 { 65 struct bcma_corecfg *cfg; 66 67 cfg = malloc(sizeof(*cfg), M_BHND, M_NOWAIT); 68 if (cfg == NULL) 69 return NULL; 70 71 cfg->core_info = (struct bhnd_core_info) { 72 .vendor = vendor, 73 .device = device, 74 .hwrev = hwrev, 75 .core_idx = core_index, 76 .unit = core_unit 77 }; 78 79 STAILQ_INIT(&cfg->master_ports); 80 cfg->num_master_ports = 0; 81 82 STAILQ_INIT(&cfg->dev_ports); 83 cfg->num_dev_ports = 0; 84 85 STAILQ_INIT(&cfg->bridge_ports); 86 cfg->num_bridge_ports = 0; 87 88 STAILQ_INIT(&cfg->wrapper_ports); 89 cfg->num_wrapper_ports = 0; 90 91 return (cfg); 92 } 93 94 /** 95 * Deallocate the given core config and any associated resources. 96 * 97 * @param corecfg Core info to be deallocated. 98 */ 99 void 100 bcma_free_corecfg(struct bcma_corecfg *corecfg) 101 { 102 struct bcma_mport *mport, *mnext; 103 struct bcma_sport *sport, *snext; 104 105 STAILQ_FOREACH_SAFE(mport, &corecfg->master_ports, mp_link, mnext) { 106 free(mport, M_BHND); 107 } 108 109 STAILQ_FOREACH_SAFE(sport, &corecfg->dev_ports, sp_link, snext) { 110 bcma_free_sport(sport); 111 } 112 113 STAILQ_FOREACH_SAFE(sport, &corecfg->bridge_ports, sp_link, snext) { 114 bcma_free_sport(sport); 115 } 116 117 STAILQ_FOREACH_SAFE(sport, &corecfg->wrapper_ports, sp_link, snext) { 118 bcma_free_sport(sport); 119 } 120 121 free(corecfg, M_BHND); 122 } 123 124 /** 125 * Return the @p cfg port list for @p type. 126 * 127 * @param cfg The core configuration. 128 * @param type The requested port type. 129 */ 130 struct bcma_sport_list * 131 bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type) 132 { 133 switch (type) { 134 case BHND_PORT_DEVICE: 135 return (&cfg->dev_ports); 136 break; 137 case BHND_PORT_BRIDGE: 138 return (&cfg->bridge_ports); 139 break; 140 case BHND_PORT_AGENT: 141 return (&cfg->wrapper_ports); 142 break; 143 default: 144 return (NULL); 145 } 146 } 147 148 /** 149 * Populate the resource list and bcma_map RIDs using the maps defined on 150 * @p ports. 151 * 152 * @param bus The requesting bus device. 153 * @param dinfo The device info instance to be initialized. 154 * @param ports The set of ports to be enumerated 155 */ 156 static void 157 bcma_dinfo_init_resource_info(device_t bus, struct bcma_devinfo *dinfo, 158 struct bcma_sport_list *ports) 159 { 160 struct bcma_map *map; 161 struct bcma_sport *port; 162 bhnd_addr_t end; 163 164 STAILQ_FOREACH(port, ports, sp_link) { 165 STAILQ_FOREACH(map, &port->sp_maps, m_link) { 166 /* 167 * Create the corresponding device resource list entry. 168 * 169 * We necessarily skip registration if the region's 170 * device memory range is not representable via 171 * rman_res_t. 172 * 173 * When rman_res_t is migrated to uintmax_t, any 174 * range should be representable. 175 */ 176 end = map->m_base + map->m_size; 177 if (map->m_base <= RM_MAX_END && end <= RM_MAX_END) { 178 map->m_rid = resource_list_add_next( 179 &dinfo->resources, SYS_RES_MEMORY, 180 map->m_base, end, map->m_size); 181 } else if (bootverbose) { 182 device_printf(bus, 183 "core%u %s%u.%u: region %llx-%llx extends " 184 "beyond supported addressable range\n", 185 dinfo->corecfg->core_info.core_idx, 186 bhnd_port_type_name(port->sp_type), 187 port->sp_num, map->m_region_num, 188 (unsigned long long) map->m_base, 189 (unsigned long long) end); 190 } 191 } 192 } 193 } 194 195 196 /** 197 * Allocate and return a new empty device info structure. 198 * 199 * @param bus The requesting bus device. 200 * 201 * @retval NULL if allocation failed. 202 */ 203 struct bcma_devinfo * 204 bcma_alloc_dinfo(device_t bus) 205 { 206 struct bcma_devinfo *dinfo; 207 208 dinfo = malloc(sizeof(struct bcma_devinfo), M_BHND, M_NOWAIT|M_ZERO); 209 if (dinfo == NULL) 210 return (NULL); 211 212 dinfo->corecfg = NULL; 213 dinfo->res_agent = NULL; 214 dinfo->rid_agent = -1; 215 216 resource_list_init(&dinfo->resources); 217 218 return (dinfo); 219 } 220 221 /** 222 * Initialize a device info structure previously allocated via 223 * bcma_alloc_dinfo, assuming ownership of the provided core 224 * configuration. 225 * 226 * @param bus The requesting bus device. 227 * @param dinfo The device info instance. 228 * @param corecfg Device core configuration; ownership of this value 229 * will be assumed by @p dinfo. 230 * 231 * @retval 0 success 232 * @retval non-zero initialization failed. 233 */ 234 int 235 bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo, 236 struct bcma_corecfg *corecfg) 237 { 238 KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized")); 239 240 /* Save core configuration value */ 241 dinfo->corecfg = corecfg; 242 243 /* The device ports must always be initialized first to ensure that 244 * rid 0 maps to the first device port */ 245 bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->dev_ports); 246 247 bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->bridge_ports); 248 bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->wrapper_ports); 249 250 return (0); 251 } 252 253 254 /** 255 * Allocate the per-core agent register block for a device info structure 256 * previous initialized via bcma_init_dinfo(). 257 * 258 * If an agent0.0 region is not defined on @p dinfo, the device info 259 * agent resource is set to NULL and 0 is returned. 260 * 261 * @param bus The requesting bus device. 262 * @param child The bcma child device. 263 * @param dinfo The device info associated with @p child 264 * 265 * @retval 0 success 266 * @retval non-zero resource allocation failed. 267 */ 268 int 269 bcma_dinfo_alloc_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo) 270 { 271 bhnd_addr_t addr; 272 bhnd_size_t size; 273 rman_res_t r_start, r_count, r_end; 274 int error; 275 276 KASSERT(dinfo->res_agent == NULL, ("double allocation of agent")); 277 278 /* Verify that the agent register block exists and is 279 * mappable */ 280 if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1) 281 return (0); /* nothing to do */ 282 283 /* Fetch the address of the agent register block */ 284 error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0, 285 &addr, &size); 286 if (error) { 287 device_printf(bus, "failed fetching agent register block " 288 "address for core %u\n", BCMA_DINFO_COREIDX(dinfo)); 289 return (error); 290 } 291 292 /* Allocate the resource */ 293 r_start = addr; 294 r_count = size; 295 r_end = r_start + r_count - 1; 296 297 dinfo->rid_agent = BCMA_AGENT_RID(dinfo); 298 dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY, 299 &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE); 300 if (dinfo->res_agent == NULL) { 301 device_printf(bus, "failed allocating agent register block for " 302 "core %u\n", BCMA_DINFO_COREIDX(dinfo)); 303 return (ENXIO); 304 } 305 306 return (0); 307 } 308 309 310 /** 311 * Deallocate the given device info structure and any associated resources. 312 * 313 * @param bus The requesting bus device. 314 * @param dinfo Device info to be deallocated. 315 */ 316 void 317 bcma_free_dinfo(device_t bus, struct bcma_devinfo *dinfo) 318 { 319 resource_list_free(&dinfo->resources); 320 321 if (dinfo->corecfg != NULL) 322 bcma_free_corecfg(dinfo->corecfg); 323 324 /* Release agent resource, if any */ 325 if (dinfo->res_agent != NULL) { 326 bhnd_release_resource(bus, SYS_RES_MEMORY, dinfo->rid_agent, 327 dinfo->res_agent); 328 } 329 330 free(dinfo, M_BHND); 331 } 332 333 334 /** 335 * Allocate and initialize new slave port descriptor. 336 * 337 * @param port_num Per-core port number. 338 * @param port_type Port type. 339 */ 340 struct bcma_sport * 341 bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type) 342 { 343 struct bcma_sport *sport; 344 345 sport = malloc(sizeof(struct bcma_sport), M_BHND, M_NOWAIT); 346 if (sport == NULL) 347 return NULL; 348 349 sport->sp_num = port_num; 350 sport->sp_type = port_type; 351 sport->sp_num_maps = 0; 352 STAILQ_INIT(&sport->sp_maps); 353 354 return sport; 355 } 356 357 /** 358 * Deallocate all resources associated with the given port descriptor. 359 * 360 * @param sport Port descriptor to be deallocated. 361 */ 362 void 363 bcma_free_sport(struct bcma_sport *sport) { 364 struct bcma_map *map, *mapnext; 365 366 STAILQ_FOREACH_SAFE(map, &sport->sp_maps, m_link, mapnext) { 367 free(map, M_BHND); 368 } 369 370 free(sport, M_BHND); 371 } 372 373 374 /** 375 * Given a bcma(4) child's device info, spin waiting for the device's DMP 376 * resetstatus register to clear. 377 * 378 * @param child The bcma(4) child device. 379 * @param dinfo The @p child device info. 380 * 381 * @retval 0 success 382 * @retval ENODEV if @p dinfo does not map an agent register resource. 383 * @retval ETIMEDOUT if timeout occurs 384 */ 385 int 386 bcma_dmp_wait_reset(device_t child, struct bcma_devinfo *dinfo) 387 { 388 uint32_t rst; 389 390 if (dinfo->res_agent == NULL) 391 return (ENODEV); 392 393 /* 300us should be long enough, but there are references to this 394 * requiring up to 10ms when performing reset of an 80211 core 395 * after a MAC PSM microcode watchdog event. */ 396 for (int i = 0; i < 10000; i += 10) { 397 rst = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETSTATUS); 398 if (rst == 0) 399 return (0); 400 401 DELAY(10); 402 } 403 404 device_printf(child, "BCMA_DMP_RESETSTATUS timeout\n"); 405 return (ETIMEDOUT); 406 } 407 408 /** 409 * Set the bcma(4) child's DMP resetctrl register value, and then wait 410 * for all backplane operations to complete. 411 * 412 * @param child The bcma(4) child device. 413 * @param dinfo The @p child device info. 414 * @param value The new ioctrl value to set. 415 * 416 * @retval 0 success 417 * @retval ENODEV if @p dinfo does not map an agent register resource. 418 * @retval ETIMEDOUT if timeout occurs waiting for reset completion 419 */ 420 int 421 bcma_dmp_write_reset(device_t child, struct bcma_devinfo *dinfo, uint32_t value) 422 { 423 if (dinfo->res_agent == NULL) 424 return (ENODEV); 425 426 bhnd_bus_write_4(dinfo->res_agent, BCMA_DMP_RESETCTRL, value); 427 bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL); /* read-back */ 428 DELAY(10); 429 430 return (bcma_dmp_wait_reset(child, dinfo)); 431 } 432