Deleted Added
full compact
bcma.c (299314) bcma.c (301410)
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>
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: head/sys/dev/bhnd/bcma/bcma.c 299314 2016-05-10 04:55:57Z adrian $");
31__FBSDID("$FreeBSD: head/sys/dev/bhnd/bcma/bcma.c 301410 2016-06-04 19:53:47Z landonf $");
32
33#include <sys/param.h>
34#include <sys/bus.h>
35#include <sys/kernel.h>
36#include <sys/malloc.h>
37#include <sys/module.h>
38#include <sys/systm.h>
39
40#include <machine/bus.h>
41
42#include "bcmavar.h"
43
44#include "bcma_eromreg.h"
45#include "bcma_eromvar.h"
46
47int
48bcma_probe(device_t dev)
49{
50 device_set_desc(dev, "BCMA BHND bus");
51 return (BUS_PROBE_DEFAULT);
52}
53
54int
55bcma_attach(device_t dev)
56{
57 struct bcma_devinfo *dinfo;
58 device_t *devs, child;
59 int ndevs;
60 int error;
61
62
63 if ((error = device_get_children(dev, &devs, &ndevs)))
64 return (error);
65
66 /*
67 * Map our children's agent register block.
68 */
69 for (int i = 0; i < ndevs; i++) {
70 bhnd_addr_t addr;
71 bhnd_size_t size;
72 rman_res_t r_start, r_count, r_end;
73
74 child = devs[i];
75 dinfo = device_get_ivars(child);
76
77 KASSERT(!device_is_suspended(child),
78 ("bcma(4) stateful suspend handling requires that devices "
79 "not be suspended before bcma_attach()"));
80
81 /* Verify that the agent register block exists and is
82 * mappable */
83 if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
84 continue;
85
86 /* Fetch the address of the agent register block */
87 error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
88 &addr, &size);
89 if (error) {
90 device_printf(dev, "failed fetching agent register "
91 "block address for core %d\n", i);
92 goto cleanup;
93 }
94
95 /* Allocate the resource */
96 r_start = addr;
97 r_count = size;
98 r_end = r_start + r_count - 1;
99
100 dinfo->rid_agent = i + 1;
32
33#include <sys/param.h>
34#include <sys/bus.h>
35#include <sys/kernel.h>
36#include <sys/malloc.h>
37#include <sys/module.h>
38#include <sys/systm.h>
39
40#include <machine/bus.h>
41
42#include "bcmavar.h"
43
44#include "bcma_eromreg.h"
45#include "bcma_eromvar.h"
46
47int
48bcma_probe(device_t dev)
49{
50 device_set_desc(dev, "BCMA BHND bus");
51 return (BUS_PROBE_DEFAULT);
52}
53
54int
55bcma_attach(device_t dev)
56{
57 struct bcma_devinfo *dinfo;
58 device_t *devs, child;
59 int ndevs;
60 int error;
61
62
63 if ((error = device_get_children(dev, &devs, &ndevs)))
64 return (error);
65
66 /*
67 * Map our children's agent register block.
68 */
69 for (int i = 0; i < ndevs; i++) {
70 bhnd_addr_t addr;
71 bhnd_size_t size;
72 rman_res_t r_start, r_count, r_end;
73
74 child = devs[i];
75 dinfo = device_get_ivars(child);
76
77 KASSERT(!device_is_suspended(child),
78 ("bcma(4) stateful suspend handling requires that devices "
79 "not be suspended before bcma_attach()"));
80
81 /* Verify that the agent register block exists and is
82 * mappable */
83 if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
84 continue;
85
86 /* Fetch the address of the agent register block */
87 error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
88 &addr, &size);
89 if (error) {
90 device_printf(dev, "failed fetching agent register "
91 "block address for core %d\n", i);
92 goto cleanup;
93 }
94
95 /* Allocate the resource */
96 r_start = addr;
97 r_count = size;
98 r_end = r_start + r_count - 1;
99
100 dinfo->rid_agent = i + 1;
101 dinfo->res_agent = bhnd_alloc_resource(dev, SYS_RES_MEMORY,
102 &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE);
101 dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(dev, dev,
102 SYS_RES_MEMORY, &dinfo->rid_agent, r_start, r_end, r_count,
103 RF_ACTIVE);
103 if (dinfo->res_agent == NULL) {
104 device_printf(dev, "failed allocating agent register "
105 "block for core %d\n", i);
106 error = ENXIO;
107 goto cleanup;
108 }
109 }
110
111cleanup:
112 free(devs, M_BHND);
113 if (error)
114 return (error);
115
116 return (bhnd_generic_attach(dev));
117}
118
119int
120bcma_detach(device_t dev)
121{
122 return (bhnd_generic_detach(dev));
123}
124
125static int
126bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
127{
128 const struct bcma_devinfo *dinfo;
129 const struct bhnd_core_info *ci;
130
131 dinfo = device_get_ivars(child);
132 ci = &dinfo->corecfg->core_info;
133
134 switch (index) {
135 case BHND_IVAR_VENDOR:
136 *result = ci->vendor;
137 return (0);
138 case BHND_IVAR_DEVICE:
139 *result = ci->device;
140 return (0);
141 case BHND_IVAR_HWREV:
142 *result = ci->hwrev;
143 return (0);
144 case BHND_IVAR_DEVICE_CLASS:
145 *result = bhnd_core_class(ci);
146 return (0);
147 case BHND_IVAR_VENDOR_NAME:
148 *result = (uintptr_t) bhnd_vendor_name(ci->vendor);
149 return (0);
150 case BHND_IVAR_DEVICE_NAME:
151 *result = (uintptr_t) bhnd_core_name(ci);
152 return (0);
153 case BHND_IVAR_CORE_INDEX:
154 *result = ci->core_idx;
155 return (0);
156 case BHND_IVAR_CORE_UNIT:
157 *result = ci->unit;
158 return (0);
159 default:
160 return (ENOENT);
161 }
162}
163
164static int
165bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
166{
167 switch (index) {
168 case BHND_IVAR_VENDOR:
169 case BHND_IVAR_DEVICE:
170 case BHND_IVAR_HWREV:
171 case BHND_IVAR_DEVICE_CLASS:
172 case BHND_IVAR_VENDOR_NAME:
173 case BHND_IVAR_DEVICE_NAME:
174 case BHND_IVAR_CORE_INDEX:
175 case BHND_IVAR_CORE_UNIT:
176 return (EINVAL);
177 default:
178 return (ENOENT);
179 }
180}
181
182static void
183bcma_child_deleted(device_t dev, device_t child)
184{
185 struct bcma_devinfo *dinfo = device_get_ivars(child);
186 if (dinfo != NULL)
187 bcma_free_dinfo(dev, dinfo);
188}
189
190static struct resource_list *
191bcma_get_resource_list(device_t dev, device_t child)
192{
193 struct bcma_devinfo *dinfo = device_get_ivars(child);
194 return (&dinfo->resources);
195}
196
197static device_t
198bcma_find_hostb_device(device_t dev)
199{
200 struct bcma_softc *sc = device_get_softc(dev);
201
202 /* This is set (or not) by the concrete bcma driver subclass. */
203 return (sc->hostb_dev);
204}
205
206static int
207bcma_reset_core(device_t dev, device_t child, uint16_t flags)
208{
209 struct bcma_devinfo *dinfo;
210
211 if (device_get_parent(child) != dev)
212 BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
213
214 dinfo = device_get_ivars(child);
215
216 /* Can't reset the core without access to the agent registers */
217 if (dinfo->res_agent == NULL)
218 return (ENODEV);
219
220 // TODO - perform reset
221
222 return (ENXIO);
223}
224
225static int
226bcma_suspend_core(device_t dev, device_t child)
227{
228 struct bcma_devinfo *dinfo;
229
230 if (device_get_parent(child) != dev)
231 BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
232
233 dinfo = device_get_ivars(child);
234
235 /* Can't suspend the core without access to the agent registers */
236 if (dinfo->res_agent == NULL)
237 return (ENODEV);
238
239 // TODO - perform suspend
240
241 return (ENXIO);
242}
243
244static u_int
245bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
246{
247 struct bcma_devinfo *dinfo;
248
249 /* delegate non-bus-attached devices to our parent */
250 if (device_get_parent(child) != dev)
251 return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
252 type));
253
254 dinfo = device_get_ivars(child);
255 switch (type) {
256 case BHND_PORT_DEVICE:
257 return (dinfo->corecfg->num_dev_ports);
258 case BHND_PORT_BRIDGE:
259 return (dinfo->corecfg->num_bridge_ports);
260 case BHND_PORT_AGENT:
261 return (dinfo->corecfg->num_wrapper_ports);
262 default:
263 device_printf(dev, "%s: unknown type (%d)\n",
264 __func__,
265 type);
266 return (0);
267 }
268}
269
270static u_int
271bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
272 u_int port_num)
273{
274 struct bcma_devinfo *dinfo;
275 struct bcma_sport_list *ports;
276 struct bcma_sport *port;
277
278 /* delegate non-bus-attached devices to our parent */
279 if (device_get_parent(child) != dev)
280 return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
281 type, port_num));
282
283 dinfo = device_get_ivars(child);
284 ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
285
286 STAILQ_FOREACH(port, ports, sp_link) {
287 if (port->sp_num == port_num)
288 return (port->sp_num_maps);
289 }
290
291 /* not found */
292 return (0);
293}
294
295static int
296bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
297 u_int port_num, u_int region_num)
298{
299 struct bcma_devinfo *dinfo;
300 struct bcma_map *map;
301 struct bcma_sport_list *ports;
302 struct bcma_sport *port;
303
304 dinfo = device_get_ivars(child);
305 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
306
307 STAILQ_FOREACH(port, ports, sp_link) {
308 if (port->sp_num != port_num)
309 continue;
310
311 STAILQ_FOREACH(map, &port->sp_maps, m_link)
312 if (map->m_region_num == region_num)
313 return map->m_rid;
314 }
315
316 return -1;
317}
318
319static int
320bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
321 bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
322{
323 struct bcma_devinfo *dinfo;
324 struct bcma_map *map;
325 struct bcma_sport_list *ports;
326 struct bcma_sport *port;
327
328 dinfo = device_get_ivars(child);
329
330 /* Ports are always memory mapped */
331 if (type != SYS_RES_MEMORY)
332 return (EINVAL);
333
334 /* Starting with the most likely device list, search all three port
335 * lists */
336 bhnd_port_type types[] = {
337 BHND_PORT_DEVICE,
338 BHND_PORT_AGENT,
339 BHND_PORT_BRIDGE
340 };
341
342 for (int i = 0; i < nitems(types); i++) {
343 ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
344
345 STAILQ_FOREACH(port, ports, sp_link) {
346 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
347 if (map->m_rid != rid)
348 continue;
349
350 *port_type = port->sp_type;
351 *port_num = port->sp_num;
352 *region_num = map->m_region_num;
353 return (0);
354 }
355 }
356 }
357
358 return (ENOENT);
359}
360
361static int
362bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
363 u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
364{
365 struct bcma_devinfo *dinfo;
366 struct bcma_map *map;
367 struct bcma_sport_list *ports;
368 struct bcma_sport *port;
369
370 dinfo = device_get_ivars(child);
371 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
372
373 /* Search the port list */
374 STAILQ_FOREACH(port, ports, sp_link) {
375 if (port->sp_num != port_num)
376 continue;
377
378 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
379 if (map->m_region_num != region_num)
380 continue;
381
382 /* Found! */
383 *addr = map->m_base;
384 *size = map->m_size;
385 return (0);
386 }
387 }
388
389 return (ENOENT);
390}
391
392/**
393 * Scan a device enumeration ROM table, adding all valid discovered cores to
394 * the bus.
395 *
396 * @param bus The bcma bus.
397 * @param erom_res An active resource mapping the EROM core.
398 * @param erom_offset Base offset of the EROM core's register mapping.
399 */
400int
401bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset)
402{
403 struct bcma_erom erom;
404 struct bcma_corecfg *corecfg;
405 struct bcma_devinfo *dinfo;
406 device_t child;
407 int error;
408
409 dinfo = NULL;
410 corecfg = NULL;
411
412 /* Initialize our reader */
413 error = bcma_erom_open(&erom, erom_res, erom_offset);
414 if (error)
415 return (error);
416
417 /* Add all cores. */
418 while (!error) {
419 /* Parse next core */
420 error = bcma_erom_parse_corecfg(&erom, &corecfg);
421 if (error && error == ENOENT) {
422 return (0);
423 } else if (error) {
424 goto failed;
425 }
426
427 /* Allocate per-device bus info */
428 dinfo = bcma_alloc_dinfo(bus, corecfg);
429 if (dinfo == NULL) {
430 error = ENXIO;
431 goto failed;
432 }
433
434 /* The dinfo instance now owns the corecfg value */
435 corecfg = NULL;
436
437 /* Add the child device */
438 child = device_add_child(bus, NULL, -1);
439 if (child == NULL) {
440 error = ENXIO;
441 goto failed;
442 }
443
444 /* The child device now owns the dinfo pointer */
445 device_set_ivars(child, dinfo);
446 dinfo = NULL;
447
448 /* If pins are floating or the hardware is otherwise
449 * unpopulated, the device shouldn't be used. */
450 if (bhnd_is_hw_disabled(child))
451 device_disable(child);
452 }
453
454 /* Hit EOF parsing cores? */
455 if (error == ENOENT)
456 return (0);
457
458failed:
459 if (dinfo != NULL)
460 bcma_free_dinfo(bus, dinfo);
461
462 if (corecfg != NULL)
463 bcma_free_corecfg(corecfg);
464
465 return (error);
466}
467
468
469static device_method_t bcma_methods[] = {
470 /* Device interface */
471 DEVMETHOD(device_probe, bcma_probe),
472 DEVMETHOD(device_attach, bcma_attach),
473 DEVMETHOD(device_detach, bcma_detach),
474
475 /* Bus interface */
476 DEVMETHOD(bus_child_deleted, bcma_child_deleted),
477 DEVMETHOD(bus_read_ivar, bcma_read_ivar),
478 DEVMETHOD(bus_write_ivar, bcma_write_ivar),
479 DEVMETHOD(bus_get_resource_list, bcma_get_resource_list),
480
481 /* BHND interface */
482 DEVMETHOD(bhnd_bus_find_hostb_device, bcma_find_hostb_device),
483 DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core),
484 DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core),
485 DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count),
486 DEVMETHOD(bhnd_bus_get_region_count, bcma_get_region_count),
487 DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid),
488 DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid),
489 DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr),
490
491 DEVMETHOD_END
492};
493
494DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
495MODULE_VERSION(bcma, 1);
496MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
104 if (dinfo->res_agent == NULL) {
105 device_printf(dev, "failed allocating agent register "
106 "block for core %d\n", i);
107 error = ENXIO;
108 goto cleanup;
109 }
110 }
111
112cleanup:
113 free(devs, M_BHND);
114 if (error)
115 return (error);
116
117 return (bhnd_generic_attach(dev));
118}
119
120int
121bcma_detach(device_t dev)
122{
123 return (bhnd_generic_detach(dev));
124}
125
126static int
127bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
128{
129 const struct bcma_devinfo *dinfo;
130 const struct bhnd_core_info *ci;
131
132 dinfo = device_get_ivars(child);
133 ci = &dinfo->corecfg->core_info;
134
135 switch (index) {
136 case BHND_IVAR_VENDOR:
137 *result = ci->vendor;
138 return (0);
139 case BHND_IVAR_DEVICE:
140 *result = ci->device;
141 return (0);
142 case BHND_IVAR_HWREV:
143 *result = ci->hwrev;
144 return (0);
145 case BHND_IVAR_DEVICE_CLASS:
146 *result = bhnd_core_class(ci);
147 return (0);
148 case BHND_IVAR_VENDOR_NAME:
149 *result = (uintptr_t) bhnd_vendor_name(ci->vendor);
150 return (0);
151 case BHND_IVAR_DEVICE_NAME:
152 *result = (uintptr_t) bhnd_core_name(ci);
153 return (0);
154 case BHND_IVAR_CORE_INDEX:
155 *result = ci->core_idx;
156 return (0);
157 case BHND_IVAR_CORE_UNIT:
158 *result = ci->unit;
159 return (0);
160 default:
161 return (ENOENT);
162 }
163}
164
165static int
166bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
167{
168 switch (index) {
169 case BHND_IVAR_VENDOR:
170 case BHND_IVAR_DEVICE:
171 case BHND_IVAR_HWREV:
172 case BHND_IVAR_DEVICE_CLASS:
173 case BHND_IVAR_VENDOR_NAME:
174 case BHND_IVAR_DEVICE_NAME:
175 case BHND_IVAR_CORE_INDEX:
176 case BHND_IVAR_CORE_UNIT:
177 return (EINVAL);
178 default:
179 return (ENOENT);
180 }
181}
182
183static void
184bcma_child_deleted(device_t dev, device_t child)
185{
186 struct bcma_devinfo *dinfo = device_get_ivars(child);
187 if (dinfo != NULL)
188 bcma_free_dinfo(dev, dinfo);
189}
190
191static struct resource_list *
192bcma_get_resource_list(device_t dev, device_t child)
193{
194 struct bcma_devinfo *dinfo = device_get_ivars(child);
195 return (&dinfo->resources);
196}
197
198static device_t
199bcma_find_hostb_device(device_t dev)
200{
201 struct bcma_softc *sc = device_get_softc(dev);
202
203 /* This is set (or not) by the concrete bcma driver subclass. */
204 return (sc->hostb_dev);
205}
206
207static int
208bcma_reset_core(device_t dev, device_t child, uint16_t flags)
209{
210 struct bcma_devinfo *dinfo;
211
212 if (device_get_parent(child) != dev)
213 BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
214
215 dinfo = device_get_ivars(child);
216
217 /* Can't reset the core without access to the agent registers */
218 if (dinfo->res_agent == NULL)
219 return (ENODEV);
220
221 // TODO - perform reset
222
223 return (ENXIO);
224}
225
226static int
227bcma_suspend_core(device_t dev, device_t child)
228{
229 struct bcma_devinfo *dinfo;
230
231 if (device_get_parent(child) != dev)
232 BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
233
234 dinfo = device_get_ivars(child);
235
236 /* Can't suspend the core without access to the agent registers */
237 if (dinfo->res_agent == NULL)
238 return (ENODEV);
239
240 // TODO - perform suspend
241
242 return (ENXIO);
243}
244
245static u_int
246bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
247{
248 struct bcma_devinfo *dinfo;
249
250 /* delegate non-bus-attached devices to our parent */
251 if (device_get_parent(child) != dev)
252 return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
253 type));
254
255 dinfo = device_get_ivars(child);
256 switch (type) {
257 case BHND_PORT_DEVICE:
258 return (dinfo->corecfg->num_dev_ports);
259 case BHND_PORT_BRIDGE:
260 return (dinfo->corecfg->num_bridge_ports);
261 case BHND_PORT_AGENT:
262 return (dinfo->corecfg->num_wrapper_ports);
263 default:
264 device_printf(dev, "%s: unknown type (%d)\n",
265 __func__,
266 type);
267 return (0);
268 }
269}
270
271static u_int
272bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
273 u_int port_num)
274{
275 struct bcma_devinfo *dinfo;
276 struct bcma_sport_list *ports;
277 struct bcma_sport *port;
278
279 /* delegate non-bus-attached devices to our parent */
280 if (device_get_parent(child) != dev)
281 return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
282 type, port_num));
283
284 dinfo = device_get_ivars(child);
285 ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
286
287 STAILQ_FOREACH(port, ports, sp_link) {
288 if (port->sp_num == port_num)
289 return (port->sp_num_maps);
290 }
291
292 /* not found */
293 return (0);
294}
295
296static int
297bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
298 u_int port_num, u_int region_num)
299{
300 struct bcma_devinfo *dinfo;
301 struct bcma_map *map;
302 struct bcma_sport_list *ports;
303 struct bcma_sport *port;
304
305 dinfo = device_get_ivars(child);
306 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
307
308 STAILQ_FOREACH(port, ports, sp_link) {
309 if (port->sp_num != port_num)
310 continue;
311
312 STAILQ_FOREACH(map, &port->sp_maps, m_link)
313 if (map->m_region_num == region_num)
314 return map->m_rid;
315 }
316
317 return -1;
318}
319
320static int
321bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
322 bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
323{
324 struct bcma_devinfo *dinfo;
325 struct bcma_map *map;
326 struct bcma_sport_list *ports;
327 struct bcma_sport *port;
328
329 dinfo = device_get_ivars(child);
330
331 /* Ports are always memory mapped */
332 if (type != SYS_RES_MEMORY)
333 return (EINVAL);
334
335 /* Starting with the most likely device list, search all three port
336 * lists */
337 bhnd_port_type types[] = {
338 BHND_PORT_DEVICE,
339 BHND_PORT_AGENT,
340 BHND_PORT_BRIDGE
341 };
342
343 for (int i = 0; i < nitems(types); i++) {
344 ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
345
346 STAILQ_FOREACH(port, ports, sp_link) {
347 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
348 if (map->m_rid != rid)
349 continue;
350
351 *port_type = port->sp_type;
352 *port_num = port->sp_num;
353 *region_num = map->m_region_num;
354 return (0);
355 }
356 }
357 }
358
359 return (ENOENT);
360}
361
362static int
363bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
364 u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
365{
366 struct bcma_devinfo *dinfo;
367 struct bcma_map *map;
368 struct bcma_sport_list *ports;
369 struct bcma_sport *port;
370
371 dinfo = device_get_ivars(child);
372 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
373
374 /* Search the port list */
375 STAILQ_FOREACH(port, ports, sp_link) {
376 if (port->sp_num != port_num)
377 continue;
378
379 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
380 if (map->m_region_num != region_num)
381 continue;
382
383 /* Found! */
384 *addr = map->m_base;
385 *size = map->m_size;
386 return (0);
387 }
388 }
389
390 return (ENOENT);
391}
392
393/**
394 * Scan a device enumeration ROM table, adding all valid discovered cores to
395 * the bus.
396 *
397 * @param bus The bcma bus.
398 * @param erom_res An active resource mapping the EROM core.
399 * @param erom_offset Base offset of the EROM core's register mapping.
400 */
401int
402bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset)
403{
404 struct bcma_erom erom;
405 struct bcma_corecfg *corecfg;
406 struct bcma_devinfo *dinfo;
407 device_t child;
408 int error;
409
410 dinfo = NULL;
411 corecfg = NULL;
412
413 /* Initialize our reader */
414 error = bcma_erom_open(&erom, erom_res, erom_offset);
415 if (error)
416 return (error);
417
418 /* Add all cores. */
419 while (!error) {
420 /* Parse next core */
421 error = bcma_erom_parse_corecfg(&erom, &corecfg);
422 if (error && error == ENOENT) {
423 return (0);
424 } else if (error) {
425 goto failed;
426 }
427
428 /* Allocate per-device bus info */
429 dinfo = bcma_alloc_dinfo(bus, corecfg);
430 if (dinfo == NULL) {
431 error = ENXIO;
432 goto failed;
433 }
434
435 /* The dinfo instance now owns the corecfg value */
436 corecfg = NULL;
437
438 /* Add the child device */
439 child = device_add_child(bus, NULL, -1);
440 if (child == NULL) {
441 error = ENXIO;
442 goto failed;
443 }
444
445 /* The child device now owns the dinfo pointer */
446 device_set_ivars(child, dinfo);
447 dinfo = NULL;
448
449 /* If pins are floating or the hardware is otherwise
450 * unpopulated, the device shouldn't be used. */
451 if (bhnd_is_hw_disabled(child))
452 device_disable(child);
453 }
454
455 /* Hit EOF parsing cores? */
456 if (error == ENOENT)
457 return (0);
458
459failed:
460 if (dinfo != NULL)
461 bcma_free_dinfo(bus, dinfo);
462
463 if (corecfg != NULL)
464 bcma_free_corecfg(corecfg);
465
466 return (error);
467}
468
469
470static device_method_t bcma_methods[] = {
471 /* Device interface */
472 DEVMETHOD(device_probe, bcma_probe),
473 DEVMETHOD(device_attach, bcma_attach),
474 DEVMETHOD(device_detach, bcma_detach),
475
476 /* Bus interface */
477 DEVMETHOD(bus_child_deleted, bcma_child_deleted),
478 DEVMETHOD(bus_read_ivar, bcma_read_ivar),
479 DEVMETHOD(bus_write_ivar, bcma_write_ivar),
480 DEVMETHOD(bus_get_resource_list, bcma_get_resource_list),
481
482 /* BHND interface */
483 DEVMETHOD(bhnd_bus_find_hostb_device, bcma_find_hostb_device),
484 DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core),
485 DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core),
486 DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count),
487 DEVMETHOD(bhnd_bus_get_region_count, bcma_get_region_count),
488 DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid),
489 DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid),
490 DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr),
491
492 DEVMETHOD_END
493};
494
495DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
496MODULE_VERSION(bcma, 1);
497MODULE_DEPEND(bcma, bhnd, 1, 1, 1);