|
@@ -46,52 +46,51 @@
|
|
|
#define MAX_PKT_SIZE 1518
|
|
|
#define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * private data
|
|
|
- *****************************************************************************/
|
|
|
struct ftgmac100_descs {
|
|
|
struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
|
|
|
struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES];
|
|
|
};
|
|
|
|
|
|
struct ftgmac100 {
|
|
|
+ /* Registers */
|
|
|
struct resource *res;
|
|
|
void __iomem *base;
|
|
|
- int irq;
|
|
|
|
|
|
struct ftgmac100_descs *descs;
|
|
|
dma_addr_t descs_dma_addr;
|
|
|
|
|
|
+ /* Rx ring */
|
|
|
struct page *rx_pages[RX_QUEUE_ENTRIES];
|
|
|
-
|
|
|
unsigned int rx_pointer;
|
|
|
+ u32 rxdes0_edorr_mask;
|
|
|
+
|
|
|
+ /* Tx ring */
|
|
|
unsigned int tx_clean_pointer;
|
|
|
unsigned int tx_pointer;
|
|
|
unsigned int tx_pending;
|
|
|
-
|
|
|
+ u32 txdes0_edotr_mask;
|
|
|
spinlock_t tx_lock;
|
|
|
|
|
|
+ /* Component structures */
|
|
|
struct net_device *netdev;
|
|
|
struct device *dev;
|
|
|
struct ncsi_dev *ndev;
|
|
|
struct napi_struct napi;
|
|
|
-
|
|
|
+ struct work_struct reset_task;
|
|
|
struct mii_bus *mii_bus;
|
|
|
- int old_speed;
|
|
|
- int int_mask_all;
|
|
|
+
|
|
|
+ /* Link management */
|
|
|
+ int cur_speed;
|
|
|
+ int cur_duplex;
|
|
|
bool use_ncsi;
|
|
|
- bool enabled;
|
|
|
|
|
|
- u32 rxdes0_edorr_mask;
|
|
|
- u32 txdes0_edotr_mask;
|
|
|
+ /* Misc */
|
|
|
+ bool need_mac_restart;
|
|
|
};
|
|
|
|
|
|
static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
|
|
|
struct ftgmac100_rxdes *rxdes, gfp_t gfp);
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * internal functions (hardware register access)
|
|
|
- *****************************************************************************/
|
|
|
static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr)
|
|
|
{
|
|
|
iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
|
|
@@ -115,27 +114,64 @@ static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv)
|
|
|
iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD);
|
|
|
}
|
|
|
|
|
|
-static int ftgmac100_reset_hw(struct ftgmac100 *priv)
|
|
|
+static int ftgmac100_reset_mac(struct ftgmac100 *priv, u32 maccr)
|
|
|
{
|
|
|
struct net_device *netdev = priv->netdev;
|
|
|
int i;
|
|
|
|
|
|
/* NOTE: reset clears all registers */
|
|
|
- iowrite32(FTGMAC100_MACCR_SW_RST, priv->base + FTGMAC100_OFFSET_MACCR);
|
|
|
- for (i = 0; i < 5; i++) {
|
|
|
+ iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
|
|
|
+ iowrite32(maccr | FTGMAC100_MACCR_SW_RST,
|
|
|
+ priv->base + FTGMAC100_OFFSET_MACCR);
|
|
|
+ for (i = 0; i < 50; i++) {
|
|
|
unsigned int maccr;
|
|
|
|
|
|
maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
|
|
|
if (!(maccr & FTGMAC100_MACCR_SW_RST))
|
|
|
return 0;
|
|
|
|
|
|
- udelay(1000);
|
|
|
+ udelay(1);
|
|
|
}
|
|
|
|
|
|
- netdev_err(netdev, "software reset failed\n");
|
|
|
+ netdev_err(netdev, "Hardware reset failed\n");
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
|
+static int ftgmac100_reset_and_config_mac(struct ftgmac100 *priv)
|
|
|
+{
|
|
|
+ u32 maccr = 0;
|
|
|
+
|
|
|
+ switch (priv->cur_speed) {
|
|
|
+ case SPEED_10:
|
|
|
+ case 0: /* no link */
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SPEED_100:
|
|
|
+ maccr |= FTGMAC100_MACCR_FAST_MODE;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SPEED_1000:
|
|
|
+ maccr |= FTGMAC100_MACCR_GIGA_MODE;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ netdev_err(priv->netdev, "Unknown speed %d !\n",
|
|
|
+ priv->cur_speed);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* (Re)initialize the queue pointers */
|
|
|
+ priv->rx_pointer = 0;
|
|
|
+ priv->tx_clean_pointer = 0;
|
|
|
+ priv->tx_pointer = 0;
|
|
|
+ priv->tx_pending = 0;
|
|
|
+
|
|
|
+ /* The doc says reset twice with 10us interval */
|
|
|
+ if (ftgmac100_reset_mac(priv, maccr))
|
|
|
+ return -EIO;
|
|
|
+ usleep_range(10, 1000);
|
|
|
+ return ftgmac100_reset_mac(priv, maccr);
|
|
|
+}
|
|
|
+
|
|
|
static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac)
|
|
|
{
|
|
|
unsigned int maddr = mac[0] << 8 | mac[1];
|
|
@@ -211,33 +247,28 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv)
|
|
|
ftgmac100_set_mac(priv, priv->netdev->dev_addr);
|
|
|
}
|
|
|
|
|
|
-#define MACCR_ENABLE_ALL (FTGMAC100_MACCR_TXDMA_EN | \
|
|
|
- FTGMAC100_MACCR_RXDMA_EN | \
|
|
|
- FTGMAC100_MACCR_TXMAC_EN | \
|
|
|
- FTGMAC100_MACCR_RXMAC_EN | \
|
|
|
- FTGMAC100_MACCR_FULLDUP | \
|
|
|
- FTGMAC100_MACCR_CRC_APD | \
|
|
|
- FTGMAC100_MACCR_RX_RUNT | \
|
|
|
- FTGMAC100_MACCR_RX_BROADPKT)
|
|
|
-
|
|
|
-static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed)
|
|
|
+static void ftgmac100_start_hw(struct ftgmac100 *priv)
|
|
|
{
|
|
|
- int maccr = MACCR_ENABLE_ALL;
|
|
|
+ u32 maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
|
|
|
|
|
|
- switch (speed) {
|
|
|
- default:
|
|
|
- case 10:
|
|
|
- break;
|
|
|
+ /* Keep the original GMAC and FAST bits */
|
|
|
+ maccr &= (FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_GIGA_MODE);
|
|
|
|
|
|
- case 100:
|
|
|
- maccr |= FTGMAC100_MACCR_FAST_MODE;
|
|
|
- break;
|
|
|
+ /* Add all the main enable bits */
|
|
|
+ maccr |= FTGMAC100_MACCR_TXDMA_EN |
|
|
|
+ FTGMAC100_MACCR_RXDMA_EN |
|
|
|
+ FTGMAC100_MACCR_TXMAC_EN |
|
|
|
+ FTGMAC100_MACCR_RXMAC_EN |
|
|
|
+ FTGMAC100_MACCR_CRC_APD |
|
|
|
+ FTGMAC100_MACCR_PHY_LINK_LEVEL |
|
|
|
+ FTGMAC100_MACCR_RX_RUNT |
|
|
|
+ FTGMAC100_MACCR_RX_BROADPKT;
|
|
|
|
|
|
- case 1000:
|
|
|
- maccr |= FTGMAC100_MACCR_GIGA_MODE;
|
|
|
- break;
|
|
|
- }
|
|
|
+ /* Add other bits as needed */
|
|
|
+ if (priv->cur_duplex == DUPLEX_FULL)
|
|
|
+ maccr |= FTGMAC100_MACCR_FULLDUP;
|
|
|
|
|
|
+ /* Hit the HW */
|
|
|
iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
|
|
|
}
|
|
|
|
|
@@ -246,9 +277,6 @@ static void ftgmac100_stop_hw(struct ftgmac100 *priv)
|
|
|
iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR);
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * internal functions (receive descriptor)
|
|
|
- *****************************************************************************/
|
|
|
static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes)
|
|
|
{
|
|
|
return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS);
|
|
@@ -373,9 +401,6 @@ static struct page *ftgmac100_rxdes_get_page(struct ftgmac100 *priv,
|
|
|
return *ftgmac100_rxdes_page_slot(priv, rxdes);
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * internal functions (receive)
|
|
|
- *****************************************************************************/
|
|
|
static int ftgmac100_next_rx_pointer(int pointer)
|
|
|
{
|
|
|
return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
|
|
@@ -560,9 +585,6 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * internal functions (transmit descriptor)
|
|
|
- *****************************************************************************/
|
|
|
static void ftgmac100_txdes_reset(const struct ftgmac100 *priv,
|
|
|
struct ftgmac100_txdes *txdes)
|
|
|
{
|
|
@@ -656,9 +678,6 @@ static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes)
|
|
|
return (struct sk_buff *)txdes->txdes2;
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * internal functions (transmit)
|
|
|
- *****************************************************************************/
|
|
|
static int ftgmac100_next_tx_pointer(int pointer)
|
|
|
{
|
|
|
return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
|
|
@@ -774,9 +793,6 @@ static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb,
|
|
|
return NETDEV_TX_OK;
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * internal functions (buffer)
|
|
|
- *****************************************************************************/
|
|
|
static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
|
|
|
struct ftgmac100_rxdes *rxdes, gfp_t gfp)
|
|
|
{
|
|
@@ -809,6 +825,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
+ /* Free all RX buffers */
|
|
|
for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
|
|
|
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
|
|
|
struct page *page = ftgmac100_rxdes_get_page(priv, rxdes);
|
|
@@ -821,6 +838,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
|
|
|
__free_page(page);
|
|
|
}
|
|
|
|
|
|
+ /* Free all TX buffers */
|
|
|
for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
|
|
|
struct ftgmac100_txdes *txdes = &priv->descs->txdes[i];
|
|
|
struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes);
|
|
@@ -832,70 +850,90 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
|
|
|
dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
|
|
|
kfree_skb(skb);
|
|
|
}
|
|
|
-
|
|
|
- dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs),
|
|
|
- priv->descs, priv->descs_dma_addr);
|
|
|
}
|
|
|
|
|
|
-static int ftgmac100_alloc_buffers(struct ftgmac100 *priv)
|
|
|
+static void ftgmac100_free_rings(struct ftgmac100 *priv)
|
|
|
{
|
|
|
- int i;
|
|
|
+ /* Free descriptors */
|
|
|
+ if (priv->descs)
|
|
|
+ dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs),
|
|
|
+ priv->descs, priv->descs_dma_addr);
|
|
|
+}
|
|
|
|
|
|
+static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
|
|
|
+{
|
|
|
+ /* Allocate descriptors */
|
|
|
priv->descs = dma_zalloc_coherent(priv->dev,
|
|
|
sizeof(struct ftgmac100_descs),
|
|
|
&priv->descs_dma_addr, GFP_KERNEL);
|
|
|
if (!priv->descs)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- /* initialize RX ring */
|
|
|
- ftgmac100_rxdes_set_end_of_ring(priv,
|
|
|
- &priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void ftgmac100_init_rings(struct ftgmac100 *priv)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Initialize RX ring */
|
|
|
+ for (i = 0; i < RX_QUEUE_ENTRIES; i++)
|
|
|
+ priv->descs->rxdes[i].rxdes0 = 0;
|
|
|
+ ftgmac100_rxdes_set_end_of_ring(priv, &priv->descs->rxdes[i - 1]);
|
|
|
+
|
|
|
+ /* Initialize TX ring */
|
|
|
+ for (i = 0; i < TX_QUEUE_ENTRIES; i++)
|
|
|
+ priv->descs->txdes[i].txdes0 = 0;
|
|
|
+ ftgmac100_txdes_set_end_of_ring(priv, &priv->descs->txdes[i -1]);
|
|
|
+}
|
|
|
+
|
|
|
+static int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv)
|
|
|
+{
|
|
|
+ int i;
|
|
|
|
|
|
for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
|
|
|
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
|
|
|
|
|
|
if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL))
|
|
|
- goto err;
|
|
|
+ return -ENOMEM;
|
|
|
}
|
|
|
-
|
|
|
- /* initialize TX ring */
|
|
|
- ftgmac100_txdes_set_end_of_ring(priv,
|
|
|
- &priv->descs->txdes[TX_QUEUE_ENTRIES - 1]);
|
|
|
return 0;
|
|
|
-
|
|
|
-err:
|
|
|
- ftgmac100_free_buffers(priv);
|
|
|
- return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * internal functions (mdio)
|
|
|
- *****************************************************************************/
|
|
|
static void ftgmac100_adjust_link(struct net_device *netdev)
|
|
|
{
|
|
|
struct ftgmac100 *priv = netdev_priv(netdev);
|
|
|
struct phy_device *phydev = netdev->phydev;
|
|
|
- int ier;
|
|
|
+ int new_speed;
|
|
|
|
|
|
- if (phydev->speed == priv->old_speed)
|
|
|
+ /* We store "no link" as speed 0 */
|
|
|
+ if (!phydev->link)
|
|
|
+ new_speed = 0;
|
|
|
+ else
|
|
|
+ new_speed = phydev->speed;
|
|
|
+
|
|
|
+ if (phydev->speed == priv->cur_speed &&
|
|
|
+ phydev->duplex == priv->cur_duplex)
|
|
|
return;
|
|
|
|
|
|
- priv->old_speed = phydev->speed;
|
|
|
+ /* Print status if we have a link or we had one and just lost it,
|
|
|
+ * don't print otherwise.
|
|
|
+ */
|
|
|
+ if (new_speed || priv->cur_speed)
|
|
|
+ phy_print_status(phydev);
|
|
|
|
|
|
- ier = ioread32(priv->base + FTGMAC100_OFFSET_IER);
|
|
|
+ priv->cur_speed = new_speed;
|
|
|
+ priv->cur_duplex = phydev->duplex;
|
|
|
|
|
|
- /* disable all interrupts */
|
|
|
- iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
-
|
|
|
- netif_stop_queue(netdev);
|
|
|
- ftgmac100_stop_hw(priv);
|
|
|
+ /* Link is down, do nothing else */
|
|
|
+ if (!new_speed)
|
|
|
+ return;
|
|
|
|
|
|
- netif_start_queue(netdev);
|
|
|
- ftgmac100_init_hw(priv);
|
|
|
- ftgmac100_start_hw(priv, phydev->speed);
|
|
|
+ /* Disable all interrupts */
|
|
|
+ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
|
|
|
- /* re-enable interrupts */
|
|
|
- iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
+ /* Reset the adapter asynchronously */
|
|
|
+ schedule_work(&priv->reset_task);
|
|
|
}
|
|
|
|
|
|
static int ftgmac100_mii_probe(struct ftgmac100 *priv)
|
|
@@ -920,9 +958,6 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * struct mii_bus functions
|
|
|
- *****************************************************************************/
|
|
|
static int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
|
|
|
{
|
|
|
struct net_device *netdev = bus->priv;
|
|
@@ -994,9 +1029,6 @@ static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr,
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * struct ethtool_ops functions
|
|
|
- *****************************************************************************/
|
|
|
static void ftgmac100_get_drvinfo(struct net_device *netdev,
|
|
|
struct ethtool_drvinfo *info)
|
|
|
{
|
|
@@ -1012,168 +1044,260 @@ static const struct ethtool_ops ftgmac100_ethtool_ops = {
|
|
|
.set_link_ksettings = phy_ethtool_set_link_ksettings,
|
|
|
};
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * interrupt handler
|
|
|
- *****************************************************************************/
|
|
|
static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
|
|
|
{
|
|
|
struct net_device *netdev = dev_id;
|
|
|
struct ftgmac100 *priv = netdev_priv(netdev);
|
|
|
+ unsigned int status, new_mask = FTGMAC100_INT_BAD;
|
|
|
|
|
|
- /* When running in NCSI mode, the interface should be ready for
|
|
|
- * receiving or transmitting NCSI packets before it's opened.
|
|
|
- */
|
|
|
- if (likely(priv->use_ncsi || netif_running(netdev))) {
|
|
|
- /* Disable interrupts for polling */
|
|
|
- iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
- napi_schedule(&priv->napi);
|
|
|
+ /* Fetch and clear interrupt bits, process abnormal ones */
|
|
|
+ status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
|
|
|
+ iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
|
|
|
+ if (unlikely(status & FTGMAC100_INT_BAD)) {
|
|
|
+
|
|
|
+ /* RX buffer unavailable */
|
|
|
+ if (status & FTGMAC100_INT_NO_RXBUF)
|
|
|
+ netdev->stats.rx_over_errors++;
|
|
|
+
|
|
|
+ /* received packet lost due to RX FIFO full */
|
|
|
+ if (status & FTGMAC100_INT_RPKT_LOST)
|
|
|
+ netdev->stats.rx_fifo_errors++;
|
|
|
+
|
|
|
+ /* sent packet lost due to excessive TX collision */
|
|
|
+ if (status & FTGMAC100_INT_XPKT_LOST)
|
|
|
+ netdev->stats.tx_fifo_errors++;
|
|
|
+
|
|
|
+ /* AHB error -> Reset the chip */
|
|
|
+ if (status & FTGMAC100_INT_AHB_ERR) {
|
|
|
+ if (net_ratelimit())
|
|
|
+ netdev_warn(netdev,
|
|
|
+ "AHB bus error ! Resetting chip.\n");
|
|
|
+ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
+ schedule_work(&priv->reset_task);
|
|
|
+ return IRQ_HANDLED;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We may need to restart the MAC after such errors, delay
|
|
|
+ * this until after we have freed some Rx buffers though
|
|
|
+ */
|
|
|
+ priv->need_mac_restart = true;
|
|
|
+
|
|
|
+ /* Disable those errors until we restart */
|
|
|
+ new_mask &= ~status;
|
|
|
}
|
|
|
|
|
|
+ /* Only enable "bad" interrupts while NAPI is on */
|
|
|
+ iowrite32(new_mask, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
+
|
|
|
+ /* Schedule NAPI bh */
|
|
|
+ napi_schedule_irqoff(&priv->napi);
|
|
|
+
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * struct napi_struct functions
|
|
|
- *****************************************************************************/
|
|
|
static int ftgmac100_poll(struct napi_struct *napi, int budget)
|
|
|
{
|
|
|
struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi);
|
|
|
- struct net_device *netdev = priv->netdev;
|
|
|
- unsigned int status;
|
|
|
- bool completed = true;
|
|
|
+ bool more, completed = true;
|
|
|
int rx = 0;
|
|
|
|
|
|
- status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
|
|
|
- iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
|
|
|
-
|
|
|
- if (status & (FTGMAC100_INT_RPKT_BUF | FTGMAC100_INT_NO_RXBUF)) {
|
|
|
- /*
|
|
|
- * FTGMAC100_INT_RPKT_BUF:
|
|
|
- * RX DMA has received packets into RX buffer successfully
|
|
|
- *
|
|
|
- * FTGMAC100_INT_NO_RXBUF:
|
|
|
- * RX buffer unavailable
|
|
|
- */
|
|
|
- bool retry;
|
|
|
-
|
|
|
- do {
|
|
|
- retry = ftgmac100_rx_packet(priv, &rx);
|
|
|
- } while (retry && rx < budget);
|
|
|
+ ftgmac100_tx_complete(priv);
|
|
|
|
|
|
- if (retry && rx == budget)
|
|
|
- completed = false;
|
|
|
- }
|
|
|
+ do {
|
|
|
+ more = ftgmac100_rx_packet(priv, &rx);
|
|
|
+ } while (more && rx < budget);
|
|
|
|
|
|
- if (status & (FTGMAC100_INT_XPKT_ETH | FTGMAC100_INT_XPKT_LOST)) {
|
|
|
- /*
|
|
|
- * FTGMAC100_INT_XPKT_ETH:
|
|
|
- * packet transmitted to ethernet successfully
|
|
|
- *
|
|
|
- * FTGMAC100_INT_XPKT_LOST:
|
|
|
- * packet transmitted to ethernet lost due to late
|
|
|
- * collision or excessive collision
|
|
|
- */
|
|
|
- ftgmac100_tx_complete(priv);
|
|
|
- }
|
|
|
+ if (more && rx == budget)
|
|
|
+ completed = false;
|
|
|
|
|
|
- if (status & priv->int_mask_all & (FTGMAC100_INT_NO_RXBUF |
|
|
|
- FTGMAC100_INT_RPKT_LOST | FTGMAC100_INT_AHB_ERR)) {
|
|
|
- if (net_ratelimit())
|
|
|
- netdev_info(netdev, "[ISR] = 0x%x: %s%s%s\n", status,
|
|
|
- status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
|
|
|
- status & FTGMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "",
|
|
|
- status & FTGMAC100_INT_AHB_ERR ? "AHB_ERR " : "");
|
|
|
|
|
|
- if (status & FTGMAC100_INT_NO_RXBUF) {
|
|
|
- /* RX buffer unavailable */
|
|
|
- netdev->stats.rx_over_errors++;
|
|
|
- }
|
|
|
+ /* The interrupt is telling us to kick the MAC back to life
|
|
|
+ * after an RX overflow
|
|
|
+ */
|
|
|
+ if (unlikely(priv->need_mac_restart)) {
|
|
|
+ ftgmac100_start_hw(priv);
|
|
|
|
|
|
- if (status & FTGMAC100_INT_RPKT_LOST) {
|
|
|
- /* received packet lost due to RX FIFO full */
|
|
|
- netdev->stats.rx_fifo_errors++;
|
|
|
- }
|
|
|
+ /* Re-enable "bad" interrupts */
|
|
|
+ iowrite32(FTGMAC100_INT_BAD,
|
|
|
+ priv->base + FTGMAC100_OFFSET_IER);
|
|
|
}
|
|
|
|
|
|
+ /* Keep NAPI going if we have still packets to reclaim */
|
|
|
+ if (priv->tx_pending)
|
|
|
+ return budget;
|
|
|
+
|
|
|
if (completed) {
|
|
|
+ /* We are about to re-enable all interrupts. However
|
|
|
+ * the HW has been latching RX/TX packet interrupts while
|
|
|
+ * they were masked. So we clear them first, then we need
|
|
|
+ * to re-check if there's something to process
|
|
|
+ */
|
|
|
+ iowrite32(FTGMAC100_INT_RXTX,
|
|
|
+ priv->base + FTGMAC100_OFFSET_ISR);
|
|
|
+ if (ftgmac100_rxdes_packet_ready
|
|
|
+ (ftgmac100_current_rxdes(priv)) || priv->tx_pending)
|
|
|
+ return budget;
|
|
|
+
|
|
|
+ /* deschedule NAPI */
|
|
|
napi_complete(napi);
|
|
|
|
|
|
/* enable all interrupts */
|
|
|
- iowrite32(priv->int_mask_all,
|
|
|
+ iowrite32(FTGMAC100_INT_ALL,
|
|
|
priv->base + FTGMAC100_OFFSET_IER);
|
|
|
}
|
|
|
|
|
|
return rx;
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * struct net_device_ops functions
|
|
|
- *****************************************************************************/
|
|
|
-static int ftgmac100_open(struct net_device *netdev)
|
|
|
+static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err)
|
|
|
{
|
|
|
- struct ftgmac100 *priv = netdev_priv(netdev);
|
|
|
- unsigned int status;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ /* Re-init descriptors (adjust queue sizes) */
|
|
|
+ ftgmac100_init_rings(priv);
|
|
|
+
|
|
|
+ /* Realloc rx descriptors */
|
|
|
+ err = ftgmac100_alloc_rx_buffers(priv);
|
|
|
+ if (err && !ignore_alloc_err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ /* Reinit and restart HW */
|
|
|
+ ftgmac100_init_hw(priv);
|
|
|
+ ftgmac100_start_hw(priv);
|
|
|
+
|
|
|
+ /* Re-enable the device */
|
|
|
+ napi_enable(&priv->napi);
|
|
|
+ netif_start_queue(priv->netdev);
|
|
|
+
|
|
|
+ /* Enable all interrupts */
|
|
|
+ iowrite32(FTGMAC100_INT_ALL, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static void ftgmac100_reset_task(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct ftgmac100 *priv = container_of(work, struct ftgmac100,
|
|
|
+ reset_task);
|
|
|
+ struct net_device *netdev = priv->netdev;
|
|
|
int err;
|
|
|
|
|
|
- err = ftgmac100_alloc_buffers(priv);
|
|
|
+ netdev_dbg(netdev, "Resetting NIC...\n");
|
|
|
+
|
|
|
+ /* Lock the world */
|
|
|
+ rtnl_lock();
|
|
|
+ if (netdev->phydev)
|
|
|
+ mutex_lock(&netdev->phydev->lock);
|
|
|
+ if (priv->mii_bus)
|
|
|
+ mutex_lock(&priv->mii_bus->mdio_lock);
|
|
|
+
|
|
|
+
|
|
|
+ /* Check if the interface is still up */
|
|
|
+ if (!netif_running(netdev))
|
|
|
+ goto bail;
|
|
|
+
|
|
|
+ /* Stop the network stack */
|
|
|
+ netif_trans_update(netdev);
|
|
|
+ napi_disable(&priv->napi);
|
|
|
+ netif_tx_disable(netdev);
|
|
|
+
|
|
|
+ /* Stop and reset the MAC */
|
|
|
+ ftgmac100_stop_hw(priv);
|
|
|
+ err = ftgmac100_reset_and_config_mac(priv);
|
|
|
if (err) {
|
|
|
- netdev_err(netdev, "failed to allocate buffers\n");
|
|
|
- goto err_alloc;
|
|
|
+ /* Not much we can do ... it might come back... */
|
|
|
+ netdev_err(netdev, "attempting to continue...\n");
|
|
|
}
|
|
|
|
|
|
- err = request_irq(priv->irq, ftgmac100_interrupt, 0, netdev->name, netdev);
|
|
|
+ /* Free all rx and tx buffers */
|
|
|
+ ftgmac100_free_buffers(priv);
|
|
|
+
|
|
|
+ /* Setup everything again and restart chip */
|
|
|
+ ftgmac100_init_all(priv, true);
|
|
|
+
|
|
|
+ netdev_dbg(netdev, "Reset done !\n");
|
|
|
+ bail:
|
|
|
+ if (priv->mii_bus)
|
|
|
+ mutex_unlock(&priv->mii_bus->mdio_lock);
|
|
|
+ if (netdev->phydev)
|
|
|
+ mutex_unlock(&netdev->phydev->lock);
|
|
|
+ rtnl_unlock();
|
|
|
+}
|
|
|
+
|
|
|
+static int ftgmac100_open(struct net_device *netdev)
|
|
|
+{
|
|
|
+ struct ftgmac100 *priv = netdev_priv(netdev);
|
|
|
+ int err;
|
|
|
+
|
|
|
+ /* Allocate ring buffers */
|
|
|
+ err = ftgmac100_alloc_rings(priv);
|
|
|
if (err) {
|
|
|
- netdev_err(netdev, "failed to request irq %d\n", priv->irq);
|
|
|
- goto err_irq;
|
|
|
+ netdev_err(netdev, "Failed to allocate descriptors\n");
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
- priv->rx_pointer = 0;
|
|
|
- priv->tx_clean_pointer = 0;
|
|
|
- priv->tx_pointer = 0;
|
|
|
- priv->tx_pending = 0;
|
|
|
+ /* When using NC-SI we force the speed to 100Mbit/s full duplex,
|
|
|
+ *
|
|
|
+ * Otherwise we leave it set to 0 (no link), the link
|
|
|
+ * message from the PHY layer will handle setting it up to
|
|
|
+ * something else if needed.
|
|
|
+ */
|
|
|
+ if (priv->use_ncsi) {
|
|
|
+ priv->cur_duplex = DUPLEX_FULL;
|
|
|
+ priv->cur_speed = SPEED_100;
|
|
|
+ } else {
|
|
|
+ priv->cur_duplex = 0;
|
|
|
+ priv->cur_speed = 0;
|
|
|
+ }
|
|
|
|
|
|
- err = ftgmac100_reset_hw(priv);
|
|
|
+ /* Reset the hardware */
|
|
|
+ err = ftgmac100_reset_and_config_mac(priv);
|
|
|
if (err)
|
|
|
goto err_hw;
|
|
|
|
|
|
- ftgmac100_init_hw(priv);
|
|
|
- ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10);
|
|
|
+ /* Initialize NAPI */
|
|
|
+ netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64);
|
|
|
|
|
|
- /* Clear stale interrupts */
|
|
|
- status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
|
|
|
- iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
|
|
|
+ /* Grab our interrupt */
|
|
|
+ err = request_irq(netdev->irq, ftgmac100_interrupt, 0, netdev->name, netdev);
|
|
|
+ if (err) {
|
|
|
+ netdev_err(netdev, "failed to request irq %d\n", netdev->irq);
|
|
|
+ goto err_irq;
|
|
|
+ }
|
|
|
|
|
|
- if (netdev->phydev)
|
|
|
+ /* Start things up */
|
|
|
+ err = ftgmac100_init_all(priv, false);
|
|
|
+ if (err) {
|
|
|
+ netdev_err(netdev, "Failed to allocate packet buffers\n");
|
|
|
+ goto err_alloc;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (netdev->phydev) {
|
|
|
+ /* If we have a PHY, start polling */
|
|
|
phy_start(netdev->phydev);
|
|
|
- else if (priv->use_ncsi)
|
|
|
+ } else if (priv->use_ncsi) {
|
|
|
+ /* If using NC-SI, set our carrier on and start the stack */
|
|
|
netif_carrier_on(netdev);
|
|
|
|
|
|
- napi_enable(&priv->napi);
|
|
|
- netif_start_queue(netdev);
|
|
|
-
|
|
|
- /* enable all interrupts */
|
|
|
- iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
-
|
|
|
- /* Start the NCSI device */
|
|
|
- if (priv->use_ncsi) {
|
|
|
+ /* Start the NCSI device */
|
|
|
err = ncsi_start_dev(priv->ndev);
|
|
|
if (err)
|
|
|
goto err_ncsi;
|
|
|
}
|
|
|
|
|
|
- priv->enabled = true;
|
|
|
-
|
|
|
return 0;
|
|
|
|
|
|
-err_ncsi:
|
|
|
+ err_ncsi:
|
|
|
napi_disable(&priv->napi);
|
|
|
netif_stop_queue(netdev);
|
|
|
- iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
-err_hw:
|
|
|
- free_irq(priv->irq, netdev);
|
|
|
-err_irq:
|
|
|
+ err_alloc:
|
|
|
ftgmac100_free_buffers(priv);
|
|
|
-err_alloc:
|
|
|
+ free_irq(netdev->irq, netdev);
|
|
|
+ err_irq:
|
|
|
+ netif_napi_del(&priv->napi);
|
|
|
+ err_hw:
|
|
|
+ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
+ ftgmac100_free_rings(priv);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -1181,23 +1305,29 @@ static int ftgmac100_stop(struct net_device *netdev)
|
|
|
{
|
|
|
struct ftgmac100 *priv = netdev_priv(netdev);
|
|
|
|
|
|
- if (!priv->enabled)
|
|
|
- return 0;
|
|
|
+ /* Note about the reset task: We are called with the rtnl lock
|
|
|
+ * held, so we are synchronized against the core of the reset
|
|
|
+ * task. We must not try to synchronously cancel it otherwise
|
|
|
+ * we can deadlock. But since it will test for netif_running()
|
|
|
+ * which has already been cleared by the net core, we don't
|
|
|
+ * anything special to do.
|
|
|
+ */
|
|
|
|
|
|
/* disable all interrupts */
|
|
|
- priv->enabled = false;
|
|
|
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
|
|
|
|
|
netif_stop_queue(netdev);
|
|
|
napi_disable(&priv->napi);
|
|
|
+ netif_napi_del(&priv->napi);
|
|
|
if (netdev->phydev)
|
|
|
phy_stop(netdev->phydev);
|
|
|
else if (priv->use_ncsi)
|
|
|
ncsi_stop_dev(priv->ndev);
|
|
|
|
|
|
ftgmac100_stop_hw(priv);
|
|
|
- free_irq(priv->irq, netdev);
|
|
|
+ free_irq(netdev->irq, netdev);
|
|
|
ftgmac100_free_buffers(priv);
|
|
|
+ ftgmac100_free_rings(priv);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1321,9 +1451,6 @@ static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
|
|
|
nd->link_up ? "up" : "down");
|
|
|
}
|
|
|
|
|
|
-/******************************************************************************
|
|
|
- * struct platform_driver functions
|
|
|
- *****************************************************************************/
|
|
|
static int ftgmac100_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
struct resource *res;
|
|
@@ -1361,12 +1488,10 @@ static int ftgmac100_probe(struct platform_device *pdev)
|
|
|
priv = netdev_priv(netdev);
|
|
|
priv->netdev = netdev;
|
|
|
priv->dev = &pdev->dev;
|
|
|
+ INIT_WORK(&priv->reset_task, ftgmac100_reset_task);
|
|
|
|
|
|
spin_lock_init(&priv->tx_lock);
|
|
|
|
|
|
- /* initialize NAPI */
|
|
|
- netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64);
|
|
|
-
|
|
|
/* map io memory */
|
|
|
priv->res = request_mem_region(res->start, resource_size(res),
|
|
|
dev_name(&pdev->dev));
|
|
@@ -1383,18 +1508,11 @@ static int ftgmac100_probe(struct platform_device *pdev)
|
|
|
goto err_ioremap;
|
|
|
}
|
|
|
|
|
|
- priv->irq = irq;
|
|
|
+ netdev->irq = irq;
|
|
|
|
|
|
/* MAC address from chip or random one */
|
|
|
ftgmac100_setup_mac(priv);
|
|
|
|
|
|
- priv->int_mask_all = (FTGMAC100_INT_RPKT_LOST |
|
|
|
- FTGMAC100_INT_XPKT_ETH |
|
|
|
- FTGMAC100_INT_XPKT_LOST |
|
|
|
- FTGMAC100_INT_AHB_ERR |
|
|
|
- FTGMAC100_INT_RPKT_BUF |
|
|
|
- FTGMAC100_INT_NO_RXBUF);
|
|
|
-
|
|
|
if (of_machine_is_compatible("aspeed,ast2400") ||
|
|
|
of_machine_is_compatible("aspeed,ast2500")) {
|
|
|
priv->rxdes0_edorr_mask = BIT(30);
|
|
@@ -1440,7 +1558,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
|
|
|
goto err_register_netdev;
|
|
|
}
|
|
|
|
|
|
- netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);
|
|
|
+ netdev_info(netdev, "irq %d, mapped at %p\n", netdev->irq, priv->base);
|
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -1467,6 +1585,12 @@ static int ftgmac100_remove(struct platform_device *pdev)
|
|
|
priv = netdev_priv(netdev);
|
|
|
|
|
|
unregister_netdev(netdev);
|
|
|
+
|
|
|
+ /* There's a small chance the reset task will have been re-queued,
|
|
|
+ * during stop, make sure it's gone before we free the structure.
|
|
|
+ */
|
|
|
+ cancel_work_sync(&priv->reset_task);
|
|
|
+
|
|
|
ftgmac100_destroy_mdio(netdev);
|
|
|
|
|
|
iounmap(priv->base);
|