18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 PA Semi, Inc 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for the PA Semi PWRficient onchip 1G/10G Ethernet MACs 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 178c2ecf20Sopenharmony_ci#include <asm/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include <linux/in.h> 198c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/ip.h> 228c2ecf20Sopenharmony_ci#include <net/checksum.h> 238c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/irq.h> 268c2ecf20Sopenharmony_ci#include <asm/firmware.h> 278c2ecf20Sopenharmony_ci#include <asm/pasemi_dma.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "pasemi_mac.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* We have our own align, since ppc64 in general has it at 0 because 328c2ecf20Sopenharmony_ci * of design flaws in some of the server bridge chips. However, for 338c2ecf20Sopenharmony_ci * PWRficient doing the unaligned copies is more expensive than doing 348c2ecf20Sopenharmony_ci * unaligned DMA, so make sure the data is aligned instead. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#define LOCAL_SKB_ALIGN 2 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* TODO list 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * - Multicast support 418c2ecf20Sopenharmony_ci * - Large MTU support 428c2ecf20Sopenharmony_ci * - Multiqueue RX/TX 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define PE_MIN_MTU (ETH_ZLEN + ETH_HLEN) 468c2ecf20Sopenharmony_ci#define PE_MAX_MTU 9000 478c2ecf20Sopenharmony_ci#define PE_DEF_MTU ETH_DATA_LEN 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define DEFAULT_MSG_ENABLE \ 508c2ecf20Sopenharmony_ci (NETIF_MSG_DRV | \ 518c2ecf20Sopenharmony_ci NETIF_MSG_PROBE | \ 528c2ecf20Sopenharmony_ci NETIF_MSG_LINK | \ 538c2ecf20Sopenharmony_ci NETIF_MSG_TIMER | \ 548c2ecf20Sopenharmony_ci NETIF_MSG_IFDOWN | \ 558c2ecf20Sopenharmony_ci NETIF_MSG_IFUP | \ 568c2ecf20Sopenharmony_ci NETIF_MSG_RX_ERR | \ 578c2ecf20Sopenharmony_ci NETIF_MSG_TX_ERR) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 608c2ecf20Sopenharmony_ciMODULE_AUTHOR ("Olof Johansson <olof@lixom.net>"); 618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PA Semi PWRficient Ethernet driver"); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int debug = -1; /* -1 == use DEFAULT_MSG_ENABLE as value */ 648c2ecf20Sopenharmony_cimodule_param(debug, int, 0); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "PA Semi MAC bitmapped debugging message enable value"); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ciextern const struct ethtool_ops pasemi_mac_ethtool_ops; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int translation_enabled(void) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci#if defined(CONFIG_PPC_PASEMI_IOMMU_DMA_FORCE) 728c2ecf20Sopenharmony_ci return 1; 738c2ecf20Sopenharmony_ci#else 748c2ecf20Sopenharmony_ci return firmware_has_feature(FW_FEATURE_LPAR); 758c2ecf20Sopenharmony_ci#endif 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void write_iob_reg(unsigned int reg, unsigned int val) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci pasemi_write_iob_reg(reg, val); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic unsigned int read_mac_reg(const struct pasemi_mac *mac, unsigned int reg) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return pasemi_read_mac_reg(mac->dma_if, reg); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void write_mac_reg(const struct pasemi_mac *mac, unsigned int reg, 898c2ecf20Sopenharmony_ci unsigned int val) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci pasemi_write_mac_reg(mac->dma_if, reg, val); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic unsigned int read_dma_reg(unsigned int reg) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci return pasemi_read_dma_reg(reg); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void write_dma_reg(unsigned int reg, unsigned int val) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci pasemi_write_dma_reg(reg, val); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic struct pasemi_mac_rxring *rx_ring(const struct pasemi_mac *mac) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci return mac->rx; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct pasemi_mac_txring *tx_ring(const struct pasemi_mac *mac) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci return mac->tx; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic inline void prefetch_skb(const struct sk_buff *skb) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci const void *d = skb; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci prefetch(d); 1198c2ecf20Sopenharmony_ci prefetch(d+64); 1208c2ecf20Sopenharmony_ci prefetch(d+128); 1218c2ecf20Sopenharmony_ci prefetch(d+192); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int mac_to_intf(struct pasemi_mac *mac) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct pci_dev *pdev = mac->pdev; 1278c2ecf20Sopenharmony_ci u32 tmp; 1288c2ecf20Sopenharmony_ci int nintf, off, i, j; 1298c2ecf20Sopenharmony_ci int devfn = pdev->devfn; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci tmp = read_dma_reg(PAS_DMA_CAP_IFI); 1328c2ecf20Sopenharmony_ci nintf = (tmp & PAS_DMA_CAP_IFI_NIN_M) >> PAS_DMA_CAP_IFI_NIN_S; 1338c2ecf20Sopenharmony_ci off = (tmp & PAS_DMA_CAP_IFI_IOFF_M) >> PAS_DMA_CAP_IFI_IOFF_S; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* IOFF contains the offset to the registers containing the 1368c2ecf20Sopenharmony_ci * DMA interface-to-MAC-pci-id mappings, and NIN contains number 1378c2ecf20Sopenharmony_ci * of total interfaces. Each register contains 4 devfns. 1388c2ecf20Sopenharmony_ci * Just do a linear search until we find the devfn of the MAC 1398c2ecf20Sopenharmony_ci * we're trying to look up. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci for (i = 0; i < (nintf+3)/4; i++) { 1438c2ecf20Sopenharmony_ci tmp = read_dma_reg(off+4*i); 1448c2ecf20Sopenharmony_ci for (j = 0; j < 4; j++) { 1458c2ecf20Sopenharmony_ci if (((tmp >> (8*j)) & 0xff) == devfn) 1468c2ecf20Sopenharmony_ci return i*4 + j; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci return -1; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void pasemi_mac_intf_disable(struct pasemi_mac *mac) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci unsigned int flags; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); 1578c2ecf20Sopenharmony_ci flags &= ~PAS_MAC_CFG_PCFG_PE; 1588c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void pasemi_mac_intf_enable(struct pasemi_mac *mac) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci unsigned int flags; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); 1668c2ecf20Sopenharmony_ci flags |= PAS_MAC_CFG_PCFG_PE; 1678c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int pasemi_get_mac_addr(struct pasemi_mac *mac) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct pci_dev *pdev = mac->pdev; 1738c2ecf20Sopenharmony_ci struct device_node *dn = pci_device_to_OF_node(pdev); 1748c2ecf20Sopenharmony_ci int len; 1758c2ecf20Sopenharmony_ci const u8 *maddr; 1768c2ecf20Sopenharmony_ci u8 addr[ETH_ALEN]; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!dn) { 1798c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, 1808c2ecf20Sopenharmony_ci "No device node for mac, not configuring\n"); 1818c2ecf20Sopenharmony_ci return -ENOENT; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci maddr = of_get_property(dn, "local-mac-address", &len); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (maddr && len == ETH_ALEN) { 1878c2ecf20Sopenharmony_ci memcpy(mac->mac_addr, maddr, ETH_ALEN); 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Some old versions of firmware mistakenly uses mac-address 1928c2ecf20Sopenharmony_ci * (and as a string) instead of a byte array in local-mac-address. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (maddr == NULL) 1968c2ecf20Sopenharmony_ci maddr = of_get_property(dn, "mac-address", NULL); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (maddr == NULL) { 1998c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 2008c2ecf20Sopenharmony_ci "no mac address in device tree, not configuring\n"); 2018c2ecf20Sopenharmony_ci return -ENOENT; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!mac_pton(maddr, addr)) { 2058c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 2068c2ecf20Sopenharmony_ci "can't parse mac address, not configuring\n"); 2078c2ecf20Sopenharmony_ci return -EINVAL; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci memcpy(mac->mac_addr, addr, ETH_ALEN); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int pasemi_mac_set_mac_addr(struct net_device *dev, void *p) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct pasemi_mac *mac = netdev_priv(dev); 2188c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 2198c2ecf20Sopenharmony_ci unsigned int adr0, adr1; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 2228c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci adr0 = dev->dev_addr[2] << 24 | 2278c2ecf20Sopenharmony_ci dev->dev_addr[3] << 16 | 2288c2ecf20Sopenharmony_ci dev->dev_addr[4] << 8 | 2298c2ecf20Sopenharmony_ci dev->dev_addr[5]; 2308c2ecf20Sopenharmony_ci adr1 = read_mac_reg(mac, PAS_MAC_CFG_ADR1); 2318c2ecf20Sopenharmony_ci adr1 &= ~0xffff; 2328c2ecf20Sopenharmony_ci adr1 |= dev->dev_addr[0] << 8 | dev->dev_addr[1]; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci pasemi_mac_intf_disable(mac); 2358c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_CFG_ADR0, adr0); 2368c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_CFG_ADR1, adr1); 2378c2ecf20Sopenharmony_ci pasemi_mac_intf_enable(mac); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int pasemi_mac_unmap_tx_skb(struct pasemi_mac *mac, 2438c2ecf20Sopenharmony_ci const int nfrags, 2448c2ecf20Sopenharmony_ci struct sk_buff *skb, 2458c2ecf20Sopenharmony_ci const dma_addr_t *dmas) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci int f; 2488c2ecf20Sopenharmony_ci struct pci_dev *pdev = mac->dma_pdev; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci pci_unmap_single(pdev, dmas[0], skb_headlen(skb), PCI_DMA_TODEVICE); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci for (f = 0; f < nfrags; f++) { 2538c2ecf20Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci pci_unmap_page(pdev, dmas[f+1], skb_frag_size(frag), PCI_DMA_TODEVICE); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Freed descriptor slot + main SKB ptr + nfrags additional ptrs, 2608c2ecf20Sopenharmony_ci * aligned up to a power of 2 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_ci return (nfrags + 3) & ~1; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic struct pasemi_mac_csring *pasemi_mac_setup_csring(struct pasemi_mac *mac) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct pasemi_mac_csring *ring; 2688c2ecf20Sopenharmony_ci u32 val; 2698c2ecf20Sopenharmony_ci unsigned int cfg; 2708c2ecf20Sopenharmony_ci int chno; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ring = pasemi_dma_alloc_chan(TXCHAN, sizeof(struct pasemi_mac_csring), 2738c2ecf20Sopenharmony_ci offsetof(struct pasemi_mac_csring, chan)); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!ring) { 2768c2ecf20Sopenharmony_ci dev_err(&mac->pdev->dev, "Can't allocate checksum channel\n"); 2778c2ecf20Sopenharmony_ci goto out_chan; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci chno = ring->chan.chno; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ring->size = CS_RING_SIZE; 2838c2ecf20Sopenharmony_ci ring->next_to_fill = 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Allocate descriptors */ 2868c2ecf20Sopenharmony_ci if (pasemi_dma_alloc_ring(&ring->chan, CS_RING_SIZE)) 2878c2ecf20Sopenharmony_ci goto out_ring_desc; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_BASEL(chno), 2908c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_BASEL_BRBL(ring->chan.ring_dma)); 2918c2ecf20Sopenharmony_ci val = PAS_DMA_TXCHAN_BASEU_BRBH(ring->chan.ring_dma >> 32); 2928c2ecf20Sopenharmony_ci val |= PAS_DMA_TXCHAN_BASEU_SIZ(CS_RING_SIZE >> 3); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_BASEU(chno), val); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ring->events[0] = pasemi_dma_alloc_flag(); 2978c2ecf20Sopenharmony_ci ring->events[1] = pasemi_dma_alloc_flag(); 2988c2ecf20Sopenharmony_ci if (ring->events[0] < 0 || ring->events[1] < 0) 2998c2ecf20Sopenharmony_ci goto out_flags; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci pasemi_dma_clear_flag(ring->events[0]); 3028c2ecf20Sopenharmony_ci pasemi_dma_clear_flag(ring->events[1]); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci ring->fun = pasemi_dma_alloc_fun(); 3058c2ecf20Sopenharmony_ci if (ring->fun < 0) 3068c2ecf20Sopenharmony_ci goto out_fun; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci cfg = PAS_DMA_TXCHAN_CFG_TY_FUNC | PAS_DMA_TXCHAN_CFG_UP | 3098c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_CFG_TATTR(ring->fun) | 3108c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_CFG_LPSQ | PAS_DMA_TXCHAN_CFG_LPDQ; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (translation_enabled()) 3138c2ecf20Sopenharmony_ci cfg |= PAS_DMA_TXCHAN_CFG_TRD | PAS_DMA_TXCHAN_CFG_TRR; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_CFG(chno), cfg); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* enable channel */ 3188c2ecf20Sopenharmony_ci pasemi_dma_start_chan(&ring->chan, PAS_DMA_TXCHAN_TCMDSTA_SZ | 3198c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_TCMDSTA_DB | 3208c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_TCMDSTA_DE | 3218c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_TCMDSTA_DA); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return ring; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ciout_fun: 3268c2ecf20Sopenharmony_ciout_flags: 3278c2ecf20Sopenharmony_ci if (ring->events[0] >= 0) 3288c2ecf20Sopenharmony_ci pasemi_dma_free_flag(ring->events[0]); 3298c2ecf20Sopenharmony_ci if (ring->events[1] >= 0) 3308c2ecf20Sopenharmony_ci pasemi_dma_free_flag(ring->events[1]); 3318c2ecf20Sopenharmony_ci pasemi_dma_free_ring(&ring->chan); 3328c2ecf20Sopenharmony_ciout_ring_desc: 3338c2ecf20Sopenharmony_ci pasemi_dma_free_chan(&ring->chan); 3348c2ecf20Sopenharmony_ciout_chan: 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return NULL; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic void pasemi_mac_setup_csrings(struct pasemi_mac *mac) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci int i; 3428c2ecf20Sopenharmony_ci mac->cs[0] = pasemi_mac_setup_csring(mac); 3438c2ecf20Sopenharmony_ci if (mac->type == MAC_TYPE_XAUI) 3448c2ecf20Sopenharmony_ci mac->cs[1] = pasemi_mac_setup_csring(mac); 3458c2ecf20Sopenharmony_ci else 3468c2ecf20Sopenharmony_ci mac->cs[1] = 0; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci for (i = 0; i < MAX_CS; i++) 3498c2ecf20Sopenharmony_ci if (mac->cs[i]) 3508c2ecf20Sopenharmony_ci mac->num_cs++; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void pasemi_mac_free_csring(struct pasemi_mac_csring *csring) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci pasemi_dma_stop_chan(&csring->chan); 3568c2ecf20Sopenharmony_ci pasemi_dma_free_flag(csring->events[0]); 3578c2ecf20Sopenharmony_ci pasemi_dma_free_flag(csring->events[1]); 3588c2ecf20Sopenharmony_ci pasemi_dma_free_ring(&csring->chan); 3598c2ecf20Sopenharmony_ci pasemi_dma_free_chan(&csring->chan); 3608c2ecf20Sopenharmony_ci pasemi_dma_free_fun(csring->fun); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int pasemi_mac_setup_rx_resources(const struct net_device *dev) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct pasemi_mac_rxring *ring; 3668c2ecf20Sopenharmony_ci struct pasemi_mac *mac = netdev_priv(dev); 3678c2ecf20Sopenharmony_ci int chno; 3688c2ecf20Sopenharmony_ci unsigned int cfg; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ring = pasemi_dma_alloc_chan(RXCHAN, sizeof(struct pasemi_mac_rxring), 3718c2ecf20Sopenharmony_ci offsetof(struct pasemi_mac_rxring, chan)); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (!ring) { 3748c2ecf20Sopenharmony_ci dev_err(&mac->pdev->dev, "Can't allocate RX channel\n"); 3758c2ecf20Sopenharmony_ci goto out_chan; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci chno = ring->chan.chno; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci spin_lock_init(&ring->lock); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ring->size = RX_RING_SIZE; 3828c2ecf20Sopenharmony_ci ring->ring_info = kcalloc(RX_RING_SIZE, 3838c2ecf20Sopenharmony_ci sizeof(struct pasemi_mac_buffer), 3848c2ecf20Sopenharmony_ci GFP_KERNEL); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!ring->ring_info) 3878c2ecf20Sopenharmony_ci goto out_ring_info; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Allocate descriptors */ 3908c2ecf20Sopenharmony_ci if (pasemi_dma_alloc_ring(&ring->chan, RX_RING_SIZE)) 3918c2ecf20Sopenharmony_ci goto out_ring_desc; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci ring->buffers = dma_alloc_coherent(&mac->dma_pdev->dev, 3948c2ecf20Sopenharmony_ci RX_RING_SIZE * sizeof(u64), 3958c2ecf20Sopenharmony_ci &ring->buf_dma, GFP_KERNEL); 3968c2ecf20Sopenharmony_ci if (!ring->buffers) 3978c2ecf20Sopenharmony_ci goto out_ring_desc; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXCHAN_BASEL(chno), 4008c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_BASEL_BRBL(ring->chan.ring_dma)); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXCHAN_BASEU(chno), 4038c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_BASEU_BRBH(ring->chan.ring_dma >> 32) | 4048c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_BASEU_SIZ(RX_RING_SIZE >> 3)); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci cfg = PAS_DMA_RXCHAN_CFG_HBU(2); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (translation_enabled()) 4098c2ecf20Sopenharmony_ci cfg |= PAS_DMA_RXCHAN_CFG_CTR; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXCHAN_CFG(chno), cfg); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXINT_BASEL(mac->dma_if), 4148c2ecf20Sopenharmony_ci PAS_DMA_RXINT_BASEL_BRBL(ring->buf_dma)); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXINT_BASEU(mac->dma_if), 4178c2ecf20Sopenharmony_ci PAS_DMA_RXINT_BASEU_BRBH(ring->buf_dma >> 32) | 4188c2ecf20Sopenharmony_ci PAS_DMA_RXINT_BASEU_SIZ(RX_RING_SIZE >> 3)); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci cfg = PAS_DMA_RXINT_CFG_DHL(2) | PAS_DMA_RXINT_CFG_L2 | 4218c2ecf20Sopenharmony_ci PAS_DMA_RXINT_CFG_LW | PAS_DMA_RXINT_CFG_RBP | 4228c2ecf20Sopenharmony_ci PAS_DMA_RXINT_CFG_HEN; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (translation_enabled()) 4258c2ecf20Sopenharmony_ci cfg |= PAS_DMA_RXINT_CFG_ITRR | PAS_DMA_RXINT_CFG_ITR; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXINT_CFG(mac->dma_if), cfg); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci ring->next_to_fill = 0; 4308c2ecf20Sopenharmony_ci ring->next_to_clean = 0; 4318c2ecf20Sopenharmony_ci ring->mac = mac; 4328c2ecf20Sopenharmony_ci mac->rx = ring; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ciout_ring_desc: 4378c2ecf20Sopenharmony_ci kfree(ring->ring_info); 4388c2ecf20Sopenharmony_ciout_ring_info: 4398c2ecf20Sopenharmony_ci pasemi_dma_free_chan(&ring->chan); 4408c2ecf20Sopenharmony_ciout_chan: 4418c2ecf20Sopenharmony_ci return -ENOMEM; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic struct pasemi_mac_txring * 4458c2ecf20Sopenharmony_cipasemi_mac_setup_tx_resources(const struct net_device *dev) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct pasemi_mac *mac = netdev_priv(dev); 4488c2ecf20Sopenharmony_ci u32 val; 4498c2ecf20Sopenharmony_ci struct pasemi_mac_txring *ring; 4508c2ecf20Sopenharmony_ci unsigned int cfg; 4518c2ecf20Sopenharmony_ci int chno; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci ring = pasemi_dma_alloc_chan(TXCHAN, sizeof(struct pasemi_mac_txring), 4548c2ecf20Sopenharmony_ci offsetof(struct pasemi_mac_txring, chan)); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (!ring) { 4578c2ecf20Sopenharmony_ci dev_err(&mac->pdev->dev, "Can't allocate TX channel\n"); 4588c2ecf20Sopenharmony_ci goto out_chan; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci chno = ring->chan.chno; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci spin_lock_init(&ring->lock); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci ring->size = TX_RING_SIZE; 4668c2ecf20Sopenharmony_ci ring->ring_info = kcalloc(TX_RING_SIZE, 4678c2ecf20Sopenharmony_ci sizeof(struct pasemi_mac_buffer), 4688c2ecf20Sopenharmony_ci GFP_KERNEL); 4698c2ecf20Sopenharmony_ci if (!ring->ring_info) 4708c2ecf20Sopenharmony_ci goto out_ring_info; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* Allocate descriptors */ 4738c2ecf20Sopenharmony_ci if (pasemi_dma_alloc_ring(&ring->chan, TX_RING_SIZE)) 4748c2ecf20Sopenharmony_ci goto out_ring_desc; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_BASEL(chno), 4778c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_BASEL_BRBL(ring->chan.ring_dma)); 4788c2ecf20Sopenharmony_ci val = PAS_DMA_TXCHAN_BASEU_BRBH(ring->chan.ring_dma >> 32); 4798c2ecf20Sopenharmony_ci val |= PAS_DMA_TXCHAN_BASEU_SIZ(TX_RING_SIZE >> 3); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_BASEU(chno), val); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci cfg = PAS_DMA_TXCHAN_CFG_TY_IFACE | 4848c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_CFG_TATTR(mac->dma_if) | 4858c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_CFG_UP | 4868c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_CFG_WT(4); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (translation_enabled()) 4898c2ecf20Sopenharmony_ci cfg |= PAS_DMA_TXCHAN_CFG_TRD | PAS_DMA_TXCHAN_CFG_TRR; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_CFG(chno), cfg); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci ring->next_to_fill = 0; 4948c2ecf20Sopenharmony_ci ring->next_to_clean = 0; 4958c2ecf20Sopenharmony_ci ring->mac = mac; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return ring; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ciout_ring_desc: 5008c2ecf20Sopenharmony_ci kfree(ring->ring_info); 5018c2ecf20Sopenharmony_ciout_ring_info: 5028c2ecf20Sopenharmony_ci pasemi_dma_free_chan(&ring->chan); 5038c2ecf20Sopenharmony_ciout_chan: 5048c2ecf20Sopenharmony_ci return NULL; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void pasemi_mac_free_tx_resources(struct pasemi_mac *mac) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct pasemi_mac_txring *txring = tx_ring(mac); 5108c2ecf20Sopenharmony_ci unsigned int i, j; 5118c2ecf20Sopenharmony_ci struct pasemi_mac_buffer *info; 5128c2ecf20Sopenharmony_ci dma_addr_t dmas[MAX_SKB_FRAGS+1]; 5138c2ecf20Sopenharmony_ci int freed, nfrags; 5148c2ecf20Sopenharmony_ci int start, limit; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci start = txring->next_to_clean; 5178c2ecf20Sopenharmony_ci limit = txring->next_to_fill; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* Compensate for when fill has wrapped and clean has not */ 5208c2ecf20Sopenharmony_ci if (start > limit) 5218c2ecf20Sopenharmony_ci limit += TX_RING_SIZE; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci for (i = start; i < limit; i += freed) { 5248c2ecf20Sopenharmony_ci info = &txring->ring_info[(i+1) & (TX_RING_SIZE-1)]; 5258c2ecf20Sopenharmony_ci if (info->dma && info->skb) { 5268c2ecf20Sopenharmony_ci nfrags = skb_shinfo(info->skb)->nr_frags; 5278c2ecf20Sopenharmony_ci for (j = 0; j <= nfrags; j++) 5288c2ecf20Sopenharmony_ci dmas[j] = txring->ring_info[(i+1+j) & 5298c2ecf20Sopenharmony_ci (TX_RING_SIZE-1)].dma; 5308c2ecf20Sopenharmony_ci freed = pasemi_mac_unmap_tx_skb(mac, nfrags, 5318c2ecf20Sopenharmony_ci info->skb, dmas); 5328c2ecf20Sopenharmony_ci } else { 5338c2ecf20Sopenharmony_ci freed = 2; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci kfree(txring->ring_info); 5388c2ecf20Sopenharmony_ci pasemi_dma_free_chan(&txring->chan); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic void pasemi_mac_free_rx_buffers(struct pasemi_mac *mac) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct pasemi_mac_rxring *rx = rx_ring(mac); 5458c2ecf20Sopenharmony_ci unsigned int i; 5468c2ecf20Sopenharmony_ci struct pasemi_mac_buffer *info; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 5498c2ecf20Sopenharmony_ci info = &RX_DESC_INFO(rx, i); 5508c2ecf20Sopenharmony_ci if (info->skb && info->dma) { 5518c2ecf20Sopenharmony_ci pci_unmap_single(mac->dma_pdev, 5528c2ecf20Sopenharmony_ci info->dma, 5538c2ecf20Sopenharmony_ci info->skb->len, 5548c2ecf20Sopenharmony_ci PCI_DMA_FROMDEVICE); 5558c2ecf20Sopenharmony_ci dev_kfree_skb_any(info->skb); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci info->dma = 0; 5588c2ecf20Sopenharmony_ci info->skb = NULL; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) 5628c2ecf20Sopenharmony_ci RX_BUFF(rx, i) = 0; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic void pasemi_mac_free_rx_resources(struct pasemi_mac *mac) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci pasemi_mac_free_rx_buffers(mac); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64), 5708c2ecf20Sopenharmony_ci rx_ring(mac)->buffers, rx_ring(mac)->buf_dma); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci kfree(rx_ring(mac)->ring_info); 5738c2ecf20Sopenharmony_ci pasemi_dma_free_chan(&rx_ring(mac)->chan); 5748c2ecf20Sopenharmony_ci mac->rx = NULL; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic void pasemi_mac_replenish_rx_ring(struct net_device *dev, 5788c2ecf20Sopenharmony_ci const int limit) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci const struct pasemi_mac *mac = netdev_priv(dev); 5818c2ecf20Sopenharmony_ci struct pasemi_mac_rxring *rx = rx_ring(mac); 5828c2ecf20Sopenharmony_ci int fill, count; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (limit <= 0) 5858c2ecf20Sopenharmony_ci return; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci fill = rx_ring(mac)->next_to_fill; 5888c2ecf20Sopenharmony_ci for (count = 0; count < limit; count++) { 5898c2ecf20Sopenharmony_ci struct pasemi_mac_buffer *info = &RX_DESC_INFO(rx, fill); 5908c2ecf20Sopenharmony_ci u64 *buff = &RX_BUFF(rx, fill); 5918c2ecf20Sopenharmony_ci struct sk_buff *skb; 5928c2ecf20Sopenharmony_ci dma_addr_t dma; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* Entry in use? */ 5958c2ecf20Sopenharmony_ci WARN_ON(*buff); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, mac->bufsz); 5988c2ecf20Sopenharmony_ci skb_reserve(skb, LOCAL_SKB_ALIGN); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (unlikely(!skb)) 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci dma = pci_map_single(mac->dma_pdev, skb->data, 6048c2ecf20Sopenharmony_ci mac->bufsz - LOCAL_SKB_ALIGN, 6058c2ecf20Sopenharmony_ci PCI_DMA_FROMDEVICE); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (unlikely(pci_dma_mapping_error(mac->dma_pdev, dma))) { 6088c2ecf20Sopenharmony_ci dev_kfree_skb_irq(info->skb); 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci info->skb = skb; 6138c2ecf20Sopenharmony_ci info->dma = dma; 6148c2ecf20Sopenharmony_ci *buff = XCT_RXB_LEN(mac->bufsz) | XCT_RXB_ADDR(dma); 6158c2ecf20Sopenharmony_ci fill++; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci wmb(); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXINT_INCR(mac->dma_if), count); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci rx_ring(mac)->next_to_fill = (rx_ring(mac)->next_to_fill + count) & 6238c2ecf20Sopenharmony_ci (RX_RING_SIZE - 1); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic void pasemi_mac_restart_rx_intr(const struct pasemi_mac *mac) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct pasemi_mac_rxring *rx = rx_ring(mac); 6298c2ecf20Sopenharmony_ci unsigned int reg, pcnt; 6308c2ecf20Sopenharmony_ci /* Re-enable packet count interrupts: finally 6318c2ecf20Sopenharmony_ci * ack the packet count interrupt we got in rx_intr. 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci pcnt = *rx->chan.status & PAS_STATUS_PCNT_M; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci reg = PAS_IOB_DMA_RXCH_RESET_PCNT(pcnt) | PAS_IOB_DMA_RXCH_RESET_PINTC; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (*rx->chan.status & PAS_STATUS_TIMER) 6398c2ecf20Sopenharmony_ci reg |= PAS_IOB_DMA_RXCH_RESET_TINTC; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci write_iob_reg(PAS_IOB_DMA_RXCH_RESET(mac->rx->chan.chno), reg); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic void pasemi_mac_restart_tx_intr(const struct pasemi_mac *mac) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci unsigned int reg, pcnt; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* Re-enable packet count interrupts */ 6498c2ecf20Sopenharmony_ci pcnt = *tx_ring(mac)->chan.status & PAS_STATUS_PCNT_M; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci reg = PAS_IOB_DMA_TXCH_RESET_PCNT(pcnt) | PAS_IOB_DMA_TXCH_RESET_PINTC; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci write_iob_reg(PAS_IOB_DMA_TXCH_RESET(tx_ring(mac)->chan.chno), reg); 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic inline void pasemi_mac_rx_error(const struct pasemi_mac *mac, 6588c2ecf20Sopenharmony_ci const u64 macrx) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci unsigned int rcmdsta, ccmdsta; 6618c2ecf20Sopenharmony_ci struct pasemi_dmachan *chan = &rx_ring(mac)->chan; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (!netif_msg_rx_err(mac)) 6648c2ecf20Sopenharmony_ci return; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci rcmdsta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); 6678c2ecf20Sopenharmony_ci ccmdsta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(chan->chno)); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci printk(KERN_ERR "pasemi_mac: rx error. macrx %016llx, rx status %llx\n", 6708c2ecf20Sopenharmony_ci macrx, *chan->status); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci printk(KERN_ERR "pasemi_mac: rcmdsta %08x ccmdsta %08x\n", 6738c2ecf20Sopenharmony_ci rcmdsta, ccmdsta); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic inline void pasemi_mac_tx_error(const struct pasemi_mac *mac, 6778c2ecf20Sopenharmony_ci const u64 mactx) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci unsigned int cmdsta; 6808c2ecf20Sopenharmony_ci struct pasemi_dmachan *chan = &tx_ring(mac)->chan; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (!netif_msg_tx_err(mac)) 6838c2ecf20Sopenharmony_ci return; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci cmdsta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(chan->chno)); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci printk(KERN_ERR "pasemi_mac: tx error. mactx 0x%016llx, "\ 6888c2ecf20Sopenharmony_ci "tx status 0x%016llx\n", mactx, *chan->status); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci printk(KERN_ERR "pasemi_mac: tcmdsta 0x%08x\n", cmdsta); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx, 6948c2ecf20Sopenharmony_ci const int limit) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci const struct pasemi_dmachan *chan = &rx->chan; 6978c2ecf20Sopenharmony_ci struct pasemi_mac *mac = rx->mac; 6988c2ecf20Sopenharmony_ci struct pci_dev *pdev = mac->dma_pdev; 6998c2ecf20Sopenharmony_ci unsigned int n; 7008c2ecf20Sopenharmony_ci int count, buf_index, tot_bytes, packets; 7018c2ecf20Sopenharmony_ci struct pasemi_mac_buffer *info; 7028c2ecf20Sopenharmony_ci struct sk_buff *skb; 7038c2ecf20Sopenharmony_ci unsigned int len; 7048c2ecf20Sopenharmony_ci u64 macrx, eval; 7058c2ecf20Sopenharmony_ci dma_addr_t dma; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci tot_bytes = 0; 7088c2ecf20Sopenharmony_ci packets = 0; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci spin_lock(&rx->lock); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci n = rx->next_to_clean; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci prefetch(&RX_DESC(rx, n)); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci for (count = 0; count < limit; count++) { 7178c2ecf20Sopenharmony_ci macrx = RX_DESC(rx, n); 7188c2ecf20Sopenharmony_ci prefetch(&RX_DESC(rx, n+4)); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if ((macrx & XCT_MACRX_E) || 7218c2ecf20Sopenharmony_ci (*chan->status & PAS_STATUS_ERROR)) 7228c2ecf20Sopenharmony_ci pasemi_mac_rx_error(mac, macrx); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (!(macrx & XCT_MACRX_O)) 7258c2ecf20Sopenharmony_ci break; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci info = NULL; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci BUG_ON(!(macrx & XCT_MACRX_RR_8BRES)); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci eval = (RX_DESC(rx, n+1) & XCT_RXRES_8B_EVAL_M) >> 7328c2ecf20Sopenharmony_ci XCT_RXRES_8B_EVAL_S; 7338c2ecf20Sopenharmony_ci buf_index = eval-1; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci dma = (RX_DESC(rx, n+2) & XCT_PTR_ADDR_M); 7368c2ecf20Sopenharmony_ci info = &RX_DESC_INFO(rx, buf_index); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci skb = info->skb; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci prefetch_skb(skb); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci len = (macrx & XCT_MACRX_LLEN_M) >> XCT_MACRX_LLEN_S; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci pci_unmap_single(pdev, dma, mac->bufsz - LOCAL_SKB_ALIGN, 7458c2ecf20Sopenharmony_ci PCI_DMA_FROMDEVICE); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (macrx & XCT_MACRX_CRC) { 7488c2ecf20Sopenharmony_ci /* CRC error flagged */ 7498c2ecf20Sopenharmony_ci mac->netdev->stats.rx_errors++; 7508c2ecf20Sopenharmony_ci mac->netdev->stats.rx_crc_errors++; 7518c2ecf20Sopenharmony_ci /* No need to free skb, it'll be reused */ 7528c2ecf20Sopenharmony_ci goto next; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci info->skb = NULL; 7568c2ecf20Sopenharmony_ci info->dma = 0; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (likely((macrx & XCT_MACRX_HTY_M) == XCT_MACRX_HTY_IPV4_OK)) { 7598c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 7608c2ecf20Sopenharmony_ci skb->csum = (macrx & XCT_MACRX_CSUM_M) >> 7618c2ecf20Sopenharmony_ci XCT_MACRX_CSUM_S; 7628c2ecf20Sopenharmony_ci } else { 7638c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci packets++; 7678c2ecf20Sopenharmony_ci tot_bytes += len; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* Don't include CRC */ 7708c2ecf20Sopenharmony_ci skb_put(skb, len-4); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, mac->netdev); 7738c2ecf20Sopenharmony_ci napi_gro_receive(&mac->napi, skb); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cinext: 7768c2ecf20Sopenharmony_ci RX_DESC(rx, n) = 0; 7778c2ecf20Sopenharmony_ci RX_DESC(rx, n+1) = 0; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* Need to zero it out since hardware doesn't, since the 7808c2ecf20Sopenharmony_ci * replenish loop uses it to tell when it's done. 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_ci RX_BUFF(rx, buf_index) = 0; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci n += 4; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if (n > RX_RING_SIZE) { 7888c2ecf20Sopenharmony_ci /* Errata 5971 workaround: L2 target of headers */ 7898c2ecf20Sopenharmony_ci write_iob_reg(PAS_IOB_COM_PKTHDRCNT, 0); 7908c2ecf20Sopenharmony_ci n &= (RX_RING_SIZE-1); 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci rx_ring(mac)->next_to_clean = n; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* Increase is in number of 16-byte entries, and since each descriptor 7968c2ecf20Sopenharmony_ci * with an 8BRES takes up 3x8 bytes (padded to 4x8), increase with 7978c2ecf20Sopenharmony_ci * count*2. 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXCHAN_INCR(mac->rx->chan.chno), count << 1); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci pasemi_mac_replenish_rx_ring(mac->netdev, count); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci mac->netdev->stats.rx_bytes += tot_bytes; 8048c2ecf20Sopenharmony_ci mac->netdev->stats.rx_packets += packets; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci spin_unlock(&rx_ring(mac)->lock); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return count; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/* Can't make this too large or we blow the kernel stack limits */ 8128c2ecf20Sopenharmony_ci#define TX_CLEAN_BATCHSIZE (128/MAX_SKB_FRAGS) 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic int pasemi_mac_clean_tx(struct pasemi_mac_txring *txring) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci struct pasemi_dmachan *chan = &txring->chan; 8178c2ecf20Sopenharmony_ci struct pasemi_mac *mac = txring->mac; 8188c2ecf20Sopenharmony_ci int i, j; 8198c2ecf20Sopenharmony_ci unsigned int start, descr_count, buf_count, batch_limit; 8208c2ecf20Sopenharmony_ci unsigned int ring_limit; 8218c2ecf20Sopenharmony_ci unsigned int total_count; 8228c2ecf20Sopenharmony_ci unsigned long flags; 8238c2ecf20Sopenharmony_ci struct sk_buff *skbs[TX_CLEAN_BATCHSIZE]; 8248c2ecf20Sopenharmony_ci dma_addr_t dmas[TX_CLEAN_BATCHSIZE][MAX_SKB_FRAGS+1]; 8258c2ecf20Sopenharmony_ci int nf[TX_CLEAN_BATCHSIZE]; 8268c2ecf20Sopenharmony_ci int nr_frags; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci total_count = 0; 8298c2ecf20Sopenharmony_ci batch_limit = TX_CLEAN_BATCHSIZE; 8308c2ecf20Sopenharmony_cirestart: 8318c2ecf20Sopenharmony_ci spin_lock_irqsave(&txring->lock, flags); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci start = txring->next_to_clean; 8348c2ecf20Sopenharmony_ci ring_limit = txring->next_to_fill; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci prefetch(&TX_DESC_INFO(txring, start+1).skb); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci /* Compensate for when fill has wrapped but clean has not */ 8398c2ecf20Sopenharmony_ci if (start > ring_limit) 8408c2ecf20Sopenharmony_ci ring_limit += TX_RING_SIZE; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci buf_count = 0; 8438c2ecf20Sopenharmony_ci descr_count = 0; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci for (i = start; 8468c2ecf20Sopenharmony_ci descr_count < batch_limit && i < ring_limit; 8478c2ecf20Sopenharmony_ci i += buf_count) { 8488c2ecf20Sopenharmony_ci u64 mactx = TX_DESC(txring, i); 8498c2ecf20Sopenharmony_ci struct sk_buff *skb; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if ((mactx & XCT_MACTX_E) || 8528c2ecf20Sopenharmony_ci (*chan->status & PAS_STATUS_ERROR)) 8538c2ecf20Sopenharmony_ci pasemi_mac_tx_error(mac, mactx); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* Skip over control descriptors */ 8568c2ecf20Sopenharmony_ci if (!(mactx & XCT_MACTX_LLEN_M)) { 8578c2ecf20Sopenharmony_ci TX_DESC(txring, i) = 0; 8588c2ecf20Sopenharmony_ci TX_DESC(txring, i+1) = 0; 8598c2ecf20Sopenharmony_ci buf_count = 2; 8608c2ecf20Sopenharmony_ci continue; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci skb = TX_DESC_INFO(txring, i+1).skb; 8648c2ecf20Sopenharmony_ci nr_frags = TX_DESC_INFO(txring, i).dma; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (unlikely(mactx & XCT_MACTX_O)) 8678c2ecf20Sopenharmony_ci /* Not yet transmitted */ 8688c2ecf20Sopenharmony_ci break; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci buf_count = 2 + nr_frags; 8718c2ecf20Sopenharmony_ci /* Since we always fill with an even number of entries, make 8728c2ecf20Sopenharmony_ci * sure we skip any unused one at the end as well. 8738c2ecf20Sopenharmony_ci */ 8748c2ecf20Sopenharmony_ci if (buf_count & 1) 8758c2ecf20Sopenharmony_ci buf_count++; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci for (j = 0; j <= nr_frags; j++) 8788c2ecf20Sopenharmony_ci dmas[descr_count][j] = TX_DESC_INFO(txring, i+1+j).dma; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci skbs[descr_count] = skb; 8818c2ecf20Sopenharmony_ci nf[descr_count] = nr_frags; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci TX_DESC(txring, i) = 0; 8848c2ecf20Sopenharmony_ci TX_DESC(txring, i+1) = 0; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci descr_count++; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci txring->next_to_clean = i & (TX_RING_SIZE-1); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&txring->lock, flags); 8918c2ecf20Sopenharmony_ci netif_wake_queue(mac->netdev); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci for (i = 0; i < descr_count; i++) 8948c2ecf20Sopenharmony_ci pasemi_mac_unmap_tx_skb(mac, nf[i], skbs[i], dmas[i]); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci total_count += descr_count; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* If the batch was full, try to clean more */ 8998c2ecf20Sopenharmony_ci if (descr_count == batch_limit) 9008c2ecf20Sopenharmony_ci goto restart; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci return total_count; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic irqreturn_t pasemi_mac_rx_intr(int irq, void *data) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci const struct pasemi_mac_rxring *rxring = data; 9098c2ecf20Sopenharmony_ci struct pasemi_mac *mac = rxring->mac; 9108c2ecf20Sopenharmony_ci const struct pasemi_dmachan *chan = &rxring->chan; 9118c2ecf20Sopenharmony_ci unsigned int reg; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (!(*chan->status & PAS_STATUS_CAUSE_M)) 9148c2ecf20Sopenharmony_ci return IRQ_NONE; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* Don't reset packet count so it won't fire again but clear 9178c2ecf20Sopenharmony_ci * all others. 9188c2ecf20Sopenharmony_ci */ 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci reg = 0; 9218c2ecf20Sopenharmony_ci if (*chan->status & PAS_STATUS_SOFT) 9228c2ecf20Sopenharmony_ci reg |= PAS_IOB_DMA_RXCH_RESET_SINTC; 9238c2ecf20Sopenharmony_ci if (*chan->status & PAS_STATUS_ERROR) 9248c2ecf20Sopenharmony_ci reg |= PAS_IOB_DMA_RXCH_RESET_DINTC; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci napi_schedule(&mac->napi); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci write_iob_reg(PAS_IOB_DMA_RXCH_RESET(chan->chno), reg); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci#define TX_CLEAN_INTERVAL HZ 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic void pasemi_mac_tx_timer(struct timer_list *t) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct pasemi_mac_txring *txring = from_timer(txring, t, clean_timer); 9388c2ecf20Sopenharmony_ci struct pasemi_mac *mac = txring->mac; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci pasemi_mac_clean_tx(txring); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci mod_timer(&txring->clean_timer, jiffies + TX_CLEAN_INTERVAL); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci pasemi_mac_restart_tx_intr(mac); 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic irqreturn_t pasemi_mac_tx_intr(int irq, void *data) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct pasemi_mac_txring *txring = data; 9508c2ecf20Sopenharmony_ci const struct pasemi_dmachan *chan = &txring->chan; 9518c2ecf20Sopenharmony_ci struct pasemi_mac *mac = txring->mac; 9528c2ecf20Sopenharmony_ci unsigned int reg; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (!(*chan->status & PAS_STATUS_CAUSE_M)) 9558c2ecf20Sopenharmony_ci return IRQ_NONE; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci reg = 0; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (*chan->status & PAS_STATUS_SOFT) 9608c2ecf20Sopenharmony_ci reg |= PAS_IOB_DMA_TXCH_RESET_SINTC; 9618c2ecf20Sopenharmony_ci if (*chan->status & PAS_STATUS_ERROR) 9628c2ecf20Sopenharmony_ci reg |= PAS_IOB_DMA_TXCH_RESET_DINTC; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci mod_timer(&txring->clean_timer, jiffies + (TX_CLEAN_INTERVAL)*2); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci napi_schedule(&mac->napi); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci if (reg) 9698c2ecf20Sopenharmony_ci write_iob_reg(PAS_IOB_DMA_TXCH_RESET(chan->chno), reg); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic void pasemi_adjust_link(struct net_device *dev) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci struct pasemi_mac *mac = netdev_priv(dev); 9778c2ecf20Sopenharmony_ci int msg; 9788c2ecf20Sopenharmony_ci unsigned int flags; 9798c2ecf20Sopenharmony_ci unsigned int new_flags; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (!dev->phydev->link) { 9828c2ecf20Sopenharmony_ci /* If no link, MAC speed settings don't matter. Just report 9838c2ecf20Sopenharmony_ci * link down and return. 9848c2ecf20Sopenharmony_ci */ 9858c2ecf20Sopenharmony_ci if (mac->link && netif_msg_link(mac)) 9868c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Link is down.\n", dev->name); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci netif_carrier_off(dev); 9898c2ecf20Sopenharmony_ci pasemi_mac_intf_disable(mac); 9908c2ecf20Sopenharmony_ci mac->link = 0; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci return; 9938c2ecf20Sopenharmony_ci } else { 9948c2ecf20Sopenharmony_ci pasemi_mac_intf_enable(mac); 9958c2ecf20Sopenharmony_ci netif_carrier_on(dev); 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); 9998c2ecf20Sopenharmony_ci new_flags = flags & ~(PAS_MAC_CFG_PCFG_HD | PAS_MAC_CFG_PCFG_SPD_M | 10008c2ecf20Sopenharmony_ci PAS_MAC_CFG_PCFG_TSR_M); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (!dev->phydev->duplex) 10038c2ecf20Sopenharmony_ci new_flags |= PAS_MAC_CFG_PCFG_HD; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci switch (dev->phydev->speed) { 10068c2ecf20Sopenharmony_ci case 1000: 10078c2ecf20Sopenharmony_ci new_flags |= PAS_MAC_CFG_PCFG_SPD_1G | 10088c2ecf20Sopenharmony_ci PAS_MAC_CFG_PCFG_TSR_1G; 10098c2ecf20Sopenharmony_ci break; 10108c2ecf20Sopenharmony_ci case 100: 10118c2ecf20Sopenharmony_ci new_flags |= PAS_MAC_CFG_PCFG_SPD_100M | 10128c2ecf20Sopenharmony_ci PAS_MAC_CFG_PCFG_TSR_100M; 10138c2ecf20Sopenharmony_ci break; 10148c2ecf20Sopenharmony_ci case 10: 10158c2ecf20Sopenharmony_ci new_flags |= PAS_MAC_CFG_PCFG_SPD_10M | 10168c2ecf20Sopenharmony_ci PAS_MAC_CFG_PCFG_TSR_10M; 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci default: 10198c2ecf20Sopenharmony_ci printk("Unsupported speed %d\n", dev->phydev->speed); 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* Print on link or speed/duplex change */ 10238c2ecf20Sopenharmony_ci msg = mac->link != dev->phydev->link || flags != new_flags; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci mac->duplex = dev->phydev->duplex; 10268c2ecf20Sopenharmony_ci mac->speed = dev->phydev->speed; 10278c2ecf20Sopenharmony_ci mac->link = dev->phydev->link; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (new_flags != flags) 10308c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_CFG_PCFG, new_flags); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci if (msg && netif_msg_link(mac)) 10338c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Link is up at %d Mbps, %s duplex.\n", 10348c2ecf20Sopenharmony_ci dev->name, mac->speed, mac->duplex ? "full" : "half"); 10358c2ecf20Sopenharmony_ci} 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_cistatic int pasemi_mac_phy_init(struct net_device *dev) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci struct pasemi_mac *mac = netdev_priv(dev); 10408c2ecf20Sopenharmony_ci struct device_node *dn, *phy_dn; 10418c2ecf20Sopenharmony_ci struct phy_device *phydev; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci dn = pci_device_to_OF_node(mac->pdev); 10448c2ecf20Sopenharmony_ci phy_dn = of_parse_phandle(dn, "phy-handle", 0); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci mac->link = 0; 10478c2ecf20Sopenharmony_ci mac->speed = 0; 10488c2ecf20Sopenharmony_ci mac->duplex = -1; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci phydev = of_phy_connect(dev, phy_dn, &pasemi_adjust_link, 0, 10518c2ecf20Sopenharmony_ci PHY_INTERFACE_MODE_SGMII); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci of_node_put(phy_dn); 10548c2ecf20Sopenharmony_ci if (!phydev) { 10558c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Could not attach to phy\n", dev->name); 10568c2ecf20Sopenharmony_ci return -ENODEV; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci return 0; 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic int pasemi_mac_open(struct net_device *dev) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct pasemi_mac *mac = netdev_priv(dev); 10668c2ecf20Sopenharmony_ci unsigned int flags; 10678c2ecf20Sopenharmony_ci int i, ret; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci flags = PAS_MAC_CFG_TXP_FCE | PAS_MAC_CFG_TXP_FPC(3) | 10708c2ecf20Sopenharmony_ci PAS_MAC_CFG_TXP_SL(3) | PAS_MAC_CFG_TXP_COB(0xf) | 10718c2ecf20Sopenharmony_ci PAS_MAC_CFG_TXP_TIFT(8) | PAS_MAC_CFG_TXP_TIFG(12); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_CFG_TXP, flags); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci ret = pasemi_mac_setup_rx_resources(dev); 10768c2ecf20Sopenharmony_ci if (ret) 10778c2ecf20Sopenharmony_ci goto out_rx_resources; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci mac->tx = pasemi_mac_setup_tx_resources(dev); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (!mac->tx) { 10828c2ecf20Sopenharmony_ci ret = -ENOMEM; 10838c2ecf20Sopenharmony_ci goto out_tx_ring; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* We might already have allocated rings in case mtu was changed 10878c2ecf20Sopenharmony_ci * before interface was brought up. 10888c2ecf20Sopenharmony_ci */ 10898c2ecf20Sopenharmony_ci if (dev->mtu > 1500 && !mac->num_cs) { 10908c2ecf20Sopenharmony_ci pasemi_mac_setup_csrings(mac); 10918c2ecf20Sopenharmony_ci if (!mac->num_cs) { 10928c2ecf20Sopenharmony_ci ret = -ENOMEM; 10938c2ecf20Sopenharmony_ci goto out_tx_ring; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* Zero out rmon counters */ 10988c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) 10998c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_RMON(i), 0); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* 0x3ff with 33MHz clock is about 31us */ 11028c2ecf20Sopenharmony_ci write_iob_reg(PAS_IOB_DMA_COM_TIMEOUTCFG, 11038c2ecf20Sopenharmony_ci PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(0x3ff)); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci write_iob_reg(PAS_IOB_DMA_RXCH_CFG(mac->rx->chan.chno), 11068c2ecf20Sopenharmony_ci PAS_IOB_DMA_RXCH_CFG_CNTTH(256)); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci write_iob_reg(PAS_IOB_DMA_TXCH_CFG(mac->tx->chan.chno), 11098c2ecf20Sopenharmony_ci PAS_IOB_DMA_TXCH_CFG_CNTTH(32)); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_IPC_CHNL, 11128c2ecf20Sopenharmony_ci PAS_MAC_IPC_CHNL_DCHNO(mac->rx->chan.chno) | 11138c2ecf20Sopenharmony_ci PAS_MAC_IPC_CHNL_BCH(mac->rx->chan.chno)); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci /* enable rx if */ 11168c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 11178c2ecf20Sopenharmony_ci PAS_DMA_RXINT_RCMDSTA_EN | 11188c2ecf20Sopenharmony_ci PAS_DMA_RXINT_RCMDSTA_DROPS_M | 11198c2ecf20Sopenharmony_ci PAS_DMA_RXINT_RCMDSTA_BP | 11208c2ecf20Sopenharmony_ci PAS_DMA_RXINT_RCMDSTA_OO | 11218c2ecf20Sopenharmony_ci PAS_DMA_RXINT_RCMDSTA_BT); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci /* enable rx channel */ 11248c2ecf20Sopenharmony_ci pasemi_dma_start_chan(&rx_ring(mac)->chan, PAS_DMA_RXCHAN_CCMDSTA_DU | 11258c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_CCMDSTA_OD | 11268c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_CCMDSTA_FD | 11278c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_CCMDSTA_DT); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci /* enable tx channel */ 11308c2ecf20Sopenharmony_ci pasemi_dma_start_chan(&tx_ring(mac)->chan, PAS_DMA_TXCHAN_TCMDSTA_SZ | 11318c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_TCMDSTA_DB | 11328c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_TCMDSTA_DE | 11338c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_TCMDSTA_DA); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci pasemi_mac_replenish_rx_ring(dev, RX_RING_SIZE); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXCHAN_INCR(rx_ring(mac)->chan.chno), 11388c2ecf20Sopenharmony_ci RX_RING_SIZE>>1); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci /* Clear out any residual packet count state from firmware */ 11418c2ecf20Sopenharmony_ci pasemi_mac_restart_rx_intr(mac); 11428c2ecf20Sopenharmony_ci pasemi_mac_restart_tx_intr(mac); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci flags = PAS_MAC_CFG_PCFG_S1 | PAS_MAC_CFG_PCFG_PR | PAS_MAC_CFG_PCFG_CE; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (mac->type == MAC_TYPE_GMAC) 11478c2ecf20Sopenharmony_ci flags |= PAS_MAC_CFG_PCFG_TSR_1G | PAS_MAC_CFG_PCFG_SPD_1G; 11488c2ecf20Sopenharmony_ci else 11498c2ecf20Sopenharmony_ci flags |= PAS_MAC_CFG_PCFG_TSR_10G | PAS_MAC_CFG_PCFG_SPD_10G; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci /* Enable interface in MAC */ 11528c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci ret = pasemi_mac_phy_init(dev); 11558c2ecf20Sopenharmony_ci if (ret) { 11568c2ecf20Sopenharmony_ci /* Since we won't get link notification, just enable RX */ 11578c2ecf20Sopenharmony_ci pasemi_mac_intf_enable(mac); 11588c2ecf20Sopenharmony_ci if (mac->type == MAC_TYPE_GMAC) { 11598c2ecf20Sopenharmony_ci /* Warn for missing PHY on SGMII (1Gig) ports */ 11608c2ecf20Sopenharmony_ci dev_warn(&mac->pdev->dev, 11618c2ecf20Sopenharmony_ci "PHY init failed: %d.\n", ret); 11628c2ecf20Sopenharmony_ci dev_warn(&mac->pdev->dev, 11638c2ecf20Sopenharmony_ci "Defaulting to 1Gbit full duplex\n"); 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci netif_start_queue(dev); 11688c2ecf20Sopenharmony_ci napi_enable(&mac->napi); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci snprintf(mac->tx_irq_name, sizeof(mac->tx_irq_name), "%s tx", 11718c2ecf20Sopenharmony_ci dev->name); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci ret = request_irq(mac->tx->chan.irq, pasemi_mac_tx_intr, 0, 11748c2ecf20Sopenharmony_ci mac->tx_irq_name, mac->tx); 11758c2ecf20Sopenharmony_ci if (ret) { 11768c2ecf20Sopenharmony_ci dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n", 11778c2ecf20Sopenharmony_ci mac->tx->chan.irq, ret); 11788c2ecf20Sopenharmony_ci goto out_tx_int; 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci snprintf(mac->rx_irq_name, sizeof(mac->rx_irq_name), "%s rx", 11828c2ecf20Sopenharmony_ci dev->name); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci ret = request_irq(mac->rx->chan.irq, pasemi_mac_rx_intr, 0, 11858c2ecf20Sopenharmony_ci mac->rx_irq_name, mac->rx); 11868c2ecf20Sopenharmony_ci if (ret) { 11878c2ecf20Sopenharmony_ci dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n", 11888c2ecf20Sopenharmony_ci mac->rx->chan.irq, ret); 11898c2ecf20Sopenharmony_ci goto out_rx_int; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci if (dev->phydev) 11938c2ecf20Sopenharmony_ci phy_start(dev->phydev); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci timer_setup(&mac->tx->clean_timer, pasemi_mac_tx_timer, 0); 11968c2ecf20Sopenharmony_ci mod_timer(&mac->tx->clean_timer, jiffies + HZ); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return 0; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ciout_rx_int: 12018c2ecf20Sopenharmony_ci free_irq(mac->tx->chan.irq, mac->tx); 12028c2ecf20Sopenharmony_ciout_tx_int: 12038c2ecf20Sopenharmony_ci napi_disable(&mac->napi); 12048c2ecf20Sopenharmony_ci netif_stop_queue(dev); 12058c2ecf20Sopenharmony_ciout_tx_ring: 12068c2ecf20Sopenharmony_ci if (mac->tx) 12078c2ecf20Sopenharmony_ci pasemi_mac_free_tx_resources(mac); 12088c2ecf20Sopenharmony_ci pasemi_mac_free_rx_resources(mac); 12098c2ecf20Sopenharmony_ciout_rx_resources: 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci return ret; 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci#define MAX_RETRIES 5000 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cistatic void pasemi_mac_pause_txchan(struct pasemi_mac *mac) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci unsigned int sta, retries; 12198c2ecf20Sopenharmony_ci int txch = tx_ring(mac)->chan.chno; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 12228c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_TCMDSTA_ST); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci for (retries = 0; retries < MAX_RETRIES; retries++) { 12258c2ecf20Sopenharmony_ci sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch)); 12268c2ecf20Sopenharmony_ci if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)) 12278c2ecf20Sopenharmony_ci break; 12288c2ecf20Sopenharmony_ci cond_resched(); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT) 12328c2ecf20Sopenharmony_ci dev_err(&mac->dma_pdev->dev, 12338c2ecf20Sopenharmony_ci "Failed to stop tx channel, tcmdsta %08x\n", sta); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0); 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_cistatic void pasemi_mac_pause_rxchan(struct pasemi_mac *mac) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci unsigned int sta, retries; 12418c2ecf20Sopenharmony_ci int rxch = rx_ring(mac)->chan.chno; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 12448c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_CCMDSTA_ST); 12458c2ecf20Sopenharmony_ci for (retries = 0; retries < MAX_RETRIES; retries++) { 12468c2ecf20Sopenharmony_ci sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch)); 12478c2ecf20Sopenharmony_ci if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)) 12488c2ecf20Sopenharmony_ci break; 12498c2ecf20Sopenharmony_ci cond_resched(); 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT) 12538c2ecf20Sopenharmony_ci dev_err(&mac->dma_pdev->dev, 12548c2ecf20Sopenharmony_ci "Failed to stop rx channel, ccmdsta 08%x\n", sta); 12558c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0); 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic void pasemi_mac_pause_rxint(struct pasemi_mac *mac) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci unsigned int sta, retries; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 12638c2ecf20Sopenharmony_ci PAS_DMA_RXINT_RCMDSTA_ST); 12648c2ecf20Sopenharmony_ci for (retries = 0; retries < MAX_RETRIES; retries++) { 12658c2ecf20Sopenharmony_ci sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); 12668c2ecf20Sopenharmony_ci if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT)) 12678c2ecf20Sopenharmony_ci break; 12688c2ecf20Sopenharmony_ci cond_resched(); 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci if (sta & PAS_DMA_RXINT_RCMDSTA_ACT) 12728c2ecf20Sopenharmony_ci dev_err(&mac->dma_pdev->dev, 12738c2ecf20Sopenharmony_ci "Failed to stop rx interface, rcmdsta %08x\n", sta); 12748c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0); 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic int pasemi_mac_close(struct net_device *dev) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci struct pasemi_mac *mac = netdev_priv(dev); 12808c2ecf20Sopenharmony_ci unsigned int sta; 12818c2ecf20Sopenharmony_ci int rxch, txch, i; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci rxch = rx_ring(mac)->chan.chno; 12848c2ecf20Sopenharmony_ci txch = tx_ring(mac)->chan.chno; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (dev->phydev) { 12878c2ecf20Sopenharmony_ci phy_stop(dev->phydev); 12888c2ecf20Sopenharmony_ci phy_disconnect(dev->phydev); 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci del_timer_sync(&mac->tx->clean_timer); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci netif_stop_queue(dev); 12948c2ecf20Sopenharmony_ci napi_disable(&mac->napi); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); 12978c2ecf20Sopenharmony_ci if (sta & (PAS_DMA_RXINT_RCMDSTA_BP | 12988c2ecf20Sopenharmony_ci PAS_DMA_RXINT_RCMDSTA_OO | 12998c2ecf20Sopenharmony_ci PAS_DMA_RXINT_RCMDSTA_BT)) 13008c2ecf20Sopenharmony_ci printk(KERN_DEBUG "pasemi_mac: rcmdsta error: 0x%08x\n", sta); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch)); 13038c2ecf20Sopenharmony_ci if (sta & (PAS_DMA_RXCHAN_CCMDSTA_DU | 13048c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_CCMDSTA_OD | 13058c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_CCMDSTA_FD | 13068c2ecf20Sopenharmony_ci PAS_DMA_RXCHAN_CCMDSTA_DT)) 13078c2ecf20Sopenharmony_ci printk(KERN_DEBUG "pasemi_mac: ccmdsta error: 0x%08x\n", sta); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch)); 13108c2ecf20Sopenharmony_ci if (sta & (PAS_DMA_TXCHAN_TCMDSTA_SZ | PAS_DMA_TXCHAN_TCMDSTA_DB | 13118c2ecf20Sopenharmony_ci PAS_DMA_TXCHAN_TCMDSTA_DE | PAS_DMA_TXCHAN_TCMDSTA_DA)) 13128c2ecf20Sopenharmony_ci printk(KERN_DEBUG "pasemi_mac: tcmdsta error: 0x%08x\n", sta); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* Clean out any pending buffers */ 13158c2ecf20Sopenharmony_ci pasemi_mac_clean_tx(tx_ring(mac)); 13168c2ecf20Sopenharmony_ci pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci pasemi_mac_pause_txchan(mac); 13198c2ecf20Sopenharmony_ci pasemi_mac_pause_rxint(mac); 13208c2ecf20Sopenharmony_ci pasemi_mac_pause_rxchan(mac); 13218c2ecf20Sopenharmony_ci pasemi_mac_intf_disable(mac); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci free_irq(mac->tx->chan.irq, mac->tx); 13248c2ecf20Sopenharmony_ci free_irq(mac->rx->chan.irq, mac->rx); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci for (i = 0; i < mac->num_cs; i++) { 13278c2ecf20Sopenharmony_ci pasemi_mac_free_csring(mac->cs[i]); 13288c2ecf20Sopenharmony_ci mac->cs[i] = NULL; 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci mac->num_cs = 0; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci /* Free resources */ 13348c2ecf20Sopenharmony_ci pasemi_mac_free_rx_resources(mac); 13358c2ecf20Sopenharmony_ci pasemi_mac_free_tx_resources(mac); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci return 0; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic void pasemi_mac_queue_csdesc(const struct sk_buff *skb, 13418c2ecf20Sopenharmony_ci const dma_addr_t *map, 13428c2ecf20Sopenharmony_ci const unsigned int *map_size, 13438c2ecf20Sopenharmony_ci struct pasemi_mac_txring *txring, 13448c2ecf20Sopenharmony_ci struct pasemi_mac_csring *csring) 13458c2ecf20Sopenharmony_ci{ 13468c2ecf20Sopenharmony_ci u64 fund; 13478c2ecf20Sopenharmony_ci dma_addr_t cs_dest; 13488c2ecf20Sopenharmony_ci const int nh_off = skb_network_offset(skb); 13498c2ecf20Sopenharmony_ci const int nh_len = skb_network_header_len(skb); 13508c2ecf20Sopenharmony_ci const int nfrags = skb_shinfo(skb)->nr_frags; 13518c2ecf20Sopenharmony_ci int cs_size, i, fill, hdr, evt; 13528c2ecf20Sopenharmony_ci dma_addr_t csdma; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci fund = XCT_FUN_ST | XCT_FUN_RR_8BRES | 13558c2ecf20Sopenharmony_ci XCT_FUN_O | XCT_FUN_FUN(csring->fun) | 13568c2ecf20Sopenharmony_ci XCT_FUN_CRM_SIG | XCT_FUN_LLEN(skb->len - nh_off) | 13578c2ecf20Sopenharmony_ci XCT_FUN_SHL(nh_len >> 2) | XCT_FUN_SE; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci switch (ip_hdr(skb)->protocol) { 13608c2ecf20Sopenharmony_ci case IPPROTO_TCP: 13618c2ecf20Sopenharmony_ci fund |= XCT_FUN_SIG_TCP4; 13628c2ecf20Sopenharmony_ci /* TCP checksum is 16 bytes into the header */ 13638c2ecf20Sopenharmony_ci cs_dest = map[0] + skb_transport_offset(skb) + 16; 13648c2ecf20Sopenharmony_ci break; 13658c2ecf20Sopenharmony_ci case IPPROTO_UDP: 13668c2ecf20Sopenharmony_ci fund |= XCT_FUN_SIG_UDP4; 13678c2ecf20Sopenharmony_ci /* UDP checksum is 6 bytes into the header */ 13688c2ecf20Sopenharmony_ci cs_dest = map[0] + skb_transport_offset(skb) + 6; 13698c2ecf20Sopenharmony_ci break; 13708c2ecf20Sopenharmony_ci default: 13718c2ecf20Sopenharmony_ci BUG(); 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci /* Do the checksum offloaded */ 13758c2ecf20Sopenharmony_ci fill = csring->next_to_fill; 13768c2ecf20Sopenharmony_ci hdr = fill; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci CS_DESC(csring, fill++) = fund; 13798c2ecf20Sopenharmony_ci /* Room for 8BRES. Checksum result is really 2 bytes into it */ 13808c2ecf20Sopenharmony_ci csdma = csring->chan.ring_dma + (fill & (CS_RING_SIZE-1)) * 8 + 2; 13818c2ecf20Sopenharmony_ci CS_DESC(csring, fill++) = 0; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci CS_DESC(csring, fill) = XCT_PTR_LEN(map_size[0]-nh_off) | XCT_PTR_ADDR(map[0]+nh_off); 13848c2ecf20Sopenharmony_ci for (i = 1; i <= nfrags; i++) 13858c2ecf20Sopenharmony_ci CS_DESC(csring, fill+i) = XCT_PTR_LEN(map_size[i]) | XCT_PTR_ADDR(map[i]); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci fill += i; 13888c2ecf20Sopenharmony_ci if (fill & 1) 13898c2ecf20Sopenharmony_ci fill++; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci /* Copy the result into the TCP packet */ 13928c2ecf20Sopenharmony_ci CS_DESC(csring, fill++) = XCT_FUN_O | XCT_FUN_FUN(csring->fun) | 13938c2ecf20Sopenharmony_ci XCT_FUN_LLEN(2) | XCT_FUN_SE; 13948c2ecf20Sopenharmony_ci CS_DESC(csring, fill++) = XCT_PTR_LEN(2) | XCT_PTR_ADDR(cs_dest) | XCT_PTR_T; 13958c2ecf20Sopenharmony_ci CS_DESC(csring, fill++) = XCT_PTR_LEN(2) | XCT_PTR_ADDR(csdma); 13968c2ecf20Sopenharmony_ci fill++; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci evt = !csring->last_event; 13998c2ecf20Sopenharmony_ci csring->last_event = evt; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci /* Event handshaking with MAC TX */ 14028c2ecf20Sopenharmony_ci CS_DESC(csring, fill++) = CTRL_CMD_T | CTRL_CMD_META_EVT | CTRL_CMD_O | 14038c2ecf20Sopenharmony_ci CTRL_CMD_ETYPE_SET | CTRL_CMD_REG(csring->events[evt]); 14048c2ecf20Sopenharmony_ci CS_DESC(csring, fill++) = 0; 14058c2ecf20Sopenharmony_ci CS_DESC(csring, fill++) = CTRL_CMD_T | CTRL_CMD_META_EVT | CTRL_CMD_O | 14068c2ecf20Sopenharmony_ci CTRL_CMD_ETYPE_WCLR | CTRL_CMD_REG(csring->events[!evt]); 14078c2ecf20Sopenharmony_ci CS_DESC(csring, fill++) = 0; 14088c2ecf20Sopenharmony_ci csring->next_to_fill = fill & (CS_RING_SIZE-1); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci cs_size = fill - hdr; 14118c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_INCR(csring->chan.chno), (cs_size) >> 1); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* TX-side event handshaking */ 14148c2ecf20Sopenharmony_ci fill = txring->next_to_fill; 14158c2ecf20Sopenharmony_ci TX_DESC(txring, fill++) = CTRL_CMD_T | CTRL_CMD_META_EVT | CTRL_CMD_O | 14168c2ecf20Sopenharmony_ci CTRL_CMD_ETYPE_WSET | CTRL_CMD_REG(csring->events[evt]); 14178c2ecf20Sopenharmony_ci TX_DESC(txring, fill++) = 0; 14188c2ecf20Sopenharmony_ci TX_DESC(txring, fill++) = CTRL_CMD_T | CTRL_CMD_META_EVT | CTRL_CMD_O | 14198c2ecf20Sopenharmony_ci CTRL_CMD_ETYPE_CLR | CTRL_CMD_REG(csring->events[!evt]); 14208c2ecf20Sopenharmony_ci TX_DESC(txring, fill++) = 0; 14218c2ecf20Sopenharmony_ci txring->next_to_fill = fill; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_INCR(txring->chan.chno), 2); 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic netdev_tx_t pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci struct pasemi_mac * const mac = netdev_priv(dev); 14298c2ecf20Sopenharmony_ci struct pasemi_mac_txring * const txring = tx_ring(mac); 14308c2ecf20Sopenharmony_ci struct pasemi_mac_csring *csring; 14318c2ecf20Sopenharmony_ci u64 dflags = 0; 14328c2ecf20Sopenharmony_ci u64 mactx; 14338c2ecf20Sopenharmony_ci dma_addr_t map[MAX_SKB_FRAGS+1]; 14348c2ecf20Sopenharmony_ci unsigned int map_size[MAX_SKB_FRAGS+1]; 14358c2ecf20Sopenharmony_ci unsigned long flags; 14368c2ecf20Sopenharmony_ci int i, nfrags; 14378c2ecf20Sopenharmony_ci int fill; 14388c2ecf20Sopenharmony_ci const int nh_off = skb_network_offset(skb); 14398c2ecf20Sopenharmony_ci const int nh_len = skb_network_header_len(skb); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci prefetch(&txring->ring_info); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci dflags = XCT_MACTX_O | XCT_MACTX_ST | XCT_MACTX_CRC_PAD; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci nfrags = skb_shinfo(skb)->nr_frags; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci map[0] = pci_map_single(mac->dma_pdev, skb->data, skb_headlen(skb), 14488c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 14498c2ecf20Sopenharmony_ci map_size[0] = skb_headlen(skb); 14508c2ecf20Sopenharmony_ci if (pci_dma_mapping_error(mac->dma_pdev, map[0])) 14518c2ecf20Sopenharmony_ci goto out_err_nolock; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci for (i = 0; i < nfrags; i++) { 14548c2ecf20Sopenharmony_ci skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci map[i + 1] = skb_frag_dma_map(&mac->dma_pdev->dev, frag, 0, 14578c2ecf20Sopenharmony_ci skb_frag_size(frag), DMA_TO_DEVICE); 14588c2ecf20Sopenharmony_ci map_size[i+1] = skb_frag_size(frag); 14598c2ecf20Sopenharmony_ci if (dma_mapping_error(&mac->dma_pdev->dev, map[i + 1])) { 14608c2ecf20Sopenharmony_ci nfrags = i; 14618c2ecf20Sopenharmony_ci goto out_err_nolock; 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL && skb->len <= 1540) { 14668c2ecf20Sopenharmony_ci switch (ip_hdr(skb)->protocol) { 14678c2ecf20Sopenharmony_ci case IPPROTO_TCP: 14688c2ecf20Sopenharmony_ci dflags |= XCT_MACTX_CSUM_TCP; 14698c2ecf20Sopenharmony_ci dflags |= XCT_MACTX_IPH(nh_len >> 2); 14708c2ecf20Sopenharmony_ci dflags |= XCT_MACTX_IPO(nh_off); 14718c2ecf20Sopenharmony_ci break; 14728c2ecf20Sopenharmony_ci case IPPROTO_UDP: 14738c2ecf20Sopenharmony_ci dflags |= XCT_MACTX_CSUM_UDP; 14748c2ecf20Sopenharmony_ci dflags |= XCT_MACTX_IPH(nh_len >> 2); 14758c2ecf20Sopenharmony_ci dflags |= XCT_MACTX_IPO(nh_off); 14768c2ecf20Sopenharmony_ci break; 14778c2ecf20Sopenharmony_ci default: 14788c2ecf20Sopenharmony_ci WARN_ON(1); 14798c2ecf20Sopenharmony_ci } 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci mactx = dflags | XCT_MACTX_LLEN(skb->len); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci spin_lock_irqsave(&txring->lock, flags); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci /* Avoid stepping on the same cache line that the DMA controller 14878c2ecf20Sopenharmony_ci * is currently about to send, so leave at least 8 words available. 14888c2ecf20Sopenharmony_ci * Total free space needed is mactx + fragments + 8 14898c2ecf20Sopenharmony_ci */ 14908c2ecf20Sopenharmony_ci if (RING_AVAIL(txring) < nfrags + 14) { 14918c2ecf20Sopenharmony_ci /* no room -- stop the queue and wait for tx intr */ 14928c2ecf20Sopenharmony_ci netif_stop_queue(dev); 14938c2ecf20Sopenharmony_ci goto out_err; 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* Queue up checksum + event descriptors, if needed */ 14978c2ecf20Sopenharmony_ci if (mac->num_cs && skb->ip_summed == CHECKSUM_PARTIAL && skb->len > 1540) { 14988c2ecf20Sopenharmony_ci csring = mac->cs[mac->last_cs]; 14998c2ecf20Sopenharmony_ci mac->last_cs = (mac->last_cs + 1) % mac->num_cs; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci pasemi_mac_queue_csdesc(skb, map, map_size, txring, csring); 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci fill = txring->next_to_fill; 15058c2ecf20Sopenharmony_ci TX_DESC(txring, fill) = mactx; 15068c2ecf20Sopenharmony_ci TX_DESC_INFO(txring, fill).dma = nfrags; 15078c2ecf20Sopenharmony_ci fill++; 15088c2ecf20Sopenharmony_ci TX_DESC_INFO(txring, fill).skb = skb; 15098c2ecf20Sopenharmony_ci for (i = 0; i <= nfrags; i++) { 15108c2ecf20Sopenharmony_ci TX_DESC(txring, fill+i) = 15118c2ecf20Sopenharmony_ci XCT_PTR_LEN(map_size[i]) | XCT_PTR_ADDR(map[i]); 15128c2ecf20Sopenharmony_ci TX_DESC_INFO(txring, fill+i).dma = map[i]; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci /* We have to add an even number of 8-byte entries to the ring 15168c2ecf20Sopenharmony_ci * even if the last one is unused. That means always an odd number 15178c2ecf20Sopenharmony_ci * of pointers + one mactx descriptor. 15188c2ecf20Sopenharmony_ci */ 15198c2ecf20Sopenharmony_ci if (nfrags & 1) 15208c2ecf20Sopenharmony_ci nfrags++; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci txring->next_to_fill = (fill + nfrags + 1) & (TX_RING_SIZE-1); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 15258c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&txring->lock, flags); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_TXCHAN_INCR(txring->chan.chno), (nfrags+2) >> 1); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ciout_err: 15348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&txring->lock, flags); 15358c2ecf20Sopenharmony_ciout_err_nolock: 15368c2ecf20Sopenharmony_ci while (nfrags--) 15378c2ecf20Sopenharmony_ci pci_unmap_single(mac->dma_pdev, map[nfrags], map_size[nfrags], 15388c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic void pasemi_mac_set_rx_mode(struct net_device *dev) 15448c2ecf20Sopenharmony_ci{ 15458c2ecf20Sopenharmony_ci const struct pasemi_mac *mac = netdev_priv(dev); 15468c2ecf20Sopenharmony_ci unsigned int flags; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci /* Set promiscuous */ 15518c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) 15528c2ecf20Sopenharmony_ci flags |= PAS_MAC_CFG_PCFG_PR; 15538c2ecf20Sopenharmony_ci else 15548c2ecf20Sopenharmony_ci flags &= ~PAS_MAC_CFG_PCFG_PR; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); 15578c2ecf20Sopenharmony_ci} 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_cistatic int pasemi_mac_poll(struct napi_struct *napi, int budget) 15618c2ecf20Sopenharmony_ci{ 15628c2ecf20Sopenharmony_ci struct pasemi_mac *mac = container_of(napi, struct pasemi_mac, napi); 15638c2ecf20Sopenharmony_ci int pkts; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci pasemi_mac_clean_tx(tx_ring(mac)); 15668c2ecf20Sopenharmony_ci pkts = pasemi_mac_clean_rx(rx_ring(mac), budget); 15678c2ecf20Sopenharmony_ci if (pkts < budget) { 15688c2ecf20Sopenharmony_ci /* all done, no more packets present */ 15698c2ecf20Sopenharmony_ci napi_complete_done(napi, pkts); 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci pasemi_mac_restart_rx_intr(mac); 15728c2ecf20Sopenharmony_ci pasemi_mac_restart_tx_intr(mac); 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci return pkts; 15758c2ecf20Sopenharmony_ci} 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 15788c2ecf20Sopenharmony_ci/* 15798c2ecf20Sopenharmony_ci * Polling 'interrupt' - used by things like netconsole to send skbs 15808c2ecf20Sopenharmony_ci * without having to re-enable interrupts. It's not called while 15818c2ecf20Sopenharmony_ci * the interrupt routine is executing. 15828c2ecf20Sopenharmony_ci */ 15838c2ecf20Sopenharmony_cistatic void pasemi_mac_netpoll(struct net_device *dev) 15848c2ecf20Sopenharmony_ci{ 15858c2ecf20Sopenharmony_ci const struct pasemi_mac *mac = netdev_priv(dev); 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci disable_irq(mac->tx->chan.irq); 15888c2ecf20Sopenharmony_ci pasemi_mac_tx_intr(mac->tx->chan.irq, mac->tx); 15898c2ecf20Sopenharmony_ci enable_irq(mac->tx->chan.irq); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci disable_irq(mac->rx->chan.irq); 15928c2ecf20Sopenharmony_ci pasemi_mac_rx_intr(mac->rx->chan.irq, mac->rx); 15938c2ecf20Sopenharmony_ci enable_irq(mac->rx->chan.irq); 15948c2ecf20Sopenharmony_ci} 15958c2ecf20Sopenharmony_ci#endif 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_cistatic int pasemi_mac_change_mtu(struct net_device *dev, int new_mtu) 15988c2ecf20Sopenharmony_ci{ 15998c2ecf20Sopenharmony_ci struct pasemi_mac *mac = netdev_priv(dev); 16008c2ecf20Sopenharmony_ci unsigned int reg; 16018c2ecf20Sopenharmony_ci unsigned int rcmdsta = 0; 16028c2ecf20Sopenharmony_ci int running; 16038c2ecf20Sopenharmony_ci int ret = 0; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci running = netif_running(dev); 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci if (running) { 16088c2ecf20Sopenharmony_ci /* Need to stop the interface, clean out all already 16098c2ecf20Sopenharmony_ci * received buffers, free all unused buffers on the RX 16108c2ecf20Sopenharmony_ci * interface ring, then finally re-fill the rx ring with 16118c2ecf20Sopenharmony_ci * the new-size buffers and restart. 16128c2ecf20Sopenharmony_ci */ 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci napi_disable(&mac->napi); 16158c2ecf20Sopenharmony_ci netif_tx_disable(dev); 16168c2ecf20Sopenharmony_ci pasemi_mac_intf_disable(mac); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci rcmdsta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); 16198c2ecf20Sopenharmony_ci pasemi_mac_pause_rxint(mac); 16208c2ecf20Sopenharmony_ci pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE); 16218c2ecf20Sopenharmony_ci pasemi_mac_free_rx_buffers(mac); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci /* Setup checksum channels if large MTU and none already allocated */ 16268c2ecf20Sopenharmony_ci if (new_mtu > PE_DEF_MTU && !mac->num_cs) { 16278c2ecf20Sopenharmony_ci pasemi_mac_setup_csrings(mac); 16288c2ecf20Sopenharmony_ci if (!mac->num_cs) { 16298c2ecf20Sopenharmony_ci ret = -ENOMEM; 16308c2ecf20Sopenharmony_ci goto out; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci /* Change maxf, i.e. what size frames are accepted. 16358c2ecf20Sopenharmony_ci * Need room for ethernet header and CRC word 16368c2ecf20Sopenharmony_ci */ 16378c2ecf20Sopenharmony_ci reg = read_mac_reg(mac, PAS_MAC_CFG_MACCFG); 16388c2ecf20Sopenharmony_ci reg &= ~PAS_MAC_CFG_MACCFG_MAXF_M; 16398c2ecf20Sopenharmony_ci reg |= PAS_MAC_CFG_MACCFG_MAXF(new_mtu + ETH_HLEN + 4); 16408c2ecf20Sopenharmony_ci write_mac_reg(mac, PAS_MAC_CFG_MACCFG, reg); 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 16438c2ecf20Sopenharmony_ci /* MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ 16448c2ecf20Sopenharmony_ci mac->bufsz = new_mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ciout: 16478c2ecf20Sopenharmony_ci if (running) { 16488c2ecf20Sopenharmony_ci write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 16498c2ecf20Sopenharmony_ci rcmdsta | PAS_DMA_RXINT_RCMDSTA_EN); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci rx_ring(mac)->next_to_fill = 0; 16528c2ecf20Sopenharmony_ci pasemi_mac_replenish_rx_ring(dev, RX_RING_SIZE-1); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci napi_enable(&mac->napi); 16558c2ecf20Sopenharmony_ci netif_start_queue(dev); 16568c2ecf20Sopenharmony_ci pasemi_mac_intf_enable(mac); 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci return ret; 16608c2ecf20Sopenharmony_ci} 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_cistatic const struct net_device_ops pasemi_netdev_ops = { 16638c2ecf20Sopenharmony_ci .ndo_open = pasemi_mac_open, 16648c2ecf20Sopenharmony_ci .ndo_stop = pasemi_mac_close, 16658c2ecf20Sopenharmony_ci .ndo_start_xmit = pasemi_mac_start_tx, 16668c2ecf20Sopenharmony_ci .ndo_set_rx_mode = pasemi_mac_set_rx_mode, 16678c2ecf20Sopenharmony_ci .ndo_set_mac_address = pasemi_mac_set_mac_addr, 16688c2ecf20Sopenharmony_ci .ndo_change_mtu = pasemi_mac_change_mtu, 16698c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 16708c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 16718c2ecf20Sopenharmony_ci .ndo_poll_controller = pasemi_mac_netpoll, 16728c2ecf20Sopenharmony_ci#endif 16738c2ecf20Sopenharmony_ci}; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_cistatic int 16768c2ecf20Sopenharmony_cipasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 16778c2ecf20Sopenharmony_ci{ 16788c2ecf20Sopenharmony_ci struct net_device *dev; 16798c2ecf20Sopenharmony_ci struct pasemi_mac *mac; 16808c2ecf20Sopenharmony_ci int err, ret; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 16838c2ecf20Sopenharmony_ci if (err) 16848c2ecf20Sopenharmony_ci return err; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct pasemi_mac)); 16878c2ecf20Sopenharmony_ci if (dev == NULL) { 16888c2ecf20Sopenharmony_ci err = -ENOMEM; 16898c2ecf20Sopenharmony_ci goto out_disable_device; 16908c2ecf20Sopenharmony_ci } 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 16938c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci mac = netdev_priv(dev); 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci mac->pdev = pdev; 16988c2ecf20Sopenharmony_ci mac->netdev = dev; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci netif_napi_add(dev, &mac->napi, pasemi_mac_poll, 64); 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci dev->features = NETIF_F_IP_CSUM | NETIF_F_LLTX | NETIF_F_SG | 17038c2ecf20Sopenharmony_ci NETIF_F_HIGHDMA | NETIF_F_GSO; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci mac->dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL); 17068c2ecf20Sopenharmony_ci if (!mac->dma_pdev) { 17078c2ecf20Sopenharmony_ci dev_err(&mac->pdev->dev, "Can't find DMA Controller\n"); 17088c2ecf20Sopenharmony_ci err = -ENODEV; 17098c2ecf20Sopenharmony_ci goto out; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci dma_set_mask(&mac->dma_pdev->dev, DMA_BIT_MASK(64)); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci mac->iob_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL); 17148c2ecf20Sopenharmony_ci if (!mac->iob_pdev) { 17158c2ecf20Sopenharmony_ci dev_err(&mac->pdev->dev, "Can't find I/O Bridge\n"); 17168c2ecf20Sopenharmony_ci err = -ENODEV; 17178c2ecf20Sopenharmony_ci goto out; 17188c2ecf20Sopenharmony_ci } 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci /* get mac addr from device tree */ 17218c2ecf20Sopenharmony_ci if (pasemi_get_mac_addr(mac) || !is_valid_ether_addr(mac->mac_addr)) { 17228c2ecf20Sopenharmony_ci err = -ENODEV; 17238c2ecf20Sopenharmony_ci goto out; 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, mac->mac_addr, sizeof(mac->mac_addr)); 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci ret = mac_to_intf(mac); 17288c2ecf20Sopenharmony_ci if (ret < 0) { 17298c2ecf20Sopenharmony_ci dev_err(&mac->pdev->dev, "Can't map DMA interface\n"); 17308c2ecf20Sopenharmony_ci err = -ENODEV; 17318c2ecf20Sopenharmony_ci goto out; 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci mac->dma_if = ret; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci switch (pdev->device) { 17368c2ecf20Sopenharmony_ci case 0xa005: 17378c2ecf20Sopenharmony_ci mac->type = MAC_TYPE_GMAC; 17388c2ecf20Sopenharmony_ci break; 17398c2ecf20Sopenharmony_ci case 0xa006: 17408c2ecf20Sopenharmony_ci mac->type = MAC_TYPE_XAUI; 17418c2ecf20Sopenharmony_ci break; 17428c2ecf20Sopenharmony_ci default: 17438c2ecf20Sopenharmony_ci err = -ENODEV; 17448c2ecf20Sopenharmony_ci goto out; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci dev->netdev_ops = &pasemi_netdev_ops; 17488c2ecf20Sopenharmony_ci dev->mtu = PE_DEF_MTU; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci /* MTU range: 64 - 9000 */ 17518c2ecf20Sopenharmony_ci dev->min_mtu = PE_MIN_MTU; 17528c2ecf20Sopenharmony_ci dev->max_mtu = PE_MAX_MTU; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ 17558c2ecf20Sopenharmony_ci mac->bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci dev->ethtool_ops = &pasemi_mac_ethtool_ops; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci if (err) 17608c2ecf20Sopenharmony_ci goto out; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci mac->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci /* Enable most messages by default */ 17658c2ecf20Sopenharmony_ci mac->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci err = register_netdev(dev); 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci if (err) { 17708c2ecf20Sopenharmony_ci dev_err(&mac->pdev->dev, "register_netdev failed with error %d\n", 17718c2ecf20Sopenharmony_ci err); 17728c2ecf20Sopenharmony_ci goto out; 17738c2ecf20Sopenharmony_ci } else if (netif_msg_probe(mac)) { 17748c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: PA Semi %s: intf %d, hw addr %pM\n", 17758c2ecf20Sopenharmony_ci dev->name, mac->type == MAC_TYPE_GMAC ? "GMAC" : "XAUI", 17768c2ecf20Sopenharmony_ci mac->dma_if, dev->dev_addr); 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci return err; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ciout: 17828c2ecf20Sopenharmony_ci pci_dev_put(mac->iob_pdev); 17838c2ecf20Sopenharmony_ci pci_dev_put(mac->dma_pdev); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci free_netdev(dev); 17868c2ecf20Sopenharmony_ciout_disable_device: 17878c2ecf20Sopenharmony_ci pci_disable_device(pdev); 17888c2ecf20Sopenharmony_ci return err; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci} 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_cistatic void pasemi_mac_remove(struct pci_dev *pdev) 17938c2ecf20Sopenharmony_ci{ 17948c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 17958c2ecf20Sopenharmony_ci struct pasemi_mac *mac; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci if (!netdev) 17988c2ecf20Sopenharmony_ci return; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci mac = netdev_priv(netdev); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci unregister_netdev(netdev); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci pci_disable_device(pdev); 18058c2ecf20Sopenharmony_ci pci_dev_put(mac->dma_pdev); 18068c2ecf20Sopenharmony_ci pci_dev_put(mac->iob_pdev); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci pasemi_dma_free_chan(&mac->tx->chan); 18098c2ecf20Sopenharmony_ci pasemi_dma_free_chan(&mac->rx->chan); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci free_netdev(netdev); 18128c2ecf20Sopenharmony_ci} 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_cistatic const struct pci_device_id pasemi_mac_pci_tbl[] = { 18158c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa005) }, 18168c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa006) }, 18178c2ecf20Sopenharmony_ci { }, 18188c2ecf20Sopenharmony_ci}; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pasemi_mac_pci_tbl); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_cistatic struct pci_driver pasemi_mac_driver = { 18238c2ecf20Sopenharmony_ci .name = "pasemi_mac", 18248c2ecf20Sopenharmony_ci .id_table = pasemi_mac_pci_tbl, 18258c2ecf20Sopenharmony_ci .probe = pasemi_mac_probe, 18268c2ecf20Sopenharmony_ci .remove = pasemi_mac_remove, 18278c2ecf20Sopenharmony_ci}; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_cistatic void __exit pasemi_mac_cleanup_module(void) 18308c2ecf20Sopenharmony_ci{ 18318c2ecf20Sopenharmony_ci pci_unregister_driver(&pasemi_mac_driver); 18328c2ecf20Sopenharmony_ci} 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_cistatic int pasemi_mac_init_module(void) 18358c2ecf20Sopenharmony_ci{ 18368c2ecf20Sopenharmony_ci int err; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci err = pasemi_dma_init(); 18398c2ecf20Sopenharmony_ci if (err) 18408c2ecf20Sopenharmony_ci return err; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci return pci_register_driver(&pasemi_mac_driver); 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_cimodule_init(pasemi_mac_init_module); 18468c2ecf20Sopenharmony_cimodule_exit(pasemi_mac_cleanup_module); 1847