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