diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/drivers/net/jme.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/net/jme.c')
-rw-r--r-- | qemu/roms/ipxe/src/drivers/net/jme.c | 1309 |
1 files changed, 1309 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/drivers/net/jme.c b/qemu/roms/ipxe/src/drivers/net/jme.c new file mode 100644 index 000000000..29694b699 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/jme.c @@ -0,0 +1,1309 @@ +/* + * JMicron JMC2x0 series PCIe Ethernet gPXE Device Driver + * + * Copyright 2010 Guo-Fu Tseng <cooldavid@cooldavid.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ipxe/io.h> +#include <errno.h> +#include <unistd.h> +#include <byteswap.h> +#include <ipxe/pci.h> +#include <ipxe/if_ether.h> +#include <ipxe/ethernet.h> +#include <ipxe/iobuf.h> +#include <ipxe/netdevice.h> +#include <ipxe/malloc.h> +#include <mii.h> +#include "jme.h" + +static int +jme_mdio_read(struct net_device *netdev, int phy, int reg) +{ + struct jme_adapter *jme = netdev->priv; + int i, val, again = (reg == MII_BMSR) ? 1 : 0; + +read_again: + jwrite32(jme, JME_SMI, SMI_OP_REQ | + smi_phy_addr(phy) | + smi_reg_addr(reg)); + + for (i = JME_PHY_TIMEOUT * 50 ; i > 0 ; --i) { + udelay(20); + val = jread32(jme, JME_SMI); + if ((val & SMI_OP_REQ) == 0) + break; + } + + if (i == 0) { + DBG("phy(%d) read timeout : %d\n", phy, reg); + return 0; + } + + if (again--) + goto read_again; + + return (val & SMI_DATA_MASK) >> SMI_DATA_SHIFT; +} + +static void +jme_mdio_write(struct net_device *netdev, + int phy, int reg, int val) +{ + struct jme_adapter *jme = netdev->priv; + int i; + + jwrite32(jme, JME_SMI, SMI_OP_WRITE | SMI_OP_REQ | + ((val << SMI_DATA_SHIFT) & SMI_DATA_MASK) | + smi_phy_addr(phy) | smi_reg_addr(reg)); + + wmb(); + for (i = JME_PHY_TIMEOUT * 50 ; i > 0 ; --i) { + udelay(20); + if ((jread32(jme, JME_SMI) & SMI_OP_REQ) == 0) + break; + } + + if (i == 0) + DBG("phy(%d) write timeout : %d\n", phy, reg); + + return; +} + +static void +jme_reset_phy_processor(struct jme_adapter *jme) +{ + u32 val; + + jme_mdio_write(jme->mii_if.dev, + jme->mii_if.phy_id, + MII_ADVERTISE, ADVERTISE_ALL | + ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + + if (jme->pdev->device == PCI_DEVICE_ID_JMICRON_JMC250) + jme_mdio_write(jme->mii_if.dev, + jme->mii_if.phy_id, + MII_CTRL1000, + ADVERTISE_1000FULL | ADVERTISE_1000HALF); + + val = jme_mdio_read(jme->mii_if.dev, + jme->mii_if.phy_id, + MII_BMCR); + + jme_mdio_write(jme->mii_if.dev, + jme->mii_if.phy_id, + MII_BMCR, val | BMCR_RESET); + + return; +} + +static void +jme_phy_init(struct jme_adapter *jme) +{ + u16 reg26; + + reg26 = jme_mdio_read(jme->mii_if.dev, jme->mii_if.phy_id, 26); + jme_mdio_write(jme->mii_if.dev, jme->mii_if.phy_id, 26, reg26 | 0x1000); +} + +static void +jme_set_phyfifoa(struct jme_adapter *jme) +{ + jme_mdio_write(jme->mii_if.dev, jme->mii_if.phy_id, 27, 0x0004); +} + +static void +jme_set_phyfifob(struct jme_adapter *jme) +{ + jme_mdio_write(jme->mii_if.dev, jme->mii_if.phy_id, 27, 0x0000); +} + +static void +jme_phy_off(struct jme_adapter *jme) +{ + jme_mdio_write(jme->mii_if.dev, jme->mii_if.phy_id, MII_BMCR, BMCR_PDOWN); +} + +static void +jme_restart_an(struct jme_adapter *jme) +{ + uint32_t bmcr; + + bmcr = jme_mdio_read(jme->mii_if.dev, jme->mii_if.phy_id, MII_BMCR); + bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); + jme_mdio_write(jme->mii_if.dev, jme->mii_if.phy_id, MII_BMCR, bmcr); +} + +static void +jme_reset_ghc_speed(struct jme_adapter *jme) +{ + jme->reg_ghc &= ~(GHC_SPEED_1000M | GHC_DPX); + jwrite32(jme, JME_GHC, jme->reg_ghc); +} + +static void +jme_start_irq(struct jme_adapter *jme) +{ + /* + * Enable Interrupts + */ + jwrite32(jme, JME_IENS, INTR_ENABLE); +} + +static void +jme_stop_irq(struct jme_adapter *jme) +{ + /* + * Disable Interrupts + */ + jwrite32f(jme, JME_IENC, INTR_ENABLE); +} + +static void +jme_setup_wakeup_frame(struct jme_adapter *jme, + u32 *mask, u32 crc, int fnr) +{ + int i; + + /* + * Setup CRC pattern + */ + jwrite32(jme, JME_WFOI, WFOI_CRC_SEL | (fnr & WFOI_FRAME_SEL)); + wmb(); + jwrite32(jme, JME_WFODP, crc); + wmb(); + + /* + * Setup Mask + */ + for (i = 0 ; i < WAKEUP_FRAME_MASK_DWNR ; ++i) { + jwrite32(jme, JME_WFOI, + ((i << WFOI_MASK_SHIFT) & WFOI_MASK_SEL) | + (fnr & WFOI_FRAME_SEL)); + wmb(); + jwrite32(jme, JME_WFODP, mask[i]); + wmb(); + } +} + +static void +jme_reset_mac_processor(struct jme_adapter *jme) +{ + u32 mask[WAKEUP_FRAME_MASK_DWNR] = {0, 0, 0, 0}; + u32 crc = 0xCDCDCDCD; + int i; + + jwrite32(jme, JME_GHC, jme->reg_ghc | GHC_SWRST); + udelay(2); + jwrite32(jme, JME_GHC, jme->reg_ghc); + + jwrite32(jme, JME_RXDBA_LO, 0x00000000); + jwrite32(jme, JME_RXDBA_HI, 0x00000000); + jwrite32(jme, JME_RXQDC, 0x00000000); + jwrite32(jme, JME_RXNDA, 0x00000000); + jwrite32(jme, JME_TXDBA_LO, 0x00000000); + jwrite32(jme, JME_TXDBA_HI, 0x00000000); + jwrite32(jme, JME_TXQDC, 0x00000000); + jwrite32(jme, JME_TXNDA, 0x00000000); + + jwrite32(jme, JME_RXMCHT_LO, 0x00000000); + jwrite32(jme, JME_RXMCHT_HI, 0x00000000); + for (i = 0 ; i < WAKEUP_FRAME_NR ; ++i) + jme_setup_wakeup_frame(jme, mask, crc, i); + jwrite32(jme, JME_GPREG0, GPREG0_DEFAULT); + jwrite32(jme, JME_GPREG1, GPREG1_DEFAULT); +} + +static void +jme_free_tx_buffers(struct jme_adapter *jme) +{ + struct jme_ring *txring = &jme->txring; + struct io_buffer *txbi; + unsigned int i; + + for (i = 0; i < jme->tx_ring_size; ++i) { + txbi = txring->bufinf[i]; + if (txbi) { + netdev_tx_complete_err(jme->mii_if.dev, + txbi, -ENOLINK); + txring->bufinf[i] = NULL; + } + } +} + +static void +jme_free_tx_resources(struct jme_adapter *jme) +{ + struct jme_ring *txring = &jme->txring; + + if (txring->desc) { + if (txring->bufinf) { + memset(txring->bufinf, 0, + sizeof(struct io_buffer *) * jme->tx_ring_size); + free(txring->bufinf); + } + free_dma(txring->desc, jme->tx_ring_size * TX_DESC_SIZE); + txring->desc = NULL; + txring->dma = 0; + txring->bufinf = NULL; + } + txring->next_to_use = 0; + txring->next_to_clean = 0; + txring->nr_free = 0; +} + +static int +jme_alloc_tx_resources(struct jme_adapter *jme) +{ + struct jme_ring *txring = &jme->txring; + + txring->desc = malloc_dma(jme->tx_ring_size * TX_DESC_SIZE, + RING_DESC_ALIGN); + if (!txring->desc) { + DBG("Can not allocate transmit ring descriptors.\n"); + goto err_out; + } + + /* + * 16 Bytes align + */ + txring->dma = virt_to_bus(txring->desc); + txring->bufinf = malloc(sizeof(struct io_buffer *) * + jme->tx_ring_size); + if (!(txring->bufinf)) { + DBG("Can not allocate transmit buffer info.\n"); + goto err_out; + } + + /* + * Initialize Transmit Buffer Pointers + */ + memset(txring->bufinf, 0, + sizeof(struct io_buffer *) * jme->tx_ring_size); + + return 0; + +err_out: + jme_free_tx_resources(jme); + return -ENOMEM; +} + +static void +jme_init_tx_ring(struct jme_adapter *jme) +{ + struct jme_ring *txring = &jme->txring; + + txring->next_to_clean = 0; + txring->next_to_use = 0; + txring->nr_free = jme->tx_ring_size; + + /* + * Initialize Transmit Descriptors + */ + memset(txring->desc, 0, jme->tx_ring_size * TX_DESC_SIZE); + jme_free_tx_buffers(jme); +} + +static void +jme_enable_tx_engine(struct jme_adapter *jme) +{ + /* + * Select Queue 0 + */ + jwrite32(jme, JME_TXCS, TXCS_DEFAULT | TXCS_SELECT_QUEUE0); + wmb(); + + /* + * Setup TX Queue 0 DMA Bass Address + */ + jwrite32(jme, JME_TXDBA_LO, (uint64_t)jme->txring.dma & 0xFFFFFFFFUL); + jwrite32(jme, JME_TXDBA_HI, (uint64_t)(jme->txring.dma) >> 32); + jwrite32(jme, JME_TXNDA, (uint64_t)jme->txring.dma & 0xFFFFFFFFUL); + + /* + * Setup TX Descptor Count + */ + jwrite32(jme, JME_TXQDC, jme->tx_ring_size); + + /* + * Enable TX Engine + */ + wmb(); + jwrite32(jme, JME_TXCS, jme->reg_txcs | + TXCS_SELECT_QUEUE0 | + TXCS_ENABLE); + +} + +static void +jme_disable_tx_engine(struct jme_adapter *jme) +{ + int i; + u32 val; + + /* + * Disable TX Engine + */ + jwrite32(jme, JME_TXCS, jme->reg_txcs | TXCS_SELECT_QUEUE0); + wmb(); + + val = jread32(jme, JME_TXCS); + for (i = JME_TX_DISABLE_TIMEOUT ; (val & TXCS_ENABLE) && i > 0 ; --i) { + mdelay(1); + val = jread32(jme, JME_TXCS); + rmb(); + } + + if (!i) + DBG("Disable TX engine timeout.\n"); +} + + +static void +jme_set_clean_rxdesc(struct jme_adapter *jme, int i) +{ + struct jme_ring *rxring = &jme->rxring; + register struct rxdesc *rxdesc = rxring->desc; + struct io_buffer *rxbi = rxring->bufinf[i]; + uint64_t mapping; + + rxdesc += i; + mapping = virt_to_bus(rxbi->data); + + rxdesc->dw[0] = 0; + rxdesc->dw[1] = 0; + rxdesc->desc1.bufaddrh = cpu_to_le32(mapping >> 32); + rxdesc->desc1.bufaddrl = cpu_to_le32(mapping & 0xFFFFFFFFUL); + rxdesc->desc1.datalen = cpu_to_le16(RX_ALLOC_LEN); + wmb(); + rxdesc->desc1.flags |= RXFLAG_OWN | RXFLAG_INT; +} + +static int +jme_make_new_rx_buf(struct io_buffer **rxbip) +{ + struct io_buffer *inbuf; + + /* + * IOB_ALIGN == 2048 + */ + inbuf = alloc_iob(RX_ALLOC_LEN); + if (!inbuf) { + DBG("Allocate receive iob error.\n"); + return -ENOMEM; + } + *rxbip = inbuf; + + return 0; +} + +static void +jme_free_rx_buf(struct jme_adapter *jme, int i) +{ + struct jme_ring *rxring = &jme->rxring; + struct io_buffer *rxbi = rxring->bufinf[i]; + + if (rxbi) { + free_iob(rxbi); + rxring->bufinf[i] = NULL; + } +} + +static void +jme_free_rx_resources(struct jme_adapter *jme) +{ + unsigned int i; + struct jme_ring *rxring = &jme->rxring; + + if (rxring->desc) { + if (rxring->bufinf) { + for (i = 0 ; i < jme->rx_ring_size ; ++i) + jme_free_rx_buf(jme, i); + free(rxring->bufinf); + } + + free_dma(rxring->desc, jme->rx_ring_size * RX_DESC_SIZE); + rxring->desc = NULL; + rxring->dma = 0; + rxring->bufinf = NULL; + } + rxring->next_to_fill = 0; + rxring->next_to_clean = 0; +} + +static int +jme_alloc_rx_resources(struct jme_adapter *jme) +{ + unsigned int i; + struct jme_ring *rxring = &jme->rxring; + struct io_buffer **bufinf; + + rxring->desc = malloc_dma(jme->rx_ring_size * RX_DESC_SIZE, + RING_DESC_ALIGN); + if (!rxring->desc) { + DBG("Can not allocate receive ring descriptors.\n"); + goto err_out; + } + + /* + * 16 Bytes align + */ + rxring->dma = virt_to_bus(rxring->desc); + rxring->bufinf = malloc(sizeof(struct io_buffer *) * + jme->rx_ring_size); + if (!(rxring->bufinf)) { + DBG("Can not allocate receive buffer info.\n"); + goto err_out; + } + + /* + * Initiallize Receive Buffer Pointers + */ + bufinf = rxring->bufinf; + memset(bufinf, 0, sizeof(struct io_buffer *) * jme->rx_ring_size); + for (i = 0 ; i < jme->rx_ring_size ; ++i) { + if (jme_make_new_rx_buf(bufinf)) + goto err_out; + ++bufinf; + } + + return 0; + +err_out: + jme_free_rx_resources(jme); + return -ENOMEM; +} + +static void +jme_init_rx_ring(struct jme_adapter *jme) +{ + unsigned int i; + struct jme_ring *rxring = &jme->rxring; + + for (i = 0 ; i < jme->rx_ring_size ; ++i) + jme_set_clean_rxdesc(jme, i); + + rxring->next_to_fill = 0; + rxring->next_to_clean = 0; +} + +static void +jme_set_multi(struct jme_adapter *jme) +{ + /* + * Just receive all kind of packet for new. + */ + jme->reg_rxmcs |= RXMCS_ALLFRAME | RXMCS_BRDFRAME | RXMCS_UNIFRAME; + jwrite32(jme, JME_RXMCS, jme->reg_rxmcs); +} + +static void +jme_enable_rx_engine(struct jme_adapter *jme) +{ + /* + * Select Queue 0 + */ + jwrite32(jme, JME_RXCS, jme->reg_rxcs | + RXCS_QUEUESEL_Q0); + wmb(); + + /* + * Setup RX DMA Bass Address + */ + jwrite32(jme, JME_RXDBA_LO, (uint64_t)(jme->rxring.dma) & 0xFFFFFFFFUL); + jwrite32(jme, JME_RXDBA_HI, (uint64_t)(jme->rxring.dma) >> 32); + jwrite32(jme, JME_RXNDA, (uint64_t)(jme->rxring.dma) & 0xFFFFFFFFUL); + + /* + * Setup RX Descriptor Count + */ + jwrite32(jme, JME_RXQDC, jme->rx_ring_size); + + /* + * Setup Unicast Filter + */ + jme_set_multi(jme); + + /* + * Enable RX Engine + */ + wmb(); + jwrite32(jme, JME_RXCS, jme->reg_rxcs | + RXCS_QUEUESEL_Q0 | + RXCS_ENABLE | + RXCS_QST); +} + +static void +jme_restart_rx_engine(struct jme_adapter *jme) +{ + /* + * Start RX Engine + */ + jwrite32(jme, JME_RXCS, jme->reg_rxcs | + RXCS_QUEUESEL_Q0 | + RXCS_ENABLE | + RXCS_QST); +} + +static void +jme_disable_rx_engine(struct jme_adapter *jme) +{ + int i; + u32 val; + + /* + * Disable RX Engine + */ + jwrite32(jme, JME_RXCS, jme->reg_rxcs); + wmb(); + + val = jread32(jme, JME_RXCS); + for (i = JME_RX_DISABLE_TIMEOUT ; (val & RXCS_ENABLE) && i > 0 ; --i) { + mdelay(1); + val = jread32(jme, JME_RXCS); + rmb(); + } + + if (!i) + DBG("Disable RX engine timeout.\n"); + +} + +static void +jme_refill_rx_ring(struct jme_adapter *jme, int curhole) +{ + struct jme_ring *rxring = &jme->rxring; + int i = rxring->next_to_fill; + struct io_buffer **bufinf = rxring->bufinf; + int mask = jme->rx_ring_mask; + int limit = jme->rx_ring_size; + + while (limit--) { + if (!bufinf[i]) { + if (jme_make_new_rx_buf(bufinf + i)) + break; + jme_set_clean_rxdesc(jme, i); + } + if (i == curhole) + limit = 0; + i = (i + 1) & mask; + } + rxring->next_to_fill = i; +} + +static void +jme_alloc_and_feed_iob(struct jme_adapter *jme, int idx) +{ + struct jme_ring *rxring = &jme->rxring; + struct rxdesc *rxdesc = rxring->desc; + struct io_buffer *rxbi = rxring->bufinf[idx]; + struct net_device *netdev = jme->mii_if.dev; + int framesize; + + rxdesc += idx; + + framesize = le16_to_cpu(rxdesc->descwb.framesize); + iob_put(rxbi, framesize); + netdev_rx(netdev, rxbi); + + rxring->bufinf[idx] = NULL; + jme_refill_rx_ring(jme, idx); +} + +static void +jme_process_receive(struct jme_adapter *jme) +{ + struct jme_ring *rxring = &jme->rxring; + struct rxdesc *rxdesc = rxring->desc; + struct net_device *netdev = jme->mii_if.dev; + int i, j, ccnt, desccnt, mask = jme->rx_ring_mask; + unsigned int limit = jme->rx_ring_size; + + i = rxring->next_to_clean; + rxdesc += i; + while (rxring->bufinf[i] && + !(rxdesc->descwb.flags & cpu_to_le16(RXWBFLAG_OWN)) && + (rxdesc->descwb.desccnt & RXWBDCNT_WBCPL) && + limit--) { + + rmb(); + desccnt = rxdesc->descwb.desccnt & RXWBDCNT_DCNT; + DBG2("Cleaning rx desc=%d, cnt=%d\n", i, desccnt); + + if (desccnt > 1 || rxdesc->descwb.errstat & RXWBERR_ALLERR) { + for (j = i, ccnt = desccnt ; ccnt-- ; ) { + jme_set_clean_rxdesc(jme, j); + j = (j + 1) & (mask); + } + DBG("Dropped packet due to "); + if (desccnt > 1) + DBG("long packet.(%d descriptors)\n", desccnt); + else + DBG("Packet error.\n"); + netdev_rx_err(netdev, NULL, -EINVAL); + } else { + jme_alloc_and_feed_iob(jme, i); + } + + i = (i + desccnt) & (mask); + rxdesc = rxring->desc; + rxdesc += i; + } + rxring->next_to_clean = i; + + return; +} + +static void +jme_set_custom_macaddr(struct net_device *netdev) +{ + struct jme_adapter *jme = netdev->priv; + uint8_t *addr = netdev->ll_addr; + u32 val; + + val = (addr[3] & 0xff) << 24 | + (addr[2] & 0xff) << 16 | + (addr[1] & 0xff) << 8 | + (addr[0] & 0xff); + jwrite32(jme, JME_RXUMA_LO, val); + val = (addr[5] & 0xff) << 8 | + (addr[4] & 0xff); + jwrite32(jme, JME_RXUMA_HI, val); +} + +/** + * Open NIC + * + * @v netdev Net device + * @ret rc Return status code + */ +static int +jme_open(struct net_device *netdev) +{ + struct jme_adapter *jme = netdev->priv; + int rc; + + /* + * Allocate receive resources + */ + rc = jme_alloc_rx_resources(jme); + if (rc) { + DBG("Allocate receive resources error.\n"); + goto nomem_out; + } + + /* + * Allocate transmit resources + */ + rc = jme_alloc_tx_resources(jme); + if (rc) { + DBG("Allocate transmit resources error.\n"); + goto free_rx_resources_out; + } + + jme_set_custom_macaddr(netdev); + jme_reset_phy_processor(jme); + jme_restart_an(jme); + + return 0; + +free_rx_resources_out: + jme_free_rx_resources(jme); +nomem_out: + return rc; +} + +/** + * Close NIC + * + * @v netdev Net device + */ +static void +jme_close(struct net_device *netdev) +{ + struct jme_adapter *jme = netdev->priv; + + jme_free_tx_resources(jme); + jme_free_rx_resources(jme); + jme_reset_mac_processor(jme); + jme->phylink = 0; + jme_phy_off(jme); + netdev_link_down(netdev); +} + +static int +jme_alloc_txdesc(struct jme_adapter *jme) +{ + struct jme_ring *txring = &jme->txring; + int idx; + + idx = txring->next_to_use; + if (txring->nr_free < 1) + return -1; + --(txring->nr_free); + txring->next_to_use = (txring->next_to_use + 1) & jme->tx_ring_mask; + + return idx; +} + +static void +jme_fill_tx_desc(struct jme_adapter *jme, struct io_buffer *iob, int idx) +{ + struct jme_ring *txring = &jme->txring; + struct txdesc *txdesc = txring->desc; + uint16_t len = iob_len(iob); + unsigned long int mapping; + + txdesc += idx; + mapping = virt_to_bus(iob->data); + DBG2("TX buffer address: %p(%08lx+%x)\n", + iob->data, mapping, len); + txdesc->dw[0] = 0; + txdesc->dw[1] = 0; + txdesc->dw[2] = 0; + txdesc->dw[3] = 0; + txdesc->desc1.datalen = cpu_to_le16(len); + txdesc->desc1.pktsize = cpu_to_le16(len); + txdesc->desc1.bufaddr = cpu_to_le32(mapping); + /* + * Set OWN bit at final. + * When kernel transmit faster than NIC. + * And NIC trying to send this descriptor before we tell + * it to start sending this TX queue. + * Other fields are already filled correctly. + */ + wmb(); + txdesc->desc1.flags = TXFLAG_OWN | TXFLAG_INT; + /* + * Set tx buffer info after telling NIC to send + * For better tx_clean timing + */ + wmb(); + txring->bufinf[idx] = iob; +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int +jme_transmit(struct net_device *netdev, struct io_buffer *iobuf) +{ + struct jme_adapter *jme = netdev->priv; + int idx; + + idx = jme_alloc_txdesc(jme); + if (idx < 0) { + /* + * Pause transmit queue somehow if possible. + */ + DBG("TX ring full!\n"); + return -EOVERFLOW; + } + + jme_fill_tx_desc(jme, iobuf, idx); + + jwrite32(jme, JME_TXCS, jme->reg_txcs | + TXCS_SELECT_QUEUE0 | + TXCS_QUEUE0S | + TXCS_ENABLE); + DBG2("xmit: idx=%d\n", idx); + + return 0; +} + +static int +jme_check_link(struct net_device *netdev, int testonly) +{ + struct jme_adapter *jme = netdev->priv; + u32 phylink, ghc, cnt = JME_SPDRSV_TIMEOUT, gpreg1; + int rc = 0; + + phylink = jread32(jme, JME_PHY_LINK); + + if (phylink & PHY_LINK_UP) { + /* + * Keep polling for speed/duplex resolve complete + */ + while (!(phylink & PHY_LINK_SPEEDDPU_RESOLVED) && + --cnt) { + + udelay(1); + phylink = jread32(jme, JME_PHY_LINK); + } + if (!cnt) + DBG("Waiting speed resolve timeout.\n"); + + if (jme->phylink == phylink) { + rc = 1; + goto out; + } + if (testonly) + goto out; + + jme->phylink = phylink; + + ghc = jme->reg_ghc & ~(GHC_SPEED | GHC_DPX | + GHC_TO_CLK_PCIE | GHC_TXMAC_CLK_PCIE | + GHC_TO_CLK_GPHY | GHC_TXMAC_CLK_GPHY); + switch (phylink & PHY_LINK_SPEED_MASK) { + case PHY_LINK_SPEED_10M: + ghc |= GHC_SPEED_10M | + GHC_TO_CLK_PCIE | GHC_TXMAC_CLK_PCIE; + break; + case PHY_LINK_SPEED_100M: + ghc |= GHC_SPEED_100M | + GHC_TO_CLK_PCIE | GHC_TXMAC_CLK_PCIE; + break; + case PHY_LINK_SPEED_1000M: + ghc |= GHC_SPEED_1000M | + GHC_TO_CLK_GPHY | GHC_TXMAC_CLK_GPHY; + break; + default: + break; + } + + if (phylink & PHY_LINK_DUPLEX) { + jwrite32(jme, JME_TXMCS, TXMCS_DEFAULT); + ghc |= GHC_DPX; + } else { + jwrite32(jme, JME_TXMCS, TXMCS_DEFAULT | + TXMCS_BACKOFF | + TXMCS_CARRIERSENSE | + TXMCS_COLLISION); + jwrite32(jme, JME_TXTRHD, TXTRHD_TXPEN | + ((0x2000 << TXTRHD_TXP_SHIFT) & TXTRHD_TXP) | + TXTRHD_TXREN | + ((8 << TXTRHD_TXRL_SHIFT) & TXTRHD_TXRL)); + } + + gpreg1 = GPREG1_DEFAULT; + if (is_buggy250(jme->pdev->device, jme->chiprev)) { + if (!(phylink & PHY_LINK_DUPLEX)) + gpreg1 |= GPREG1_HALFMODEPATCH; + switch (phylink & PHY_LINK_SPEED_MASK) { + case PHY_LINK_SPEED_10M: + jme_set_phyfifoa(jme); + gpreg1 |= GPREG1_RSSPATCH; + break; + case PHY_LINK_SPEED_100M: + jme_set_phyfifob(jme); + gpreg1 |= GPREG1_RSSPATCH; + break; + case PHY_LINK_SPEED_1000M: + jme_set_phyfifoa(jme); + break; + default: + break; + } + } + + jwrite32(jme, JME_GPREG1, gpreg1); + jwrite32(jme, JME_GHC, ghc); + jme->reg_ghc = ghc; + + DBG("Link is up at %d Mbps, %s-Duplex, MDI%s.\n", + ((phylink & PHY_LINK_SPEED_MASK) + == PHY_LINK_SPEED_1000M) ? 1000 : + ((phylink & PHY_LINK_SPEED_MASK) + == PHY_LINK_SPEED_100M) ? 100 : 10, + (phylink & PHY_LINK_DUPLEX) ? "Full" : "Half", + (phylink & PHY_LINK_MDI_STAT) ? "-X" : ""); + netdev_link_up(netdev); + } else { + if (testonly) + goto out; + + DBG("Link is down.\n"); + jme->phylink = 0; + netdev_link_down(netdev); + } + +out: + return rc; +} + +static void +jme_link_change(struct net_device *netdev) +{ + struct jme_adapter *jme = netdev->priv; + + /* + * Do nothing if the link status did not change. + */ + if (jme_check_link(netdev, 1)) + return; + + if (netdev_link_ok(netdev)) { + netdev_link_down(netdev); + jme_disable_rx_engine(jme); + jme_disable_tx_engine(jme); + jme_reset_ghc_speed(jme); + jme_reset_mac_processor(jme); + } + + jme_check_link(netdev, 0); + if (netdev_link_ok(netdev)) { + jme_init_rx_ring(jme); + jme_enable_rx_engine(jme); + jme_init_tx_ring(jme); + jme_enable_tx_engine(jme); + } + + return; +} + +static void +jme_tx_clean(struct jme_adapter *jme) +{ + struct jme_ring *txring = &jme->txring; + struct txdesc *txdesc = txring->desc; + struct io_buffer *txbi; + struct net_device *netdev = jme->mii_if.dev; + int i, cnt = 0, max, err, mask; + + max = jme->tx_ring_size - txring->nr_free; + mask = jme->tx_ring_mask; + + for (i = txring->next_to_clean ; cnt < max ; ++cnt) { + + txbi = txring->bufinf[i]; + + if (txbi && !(txdesc[i].descwb.flags & TXWBFLAG_OWN)) { + DBG2("TX clean address: %08lx(%08lx+%zx)\n", + (unsigned long)txbi->data, + virt_to_bus(txbi->data), + iob_len(txbi)); + err = txdesc[i].descwb.flags & TXWBFLAG_ALLERR; + if (err) + netdev_tx_complete_err(netdev, txbi, -EIO); + else + netdev_tx_complete(netdev, txbi); + txring->bufinf[i] = NULL; + } else { + break; + } + + i = (i + 1) & mask; + } + + DBG2("txclean: next %d\n", i); + txring->next_to_clean = i; + txring->nr_free += cnt; +} +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void +jme_poll(struct net_device *netdev) +{ + struct jme_adapter *jme = netdev->priv; + u32 intrstat; + + intrstat = jread32(jme, JME_IEVE); + + /* + * Check if any actions needs to perform. + */ + if ((intrstat & INTR_ENABLE) == 0) + return; + + /* + * Check if the device still exist + */ + if (intrstat == ~((typeof(intrstat))0)) + return; + + DBG2("intrstat 0x%08x\n", intrstat); + if (intrstat & (INTR_LINKCH | INTR_SWINTR)) { + DBG2("Link changed\n"); + jme_link_change(netdev); + + /* + * Clear all interrupt status + */ + jwrite32(jme, JME_IEVE, intrstat); + + /* + * Link change event is critical + * all other events are ignored + */ + return; + } + + /* + * Process transmission complete first to free more memory. + */ + if (intrstat & INTR_TX0) { + DBG2("Packet transmit complete\n"); + jme_tx_clean(jme); + jwrite32(jme, JME_IEVE, intrstat & INTR_TX0); + } + + if (intrstat & (INTR_RX0 | INTR_RX0EMP)) { + DBG2("Packet received\n"); + jme_process_receive(jme); + jwrite32(jme, JME_IEVE, + intrstat & (INTR_RX0 | INTR_RX0EMP)); + if (intrstat & INTR_RX0EMP) + jme_restart_rx_engine(jme); + } + + /* + * Clean all other interrupt status + */ + jwrite32(jme, JME_IEVE, + intrstat & ~(INTR_RX0 | INTR_RX0EMP | INTR_TX0)); +} + +/** + * Enable/disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void +jme_irq(struct net_device *netdev, int enable) +{ + struct jme_adapter *jme = netdev->priv; + + DBG("jme interrupts %s\n", (enable ? "enabled" : "disabled")); + if (enable) + jme_start_irq(jme); + else + jme_stop_irq(jme); +} + +/** JME net device operations */ +static struct net_device_operations jme_operations = { + .open = jme_open, + .close = jme_close, + .transmit = jme_transmit, + .poll = jme_poll, + .irq = jme_irq, +}; + +static void +jme_check_hw_ver(struct jme_adapter *jme) +{ + u32 chipmode; + + chipmode = jread32(jme, JME_CHIPMODE); + + jme->fpgaver = (chipmode & CM_FPGAVER_MASK) >> CM_FPGAVER_SHIFT; + jme->chiprev = (chipmode & CM_CHIPREV_MASK) >> CM_CHIPREV_SHIFT; +} + +static int +jme_reload_eeprom(struct jme_adapter *jme) +{ + u32 val; + int i; + + val = jread32(jme, JME_SMBCSR); + + if (val & SMBCSR_EEPROMD) { + val |= SMBCSR_CNACK; + jwrite32(jme, JME_SMBCSR, val); + val |= SMBCSR_RELOAD; + jwrite32(jme, JME_SMBCSR, val); + mdelay(12); + + for (i = JME_EEPROM_RELOAD_TIMEOUT; i > 0; --i) { + mdelay(1); + if ((jread32(jme, JME_SMBCSR) & SMBCSR_RELOAD) == 0) + break; + } + + if (i == 0) { + DBG("eeprom reload timeout\n"); + return -EIO; + } + } + + return 0; +} + +static void +jme_load_macaddr(struct net_device *netdev) +{ + struct jme_adapter *jme = netdev_priv(netdev); + unsigned char macaddr[6]; + u32 val; + + val = jread32(jme, JME_RXUMA_LO); + macaddr[0] = (val >> 0) & 0xFF; + macaddr[1] = (val >> 8) & 0xFF; + macaddr[2] = (val >> 16) & 0xFF; + macaddr[3] = (val >> 24) & 0xFF; + val = jread32(jme, JME_RXUMA_HI); + macaddr[4] = (val >> 0) & 0xFF; + macaddr[5] = (val >> 8) & 0xFF; + memcpy(netdev->hw_addr, macaddr, 6); +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int +jme_probe(struct pci_device *pci) +{ + struct net_device *netdev; + struct jme_adapter *jme; + int rc; + uint8_t mrrs; + + /* Allocate net device */ + netdev = alloc_etherdev(sizeof(*jme)); + if (!netdev) + return -ENOMEM; + netdev_init(netdev, &jme_operations); + jme = netdev->priv; + pci_set_drvdata(pci, netdev); + netdev->dev = &pci->dev; + jme->regs = ioremap(pci->membase, JME_REGS_SIZE); + if (!(jme->regs)) { + DBG("Mapping PCI resource region error.\n"); + rc = -ENOMEM; + goto err_out; + } + jme->reg_ghc = 0; + jme->reg_rxcs = RXCS_DEFAULT; + jme->reg_rxmcs = RXMCS_DEFAULT; + jme->phylink = 0; + jme->pdev = pci; + jme->mii_if.dev = netdev; + jme->mii_if.phy_id = 1; + jme->mii_if.mdio_read = jme_mdio_read; + jme->mii_if.mdio_write = jme_mdio_write; + jme->rx_ring_size = 1 << 4; + jme->rx_ring_mask = jme->rx_ring_size - 1; + jme->tx_ring_size = 1 << 4; + jme->tx_ring_mask = jme->tx_ring_size - 1; + + /* Fix up PCI device */ + adjust_pci_device(pci); + + /* + * Get Max Read Req Size from PCI Config Space + */ + pci_read_config_byte(pci, PCI_DCSR_MRRS, &mrrs); + mrrs &= PCI_DCSR_MRRS_MASK; + switch (mrrs) { + case MRRS_128B: + jme->reg_txcs = TXCS_DEFAULT | TXCS_DMASIZE_128B; + break; + case MRRS_256B: + jme->reg_txcs = TXCS_DEFAULT | TXCS_DMASIZE_256B; + break; + default: + jme->reg_txcs = TXCS_DEFAULT | TXCS_DMASIZE_512B; + break; + }; + + /* + * Get basic hardware info. + */ + jme_check_hw_ver(jme); + if (pci->device == PCI_DEVICE_ID_JMICRON_JMC250) + jme->mii_if.supports_gmii = 1; + else + jme->mii_if.supports_gmii = 0; + + /* + * Initialize PHY + */ + jme_set_phyfifoa(jme); + jme_phy_init(jme); + + /* + * Bring down phy before interface is opened. + */ + jme_phy_off(jme); + + /* + * Reset MAC processor and reload EEPROM for MAC Address + */ + jme_reset_mac_processor(jme); + rc = jme_reload_eeprom(jme); + if (rc) { + DBG("Reload eeprom for reading MAC Address error.\n"); + goto err_unmap; + } + jme_load_macaddr(netdev); + + /* Register network device */ + if ((rc = register_netdev(netdev)) != 0) { + DBG("Register net_device error.\n"); + goto err_unmap; + } + + return 0; + +err_unmap: + iounmap(jme->regs); +err_out: + netdev_nullify(netdev); + netdev_put(netdev); + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void +jme_remove(struct pci_device *pci) +{ + struct net_device *netdev = pci_get_drvdata(pci); + struct jme_adapter *jme = netdev->priv; + + iounmap(jme->regs); + unregister_netdev(netdev); + netdev_nullify(netdev); + netdev_put(netdev); +} + +static struct pci_device_id jm_nics[] = { +PCI_ROM(0x197b, 0x0250, "jme", "JMicron Gigabit Ethernet", 0), +PCI_ROM(0x197b, 0x0260, "jmfe", "JMicron Fast Ethernet", 0), +}; + +struct pci_driver jme_driver __pci_driver = { + .ids = jm_nics, + .id_count = ( sizeof ( jm_nics ) / sizeof ( jm_nics[0] ) ), + .probe = jme_probe, + .remove = jme_remove, +}; + |