1/* $NetBSD: 3c90xb.c,v 1.13 2007/03/04 05:59:59 christos Exp $ */ 2 3/* 4 * Copyright (c) 1999 5 * Matthias Drochner. 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, this list of conditions, and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/types.h> 30#include <machine/pio.h> 31 32struct mbuf; /* XXX */ 33typedef int bus_dmamap_t; /* XXX */ 34#include <dev/ic/elink3reg.h> 35#include <dev/ic/elinkxlreg.h> 36 37#include <lib/libsa/stand.h> 38 39#include <libi386.h> 40#include <pcivar.h> 41 42#if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD) 43#include <lib/libkern/libkern.h> 44#include <bootinfo.h> 45#endif 46 47#include "etherdrv.h" 48 49#define RECVBUF_SIZE 1600 /* struct ex_upd + packet */ 50 51#ifdef _STANDALONE 52 53static pcihdl_t mytag; 54static char recvbuf[RECVBUF_SIZE]; 55#define RECVBUF_PHYS vtophys(recvbuf) 56#define RECVBUF_VIRT ((void *)recvbuf) 57static struct ex_dpd sndbuf; 58#define SNDBUF_PHYS vtophys(&sndbuf) 59#define SNDBUF_VIRT ((void *)&sndbuf) 60 61#else /* !standalone, userspace testing environment */ 62 63#define PCI_MODE1_ENABLE 0x80000000UL 64#define PCIBUSNO 1 65#define PCIDEVNO 4 66static pcihdl_t mytag = PCI_MODE1_ENABLE | (PCIBUSNO << 16) | (PCIDEVNO << 11); 67 68extern void *mapmem(int, int); 69void *dmamem; /* virtual */ 70#define DMABASE 0x3ffd800 71#define DMASIZE 10240 72#define RECVBUF_PHYS DMABASE 73#define RECVBUF_VIRT dmamem 74#define SNDBUF_PHYS (DMABASE + RECVBUF_SIZE) 75#define SNDBUF_VIRT ((void *)(((char *)dmamem) + RECVBUF_SIZE)) 76 77#endif /* _STANDALONE */ 78 79 80#define CSR_READ_1(reg) inb(iobase + (reg)) 81#define CSR_READ_2(reg) inw(iobase + (reg)) 82#define CSR_READ_4(reg) inl(iobase + (reg)) 83#define CSR_WRITE_1(reg, val) outb(iobase + (reg), val) 84#define CSR_WRITE_2(reg, val) outw(iobase + (reg), val) 85#define CSR_WRITE_4(reg, val) outl(iobase + (reg), val) 86 87#undef GO_WINDOW 88#define GO_WINDOW(x) CSR_WRITE_2(ELINK_COMMAND, WINDOW_SELECT | x) 89 90static int iobase; 91static u_char myethaddr[6]; 92unsigned ether_medium; 93 94static struct { 95 int did; 96 int mii; 97} excards[] = { 98 {0x9005, 0}, /* 3c900b Combo */ 99 {0x9055, 1}, /* 3c905b TP */ 100 {0x9058, 0}, /* 3c905b Combo */ 101 {-1} 102}, *excard; 103 104static struct mtabentry { 105 int address_cfg; /* configured connector */ 106 int config_bit; /* connector present */ 107 char *name; 108} mediatab[] = { /* indexed by media type - etherdrv.h */ 109 {ELINKMEDIA_10BASE_2, ELINK_PCI_BNC, "BNC"}, 110 {ELINKMEDIA_10BASE_T, ELINK_PCI_10BASE_T, "UTP"}, 111 {ELINKMEDIA_AUI, ELINK_PCI_AUI, "AUI"}, 112 {ELINKMEDIA_MII, ELINK_PCI_100BASE_MII, "MII"}, 113 {ELINKMEDIA_100BASE_TX, ELINK_PCI_100BASE_TX, "100TX"}, 114}; 115 116#if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD) 117static struct btinfo_netif bi_netif; 118#endif 119 120#define ex_waitcmd() \ 121 do { \ 122 while (CSR_READ_2(ELINK_STATUS) & COMMAND_IN_PROGRESS) \ 123 continue; \ 124 } while (0) 125 126void ex_reset(void); 127uint16_t ex_read_eeprom(int); 128static int ex_eeprom_busy(void); 129void ex_init(void); 130void ex_set_media(void); 131 132void 133ex_reset(void) 134{ 135 CSR_WRITE_2(ELINK_COMMAND, GLOBAL_RESET); 136 delay(100000); 137 ex_waitcmd(); 138} 139 140/* 141 * Read EEPROM data. 142 * XXX what to do if EEPROM doesn't unbusy? 143 */ 144uint16_t 145ex_read_eeprom(int offset) 146{ 147 uint16_t data = 0; 148 149 GO_WINDOW(0); 150 if (ex_eeprom_busy()) 151 goto out; 152 CSR_WRITE_1(ELINK_W0_EEPROM_COMMAND, READ_EEPROM | (offset & 0x3f)); 153 if (ex_eeprom_busy()) 154 goto out; 155 data = CSR_READ_2(ELINK_W0_EEPROM_DATA); 156out: 157 return data; 158} 159 160static int 161ex_eeprom_busy(void) 162{ 163 int i = 100; 164 165 while (i--) { 166 if (!(CSR_READ_2(ELINK_W0_EEPROM_COMMAND) & EEPROM_BUSY)) 167 return 0; 168 delay(100); 169 } 170 printf("\nex: eeprom stays busy.\n"); 171 return 1; 172} 173 174/* 175 * Bring device up. 176 */ 177void 178ex_init(void) 179{ 180 int i; 181 182 ex_waitcmd(); 183 EtherStop(); 184 185 /* 186 * Set the station address and clear the station mask. The latter 187 * is needed for 90x cards, 0 is the default for 90xB cards. 188 */ 189 GO_WINDOW(2); 190 for (i = 0; i < 6; i++) { 191 CSR_WRITE_1(ELINK_W2_ADDR_0 + i, 192 myethaddr[i]); 193 CSR_WRITE_1(ELINK_W2_RECVMASK_0 + i, 0); 194 } 195 196 GO_WINDOW(3); 197 198 CSR_WRITE_2(ELINK_COMMAND, RX_RESET); 199 ex_waitcmd(); 200 CSR_WRITE_2(ELINK_COMMAND, TX_RESET); 201 ex_waitcmd(); 202 203 CSR_WRITE_2(ELINK_COMMAND, SET_INTR_MASK | 0); /* disable */ 204 CSR_WRITE_2(ELINK_COMMAND, ACK_INTR | 0xff); 205 206 ex_set_media(); 207 208 CSR_WRITE_2(ELINK_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST); 209 210 CSR_WRITE_4(ELINK_DNLISTPTR, 0); 211 CSR_WRITE_2(ELINK_COMMAND, TX_ENABLE); 212 213 CSR_WRITE_4(ELINK_UPLISTPTR, RECVBUF_PHYS); 214 CSR_WRITE_2(ELINK_COMMAND, RX_ENABLE); 215 CSR_WRITE_2(ELINK_COMMAND, ELINK_UPUNSTALL); 216 217 GO_WINDOW(1); 218} 219 220void 221ex_set_media(void) 222{ 223 int config0, config1; 224 225 CSR_WRITE_2(ELINK_W3_MAC_CONTROL, 0); 226 227 if (ether_medium == ETHERMEDIUM_MII) 228 goto setcfg; 229 230 GO_WINDOW(4); 231 CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, 0); 232 CSR_WRITE_2(ELINK_COMMAND, STOP_TRANSCEIVER); 233 delay(800); 234 235 switch (ether_medium) { 236 case ETHERMEDIUM_UTP: 237 CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, 238 JABBER_GUARD_ENABLE | LINKBEAT_ENABLE); 239 break; 240 case ETHERMEDIUM_BNC: 241 CSR_WRITE_2(ELINK_COMMAND, START_TRANSCEIVER); 242 delay(800); 243 break; 244 case ETHERMEDIUM_AUI: 245 CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, SQE_ENABLE); 246 delay(800); 247 break; 248 case ETHERMEDIUM_100TX: 249 CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, LINKBEAT_ENABLE); 250 break; 251 } 252 253setcfg: 254 GO_WINDOW(3); 255 256 config0 = CSR_READ_2(ELINK_W3_INTERNAL_CONFIG); 257 config1 = CSR_READ_2(ELINK_W3_INTERNAL_CONFIG + 2); 258 259 config1 = config1 & ~CONFIG_MEDIAMASK; 260 config1 |= (mediatab[ether_medium].address_cfg 261 << CONFIG_MEDIAMASK_SHIFT); 262 263 CSR_WRITE_2(ELINK_W3_INTERNAL_CONFIG, config0); 264 CSR_WRITE_2(ELINK_W3_INTERNAL_CONFIG + 2, config1); 265} 266 267static void 268ex_probemedia(void) 269{ 270 int i, j; 271 struct mtabentry *m; 272 273 /* test for presence of connectors */ 274 GO_WINDOW(3); 275 i = CSR_READ_1(ELINK_W3_RESET_OPTIONS); 276 j = (CSR_READ_2(ELINK_W3_INTERNAL_CONFIG + 2) & CONFIG_MEDIAMASK) 277 >> CONFIG_MEDIAMASK_SHIFT; 278 GO_WINDOW(0); 279 280 for (ether_medium = 0, m = mediatab; 281 ether_medium < sizeof(mediatab) / sizeof(mediatab[0]); 282 ether_medium++, m++) { 283 if (j == m->address_cfg) { 284 if (!(i & m->config_bit)) { 285 printf("%s not present\n", m->name); 286 goto bad; 287 } 288 printf("using %s\n", m->name); 289 return; 290 } 291 } 292 printf("unknown connector\n"); 293bad: 294 ether_medium = -1; 295} 296 297int 298EtherInit(unsigned char *myadr) 299{ 300 uint32_t pcicsr; 301 uint16_t val; 302 volatile struct ex_upd *upd; 303#ifndef _STANDALONE 304 uint32_t id; 305#endif 306 307 if (pcicheck()) { 308 printf("pcicheck failed\n"); 309 return 0; 310 } 311#ifndef _STANDALONE 312 pcicfgread(&mytag, 0, &id); 313#endif 314 for (excard = &excards[0]; excard->did != -1; excard++) { 315#ifdef _STANDALONE 316 if (pcifinddev(0x10b7, excard->did, &mytag) == 0) 317 goto found; 318#else 319 if (id == (0x10b7 | (excard->did << 16))) 320 goto found; 321#endif 322 } 323 printf("no ex\n"); 324 return 0; 325 326found: 327 pcicfgread(&mytag, 0x10, &iobase); 328 iobase &= ~3; 329 330#ifndef _STANDALONE 331 dmamem = mapmem(DMABASE, DMASIZE); 332 if (!dmamem) 333 return 0; 334#endif 335 336 /* enable bus mastering in PCI command register */ 337 if (pcicfgread(&mytag, 0x04, (int *)&pcicsr) 338 || pcicfgwrite(&mytag, 0x04, pcicsr | 4)) { 339 printf("cannot enable DMA\n"); 340 return 0; 341 } 342 343 ex_reset(); 344 345 if (excard->mii) 346 ether_medium = ETHERMEDIUM_MII; 347 else { 348 ex_probemedia(); 349 if (ether_medium < 0) 350 return 0; 351 } 352 353 val = ex_read_eeprom(EEPROM_OEM_ADDR0); 354 myethaddr[0] = val >> 8; 355 myethaddr[1] = val & 0xff; 356 val = ex_read_eeprom(EEPROM_OEM_ADDR1); 357 myethaddr[2] = val >> 8; 358 myethaddr[3] = val & 0xff; 359 val = ex_read_eeprom(EEPROM_OEM_ADDR2); 360 myethaddr[4] = val >> 8; 361 myethaddr[5] = val & 0xff; 362 memcpy(myadr, myethaddr, 6); 363 364 upd = RECVBUF_VIRT; 365 upd->upd_nextptr = RECVBUF_PHYS; 366 upd->upd_pktstatus = 1500; 367 upd->upd_frags[0].fr_addr = RECVBUF_PHYS + 100; 368 upd->upd_frags[0].fr_len = 1500 | EX_FR_LAST; 369 370 ex_init(); 371 372#if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD) 373 strncpy(bi_netif.ifname, "ex", sizeof(bi_netif.ifname)); 374 bi_netif.bus = BI_BUS_PCI; 375 bi_netif.addr.tag = mytag; 376 377 BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); 378#endif 379 380 return 1; 381} 382 383void 384EtherStop(void) 385{ 386 /* 387 * Issue software reset 388 */ 389 CSR_WRITE_2(ELINK_COMMAND, RX_DISABLE); 390 CSR_WRITE_2(ELINK_COMMAND, TX_DISABLE); 391 CSR_WRITE_2(ELINK_COMMAND, STOP_TRANSCEIVER); 392 CSR_WRITE_2(ELINK_COMMAND, INTR_LATCH); 393} 394 395int 396EtherSend(char *pkt, int len) 397{ 398 volatile struct ex_dpd *dpd; 399 int i; 400 401 dpd = SNDBUF_VIRT; 402 403 dpd->dpd_nextptr = 0; 404 dpd->dpd_fsh = len; 405#ifdef _STANDALONE 406 dpd->dpd_frags[0].fr_addr = vtophys(pkt); 407#else 408 memcpy(SNDBUF_VIRT + 100, pkt, len); 409 dpd->dpd_frags[0].fr_addr = SNDBUF_PHYS + 100; 410#endif 411 dpd->dpd_frags[0].fr_len = len | EX_FR_LAST; 412 413 CSR_WRITE_4(ELINK_DNLISTPTR, SNDBUF_PHYS); 414 CSR_WRITE_2(ELINK_COMMAND, ELINK_DNUNSTALL); 415 416 i = 10000; 417 while (!(dpd->dpd_fsh & 0x00010000)) { 418 if (--i < 0) { 419 printf("3c90xb: send timeout\n"); 420 return -1; 421 } 422 delay(1); 423 } 424 425 return len; 426} 427 428int 429EtherReceive(char *pkt, int maxlen) 430{ 431 volatile struct ex_upd *upd; 432 int len; 433 434 upd = RECVBUF_VIRT; 435 436 if (!(upd->upd_pktstatus & ~EX_UPD_PKTLENMASK)) 437 return 0; 438 439 len = upd->upd_pktstatus & EX_UPD_PKTLENMASK; 440 if (len > maxlen) 441 len = 0; 442 else 443 memcpy(pkt, RECVBUF_VIRT + 100, len); 444 445 upd->upd_pktstatus = 1500; 446 CSR_WRITE_2(ELINK_COMMAND, ELINK_UPUNSTALL); 447 448 return len; 449} 450