diff options
Diffstat (limited to 'drivers/net/pasemi_mac.c')
-rw-r--r-- | drivers/net/pasemi_mac.c | 259 |
1 files changed, 187 insertions, 72 deletions
diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index bb88a41b7591..2e39e0285d8f 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c @@ -62,6 +62,10 @@ #define LRO_MAX_AGGR 64 +#define PE_MIN_MTU 64 +#define PE_MAX_MTU 1500 +#define PE_DEF_MTU ETH_DATA_LEN + #define DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -82,8 +86,6 @@ & ((ring)->size - 1)) #define RING_AVAIL(ring) ((ring->size) - RING_USED(ring)) -#define BUF_SIZE 1646 /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ - MODULE_LICENSE("GPL"); MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>"); MODULE_DESCRIPTION("PA Semi PWRficient Ethernet driver"); @@ -175,6 +177,24 @@ static int mac_to_intf(struct pasemi_mac *mac) return -1; } +static void pasemi_mac_intf_disable(struct pasemi_mac *mac) +{ + unsigned int flags; + + flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); + flags &= ~PAS_MAC_CFG_PCFG_PE; + write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); +} + +static void pasemi_mac_intf_enable(struct pasemi_mac *mac) +{ + unsigned int flags; + + flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); + flags |= PAS_MAC_CFG_PCFG_PE; + write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); +} + static int pasemi_get_mac_addr(struct pasemi_mac *mac) { struct pci_dev *pdev = mac->pdev; @@ -221,6 +241,33 @@ static int pasemi_get_mac_addr(struct pasemi_mac *mac) return 0; } +static int pasemi_mac_set_mac_addr(struct net_device *dev, void *p) +{ + struct pasemi_mac *mac = netdev_priv(dev); + struct sockaddr *addr = p; + unsigned int adr0, adr1; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EINVAL; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + adr0 = dev->dev_addr[2] << 24 | + dev->dev_addr[3] << 16 | + dev->dev_addr[4] << 8 | + dev->dev_addr[5]; + adr1 = read_mac_reg(mac, PAS_MAC_CFG_ADR1); + adr1 &= ~0xffff; + adr1 |= dev->dev_addr[0] << 8 | dev->dev_addr[1]; + + pasemi_mac_intf_disable(mac); + write_mac_reg(mac, PAS_MAC_CFG_ADR0, adr0); + write_mac_reg(mac, PAS_MAC_CFG_ADR1, adr1); + pasemi_mac_intf_enable(mac); + + return 0; +} + static int get_skb_hdr(struct sk_buff *skb, void **iphdr, void **tcph, u64 *hdr_flags, void *data) { @@ -453,7 +500,7 @@ static void pasemi_mac_free_tx_resources(struct pasemi_mac *mac) } -static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac) +static void pasemi_mac_free_rx_buffers(struct pasemi_mac *mac) { struct pasemi_mac_rxring *rx = rx_ring(mac); unsigned int i; @@ -473,7 +520,12 @@ static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac) } for (i = 0; i < RX_RING_SIZE; i++) - RX_DESC(rx, i) = 0; + RX_BUFF(rx, i) = 0; +} + +static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac) +{ + pasemi_mac_free_rx_buffers(mac); dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64), rx_ring(mac)->buffers, rx_ring(mac)->buf_dma); @@ -503,14 +555,14 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev, /* Entry in use? */ WARN_ON(*buff); - skb = dev_alloc_skb(BUF_SIZE); + skb = dev_alloc_skb(mac->bufsz); skb_reserve(skb, LOCAL_SKB_ALIGN); if (unlikely(!skb)) break; dma = pci_map_single(mac->dma_pdev, skb->data, - BUF_SIZE - LOCAL_SKB_ALIGN, + mac->bufsz - LOCAL_SKB_ALIGN, PCI_DMA_FROMDEVICE); if (unlikely(dma_mapping_error(dma))) { @@ -520,7 +572,7 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev, info->skb = skb; info->dma = dma; - *buff = XCT_RXB_LEN(BUF_SIZE) | XCT_RXB_ADDR(dma); + *buff = XCT_RXB_LEN(mac->bufsz) | XCT_RXB_ADDR(dma); fill++; } @@ -650,7 +702,7 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx, len = (macrx & XCT_MACRX_LLEN_M) >> XCT_MACRX_LLEN_S; - pci_unmap_single(pdev, dma, BUF_SIZE-LOCAL_SKB_ALIGN, + pci_unmap_single(pdev, dma, mac->bufsz - LOCAL_SKB_ALIGN, PCI_DMA_FROMDEVICE); if (macrx & XCT_MACRX_CRC) { @@ -874,24 +926,6 @@ static irqreturn_t pasemi_mac_tx_intr(int irq, void *data) return IRQ_HANDLED; } -static void pasemi_mac_intf_disable(struct pasemi_mac *mac) -{ - unsigned int flags; - - flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); - flags &= ~PAS_MAC_CFG_PCFG_PE; - write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); -} - -static void pasemi_mac_intf_enable(struct pasemi_mac *mac) -{ - unsigned int flags; - - flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); - flags |= PAS_MAC_CFG_PCFG_PE; - write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); -} - static void pasemi_adjust_link(struct net_device *dev) { struct pasemi_mac *mac = netdev_priv(dev); @@ -1148,11 +1182,71 @@ out_rx_resources: #define MAX_RETRIES 5000 +static void pasemi_mac_pause_txchan(struct pasemi_mac *mac) +{ + unsigned int sta, retries; + int txch = tx_ring(mac)->chan.chno; + + write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), + PAS_DMA_TXCHAN_TCMDSTA_ST); + + for (retries = 0; retries < MAX_RETRIES; retries++) { + sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch)); + if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)) + break; + cond_resched(); + } + + if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT) + dev_err(&mac->dma_pdev->dev, + "Failed to stop tx channel, tcmdsta %08x\n", sta); + + write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0); +} + +static void pasemi_mac_pause_rxchan(struct pasemi_mac *mac) +{ + unsigned int sta, retries; + int rxch = rx_ring(mac)->chan.chno; + + write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), + PAS_DMA_RXCHAN_CCMDSTA_ST); + for (retries = 0; retries < MAX_RETRIES; retries++) { + sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch)); + if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)) + break; + cond_resched(); + } + + if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT) + dev_err(&mac->dma_pdev->dev, + "Failed to stop rx channel, ccmdsta 08%x\n", sta); + write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0); +} + +static void pasemi_mac_pause_rxint(struct pasemi_mac *mac) +{ + unsigned int sta, retries; + + write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), + PAS_DMA_RXINT_RCMDSTA_ST); + for (retries = 0; retries < MAX_RETRIES; retries++) { + sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); + if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT)) + break; + cond_resched(); + } + + if (sta & PAS_DMA_RXINT_RCMDSTA_ACT) + dev_err(&mac->dma_pdev->dev, + "Failed to stop rx interface, rcmdsta %08x\n", sta); + write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0); +} + static int pasemi_mac_close(struct net_device *dev) { struct pasemi_mac *mac = netdev_priv(dev); unsigned int sta; - int retries; int rxch, txch; rxch = rx_ring(mac)->chan.chno; @@ -1190,51 +1284,10 @@ static int pasemi_mac_close(struct net_device *dev) pasemi_mac_clean_tx(tx_ring(mac)); pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE); - /* Disable interface */ - write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), - PAS_DMA_TXCHAN_TCMDSTA_ST); - write_dma_reg( PAS_DMA_RXINT_RCMDSTA(mac->dma_if), - PAS_DMA_RXINT_RCMDSTA_ST); - write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), - PAS_DMA_RXCHAN_CCMDSTA_ST); - - for (retries = 0; retries < MAX_RETRIES; retries++) { - sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(rxch)); - if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)) - break; - cond_resched(); - } - - if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT) - dev_err(&mac->dma_pdev->dev, "Failed to stop tx channel\n"); - - for (retries = 0; retries < MAX_RETRIES; retries++) { - sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch)); - if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)) - break; - cond_resched(); - } - - if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT) - dev_err(&mac->dma_pdev->dev, "Failed to stop rx channel\n"); - - for (retries = 0; retries < MAX_RETRIES; retries++) { - sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); - if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT)) - break; - cond_resched(); - } - - if (sta & PAS_DMA_RXINT_RCMDSTA_ACT) - dev_err(&mac->dma_pdev->dev, "Failed to stop rx interface\n"); - - /* Then, disable the channel. This must be done separately from - * stopping, since you can't disable when active. - */ - - write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0); - write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0); - write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0); + pasemi_mac_pause_txchan(mac); + pasemi_mac_pause_rxint(mac); + pasemi_mac_pause_rxchan(mac); + pasemi_mac_intf_disable(mac); free_irq(mac->tx->chan.irq, mac->tx); free_irq(mac->rx->chan.irq, mac->rx); @@ -1388,6 +1441,62 @@ static int pasemi_mac_poll(struct napi_struct *napi, int budget) return pkts; } +static int pasemi_mac_change_mtu(struct net_device *dev, int new_mtu) +{ + struct pasemi_mac *mac = netdev_priv(dev); + unsigned int reg; + unsigned int rcmdsta; + int running; + + if (new_mtu < PE_MIN_MTU || new_mtu > PE_MAX_MTU) + return -EINVAL; + + running = netif_running(dev); + + if (running) { + /* Need to stop the interface, clean out all already + * received buffers, free all unused buffers on the RX + * interface ring, then finally re-fill the rx ring with + * the new-size buffers and restart. + */ + + napi_disable(&mac->napi); + netif_tx_disable(dev); + pasemi_mac_intf_disable(mac); + + rcmdsta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); + pasemi_mac_pause_rxint(mac); + pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE); + pasemi_mac_free_rx_buffers(mac); + } + + /* Change maxf, i.e. what size frames are accepted. + * Need room for ethernet header and CRC word + */ + reg = read_mac_reg(mac, PAS_MAC_CFG_MACCFG); + reg &= ~PAS_MAC_CFG_MACCFG_MAXF_M; + reg |= PAS_MAC_CFG_MACCFG_MAXF(new_mtu + ETH_HLEN + 4); + write_mac_reg(mac, PAS_MAC_CFG_MACCFG, reg); + + dev->mtu = new_mtu; + /* MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ + mac->bufsz = new_mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128; + + if (running) { + write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), + rcmdsta | PAS_DMA_RXINT_RCMDSTA_EN); + + rx_ring(mac)->next_to_fill = 0; + pasemi_mac_replenish_rx_ring(dev, RX_RING_SIZE-1); + + napi_enable(&mac->napi); + netif_start_queue(dev); + pasemi_mac_intf_enable(mac); + } + + return 0; +} + static int __devinit pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -1475,6 +1584,12 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev->stop = pasemi_mac_close; dev->hard_start_xmit = pasemi_mac_start_tx; dev->set_multicast_list = pasemi_mac_set_rx_mode; + dev->set_mac_address = pasemi_mac_set_mac_addr; + dev->mtu = PE_DEF_MTU; + /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ + mac->bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128; + + dev->change_mtu = pasemi_mac_change_mtu; if (err) goto out; |