if_ep_isa.c revision 51673
1/* 2 * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Herb Peyerl. 16 * 4. The name of Herb Peyerl may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * $FreeBSD: head/sys/dev/ep/if_ep_isa.c 51673 1999-09-26 06:42:36Z mdodd $ 31 */ 32 33#include <sys/param.h> 34#include <sys/kernel.h> 35#include <sys/systm.h> 36#include <sys/malloc.h> 37#include <sys/mbuf.h> 38#include <sys/socket.h> 39#include <sys/sockio.h> 40 41#include <net/ethernet.h> 42#include <net/if.h> 43#include <netinet/in.h> 44#include <netinet/if_ether.h> 45 46#include <machine/clock.h> 47 48#include <i386/isa/isa_device.h> 49 50#include <dev/ep/if_epreg.h> 51#include <dev/ep/if_epvar.h> 52#include <i386/isa/elink.h> 53 54static int ep_isa_probe (struct isa_device *); 55static int ep_isa_attach (struct isa_device *); 56static struct ep_board *ep_look_for_board_at (struct isa_device *is); 57static int get_eeprom_data (int, int); 58static void epintr (int); 59 60#if 0 61static int send_ID_sequence (int); 62#endif 63 64static int ep_current_tag = EP_LAST_TAG + 1; 65 66struct isa_driver epdriver = { 67 ep_isa_probe, 68 ep_isa_attach, 69 "ep", 70 0 71}; 72 73int 74ep_isa_probe(is) 75 struct isa_device *is; 76{ 77 struct ep_softc *sc; 78 struct ep_board *epb; 79 u_short k; 80 81 if ((epb = ep_look_for_board_at(is)) == 0) 82 return (0); 83 84 /* 85 * Allocate a storage area for us 86 */ 87 sc = ep_alloc(ep_unit, epb); 88 if (!sc) 89 return (0); 90 91 is->id_unit = ep_unit++; 92 93 /* 94 * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be 95 * 0x9[0-f]50 (IBM-PC) 96 * 0x9[0-f]5[0-f] (PC-98) 97 */ 98 GO_WINDOW(0); 99 k = sc->epb->prod_id; 100#ifdef PC98 101 if ((k & 0xf0f0) != (PROD_ID & 0xf0f0)) { 102#else 103 if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) { 104#endif 105 printf("ep_isa_probe: ignoring model %04x\n", k); 106 ep_free(sc); 107 return (0); 108 } 109 110 k = sc->epb->res_cfg; 111 112 k >>= 12; 113 114 /* Now we have two cases again: 115 * 116 * 1. Device was configured with 'irq?' 117 * In this case we use irq read from the board 118 * 119 * 2. Device was configured with 'irq xxx' 120 * In this case we set up the board to use specified interrupt 121 * 122 */ 123 124 if (is->id_irq == 0) { /* irq? */ 125 is->id_irq = 1 << ((k == 2) ? 9 : k); 126 } 127 128 sc->stat = 0; /* 16 bit access */ 129 130 /* By now, the adapter is already activated */ 131 132 return (EP_IOSIZE); /* 16 bytes of I/O space used. */ 133} 134 135static int 136ep_isa_attach(is) 137 struct isa_device *is; 138{ 139 struct ep_softc *sc = ep_softc[is->id_unit]; 140 u_short config; 141 int irq; 142 143 is->id_ointr = epintr; 144 sc->ep_connectors = 0; 145 config = inw(IS_BASE + EP_W0_CONFIG_CTRL); 146 if (config & IS_AUI) { 147 sc->ep_connectors |= AUI; 148 } 149 if (config & IS_BNC) { 150 sc->ep_connectors |= BNC; 151 } 152 if (config & IS_UTP) { 153 sc->ep_connectors |= UTP; 154 } 155 if (!(sc->ep_connectors & 7)) 156 printf("no connectors!"); 157 sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS; 158 /* 159 * Write IRQ value to board 160 */ 161 162 irq = ffs(is->id_irq) - 1; 163 if (irq == -1) { 164 printf(" invalid irq... cannot attach\n"); 165 return 0; 166 } 167 168 GO_WINDOW(0); 169 SET_IRQ(BASE, irq); 170 171 ep_attach(sc); 172 return 1; 173} 174 175static struct ep_board * 176ep_look_for_board_at(is) 177 struct isa_device *is; 178{ 179 int data, i, j, id_port = ELINK_ID_PORT; 180 int count = 0; 181 182 if (ep_current_tag == (EP_LAST_TAG + 1)) { 183 /* Come here just one time */ 184 185 ep_current_tag--; 186 187 /* Look for the ISA boards. Init and leave them actived */ 188 outb(id_port, 0); 189 outb(id_port, 0); 190 191 elink_idseq(0xCF); 192 193 elink_reset(); 194 DELAY(DELAY_MULTIPLE * 10000); 195 for (i = 0; i < EP_MAX_BOARDS; i++) { 196 outb(id_port, 0); 197 outb(id_port, 0); 198 elink_idseq(0xCF); 199 200 data = get_eeprom_data(id_port, EEPROM_MFG_ID); 201 if (data != MFG_ID) 202 break; 203 204 /* resolve contention using the Ethernet address */ 205 206 for (j = 0; j < 3; j++) 207 get_eeprom_data(id_port, j); 208 209 /* and save this address for later use */ 210 211 for (j = 0; j < 3; j++) 212 ep_board[ep_boards].eth_addr[j] = get_eeprom_data(id_port, j); 213 214 ep_board[ep_boards].res_cfg = 215 get_eeprom_data(id_port, EEPROM_RESOURCE_CFG); 216 217 ep_board[ep_boards].prod_id = 218 get_eeprom_data(id_port, EEPROM_PROD_ID); 219 220 ep_board[ep_boards].epb_used = 0; 221#ifdef PC98 222 ep_board[ep_boards].epb_addr = 223 (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 224 0x100 + 0x40d0; 225#else 226 ep_board[ep_boards].epb_addr = 227 (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 228 0x10 + 0x200; 229 230 if (ep_board[ep_boards].epb_addr > 0x3E0) 231 /* Board in EISA configuration mode */ 232 continue; 233#endif /* PC98 */ 234 235 outb(id_port, ep_current_tag); /* tags board */ 236 outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG); 237 ep_boards++; 238 count++; 239 ep_current_tag--; 240 } 241 242 ep_board[ep_boards].epb_addr = 0; 243 if (count) { 244 printf("%d 3C5x9 board(s) on ISA found at", count); 245 for (j = 0; ep_board[j].epb_addr; j++) 246 if (ep_board[j].epb_addr <= 0x3E0) 247 printf(" 0x%x", ep_board[j].epb_addr); 248 printf("\n"); 249 } 250 } 251 252 /* we have two cases: 253 * 254 * 1. Device was configured with 'port ?' 255 * In this case we search for the first unused card in list 256 * 257 * 2. Device was configured with 'port xxx' 258 * In this case we search for the unused card with that address 259 * 260 */ 261 262 if (IS_BASE == -1) { /* port? */ 263 for (i = 0; ep_board[i].epb_addr && ep_board[i].epb_used; i++) 264 ; 265 if (ep_board[i].epb_addr == 0) 266 return 0; 267 268 IS_BASE = ep_board[i].epb_addr; 269 ep_board[i].epb_used = 1; 270 271 return &ep_board[i]; 272 } else { 273 for (i = 0; 274 ep_board[i].epb_addr && ep_board[i].epb_addr != IS_BASE; 275 i++) 276 ; 277 278 if (ep_board[i].epb_used || ep_board[i].epb_addr != IS_BASE) 279 return 0; 280 281 if (inw(IS_BASE + EP_W0_EEPROM_COMMAND) & EEPROM_TST_MODE) { 282 printf("ep%d: 3c5x9 at 0x%x in PnP mode. Disable PnP mode!\n", 283 is->id_unit, IS_BASE); 284 } 285 ep_board[i].epb_used = 1; 286 287 return &ep_board[i]; 288 } 289} 290 291/* 292 * We get eeprom data from the id_port given an offset into the eeprom. 293 * Basically; after the ID_sequence is sent to all of the cards; they enter 294 * the ID_CMD state where they will accept command requests. 0x80-0xbf loads 295 * the eeprom data. We then read the port 16 times and with every read; the 296 * cards check for contention (ie: if one card writes a 0 bit and another 297 * writes a 1 bit then the host sees a 0. At the end of the cycle; each card 298 * compares the data on the bus; if there is a difference then that card goes 299 * into ID_WAIT state again). In the meantime; one bit of data is returned in 300 * the AX register which is conveniently returned to us by inb(). Hence; we 301 * read 16 times getting one bit of data with each read. 302 */ 303 304static int 305get_eeprom_data(id_port, offset) 306 int id_port; 307 int offset; 308{ 309 int i, data = 0; 310 outb(id_port, 0x80 + offset); 311 for (i = 0; i < 16; i++) { 312 DELAY(BIT_DELAY_MULTIPLE * 1000); 313 data = (data << 1) | (inw(id_port) & 1); 314 } 315 return (data); 316} 317 318void 319epintr(unit) 320 int unit; 321{ 322 register struct ep_softc *sc = ep_softc[unit]; 323 324 ep_intr(sc); 325 326 return; 327} 328 329#if 0 330static int 331send_ID_sequence(port) 332 int port; 333{ 334 int cx, al; 335 336 for (al = 0xff, cx = 0; cx < 255; cx++) { 337 outb(port, al); 338 al <<= 1; 339 if (al & 0x100) 340 al ^= 0xcf; 341 } 342 return (1); 343} 344#endif 345