2 * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/limits.h>
37 #include <sys/systm.h>
39 #include <machine/bus.h>
40 #include <machine/resource.h>
42 #include <dev/bhnd/bhndvar.h>
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))
53 * Allocate and initialize new core config structure.
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.
62 bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor,
63 uint16_t device, uint8_t hwrev)
65 struct bcma_corecfg *cfg;
67 cfg = malloc(sizeof(*cfg), M_BHND, M_NOWAIT);
71 cfg->core_info = (struct bhnd_core_info) {
75 .core_idx = core_index,
79 STAILQ_INIT(&cfg->master_ports);
80 cfg->num_master_ports = 0;
82 STAILQ_INIT(&cfg->dev_ports);
83 cfg->num_dev_ports = 0;
85 STAILQ_INIT(&cfg->bridge_ports);
86 cfg->num_bridge_ports = 0;
88 STAILQ_INIT(&cfg->wrapper_ports);
89 cfg->num_wrapper_ports = 0;
95 * Deallocate the given core config and any associated resources.
97 * @param corecfg Core info to be deallocated.
100 bcma_free_corecfg(struct bcma_corecfg *corecfg)
102 struct bcma_mport *mport, *mnext;
103 struct bcma_sport *sport, *snext;
105 STAILQ_FOREACH_SAFE(mport, &corecfg->master_ports, mp_link, mnext) {
109 STAILQ_FOREACH_SAFE(sport, &corecfg->dev_ports, sp_link, snext) {
110 bcma_free_sport(sport);
113 STAILQ_FOREACH_SAFE(sport, &corecfg->bridge_ports, sp_link, snext) {
114 bcma_free_sport(sport);
117 STAILQ_FOREACH_SAFE(sport, &corecfg->wrapper_ports, sp_link, snext) {
118 bcma_free_sport(sport);
121 free(corecfg, M_BHND);
125 * Return the @p cfg port list for @p type.
127 * @param cfg The core configuration.
128 * @param type The requested port type.
130 struct bcma_sport_list *
131 bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type)
134 case BHND_PORT_DEVICE:
135 return (&cfg->dev_ports);
137 case BHND_PORT_BRIDGE:
138 return (&cfg->bridge_ports);
140 case BHND_PORT_AGENT:
141 return (&cfg->wrapper_ports);
149 * Populate the resource list and bcma_map RIDs using the maps defined on
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
157 bcma_dinfo_init_resource_info(device_t bus, struct bcma_devinfo *dinfo,
158 struct bcma_sport_list *ports)
160 struct bcma_map *map;
161 struct bcma_sport *port;
164 STAILQ_FOREACH(port, ports, sp_link) {
165 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
167 * Create the corresponding device resource list entry.
169 * We necessarily skip registration if the region's
170 * device memory range is not representable via
173 * When rman_res_t is migrated to uintmax_t, any
174 * range should be representable.
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) {
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);
197 * Allocate and return a new empty device info structure.
199 * @param bus The requesting bus device.
201 * @retval NULL if allocation failed.
203 struct bcma_devinfo *
204 bcma_alloc_dinfo(device_t bus)
206 struct bcma_devinfo *dinfo;
208 dinfo = malloc(sizeof(struct bcma_devinfo), M_BHND, M_NOWAIT|M_ZERO);
212 dinfo->corecfg = NULL;
213 dinfo->res_agent = NULL;
214 dinfo->rid_agent = -1;
216 resource_list_init(&dinfo->resources);
222 * Initialize a device info structure previously allocated via
223 * bcma_alloc_dinfo, assuming ownership of the provided core
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.
232 * @retval non-zero initialization failed.
235 bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo,
236 struct bcma_corecfg *corecfg)
238 KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized"));
240 /* Save core configuration value */
241 dinfo->corecfg = corecfg;
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);
247 bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->bridge_ports);
248 bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->wrapper_ports);
255 * Allocate the per-core agent register block for a device info structure
256 * previous initialized via bcma_init_dinfo().
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.
261 * @param bus The requesting bus device.
262 * @param child The bcma child device.
263 * @param dinfo The device info associated with @p child
266 * @retval non-zero resource allocation failed.
269 bcma_dinfo_alloc_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo)
273 rman_res_t r_start, r_count, r_end;
276 KASSERT(dinfo->res_agent == NULL, ("double allocation of agent"));
278 /* Verify that the agent register block exists and is
280 if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
281 return (0); /* nothing to do */
283 /* Fetch the address of the agent register block */
284 error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
287 device_printf(bus, "failed fetching agent register block "
288 "address for core %u\n", BCMA_DINFO_COREIDX(dinfo));
292 /* Allocate the resource */
295 r_end = r_start + r_count - 1;
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));
311 * Deallocate the given device info structure and any associated resources.
313 * @param bus The requesting bus device.
314 * @param dinfo Device info to be deallocated.
317 bcma_free_dinfo(device_t bus, struct bcma_devinfo *dinfo)
319 resource_list_free(&dinfo->resources);
321 if (dinfo->corecfg != NULL)
322 bcma_free_corecfg(dinfo->corecfg);
324 /* Release agent resource, if any */
325 if (dinfo->res_agent != NULL) {
326 bhnd_release_resource(bus, SYS_RES_MEMORY, dinfo->rid_agent,
335 * Allocate and initialize new slave port descriptor.
337 * @param port_num Per-core port number.
338 * @param port_type Port type.
341 bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type)
343 struct bcma_sport *sport;
345 sport = malloc(sizeof(struct bcma_sport), M_BHND, M_NOWAIT);
349 sport->sp_num = port_num;
350 sport->sp_type = port_type;
351 sport->sp_num_maps = 0;
352 STAILQ_INIT(&sport->sp_maps);
358 * Deallocate all resources associated with the given port descriptor.
360 * @param sport Port descriptor to be deallocated.
363 bcma_free_sport(struct bcma_sport *sport) {
364 struct bcma_map *map, *mapnext;
366 STAILQ_FOREACH_SAFE(map, &sport->sp_maps, m_link, mapnext) {
375 * Given a bcma(4) child's device info, spin waiting for the device's DMP
376 * resetstatus register to clear.
378 * @param child The bcma(4) child device.
379 * @param dinfo The @p child device info.
382 * @retval ENODEV if @p dinfo does not map an agent register resource.
383 * @retval ETIMEDOUT if timeout occurs
386 bcma_dmp_wait_reset(device_t child, struct bcma_devinfo *dinfo)
390 if (dinfo->res_agent == NULL)
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);
404 device_printf(child, "BCMA_DMP_RESETSTATUS timeout\n");
409 * Set the bcma(4) child's DMP resetctrl register value, and then wait
410 * for all backplane operations to complete.
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.
417 * @retval ENODEV if @p dinfo does not map an agent register resource.
418 * @retval ETIMEDOUT if timeout occurs waiting for reset completion
421 bcma_dmp_write_reset(device_t child, struct bcma_devinfo *dinfo, uint32_t value)
423 if (dinfo->res_agent == NULL)
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 */
430 return (bcma_dmp_wait_reset(child, dinfo));