18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * sonic.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) 2005 Finn Thain
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Converted to DMA API, added zero-copy buffer handling, and
88c2ecf20Sopenharmony_ci * (from the mac68k project) introduced dhd's support for 16-bit cards.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * (C) 1996,1998 by Thomas Bogendoerfer (tsbogend@alpha.franken.de)
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This driver is based on work from Andreas Busse, but most of
138c2ecf20Sopenharmony_ci * the code is rewritten.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *    Core code included by system sonic drivers
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * And... partially rewritten again by David Huggins-Daines in order
208c2ecf20Sopenharmony_ci * to cope with screwed up Macintosh NICs that may or may not use
218c2ecf20Sopenharmony_ci * 16-bit DMA.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * (C) 1999 David Huggins-Daines <dhd@debian.org>
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * Sources: Olivetti M700-10 Risc Personal Computer hardware handbook,
298c2ecf20Sopenharmony_ci * National Semiconductors data sheet for the DP83932B Sonic Ethernet
308c2ecf20Sopenharmony_ci * controller, and the files "8390.c" and "skeleton.c" in this directory.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Additional sources: Nat Semi data sheet for the DP83932C and Nat Semi
338c2ecf20Sopenharmony_ci * Application Note AN-746, the files "lance.c" and "ibmlana.c". See also
348c2ecf20Sopenharmony_ci * the NetBSD file "sys/arch/mac68k/dev/if_sn.c".
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic unsigned int version_printed;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int sonic_debug = -1;
408c2ecf20Sopenharmony_cimodule_param(sonic_debug, int, 0);
418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sonic_debug, "debug message level");
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void sonic_msg_init(struct net_device *dev)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	lp->msg_enable = netif_msg_init(sonic_debug, 0);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (version_printed++ == 0)
508c2ecf20Sopenharmony_ci		netif_dbg(lp, drv, dev, "%s", version);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int sonic_alloc_descriptors(struct net_device *dev)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* Allocate a chunk of memory for the descriptors. Note that this
588c2ecf20Sopenharmony_ci	 * must not cross a 64K boundary. It is smaller than one page which
598c2ecf20Sopenharmony_ci	 * means that page alignment is a sufficient condition.
608c2ecf20Sopenharmony_ci	 */
618c2ecf20Sopenharmony_ci	lp->descriptors =
628c2ecf20Sopenharmony_ci		dma_alloc_coherent(lp->device,
638c2ecf20Sopenharmony_ci				   SIZEOF_SONIC_DESC *
648c2ecf20Sopenharmony_ci				   SONIC_BUS_SCALE(lp->dma_bitmode),
658c2ecf20Sopenharmony_ci				   &lp->descriptors_laddr, GFP_KERNEL);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (!lp->descriptors)
688c2ecf20Sopenharmony_ci		return -ENOMEM;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	lp->cda = lp->descriptors;
718c2ecf20Sopenharmony_ci	lp->tda = lp->cda + SIZEOF_SONIC_CDA *
728c2ecf20Sopenharmony_ci			    SONIC_BUS_SCALE(lp->dma_bitmode);
738c2ecf20Sopenharmony_ci	lp->rda = lp->tda + SIZEOF_SONIC_TD * SONIC_NUM_TDS *
748c2ecf20Sopenharmony_ci			    SONIC_BUS_SCALE(lp->dma_bitmode);
758c2ecf20Sopenharmony_ci	lp->rra = lp->rda + SIZEOF_SONIC_RD * SONIC_NUM_RDS *
768c2ecf20Sopenharmony_ci			    SONIC_BUS_SCALE(lp->dma_bitmode);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	lp->cda_laddr = lp->descriptors_laddr;
798c2ecf20Sopenharmony_ci	lp->tda_laddr = lp->cda_laddr + SIZEOF_SONIC_CDA *
808c2ecf20Sopenharmony_ci					SONIC_BUS_SCALE(lp->dma_bitmode);
818c2ecf20Sopenharmony_ci	lp->rda_laddr = lp->tda_laddr + SIZEOF_SONIC_TD * SONIC_NUM_TDS *
828c2ecf20Sopenharmony_ci					SONIC_BUS_SCALE(lp->dma_bitmode);
838c2ecf20Sopenharmony_ci	lp->rra_laddr = lp->rda_laddr + SIZEOF_SONIC_RD * SONIC_NUM_RDS *
848c2ecf20Sopenharmony_ci					SONIC_BUS_SCALE(lp->dma_bitmode);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/*
908c2ecf20Sopenharmony_ci * Open/initialize the SONIC controller.
918c2ecf20Sopenharmony_ci *
928c2ecf20Sopenharmony_ci * This routine should set everything up anew at each open, even
938c2ecf20Sopenharmony_ci *  registers that "should" only need to be set once at boot, so that
948c2ecf20Sopenharmony_ci *  there is non-reboot way to recover if something goes wrong.
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_cistatic int sonic_open(struct net_device *dev)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
998c2ecf20Sopenharmony_ci	int i;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	netif_dbg(lp, ifup, dev, "%s: initializing sonic driver\n", __func__);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	spin_lock_init(&lp->lock);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	for (i = 0; i < SONIC_NUM_RRS; i++) {
1068c2ecf20Sopenharmony_ci		struct sk_buff *skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2);
1078c2ecf20Sopenharmony_ci		if (skb == NULL) {
1088c2ecf20Sopenharmony_ci			while(i > 0) { /* free any that were allocated successfully */
1098c2ecf20Sopenharmony_ci				i--;
1108c2ecf20Sopenharmony_ci				dev_kfree_skb(lp->rx_skb[i]);
1118c2ecf20Sopenharmony_ci				lp->rx_skb[i] = NULL;
1128c2ecf20Sopenharmony_ci			}
1138c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: couldn't allocate receive buffers\n",
1148c2ecf20Sopenharmony_ci			       dev->name);
1158c2ecf20Sopenharmony_ci			return -ENOMEM;
1168c2ecf20Sopenharmony_ci		}
1178c2ecf20Sopenharmony_ci		/* align IP header unless DMA requires otherwise */
1188c2ecf20Sopenharmony_ci		if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
1198c2ecf20Sopenharmony_ci			skb_reserve(skb, 2);
1208c2ecf20Sopenharmony_ci		lp->rx_skb[i] = skb;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	for (i = 0; i < SONIC_NUM_RRS; i++) {
1248c2ecf20Sopenharmony_ci		dma_addr_t laddr = dma_map_single(lp->device, skb_put(lp->rx_skb[i], SONIC_RBSIZE),
1258c2ecf20Sopenharmony_ci		                                  SONIC_RBSIZE, DMA_FROM_DEVICE);
1268c2ecf20Sopenharmony_ci		if (dma_mapping_error(lp->device, laddr)) {
1278c2ecf20Sopenharmony_ci			while(i > 0) { /* free any that were mapped successfully */
1288c2ecf20Sopenharmony_ci				i--;
1298c2ecf20Sopenharmony_ci				dma_unmap_single(lp->device, lp->rx_laddr[i], SONIC_RBSIZE, DMA_FROM_DEVICE);
1308c2ecf20Sopenharmony_ci				lp->rx_laddr[i] = (dma_addr_t)0;
1318c2ecf20Sopenharmony_ci			}
1328c2ecf20Sopenharmony_ci			for (i = 0; i < SONIC_NUM_RRS; i++) {
1338c2ecf20Sopenharmony_ci				dev_kfree_skb(lp->rx_skb[i]);
1348c2ecf20Sopenharmony_ci				lp->rx_skb[i] = NULL;
1358c2ecf20Sopenharmony_ci			}
1368c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: couldn't map rx DMA buffers\n",
1378c2ecf20Sopenharmony_ci			       dev->name);
1388c2ecf20Sopenharmony_ci			return -ENOMEM;
1398c2ecf20Sopenharmony_ci		}
1408c2ecf20Sopenharmony_ci		lp->rx_laddr[i] = laddr;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/*
1448c2ecf20Sopenharmony_ci	 * Initialize the SONIC
1458c2ecf20Sopenharmony_ci	 */
1468c2ecf20Sopenharmony_ci	sonic_init(dev, true);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	netif_start_queue(dev);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	netif_dbg(lp, ifup, dev, "%s: Initialization done\n", __func__);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/* Wait for the SONIC to become idle. */
1568c2ecf20Sopenharmony_cistatic void sonic_quiesce(struct net_device *dev, u16 mask, bool may_sleep)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct sonic_local * __maybe_unused lp = netdev_priv(dev);
1598c2ecf20Sopenharmony_ci	int i;
1608c2ecf20Sopenharmony_ci	u16 bits;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	for (i = 0; i < 1000; ++i) {
1638c2ecf20Sopenharmony_ci		bits = SONIC_READ(SONIC_CMD) & mask;
1648c2ecf20Sopenharmony_ci		if (!bits)
1658c2ecf20Sopenharmony_ci			return;
1668c2ecf20Sopenharmony_ci		if (!may_sleep)
1678c2ecf20Sopenharmony_ci			udelay(20);
1688c2ecf20Sopenharmony_ci		else
1698c2ecf20Sopenharmony_ci			usleep_range(100, 200);
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	WARN_ONCE(1, "command deadline expired! 0x%04x\n", bits);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/*
1758c2ecf20Sopenharmony_ci * Close the SONIC device
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_cistatic int sonic_close(struct net_device *dev)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
1808c2ecf20Sopenharmony_ci	int i;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	netif_dbg(lp, ifdown, dev, "%s\n", __func__);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/*
1878c2ecf20Sopenharmony_ci	 * stop the SONIC, disable interrupts
1888c2ecf20Sopenharmony_ci	 */
1898c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS);
1908c2ecf20Sopenharmony_ci	sonic_quiesce(dev, SONIC_CR_ALL, true);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_IMR, 0);
1938c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_ISR, 0x7fff);
1948c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* unmap and free skbs that haven't been transmitted */
1978c2ecf20Sopenharmony_ci	for (i = 0; i < SONIC_NUM_TDS; i++) {
1988c2ecf20Sopenharmony_ci		if(lp->tx_laddr[i]) {
1998c2ecf20Sopenharmony_ci			dma_unmap_single(lp->device, lp->tx_laddr[i], lp->tx_len[i], DMA_TO_DEVICE);
2008c2ecf20Sopenharmony_ci			lp->tx_laddr[i] = (dma_addr_t)0;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci		if(lp->tx_skb[i]) {
2038c2ecf20Sopenharmony_ci			dev_kfree_skb(lp->tx_skb[i]);
2048c2ecf20Sopenharmony_ci			lp->tx_skb[i] = NULL;
2058c2ecf20Sopenharmony_ci		}
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* unmap and free the receive buffers */
2098c2ecf20Sopenharmony_ci	for (i = 0; i < SONIC_NUM_RRS; i++) {
2108c2ecf20Sopenharmony_ci		if(lp->rx_laddr[i]) {
2118c2ecf20Sopenharmony_ci			dma_unmap_single(lp->device, lp->rx_laddr[i], SONIC_RBSIZE, DMA_FROM_DEVICE);
2128c2ecf20Sopenharmony_ci			lp->rx_laddr[i] = (dma_addr_t)0;
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci		if(lp->rx_skb[i]) {
2158c2ecf20Sopenharmony_ci			dev_kfree_skb(lp->rx_skb[i]);
2168c2ecf20Sopenharmony_ci			lp->rx_skb[i] = NULL;
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void sonic_tx_timeout(struct net_device *dev, unsigned int txqueue)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
2268c2ecf20Sopenharmony_ci	int i;
2278c2ecf20Sopenharmony_ci	/*
2288c2ecf20Sopenharmony_ci	 * put the Sonic into software-reset mode and
2298c2ecf20Sopenharmony_ci	 * disable all interrupts before releasing DMA buffers
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS);
2328c2ecf20Sopenharmony_ci	sonic_quiesce(dev, SONIC_CR_ALL, false);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_IMR, 0);
2358c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_ISR, 0x7fff);
2368c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
2378c2ecf20Sopenharmony_ci	/* We could resend the original skbs. Easier to re-initialise. */
2388c2ecf20Sopenharmony_ci	for (i = 0; i < SONIC_NUM_TDS; i++) {
2398c2ecf20Sopenharmony_ci		if(lp->tx_laddr[i]) {
2408c2ecf20Sopenharmony_ci			dma_unmap_single(lp->device, lp->tx_laddr[i], lp->tx_len[i], DMA_TO_DEVICE);
2418c2ecf20Sopenharmony_ci			lp->tx_laddr[i] = (dma_addr_t)0;
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci		if(lp->tx_skb[i]) {
2448c2ecf20Sopenharmony_ci			dev_kfree_skb(lp->tx_skb[i]);
2458c2ecf20Sopenharmony_ci			lp->tx_skb[i] = NULL;
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci	/* Try to restart the adaptor. */
2498c2ecf20Sopenharmony_ci	sonic_init(dev, false);
2508c2ecf20Sopenharmony_ci	lp->stats.tx_errors++;
2518c2ecf20Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
2528c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/*
2568c2ecf20Sopenharmony_ci * transmit packet
2578c2ecf20Sopenharmony_ci *
2588c2ecf20Sopenharmony_ci * Appends new TD during transmission thus avoiding any TX interrupts
2598c2ecf20Sopenharmony_ci * until we run out of TDs.
2608c2ecf20Sopenharmony_ci * This routine interacts closely with the ISR in that it may,
2618c2ecf20Sopenharmony_ci *   set tx_skb[i]
2628c2ecf20Sopenharmony_ci *   reset the status flags of the new TD
2638c2ecf20Sopenharmony_ci *   set and reset EOL flags
2648c2ecf20Sopenharmony_ci *   stop the tx queue
2658c2ecf20Sopenharmony_ci * The ISR interacts with this routine in various ways. It may,
2668c2ecf20Sopenharmony_ci *   reset tx_skb[i]
2678c2ecf20Sopenharmony_ci *   test the EOL and status flags of the TDs
2688c2ecf20Sopenharmony_ci *   wake the tx queue
2698c2ecf20Sopenharmony_ci * Concurrently with all of this, the SONIC is potentially writing to
2708c2ecf20Sopenharmony_ci * the status flags of the TDs.
2718c2ecf20Sopenharmony_ci */
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic int sonic_send_packet(struct sk_buff *skb, struct net_device *dev)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
2768c2ecf20Sopenharmony_ci	dma_addr_t laddr;
2778c2ecf20Sopenharmony_ci	int length;
2788c2ecf20Sopenharmony_ci	int entry;
2798c2ecf20Sopenharmony_ci	unsigned long flags;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	netif_dbg(lp, tx_queued, dev, "%s: skb=%p\n", __func__, skb);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	length = skb->len;
2848c2ecf20Sopenharmony_ci	if (length < ETH_ZLEN) {
2858c2ecf20Sopenharmony_ci		if (skb_padto(skb, ETH_ZLEN))
2868c2ecf20Sopenharmony_ci			return NETDEV_TX_OK;
2878c2ecf20Sopenharmony_ci		length = ETH_ZLEN;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/*
2918c2ecf20Sopenharmony_ci	 * Map the packet data into the logical DMA address space
2928c2ecf20Sopenharmony_ci	 */
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	laddr = dma_map_single(lp->device, skb->data, length, DMA_TO_DEVICE);
2958c2ecf20Sopenharmony_ci	if (dma_mapping_error(lp->device, laddr)) {
2968c2ecf20Sopenharmony_ci		pr_err_ratelimited("%s: failed to map tx DMA buffer.\n", dev->name);
2978c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
2988c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	entry = (lp->eol_tx + 1) & SONIC_TDS_MASK;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	sonic_tda_put(dev, entry, SONIC_TD_STATUS, 0);       /* clear status */
3068c2ecf20Sopenharmony_ci	sonic_tda_put(dev, entry, SONIC_TD_FRAG_COUNT, 1);   /* single fragment */
3078c2ecf20Sopenharmony_ci	sonic_tda_put(dev, entry, SONIC_TD_PKTSIZE, length); /* length of packet */
3088c2ecf20Sopenharmony_ci	sonic_tda_put(dev, entry, SONIC_TD_FRAG_PTR_L, laddr & 0xffff);
3098c2ecf20Sopenharmony_ci	sonic_tda_put(dev, entry, SONIC_TD_FRAG_PTR_H, laddr >> 16);
3108c2ecf20Sopenharmony_ci	sonic_tda_put(dev, entry, SONIC_TD_FRAG_SIZE, length);
3118c2ecf20Sopenharmony_ci	sonic_tda_put(dev, entry, SONIC_TD_LINK,
3128c2ecf20Sopenharmony_ci		sonic_tda_get(dev, entry, SONIC_TD_LINK) | SONIC_EOL);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	sonic_tda_put(dev, lp->eol_tx, SONIC_TD_LINK, ~SONIC_EOL &
3158c2ecf20Sopenharmony_ci		      sonic_tda_get(dev, lp->eol_tx, SONIC_TD_LINK));
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	netif_dbg(lp, tx_queued, dev, "%s: issuing Tx command\n", __func__);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_TXP);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	lp->tx_len[entry] = length;
3228c2ecf20Sopenharmony_ci	lp->tx_laddr[entry] = laddr;
3238c2ecf20Sopenharmony_ci	lp->tx_skb[entry] = skb;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	lp->eol_tx = entry;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	entry = (entry + 1) & SONIC_TDS_MASK;
3288c2ecf20Sopenharmony_ci	if (lp->tx_skb[entry]) {
3298c2ecf20Sopenharmony_ci		/* The ring is full, the ISR has yet to process the next TD. */
3308c2ecf20Sopenharmony_ci		netif_dbg(lp, tx_queued, dev, "%s: stopping queue\n", __func__);
3318c2ecf20Sopenharmony_ci		netif_stop_queue(dev);
3328c2ecf20Sopenharmony_ci		/* after this packet, wait for ISR to free up some TDAs */
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci/*
3418c2ecf20Sopenharmony_ci * The typical workload of the driver:
3428c2ecf20Sopenharmony_ci * Handle the network interface interrupts.
3438c2ecf20Sopenharmony_ci */
3448c2ecf20Sopenharmony_cistatic irqreturn_t sonic_interrupt(int irq, void *dev_id)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
3478c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
3488c2ecf20Sopenharmony_ci	int status;
3498c2ecf20Sopenharmony_ci	unsigned long flags;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* The lock has two purposes. Firstly, it synchronizes sonic_interrupt()
3528c2ecf20Sopenharmony_ci	 * with sonic_send_packet() so that the two functions can share state.
3538c2ecf20Sopenharmony_ci	 * Secondly, it makes sonic_interrupt() re-entrant, as that is required
3548c2ecf20Sopenharmony_ci	 * by macsonic which must use two IRQs with different priority levels.
3558c2ecf20Sopenharmony_ci	 */
3568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT;
3598c2ecf20Sopenharmony_ci	if (!status) {
3608c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&lp->lock, flags);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		return IRQ_NONE;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	do {
3668c2ecf20Sopenharmony_ci		SONIC_WRITE(SONIC_ISR, status); /* clear the interrupt(s) */
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		if (status & SONIC_INT_PKTRX) {
3698c2ecf20Sopenharmony_ci			netif_dbg(lp, intr, dev, "%s: packet rx\n", __func__);
3708c2ecf20Sopenharmony_ci			sonic_rx(dev);	/* got packet(s) */
3718c2ecf20Sopenharmony_ci		}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		if (status & SONIC_INT_TXDN) {
3748c2ecf20Sopenharmony_ci			int entry = lp->cur_tx;
3758c2ecf20Sopenharmony_ci			int td_status;
3768c2ecf20Sopenharmony_ci			int freed_some = 0;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci			/* The state of a Transmit Descriptor may be inferred
3798c2ecf20Sopenharmony_ci			 * from { tx_skb[entry], td_status } as follows.
3808c2ecf20Sopenharmony_ci			 * { clear, clear } => the TD has never been used
3818c2ecf20Sopenharmony_ci			 * { set,   clear } => the TD was handed to SONIC
3828c2ecf20Sopenharmony_ci			 * { set,   set   } => the TD was handed back
3838c2ecf20Sopenharmony_ci			 * { clear, set   } => the TD is available for re-use
3848c2ecf20Sopenharmony_ci			 */
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci			netif_dbg(lp, intr, dev, "%s: tx done\n", __func__);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci			while (lp->tx_skb[entry] != NULL) {
3898c2ecf20Sopenharmony_ci				if ((td_status = sonic_tda_get(dev, entry, SONIC_TD_STATUS)) == 0)
3908c2ecf20Sopenharmony_ci					break;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci				if (td_status & SONIC_TCR_PTX) {
3938c2ecf20Sopenharmony_ci					lp->stats.tx_packets++;
3948c2ecf20Sopenharmony_ci					lp->stats.tx_bytes += sonic_tda_get(dev, entry, SONIC_TD_PKTSIZE);
3958c2ecf20Sopenharmony_ci				} else {
3968c2ecf20Sopenharmony_ci					if (td_status & (SONIC_TCR_EXD |
3978c2ecf20Sopenharmony_ci					    SONIC_TCR_EXC | SONIC_TCR_BCM))
3988c2ecf20Sopenharmony_ci						lp->stats.tx_aborted_errors++;
3998c2ecf20Sopenharmony_ci					if (td_status &
4008c2ecf20Sopenharmony_ci					    (SONIC_TCR_NCRS | SONIC_TCR_CRLS))
4018c2ecf20Sopenharmony_ci						lp->stats.tx_carrier_errors++;
4028c2ecf20Sopenharmony_ci					if (td_status & SONIC_TCR_OWC)
4038c2ecf20Sopenharmony_ci						lp->stats.tx_window_errors++;
4048c2ecf20Sopenharmony_ci					if (td_status & SONIC_TCR_FU)
4058c2ecf20Sopenharmony_ci						lp->stats.tx_fifo_errors++;
4068c2ecf20Sopenharmony_ci				}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci				/* We must free the original skb */
4098c2ecf20Sopenharmony_ci				dev_consume_skb_irq(lp->tx_skb[entry]);
4108c2ecf20Sopenharmony_ci				lp->tx_skb[entry] = NULL;
4118c2ecf20Sopenharmony_ci				/* and unmap DMA buffer */
4128c2ecf20Sopenharmony_ci				dma_unmap_single(lp->device, lp->tx_laddr[entry], lp->tx_len[entry], DMA_TO_DEVICE);
4138c2ecf20Sopenharmony_ci				lp->tx_laddr[entry] = (dma_addr_t)0;
4148c2ecf20Sopenharmony_ci				freed_some = 1;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci				if (sonic_tda_get(dev, entry, SONIC_TD_LINK) & SONIC_EOL) {
4178c2ecf20Sopenharmony_ci					entry = (entry + 1) & SONIC_TDS_MASK;
4188c2ecf20Sopenharmony_ci					break;
4198c2ecf20Sopenharmony_ci				}
4208c2ecf20Sopenharmony_ci				entry = (entry + 1) & SONIC_TDS_MASK;
4218c2ecf20Sopenharmony_ci			}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci			if (freed_some || lp->tx_skb[entry] == NULL)
4248c2ecf20Sopenharmony_ci				netif_wake_queue(dev);  /* The ring is no longer full */
4258c2ecf20Sopenharmony_ci			lp->cur_tx = entry;
4268c2ecf20Sopenharmony_ci		}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci		/*
4298c2ecf20Sopenharmony_ci		 * check error conditions
4308c2ecf20Sopenharmony_ci		 */
4318c2ecf20Sopenharmony_ci		if (status & SONIC_INT_RFO) {
4328c2ecf20Sopenharmony_ci			netif_dbg(lp, rx_err, dev, "%s: rx fifo overrun\n",
4338c2ecf20Sopenharmony_ci				  __func__);
4348c2ecf20Sopenharmony_ci		}
4358c2ecf20Sopenharmony_ci		if (status & SONIC_INT_RDE) {
4368c2ecf20Sopenharmony_ci			netif_dbg(lp, rx_err, dev, "%s: rx descriptors exhausted\n",
4378c2ecf20Sopenharmony_ci				  __func__);
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci		if (status & SONIC_INT_RBAE) {
4408c2ecf20Sopenharmony_ci			netif_dbg(lp, rx_err, dev, "%s: rx buffer area exceeded\n",
4418c2ecf20Sopenharmony_ci				  __func__);
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		/* counter overruns; all counters are 16bit wide */
4458c2ecf20Sopenharmony_ci		if (status & SONIC_INT_FAE)
4468c2ecf20Sopenharmony_ci			lp->stats.rx_frame_errors += 65536;
4478c2ecf20Sopenharmony_ci		if (status & SONIC_INT_CRC)
4488c2ecf20Sopenharmony_ci			lp->stats.rx_crc_errors += 65536;
4498c2ecf20Sopenharmony_ci		if (status & SONIC_INT_MP)
4508c2ecf20Sopenharmony_ci			lp->stats.rx_missed_errors += 65536;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci		/* transmit error */
4538c2ecf20Sopenharmony_ci		if (status & SONIC_INT_TXER) {
4548c2ecf20Sopenharmony_ci			u16 tcr = SONIC_READ(SONIC_TCR);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci			netif_dbg(lp, tx_err, dev, "%s: TXER intr, TCR %04x\n",
4578c2ecf20Sopenharmony_ci				  __func__, tcr);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci			if (tcr & (SONIC_TCR_EXD | SONIC_TCR_EXC |
4608c2ecf20Sopenharmony_ci				   SONIC_TCR_FU | SONIC_TCR_BCM)) {
4618c2ecf20Sopenharmony_ci				/* Aborted transmission. Try again. */
4628c2ecf20Sopenharmony_ci				netif_stop_queue(dev);
4638c2ecf20Sopenharmony_ci				SONIC_WRITE(SONIC_CMD, SONIC_CR_TXP);
4648c2ecf20Sopenharmony_ci			}
4658c2ecf20Sopenharmony_ci		}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci		/* bus retry */
4688c2ecf20Sopenharmony_ci		if (status & SONIC_INT_BR) {
4698c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: Bus retry occurred! Device interrupt disabled.\n",
4708c2ecf20Sopenharmony_ci				dev->name);
4718c2ecf20Sopenharmony_ci			/* ... to help debug DMA problems causing endless interrupts. */
4728c2ecf20Sopenharmony_ci			/* Bounce the eth interface to turn on the interrupt again. */
4738c2ecf20Sopenharmony_ci			SONIC_WRITE(SONIC_IMR, 0);
4748c2ecf20Sopenharmony_ci		}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT;
4778c2ecf20Sopenharmony_ci	} while (status);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci/* Return the array index corresponding to a given Receive Buffer pointer. */
4858c2ecf20Sopenharmony_cistatic int index_from_addr(struct sonic_local *lp, dma_addr_t addr,
4868c2ecf20Sopenharmony_ci			   unsigned int last)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	unsigned int i = last;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	do {
4918c2ecf20Sopenharmony_ci		i = (i + 1) & SONIC_RRS_MASK;
4928c2ecf20Sopenharmony_ci		if (addr == lp->rx_laddr[i])
4938c2ecf20Sopenharmony_ci			return i;
4948c2ecf20Sopenharmony_ci	} while (i != last);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return -ENOENT;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci/* Allocate and map a new skb to be used as a receive buffer. */
5008c2ecf20Sopenharmony_cistatic bool sonic_alloc_rb(struct net_device *dev, struct sonic_local *lp,
5018c2ecf20Sopenharmony_ci			   struct sk_buff **new_skb, dma_addr_t *new_addr)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	*new_skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2);
5048c2ecf20Sopenharmony_ci	if (!*new_skb)
5058c2ecf20Sopenharmony_ci		return false;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
5088c2ecf20Sopenharmony_ci		skb_reserve(*new_skb, 2);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	*new_addr = dma_map_single(lp->device, skb_put(*new_skb, SONIC_RBSIZE),
5118c2ecf20Sopenharmony_ci				   SONIC_RBSIZE, DMA_FROM_DEVICE);
5128c2ecf20Sopenharmony_ci	if (dma_mapping_error(lp->device, *new_addr)) {
5138c2ecf20Sopenharmony_ci		dev_kfree_skb(*new_skb);
5148c2ecf20Sopenharmony_ci		*new_skb = NULL;
5158c2ecf20Sopenharmony_ci		return false;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	return true;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci/* Place a new receive resource in the Receive Resource Area and update RWP. */
5228c2ecf20Sopenharmony_cistatic void sonic_update_rra(struct net_device *dev, struct sonic_local *lp,
5238c2ecf20Sopenharmony_ci			     dma_addr_t old_addr, dma_addr_t new_addr)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	unsigned int entry = sonic_rr_entry(dev, SONIC_READ(SONIC_RWP));
5268c2ecf20Sopenharmony_ci	unsigned int end = sonic_rr_entry(dev, SONIC_READ(SONIC_RRP));
5278c2ecf20Sopenharmony_ci	u32 buf;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* The resources in the range [RRP, RWP) belong to the SONIC. This loop
5308c2ecf20Sopenharmony_ci	 * scans the other resources in the RRA, those in the range [RWP, RRP).
5318c2ecf20Sopenharmony_ci	 */
5328c2ecf20Sopenharmony_ci	do {
5338c2ecf20Sopenharmony_ci		buf = (sonic_rra_get(dev, entry, SONIC_RR_BUFADR_H) << 16) |
5348c2ecf20Sopenharmony_ci		      sonic_rra_get(dev, entry, SONIC_RR_BUFADR_L);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		if (buf == old_addr)
5378c2ecf20Sopenharmony_ci			break;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		entry = (entry + 1) & SONIC_RRS_MASK;
5408c2ecf20Sopenharmony_ci	} while (entry != end);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	WARN_ONCE(buf != old_addr, "failed to find resource!\n");
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	sonic_rra_put(dev, entry, SONIC_RR_BUFADR_H, new_addr >> 16);
5458c2ecf20Sopenharmony_ci	sonic_rra_put(dev, entry, SONIC_RR_BUFADR_L, new_addr & 0xffff);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	entry = (entry + 1) & SONIC_RRS_MASK;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_RWP, sonic_rr_addr(dev, entry));
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci/*
5538c2ecf20Sopenharmony_ci * We have a good packet(s), pass it/them up the network stack.
5548c2ecf20Sopenharmony_ci */
5558c2ecf20Sopenharmony_cistatic void sonic_rx(struct net_device *dev)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
5588c2ecf20Sopenharmony_ci	int entry = lp->cur_rx;
5598c2ecf20Sopenharmony_ci	int prev_entry = lp->eol_rx;
5608c2ecf20Sopenharmony_ci	bool rbe = false;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	while (sonic_rda_get(dev, entry, SONIC_RD_IN_USE) == 0) {
5638c2ecf20Sopenharmony_ci		u16 status = sonic_rda_get(dev, entry, SONIC_RD_STATUS);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci		/* If the RD has LPKT set, the chip has finished with the RB */
5668c2ecf20Sopenharmony_ci		if ((status & SONIC_RCR_PRX) && (status & SONIC_RCR_LPKT)) {
5678c2ecf20Sopenharmony_ci			struct sk_buff *new_skb;
5688c2ecf20Sopenharmony_ci			dma_addr_t new_laddr;
5698c2ecf20Sopenharmony_ci			u32 addr = (sonic_rda_get(dev, entry,
5708c2ecf20Sopenharmony_ci						  SONIC_RD_PKTPTR_H) << 16) |
5718c2ecf20Sopenharmony_ci				   sonic_rda_get(dev, entry, SONIC_RD_PKTPTR_L);
5728c2ecf20Sopenharmony_ci			int i = index_from_addr(lp, addr, entry);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci			if (i < 0) {
5758c2ecf20Sopenharmony_ci				WARN_ONCE(1, "failed to find buffer!\n");
5768c2ecf20Sopenharmony_ci				break;
5778c2ecf20Sopenharmony_ci			}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci			if (sonic_alloc_rb(dev, lp, &new_skb, &new_laddr)) {
5808c2ecf20Sopenharmony_ci				struct sk_buff *used_skb = lp->rx_skb[i];
5818c2ecf20Sopenharmony_ci				int pkt_len;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci				/* Pass the used buffer up the stack */
5848c2ecf20Sopenharmony_ci				dma_unmap_single(lp->device, addr, SONIC_RBSIZE,
5858c2ecf20Sopenharmony_ci						 DMA_FROM_DEVICE);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci				pkt_len = sonic_rda_get(dev, entry,
5888c2ecf20Sopenharmony_ci							SONIC_RD_PKTLEN);
5898c2ecf20Sopenharmony_ci				skb_trim(used_skb, pkt_len);
5908c2ecf20Sopenharmony_ci				used_skb->protocol = eth_type_trans(used_skb,
5918c2ecf20Sopenharmony_ci								    dev);
5928c2ecf20Sopenharmony_ci				netif_rx(used_skb);
5938c2ecf20Sopenharmony_ci				lp->stats.rx_packets++;
5948c2ecf20Sopenharmony_ci				lp->stats.rx_bytes += pkt_len;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci				lp->rx_skb[i] = new_skb;
5978c2ecf20Sopenharmony_ci				lp->rx_laddr[i] = new_laddr;
5988c2ecf20Sopenharmony_ci			} else {
5998c2ecf20Sopenharmony_ci				/* Failed to obtain a new buffer so re-use it */
6008c2ecf20Sopenharmony_ci				new_laddr = addr;
6018c2ecf20Sopenharmony_ci				lp->stats.rx_dropped++;
6028c2ecf20Sopenharmony_ci			}
6038c2ecf20Sopenharmony_ci			/* If RBE is already asserted when RWP advances then
6048c2ecf20Sopenharmony_ci			 * it's safe to clear RBE after processing this packet.
6058c2ecf20Sopenharmony_ci			 */
6068c2ecf20Sopenharmony_ci			rbe = rbe || SONIC_READ(SONIC_ISR) & SONIC_INT_RBE;
6078c2ecf20Sopenharmony_ci			sonic_update_rra(dev, lp, addr, new_laddr);
6088c2ecf20Sopenharmony_ci		}
6098c2ecf20Sopenharmony_ci		/*
6108c2ecf20Sopenharmony_ci		 * give back the descriptor
6118c2ecf20Sopenharmony_ci		 */
6128c2ecf20Sopenharmony_ci		sonic_rda_put(dev, entry, SONIC_RD_STATUS, 0);
6138c2ecf20Sopenharmony_ci		sonic_rda_put(dev, entry, SONIC_RD_IN_USE, 1);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci		prev_entry = entry;
6168c2ecf20Sopenharmony_ci		entry = (entry + 1) & SONIC_RDS_MASK;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	lp->cur_rx = entry;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	if (prev_entry != lp->eol_rx) {
6228c2ecf20Sopenharmony_ci		/* Advance the EOL flag to put descriptors back into service */
6238c2ecf20Sopenharmony_ci		sonic_rda_put(dev, prev_entry, SONIC_RD_LINK, SONIC_EOL |
6248c2ecf20Sopenharmony_ci			      sonic_rda_get(dev, prev_entry, SONIC_RD_LINK));
6258c2ecf20Sopenharmony_ci		sonic_rda_put(dev, lp->eol_rx, SONIC_RD_LINK, ~SONIC_EOL &
6268c2ecf20Sopenharmony_ci			      sonic_rda_get(dev, lp->eol_rx, SONIC_RD_LINK));
6278c2ecf20Sopenharmony_ci		lp->eol_rx = prev_entry;
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (rbe)
6318c2ecf20Sopenharmony_ci		SONIC_WRITE(SONIC_ISR, SONIC_INT_RBE);
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci/*
6368c2ecf20Sopenharmony_ci * Get the current statistics.
6378c2ecf20Sopenharmony_ci * This may be called with the device open or closed.
6388c2ecf20Sopenharmony_ci */
6398c2ecf20Sopenharmony_cistatic struct net_device_stats *sonic_get_stats(struct net_device *dev)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	/* read the tally counter from the SONIC and reset them */
6448c2ecf20Sopenharmony_ci	lp->stats.rx_crc_errors += SONIC_READ(SONIC_CRCT);
6458c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CRCT, 0xffff);
6468c2ecf20Sopenharmony_ci	lp->stats.rx_frame_errors += SONIC_READ(SONIC_FAET);
6478c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_FAET, 0xffff);
6488c2ecf20Sopenharmony_ci	lp->stats.rx_missed_errors += SONIC_READ(SONIC_MPT);
6498c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_MPT, 0xffff);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	return &lp->stats;
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci/*
6568c2ecf20Sopenharmony_ci * Set or clear the multicast filter for this adaptor.
6578c2ecf20Sopenharmony_ci */
6588c2ecf20Sopenharmony_cistatic void sonic_multicast_list(struct net_device *dev)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
6618c2ecf20Sopenharmony_ci	unsigned int rcr;
6628c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
6638c2ecf20Sopenharmony_ci	unsigned char *addr;
6648c2ecf20Sopenharmony_ci	int i;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	rcr = SONIC_READ(SONIC_RCR) & ~(SONIC_RCR_PRO | SONIC_RCR_AMC);
6678c2ecf20Sopenharmony_ci	rcr |= SONIC_RCR_BRD;	/* accept broadcast packets */
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {	/* set promiscuous mode */
6708c2ecf20Sopenharmony_ci		rcr |= SONIC_RCR_PRO;
6718c2ecf20Sopenharmony_ci	} else {
6728c2ecf20Sopenharmony_ci		if ((dev->flags & IFF_ALLMULTI) ||
6738c2ecf20Sopenharmony_ci		    (netdev_mc_count(dev) > 15)) {
6748c2ecf20Sopenharmony_ci			rcr |= SONIC_RCR_AMC;
6758c2ecf20Sopenharmony_ci		} else {
6768c2ecf20Sopenharmony_ci			unsigned long flags;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci			netif_dbg(lp, ifup, dev, "%s: mc_count %d\n", __func__,
6798c2ecf20Sopenharmony_ci				  netdev_mc_count(dev));
6808c2ecf20Sopenharmony_ci			sonic_set_cam_enable(dev, 1);  /* always enable our own address */
6818c2ecf20Sopenharmony_ci			i = 1;
6828c2ecf20Sopenharmony_ci			netdev_for_each_mc_addr(ha, dev) {
6838c2ecf20Sopenharmony_ci				addr = ha->addr;
6848c2ecf20Sopenharmony_ci				sonic_cda_put(dev, i, SONIC_CD_CAP0, addr[1] << 8 | addr[0]);
6858c2ecf20Sopenharmony_ci				sonic_cda_put(dev, i, SONIC_CD_CAP1, addr[3] << 8 | addr[2]);
6868c2ecf20Sopenharmony_ci				sonic_cda_put(dev, i, SONIC_CD_CAP2, addr[5] << 8 | addr[4]);
6878c2ecf20Sopenharmony_ci				sonic_set_cam_enable(dev, sonic_get_cam_enable(dev) | (1 << i));
6888c2ecf20Sopenharmony_ci				i++;
6898c2ecf20Sopenharmony_ci			}
6908c2ecf20Sopenharmony_ci			SONIC_WRITE(SONIC_CDC, 16);
6918c2ecf20Sopenharmony_ci			SONIC_WRITE(SONIC_CDP, lp->cda_laddr & 0xffff);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci			/* LCAM and TXP commands can't be used simultaneously */
6948c2ecf20Sopenharmony_ci			spin_lock_irqsave(&lp->lock, flags);
6958c2ecf20Sopenharmony_ci			sonic_quiesce(dev, SONIC_CR_TXP, false);
6968c2ecf20Sopenharmony_ci			SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM);
6978c2ecf20Sopenharmony_ci			sonic_quiesce(dev, SONIC_CR_LCAM, false);
6988c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&lp->lock, flags);
6998c2ecf20Sopenharmony_ci		}
7008c2ecf20Sopenharmony_ci	}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	netif_dbg(lp, ifup, dev, "%s: setting RCR=%x\n", __func__, rcr);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_RCR, rcr);
7058c2ecf20Sopenharmony_ci}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci/*
7098c2ecf20Sopenharmony_ci * Initialize the SONIC ethernet controller.
7108c2ecf20Sopenharmony_ci */
7118c2ecf20Sopenharmony_cistatic int sonic_init(struct net_device *dev, bool may_sleep)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct sonic_local *lp = netdev_priv(dev);
7148c2ecf20Sopenharmony_ci	int i;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/*
7178c2ecf20Sopenharmony_ci	 * put the Sonic into software-reset mode and
7188c2ecf20Sopenharmony_ci	 * disable all interrupts
7198c2ecf20Sopenharmony_ci	 */
7208c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_IMR, 0);
7218c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_ISR, 0x7fff);
7228c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	/* While in reset mode, clear CAM Enable register */
7258c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CE, 0);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/*
7288c2ecf20Sopenharmony_ci	 * clear software reset flag, disable receiver, clear and
7298c2ecf20Sopenharmony_ci	 * enable interrupts, then completely initialize the SONIC
7308c2ecf20Sopenharmony_ci	 */
7318c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, 0);
7328c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS | SONIC_CR_STP);
7338c2ecf20Sopenharmony_ci	sonic_quiesce(dev, SONIC_CR_ALL, may_sleep);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	/*
7368c2ecf20Sopenharmony_ci	 * initialize the receive resource area
7378c2ecf20Sopenharmony_ci	 */
7388c2ecf20Sopenharmony_ci	netif_dbg(lp, ifup, dev, "%s: initialize receive resource area\n",
7398c2ecf20Sopenharmony_ci		  __func__);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	for (i = 0; i < SONIC_NUM_RRS; i++) {
7428c2ecf20Sopenharmony_ci		u16 bufadr_l = (unsigned long)lp->rx_laddr[i] & 0xffff;
7438c2ecf20Sopenharmony_ci		u16 bufadr_h = (unsigned long)lp->rx_laddr[i] >> 16;
7448c2ecf20Sopenharmony_ci		sonic_rra_put(dev, i, SONIC_RR_BUFADR_L, bufadr_l);
7458c2ecf20Sopenharmony_ci		sonic_rra_put(dev, i, SONIC_RR_BUFADR_H, bufadr_h);
7468c2ecf20Sopenharmony_ci		sonic_rra_put(dev, i, SONIC_RR_BUFSIZE_L, SONIC_RBSIZE >> 1);
7478c2ecf20Sopenharmony_ci		sonic_rra_put(dev, i, SONIC_RR_BUFSIZE_H, 0);
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	/* initialize all RRA registers */
7518c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_RSA, sonic_rr_addr(dev, 0));
7528c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_REA, sonic_rr_addr(dev, SONIC_NUM_RRS));
7538c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_RRP, sonic_rr_addr(dev, 0));
7548c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_RWP, sonic_rr_addr(dev, SONIC_NUM_RRS - 1));
7558c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_URRA, lp->rra_laddr >> 16);
7568c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_EOBC, (SONIC_RBSIZE >> 1) - (lp->dma_bitmode ? 2 : 1));
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	/* load the resource pointers */
7598c2ecf20Sopenharmony_ci	netif_dbg(lp, ifup, dev, "%s: issuing RRRA command\n", __func__);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_RRRA);
7628c2ecf20Sopenharmony_ci	sonic_quiesce(dev, SONIC_CR_RRRA, may_sleep);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	/*
7658c2ecf20Sopenharmony_ci	 * Initialize the receive descriptors so that they
7668c2ecf20Sopenharmony_ci	 * become a circular linked list, ie. let the last
7678c2ecf20Sopenharmony_ci	 * descriptor point to the first again.
7688c2ecf20Sopenharmony_ci	 */
7698c2ecf20Sopenharmony_ci	netif_dbg(lp, ifup, dev, "%s: initialize receive descriptors\n",
7708c2ecf20Sopenharmony_ci		  __func__);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	for (i=0; i<SONIC_NUM_RDS; i++) {
7738c2ecf20Sopenharmony_ci		sonic_rda_put(dev, i, SONIC_RD_STATUS, 0);
7748c2ecf20Sopenharmony_ci		sonic_rda_put(dev, i, SONIC_RD_PKTLEN, 0);
7758c2ecf20Sopenharmony_ci		sonic_rda_put(dev, i, SONIC_RD_PKTPTR_L, 0);
7768c2ecf20Sopenharmony_ci		sonic_rda_put(dev, i, SONIC_RD_PKTPTR_H, 0);
7778c2ecf20Sopenharmony_ci		sonic_rda_put(dev, i, SONIC_RD_SEQNO, 0);
7788c2ecf20Sopenharmony_ci		sonic_rda_put(dev, i, SONIC_RD_IN_USE, 1);
7798c2ecf20Sopenharmony_ci		sonic_rda_put(dev, i, SONIC_RD_LINK,
7808c2ecf20Sopenharmony_ci			lp->rda_laddr +
7818c2ecf20Sopenharmony_ci			((i+1) * SIZEOF_SONIC_RD * SONIC_BUS_SCALE(lp->dma_bitmode)));
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci	/* fix last descriptor */
7848c2ecf20Sopenharmony_ci	sonic_rda_put(dev, SONIC_NUM_RDS - 1, SONIC_RD_LINK,
7858c2ecf20Sopenharmony_ci		(lp->rda_laddr & 0xffff) | SONIC_EOL);
7868c2ecf20Sopenharmony_ci	lp->eol_rx = SONIC_NUM_RDS - 1;
7878c2ecf20Sopenharmony_ci	lp->cur_rx = 0;
7888c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_URDA, lp->rda_laddr >> 16);
7898c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CRDA, lp->rda_laddr & 0xffff);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	/*
7928c2ecf20Sopenharmony_ci	 * initialize transmit descriptors
7938c2ecf20Sopenharmony_ci	 */
7948c2ecf20Sopenharmony_ci	netif_dbg(lp, ifup, dev, "%s: initialize transmit descriptors\n",
7958c2ecf20Sopenharmony_ci		  __func__);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	for (i = 0; i < SONIC_NUM_TDS; i++) {
7988c2ecf20Sopenharmony_ci		sonic_tda_put(dev, i, SONIC_TD_STATUS, 0);
7998c2ecf20Sopenharmony_ci		sonic_tda_put(dev, i, SONIC_TD_CONFIG, 0);
8008c2ecf20Sopenharmony_ci		sonic_tda_put(dev, i, SONIC_TD_PKTSIZE, 0);
8018c2ecf20Sopenharmony_ci		sonic_tda_put(dev, i, SONIC_TD_FRAG_COUNT, 0);
8028c2ecf20Sopenharmony_ci		sonic_tda_put(dev, i, SONIC_TD_LINK,
8038c2ecf20Sopenharmony_ci			(lp->tda_laddr & 0xffff) +
8048c2ecf20Sopenharmony_ci			(i + 1) * SIZEOF_SONIC_TD * SONIC_BUS_SCALE(lp->dma_bitmode));
8058c2ecf20Sopenharmony_ci		lp->tx_skb[i] = NULL;
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci	/* fix last descriptor */
8088c2ecf20Sopenharmony_ci	sonic_tda_put(dev, SONIC_NUM_TDS - 1, SONIC_TD_LINK,
8098c2ecf20Sopenharmony_ci		(lp->tda_laddr & 0xffff));
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_UTDA, lp->tda_laddr >> 16);
8128c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CTDA, lp->tda_laddr & 0xffff);
8138c2ecf20Sopenharmony_ci	lp->cur_tx = 0;
8148c2ecf20Sopenharmony_ci	lp->eol_tx = SONIC_NUM_TDS - 1;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	/*
8178c2ecf20Sopenharmony_ci	 * put our own address to CAM desc[0]
8188c2ecf20Sopenharmony_ci	 */
8198c2ecf20Sopenharmony_ci	sonic_cda_put(dev, 0, SONIC_CD_CAP0, dev->dev_addr[1] << 8 | dev->dev_addr[0]);
8208c2ecf20Sopenharmony_ci	sonic_cda_put(dev, 0, SONIC_CD_CAP1, dev->dev_addr[3] << 8 | dev->dev_addr[2]);
8218c2ecf20Sopenharmony_ci	sonic_cda_put(dev, 0, SONIC_CD_CAP2, dev->dev_addr[5] << 8 | dev->dev_addr[4]);
8228c2ecf20Sopenharmony_ci	sonic_set_cam_enable(dev, 1);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
8258c2ecf20Sopenharmony_ci		sonic_cda_put(dev, i, SONIC_CD_ENTRY_POINTER, i);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	/*
8288c2ecf20Sopenharmony_ci	 * initialize CAM registers
8298c2ecf20Sopenharmony_ci	 */
8308c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CDP, lp->cda_laddr & 0xffff);
8318c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CDC, 16);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	/*
8348c2ecf20Sopenharmony_ci	 * load the CAM
8358c2ecf20Sopenharmony_ci	 */
8368c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM);
8378c2ecf20Sopenharmony_ci	sonic_quiesce(dev, SONIC_CR_LCAM, may_sleep);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/*
8408c2ecf20Sopenharmony_ci	 * enable receiver, disable loopback
8418c2ecf20Sopenharmony_ci	 * and enable all interrupts
8428c2ecf20Sopenharmony_ci	 */
8438c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_RCR, SONIC_RCR_DEFAULT);
8448c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_TCR, SONIC_TCR_DEFAULT);
8458c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_ISR, 0x7fff);
8468c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_IMR, SONIC_IMR_DEFAULT);
8478c2ecf20Sopenharmony_ci	SONIC_WRITE(SONIC_CMD, SONIC_CR_RXEN);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	netif_dbg(lp, ifup, dev, "%s: new status=%x\n", __func__,
8508c2ecf20Sopenharmony_ci		  SONIC_READ(SONIC_CMD));
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	return 0;
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
856