Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/bhnd/bhnd_erom.c
39507 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 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/kobj.h>
39
40
#include <machine/bus.h>
41
#include <sys/rman.h>
42
#include <machine/resource.h>
43
44
#include <dev/bhnd/bhndreg.h>
45
#include <dev/bhnd/bhndvar.h>
46
47
#include <dev/bhnd/bhnd_erom.h>
48
#include <dev/bhnd/bhnd_eromvar.h>
49
50
#include <dev/bhnd/cores/chipc/chipcreg.h>
51
52
static int bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
53
bhnd_size_t size);
54
static int bhnd_erom_iores_tell(struct bhnd_erom_io *eio,
55
bhnd_addr_t *addr, bhnd_size_t *size);
56
static uint32_t bhnd_erom_iores_read(struct bhnd_erom_io *eio,
57
bhnd_size_t offset, u_int width);
58
static void bhnd_erom_iores_fini(struct bhnd_erom_io *eio);
59
60
static int bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
61
bhnd_size_t size);
62
static int bhnd_erom_iobus_tell(struct bhnd_erom_io *eio,
63
bhnd_addr_t *addr, bhnd_size_t *size);
64
static uint32_t bhnd_erom_iobus_read(struct bhnd_erom_io *eio,
65
bhnd_size_t offset, u_int width);
66
67
/**
68
* An implementation of bhnd_erom_io that manages mappings via
69
* bhnd_alloc_resource() and bhnd_release_resource().
70
*/
71
struct bhnd_erom_iores {
72
struct bhnd_erom_io eio;
73
device_t owner; /**< device from which we'll allocate resources */
74
int owner_rid; /**< rid to use when allocating new mappings */
75
struct bhnd_resource *mapped; /**< current mapping, or NULL */
76
int mapped_rid; /**< resource ID of current mapping, or -1 */
77
};
78
79
/**
80
* Fetch the device enumeration parser class from all bhnd(4)-compatible drivers
81
* registered for @p bus_devclass, probe @p eio for supporting parser classes,
82
* and return the best available supporting enumeration parser class.
83
*
84
* @param bus_devclass The bus device class to be queried for
85
* bhnd(4)-compatible drivers.
86
* @param eio An erom bus I/O instance, configured with a
87
* mapping of the first bus core.
88
* @param hint Identification hint used to identify the device.
89
* If the chipset supports standard chip
90
* identification registers within the first core,
91
* this parameter should be NULL.
92
* @param[out] cid On success, the probed chip identifier.
93
*
94
* @retval non-NULL on success, the best available EROM class.
95
* @retval NULL if no erom class returned a successful probe result for
96
* @p eio.
97
*/
98
bhnd_erom_class_t *
99
bhnd_erom_probe_driver_classes(devclass_t bus_devclass,
100
struct bhnd_erom_io *eio, const struct bhnd_chipid *hint,
101
struct bhnd_chipid *cid)
102
{
103
driver_t **drivers;
104
int drv_count;
105
bhnd_erom_class_t *erom_cls;
106
int error, prio, result;
107
108
erom_cls = NULL;
109
prio = 0;
110
111
/* Fetch all available drivers */
112
error = devclass_get_drivers(bus_devclass, &drivers, &drv_count);
113
if (error) {
114
printf("error fetching bhnd(4) drivers for %s: %d\n",
115
devclass_get_name(bus_devclass), error);
116
return (NULL);
117
}
118
119
/* Enumerate the drivers looking for the best available EROM class */
120
for (int i = 0; i < drv_count; i++) {
121
struct bhnd_chipid pcid;
122
bhnd_erom_class_t *cls;
123
124
/* The default implementation of BHND_BUS_GET_EROM_CLASS()
125
* returns NULL if unimplemented; this should always be safe
126
* to call on arbitrary drivers */
127
cls = bhnd_driver_get_erom_class(drivers[i]);
128
if (cls == NULL)
129
continue;
130
131
kobj_class_compile(cls);
132
133
/* Probe the bus */
134
result = bhnd_erom_probe(cls, eio, hint, &pcid);
135
136
/* The parser did not match if an error was returned */
137
if (result > 0)
138
continue;
139
140
/* Check for a new highest priority match */
141
if (erom_cls == NULL || result > prio) {
142
prio = result;
143
144
*cid = pcid;
145
erom_cls = cls;
146
}
147
148
/* Terminate immediately on BUS_PROBE_SPECIFIC */
149
if (result == BUS_PROBE_SPECIFIC)
150
break;
151
}
152
153
free(drivers, M_TEMP);
154
return (erom_cls);
155
}
156
157
/**
158
* Allocate and return a new device enumeration table parser.
159
*
160
* @param cls The parser class for which an instance will be
161
* allocated.
162
* @param eio The bus I/O callbacks to use when reading the device
163
* enumeration table.
164
* @param cid The device's chip identifier.
165
*
166
* @retval non-NULL success
167
* @retval NULL if an error occurred allocating or initializing the
168
* EROM parser.
169
*/
170
bhnd_erom_t *
171
bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
172
struct bhnd_erom_io *eio)
173
{
174
bhnd_erom_t *erom;
175
int error;
176
177
erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
178
M_WAITOK|M_ZERO);
179
180
if ((error = BHND_EROM_INIT(erom, cid, eio))) {
181
printf("error initializing %s parser at %#jx: %d\n", cls->name,
182
(uintmax_t)cid->enum_addr, error);
183
184
kobj_delete((kobj_t)erom, M_BHND);
185
return (NULL);
186
}
187
188
return (erom);
189
}
190
191
/**
192
* Perform static initialization of a device enumeration table parser.
193
*
194
* This may be used to initialize a caller-allocated erom instance state
195
* during early boot, prior to malloc availability.
196
*
197
* @param cls The parser class for which an instance will be
198
* allocated.
199
* @param erom The erom parser instance to initialize.
200
* @param esize The total available number of bytes allocated for
201
* @p erom. If this is less than is required by @p cls,
202
* ENOMEM will be returned.
203
* @param cid The device's chip identifier.
204
* @param eio The bus I/O callbacks to use when reading the device
205
* enumeration table.
206
*
207
* @retval 0 success
208
* @retval ENOMEM if @p esize is smaller than required by @p cls.
209
* @retval non-zero if an error occurs initializing the EROM parser,
210
* a regular unix error code will be returned.
211
*/
212
int
213
bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
214
const struct bhnd_chipid *cid, struct bhnd_erom_io *eio)
215
{
216
kobj_class_t kcls;
217
218
kcls = (kobj_class_t)cls;
219
220
/* Verify allocation size */
221
if (kcls->size > esize)
222
return (ENOMEM);
223
224
/* Perform instance initialization */
225
kobj_init_static((kobj_t)erom, kcls);
226
return (BHND_EROM_INIT(erom, cid, eio));
227
}
228
229
/**
230
* Release any resources held by a @p erom parser previously
231
* initialized via bhnd_erom_init_static().
232
*
233
* @param erom An erom parser instance previously initialized via
234
* bhnd_erom_init_static().
235
*/
236
void
237
bhnd_erom_fini_static(bhnd_erom_t *erom)
238
{
239
return (BHND_EROM_FINI(erom));
240
}
241
242
/**
243
* Release all resources held by a @p erom parser previously
244
* allocated via bhnd_erom_alloc().
245
*
246
* @param erom An erom parser instance previously allocated via
247
* bhnd_erom_alloc().
248
*/
249
void
250
bhnd_erom_free(bhnd_erom_t *erom)
251
{
252
BHND_EROM_FINI(erom);
253
kobj_delete((kobj_t)erom, M_BHND);
254
}
255
256
/**
257
* Read the chip identification registers mapped by @p eio, popuating @p cid
258
* with the parsed result
259
*
260
* @param eio A bus I/O instance, configured with a mapping
261
* of the ChipCommon core.
262
* @param[out] cid On success, the parsed chip identification.
263
*
264
* @warning
265
* On early siba(4) devices, the ChipCommon core does not provide
266
* a valid CHIPC_ID_NUMCORE field. On these ChipCommon revisions
267
* (see CHIPC_NCORES_MIN_HWREV()), this function will parse and return
268
* an invalid `ncores` value.
269
*/
270
int
271
bhnd_erom_read_chipid(struct bhnd_erom_io *eio, struct bhnd_chipid *cid)
272
{
273
bhnd_addr_t cc_addr;
274
bhnd_size_t cc_size;
275
uint32_t idreg, cc_caps;
276
int error;
277
278
/* Fetch ChipCommon address */
279
if ((error = bhnd_erom_io_tell(eio, &cc_addr, &cc_size)))
280
return (error);
281
282
/* Read chip identifier */
283
idreg = bhnd_erom_io_read(eio, CHIPC_ID, 4);
284
285
/* Extract the basic chip info */
286
cid->chip_id = CHIPC_GET_BITS(idreg, CHIPC_ID_CHIP);
287
cid->chip_pkg = CHIPC_GET_BITS(idreg, CHIPC_ID_PKG);
288
cid->chip_rev = CHIPC_GET_BITS(idreg, CHIPC_ID_REV);
289
cid->chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS);
290
cid->ncores = CHIPC_GET_BITS(idreg, CHIPC_ID_NUMCORE);
291
292
/* Populate EROM address */
293
if (BHND_CHIPTYPE_HAS_EROM(cid->chip_type)) {
294
cid->enum_addr = bhnd_erom_io_read(eio, CHIPC_EROMPTR, 4);
295
} else {
296
cid->enum_addr = cc_addr;
297
}
298
299
/* Populate capability flags */
300
cc_caps = bhnd_erom_io_read(eio, CHIPC_CAPABILITIES, 4);
301
cid->chip_caps = 0x0;
302
303
if (cc_caps & CHIPC_CAP_BKPLN64)
304
cid->chip_caps |= BHND_CAP_BP64;
305
306
if (cc_caps & CHIPC_CAP_PMU)
307
cid->chip_caps |= BHND_CAP_PMU;
308
309
return (0);
310
}
311
312
/**
313
* Attempt to map @p size bytes at @p addr, replacing any existing
314
* @p eio mapping.
315
*
316
* @param eio I/O instance state.
317
* @param addr The address to be mapped.
318
* @param size The number of bytes to be mapped at @p addr.
319
*
320
* @retval 0 success
321
* @retval non-zero if mapping @p addr otherwise fails, a regular
322
* unix error code should be returned.
323
*/
324
int
325
bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)
326
{
327
return (eio->map(eio, addr, size));
328
}
329
330
/**
331
* Return the address range mapped by @p eio, if any.
332
*
333
* @param eio I/O instance state.
334
* @param[out] addr The address mapped by @p eio.
335
* @param[out] size The number of bytes mapped at @p addr.
336
*
337
* @retval 0 success
338
* @retval ENXIO if @p eio has no mapping.
339
*/
340
int
341
bhnd_erom_io_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
342
bhnd_size_t *size)
343
{
344
return (eio->tell(eio, addr, size));
345
}
346
347
/**
348
* Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset
349
* relative to @p eio's current mapping.
350
*
351
* @param eio erom I/O callbacks
352
* @param offset read offset.
353
* @param width item width (1, 2, or 4 bytes).
354
*/
355
uint32_t
356
bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
357
{
358
return (eio->read(eio, offset, width));
359
}
360
361
/**
362
* Free all resources held by @p eio.
363
*/
364
void
365
bhnd_erom_io_fini(struct bhnd_erom_io *eio)
366
{
367
if (eio->fini != NULL)
368
return (eio->fini(eio));
369
}
370
371
/**
372
* Allocate, initialize, and return a new I/O instance that will perform
373
* mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid.
374
*
375
* @param dev The device to pass to bhnd_alloc_resource() and
376
* bhnd_release_resource() functions.
377
* @param rid The resource ID to be used when allocating memory resources.
378
*/
379
struct bhnd_erom_io *
380
bhnd_erom_iores_new(device_t dev, int rid)
381
{
382
struct bhnd_erom_iores *iores;
383
384
iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO);
385
iores->eio.map = bhnd_erom_iores_map;
386
iores->eio.tell = bhnd_erom_iores_tell;
387
iores->eio.read = bhnd_erom_iores_read;
388
iores->eio.fini = bhnd_erom_iores_fini;
389
390
iores->owner = dev;
391
iores->owner_rid = rid;
392
iores->mapped = NULL;
393
iores->mapped_rid = -1;
394
395
return (&iores->eio);
396
}
397
398
static int
399
bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
400
bhnd_size_t size)
401
{
402
struct bhnd_erom_iores *iores;
403
404
iores = (struct bhnd_erom_iores *)eio;
405
406
/* Sanity check the addr/size */
407
if (size == 0)
408
return (EINVAL);
409
410
if (BHND_ADDR_MAX - size < addr)
411
return (EINVAL); /* would overflow */
412
413
/* Check for an existing mapping */
414
if (iores->mapped) {
415
/* If already mapped, nothing else to do */
416
if (rman_get_start(iores->mapped->res) == addr &&
417
rman_get_size(iores->mapped->res) == size)
418
{
419
return (0);
420
}
421
422
/* Otherwise, we need to drop the existing mapping */
423
bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
424
iores->mapped_rid, iores->mapped);
425
iores->mapped = NULL;
426
iores->mapped_rid = -1;
427
}
428
429
/* Try to allocate the new mapping */
430
iores->mapped_rid = iores->owner_rid;
431
iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY,
432
&iores->mapped_rid, addr, addr+size-1, size,
433
RF_ACTIVE|RF_SHAREABLE);
434
if (iores->mapped == NULL) {
435
iores->mapped_rid = -1;
436
return (ENXIO);
437
}
438
439
return (0);
440
}
441
442
static int
443
bhnd_erom_iores_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
444
bhnd_size_t *size)
445
{
446
struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
447
448
if (iores->mapped == NULL)
449
return (ENXIO);
450
451
*addr = rman_get_start(iores->mapped->res);
452
*size = rman_get_size(iores->mapped->res);
453
454
return (0);
455
}
456
457
static uint32_t
458
bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
459
{
460
struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
461
462
if (iores->mapped == NULL)
463
panic("read with invalid mapping");
464
465
switch (width) {
466
case 1:
467
return (bhnd_bus_read_1(iores->mapped, offset));
468
case 2:
469
return (bhnd_bus_read_2(iores->mapped, offset));
470
case 4:
471
return (bhnd_bus_read_4(iores->mapped, offset));
472
default:
473
panic("invalid width %u", width);
474
}
475
}
476
477
static void
478
bhnd_erom_iores_fini(struct bhnd_erom_io *eio)
479
{
480
struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
481
482
/* Release any mapping */
483
if (iores->mapped) {
484
bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
485
iores->mapped_rid, iores->mapped);
486
iores->mapped = NULL;
487
iores->mapped_rid = -1;
488
}
489
490
free(eio, M_BHND);
491
}
492
493
/**
494
* Initialize an I/O instance that will perform mapping directly from the
495
* given bus space tag and handle.
496
*
497
* @param iobus The I/O instance to be initialized.
498
* @param addr The base address mapped by @p bsh.
499
* @param size The total size mapped by @p bsh.
500
* @param bst Bus space tag for @p bsh.
501
* @param bsh Bus space handle mapping the full bus enumeration space.
502
*
503
* @retval 0 success
504
* @retval non-zero if initializing @p iobus otherwise fails, a regular
505
* unix error code will be returned.
506
*/
507
int
508
bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr,
509
bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh)
510
{
511
iobus->eio.map = bhnd_erom_iobus_map;
512
iobus->eio.tell = bhnd_erom_iobus_tell;
513
iobus->eio.read = bhnd_erom_iobus_read;
514
iobus->eio.fini = NULL;
515
516
iobus->addr = addr;
517
iobus->size = size;
518
iobus->bst = bst;
519
iobus->bsh = bsh;
520
iobus->mapped = false;
521
522
return (0);
523
}
524
525
static int
526
bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
527
bhnd_size_t size)
528
{
529
struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
530
531
/* Sanity check the addr/size */
532
if (size == 0)
533
return (EINVAL);
534
535
/* addr+size must not overflow */
536
if (BHND_ADDR_MAX - size < addr)
537
return (EINVAL);
538
539
/* addr/size must fit within our bus tag's mapping */
540
if (addr < iobus->addr || size > iobus->size)
541
return (ENXIO);
542
543
if (iobus->size - (addr - iobus->addr) < size)
544
return (ENXIO);
545
546
/* The new addr offset and size must be representible as a bus_size_t */
547
if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)
548
return (ENXIO);
549
550
if (size > BUS_SPACE_MAXSIZE)
551
return (ENXIO);
552
553
iobus->offset = addr - iobus->addr;
554
iobus->limit = size;
555
iobus->mapped = true;
556
557
return (0);
558
}
559
560
static int
561
bhnd_erom_iobus_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
562
bhnd_size_t *size)
563
{
564
struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
565
566
if (!iobus->mapped)
567
return (ENXIO);
568
569
*addr = iobus->addr + iobus->offset;
570
*size = iobus->limit;
571
572
return (0);
573
}
574
575
static uint32_t
576
bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
577
{
578
struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
579
580
if (!iobus->mapped)
581
panic("no active mapping");
582
583
if (iobus->limit < width || iobus->limit - width < offset)
584
panic("invalid offset %#jx", offset);
585
586
switch (width) {
587
case 1:
588
return (bus_space_read_1(iobus->bst, iobus->bsh,
589
iobus->offset + offset));
590
case 2:
591
return (bus_space_read_2(iobus->bst, iobus->bsh,
592
iobus->offset + offset));
593
case 4:
594
return (bus_space_read_4(iobus->bst, iobus->bsh,
595
iobus->offset + offset));
596
default:
597
panic("invalid width %u", width);
598
}
599
}
600
601