| /* |
| * Atheros AR71xx built-in ethernet mac driver |
| * |
| * Copyright (c) 2013 The Linux Foundation. All rights reserved. |
| * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> |
| * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> |
| * |
| * Based on Atheros' AG7100 driver |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 as published |
| * by the Free Software Foundation. |
| */ |
| |
| #include "ag71xx.h" |
| #ifdef CONFIG_OF |
| #include <linux/of.h> |
| #include <linux/of_platform.h> |
| #endif |
| |
| #ifndef UNUSED |
| #define UNUSED(__x) (void)(__x) |
| #endif |
| |
| static int ag71xx_gmac_num = 0; |
| |
| #define AG71XX_DEFAULT_MSG_ENABLE \ |
| (NETIF_MSG_DRV \ |
| | NETIF_MSG_PROBE \ |
| | NETIF_MSG_LINK \ |
| | NETIF_MSG_TIMER \ |
| | NETIF_MSG_IFDOWN \ |
| | NETIF_MSG_IFUP \ |
| | NETIF_MSG_RX_ERR \ |
| | NETIF_MSG_TX_ERR) |
| |
| static int ag71xx_msg_level = -1; |
| |
| module_param_named(msg_level, ag71xx_msg_level, int, 0); |
| MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)"); |
| |
| #ifdef CONFIG_AG71XX_SRAM_DESCRIPTORS |
| #define MAX_AG71XX_USING_SRAM 2 |
| #define MAX_AG71XX_SRAM_RINGS (MAX_AG71XX_USING_SRAM) * 2 |
| static unsigned long ag71xx_ring_bufs[MAX_AG71XX_SRAM_RINGS] = { |
| 0x1d000000UL, |
| 0x1d001000UL, |
| 0x1d002000UL, |
| 0x1d003000UL |
| }; |
| #endif /* CONFIG_AG71XX_SRAM_DESCRIPTORS */ |
| |
| #ifdef DEBUG |
| static void ag71xx_dump_dma_regs(struct ag71xx *ag) |
| { |
| DBG("%s: dma_tx_ctrl=%08x, dma_tx_desc=%08x, dma_tx_status=%08x\n", |
| ag->dev->name, |
| ag71xx_rr(ag, AG71XX_REG_TX_CTRL), |
| ag71xx_rr(ag, AG71XX_REG_TX_DESC), |
| ag71xx_rr(ag, AG71XX_REG_TX_STATUS)); |
| |
| DBG("%s: dma_rx_ctrl=%08x, dma_rx_desc=%08x, dma_rx_status=%08x\n", |
| ag->dev->name, |
| ag71xx_rr(ag, AG71XX_REG_RX_CTRL), |
| ag71xx_rr(ag, AG71XX_REG_RX_DESC), |
| ag71xx_rr(ag, AG71XX_REG_RX_STATUS)); |
| } |
| |
| static void ag71xx_dump_regs(struct ag71xx *ag) |
| { |
| DBG("%s: mac_cfg1=%08x, mac_cfg2=%08x, ipg=%08x, hdx=%08x, mfl=%08x\n", |
| ag->dev->name, |
| ag71xx_rr(ag, AG71XX_REG_MAC_CFG1), |
| ag71xx_rr(ag, AG71XX_REG_MAC_CFG2), |
| ag71xx_rr(ag, AG71XX_REG_MAC_IPG), |
| ag71xx_rr(ag, AG71XX_REG_MAC_HDX), |
| ag71xx_rr(ag, AG71XX_REG_MAC_MFL)); |
| DBG("%s: mac_ifctl=%08x, mac_addr1=%08x, mac_addr2=%08x\n", |
| ag->dev->name, |
| ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL), |
| ag71xx_rr(ag, AG71XX_REG_MAC_ADDR1), |
| ag71xx_rr(ag, AG71XX_REG_MAC_ADDR2)); |
| DBG("%s: fifo_cfg0=%08x, fifo_cfg1=%08x, fifo_cfg2=%08x\n", |
| ag->dev->name, |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0), |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1), |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2)); |
| DBG("%s: fifo_cfg3=%08x, fifo_cfg4=%08x, fifo_cfg5=%08x\n", |
| ag->dev->name, |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3), |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4), |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5)); |
| } |
| |
| static inline void ag71xx_dump_intr(struct ag71xx *ag, char *label, u32 intr) |
| { |
| DBG("%s: %s intr=%08x %s%s%s%s%s%s\n", |
| ag->dev->name, label, intr, |
| (intr & AG71XX_INT_TX_PS) ? "TXPS " : "", |
| (intr & AG71XX_INT_TX_UR) ? "TXUR " : "", |
| (intr & AG71XX_INT_TX_BE) ? "TXBE " : "", |
| (intr & AG71XX_INT_RX_PR) ? "RXPR " : "", |
| (intr & AG71XX_INT_RX_OF) ? "RXOF " : "", |
| (intr & AG71XX_INT_RX_BE) ? "RXBE " : ""); |
| } |
| #else /* !DEBUG */ |
| #define ag71xx_dump_dma_regs(__ag) |
| #define ag71xx_dump_regs(__ag) |
| #define ag71xx_dump_intr(__ag, __label, __intr) |
| #endif /* DEBUG */ |
| |
| static void ag71xx_ring_free(struct ag71xx_ring *ring) |
| { |
| if (ring->descs_cpu) { |
| if (ring->iomem) { |
| iounmap(ring->iomem); |
| } else { |
| dma_free_coherent(NULL, ring->size * ring->desc_size, |
| ring->descs_cpu, ring->descs_dma); |
| } |
| } |
| } |
| |
| static int ag71xx_ring_alloc(struct ag71xx *ag, struct ag71xx_ring *ring, |
| unsigned int id) |
| { |
| int i; |
| |
| ring->desc_size = sizeof(struct ag71xx_desc); |
| if (ring->desc_size % cache_line_size()) { |
| DBG("ag71xx: ring %p, desc size %u rounded to %u\n", |
| ring, ring->desc_size, |
| roundup(ring->desc_size, cache_line_size())); |
| ring->desc_size = roundup(ring->desc_size, cache_line_size()); |
| } |
| |
| #ifdef CONFIG_AG71XX_SRAM_DESCRIPTORS |
| if (id < MAX_AG71XX_USING_SRAM) { |
| DBG("ag71xx: descriptors in SRAM\n"); |
| ring->iomem = ioremap_nocache(ag71xx_ring_bufs[id], 0x1000); |
| if (ring->iomem == NULL) |
| return -ENOMEM; |
| |
| ring->descs_cpu = (u8 *)ring->iomem; |
| ring->descs_dma = ((dma_addr_t)(ring->iomem) & 0x1fffffff); |
| goto descs_allocated; |
| } |
| #else |
| UNUSED(id); |
| #endif /* CONFIG_AG71XX_SRAM_DESCRIPTORS */ |
| ring->iomem = NULL; |
| ring->descs_cpu = dma_alloc_coherent(NULL, |
| ring->size * ring->desc_size, |
| &ring->descs_dma, |
| GFP_ATOMIC); |
| if (!ring->descs_cpu) { |
| return -ENOMEM; |
| } |
| |
| #ifdef CONFIG_AG71XX_SRAM_DESCRIPTORS |
| descs_allocated: |
| #endif /* CONFIG_AG71XX_SRAM_DESCRIPTORS */ |
| /* The even numbered ring at the beginning, odd gets the end */ |
| if ((id & 0x1) == 0) { |
| ring->buf = &ag->ring_bufs[0]; |
| } else { |
| /* |
| * This looks weird but tries very hard to avoid aliasing |
| * problems in the D-cache. |
| */ |
| unsigned int rings_total; |
| rings_total = AG71XX_TX_RING_SIZE_MAX + AG71XX_RX_RING_SIZE_MAX; |
| ring->buf = &ag->ring_bufs[rings_total - ring->size]; |
| } |
| |
| for (i = 0; i < ring->size; i++) { |
| int idx = i * ring->desc_size; |
| ring->buf[i].desc = (struct ag71xx_desc *)&ring->descs_cpu[idx]; |
| DBG("ag71xx: ring %p, desc %d at %p\n", |
| ring, i, ring->buf[i].desc); |
| } |
| |
| return 0; |
| } |
| |
| static void ag71xx_ring_tx_clean(struct ag71xx *ag) |
| { |
| struct ag71xx_ring *ring = &ag->tx_ring; |
| struct net_device *dev = ag->dev; |
| unsigned int bytes_compl = 0; |
| unsigned int pkts_compl = 0; |
| struct ag71xx_buf *dirty = ring->dirty; |
| unsigned int used = ring->used; |
| |
| if (!ring->buf) { |
| return; |
| } |
| |
| while (used) { |
| struct ag71xx_desc *desc = dirty->desc; |
| struct sk_buff *skb; |
| |
| /* |
| * If the descriptor is not marked as empty then mark it as |
| * empty and record a TX error. |
| */ |
| if (!(desc->ctrl & DESC_EMPTY)) { |
| desc->ctrl = DESC_EMPTY; |
| dev->stats.tx_errors++; |
| } |
| |
| skb = dirty->skb; |
| dirty->skb = NULL; |
| dirty = dirty->next; |
| |
| bytes_compl += skb->len; |
| pkts_compl++; |
| dev_kfree_skb(skb); |
| |
| used--; |
| } |
| |
| ring->dirty = dirty; |
| ring->used = used; |
| |
| netdev_completed_queue(dev, pkts_compl, bytes_compl); |
| } |
| |
| static void ag71xx_ring_tx_init(struct ag71xx *ag) |
| { |
| struct ag71xx_ring *ring = &ag->tx_ring; |
| unsigned int mask = ring->mask; |
| unsigned int size = ring->size; |
| int i; |
| |
| for (i = 0; i < size; i++) { |
| struct ag71xx_buf *buf = &ring->buf[i]; |
| struct ag71xx_desc *desc = buf->desc; |
| |
| desc->next = (u32)(ring->descs_dma + |
| ring->desc_size * ((i + 1) & mask)); |
| |
| desc->ctrl = DESC_EMPTY; |
| buf->skb = NULL; |
| buf->next = &ring->buf[(i + 1) & mask]; |
| } |
| |
| ring->curr = ring->buf; |
| ring->dirty = ring->buf; |
| ring->used = 0; |
| netdev_reset_queue(ag->dev); |
| } |
| |
| static void ag71xx_ring_rx_clean(struct ag71xx *ag) |
| { |
| struct ag71xx_ring *ring = &ag->rx_ring; |
| struct net_device *dev = ag->dev; |
| int i; |
| |
| if (!ring->buf) { |
| return; |
| } |
| |
| for (i = 0; i < ring->size; i++) { |
| struct ag71xx_buf *buf = &ring->buf[i]; |
| struct sk_buff *skb = buf->skb; |
| |
| if (skb) { |
| dma_unmap_single(&dev->dev, buf->dma_addr, |
| ag->rx_buf_size, DMA_FROM_DEVICE); |
| dev_kfree_skb(skb); |
| buf->skb = NULL; |
| } |
| } |
| } |
| |
| static int ag71xx_ring_rx_init(struct ag71xx *ag) |
| { |
| struct ag71xx_ring *ring = &ag->rx_ring; |
| struct net_device *dev = ag->dev; |
| unsigned int mask = ring->mask; |
| unsigned int size = ring->size; |
| unsigned int rx_buf_size = ag->rx_buf_size; |
| unsigned int rx_buf_offset = ag->rx_buf_offset; |
| unsigned int i; |
| |
| for (i = 0; i < size; i++) { |
| struct ag71xx_buf *buf = &ring->buf[i]; |
| struct ag71xx_desc *desc = buf->desc; |
| struct sk_buff *skb; |
| |
| desc->next = (u32)(ring->descs_dma + |
| ring->desc_size * ((i + 1) & mask)); |
| |
| skb = dev_alloc_skb(rx_buf_size + rx_buf_offset); |
| if (unlikely(!skb)) { |
| return -ENOMEM; |
| } |
| |
| skb_reserve(skb, rx_buf_offset); |
| |
| buf->skb = skb; |
| buf->next = &ring->buf[(i + 1) & mask]; |
| buf->dma_addr = dma_map_single(&dev->dev, skb->data, |
| rx_buf_size, DMA_FROM_DEVICE); |
| |
| desc->data = (u32)buf->dma_addr; |
| desc->ctrl = DESC_EMPTY; |
| } |
| |
| ring->curr = ring->buf; |
| ring->dirty = ring->buf; |
| ring->used = size; |
| |
| return 0; |
| } |
| |
| static int ag71xx_rings_init(struct ag71xx *ag) |
| { |
| unsigned int rings_total; |
| int ret; |
| |
| rings_total = AG71XX_TX_RING_SIZE_MAX + AG71XX_RX_RING_SIZE_MAX; |
| ag->ring_bufs = kzalloc((rings_total * sizeof(struct ag71xx_buf)), |
| GFP_KERNEL); |
| |
| if (!ag->ring_bufs) |
| return -ENOMEM; |
| |
| ret = ag71xx_ring_alloc(ag, &ag->tx_ring, (ag->gmac_num * 2)); |
| if (ret) |
| return ret; |
| |
| ag71xx_ring_tx_init(ag); |
| |
| ret = ag71xx_ring_alloc(ag, &ag->rx_ring, (ag->gmac_num * 2) + 1); |
| if (ret) |
| return ret; |
| |
| ret = ag71xx_ring_rx_init(ag); |
| return ret; |
| } |
| |
| static void ag71xx_rings_cleanup(struct ag71xx *ag) |
| { |
| ag71xx_ring_rx_clean(ag); |
| ag71xx_ring_free(&ag->rx_ring); |
| |
| ag71xx_ring_tx_clean(ag); |
| netdev_reset_queue(ag->dev); |
| ag71xx_ring_free(&ag->tx_ring); |
| |
| if (ag->ring_bufs) |
| kfree(ag->ring_bufs); |
| } |
| |
| static unsigned char *ag71xx_speed_str(struct ag71xx *ag) |
| { |
| switch (ag->speed) { |
| case SPEED_1000: |
| return "1000"; |
| case SPEED_100: |
| return "100"; |
| case SPEED_10: |
| return "10"; |
| } |
| |
| return "?"; |
| } |
| |
| static void ag71xx_hw_set_macaddr(struct ag71xx *ag, unsigned char *mac) |
| { |
| u32 t; |
| |
| t = (((u32)mac[5]) << 24) | (((u32)mac[4]) << 16) |
| | (((u32)mac[3]) << 8) | ((u32)mac[2]); |
| |
| ag71xx_wr(ag, AG71XX_REG_MAC_ADDR1, t); |
| |
| t = (((u32)mac[1]) << 24) | (((u32)mac[0]) << 16); |
| ag71xx_wr(ag, AG71XX_REG_MAC_ADDR2, t); |
| } |
| |
| static void ag71xx_dma_reset(struct ag71xx *ag) |
| { |
| u32 val; |
| int i; |
| |
| ag71xx_dump_dma_regs(ag); |
| |
| /* stop RX and TX */ |
| ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); |
| ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); |
| |
| /* |
| * give the hardware some time to really stop all rx/tx activity |
| * clearing the descriptors too early causes random memory corruption |
| */ |
| mdelay(1); |
| |
| /* clear descriptor addresses */ |
| ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->stop_desc_dma); |
| ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->stop_desc_dma); |
| |
| /* clear pending RX/TX interrupts */ |
| for (i = 0; i < 256; i++) { |
| ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); |
| ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); |
| } |
| |
| /* clear pending errors */ |
| ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE | RX_STATUS_OF); |
| ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE | TX_STATUS_UR); |
| |
| val = ag71xx_rr(ag, AG71XX_REG_RX_STATUS); |
| if (val) |
| pr_alert("%s: unable to clear DMA Rx status: %08x\n", |
| ag->dev->name, val); |
| |
| val = ag71xx_rr(ag, AG71XX_REG_TX_STATUS); |
| |
| /* mask out reserved bits */ |
| val &= ~0xff000000; |
| |
| if (val) |
| pr_alert("%s: unable to clear DMA Tx status: %08x\n", |
| ag->dev->name, val); |
| |
| ag71xx_dump_dma_regs(ag); |
| } |
| |
| #define MAC_CFG1_INIT (MAC_CFG1_RXE | MAC_CFG1_TXE | \ |
| MAC_CFG1_SRX | MAC_CFG1_STX) |
| |
| #define FIFO_CFG0_INIT (FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT) |
| |
| #define FIFO_CFG4_INIT (FIFO_CFG4_DE | FIFO_CFG4_DV | FIFO_CFG4_FC | \ |
| FIFO_CFG4_CE | FIFO_CFG4_CR | FIFO_CFG4_LM | \ |
| FIFO_CFG4_LO | FIFO_CFG4_OK | FIFO_CFG4_MC | \ |
| FIFO_CFG4_BC | FIFO_CFG4_DR | FIFO_CFG4_LE | \ |
| FIFO_CFG4_CF | FIFO_CFG4_PF | FIFO_CFG4_UO | \ |
| FIFO_CFG4_VT) |
| |
| #define FIFO_CFG5_INIT (FIFO_CFG5_DE | FIFO_CFG5_DV | FIFO_CFG5_FC | \ |
| FIFO_CFG5_CE | FIFO_CFG5_LO | FIFO_CFG5_OK | \ |
| FIFO_CFG5_MC | FIFO_CFG5_BC | FIFO_CFG5_DR | \ |
| FIFO_CFG5_CF | FIFO_CFG5_PF | FIFO_CFG5_VT | \ |
| FIFO_CFG5_LE | FIFO_CFG5_FT | FIFO_CFG5_16 | \ |
| FIFO_CFG5_17 | FIFO_CFG5_SF) |
| |
| static void ag71xx_hw_stop(struct ag71xx *ag) |
| { |
| /* disable all interrupts and stop the rx/tx engine */ |
| ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, 0); |
| ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); |
| ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); |
| } |
| |
| static void ag71xx_hw_setup(struct ag71xx *ag) |
| { |
| struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); |
| |
| /* setup MAC configuration registers */ |
| ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_INIT); |
| |
| ag71xx_sb(ag, AG71XX_REG_MAC_CFG2, |
| MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK); |
| |
| /* setup max frame length */ |
| if(ag->dev->mtu < AG71XX_TX_MTU_LEN) |
| ag71xx_wr(ag, AG71XX_REG_MAC_MFL, AG71XX_TX_MTU_LEN); |
| else |
| ag71xx_wr(ag, AG71XX_REG_MAC_MFL, ag->dev->mtu); |
| |
| /* setup FIFO configuration registers */ |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG0, FIFO_CFG0_INIT); |
| if (pdata->is_ar724x) { |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, pdata->fifo_cfg1); |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, pdata->fifo_cfg2); |
| } else { |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, 0x0fff0000); |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, 0x00001fff); |
| } |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG4, FIFO_CFG4_INIT); |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, FIFO_CFG5_INIT); |
| } |
| |
| static void ag71xx_hw_init(struct ag71xx *ag) |
| { |
| struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); |
| u32 reset_mask = pdata->reset_bit; |
| |
| ag71xx_hw_stop(ag); |
| |
| if (pdata->is_ar724x) { |
| u32 reset_phy = reset_mask; |
| |
| reset_phy &= AR71XX_RESET_GE0_PHY | AR71XX_RESET_GE1_PHY; |
| reset_mask &= ~(AR71XX_RESET_GE0_PHY | AR71XX_RESET_GE1_PHY); |
| |
| ath79_device_reset_set(reset_phy); |
| mdelay(50); |
| ath79_device_reset_clear(reset_phy); |
| mdelay(200); |
| } |
| |
| ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR); |
| udelay(20); |
| |
| ath79_device_reset_set(reset_mask); |
| mdelay(100); |
| ath79_device_reset_clear(reset_mask); |
| mdelay(200); |
| |
| ag71xx_hw_setup(ag); |
| |
| ag71xx_dma_reset(ag); |
| } |
| |
| static void ag71xx_fast_reset(struct ag71xx *ag) |
| { |
| struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); |
| struct net_device *dev = ag->dev; |
| u32 reset_mask = pdata->reset_bit; |
| u32 rx_ds, tx_ds; |
| u32 mii_reg; |
| |
| reset_mask &= AR71XX_RESET_GE0_MAC | AR71XX_RESET_GE1_MAC; |
| |
| mii_reg = ag71xx_rr(ag, AG71XX_REG_MII_CFG); |
| rx_ds = ag71xx_rr(ag, AG71XX_REG_RX_DESC); |
| tx_ds = ag71xx_rr(ag, AG71XX_REG_TX_DESC); |
| |
| ath79_device_reset_set(reset_mask); |
| udelay(10); |
| ath79_device_reset_clear(reset_mask); |
| udelay(10); |
| |
| ag71xx_dma_reset(ag); |
| ag71xx_hw_setup(ag); |
| |
| ag71xx_wr(ag, AG71XX_REG_RX_DESC, rx_ds); |
| ag71xx_wr(ag, AG71XX_REG_TX_DESC, tx_ds); |
| ag71xx_wr(ag, AG71XX_REG_MII_CFG, mii_reg); |
| |
| ag71xx_hw_set_macaddr(ag, dev->dev_addr); |
| } |
| |
| static void ag71xx_hw_start(struct ag71xx *ag) |
| { |
| /* start RX engine */ |
| ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); |
| |
| /* enable interrupts */ |
| ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, AG71XX_INT_INIT); |
| } |
| |
| static void ag71xx_disable_inline_chksum_engine(struct ag71xx *ag) |
| { |
| void __iomem *dam = ioremap_nocache(QCA956X_DAM_RESET_OFFSET, |
| QCA956X_DAM_RESET_SIZE); |
| if (!dam) { |
| dev_err(&ag->dev, "unable to ioremap DAM_RESET_OFFSET\n"); |
| } else { |
| /* |
| * can not use the wr, rr functions since this is outside of |
| * the normal ag71xx register block |
| */ |
| __raw_writel(__raw_readl(dam) & ~QCA956X_INLINE_CHKSUM_ENG, dam); |
| (void)__raw_readl(dam); |
| iounmap(dam); |
| } |
| } |
| |
| void ag71xx_link_adjust(struct ag71xx *ag) |
| { |
| struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); |
| u32 cfg2; |
| u32 ifctl; |
| u32 fifo5; |
| |
| if (!ag->link) { |
| ag71xx_hw_stop(ag); |
| netif_carrier_off(ag->dev); |
| if (netif_msg_link(ag)) |
| pr_info("%s: link down\n", ag->dev->name); |
| return; |
| } |
| |
| if (pdata->is_ar724x) |
| ag71xx_fast_reset(ag); |
| |
| cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2); |
| cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX); |
| cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0; |
| |
| ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL); |
| ifctl &= ~(MAC_IFCTL_SPEED); |
| |
| fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5); |
| fifo5 &= ~FIFO_CFG5_BM; |
| |
| switch (ag->speed) { |
| case SPEED_1000: |
| cfg2 |= MAC_CFG2_IF_1000; |
| fifo5 |= FIFO_CFG5_BM; |
| break; |
| case SPEED_100: |
| cfg2 |= MAC_CFG2_IF_10_100; |
| ifctl |= MAC_IFCTL_SPEED; |
| break; |
| case SPEED_10: |
| cfg2 |= MAC_CFG2_IF_10_100; |
| break; |
| default: |
| BUG(); |
| return; |
| } |
| |
| if (pdata->is_ar91xx) |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x00780fff); |
| else if (pdata->is_ar724x) |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, pdata->fifo_cfg3); |
| else |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x008001ff); |
| |
| if (pdata->set_speed) |
| pdata->set_speed(ag->speed); |
| |
| ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2); |
| ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5); |
| ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl); |
| ag71xx_hw_start(ag); |
| |
| if (pdata->is_qca956x) { |
| ag71xx_disable_inline_chksum_engine(ag); |
| } |
| |
| netif_carrier_on(ag->dev); |
| if (netif_msg_link(ag)) |
| pr_info("%s: link up (%sMbps/%s duplex)\n", |
| ag->dev->name, |
| ag71xx_speed_str(ag), |
| (DUPLEX_FULL == ag->duplex) ? "Full" : "Half"); |
| |
| DBG("%s: fifo_cfg0=%#x, fifo_cfg1=%#x, fifo_cfg2=%#x\n", |
| ag->dev->name, |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0), |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1), |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2)); |
| |
| DBG("%s: fifo_cfg3=%#x, fifo_cfg4=%#x, fifo_cfg5=%#x\n", |
| ag->dev->name, |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3), |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4), |
| ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5)); |
| |
| DBG("%s: mac_cfg2=%#x, mac_ifctl=%#x\n", |
| ag->dev->name, |
| ag71xx_rr(ag, AG71XX_REG_MAC_CFG2), |
| ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL)); |
| } |
| |
| static int ag71xx_open(struct net_device *dev) |
| { |
| struct ag71xx *ag = netdev_priv(dev); |
| int ret; |
| |
| /* |
| * Compute the RX buffer size. |
| */ |
| if (dev->mtu > AG71XX_TX_MTU_LEN) { |
| ag->rx_buf_size = dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN + NET_SKB_PAD + NET_IP_ALIGN; |
| } else { |
| ag->rx_buf_size = AG71XX_RX_BUF_SIZE; |
| } |
| |
| /* |
| * Compute the RX buffer offset. On AR71xx/AR91xx packets must be |
| * 4-byte aligned. |
| * |
| * When using builtin AR8216 support, hardware adds a 2-byte header, |
| * so we don't need any extra alignment in that case. |
| */ |
| if (!ag71xx_get_pdata(ag)->is_ar724x || ag71xx_has_ar8216(ag)) { |
| ag->rx_buf_offset = AG71XX_HACK_WIFI_HEADROOM; |
| } else { |
| ag->rx_buf_offset = AG71XX_HACK_WIFI_HEADROOM + NET_IP_ALIGN; |
| } |
| |
| ret = ag71xx_rings_init(ag); |
| if (ret) { |
| ag71xx_rings_cleanup(ag); |
| return ret; |
| } |
| |
| napi_enable(&ag->napi); |
| |
| netif_carrier_off(dev); |
| ag71xx_phy_start(ag); |
| |
| ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma); |
| ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma); |
| |
| ag71xx_hw_set_macaddr(ag, dev->dev_addr); |
| |
| netif_start_queue(dev); |
| ag->tx_stopped = false; |
| |
| return 0; |
| } |
| |
| static int ag71xx_stop(struct net_device *dev) |
| { |
| struct ag71xx *ag = netdev_priv(dev); |
| unsigned long flags; |
| |
| netif_carrier_off(dev); |
| ag71xx_phy_stop(ag); |
| |
| spin_lock_irqsave(&ag->lock, flags); |
| |
| ag->tx_stopped = true; |
| netif_stop_queue(dev); |
| |
| ag71xx_hw_stop(ag); |
| ag71xx_dma_reset(ag); |
| |
| napi_disable(&ag->napi); |
| |
| spin_unlock_irqrestore(&ag->lock, flags); |
| |
| ag71xx_rings_cleanup(ag); |
| |
| return 0; |
| } |
| |
| static netdev_tx_t ag71xx_hard_start_xmit(struct sk_buff *skb, |
| struct net_device *dev) |
| { |
| struct ag71xx *ag = netdev_priv(dev); |
| struct ag71xx_ring *ring = &ag->tx_ring; |
| struct ag71xx_buf *curr = ring->curr; |
| struct ag71xx_desc *desc = curr->desc; |
| unsigned int used = ring->used; |
| unsigned int size = ring->size; |
| unsigned int len; |
| dma_addr_t dma_addr; |
| |
| /* |
| * We shouldn't ever see our ring fully used and reach here but just in case! |
| */ |
| if (unlikely(used == size)) { |
| DBG("%s: tx queue full\n", dev->name); |
| ag->tx_stopped = true; |
| netif_stop_queue(dev); |
| goto err_drop; |
| } |
| |
| if (unlikely(ag71xx_has_ar8216(ag))) { |
| ag71xx_add_ar8216_header(ag, skb); |
| } |
| |
| len = skb->len; |
| if (unlikely(len <= 0)) { |
| DBG("%s: packet len is too small\n", dev->name); |
| goto err_drop; |
| } |
| |
| netdev_sent_queue(dev, len); |
| curr->skb = skb; |
| curr->len = len; |
| |
| dma_addr = dma_map_single(&dev->dev, skb->data, len, DMA_TO_DEVICE); |
| |
| /* setup descriptor fields */ |
| desc->data = (u32)dma_addr; |
| desc->ctrl = len & DESC_PKTLEN_M; |
| |
| curr = curr->next; |
| ring->curr = curr; |
| |
| used++; |
| ring->used = used; |
| |
| /* |
| * If our transmit ring is full then stop transmitting. |
| */ |
| if (unlikely(used == size)) { |
| DBG("%s: tx queue full\n", ag->dev->name); |
| ag->tx_stopped = true; |
| netif_stop_queue(dev); |
| } |
| |
| DBG("%s: packet injected into TX queue\n", ag->dev->name); |
| |
| dev->trans_start = jiffies; |
| |
| /* enable TX engine */ |
| ag71xx_wr_fast(ag->tx_ctrl_reg, TX_CTRL_TXE); |
| ag71xx_wr_flush(ag->tx_ctrl_reg); |
| |
| return NETDEV_TX_OK; |
| |
| err_drop: |
| dev->stats.tx_dropped++; |
| |
| dev_kfree_skb(skb); |
| return NETDEV_TX_OK; |
| } |
| |
| static int ag71xx_do_ioctl_switchport(struct net_device *dev, struct ifreq *ifr, |
| int cmd) |
| { |
| struct ag71xx *ag = netdev_priv(dev); |
| struct net_device *netdev = ag->dev; |
| struct mii_ioctl_data *mii_data = if_mii(ifr); |
| |
| switch (cmd) { |
| case SIOCGMIIPHY: |
| mii_data->phy_id = netdev->dev_id; |
| /* fall through */ |
| |
| case SIOCGMIIREG: |
| mii_data->val_out = ag->mii_bus->read(ag->mii_bus, netdev->dev_id, |
| mii_data->reg_num); |
| if (mii_data->val_out == 0xffff) |
| return -ENODEV; |
| |
| break; |
| case SIOCSMIIREG: |
| ag->mii_bus->write(ag->mii_bus, netdev->dev_id, mii_data->reg_num, |
| mii_data->val_in); |
| break; |
| default: |
| return -EOPNOTSUPP; |
| } |
| |
| return 0; |
| } |
| |
| static int ag71xx_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
| { |
| struct ag71xx *ag = netdev_priv(dev); |
| int ret; |
| |
| switch (cmd) { |
| case SIOCETHTOOL: |
| if (ag->phy_dev == NULL) |
| break; |
| |
| spin_lock_irq(&ag->lock); |
| ret = phy_ethtool_ioctl(ag->phy_dev, (void *) ifr->ifr_data); |
| spin_unlock_irq(&ag->lock); |
| return ret; |
| |
| case SIOCSIFHWADDR: |
| if (copy_from_user |
| (dev->dev_addr, ifr->ifr_data, sizeof(dev->dev_addr))) |
| return -EFAULT; |
| return 0; |
| |
| case SIOCGIFHWADDR: |
| if (copy_to_user |
| (ifr->ifr_data, dev->dev_addr, sizeof(dev->dev_addr))) |
| return -EFAULT; |
| return 0; |
| |
| case SIOCGMIIPHY: |
| case SIOCGMIIREG: |
| case SIOCSMIIREG: |
| if (ag->phy_dev == NULL) |
| return ag71xx_do_ioctl_switchport(dev, ifr, cmd); |
| else |
| return phy_mii_ioctl(ag->phy_dev, ifr, cmd); |
| |
| default: |
| break; |
| } |
| |
| return -EOPNOTSUPP; |
| } |
| |
| static void ag71xx_tx_timeout(struct net_device *dev) |
| { |
| struct ag71xx *ag = netdev_priv(dev); |
| |
| if (netif_msg_tx_err(ag)) |
| pr_info("%s: tx timeout\n", ag->dev->name); |
| |
| schedule_work(&ag->restart_work); |
| } |
| |
| static void ag71xx_restart_work_func(struct work_struct *work) |
| { |
| struct ag71xx *ag = container_of(work, struct ag71xx, restart_work); |
| |
| if (ag71xx_get_pdata(ag)->is_ar724x) { |
| ag->link = 0; |
| ag71xx_link_adjust(ag); |
| return; |
| } |
| |
| ag71xx_stop(ag->dev); |
| ag71xx_open(ag->dev); |
| } |
| |
| static bool ag71xx_check_dma_stuck(struct ag71xx *ag, struct net_device *dev) |
| { |
| u32 rx_sm, tx_sm, rx_fd; |
| |
| if (likely(time_before(jiffies, dev->trans_start + HZ / 10))) |
| return false; |
| |
| if (!netif_carrier_ok(dev)) |
| return false; |
| |
| rx_sm = ag71xx_rr(ag, AG71XX_REG_RX_SM); |
| if ((rx_sm & 0x7) == 0x3 && ((rx_sm >> 4) & 0x7) == 0x6) |
| return true; |
| |
| tx_sm = ag71xx_rr(ag, AG71XX_REG_TX_SM); |
| rx_fd = ag71xx_rr(ag, AG71XX_REG_FIFO_DEPTH); |
| if (((tx_sm >> 4) & 0x7) == 0 && ((rx_sm & 0x7) == 0) && |
| ((rx_sm >> 4) & 0x7) == 0 && rx_fd == 0) |
| return true; |
| |
| return false; |
| } |
| |
| static int ag71xx_tx_packets(struct ag71xx *ag, struct net_device *dev, |
| bool is_ar7240) |
| { |
| struct ag71xx_ring *ring = &ag->tx_ring; |
| unsigned int sent = 0; |
| unsigned int bytes_compl = 0; |
| struct ag71xx_buf *dirty = ring->dirty; |
| struct ag71xx_desc *desc; |
| unsigned int used = ring->used; |
| struct sk_buff *skb; |
| |
| DBG("%s: processing TX ring\n", dev->name); |
| |
| /* |
| * If we haven't transmitted anything then we're done! |
| */ |
| if (!used) |
| return sent; |
| |
| /* |
| * Start by looking at the SKB that will be up next. |
| */ |
| skb = dirty->skb; |
| desc = dirty->desc; |
| |
| do { |
| struct sk_buff *next_skb; |
| |
| if (unlikely(!(desc->ctrl & DESC_EMPTY))) { |
| if (is_ar7240) { |
| if (unlikely(ag71xx_check_dma_stuck(ag, dev))) { |
| schedule_work(&ag->restart_work); |
| } |
| } |
| break; |
| } |
| |
| sent++; |
| bytes_compl += dirty->len; |
| |
| dirty->skb = NULL; |
| dirty = dirty->next; |
| next_skb = dirty->skb; |
| desc = dirty->desc; |
| |
| /* |
| * There's a good chance that the next SKB may be cold in |
| * the cache so try to give some help. |
| */ |
| if (likely(next_skb)) { |
| prefetch(skb_shinfo(next_skb)); |
| prefetch(&next_skb->users); |
| } |
| |
| ag71xx_wr_fast(ag->tx_status_reg, TX_STATUS_PS); |
| |
| dev_kfree_skb(skb); |
| |
| skb = next_skb; |
| |
| used--; |
| } while (used); |
| |
| ag71xx_wr_flush(ag->tx_status_reg); |
| |
| ring->dirty = dirty; |
| ring->used = used; |
| |
| dev->stats.tx_bytes += bytes_compl; |
| dev->stats.tx_packets += sent; |
| |
| DBG("%s: %u packets sent out\n", dev->name, sent); |
| |
| /* |
| * Mark the amount of work we've done. |
| */ |
| netdev_completed_queue(dev, sent, bytes_compl); |
| |
| /* |
| * If our transmit queue was previously stopped because we'd run out |
| * of space and we've now successfully freed some space then restart |
| * the transmit queue again. |
| */ |
| if (unlikely(ag->tx_stopped) && sent) { |
| netif_wake_queue(dev); |
| ag->tx_stopped = false; |
| } |
| |
| return sent; |
| } |
| |
| static int ag71xx_rx_packets(struct ag71xx *ag, struct net_device *dev, int limit) |
| { |
| struct ag71xx_ring *ring = &ag->rx_ring; |
| struct ag71xx_buf *curr = ring->curr; |
| struct ag71xx_desc *desc = curr->desc; |
| unsigned int rx_buf_size = ag->rx_buf_size; |
| unsigned int rx_buf_offset = ag->rx_buf_offset; |
| int received = 0; |
| struct sk_buff *skb; |
| bool has_ar8216; |
| |
| has_ar8216 = ag71xx_has_ar8216(ag); |
| |
| /* |
| * Start by looking at the SKB that will be up next. |
| */ |
| skb = curr->skb; |
| |
| /* |
| * Process newly received packets. |
| */ |
| do { |
| u32 desc_ctrl; |
| struct sk_buff *next_skb; |
| struct sk_buff *new_skb; |
| int pktlen; |
| |
| /* |
| * Is our descriptor marked as empty? If it is then we're done. |
| */ |
| desc_ctrl = desc->ctrl; |
| if (unlikely(desc_ctrl & DESC_EMPTY)) { |
| break; |
| } |
| |
| /* |
| * Speed up eth_type_trans() since it will inspect the packet |
| * payload and write the protocol. Strictly speaking this is a |
| * little premature as the next SKB alloc could fail but in |
| * practice it never will so this is good :-) |
| */ |
| prefetch(skb->data); |
| prefetch(&skb->protocol); |
| |
| /* |
| * When we receive a packet we also allocate a new buffer. If |
| * for some reason we can't allocate the buffer then we're not |
| * going to try to process the received buffer yet either. |
| */ |
| new_skb = dev_alloc_skb(rx_buf_size + rx_buf_offset); |
| if (unlikely(!new_skb)) { |
| break; |
| } |
| |
| skb_reserve(new_skb, rx_buf_offset); |
| |
| /* |
| * This is where we'd unmap our buffer from the GMAC in a |
| * general use of the DMA API. On a MIPS platform this would |
| * be a complete no-op so we don't bother: |
| * |
| * dma_unmap_single(&dev->dev, curr->dma_addr, |
| * rx_buf_size, DMA_FROM_DEVICE); |
| */ |
| |
| /* |
| * Update the descriptor records to account for the new SKB. |
| */ |
| curr->skb = new_skb; |
| curr->dma_addr = dma_map_single(&dev->dev, new_skb->data, |
| rx_buf_size, DMA_FROM_DEVICE); |
| |
| desc->data = (u32)curr->dma_addr; |
| desc->ctrl = DESC_EMPTY; |
| |
| /* |
| * Move forward to what will be the next RX descriptor. |
| */ |
| curr = curr->next; |
| next_skb = curr->skb; |
| desc = curr->desc; |
| |
| /* |
| * Our next skb is almost certainly cold in the cache as we last |
| * saw it when we replenished this slot. We'll take cache |
| * misses on almost every access. Try to mitigate this by |
| * issuing some prefetches. |
| * |
| * Note that what we're prefetching here are the fields that |
| * we'll need within the next iteration of this function. |
| */ |
| if (likely(next_skb)) { |
| /* |
| * For a MIPS platform we shouldn't issue more than 3 |
| * prefetches at a time. |
| */ |
| prefetch(&next_skb->data); |
| prefetch(&next_skb->tail); |
| prefetch(&next_skb->len); |
| } |
| |
| /* |
| * Notify the GMAC that we received the packet. |
| */ |
| ag71xx_wr_fast(ag->rx_status_reg, RX_STATUS_PR); |
| |
| /* |
| * Determine the size of the packet we just received. |
| */ |
| pktlen = desc_ctrl & DESC_PKTLEN_M; |
| pktlen -= ETH_FCS_LEN; |
| |
| /* |
| * Update device stats. |
| */ |
| dev->stats.rx_packets++; |
| dev->stats.rx_bytes += pktlen; |
| |
| /* |
| * Set up the length of the skb. |
| */ |
| skb->tail += pktlen; |
| skb->len += pktlen; |
| |
| if (unlikely(has_ar8216)) { |
| int err = ag71xx_remove_ar8216_header(ag, skb, pktlen); |
| if (err) { |
| dev->stats.rx_dropped++; |
| dev_kfree_skb(skb); |
| goto next; |
| } |
| } |
| |
| skb->protocol = eth_type_trans(skb, dev); |
| skb_checksum_none_assert(skb); |
| netif_receive_skb(skb); |
| |
| next: |
| skb = next_skb; |
| received++; |
| } while (received < limit); |
| |
| ag71xx_wr_flush(ag->rx_status_reg); |
| |
| ring->curr = curr; |
| return received; |
| } |
| |
| static int ag71xx_poll(struct napi_struct *napi, int limit) |
| { |
| struct ag71xx *ag = container_of(napi, struct ag71xx, napi); |
| struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); |
| struct net_device *dev = ag->dev; |
| unsigned long flags; |
| u32 status; |
| int tx_done; |
| int rx_done; |
| |
| pdata->ddr_flush(); |
| |
| /* |
| * First empty any packets that we have transmitted! In theory it might |
| * seem better to handle packets that we've received but we |
| * really want to get packets that completed TX to be used to replenish |
| * the RX descriptor ring and keeping those two operations adjacent |
| * will help keep any recycled skbs hotter in the D-cache. |
| */ |
| tx_done = ag71xx_tx_packets(ag, dev, pdata->is_ar7240); |
| rx_done = ag71xx_rx_packets(ag, dev, limit); |
| |
| ag71xx_debugfs_update_napi_stats(ag, rx_done, tx_done); |
| |
| status = ag71xx_rr_fast(ag->rx_status_reg); |
| if (unlikely(status & RX_STATUS_OF)) { |
| ag71xx_wr_fast(ag->rx_status_reg, RX_STATUS_OF); |
| ag71xx_wr_flush(ag->rx_status_reg); |
| dev->stats.rx_fifo_errors++; |
| |
| /* restart RX */ |
| ag71xx_wr_fast(ag->rx_ctrl_reg, RX_CTRL_RXE); |
| ag71xx_wr_flush(ag->rx_ctrl_reg); |
| } |
| |
| if (rx_done < limit) { |
| DBG("%s: disable polling mode, rx=%d, tx=%d,limit=%d\n", |
| dev->name, rx_done, tx_done, limit); |
| |
| napi_complete(napi); |
| |
| /* enable interrupts */ |
| spin_lock_irqsave(&ag->lock, flags); |
| ag71xx_int_enable(ag, AG71XX_INT_POLL); |
| spin_unlock_irqrestore(&ag->lock, flags); |
| return rx_done; |
| } |
| |
| more: |
| DBG("%s: stay in polling mode, rx=%d, tx=%d, limit=%d\n", |
| dev->name, rx_done, tx_done, limit); |
| return rx_done; |
| } |
| |
| static irqreturn_t ag71xx_interrupt(int irq, void *dev_id) |
| { |
| struct net_device *dev = dev_id; |
| struct ag71xx *ag = netdev_priv(dev); |
| u32 status; |
| |
| status = ag71xx_rr_fast(ag->int_status_reg); |
| ag71xx_dump_intr(ag, "raw", status); |
| |
| if (unlikely(!status)) |
| return IRQ_NONE; |
| |
| if (unlikely(status & AG71XX_INT_ERR)) { |
| if (status & AG71XX_INT_TX_BE) { |
| ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE); |
| dev_err(&dev->dev, "TX BUS error\n"); |
| } |
| if (status & AG71XX_INT_RX_BE) { |
| ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE); |
| dev_err(&dev->dev, "RX BUS error\n"); |
| } |
| } |
| |
| if (likely(status & AG71XX_INT_POLL)) { |
| ag71xx_int_disable(ag, AG71XX_INT_POLL); |
| DBG("%s: enable polling mode\n", dev->name); |
| napi_schedule(&ag->napi); |
| } |
| |
| ag71xx_debugfs_update_int_stats(ag, status); |
| |
| return IRQ_HANDLED; |
| } |
| |
| #ifdef CONFIG_NET_POLL_CONTROLLER |
| /* |
| * Polling 'interrupt' - used by things like netconsole to send skbs |
| * without having to re-enable interrupts. It's not called while |
| * the interrupt routine is executing. |
| */ |
| static void ag71xx_netpoll(struct net_device *dev) |
| { |
| disable_irq(dev->irq); |
| ag71xx_interrupt(dev->irq, dev); |
| enable_irq(dev->irq); |
| } |
| #endif |
| static int ag71xx_change_mtu(struct net_device *dev, int new_mtu) |
| { |
| int ret; |
| |
| if (new_mtu < 68 || new_mtu > AG71XX_JUMBO_LEN) |
| return -EINVAL; |
| |
| if (!netif_running(dev)) { |
| dev->mtu = new_mtu; |
| return 0; |
| } |
| |
| ag71xx_stop(dev); |
| printk("%s:%s new_mtu is %d\n",__func__,dev->name,new_mtu); |
| |
| dev->mtu = new_mtu; |
| |
| ret = ag71xx_open(dev); |
| if (ret) |
| dev_close(dev); |
| |
| return ret; |
| } |
| |
| static const struct net_device_ops ag71xx_netdev_ops = { |
| .ndo_open = ag71xx_open, |
| .ndo_stop = ag71xx_stop, |
| .ndo_start_xmit = ag71xx_hard_start_xmit, |
| .ndo_do_ioctl = ag71xx_do_ioctl, |
| .ndo_tx_timeout = ag71xx_tx_timeout, |
| .ndo_change_mtu = ag71xx_change_mtu, |
| .ndo_set_mac_address = eth_mac_addr, |
| .ndo_validate_addr = eth_validate_addr, |
| #ifdef CONFIG_NET_POLL_CONTROLLER |
| .ndo_poll_controller = ag71xx_netpoll, |
| #endif |
| }; |
| |
| #ifdef CONFIG_OF |
| static void ag71xx_of_gmac_setup(struct platform_device *pdev, u32 mask) |
| { |
| struct resource *res; |
| void __iomem *cfg_base; |
| |
| if (!(res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base"))) |
| return; |
| |
| cfg_base = ioremap_nocache(res->start, res->end - res->start + 1); |
| if (!cfg_base) { |
| dev_err(&pdev->dev, "unable to ioremap cfg_base\n"); |
| return; |
| } |
| |
| __raw_writel(__raw_readl(cfg_base) | mask, cfg_base); |
| /* flush write */ |
| (void)__raw_readl(cfg_base); |
| |
| iounmap(cfg_base); |
| } |
| |
| static int ag71xx_of_pdata_update(struct platform_device *pdev) |
| { |
| u32 value[4]; |
| const phandle *ph; |
| struct device_node *mdio; |
| struct platform_device *pdev_mdio; |
| struct ag71xx_platform_data *pdata = pdev->dev.platform_data; |
| |
| if (!pdev->dev.of_node) |
| return -EINVAL; |
| |
| ph = of_get_property(pdev->dev.of_node, "mdio-handle", NULL); |
| if (!ph) { |
| dev_err(&pdev->dev, "No mdio-handle in dtb\n"); |
| return -EINVAL; |
| } |
| |
| mdio = of_find_node_by_phandle(*ph); |
| if (!mdio) { |
| dev_err(&pdev->dev, "No mdio device found by phandle\n"); |
| return -EINVAL; |
| } |
| |
| pdev_mdio = of_find_device_by_node(mdio); |
| pdata->mii_bus_dev = &pdev_mdio->dev; |
| of_node_put(mdio); |
| |
| if (!of_property_read_u32(pdev->dev.of_node, "eth-cfg", &value[0])) |
| ag71xx_of_gmac_setup(pdev, value[0]); |
| |
| if (pdata->update_pll && |
| !of_property_read_u32_array(pdev->dev.of_node, "eth-pll-data", value, 3)) |
| pdata->update_pll(value[0], value[1], value[2]); |
| |
| if (!of_property_read_u32_array(pdev->dev.of_node, "eth-phy-cfg", value, 4)) { |
| pdata->phy_if_mode = value[0]; |
| pdata->phy_mask = value[1]; |
| pdata->speed = value[2]; |
| pdata->duplex = value[3]; |
| } |
| |
| if (!of_property_read_u32_array(pdev->dev.of_node, "eth-fifo-cfg", value, 3)) { |
| pdata->fifo_cfg1 = value[0]; |
| pdata->fifo_cfg2 = value[1]; |
| pdata->fifo_cfg3 = value[2]; |
| } |
| |
| if (pdata->switch_data && |
| !of_property_read_u32_array(pdev->dev.of_node, "eth-sw-cfg", value, 2)) { |
| pdata->switch_data->phy4_mii_en = value[0]; |
| pdata->switch_data->phy_poll_mask = value[1]; |
| } |
| |
| return 0; |
| } |
| #else |
| static int ag71xx_of_pdata_update(struct platform_device *pdev) |
| { |
| return -EINVAL; |
| } |
| #endif |
| |
| static int __devinit ag71xx_probe(struct platform_device *pdev) |
| { |
| struct net_device *dev; |
| struct resource *res; |
| struct ag71xx *ag; |
| struct ag71xx_desc *ag_stop_desc; |
| struct ag71xx_platform_data *pdata; |
| int err; |
| |
| pdata = pdev->dev.platform_data; |
| if (!pdata) { |
| dev_err(&pdev->dev, "no platform data specified\n"); |
| err = -ENXIO; |
| goto err_out; |
| } |
| |
| if (pdata->mii_bus_dev == NULL && |
| ag71xx_of_pdata_update(pdev)) { |
| dev_err(&pdev->dev, "no MII bus device specified\n"); |
| err = -EINVAL; |
| goto err_out; |
| } |
| |
| dev = alloc_etherdev(sizeof(*ag)); |
| if (!dev) { |
| dev_err(&pdev->dev, "alloc_etherdev failed\n"); |
| err = -ENOMEM; |
| goto err_out; |
| } |
| |
| SET_NETDEV_DEV(dev, &pdev->dev); |
| |
| ag = netdev_priv(dev); |
| ag->pdev = pdev; |
| ag->dev = dev; |
| ag->msg_enable = netif_msg_init(ag71xx_msg_level, |
| AG71XX_DEFAULT_MSG_ENABLE); |
| ag->gmac_num = ag71xx_gmac_num++; |
| spin_lock_init(&ag->lock); |
| |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac_base"); |
| if (!res) { |
| dev_err(&pdev->dev, "no mac_base resource found\n"); |
| err = -ENXIO; |
| goto err_out; |
| } |
| |
| ag->mac_base = ioremap_nocache(res->start, res->end - res->start + 1); |
| if (!ag->mac_base) { |
| dev_err(&pdev->dev, "unable to ioremap mac_base\n"); |
| err = -ENOMEM; |
| goto err_free_dev; |
| } |
| |
| ag->rx_ctrl_reg = ag->mac_base + AG71XX_REG_RX_CTRL; |
| ag->rx_status_reg = ag->mac_base + AG71XX_REG_RX_STATUS; |
| ag->tx_ctrl_reg = ag->mac_base + AG71XX_REG_TX_CTRL; |
| ag->tx_status_reg = ag->mac_base + AG71XX_REG_TX_STATUS; |
| ag->int_status_reg = ag->mac_base + AG71XX_REG_INT_STATUS; |
| |
| dev->irq = platform_get_irq(pdev, 0); |
| err = request_irq(dev->irq, ag71xx_interrupt, |
| IRQF_DISABLED, |
| dev->name, dev); |
| if (err) { |
| dev_err(&pdev->dev, "unable to request IRQ %d\n", dev->irq); |
| goto err_unmap_base; |
| } |
| |
| dev->base_addr = (unsigned long)ag->mac_base; |
| dev->netdev_ops = &ag71xx_netdev_ops; |
| dev->ethtool_ops = &ag71xx_ethtool_ops; |
| |
| INIT_WORK(&ag->restart_work, ag71xx_restart_work_func); |
| |
| ag->tx_ring.size = AG71XX_TX_RING_SIZE_DEFAULT; |
| ag->tx_ring.mask = AG71XX_TX_RING_SIZE_DEFAULT - 1; |
| ag->rx_ring.size = AG71XX_RX_RING_SIZE_DEFAULT; |
| ag->rx_ring.mask = AG71XX_RX_RING_SIZE_DEFAULT - 1; |
| |
| ag_stop_desc = dma_alloc_coherent(NULL, |
| sizeof(struct ag71xx_desc), &ag->stop_desc_dma, GFP_KERNEL); |
| |
| if (!ag_stop_desc) |
| goto err_free_irq; |
| |
| ag_stop_desc->data = 0; |
| ag_stop_desc->ctrl = 0; |
| ag_stop_desc->next = (u32)ag->stop_desc_dma; |
| ag->stop_desc = ag_stop_desc; |
| |
| memcpy(dev->dev_addr, pdata->mac_addr, ETH_ALEN); |
| |
| netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT); |
| |
| err = register_netdev(dev); |
| if (err) { |
| dev_err(&pdev->dev, "unable to register net device\n"); |
| goto err_free_desc; |
| } |
| |
| pr_info("%s: Atheros AG71xx at 0x%08lx, irq %d\n", |
| dev->name, dev->base_addr, dev->irq); |
| |
| ag71xx_dump_regs(ag); |
| |
| ag71xx_hw_init(ag); |
| |
| ag71xx_dump_regs(ag); |
| |
| err = ag71xx_phy_connect(ag); |
| if (err) |
| goto err_unregister_netdev; |
| |
| err = ag71xx_debugfs_init(ag); |
| if (err) |
| goto err_phy_disconnect; |
| |
| platform_set_drvdata(pdev, dev); |
| |
| return 0; |
| |
| err_phy_disconnect: |
| ag71xx_phy_disconnect(ag); |
| err_unregister_netdev: |
| unregister_netdev(dev); |
| err_free_desc: |
| dma_free_coherent(NULL, sizeof(struct ag71xx_desc), ag->stop_desc, |
| ag->stop_desc_dma); |
| err_free_irq: |
| free_irq(dev->irq, dev); |
| err_unmap_base: |
| iounmap(ag->mac_base); |
| err_free_dev: |
| kfree(dev); |
| err_out: |
| platform_set_drvdata(pdev, NULL); |
| return err; |
| } |
| |
| static int __devexit ag71xx_remove(struct platform_device *pdev) |
| { |
| struct net_device *dev = platform_get_drvdata(pdev); |
| |
| if (dev) { |
| struct ag71xx *ag = netdev_priv(dev); |
| |
| ag71xx_debugfs_exit(ag); |
| ag71xx_phy_disconnect(ag); |
| unregister_netdev(dev); |
| free_irq(dev->irq, dev); |
| iounmap(ag->mac_base); |
| kfree(dev); |
| platform_set_drvdata(pdev, NULL); |
| } |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_OF |
| static const struct of_device_id ag71xx_of_match_table[] = { |
| {.compatible = "qcom,ag71xx-eth"}, |
| {} |
| }; |
| #else |
| #define ag71xx_of_match_table NULL |
| #endif |
| |
| static struct platform_driver ag71xx_driver = { |
| .probe = ag71xx_probe, |
| .remove = __exit_p(ag71xx_remove), |
| .driver = { |
| .name = AG71XX_DRV_NAME, |
| .of_match_table = ag71xx_of_match_table, |
| } |
| }; |
| |
| static int __init ag71xx_module_init(void) |
| { |
| int ret; |
| |
| ret = ag71xx_debugfs_root_init(); |
| if (ret) |
| goto err_out; |
| |
| ret = ag71xx_mdio_driver_init(); |
| if (ret) |
| goto err_debugfs_exit; |
| |
| ret = platform_driver_register(&ag71xx_driver); |
| if (ret) |
| goto err_mdio_exit; |
| |
| return 0; |
| |
| err_mdio_exit: |
| ag71xx_mdio_driver_exit(); |
| err_debugfs_exit: |
| ag71xx_debugfs_root_exit(); |
| err_out: |
| return ret; |
| } |
| |
| static void __exit ag71xx_module_exit(void) |
| { |
| platform_driver_unregister(&ag71xx_driver); |
| ag71xx_mdio_driver_exit(); |
| ag71xx_debugfs_root_exit(); |
| } |
| |
| module_init(ag71xx_module_init); |
| module_exit(ag71xx_module_exit); |
| |
| MODULE_VERSION(AG71XX_DRV_VERSION); |
| MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); |
| MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>"); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_ALIAS("platform:" AG71XX_DRV_NAME); |