if_ep_isa.c revision 63379
1132718Skan/* 2169699Skan * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> 3132718Skan * All rights reserved. 4132718Skan * 5132718Skan * Redistribution and use in source and binary forms, with or without 6132718Skan * modification, are permitted provided that the following conditions 7132718Skan * are met: 8132718Skan * 1. Redistributions of source code must retain the above copyright 9132718Skan * notice, this list of conditions and the following disclaimer. 10169699Skan * 2. Redistributions in binary form must reproduce the above copyright 11132718Skan * notice, this list of conditions and the following disclaimer in the 12132718Skan * documentation and/or other materials provided with the distribution. 13132718Skan * 3. All advertising materials mentioning features or use of this software 14132718Skan * must display the following acknowledgement: 15169699Skan * This product includes software developed by Herb Peyerl. 16132718Skan * 4. The name of Herb Peyerl may not be used to endorse or promote products 17132718Skan * derived from this software without specific prior written permission. 18169699Skan * 19169699Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20132718Skan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21169699Skan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22132718Skan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23169699Skan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24132718Skan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25132718Skan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26132718Skan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27132718Skan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28132718Skan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29132718Skan * 30132718Skan * $FreeBSD: head/sys/dev/ep/if_ep_isa.c 63379 2000-07-18 06:37:08Z mdodd $ 31132718Skan */ 32132718Skan 33132718Skan#include <sys/param.h> 34132718Skan#include <sys/systm.h> 35132718Skan#include <sys/kernel.h> 36132718Skan#include <sys/socket.h> 37132718Skan 38132718Skan#include <sys/module.h> 39132718Skan#include <sys/bus.h> 40132718Skan 41132718Skan#include <machine/bus.h> 42132718Skan#include <machine/resource.h> 43132718Skan#include <sys/rman.h> 44132718Skan 45132718Skan#include <net/if.h> 46132718Skan#include <net/if_arp.h> 47132718Skan#include <net/if_media.h> 48132718Skan 49132718Skan#include <machine/clock.h> 50132718Skan 51132718Skan#include <isa/isavar.h> 52132718Skan#include <isa/pnpvar.h> 53132718Skan 54132718Skan#include <dev/ep/if_epreg.h> 55132718Skan#include <dev/ep/if_epvar.h> 56132718Skan#include <i386/isa/elink.h> 57132718Skan 58132718Skanstatic u_int16_t get_eeprom_data (int, int); 59132718Skan 60169699Skanstatic void ep_isa_identify (driver_t *, device_t); 61169699Skanstatic int ep_isa_probe (device_t); 62169699Skanstatic int ep_isa_attach (device_t); 63169699Skan 64132718Skanstruct isa_ident { 65132718Skan u_int32_t id; 66132718Skan char * name; 67132718Skan}; 68132718Skanconst char * ep_isa_match_id (u_int32_t, struct isa_ident *); 69132718Skan 70169699Skan#define ISA_ID_3C509_XXX 0x0506d509 71132718Skan#define ISA_ID_3C509_TP 0x506d5090 72132718Skan#define ISA_ID_3C509_BNC 0x506d5091 73132718Skan#define ISA_ID_3C509_COMBO 0x506d5094 74132718Skan#define ISA_ID_3C509_TPO 0x506d5095 75132718Skan#define ISA_ID_3C509_TPC 0x506d5098 76132718Skan 77132718Skanstatic struct isa_ident ep_isa_devs[] = { 78132718Skan { ISA_ID_3C509_TP, "3Com 3C509-TP EtherLink III" }, 79132718Skan { ISA_ID_3C509_BNC, "3Com 3C509-BNC EtherLink III" }, 80132718Skan { ISA_ID_3C509_COMBO, "3Com 3C509-Combo EtherLink III" }, 81132718Skan { ISA_ID_3C509_TPO, "3Com 3C509-TPO EtherLink III" }, 82132718Skan { ISA_ID_3C509_TPC, "3Com 3C509-TPC EtherLink III" }, 83132718Skan { 0, NULL }, 84132718Skan}; 85132718Skan 86132718Skanstatic struct isa_pnp_id ep_ids[] = { 87132718Skan { 0x90506d50, "3Com 3C509B-TP EtherLink III (PnP)" }, /* TCM5090 */ 88132718Skan { 0x91506d50, "3Com 3C509B-BNC EtherLink III (PnP)" },/* TCM5091 */ 89132718Skan { 0x94506d50, "3Com 3C509B-Combo EtherLink III (PnP)" },/* TCM5094 */ 90132718Skan { 0x95506d50, "3Com 3C509B-TPO EtherLink III (PnP)" },/* TCM5095 */ 91132718Skan { 0x98506d50, "3Com 3C509B-TPC EtherLink III (PnP)" },/* TCM5098 */ 92132718Skan { 0xf780d041, NULL }, /* PNP80f7 */ 93132718Skan { 0, NULL }, 94132718Skan}; 95132718Skan 96132718Skan/* 97132718Skan * We get eeprom data from the id_port given an offset into the eeprom. 98132718Skan * Basically; after the ID_sequence is sent to all of the cards; they enter 99132718Skan * the ID_CMD state where they will accept command requests. 0x80-0xbf loads 100132718Skan * the eeprom data. We then read the port 16 times and with every read; the 101132718Skan * cards check for contention (ie: if one card writes a 0 bit and another 102132718Skan * writes a 1 bit then the host sees a 0. At the end of the cycle; each card 103132718Skan * compares the data on the bus; if there is a difference then that card goes 104132718Skan * into ID_WAIT state again). In the meantime; one bit of data is returned in 105132718Skan * the AX register which is conveniently returned to us by inb(). Hence; we 106132718Skan * read 16 times getting one bit of data with each read. 107132718Skan */ 108132718Skan 109132718Skanstatic u_int16_t 110132718Skanget_eeprom_data(id_port, offset) 111132718Skan int id_port; 112132718Skan int offset; 113132718Skan{ 114132718Skan int i; 115132718Skan u_int16_t data = 0; 116132718Skan 117169699Skan outb(id_port, EEPROM_CMD_RD|offset); 118169699Skan DELAY(BIT_DELAY_MULTIPLE * 1000); 119132718Skan for (i = 0; i < 16; i++) { 120169699Skan DELAY(50); 121169699Skan data = (data << 1) | (inw(id_port) & 1); 122169699Skan } 123169699Skan return (data); 124132718Skan} 125132718Skan 126132718Skanconst char * 127132718Skanep_isa_match_id (id, isa_devs) 128169699Skan u_int32_t id; 129169699Skan struct isa_ident * isa_devs; 130169699Skan{ 131169699Skan struct isa_ident * i = isa_devs; 132132718Skan while(i->name != NULL) { 133169699Skan if (id == i->id) 134132718Skan return (i->name); 135132718Skan i++; 136169699Skan } 137169699Skan /* 138169699Skan * If we see a card that is likely to be a 3c509 139169699Skan * return something so that it will work; be annoying 140169699Skan * so that the user will tell us about it though. 141132718Skan */ 142169699Skan if ((id >> 4) == ISA_ID_3C509_XXX) { 143132718Skan return ("Unknown 3c509; notify maintainer!"); 144132718Skan } 145132718Skan return (NULL); 146169699Skan} 147132718Skan 148132718Skanstatic void 149132718Skanep_isa_identify (driver_t *driver, device_t parent) 150132718Skan{ 151132718Skan int tag = EP_LAST_TAG; 152132718Skan int found = 0; 153132718Skan int i; 154132718Skan int j; 155132718Skan const char * desc; 156132718Skan u_int16_t data; 157132718Skan u_int32_t irq; 158169699Skan u_int32_t ioport; 159132718Skan u_int32_t isa_id; 160132718Skan device_t child; 161132718Skan 162169699Skan outb(ELINK_ID_PORT, 0); 163132718Skan outb(ELINK_ID_PORT, 0); 164132718Skan elink_idseq(ELINK_509_POLY); 165132718Skan elink_reset(); 166169699Skan 167132718Skan DELAY(DELAY_MULTIPLE * 10000); 168132718Skan 169132718Skan for (i = 0; i < EP_MAX_BOARDS; i++) { 170169699Skan 171132718Skan outb(ELINK_ID_PORT, 0); 172132718Skan outb(ELINK_ID_PORT, 0); 173132718Skan elink_idseq(ELINK_509_POLY); 174169699Skan DELAY(400); 175132718Skan 176132718Skan /* For the first probe, clear all 177132718Skan * board's tag registers. 178169699Skan * Otherwise kill off already-found 179132718Skan * boards. -- linux 3c509.c 180132718Skan */ 181132718Skan if (i == 0) { 182132718Skan outb(ELINK_ID_PORT, 0xd0); 183132718Skan } else { 184132718Skan outb(ELINK_ID_PORT, 0xd8); 185132718Skan } 186132718Skan 187132718Skan /* Get out of loop if we're out of cards. */ 188132718Skan data = get_eeprom_data(ELINK_ID_PORT, EEPROM_MFG_ID); 189132718Skan if (data != MFG_ID) { 190132718Skan break; 191132718Skan } 192132718Skan 193132718Skan /* resolve contention using the Ethernet address */ 194169699Skan for (j = 0; j < 3; j++) { 195132718Skan (void)get_eeprom_data(ELINK_ID_PORT, j); 196132718Skan } 197132718Skan 198132718Skan /* 199132718Skan * Construct an 'isa_id' in 'EISA' 200132718Skan * format. 201132718Skan */ 202169699Skan data = get_eeprom_data(ELINK_ID_PORT, EEPROM_MFG_ID); 203132718Skan isa_id = (htons(data) << 16); 204132718Skan data = get_eeprom_data(ELINK_ID_PORT, EEPROM_PROD_ID); 205132718Skan isa_id |= htons(data); 206169699Skan 207132718Skan /* Find known ISA boards */ 208132718Skan desc = ep_isa_match_id(isa_id, ep_isa_devs); 209132718Skan if (!desc) { 210169699Skan if (bootverbose) { 211132718Skan device_printf(parent, "if_ep: unknown ID 0x%08x\n", 212132718Skan isa_id); 213132718Skan } 214169699Skan continue; 215132718Skan } 216132718Skan 217132718Skan /* Retreive IRQ */ 218169699Skan data = get_eeprom_data(ELINK_ID_PORT, EEPROM_RESOURCE_CFG); 219169699Skan irq = (data >> 12); 220132718Skan 221132718Skan /* Retreive IOPORT */ 222132718Skan data = get_eeprom_data(ELINK_ID_PORT, EEPROM_ADDR_CFG); 223132718Skan#ifdef PC98 224132718Skan ioport = (((data & ADDR_CFG_MASK) * 0x100) + 0x40d0); 225169699Skan#else 226169699Skan ioport = (((data & ADDR_CFG_MASK) << 4) + 0x200); 227132718Skan#endif 228132718Skan 229132718Skan if ((data & ADDR_CFG_MASK) == ADDR_CFG_EISA) { 230132718Skan device_printf(parent, "if_ep: <%s> at port 0x%03x in EISA mode!\n", 231132718Skan desc, ioport); 232169699Skan /* Set the adaptor tag so that the next card can be found. */ 233132718Skan outb(ELINK_ID_PORT, tag--); 234132718Skan continue; 235132718Skan } 236169699Skan 237132718Skan /* Test for an adapter with PnP support. */ 238132718Skan data = get_eeprom_data(ELINK_ID_PORT, EEPROM_CAP); 239132718Skan if (data == CAP_ISA) { 240132718Skan data = get_eeprom_data(ELINK_ID_PORT, EEPROM_INT_CONFIG_1); 241169699Skan if (data & ICW1_IAS_PNP) { 242132718Skan if (bootverbose) { 243169699Skan device_printf(parent, "if_ep: <%s> at 0x%03x in PnP mode!\n", 244169699Skan desc, ioport); 245169699Skan } 246169699Skan /* Set the adaptor tag so that the next card can be found. */ 247132718Skan outb(ELINK_ID_PORT, tag--); 248169699Skan continue; 249132718Skan } 250132718Skan } 251132718Skan 252132718Skan /* Set the adaptor tag so that the next card can be found. */ 253132718Skan outb(ELINK_ID_PORT, tag--); 254132718Skan 255132718Skan /* Activate the adaptor at the EEPROM location. */ 256169699Skan outb(ELINK_ID_PORT, ACTIVATE_ADAPTER_TO_CONFIG); 257132718Skan 258132718Skan /* Test for an adapter in TEST mode. */ 259132718Skan outw(ioport + EP_COMMAND, WINDOW_SELECT | 0); 260132718Skan data = inw(ioport + EP_W0_EEPROM_COMMAND); 261132718Skan if (data & EEPROM_TST_MODE) { 262132718Skan device_printf(parent, "if_ep: <%s> at port 0x%03x in TEST mode! Erase pencil mark.\n", 263132718Skan desc, ioport); 264169699Skan continue; 265132718Skan } 266132718Skan 267132718Skan child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ep", -1); 268169699Skan device_set_desc_copy(child, desc); 269132718Skan device_set_driver(child, driver); 270132718Skan bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); 271169699Skan bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, EP_IOSIZE); 272169699Skan 273169699Skan if (bootverbose) { 274169699Skan device_printf(parent, "if_ep: <%s> at port 0x%03x-0x%03x irq %d\n", 275132718Skan desc, ioport, ioport + EP_IOSIZE, irq); 276169699Skan } 277132718Skan 278132718Skan found++; 279169699Skan } 280169699Skan 281169699Skan return; 282169699Skan} 283132718Skan 284169699Skanstatic int 285132718Skanep_isa_probe (device_t dev) 286132718Skan{ 287132718Skan int error = 0; 288132718Skan 289132718Skan /* Check isapnp ids */ 290132718Skan error = ISA_PNP_PROBE(device_get_parent(dev), dev, ep_ids); 291132718Skan 292169699Skan /* If the card had a PnP ID that didn't match any we know about */ 293132718Skan if (error == ENXIO) { 294132718Skan return (error); 295132718Skan } 296169699Skan 297132718Skan /* If we had some other problem. */ 298132718Skan if (!(error == 0 || error == ENOENT)) { 299132718Skan return (error); 300169699Skan } 301132718Skan 302132718Skan /* If we have the resources we need then we're good to go. */ 303132718Skan if ((bus_get_resource_start(dev, SYS_RES_IOPORT, 0) != 0) && 304169699Skan (bus_get_resource_start(dev, SYS_RES_IRQ, 0) != 0)) { 305169699Skan return (0); 306132718Skan } 307169699Skan 308169699Skan return (ENXIO); 309169699Skan} 310169699Skan 311132718Skanstatic int 312169699Skanep_isa_attach (device_t dev) 313132718Skan{ 314132718Skan struct ep_softc * sc = device_get_softc(dev); 315132718Skan int error = 0; 316169699Skan 317132718Skan if ((error = ep_alloc(dev))) { 318132718Skan device_printf(dev, "ep_alloc() failed! (%d)\n", error); 319169699Skan goto bad; 320169699Skan } 321169699Skan 322169699Skan ep_get_media(sc); 323132718Skan 324169699Skan GO_WINDOW(0); 325132718Skan SET_IRQ(BASE, rman_get_start(sc->irq)); 326132718Skan 327169699Skan if ((error = ep_attach(sc))) { 328169699Skan device_printf(dev, "ep_attach() failed! (%d)\n", error); 329169699Skan goto bad; 330169699Skan } 331132718Skan 332169699Skan if ((error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, ep_intr, 333132718Skan sc, &sc->ep_intrhand))) { 334132718Skan device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); 335132718Skan goto bad; 336169699Skan } 337132718Skan 338132718Skan return (0); 339132718Skanbad: 340169699Skan ep_free(dev); 341132718Skan return (error); 342132718Skan} 343169699Skan 344169699Skanstatic device_method_t ep_isa_methods[] = { 345169699Skan /* Device interface */ 346169699Skan DEVMETHOD(device_identify, ep_isa_identify), 347169699Skan DEVMETHOD(device_probe, ep_isa_probe), 348169699Skan DEVMETHOD(device_attach, ep_isa_attach), 349169699Skan 350169699Skan { 0, 0 } 351132718Skan}; 352169699Skan 353132718Skanstatic driver_t ep_isa_driver = { 354132718Skan "ep", 355132718Skan ep_isa_methods, 356169699Skan sizeof(struct ep_softc), 357132718Skan}; 358132718Skan 359132718Skanextern devclass_t ep_devclass; 360169699Skan 361132718SkanDRIVER_MODULE(ep, isa, ep_isa_driver, ep_devclass, 0, 0); 362132718Skan