| /* | |
| * 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_phys(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_phys(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_phys(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_phys(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; | |
| 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 = pci_ioremap(pci, 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, | |
| }; | |