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