/* $NetBSD: i82557.c,v 1.11 2008/12/14 18:46:33 christos Exp $ */ /* * Copyright (c) 1998, 1999 * Matthias Drochner. All rights reserved. * Copyright (c) 1995, David Greenman * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD) #include #include #endif #include "etherdrv.h" #define RECVBUF_SIZE 1600 /* struct fxp_rfa + packet */ #ifdef _STANDALONE static pcihdl_t mytag; static char recvbuf[RECVBUF_SIZE]; #define RECVBUF_PHYS vtophys(recvbuf) #define RECVBUF_VIRT ((void *)recvbuf) static union _sndbuf { struct fxp_cb_config cbp; struct fxp_cb_ias cb_ias; struct fxp_cb_tx txp; } sndbuf; #define SNDBUF_PHYS vtophys(&sndbuf) #define SNDBUF_VIRT ((void *)&sndbuf) #else /* !standalone, userspace testing environment */ #define PCI_MODE1_ENABLE 0x80000000UL static pcihdl_t mytag = PCI_MODE1_ENABLE | (PCIDEVNO << 11); extern void *mapmem(int, int); void *dmamem; /* virtual */ #define RECVBUF_PHYS DMABASE #define RECVBUF_VIRT dmamem #define SNDBUF_PHYS (DMABASE + RECVBUF_SIZE) #define SNDBUF_VIRT ((void *)(((char *)dmamem) + RECVBUF_SIZE)) #endif /* _STANDALONE */ static void fxp_read_eeprom(uint16_t *, int, int); static inline void fxp_scb_wait(void); #ifdef DEBUG static void fxp_checkintr(char *); #else #define fxp_checkintr(x) #endif static void fxp_startreceiver(void); /* * Template for default configuration parameters. * See struct fxp_cb_config for the bit definitions. */ static uint8_t fxp_cb_config_template[] = { 0x0, 0x0, /* cb_status */ 0x80, 0x2, /* cb_command */ 0xff, 0xff, 0xff, 0xff, /* link_addr */ 0x16, /* 0 */ 0x8, /* 1 */ 0x0, /* 2 */ 0x0, /* 3 */ 0x0, /* 4 */ 0x80, /* 5 */ 0xb2, /* 6 */ 0x3, /* 7 */ 0x1, /* 8 */ 0x0, /* 9 */ 0x26, /* 10 */ 0x0, /* 11 */ 0x60, /* 12 */ 0x0, /* 13 */ 0xf2, /* 14 */ 0x48, /* 15 */ 0x0, /* 16 */ 0x40, /* 17 */ 0xf3, /* 18 */ 0x0, /* 19 */ 0x3f, /* 20 */ 0x5 /* 21 */ }; static int tx_threshold = 64; /* x8, max 192 */ #define CSR_READ_1(reg) inb(iobase + (reg)) #define CSR_READ_2(reg) inw(iobase + (reg)) #define CSR_WRITE_1(reg, val) outb(iobase + (reg), val) #define CSR_WRITE_2(reg, val) outw(iobase + (reg), val) #define CSR_WRITE_4(reg, val) outl(iobase + (reg), val) #define DELAY(n) delay(n) static int iobase; #if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD) static struct btinfo_netif bi_netif; #endif /* * Wait for the previous command to be accepted (but not necessarily * completed). */ static inline void fxp_scb_wait(void) { int i = 10000; while (CSR_READ_1(FXP_CSR_SCB_COMMAND) && --i) DELAY(1); if (i == 0) printf("fxp: WARNING: SCB timed out!\n"); } #ifdef DEBUG static void fxp_checkintr(char *msg) { uint8_t statack; int i = 10000; do { statack = CSR_READ_1(FXP_CSR_SCB_STATACK); } while ((statack == 0) && (--i > 0)); if (statack != 0) { CSR_WRITE_1(FXP_CSR_SCB_STATACK, statack); printf("%s: ack'd irq %x, i=%d\n", msg, statack, i); } } #endif int EtherInit(unsigned char *myadr) { #ifndef _STANDALONE uint32_t id; #endif volatile struct fxp_cb_config *cbp; volatile struct fxp_cb_ias *cb_ias; int i; if (pcicheck()) { printf("pcicheck failed\n"); return 0; } #ifdef _STANDALONE if (pcifinddev(0x8086, 0x1229, &mytag)) { printf("no fxp\n"); return 0; } #else pcicfgread(&mytag, 0, &id); if (id != 0x12298086) { printf("no fxp\n"); return 0; } #endif pcicfgread(&mytag, FXP_PCI_IOBA, &iobase); iobase &= ~3; #ifndef _STANDALONE dmamem = mapmem(DMABASE, DMASIZE); if (!dmamem) return 0; #endif fxp_read_eeprom((void *)myadr, 0, 3); /* * Initialize base of CBL and RFA memory. Loading with zero * sets it up for regular linear addressing. */ CSR_WRITE_4(FXP_CSR_SCB_GENERAL, 0); CSR_WRITE_1(FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_BASE); fxp_scb_wait(); CSR_WRITE_1(FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_BASE); cbp = SNDBUF_VIRT; /* * This memcpy is kind of disgusting, but there are a bunch of must be * zero and must be one bits in this structure and this is the easiest * way to initialize them all to proper values. */ memcpy((void *)cbp, fxp_cb_config_template, sizeof(fxp_cb_config_template)); #define prm 0 #define phy_10Mbps_only 0 #define all_mcasts 0 cbp->cb_status = 0; cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL; cbp->link_addr = -1; /* (no) next command */ cbp->byte_count = 22; /* (22) bytes to config */ cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */ cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */ cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */ cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */ cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */ cbp->dma_mbce = 0; /* (disable) dma max counters */ cbp->late_scb = 0; /* (don't) defer SCB update */ cbp->tno_int_or_tco_en = 0; /* (disable) tx not okay interrupt */ cbp->ci_int = 0; /* interrupt on CU not active */ cbp->save_bf = prm; /* save bad frames */ cbp->disc_short_rx = !prm; /* discard short packets */ cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */ cbp->mediatype = !phy_10Mbps_only; /* interface mode */ cbp->nsai = 1; /* (don't) disable source addr insert */ cbp->preamble_length = 2; /* (7 byte) preamble */ cbp->loopback = 0; /* (don't) loopback */ cbp->linear_priority = 0; /* (normal CSMA/CD operation) */ cbp->linear_pri_mode = 0; /* (wait after xmit only) */ cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */ cbp->promiscuous = prm; /* promiscuous mode */ cbp->bcast_disable = 0; /* (don't) disable broadcasts */ cbp->crscdt = 0; /* (CRS only) */ cbp->stripping = !prm; /* truncate rx packet to byte count */ cbp->padding = 1; /* (do) pad short tx packets */ cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */ cbp->force_fdx = 0; /* (don't) force full duplex */ cbp->fdx_pin_en = 1; /* (enable) FDX# pin */ cbp->multi_ia = 0; /* (don't) accept multiple IAs */ cbp->mc_all = all_mcasts;/* accept all multicasts */ #undef prm #undef phy_10Mbps_only #undef all_mcasts /* * Start the config command/DMA. */ fxp_scb_wait(); CSR_WRITE_4(FXP_CSR_SCB_GENERAL, SNDBUF_PHYS); CSR_WRITE_1(FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ i = 10000; while (!(cbp->cb_status & FXP_CB_STATUS_C) && (--i > 0)) DELAY(1); if (i == 0) printf("config timeout"); fxp_checkintr("config"); cb_ias = SNDBUF_VIRT; /* * Now initialize the station address. Temporarily use the TxCB * memory area like we did above for the config CB. */ cb_ias->cb_status = 0; cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL; cb_ias->link_addr = -1; memcpy((void *)cb_ias->macaddr, myadr, 6); /* * Start the IAS (Individual Address Setup) command/DMA. */ fxp_scb_wait(); /* address is still there */ CSR_WRITE_1(FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ i = 10000; while (!(cb_ias->cb_status & FXP_CB_STATUS_C) && (--i > 0)) DELAY(1); if (i == 0) printf("ias timeout"); fxp_checkintr("ias"); fxp_startreceiver(); #if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD) strncpy(bi_netif.ifname, "fxp", sizeof(bi_netif.ifname)); bi_netif.bus = BI_BUS_PCI; bi_netif.addr.tag = mytag; BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); #endif return 1; } void EtherStop(void) { /* * Issue software reset */ CSR_WRITE_4(FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(10); } int EtherSend(char *pkt, int len) { volatile struct fxp_cb_tx *txp; #ifdef _STANDALONE static volatile struct fxp_tbd tbd; #endif volatile struct fxp_tbd *tbdp; int i; txp = SNDBUF_VIRT; #ifdef _STANDALONE tbdp = &tbd; txp->tbd_array_addr = vtophys((void *)&tbd); tbdp->tb_addr = vtophys(pkt); #else /* XXX assuming we send at max 400 bytes */ tbdp = (struct fxp_tbd *)(SNDBUF_VIRT + 440); txp->tbd_array_addr = SNDBUF_PHYS + 440; memcpy(SNDBUF_VIRT + 400, pkt, len); tbdp->tb_addr = SNDBUF_PHYS + 400; #endif tbdp->tb_size = len; txp->tbd_number = 1; txp->cb_status = 0; txp->cb_command = FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_EL; txp->tx_threshold = tx_threshold; txp->link_addr = -1; txp->byte_count = 0; fxp_scb_wait(); CSR_WRITE_4(FXP_CSR_SCB_GENERAL, SNDBUF_PHYS); CSR_WRITE_1(FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ i = 10000; while (!(txp->cb_status & FXP_CB_STATUS_C) && (--i > 0)) DELAY(1); if (i == 0) printf("send timeout"); fxp_checkintr("send"); return len; } static void fxp_startreceiver(void) { volatile struct fxp_rfa *rfa; uint32_t v; rfa = RECVBUF_VIRT; rfa->size = RECVBUF_SIZE - sizeof(struct fxp_rfa); rfa->rfa_status = 0; rfa->rfa_control = FXP_RFA_CONTROL_S; rfa->actual_size = 0; v = RECVBUF_PHYS; /* close the "ring" */ memcpy((void *)&rfa->link_addr, &v, sizeof(v)); v = -1; memcpy((void *)&rfa->rbd_addr, &v, sizeof(v)); fxp_scb_wait(); CSR_WRITE_4(FXP_CSR_SCB_GENERAL, RECVBUF_PHYS); CSR_WRITE_1(FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START); } int EtherReceive(char *pkt, int maxlen) { uint8_t ruscus; volatile struct fxp_rfa *rfa; int len = 0; ruscus = CSR_READ_1(FXP_CSR_SCB_RUSCUS); if (((ruscus >> 2) & 0x0f) == FXP_SCB_RUS_READY) return 0; if (((ruscus >> 2) & 0x0f) != FXP_SCB_RUS_SUSPENDED) { printf("rcv: ruscus=%x\n", ruscus); return 0; } rfa = RECVBUF_VIRT; if (rfa->rfa_status & FXP_RFA_STATUS_C) { len = rfa->actual_size & 0x7ff; if (len <= maxlen) { memcpy(pkt, (char *) rfa + RFA_SIZE, maxlen); #if 0 printf("rfa status=%x, len=%x\n", rfa->rfa_status, len); #endif } else len = 0; } fxp_scb_wait(); CSR_WRITE_1(FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_RESUME); return len; } /* * Read from the serial EEPROM. Basically, you manually shift in * the read opcode (one bit at a time) and then shift in the address, * and then you shift out the data (all of this one bit at a time). * The word size is 16 bits, so you have to provide the address for * every 16 bits of data. */ static void fxp_read_eeprom(uint16_t *data, int offset, int words) { uint16_t reg; int i, x; for (i = 0; i < words; i++) { CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); /* * Shift in read opcode. */ for (x = 3; x > 0; x--) { if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { reg = FXP_EEPROM_EECS; } CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, reg); CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } /* * Shift in address. */ for (x = 6; x > 0; x--) { if ((i + offset) & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { reg = FXP_EEPROM_EECS; } CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, reg); CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } reg = FXP_EEPROM_EECS; data[i] = 0; /* * Shift out data. */ for (x = 16; x > 0; x--) { CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); if (CSR_READ_2(FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) data[i] |= (1 << (x - 1)); CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } CSR_WRITE_2(FXP_CSR_EEPROMCONTROL, 0); DELAY(1); } }