Merge branch 'master' of ssh://crater.dragonflybsd.org/repository/git/dragonfly
[dragonfly.git] / sys / bus / pci / pci_compat.c
blobe194780bb86e9f2c8bfbcd851c728eba3508fb2a
1 /*
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/sys/pci/pci_compat.c,v 1.35.2.1 2001/10/14 21:14:14 luigi Exp $
27 * $DragonFly: src/sys/bus/pci/pci_compat.c,v 1.14 2007/05/13 18:33:56 swildner Exp $
31 #include "opt_bus.h"
33 /* for compatibility to FreeBSD-2.2 and 3.x versions of PCI code */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
40 #include <vm/vm.h>
41 #include <vm/pmap.h>
43 #include <sys/bus.h>
44 #include <sys/rman.h>
45 #include <machine/smp.h>
46 #include <sys/interrupt.h>
48 #include <sys/pciio.h>
49 #include "pcireg.h"
50 #include "pcivar.h"
52 struct pci_compat_driver {
53 driver_t driver;
54 struct pci_device *dvp;
57 /* ------------------------------------------------------------------------- */
59 u_long
60 pci_conf_read(pcici_t cfg, u_long reg)
62 return (pci_read_config(cfg->dev, reg, 4));
65 void
66 pci_conf_write(pcici_t cfg, u_long reg, u_long data)
68 pci_write_config(cfg->dev, reg, data, 4);
71 int
72 pci_map_port(pcici_t cfg, u_long reg, pci_port_t* pa)
74 int rid;
75 struct resource *res;
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);
84 return (0);
87 int
88 pci_map_mem(pcici_t cfg, u_long reg, vm_offset_t* va, vm_offset_t* pa)
90 int rid;
91 struct resource *res;
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);
101 return (0);
105 pci_map_int(pcici_t cfg, pci_inthand_t *handler, void *arg)
107 return (pci_map_int_right(cfg, handler, arg, 0));
111 pci_map_int_right(pcici_t cfg, pci_inthand_t *handler, void *arg, u_int intflags)
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;
125 if (intflags & INTR_FAST)
126 flags |= INTR_FAST;
127 if (intflags & INTR_EXCL)
128 resflags &= ~RF_SHAREABLE;
130 res = bus_alloc_resource(cfg->dev, SYS_RES_IRQ, &rid,
131 irq, irq, 1, resflags);
132 if (!res) {
133 kprintf("pci_map_int: can't allocate interrupt\n");
134 return 0;
138 * This is ugly. Translate the mask into an interrupt type.
141 error = BUS_SETUP_INTR(device_get_parent(cfg->dev), cfg->dev,
142 res, flags, handler, arg, &ih, NULL);
143 if (error != 0)
144 return 0;
146 #ifdef NEW_BUS_PCI
148 * XXX this apic stuff looks totally busted. It should
149 * move to the nexus code which actually registers the
150 * interrupt.
152 #endif
154 #ifdef APIC_IO
155 nextpin = next_apic_irq(irq);
157 if (nextpin < 0)
158 return 1;
161 * Attempt handling of some broken mp tables.
163 * It's OK to yell (since the mp tables are broken).
165 * Hanging in the boot is not OK
168 muxcnt = 2;
169 nextpin = next_apic_irq(nextpin);
170 while (muxcnt < 5 && nextpin >= 0) {
171 muxcnt++;
172 nextpin = next_apic_irq(nextpin);
174 if (muxcnt >= 5) {
175 kprintf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n");
176 return 0;
179 kprintf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt);
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 kprintf("pci_map_int: can't allocate extra interrupt\n");
189 return 0;
191 error = BUS_SETUP_INTR(device_get_parent(cfg->dev),
192 cfg->dev, res, flags,
193 handler, arg, &ih, NULL);
194 if (error != 0) {
195 kprintf("pci_map_int: BUS_SETUP_INTR failed\n");
196 return 0;
198 kprintf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq);
199 nextpin = next_apic_irq(nextpin);
201 #endif
203 return (1);
207 pci_unmap_int(pcici_t cfg)
209 return (0); /* not supported, yet, since cfg doesn't know about idesc */
212 pcici_t
213 pci_get_parent_from_tag(pcici_t tag)
215 return (pcici_t)pci_devlist_get_parent(tag);
219 pci_get_bus_from_tag(pcici_t tag)
221 return tag->bus;
225 * A simple driver to wrap the old pci driver mechanism for back-compat.
228 static int
229 pci_compat_probe(device_t dev)
231 struct pci_device *dvp;
232 struct pci_devinfo *dinfo;
233 pcicfgregs *cfg;
234 const char *name;
235 int error;
237 dinfo = device_get_ivars(dev);
238 cfg = &dinfo->cfg;
239 dvp = ((struct pci_compat_driver *)device_get_driver(dev))->dvp;
242 * Do the wrapped probe.
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;
254 return error;
257 static int
258 pci_compat_attach(device_t dev)
260 struct pci_device *dvp;
261 struct pci_devinfo *dinfo;
262 pcicfgregs *cfg;
263 int unit;
265 dinfo = device_get_ivars(dev);
266 cfg = &dinfo->cfg;
267 dvp = ((struct pci_compat_driver *)device_get_driver(dev))->dvp;
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 compatibility shims\n");
275 return 0;
278 static device_method_t pci_compat_methods[] = {
279 /* Device interface */
280 DEVMETHOD(device_probe, pci_compat_probe),
281 DEVMETHOD(device_attach, pci_compat_attach),
283 { 0, 0 }
287 * Create a new style driver around each old pci driver.
290 compat_pci_handler(module_t mod, int type, void *data)
292 struct pci_device *dvp = (struct pci_device *)data;
293 struct pci_compat_driver *driver;
294 devclass_t pci_devclass = devclass_find("pci");
296 switch (type) {
297 case MOD_LOAD:
298 driver = kmalloc(sizeof(struct pci_compat_driver), M_DEVBUF, M_WAITOK | M_ZERO);
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);
304 break;
305 case MOD_UNLOAD:
306 kprintf("%s: module unload not supported!\n", dvp->pd_name);
307 return EOPNOTSUPP;
308 default:
309 break;
311 return 0;