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