if_gem_pci.c revision 172334
1139749Simp/*-
291398Stmm * Copyright (C) 2001 Eduardo Horvath.
391398Stmm * All rights reserved.
491398Stmm *
591398Stmm *
691398Stmm * Redistribution and use in source and binary forms, with or without
791398Stmm * modification, are permitted provided that the following conditions
891398Stmm * are met:
991398Stmm * 1. Redistributions of source code must retain the above copyright
1091398Stmm *    notice, this list of conditions and the following disclaimer.
1191398Stmm * 2. Redistributions in binary form must reproduce the above copyright
1291398Stmm *    notice, this list of conditions and the following disclaimer in the
1391398Stmm *    documentation and/or other materials provided with the distribution.
1491398Stmm *
1591398Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR  ``AS IS'' AND
1691398Stmm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1791398Stmm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1891398Stmm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR  BE LIABLE
1991398Stmm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2091398Stmm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2191398Stmm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2291398Stmm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2391398Stmm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2491398Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2591398Stmm * SUCH DAMAGE.
2691398Stmm *
2791398Stmm *	from: NetBSD: if_gem_pci.c,v 1.7 2001/10/18 15:09:15 thorpej Exp
2891398Stmm */
2991398Stmm
30119418Sobrien#include <sys/cdefs.h>
31119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/gem/if_gem_pci.c 172334 2007-09-26 21:14:18Z marius $");
32119418Sobrien
3391398Stmm/*
34172334Smarius * PCI bindings for Apple GMAC, Sun ERI and Sun GEM Ethernet controllers
3591398Stmm */
3691398Stmm
3791398Stmm#include <sys/param.h>
3891398Stmm#include <sys/systm.h>
3991398Stmm#include <sys/bus.h>
4091398Stmm#include <sys/kernel.h>
41148369Smarius#include <sys/lock.h>
42130026Sphk#include <sys/module.h>
43148369Smarius#include <sys/mutex.h>
4491398Stmm#include <sys/resource.h>
45172334Smarius#include <sys/rman.h>
4691398Stmm#include <sys/socket.h>
4791398Stmm
4891398Stmm#include <net/ethernet.h>
4991398Stmm#include <net/if.h>
5091398Stmm
5191398Stmm#include <machine/bus.h>
52172334Smarius#if defined(__powerpc__) || defined(__sparc64__)
53119696Smarcel#include <dev/ofw/openfirm.h>
5491398Stmm#include <machine/ofw_machdep.h>
55172334Smarius#endif
56172334Smarius#include <machine/resource.h>
5791398Stmm
5891398Stmm#include <dev/gem/if_gemreg.h>
5991398Stmm#include <dev/gem/if_gemvar.h>
6091398Stmm
61172334Smarius#include <dev/pci/pcireg.h>
6291398Stmm#include <dev/pci/pcivar.h>
6391398Stmm
6491398Stmm#include "miibus_if.h"
6591398Stmm
6692739Salfredstatic int	gem_pci_probe(device_t);
6792739Salfredstatic int	gem_pci_attach(device_t);
68108964Stmmstatic int	gem_pci_detach(device_t);
69108964Stmmstatic int	gem_pci_suspend(device_t);
70108964Stmmstatic int	gem_pci_resume(device_t);
7191398Stmm
7291398Stmmstatic device_method_t gem_pci_methods[] = {
7391398Stmm	/* Device interface */
7491398Stmm	DEVMETHOD(device_probe,		gem_pci_probe),
7591398Stmm	DEVMETHOD(device_attach,	gem_pci_attach),
76108964Stmm	DEVMETHOD(device_detach,	gem_pci_detach),
77108964Stmm	DEVMETHOD(device_suspend,	gem_pci_suspend),
78108964Stmm	DEVMETHOD(device_resume,	gem_pci_resume),
79108964Stmm	/* Use the suspend handler here, it is all that is required. */
80108964Stmm	DEVMETHOD(device_shutdown,	gem_pci_suspend),
8191398Stmm
8291398Stmm	/* bus interface */
8391398Stmm	DEVMETHOD(bus_print_child,	bus_generic_print_child),
8491398Stmm	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
8591398Stmm
8691398Stmm	/* MII interface */
8791398Stmm	DEVMETHOD(miibus_readreg,	gem_mii_readreg),
8891398Stmm	DEVMETHOD(miibus_writereg,	gem_mii_writereg),
8991398Stmm	DEVMETHOD(miibus_statchg,	gem_mii_statchg),
9091398Stmm
9191398Stmm	{ 0, 0 }
9291398Stmm};
9391398Stmm
9491398Stmmstatic driver_t gem_pci_driver = {
9591398Stmm	"gem",
9691398Stmm	gem_pci_methods,
97169269Sphk	sizeof(struct gem_softc)
9891398Stmm};
9991398Stmm
100113506SmdoddDRIVER_MODULE(gem, pci, gem_pci_driver, gem_devclass, 0, 0);
101113506SmdoddMODULE_DEPEND(gem, pci, 1, 1, 1);
102113506SmdoddMODULE_DEPEND(gem, ether, 1, 1, 1);
10391398Stmm
104172334Smariusstatic const struct gem_pci_dev {
105172334Smarius	uint32_t	gpd_devid;
106172334Smarius	int		gpd_variant;
107172334Smarius	const char	*gpd_desc;
10891398Stmm} gem_pci_devlist[] = {
109172334Smarius	{ 0x1101108e, GEM_SUN_ERI,	"Sun ERI 10/100 Ethernet" },
110172334Smarius	{ 0x2bad108e, GEM_SUN_GEM,	"Sun GEM Gigabit Ethernet" },
111172334Smarius	{ 0x0021106b, GEM_APPLE_GMAC,	"Apple UniNorth GMAC Ethernet" },
112172334Smarius	{ 0x0024106b, GEM_APPLE_GMAC,	"Apple Pangea GMAC Ethernet" },
113172334Smarius	{ 0x0032106b, GEM_APPLE_GMAC,	"Apple UniNorth2 GMAC Ethernet" },
114172334Smarius	{ 0x004c106b, GEM_APPLE_K2_GMAC,"Apple K2 GMAC Ethernet" },
115172334Smarius	{ 0x0051106b, GEM_APPLE_GMAC,	"Apple Shasta GMAC Ethernet" },
116172334Smarius	{ 0x006b106b, GEM_APPLE_GMAC,	"Apple Intrepid 2 GMAC Ethernet" },
117123851Sobrien	{ 0, 0, NULL }
11891398Stmm};
11991398Stmm
12091398Stmmstatic int
12191398Stmmgem_pci_probe(dev)
12291398Stmm	device_t dev;
12391398Stmm{
12491398Stmm	int i;
12591398Stmm
12691398Stmm	for (i = 0; gem_pci_devlist[i].gpd_desc != NULL; i++) {
127172334Smarius		if (pci_get_devid(dev) == gem_pci_devlist[i].gpd_devid) {
12891398Stmm			device_set_desc(dev, gem_pci_devlist[i].gpd_desc);
129143161Simp			return (BUS_PROBE_DEFAULT);
13091398Stmm		}
13191398Stmm	}
13291398Stmm
13391398Stmm	return (ENXIO);
13491398Stmm}
13591398Stmm
136169269Sphkstatic struct resource_spec gem_pci_res_spec[] = {
137172334Smarius	{ SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE },
138169269Sphk	{ SYS_RES_IRQ, 0, RF_SHAREABLE | RF_ACTIVE },
139169269Sphk	{ -1, 0 }
140169269Sphk};
141169269Sphk
14291398Stmmstatic int
14391398Stmmgem_pci_attach(dev)
14491398Stmm	device_t dev;
14591398Stmm{
146172334Smarius	struct gem_softc *sc;
147172334Smarius	int i;
148172334Smarius#if !(defined(__powerpc__) || defined(__sparc64__))
149172334Smarius	int j;
150172334Smarius#endif
15191398Stmm
152172334Smarius	sc = device_get_softc(dev);
153172334Smarius	sc->sc_variant = GEM_UNKNOWN;
154172334Smarius	for (i = 0; gem_pci_devlist[i].gpd_desc != NULL; i++) {
155172334Smarius		if (pci_get_devid(dev) == gem_pci_devlist[i].gpd_devid) {
156172334Smarius			sc->sc_variant = gem_pci_devlist[i].gpd_variant;
157172334Smarius			break;
158172334Smarius		}
159172334Smarius	}
160172334Smarius	if (sc->sc_variant == GEM_UNKNOWN) {
161172334Smarius		device_printf(dev, "unknown adaptor\n");
162172334Smarius		return (ENXIO);
163172334Smarius	}
164172334Smarius
165117116Stmm	pci_enable_busmaster(dev);
166117116Stmm
16791963Stmm	/*
168117116Stmm	 * Some Sun GEMs/ERIs do have their intpin register bogusly set to 0,
169117116Stmm	 * although it should be 1. correct that.
17091963Stmm	 */
171117116Stmm	if (pci_get_intpin(dev) == 0)
172117116Stmm		pci_set_intpin(dev, 1);
17391963Stmm
17491398Stmm	sc->sc_dev = dev;
175172334Smarius	sc->sc_flags |= GEM_PCI;	/* XXX */
17691398Stmm
177169269Sphk	if (bus_alloc_resources(dev, gem_pci_res_spec, sc->sc_res)) {
178169269Sphk		device_printf(dev, "failed to allocate resources\n");
179169269Sphk		bus_release_resources(dev, gem_pci_res_spec, sc->sc_res);
180169269Sphk		return (ENXIO);
18191398Stmm	}
18291398Stmm
183169269Sphk	GEM_LOCK_INIT(sc, device_get_nameunit(dev));
18491398Stmm
185172334Smarius#if defined(__powerpc__) || defined(__sparc64__)
186147256Sbrooks	OF_getetheraddr(dev, sc->sc_enaddr);
187172334Smarius#else
188172334Smarius	/*
189172334Smarius	 * Dig out VPD (vital product data) and read NA (network address).
190172334Smarius	 * The VPD of GEM resides in the PCI Expansion ROM (PCI FCode) and
191172334Smarius	 * can't be accessed via the PCI capability pointer.
192172334Smarius	 * ``Writing FCode 3.x Programs'' (newer ones, dated 1997 and later)
193172334Smarius	 * chapter 2 describes the data structure.
194172334Smarius	 */
19591398Stmm
196172334Smarius#define	PCI_ROMHDR_SIZE			0x1c
197172334Smarius#define	PCI_ROMHDR_SIG			0x00
198172334Smarius#define	PCI_ROMHDR_SIG_MAGIC		0xaa55		/* little endian */
199172334Smarius#define	PCI_ROMHDR_PTR_DATA		0x18
200172334Smarius#define	PCI_ROM_SIZE			0x18
201172334Smarius#define	PCI_ROM_SIG			0x00
202172334Smarius#define	PCI_ROM_SIG_MAGIC		0x52494350	/* "PCIR", endian */
203172334Smarius							/* reversed */
204172334Smarius#define	PCI_ROM_VENDOR			0x04
205172334Smarius#define	PCI_ROM_DEVICE			0x06
206172334Smarius#define	PCI_ROM_PTR_VPD			0x08
207172334Smarius#define	PCI_VPDRES_BYTE0		0x00
208172334Smarius#define	PCI_VPDRES_ISLARGE(x)		((x) & 0x80)
209172334Smarius#define	PCI_VPDRES_LARGE_NAME(x)	((x) & 0x7f)
210172334Smarius#define	PCI_VPDRES_TYPE_VPD		0x10		/* large */
211172334Smarius#define	PCI_VPDRES_LARGE_LEN_LSB	0x01
212172334Smarius#define	PCI_VPDRES_LARGE_LEN_MSB	0x02
213172334Smarius#define	PCI_VPDRES_LARGE_DATA		0x03
214172334Smarius#define	PCI_VPD_SIZE			0x03
215172334Smarius#define	PCI_VPD_KEY0			0x00
216172334Smarius#define	PCI_VPD_KEY1			0x01
217172334Smarius#define	PCI_VPD_LEN			0x02
218172334Smarius#define	PCI_VPD_DATA			0x03
219172334Smarius
220172334Smarius#define	GEM_ROM_READ_N(n, sc, offs)					\
221172334Smarius	bus_read_ ## n ((sc)->sc_res[0], GEM_PCI_ROM_OFFSET + (offs))
222172334Smarius#define	GEM_ROM_READ_1(sc, offs)	GEM_ROM_READ_N(1, (sc), (offs))
223172334Smarius#define	GEM_ROM_READ_2(sc, offs)	GEM_ROM_READ_N(2, (sc), (offs))
224172334Smarius#define	GEM_ROM_READ_4(sc, offs)	GEM_ROM_READ_N(4, (sc), (offs))
225172334Smarius
226172334Smarius	/* Read PCI Expansion ROM header. */
227172334Smarius	if (GEM_ROM_READ_2(sc, PCI_ROMHDR_SIG) != PCI_ROMHDR_SIG_MAGIC ||
228172334Smarius	    (i = GEM_ROM_READ_2(sc, PCI_ROMHDR_PTR_DATA)) < PCI_ROMHDR_SIZE) {
229172334Smarius		device_printf(dev, "unexpected PCI Expansion ROM header\n");
230172334Smarius		goto fail;
231172334Smarius	}
232172334Smarius
233172334Smarius	/* Read PCI Expansion ROM data. */
234172334Smarius	if (GEM_ROM_READ_4(sc, i + PCI_ROM_SIG) != PCI_ROM_SIG_MAGIC ||
235172334Smarius	    GEM_ROM_READ_2(sc, i + PCI_ROM_VENDOR) != pci_get_vendor(dev) ||
236172334Smarius	    GEM_ROM_READ_2(sc, i + PCI_ROM_DEVICE) != pci_get_device(dev) ||
237172334Smarius	    (j = GEM_ROM_READ_2(sc, i + PCI_ROM_PTR_VPD)) < i + PCI_ROM_SIZE) {
238172334Smarius		device_printf(dev, "unexpected PCI Expansion ROM data\n");
239172334Smarius		goto fail;
240172334Smarius	}
241172334Smarius
24291398Stmm	/*
243172334Smarius	 * Read PCI VPD.
244172334Smarius	 * SUNW,pci-gem cards have a single large resource VPD-R tag
245172334Smarius	 * containing one NA. The VPD used is not in PCI 2.2 standard
246172334Smarius	 * format however. The length in the resource header is in big
247172334Smarius	 * endian and the end tag is non-standard (0x79) and followed
248172334Smarius	 * by an all-zero "checksum" byte. Sun calls this a "Fresh
249172334Smarius	 * Choice Ethernet" VPD...
250172334Smarius	 */
251172334Smarius	if (PCI_VPDRES_ISLARGE(GEM_ROM_READ_1(sc, j + PCI_VPDRES_BYTE0)) == 0 ||
252172334Smarius	    PCI_VPDRES_LARGE_NAME(GEM_ROM_READ_1(sc, j + PCI_VPDRES_BYTE0)) !=
253172334Smarius	    PCI_VPDRES_TYPE_VPD ||
254172334Smarius	    (GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_LEN_LSB) << 8 |
255172334Smarius	    GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_LEN_MSB)) !=
256172334Smarius	    PCI_VPD_SIZE + ETHER_ADDR_LEN ||
257172334Smarius	    GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_DATA + PCI_VPD_KEY0) !=
258172334Smarius	    0x4e /* N */ ||
259172334Smarius	    GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_DATA + PCI_VPD_KEY1) !=
260172334Smarius	    0x41 /* A */ ||
261172334Smarius	    GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_DATA + PCI_VPD_LEN) !=
262172334Smarius	    ETHER_ADDR_LEN ||
263172334Smarius	    GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_DATA + PCI_VPD_DATA +
264172334Smarius	    ETHER_ADDR_LEN) != 0x79) {
265172334Smarius		device_printf(dev, "unexpected PCI VPD\n");
266172334Smarius		goto fail;
267172334Smarius	}
268172334Smarius	bus_read_region_1(sc->sc_res[0], GEM_PCI_ROM_OFFSET + j +
269172334Smarius	    PCI_VPDRES_LARGE_DATA + PCI_VPD_DATA, sc->sc_enaddr,
270172334Smarius	    ETHER_ADDR_LEN);
271172334Smarius#endif
272172334Smarius
273172334Smarius	/*
27491398Stmm	 * call the main configure
27591398Stmm	 */
27691398Stmm	if (gem_attach(sc) != 0) {
27791398Stmm		device_printf(dev, "could not be configured\n");
278169269Sphk		goto fail;
27991398Stmm	}
28091398Stmm
281169269Sphk	if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_NET | INTR_MPSAFE,
282169269Sphk	    NULL, gem_intr, sc, &sc->sc_ih) != 0) {
28391398Stmm		device_printf(dev, "failed to set up interrupt\n");
284109650Stmm		gem_detach(sc);
285169269Sphk		goto fail;
28691398Stmm	}
28791398Stmm	return (0);
28891398Stmm
289169269Sphkfail:
290172334Smarius	GEM_LOCK_DESTROY(sc);
291169269Sphk	bus_release_resources(dev, gem_pci_res_spec, sc->sc_res);
29291398Stmm	return (ENXIO);
29391398Stmm}
294108964Stmm
295108964Stmmstatic int
296108964Stmmgem_pci_detach(dev)
297108964Stmm	device_t dev;
298108964Stmm{
299169269Sphk	struct gem_softc *sc = device_get_softc(dev);
300108964Stmm
301169269Sphk	bus_teardown_intr(dev, sc->sc_res[1], sc->sc_ih);
302108964Stmm	gem_detach(sc);
303148369Smarius	GEM_LOCK_DESTROY(sc);
304169269Sphk	bus_release_resources(dev, gem_pci_res_spec, sc->sc_res);
305108964Stmm	return (0);
306108964Stmm}
307108964Stmm
308108964Stmmstatic int
309108964Stmmgem_pci_suspend(dev)
310108964Stmm	device_t dev;
311108964Stmm{
312169269Sphk	struct gem_softc *sc = device_get_softc(dev);
313108964Stmm
314108964Stmm	gem_suspend(sc);
315108964Stmm	return (0);
316108964Stmm}
317108964Stmm
318108964Stmmstatic int
319108964Stmmgem_pci_resume(dev)
320108964Stmm	device_t dev;
321108964Stmm{
322169269Sphk	struct gem_softc *sc = device_get_softc(dev);
323108964Stmm
324108964Stmm	gem_resume(sc);
325108964Stmm	return (0);
326108964Stmm}
327