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