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