Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/psim/iobus.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright 2002 by Peter Grehan. All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
* 3. The name of the author may not be used to endorse or promote products
15
* derived from this software without specific prior written permission.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
/*
31
* PSIM 'iobus' local bus. Should be set up in the device tree like:
32
*
33
* /iobus@0x80000000/name psim-iobus
34
*
35
* Code borrowed from various nexus.c and uninorth.c :-)
36
*/
37
38
#include <sys/param.h>
39
#include <sys/systm.h>
40
#include <sys/kernel.h>
41
#include <sys/malloc.h>
42
#include <sys/module.h>
43
#include <sys/bus.h>
44
#include <machine/bus.h>
45
#include <sys/rman.h>
46
47
#include <dev/ofw/ofw_bus.h>
48
#include <dev/ofw/openfirm.h>
49
50
#include <machine/vmparam.h>
51
#include <vm/vm.h>
52
#include <vm/pmap.h>
53
54
#include <machine/resource.h>
55
56
#include <powerpc/psim/iobusvar.h>
57
58
struct iobus_softc {
59
phandle_t sc_node;
60
vm_offset_t sc_addr;
61
vm_offset_t sc_size;
62
struct rman sc_mem_rman;
63
};
64
65
static MALLOC_DEFINE(M_IOBUS, "iobus", "iobus device information");
66
67
static int iobus_probe(device_t);
68
static int iobus_attach(device_t);
69
static int iobus_print_child(device_t dev, device_t child);
70
static void iobus_probe_nomatch(device_t, device_t);
71
static int iobus_read_ivar(device_t, device_t, int, uintptr_t *);
72
static int iobus_write_ivar(device_t, device_t, int, uintptr_t);
73
static struct rman *iobus_get_rman(device_t, int, u_int);
74
static struct resource *iobus_alloc_resource(device_t, device_t, int, int *,
75
rman_res_t, rman_res_t, rman_res_t,
76
u_int);
77
static int iobus_adjust_resource(device_t, device_t, struct resource *,
78
rman_res_t, rman_res_t);
79
static int iobus_activate_resource(device_t, device_t, struct resource *);
80
static int iobus_deactivate_resource(device_t, device_t, struct resource *);
81
static int iobus_map_resource(device_t, device_t, struct resource *,
82
struct resource_map_request *,
83
struct resource_map *);
84
static int iobus_unmap_resource(device_t, device_t, struct resource *,
85
struct resource_map *);
86
static int iobus_release_resource(device_t, device_t, struct resource *);
87
88
/*
89
* Bus interface definition
90
*/
91
static device_method_t iobus_methods[] = {
92
/* Device interface */
93
DEVMETHOD(device_probe, iobus_probe),
94
DEVMETHOD(device_attach, iobus_attach),
95
DEVMETHOD(device_detach, bus_generic_detach),
96
DEVMETHOD(device_shutdown, bus_generic_shutdown),
97
DEVMETHOD(device_suspend, bus_generic_suspend),
98
DEVMETHOD(device_resume, bus_generic_resume),
99
100
/* Bus interface */
101
DEVMETHOD(bus_print_child, iobus_print_child),
102
DEVMETHOD(bus_probe_nomatch, iobus_probe_nomatch),
103
DEVMETHOD(bus_read_ivar, iobus_read_ivar),
104
DEVMETHOD(bus_write_ivar, iobus_write_ivar),
105
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
106
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
107
108
DEVMETHOD(bus_get_rman, iobus_get_rman),
109
DEVMETHOD(bus_alloc_resource, iobus_alloc_resource),
110
DEVMETHOD(bus_adjust_resource, iobus_adjust_resource),
111
DEVMETHOD(bus_release_resource, iobus_release_resource),
112
DEVMETHOD(bus_activate_resource, iobus_activate_resource),
113
DEVMETHOD(bus_deactivate_resource, iobus_deactivate_resource),
114
DEVMETHOD(bus_map_resource, iobus_map_resource),
115
DEVMETHOD(bus_unmap_resource, iobus_unmap_resource),
116
{ 0, 0 }
117
};
118
119
static driver_t iobus_driver = {
120
"iobus",
121
iobus_methods,
122
sizeof(struct iobus_softc)
123
};
124
125
DRIVER_MODULE(iobus, ofwbus, iobus_driver, 0, 0);
126
127
static int
128
iobus_probe(device_t dev)
129
{
130
const char *type = ofw_bus_get_name(dev);
131
132
if (strcmp(type, "psim-iobus") != 0)
133
return (ENXIO);
134
135
device_set_desc(dev, "PSIM local bus");
136
return (0);
137
}
138
139
/*
140
* Add interrupt/addr range to the dev's resource list if present
141
*/
142
static void
143
iobus_add_intr(phandle_t devnode, struct iobus_devinfo *dinfo)
144
{
145
u_int intr = -1;
146
147
if (OF_getprop(devnode, "interrupt", &intr, sizeof(intr)) != -1) {
148
resource_list_add(&dinfo->id_resources,
149
SYS_RES_IRQ, 0, intr, intr, 1);
150
}
151
dinfo->id_interrupt = intr;
152
}
153
154
static void
155
iobus_add_reg(phandle_t devnode, struct iobus_devinfo *dinfo,
156
vm_offset_t iobus_off)
157
{
158
u_int size;
159
int i;
160
161
size = OF_getprop(devnode, "reg", dinfo->id_reg,sizeof(dinfo->id_reg));
162
163
if (size != -1) {
164
dinfo->id_nregs = size / (sizeof(dinfo->id_reg[0]));
165
166
for (i = 0; i < dinfo->id_nregs; i+= 3) {
167
/*
168
* Scale the absolute addresses back to iobus
169
* relative offsets. This is to better simulate
170
* macio
171
*/
172
dinfo->id_reg[i+1] -= iobus_off;
173
174
resource_list_add(&dinfo->id_resources,
175
SYS_RES_MEMORY, 0,
176
dinfo->id_reg[i+1],
177
dinfo->id_reg[i+1] +
178
dinfo->id_reg[i+2],
179
dinfo->id_reg[i+2]);
180
}
181
}
182
}
183
184
static int
185
iobus_attach(device_t dev)
186
{
187
struct iobus_softc *sc;
188
struct iobus_devinfo *dinfo;
189
phandle_t root;
190
phandle_t child;
191
device_t cdev;
192
char *name;
193
u_int reg[2];
194
int size;
195
196
sc = device_get_softc(dev);
197
sc->sc_node = ofw_bus_get_node(dev);
198
199
/*
200
* Find the base addr/size of the iobus, and initialize the
201
* resource manager
202
*/
203
size = OF_getprop(sc->sc_node, "reg", reg, sizeof(reg));
204
if (size == sizeof(reg)) {
205
sc->sc_addr = reg[0];
206
sc->sc_size = reg[1];
207
} else {
208
return (ENXIO);
209
}
210
211
sc->sc_mem_rman.rm_type = RMAN_ARRAY;
212
sc->sc_mem_rman.rm_descr = "IOBus Device Memory";
213
if (rman_init(&sc->sc_mem_rman) != 0) {
214
device_printf(dev,
215
"failed to init mem range resources\n");
216
return (ENXIO);
217
}
218
rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size);
219
220
/*
221
* Iterate through the sub-devices
222
*/
223
root = sc->sc_node;
224
225
for (child = OF_child(root); child != 0; child = OF_peer(child)) {
226
OF_getprop_alloc(child, "name", (void **)&name);
227
228
cdev = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
229
if (cdev != NULL) {
230
dinfo = malloc(sizeof(*dinfo), M_IOBUS, M_WAITOK);
231
memset(dinfo, 0, sizeof(*dinfo));
232
resource_list_init(&dinfo->id_resources);
233
dinfo->id_node = child;
234
dinfo->id_name = name;
235
iobus_add_intr(child, dinfo);
236
iobus_add_reg(child, dinfo, sc->sc_addr);
237
device_set_ivars(cdev, dinfo);
238
} else {
239
OF_prop_free(name);
240
}
241
}
242
243
bus_attach_children(dev);
244
return (0);
245
}
246
247
static int
248
iobus_print_child(device_t dev, device_t child)
249
{
250
struct iobus_devinfo *dinfo;
251
struct resource_list *rl;
252
int retval = 0;
253
254
dinfo = device_get_ivars(child);
255
rl = &dinfo->id_resources;
256
257
retval += bus_print_child_header(dev, child);
258
259
retval += printf(" offset 0x%x", dinfo->id_reg[1]);
260
retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd");
261
262
retval += bus_print_child_footer(dev, child);
263
264
return (retval);
265
}
266
267
static void
268
iobus_probe_nomatch(device_t dev, device_t child)
269
{
270
}
271
272
static int
273
iobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
274
{
275
struct iobus_devinfo *dinfo;
276
277
if ((dinfo = device_get_ivars(child)) == NULL)
278
return (ENOENT);
279
280
switch (which) {
281
case IOBUS_IVAR_NODE:
282
*result = dinfo->id_node;
283
break;
284
case IOBUS_IVAR_NAME:
285
*result = (uintptr_t)dinfo->id_name;
286
break;
287
case IOBUS_IVAR_NREGS:
288
*result = dinfo->id_nregs;
289
break;
290
case IOBUS_IVAR_REGS:
291
*result = (uintptr_t)dinfo->id_reg;
292
break;
293
default:
294
return (ENOENT);
295
}
296
297
return (0);
298
}
299
300
static int
301
iobus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
302
{
303
return (EINVAL);
304
}
305
306
static struct rman *
307
iobus_get_rman(device_t bus, int type, u_int flags)
308
{
309
struct iobus_softc *sc;
310
311
sc = device_get_softc(bus);
312
switch (type) {
313
case SYS_RES_MEMORY:
314
case SYS_RES_IOPORT:
315
return (&sc->sc_mem_rman);
316
default:
317
return (NULL);
318
}
319
}
320
321
static struct resource *
322
iobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
323
rman_res_t start, rman_res_t end, rman_res_t count,
324
u_int flags)
325
{
326
327
switch (type) {
328
case SYS_RES_MEMORY:
329
case SYS_RES_IOPORT:
330
return (bus_generic_rman_alloc_resource(bus, child, type, rid,
331
start, end, count, flags));
332
case SYS_RES_IRQ:
333
return (bus_alloc_resource(bus, type, rid, start, end, count,
334
flags));
335
default:
336
device_printf(bus, "unknown resource request from %s\n",
337
device_get_nameunit(child));
338
return (NULL);
339
}
340
}
341
342
static int
343
iobus_adjust_resource(device_t bus, device_t child, struct resource *r,
344
rman_res_t start, rman_res_t end)
345
{
346
347
switch (rman_get_type(r)) {
348
case SYS_RES_MEMORY:
349
case SYS_RES_IOPORT:
350
return (bus_generic_rman_adjust_resource(bus, child, r, start,
351
end));
352
case SYS_RES_IRQ:
353
return (bus_generic_adjust_resource(bus, child, r, start, end));
354
default:
355
return (EINVAL);
356
}
357
}
358
359
static int
360
iobus_release_resource(device_t bus, device_t child, struct resource *res)
361
{
362
363
switch (rman_get_type(res)) {
364
case SYS_RES_MEMORY:
365
case SYS_RES_IOPORT:
366
return (bus_generic_rman_release_resource(bus, child, res));
367
case SYS_RES_IRQ:
368
return (bus_generic_release_resource(bus, child, res));
369
default:
370
return (EINVAL);
371
}
372
}
373
374
static int
375
iobus_activate_resource(device_t bus, device_t child, struct resource *res)
376
{
377
378
switch (rman_get_type(res)) {
379
case SYS_RES_IRQ:
380
return (bus_generic_activate_resource(bus, child, res));
381
case SYS_RES_IOPORT:
382
case SYS_RES_MEMORY:
383
return (bus_generic_rman_activate_resource(bus, child, res));
384
default:
385
return (EINVAL);
386
}
387
}
388
389
static int
390
iobus_deactivate_resource(device_t bus, device_t child, struct resource *res)
391
{
392
393
switch (rman_get_type(res)) {
394
case SYS_RES_IRQ:
395
return (bus_generic_deactivate_resource(bus, child, res));
396
case SYS_RES_IOPORT:
397
case SYS_RES_MEMORY:
398
return (bus_generic_rman_deactivate_resource(bus, child, res));
399
default:
400
return (EINVAL);
401
}
402
}
403
404
static int
405
iobus_map_resource(device_t bus, device_t child, struct resource *r,
406
struct resource_map_request *argsp, struct resource_map *map)
407
{
408
struct resource_map_request args;
409
struct iobus_softc *sc;
410
rman_res_t length, start;
411
int error;
412
413
/* Resources must be active to be mapped. */
414
if (!(rman_get_flags(r) & RF_ACTIVE))
415
return (ENXIO);
416
417
/* Mappings are only supported on I/O and memory resources. */
418
switch (rman_get_type(r)) {
419
case SYS_RES_IOPORT:
420
case SYS_RES_MEMORY:
421
break;
422
default:
423
return (EINVAL);
424
}
425
426
resource_init_map_request(&args);
427
error = resource_validate_map_request(r, argsp, &args, &start, &length);
428
if (error)
429
return (error);
430
431
sc = device_get_softc(bus);
432
map->r_vaddr = pmap_mapdev_attr((vm_paddr_t)start + sc->sc_addr,
433
(vm_size_t)length, args.memattr);
434
if (map->r_vaddr == NULL)
435
return (ENOMEM);
436
map->r_bustag = &bs_le_tag;
437
map->r_bushandle = (vm_offset_t)map->r_vaddr;
438
map->r_size = length;
439
return (0);
440
}
441
442
static int
443
iobus_unmap_resource(device_t bus, device_t child, struct resource *r,
444
struct resource_map *map)
445
{
446
447
switch (rman_get_type(r)) {
448
case SYS_RES_IOPORT:
449
case SYS_RES_MEMORY:
450
pmap_unmapdev(map->r_vaddr, map->r_size);
451
return (0);
452
default:
453
return (EINVAL);
454
}
455
}
456
457