18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Gigabit Ethernet adapters based on the Session Layer
48c2ecf20Sopenharmony_ci * Interface (SLIC) technology by Alacritech. The driver does not
58c2ecf20Sopenharmony_ci * support the hardware acceleration features provided by these cards.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2016 Lino Sanfilippo <LinoSanfilippo@gmx.de>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/pci.h>
138c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
148c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
168c2ecf20Sopenharmony_ci#include <linux/crc32.h>
178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
188c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
198c2ecf20Sopenharmony_ci#include <linux/mii.h>
208c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
218c2ecf20Sopenharmony_ci#include <linux/delay.h>
228c2ecf20Sopenharmony_ci#include <linux/firmware.h>
238c2ecf20Sopenharmony_ci#include <linux/list.h>
248c2ecf20Sopenharmony_ci#include <linux/u64_stats_sync.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "slic.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define DRV_NAME			"slicoss"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic const struct pci_device_id slic_id_tbl[] = {
318c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH,
328c2ecf20Sopenharmony_ci		     PCI_DEVICE_ID_ALACRITECH_MOJAVE) },
338c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH,
348c2ecf20Sopenharmony_ci		     PCI_DEVICE_ID_ALACRITECH_OASIS) },
358c2ecf20Sopenharmony_ci	{ 0 }
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic const char slic_stats_strings[][ETH_GSTRING_LEN] = {
398c2ecf20Sopenharmony_ci	"rx_packets",
408c2ecf20Sopenharmony_ci	"rx_bytes",
418c2ecf20Sopenharmony_ci	"rx_multicasts",
428c2ecf20Sopenharmony_ci	"rx_errors",
438c2ecf20Sopenharmony_ci	"rx_buff_miss",
448c2ecf20Sopenharmony_ci	"rx_tp_csum",
458c2ecf20Sopenharmony_ci	"rx_tp_oflow",
468c2ecf20Sopenharmony_ci	"rx_tp_hlen",
478c2ecf20Sopenharmony_ci	"rx_ip_csum",
488c2ecf20Sopenharmony_ci	"rx_ip_len",
498c2ecf20Sopenharmony_ci	"rx_ip_hdr_len",
508c2ecf20Sopenharmony_ci	"rx_early",
518c2ecf20Sopenharmony_ci	"rx_buff_oflow",
528c2ecf20Sopenharmony_ci	"rx_lcode",
538c2ecf20Sopenharmony_ci	"rx_drbl",
548c2ecf20Sopenharmony_ci	"rx_crc",
558c2ecf20Sopenharmony_ci	"rx_oflow_802",
568c2ecf20Sopenharmony_ci	"rx_uflow_802",
578c2ecf20Sopenharmony_ci	"tx_packets",
588c2ecf20Sopenharmony_ci	"tx_bytes",
598c2ecf20Sopenharmony_ci	"tx_carrier",
608c2ecf20Sopenharmony_ci	"tx_dropped",
618c2ecf20Sopenharmony_ci	"irq_errs",
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic inline int slic_next_queue_idx(unsigned int idx, unsigned int qlen)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	return (idx + 1) & (qlen - 1);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic inline int slic_get_free_queue_descs(unsigned int put_idx,
708c2ecf20Sopenharmony_ci					    unsigned int done_idx,
718c2ecf20Sopenharmony_ci					    unsigned int qlen)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	if (put_idx >= done_idx)
748c2ecf20Sopenharmony_ci		return (qlen - (put_idx - done_idx) - 1);
758c2ecf20Sopenharmony_ci	return (done_idx - put_idx - 1);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic unsigned int slic_next_compl_idx(struct slic_device *sdev)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct slic_stat_queue *stq = &sdev->stq;
818c2ecf20Sopenharmony_ci	unsigned int active = stq->active_array;
828c2ecf20Sopenharmony_ci	struct slic_stat_desc *descs;
838c2ecf20Sopenharmony_ci	struct slic_stat_desc *stat;
848c2ecf20Sopenharmony_ci	unsigned int idx;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	descs = stq->descs[active];
878c2ecf20Sopenharmony_ci	stat = &descs[stq->done_idx];
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (!stat->status)
908c2ecf20Sopenharmony_ci		return SLIC_INVALID_STAT_DESC_IDX;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	idx = (le32_to_cpu(stat->hnd) & 0xffff) - 1;
938c2ecf20Sopenharmony_ci	/* reset desc */
948c2ecf20Sopenharmony_ci	stat->hnd = 0;
958c2ecf20Sopenharmony_ci	stat->status = 0;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	stq->done_idx = slic_next_queue_idx(stq->done_idx, stq->len);
988c2ecf20Sopenharmony_ci	/* check for wraparound */
998c2ecf20Sopenharmony_ci	if (!stq->done_idx) {
1008c2ecf20Sopenharmony_ci		dma_addr_t paddr = stq->paddr[active];
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) |
1038c2ecf20Sopenharmony_ci						stq->len);
1048c2ecf20Sopenharmony_ci		/* make sure new status descriptors are immediately available */
1058c2ecf20Sopenharmony_ci		slic_flush_write(sdev);
1068c2ecf20Sopenharmony_ci		active++;
1078c2ecf20Sopenharmony_ci		active &= (SLIC_NUM_STAT_DESC_ARRAYS - 1);
1088c2ecf20Sopenharmony_ci		stq->active_array = active;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	return idx;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic unsigned int slic_get_free_tx_descs(struct slic_tx_queue *txq)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	/* ensure tail idx is updated */
1168c2ecf20Sopenharmony_ci	smp_mb();
1178c2ecf20Sopenharmony_ci	return slic_get_free_queue_descs(txq->put_idx, txq->done_idx, txq->len);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic unsigned int slic_get_free_rx_descs(struct slic_rx_queue *rxq)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	return slic_get_free_queue_descs(rxq->put_idx, rxq->done_idx, rxq->len);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void slic_clear_upr_list(struct slic_upr_list *upr_list)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct slic_upr *upr;
1288c2ecf20Sopenharmony_ci	struct slic_upr *tmp;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	spin_lock_bh(&upr_list->lock);
1318c2ecf20Sopenharmony_ci	list_for_each_entry_safe(upr, tmp, &upr_list->list, list) {
1328c2ecf20Sopenharmony_ci		list_del(&upr->list);
1338c2ecf20Sopenharmony_ci		kfree(upr);
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci	upr_list->pending = false;
1368c2ecf20Sopenharmony_ci	spin_unlock_bh(&upr_list->lock);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic void slic_start_upr(struct slic_device *sdev, struct slic_upr *upr)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	u32 reg;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	reg = (upr->type == SLIC_UPR_CONFIG) ? SLIC_REG_RCONFIG :
1448c2ecf20Sopenharmony_ci					       SLIC_REG_LSTAT;
1458c2ecf20Sopenharmony_ci	slic_write(sdev, reg, lower_32_bits(upr->paddr));
1468c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void slic_queue_upr(struct slic_device *sdev, struct slic_upr *upr)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct slic_upr_list *upr_list = &sdev->upr_list;
1528c2ecf20Sopenharmony_ci	bool pending;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	spin_lock_bh(&upr_list->lock);
1558c2ecf20Sopenharmony_ci	pending = upr_list->pending;
1568c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&upr->list);
1578c2ecf20Sopenharmony_ci	list_add_tail(&upr->list, &upr_list->list);
1588c2ecf20Sopenharmony_ci	upr_list->pending = true;
1598c2ecf20Sopenharmony_ci	spin_unlock_bh(&upr_list->lock);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (!pending)
1628c2ecf20Sopenharmony_ci		slic_start_upr(sdev, upr);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic struct slic_upr *slic_dequeue_upr(struct slic_device *sdev)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct slic_upr_list *upr_list = &sdev->upr_list;
1688c2ecf20Sopenharmony_ci	struct slic_upr *next_upr = NULL;
1698c2ecf20Sopenharmony_ci	struct slic_upr *upr = NULL;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	spin_lock_bh(&upr_list->lock);
1728c2ecf20Sopenharmony_ci	if (!list_empty(&upr_list->list)) {
1738c2ecf20Sopenharmony_ci		upr = list_first_entry(&upr_list->list, struct slic_upr, list);
1748c2ecf20Sopenharmony_ci		list_del(&upr->list);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		if (list_empty(&upr_list->list))
1778c2ecf20Sopenharmony_ci			upr_list->pending = false;
1788c2ecf20Sopenharmony_ci		else
1798c2ecf20Sopenharmony_ci			next_upr = list_first_entry(&upr_list->list,
1808c2ecf20Sopenharmony_ci						    struct slic_upr, list);
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci	spin_unlock_bh(&upr_list->lock);
1838c2ecf20Sopenharmony_ci	/* trigger processing of the next upr in list */
1848c2ecf20Sopenharmony_ci	if (next_upr)
1858c2ecf20Sopenharmony_ci		slic_start_upr(sdev, next_upr);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return upr;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic int slic_new_upr(struct slic_device *sdev, unsigned int type,
1918c2ecf20Sopenharmony_ci			dma_addr_t paddr)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct slic_upr *upr;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	upr = kmalloc(sizeof(*upr), GFP_ATOMIC);
1968c2ecf20Sopenharmony_ci	if (!upr)
1978c2ecf20Sopenharmony_ci		return -ENOMEM;
1988c2ecf20Sopenharmony_ci	upr->type = type;
1998c2ecf20Sopenharmony_ci	upr->paddr = paddr;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	slic_queue_upr(sdev, upr);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return 0;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic void slic_set_mcast_bit(u64 *mcmask, unsigned char const *addr)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	u64 mask = *mcmask;
2098c2ecf20Sopenharmony_ci	u8 crc;
2108c2ecf20Sopenharmony_ci	/* Get the CRC polynomial for the mac address: we use bits 1-8 (lsb),
2118c2ecf20Sopenharmony_ci	 * bitwise reversed, msb (= lsb bit 0 before bitrev) is automatically
2128c2ecf20Sopenharmony_ci	 * discarded.
2138c2ecf20Sopenharmony_ci	 */
2148c2ecf20Sopenharmony_ci	crc = ether_crc(ETH_ALEN, addr) >> 23;
2158c2ecf20Sopenharmony_ci	 /* we only have space on the SLIC for 64 entries */
2168c2ecf20Sopenharmony_ci	crc &= 0x3F;
2178c2ecf20Sopenharmony_ci	mask |= (u64)1 << crc;
2188c2ecf20Sopenharmony_ci	*mcmask = mask;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/* must be called with link_lock held */
2228c2ecf20Sopenharmony_cistatic void slic_configure_rcv(struct slic_device *sdev)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	u32 val;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	val = SLIC_GRCR_RESET | SLIC_GRCR_ADDRAEN | SLIC_GRCR_RCVEN |
2278c2ecf20Sopenharmony_ci	      SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT | SLIC_GRCR_RCVBAD;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (sdev->duplex == DUPLEX_FULL)
2308c2ecf20Sopenharmony_ci		val |= SLIC_GRCR_CTLEN;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (sdev->promisc)
2338c2ecf20Sopenharmony_ci		val |= SLIC_GRCR_RCVALL;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRCFG, val);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci/* must be called with link_lock held */
2398c2ecf20Sopenharmony_cistatic void slic_configure_xmt(struct slic_device *sdev)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	u32 val;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	val = SLIC_GXCR_RESET | SLIC_GXCR_XMTEN;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (sdev->duplex == DUPLEX_FULL)
2468c2ecf20Sopenharmony_ci		val |= SLIC_GXCR_PAUSEEN;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WXCFG, val);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/* must be called with link_lock held */
2528c2ecf20Sopenharmony_cistatic void slic_configure_mac(struct slic_device *sdev)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	u32 val;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (sdev->speed == SPEED_1000) {
2578c2ecf20Sopenharmony_ci		val = SLIC_GMCR_GAPBB_1000 << SLIC_GMCR_GAPBB_SHIFT |
2588c2ecf20Sopenharmony_ci		      SLIC_GMCR_GAPR1_1000 << SLIC_GMCR_GAPR1_SHIFT |
2598c2ecf20Sopenharmony_ci		      SLIC_GMCR_GAPR2_1000 << SLIC_GMCR_GAPR2_SHIFT |
2608c2ecf20Sopenharmony_ci		      SLIC_GMCR_GBIT; /* enable GMII */
2618c2ecf20Sopenharmony_ci	} else {
2628c2ecf20Sopenharmony_ci		val = SLIC_GMCR_GAPBB_100 << SLIC_GMCR_GAPBB_SHIFT |
2638c2ecf20Sopenharmony_ci		      SLIC_GMCR_GAPR1_100 << SLIC_GMCR_GAPR1_SHIFT |
2648c2ecf20Sopenharmony_ci		      SLIC_GMCR_GAPR2_100 << SLIC_GMCR_GAPR2_SHIFT;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (sdev->duplex == DUPLEX_FULL)
2688c2ecf20Sopenharmony_ci		val |= SLIC_GMCR_FULLD;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WMCFG, val);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void slic_configure_link_locked(struct slic_device *sdev, int speed,
2748c2ecf20Sopenharmony_ci				       unsigned int duplex)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct net_device *dev = sdev->netdev;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (sdev->speed == speed && sdev->duplex == duplex)
2798c2ecf20Sopenharmony_ci		return;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	sdev->speed = speed;
2828c2ecf20Sopenharmony_ci	sdev->duplex = duplex;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (sdev->speed == SPEED_UNKNOWN) {
2858c2ecf20Sopenharmony_ci		if (netif_carrier_ok(dev))
2868c2ecf20Sopenharmony_ci			netif_carrier_off(dev);
2878c2ecf20Sopenharmony_ci	} else {
2888c2ecf20Sopenharmony_ci		/* (re)configure link settings */
2898c2ecf20Sopenharmony_ci		slic_configure_mac(sdev);
2908c2ecf20Sopenharmony_ci		slic_configure_xmt(sdev);
2918c2ecf20Sopenharmony_ci		slic_configure_rcv(sdev);
2928c2ecf20Sopenharmony_ci		slic_flush_write(sdev);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		if (!netif_carrier_ok(dev))
2958c2ecf20Sopenharmony_ci			netif_carrier_on(dev);
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic void slic_configure_link(struct slic_device *sdev, int speed,
3008c2ecf20Sopenharmony_ci				unsigned int duplex)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	spin_lock_bh(&sdev->link_lock);
3038c2ecf20Sopenharmony_ci	slic_configure_link_locked(sdev, speed, duplex);
3048c2ecf20Sopenharmony_ci	spin_unlock_bh(&sdev->link_lock);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void slic_set_rx_mode(struct net_device *dev)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
3108c2ecf20Sopenharmony_ci	struct netdev_hw_addr *hwaddr;
3118c2ecf20Sopenharmony_ci	bool set_promisc;
3128c2ecf20Sopenharmony_ci	u64 mcmask;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
3158c2ecf20Sopenharmony_ci		/* Turn on all multicast addresses. We have to do this for
3168c2ecf20Sopenharmony_ci		 * promiscuous mode as well as ALLMCAST mode (it saves the
3178c2ecf20Sopenharmony_ci		 * microcode from having to keep state about the MAC
3188c2ecf20Sopenharmony_ci		 * configuration).
3198c2ecf20Sopenharmony_ci		 */
3208c2ecf20Sopenharmony_ci		mcmask = ~(u64)0;
3218c2ecf20Sopenharmony_ci	} else  {
3228c2ecf20Sopenharmony_ci		mcmask = 0;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(hwaddr, dev) {
3258c2ecf20Sopenharmony_ci			slic_set_mcast_bit(&mcmask, hwaddr->addr);
3268c2ecf20Sopenharmony_ci		}
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_MCASTLOW, lower_32_bits(mcmask));
3308c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_MCASTHIGH, upper_32_bits(mcmask));
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	set_promisc = !!(dev->flags & IFF_PROMISC);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	spin_lock_bh(&sdev->link_lock);
3358c2ecf20Sopenharmony_ci	if (sdev->promisc != set_promisc) {
3368c2ecf20Sopenharmony_ci		sdev->promisc = set_promisc;
3378c2ecf20Sopenharmony_ci		slic_configure_rcv(sdev);
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci	spin_unlock_bh(&sdev->link_lock);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic void slic_xmit_complete(struct slic_device *sdev)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct slic_tx_queue *txq = &sdev->txq;
3458c2ecf20Sopenharmony_ci	struct net_device *dev = sdev->netdev;
3468c2ecf20Sopenharmony_ci	struct slic_tx_buffer *buff;
3478c2ecf20Sopenharmony_ci	unsigned int frames = 0;
3488c2ecf20Sopenharmony_ci	unsigned int bytes = 0;
3498c2ecf20Sopenharmony_ci	unsigned int idx;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* Limit processing to SLIC_MAX_TX_COMPLETIONS frames to avoid that new
3528c2ecf20Sopenharmony_ci	 * completions during processing keeps the loop running endlessly.
3538c2ecf20Sopenharmony_ci	 */
3548c2ecf20Sopenharmony_ci	do {
3558c2ecf20Sopenharmony_ci		idx = slic_next_compl_idx(sdev);
3568c2ecf20Sopenharmony_ci		if (idx == SLIC_INVALID_STAT_DESC_IDX)
3578c2ecf20Sopenharmony_ci			break;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		txq->done_idx = idx;
3608c2ecf20Sopenharmony_ci		buff = &txq->txbuffs[idx];
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		if (unlikely(!buff->skb)) {
3638c2ecf20Sopenharmony_ci			netdev_warn(dev,
3648c2ecf20Sopenharmony_ci				    "no skb found for desc idx %i\n", idx);
3658c2ecf20Sopenharmony_ci			continue;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci		dma_unmap_single(&sdev->pdev->dev,
3688c2ecf20Sopenharmony_ci				 dma_unmap_addr(buff, map_addr),
3698c2ecf20Sopenharmony_ci				 dma_unmap_len(buff, map_len), DMA_TO_DEVICE);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		bytes += buff->skb->len;
3728c2ecf20Sopenharmony_ci		frames++;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci		dev_kfree_skb_any(buff->skb);
3758c2ecf20Sopenharmony_ci		buff->skb = NULL;
3768c2ecf20Sopenharmony_ci	} while (frames < SLIC_MAX_TX_COMPLETIONS);
3778c2ecf20Sopenharmony_ci	/* make sure xmit sees the new value for done_idx */
3788c2ecf20Sopenharmony_ci	smp_wmb();
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	u64_stats_update_begin(&sdev->stats.syncp);
3818c2ecf20Sopenharmony_ci	sdev->stats.tx_bytes += bytes;
3828c2ecf20Sopenharmony_ci	sdev->stats.tx_packets += frames;
3838c2ecf20Sopenharmony_ci	u64_stats_update_end(&sdev->stats.syncp);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	netif_tx_lock(dev);
3868c2ecf20Sopenharmony_ci	if (netif_queue_stopped(dev) &&
3878c2ecf20Sopenharmony_ci	    (slic_get_free_tx_descs(txq) >= SLIC_MIN_TX_WAKEUP_DESCS))
3888c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
3898c2ecf20Sopenharmony_ci	netif_tx_unlock(dev);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic void slic_refill_rx_queue(struct slic_device *sdev, gfp_t gfp)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	const unsigned int ALIGN_MASK = SLIC_RX_BUFF_ALIGN - 1;
3958c2ecf20Sopenharmony_ci	unsigned int maplen = SLIC_RX_BUFF_SIZE;
3968c2ecf20Sopenharmony_ci	struct slic_rx_queue *rxq = &sdev->rxq;
3978c2ecf20Sopenharmony_ci	struct net_device *dev = sdev->netdev;
3988c2ecf20Sopenharmony_ci	struct slic_rx_buffer *buff;
3998c2ecf20Sopenharmony_ci	struct slic_rx_desc *desc;
4008c2ecf20Sopenharmony_ci	unsigned int misalign;
4018c2ecf20Sopenharmony_ci	unsigned int offset;
4028c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4038c2ecf20Sopenharmony_ci	dma_addr_t paddr;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	while (slic_get_free_rx_descs(rxq) > SLIC_MAX_REQ_RX_DESCS) {
4068c2ecf20Sopenharmony_ci		skb = alloc_skb(maplen + ALIGN_MASK, gfp);
4078c2ecf20Sopenharmony_ci		if (!skb)
4088c2ecf20Sopenharmony_ci			break;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen,
4118c2ecf20Sopenharmony_ci				       DMA_FROM_DEVICE);
4128c2ecf20Sopenharmony_ci		if (dma_mapping_error(&sdev->pdev->dev, paddr)) {
4138c2ecf20Sopenharmony_ci			netdev_err(dev, "mapping rx packet failed\n");
4148c2ecf20Sopenharmony_ci			/* drop skb */
4158c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
4168c2ecf20Sopenharmony_ci			break;
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci		/* ensure head buffer descriptors are 256 byte aligned */
4198c2ecf20Sopenharmony_ci		offset = 0;
4208c2ecf20Sopenharmony_ci		misalign = paddr & ALIGN_MASK;
4218c2ecf20Sopenharmony_ci		if (misalign) {
4228c2ecf20Sopenharmony_ci			offset = SLIC_RX_BUFF_ALIGN - misalign;
4238c2ecf20Sopenharmony_ci			skb_reserve(skb, offset);
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci		/* the HW expects dma chunks for descriptor + frame data */
4268c2ecf20Sopenharmony_ci		desc = (struct slic_rx_desc *)skb->data;
4278c2ecf20Sopenharmony_ci		/* temporarily sync descriptor for CPU to clear status */
4288c2ecf20Sopenharmony_ci		dma_sync_single_for_cpu(&sdev->pdev->dev, paddr,
4298c2ecf20Sopenharmony_ci					offset + sizeof(*desc),
4308c2ecf20Sopenharmony_ci					DMA_FROM_DEVICE);
4318c2ecf20Sopenharmony_ci		desc->status = 0;
4328c2ecf20Sopenharmony_ci		/* return it to HW again */
4338c2ecf20Sopenharmony_ci		dma_sync_single_for_device(&sdev->pdev->dev, paddr,
4348c2ecf20Sopenharmony_ci					   offset + sizeof(*desc),
4358c2ecf20Sopenharmony_ci					   DMA_FROM_DEVICE);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		buff = &rxq->rxbuffs[rxq->put_idx];
4388c2ecf20Sopenharmony_ci		buff->skb = skb;
4398c2ecf20Sopenharmony_ci		dma_unmap_addr_set(buff, map_addr, paddr);
4408c2ecf20Sopenharmony_ci		dma_unmap_len_set(buff, map_len, maplen);
4418c2ecf20Sopenharmony_ci		buff->addr_offset = offset;
4428c2ecf20Sopenharmony_ci		/* complete write to descriptor before it is handed to HW */
4438c2ecf20Sopenharmony_ci		wmb();
4448c2ecf20Sopenharmony_ci		/* head buffer descriptors are placed immediately before skb */
4458c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_HBAR, lower_32_bits(paddr) + offset);
4468c2ecf20Sopenharmony_ci		rxq->put_idx = slic_next_queue_idx(rxq->put_idx, rxq->len);
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic void slic_handle_frame_error(struct slic_device *sdev,
4518c2ecf20Sopenharmony_ci				    struct sk_buff *skb)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct slic_stats *stats = &sdev->stats;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (sdev->model == SLIC_MODEL_OASIS) {
4568c2ecf20Sopenharmony_ci		struct slic_rx_info_oasis *info;
4578c2ecf20Sopenharmony_ci		u32 status_b;
4588c2ecf20Sopenharmony_ci		u32 status;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		info = (struct slic_rx_info_oasis *)skb->data;
4618c2ecf20Sopenharmony_ci		status = le32_to_cpu(info->frame_status);
4628c2ecf20Sopenharmony_ci		status_b = le32_to_cpu(info->frame_status_b);
4638c2ecf20Sopenharmony_ci		/* transport layer */
4648c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_TPCSUM)
4658c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_tpcsum);
4668c2ecf20Sopenharmony_ci		if (status & SLIC_VRHSTAT_TPOFLO)
4678c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_tpoflow);
4688c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_TPHLEN)
4698c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_tphlen);
4708c2ecf20Sopenharmony_ci		/* ip layer */
4718c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_IPCSUM)
4728c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_ipcsum);
4738c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_IPLERR)
4748c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_iplen);
4758c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_IPHERR)
4768c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_iphlen);
4778c2ecf20Sopenharmony_ci		/* link layer */
4788c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_RCVE)
4798c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_early);
4808c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_BUFF)
4818c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_buffoflow);
4828c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_CODE)
4838c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_lcode);
4848c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_DRBL)
4858c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_drbl);
4868c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_CRC)
4878c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_crc);
4888c2ecf20Sopenharmony_ci		if (status & SLIC_VRHSTAT_802OE)
4898c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_oflow802);
4908c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_802UE)
4918c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_uflow802);
4928c2ecf20Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_CARRE)
4938c2ecf20Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, tx_carrier);
4948c2ecf20Sopenharmony_ci	} else { /* mojave */
4958c2ecf20Sopenharmony_ci		struct slic_rx_info_mojave *info;
4968c2ecf20Sopenharmony_ci		u32 status;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci		info = (struct slic_rx_info_mojave *)skb->data;
4998c2ecf20Sopenharmony_ci		status = le32_to_cpu(info->frame_status);
5008c2ecf20Sopenharmony_ci		/* transport layer */
5018c2ecf20Sopenharmony_ci		if (status & SLIC_VGBSTAT_XPERR) {
5028c2ecf20Sopenharmony_ci			u32 xerr = status >> SLIC_VGBSTAT_XERRSHFT;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci			if (xerr == SLIC_VGBSTAT_XCSERR)
5058c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_tpcsum);
5068c2ecf20Sopenharmony_ci			if (xerr == SLIC_VGBSTAT_XUFLOW)
5078c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_tpoflow);
5088c2ecf20Sopenharmony_ci			if (xerr == SLIC_VGBSTAT_XHLEN)
5098c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_tphlen);
5108c2ecf20Sopenharmony_ci		}
5118c2ecf20Sopenharmony_ci		/* ip layer */
5128c2ecf20Sopenharmony_ci		if (status & SLIC_VGBSTAT_NETERR) {
5138c2ecf20Sopenharmony_ci			u32 nerr = status >> SLIC_VGBSTAT_NERRSHFT &
5148c2ecf20Sopenharmony_ci				   SLIC_VGBSTAT_NERRMSK;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci			if (nerr == SLIC_VGBSTAT_NCSERR)
5178c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_ipcsum);
5188c2ecf20Sopenharmony_ci			if (nerr == SLIC_VGBSTAT_NUFLOW)
5198c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_iplen);
5208c2ecf20Sopenharmony_ci			if (nerr == SLIC_VGBSTAT_NHLEN)
5218c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_iphlen);
5228c2ecf20Sopenharmony_ci		}
5238c2ecf20Sopenharmony_ci		/* link layer */
5248c2ecf20Sopenharmony_ci		if (status & SLIC_VGBSTAT_LNKERR) {
5258c2ecf20Sopenharmony_ci			u32 lerr = status & SLIC_VGBSTAT_LERRMSK;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LDEARLY)
5288c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_early);
5298c2ecf20Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LBOFLO)
5308c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_buffoflow);
5318c2ecf20Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LCODERR)
5328c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_lcode);
5338c2ecf20Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LDBLNBL)
5348c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_drbl);
5358c2ecf20Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LCRCERR)
5368c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_crc);
5378c2ecf20Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LOFLO)
5388c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_oflow802);
5398c2ecf20Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LUFLO)
5408c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_uflow802);
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci	SLIC_INC_STATS_COUNTER(stats, rx_errors);
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic void slic_handle_receive(struct slic_device *sdev, unsigned int todo,
5478c2ecf20Sopenharmony_ci				unsigned int *done)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct slic_rx_queue *rxq = &sdev->rxq;
5508c2ecf20Sopenharmony_ci	struct net_device *dev = sdev->netdev;
5518c2ecf20Sopenharmony_ci	struct slic_rx_buffer *buff;
5528c2ecf20Sopenharmony_ci	struct slic_rx_desc *desc;
5538c2ecf20Sopenharmony_ci	unsigned int frames = 0;
5548c2ecf20Sopenharmony_ci	unsigned int bytes = 0;
5558c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5568c2ecf20Sopenharmony_ci	u32 status;
5578c2ecf20Sopenharmony_ci	u32 len;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	while (todo && (rxq->done_idx != rxq->put_idx)) {
5608c2ecf20Sopenharmony_ci		buff = &rxq->rxbuffs[rxq->done_idx];
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci		skb = buff->skb;
5638c2ecf20Sopenharmony_ci		if (!skb)
5648c2ecf20Sopenharmony_ci			break;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci		desc = (struct slic_rx_desc *)skb->data;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci		dma_sync_single_for_cpu(&sdev->pdev->dev,
5698c2ecf20Sopenharmony_ci					dma_unmap_addr(buff, map_addr),
5708c2ecf20Sopenharmony_ci					buff->addr_offset + sizeof(*desc),
5718c2ecf20Sopenharmony_ci					DMA_FROM_DEVICE);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci		status = le32_to_cpu(desc->status);
5748c2ecf20Sopenharmony_ci		if (!(status & SLIC_IRHDDR_SVALID)) {
5758c2ecf20Sopenharmony_ci			dma_sync_single_for_device(&sdev->pdev->dev,
5768c2ecf20Sopenharmony_ci						   dma_unmap_addr(buff,
5778c2ecf20Sopenharmony_ci								  map_addr),
5788c2ecf20Sopenharmony_ci						   buff->addr_offset +
5798c2ecf20Sopenharmony_ci						   sizeof(*desc),
5808c2ecf20Sopenharmony_ci						   DMA_FROM_DEVICE);
5818c2ecf20Sopenharmony_ci			break;
5828c2ecf20Sopenharmony_ci		}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci		buff->skb = NULL;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		dma_unmap_single(&sdev->pdev->dev,
5878c2ecf20Sopenharmony_ci				 dma_unmap_addr(buff, map_addr),
5888c2ecf20Sopenharmony_ci				 dma_unmap_len(buff, map_len),
5898c2ecf20Sopenharmony_ci				 DMA_FROM_DEVICE);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		/* skip rx descriptor that is placed before the frame data */
5928c2ecf20Sopenharmony_ci		skb_reserve(skb, SLIC_RX_BUFF_HDR_SIZE);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		if (unlikely(status & SLIC_IRHDDR_ERR)) {
5958c2ecf20Sopenharmony_ci			slic_handle_frame_error(sdev, skb);
5968c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
5978c2ecf20Sopenharmony_ci		} else {
5988c2ecf20Sopenharmony_ci			struct ethhdr *eh = (struct ethhdr *)skb->data;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci			if (is_multicast_ether_addr(eh->h_dest))
6018c2ecf20Sopenharmony_ci				SLIC_INC_STATS_COUNTER(&sdev->stats, rx_mcasts);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci			len = le32_to_cpu(desc->length) & SLIC_IRHDDR_FLEN_MSK;
6048c2ecf20Sopenharmony_ci			skb_put(skb, len);
6058c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
6068c2ecf20Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci			napi_gro_receive(&sdev->napi, skb);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci			bytes += len;
6118c2ecf20Sopenharmony_ci			frames++;
6128c2ecf20Sopenharmony_ci		}
6138c2ecf20Sopenharmony_ci		rxq->done_idx = slic_next_queue_idx(rxq->done_idx, rxq->len);
6148c2ecf20Sopenharmony_ci		todo--;
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	u64_stats_update_begin(&sdev->stats.syncp);
6188c2ecf20Sopenharmony_ci	sdev->stats.rx_bytes += bytes;
6198c2ecf20Sopenharmony_ci	sdev->stats.rx_packets += frames;
6208c2ecf20Sopenharmony_ci	u64_stats_update_end(&sdev->stats.syncp);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	slic_refill_rx_queue(sdev, GFP_ATOMIC);
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic void slic_handle_link_irq(struct slic_device *sdev)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
6288c2ecf20Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
6298c2ecf20Sopenharmony_ci	unsigned int duplex;
6308c2ecf20Sopenharmony_ci	int speed;
6318c2ecf20Sopenharmony_ci	u32 link;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	link = le32_to_cpu(sm_data->link);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (link & SLIC_GIG_LINKUP) {
6368c2ecf20Sopenharmony_ci		if (link & SLIC_GIG_SPEED_1000)
6378c2ecf20Sopenharmony_ci			speed = SPEED_1000;
6388c2ecf20Sopenharmony_ci		else if (link & SLIC_GIG_SPEED_100)
6398c2ecf20Sopenharmony_ci			speed = SPEED_100;
6408c2ecf20Sopenharmony_ci		else
6418c2ecf20Sopenharmony_ci			speed = SPEED_10;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		duplex = (link & SLIC_GIG_FULLDUPLEX) ? DUPLEX_FULL :
6448c2ecf20Sopenharmony_ci							DUPLEX_HALF;
6458c2ecf20Sopenharmony_ci	} else {
6468c2ecf20Sopenharmony_ci		duplex = DUPLEX_UNKNOWN;
6478c2ecf20Sopenharmony_ci		speed = SPEED_UNKNOWN;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci	slic_configure_link(sdev, speed, duplex);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic void slic_handle_upr_irq(struct slic_device *sdev, u32 irqs)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	struct slic_upr *upr;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/* remove upr that caused this irq (always the first entry in list) */
6578c2ecf20Sopenharmony_ci	upr = slic_dequeue_upr(sdev);
6588c2ecf20Sopenharmony_ci	if (!upr) {
6598c2ecf20Sopenharmony_ci		netdev_warn(sdev->netdev, "no upr found on list\n");
6608c2ecf20Sopenharmony_ci		return;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if (upr->type == SLIC_UPR_LSTAT) {
6648c2ecf20Sopenharmony_ci		if (unlikely(irqs & SLIC_ISR_UPCERR_MASK)) {
6658c2ecf20Sopenharmony_ci			/* try again */
6668c2ecf20Sopenharmony_ci			slic_queue_upr(sdev, upr);
6678c2ecf20Sopenharmony_ci			return;
6688c2ecf20Sopenharmony_ci		}
6698c2ecf20Sopenharmony_ci		slic_handle_link_irq(sdev);
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci	kfree(upr);
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic int slic_handle_link_change(struct slic_device *sdev)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	return slic_new_upr(sdev, SLIC_UPR_LSTAT, sdev->shmem.link_paddr);
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic void slic_handle_err_irq(struct slic_device *sdev, u32 isr)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	struct slic_stats *stats = &sdev->stats;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (isr & SLIC_ISR_RMISS)
6848c2ecf20Sopenharmony_ci		SLIC_INC_STATS_COUNTER(stats, rx_buff_miss);
6858c2ecf20Sopenharmony_ci	if (isr & SLIC_ISR_XDROP)
6868c2ecf20Sopenharmony_ci		SLIC_INC_STATS_COUNTER(stats, tx_dropped);
6878c2ecf20Sopenharmony_ci	if (!(isr & (SLIC_ISR_RMISS | SLIC_ISR_XDROP)))
6888c2ecf20Sopenharmony_ci		SLIC_INC_STATS_COUNTER(stats, irq_errs);
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic void slic_handle_irq(struct slic_device *sdev, u32 isr,
6928c2ecf20Sopenharmony_ci			    unsigned int todo, unsigned int *done)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	if (isr & SLIC_ISR_ERR)
6958c2ecf20Sopenharmony_ci		slic_handle_err_irq(sdev, isr);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (isr & SLIC_ISR_LEVENT)
6988c2ecf20Sopenharmony_ci		slic_handle_link_change(sdev);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (isr & SLIC_ISR_UPC_MASK)
7018c2ecf20Sopenharmony_ci		slic_handle_upr_irq(sdev, isr);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	if (isr & SLIC_ISR_RCV)
7048c2ecf20Sopenharmony_ci		slic_handle_receive(sdev, todo, done);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	if (isr & SLIC_ISR_CMD)
7078c2ecf20Sopenharmony_ci		slic_xmit_complete(sdev);
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic int slic_poll(struct napi_struct *napi, int todo)
7118c2ecf20Sopenharmony_ci{
7128c2ecf20Sopenharmony_ci	struct slic_device *sdev = container_of(napi, struct slic_device, napi);
7138c2ecf20Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
7148c2ecf20Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
7158c2ecf20Sopenharmony_ci	u32 isr = le32_to_cpu(sm_data->isr);
7168c2ecf20Sopenharmony_ci	int done = 0;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	slic_handle_irq(sdev, isr, todo, &done);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	if (done < todo) {
7218c2ecf20Sopenharmony_ci		napi_complete_done(napi, done);
7228c2ecf20Sopenharmony_ci		/* reenable irqs */
7238c2ecf20Sopenharmony_ci		sm_data->isr = 0;
7248c2ecf20Sopenharmony_ci		/* make sure sm_data->isr is cleard before irqs are reenabled */
7258c2ecf20Sopenharmony_ci		wmb();
7268c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_ISR, 0);
7278c2ecf20Sopenharmony_ci		slic_flush_write(sdev);
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	return done;
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic irqreturn_t slic_irq(int irq, void *dev_id)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	struct slic_device *sdev = dev_id;
7368c2ecf20Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
7378c2ecf20Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_MASK);
7408c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
7418c2ecf20Sopenharmony_ci	/* make sure sm_data->isr is read after ICR_INT_MASK is set */
7428c2ecf20Sopenharmony_ci	wmb();
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (!sm_data->isr) {
7458c2ecf20Sopenharmony_ci		dma_rmb();
7468c2ecf20Sopenharmony_ci		/* spurious interrupt */
7478c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_ISR, 0);
7488c2ecf20Sopenharmony_ci		slic_flush_write(sdev);
7498c2ecf20Sopenharmony_ci		return IRQ_NONE;
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	napi_schedule_irqoff(&sdev->napi);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic void slic_card_reset(struct slic_device *sdev)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	u16 cmd;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_RESET, SLIC_RESET_MAGIC);
7628c2ecf20Sopenharmony_ci	/* flush write by means of config space */
7638c2ecf20Sopenharmony_ci	pci_read_config_word(sdev->pdev, PCI_COMMAND, &cmd);
7648c2ecf20Sopenharmony_ci	mdelay(1);
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_cistatic int slic_init_stat_queue(struct slic_device *sdev)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	const unsigned int DESC_ALIGN_MASK = SLIC_STATS_DESC_ALIGN - 1;
7708c2ecf20Sopenharmony_ci	struct slic_stat_queue *stq = &sdev->stq;
7718c2ecf20Sopenharmony_ci	struct slic_stat_desc *descs;
7728c2ecf20Sopenharmony_ci	unsigned int misalign;
7738c2ecf20Sopenharmony_ci	unsigned int offset;
7748c2ecf20Sopenharmony_ci	dma_addr_t paddr;
7758c2ecf20Sopenharmony_ci	size_t size;
7768c2ecf20Sopenharmony_ci	int err;
7778c2ecf20Sopenharmony_ci	int i;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	stq->len = SLIC_NUM_STAT_DESCS;
7808c2ecf20Sopenharmony_ci	stq->active_array = 0;
7818c2ecf20Sopenharmony_ci	stq->done_idx = 0;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	size = stq->len * sizeof(*descs) + DESC_ALIGN_MASK;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) {
7868c2ecf20Sopenharmony_ci		descs = dma_alloc_coherent(&sdev->pdev->dev, size, &paddr,
7878c2ecf20Sopenharmony_ci					   GFP_KERNEL);
7888c2ecf20Sopenharmony_ci		if (!descs) {
7898c2ecf20Sopenharmony_ci			netdev_err(sdev->netdev,
7908c2ecf20Sopenharmony_ci				   "failed to allocate status descriptors\n");
7918c2ecf20Sopenharmony_ci			err = -ENOMEM;
7928c2ecf20Sopenharmony_ci			goto free_descs;
7938c2ecf20Sopenharmony_ci		}
7948c2ecf20Sopenharmony_ci		/* ensure correct alignment */
7958c2ecf20Sopenharmony_ci		offset = 0;
7968c2ecf20Sopenharmony_ci		misalign = paddr & DESC_ALIGN_MASK;
7978c2ecf20Sopenharmony_ci		if (misalign) {
7988c2ecf20Sopenharmony_ci			offset = SLIC_STATS_DESC_ALIGN - misalign;
7998c2ecf20Sopenharmony_ci			descs += offset;
8008c2ecf20Sopenharmony_ci			paddr += offset;
8018c2ecf20Sopenharmony_ci		}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) |
8048c2ecf20Sopenharmony_ci						stq->len);
8058c2ecf20Sopenharmony_ci		stq->descs[i] = descs;
8068c2ecf20Sopenharmony_ci		stq->paddr[i] = paddr;
8078c2ecf20Sopenharmony_ci		stq->addr_offset[i] = offset;
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	stq->mem_size = size;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return 0;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_cifree_descs:
8158c2ecf20Sopenharmony_ci	while (i--) {
8168c2ecf20Sopenharmony_ci		dma_free_coherent(&sdev->pdev->dev, stq->mem_size,
8178c2ecf20Sopenharmony_ci				  stq->descs[i] - stq->addr_offset[i],
8188c2ecf20Sopenharmony_ci				  stq->paddr[i] - stq->addr_offset[i]);
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	return err;
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic void slic_free_stat_queue(struct slic_device *sdev)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	struct slic_stat_queue *stq = &sdev->stq;
8278c2ecf20Sopenharmony_ci	int i;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) {
8308c2ecf20Sopenharmony_ci		dma_free_coherent(&sdev->pdev->dev, stq->mem_size,
8318c2ecf20Sopenharmony_ci				  stq->descs[i] - stq->addr_offset[i],
8328c2ecf20Sopenharmony_ci				  stq->paddr[i] - stq->addr_offset[i]);
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cistatic int slic_init_tx_queue(struct slic_device *sdev)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct slic_tx_queue *txq = &sdev->txq;
8398c2ecf20Sopenharmony_ci	struct slic_tx_buffer *buff;
8408c2ecf20Sopenharmony_ci	struct slic_tx_desc *desc;
8418c2ecf20Sopenharmony_ci	unsigned int i;
8428c2ecf20Sopenharmony_ci	int err;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	txq->len = SLIC_NUM_TX_DESCS;
8458c2ecf20Sopenharmony_ci	txq->put_idx = 0;
8468c2ecf20Sopenharmony_ci	txq->done_idx = 0;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	txq->txbuffs = kcalloc(txq->len, sizeof(*buff), GFP_KERNEL);
8498c2ecf20Sopenharmony_ci	if (!txq->txbuffs)
8508c2ecf20Sopenharmony_ci		return -ENOMEM;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	txq->dma_pool = dma_pool_create("slic_pool", &sdev->pdev->dev,
8538c2ecf20Sopenharmony_ci					sizeof(*desc), SLIC_TX_DESC_ALIGN,
8548c2ecf20Sopenharmony_ci					4096);
8558c2ecf20Sopenharmony_ci	if (!txq->dma_pool) {
8568c2ecf20Sopenharmony_ci		err = -ENOMEM;
8578c2ecf20Sopenharmony_ci		netdev_err(sdev->netdev, "failed to create dma pool\n");
8588c2ecf20Sopenharmony_ci		goto free_buffs;
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	for (i = 0; i < txq->len; i++) {
8628c2ecf20Sopenharmony_ci		buff = &txq->txbuffs[i];
8638c2ecf20Sopenharmony_ci		desc = dma_pool_zalloc(txq->dma_pool, GFP_KERNEL,
8648c2ecf20Sopenharmony_ci				       &buff->desc_paddr);
8658c2ecf20Sopenharmony_ci		if (!desc) {
8668c2ecf20Sopenharmony_ci			netdev_err(sdev->netdev,
8678c2ecf20Sopenharmony_ci				   "failed to alloc pool chunk (%i)\n", i);
8688c2ecf20Sopenharmony_ci			err = -ENOMEM;
8698c2ecf20Sopenharmony_ci			goto free_descs;
8708c2ecf20Sopenharmony_ci		}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci		desc->hnd = cpu_to_le32((u32)(i + 1));
8738c2ecf20Sopenharmony_ci		desc->cmd = SLIC_CMD_XMT_REQ;
8748c2ecf20Sopenharmony_ci		desc->flags = 0;
8758c2ecf20Sopenharmony_ci		desc->type = cpu_to_le32(SLIC_CMD_TYPE_DUMB);
8768c2ecf20Sopenharmony_ci		buff->desc = desc;
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	return 0;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cifree_descs:
8828c2ecf20Sopenharmony_ci	while (i--) {
8838c2ecf20Sopenharmony_ci		buff = &txq->txbuffs[i];
8848c2ecf20Sopenharmony_ci		dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr);
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci	dma_pool_destroy(txq->dma_pool);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cifree_buffs:
8898c2ecf20Sopenharmony_ci	kfree(txq->txbuffs);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	return err;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_cistatic void slic_free_tx_queue(struct slic_device *sdev)
8958c2ecf20Sopenharmony_ci{
8968c2ecf20Sopenharmony_ci	struct slic_tx_queue *txq = &sdev->txq;
8978c2ecf20Sopenharmony_ci	struct slic_tx_buffer *buff;
8988c2ecf20Sopenharmony_ci	unsigned int i;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	for (i = 0; i < txq->len; i++) {
9018c2ecf20Sopenharmony_ci		buff = &txq->txbuffs[i];
9028c2ecf20Sopenharmony_ci		dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr);
9038c2ecf20Sopenharmony_ci		if (!buff->skb)
9048c2ecf20Sopenharmony_ci			continue;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci		dma_unmap_single(&sdev->pdev->dev,
9078c2ecf20Sopenharmony_ci				 dma_unmap_addr(buff, map_addr),
9088c2ecf20Sopenharmony_ci				 dma_unmap_len(buff, map_len), DMA_TO_DEVICE);
9098c2ecf20Sopenharmony_ci		consume_skb(buff->skb);
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci	dma_pool_destroy(txq->dma_pool);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	kfree(txq->txbuffs);
9148c2ecf20Sopenharmony_ci}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_cistatic int slic_init_rx_queue(struct slic_device *sdev)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	struct slic_rx_queue *rxq = &sdev->rxq;
9198c2ecf20Sopenharmony_ci	struct slic_rx_buffer *buff;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	rxq->len = SLIC_NUM_RX_LES;
9228c2ecf20Sopenharmony_ci	rxq->done_idx = 0;
9238c2ecf20Sopenharmony_ci	rxq->put_idx = 0;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	buff = kcalloc(rxq->len, sizeof(*buff), GFP_KERNEL);
9268c2ecf20Sopenharmony_ci	if (!buff)
9278c2ecf20Sopenharmony_ci		return -ENOMEM;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	rxq->rxbuffs = buff;
9308c2ecf20Sopenharmony_ci	slic_refill_rx_queue(sdev, GFP_KERNEL);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	return 0;
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic void slic_free_rx_queue(struct slic_device *sdev)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	struct slic_rx_queue *rxq = &sdev->rxq;
9388c2ecf20Sopenharmony_ci	struct slic_rx_buffer *buff;
9398c2ecf20Sopenharmony_ci	unsigned int i;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	/* free rx buffers */
9428c2ecf20Sopenharmony_ci	for (i = 0; i < rxq->len; i++) {
9438c2ecf20Sopenharmony_ci		buff = &rxq->rxbuffs[i];
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci		if (!buff->skb)
9468c2ecf20Sopenharmony_ci			continue;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci		dma_unmap_single(&sdev->pdev->dev,
9498c2ecf20Sopenharmony_ci				 dma_unmap_addr(buff, map_addr),
9508c2ecf20Sopenharmony_ci				 dma_unmap_len(buff, map_len),
9518c2ecf20Sopenharmony_ci				 DMA_FROM_DEVICE);
9528c2ecf20Sopenharmony_ci		consume_skb(buff->skb);
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci	kfree(rxq->rxbuffs);
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_cistatic void slic_set_link_autoneg(struct slic_device *sdev)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	unsigned int subid = sdev->pdev->subsystem_device;
9608c2ecf20Sopenharmony_ci	u32 val;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	if (sdev->is_fiber) {
9638c2ecf20Sopenharmony_ci		/* We've got a fiber gigabit interface, and register 4 is
9648c2ecf20Sopenharmony_ci		 * different in fiber mode than in copper mode.
9658c2ecf20Sopenharmony_ci		 */
9668c2ecf20Sopenharmony_ci		/* advertise FD only @1000 Mb */
9678c2ecf20Sopenharmony_ci		val = MII_ADVERTISE << 16 | ADVERTISE_1000XFULL |
9688c2ecf20Sopenharmony_ci		      ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM;
9698c2ecf20Sopenharmony_ci		/* enable PAUSE frames */
9708c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_WPHY, val);
9718c2ecf20Sopenharmony_ci		/* reset phy, enable auto-neg  */
9728c2ecf20Sopenharmony_ci		val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE |
9738c2ecf20Sopenharmony_ci		      BMCR_ANRESTART;
9748c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_WPHY, val);
9758c2ecf20Sopenharmony_ci	} else {	/* copper gigabit */
9768c2ecf20Sopenharmony_ci		/* We've got a copper gigabit interface, and register 4 is
9778c2ecf20Sopenharmony_ci		 * different in copper mode than in fiber mode.
9788c2ecf20Sopenharmony_ci		 */
9798c2ecf20Sopenharmony_ci		/* advertise 10/100 Mb modes   */
9808c2ecf20Sopenharmony_ci		val = MII_ADVERTISE << 16 | ADVERTISE_100FULL |
9818c2ecf20Sopenharmony_ci		      ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF;
9828c2ecf20Sopenharmony_ci		/* enable PAUSE frames  */
9838c2ecf20Sopenharmony_ci		val |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
9848c2ecf20Sopenharmony_ci		/* required by the Cicada PHY  */
9858c2ecf20Sopenharmony_ci		val |= ADVERTISE_CSMA;
9868c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_WPHY, val);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci		/* advertise FD only @1000 Mb  */
9898c2ecf20Sopenharmony_ci		val = MII_CTRL1000 << 16 | ADVERTISE_1000FULL;
9908c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_WPHY, val);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci		if (subid != PCI_SUBDEVICE_ID_ALACRITECH_CICADA) {
9938c2ecf20Sopenharmony_ci			 /* if a Marvell PHY enable auto crossover */
9948c2ecf20Sopenharmony_ci			val = SLIC_MIICR_REG_16 | SLIC_MRV_REG16_XOVERON;
9958c2ecf20Sopenharmony_ci			slic_write(sdev, SLIC_REG_WPHY, val);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci			/* reset phy, enable auto-neg  */
9988c2ecf20Sopenharmony_ci			val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE |
9998c2ecf20Sopenharmony_ci			      BMCR_ANRESTART;
10008c2ecf20Sopenharmony_ci			slic_write(sdev, SLIC_REG_WPHY, val);
10018c2ecf20Sopenharmony_ci		} else {
10028c2ecf20Sopenharmony_ci			/* enable and restart auto-neg (don't reset)  */
10038c2ecf20Sopenharmony_ci			val = MII_BMCR << 16 | BMCR_ANENABLE | BMCR_ANRESTART;
10048c2ecf20Sopenharmony_ci			slic_write(sdev, SLIC_REG_WPHY, val);
10058c2ecf20Sopenharmony_ci		}
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_cistatic void slic_set_mac_address(struct slic_device *sdev)
10108c2ecf20Sopenharmony_ci{
10118c2ecf20Sopenharmony_ci	u8 *addr = sdev->netdev->dev_addr;
10128c2ecf20Sopenharmony_ci	u32 val;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	val = addr[5] | addr[4] << 8 | addr[3] << 16 | addr[2] << 24;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRADDRAL, val);
10178c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRADDRBL, val);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	val = addr[0] << 8 | addr[1];
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRADDRAH, val);
10228c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRADDRBH, val);
10238c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic u32 slic_read_dword_from_firmware(const struct firmware *fw, int *offset)
10278c2ecf20Sopenharmony_ci{
10288c2ecf20Sopenharmony_ci	int idx = *offset;
10298c2ecf20Sopenharmony_ci	__le32 val;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	memcpy(&val, fw->data + *offset, sizeof(val));
10328c2ecf20Sopenharmony_ci	idx += 4;
10338c2ecf20Sopenharmony_ci	*offset = idx;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	return le32_to_cpu(val);
10368c2ecf20Sopenharmony_ci}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SLIC_RCV_FIRMWARE_MOJAVE);
10398c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SLIC_RCV_FIRMWARE_OASIS);
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_cistatic int slic_load_rcvseq_firmware(struct slic_device *sdev)
10428c2ecf20Sopenharmony_ci{
10438c2ecf20Sopenharmony_ci	const struct firmware *fw;
10448c2ecf20Sopenharmony_ci	const char *file;
10458c2ecf20Sopenharmony_ci	u32 codelen;
10468c2ecf20Sopenharmony_ci	int idx = 0;
10478c2ecf20Sopenharmony_ci	u32 instr;
10488c2ecf20Sopenharmony_ci	u32 addr;
10498c2ecf20Sopenharmony_ci	int err;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	file = (sdev->model == SLIC_MODEL_OASIS) ?  SLIC_RCV_FIRMWARE_OASIS :
10528c2ecf20Sopenharmony_ci						    SLIC_RCV_FIRMWARE_MOJAVE;
10538c2ecf20Sopenharmony_ci	err = request_firmware(&fw, file, &sdev->pdev->dev);
10548c2ecf20Sopenharmony_ci	if (err) {
10558c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev,
10568c2ecf20Sopenharmony_ci			"failed to load receive sequencer firmware %s\n", file);
10578c2ecf20Sopenharmony_ci		return err;
10588c2ecf20Sopenharmony_ci	}
10598c2ecf20Sopenharmony_ci	/* Do an initial sanity check concerning firmware size now. A further
10608c2ecf20Sopenharmony_ci	 * check follows below.
10618c2ecf20Sopenharmony_ci	 */
10628c2ecf20Sopenharmony_ci	if (fw->size < SLIC_FIRMWARE_MIN_SIZE) {
10638c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev,
10648c2ecf20Sopenharmony_ci			"invalid firmware size %zu (min %u expected)\n",
10658c2ecf20Sopenharmony_ci			fw->size, SLIC_FIRMWARE_MIN_SIZE);
10668c2ecf20Sopenharmony_ci		err = -EINVAL;
10678c2ecf20Sopenharmony_ci		goto release;
10688c2ecf20Sopenharmony_ci	}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	codelen = slic_read_dword_from_firmware(fw, &idx);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	/* do another sanity check against firmware size */
10738c2ecf20Sopenharmony_ci	if ((codelen + 4) > fw->size) {
10748c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev,
10758c2ecf20Sopenharmony_ci			"invalid rcv-sequencer firmware size %zu\n", fw->size);
10768c2ecf20Sopenharmony_ci		err = -EINVAL;
10778c2ecf20Sopenharmony_ci		goto release;
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	/* download sequencer code to card */
10818c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_BEGIN);
10828c2ecf20Sopenharmony_ci	for (addr = 0; addr < codelen; addr++) {
10838c2ecf20Sopenharmony_ci		__le32 val;
10848c2ecf20Sopenharmony_ci		/* write out instruction address */
10858c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_RCV_WCS, addr);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci		instr = slic_read_dword_from_firmware(fw, &idx);
10888c2ecf20Sopenharmony_ci		/* write out the instruction data low addr */
10898c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_RCV_WCS, instr);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci		val = (__le32)fw->data[idx];
10928c2ecf20Sopenharmony_ci		instr = le32_to_cpu(val);
10938c2ecf20Sopenharmony_ci		idx++;
10948c2ecf20Sopenharmony_ci		/* write out the instruction data high addr */
10958c2ecf20Sopenharmony_ci		slic_write(sdev, SLIC_REG_RCV_WCS, instr);
10968c2ecf20Sopenharmony_ci	}
10978c2ecf20Sopenharmony_ci	/* finish download */
10988c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_FINISH);
10998c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
11008c2ecf20Sopenharmony_cirelease:
11018c2ecf20Sopenharmony_ci	release_firmware(fw);
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	return err;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SLIC_FIRMWARE_MOJAVE);
11078c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SLIC_FIRMWARE_OASIS);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic int slic_load_firmware(struct slic_device *sdev)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	u32 sectstart[SLIC_FIRMWARE_MAX_SECTIONS];
11128c2ecf20Sopenharmony_ci	u32 sectsize[SLIC_FIRMWARE_MAX_SECTIONS];
11138c2ecf20Sopenharmony_ci	const struct firmware *fw;
11148c2ecf20Sopenharmony_ci	unsigned int datalen;
11158c2ecf20Sopenharmony_ci	const char *file;
11168c2ecf20Sopenharmony_ci	int code_start;
11178c2ecf20Sopenharmony_ci	unsigned int i;
11188c2ecf20Sopenharmony_ci	u32 numsects;
11198c2ecf20Sopenharmony_ci	int idx = 0;
11208c2ecf20Sopenharmony_ci	u32 sect;
11218c2ecf20Sopenharmony_ci	u32 instr;
11228c2ecf20Sopenharmony_ci	u32 addr;
11238c2ecf20Sopenharmony_ci	u32 base;
11248c2ecf20Sopenharmony_ci	int err;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	file = (sdev->model == SLIC_MODEL_OASIS) ?  SLIC_FIRMWARE_OASIS :
11278c2ecf20Sopenharmony_ci						    SLIC_FIRMWARE_MOJAVE;
11288c2ecf20Sopenharmony_ci	err = request_firmware(&fw, file, &sdev->pdev->dev);
11298c2ecf20Sopenharmony_ci	if (err) {
11308c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to load firmware %s\n", file);
11318c2ecf20Sopenharmony_ci		return err;
11328c2ecf20Sopenharmony_ci	}
11338c2ecf20Sopenharmony_ci	/* Do an initial sanity check concerning firmware size now. A further
11348c2ecf20Sopenharmony_ci	 * check follows below.
11358c2ecf20Sopenharmony_ci	 */
11368c2ecf20Sopenharmony_ci	if (fw->size < SLIC_FIRMWARE_MIN_SIZE) {
11378c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev,
11388c2ecf20Sopenharmony_ci			"invalid firmware size %zu (min is %u)\n", fw->size,
11398c2ecf20Sopenharmony_ci			SLIC_FIRMWARE_MIN_SIZE);
11408c2ecf20Sopenharmony_ci		err = -EINVAL;
11418c2ecf20Sopenharmony_ci		goto release;
11428c2ecf20Sopenharmony_ci	}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	numsects = slic_read_dword_from_firmware(fw, &idx);
11458c2ecf20Sopenharmony_ci	if (numsects == 0 || numsects > SLIC_FIRMWARE_MAX_SECTIONS) {
11468c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev,
11478c2ecf20Sopenharmony_ci			"invalid number of sections in firmware: %u", numsects);
11488c2ecf20Sopenharmony_ci		err = -EINVAL;
11498c2ecf20Sopenharmony_ci		goto release;
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	datalen = numsects * 8 + 4;
11538c2ecf20Sopenharmony_ci	for (i = 0; i < numsects; i++) {
11548c2ecf20Sopenharmony_ci		sectsize[i] = slic_read_dword_from_firmware(fw, &idx);
11558c2ecf20Sopenharmony_ci		datalen += sectsize[i];
11568c2ecf20Sopenharmony_ci	}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	/* do another sanity check against firmware size */
11598c2ecf20Sopenharmony_ci	if (datalen > fw->size) {
11608c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev,
11618c2ecf20Sopenharmony_ci			"invalid firmware size %zu (expected >= %u)\n",
11628c2ecf20Sopenharmony_ci			fw->size, datalen);
11638c2ecf20Sopenharmony_ci		err = -EINVAL;
11648c2ecf20Sopenharmony_ci		goto release;
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci	/* get sections */
11678c2ecf20Sopenharmony_ci	for (i = 0; i < numsects; i++)
11688c2ecf20Sopenharmony_ci		sectstart[i] = slic_read_dword_from_firmware(fw, &idx);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	code_start = idx;
11718c2ecf20Sopenharmony_ci	instr = slic_read_dword_from_firmware(fw, &idx);
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	for (sect = 0; sect < numsects; sect++) {
11748c2ecf20Sopenharmony_ci		unsigned int ssize = sectsize[sect] >> 3;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci		base = sectstart[sect];
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci		for (addr = 0; addr < ssize; addr++) {
11798c2ecf20Sopenharmony_ci			/* write out instruction address */
11808c2ecf20Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, base + addr);
11818c2ecf20Sopenharmony_ci			/* write out instruction to low addr */
11828c2ecf20Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, instr);
11838c2ecf20Sopenharmony_ci			instr = slic_read_dword_from_firmware(fw, &idx);
11848c2ecf20Sopenharmony_ci			/* write out instruction to high addr */
11858c2ecf20Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, instr);
11868c2ecf20Sopenharmony_ci			instr = slic_read_dword_from_firmware(fw, &idx);
11878c2ecf20Sopenharmony_ci		}
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	idx = code_start;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	for (sect = 0; sect < numsects; sect++) {
11938c2ecf20Sopenharmony_ci		unsigned int ssize = sectsize[sect] >> 3;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci		instr = slic_read_dword_from_firmware(fw, &idx);
11968c2ecf20Sopenharmony_ci		base = sectstart[sect];
11978c2ecf20Sopenharmony_ci		if (base < 0x8000)
11988c2ecf20Sopenharmony_ci			continue;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci		for (addr = 0; addr < ssize; addr++) {
12018c2ecf20Sopenharmony_ci			/* write out instruction address */
12028c2ecf20Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS,
12038c2ecf20Sopenharmony_ci				   SLIC_WCS_COMPARE | (base + addr));
12048c2ecf20Sopenharmony_ci			/* write out instruction to low addr */
12058c2ecf20Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, instr);
12068c2ecf20Sopenharmony_ci			instr = slic_read_dword_from_firmware(fw, &idx);
12078c2ecf20Sopenharmony_ci			/* write out instruction to high addr */
12088c2ecf20Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, instr);
12098c2ecf20Sopenharmony_ci			instr = slic_read_dword_from_firmware(fw, &idx);
12108c2ecf20Sopenharmony_ci		}
12118c2ecf20Sopenharmony_ci	}
12128c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
12138c2ecf20Sopenharmony_ci	mdelay(10);
12148c2ecf20Sopenharmony_ci	/* everything OK, kick off the card */
12158c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WCS, SLIC_WCS_START);
12168c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
12178c2ecf20Sopenharmony_ci	/* wait long enough for ucode to init card and reach the mainloop */
12188c2ecf20Sopenharmony_ci	mdelay(20);
12198c2ecf20Sopenharmony_cirelease:
12208c2ecf20Sopenharmony_ci	release_firmware(fw);
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	return err;
12238c2ecf20Sopenharmony_ci}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_cistatic int slic_init_shmem(struct slic_device *sdev)
12268c2ecf20Sopenharmony_ci{
12278c2ecf20Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
12288c2ecf20Sopenharmony_ci	struct slic_shmem_data *sm_data;
12298c2ecf20Sopenharmony_ci	dma_addr_t paddr;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	sm_data = dma_alloc_coherent(&sdev->pdev->dev, sizeof(*sm_data),
12328c2ecf20Sopenharmony_ci				     &paddr, GFP_KERNEL);
12338c2ecf20Sopenharmony_ci	if (!sm_data) {
12348c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to allocate shared memory\n");
12358c2ecf20Sopenharmony_ci		return -ENOMEM;
12368c2ecf20Sopenharmony_ci	}
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	sm->shmem_data = sm_data;
12398c2ecf20Sopenharmony_ci	sm->isr_paddr = paddr;
12408c2ecf20Sopenharmony_ci	sm->link_paddr = paddr + offsetof(struct slic_shmem_data, link);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	return 0;
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic void slic_free_shmem(struct slic_device *sdev)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
12488c2ecf20Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	dma_free_coherent(&sdev->pdev->dev, sizeof(*sm_data), sm_data,
12518c2ecf20Sopenharmony_ci			  sm->isr_paddr);
12528c2ecf20Sopenharmony_ci}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_cistatic int slic_init_iface(struct slic_device *sdev)
12558c2ecf20Sopenharmony_ci{
12568c2ecf20Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
12578c2ecf20Sopenharmony_ci	int err;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	sdev->upr_list.pending = false;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	err = slic_init_shmem(sdev);
12628c2ecf20Sopenharmony_ci	if (err) {
12638c2ecf20Sopenharmony_ci		netdev_err(sdev->netdev, "failed to init shared memory\n");
12648c2ecf20Sopenharmony_ci		return err;
12658c2ecf20Sopenharmony_ci	}
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	err = slic_load_firmware(sdev);
12688c2ecf20Sopenharmony_ci	if (err) {
12698c2ecf20Sopenharmony_ci		netdev_err(sdev->netdev, "failed to load firmware\n");
12708c2ecf20Sopenharmony_ci		goto free_sm;
12718c2ecf20Sopenharmony_ci	}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	err = slic_load_rcvseq_firmware(sdev);
12748c2ecf20Sopenharmony_ci	if (err) {
12758c2ecf20Sopenharmony_ci		netdev_err(sdev->netdev,
12768c2ecf20Sopenharmony_ci			   "failed to load firmware for receive sequencer\n");
12778c2ecf20Sopenharmony_ci		goto free_sm;
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF);
12818c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
12828c2ecf20Sopenharmony_ci	mdelay(1);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	err = slic_init_rx_queue(sdev);
12858c2ecf20Sopenharmony_ci	if (err) {
12868c2ecf20Sopenharmony_ci		netdev_err(sdev->netdev, "failed to init rx queue: %u\n", err);
12878c2ecf20Sopenharmony_ci		goto free_sm;
12888c2ecf20Sopenharmony_ci	}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	err = slic_init_tx_queue(sdev);
12918c2ecf20Sopenharmony_ci	if (err) {
12928c2ecf20Sopenharmony_ci		netdev_err(sdev->netdev, "failed to init tx queue: %u\n", err);
12938c2ecf20Sopenharmony_ci		goto free_rxq;
12948c2ecf20Sopenharmony_ci	}
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	err = slic_init_stat_queue(sdev);
12978c2ecf20Sopenharmony_ci	if (err) {
12988c2ecf20Sopenharmony_ci		netdev_err(sdev->netdev, "failed to init status queue: %u\n",
12998c2ecf20Sopenharmony_ci			   err);
13008c2ecf20Sopenharmony_ci		goto free_txq;
13018c2ecf20Sopenharmony_ci	}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr));
13048c2ecf20Sopenharmony_ci	napi_enable(&sdev->napi);
13058c2ecf20Sopenharmony_ci	/* disable irq mitigation */
13068c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_INTAGG, 0);
13078c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISR, 0);
13088c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	slic_set_mac_address(sdev);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	spin_lock_bh(&sdev->link_lock);
13138c2ecf20Sopenharmony_ci	sdev->duplex = DUPLEX_UNKNOWN;
13148c2ecf20Sopenharmony_ci	sdev->speed = SPEED_UNKNOWN;
13158c2ecf20Sopenharmony_ci	spin_unlock_bh(&sdev->link_lock);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	slic_set_link_autoneg(sdev);
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	err = request_irq(sdev->pdev->irq, slic_irq, IRQF_SHARED, DRV_NAME,
13208c2ecf20Sopenharmony_ci			  sdev);
13218c2ecf20Sopenharmony_ci	if (err) {
13228c2ecf20Sopenharmony_ci		netdev_err(sdev->netdev, "failed to request irq: %u\n", err);
13238c2ecf20Sopenharmony_ci		goto disable_napi;
13248c2ecf20Sopenharmony_ci	}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_ON);
13278c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
13288c2ecf20Sopenharmony_ci	/* request initial link status */
13298c2ecf20Sopenharmony_ci	err = slic_handle_link_change(sdev);
13308c2ecf20Sopenharmony_ci	if (err)
13318c2ecf20Sopenharmony_ci		netdev_warn(sdev->netdev,
13328c2ecf20Sopenharmony_ci			    "failed to set initial link state: %u\n", err);
13338c2ecf20Sopenharmony_ci	return 0;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_cidisable_napi:
13368c2ecf20Sopenharmony_ci	napi_disable(&sdev->napi);
13378c2ecf20Sopenharmony_ci	slic_free_stat_queue(sdev);
13388c2ecf20Sopenharmony_cifree_txq:
13398c2ecf20Sopenharmony_ci	slic_free_tx_queue(sdev);
13408c2ecf20Sopenharmony_cifree_rxq:
13418c2ecf20Sopenharmony_ci	slic_free_rx_queue(sdev);
13428c2ecf20Sopenharmony_cifree_sm:
13438c2ecf20Sopenharmony_ci	slic_free_shmem(sdev);
13448c2ecf20Sopenharmony_ci	slic_card_reset(sdev);
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	return err;
13478c2ecf20Sopenharmony_ci}
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_cistatic int slic_open(struct net_device *dev)
13508c2ecf20Sopenharmony_ci{
13518c2ecf20Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
13528c2ecf20Sopenharmony_ci	int err;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	err = slic_init_iface(sdev);
13578c2ecf20Sopenharmony_ci	if (err) {
13588c2ecf20Sopenharmony_ci		netdev_err(dev, "failed to initialize interface: %i\n", err);
13598c2ecf20Sopenharmony_ci		return err;
13608c2ecf20Sopenharmony_ci	}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	netif_start_queue(dev);
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	return 0;
13658c2ecf20Sopenharmony_ci}
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_cistatic int slic_close(struct net_device *dev)
13688c2ecf20Sopenharmony_ci{
13698c2ecf20Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
13708c2ecf20Sopenharmony_ci	u32 val;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	/* stop irq handling */
13758c2ecf20Sopenharmony_ci	napi_disable(&sdev->napi);
13768c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF);
13778c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISR, 0);
13788c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	free_irq(sdev->pdev->irq, sdev);
13818c2ecf20Sopenharmony_ci	/* turn off RCV and XMT and power down PHY */
13828c2ecf20Sopenharmony_ci	val = SLIC_GXCR_RESET | SLIC_GXCR_PAUSEEN;
13838c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WXCFG, val);
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	val = SLIC_GRCR_RESET | SLIC_GRCR_CTLEN | SLIC_GRCR_ADDRAEN |
13868c2ecf20Sopenharmony_ci	      SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT;
13878c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRCFG, val);
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	val = MII_BMCR << 16 | BMCR_PDOWN;
13908c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_WPHY, val);
13918c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	slic_clear_upr_list(&sdev->upr_list);
13948c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_QUIESCE, 0);
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	slic_free_stat_queue(sdev);
13978c2ecf20Sopenharmony_ci	slic_free_tx_queue(sdev);
13988c2ecf20Sopenharmony_ci	slic_free_rx_queue(sdev);
13998c2ecf20Sopenharmony_ci	slic_free_shmem(sdev);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	slic_card_reset(sdev);
14028c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	return 0;
14058c2ecf20Sopenharmony_ci}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_cistatic netdev_tx_t slic_xmit(struct sk_buff *skb, struct net_device *dev)
14088c2ecf20Sopenharmony_ci{
14098c2ecf20Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
14108c2ecf20Sopenharmony_ci	struct slic_tx_queue *txq = &sdev->txq;
14118c2ecf20Sopenharmony_ci	struct slic_tx_buffer *buff;
14128c2ecf20Sopenharmony_ci	struct slic_tx_desc *desc;
14138c2ecf20Sopenharmony_ci	dma_addr_t paddr;
14148c2ecf20Sopenharmony_ci	u32 cbar_val;
14158c2ecf20Sopenharmony_ci	u32 maplen;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	if (unlikely(slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS)) {
14188c2ecf20Sopenharmony_ci		netdev_err(dev, "BUG! not enough tx LEs left: %u\n",
14198c2ecf20Sopenharmony_ci			   slic_get_free_tx_descs(txq));
14208c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
14218c2ecf20Sopenharmony_ci	}
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	maplen = skb_headlen(skb);
14248c2ecf20Sopenharmony_ci	paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen,
14258c2ecf20Sopenharmony_ci			       DMA_TO_DEVICE);
14268c2ecf20Sopenharmony_ci	if (dma_mapping_error(&sdev->pdev->dev, paddr)) {
14278c2ecf20Sopenharmony_ci		netdev_err(dev, "failed to map tx buffer\n");
14288c2ecf20Sopenharmony_ci		goto drop_skb;
14298c2ecf20Sopenharmony_ci	}
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	buff = &txq->txbuffs[txq->put_idx];
14328c2ecf20Sopenharmony_ci	buff->skb = skb;
14338c2ecf20Sopenharmony_ci	dma_unmap_addr_set(buff, map_addr, paddr);
14348c2ecf20Sopenharmony_ci	dma_unmap_len_set(buff, map_len, maplen);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	desc = buff->desc;
14378c2ecf20Sopenharmony_ci	desc->totlen = cpu_to_le32(maplen);
14388c2ecf20Sopenharmony_ci	desc->paddrl = cpu_to_le32(lower_32_bits(paddr));
14398c2ecf20Sopenharmony_ci	desc->paddrh = cpu_to_le32(upper_32_bits(paddr));
14408c2ecf20Sopenharmony_ci	desc->len = cpu_to_le32(maplen);
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	txq->put_idx = slic_next_queue_idx(txq->put_idx, txq->len);
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	cbar_val = lower_32_bits(buff->desc_paddr) | 1;
14458c2ecf20Sopenharmony_ci	/* complete writes to RAM and DMA before hardware is informed */
14468c2ecf20Sopenharmony_ci	wmb();
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_CBAR, cbar_val);
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	if (slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS)
14518c2ecf20Sopenharmony_ci		netif_stop_queue(dev);
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
14548c2ecf20Sopenharmony_cidrop_skb:
14558c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb);
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
14588c2ecf20Sopenharmony_ci}
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_cistatic void slic_get_stats(struct net_device *dev,
14618c2ecf20Sopenharmony_ci			   struct rtnl_link_stats64 *lst)
14628c2ecf20Sopenharmony_ci{
14638c2ecf20Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
14648c2ecf20Sopenharmony_ci	struct slic_stats *stats = &sdev->stats;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_packets, stats, rx_packets);
14678c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->tx_packets, stats, tx_packets);
14688c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_bytes, stats, rx_bytes);
14698c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->tx_bytes, stats, tx_bytes);
14708c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_errors, stats, rx_errors);
14718c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_dropped, stats, rx_buff_miss);
14728c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->tx_dropped, stats, tx_dropped);
14738c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->multicast, stats, rx_mcasts);
14748c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_over_errors, stats, rx_buffoflow);
14758c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_crc_errors, stats, rx_crc);
14768c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_fifo_errors, stats, rx_oflow802);
14778c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->tx_carrier_errors, stats, tx_carrier);
14788c2ecf20Sopenharmony_ci}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_cistatic int slic_get_sset_count(struct net_device *dev, int sset)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	switch (sset) {
14838c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
14848c2ecf20Sopenharmony_ci		return ARRAY_SIZE(slic_stats_strings);
14858c2ecf20Sopenharmony_ci	default:
14868c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
14878c2ecf20Sopenharmony_ci	}
14888c2ecf20Sopenharmony_ci}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_cistatic void slic_get_ethtool_stats(struct net_device *dev,
14918c2ecf20Sopenharmony_ci				   struct ethtool_stats *eth_stats, u64 *data)
14928c2ecf20Sopenharmony_ci{
14938c2ecf20Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
14948c2ecf20Sopenharmony_ci	struct slic_stats *stats = &sdev->stats;
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[0], stats, rx_packets);
14978c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[1], stats, rx_bytes);
14988c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[2], stats, rx_mcasts);
14998c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[3], stats, rx_errors);
15008c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[4], stats, rx_buff_miss);
15018c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[5], stats, rx_tpcsum);
15028c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[6], stats, rx_tpoflow);
15038c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[7], stats, rx_tphlen);
15048c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[8], stats, rx_ipcsum);
15058c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[9], stats, rx_iplen);
15068c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[10], stats, rx_iphlen);
15078c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[11], stats, rx_early);
15088c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[12], stats, rx_buffoflow);
15098c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[13], stats, rx_lcode);
15108c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[14], stats, rx_drbl);
15118c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[15], stats, rx_crc);
15128c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[16], stats, rx_oflow802);
15138c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[17], stats, rx_uflow802);
15148c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[18], stats, tx_packets);
15158c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[19], stats, tx_bytes);
15168c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[20], stats, tx_carrier);
15178c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[21], stats, tx_dropped);
15188c2ecf20Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[22], stats, irq_errs);
15198c2ecf20Sopenharmony_ci}
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cistatic void slic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
15228c2ecf20Sopenharmony_ci{
15238c2ecf20Sopenharmony_ci	if (stringset == ETH_SS_STATS) {
15248c2ecf20Sopenharmony_ci		memcpy(data, slic_stats_strings, sizeof(slic_stats_strings));
15258c2ecf20Sopenharmony_ci		data += sizeof(slic_stats_strings);
15268c2ecf20Sopenharmony_ci	}
15278c2ecf20Sopenharmony_ci}
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_cistatic void slic_get_drvinfo(struct net_device *dev,
15308c2ecf20Sopenharmony_ci			     struct ethtool_drvinfo *info)
15318c2ecf20Sopenharmony_ci{
15328c2ecf20Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
15358c2ecf20Sopenharmony_ci	strlcpy(info->bus_info, pci_name(sdev->pdev), sizeof(info->bus_info));
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_cistatic const struct ethtool_ops slic_ethtool_ops = {
15398c2ecf20Sopenharmony_ci	.get_drvinfo		= slic_get_drvinfo,
15408c2ecf20Sopenharmony_ci	.get_link		= ethtool_op_get_link,
15418c2ecf20Sopenharmony_ci	.get_strings		= slic_get_strings,
15428c2ecf20Sopenharmony_ci	.get_ethtool_stats	= slic_get_ethtool_stats,
15438c2ecf20Sopenharmony_ci	.get_sset_count		= slic_get_sset_count,
15448c2ecf20Sopenharmony_ci};
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_cistatic const struct net_device_ops slic_netdev_ops = {
15478c2ecf20Sopenharmony_ci	.ndo_open		= slic_open,
15488c2ecf20Sopenharmony_ci	.ndo_stop		= slic_close,
15498c2ecf20Sopenharmony_ci	.ndo_start_xmit		= slic_xmit,
15508c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
15518c2ecf20Sopenharmony_ci	.ndo_get_stats64	= slic_get_stats,
15528c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= slic_set_rx_mode,
15538c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
15548c2ecf20Sopenharmony_ci};
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_cistatic u16 slic_eeprom_csum(unsigned char *eeprom, unsigned int len)
15578c2ecf20Sopenharmony_ci{
15588c2ecf20Sopenharmony_ci	unsigned char *ptr = eeprom;
15598c2ecf20Sopenharmony_ci	u32 csum = 0;
15608c2ecf20Sopenharmony_ci	__le16 data;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	while (len > 1) {
15638c2ecf20Sopenharmony_ci		memcpy(&data, ptr, sizeof(data));
15648c2ecf20Sopenharmony_ci		csum += le16_to_cpu(data);
15658c2ecf20Sopenharmony_ci		ptr += 2;
15668c2ecf20Sopenharmony_ci		len -= 2;
15678c2ecf20Sopenharmony_ci	}
15688c2ecf20Sopenharmony_ci	if (len > 0)
15698c2ecf20Sopenharmony_ci		csum += *(u8 *)ptr;
15708c2ecf20Sopenharmony_ci	while (csum >> 16)
15718c2ecf20Sopenharmony_ci		csum = (csum & 0xFFFF) + ((csum >> 16) & 0xFFFF);
15728c2ecf20Sopenharmony_ci	return ~csum;
15738c2ecf20Sopenharmony_ci}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci/* check eeprom size, magic and checksum */
15768c2ecf20Sopenharmony_cistatic bool slic_eeprom_valid(unsigned char *eeprom, unsigned int size)
15778c2ecf20Sopenharmony_ci{
15788c2ecf20Sopenharmony_ci	const unsigned int MAX_SIZE = 128;
15798c2ecf20Sopenharmony_ci	const unsigned int MIN_SIZE = 98;
15808c2ecf20Sopenharmony_ci	__le16 magic;
15818c2ecf20Sopenharmony_ci	__le16 csum;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	if (size < MIN_SIZE || size > MAX_SIZE)
15848c2ecf20Sopenharmony_ci		return false;
15858c2ecf20Sopenharmony_ci	memcpy(&magic, eeprom, sizeof(magic));
15868c2ecf20Sopenharmony_ci	if (le16_to_cpu(magic) != SLIC_EEPROM_MAGIC)
15878c2ecf20Sopenharmony_ci		return false;
15888c2ecf20Sopenharmony_ci	/* cut checksum bytes */
15898c2ecf20Sopenharmony_ci	size -= 2;
15908c2ecf20Sopenharmony_ci	memcpy(&csum, eeprom + size, sizeof(csum));
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	return (le16_to_cpu(csum) == slic_eeprom_csum(eeprom, size));
15938c2ecf20Sopenharmony_ci}
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_cistatic int slic_read_eeprom(struct slic_device *sdev)
15968c2ecf20Sopenharmony_ci{
15978c2ecf20Sopenharmony_ci	unsigned int devfn = PCI_FUNC(sdev->pdev->devfn);
15988c2ecf20Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
15998c2ecf20Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
16008c2ecf20Sopenharmony_ci	const unsigned int MAX_LOOPS = 5000;
16018c2ecf20Sopenharmony_ci	unsigned int codesize;
16028c2ecf20Sopenharmony_ci	unsigned char *eeprom;
16038c2ecf20Sopenharmony_ci	struct slic_upr *upr;
16048c2ecf20Sopenharmony_ci	unsigned int i = 0;
16058c2ecf20Sopenharmony_ci	dma_addr_t paddr;
16068c2ecf20Sopenharmony_ci	int err = 0;
16078c2ecf20Sopenharmony_ci	u8 *mac[2];
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	eeprom = dma_alloc_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE,
16108c2ecf20Sopenharmony_ci				    &paddr, GFP_KERNEL);
16118c2ecf20Sopenharmony_ci	if (!eeprom)
16128c2ecf20Sopenharmony_ci		return -ENOMEM;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF);
16158c2ecf20Sopenharmony_ci	/* setup ISP temporarily */
16168c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr));
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	err = slic_new_upr(sdev, SLIC_UPR_CONFIG, paddr);
16198c2ecf20Sopenharmony_ci	if (!err) {
16208c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_LOOPS; i++) {
16218c2ecf20Sopenharmony_ci			if (le32_to_cpu(sm_data->isr) & SLIC_ISR_UPC)
16228c2ecf20Sopenharmony_ci				break;
16238c2ecf20Sopenharmony_ci			mdelay(1);
16248c2ecf20Sopenharmony_ci		}
16258c2ecf20Sopenharmony_ci		if (i == MAX_LOOPS) {
16268c2ecf20Sopenharmony_ci			dev_err(&sdev->pdev->dev,
16278c2ecf20Sopenharmony_ci				"timed out while waiting for eeprom data\n");
16288c2ecf20Sopenharmony_ci			err = -ETIMEDOUT;
16298c2ecf20Sopenharmony_ci		}
16308c2ecf20Sopenharmony_ci		upr = slic_dequeue_upr(sdev);
16318c2ecf20Sopenharmony_ci		kfree(upr);
16328c2ecf20Sopenharmony_ci	}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISP, 0);
16358c2ecf20Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISR, 0);
16368c2ecf20Sopenharmony_ci	slic_flush_write(sdev);
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	if (err)
16398c2ecf20Sopenharmony_ci		goto free_eeprom;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	if (sdev->model == SLIC_MODEL_OASIS) {
16428c2ecf20Sopenharmony_ci		struct slic_oasis_eeprom *oee;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci		oee = (struct slic_oasis_eeprom *)eeprom;
16458c2ecf20Sopenharmony_ci		mac[0] = oee->mac;
16468c2ecf20Sopenharmony_ci		mac[1] = oee->mac2;
16478c2ecf20Sopenharmony_ci		codesize = le16_to_cpu(oee->eeprom_code_size);
16488c2ecf20Sopenharmony_ci	} else {
16498c2ecf20Sopenharmony_ci		struct slic_mojave_eeprom *mee;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci		mee = (struct slic_mojave_eeprom *)eeprom;
16528c2ecf20Sopenharmony_ci		mac[0] = mee->mac;
16538c2ecf20Sopenharmony_ci		mac[1] = mee->mac2;
16548c2ecf20Sopenharmony_ci		codesize = le16_to_cpu(mee->eeprom_code_size);
16558c2ecf20Sopenharmony_ci	}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	if (!slic_eeprom_valid(eeprom, codesize)) {
16588c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev, "invalid checksum in eeprom\n");
16598c2ecf20Sopenharmony_ci		err = -EINVAL;
16608c2ecf20Sopenharmony_ci		goto free_eeprom;
16618c2ecf20Sopenharmony_ci	}
16628c2ecf20Sopenharmony_ci	/* set mac address */
16638c2ecf20Sopenharmony_ci	ether_addr_copy(sdev->netdev->dev_addr, mac[devfn]);
16648c2ecf20Sopenharmony_cifree_eeprom:
16658c2ecf20Sopenharmony_ci	dma_free_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE, eeprom, paddr);
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	return err;
16688c2ecf20Sopenharmony_ci}
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_cistatic int slic_init(struct slic_device *sdev)
16718c2ecf20Sopenharmony_ci{
16728c2ecf20Sopenharmony_ci	int err;
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	spin_lock_init(&sdev->upper_lock);
16758c2ecf20Sopenharmony_ci	spin_lock_init(&sdev->link_lock);
16768c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&sdev->upr_list.list);
16778c2ecf20Sopenharmony_ci	spin_lock_init(&sdev->upr_list.lock);
16788c2ecf20Sopenharmony_ci	u64_stats_init(&sdev->stats.syncp);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	slic_card_reset(sdev);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	err = slic_load_firmware(sdev);
16838c2ecf20Sopenharmony_ci	if (err) {
16848c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to load firmware\n");
16858c2ecf20Sopenharmony_ci		return err;
16868c2ecf20Sopenharmony_ci	}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	/* we need the shared memory to read EEPROM so set it up temporarily */
16898c2ecf20Sopenharmony_ci	err = slic_init_shmem(sdev);
16908c2ecf20Sopenharmony_ci	if (err) {
16918c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to init shared memory\n");
16928c2ecf20Sopenharmony_ci		return err;
16938c2ecf20Sopenharmony_ci	}
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	err = slic_read_eeprom(sdev);
16968c2ecf20Sopenharmony_ci	if (err) {
16978c2ecf20Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to read eeprom\n");
16988c2ecf20Sopenharmony_ci		goto free_sm;
16998c2ecf20Sopenharmony_ci	}
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	slic_card_reset(sdev);
17028c2ecf20Sopenharmony_ci	slic_free_shmem(sdev);
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	return 0;
17058c2ecf20Sopenharmony_cifree_sm:
17068c2ecf20Sopenharmony_ci	slic_free_shmem(sdev);
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	return err;
17098c2ecf20Sopenharmony_ci}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_cistatic bool slic_is_fiber(unsigned short subdev)
17128c2ecf20Sopenharmony_ci{
17138c2ecf20Sopenharmony_ci	switch (subdev) {
17148c2ecf20Sopenharmony_ci	/* Mojave */
17158c2ecf20Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_1000X1F:
17168c2ecf20Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SES1001F: fallthrough;
17178c2ecf20Sopenharmony_ci	/* Oasis */
17188c2ecf20Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2002XF:
17198c2ecf20Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2001XF:
17208c2ecf20Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2104EF:
17218c2ecf20Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2102EF:
17228c2ecf20Sopenharmony_ci		return true;
17238c2ecf20Sopenharmony_ci	}
17248c2ecf20Sopenharmony_ci	return false;
17258c2ecf20Sopenharmony_ci}
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_cistatic void slic_configure_pci(struct pci_dev *pdev)
17288c2ecf20Sopenharmony_ci{
17298c2ecf20Sopenharmony_ci	u16 old;
17308c2ecf20Sopenharmony_ci	u16 cmd;
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, PCI_COMMAND, &old);
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	cmd = old | PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
17358c2ecf20Sopenharmony_ci	if (old != cmd)
17368c2ecf20Sopenharmony_ci		pci_write_config_word(pdev, PCI_COMMAND, cmd);
17378c2ecf20Sopenharmony_ci}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_cistatic int slic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
17408c2ecf20Sopenharmony_ci{
17418c2ecf20Sopenharmony_ci	struct slic_device *sdev;
17428c2ecf20Sopenharmony_ci	struct net_device *dev;
17438c2ecf20Sopenharmony_ci	int err;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	err = pci_enable_device(pdev);
17468c2ecf20Sopenharmony_ci	if (err) {
17478c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable PCI device\n");
17488c2ecf20Sopenharmony_ci		return err;
17498c2ecf20Sopenharmony_ci	}
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	pci_set_master(pdev);
17528c2ecf20Sopenharmony_ci	pci_try_set_mwi(pdev);
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	slic_configure_pci(pdev);
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci	err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
17578c2ecf20Sopenharmony_ci	if (err) {
17588c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to setup DMA\n");
17598c2ecf20Sopenharmony_ci		goto disable;
17608c2ecf20Sopenharmony_ci	}
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
17658c2ecf20Sopenharmony_ci	if (err) {
17668c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to obtain PCI regions\n");
17678c2ecf20Sopenharmony_ci		goto disable;
17688c2ecf20Sopenharmony_ci	}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(*sdev));
17718c2ecf20Sopenharmony_ci	if (!dev) {
17728c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to alloc ethernet device\n");
17738c2ecf20Sopenharmony_ci		err = -ENOMEM;
17748c2ecf20Sopenharmony_ci		goto free_regions;
17758c2ecf20Sopenharmony_ci	}
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
17788c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, dev);
17798c2ecf20Sopenharmony_ci	dev->irq = pdev->irq;
17808c2ecf20Sopenharmony_ci	dev->netdev_ops = &slic_netdev_ops;
17818c2ecf20Sopenharmony_ci	dev->hw_features = NETIF_F_RXCSUM;
17828c2ecf20Sopenharmony_ci	dev->features |= dev->hw_features;
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	dev->ethtool_ops = &slic_ethtool_ops;
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	sdev = netdev_priv(dev);
17878c2ecf20Sopenharmony_ci	sdev->model = (pdev->device == PCI_DEVICE_ID_ALACRITECH_OASIS) ?
17888c2ecf20Sopenharmony_ci		      SLIC_MODEL_OASIS : SLIC_MODEL_MOJAVE;
17898c2ecf20Sopenharmony_ci	sdev->is_fiber = slic_is_fiber(pdev->subsystem_device);
17908c2ecf20Sopenharmony_ci	sdev->pdev = pdev;
17918c2ecf20Sopenharmony_ci	sdev->netdev = dev;
17928c2ecf20Sopenharmony_ci	sdev->regs = ioremap(pci_resource_start(pdev, 0),
17938c2ecf20Sopenharmony_ci				     pci_resource_len(pdev, 0));
17948c2ecf20Sopenharmony_ci	if (!sdev->regs) {
17958c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to map registers\n");
17968c2ecf20Sopenharmony_ci		err = -ENOMEM;
17978c2ecf20Sopenharmony_ci		goto free_netdev;
17988c2ecf20Sopenharmony_ci	}
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	err = slic_init(sdev);
18018c2ecf20Sopenharmony_ci	if (err) {
18028c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to initialize driver\n");
18038c2ecf20Sopenharmony_ci		goto unmap;
18048c2ecf20Sopenharmony_ci	}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	netif_napi_add(dev, &sdev->napi, slic_poll, SLIC_NAPI_WEIGHT);
18078c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	err = register_netdev(dev);
18108c2ecf20Sopenharmony_ci	if (err) {
18118c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register net device: %i\n", err);
18128c2ecf20Sopenharmony_ci		goto unmap;
18138c2ecf20Sopenharmony_ci	}
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	return 0;
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ciunmap:
18188c2ecf20Sopenharmony_ci	iounmap(sdev->regs);
18198c2ecf20Sopenharmony_cifree_netdev:
18208c2ecf20Sopenharmony_ci	free_netdev(dev);
18218c2ecf20Sopenharmony_cifree_regions:
18228c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
18238c2ecf20Sopenharmony_cidisable:
18248c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	return err;
18278c2ecf20Sopenharmony_ci}
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_cistatic void slic_remove(struct pci_dev *pdev)
18308c2ecf20Sopenharmony_ci{
18318c2ecf20Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
18328c2ecf20Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci	unregister_netdev(dev);
18358c2ecf20Sopenharmony_ci	iounmap(sdev->regs);
18368c2ecf20Sopenharmony_ci	free_netdev(dev);
18378c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
18388c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
18398c2ecf20Sopenharmony_ci}
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_cistatic struct pci_driver slic_driver = {
18428c2ecf20Sopenharmony_ci	.name = DRV_NAME,
18438c2ecf20Sopenharmony_ci	.id_table = slic_id_tbl,
18448c2ecf20Sopenharmony_ci	.probe = slic_probe,
18458c2ecf20Sopenharmony_ci	.remove = slic_remove,
18468c2ecf20Sopenharmony_ci};
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_cimodule_pci_driver(slic_driver);
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Alacritech non-accelerated SLIC driver");
18518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lino Sanfilippo <LinoSanfilippo@gmx.de>");
18528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1853