1/*-
2 * Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
3 * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
4 * Copyright (c) 2000 BSDi
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34/*
35 * PCI:ISA bridge support
36 */
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <sys/bus.h>
43
44#include <isa/isavar.h>
45#include <dev/pci/pcivar.h>
46#include <dev/pci/pcireg.h>
47
48#include <machine/bus.h>
49#include <sys/rman.h>
50#include <machine/resource.h>
51
52static int	isab_pci_probe(device_t dev);
53static int	isab_pci_attach(device_t dev);
54static struct resource *	isab_pci_alloc_resource(device_t dev,
55    device_t child, int type, int *rid, rman_res_t start, rman_res_t end,
56    rman_res_t count, u_int flags);
57static int	isab_pci_release_resource(device_t dev, device_t child,
58    int type, int rid, struct resource *r);
59
60static device_method_t isab_methods[] = {
61    /* Device interface */
62    DEVMETHOD(device_probe,		isab_pci_probe),
63    DEVMETHOD(device_attach,		isab_pci_attach),
64    DEVMETHOD(device_detach,		bus_generic_detach),
65    DEVMETHOD(device_shutdown,		bus_generic_shutdown),
66    DEVMETHOD(device_suspend,		bus_generic_suspend),
67    DEVMETHOD(device_resume,		bus_generic_resume),
68
69    /* Bus interface */
70    DEVMETHOD(bus_add_child,		bus_generic_add_child),
71    DEVMETHOD(bus_alloc_resource,	isab_pci_alloc_resource),
72    DEVMETHOD(bus_release_resource,	isab_pci_release_resource),
73    DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
74    DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
75    DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
76    DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
77
78    DEVMETHOD_END
79};
80
81struct isab_pci_resource {
82	struct resource	*ip_res;
83	int	ip_refs;
84};
85
86struct isab_pci_softc {
87	struct isab_pci_resource isab_pci_res[PCIR_MAX_BAR_0 + 1];
88};
89
90static driver_t isab_driver = {
91    "isab",
92    isab_methods,
93    sizeof(struct isab_pci_softc),
94};
95
96DRIVER_MODULE(isab, pci, isab_driver, isab_devclass, 0, 0);
97
98/*
99 * XXX we need to add a quirk list here for bridges that don't correctly
100 *     report themselves.
101 */
102static int
103isab_pci_probe(device_t dev)
104{
105    int		matched = 0;
106
107    /*
108     * Try for a generic match based on class/subclass.
109     */
110    if ((pci_get_class(dev) == PCIC_BRIDGE) &&
111	(pci_get_subclass(dev) == PCIS_BRIDGE_ISA)) {
112	matched = 1;
113    } else {
114	/*
115	 * These are devices that we *know* are PCI:ISA bridges.
116	 * Sometimes, however, they don't report themselves as
117	 * such.  Check in case one of them is pretending to be
118	 * something else.
119	 */
120	switch (pci_get_devid(dev)) {
121	case 0x04848086:	/* Intel 82378ZB/82378IB */
122	case 0x122e8086:	/* Intel 82371FB */
123	case 0x70008086:	/* Intel 82371SB */
124	case 0x71108086:	/* Intel 82371AB */
125	case 0x71988086:	/* Intel 82443MX */
126	case 0x24108086:	/* Intel 82801AA (ICH) */
127	case 0x24208086:	/* Intel 82801AB (ICH0) */
128	case 0x24408086:	/* Intel 82801AB (ICH2) */
129	case 0x00061004:	/* VLSI 82C593 */
130	case 0x05861106:	/* VIA 82C586 */
131	case 0x05961106:	/* VIA 82C596 */
132	case 0x06861106:	/* VIA 82C686 */
133	case 0x153310b9:	/* AcerLabs M1533 */
134	case 0x154310b9:	/* AcerLabs M1543 */
135	case 0x00081039:	/* SiS 85c503 */
136	case 0x00001078:	/* Cyrix Cx5510 */
137	case 0x01001078:	/* Cyrix Cx5530 */
138	case 0xc7001045:	/* OPTi 82C700 (FireStar) */
139	case 0x00011033:	/* NEC 0001 (C-bus) */
140	case 0x002c1033:	/* NEC 002C (C-bus) */
141	case 0x003b1033:	/* NEC 003B (C-bus) */
142	case 0x886a1060:	/* UMC UM8886 ISA */
143	case 0x02001166:	/* ServerWorks IB6566 PCI */
144	    if (bootverbose)
145		printf("PCI-ISA bridge with incorrect subclass 0x%x\n",
146		       pci_get_subclass(dev));
147	    matched = 1;
148	    break;
149
150	default:
151	    break;
152	}
153    }
154
155    if (matched) {
156	device_set_desc(dev, "PCI-ISA bridge");
157	return(-10000);
158    }
159    return(ENXIO);
160}
161
162static int
163isab_pci_attach(device_t dev)
164{
165
166	bus_generic_probe(dev);
167	return (isab_attach(dev));
168}
169
170static struct resource *
171isab_pci_alloc_resource(device_t dev, device_t child, int type, int *rid,
172    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
173{
174	struct isab_pci_softc *sc;
175	int bar;
176
177	if (device_get_parent(child) != dev)
178		return bus_generic_alloc_resource(dev, child, type, rid, start,
179		    end, count, flags);
180
181	switch (type) {
182	case SYS_RES_MEMORY:
183	case SYS_RES_IOPORT:
184		/*
185		 * For BARs, we cache the resource so that we only allocate it
186		 * from the PCI bus once.
187		 */
188		bar = PCI_RID2BAR(*rid);
189		if (bar < 0 || bar > PCIR_MAX_BAR_0)
190			return (NULL);
191		sc = device_get_softc(dev);
192		if (sc->isab_pci_res[bar].ip_res == NULL)
193			sc->isab_pci_res[bar].ip_res = bus_alloc_resource(dev, type,
194			    rid, start, end, count, flags);
195		if (sc->isab_pci_res[bar].ip_res != NULL)
196			sc->isab_pci_res[bar].ip_refs++;
197		return (sc->isab_pci_res[bar].ip_res);
198	}
199
200	return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid,
201		start, end, count, flags));
202}
203
204static int
205isab_pci_release_resource(device_t dev, device_t child, int type, int rid,
206    struct resource *r)
207{
208	struct isab_pci_softc *sc;
209	int bar, error;
210
211	if (device_get_parent(child) != dev)
212		return bus_generic_release_resource(dev, child, type, rid, r);
213
214	switch (type) {
215	case SYS_RES_MEMORY:
216	case SYS_RES_IOPORT:
217		/*
218		 * For BARs, we release the resource from the PCI bus
219		 * when the last child reference goes away.
220		 */
221		bar = PCI_RID2BAR(rid);
222		if (bar < 0 || bar > PCIR_MAX_BAR_0)
223			return (EINVAL);
224		sc = device_get_softc(dev);
225		if (sc->isab_pci_res[bar].ip_res == NULL)
226			return (EINVAL);
227		KASSERT(sc->isab_pci_res[bar].ip_res == r,
228		    ("isa_pci resource mismatch"));
229		if (sc->isab_pci_res[bar].ip_refs > 1) {
230			sc->isab_pci_res[bar].ip_refs--;
231			return (0);
232		}
233		KASSERT(sc->isab_pci_res[bar].ip_refs > 0,
234		    ("isa_pci resource reference count underflow"));
235		error = bus_release_resource(dev, type, rid, r);
236		if (error == 0) {
237			sc->isab_pci_res[bar].ip_res = NULL;
238			sc->isab_pci_res[bar].ip_refs = 0;
239		}
240		return (error);
241	}
242
243	return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type,
244		rid, r));
245}
246