1112790Smdodd/*-
2112790Smdodd * Copyright (c) 2003 Matthew N. Dodd
3112790Smdodd * All rights reserved.
4112790Smdodd *
5112790Smdodd * Redistribution and use in source and binary forms, with or without
6112790Smdodd * modification, are permitted provided that the following conditions
7112790Smdodd * are met:
8112790Smdodd * 1. Redistributions of source code must retain the above copyright
9112790Smdodd *    notice, this list of conditions and the following disclaimer.
10112790Smdodd * 2. Redistributions in binary form must reproduce the above copyright
11112790Smdodd *    notice, this list of conditions and the following disclaimer in the
12112790Smdodd *    documentation and/or other materials provided with the distribution.
13112790Smdodd *
14112790Smdodd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15112790Smdodd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16112790Smdodd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17112790Smdodd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18112790Smdodd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19112790Smdodd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20112790Smdodd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21112790Smdodd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22112790Smdodd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23112790Smdodd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24112790Smdodd * SUCH DAMAGE.
25112790Smdodd *
26112790Smdodd * Portions:
27112790Smdodd * Copyright (c) 1992, 1993, University of Vermont and State
28112790Smdodd *  Agricultural College.
29112790Smdodd * Copyright (c) 1992, 1993, Garrett A. Wollman.
30112790Smdodd * Copyright (c) 1990, 1991, William F. Jolitz
31112790Smdodd * Copyright (c) 1990, The Regents of the University of California
32112790Smdodd * Copyright (c) 1993, 1994, Charles M. Hannum
33112790Smdodd * Copyright (c) 1993, 1994, 1995, Rodney W. Grimes
34112790Smdodd * Copyright (c) 1997, Aaron C. Smith
35112790Smdodd *
36112790Smdodd * See if_ie.c for applicable license.
37112790Smdodd */
38112790Smdodd
39119418Sobrien#include <sys/cdefs.h>
40119418Sobrien__FBSDID("$FreeBSD: releng/11.0/sys/dev/ie/if_ie_isa.c 297793 2016-04-10 23:07:00Z pfg $");
41119418Sobrien
42112790Smdodd#include <sys/param.h>
43112790Smdodd#include <sys/systm.h>
44257324Sglebius#include <sys/lock.h>
45112790Smdodd#include <sys/kernel.h>
46257324Sglebius#include <sys/malloc.h>
47257324Sglebius#include <sys/mutex.h>
48112790Smdodd#include <sys/socket.h>
49112790Smdodd
50112790Smdodd#include <sys/module.h>
51112790Smdodd#include <sys/bus.h>
52112790Smdodd
53112790Smdodd#include <machine/bus.h>
54112790Smdodd#include <machine/resource.h>
55112790Smdodd#include <sys/rman.h>
56112790Smdodd
57112790Smdodd#include <machine/md_var.h>
58112790Smdodd
59112790Smdodd#include <net/if.h>
60112790Smdodd#include <net/if_media.h>
61112790Smdodd
62112790Smdodd#include <isa/isavar.h>
63112790Smdodd#include <isa/pnpvar.h>
64112790Smdodd
65112790Smdodd#include <i386/isa/elink.h>
66112790Smdodd
67112790Smdodd#include <dev/ic/i82586.h>
68112790Smdodd#include <dev/ie/if_ie507.h>
69112790Smdodd#include <dev/ie/if_iee16.h>
70112790Smdodd#include <dev/ie/if_iereg.h>
71112790Smdodd#include <dev/ie/if_ievar.h>
72112790Smdodd
73112790Smdoddstatic int		ie_modevent		(module_t, int, void *);
74112790Smdodd
75112790Smdoddstatic void		ie_isa_3C507_identify	(driver_t *, device_t);
76112790Smdoddstatic int		ie_isa_3C507_probe	(device_t);
77112790Smdoddstatic int		ie_isa_3C507_attach	(device_t);
78112790Smdoddstatic int		ie_3C507_port_check	(u_int32_t);
79112790Smdodd
80112790Smdoddstatic void		ie_isa_ee16_identify	(driver_t *, device_t);
81112790Smdoddstatic int		ie_isa_ee16_probe	(device_t);
82112790Smdoddstatic int		ie_isa_ee16_attach	(device_t);
83181134Sjhbstatic int		ie_isa_ee16_shutdown	(device_t);
84112790Smdoddstatic int		ie_ee16_port_check	(u_int32_t port);
85112790Smdoddstatic u_int16_t	ie_ee16_hw_read_eeprom	(u_int32_t port, int loc);
86112790Smdodd
87112790Smdoddstatic int		ie_isa_sl_probe		(device_t);
88112790Smdoddstatic int		ie_isa_sl_attach	(device_t);
89112790Smdoddstatic enum ie_hardware	ie_isa_sl_get_hard_type	(u_int32_t);
90112790Smdodd
91112790Smdodd/*
92112790Smdodd * 3Com 3C507 Etherlink 16
93112790Smdodd */
94112790Smdodd#define IE_3C507_IOBASE_LOW	0x200
95112790Smdodd#define IE_3C507_IOBASE_HIGH	0x3e0
96112790Smdodd#define IE_3C507_IOSIZE		16
97112790Smdodd
98112790Smdodd#define IE_3C507_IRQ_MASK	0x0f
99112790Smdodd
100112790Smdodd#define IE_3C507_MADDR_HIGH	0x20
101112790Smdodd#define IE_3C507_MADDR_MASK	0x1c
102112790Smdodd#define IE_3C507_MADDR_BASE	0xc0000
103112790Smdodd#define IE_3C507_MADDR_SHIFT	12
104112790Smdodd
105112790Smdodd#define IE_3C507_MSIZE_MASK	3
106112790Smdodd#define IE_3C507_MSIZE_SHIFT	14
107112790Smdodd
108112790Smdoddstatic void
109112790Smdoddie_isa_3C507_identify (driver_t *driver, device_t parent)
110112790Smdodd{
111112790Smdodd	char *		desc = "3Com 3C507 Etherlink 16";
112112790Smdodd	device_t	child;
113112790Smdodd	u_int32_t	port, maddr, msize;
114112790Smdodd	u_int8_t	irq, data;
115112790Smdodd	int		error;
116112790Smdodd
117112790Smdodd	/* Reset and put card in CONFIG state without changing address. */
118112790Smdodd	elink_reset();
119112790Smdodd	elink_idseq(ELINK_507_POLY);
120112790Smdodd	elink_idseq(ELINK_507_POLY);
121112790Smdodd	outb(ELINK_ID_PORT, 0xff);
122112790Smdodd
123112790Smdodd	for (port = IE_3C507_IOBASE_LOW;
124112790Smdodd	     port <= IE_3C507_IOBASE_HIGH;
125112790Smdodd	     port += IE_3C507_IOSIZE) {
126112790Smdodd
127112790Smdodd		if (ie_3C507_port_check(port)) {
128153110Sru#ifdef DEBUG
129112790Smdodd			if (bootverbose) {
130112790Smdodd				device_printf(parent,
131112790Smdodd					"(if_ie) (3C507) not found at port %#x\n",
132112790Smdodd					port);
133112790Smdodd			}
134112790Smdodd#endif
135112790Smdodd			continue;
136112790Smdodd		}
137112790Smdodd
138112790Smdodd		outb(port + IE507_CTRL, EL_CTRL_NRST);
139112790Smdodd
140112790Smdodd		data = inb(port + IE507_IRQ);
141112790Smdodd		irq = data & IE_3C507_IRQ_MASK;
142112790Smdodd
143112790Smdodd		data = inb(port + IE507_MADDR);
144112790Smdodd
145112790Smdodd		if (data & IE_3C507_MADDR_HIGH) {
146112790Smdodd			if (bootverbose) {
147112790Smdodd				device_printf(parent,
148112790Smdodd					"(if_ie) can't map 3C507 RAM in high memory\n");
149112790Smdodd			}
150112790Smdodd			continue;
151112790Smdodd		}
152112790Smdodd
153112790Smdodd		maddr = IE_3C507_MADDR_BASE +
154112790Smdodd			((data & IE_3C507_MADDR_MASK)
155112790Smdodd			<< IE_3C507_MADDR_SHIFT);
156112790Smdodd		msize = ((data & IE_3C507_MSIZE_MASK) + 1)
157112790Smdodd			<< IE_3C507_MSIZE_SHIFT;
158112790Smdodd
159112790Smdodd		child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ie", -1);
160112790Smdodd		device_set_desc_copy(child, desc);
161112790Smdodd		device_set_driver(child, driver);
162112790Smdodd
163112790Smdodd		error = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
164112790Smdodd		if (error) {
165112790Smdodd			device_printf(parent, "(if_ie) Unable to set IRQ resource %d.\n",
166112790Smdodd					irq);
167112790Smdodd			error = device_delete_child(parent, child);
168112790Smdodd			continue;
169112790Smdodd		}
170112790Smdodd
171112790Smdodd		error = bus_set_resource(child, SYS_RES_IOPORT, 0, port, IE_3C507_IOSIZE);
172112790Smdodd		if (error) {
173112790Smdodd			device_printf(parent, "(if_ie) Unable to set IOPORT resource %#x-%#x.\n",
174112790Smdodd					port, port+IE_3C507_IOSIZE);
175112790Smdodd			error = device_delete_child(parent, child);
176112790Smdodd			continue;
177112790Smdodd		}
178112790Smdodd
179112790Smdodd		error = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize);
180112790Smdodd		if (error) {
181112790Smdodd			device_printf(parent, "(if_ie) Unable to set MEMORY resource %#x-%#x.\n",
182112790Smdodd					maddr, maddr+msize);
183112790Smdodd			error = device_delete_child(parent, child);
184112790Smdodd			continue;
185112790Smdodd		}
186112790Smdodd
187112790Smdodd		if (bootverbose) {
188112790Smdodd			device_printf(parent,
189112790Smdodd				"(if_ie) <%s> at port %#x-%#x irq %d iomem %#lx-%#lx (%dKB)\n",
190112790Smdodd				desc,
191112790Smdodd				port, (port + IE_3C507_IOSIZE) - 1,
192112790Smdodd				irq,
193112790Smdodd				(u_long)maddr, (u_long)(maddr + msize) - 1,
194112790Smdodd				(msize / 1024));
195112790Smdodd		}
196112790Smdodd	}
197112790Smdodd
198112790Smdodd	/* go to RUN state */
199112790Smdodd	outb(ELINK_ID_PORT, 0x00);
200112790Smdodd	elink_idseq(ELINK_507_POLY);
201112790Smdodd	outb(ELINK_ID_PORT, 0x00);
202112790Smdodd
203112790Smdodd	return;
204112790Smdodd}
205112790Smdodd
206112790Smdoddstatic int
207112790Smdoddie_isa_3C507_probe (device_t dev)
208112790Smdodd{
209112790Smdodd	u_int32_t	iobase;
210112790Smdodd
211112790Smdodd	/* No ISA-PnP support */
212112790Smdodd	if (isa_get_vendorid(dev)) {
213112790Smdodd		return (ENXIO);
214112790Smdodd	}
215112790Smdodd
216112790Smdodd	/* No ISA-HINT support */
217112790Smdodd	if (!device_get_desc(dev)) {
218112790Smdodd		return (EBUSY);
219112790Smdodd	}
220112790Smdodd
221112790Smdodd	/* Have we at least an ioport? */
222112790Smdodd	if ((iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0) {
223112790Smdodd		return (ENXIO);
224112790Smdodd	}
225112790Smdodd
226112790Smdodd	/* Is this thing really a 3c507? */
227112790Smdodd	if (ie_3C507_port_check(iobase)) {
228112790Smdodd		return (ENXIO);
229112790Smdodd	}
230112790Smdodd
231112790Smdodd	return (0);
232112790Smdodd}
233112790Smdodd
234112790Smdoddstatic int
235112790Smdoddie_isa_3C507_attach (device_t dev)
236112790Smdodd{
237112790Smdodd	struct ie_softc *	sc;
238112790Smdodd	int			error;
239112790Smdodd
240112790Smdodd	sc = device_get_softc(dev);
241112790Smdodd
242112790Smdodd	sc->io_rid = 0;
243112790Smdodd	sc->irq_rid = 0;
244112790Smdodd	sc->mem_rid = 0;
245112790Smdodd
246112790Smdodd	error = ie_alloc_resources(dev);
247112790Smdodd	if (error) {
248112790Smdodd		goto bad;
249112790Smdodd	}
250112790Smdodd
251112790Smdodd	sc->bus_use = 0;
252112790Smdodd	sc->ie_reset_586 = el_reset_586;
253112790Smdodd	sc->ie_chan_attn = el_chan_attn;
254112790Smdodd	sc->hard_type = IE_3C507;
255112790Smdodd	sc->hard_vers = 0;
256112790Smdodd
257112790Smdodd	outb(PORT(sc) + IE507_CTRL, EL_CTRL_NORMAL);
258112790Smdodd
259112790Smdodd	if (!check_ie_present(sc)) {
260112790Smdodd		error = ENXIO;
261112790Smdodd		goto bad;
262112790Smdodd	}
263112790Smdodd
264147256Sbrooks	sl_read_ether(sc, sc->enaddr);
265112790Smdodd
266112790Smdodd	/* Clear the interrupt latch just in case. */
267112790Smdodd	outb(PORT(sc) + IE507_ICTRL, 1);
268112790Smdodd
269112790Smdodd	error = ie_attach(dev);
270112790Smdodd	if (error) {
271112790Smdodd		device_printf(dev, "ie_attach() failed.\n");
272112790Smdodd		goto bad;
273112790Smdodd	}
274112790Smdodd
275112790Smdodd	return (0);
276112790Smdoddbad:
277112790Smdodd	ie_release_resources(dev);
278112790Smdodd
279112790Smdodd	return (error);
280112790Smdodd}
281112790Smdodd
282112790Smdodd/*
283112790Smdodd * If a 3c507 is present, return 0
284112790Smdodd * else, return 1.
285112790Smdodd */
286112790Smdoddstatic int
287112790Smdoddie_3C507_port_check (u_int32_t port)
288112790Smdodd{
289112790Smdodd	u_char *	signature = "*3COM*";
290112790Smdodd	int		i;
291112790Smdodd
292112790Smdodd	for (i = 0; i < 6; i++)
293112790Smdodd		if (inb(port + i) != signature[i])
294112790Smdodd			return (ENXIO);
295112790Smdodd
296112790Smdodd	return (0);
297112790Smdodd}
298112790Smdodd
299112790Smdodd/*
300112790Smdodd * Intel EtherExpress 16
301112790Smdodd */
302112790Smdodd#define IE_EE16_ID_PORT			0x0f
303112790Smdodd#define IE_EE16_ID			0xbaba
304112790Smdodd#define IE_EE16_EEPROM_CONFIG1		0x00
305112790Smdodd#define IE_EE16_EEPROM_IRQ_MASK		0xe000
306112790Smdodd#define IE_EE16_EEPROM_IRQ_SHIFT	13
307112790Smdodd#define IE_EE16_EEPROM_MEMCFG		0x06
308112790Smdodd#define IE_EE16_IOSIZE			16
309112790Smdodd
310112790Smdodd/*
311112790Smdodd * TODO:
312112790Smdodd *		Test for 8/16 bit mode.
313112790Smdodd *		Test for invalid mem sizes.
314112790Smdodd */
315112790Smdoddstatic void
316112790Smdoddie_isa_ee16_identify (driver_t *driver, device_t parent)
317112790Smdodd{
318112790Smdodd	char *		desc = "Intel EtherExpress 16";
319112790Smdodd	device_t	child;
320112790Smdodd	u_int16_t	ports[] = {
321112790Smdodd				0x300, 0x310, 0x320, 0x330,
322112790Smdodd				0x340, 0x350, 0x360, 0x370,
323112790Smdodd				0x200, 0x210, 0x220, 0x230,
324112790Smdodd				0x240, 0x250, 0x260, 0x270,
325112790Smdodd				0
326112790Smdodd			};
327112790Smdodd	u_int16_t	irqs[] = { 0, 0x09, 0x03, 0x04, 0x05, 0x0a, 0x0b, 0 };
328112790Smdodd	u_int32_t	port, maddr, msize;
329112790Smdodd	u_int8_t	irq;
330112790Smdodd	u_int16_t	data;
331112790Smdodd	int		i, error;
332112790Smdodd
333112790Smdodd	for (i = 0; ports[i]; i++) {
334112790Smdodd		port = ports[i];
335112790Smdodd
336112790Smdodd		if (ie_ee16_port_check(port)) {
337153110Sru#ifdef DEBUG
338112790Smdodd			if (bootverbose) {
339112790Smdodd				device_printf(parent,
340112790Smdodd					"if_ie: (EE16) not found at port %#x\n",
341112790Smdodd					port);
342112790Smdodd			}
343112790Smdodd#endif
344112790Smdodd			continue;
345112790Smdodd		}
346112790Smdodd
347112790Smdodd		/* reset any ee16 at the current iobase */
348112790Smdodd		outb(port + IEE16_ECTRL, IEE16_RESET_ASIC);
349112790Smdodd		outb(port + IEE16_ECTRL, 0);
350112790Smdodd		DELAY(240);
351112790Smdodd
352112790Smdodd		data = ie_ee16_hw_read_eeprom(port, IE_EE16_EEPROM_CONFIG1);
353112790Smdodd		irq = irqs[((data & IE_EE16_EEPROM_IRQ_MASK)
354112790Smdodd			   >> IE_EE16_EEPROM_IRQ_SHIFT)];
355112790Smdodd
356112790Smdodd		data = ie_ee16_hw_read_eeprom(port, IE_EE16_EEPROM_MEMCFG);
357112790Smdodd		maddr = 0xc0000 + ((ffs(data & 0x00ff) - 1) * 0x4000);
358112790Smdodd		msize = (fls((data & 0x00ff) >> (ffs(data & 0x00ff) - 1)))
359112790Smdodd			* 0x4000;
360112790Smdodd
361112790Smdodd		child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ie", -1);
362112790Smdodd		device_set_desc_copy(child, desc);
363112790Smdodd		device_set_driver(child, driver);
364112790Smdodd
365112790Smdodd		error = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
366112790Smdodd		if (error) {
367112790Smdodd			device_printf(parent, "(if_ie) Unable to set IRQ resource %d.\n",
368112790Smdodd					irq);
369112790Smdodd			error = device_delete_child(parent, child);
370112790Smdodd			continue;
371112790Smdodd		}
372112790Smdodd
373112790Smdodd		error = bus_set_resource(child, SYS_RES_IOPORT, 0, port, IE_EE16_IOSIZE);
374112790Smdodd		if (error) {
375112790Smdodd			device_printf(parent, "(if_ie) Unable to set IOPORT resource %#x-%#x.\n",
376112790Smdodd					port, port+IE_EE16_IOSIZE);
377112790Smdodd			error = device_delete_child(parent, child);
378112790Smdodd			continue;
379112790Smdodd		}
380112790Smdodd
381112790Smdodd		error = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize);
382112790Smdodd		if (error) {
383112790Smdodd			device_printf(parent, "(if_ie) Unable to set MEMORY resource %#x-%#x.\n",
384112790Smdodd					maddr, maddr+msize);
385112790Smdodd			error = device_delete_child(parent, child);
386112790Smdodd			continue;
387112790Smdodd		}
388112790Smdodd
389112790Smdodd		if (bootverbose) {
390112790Smdodd			device_printf(parent,
391112790Smdodd				"if_ie: <%s> at port %#x-%#x irq %d iomem %#lx-%#lx (%dKB)\n",
392112790Smdodd				desc,
393112790Smdodd				port, (port + IE_EE16_IOSIZE) - 1,
394112790Smdodd				irq,
395112790Smdodd				(u_long)maddr, (u_long)(maddr + msize) - 1,
396112790Smdodd				(msize / 1024));
397112790Smdodd		}
398112790Smdodd	}
399112790Smdodd
400112790Smdodd	return;
401112790Smdodd}
402112790Smdodd
403112790Smdoddstatic int
404112790Smdoddie_isa_ee16_probe (device_t dev)
405112790Smdodd{
406112790Smdodd	u_int32_t	iobase;
407112790Smdodd
408112790Smdodd	/* No ISA-PnP support */
409112790Smdodd	if (isa_get_vendorid(dev))
410112790Smdodd		return (ENXIO);
411112790Smdodd
412112790Smdodd	/* No ISA-HINT support */
413112790Smdodd	if (!device_get_desc(dev))
414112790Smdodd		return (EBUSY);
415112790Smdodd
416112790Smdodd	/* Have we at least an ioport? */
417112790Smdodd	if ((iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0)
418112790Smdodd		return (ENXIO);
419112790Smdodd
420112790Smdodd	/* Is this really an EE16? */
421112790Smdodd	if (ie_ee16_port_check(iobase))
422112790Smdodd		return (ENXIO);
423112790Smdodd
424112790Smdodd	return (0);
425112790Smdodd}
426112790Smdodd
427112790Smdoddstatic int
428112790Smdoddie_isa_ee16_attach (device_t dev)
429112790Smdodd{
430112790Smdodd	struct ie_softc *	sc;
431112790Smdodd	int			i, error;
432112790Smdodd	u_int16_t		checksum;
433112790Smdodd	u_short			eaddrtemp, pg, adjust, decode, edecode;
434112790Smdodd	u_char			bart_config;
435112790Smdodd
436112790Smdodd	sc = device_get_softc(dev);
437112790Smdodd
438112790Smdodd	sc->io_rid = 0;
439112790Smdodd	sc->irq_rid = 0;
440112790Smdodd	sc->mem_rid = 0;
441112790Smdodd
442112790Smdodd	error = ie_alloc_resources(dev);
443112790Smdodd	if (error) {
444112790Smdodd		goto bad;
445112790Smdodd	}
446112790Smdodd
447112790Smdodd	sc->bus_use = 0;
448112790Smdodd	sc->ie_reset_586 = ee16_reset_586;
449112790Smdodd	sc->ie_chan_attn = ee16_chan_attn;
450112790Smdodd	sc->hard_type = IE_EE16;
451112790Smdodd	sc->hard_vers = 0;
452112790Smdodd	sc->iomem = 0;
453112790Smdodd
454112790Smdodd	/* reset any ee16 at the current iobase */
455112790Smdodd	outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_ASIC);
456112790Smdodd	outb(PORT(sc) + IEE16_ECTRL, 0);
457112790Smdodd	DELAY(240);
458112790Smdodd
459112790Smdodd	/* Is this really an EE16? */
460112790Smdodd	if (ie_ee16_port_check(PORT(sc))) {
461112790Smdodd		device_printf(dev, "ie_ee16_port_check() failed\n");
462112790Smdodd		error = ENXIO;
463112790Smdodd		goto bad;
464112790Smdodd	}
465112790Smdodd
466112790Smdodd	/* need to put the 586 in RESET while we access the eeprom. */
467112790Smdodd	outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_586);
468112790Smdodd
469112790Smdodd	/* read the eeprom and checksum it, should == IE_E16_ID */
470112790Smdodd	checksum = 0;
471112790Smdodd	for (i = 0; i < 0x40; i++)
472112790Smdodd		checksum += ie_ee16_hw_read_eeprom(PORT(sc), i);
473112790Smdodd
474112790Smdodd	if (checksum != IE_EE16_ID) {
475112790Smdodd		device_printf(dev, "invalid eeprom checksum: %x\n", checksum);
476112790Smdodd		error = ENXIO;
477112790Smdodd		goto bad;
478112790Smdodd	}
479112790Smdodd
480112790Smdodd	if ((kvtop(sc->iomembot) < 0xC0000) ||
481112790Smdodd	     (kvtop(sc->iomembot) + sc->iosize > 0xF0000)) {
482112790Smdodd		device_printf(sc->dev, "mapped memory location %p out of range\n",
483112790Smdodd			(void *)sc->iomembot);
484112790Smdodd		error = ENXIO;
485112790Smdodd		goto bad;
486112790Smdodd	}
487112790Smdodd
488112790Smdodd	pg = ((kvtop(sc->iomembot)) & 0x3C000) >> 14;
489112790Smdodd	adjust = IEE16_MCTRL_FMCS16 | (pg & 0x3) << 2;
490112790Smdodd	decode = ((1 << (sc->iosize / 16384)) - 1) << pg;
491112790Smdodd	edecode = ((~decode >> 4) & 0xF0) | (decode >> 8);
492112790Smdodd
493112790Smdodd	/* ZZZ This should be checked against eeprom location 6, low byte */
494112790Smdodd	outb(PORT(sc) + IEE16_MEMDEC, decode & 0xFF);
495112790Smdodd	/* ZZZ This should be checked against eeprom location 1, low byte */
496112790Smdodd	outb(PORT(sc) + IEE16_MCTRL, adjust);
497112790Smdodd	/* ZZZ Now if I could find this one I would have it made */
498112790Smdodd	outb(PORT(sc) + IEE16_MPCTRL, (~decode & 0xFF));
499112790Smdodd	/* ZZZ I think this is location 6, high byte */
500112790Smdodd	outb(PORT(sc) + IEE16_MECTRL, edecode); /* XXX disable Exxx */
501112790Smdodd
502112790Smdodd#if 0
503112790Smdodd	(void) kvtop(sc->iomembot);
504112790Smdodd#endif
505112790Smdodd
506112790Smdodd	/*
507112790Smdodd	 * first prime the stupid bart DRAM controller so that it works,
508112790Smdodd	 * then zero out all of memory.
509112790Smdodd	 */
510112790Smdodd	bzero(sc->iomembot, 32);
511112790Smdodd	bzero(sc->iomembot, sc->iosize);
512112790Smdodd
513112790Smdodd	/* Get the encoded interrupt number from the EEPROM */
514112790Smdodd	sc->irq_encoded = ie_ee16_hw_read_eeprom(PORT(sc),
515112790Smdodd						 IE_EE16_EEPROM_CONFIG1);
516112790Smdodd	sc->irq_encoded = (sc->irq_encoded & IE_EE16_EEPROM_IRQ_MASK) >>
517112790Smdodd			   IE_EE16_EEPROM_IRQ_SHIFT;
518112790Smdodd
519112790Smdodd	/*
520112790Smdodd	 * Get the hardware ethernet address from the EEPROM and save it in
521112790Smdodd	 * the softc for use by the 586 setup code.
522112790Smdodd	 */
523112790Smdodd	eaddrtemp = ie_ee16_hw_read_eeprom(PORT(sc), IEE16_EEPROM_ENET_HIGH);
524147256Sbrooks	sc->enaddr[1] = eaddrtemp & 0xFF;
525147256Sbrooks	sc->enaddr[0] = eaddrtemp >> 8;
526112790Smdodd	eaddrtemp = ie_ee16_hw_read_eeprom(PORT(sc), IEE16_EEPROM_ENET_MID);
527147256Sbrooks	sc->enaddr[3] = eaddrtemp & 0xFF;
528147256Sbrooks	sc->enaddr[2] = eaddrtemp >> 8;
529112790Smdodd	eaddrtemp = ie_ee16_hw_read_eeprom(PORT(sc), IEE16_EEPROM_ENET_LOW);
530147256Sbrooks	sc->enaddr[5] = eaddrtemp & 0xFF;
531147256Sbrooks	sc->enaddr[4] = eaddrtemp >> 8;
532112790Smdodd
533112790Smdodd	/* disable the board interrupts */
534112790Smdodd	outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded);
535112790Smdodd
536112790Smdodd	/* enable loopback to keep bad packets off the wire */
537112790Smdodd	bart_config = inb(PORT(sc) + IEE16_CONFIG);
538112790Smdodd	bart_config |= IEE16_BART_LOOPBACK;
539112790Smdodd	bart_config |= IEE16_BART_MCS16_TEST;/* inb doesn't get bit! */
540112790Smdodd	outb(PORT(sc) + IEE16_CONFIG, bart_config);
541112790Smdodd	bart_config = inb(PORT(sc) + IEE16_CONFIG);
542112790Smdodd
543112790Smdodd	/* take the board out of reset state */
544112790Smdodd	outb(PORT(sc) + IEE16_ECTRL, 0);
545112790Smdodd	DELAY(100);
546112790Smdodd
547112790Smdodd	if (!check_ie_present(sc)) {
548112790Smdodd		device_printf(dev, "check_ie_present() returned false.\n");
549112790Smdodd		error = ENXIO;
550112790Smdodd		goto bad;
551112790Smdodd	}
552112790Smdodd
553112790Smdodd	error = ie_attach(dev);
554112790Smdodd	if (error) {
555112790Smdodd		device_printf(dev, "ie_attach() failed.\n");
556112790Smdodd		goto bad;
557112790Smdodd	}
558112790Smdodd
559112790Smdodd	return (0);
560112790Smdoddbad:
561112790Smdodd	ie_release_resources(dev);
562112790Smdodd
563112790Smdodd	return (error);
564112790Smdodd}
565112790Smdodd
566181134Sjhbstatic int
567181134Sjhbie_isa_ee16_shutdown(device_t dev)
568181134Sjhb{
569181134Sjhb	struct ie_softc *	sc;
570181134Sjhb
571181134Sjhb	sc = device_get_softc(dev);
572181134Sjhb	IE_LOCK(sc);
573181134Sjhb	ee16_shutdown(sc);
574181134Sjhb	IE_UNLOCK(sc);
575181134Sjhb
576181134Sjhb	return (0);
577181134Sjhb}
578181134Sjhb
579112790Smdodd/*
580112790Smdodd * If an EE16 is present, return 0
581112790Smdodd * else, return 1.
582112790Smdodd */
583112790Smdoddstatic int
584112790Smdoddie_ee16_port_check (u_int32_t port)
585112790Smdodd{
586112790Smdodd	int		i;
587112790Smdodd	u_int16_t	board_id;
588112790Smdodd	u_int8_t	data;
589112790Smdodd
590112790Smdodd	board_id = 0;
591112790Smdodd	for (i = 0; i < 4; i++) {
592112790Smdodd		data = inb(port + IE_EE16_ID_PORT);
593112790Smdodd		board_id |= ((data >> 4) << ((data & 0x03) << 2));
594112790Smdodd	}
595112790Smdodd
596112790Smdodd	if (board_id != IE_EE16_ID)
597112790Smdodd		return (1);
598112790Smdodd
599112790Smdodd	return (0);
600112790Smdodd}
601112790Smdodd
602112790Smdoddstatic void
603112790Smdoddie_ee16_hw_eeprom_clock (u_int32_t port, int state)
604112790Smdodd{
605112790Smdodd	u_int8_t	ectrl;
606112790Smdodd
607112790Smdodd	ectrl = inb(port + IEE16_ECTRL);
608112790Smdodd	ectrl &= ~(IEE16_RESET_ASIC | IEE16_ECTRL_EESK);
609112790Smdodd
610112790Smdodd	if (state) {
611112790Smdodd		ectrl |= IEE16_ECTRL_EESK;
612112790Smdodd	}
613112790Smdodd	outb(port + IEE16_ECTRL, ectrl);
614112790Smdodd	DELAY(9);		/* EESK must be stable for 8.38 uSec */
615112790Smdodd}
616112790Smdodd
617112790Smdoddstatic void
618112790Smdoddie_ee16_hw_eeprom_out (u_int32_t port, u_int16_t edata, int count)
619112790Smdodd{
620112790Smdodd	u_int8_t	ectrl;
621112790Smdodd	int		i;
622112790Smdodd
623112790Smdodd	ectrl = inb(port + IEE16_ECTRL);
624112790Smdodd	ectrl &= ~IEE16_RESET_ASIC;
625112790Smdodd
626112790Smdodd	for (i = count - 1; i >= 0; i--) {
627112790Smdodd		ectrl &= ~IEE16_ECTRL_EEDI;
628112790Smdodd		if (edata & (1 << i)) {
629112790Smdodd			ectrl |= IEE16_ECTRL_EEDI;
630112790Smdodd		}
631112790Smdodd		outb(port + IEE16_ECTRL, ectrl);
632112790Smdodd		DELAY(1);       /* eeprom data must be setup for 0.4 uSec */
633112790Smdodd		ie_ee16_hw_eeprom_clock(port, 1);
634112790Smdodd		ie_ee16_hw_eeprom_clock(port, 0);
635112790Smdodd	}
636112790Smdodd	ectrl &= ~IEE16_ECTRL_EEDI;
637112790Smdodd	outb(port + IEE16_ECTRL, ectrl);
638112790Smdodd	DELAY(1);               /* eeprom data must be held for 0.4 uSec */
639112790Smdodd
640112790Smdodd	return;
641112790Smdodd}
642112790Smdodd
643112790Smdoddstatic u_int16_t
644112790Smdoddie_ee16_hw_eeprom_in (u_int32_t port)
645112790Smdodd{
646112790Smdodd	u_int8_t	ectrl;
647112790Smdodd	u_int16_t	edata;
648112790Smdodd	int		i;
649112790Smdodd
650112790Smdodd	ectrl = inb(port + IEE16_ECTRL);
651112790Smdodd	ectrl &= ~IEE16_RESET_ASIC;
652112790Smdodd
653112790Smdodd	for (edata = 0, i = 0; i < 16; i++) {
654112790Smdodd		edata = edata << 1;
655112790Smdodd		ie_ee16_hw_eeprom_clock(port, 1);
656112790Smdodd		ectrl = inb(port + IEE16_ECTRL);
657112790Smdodd		if (ectrl & IEE16_ECTRL_EEDO) {
658112790Smdodd			edata |= 1;
659112790Smdodd		}
660112790Smdodd		ie_ee16_hw_eeprom_clock(port, 0);
661112790Smdodd	}
662112790Smdodd	return (edata);
663112790Smdodd}
664112790Smdodd
665112790Smdoddstatic u_int16_t
666112790Smdoddie_ee16_hw_read_eeprom (u_int32_t port, int loc)
667112790Smdodd{
668112790Smdodd	u_int8_t	ectrl;
669112790Smdodd	u_int16_t	edata;
670112790Smdodd
671112790Smdodd	ectrl = inb(port + IEE16_ECTRL);
672112790Smdodd	ectrl &= IEE16_ECTRL_MASK;
673112790Smdodd	ectrl |= IEE16_ECTRL_EECS;
674112790Smdodd	outb(port + IEE16_ECTRL, ectrl);
675112790Smdodd
676112790Smdodd	ie_ee16_hw_eeprom_out(port, IEE16_EEPROM_READ, IEE16_EEPROM_OPSIZE1);
677112790Smdodd	ie_ee16_hw_eeprom_out(port, loc, IEE16_EEPROM_ADDR_SIZE);
678112790Smdodd	edata = ie_ee16_hw_eeprom_in(port);
679112790Smdodd
680112790Smdodd	ectrl = inb(port + IEE16_ECTRL);
681112790Smdodd	ectrl &= ~(IEE16_RESET_ASIC | IEE16_ECTRL_EEDI | IEE16_ECTRL_EECS);
682112790Smdodd	outb(port + IEE16_ECTRL, ectrl);
683112790Smdodd
684112790Smdodd	ie_ee16_hw_eeprom_clock(port, 1);
685112790Smdodd	ie_ee16_hw_eeprom_clock(port, 0);
686112790Smdodd
687112790Smdodd	return (edata);
688112790Smdodd}
689112790Smdodd
690112790Smdodd/*
691112790Smdodd * AT&T StarLan/
692112790Smdodd */
693112790Smdodd
694112790Smdoddstatic int
695112790Smdoddie_isa_sl_probe (device_t dev)
696112790Smdodd{
697112790Smdodd	u_int32_t	iobase;
698112790Smdodd
699112790Smdodd	/* No ISA-PnP support */
700112790Smdodd	if (isa_get_vendorid(dev))
701112790Smdodd		return (ENXIO);
702112790Smdodd
703112790Smdodd	/* ISA-HINT support only! */
704112790Smdodd	if (device_get_desc(dev))
705112790Smdodd		return (EBUSY);
706112790Smdodd
707112790Smdodd	/* Have we at least an ioport? */
708112790Smdodd	if ((iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0)
709112790Smdodd		return (ENXIO);
710112790Smdodd
711112790Smdodd	/* Is this really an SL board? */
712112790Smdodd	if (ie_isa_sl_get_hard_type(iobase) == IE_NONE)
713112790Smdodd		return (ENXIO);
714112790Smdodd
715112790Smdodd	return (ENXIO);
716112790Smdodd}
717112790Smdodd
718112790Smdoddstatic int
719112790Smdoddie_isa_sl_attach (device_t dev)
720112790Smdodd{
721112790Smdodd	struct ie_softc *	sc;
722112790Smdodd	int			error;
723112790Smdodd
724112790Smdodd	sc = device_get_softc(dev);
725112790Smdodd
726112790Smdodd	sc->io_rid = 0;
727112790Smdodd	sc->irq_rid = 0;
728112790Smdodd	sc->mem_rid = 0;
729112790Smdodd
730112790Smdodd	error = ie_alloc_resources(dev);
731112790Smdodd	if (error) {
732112790Smdodd		goto bad;
733112790Smdodd	}
734112790Smdodd
735112790Smdodd	/* Is this really an SL board? */
736112790Smdodd	if ((sc->hard_type = ie_isa_sl_get_hard_type(PORT(sc))) == IE_NONE) {
737112790Smdodd		error = ENXIO;
738112790Smdodd		goto bad;
739112790Smdodd	}
740112790Smdodd
741112790Smdodd	sc->hard_vers = SL_REV(inb(PORT(sc) + IEATT_REVISION));
742112790Smdodd	if (sc->hard_type == IE_NI5210) {
743112790Smdodd		sc->bus_use = 1;
744112790Smdodd	} else {
745112790Smdodd		sc->bus_use = 0;
746112790Smdodd	}
747112790Smdodd
748112790Smdodd	sc->ie_reset_586 = sl_reset_586;
749112790Smdodd	sc->ie_chan_attn = sl_chan_attn;
750112790Smdodd
751112790Smdodd	if (!check_ie_present(sc)) {
752112790Smdodd		error = ENXIO;
753112790Smdodd		goto bad;
754112790Smdodd	}
755112790Smdodd
756112790Smdodd	switch (sc->hard_type) {
757112790Smdodd		case IE_EN100:
758112790Smdodd		case IE_STARLAN10:
759112790Smdodd		case IE_SLFIBER:
760112790Smdodd		case IE_NI5210:
761147256Sbrooks			sl_read_ether(sc, sc->enaddr);
762112790Smdodd			break;
763112790Smdodd		default:
764112790Smdodd			if (bootverbose)
765112790Smdodd				device_printf(sc->dev, "unknown AT&T board type code %d\n", sc->hard_type);
766112790Smdodd			error = ENXIO;
767112790Smdodd			goto bad;
768112790Smdodd			break;
769112790Smdodd	}
770112790Smdodd
771112790Smdodd	error = ie_attach(dev);
772112790Smdodd	if (error) {
773112790Smdodd		device_printf(dev, "ie_attach() failed.\n");
774112790Smdodd		goto bad;
775112790Smdodd	}
776112790Smdodd
777112790Smdodd	return (0);
778112790Smdoddbad:
779112790Smdodd	ie_release_resources(dev);
780112790Smdodd
781112790Smdodd	return (error);
782112790Smdodd}
783112790Smdodd
784112790Smdoddstatic enum ie_hardware
785112790Smdoddie_isa_sl_get_hard_type (u_int32_t port)
786112790Smdodd{
787112790Smdodd	u_char			c;
788112790Smdodd	enum ie_hardware	retval;
789112790Smdodd
790112790Smdodd	c = inb(port + IEATT_REVISION);
791112790Smdodd	switch (SL_BOARD(c)) {
792112790Smdodd		case SL1_BOARD:
793112790Smdodd			if (inb(port + IEATT_ATTRIB) != NI5210_BOARD)
794112790Smdodd				retval = IE_NONE;
795112790Smdodd			retval = IE_NI5210;
796112790Smdodd			break;
797112790Smdodd		case SL10_BOARD:
798112790Smdodd			retval = IE_STARLAN10;
799112790Smdodd			break;
800112790Smdodd		case EN100_BOARD:
801112790Smdodd			retval = IE_EN100;
802112790Smdodd			break;
803112790Smdodd		case SLFIBER_BOARD:
804112790Smdodd			retval = IE_SLFIBER;
805112790Smdodd			break;
806112790Smdodd		default:
807112790Smdodd			retval = IE_NONE;
808112790Smdodd	}
809112790Smdodd	return (retval);
810112790Smdodd}
811112790Smdodd
812112790Smdoddstatic devclass_t ie_devclass;
813112790Smdodd
814112790Smdoddstatic device_method_t ie_isa_3C507_methods[] = {
815112790Smdodd	DEVMETHOD(device_identify,	ie_isa_3C507_identify),
816112790Smdodd	DEVMETHOD(device_probe,		ie_isa_3C507_probe),
817112790Smdodd	DEVMETHOD(device_attach,	ie_isa_3C507_attach),
818112790Smdodd	DEVMETHOD(device_detach,	ie_detach),
819112790Smdodd	{ 0, 0 }
820112790Smdodd};
821179559Sjhb
822112790Smdoddstatic driver_t ie_isa_3C507_driver = {
823112790Smdodd	"ie",
824112790Smdodd	ie_isa_3C507_methods,
825112790Smdodd	sizeof(struct ie_softc),
826112790Smdodd};
827179559Sjhb
828112790SmdoddDRIVER_MODULE(ie_3C507, isa, ie_isa_3C507_driver, ie_devclass, ie_modevent, 0);
829112790SmdoddMODULE_DEPEND(ie_3C507, elink, 1, 1, 1);
830112790Smdodd
831112790Smdoddstatic device_method_t ie_isa_ee16_methods[] = {
832112790Smdodd	DEVMETHOD(device_identify,	ie_isa_ee16_identify),
833112790Smdodd	DEVMETHOD(device_probe,		ie_isa_ee16_probe),
834112790Smdodd	DEVMETHOD(device_attach,	ie_isa_ee16_attach),
835181134Sjhb	DEVMETHOD(device_shutdown,	ie_isa_ee16_shutdown),
836112790Smdodd	DEVMETHOD(device_detach,	ie_detach),
837112790Smdodd	{ 0, 0 }
838112790Smdodd};
839179559Sjhb
840112790Smdoddstatic driver_t ie_isa_ee16_driver = {
841112790Smdodd	"ie",
842112790Smdodd	ie_isa_ee16_methods,
843112790Smdodd	sizeof(struct ie_softc),
844112790Smdodd};
845112790Smdodd
846179559SjhbDRIVER_MODULE(ie, isa, ie_isa_ee16_driver, ie_devclass, ie_modevent, 0);
847179559Sjhb
848112790Smdoddstatic device_method_t ie_isa_sl_methods[] = {
849112790Smdodd	DEVMETHOD(device_probe,		ie_isa_sl_probe),
850112790Smdodd	DEVMETHOD(device_attach,	ie_isa_sl_attach),
851112790Smdodd	DEVMETHOD(device_detach,	ie_detach),
852112790Smdodd	{ 0, 0 }
853112790Smdodd};
854179559Sjhb
855112790Smdoddstatic driver_t ie_isa_sl_driver = {
856112790Smdodd	"ie",
857112790Smdodd	ie_isa_sl_methods,
858112790Smdodd	sizeof(struct ie_softc),
859112790Smdodd};
860179559Sjhb
861112790SmdoddDRIVER_MODULE(ie_SL, isa, ie_isa_sl_driver, ie_devclass, ie_modevent, 0);
862112790Smdodd
863112790Smdoddstatic int
864112790Smdoddie_modevent (mod, what, arg)
865112790Smdodd	module_t	mod;
866112790Smdodd	int		what;
867112790Smdodd	void *		arg;
868112790Smdodd{
869112790Smdodd	device_t *	devs;
870112790Smdodd	int		count;
871112790Smdodd	int		i;
872112790Smdodd
873112790Smdodd	switch (what) {
874112790Smdodd	case MOD_LOAD:
875112790Smdodd		break;
876112790Smdodd	case MOD_UNLOAD:
877112790Smdodd		devclass_get_devices(ie_devclass, &devs, &count);
878112790Smdodd		for (i = 0; i < count; i++)
879112790Smdodd			device_delete_child(device_get_parent(devs[i]), devs[i]);
880241066Skevlo		free(devs, M_TEMP);
881112790Smdodd		break;
882112790Smdodd	default:
883112790Smdodd		break;
884297793Spfg	}
885112790Smdodd
886112790Smdodd	return (0);
887112790Smdodd}
888