if_ep_isa.c revision 110835
1174698Sdas/* 2174698Sdas * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> 3174698Sdas * All rights reserved. 4174698Sdas * 5174698Sdas * Redistribution and use in source and binary forms, with or without 6174698Sdas * modification, are permitted provided that the following conditions 7174698Sdas * are met: 8174698Sdas * 1. Redistributions of source code must retain the above copyright 9174698Sdas * notice, this list of conditions and the following disclaimer. 10174698Sdas * 2. Redistributions in binary form must reproduce the above copyright 11174698Sdas * notice, this list of conditions and the following disclaimer in the 12174698Sdas * documentation and/or other materials provided with the distribution. 13324006Sdim * 3. All advertising materials mentioning features or use of this software 14324006Sdim * must display the following acknowledgement: 15174698Sdas * This product includes software developed by Herb Peyerl. 16174698Sdas * 4. The name of Herb Peyerl may not be used to endorse or promote products 17174698Sdas * derived from this software without specific prior written permission. 18174698Sdas * 19174698Sdas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20174698Sdas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21174698Sdas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22174698Sdas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23174698Sdas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24174698Sdas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25174698Sdas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26174698Sdas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27174698Sdas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28174698Sdas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29174698Sdas * 30174698Sdas * $FreeBSD: head/sys/dev/ep/if_ep_isa.c 110835 2003-02-13 23:01:59Z mdodd $ 31174698Sdas */ 32174698Sdas 33174698Sdas#include <sys/param.h> 34174698Sdas#include <sys/systm.h> 35174698Sdas#include <sys/kernel.h> 36174698Sdas#include <sys/socket.h> 37174698Sdas 38174698Sdas#include <sys/module.h> 39174698Sdas#include <sys/bus.h> 40174698Sdas 41174698Sdas#include <machine/bus.h> 42174698Sdas#include <machine/resource.h> 43174698Sdas#include <sys/rman.h> 44174698Sdas 45174698Sdas#include <net/if.h> 46174698Sdas#include <net/if_arp.h> 47174698Sdas#include <net/if_media.h> 48174698Sdas 49174698Sdas#include <isa/isavar.h> 50174698Sdas 51174698Sdas#include <dev/ep/if_epreg.h> 52174698Sdas#include <dev/ep/if_epvar.h> 53174698Sdas 54174698Sdas#ifdef __i386__ 55#include <i386/isa/elink.h> 56#endif 57 58static u_int16_t get_eeprom_data (int, int); 59 60#ifdef __i386__ 61static void ep_isa_identify (driver_t *, device_t); 62#endif 63static int ep_isa_probe (device_t); 64static int ep_isa_attach (device_t); 65 66struct isa_ident { 67 u_int32_t id; 68 char * name; 69}; 70const char * ep_isa_match_id (u_int32_t, struct isa_ident *); 71 72#define ISA_ID_3C509_XXX 0x0506d509 73#define ISA_ID_3C509_TP 0x506d5090 74#define ISA_ID_3C509_BNC 0x506d5091 75#define ISA_ID_3C509_COMBO 0x506d5094 76#define ISA_ID_3C509_TPO 0x506d5095 77#define ISA_ID_3C509_TPC 0x506d5098 78#ifdef PC98 79#define ISA_ID_3C569B_COMBO 0x506d5694 80#define ISA_ID_3C569B_TPO 0x506d5695 81#endif 82 83static struct isa_ident ep_isa_devs[] = { 84 { ISA_ID_3C509_TP, "3Com 3C509-TP EtherLink III" }, 85 { ISA_ID_3C509_BNC, "3Com 3C509-BNC EtherLink III" }, 86 { ISA_ID_3C509_COMBO, "3Com 3C509-Combo EtherLink III" }, 87 { ISA_ID_3C509_TPO, "3Com 3C509-TPO EtherLink III" }, 88 { ISA_ID_3C509_TPC, "3Com 3C509-TPC EtherLink III" }, 89#ifdef PC98 90 { ISA_ID_3C569B_COMBO, "3Com 3C569B-J-Combo EtherLink III" }, 91 { ISA_ID_3C569B_TPO, "3Com 3C569B-J-TPO EtherLink III" }, 92#endif 93 { 0, NULL }, 94}; 95 96static struct isa_pnp_id ep_ids[] = { 97 { 0x90506d50, "3Com 3C509B-TP EtherLink III (PnP)" }, /* TCM5090 */ 98 { 0x91506d50, "3Com 3C509B-BNC EtherLink III (PnP)" },/* TCM5091 */ 99 { 0x94506d50, "3Com 3C509B-Combo EtherLink III (PnP)" },/* TCM5094 */ 100 { 0x95506d50, "3Com 3C509B-TPO EtherLink III (PnP)" },/* TCM5095 */ 101 { 0x98506d50, "3Com 3C509B-TPC EtherLink III (PnP)" },/* TCM5098 */ 102 { 0xf780d041, NULL }, /* PNP80f7 */ 103 { 0, NULL }, 104}; 105 106/* 107 * We get eeprom data from the id_port given an offset into the eeprom. 108 * Basically; after the ID_sequence is sent to all of the cards; they enter 109 * the ID_CMD state where they will accept command requests. 0x80-0xbf loads 110 * the eeprom data. We then read the port 16 times and with every read; the 111 * cards check for contention (ie: if one card writes a 0 bit and another 112 * writes a 1 bit then the host sees a 0. At the end of the cycle; each card 113 * compares the data on the bus; if there is a difference then that card goes 114 * into ID_WAIT state again). In the meantime; one bit of data is returned in 115 * the AX register which is conveniently returned to us by inb(). Hence; we 116 * read 16 times getting one bit of data with each read. 117 */ 118 119static u_int16_t 120get_eeprom_data(id_port, offset) 121 int id_port; 122 int offset; 123{ 124 int i; 125 u_int16_t data = 0; 126 127 outb(id_port, EEPROM_CMD_RD|offset); 128 DELAY(BIT_DELAY_MULTIPLE * 1000); 129 for (i = 0; i < 16; i++) { 130 DELAY(50); 131 data = (data << 1) | (inw(id_port) & 1); 132 } 133 return (data); 134} 135 136const char * 137ep_isa_match_id (id, isa_devs) 138 u_int32_t id; 139 struct isa_ident * isa_devs; 140{ 141 struct isa_ident * i = isa_devs; 142 while(i->name != NULL) { 143 if (id == i->id) 144 return (i->name); 145 i++; 146 } 147 /* 148 * If we see a card that is likely to be a 3c509 149 * return something so that it will work; be annoying 150 * so that the user will tell us about it though. 151 */ 152 if ((id >> 4) == ISA_ID_3C509_XXX) { 153 return ("Unknown 3c509; notify maintainer!"); 154 } 155 return (NULL); 156} 157 158#ifdef __i386__ 159static void 160ep_isa_identify (driver_t *driver, device_t parent) 161{ 162 int tag = EP_LAST_TAG; 163 int found = 0; 164 int i; 165 int j; 166 const char * desc; 167 u_int16_t data; 168 u_int32_t irq; 169 u_int32_t ioport; 170 u_int32_t isa_id; 171 device_t child; 172 173 outb(ELINK_ID_PORT, 0); 174 outb(ELINK_ID_PORT, 0); 175 elink_idseq(ELINK_509_POLY); 176 elink_reset(); 177 178 DELAY(DELAY_MULTIPLE * 10000); 179 180 for (i = 0; i < EP_MAX_BOARDS; i++) { 181 182 outb(ELINK_ID_PORT, 0); 183 outb(ELINK_ID_PORT, 0); 184 elink_idseq(ELINK_509_POLY); 185 DELAY(400); 186 187 /* For the first probe, clear all 188 * board's tag registers. 189 * Otherwise kill off already-found 190 * boards. -- linux 3c509.c 191 */ 192 if (i == 0) { 193 outb(ELINK_ID_PORT, 0xd0); 194 } else { 195 outb(ELINK_ID_PORT, 0xd8); 196 } 197 198 /* Get out of loop if we're out of cards. */ 199 data = get_eeprom_data(ELINK_ID_PORT, EEPROM_MFG_ID); 200 if (data != MFG_ID) { 201 break; 202 } 203 204 /* resolve contention using the Ethernet address */ 205 for (j = 0; j < 3; j++) { 206 (void)get_eeprom_data(ELINK_ID_PORT, j); 207 } 208 209 /* 210 * Construct an 'isa_id' in 'EISA' 211 * format. 212 */ 213 data = get_eeprom_data(ELINK_ID_PORT, EEPROM_MFG_ID); 214 isa_id = (htons(data) << 16); 215 data = get_eeprom_data(ELINK_ID_PORT, EEPROM_PROD_ID); 216 isa_id |= htons(data); 217 218 /* Find known ISA boards */ 219 desc = ep_isa_match_id(isa_id, ep_isa_devs); 220 if (!desc) { 221 if (bootverbose) { 222 device_printf(parent, "if_ep: unknown ID 0x%08x\n", 223 isa_id); 224 } 225 continue; 226 } 227 228 /* Retreive IRQ */ 229 data = get_eeprom_data(ELINK_ID_PORT, EEPROM_RESOURCE_CFG); 230 irq = (data >> 12); 231 232 /* Retreive IOPORT */ 233 data = get_eeprom_data(ELINK_ID_PORT, EEPROM_ADDR_CFG); 234#ifdef PC98 235 ioport = (((data & ADDR_CFG_MASK) * 0x100) + 0x40d0); 236#else 237 ioport = (((data & ADDR_CFG_MASK) << 4) + 0x200); 238#endif 239 240 if ((data & ADDR_CFG_MASK) == ADDR_CFG_EISA) { 241 device_printf(parent, "if_ep: <%s> at port 0x%03x in EISA mode!\n", 242 desc, ioport); 243 /* Set the adaptor tag so that the next card can be found. */ 244 outb(ELINK_ID_PORT, tag--); 245 continue; 246 } 247 248 /* Test for an adapter with PnP support. */ 249 data = get_eeprom_data(ELINK_ID_PORT, EEPROM_CAP); 250 if (data == CAP_ISA) { 251 data = get_eeprom_data(ELINK_ID_PORT, EEPROM_INT_CONFIG_1); 252 if (data & ICW1_IAS_PNP) { 253 if (bootverbose) { 254 device_printf(parent, "if_ep: <%s> at 0x%03x in PnP mode!\n", 255 desc, ioport); 256 } 257 /* Set the adaptor tag so that the next card can be found. */ 258 outb(ELINK_ID_PORT, tag--); 259 continue; 260 } 261 } 262 263 /* Set the adaptor tag so that the next card can be found. */ 264 outb(ELINK_ID_PORT, tag--); 265 266 /* Activate the adaptor at the EEPROM location. */ 267 outb(ELINK_ID_PORT, ACTIVATE_ADAPTER_TO_CONFIG); 268 269 /* Test for an adapter in TEST mode. */ 270 outw(ioport + EP_COMMAND, WINDOW_SELECT | 0); 271 data = inw(ioport + EP_W0_EEPROM_COMMAND); 272 if (data & EEPROM_TST_MODE) { 273 device_printf(parent, "if_ep: <%s> at port 0x%03x in TEST mode! Erase pencil mark.\n", 274 desc, ioport); 275 continue; 276 } 277 278 child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ep", -1); 279 device_set_desc_copy(child, desc); 280 device_set_driver(child, driver); 281 bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); 282 bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, EP_IOSIZE); 283 284 if (bootverbose) { 285 device_printf(parent, "if_ep: <%s> at port 0x%03x-0x%03x irq %d\n", 286 desc, ioport, ioport + EP_IOSIZE, irq); 287 } 288 289 found++; 290 } 291 292 return; 293} 294#endif 295 296static int 297ep_isa_probe (device_t dev) 298{ 299 int error = 0; 300 301 /* Check isapnp ids */ 302 error = ISA_PNP_PROBE(device_get_parent(dev), dev, ep_ids); 303 304 /* If the card had a PnP ID that didn't match any we know about */ 305 if (error == ENXIO) { 306 return (error); 307 } 308 309 /* If we had some other problem. */ 310 if (!(error == 0 || error == ENOENT)) { 311 return (error); 312 } 313 314 /* If we have the resources we need then we're good to go. */ 315 if ((bus_get_resource_start(dev, SYS_RES_IOPORT, 0) != 0) && 316 (bus_get_resource_start(dev, SYS_RES_IRQ, 0) != 0)) { 317 return (0); 318 } 319 320 return (ENXIO); 321} 322 323static int 324ep_isa_attach (device_t dev) 325{ 326 struct ep_softc * sc = device_get_softc(dev); 327 int error = 0; 328 329 if ((error = ep_alloc(dev))) { 330 device_printf(dev, "ep_alloc() failed! (%d)\n", error); 331 goto bad; 332 } 333 334 ep_get_media(sc); 335 336 GO_WINDOW(0); 337 SET_IRQ(BASE, rman_get_start(sc->irq)); 338 339 if ((error = ep_attach(sc))) { 340 device_printf(dev, "ep_attach() failed! (%d)\n", error); 341 goto bad; 342 } 343 344 if ((error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, ep_intr, 345 sc, &sc->ep_intrhand))) { 346 device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); 347 goto bad; 348 } 349 350 return (0); 351bad: 352 ep_free(dev); 353 return (error); 354} 355 356static device_method_t ep_isa_methods[] = { 357 /* Device interface */ 358#ifdef __i386__ 359 DEVMETHOD(device_identify, ep_isa_identify), 360#endif 361 DEVMETHOD(device_probe, ep_isa_probe), 362 DEVMETHOD(device_attach, ep_isa_attach), 363 364 { 0, 0 } 365}; 366 367static driver_t ep_isa_driver = { 368 "ep", 369 ep_isa_methods, 370 sizeof(struct ep_softc), 371}; 372 373extern devclass_t ep_devclass; 374 375DRIVER_MODULE(ep, isa, ep_isa_driver, ep_devclass, 0, 0); 376