if_ep_isa.c revision 51673
151673Smdodd/* 251673Smdodd * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> 351673Smdodd * All rights reserved. 451673Smdodd * 551673Smdodd * Redistribution and use in source and binary forms, with or without 651673Smdodd * modification, are permitted provided that the following conditions 751673Smdodd * are met: 851673Smdodd * 1. Redistributions of source code must retain the above copyright 951673Smdodd * notice, this list of conditions and the following disclaimer. 1051673Smdodd * 2. Redistributions in binary form must reproduce the above copyright 1151673Smdodd * notice, this list of conditions and the following disclaimer in the 1251673Smdodd * documentation and/or other materials provided with the distribution. 1351673Smdodd * 3. All advertising materials mentioning features or use of this software 1451673Smdodd * must display the following acknowledgement: 1551673Smdodd * This product includes software developed by Herb Peyerl. 1651673Smdodd * 4. The name of Herb Peyerl may not be used to endorse or promote products 1751673Smdodd * derived from this software without specific prior written permission. 1851673Smdodd * 1951673Smdodd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2051673Smdodd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2151673Smdodd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2251673Smdodd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2351673Smdodd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2451673Smdodd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2551673Smdodd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2651673Smdodd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2751673Smdodd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2851673Smdodd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2951673Smdodd * 3051673Smdodd * $FreeBSD: head/sys/dev/ep/if_ep_isa.c 51673 1999-09-26 06:42:36Z mdodd $ 3151673Smdodd */ 3251673Smdodd 3351673Smdodd#include <sys/param.h> 3451673Smdodd#include <sys/kernel.h> 3551673Smdodd#include <sys/systm.h> 3651673Smdodd#include <sys/malloc.h> 3751673Smdodd#include <sys/mbuf.h> 3851673Smdodd#include <sys/socket.h> 3951673Smdodd#include <sys/sockio.h> 4051673Smdodd 4151673Smdodd#include <net/ethernet.h> 4251673Smdodd#include <net/if.h> 4351673Smdodd#include <netinet/in.h> 4451673Smdodd#include <netinet/if_ether.h> 4551673Smdodd 4651673Smdodd#include <machine/clock.h> 4751673Smdodd 4851673Smdodd#include <i386/isa/isa_device.h> 4951673Smdodd 5051673Smdodd#include <dev/ep/if_epreg.h> 5151673Smdodd#include <dev/ep/if_epvar.h> 5251673Smdodd#include <i386/isa/elink.h> 5351673Smdodd 5451673Smdoddstatic int ep_isa_probe (struct isa_device *); 5551673Smdoddstatic int ep_isa_attach (struct isa_device *); 5651673Smdoddstatic struct ep_board *ep_look_for_board_at (struct isa_device *is); 5751673Smdoddstatic int get_eeprom_data (int, int); 5851673Smdoddstatic void epintr (int); 5951673Smdodd 6051673Smdodd#if 0 6151673Smdoddstatic int send_ID_sequence (int); 6251673Smdodd#endif 6351673Smdodd 6451673Smdoddstatic int ep_current_tag = EP_LAST_TAG + 1; 6551673Smdodd 6651673Smdoddstruct isa_driver epdriver = { 6751673Smdodd ep_isa_probe, 6851673Smdodd ep_isa_attach, 6951673Smdodd "ep", 7051673Smdodd 0 7151673Smdodd}; 7251673Smdodd 7351673Smdoddint 7451673Smdoddep_isa_probe(is) 7551673Smdodd struct isa_device *is; 7651673Smdodd{ 7751673Smdodd struct ep_softc *sc; 7851673Smdodd struct ep_board *epb; 7951673Smdodd u_short k; 8051673Smdodd 8151673Smdodd if ((epb = ep_look_for_board_at(is)) == 0) 8251673Smdodd return (0); 8351673Smdodd 8451673Smdodd /* 8551673Smdodd * Allocate a storage area for us 8651673Smdodd */ 8751673Smdodd sc = ep_alloc(ep_unit, epb); 8851673Smdodd if (!sc) 8951673Smdodd return (0); 9051673Smdodd 9151673Smdodd is->id_unit = ep_unit++; 9251673Smdodd 9351673Smdodd /* 9451673Smdodd * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be 9551673Smdodd * 0x9[0-f]50 (IBM-PC) 9651673Smdodd * 0x9[0-f]5[0-f] (PC-98) 9751673Smdodd */ 9851673Smdodd GO_WINDOW(0); 9951673Smdodd k = sc->epb->prod_id; 10051673Smdodd#ifdef PC98 10151673Smdodd if ((k & 0xf0f0) != (PROD_ID & 0xf0f0)) { 10251673Smdodd#else 10351673Smdodd if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) { 10451673Smdodd#endif 10551673Smdodd printf("ep_isa_probe: ignoring model %04x\n", k); 10651673Smdodd ep_free(sc); 10751673Smdodd return (0); 10851673Smdodd } 10951673Smdodd 11051673Smdodd k = sc->epb->res_cfg; 11151673Smdodd 11251673Smdodd k >>= 12; 11351673Smdodd 11451673Smdodd /* Now we have two cases again: 11551673Smdodd * 11651673Smdodd * 1. Device was configured with 'irq?' 11751673Smdodd * In this case we use irq read from the board 11851673Smdodd * 11951673Smdodd * 2. Device was configured with 'irq xxx' 12051673Smdodd * In this case we set up the board to use specified interrupt 12151673Smdodd * 12251673Smdodd */ 12351673Smdodd 12451673Smdodd if (is->id_irq == 0) { /* irq? */ 12551673Smdodd is->id_irq = 1 << ((k == 2) ? 9 : k); 12651673Smdodd } 12751673Smdodd 12851673Smdodd sc->stat = 0; /* 16 bit access */ 12951673Smdodd 13051673Smdodd /* By now, the adapter is already activated */ 13151673Smdodd 13251673Smdodd return (EP_IOSIZE); /* 16 bytes of I/O space used. */ 13351673Smdodd} 13451673Smdodd 13551673Smdoddstatic int 13651673Smdoddep_isa_attach(is) 13751673Smdodd struct isa_device *is; 13851673Smdodd{ 13951673Smdodd struct ep_softc *sc = ep_softc[is->id_unit]; 14051673Smdodd u_short config; 14151673Smdodd int irq; 14251673Smdodd 14351673Smdodd is->id_ointr = epintr; 14451673Smdodd sc->ep_connectors = 0; 14551673Smdodd config = inw(IS_BASE + EP_W0_CONFIG_CTRL); 14651673Smdodd if (config & IS_AUI) { 14751673Smdodd sc->ep_connectors |= AUI; 14851673Smdodd } 14951673Smdodd if (config & IS_BNC) { 15051673Smdodd sc->ep_connectors |= BNC; 15151673Smdodd } 15251673Smdodd if (config & IS_UTP) { 15351673Smdodd sc->ep_connectors |= UTP; 15451673Smdodd } 15551673Smdodd if (!(sc->ep_connectors & 7)) 15651673Smdodd printf("no connectors!"); 15751673Smdodd sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS; 15851673Smdodd /* 15951673Smdodd * Write IRQ value to board 16051673Smdodd */ 16151673Smdodd 16251673Smdodd irq = ffs(is->id_irq) - 1; 16351673Smdodd if (irq == -1) { 16451673Smdodd printf(" invalid irq... cannot attach\n"); 16551673Smdodd return 0; 16651673Smdodd } 16751673Smdodd 16851673Smdodd GO_WINDOW(0); 16951673Smdodd SET_IRQ(BASE, irq); 17051673Smdodd 17151673Smdodd ep_attach(sc); 17251673Smdodd return 1; 17351673Smdodd} 17451673Smdodd 17551673Smdoddstatic struct ep_board * 17651673Smdoddep_look_for_board_at(is) 17751673Smdodd struct isa_device *is; 17851673Smdodd{ 17951673Smdodd int data, i, j, id_port = ELINK_ID_PORT; 18051673Smdodd int count = 0; 18151673Smdodd 18251673Smdodd if (ep_current_tag == (EP_LAST_TAG + 1)) { 18351673Smdodd /* Come here just one time */ 18451673Smdodd 18551673Smdodd ep_current_tag--; 18651673Smdodd 18751673Smdodd /* Look for the ISA boards. Init and leave them actived */ 18851673Smdodd outb(id_port, 0); 18951673Smdodd outb(id_port, 0); 19051673Smdodd 19151673Smdodd elink_idseq(0xCF); 19251673Smdodd 19351673Smdodd elink_reset(); 19451673Smdodd DELAY(DELAY_MULTIPLE * 10000); 19551673Smdodd for (i = 0; i < EP_MAX_BOARDS; i++) { 19651673Smdodd outb(id_port, 0); 19751673Smdodd outb(id_port, 0); 19851673Smdodd elink_idseq(0xCF); 19951673Smdodd 20051673Smdodd data = get_eeprom_data(id_port, EEPROM_MFG_ID); 20151673Smdodd if (data != MFG_ID) 20251673Smdodd break; 20351673Smdodd 20451673Smdodd /* resolve contention using the Ethernet address */ 20551673Smdodd 20651673Smdodd for (j = 0; j < 3; j++) 20751673Smdodd get_eeprom_data(id_port, j); 20851673Smdodd 20951673Smdodd /* and save this address for later use */ 21051673Smdodd 21151673Smdodd for (j = 0; j < 3; j++) 21251673Smdodd ep_board[ep_boards].eth_addr[j] = get_eeprom_data(id_port, j); 21351673Smdodd 21451673Smdodd ep_board[ep_boards].res_cfg = 21551673Smdodd get_eeprom_data(id_port, EEPROM_RESOURCE_CFG); 21651673Smdodd 21751673Smdodd ep_board[ep_boards].prod_id = 21851673Smdodd get_eeprom_data(id_port, EEPROM_PROD_ID); 21951673Smdodd 22051673Smdodd ep_board[ep_boards].epb_used = 0; 22151673Smdodd#ifdef PC98 22251673Smdodd ep_board[ep_boards].epb_addr = 22351673Smdodd (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 22451673Smdodd 0x100 + 0x40d0; 22551673Smdodd#else 22651673Smdodd ep_board[ep_boards].epb_addr = 22751673Smdodd (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 22851673Smdodd 0x10 + 0x200; 22951673Smdodd 23051673Smdodd if (ep_board[ep_boards].epb_addr > 0x3E0) 23151673Smdodd /* Board in EISA configuration mode */ 23251673Smdodd continue; 23351673Smdodd#endif /* PC98 */ 23451673Smdodd 23551673Smdodd outb(id_port, ep_current_tag); /* tags board */ 23651673Smdodd outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG); 23751673Smdodd ep_boards++; 23851673Smdodd count++; 23951673Smdodd ep_current_tag--; 24051673Smdodd } 24151673Smdodd 24251673Smdodd ep_board[ep_boards].epb_addr = 0; 24351673Smdodd if (count) { 24451673Smdodd printf("%d 3C5x9 board(s) on ISA found at", count); 24551673Smdodd for (j = 0; ep_board[j].epb_addr; j++) 24651673Smdodd if (ep_board[j].epb_addr <= 0x3E0) 24751673Smdodd printf(" 0x%x", ep_board[j].epb_addr); 24851673Smdodd printf("\n"); 24951673Smdodd } 25051673Smdodd } 25151673Smdodd 25251673Smdodd /* we have two cases: 25351673Smdodd * 25451673Smdodd * 1. Device was configured with 'port ?' 25551673Smdodd * In this case we search for the first unused card in list 25651673Smdodd * 25751673Smdodd * 2. Device was configured with 'port xxx' 25851673Smdodd * In this case we search for the unused card with that address 25951673Smdodd * 26051673Smdodd */ 26151673Smdodd 26251673Smdodd if (IS_BASE == -1) { /* port? */ 26351673Smdodd for (i = 0; ep_board[i].epb_addr && ep_board[i].epb_used; i++) 26451673Smdodd ; 26551673Smdodd if (ep_board[i].epb_addr == 0) 26651673Smdodd return 0; 26751673Smdodd 26851673Smdodd IS_BASE = ep_board[i].epb_addr; 26951673Smdodd ep_board[i].epb_used = 1; 27051673Smdodd 27151673Smdodd return &ep_board[i]; 27251673Smdodd } else { 27351673Smdodd for (i = 0; 27451673Smdodd ep_board[i].epb_addr && ep_board[i].epb_addr != IS_BASE; 27551673Smdodd i++) 27651673Smdodd ; 27751673Smdodd 27851673Smdodd if (ep_board[i].epb_used || ep_board[i].epb_addr != IS_BASE) 27951673Smdodd return 0; 28051673Smdodd 28151673Smdodd if (inw(IS_BASE + EP_W0_EEPROM_COMMAND) & EEPROM_TST_MODE) { 28251673Smdodd printf("ep%d: 3c5x9 at 0x%x in PnP mode. Disable PnP mode!\n", 28351673Smdodd is->id_unit, IS_BASE); 28451673Smdodd } 28551673Smdodd ep_board[i].epb_used = 1; 28651673Smdodd 28751673Smdodd return &ep_board[i]; 28851673Smdodd } 28951673Smdodd} 29051673Smdodd 29151673Smdodd/* 29251673Smdodd * We get eeprom data from the id_port given an offset into the eeprom. 29351673Smdodd * Basically; after the ID_sequence is sent to all of the cards; they enter 29451673Smdodd * the ID_CMD state where they will accept command requests. 0x80-0xbf loads 29551673Smdodd * the eeprom data. We then read the port 16 times and with every read; the 29651673Smdodd * cards check for contention (ie: if one card writes a 0 bit and another 29751673Smdodd * writes a 1 bit then the host sees a 0. At the end of the cycle; each card 29851673Smdodd * compares the data on the bus; if there is a difference then that card goes 29951673Smdodd * into ID_WAIT state again). In the meantime; one bit of data is returned in 30051673Smdodd * the AX register which is conveniently returned to us by inb(). Hence; we 30151673Smdodd * read 16 times getting one bit of data with each read. 30251673Smdodd */ 30351673Smdodd 30451673Smdoddstatic int 30551673Smdoddget_eeprom_data(id_port, offset) 30651673Smdodd int id_port; 30751673Smdodd int offset; 30851673Smdodd{ 30951673Smdodd int i, data = 0; 31051673Smdodd outb(id_port, 0x80 + offset); 31151673Smdodd for (i = 0; i < 16; i++) { 31251673Smdodd DELAY(BIT_DELAY_MULTIPLE * 1000); 31351673Smdodd data = (data << 1) | (inw(id_port) & 1); 31451673Smdodd } 31551673Smdodd return (data); 31651673Smdodd} 31751673Smdodd 31851673Smdoddvoid 31951673Smdoddepintr(unit) 32051673Smdodd int unit; 32151673Smdodd{ 32251673Smdodd register struct ep_softc *sc = ep_softc[unit]; 32351673Smdodd 32451673Smdodd ep_intr(sc); 32551673Smdodd 32651673Smdodd return; 32751673Smdodd} 32851673Smdodd 32951673Smdodd#if 0 33051673Smdoddstatic int 33151673Smdoddsend_ID_sequence(port) 33251673Smdodd int port; 33351673Smdodd{ 33451673Smdodd int cx, al; 33551673Smdodd 33651673Smdodd for (al = 0xff, cx = 0; cx < 255; cx++) { 33751673Smdodd outb(port, al); 33851673Smdodd al <<= 1; 33951673Smdodd if (al & 0x100) 34051673Smdodd al ^= 0xcf; 34151673Smdodd } 34251673Smdodd return (1); 34351673Smdodd} 34451673Smdodd#endif 345