Do a major clean-up of the BUSDMA architecture. A large number of
[dragonfly.git] / sys / bus / pci / pci_compat.c
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
10 * 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 ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD: src/sys/pci/pci_compat.c,v 1.35.2.1 2001/10/14 21:14:14 luigi Exp $
1f7ab7c9 27 * $DragonFly: src/sys/bus/pci/pci_compat.c,v 1.12 2006/10/25 20:55:51 dillon Exp $
984263bc
MD
28 *
29 */
30
31#include "opt_bus.h"
32
dc5a7bd2 33/* for compatibility to FreeBSD-2.2 and 3.x versions of PCI code */
984263bc
MD
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/malloc.h>
dc5a7bd2 38#include <sys/module.h>
984263bc
MD
39
40#include <vm/vm.h>
41#include <vm/pmap.h>
984263bc
MD
42
43#include <sys/bus.h>
984263bc 44#include <sys/rman.h>
97359a5b 45#include <machine/smp.h>
dc5a7bd2 46#include <sys/interrupt.h>
984263bc 47
dc5a7bd2 48#include <sys/pciio.h>
1f2de5d4
MD
49#include "pcireg.h"
50#include "pcivar.h"
984263bc 51
e1bc6fbb
JS
52struct pci_compat_driver {
53 driver_t driver;
54 struct pci_device *dvp;
55};
56
984263bc
MD
57/* ------------------------------------------------------------------------- */
58
59u_long
60pci_conf_read(pcici_t cfg, u_long reg)
61{
62 return (pci_read_config(cfg->dev, reg, 4));
63}
64
65void
66pci_conf_write(pcici_t cfg, u_long reg, u_long data)
67{
68 pci_write_config(cfg->dev, reg, data, 4);
69}
70
71int
72pci_map_port(pcici_t cfg, u_long reg, pci_port_t* pa)
73{
74 int rid;
75 struct resource *res;
76
77 rid = reg;
78 res = bus_alloc_resource(cfg->dev, SYS_RES_IOPORT, &rid,
79 0, ~0, 1, RF_ACTIVE);
80 if (res) {
81 *pa = rman_get_start(res);
82 return (1);
83 }
84 return (0);
85}
86
87int
88pci_map_mem(pcici_t cfg, u_long reg, vm_offset_t* va, vm_offset_t* pa)
89{
90 int rid;
91 struct resource *res;
92
93 rid = reg;
94 res = bus_alloc_resource(cfg->dev, SYS_RES_MEMORY, &rid,
95 0, ~0, 1, RF_ACTIVE);
96 if (res) {
97 *pa = rman_get_start(res);
98 *va = (vm_offset_t) rman_get_virtual(res);
99 return (1);
100 }
101 return (0);
102}
103
104int
38787eef 105pci_map_int(pcici_t cfg, pci_inthand_t *handler, void *arg)
984263bc 106{
38787eef 107 return (pci_map_int_right(cfg, handler, arg, 0));
984263bc
MD
108}
109
110int
38787eef 111pci_map_int_right(pcici_t cfg, pci_inthand_t *handler, void *arg, u_int intflags)
984263bc
MD
112{
113 int error;
114#ifdef APIC_IO
115 int nextpin, muxcnt;
116#endif
117 if (cfg->intpin != 0) {
118 int irq = cfg->intline;
119 int rid = 0;
120 struct resource *res;
121 int flags = 0;
122 int resflags = RF_SHAREABLE|RF_ACTIVE;
123 void *ih;
124
984263bc 125 if (intflags & INTR_FAST)
e126caf1 126 flags |= INTR_FAST;
984263bc
MD
127 if (intflags & INTR_EXCL)
128 resflags &= ~RF_SHAREABLE;
984263bc
MD
129
130 res = bus_alloc_resource(cfg->dev, SYS_RES_IRQ, &rid,
131 irq, irq, 1, resflags);
132 if (!res) {
133 printf("pci_map_int: can't allocate interrupt\n");
134 return 0;
135 }
136
137 /*
138 * This is ugly. Translate the mask into an interrupt type.
139 */
984263bc
MD
140
141 error = BUS_SETUP_INTR(device_get_parent(cfg->dev), cfg->dev,
e9cb6d99 142 res, flags, handler, arg, &ih, NULL);
984263bc
MD
143 if (error != 0)
144 return 0;
145
146#ifdef NEW_BUS_PCI
147 /*
148 * XXX this apic stuff looks totally busted. It should
149 * move to the nexus code which actually registers the
150 * interrupt.
151 */
152#endif
153
154#ifdef APIC_IO
155 nextpin = next_apic_irq(irq);
156
157 if (nextpin < 0)
158 return 1;
159
160 /*
161 * Attempt handling of some broken mp tables.
162 *
163 * It's OK to yell (since the mp tables are broken).
164 *
165 * Hanging in the boot is not OK
166 */
167
168 muxcnt = 2;
169 nextpin = next_apic_irq(nextpin);
170 while (muxcnt < 5 && nextpin >= 0) {
171 muxcnt++;
172 nextpin = next_apic_irq(nextpin);
173 }
174 if (muxcnt >= 5) {
175 printf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n");
176 return 0;
177 }
178
179 printf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt);
180
181 nextpin = next_apic_irq(irq);
182 while (nextpin >= 0) {
183 rid = 0;
184 res = bus_alloc_resource(cfg->dev, SYS_RES_IRQ, &rid,
185 nextpin, nextpin, 1,
186 resflags);
187 if (!res) {
188 printf("pci_map_int: can't allocate extra interrupt\n");
189 return 0;
190 }
191 error = BUS_SETUP_INTR(device_get_parent(cfg->dev),
192 cfg->dev, res, flags,
e9cb6d99 193 handler, arg, &ih, NULL);
984263bc
MD
194 if (error != 0) {
195 printf("pci_map_int: BUS_SETUP_INTR failed\n");
196 return 0;
197 }
198 printf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq);
199 nextpin = next_apic_irq(nextpin);
200 }
201#endif
202 }
203 return (1);
204}
205
206int
207pci_unmap_int(pcici_t cfg)
208{
209 return (0); /* not supported, yet, since cfg doesn't know about idesc */
210}
211
212pcici_t
213pci_get_parent_from_tag(pcici_t tag)
214{
215 return (pcici_t)pci_devlist_get_parent(tag);
216}
217
218int
219pci_get_bus_from_tag(pcici_t tag)
220{
221 return tag->bus;
222}
223
dc5a7bd2
JS
224/*
225 * A simple driver to wrap the old pci driver mechanism for back-compat.
226 */
227
228static int
229pci_compat_probe(device_t dev)
230{
231 struct pci_device *dvp;
232 struct pci_devinfo *dinfo;
233 pcicfgregs *cfg;
234 const char *name;
235 int error;
236
237 dinfo = device_get_ivars(dev);
238 cfg = &dinfo->cfg;
e1bc6fbb 239 dvp = ((struct pci_compat_driver *)device_get_driver(dev))->dvp;
dc5a7bd2
JS
240
241 /*
242 * Do the wrapped probe.
243 */
244 error = ENXIO;
245 if (dvp && dvp->pd_probe) {
246 name = dvp->pd_probe(cfg, (cfg->device << 16) + cfg->vendor);
247 if (name) {
248 device_set_desc_copy(dev, name);
249 /* Allow newbus drivers to match "better" */
250 error = -200;
251 }
252 }
253
254 return error;
255}
256
257static int
258pci_compat_attach(device_t dev)
259{
260 struct pci_device *dvp;
261 struct pci_devinfo *dinfo;
262 pcicfgregs *cfg;
263 int unit;
264
265 dinfo = device_get_ivars(dev);
266 cfg = &dinfo->cfg;
e1bc6fbb 267 dvp = ((struct pci_compat_driver *)device_get_driver(dev))->dvp;
dc5a7bd2
JS
268
269 unit = device_get_unit(dev);
270 if (unit > *dvp->pd_count)
271 *dvp->pd_count = unit;
272 if (dvp->pd_attach)
273 dvp->pd_attach(cfg, unit);
274 device_printf(dev, "driver is using old-style compatability shims\n");
275 return 0;
276}
277
278static device_method_t pci_compat_methods[] = {
279 /* Device interface */
280 DEVMETHOD(device_probe, pci_compat_probe),
281 DEVMETHOD(device_attach, pci_compat_attach),
282
283 { 0, 0 }
284};
285
286/*
287 * Create a new style driver around each old pci driver.
288 */
289int
290compat_pci_handler(module_t mod, int type, void *data)
291{
292 struct pci_device *dvp = (struct pci_device *)data;
e1bc6fbb 293 struct pci_compat_driver *driver;
dc5a7bd2
JS
294 devclass_t pci_devclass = devclass_find("pci");
295
296 switch (type) {
297 case MOD_LOAD:
efda3bd0 298 driver = kmalloc(sizeof(struct pci_compat_driver), M_DEVBUF, M_WAITOK | M_ZERO);
e1bc6fbb
JS
299 driver->driver.name = dvp->pd_name;
300 driver->driver.methods = pci_compat_methods;
301 driver->driver.size = sizeof(struct pci_devinfo *);
302 driver->dvp = dvp;
303 devclass_add_driver(pci_devclass, (driver_t *)driver);
dc5a7bd2
JS
304 break;
305 case MOD_UNLOAD:
306 printf("%s: module unload not supported!\n", dvp->pd_name);
307 return EOPNOTSUPP;
308 default:
309 break;
310 }
311 return 0;
312}