]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/dev/ed/if_ed_wd80x3.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / dev / ed / if_ed_wd80x3.c
1 /*-
2  * Copyright (c) 2005, M. Warner Losh
3  * All rights reserved.
4  * Copyright (c) 1995, David Greenman 
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include "opt_ed.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/sockio.h>
38 #include <sys/mbuf.h>
39 #include <sys/kernel.h>
40 #include <sys/socket.h>
41 #include <sys/syslog.h>
42
43 #include <sys/bus.h>
44
45 #include <machine/bus.h>
46 #include <sys/rman.h>
47 #include <machine/resource.h>
48
49 #include <net/ethernet.h>
50 #include <net/if.h>
51 #include <net/if_arp.h>
52 #include <net/if_dl.h>
53 #include <net/if_mib.h>
54 #include <net/if_media.h>
55
56 #include <net/bpf.h>
57
58 #include <dev/ed/if_edreg.h>
59 #include <dev/ed/if_edvar.h>
60
61 /*
62  * Interrupt conversion table for WD/SMC ASIC/83C584
63  */
64 static uint16_t ed_intr_val[] = {
65         9,
66         3,
67         5,
68         7,
69         10,
70         11,
71         15,
72         4
73 };
74
75 /*
76  * Interrupt conversion table for 83C790
77  */
78 static uint16_t ed_790_intr_val[] = {
79         0,
80         9,
81         3,
82         5,
83         7,
84         10,
85         11,
86         15
87 };
88
89 /*
90  * Probe and vendor-specific initialization routine for SMC/WD80x3 boards
91  */
92 int
93 ed_probe_WD80x3_generic(device_t dev, int flags, uint16_t *intr_vals[])
94 {
95         struct ed_softc *sc = device_get_softc(dev);
96         int     error;
97         int     i;
98         u_int   memsize;
99         u_char  iptr, isa16bit, sum, totalsum;
100         u_long  irq, junk, pmem;
101
102         sc->chip_type = ED_CHIP_TYPE_DP8390;
103
104         if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_TOSH_ETHER) {
105                 totalsum = ED_WD_ROM_CHECKSUM_TOTAL_TOSH_ETHER;
106                 ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_POW);
107                 DELAY(10000);
108         }
109         else
110                 totalsum = ED_WD_ROM_CHECKSUM_TOTAL;
111
112         /*
113          * Attempt to do a checksum over the station address PROM. If it
114          * fails, it's probably not a SMC/WD board. There is a problem with
115          * this, though: some clone WD boards don't pass the checksum test.
116          * Danpex boards for one.
117          */
118         for (sum = 0, i = 0; i < 8; ++i)
119                 sum += ed_asic_inb(sc, ED_WD_PROM + i);
120
121         if (sum != totalsum) {
122                 /*
123                  * Checksum is invalid. This often happens with cheap WD8003E
124                  * clones.  In this case, the checksum byte (the eighth byte)
125                  * seems to always be zero.
126                  */
127                 if (ed_asic_inb(sc, ED_WD_CARD_ID) != ED_TYPE_WD8003E ||
128                     ed_asic_inb(sc, ED_WD_PROM + 7) != 0)
129                         return (ENXIO);
130         }
131         /* reset card to force it into a known state. */
132         if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_TOSH_ETHER)
133                 ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW);
134         else
135                 ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_RST);
136
137         DELAY(100);
138         ed_asic_outb(sc, ED_WD_MSR, ed_asic_inb(sc, ED_WD_MSR) & ~ED_WD_MSR_RST);
139         /* wait in the case this card is reading its EEROM */
140         DELAY(5000);
141
142         sc->vendor = ED_VENDOR_WD_SMC;
143         sc->type = ed_asic_inb(sc, ED_WD_CARD_ID);
144
145         /*
146          * Set initial values for width/size.
147          */
148         memsize = 8192;
149         isa16bit = 0;
150         switch (sc->type) {
151         case ED_TYPE_WD8003S:
152                 sc->type_str = "WD8003S";
153                 break;
154         case ED_TYPE_WD8003E:
155                 sc->type_str = "WD8003E";
156                 break;
157         case ED_TYPE_WD8003EB:
158                 sc->type_str = "WD8003EB";
159                 break;
160         case ED_TYPE_WD8003W:
161                 sc->type_str = "WD8003W";
162                 break;
163         case ED_TYPE_WD8013EBT:
164                 sc->type_str = "WD8013EBT";
165                 memsize = 16384;
166                 isa16bit = 1;
167                 break;
168         case ED_TYPE_WD8013W:
169                 sc->type_str = "WD8013W";
170                 memsize = 16384;
171                 isa16bit = 1;
172                 break;
173         case ED_TYPE_WD8013EP:  /* also WD8003EP */
174                 if (ed_asic_inb(sc, ED_WD_ICR) & ED_WD_ICR_16BIT) {
175                         isa16bit = 1;
176                         memsize = 16384;
177                         sc->type_str = "WD8013EP";
178                 } else
179                         sc->type_str = "WD8003EP";
180                 break;
181         case ED_TYPE_WD8013WC:
182                 sc->type_str = "WD8013WC";
183                 memsize = 16384;
184                 isa16bit = 1;
185                 break;
186         case ED_TYPE_WD8013EBP:
187                 sc->type_str = "WD8013EBP";
188                 memsize = 16384;
189                 isa16bit = 1;
190                 break;
191         case ED_TYPE_WD8013EPC:
192                 sc->type_str = "WD8013EPC";
193                 memsize = 16384;
194                 isa16bit = 1;
195                 break;
196         case ED_TYPE_SMC8216C: /* 8216 has 16K shared mem -- 8416 has 8K */
197         case ED_TYPE_SMC8216T:
198                 if (sc->type == ED_TYPE_SMC8216C)
199                         sc->type_str = "SMC8216/SMC8216C";
200                 else
201                         sc->type_str = "SMC8216T";
202
203                 ed_asic_outb(sc, ED_WD790_HWR,
204                     ed_asic_inb(sc, ED_WD790_HWR) | ED_WD790_HWR_SWH);
205                 switch (ed_asic_inb(sc, ED_WD790_RAR) & ED_WD790_RAR_SZ64) {
206                 case ED_WD790_RAR_SZ64:
207                         memsize = 65536;
208                         break;
209                 case ED_WD790_RAR_SZ32:
210                         memsize = 32768;
211                         break;
212                 case ED_WD790_RAR_SZ16:
213                         memsize = 16384;
214                         break;
215                 case ED_WD790_RAR_SZ8:
216                         /* 8216 has 16K shared mem -- 8416 has 8K */
217                         if (sc->type == ED_TYPE_SMC8216C)
218                                 sc->type_str = "SMC8416C/SMC8416BT";
219                         else
220                                 sc->type_str = "SMC8416T";
221                         memsize = 8192;
222                         break;
223                 }
224                 ed_asic_outb(sc, ED_WD790_HWR,
225                     ed_asic_inb(sc, ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
226
227                 isa16bit = 1;
228                 sc->chip_type = ED_CHIP_TYPE_WD790;
229                 break;
230         case ED_TYPE_TOSHIBA1:
231                 sc->type_str = "Toshiba1";
232                 memsize = 32768;
233                 isa16bit = 1;
234                 break;
235         case ED_TYPE_TOSHIBA4:
236                 sc->type_str = "Toshiba4";
237                 memsize = 32768;
238                 isa16bit = 1;
239                 break;
240         default:
241                 sc->type_str = "";
242                 break;
243         }
244
245         /*
246          * Make some adjustments to initial values depending on what is found
247          * in the ICR.
248          */
249         if (isa16bit && (sc->type != ED_TYPE_WD8013EBT)
250           && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4)
251             && ((ed_asic_inb(sc, ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) {
252                 isa16bit = 0;
253                 memsize = 8192;
254         }
255
256         /* Override memsize? XXX */
257         error = ed_alloc_memory(dev, 0, memsize);
258         if (error)
259                 return (error);
260         sc->mem_start = 0;
261
262 #ifdef ED_DEBUG
263         printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%lu\n",
264             sc->type, sc->type_str, isa16bit, memsize,
265             rman_get_size(sc->mem_res));
266         for (i = 0; i < 8; i++)
267                 printf("%x -> %x\n", i, ed_asic_inb(sc, i));
268 #endif
269         pmem = rman_get_start(sc->mem_res);
270         if (!(flags & ED_FLAGS_PCCARD)) {
271                 error = ed_isa_mem_ok(dev, pmem, memsize);
272                 if (error)
273                         return (error);
274         }
275
276         /*
277          * (note that if the user specifies both of the following flags that
278          * '8bit' mode intentionally has precedence)
279          */
280         if (flags & ED_FLAGS_FORCE_16BIT_MODE)
281                 isa16bit = 1;
282         if (flags & ED_FLAGS_FORCE_8BIT_MODE)
283                 isa16bit = 0;
284
285         /*
286          * If possible, get the assigned interrupt number from the card and
287          * use it.
288          */
289         if ((sc->type & ED_WD_SOFTCONFIG) &&
290             (sc->chip_type != ED_CHIP_TYPE_WD790)) {
291
292                 /*
293                  * Assemble together the encoded interrupt number.
294                  */
295                 iptr = (ed_asic_inb(sc, ED_WD_ICR) & ED_WD_ICR_IR2) |
296                     ((ed_asic_inb(sc, ED_WD_IRR) &
297                       (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5);
298
299                 /*
300                  * If no interrupt specified (or "?"), use what the board tells us.
301                  */
302                 error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
303                 if (error && intr_vals[0] != NULL)
304                         error = bus_set_resource(dev, SYS_RES_IRQ, 0,
305                             intr_vals[0][iptr], 1);
306                 if (error)
307                         return (error);
308
309                 /*
310                  * Enable the interrupt.
311                  */
312                 ed_asic_outb(sc, ED_WD_IRR,
313                      ed_asic_inb(sc, ED_WD_IRR) | ED_WD_IRR_IEN);
314         }
315         if (sc->chip_type == ED_CHIP_TYPE_WD790) {
316                 ed_asic_outb(sc, ED_WD790_HWR,
317                   ed_asic_inb(sc, ED_WD790_HWR) | ED_WD790_HWR_SWH);
318                 iptr = (((ed_asic_inb(sc, ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) |
319                         (ed_asic_inb(sc, ED_WD790_GCR) &
320                          (ED_WD790_GCR_IR1 | ED_WD790_GCR_IR0)) >> 2);
321                 ed_asic_outb(sc, ED_WD790_HWR,
322                     ed_asic_inb(sc, ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
323
324                 /*
325                  * If no interrupt specified (or "?"), use what the board tells us.
326                  */
327                 error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
328                 if (error && intr_vals[1] != NULL)
329                         error = bus_set_resource(dev, SYS_RES_IRQ, 0,
330                           intr_vals[1][iptr], 1);
331                 if (error)
332                         return (error);
333
334                 /*
335                  * Enable interrupts.
336                  */
337                 ed_asic_outb(sc, ED_WD790_ICR,
338                   ed_asic_inb(sc, ED_WD790_ICR) | ED_WD790_ICR_EIL);
339         }
340         error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
341         if (error) {
342                 device_printf(dev, "%s cards don't support auto-detected/assigned interrupts.\n",
343                               sc->type_str);
344                 return (ENXIO);
345         }
346         sc->isa16bit = isa16bit;
347         sc->mem_shared = 1;
348
349         /*
350          * allocate one xmit buffer if < 16k, two buffers otherwise
351          */
352         if (memsize < 16384 || (flags & ED_FLAGS_NO_MULTI_BUFFERING))
353                 sc->txb_cnt = 1;
354         else
355                 sc->txb_cnt = 2;
356         sc->tx_page_start = ED_WD_PAGE_OFFSET;
357         sc->rec_page_start = ED_WD_PAGE_OFFSET + ED_TXBUF_SIZE * sc->txb_cnt;
358         sc->rec_page_stop = ED_WD_PAGE_OFFSET + memsize / ED_PAGE_SIZE;
359         sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * sc->rec_page_start);
360         sc->mem_size = memsize;
361         sc->mem_end = sc->mem_start + memsize;
362
363         /*
364          * Get station address from on-board ROM
365          */
366         for (i = 0; i < ETHER_ADDR_LEN; ++i)
367                 sc->enaddr[i] = ed_asic_inb(sc, ED_WD_PROM + i);
368
369         /*
370          * Set upper address bits and 8/16 bit access to shared memory.
371          */
372         if (isa16bit) {
373                 if (sc->chip_type == ED_CHIP_TYPE_WD790)
374                         sc->wd_laar_proto = ed_asic_inb(sc, ED_WD_LAAR);
375                 else
376                         sc->wd_laar_proto = ED_WD_LAAR_L16EN |
377                             ((pmem >> 19) & ED_WD_LAAR_ADDRHI);
378                 /*
379                  * Enable 16bit access
380                  */
381                 ed_asic_outb(sc, ED_WD_LAAR, sc->wd_laar_proto |
382                     ED_WD_LAAR_M16EN);
383         } else {
384                 if (((sc->type & ED_WD_SOFTCONFIG) ||
385                      (sc->type == ED_TYPE_TOSHIBA1) ||
386                      (sc->type == ED_TYPE_TOSHIBA4) ||
387                      (sc->type == ED_TYPE_WD8013EBT)) &&
388                     (sc->chip_type != ED_CHIP_TYPE_WD790)) {
389                         sc->wd_laar_proto = (pmem >> 19) &
390                             ED_WD_LAAR_ADDRHI;
391                         ed_asic_outb(sc, ED_WD_LAAR, sc->wd_laar_proto);
392                 }
393         }
394
395         /*
396          * Set address and enable interface shared memory.
397          */
398         if (sc->chip_type != ED_CHIP_TYPE_WD790) {
399                 if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_TOSH_ETHER) {
400                         ed_asic_outb(sc, ED_WD_MSR + 1,
401                             ((pmem >> 8) & 0xe0) | 4);
402                         ed_asic_outb(sc, ED_WD_MSR + 2, ((pmem >> 16) & 0x0f));
403                         ed_asic_outb(sc, ED_WD_MSR,
404                             ED_WD_MSR_MENB | ED_WD_MSR_POW);
405                 } else {
406                         ed_asic_outb(sc, ED_WD_MSR, ((pmem >> 13) &
407                             ED_WD_MSR_ADDR) | ED_WD_MSR_MENB);
408                 }
409                 sc->cr_proto = ED_CR_RD2;
410         } else {
411                 ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_MENB);
412                 ed_asic_outb(sc, ED_WD790_HWR,
413                     (ed_asic_inb(sc, ED_WD790_HWR) | ED_WD790_HWR_SWH));
414                 ed_asic_outb(sc, ED_WD790_RAR,
415                     ((pmem >> 13) & 0x0f) | ((pmem >> 11) & 0x40) |
416                      (ed_asic_inb(sc, ED_WD790_RAR) & 0xb0));
417                 ed_asic_outb(sc, ED_WD790_HWR,
418                     (ed_asic_inb(sc, ED_WD790_HWR) & ~ED_WD790_HWR_SWH));
419                 sc->cr_proto = 0;
420         }
421
422         /*
423          * Disable 16bit access to shared memory - we leave it
424          * disabled so that 1) machines reboot properly when the board
425          * is set 16 bit mode and there are conflicting 8bit
426          * devices/ROMS in the same 128k address space as this boards
427          * shared memory. and 2) so that other 8 bit devices with
428          * shared memory can be used in this 128k region, too.
429          */
430         error = ed_clear_memory(dev);
431         ed_disable_16bit_access(sc);
432         sc->sc_write_mbufs = ed_shmem_write_mbufs;
433         return (error);
434 }
435
436 int
437 ed_probe_WD80x3(device_t dev, int port_rid, int flags)
438 {
439         struct ed_softc *sc = device_get_softc(dev);
440         int     error;
441         static uint16_t *intr_vals[] = {ed_intr_val, ed_790_intr_val};
442
443         error = ed_alloc_port(dev, port_rid, ED_WD_IO_PORTS);
444         if (error)
445                 return (error);
446
447         sc->asic_offset = ED_WD_ASIC_OFFSET;
448         sc->nic_offset  = ED_WD_NIC_OFFSET;
449
450         return ed_probe_WD80x3_generic(dev, flags, intr_vals);
451 }