if_ep_isa.c revision 139749
14374Slars/*- 24374Slars * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> 34374Slars * All rights reserved. 44374Slars * 511990Speter * Redistribution and use in source and binary forms, with or without 611990Speter * modification, are permitted provided that the following conditions 711990Speter * are met: 834766Speter * 1. Redistributions of source code must retain the above copyright 911990Speter * notice, this list of conditions and the following disclaimer. 1011990Speter * 2. Redistributions in binary form must reproduce the above copyright 1111990Speter * notice, this list of conditions and the following disclaimer in the 1211990Speter * documentation and/or other materials provided with the distribution. 1311990Speter * 3. All advertising materials mentioning features or use of this software 144374Slars * must display the following acknowledgement: 154374Slars * This product includes software developed by Herb Peyerl. 1628597Speter * 4. The name of Herb Peyerl may not be used to endorse or promote products 1734766Speter * derived from this software without specific prior written permission. 1834766Speter * 1934766Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2034766Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 214374Slars * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2228597Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2328597Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2428597Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2528597Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 264374Slars * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2728597Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2828597Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2928597Speter */ 3028597Speter 3128597Speter#include <sys/cdefs.h> 3228597Speter__FBSDID("$FreeBSD: head/sys/dev/ep/if_ep_isa.c 139749 2005-01-06 01:43:34Z imp $"); 3328597Speter 3428597Speter#include <sys/param.h> 3528597Speter#include <sys/systm.h> 3628597Speter#include <sys/kernel.h> 3728597Speter#include <sys/socket.h> 3828597Speter#include <sys/module.h> 3928597Speter#include <sys/bus.h> 4028597Speter 4128597Speter#include <machine/bus.h> 4228597Speter#include <machine/resource.h> 4328597Speter#include <sys/rman.h> 4428597Speter 4528597Speter#include <net/if.h> 4628597Speter#include <net/if_arp.h> 4728597Speter#include <net/if_media.h> 4828597Speter 4928597Speter#include <isa/isavar.h> 5028597Speter 5128597Speter#include <dev/ep/if_epreg.h> 5211990Speter#include <dev/ep/if_epvar.h> 5311990Speter 5411990Speter#ifdef __i386__ 5534766Speter#include <i386/isa/elink.h> 5634766Speter#endif 5734766Speter 5828597Speter#ifdef __i386__ 5934766Speterstatic u_int16_t get_eeprom_data(int, int); 6028597Speterstatic void ep_isa_identify(driver_t *, device_t); 6128597Speter#endif 6228597Speter 6328597Speterstatic int ep_isa_probe(device_t); 6428597Speterstatic int ep_isa_attach(device_t); 6528597Speterstatic int ep_eeprom_cksum(struct ep_softc *); 6628597Speter 6728597Speterstruct isa_ident { 6828597Speter u_int32_t id; 694374Slars char *name; 704374Slars}; 714374Slarsconst char *ep_isa_match_id(u_int32_t, struct isa_ident *); 724374Slars 734374Slars#define ISA_ID_3C509_XXX 0x0506d509 744374Slars#define ISA_ID_3C509_TP 0x506d5090 754374Slars#define ISA_ID_3C509_BNC 0x506d5091 7611990Speter#define ISA_ID_3C509_COMBO 0x506d5094 7728597Speter#define ISA_ID_3C509_TPO 0x506d5095 784374Slars#define ISA_ID_3C509_TPC 0x506d5098 794374Slars#ifdef PC98 8028597Speter#define ISA_ID_3C569B_COMBO 0x506d5694 8141568Sarchie#define ISA_ID_3C569B_TPO 0x506d5695 8250477Speter#endif 8328597Speter 844374Slars#ifdef __i386__ 854374Slarsstatic struct isa_ident ep_isa_devs[] = { 8628597Speter {ISA_ID_3C509_TP, "3Com 3C509-TP EtherLink III"}, 8711990Speter {ISA_ID_3C509_BNC, "3Com 3C509-BNC EtherLink III"}, 884374Slars {ISA_ID_3C509_COMBO, "3Com 3C509-Combo EtherLink III"}, 894374Slars {ISA_ID_3C509_TPO, "3Com 3C509-TPO EtherLink III"}, 904374Slars {ISA_ID_3C509_TPC, "3Com 3C509-TPC EtherLink III"}, 914374Slars#ifdef PC98 924374Slars {ISA_ID_3C569B_COMBO, "3Com 3C569B-J-Combo EtherLink III"}, 9328597Speter {ISA_ID_3C569B_TPO, "3Com 3C569B-J-TPO EtherLink III"}, 944374Slars#endif 954374Slars {0, NULL}, 964374Slars}; 974374Slars#endif 984374Slars 994374Slarsstatic struct isa_pnp_id ep_ids[] = { 1004374Slars {0x90506d50, "3Com 3C509B-TP EtherLink III (PnP)"}, /* TCM5090 */ 1014374Slars {0x91506d50, "3Com 3C509B-BNC EtherLink III (PnP)"}, /* TCM5091 */ 1024374Slars {0x94506d50, "3Com 3C509B-Combo EtherLink III (PnP)"}, /* TCM5094 */ 1034374Slars {0x95506d50, "3Com 3C509B-TPO EtherLink III (PnP)"}, /* TCM5095 */ 1044374Slars {0x98506d50, "3Com 3C509B-TPC EtherLink III (PnP)"}, /* TCM5098 */ 1054374Slars {0xf780d041, NULL}, /* PNP80f7 */ 1064374Slars {0, NULL}, 1074374Slars}; 1084374Slars 1094374Slars/* 1104374Slars * We get eeprom data from the id_port given an offset into the eeprom. 1114374Slars * Basically; after the ID_sequence is sent to all of the cards; they enter 1124374Slars * the ID_CMD state where they will accept command requests. 0x80-0xbf loads 1134374Slars * the eeprom data. We then read the port 16 times and with every read; the 1144374Slars * cards check for contention (ie: if one card writes a 0 bit and another 1154374Slars * writes a 1 bit then the host sees a 0. At the end of the cycle; each card 11634766Speter * compares the data on the bus; if there is a difference then that card goes 11734766Speter * into ID_WAIT state again). In the meantime; one bit of data is returned in 11834766Speter * the AX register which is conveniently returned to us by inb(). Hence; we 1194374Slars * read 16 times getting one bit of data with each read. 12034766Speter */ 12134766Speter#ifdef __i386__ 1224374Slarsstatic u_int16_t 1234374Slarsget_eeprom_data(int id_port, int offset) 12434766Speter{ 12534766Speter int i; 1264374Slars u_int16_t data = 0; 1274374Slars 1284374Slars outb(id_port, EEPROM_CMD_RD | offset); 1294374Slars DELAY(BIT_DELAY_MULTIPLE * 1000); 13011990Speter for (i = 0; i < 16; i++) { 13111990Speter DELAY(50); 13211990Speter data = (data << 1) | (inw(id_port) & 1); 13311990Speter } 13434766Speter return (data); 13534766Speter} 13634766Speter#endif 13734766Speter 13834766Speterconst char * 13934766Speterep_isa_match_id(u_int32_t id, struct isa_ident *isa_devs) 14034766Speter{ 14134766Speter struct isa_ident *i = isa_devs; 1424374Slars 1434374Slars while (i->name != NULL) { 1444374Slars if (id == i->id) 1454374Slars return (i->name); 1464374Slars i++; 1474374Slars } 1484374Slars /* 1494374Slars * If we see a card that is likely to be a 3c509 1504374Slars * return something so that it will work; be annoying 1514374Slars * so that the user will tell us about it though. 1524374Slars */ 1534374Slars if ((id >> 4) == ISA_ID_3C509_XXX) 1544374Slars return ("Unknown 3c509; notify maintainer!"); 15511990Speter return (NULL); 1564374Slars} 1574374Slars 15828597Speter#ifdef __i386__ 15911990Speterstatic void 16034766Speterep_isa_identify(driver_t * driver, device_t parent) 16134766Speter{ 16228597Speter int tag = EP_LAST_TAG; 16311990Speter int found = 0; 16411990Speter int i; 16511990Speter int j; 16611990Speter const char *desc; 16711990Speter u_int16_t data; 16811990Speter u_int32_t irq; 16934766Speter u_int32_t ioport; 17034766Speter u_int32_t isa_id; 17111990Speter device_t child; 1724374Slars 1734374Slars outb(ELINK_ID_PORT, 0); 17411990Speter outb(ELINK_ID_PORT, 0); 1754374Slars elink_idseq(ELINK_509_POLY); 17611990Speter elink_reset(); 17711990Speter 17811990Speter DELAY(DELAY_MULTIPLE * 10000); 1794374Slars 1804374Slars for (i = 0; i < EP_MAX_BOARDS; i++) { 18111990Speter 1824374Slars outb(ELINK_ID_PORT, 0); 18311990Speter outb(ELINK_ID_PORT, 0); 18411990Speter elink_idseq(ELINK_509_POLY); 18511990Speter DELAY(400); 1864374Slars 1874374Slars /* 1884374Slars * For the first probe, clear all board's tag registers. 1894374Slars * Otherwise kill off already-found boards. -- linux 3c509.c 1904374Slars */ 19128597Speter if (i == 0) 19228597Speter outb(ELINK_ID_PORT, 0xd0); 1934374Slars else 19411990Speter outb(ELINK_ID_PORT, 0xd8); 19511990Speter 19611990Speter /* Get out of loop if we're out of cards. */ 19728597Speter data = get_eeprom_data(ELINK_ID_PORT, EEPROM_MFG_ID); 19811990Speter if (data != MFG_ID) 19928597Speter break; 20028597Speter /* resolve contention using the Ethernet address */ 2014374Slars for (j = 0; j < 3; j++) 2024374Slars (void)get_eeprom_data(ELINK_ID_PORT, j); 20326880Scharnier 20434766Speter /* 20534766Speter * Construct an 'isa_id' in 'EISA' 2064374Slars * format. 2074374Slars */ 2084374Slars data = get_eeprom_data(ELINK_ID_PORT, EEPROM_MFG_ID); 2094374Slars isa_id = (htons(data) << 16); 2104374Slars data = get_eeprom_data(ELINK_ID_PORT, EEPROM_PROD_ID); 2114374Slars isa_id |= htons(data); 2124374Slars 21328597Speter /* Find known ISA boards */ 2144374Slars desc = ep_isa_match_id(isa_id, ep_isa_devs); 2154374Slars if (!desc) { 2164374Slars if (bootverbose) 2174374Slars device_printf(parent, 2184374Slars "if_ep: unknown ID 0x%08x\n", isa_id); 2194374Slars continue; 22011990Speter } 2214374Slars /* Retreive IRQ */ 2224374Slars data = get_eeprom_data(ELINK_ID_PORT, EEPROM_RESOURCE_CFG); 22311990Speter irq = (data >> 12); 2244374Slars 2254374Slars /* Retreive IOPORT */ 2264374Slars data = get_eeprom_data(ELINK_ID_PORT, EEPROM_ADDR_CFG); 2274374Slars#ifdef PC98 22828597Speter ioport = (((data & ADDR_CFG_MASK) * 0x100) + 0x40d0); 22928597Speter#else 23034766Speter ioport = (((data & ADDR_CFG_MASK) << 4) + 0x200); 2314374Slars#endif 23228597Speter 23328597Speter if ((data & ADDR_CFG_MASK) == ADDR_CFG_EISA) { 2344374Slars device_printf(parent, 2354374Slars "if_ep: <%s> at port 0x%03x in EISA mode!\n", 2364374Slars desc, ioport); 23734766Speter /* 2384374Slars * Set the adaptor tag so that the next card can be 2394374Slars * found. 24034766Speter */ 24134766Speter outb(ELINK_ID_PORT, tag--); 2424374Slars continue; 2434374Slars } 24434766Speter /* Test for an adapter with PnP support. */ 2454374Slars data = get_eeprom_data(ELINK_ID_PORT, EEPROM_CAP); 2464374Slars if (data == CAP_ISA) { 2474374Slars data = get_eeprom_data(ELINK_ID_PORT, 24834766Speter EEPROM_INT_CONFIG_1); 2494374Slars if (data & ICW1_IAS_PNP) { 25034766Speter if (bootverbose) 2514374Slars device_printf(parent, 2524374Slars "if_ep: <%s> at 0x%03x " 25334766Speter "in PnP mode!\n", 25434766Speter desc, ioport); 2554374Slars /* 2564374Slars * Set the adaptor tag so that the next card 2574374Slars * can be found. 2584374Slars */ 2594374Slars outb(ELINK_ID_PORT, tag--); 2604374Slars continue; 26134766Speter } 26234766Speter } 26334766Speter /* Set the adaptor tag so that the next card can be found. */ 2644374Slars outb(ELINK_ID_PORT, tag--); 2654374Slars 2664374Slars /* Activate the adaptor at the EEPROM location. */ 26711990Speter outb(ELINK_ID_PORT, ACTIVATE_ADAPTER_TO_CONFIG); 2684374Slars 26934766Speter /* Test for an adapter in TEST mode. */ 27034766Speter outw(ioport + EP_COMMAND, WINDOW_SELECT | 0); 27134766Speter data = inw(ioport + EP_W0_EEPROM_COMMAND); 27234766Speter if (data & EEPROM_TST_MODE) { 27334766Speter device_printf(parent, 27428597Speter "if_ep: <%s> at port 0x%03x in TEST mode!" 27534766Speter " Erase pencil mark.\n", 27634766Speter desc, ioport); 27734766Speter continue; 2784374Slars } 27934766Speter child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ep", -1); 28034766Speter device_set_desc_copy(child, desc); 28134766Speter device_set_driver(child, driver); 28228597Speter bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); 28334766Speter bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, EP_IOSIZE); 28434766Speter 28534766Speter if (bootverbose) 28634766Speter device_printf(parent, 28734766Speter "if_ep: <%s>" 28834766Speter " at port 0x%03x-0x%03x irq %d\n", 28934766Speter desc, ioport, ioport + EP_IOSIZE, irq); 29034766Speter found++; 29134766Speter } 29234766Speter} 2934374Slars#endif 29434766Speter 29534766Speterstatic int 29634766Speterep_isa_probe(device_t dev) 2974374Slars{ 29834766Speter int error = 0; 29934766Speter 30034766Speter /* Check isapnp ids */ 30134766Speter error = ISA_PNP_PROBE(device_get_parent(dev), dev, ep_ids); 30234766Speter 30334766Speter /* If the card had a PnP ID that didn't match any we know about */ 3044374Slars if (error == ENXIO) 30534766Speter return (error); 30634766Speter 30734766Speter /* If we had some other problem. */ 30834766Speter if (!(error == 0 || error == ENOENT)) 30934766Speter return (error); 31034766Speter 31134766Speter /* If we have the resources we need then we're good to go. */ 31234766Speter if ((bus_get_resource_start(dev, SYS_RES_IOPORT, 0) != 0) && 31334766Speter (bus_get_resource_start(dev, SYS_RES_IRQ, 0) != 0)) 31434766Speter return (0); 31534766Speter 31634766Speter return (ENXIO); 31734766Speter} 31834766Speter 31934766Speterstatic int 3204374Slarsep_isa_attach(device_t dev) 32134766Speter{ 32234766Speter struct ep_softc *sc = device_get_softc(dev); 32334766Speter int error = 0; 32434766Speter 3254374Slars if ((error = ep_alloc(dev))) { 32634766Speter device_printf(dev, "ep_alloc() failed! (%d)\n", error); 32734766Speter goto bad; 32834766Speter } 32934766Speter ep_get_media(sc); 33034766Speter 33134766Speter GO_WINDOW(sc, 0); 33234766Speter SET_IRQ(sc, rman_get_start(sc->irq)); 33334766Speter 33434766Speter if ((error = ep_attach(sc))) { 33534766Speter device_printf(dev, "ep_attach() failed! (%d)\n", error); 33634766Speter goto bad; 33734766Speter } 33834766Speter error = ep_eeprom_cksum(sc); 33934766Speter if (error) { 34011990Speter device_printf(sc->dev, "Invalid EEPROM checksum!\n"); 34111990Speter goto bad; 34211990Speter } 34311990Speter if ((error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, ep_intr, 34411990Speter sc, &sc->ep_intrhand))) { 3454374Slars device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); 34634766Speter goto bad; 3474374Slars } 34834766Speter return (0); 3494374Slarsbad: 35034766Speter ep_free(dev); 3514374Slars return (error); 35234766Speter} 35334766Speter 35434766Speterstatic int 35534766Speterep_eeprom_cksum(struct ep_softc *sc) 3564374Slars{ 35734766Speter int i; 3584374Slars int error; 3594374Slars u_int16_t val; 36011990Speter u_int16_t cksum; 36134766Speter u_int8_t cksum_high = 0; 3624374Slars u_int8_t cksum_low = 0; 3634374Slars 3644374Slars error = get_e(sc, 0x0f, &val); 3654374Slars if (error) 3664374Slars return (ENXIO); 36734766Speter cksum = val; 36834766Speter 3694374Slars for (i = 0; i < 0x0f; i++) { 3704374Slars error = get_e(sc, i, &val); 37128597Speter if (error) 3724374Slars return (ENXIO); 3734374Slars switch (i) { 37434766Speter case 0x08: 3754374Slars case 0x09: 3764374Slars case 0x0d: 37728597Speter cksum_low ^= (u_int8_t) (val & 0x00ff) ^ 37834766Speter (u_int8_t)((val & 0xff00) >> 8); 3794374Slars break; 3804374Slars default: 3814374Slars cksum_high ^= (u_int8_t) (val & 0x00ff) ^ 3824374Slars (u_int8_t)((val & 0xff00) >> 8); 3834374Slars break; 3844374Slars } 3854374Slars } 38634766Speter return (cksum != ((u_int16_t)cksum_low | (u_int16_t)(cksum_high << 8))); 38732069Salex} 3884374Slars 3894374Slarsstatic device_method_t ep_isa_methods[] = { 3904374Slars /* Device interface */ 3914374Slars#ifdef __i386__ 39211990Speter DEVMETHOD(device_identify, ep_isa_identify), 39311990Speter#endif 39434766Speter DEVMETHOD(device_probe, ep_isa_probe), 3954374Slars DEVMETHOD(device_attach, ep_isa_attach), 3964374Slars DEVMETHOD(device_detach, ep_detach), 3974374Slars 3984374Slars {0, 0} 39934766Speter}; 4004374Slars 4014374Slarsstatic driver_t ep_isa_driver = { 4024374Slars "ep", 4034374Slars ep_isa_methods, 4044374Slars sizeof(struct ep_softc), 4054374Slars}; 40628597Speter 40728597Speterextern devclass_t ep_devclass; 40828597Speter 40934766SpeterDRIVER_MODULE(ep, isa, ep_isa_driver, ep_devclass, 0, 0); 41034766Speter#ifdef __i386__ 41128597SpeterMODULE_DEPEND(ep, elink, 1, 1, 1); 41234766Speter#endif 41334766Speter