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 "bcmavar.h" 45 46 /* Return the resource ID for a device's agent register allocation */ 47 #define BCMA_AGENT_RID(_dinfo) \ 48 (BCMA_AGENT_RID_BASE + BCMA_DINFO_COREIDX(_dinfo)) 49 50 /** 51 * Allocate and initialize new core config structure. 52 * 53 * @param core_index Core index on the bus. 54 * @param core_unit Core unit number. 55 * @param vendor Core designer. 56 * @param device Core identifier (e.g. part number). 57 * @param hwrev Core revision. 58 */ 59 struct bcma_corecfg * 60 bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor, 61 uint16_t device, uint8_t hwrev) 62 { 63 struct bcma_corecfg *cfg; 64 65 cfg = malloc(sizeof(*cfg), M_BHND, M_NOWAIT); 66 if (cfg == NULL) 67 return NULL; 68 69 cfg->core_info = (struct bhnd_core_info) { 70 .vendor = vendor, 71 .device = device, 72 .hwrev = hwrev, 73 .core_idx = core_index, 74 .unit = core_unit 75 }; 76 77 STAILQ_INIT(&cfg->master_ports); 78 cfg->num_master_ports = 0; 79 80 STAILQ_INIT(&cfg->dev_ports); 81 cfg->num_dev_ports = 0; 82 83 STAILQ_INIT(&cfg->bridge_ports); 84 cfg->num_bridge_ports = 0; 85 86 STAILQ_INIT(&cfg->wrapper_ports); 87 cfg->num_wrapper_ports = 0; 88 89 return (cfg); 90 } 91 92 /** 93 * Deallocate the given core config and any associated resources. 94 * 95 * @param corecfg Core info to be deallocated. 96 */ 97 void 98 bcma_free_corecfg(struct bcma_corecfg *corecfg) 99 { 100 struct bcma_mport *mport, *mnext; 101 struct bcma_sport *sport, *snext; 102 103 STAILQ_FOREACH_SAFE(mport, &corecfg->master_ports, mp_link, mnext) { 104 free(mport, M_BHND); 105 } 106 107 STAILQ_FOREACH_SAFE(sport, &corecfg->dev_ports, sp_link, snext) { 108 bcma_free_sport(sport); 109 } 110 111 STAILQ_FOREACH_SAFE(sport, &corecfg->bridge_ports, sp_link, snext) { 112 bcma_free_sport(sport); 113 } 114 115 STAILQ_FOREACH_SAFE(sport, &corecfg->wrapper_ports, sp_link, snext) { 116 bcma_free_sport(sport); 117 } 118 119 free(corecfg, M_BHND); 120 } 121 122 /** 123 * Return the @p cfg port list for @p type. 124 * 125 * @param cfg The core configuration. 126 * @param type The requested port type. 127 */ 128 struct bcma_sport_list * 129 bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type) 130 { 131 switch (type) { 132 case BHND_PORT_DEVICE: 133 return (&cfg->dev_ports); 134 break; 135 case BHND_PORT_BRIDGE: 136 return (&cfg->bridge_ports); 137 break; 138 case BHND_PORT_AGENT: 139 return (&cfg->wrapper_ports); 140 break; 141 default: 142 return (NULL); 143 } 144 } 145 146 /** 147 * Populate the resource list and bcma_map RIDs using the maps defined on 148 * @p ports. 149 * 150 * @param bus The requesting bus device. 151 * @param dinfo The device info instance to be initialized. 152 * @param ports The set of ports to be enumerated 153 */ 154 static void 155 bcma_dinfo_init_resource_info(device_t bus, struct bcma_devinfo *dinfo, 156 struct bcma_sport_list *ports) 157 { 158 struct bcma_map *map; 159 struct bcma_sport *port; 160 bhnd_addr_t end; 161 162 STAILQ_FOREACH(port, ports, sp_link) { 163 STAILQ_FOREACH(map, &port->sp_maps, m_link) { 164 /* 165 * Create the corresponding device resource list entry. 166 * 167 * We necessarily skip registration if the region's 168 * device memory range is not representable via 169 * rman_res_t. 170 * 171 * When rman_res_t is migrated to uintmax_t, any 172 * range should be representable. 173 */ 174 end = map->m_base + map->m_size; 175 if (map->m_base <= RM_MAX_END && end <= RM_MAX_END) { 176 map->m_rid = resource_list_add_next( 177 &dinfo->resources, SYS_RES_MEMORY, 178 map->m_base, end, map->m_size); 179 } else if (bootverbose) { 180 device_printf(bus, 181 "core%u %s%u.%u: region %llx-%llx extends " 182 "beyond supported addressable range\n", 183 dinfo->corecfg->core_info.core_idx, 184 bhnd_port_type_name(port->sp_type), 185 port->sp_num, map->m_region_num, 186 (unsigned long long) map->m_base, 187 (unsigned long long) end); 188 } 189 } 190 } 191 } 192 193 194 /** 195 * Allocate and return a new empty device info structure. 196 * 197 * @param bus The requesting bus device. 198 * 199 * @retval NULL if allocation failed. 200 */ 201 struct bcma_devinfo * 202 bcma_alloc_dinfo(device_t bus) 203 { 204 struct bcma_devinfo *dinfo; 205 206 dinfo = malloc(sizeof(struct bcma_devinfo), M_BHND, M_NOWAIT|M_ZERO); 207 if (dinfo == NULL) 208 return (NULL); 209 210 dinfo->corecfg = NULL; 211 dinfo->res_agent = NULL; 212 dinfo->rid_agent = -1; 213 214 resource_list_init(&dinfo->resources); 215 216 return (dinfo); 217 } 218 219 /** 220 * Initialize a device info structure previously allocated via 221 * bcma_alloc_dinfo, assuming ownership of the provided core 222 * configuration. 223 * 224 * @param bus The requesting bus device. 225 * @param dinfo The device info instance. 226 * @param corecfg Device core configuration; ownership of this value 227 * will be assumed by @p dinfo. 228 * 229 * @retval 0 success 230 * @retval non-zero initialization failed. 231 */ 232 int 233 bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo, 234 struct bcma_corecfg *corecfg) 235 { 236 KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized")); 237 238 /* Save core configuration value */ 239 dinfo->corecfg = corecfg; 240 241 /* The device ports must always be initialized first to ensure that 242 * rid 0 maps to the first device port */ 243 bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->dev_ports); 244 245 bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->bridge_ports); 246 bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->wrapper_ports); 247 248 return (0); 249 } 250 251 252 /** 253 * Allocate the per-core agent register block for a device info structure 254 * previous initialized via bcma_init_dinfo(). 255 * 256 * If an agent0.0 region is not defined on @p dinfo, the device info 257 * agent resource is set to NULL and 0 is returned. 258 * 259 * @param bus The requesting bus device. 260 * @param child The bcma child device. 261 * @param dinfo The device info associated with @p child 262 * 263 * @retval 0 success 264 * @retval non-zero resource allocation failed. 265 */ 266 int 267 bcma_dinfo_alloc_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo) 268 { 269 bhnd_addr_t addr; 270 bhnd_size_t size; 271 rman_res_t r_start, r_count, r_end; 272 int error; 273 274 KASSERT(dinfo->res_agent == NULL, ("double allocation of agent")); 275 276 /* Verify that the agent register block exists and is 277 * mappable */ 278 if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1) 279 return (0); /* nothing to do */ 280 281 /* Fetch the address of the agent register block */ 282 error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0, 283 &addr, &size); 284 if (error) { 285 device_printf(bus, "failed fetching agent register block " 286 "address for core %u\n", BCMA_DINFO_COREIDX(dinfo)); 287 return (error); 288 } 289 290 /* Allocate the resource */ 291 r_start = addr; 292 r_count = size; 293 r_end = r_start + r_count - 1; 294 295 dinfo->rid_agent = BCMA_AGENT_RID(dinfo); 296 dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY, 297 &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE); 298 if (dinfo->res_agent == NULL) { 299 device_printf(bus, "failed allocating agent register block for " 300 "core %u\n", BCMA_DINFO_COREIDX(dinfo)); 301 return (ENXIO); 302 } 303 304 return (0); 305 } 306 307 308 /** 309 * Deallocate the given device info structure and any associated resources. 310 * 311 * @param bus The requesting bus device. 312 * @param dinfo Device info to be deallocated. 313 */ 314 void 315 bcma_free_dinfo(device_t bus, struct bcma_devinfo *dinfo) 316 { 317 resource_list_free(&dinfo->resources); 318 319 if (dinfo->corecfg != NULL) 320 bcma_free_corecfg(dinfo->corecfg); 321 322 /* Release agent resource, if any */ 323 if (dinfo->res_agent != NULL) { 324 bhnd_release_resource(bus, SYS_RES_MEMORY, dinfo->rid_agent, 325 dinfo->res_agent); 326 } 327 328 free(dinfo, M_BHND); 329 } 330 331 332 /** 333 * Allocate and initialize new slave port descriptor. 334 * 335 * @param port_num Per-core port number. 336 * @param port_type Port type. 337 */ 338 struct bcma_sport * 339 bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type) 340 { 341 struct bcma_sport *sport; 342 343 sport = malloc(sizeof(struct bcma_sport), M_BHND, M_NOWAIT); 344 if (sport == NULL) 345 return NULL; 346 347 sport->sp_num = port_num; 348 sport->sp_type = port_type; 349 sport->sp_num_maps = 0; 350 STAILQ_INIT(&sport->sp_maps); 351 352 return sport; 353 } 354 355 /** 356 * Deallocate all resources associated with the given port descriptor. 357 * 358 * @param sport Port descriptor to be deallocated. 359 */ 360 void 361 bcma_free_sport(struct bcma_sport *sport) { 362 struct bcma_map *map, *mapnext; 363 364 STAILQ_FOREACH_SAFE(map, &sport->sp_maps, m_link, mapnext) { 365 free(map, M_BHND); 366 } 367 368 free(sport, M_BHND); 369 } 370 371