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