FreeBSD/Linux Kernel Cross Reference
sys/sparc64/isa/isa.c
1 /*-
2 * Copyright (c) 1998 Doug Rabson
3 * Copyright (c) 2001 Thomas Moestl <tmm@FreeBSD.org>
4 * 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 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * from: FreeBSD: src/sys/alpha/isa/isa.c,v 1.26 2001/07/11
28 *
29 * $FreeBSD: releng/5.4/sys/sparc64/isa/isa.c 133589 2004-08-12 17:41:33Z marius $
30 */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35
36 #include <machine/bus.h>
37
38 #include <sys/rman.h>
39
40 #include <isa/isareg.h>
41 #include <isa/isavar.h>
42 #include <isa/isa_common.h>
43
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/openfirm.h>
46
47 #include <machine/resource.h>
48
49 #include <dev/pci/pcireg.h>
50 #include <dev/pci/pcivar.h>
51
52 #include <sparc64/pci/ofw_pci.h>
53 #include <sparc64/isa/ofw_isa.h>
54
55 /* There can be only one ISA bus, so it is safe to use globals. */
56 bus_space_tag_t isa_io_bt = NULL;
57 bus_space_handle_t isa_io_hdl;
58 bus_space_tag_t isa_mem_bt = NULL;
59 bus_space_handle_t isa_mem_hdl;
60
61 static u_int64_t isa_io_base;
62 static u_int64_t isa_io_limit;
63 static u_int64_t isa_mem_base;
64 static u_int64_t isa_mem_limit;
65
66 device_t isa_bus_device;
67
68 static phandle_t isab_node;
69 static ofw_pci_intr_t isa_ino[8];
70 struct ofw_bus_iinfo isa_iinfo;
71
72 /*
73 * XXX: This is really partly partly PCI-specific, but unfortunately is
74 * differently enough to have to duplicate it here...
75 */
76 #define ISAB_RANGE_PHYS(r) \
77 (((u_int64_t)(r)->phys_mid << 32) | (u_int64_t)(r)->phys_lo)
78 #define ISAB_RANGE_SPACE(r) (((r)->phys_hi >> 24) & 0x03)
79
80 #define ISAR_SPACE_IO 0x01
81 #define ISAR_SPACE_MEM 0x02
82
83 #define INRANGE(x, start, end) ((x) >= (start) && (x) <= (end))
84
85 static int isa_route_intr_res(device_t, u_long, u_long);
86
87 intrmask_t
88 isa_irq_pending(void)
89 {
90 intrmask_t pending;
91 int i;
92
93 /* XXX: Is this correct? */
94 for (i = 7, pending = 0; i >= 0; i--) {
95 pending <<= 1;
96 if (isa_ino[i] != PCI_INVALID_IRQ) {
97 pending |= (OFW_PCI_INTR_PENDING(isa_bus_device,
98 isa_ino[i]) == 0) ? 0 : 1;
99 }
100 }
101 return (pending);
102 }
103
104 void
105 isa_init(device_t dev)
106 {
107 device_t bridge;
108 phandle_t node;
109 ofw_isa_intr_t ino;
110 struct isa_ranges *br;
111 int nbr, i;
112
113 /* The parent of the bus must be a PCI-ISA bridge. */
114 bridge = device_get_parent(dev);
115 isab_node = ofw_bus_get_node(bridge);
116 nbr = OF_getprop_alloc(isab_node, "ranges", sizeof(*br), (void **)&br);
117 if (nbr <= 0)
118 panic("isa_init: cannot get bridge range property");
119
120 ofw_bus_setup_iinfo(isab_node, &isa_iinfo, sizeof(ofw_isa_intr_t));
121
122 /*
123 * This is really a bad kludge; however, it is needed to provide
124 * isa_irq_pending(), which is unfortunately still used by some
125 * drivers.
126 */
127 for (i = 0; i < 8; i++)
128 isa_ino[i] = PCI_INVALID_IRQ;
129 for (node = OF_child(isab_node); node != 0; node = OF_peer(node)) {
130 if (OF_getprop(node, "interrupts", &ino, sizeof(ino)) == -1)
131 continue;
132 if (ino > 7)
133 panic("isa_init: XXX: ino too large");
134 isa_ino[ino] = ofw_isa_route_intr(bridge, node, &isa_iinfo,
135 ino);
136 }
137
138 for (nbr -= 1; nbr >= 0; nbr--) {
139 switch(ISAB_RANGE_SPACE(br + nbr)) {
140 case ISAR_SPACE_IO:
141 /* This is probably always 0. */
142 isa_io_base = ISAB_RANGE_PHYS(&br[nbr]);
143 isa_io_limit = br[nbr].size;
144 isa_io_hdl = OFW_PCI_GET_BUS_HANDLE(bridge,
145 SYS_RES_IOPORT, isa_io_base, &isa_io_bt);
146 break;
147 case ISAR_SPACE_MEM:
148 /* This is probably always 0. */
149 isa_mem_base = ISAB_RANGE_PHYS(&br[nbr]);
150 isa_mem_limit = br[nbr].size;
151 isa_mem_hdl = OFW_PCI_GET_BUS_HANDLE(bridge,
152 SYS_RES_MEMORY, isa_mem_base, &isa_mem_bt);
153 break;
154 }
155 }
156 free(br, M_OFWPROP);
157 }
158
159 static int
160 isa_route_intr_res(device_t bus, u_long start, u_long end)
161 {
162 int res;
163
164 if (start != end) {
165 panic("isa_route_intr_res: allocation of interrupt range not "
166 "supported (0x%lx - 0x%lx)", start, end);
167 }
168 if (start > 7)
169 panic("isa_route_intr_res: start out of isa range");
170 res = isa_ino[start];
171 if (res == PCI_INVALID_IRQ)
172 device_printf(bus, "could not map interrupt %d\n", res);
173 return (res);
174 }
175
176 struct resource *
177 isa_alloc_resource(device_t bus, device_t child, int type, int *rid,
178 u_long start, u_long end, u_long count, u_int flags)
179 {
180 /*
181 * Consider adding a resource definition.
182 */
183 int passthrough = (device_get_parent(child) != bus);
184 int isdefault = (start == 0UL && end == ~0UL);
185 struct isa_device* idev = DEVTOISA(child);
186 struct resource_list *rl = &idev->id_resources;
187 struct resource_list_entry *rle;
188 u_long base, limit;
189
190 if (!passthrough && !isdefault) {
191 rle = resource_list_find(rl, type, *rid);
192 if (!rle) {
193 if (*rid < 0)
194 return 0;
195 switch (type) {
196 case SYS_RES_IRQ:
197 if (*rid >= ISA_NIRQ)
198 return 0;
199 break;
200 case SYS_RES_DRQ:
201 if (*rid >= ISA_NDRQ)
202 return 0;
203 break;
204 case SYS_RES_MEMORY:
205 if (*rid >= ISA_NMEM)
206 return 0;
207 break;
208 case SYS_RES_IOPORT:
209 if (*rid >= ISA_NPORT)
210 return 0;
211 break;
212 default:
213 return 0;
214 }
215 resource_list_add(rl, type, *rid, start, end, count);
216 }
217 }
218
219 /*
220 * Add the base, change default allocations to be between base and
221 * limit, and reject allocations if a resource type is not enabled.
222 */
223 base = limit = 0;
224 switch(type) {
225 case SYS_RES_MEMORY:
226 if (isa_mem_bt == NULL)
227 return (NULL);
228 base = isa_mem_base;
229 limit = base + isa_mem_limit;
230 break;
231 case SYS_RES_IOPORT:
232 if (isa_io_bt == NULL)
233 return (NULL);
234 base = isa_io_base;
235 limit = base + isa_io_limit;
236 break;
237 case SYS_RES_IRQ:
238 if (isdefault && passthrough)
239 panic("isa_alloc_resource: cannot pass through default "
240 "irq allocation");
241 if (!isdefault) {
242 start = end = isa_route_intr_res(bus, start, end);
243 if (start == PCI_INVALID_IRQ)
244 return (NULL);
245 }
246 break;
247 default:
248 panic("isa_alloc_resource: unsupported resource type %d", type);
249 }
250 if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
251 start = ulmin(start + base, limit);
252 end = ulmin(end + base, limit);
253 }
254
255 /*
256 * This inlines a modified resource_list_alloc(); this is needed
257 * because the resources need to have offsets added to them, which
258 * cannot be done beforehand without patching the resource list entries
259 * (which is ugly).
260 */
261 if (passthrough) {
262 return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child,
263 type, rid, start, end, count, flags));
264 }
265
266 rle = resource_list_find(rl, type, *rid);
267 if (rle == NULL)
268 return (NULL); /* no resource of that type/rid */
269
270 if (rle->res != NULL)
271 panic("isa_alloc_resource: resource entry is busy");
272
273 if (isdefault) {
274 start = rle->start;
275 count = ulmax(count, rle->count);
276 end = ulmax(rle->end, start + count - 1);
277 switch (type) {
278 case SYS_RES_MEMORY:
279 case SYS_RES_IOPORT:
280 start += base;
281 end += base;
282 if (!INRANGE(start, base, limit) ||
283 !INRANGE(end, base, limit))
284 return (NULL);
285 break;
286 case SYS_RES_IRQ:
287 start = end = isa_route_intr_res(bus, start, end);
288 if (start == PCI_INVALID_IRQ)
289 return (NULL);
290 break;
291 }
292 }
293
294 rle->res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child,
295 type, rid, start, end, count, flags);
296
297 /*
298 * Record the new range.
299 */
300 if (rle->res != NULL) {
301 rle->start = rman_get_start(rle->res) - base;
302 rle->end = rman_get_end(rle->res) - base;
303 rle->count = count;
304 }
305
306 return (rle->res);
307 }
308
309 int
310 isa_release_resource(device_t bus, device_t child, int type, int rid,
311 struct resource *res)
312 {
313 struct isa_device* idev = DEVTOISA(child);
314 struct resource_list *rl = &idev->id_resources;
315
316 return (resource_list_release(rl, bus, child, type, rid, res));
317 }
318
319 int
320 isa_setup_intr(device_t dev, device_t child,
321 struct resource *irq, int flags,
322 driver_intr_t *intr, void *arg, void **cookiep)
323 {
324
325 /*
326 * Just pass through. This is going to be handled by either one of
327 * the parent PCI buses or the nexus device.
328 * The interrupt was routed at allocation time.
329 */
330 return (BUS_SETUP_INTR(device_get_parent(dev), child, irq, flags, intr,
331 arg, cookiep));
332 }
333
334 int
335 isa_teardown_intr(device_t dev, device_t child,
336 struct resource *irq, void *cookie)
337 {
338
339 return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, irq, cookie));
340 }
Cache object: df48aea37159ca8ad11f5a9e480f2da6
|