18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Applied Micro X-Gene SoC Ethernet v2 Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2017, Applied Micro Circuits Corporation 68c2ecf20Sopenharmony_ci * Author(s): Iyappan Subramanian <isubramanian@apm.com> 78c2ecf20Sopenharmony_ci * Keyur Chudgar <kchudgar@apm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "main.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic const struct acpi_device_id xge_acpi_match[]; 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic int xge_get_resources(struct xge_pdata *pdata) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci struct platform_device *pdev; 178c2ecf20Sopenharmony_ci struct net_device *ndev; 188c2ecf20Sopenharmony_ci int phy_mode, ret = 0; 198c2ecf20Sopenharmony_ci struct resource *res; 208c2ecf20Sopenharmony_ci struct device *dev; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci pdev = pdata->pdev; 238c2ecf20Sopenharmony_ci dev = &pdev->dev; 248c2ecf20Sopenharmony_ci ndev = pdata->ndev; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 278c2ecf20Sopenharmony_ci if (!res) { 288c2ecf20Sopenharmony_ci dev_err(dev, "Resource enet_csr not defined\n"); 298c2ecf20Sopenharmony_ci return -ENODEV; 308c2ecf20Sopenharmony_ci } 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci pdata->resources.base_addr = devm_ioremap(dev, res->start, 338c2ecf20Sopenharmony_ci resource_size(res)); 348c2ecf20Sopenharmony_ci if (!pdata->resources.base_addr) { 358c2ecf20Sopenharmony_ci dev_err(dev, "Unable to retrieve ENET Port CSR region\n"); 368c2ecf20Sopenharmony_ci return -ENOMEM; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (!device_get_mac_address(dev, ndev->dev_addr, ETH_ALEN)) 408c2ecf20Sopenharmony_ci eth_hw_addr_random(ndev); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci phy_mode = device_get_phy_mode(dev); 458c2ecf20Sopenharmony_ci if (phy_mode < 0) { 468c2ecf20Sopenharmony_ci dev_err(dev, "Unable to get phy-connection-type\n"); 478c2ecf20Sopenharmony_ci return phy_mode; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci pdata->resources.phy_mode = phy_mode; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (pdata->resources.phy_mode != PHY_INTERFACE_MODE_RGMII) { 528c2ecf20Sopenharmony_ci dev_err(dev, "Incorrect phy-connection-type specified\n"); 538c2ecf20Sopenharmony_ci return -ENODEV; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 0); 578c2ecf20Sopenharmony_ci if (ret < 0) 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci pdata->resources.irq = ret; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int xge_refill_buffers(struct net_device *ndev, u32 nbuf) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 678c2ecf20Sopenharmony_ci struct xge_desc_ring *ring = pdata->rx_ring; 688c2ecf20Sopenharmony_ci const u8 slots = XGENE_ENET_NUM_DESC - 1; 698c2ecf20Sopenharmony_ci struct device *dev = &pdata->pdev->dev; 708c2ecf20Sopenharmony_ci struct xge_raw_desc *raw_desc; 718c2ecf20Sopenharmony_ci u64 addr_lo, addr_hi; 728c2ecf20Sopenharmony_ci u8 tail = ring->tail; 738c2ecf20Sopenharmony_ci struct sk_buff *skb; 748c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 758c2ecf20Sopenharmony_ci u16 len; 768c2ecf20Sopenharmony_ci int i; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for (i = 0; i < nbuf; i++) { 798c2ecf20Sopenharmony_ci raw_desc = &ring->raw_desc[tail]; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci len = XGENE_ENET_STD_MTU; 828c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(ndev, len); 838c2ecf20Sopenharmony_ci if (unlikely(!skb)) 848c2ecf20Sopenharmony_ci return -ENOMEM; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci dma_addr = dma_map_single(dev, skb->data, len, DMA_FROM_DEVICE); 878c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) { 888c2ecf20Sopenharmony_ci netdev_err(ndev, "DMA mapping error\n"); 898c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 908c2ecf20Sopenharmony_ci return -EINVAL; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ring->pkt_info[tail].skb = skb; 948c2ecf20Sopenharmony_ci ring->pkt_info[tail].dma_addr = dma_addr; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci addr_hi = GET_BITS(NEXT_DESC_ADDRH, le64_to_cpu(raw_desc->m1)); 978c2ecf20Sopenharmony_ci addr_lo = GET_BITS(NEXT_DESC_ADDRL, le64_to_cpu(raw_desc->m1)); 988c2ecf20Sopenharmony_ci raw_desc->m1 = cpu_to_le64(SET_BITS(NEXT_DESC_ADDRL, addr_lo) | 998c2ecf20Sopenharmony_ci SET_BITS(NEXT_DESC_ADDRH, addr_hi) | 1008c2ecf20Sopenharmony_ci SET_BITS(PKT_ADDRH, 1018c2ecf20Sopenharmony_ci upper_32_bits(dma_addr))); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci dma_wmb(); 1048c2ecf20Sopenharmony_ci raw_desc->m0 = cpu_to_le64(SET_BITS(PKT_ADDRL, dma_addr) | 1058c2ecf20Sopenharmony_ci SET_BITS(E, 1)); 1068c2ecf20Sopenharmony_ci tail = (tail + 1) & slots; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ring->tail = tail; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int xge_init_hw(struct net_device *ndev) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 1178c2ecf20Sopenharmony_ci int ret; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci ret = xge_port_reset(ndev); 1208c2ecf20Sopenharmony_ci if (ret) 1218c2ecf20Sopenharmony_ci return ret; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci xge_port_init(ndev); 1248c2ecf20Sopenharmony_ci pdata->nbufs = NUM_BUFS; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic irqreturn_t xge_irq(const int irq, void *data) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct xge_pdata *pdata = data; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (napi_schedule_prep(&pdata->napi)) { 1348c2ecf20Sopenharmony_ci xge_intr_disable(pdata); 1358c2ecf20Sopenharmony_ci __napi_schedule(&pdata->napi); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int xge_request_irq(struct net_device *ndev) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 1448c2ecf20Sopenharmony_ci int ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci snprintf(pdata->irq_name, IRQ_ID_SIZE, "%s", ndev->name); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ret = request_irq(pdata->resources.irq, xge_irq, 0, pdata->irq_name, 1498c2ecf20Sopenharmony_ci pdata); 1508c2ecf20Sopenharmony_ci if (ret) 1518c2ecf20Sopenharmony_ci netdev_err(ndev, "Failed to request irq %s\n", pdata->irq_name); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void xge_free_irq(struct net_device *ndev) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci free_irq(pdata->resources.irq, pdata); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic bool is_tx_slot_available(struct xge_raw_desc *raw_desc) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci if (GET_BITS(E, le64_to_cpu(raw_desc->m0)) && 1668c2ecf20Sopenharmony_ci (GET_BITS(PKT_SIZE, le64_to_cpu(raw_desc->m0)) == SLOT_EMPTY)) 1678c2ecf20Sopenharmony_ci return true; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return false; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic netdev_tx_t xge_start_xmit(struct sk_buff *skb, struct net_device *ndev) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 1758c2ecf20Sopenharmony_ci struct device *dev = &pdata->pdev->dev; 1768c2ecf20Sopenharmony_ci struct xge_desc_ring *tx_ring; 1778c2ecf20Sopenharmony_ci struct xge_raw_desc *raw_desc; 1788c2ecf20Sopenharmony_ci static dma_addr_t dma_addr; 1798c2ecf20Sopenharmony_ci u64 addr_lo, addr_hi; 1808c2ecf20Sopenharmony_ci void *pkt_buf; 1818c2ecf20Sopenharmony_ci u8 tail; 1828c2ecf20Sopenharmony_ci u16 len; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci tx_ring = pdata->tx_ring; 1858c2ecf20Sopenharmony_ci tail = tx_ring->tail; 1868c2ecf20Sopenharmony_ci len = skb_headlen(skb); 1878c2ecf20Sopenharmony_ci raw_desc = &tx_ring->raw_desc[tail]; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!is_tx_slot_available(raw_desc)) { 1908c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 1918c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Packet buffers should be 64B aligned */ 1958c2ecf20Sopenharmony_ci pkt_buf = dma_alloc_coherent(dev, XGENE_ENET_STD_MTU, &dma_addr, 1968c2ecf20Sopenharmony_ci GFP_ATOMIC); 1978c2ecf20Sopenharmony_ci if (unlikely(!pkt_buf)) { 1988c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1998c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci memcpy(pkt_buf, skb->data, len); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci addr_hi = GET_BITS(NEXT_DESC_ADDRH, le64_to_cpu(raw_desc->m1)); 2048c2ecf20Sopenharmony_ci addr_lo = GET_BITS(NEXT_DESC_ADDRL, le64_to_cpu(raw_desc->m1)); 2058c2ecf20Sopenharmony_ci raw_desc->m1 = cpu_to_le64(SET_BITS(NEXT_DESC_ADDRL, addr_lo) | 2068c2ecf20Sopenharmony_ci SET_BITS(NEXT_DESC_ADDRH, addr_hi) | 2078c2ecf20Sopenharmony_ci SET_BITS(PKT_ADDRH, 2088c2ecf20Sopenharmony_ci upper_32_bits(dma_addr))); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci tx_ring->pkt_info[tail].skb = skb; 2118c2ecf20Sopenharmony_ci tx_ring->pkt_info[tail].dma_addr = dma_addr; 2128c2ecf20Sopenharmony_ci tx_ring->pkt_info[tail].pkt_buf = pkt_buf; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dma_wmb(); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci raw_desc->m0 = cpu_to_le64(SET_BITS(PKT_ADDRL, dma_addr) | 2178c2ecf20Sopenharmony_ci SET_BITS(PKT_SIZE, len) | 2188c2ecf20Sopenharmony_ci SET_BITS(E, 0)); 2198c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 2208c2ecf20Sopenharmony_ci xge_wr_csr(pdata, DMATXCTRL, 1); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci tx_ring->tail = (tail + 1) & (XGENE_ENET_NUM_DESC - 1); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic bool is_tx_hw_done(struct xge_raw_desc *raw_desc) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci if (GET_BITS(E, le64_to_cpu(raw_desc->m0)) && 2308c2ecf20Sopenharmony_ci !GET_BITS(PKT_SIZE, le64_to_cpu(raw_desc->m0))) 2318c2ecf20Sopenharmony_ci return true; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return false; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void xge_txc_poll(struct net_device *ndev) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 2398c2ecf20Sopenharmony_ci struct device *dev = &pdata->pdev->dev; 2408c2ecf20Sopenharmony_ci struct xge_desc_ring *tx_ring; 2418c2ecf20Sopenharmony_ci struct xge_raw_desc *raw_desc; 2428c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 2438c2ecf20Sopenharmony_ci struct sk_buff *skb; 2448c2ecf20Sopenharmony_ci void *pkt_buf; 2458c2ecf20Sopenharmony_ci u32 data; 2468c2ecf20Sopenharmony_ci u8 head; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci tx_ring = pdata->tx_ring; 2498c2ecf20Sopenharmony_ci head = tx_ring->head; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci data = xge_rd_csr(pdata, DMATXSTATUS); 2528c2ecf20Sopenharmony_ci if (!GET_BITS(TXPKTCOUNT, data)) 2538c2ecf20Sopenharmony_ci return; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci while (1) { 2568c2ecf20Sopenharmony_ci raw_desc = &tx_ring->raw_desc[head]; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!is_tx_hw_done(raw_desc)) 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci dma_rmb(); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci skb = tx_ring->pkt_info[head].skb; 2648c2ecf20Sopenharmony_ci dma_addr = tx_ring->pkt_info[head].dma_addr; 2658c2ecf20Sopenharmony_ci pkt_buf = tx_ring->pkt_info[head].pkt_buf; 2668c2ecf20Sopenharmony_ci pdata->stats.tx_packets++; 2678c2ecf20Sopenharmony_ci pdata->stats.tx_bytes += skb->len; 2688c2ecf20Sopenharmony_ci dma_free_coherent(dev, XGENE_ENET_STD_MTU, pkt_buf, dma_addr); 2698c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* clear pktstart address and pktsize */ 2728c2ecf20Sopenharmony_ci raw_desc->m0 = cpu_to_le64(SET_BITS(E, 1) | 2738c2ecf20Sopenharmony_ci SET_BITS(PKT_SIZE, SLOT_EMPTY)); 2748c2ecf20Sopenharmony_ci xge_wr_csr(pdata, DMATXSTATUS, 1); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci head = (head + 1) & (XGENE_ENET_NUM_DESC - 1); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (netif_queue_stopped(ndev)) 2808c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci tx_ring->head = head; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int xge_rx_poll(struct net_device *ndev, unsigned int budget) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 2888c2ecf20Sopenharmony_ci struct device *dev = &pdata->pdev->dev; 2898c2ecf20Sopenharmony_ci struct xge_desc_ring *rx_ring; 2908c2ecf20Sopenharmony_ci struct xge_raw_desc *raw_desc; 2918c2ecf20Sopenharmony_ci struct sk_buff *skb; 2928c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 2938c2ecf20Sopenharmony_ci int processed = 0; 2948c2ecf20Sopenharmony_ci u8 head, rx_error; 2958c2ecf20Sopenharmony_ci int i, ret; 2968c2ecf20Sopenharmony_ci u32 data; 2978c2ecf20Sopenharmony_ci u16 len; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci rx_ring = pdata->rx_ring; 3008c2ecf20Sopenharmony_ci head = rx_ring->head; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci data = xge_rd_csr(pdata, DMARXSTATUS); 3038c2ecf20Sopenharmony_ci if (!GET_BITS(RXPKTCOUNT, data)) 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci for (i = 0; i < budget; i++) { 3078c2ecf20Sopenharmony_ci raw_desc = &rx_ring->raw_desc[head]; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (GET_BITS(E, le64_to_cpu(raw_desc->m0))) 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci dma_rmb(); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci skb = rx_ring->pkt_info[head].skb; 3158c2ecf20Sopenharmony_ci rx_ring->pkt_info[head].skb = NULL; 3168c2ecf20Sopenharmony_ci dma_addr = rx_ring->pkt_info[head].dma_addr; 3178c2ecf20Sopenharmony_ci len = GET_BITS(PKT_SIZE, le64_to_cpu(raw_desc->m0)); 3188c2ecf20Sopenharmony_ci dma_unmap_single(dev, dma_addr, XGENE_ENET_STD_MTU, 3198c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci rx_error = GET_BITS(D, le64_to_cpu(raw_desc->m2)); 3228c2ecf20Sopenharmony_ci if (unlikely(rx_error)) { 3238c2ecf20Sopenharmony_ci pdata->stats.rx_errors++; 3248c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 3258c2ecf20Sopenharmony_ci goto out; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci skb_put(skb, len); 3298c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci pdata->stats.rx_packets++; 3328c2ecf20Sopenharmony_ci pdata->stats.rx_bytes += len; 3338c2ecf20Sopenharmony_ci napi_gro_receive(&pdata->napi, skb); 3348c2ecf20Sopenharmony_ciout: 3358c2ecf20Sopenharmony_ci ret = xge_refill_buffers(ndev, 1); 3368c2ecf20Sopenharmony_ci xge_wr_csr(pdata, DMARXSTATUS, 1); 3378c2ecf20Sopenharmony_ci xge_wr_csr(pdata, DMARXCTRL, 1); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (ret) 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci head = (head + 1) & (XGENE_ENET_NUM_DESC - 1); 3438c2ecf20Sopenharmony_ci processed++; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci rx_ring->head = head; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return processed; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void xge_delete_desc_ring(struct net_device *ndev, 3528c2ecf20Sopenharmony_ci struct xge_desc_ring *ring) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 3558c2ecf20Sopenharmony_ci struct device *dev = &pdata->pdev->dev; 3568c2ecf20Sopenharmony_ci u16 size; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (!ring) 3598c2ecf20Sopenharmony_ci return; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci size = XGENE_ENET_DESC_SIZE * XGENE_ENET_NUM_DESC; 3628c2ecf20Sopenharmony_ci if (ring->desc_addr) 3638c2ecf20Sopenharmony_ci dma_free_coherent(dev, size, ring->desc_addr, ring->dma_addr); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci kfree(ring->pkt_info); 3668c2ecf20Sopenharmony_ci kfree(ring); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void xge_free_buffers(struct net_device *ndev) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 3728c2ecf20Sopenharmony_ci struct xge_desc_ring *ring = pdata->rx_ring; 3738c2ecf20Sopenharmony_ci struct device *dev = &pdata->pdev->dev; 3748c2ecf20Sopenharmony_ci struct sk_buff *skb; 3758c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 3768c2ecf20Sopenharmony_ci int i; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci for (i = 0; i < XGENE_ENET_NUM_DESC; i++) { 3798c2ecf20Sopenharmony_ci skb = ring->pkt_info[i].skb; 3808c2ecf20Sopenharmony_ci dma_addr = ring->pkt_info[i].dma_addr; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (!skb) 3838c2ecf20Sopenharmony_ci continue; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci dma_unmap_single(dev, dma_addr, XGENE_ENET_STD_MTU, 3868c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 3878c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic void xge_delete_desc_rings(struct net_device *ndev) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci xge_txc_poll(ndev); 3968c2ecf20Sopenharmony_ci xge_delete_desc_ring(ndev, pdata->tx_ring); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci xge_rx_poll(ndev, 64); 3998c2ecf20Sopenharmony_ci xge_free_buffers(ndev); 4008c2ecf20Sopenharmony_ci xge_delete_desc_ring(ndev, pdata->rx_ring); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic struct xge_desc_ring *xge_create_desc_ring(struct net_device *ndev) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 4068c2ecf20Sopenharmony_ci struct device *dev = &pdata->pdev->dev; 4078c2ecf20Sopenharmony_ci struct xge_desc_ring *ring; 4088c2ecf20Sopenharmony_ci u16 size; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci ring = kzalloc(sizeof(*ring), GFP_KERNEL); 4118c2ecf20Sopenharmony_ci if (!ring) 4128c2ecf20Sopenharmony_ci return NULL; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ring->ndev = ndev; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci size = XGENE_ENET_DESC_SIZE * XGENE_ENET_NUM_DESC; 4178c2ecf20Sopenharmony_ci ring->desc_addr = dma_alloc_coherent(dev, size, &ring->dma_addr, 4188c2ecf20Sopenharmony_ci GFP_KERNEL); 4198c2ecf20Sopenharmony_ci if (!ring->desc_addr) 4208c2ecf20Sopenharmony_ci goto err; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci ring->pkt_info = kcalloc(XGENE_ENET_NUM_DESC, sizeof(*ring->pkt_info), 4238c2ecf20Sopenharmony_ci GFP_KERNEL); 4248c2ecf20Sopenharmony_ci if (!ring->pkt_info) 4258c2ecf20Sopenharmony_ci goto err; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci xge_setup_desc(ring); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return ring; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cierr: 4328c2ecf20Sopenharmony_ci xge_delete_desc_ring(ndev, ring); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return NULL; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int xge_create_desc_rings(struct net_device *ndev) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 4408c2ecf20Sopenharmony_ci struct xge_desc_ring *ring; 4418c2ecf20Sopenharmony_ci int ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* create tx ring */ 4448c2ecf20Sopenharmony_ci ring = xge_create_desc_ring(ndev); 4458c2ecf20Sopenharmony_ci if (!ring) 4468c2ecf20Sopenharmony_ci goto err; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci pdata->tx_ring = ring; 4498c2ecf20Sopenharmony_ci xge_update_tx_desc_addr(pdata); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* create rx ring */ 4528c2ecf20Sopenharmony_ci ring = xge_create_desc_ring(ndev); 4538c2ecf20Sopenharmony_ci if (!ring) 4548c2ecf20Sopenharmony_ci goto err; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci pdata->rx_ring = ring; 4578c2ecf20Sopenharmony_ci xge_update_rx_desc_addr(pdata); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci ret = xge_refill_buffers(ndev, XGENE_ENET_NUM_DESC); 4608c2ecf20Sopenharmony_ci if (ret) 4618c2ecf20Sopenharmony_ci goto err; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_cierr: 4658c2ecf20Sopenharmony_ci xge_delete_desc_rings(ndev); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return -ENOMEM; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int xge_open(struct net_device *ndev) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 4738c2ecf20Sopenharmony_ci int ret; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci ret = xge_create_desc_rings(ndev); 4768c2ecf20Sopenharmony_ci if (ret) 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci napi_enable(&pdata->napi); 4808c2ecf20Sopenharmony_ci ret = xge_request_irq(ndev); 4818c2ecf20Sopenharmony_ci if (ret) 4828c2ecf20Sopenharmony_ci return ret; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci xge_intr_enable(pdata); 4858c2ecf20Sopenharmony_ci xge_wr_csr(pdata, DMARXCTRL, 1); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci phy_start(ndev->phydev); 4888c2ecf20Sopenharmony_ci xge_mac_enable(pdata); 4898c2ecf20Sopenharmony_ci netif_start_queue(ndev); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic int xge_close(struct net_device *ndev) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 4998c2ecf20Sopenharmony_ci xge_mac_disable(pdata); 5008c2ecf20Sopenharmony_ci phy_stop(ndev->phydev); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci xge_intr_disable(pdata); 5038c2ecf20Sopenharmony_ci xge_free_irq(ndev); 5048c2ecf20Sopenharmony_ci napi_disable(&pdata->napi); 5058c2ecf20Sopenharmony_ci xge_delete_desc_rings(ndev); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int xge_napi(struct napi_struct *napi, const int budget) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct net_device *ndev = napi->dev; 5138c2ecf20Sopenharmony_ci struct xge_pdata *pdata; 5148c2ecf20Sopenharmony_ci int processed; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci pdata = netdev_priv(ndev); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci xge_txc_poll(ndev); 5198c2ecf20Sopenharmony_ci processed = xge_rx_poll(ndev, budget); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (processed < budget) { 5228c2ecf20Sopenharmony_ci napi_complete_done(napi, processed); 5238c2ecf20Sopenharmony_ci xge_intr_enable(pdata); 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return processed; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int xge_set_mac_addr(struct net_device *ndev, void *addr) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 5328c2ecf20Sopenharmony_ci int ret; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci ret = eth_mac_addr(ndev, addr); 5358c2ecf20Sopenharmony_ci if (ret) 5368c2ecf20Sopenharmony_ci return ret; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci xge_mac_set_station_addr(pdata); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic bool is_tx_pending(struct xge_raw_desc *raw_desc) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci if (!GET_BITS(E, le64_to_cpu(raw_desc->m0))) 5468c2ecf20Sopenharmony_ci return true; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci return false; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic void xge_free_pending_skb(struct net_device *ndev) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 5548c2ecf20Sopenharmony_ci struct device *dev = &pdata->pdev->dev; 5558c2ecf20Sopenharmony_ci struct xge_desc_ring *tx_ring; 5568c2ecf20Sopenharmony_ci struct xge_raw_desc *raw_desc; 5578c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 5588c2ecf20Sopenharmony_ci struct sk_buff *skb; 5598c2ecf20Sopenharmony_ci void *pkt_buf; 5608c2ecf20Sopenharmony_ci int i; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci tx_ring = pdata->tx_ring; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci for (i = 0; i < XGENE_ENET_NUM_DESC; i++) { 5658c2ecf20Sopenharmony_ci raw_desc = &tx_ring->raw_desc[i]; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (!is_tx_pending(raw_desc)) 5688c2ecf20Sopenharmony_ci continue; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci skb = tx_ring->pkt_info[i].skb; 5718c2ecf20Sopenharmony_ci dma_addr = tx_ring->pkt_info[i].dma_addr; 5728c2ecf20Sopenharmony_ci pkt_buf = tx_ring->pkt_info[i].pkt_buf; 5738c2ecf20Sopenharmony_ci dma_free_coherent(dev, XGENE_ENET_STD_MTU, pkt_buf, dma_addr); 5748c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic void xge_timeout(struct net_device *ndev, unsigned int txqueue) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci rtnl_lock(); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (!netif_running(ndev)) 5858c2ecf20Sopenharmony_ci goto out; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 5888c2ecf20Sopenharmony_ci xge_intr_disable(pdata); 5898c2ecf20Sopenharmony_ci napi_disable(&pdata->napi); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci xge_wr_csr(pdata, DMATXCTRL, 0); 5928c2ecf20Sopenharmony_ci xge_txc_poll(ndev); 5938c2ecf20Sopenharmony_ci xge_free_pending_skb(ndev); 5948c2ecf20Sopenharmony_ci xge_wr_csr(pdata, DMATXSTATUS, ~0U); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci xge_setup_desc(pdata->tx_ring); 5978c2ecf20Sopenharmony_ci xge_update_tx_desc_addr(pdata); 5988c2ecf20Sopenharmony_ci xge_mac_init(pdata); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci napi_enable(&pdata->napi); 6018c2ecf20Sopenharmony_ci xge_intr_enable(pdata); 6028c2ecf20Sopenharmony_ci xge_mac_enable(pdata); 6038c2ecf20Sopenharmony_ci netif_start_queue(ndev); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ciout: 6068c2ecf20Sopenharmony_ci rtnl_unlock(); 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic void xge_get_stats64(struct net_device *ndev, 6108c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *storage) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 6138c2ecf20Sopenharmony_ci struct xge_stats *stats = &pdata->stats; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci storage->tx_packets += stats->tx_packets; 6168c2ecf20Sopenharmony_ci storage->tx_bytes += stats->tx_bytes; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci storage->rx_packets += stats->rx_packets; 6198c2ecf20Sopenharmony_ci storage->rx_bytes += stats->rx_bytes; 6208c2ecf20Sopenharmony_ci storage->rx_errors += stats->rx_errors; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic const struct net_device_ops xgene_ndev_ops = { 6248c2ecf20Sopenharmony_ci .ndo_open = xge_open, 6258c2ecf20Sopenharmony_ci .ndo_stop = xge_close, 6268c2ecf20Sopenharmony_ci .ndo_start_xmit = xge_start_xmit, 6278c2ecf20Sopenharmony_ci .ndo_set_mac_address = xge_set_mac_addr, 6288c2ecf20Sopenharmony_ci .ndo_tx_timeout = xge_timeout, 6298c2ecf20Sopenharmony_ci .ndo_get_stats64 = xge_get_stats64, 6308c2ecf20Sopenharmony_ci}; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int xge_probe(struct platform_device *pdev) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6358c2ecf20Sopenharmony_ci struct net_device *ndev; 6368c2ecf20Sopenharmony_ci struct xge_pdata *pdata; 6378c2ecf20Sopenharmony_ci int ret; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci ndev = alloc_etherdev(sizeof(*pdata)); 6408c2ecf20Sopenharmony_ci if (!ndev) 6418c2ecf20Sopenharmony_ci return -ENOMEM; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci pdata = netdev_priv(ndev); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci pdata->pdev = pdev; 6468c2ecf20Sopenharmony_ci pdata->ndev = ndev; 6478c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, dev); 6488c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pdata); 6498c2ecf20Sopenharmony_ci ndev->netdev_ops = &xgene_ndev_ops; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_GSO | 6528c2ecf20Sopenharmony_ci NETIF_F_GRO; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ret = xge_get_resources(pdata); 6558c2ecf20Sopenharmony_ci if (ret) 6568c2ecf20Sopenharmony_ci goto err; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci ndev->hw_features = ndev->features; 6598c2ecf20Sopenharmony_ci xge_set_ethtool_ops(ndev); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); 6628c2ecf20Sopenharmony_ci if (ret) { 6638c2ecf20Sopenharmony_ci netdev_err(ndev, "No usable DMA configuration\n"); 6648c2ecf20Sopenharmony_ci goto err; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci ret = xge_init_hw(ndev); 6688c2ecf20Sopenharmony_ci if (ret) 6698c2ecf20Sopenharmony_ci goto err; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci ret = xge_mdio_config(ndev); 6728c2ecf20Sopenharmony_ci if (ret) 6738c2ecf20Sopenharmony_ci goto err; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci netif_napi_add(ndev, &pdata->napi, xge_napi, NAPI_POLL_WEIGHT); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci ret = register_netdev(ndev); 6788c2ecf20Sopenharmony_ci if (ret) { 6798c2ecf20Sopenharmony_ci netdev_err(ndev, "Failed to register netdev\n"); 6808c2ecf20Sopenharmony_ci goto err_mdio_remove; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cierr_mdio_remove: 6868c2ecf20Sopenharmony_ci xge_mdio_remove(ndev); 6878c2ecf20Sopenharmony_cierr: 6888c2ecf20Sopenharmony_ci free_netdev(ndev); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return ret; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int xge_remove(struct platform_device *pdev) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct xge_pdata *pdata; 6968c2ecf20Sopenharmony_ci struct net_device *ndev; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci pdata = platform_get_drvdata(pdev); 6998c2ecf20Sopenharmony_ci ndev = pdata->ndev; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci rtnl_lock(); 7028c2ecf20Sopenharmony_ci if (netif_running(ndev)) 7038c2ecf20Sopenharmony_ci dev_close(ndev); 7048c2ecf20Sopenharmony_ci rtnl_unlock(); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci xge_mdio_remove(ndev); 7078c2ecf20Sopenharmony_ci unregister_netdev(ndev); 7088c2ecf20Sopenharmony_ci free_netdev(ndev); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic void xge_shutdown(struct platform_device *pdev) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct xge_pdata *pdata; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci pdata = platform_get_drvdata(pdev); 7188c2ecf20Sopenharmony_ci if (!pdata) 7198c2ecf20Sopenharmony_ci return; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (!pdata->ndev) 7228c2ecf20Sopenharmony_ci return; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci xge_remove(pdev); 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic const struct acpi_device_id xge_acpi_match[] = { 7288c2ecf20Sopenharmony_ci { "APMC0D80" }, 7298c2ecf20Sopenharmony_ci { } 7308c2ecf20Sopenharmony_ci}; 7318c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, xge_acpi_match); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic struct platform_driver xge_driver = { 7348c2ecf20Sopenharmony_ci .driver = { 7358c2ecf20Sopenharmony_ci .name = "xgene-enet-v2", 7368c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(xge_acpi_match), 7378c2ecf20Sopenharmony_ci }, 7388c2ecf20Sopenharmony_ci .probe = xge_probe, 7398c2ecf20Sopenharmony_ci .remove = xge_remove, 7408c2ecf20Sopenharmony_ci .shutdown = xge_shutdown, 7418c2ecf20Sopenharmony_ci}; 7428c2ecf20Sopenharmony_cimodule_platform_driver(xge_driver); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("APM X-Gene SoC Ethernet v2 driver"); 7458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Iyappan Subramanian <isubramanian@apm.com>"); 7468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 747