1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
5 * Copyright (c) 2017 The FreeBSD Foundation
6 * All rights reserved.
7 *
8 * Portions of this software were developed by Landon Fuller
9 * under sponsorship from the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer,
16 * without modification.
17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
19 * redistribution must be conditioned upon including a substantially
20 * similar Disclaimer requirement for further binary redistribution.
21 *
22 * NO WARRANTY
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
26 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
27 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
28 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
31 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33 * THE POSSIBILITY OF SUCH DAMAGES.
34 */
35
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD: stable/12/sys/dev/bhnd/bcma/bcma_subr.c 326871 2017-12-15 04:51:47Z landonf $");
38
39 #include <sys/param.h>
40 #include <sys/bus.h>
41 #include <sys/kernel.h>
42 #include <sys/limits.h>
43 #include <sys/systm.h>
44
45 #include <machine/bus.h>
46 #include <machine/resource.h>
47
48 #include <dev/bhnd/bhndvar.h>
49
50 #include "bcma_dmp.h"
51
52 #include "bcmavar.h"
53
54 /* Return the resource ID for a device's agent register allocation */
55 #define BCMA_AGENT_RID(_dinfo) \
56 (BCMA_AGENT_RID_BASE + BCMA_DINFO_COREIDX(_dinfo))
57
58 /**
59 * Allocate and initialize new core config structure.
60 *
61 * @param core_index Core index on the bus.
62 * @param core_unit Core unit number.
63 * @param vendor Core designer.
64 * @param device Core identifier (e.g. part number).
65 * @param hwrev Core revision.
66 */
67 struct bcma_corecfg *
bcma_alloc_corecfg(u_int core_index,int core_unit,uint16_t vendor,uint16_t device,uint8_t hwrev)68 bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor,
69 uint16_t device, uint8_t hwrev)
70 {
71 struct bcma_corecfg *cfg;
72
73 cfg = malloc(sizeof(*cfg), M_BHND, M_NOWAIT);
74 if (cfg == NULL)
75 return NULL;
76
77 cfg->core_info = (struct bhnd_core_info) {
78 .vendor = vendor,
79 .device = device,
80 .hwrev = hwrev,
81 .core_idx = core_index,
82 .unit = core_unit
83 };
84
85 STAILQ_INIT(&cfg->master_ports);
86 cfg->num_master_ports = 0;
87
88 STAILQ_INIT(&cfg->dev_ports);
89 cfg->num_dev_ports = 0;
90
91 STAILQ_INIT(&cfg->bridge_ports);
92 cfg->num_bridge_ports = 0;
93
94 STAILQ_INIT(&cfg->wrapper_ports);
95 cfg->num_wrapper_ports = 0;
96
97 return (cfg);
98 }
99
100 /**
101 * Deallocate the given core config and any associated resources.
102 *
103 * @param corecfg Core info to be deallocated.
104 */
105 void
bcma_free_corecfg(struct bcma_corecfg * corecfg)106 bcma_free_corecfg(struct bcma_corecfg *corecfg)
107 {
108 struct bcma_mport *mport, *mnext;
109 struct bcma_sport *sport, *snext;
110
111 STAILQ_FOREACH_SAFE(mport, &corecfg->master_ports, mp_link, mnext) {
112 free(mport, M_BHND);
113 }
114
115 STAILQ_FOREACH_SAFE(sport, &corecfg->dev_ports, sp_link, snext) {
116 bcma_free_sport(sport);
117 }
118
119 STAILQ_FOREACH_SAFE(sport, &corecfg->bridge_ports, sp_link, snext) {
120 bcma_free_sport(sport);
121 }
122
123 STAILQ_FOREACH_SAFE(sport, &corecfg->wrapper_ports, sp_link, snext) {
124 bcma_free_sport(sport);
125 }
126
127 free(corecfg, M_BHND);
128 }
129
130 /**
131 * Return the @p cfg port list for @p type.
132 *
133 * @param cfg The core configuration.
134 * @param type The requested port type.
135 */
136 struct bcma_sport_list *
bcma_corecfg_get_port_list(struct bcma_corecfg * cfg,bhnd_port_type type)137 bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type)
138 {
139 switch (type) {
140 case BHND_PORT_DEVICE:
141 return (&cfg->dev_ports);
142 break;
143 case BHND_PORT_BRIDGE:
144 return (&cfg->bridge_ports);
145 break;
146 case BHND_PORT_AGENT:
147 return (&cfg->wrapper_ports);
148 break;
149 default:
150 return (NULL);
151 }
152 }
153
154 /**
155 * Populate the resource list and bcma_map RIDs using the maps defined on
156 * @p ports.
157 *
158 * @param bus The requesting bus device.
159 * @param dinfo The device info instance to be initialized.
160 * @param ports The set of ports to be enumerated
161 */
162 static void
bcma_dinfo_init_port_resource_info(device_t bus,struct bcma_devinfo * dinfo,struct bcma_sport_list * ports)163 bcma_dinfo_init_port_resource_info(device_t bus, struct bcma_devinfo *dinfo,
164 struct bcma_sport_list *ports)
165 {
166 struct bcma_map *map;
167 struct bcma_sport *port;
168 bhnd_addr_t end;
169
170 STAILQ_FOREACH(port, ports, sp_link) {
171 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
172 /*
173 * Create the corresponding device resource list entry.
174 *
175 * We necessarily skip registration if the region's
176 * device memory range is not representable via
177 * rman_res_t.
178 *
179 * When rman_res_t is migrated to uintmax_t, any
180 * range should be representable.
181 */
182 end = map->m_base + map->m_size;
183 if (map->m_base <= RM_MAX_END && end <= RM_MAX_END) {
184 map->m_rid = resource_list_add_next(
185 &dinfo->resources, SYS_RES_MEMORY,
186 map->m_base, end, map->m_size);
187 } else if (bootverbose) {
188 device_printf(bus,
189 "core%u %s%u.%u: region %llx-%llx extends "
190 "beyond supported addressable range\n",
191 dinfo->corecfg->core_info.core_idx,
192 bhnd_port_type_name(port->sp_type),
193 port->sp_num, map->m_region_num,
194 (unsigned long long) map->m_base,
195 (unsigned long long) end);
196 }
197 }
198 }
199 }
200
201
202
203 /**
204 * Allocate the per-core agent register block for a device info structure.
205 *
206 * If an agent0.0 region is not defined on @p dinfo, the device info
207 * agent resource is set to NULL and 0 is returned.
208 *
209 * @param bus The requesting bus device.
210 * @param child The bcma child device.
211 * @param dinfo The device info associated with @p child
212 *
213 * @retval 0 success
214 * @retval non-zero resource allocation failed.
215 */
216 static int
bcma_dinfo_init_agent(device_t bus,device_t child,struct bcma_devinfo * dinfo)217 bcma_dinfo_init_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo)
218 {
219 bhnd_addr_t addr;
220 bhnd_size_t size;
221 rman_res_t r_start, r_count, r_end;
222 int error;
223
224 KASSERT(dinfo->res_agent == NULL, ("double allocation of agent"));
225
226 /* Verify that the agent register block exists and is
227 * mappable */
228 if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
229 return (0); /* nothing to do */
230
231 /* Fetch the address of the agent register block */
232 error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
233 &addr, &size);
234 if (error) {
235 device_printf(bus, "failed fetching agent register block "
236 "address for core %u\n", BCMA_DINFO_COREIDX(dinfo));
237 return (error);
238 }
239
240 /* Allocate the resource */
241 r_start = addr;
242 r_count = size;
243 r_end = r_start + r_count - 1;
244
245 dinfo->rid_agent = BCMA_AGENT_RID(dinfo);
246 dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY,
247 &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE|RF_SHAREABLE);
248 if (dinfo->res_agent == NULL) {
249 device_printf(bus, "failed allocating agent register block for "
250 "core %u\n", BCMA_DINFO_COREIDX(dinfo));
251 return (ENXIO);
252 }
253
254 return (0);
255 }
256
257 /**
258 * Populate the list of interrupts for a device info structure
259 * previously initialized via bcma_dinfo_alloc_agent().
260 *
261 * If an agent0.0 region is not mapped on @p dinfo, the OOB interrupt bank is
262 * assumed to be unavailable and 0 is returned.
263 *
264 * @param bus The requesting bus device.
265 * @param dinfo The device info instance to be initialized.
266 */
267 static int
bcma_dinfo_init_intrs(device_t bus,device_t child,struct bcma_devinfo * dinfo)268 bcma_dinfo_init_intrs(device_t bus, device_t child,
269 struct bcma_devinfo *dinfo)
270 {
271 uint32_t dmpcfg, oobw;
272
273 /* Agent block must be mapped */
274 if (dinfo->res_agent == NULL)
275 return (0);
276
277 /* Agent must support OOB */
278 dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG);
279 if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB))
280 return (0);
281
282 /* Fetch width of the OOB interrupt bank */
283 oobw = bhnd_bus_read_4(dinfo->res_agent,
284 BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR));
285 if (oobw >= BCMA_OOB_NUM_SEL) {
286 device_printf(bus, "ignoring invalid OOBOUTWIDTH for core %u: "
287 "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw);
288 return (0);
289 }
290
291 /* Fetch OOBSEL busline values and populate list of interrupt
292 * descriptors */
293 for (uint32_t sel = 0; sel < oobw; sel++) {
294 struct bcma_intr *intr;
295 uint32_t selout;
296 uint8_t line;
297
298 if (dinfo->num_intrs == UINT_MAX)
299 return (ENOMEM);
300
301 selout = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT(
302 BCMA_OOB_BANK_INTR, sel));
303
304 line = (selout >> BCMA_DMP_OOBSEL_SHIFT(sel)) &
305 BCMA_DMP_OOBSEL_BUSLINE_MASK;
306
307 intr = bcma_alloc_intr(BCMA_OOB_BANK_INTR, sel, line);
308 if (intr == NULL) {
309 device_printf(bus, "failed allocating interrupt "
310 "descriptor %#x for core %u\n", sel,
311 BCMA_DINFO_COREIDX(dinfo));
312 return (ENOMEM);
313 }
314
315 STAILQ_INSERT_HEAD(&dinfo->intrs, intr, i_link);
316 dinfo->num_intrs++;
317 }
318
319 return (0);
320 }
321
322 /**
323 * Allocate and return a new empty device info structure.
324 *
325 * @param bus The requesting bus device.
326 *
327 * @retval NULL if allocation failed.
328 */
329 struct bcma_devinfo *
bcma_alloc_dinfo(device_t bus)330 bcma_alloc_dinfo(device_t bus)
331 {
332 struct bcma_devinfo *dinfo;
333
334 dinfo = malloc(sizeof(struct bcma_devinfo), M_BHND, M_NOWAIT|M_ZERO);
335 if (dinfo == NULL)
336 return (NULL);
337
338 dinfo->corecfg = NULL;
339 dinfo->res_agent = NULL;
340 dinfo->rid_agent = -1;
341
342 STAILQ_INIT(&dinfo->intrs);
343 dinfo->num_intrs = 0;
344
345 resource_list_init(&dinfo->resources);
346
347 return (dinfo);
348 }
349
350 /**
351 * Initialize a device info structure previously allocated via
352 * bcma_alloc_dinfo, assuming ownership of the provided core
353 * configuration.
354 *
355 * @param bus The requesting bus device.
356 * @param child The bcma child device.
357 * @param dinfo The device info associated with @p child
358 * @param corecfg Device core configuration; ownership of this value
359 * will be assumed by @p dinfo.
360 *
361 * @retval 0 success
362 * @retval non-zero initialization failed.
363 */
364 int
bcma_init_dinfo(device_t bus,device_t child,struct bcma_devinfo * dinfo,struct bcma_corecfg * corecfg)365 bcma_init_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo,
366 struct bcma_corecfg *corecfg)
367 {
368 struct bcma_intr *intr;
369 int error;
370
371 KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized"));
372
373 /* Save core configuration value */
374 dinfo->corecfg = corecfg;
375
376 /* The device ports must always be initialized first to ensure that
377 * rid 0 maps to the first device port */
378 bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->dev_ports);
379 bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->bridge_ports);
380 bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->wrapper_ports);
381
382 /* Now that we've defined the port resources, we can map the device's
383 * agent registers (if any) */
384 if ((error = bcma_dinfo_init_agent(bus, child, dinfo)))
385 goto failed;
386
387 /* With agent registers mapped, we can populate the device's interrupt
388 * descriptors */
389 if ((error = bcma_dinfo_init_intrs(bus, child, dinfo)))
390 goto failed;
391
392 /* Finally, map the interrupt descriptors */
393 STAILQ_FOREACH(intr, &dinfo->intrs, i_link) {
394 /* Already mapped? */
395 if (intr->i_mapped)
396 continue;
397
398 /* Map the interrupt */
399 error = BHND_BUS_MAP_INTR(bus, child, intr->i_sel,
400 &intr->i_irq);
401 if (error) {
402 device_printf(bus, "failed mapping interrupt line %u "
403 "for core %u: %d\n", intr->i_sel,
404 BCMA_DINFO_COREIDX(dinfo), error);
405 goto failed;
406 }
407
408 intr->i_mapped = true;
409
410 /* Add to resource list */
411 intr->i_rid = resource_list_add_next(&dinfo->resources,
412 SYS_RES_IRQ, intr->i_irq, intr->i_irq, 1);
413 }
414
415 return (0);
416
417 failed:
418 /* Owned by the caller on failure */
419 dinfo->corecfg = NULL;
420
421 return (error);
422 }
423
424 /**
425 * Deallocate the given device info structure and any associated resources.
426 *
427 * @param bus The requesting bus device.
428 * @param dinfo Device info to be deallocated.
429 */
430 void
bcma_free_dinfo(device_t bus,device_t child,struct bcma_devinfo * dinfo)431 bcma_free_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo)
432 {
433 struct bcma_intr *intr, *inext;
434
435 resource_list_free(&dinfo->resources);
436
437 if (dinfo->corecfg != NULL)
438 bcma_free_corecfg(dinfo->corecfg);
439
440 /* Release agent resource, if any */
441 if (dinfo->res_agent != NULL) {
442 bhnd_release_resource(bus, SYS_RES_MEMORY, dinfo->rid_agent,
443 dinfo->res_agent);
444 }
445
446 /* Clean up interrupt descriptors */
447 STAILQ_FOREACH_SAFE(intr, &dinfo->intrs, i_link, inext) {
448 STAILQ_REMOVE(&dinfo->intrs, intr, bcma_intr, i_link);
449
450 /* Release our IRQ mapping */
451 if (intr->i_mapped) {
452 BHND_BUS_UNMAP_INTR(bus, child, intr->i_irq);
453 intr->i_mapped = false;
454 }
455
456 bcma_free_intr(intr);
457 }
458
459 free(dinfo, M_BHND);
460 }
461
462
463 /**
464 * Allocate and initialize a new interrupt descriptor.
465 *
466 * @param bank OOB bank.
467 * @param sel OOB selector.
468 * @param line OOB bus line.
469 */
470 struct bcma_intr *
bcma_alloc_intr(uint8_t bank,uint8_t sel,uint8_t line)471 bcma_alloc_intr(uint8_t bank, uint8_t sel, uint8_t line)
472 {
473 struct bcma_intr *intr;
474
475 if (bank >= BCMA_OOB_NUM_BANKS)
476 return (NULL);
477
478 if (sel >= BCMA_OOB_NUM_SEL)
479 return (NULL);
480
481 if (line >= BCMA_OOB_NUM_BUSLINES)
482 return (NULL);
483
484 intr = malloc(sizeof(*intr), M_BHND, M_NOWAIT);
485 if (intr == NULL)
486 return (NULL);
487
488 intr->i_bank = bank;
489 intr->i_sel = sel;
490 intr->i_busline = line;
491 intr->i_mapped = false;
492 intr->i_irq = 0;
493
494 return (intr);
495 }
496
497 /**
498 * Deallocate all resources associated with the given interrupt descriptor.
499 *
500 * @param intr Interrupt descriptor to be deallocated.
501 */
502 void
bcma_free_intr(struct bcma_intr * intr)503 bcma_free_intr(struct bcma_intr *intr)
504 {
505 KASSERT(!intr->i_mapped, ("interrupt %u still mapped", intr->i_sel));
506
507 free(intr, M_BHND);
508 }
509
510 /**
511 * Allocate and initialize new slave port descriptor.
512 *
513 * @param port_num Per-core port number.
514 * @param port_type Port type.
515 */
516 struct bcma_sport *
bcma_alloc_sport(bcma_pid_t port_num,bhnd_port_type port_type)517 bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type)
518 {
519 struct bcma_sport *sport;
520
521 sport = malloc(sizeof(struct bcma_sport), M_BHND, M_NOWAIT);
522 if (sport == NULL)
523 return NULL;
524
525 sport->sp_num = port_num;
526 sport->sp_type = port_type;
527 sport->sp_num_maps = 0;
528 STAILQ_INIT(&sport->sp_maps);
529
530 return sport;
531 }
532
533 /**
534 * Deallocate all resources associated with the given port descriptor.
535 *
536 * @param sport Port descriptor to be deallocated.
537 */
538 void
bcma_free_sport(struct bcma_sport * sport)539 bcma_free_sport(struct bcma_sport *sport) {
540 struct bcma_map *map, *mapnext;
541
542 STAILQ_FOREACH_SAFE(map, &sport->sp_maps, m_link, mapnext) {
543 free(map, M_BHND);
544 }
545
546 free(sport, M_BHND);
547 }
548
549
550 /**
551 * Given a bcma(4) child's device info, spin waiting for the device's DMP
552 * resetstatus register to clear.
553 *
554 * @param child The bcma(4) child device.
555 * @param dinfo The @p child device info.
556 *
557 * @retval 0 success
558 * @retval ENODEV if @p dinfo does not map an agent register resource.
559 * @retval ETIMEDOUT if timeout occurs
560 */
561 int
bcma_dmp_wait_reset(device_t child,struct bcma_devinfo * dinfo)562 bcma_dmp_wait_reset(device_t child, struct bcma_devinfo *dinfo)
563 {
564 uint32_t rst;
565
566 if (dinfo->res_agent == NULL)
567 return (ENODEV);
568
569 /* 300us should be long enough, but there are references to this
570 * requiring up to 10ms when performing reset of an 80211 core
571 * after a MAC PSM microcode watchdog event. */
572 for (int i = 0; i < 10000; i += 10) {
573 rst = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETSTATUS);
574 if (rst == 0)
575 return (0);
576
577 DELAY(10);
578 }
579
580 device_printf(child, "BCMA_DMP_RESETSTATUS timeout\n");
581 return (ETIMEDOUT);
582 }
583
584 /**
585 * Set the bcma(4) child's DMP resetctrl register value, and then wait
586 * for all backplane operations to complete.
587 *
588 * @param child The bcma(4) child device.
589 * @param dinfo The @p child device info.
590 * @param value The new ioctrl value to set.
591 *
592 * @retval 0 success
593 * @retval ENODEV if @p dinfo does not map an agent register resource.
594 * @retval ETIMEDOUT if timeout occurs waiting for reset completion
595 */
596 int
bcma_dmp_write_reset(device_t child,struct bcma_devinfo * dinfo,uint32_t value)597 bcma_dmp_write_reset(device_t child, struct bcma_devinfo *dinfo, uint32_t value)
598 {
599 uint32_t rst;
600
601 if (dinfo->res_agent == NULL)
602 return (ENODEV);
603
604 /* Already in requested reset state? */
605 rst = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL);
606 if (rst == value)
607 return (0);
608
609 bhnd_bus_write_4(dinfo->res_agent, BCMA_DMP_RESETCTRL, value);
610 bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL); /* read-back */
611 DELAY(10);
612
613 return (bcma_dmp_wait_reset(child, dinfo));
614 }
615