Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/ofw/ofw_pcibus.c
39507 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 1997, Stefan Esser <[email protected]>
5
* Copyright (c) 2000, Michael Smith <[email protected]>
6
* Copyright (c) 2000, BSDi
7
* Copyright (c) 2003, Thomas Moestl <[email protected]>
8
* All rights reserved.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice unmodified, this list of conditions, and the following
15
* disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
*/
31
32
#include <sys/param.h>
33
#include <sys/bus.h>
34
#include <sys/kernel.h>
35
#include <sys/libkern.h>
36
#include <sys/module.h>
37
#include <sys/pciio.h>
38
#include <sys/sbuf.h>
39
#include <sys/smp.h>
40
41
#include <dev/ofw/ofw_bus.h>
42
#include <dev/ofw/ofw_bus_subr.h>
43
#include <dev/ofw/ofw_pci.h>
44
#include <dev/ofw/openfirm.h>
45
46
#include <machine/bus.h>
47
#include <machine/intr_machdep.h>
48
#include <machine/resource.h>
49
50
#include <dev/pci/pcireg.h>
51
#include <dev/pci/pcivar.h>
52
#include <dev/pci/pci_private.h>
53
54
#include "ofw_pcibus.h"
55
#include "pcib_if.h"
56
#include "pci_if.h"
57
58
typedef uint32_t ofw_pci_intr_t;
59
60
/* Methods */
61
static device_probe_t ofw_pcibus_probe;
62
static device_attach_t ofw_pcibus_attach;
63
static pci_alloc_devinfo_t ofw_pcibus_alloc_devinfo;
64
static pci_assign_interrupt_t ofw_pcibus_assign_interrupt;
65
static ofw_bus_get_devinfo_t ofw_pcibus_get_devinfo;
66
static bus_child_deleted_t ofw_pcibus_child_deleted;
67
static int ofw_pcibus_child_pnpinfo_method(device_t cbdev, device_t child,
68
struct sbuf *sb);
69
70
static void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno);
71
static void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno);
72
73
static device_method_t ofw_pcibus_methods[] = {
74
/* Device interface */
75
DEVMETHOD(device_probe, ofw_pcibus_probe),
76
DEVMETHOD(device_attach, ofw_pcibus_attach),
77
78
/* Bus interface */
79
DEVMETHOD(bus_child_deleted, ofw_pcibus_child_deleted),
80
DEVMETHOD(bus_child_pnpinfo, ofw_pcibus_child_pnpinfo_method),
81
DEVMETHOD(bus_rescan, bus_null_rescan),
82
DEVMETHOD(bus_get_cpus, ofw_pcibus_get_cpus),
83
DEVMETHOD(bus_get_domain, ofw_pcibus_get_domain),
84
85
/* PCI interface */
86
DEVMETHOD(pci_alloc_devinfo, ofw_pcibus_alloc_devinfo),
87
DEVMETHOD(pci_assign_interrupt, ofw_pcibus_assign_interrupt),
88
89
/* ofw_bus interface */
90
DEVMETHOD(ofw_bus_get_devinfo, ofw_pcibus_get_devinfo),
91
DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
92
DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
93
DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
94
DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
95
DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
96
97
DEVMETHOD_END
98
};
99
100
DEFINE_CLASS_1(pci, ofw_pcibus_driver, ofw_pcibus_methods,
101
sizeof(struct pci_softc), pci_driver);
102
EARLY_DRIVER_MODULE(ofw_pcibus, pcib, ofw_pcibus_driver, 0, 0, BUS_PASS_BUS);
103
MODULE_VERSION(ofw_pcibus, 1);
104
MODULE_DEPEND(ofw_pcibus, pci, 1, 1, 1);
105
106
static int ofw_devices_only = 0;
107
TUNABLE_INT("hw.pci.ofw_devices_only", &ofw_devices_only);
108
109
static int
110
ofw_pcibus_probe(device_t dev)
111
{
112
113
if (ofw_bus_get_node(dev) == -1)
114
return (ENXIO);
115
device_set_desc(dev, "OFW PCI bus");
116
117
return (BUS_PROBE_DEFAULT);
118
}
119
120
static int
121
ofw_pcibus_attach(device_t dev)
122
{
123
u_int busno, domain;
124
int error;
125
126
error = pci_attach_common(dev);
127
if (error)
128
return (error);
129
domain = pcib_get_domain(dev);
130
busno = pcib_get_bus(dev);
131
132
/*
133
* Attach those children represented in the device tree.
134
*/
135
136
ofw_pcibus_enum_devtree(dev, domain, busno);
137
138
/*
139
* We now attach any laggard devices. FDT, for instance, allows
140
* the device tree to enumerate only some PCI devices. Apple's
141
* OF device tree on some Grackle-based hardware can also miss
142
* functions on multi-function cards.
143
*/
144
145
if (!ofw_devices_only)
146
ofw_pcibus_enum_bus(dev, domain, busno);
147
148
bus_attach_children(dev);
149
return (0);
150
}
151
152
struct pci_devinfo *
153
ofw_pcibus_alloc_devinfo(device_t dev)
154
{
155
struct ofw_pcibus_devinfo *dinfo;
156
157
dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
158
return (&dinfo->opd_dinfo);
159
}
160
161
static void
162
ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno)
163
{
164
device_t pcib;
165
struct ofw_pci_register pcir;
166
struct ofw_pcibus_devinfo *dinfo;
167
phandle_t node, child;
168
u_int func, slot;
169
int intline;
170
171
pcib = device_get_parent(dev);
172
node = ofw_bus_get_node(dev);
173
174
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
175
if (OF_getencprop(child, "reg", (pcell_t *)&pcir,
176
sizeof(pcir)) == -1)
177
continue;
178
slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
179
func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
180
181
/* Some OFW device trees contain dupes. */
182
if (pci_find_dbsf(domain, busno, slot, func) != NULL)
183
continue;
184
185
/*
186
* The preset in the intline register is usually bogus. Reset
187
* it such that the PCI code will reroute the interrupt if
188
* needed.
189
*/
190
191
intline = PCI_INVALID_IRQ;
192
if (OF_getproplen(child, "interrupts") > 0)
193
intline = 0;
194
PCIB_WRITE_CONFIG(pcib, busno, slot, func, PCIR_INTLINE,
195
intline, 1);
196
197
/*
198
* Now set up the PCI and OFW bus layer devinfo and add it
199
* to the PCI bus.
200
*/
201
202
dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(pcib, dev,
203
domain, busno, slot, func);
204
if (dinfo == NULL)
205
continue;
206
if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) !=
207
0) {
208
pci_freecfg((struct pci_devinfo *)dinfo);
209
continue;
210
}
211
dinfo->opd_dma_tag = NULL;
212
pci_add_child(dev, (struct pci_devinfo *)dinfo);
213
214
/*
215
* Some devices don't have an intpin set, but do have
216
* interrupts. These are fully specified, and set in the
217
* interrupts property, so add that value to the device's
218
* resource list.
219
*/
220
if (dinfo->opd_dinfo.cfg.intpin == 0)
221
ofw_bus_intr_to_rl(dev, child,
222
&dinfo->opd_dinfo.resources, NULL);
223
}
224
}
225
226
/*
227
* The following is an almost exact clone of pci_add_children(), with the
228
* addition that it (a) will not add children that have already been added,
229
* and (b) will set up the OFW devinfo to point to invalid values. This is
230
* to handle non-enumerated PCI children as exist in FDT and on the second
231
* function of the Rage 128 in my Blue & White G3.
232
*/
233
234
static void
235
ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno)
236
{
237
device_t pcib;
238
struct ofw_pcibus_devinfo *dinfo;
239
int maxslots;
240
int s, f, pcifunchigh;
241
uint8_t hdrtype;
242
243
pcib = device_get_parent(dev);
244
245
maxslots = PCIB_MAXSLOTS(pcib);
246
for (s = 0; s <= maxslots; s++) {
247
pcifunchigh = 0;
248
f = 0;
249
DELAY(1);
250
hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1);
251
if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
252
continue;
253
if (hdrtype & PCIM_MFDEV)
254
pcifunchigh = PCI_FUNCMAX;
255
for (f = 0; f <= pcifunchigh; f++) {
256
/* Filter devices we have already added */
257
if (pci_find_dbsf(domain, busno, s, f) != NULL)
258
continue;
259
260
dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(
261
pcib, dev, domain, busno, s, f);
262
if (dinfo == NULL)
263
continue;
264
265
dinfo->opd_dma_tag = NULL;
266
dinfo->opd_obdinfo.obd_node = -1;
267
268
dinfo->opd_obdinfo.obd_name = NULL;
269
dinfo->opd_obdinfo.obd_compat = NULL;
270
dinfo->opd_obdinfo.obd_type = NULL;
271
dinfo->opd_obdinfo.obd_model = NULL;
272
273
/*
274
* For non OFW-devices, don't believe 0
275
* for an interrupt.
276
*/
277
if (dinfo->opd_dinfo.cfg.intline == 0) {
278
dinfo->opd_dinfo.cfg.intline = PCI_INVALID_IRQ;
279
PCIB_WRITE_CONFIG(pcib, busno, s, f,
280
PCIR_INTLINE, PCI_INVALID_IRQ, 1);
281
}
282
283
pci_add_child(dev, (struct pci_devinfo *)dinfo);
284
}
285
}
286
}
287
288
static void
289
ofw_pcibus_child_deleted(device_t dev, device_t child)
290
{
291
struct ofw_pcibus_devinfo *dinfo;
292
293
dinfo = device_get_ivars(child);
294
ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo);
295
pci_child_deleted(dev, child);
296
}
297
298
static int
299
ofw_pcibus_child_pnpinfo_method(device_t cbdev, device_t child, struct sbuf *sb)
300
{
301
pci_child_pnpinfo_method(cbdev, child, sb);
302
303
if (ofw_bus_get_node(child) != -1) {
304
sbuf_cat(sb, " "); /* Separate info */
305
ofw_bus_gen_child_pnpinfo(cbdev, child, sb);
306
}
307
308
return (0);
309
}
310
311
static int
312
ofw_pcibus_assign_interrupt(device_t dev, device_t child)
313
{
314
ofw_pci_intr_t intr[2];
315
phandle_t node, iparent;
316
int isz, icells;
317
318
node = ofw_bus_get_node(child);
319
320
if (node == -1) {
321
/* Non-firmware enumerated child, use standard routing */
322
323
intr[0] = pci_get_intpin(child);
324
return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child,
325
intr[0]));
326
}
327
328
/*
329
* Try to determine the node's interrupt parent so we know which
330
* PIC to use.
331
*/
332
333
iparent = -1;
334
if (OF_getencprop(node, "interrupt-parent", &iparent,
335
sizeof(iparent)) < 0)
336
iparent = -1;
337
icells = 1;
338
if (iparent != -1)
339
OF_getencprop(OF_node_from_xref(iparent), "#interrupt-cells",
340
&icells, sizeof(icells));
341
342
/*
343
* Any AAPL,interrupts property gets priority and is
344
* fully specified (i.e. does not need routing)
345
*/
346
347
isz = OF_getencprop(node, "AAPL,interrupts", intr, sizeof(intr));
348
if (isz == sizeof(intr[0])*icells)
349
return ((iparent == -1) ? intr[0] : ofw_bus_map_intr(dev,
350
iparent, icells, intr));
351
352
isz = OF_getencprop(node, "interrupts", intr, sizeof(intr));
353
if (isz == sizeof(intr[0])*icells) {
354
if (iparent != -1)
355
intr[0] = ofw_bus_map_intr(dev, iparent, icells, intr);
356
} else {
357
/* No property: our best guess is the intpin. */
358
intr[0] = pci_get_intpin(child);
359
}
360
361
/*
362
* If we got intr from a property, it may or may not be an intpin.
363
* For on-board devices, it frequently is not, and is completely out
364
* of the valid intpin range. For PCI slots, it hopefully is,
365
* otherwise we will have trouble interfacing with non-OFW buses
366
* such as cardbus.
367
* Since we cannot tell which it is without violating layering, we
368
* will always use the route_interrupt method, and treat exceptions
369
* on the level they become apparent.
370
*/
371
return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr[0]));
372
}
373
374
static const struct ofw_bus_devinfo *
375
ofw_pcibus_get_devinfo(device_t bus, device_t dev)
376
{
377
struct ofw_pcibus_devinfo *dinfo;
378
379
dinfo = device_get_ivars(dev);
380
return (&dinfo->opd_obdinfo);
381
}
382
383
int
384
ofw_pcibus_get_cpus(device_t dev, device_t child, enum cpu_sets op, size_t setsize,
385
cpuset_t *cpuset)
386
{
387
int d, error;
388
389
d = platform_node_numa_domain(ofw_bus_get_node(dev));
390
391
switch (op) {
392
case LOCAL_CPUS:
393
if (setsize != sizeof(cpuset_t))
394
return (EINVAL);
395
*cpuset = cpuset_domain[d];
396
return (0);
397
case INTR_CPUS:
398
error = bus_generic_get_cpus(dev, child, op, setsize, cpuset);
399
if (error != 0)
400
return (error);
401
if (setsize != sizeof(cpuset_t))
402
return (EINVAL);
403
CPU_AND(cpuset, cpuset, &cpuset_domain[d]);
404
return (0);
405
default:
406
return (bus_generic_get_cpus(dev, child, op, setsize, cpuset));
407
}
408
return (0);
409
}
410
411
/*
412
* Fetch the NUMA domain for the given device 'dev'.
413
*
414
* If a device has a _PXM method, map that to a NUMA domain.
415
* Otherwise, pass the request up to the parent.
416
* If there's no matching domain or the domain cannot be
417
* determined, return ENOENT.
418
*/
419
int
420
ofw_pcibus_get_domain(device_t dev, device_t child, int *domain)
421
{
422
*domain = platform_node_numa_domain(ofw_bus_get_node(child));
423
424
return (0);
425
}
426
427