/* $NetBSD: fxp.c,v 1.3 2011/10/30 21:08:33 phx Exp $ */ /* * most of the following code was imported from dev/ic/i82557.c; the * original copyright notice is as below. */ /*- * Copyright (c) 1997, 1998, 1999, 2001, 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * 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, 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Copyright (c) 1995, David Greenman * Copyright (c) 2001 Jonathan Lemon * 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. * * Id: if_fxp.c,v 1.113 2001/05/17 23:50:24 jlemon */ #include #include #include #include #include #include #include "globals.h" #define FRAMESIZE 1536 /* * 82559ER 8086.1209/1229 * * - reverse endian access for 16bit/32bit register. * - no vtophys() translation, vaddr_t == paddr_t. * - PIPT writeback cache aware. */ #define CSR_WRITE_1(l, r, v) out8((l)->iobase+(r), (v)) #define CSR_READ_1(l, r) in8((l)->iobase+(r)) #define CSR_WRITE_2(l, r, v) out16rb((l)->iobase+(r), (v)) #define CSR_READ_2(l, r) in16rb((l)->iobase+(r)) #define CSR_WRITE_4(l, r, v) out32rb((l)->iobase+(r), (v)) #define CSR_READ_4(l, r) in32rb((l)->iobase+(r)) #define VTOPHYS(va) (uint32_t)(va) #define DEVTOV(pa) (uint32_t)(pa) #define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz)) #define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz)) #define DELAY(n) delay(n) #define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A)) struct txdesc { volatile uint16_t cb_status; volatile uint16_t cb_command; volatile uint32_t link_addr; volatile uint32_t tbd_array_addr; volatile uint16_t byte_count; volatile uint8_t tx_threshold; volatile uint8_t tbd_number; volatile uint32_t tx_buf_addr0; volatile uint32_t tx_buf_size0; volatile uint32_t tx_buf_addr1; volatile uint32_t tx_buf_size1; }; /* mimic extended TxCB layout */ struct rxdesc { volatile uint16_t rfa_status; volatile uint16_t rfa_control; volatile uint32_t link_addr; volatile uint32_t rbd_addr; volatile uint16_t actual_size; volatile uint16_t size; }; /* 16B rfa */ struct local { struct txdesc txd; uint8_t store[sizeof(struct rxdesc) + FRAMESIZE]; unsigned iobase; unsigned eeprom_addr; }; static void autosize_eeprom(struct local *); static int read_eeprom(struct local *, int); static void fxp_scb_wait(struct local *); #ifdef DEBUG static int fxp_mdi_read(struct local *, int, int); #endif /* * 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 struct fxp_cb_config store_cbc; static struct fxp_cb_ias store_cbi; int fxp_match(unsigned tag, void *data) { unsigned v; v = pcicfgread(tag, PCI_ID_REG); switch (v) { case PCI_DEVICE(0x8086, 0x1209): case PCI_DEVICE(0x8086, 0x1229): return 1; } return 0; } void * fxp_init(unsigned tag, void *data) { struct local *sc; uint8_t *en = data; struct fxp_cb_config *cbp = &store_cbc; struct fxp_cb_ias *cb_ias = &store_cbi; struct rxdesc *rfa; unsigned v, i; sc = ALLOC(struct local, sizeof(struct txdesc)); /* desc alignment */ memset(sc, 0, sizeof(struct local)); sc->iobase = DEVTOV(pcicfgread(tag, 0x10)); /* use mem space */ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(100); autosize_eeprom(sc); v = read_eeprom(sc, 0); en[0] = v; en[1] = v >> 8; v = read_eeprom(sc, 1); en[2] = v; en[3] = v >> 8; v = read_eeprom(sc, 2); en[4] = v; en[5] = v >> 8; printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x, ", en[0], en[1], en[2], en[3], en[4], en[5]); DPRINTF(("PHY %d (%04x.%04x)\n", fxp_mdi_read(sc, 1, 18), fxp_mdi_read(sc, 1, 2), fxp_mdi_read(sc, 1, 3))); /* * Initialize base of CBL and RFA memory. Loading with zero * sets it up for regular linear addressing. */ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_BASE); fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_BASE); /* * 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(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 = htole16(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 wbinv(cbp, sizeof(*cbp)); fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, VTOPHYS(cbp)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); i = 1000; while (!(le16toh(cbp->cb_status) & FXP_CB_STATUS_C) && --i > 0) { DELAY(1); inv(&cbp->cb_status, sizeof(cbp->cb_status)); } if (i == 0) printf("cbp config timeout\n"); /* * Initialize the station address. */ cb_ias->cb_status = 0; cb_ias->cb_command = htole16(FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL); cb_ias->link_addr = -1; memcpy(cb_ias->macaddr, en, 6); /* * Start the IAS (Individual Address Setup) command/DMA. */ wbinv(cb_ias, sizeof(*cb_ias)); fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, VTOPHYS(cb_ias)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); i = 1000; while (!(le16toh(cb_ias->cb_status) & FXP_CB_STATUS_C) && --i > 0) { DELAY(1); inv(&cb_ias->cb_status, sizeof(cb_ias->cb_status)); } if (i == 0) printf("ias config timeout\n"); rfa = (struct rxdesc *)sc->store; rfa->rfa_status = 0; rfa->rfa_control = htole16(FXP_RFA_CONTROL_S); rfa->link_addr = htole32(VTOPHYS(rfa)); rfa->rbd_addr = -1; rfa->actual_size = 0; rfa->size = htole16(sizeof(sc->store) - sizeof(struct rxdesc)); wbinv(rfa, sizeof(sc->store)); fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, VTOPHYS(rfa)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START); return sc; } int fxp_send(void *dev, char *buf, unsigned len) { struct local *l = dev; struct txdesc *txd; int loop; if (len > 1520) printf("fxp_send: len > 1520 (%u)\n", len); txd = &l->txd; txd->cb_status = 0; txd->cb_command = htole16(FXP_CB_COMMAND_XMIT|FXP_CB_COMMAND_SF|FXP_CB_COMMAND_EL); txd->link_addr = -1; txd->tbd_array_addr = htole32(VTOPHYS(&txd->tx_buf_addr0)); txd->tx_buf_addr0 = htole32(VTOPHYS(buf)); txd->tx_buf_size0 = htole32(len); txd->byte_count = htole16(0x8000); txd->tx_threshold = 0x20; txd->tbd_number = 1; wbinv(buf, len); wbinv(txd, sizeof(*txd)); fxp_scb_wait(l); CSR_WRITE_4(l, FXP_CSR_SCB_GENERAL, VTOPHYS(txd)); CSR_WRITE_1(l, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); loop = 10000; while (!(le16toh(txd->cb_status) & FXP_CB_STATUS_C) && --loop > 0) { DELAY(1); inv(txd, sizeof(struct txdesc)); } if (loop == 0) printf("send timeout\n"); return len; } int fxp_recv(void *dev, char *buf, unsigned maxlen, unsigned timo) { struct local *l = dev; struct rxdesc *rfa; unsigned bound, ruscus, len; fxp_scb_wait(l); CSR_WRITE_1(l, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_RESUME); bound = 1000 * timo; do { ruscus = CSR_READ_1(l, FXP_CSR_SCB_RUSCUS); if (((ruscus >> 2) & 0x0f) != FXP_SCB_RUS_READY && (((ruscus >> 2) & 0x0f) == FXP_SCB_RUS_SUSPENDED)) goto gotone; DELAY(1000); /* 1 milli second */ } while (--bound > 0); errno = 0; return -1; gotone: rfa = (struct rxdesc *)l->store; inv(rfa, sizeof(l->store)); /* whole including received frame */ if ((le16toh(rfa->rfa_status) & FXP_RFA_STATUS_C) == 0) return 0; len = le16toh(rfa->actual_size) & 0x7ff; if (len > maxlen) len = maxlen; memcpy(buf, &l->store[sizeof(struct rxdesc)], len); rfa->rfa_status = 0; rfa->rfa_control = htole16(FXP_RFA_CONTROL_S); rfa->actual_size = 0; wbinv(rfa, sizeof(struct rxdesc)); #if 0 fxp_scb_wait(l); CSR_WRITE_1(l, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_RESUME); #endif return len; } static void eeprom_shiftin(struct local *sc, int data, int len) { uint16_t reg; int x; for (x = 1 << (len - 1); x != 0; x >>= 1) { DELAY(40); if (data & x) reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; else reg = FXP_EEPROM_EECS; CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(40); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(40); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); } DELAY(40); } void autosize_eeprom(struct local *sc) { int x; CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); DELAY(40); /* Shift in read opcode. */ eeprom_shiftin(sc, FXP_EEPROM_OPC_READ, 3); /* * Shift in address, wait for the dummy zero following a correct * address shift. */ for (x = 1; x <= 8; x++) { CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); DELAY(40); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS | FXP_EEPROM_EESK); DELAY(40); if ((CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) == 0) break; DELAY(40); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); DELAY(40); } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(40); if (x != 6 && x != 8) printf("fxp: strange EEPROM address size (%d)\n", x); else sc->eeprom_addr = x; } static int read_eeprom(struct local *sc, int offset) { uint16_t reg; int x, val; CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); /* Shift in read opcode. */ eeprom_shiftin(sc, FXP_EEPROM_OPC_READ, 3); /* Shift in address. */ eeprom_shiftin(sc, offset, sc->eeprom_addr); reg = FXP_EEPROM_EECS; val = 0; /* * Shift out data. */ for (x = 16; x > 0; x--) { CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) val |= (1 << (x - 1)); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(1); return val; } static void fxp_scb_wait(struct local *sc) { int loop = 5000; while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --loop > 0) DELAY(2); if (loop == 0) printf("SCB timeout\n"); } #ifdef DEBUG static int fxp_mdi_read(struct local *sc, int phy, int reg) { int count = 10000; int value; CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21)); while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) & 0x10000000) == 0 && count--) DELAY(10); if (count <= 0) printf("fxp_mdi_read: timed out\n"); return (value & 0xffff); } #endif