162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Gigabit Ethernet adapters based on the Session Layer
462306a36Sopenharmony_ci * Interface (SLIC) technology by Alacritech. The driver does not
562306a36Sopenharmony_ci * support the hardware acceleration features provided by these cards.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2016 Lino Sanfilippo <LinoSanfilippo@gmx.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/pci.h>
1362306a36Sopenharmony_ci#include <linux/netdevice.h>
1462306a36Sopenharmony_ci#include <linux/etherdevice.h>
1562306a36Sopenharmony_ci#include <linux/if_ether.h>
1662306a36Sopenharmony_ci#include <linux/crc32.h>
1762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1862306a36Sopenharmony_ci#include <linux/ethtool.h>
1962306a36Sopenharmony_ci#include <linux/mii.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/delay.h>
2262306a36Sopenharmony_ci#include <linux/firmware.h>
2362306a36Sopenharmony_ci#include <linux/list.h>
2462306a36Sopenharmony_ci#include <linux/u64_stats_sync.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "slic.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define DRV_NAME			"slicoss"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const struct pci_device_id slic_id_tbl[] = {
3162306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH,
3262306a36Sopenharmony_ci		     PCI_DEVICE_ID_ALACRITECH_MOJAVE) },
3362306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH,
3462306a36Sopenharmony_ci		     PCI_DEVICE_ID_ALACRITECH_OASIS) },
3562306a36Sopenharmony_ci	{ 0 }
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic const char slic_stats_strings[][ETH_GSTRING_LEN] = {
3962306a36Sopenharmony_ci	"rx_packets",
4062306a36Sopenharmony_ci	"rx_bytes",
4162306a36Sopenharmony_ci	"rx_multicasts",
4262306a36Sopenharmony_ci	"rx_errors",
4362306a36Sopenharmony_ci	"rx_buff_miss",
4462306a36Sopenharmony_ci	"rx_tp_csum",
4562306a36Sopenharmony_ci	"rx_tp_oflow",
4662306a36Sopenharmony_ci	"rx_tp_hlen",
4762306a36Sopenharmony_ci	"rx_ip_csum",
4862306a36Sopenharmony_ci	"rx_ip_len",
4962306a36Sopenharmony_ci	"rx_ip_hdr_len",
5062306a36Sopenharmony_ci	"rx_early",
5162306a36Sopenharmony_ci	"rx_buff_oflow",
5262306a36Sopenharmony_ci	"rx_lcode",
5362306a36Sopenharmony_ci	"rx_drbl",
5462306a36Sopenharmony_ci	"rx_crc",
5562306a36Sopenharmony_ci	"rx_oflow_802",
5662306a36Sopenharmony_ci	"rx_uflow_802",
5762306a36Sopenharmony_ci	"tx_packets",
5862306a36Sopenharmony_ci	"tx_bytes",
5962306a36Sopenharmony_ci	"tx_carrier",
6062306a36Sopenharmony_ci	"tx_dropped",
6162306a36Sopenharmony_ci	"irq_errs",
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic inline int slic_next_queue_idx(unsigned int idx, unsigned int qlen)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return (idx + 1) & (qlen - 1);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic inline int slic_get_free_queue_descs(unsigned int put_idx,
7062306a36Sopenharmony_ci					    unsigned int done_idx,
7162306a36Sopenharmony_ci					    unsigned int qlen)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (put_idx >= done_idx)
7462306a36Sopenharmony_ci		return (qlen - (put_idx - done_idx) - 1);
7562306a36Sopenharmony_ci	return (done_idx - put_idx - 1);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic unsigned int slic_next_compl_idx(struct slic_device *sdev)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct slic_stat_queue *stq = &sdev->stq;
8162306a36Sopenharmony_ci	unsigned int active = stq->active_array;
8262306a36Sopenharmony_ci	struct slic_stat_desc *descs;
8362306a36Sopenharmony_ci	struct slic_stat_desc *stat;
8462306a36Sopenharmony_ci	unsigned int idx;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	descs = stq->descs[active];
8762306a36Sopenharmony_ci	stat = &descs[stq->done_idx];
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (!stat->status)
9062306a36Sopenharmony_ci		return SLIC_INVALID_STAT_DESC_IDX;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	idx = (le32_to_cpu(stat->hnd) & 0xffff) - 1;
9362306a36Sopenharmony_ci	/* reset desc */
9462306a36Sopenharmony_ci	stat->hnd = 0;
9562306a36Sopenharmony_ci	stat->status = 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	stq->done_idx = slic_next_queue_idx(stq->done_idx, stq->len);
9862306a36Sopenharmony_ci	/* check for wraparound */
9962306a36Sopenharmony_ci	if (!stq->done_idx) {
10062306a36Sopenharmony_ci		dma_addr_t paddr = stq->paddr[active];
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) |
10362306a36Sopenharmony_ci						stq->len);
10462306a36Sopenharmony_ci		/* make sure new status descriptors are immediately available */
10562306a36Sopenharmony_ci		slic_flush_write(sdev);
10662306a36Sopenharmony_ci		active++;
10762306a36Sopenharmony_ci		active &= (SLIC_NUM_STAT_DESC_ARRAYS - 1);
10862306a36Sopenharmony_ci		stq->active_array = active;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	return idx;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic unsigned int slic_get_free_tx_descs(struct slic_tx_queue *txq)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	/* ensure tail idx is updated */
11662306a36Sopenharmony_ci	smp_mb();
11762306a36Sopenharmony_ci	return slic_get_free_queue_descs(txq->put_idx, txq->done_idx, txq->len);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic unsigned int slic_get_free_rx_descs(struct slic_rx_queue *rxq)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	return slic_get_free_queue_descs(rxq->put_idx, rxq->done_idx, rxq->len);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void slic_clear_upr_list(struct slic_upr_list *upr_list)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct slic_upr *upr;
12862306a36Sopenharmony_ci	struct slic_upr *tmp;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	spin_lock_bh(&upr_list->lock);
13162306a36Sopenharmony_ci	list_for_each_entry_safe(upr, tmp, &upr_list->list, list) {
13262306a36Sopenharmony_ci		list_del(&upr->list);
13362306a36Sopenharmony_ci		kfree(upr);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci	upr_list->pending = false;
13662306a36Sopenharmony_ci	spin_unlock_bh(&upr_list->lock);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void slic_start_upr(struct slic_device *sdev, struct slic_upr *upr)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	u32 reg;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	reg = (upr->type == SLIC_UPR_CONFIG) ? SLIC_REG_RCONFIG :
14462306a36Sopenharmony_ci					       SLIC_REG_LSTAT;
14562306a36Sopenharmony_ci	slic_write(sdev, reg, lower_32_bits(upr->paddr));
14662306a36Sopenharmony_ci	slic_flush_write(sdev);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic void slic_queue_upr(struct slic_device *sdev, struct slic_upr *upr)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct slic_upr_list *upr_list = &sdev->upr_list;
15262306a36Sopenharmony_ci	bool pending;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	spin_lock_bh(&upr_list->lock);
15562306a36Sopenharmony_ci	pending = upr_list->pending;
15662306a36Sopenharmony_ci	INIT_LIST_HEAD(&upr->list);
15762306a36Sopenharmony_ci	list_add_tail(&upr->list, &upr_list->list);
15862306a36Sopenharmony_ci	upr_list->pending = true;
15962306a36Sopenharmony_ci	spin_unlock_bh(&upr_list->lock);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (!pending)
16262306a36Sopenharmony_ci		slic_start_upr(sdev, upr);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic struct slic_upr *slic_dequeue_upr(struct slic_device *sdev)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct slic_upr_list *upr_list = &sdev->upr_list;
16862306a36Sopenharmony_ci	struct slic_upr *next_upr = NULL;
16962306a36Sopenharmony_ci	struct slic_upr *upr = NULL;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	spin_lock_bh(&upr_list->lock);
17262306a36Sopenharmony_ci	if (!list_empty(&upr_list->list)) {
17362306a36Sopenharmony_ci		upr = list_first_entry(&upr_list->list, struct slic_upr, list);
17462306a36Sopenharmony_ci		list_del(&upr->list);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		if (list_empty(&upr_list->list))
17762306a36Sopenharmony_ci			upr_list->pending = false;
17862306a36Sopenharmony_ci		else
17962306a36Sopenharmony_ci			next_upr = list_first_entry(&upr_list->list,
18062306a36Sopenharmony_ci						    struct slic_upr, list);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	spin_unlock_bh(&upr_list->lock);
18362306a36Sopenharmony_ci	/* trigger processing of the next upr in list */
18462306a36Sopenharmony_ci	if (next_upr)
18562306a36Sopenharmony_ci		slic_start_upr(sdev, next_upr);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return upr;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int slic_new_upr(struct slic_device *sdev, unsigned int type,
19162306a36Sopenharmony_ci			dma_addr_t paddr)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct slic_upr *upr;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	upr = kmalloc(sizeof(*upr), GFP_ATOMIC);
19662306a36Sopenharmony_ci	if (!upr)
19762306a36Sopenharmony_ci		return -ENOMEM;
19862306a36Sopenharmony_ci	upr->type = type;
19962306a36Sopenharmony_ci	upr->paddr = paddr;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	slic_queue_upr(sdev, upr);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic void slic_set_mcast_bit(u64 *mcmask, unsigned char const *addr)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	u64 mask = *mcmask;
20962306a36Sopenharmony_ci	u8 crc;
21062306a36Sopenharmony_ci	/* Get the CRC polynomial for the mac address: we use bits 1-8 (lsb),
21162306a36Sopenharmony_ci	 * bitwise reversed, msb (= lsb bit 0 before bitrev) is automatically
21262306a36Sopenharmony_ci	 * discarded.
21362306a36Sopenharmony_ci	 */
21462306a36Sopenharmony_ci	crc = ether_crc(ETH_ALEN, addr) >> 23;
21562306a36Sopenharmony_ci	 /* we only have space on the SLIC for 64 entries */
21662306a36Sopenharmony_ci	crc &= 0x3F;
21762306a36Sopenharmony_ci	mask |= (u64)1 << crc;
21862306a36Sopenharmony_ci	*mcmask = mask;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/* must be called with link_lock held */
22262306a36Sopenharmony_cistatic void slic_configure_rcv(struct slic_device *sdev)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	u32 val;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	val = SLIC_GRCR_RESET | SLIC_GRCR_ADDRAEN | SLIC_GRCR_RCVEN |
22762306a36Sopenharmony_ci	      SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT | SLIC_GRCR_RCVBAD;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (sdev->duplex == DUPLEX_FULL)
23062306a36Sopenharmony_ci		val |= SLIC_GRCR_CTLEN;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (sdev->promisc)
23362306a36Sopenharmony_ci		val |= SLIC_GRCR_RCVALL;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRCFG, val);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/* must be called with link_lock held */
23962306a36Sopenharmony_cistatic void slic_configure_xmt(struct slic_device *sdev)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	u32 val;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	val = SLIC_GXCR_RESET | SLIC_GXCR_XMTEN;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (sdev->duplex == DUPLEX_FULL)
24662306a36Sopenharmony_ci		val |= SLIC_GXCR_PAUSEEN;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WXCFG, val);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/* must be called with link_lock held */
25262306a36Sopenharmony_cistatic void slic_configure_mac(struct slic_device *sdev)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	u32 val;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (sdev->speed == SPEED_1000) {
25762306a36Sopenharmony_ci		val = SLIC_GMCR_GAPBB_1000 << SLIC_GMCR_GAPBB_SHIFT |
25862306a36Sopenharmony_ci		      SLIC_GMCR_GAPR1_1000 << SLIC_GMCR_GAPR1_SHIFT |
25962306a36Sopenharmony_ci		      SLIC_GMCR_GAPR2_1000 << SLIC_GMCR_GAPR2_SHIFT |
26062306a36Sopenharmony_ci		      SLIC_GMCR_GBIT; /* enable GMII */
26162306a36Sopenharmony_ci	} else {
26262306a36Sopenharmony_ci		val = SLIC_GMCR_GAPBB_100 << SLIC_GMCR_GAPBB_SHIFT |
26362306a36Sopenharmony_ci		      SLIC_GMCR_GAPR1_100 << SLIC_GMCR_GAPR1_SHIFT |
26462306a36Sopenharmony_ci		      SLIC_GMCR_GAPR2_100 << SLIC_GMCR_GAPR2_SHIFT;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (sdev->duplex == DUPLEX_FULL)
26862306a36Sopenharmony_ci		val |= SLIC_GMCR_FULLD;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WMCFG, val);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void slic_configure_link_locked(struct slic_device *sdev, int speed,
27462306a36Sopenharmony_ci				       unsigned int duplex)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct net_device *dev = sdev->netdev;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (sdev->speed == speed && sdev->duplex == duplex)
27962306a36Sopenharmony_ci		return;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	sdev->speed = speed;
28262306a36Sopenharmony_ci	sdev->duplex = duplex;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (sdev->speed == SPEED_UNKNOWN) {
28562306a36Sopenharmony_ci		if (netif_carrier_ok(dev))
28662306a36Sopenharmony_ci			netif_carrier_off(dev);
28762306a36Sopenharmony_ci	} else {
28862306a36Sopenharmony_ci		/* (re)configure link settings */
28962306a36Sopenharmony_ci		slic_configure_mac(sdev);
29062306a36Sopenharmony_ci		slic_configure_xmt(sdev);
29162306a36Sopenharmony_ci		slic_configure_rcv(sdev);
29262306a36Sopenharmony_ci		slic_flush_write(sdev);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (!netif_carrier_ok(dev))
29562306a36Sopenharmony_ci			netif_carrier_on(dev);
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void slic_configure_link(struct slic_device *sdev, int speed,
30062306a36Sopenharmony_ci				unsigned int duplex)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	spin_lock_bh(&sdev->link_lock);
30362306a36Sopenharmony_ci	slic_configure_link_locked(sdev, speed, duplex);
30462306a36Sopenharmony_ci	spin_unlock_bh(&sdev->link_lock);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void slic_set_rx_mode(struct net_device *dev)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
31062306a36Sopenharmony_ci	struct netdev_hw_addr *hwaddr;
31162306a36Sopenharmony_ci	bool set_promisc;
31262306a36Sopenharmony_ci	u64 mcmask;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
31562306a36Sopenharmony_ci		/* Turn on all multicast addresses. We have to do this for
31662306a36Sopenharmony_ci		 * promiscuous mode as well as ALLMCAST mode (it saves the
31762306a36Sopenharmony_ci		 * microcode from having to keep state about the MAC
31862306a36Sopenharmony_ci		 * configuration).
31962306a36Sopenharmony_ci		 */
32062306a36Sopenharmony_ci		mcmask = ~(u64)0;
32162306a36Sopenharmony_ci	} else  {
32262306a36Sopenharmony_ci		mcmask = 0;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		netdev_for_each_mc_addr(hwaddr, dev) {
32562306a36Sopenharmony_ci			slic_set_mcast_bit(&mcmask, hwaddr->addr);
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_MCASTLOW, lower_32_bits(mcmask));
33062306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_MCASTHIGH, upper_32_bits(mcmask));
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	set_promisc = !!(dev->flags & IFF_PROMISC);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	spin_lock_bh(&sdev->link_lock);
33562306a36Sopenharmony_ci	if (sdev->promisc != set_promisc) {
33662306a36Sopenharmony_ci		sdev->promisc = set_promisc;
33762306a36Sopenharmony_ci		slic_configure_rcv(sdev);
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci	spin_unlock_bh(&sdev->link_lock);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void slic_xmit_complete(struct slic_device *sdev)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct slic_tx_queue *txq = &sdev->txq;
34562306a36Sopenharmony_ci	struct net_device *dev = sdev->netdev;
34662306a36Sopenharmony_ci	struct slic_tx_buffer *buff;
34762306a36Sopenharmony_ci	unsigned int frames = 0;
34862306a36Sopenharmony_ci	unsigned int bytes = 0;
34962306a36Sopenharmony_ci	unsigned int idx;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* Limit processing to SLIC_MAX_TX_COMPLETIONS frames to avoid that new
35262306a36Sopenharmony_ci	 * completions during processing keeps the loop running endlessly.
35362306a36Sopenharmony_ci	 */
35462306a36Sopenharmony_ci	do {
35562306a36Sopenharmony_ci		idx = slic_next_compl_idx(sdev);
35662306a36Sopenharmony_ci		if (idx == SLIC_INVALID_STAT_DESC_IDX)
35762306a36Sopenharmony_ci			break;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		txq->done_idx = idx;
36062306a36Sopenharmony_ci		buff = &txq->txbuffs[idx];
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		if (unlikely(!buff->skb)) {
36362306a36Sopenharmony_ci			netdev_warn(dev,
36462306a36Sopenharmony_ci				    "no skb found for desc idx %i\n", idx);
36562306a36Sopenharmony_ci			continue;
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci		dma_unmap_single(&sdev->pdev->dev,
36862306a36Sopenharmony_ci				 dma_unmap_addr(buff, map_addr),
36962306a36Sopenharmony_ci				 dma_unmap_len(buff, map_len), DMA_TO_DEVICE);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		bytes += buff->skb->len;
37262306a36Sopenharmony_ci		frames++;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		dev_kfree_skb_any(buff->skb);
37562306a36Sopenharmony_ci		buff->skb = NULL;
37662306a36Sopenharmony_ci	} while (frames < SLIC_MAX_TX_COMPLETIONS);
37762306a36Sopenharmony_ci	/* make sure xmit sees the new value for done_idx */
37862306a36Sopenharmony_ci	smp_wmb();
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	u64_stats_update_begin(&sdev->stats.syncp);
38162306a36Sopenharmony_ci	sdev->stats.tx_bytes += bytes;
38262306a36Sopenharmony_ci	sdev->stats.tx_packets += frames;
38362306a36Sopenharmony_ci	u64_stats_update_end(&sdev->stats.syncp);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	netif_tx_lock(dev);
38662306a36Sopenharmony_ci	if (netif_queue_stopped(dev) &&
38762306a36Sopenharmony_ci	    (slic_get_free_tx_descs(txq) >= SLIC_MIN_TX_WAKEUP_DESCS))
38862306a36Sopenharmony_ci		netif_wake_queue(dev);
38962306a36Sopenharmony_ci	netif_tx_unlock(dev);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic void slic_refill_rx_queue(struct slic_device *sdev, gfp_t gfp)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	const unsigned int ALIGN_MASK = SLIC_RX_BUFF_ALIGN - 1;
39562306a36Sopenharmony_ci	unsigned int maplen = SLIC_RX_BUFF_SIZE;
39662306a36Sopenharmony_ci	struct slic_rx_queue *rxq = &sdev->rxq;
39762306a36Sopenharmony_ci	struct net_device *dev = sdev->netdev;
39862306a36Sopenharmony_ci	struct slic_rx_buffer *buff;
39962306a36Sopenharmony_ci	struct slic_rx_desc *desc;
40062306a36Sopenharmony_ci	unsigned int misalign;
40162306a36Sopenharmony_ci	unsigned int offset;
40262306a36Sopenharmony_ci	struct sk_buff *skb;
40362306a36Sopenharmony_ci	dma_addr_t paddr;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	while (slic_get_free_rx_descs(rxq) > SLIC_MAX_REQ_RX_DESCS) {
40662306a36Sopenharmony_ci		skb = alloc_skb(maplen + ALIGN_MASK, gfp);
40762306a36Sopenharmony_ci		if (!skb)
40862306a36Sopenharmony_ci			break;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen,
41162306a36Sopenharmony_ci				       DMA_FROM_DEVICE);
41262306a36Sopenharmony_ci		if (dma_mapping_error(&sdev->pdev->dev, paddr)) {
41362306a36Sopenharmony_ci			netdev_err(dev, "mapping rx packet failed\n");
41462306a36Sopenharmony_ci			/* drop skb */
41562306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
41662306a36Sopenharmony_ci			break;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci		/* ensure head buffer descriptors are 256 byte aligned */
41962306a36Sopenharmony_ci		offset = 0;
42062306a36Sopenharmony_ci		misalign = paddr & ALIGN_MASK;
42162306a36Sopenharmony_ci		if (misalign) {
42262306a36Sopenharmony_ci			offset = SLIC_RX_BUFF_ALIGN - misalign;
42362306a36Sopenharmony_ci			skb_reserve(skb, offset);
42462306a36Sopenharmony_ci		}
42562306a36Sopenharmony_ci		/* the HW expects dma chunks for descriptor + frame data */
42662306a36Sopenharmony_ci		desc = (struct slic_rx_desc *)skb->data;
42762306a36Sopenharmony_ci		/* temporarily sync descriptor for CPU to clear status */
42862306a36Sopenharmony_ci		dma_sync_single_for_cpu(&sdev->pdev->dev, paddr,
42962306a36Sopenharmony_ci					offset + sizeof(*desc),
43062306a36Sopenharmony_ci					DMA_FROM_DEVICE);
43162306a36Sopenharmony_ci		desc->status = 0;
43262306a36Sopenharmony_ci		/* return it to HW again */
43362306a36Sopenharmony_ci		dma_sync_single_for_device(&sdev->pdev->dev, paddr,
43462306a36Sopenharmony_ci					   offset + sizeof(*desc),
43562306a36Sopenharmony_ci					   DMA_FROM_DEVICE);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		buff = &rxq->rxbuffs[rxq->put_idx];
43862306a36Sopenharmony_ci		buff->skb = skb;
43962306a36Sopenharmony_ci		dma_unmap_addr_set(buff, map_addr, paddr);
44062306a36Sopenharmony_ci		dma_unmap_len_set(buff, map_len, maplen);
44162306a36Sopenharmony_ci		buff->addr_offset = offset;
44262306a36Sopenharmony_ci		/* complete write to descriptor before it is handed to HW */
44362306a36Sopenharmony_ci		wmb();
44462306a36Sopenharmony_ci		/* head buffer descriptors are placed immediately before skb */
44562306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_HBAR, lower_32_bits(paddr) + offset);
44662306a36Sopenharmony_ci		rxq->put_idx = slic_next_queue_idx(rxq->put_idx, rxq->len);
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic void slic_handle_frame_error(struct slic_device *sdev,
45162306a36Sopenharmony_ci				    struct sk_buff *skb)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct slic_stats *stats = &sdev->stats;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (sdev->model == SLIC_MODEL_OASIS) {
45662306a36Sopenharmony_ci		struct slic_rx_info_oasis *info;
45762306a36Sopenharmony_ci		u32 status_b;
45862306a36Sopenharmony_ci		u32 status;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		info = (struct slic_rx_info_oasis *)skb->data;
46162306a36Sopenharmony_ci		status = le32_to_cpu(info->frame_status);
46262306a36Sopenharmony_ci		status_b = le32_to_cpu(info->frame_status_b);
46362306a36Sopenharmony_ci		/* transport layer */
46462306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_TPCSUM)
46562306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_tpcsum);
46662306a36Sopenharmony_ci		if (status & SLIC_VRHSTAT_TPOFLO)
46762306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_tpoflow);
46862306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_TPHLEN)
46962306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_tphlen);
47062306a36Sopenharmony_ci		/* ip layer */
47162306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_IPCSUM)
47262306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_ipcsum);
47362306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_IPLERR)
47462306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_iplen);
47562306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_IPHERR)
47662306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_iphlen);
47762306a36Sopenharmony_ci		/* link layer */
47862306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_RCVE)
47962306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_early);
48062306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_BUFF)
48162306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_buffoflow);
48262306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_CODE)
48362306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_lcode);
48462306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_DRBL)
48562306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_drbl);
48662306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_CRC)
48762306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_crc);
48862306a36Sopenharmony_ci		if (status & SLIC_VRHSTAT_802OE)
48962306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_oflow802);
49062306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_802UE)
49162306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, rx_uflow802);
49262306a36Sopenharmony_ci		if (status_b & SLIC_VRHSTATB_CARRE)
49362306a36Sopenharmony_ci			SLIC_INC_STATS_COUNTER(stats, tx_carrier);
49462306a36Sopenharmony_ci	} else { /* mojave */
49562306a36Sopenharmony_ci		struct slic_rx_info_mojave *info;
49662306a36Sopenharmony_ci		u32 status;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		info = (struct slic_rx_info_mojave *)skb->data;
49962306a36Sopenharmony_ci		status = le32_to_cpu(info->frame_status);
50062306a36Sopenharmony_ci		/* transport layer */
50162306a36Sopenharmony_ci		if (status & SLIC_VGBSTAT_XPERR) {
50262306a36Sopenharmony_ci			u32 xerr = status >> SLIC_VGBSTAT_XERRSHFT;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci			if (xerr == SLIC_VGBSTAT_XCSERR)
50562306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_tpcsum);
50662306a36Sopenharmony_ci			if (xerr == SLIC_VGBSTAT_XUFLOW)
50762306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_tpoflow);
50862306a36Sopenharmony_ci			if (xerr == SLIC_VGBSTAT_XHLEN)
50962306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_tphlen);
51062306a36Sopenharmony_ci		}
51162306a36Sopenharmony_ci		/* ip layer */
51262306a36Sopenharmony_ci		if (status & SLIC_VGBSTAT_NETERR) {
51362306a36Sopenharmony_ci			u32 nerr = status >> SLIC_VGBSTAT_NERRSHFT &
51462306a36Sopenharmony_ci				   SLIC_VGBSTAT_NERRMSK;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci			if (nerr == SLIC_VGBSTAT_NCSERR)
51762306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_ipcsum);
51862306a36Sopenharmony_ci			if (nerr == SLIC_VGBSTAT_NUFLOW)
51962306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_iplen);
52062306a36Sopenharmony_ci			if (nerr == SLIC_VGBSTAT_NHLEN)
52162306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_iphlen);
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci		/* link layer */
52462306a36Sopenharmony_ci		if (status & SLIC_VGBSTAT_LNKERR) {
52562306a36Sopenharmony_ci			u32 lerr = status & SLIC_VGBSTAT_LERRMSK;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LDEARLY)
52862306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_early);
52962306a36Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LBOFLO)
53062306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_buffoflow);
53162306a36Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LCODERR)
53262306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_lcode);
53362306a36Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LDBLNBL)
53462306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_drbl);
53562306a36Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LCRCERR)
53662306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_crc);
53762306a36Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LOFLO)
53862306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_oflow802);
53962306a36Sopenharmony_ci			if (lerr == SLIC_VGBSTAT_LUFLO)
54062306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(stats, rx_uflow802);
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci	SLIC_INC_STATS_COUNTER(stats, rx_errors);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic void slic_handle_receive(struct slic_device *sdev, unsigned int todo,
54762306a36Sopenharmony_ci				unsigned int *done)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct slic_rx_queue *rxq = &sdev->rxq;
55062306a36Sopenharmony_ci	struct net_device *dev = sdev->netdev;
55162306a36Sopenharmony_ci	struct slic_rx_buffer *buff;
55262306a36Sopenharmony_ci	struct slic_rx_desc *desc;
55362306a36Sopenharmony_ci	unsigned int frames = 0;
55462306a36Sopenharmony_ci	unsigned int bytes = 0;
55562306a36Sopenharmony_ci	struct sk_buff *skb;
55662306a36Sopenharmony_ci	u32 status;
55762306a36Sopenharmony_ci	u32 len;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	while (todo && (rxq->done_idx != rxq->put_idx)) {
56062306a36Sopenharmony_ci		buff = &rxq->rxbuffs[rxq->done_idx];
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		skb = buff->skb;
56362306a36Sopenharmony_ci		if (!skb)
56462306a36Sopenharmony_ci			break;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		desc = (struct slic_rx_desc *)skb->data;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		dma_sync_single_for_cpu(&sdev->pdev->dev,
56962306a36Sopenharmony_ci					dma_unmap_addr(buff, map_addr),
57062306a36Sopenharmony_ci					buff->addr_offset + sizeof(*desc),
57162306a36Sopenharmony_ci					DMA_FROM_DEVICE);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		status = le32_to_cpu(desc->status);
57462306a36Sopenharmony_ci		if (!(status & SLIC_IRHDDR_SVALID)) {
57562306a36Sopenharmony_ci			dma_sync_single_for_device(&sdev->pdev->dev,
57662306a36Sopenharmony_ci						   dma_unmap_addr(buff,
57762306a36Sopenharmony_ci								  map_addr),
57862306a36Sopenharmony_ci						   buff->addr_offset +
57962306a36Sopenharmony_ci						   sizeof(*desc),
58062306a36Sopenharmony_ci						   DMA_FROM_DEVICE);
58162306a36Sopenharmony_ci			break;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		buff->skb = NULL;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		dma_unmap_single(&sdev->pdev->dev,
58762306a36Sopenharmony_ci				 dma_unmap_addr(buff, map_addr),
58862306a36Sopenharmony_ci				 dma_unmap_len(buff, map_len),
58962306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		/* skip rx descriptor that is placed before the frame data */
59262306a36Sopenharmony_ci		skb_reserve(skb, SLIC_RX_BUFF_HDR_SIZE);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		if (unlikely(status & SLIC_IRHDDR_ERR)) {
59562306a36Sopenharmony_ci			slic_handle_frame_error(sdev, skb);
59662306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
59762306a36Sopenharmony_ci		} else {
59862306a36Sopenharmony_ci			struct ethhdr *eh = (struct ethhdr *)skb->data;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci			if (is_multicast_ether_addr(eh->h_dest))
60162306a36Sopenharmony_ci				SLIC_INC_STATS_COUNTER(&sdev->stats, rx_mcasts);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci			len = le32_to_cpu(desc->length) & SLIC_IRHDDR_FLEN_MSK;
60462306a36Sopenharmony_ci			skb_put(skb, len);
60562306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
60662306a36Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci			napi_gro_receive(&sdev->napi, skb);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci			bytes += len;
61162306a36Sopenharmony_ci			frames++;
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci		rxq->done_idx = slic_next_queue_idx(rxq->done_idx, rxq->len);
61462306a36Sopenharmony_ci		todo--;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	u64_stats_update_begin(&sdev->stats.syncp);
61862306a36Sopenharmony_ci	sdev->stats.rx_bytes += bytes;
61962306a36Sopenharmony_ci	sdev->stats.rx_packets += frames;
62062306a36Sopenharmony_ci	u64_stats_update_end(&sdev->stats.syncp);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	slic_refill_rx_queue(sdev, GFP_ATOMIC);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic void slic_handle_link_irq(struct slic_device *sdev)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
62862306a36Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
62962306a36Sopenharmony_ci	unsigned int duplex;
63062306a36Sopenharmony_ci	int speed;
63162306a36Sopenharmony_ci	u32 link;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	link = le32_to_cpu(sm_data->link);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (link & SLIC_GIG_LINKUP) {
63662306a36Sopenharmony_ci		if (link & SLIC_GIG_SPEED_1000)
63762306a36Sopenharmony_ci			speed = SPEED_1000;
63862306a36Sopenharmony_ci		else if (link & SLIC_GIG_SPEED_100)
63962306a36Sopenharmony_ci			speed = SPEED_100;
64062306a36Sopenharmony_ci		else
64162306a36Sopenharmony_ci			speed = SPEED_10;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		duplex = (link & SLIC_GIG_FULLDUPLEX) ? DUPLEX_FULL :
64462306a36Sopenharmony_ci							DUPLEX_HALF;
64562306a36Sopenharmony_ci	} else {
64662306a36Sopenharmony_ci		duplex = DUPLEX_UNKNOWN;
64762306a36Sopenharmony_ci		speed = SPEED_UNKNOWN;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci	slic_configure_link(sdev, speed, duplex);
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic void slic_handle_upr_irq(struct slic_device *sdev, u32 irqs)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct slic_upr *upr;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	/* remove upr that caused this irq (always the first entry in list) */
65762306a36Sopenharmony_ci	upr = slic_dequeue_upr(sdev);
65862306a36Sopenharmony_ci	if (!upr) {
65962306a36Sopenharmony_ci		netdev_warn(sdev->netdev, "no upr found on list\n");
66062306a36Sopenharmony_ci		return;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (upr->type == SLIC_UPR_LSTAT) {
66462306a36Sopenharmony_ci		if (unlikely(irqs & SLIC_ISR_UPCERR_MASK)) {
66562306a36Sopenharmony_ci			/* try again */
66662306a36Sopenharmony_ci			slic_queue_upr(sdev, upr);
66762306a36Sopenharmony_ci			return;
66862306a36Sopenharmony_ci		}
66962306a36Sopenharmony_ci		slic_handle_link_irq(sdev);
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci	kfree(upr);
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic int slic_handle_link_change(struct slic_device *sdev)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	return slic_new_upr(sdev, SLIC_UPR_LSTAT, sdev->shmem.link_paddr);
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic void slic_handle_err_irq(struct slic_device *sdev, u32 isr)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	struct slic_stats *stats = &sdev->stats;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (isr & SLIC_ISR_RMISS)
68462306a36Sopenharmony_ci		SLIC_INC_STATS_COUNTER(stats, rx_buff_miss);
68562306a36Sopenharmony_ci	if (isr & SLIC_ISR_XDROP)
68662306a36Sopenharmony_ci		SLIC_INC_STATS_COUNTER(stats, tx_dropped);
68762306a36Sopenharmony_ci	if (!(isr & (SLIC_ISR_RMISS | SLIC_ISR_XDROP)))
68862306a36Sopenharmony_ci		SLIC_INC_STATS_COUNTER(stats, irq_errs);
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic void slic_handle_irq(struct slic_device *sdev, u32 isr,
69262306a36Sopenharmony_ci			    unsigned int todo, unsigned int *done)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	if (isr & SLIC_ISR_ERR)
69562306a36Sopenharmony_ci		slic_handle_err_irq(sdev, isr);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (isr & SLIC_ISR_LEVENT)
69862306a36Sopenharmony_ci		slic_handle_link_change(sdev);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (isr & SLIC_ISR_UPC_MASK)
70162306a36Sopenharmony_ci		slic_handle_upr_irq(sdev, isr);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (isr & SLIC_ISR_RCV)
70462306a36Sopenharmony_ci		slic_handle_receive(sdev, todo, done);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (isr & SLIC_ISR_CMD)
70762306a36Sopenharmony_ci		slic_xmit_complete(sdev);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic int slic_poll(struct napi_struct *napi, int todo)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	struct slic_device *sdev = container_of(napi, struct slic_device, napi);
71362306a36Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
71462306a36Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
71562306a36Sopenharmony_ci	u32 isr = le32_to_cpu(sm_data->isr);
71662306a36Sopenharmony_ci	int done = 0;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	slic_handle_irq(sdev, isr, todo, &done);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (done < todo) {
72162306a36Sopenharmony_ci		napi_complete_done(napi, done);
72262306a36Sopenharmony_ci		/* reenable irqs */
72362306a36Sopenharmony_ci		sm_data->isr = 0;
72462306a36Sopenharmony_ci		/* make sure sm_data->isr is cleard before irqs are reenabled */
72562306a36Sopenharmony_ci		wmb();
72662306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_ISR, 0);
72762306a36Sopenharmony_ci		slic_flush_write(sdev);
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	return done;
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cistatic irqreturn_t slic_irq(int irq, void *dev_id)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct slic_device *sdev = dev_id;
73662306a36Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
73762306a36Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_MASK);
74062306a36Sopenharmony_ci	slic_flush_write(sdev);
74162306a36Sopenharmony_ci	/* make sure sm_data->isr is read after ICR_INT_MASK is set */
74262306a36Sopenharmony_ci	wmb();
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (!sm_data->isr) {
74562306a36Sopenharmony_ci		dma_rmb();
74662306a36Sopenharmony_ci		/* spurious interrupt */
74762306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_ISR, 0);
74862306a36Sopenharmony_ci		slic_flush_write(sdev);
74962306a36Sopenharmony_ci		return IRQ_NONE;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	napi_schedule_irqoff(&sdev->napi);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return IRQ_HANDLED;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic void slic_card_reset(struct slic_device *sdev)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	u16 cmd;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_RESET, SLIC_RESET_MAGIC);
76262306a36Sopenharmony_ci	/* flush write by means of config space */
76362306a36Sopenharmony_ci	pci_read_config_word(sdev->pdev, PCI_COMMAND, &cmd);
76462306a36Sopenharmony_ci	mdelay(1);
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic int slic_init_stat_queue(struct slic_device *sdev)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	const unsigned int DESC_ALIGN_MASK = SLIC_STATS_DESC_ALIGN - 1;
77062306a36Sopenharmony_ci	struct slic_stat_queue *stq = &sdev->stq;
77162306a36Sopenharmony_ci	struct slic_stat_desc *descs;
77262306a36Sopenharmony_ci	unsigned int misalign;
77362306a36Sopenharmony_ci	unsigned int offset;
77462306a36Sopenharmony_ci	dma_addr_t paddr;
77562306a36Sopenharmony_ci	size_t size;
77662306a36Sopenharmony_ci	int err;
77762306a36Sopenharmony_ci	int i;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	stq->len = SLIC_NUM_STAT_DESCS;
78062306a36Sopenharmony_ci	stq->active_array = 0;
78162306a36Sopenharmony_ci	stq->done_idx = 0;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	size = stq->len * sizeof(*descs) + DESC_ALIGN_MASK;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) {
78662306a36Sopenharmony_ci		descs = dma_alloc_coherent(&sdev->pdev->dev, size, &paddr,
78762306a36Sopenharmony_ci					   GFP_KERNEL);
78862306a36Sopenharmony_ci		if (!descs) {
78962306a36Sopenharmony_ci			netdev_err(sdev->netdev,
79062306a36Sopenharmony_ci				   "failed to allocate status descriptors\n");
79162306a36Sopenharmony_ci			err = -ENOMEM;
79262306a36Sopenharmony_ci			goto free_descs;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci		/* ensure correct alignment */
79562306a36Sopenharmony_ci		offset = 0;
79662306a36Sopenharmony_ci		misalign = paddr & DESC_ALIGN_MASK;
79762306a36Sopenharmony_ci		if (misalign) {
79862306a36Sopenharmony_ci			offset = SLIC_STATS_DESC_ALIGN - misalign;
79962306a36Sopenharmony_ci			descs += offset;
80062306a36Sopenharmony_ci			paddr += offset;
80162306a36Sopenharmony_ci		}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) |
80462306a36Sopenharmony_ci						stq->len);
80562306a36Sopenharmony_ci		stq->descs[i] = descs;
80662306a36Sopenharmony_ci		stq->paddr[i] = paddr;
80762306a36Sopenharmony_ci		stq->addr_offset[i] = offset;
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	stq->mem_size = size;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return 0;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cifree_descs:
81562306a36Sopenharmony_ci	while (i--) {
81662306a36Sopenharmony_ci		dma_free_coherent(&sdev->pdev->dev, stq->mem_size,
81762306a36Sopenharmony_ci				  stq->descs[i] - stq->addr_offset[i],
81862306a36Sopenharmony_ci				  stq->paddr[i] - stq->addr_offset[i]);
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	return err;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic void slic_free_stat_queue(struct slic_device *sdev)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct slic_stat_queue *stq = &sdev->stq;
82762306a36Sopenharmony_ci	int i;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) {
83062306a36Sopenharmony_ci		dma_free_coherent(&sdev->pdev->dev, stq->mem_size,
83162306a36Sopenharmony_ci				  stq->descs[i] - stq->addr_offset[i],
83262306a36Sopenharmony_ci				  stq->paddr[i] - stq->addr_offset[i]);
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int slic_init_tx_queue(struct slic_device *sdev)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	struct slic_tx_queue *txq = &sdev->txq;
83962306a36Sopenharmony_ci	struct slic_tx_buffer *buff;
84062306a36Sopenharmony_ci	struct slic_tx_desc *desc;
84162306a36Sopenharmony_ci	unsigned int i;
84262306a36Sopenharmony_ci	int err;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	txq->len = SLIC_NUM_TX_DESCS;
84562306a36Sopenharmony_ci	txq->put_idx = 0;
84662306a36Sopenharmony_ci	txq->done_idx = 0;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	txq->txbuffs = kcalloc(txq->len, sizeof(*buff), GFP_KERNEL);
84962306a36Sopenharmony_ci	if (!txq->txbuffs)
85062306a36Sopenharmony_ci		return -ENOMEM;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	txq->dma_pool = dma_pool_create("slic_pool", &sdev->pdev->dev,
85362306a36Sopenharmony_ci					sizeof(*desc), SLIC_TX_DESC_ALIGN,
85462306a36Sopenharmony_ci					4096);
85562306a36Sopenharmony_ci	if (!txq->dma_pool) {
85662306a36Sopenharmony_ci		err = -ENOMEM;
85762306a36Sopenharmony_ci		netdev_err(sdev->netdev, "failed to create dma pool\n");
85862306a36Sopenharmony_ci		goto free_buffs;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	for (i = 0; i < txq->len; i++) {
86262306a36Sopenharmony_ci		buff = &txq->txbuffs[i];
86362306a36Sopenharmony_ci		desc = dma_pool_zalloc(txq->dma_pool, GFP_KERNEL,
86462306a36Sopenharmony_ci				       &buff->desc_paddr);
86562306a36Sopenharmony_ci		if (!desc) {
86662306a36Sopenharmony_ci			netdev_err(sdev->netdev,
86762306a36Sopenharmony_ci				   "failed to alloc pool chunk (%i)\n", i);
86862306a36Sopenharmony_ci			err = -ENOMEM;
86962306a36Sopenharmony_ci			goto free_descs;
87062306a36Sopenharmony_ci		}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci		desc->hnd = cpu_to_le32((u32)(i + 1));
87362306a36Sopenharmony_ci		desc->cmd = SLIC_CMD_XMT_REQ;
87462306a36Sopenharmony_ci		desc->flags = 0;
87562306a36Sopenharmony_ci		desc->type = cpu_to_le32(SLIC_CMD_TYPE_DUMB);
87662306a36Sopenharmony_ci		buff->desc = desc;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	return 0;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cifree_descs:
88262306a36Sopenharmony_ci	while (i--) {
88362306a36Sopenharmony_ci		buff = &txq->txbuffs[i];
88462306a36Sopenharmony_ci		dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr);
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci	dma_pool_destroy(txq->dma_pool);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cifree_buffs:
88962306a36Sopenharmony_ci	kfree(txq->txbuffs);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	return err;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic void slic_free_tx_queue(struct slic_device *sdev)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	struct slic_tx_queue *txq = &sdev->txq;
89762306a36Sopenharmony_ci	struct slic_tx_buffer *buff;
89862306a36Sopenharmony_ci	unsigned int i;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	for (i = 0; i < txq->len; i++) {
90162306a36Sopenharmony_ci		buff = &txq->txbuffs[i];
90262306a36Sopenharmony_ci		dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr);
90362306a36Sopenharmony_ci		if (!buff->skb)
90462306a36Sopenharmony_ci			continue;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		dma_unmap_single(&sdev->pdev->dev,
90762306a36Sopenharmony_ci				 dma_unmap_addr(buff, map_addr),
90862306a36Sopenharmony_ci				 dma_unmap_len(buff, map_len), DMA_TO_DEVICE);
90962306a36Sopenharmony_ci		consume_skb(buff->skb);
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci	dma_pool_destroy(txq->dma_pool);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	kfree(txq->txbuffs);
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic int slic_init_rx_queue(struct slic_device *sdev)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	struct slic_rx_queue *rxq = &sdev->rxq;
91962306a36Sopenharmony_ci	struct slic_rx_buffer *buff;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	rxq->len = SLIC_NUM_RX_LES;
92262306a36Sopenharmony_ci	rxq->done_idx = 0;
92362306a36Sopenharmony_ci	rxq->put_idx = 0;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	buff = kcalloc(rxq->len, sizeof(*buff), GFP_KERNEL);
92662306a36Sopenharmony_ci	if (!buff)
92762306a36Sopenharmony_ci		return -ENOMEM;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	rxq->rxbuffs = buff;
93062306a36Sopenharmony_ci	slic_refill_rx_queue(sdev, GFP_KERNEL);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	return 0;
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic void slic_free_rx_queue(struct slic_device *sdev)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	struct slic_rx_queue *rxq = &sdev->rxq;
93862306a36Sopenharmony_ci	struct slic_rx_buffer *buff;
93962306a36Sopenharmony_ci	unsigned int i;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/* free rx buffers */
94262306a36Sopenharmony_ci	for (i = 0; i < rxq->len; i++) {
94362306a36Sopenharmony_ci		buff = &rxq->rxbuffs[i];
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		if (!buff->skb)
94662306a36Sopenharmony_ci			continue;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci		dma_unmap_single(&sdev->pdev->dev,
94962306a36Sopenharmony_ci				 dma_unmap_addr(buff, map_addr),
95062306a36Sopenharmony_ci				 dma_unmap_len(buff, map_len),
95162306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
95262306a36Sopenharmony_ci		consume_skb(buff->skb);
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci	kfree(rxq->rxbuffs);
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_cistatic void slic_set_link_autoneg(struct slic_device *sdev)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	unsigned int subid = sdev->pdev->subsystem_device;
96062306a36Sopenharmony_ci	u32 val;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (sdev->is_fiber) {
96362306a36Sopenharmony_ci		/* We've got a fiber gigabit interface, and register 4 is
96462306a36Sopenharmony_ci		 * different in fiber mode than in copper mode.
96562306a36Sopenharmony_ci		 */
96662306a36Sopenharmony_ci		/* advertise FD only @1000 Mb */
96762306a36Sopenharmony_ci		val = MII_ADVERTISE << 16 | ADVERTISE_1000XFULL |
96862306a36Sopenharmony_ci		      ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM;
96962306a36Sopenharmony_ci		/* enable PAUSE frames */
97062306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_WPHY, val);
97162306a36Sopenharmony_ci		/* reset phy, enable auto-neg  */
97262306a36Sopenharmony_ci		val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE |
97362306a36Sopenharmony_ci		      BMCR_ANRESTART;
97462306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_WPHY, val);
97562306a36Sopenharmony_ci	} else {	/* copper gigabit */
97662306a36Sopenharmony_ci		/* We've got a copper gigabit interface, and register 4 is
97762306a36Sopenharmony_ci		 * different in copper mode than in fiber mode.
97862306a36Sopenharmony_ci		 */
97962306a36Sopenharmony_ci		/* advertise 10/100 Mb modes   */
98062306a36Sopenharmony_ci		val = MII_ADVERTISE << 16 | ADVERTISE_100FULL |
98162306a36Sopenharmony_ci		      ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF;
98262306a36Sopenharmony_ci		/* enable PAUSE frames  */
98362306a36Sopenharmony_ci		val |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
98462306a36Sopenharmony_ci		/* required by the Cicada PHY  */
98562306a36Sopenharmony_ci		val |= ADVERTISE_CSMA;
98662306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_WPHY, val);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		/* advertise FD only @1000 Mb  */
98962306a36Sopenharmony_ci		val = MII_CTRL1000 << 16 | ADVERTISE_1000FULL;
99062306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_WPHY, val);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		if (subid != PCI_SUBDEVICE_ID_ALACRITECH_CICADA) {
99362306a36Sopenharmony_ci			 /* if a Marvell PHY enable auto crossover */
99462306a36Sopenharmony_ci			val = SLIC_MIICR_REG_16 | SLIC_MRV_REG16_XOVERON;
99562306a36Sopenharmony_ci			slic_write(sdev, SLIC_REG_WPHY, val);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci			/* reset phy, enable auto-neg  */
99862306a36Sopenharmony_ci			val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE |
99962306a36Sopenharmony_ci			      BMCR_ANRESTART;
100062306a36Sopenharmony_ci			slic_write(sdev, SLIC_REG_WPHY, val);
100162306a36Sopenharmony_ci		} else {
100262306a36Sopenharmony_ci			/* enable and restart auto-neg (don't reset)  */
100362306a36Sopenharmony_ci			val = MII_BMCR << 16 | BMCR_ANENABLE | BMCR_ANRESTART;
100462306a36Sopenharmony_ci			slic_write(sdev, SLIC_REG_WPHY, val);
100562306a36Sopenharmony_ci		}
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic void slic_set_mac_address(struct slic_device *sdev)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	const u8 *addr = sdev->netdev->dev_addr;
101262306a36Sopenharmony_ci	u32 val;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	val = addr[5] | addr[4] << 8 | addr[3] << 16 | addr[2] << 24;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRADDRAL, val);
101762306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRADDRBL, val);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	val = addr[0] << 8 | addr[1];
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRADDRAH, val);
102262306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRADDRBH, val);
102362306a36Sopenharmony_ci	slic_flush_write(sdev);
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic u32 slic_read_dword_from_firmware(const struct firmware *fw, int *offset)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	int idx = *offset;
102962306a36Sopenharmony_ci	__le32 val;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	memcpy(&val, fw->data + *offset, sizeof(val));
103262306a36Sopenharmony_ci	idx += 4;
103362306a36Sopenharmony_ci	*offset = idx;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	return le32_to_cpu(val);
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ciMODULE_FIRMWARE(SLIC_RCV_FIRMWARE_MOJAVE);
103962306a36Sopenharmony_ciMODULE_FIRMWARE(SLIC_RCV_FIRMWARE_OASIS);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic int slic_load_rcvseq_firmware(struct slic_device *sdev)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	const struct firmware *fw;
104462306a36Sopenharmony_ci	const char *file;
104562306a36Sopenharmony_ci	u32 codelen;
104662306a36Sopenharmony_ci	int idx = 0;
104762306a36Sopenharmony_ci	u32 instr;
104862306a36Sopenharmony_ci	u32 addr;
104962306a36Sopenharmony_ci	int err;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	file = (sdev->model == SLIC_MODEL_OASIS) ?  SLIC_RCV_FIRMWARE_OASIS :
105262306a36Sopenharmony_ci						    SLIC_RCV_FIRMWARE_MOJAVE;
105362306a36Sopenharmony_ci	err = request_firmware(&fw, file, &sdev->pdev->dev);
105462306a36Sopenharmony_ci	if (err) {
105562306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev,
105662306a36Sopenharmony_ci			"failed to load receive sequencer firmware %s\n", file);
105762306a36Sopenharmony_ci		return err;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci	/* Do an initial sanity check concerning firmware size now. A further
106062306a36Sopenharmony_ci	 * check follows below.
106162306a36Sopenharmony_ci	 */
106262306a36Sopenharmony_ci	if (fw->size < SLIC_FIRMWARE_MIN_SIZE) {
106362306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev,
106462306a36Sopenharmony_ci			"invalid firmware size %zu (min %u expected)\n",
106562306a36Sopenharmony_ci			fw->size, SLIC_FIRMWARE_MIN_SIZE);
106662306a36Sopenharmony_ci		err = -EINVAL;
106762306a36Sopenharmony_ci		goto release;
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	codelen = slic_read_dword_from_firmware(fw, &idx);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	/* do another sanity check against firmware size */
107362306a36Sopenharmony_ci	if ((codelen + 4) > fw->size) {
107462306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev,
107562306a36Sopenharmony_ci			"invalid rcv-sequencer firmware size %zu\n", fw->size);
107662306a36Sopenharmony_ci		err = -EINVAL;
107762306a36Sopenharmony_ci		goto release;
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/* download sequencer code to card */
108162306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_BEGIN);
108262306a36Sopenharmony_ci	for (addr = 0; addr < codelen; addr++) {
108362306a36Sopenharmony_ci		__le32 val;
108462306a36Sopenharmony_ci		/* write out instruction address */
108562306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_RCV_WCS, addr);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci		instr = slic_read_dword_from_firmware(fw, &idx);
108862306a36Sopenharmony_ci		/* write out the instruction data low addr */
108962306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_RCV_WCS, instr);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci		val = (__le32)fw->data[idx];
109262306a36Sopenharmony_ci		instr = le32_to_cpu(val);
109362306a36Sopenharmony_ci		idx++;
109462306a36Sopenharmony_ci		/* write out the instruction data high addr */
109562306a36Sopenharmony_ci		slic_write(sdev, SLIC_REG_RCV_WCS, instr);
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci	/* finish download */
109862306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_FINISH);
109962306a36Sopenharmony_ci	slic_flush_write(sdev);
110062306a36Sopenharmony_cirelease:
110162306a36Sopenharmony_ci	release_firmware(fw);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return err;
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ciMODULE_FIRMWARE(SLIC_FIRMWARE_MOJAVE);
110762306a36Sopenharmony_ciMODULE_FIRMWARE(SLIC_FIRMWARE_OASIS);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic int slic_load_firmware(struct slic_device *sdev)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	u32 sectstart[SLIC_FIRMWARE_MAX_SECTIONS];
111262306a36Sopenharmony_ci	u32 sectsize[SLIC_FIRMWARE_MAX_SECTIONS];
111362306a36Sopenharmony_ci	const struct firmware *fw;
111462306a36Sopenharmony_ci	unsigned int datalen;
111562306a36Sopenharmony_ci	const char *file;
111662306a36Sopenharmony_ci	int code_start;
111762306a36Sopenharmony_ci	unsigned int i;
111862306a36Sopenharmony_ci	u32 numsects;
111962306a36Sopenharmony_ci	int idx = 0;
112062306a36Sopenharmony_ci	u32 sect;
112162306a36Sopenharmony_ci	u32 instr;
112262306a36Sopenharmony_ci	u32 addr;
112362306a36Sopenharmony_ci	u32 base;
112462306a36Sopenharmony_ci	int err;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	file = (sdev->model == SLIC_MODEL_OASIS) ?  SLIC_FIRMWARE_OASIS :
112762306a36Sopenharmony_ci						    SLIC_FIRMWARE_MOJAVE;
112862306a36Sopenharmony_ci	err = request_firmware(&fw, file, &sdev->pdev->dev);
112962306a36Sopenharmony_ci	if (err) {
113062306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to load firmware %s\n", file);
113162306a36Sopenharmony_ci		return err;
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci	/* Do an initial sanity check concerning firmware size now. A further
113462306a36Sopenharmony_ci	 * check follows below.
113562306a36Sopenharmony_ci	 */
113662306a36Sopenharmony_ci	if (fw->size < SLIC_FIRMWARE_MIN_SIZE) {
113762306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev,
113862306a36Sopenharmony_ci			"invalid firmware size %zu (min is %u)\n", fw->size,
113962306a36Sopenharmony_ci			SLIC_FIRMWARE_MIN_SIZE);
114062306a36Sopenharmony_ci		err = -EINVAL;
114162306a36Sopenharmony_ci		goto release;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	numsects = slic_read_dword_from_firmware(fw, &idx);
114562306a36Sopenharmony_ci	if (numsects == 0 || numsects > SLIC_FIRMWARE_MAX_SECTIONS) {
114662306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev,
114762306a36Sopenharmony_ci			"invalid number of sections in firmware: %u", numsects);
114862306a36Sopenharmony_ci		err = -EINVAL;
114962306a36Sopenharmony_ci		goto release;
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	datalen = numsects * 8 + 4;
115362306a36Sopenharmony_ci	for (i = 0; i < numsects; i++) {
115462306a36Sopenharmony_ci		sectsize[i] = slic_read_dword_from_firmware(fw, &idx);
115562306a36Sopenharmony_ci		datalen += sectsize[i];
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	/* do another sanity check against firmware size */
115962306a36Sopenharmony_ci	if (datalen > fw->size) {
116062306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev,
116162306a36Sopenharmony_ci			"invalid firmware size %zu (expected >= %u)\n",
116262306a36Sopenharmony_ci			fw->size, datalen);
116362306a36Sopenharmony_ci		err = -EINVAL;
116462306a36Sopenharmony_ci		goto release;
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci	/* get sections */
116762306a36Sopenharmony_ci	for (i = 0; i < numsects; i++)
116862306a36Sopenharmony_ci		sectstart[i] = slic_read_dword_from_firmware(fw, &idx);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	code_start = idx;
117162306a36Sopenharmony_ci	instr = slic_read_dword_from_firmware(fw, &idx);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	for (sect = 0; sect < numsects; sect++) {
117462306a36Sopenharmony_ci		unsigned int ssize = sectsize[sect] >> 3;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci		base = sectstart[sect];
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci		for (addr = 0; addr < ssize; addr++) {
117962306a36Sopenharmony_ci			/* write out instruction address */
118062306a36Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, base + addr);
118162306a36Sopenharmony_ci			/* write out instruction to low addr */
118262306a36Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, instr);
118362306a36Sopenharmony_ci			instr = slic_read_dword_from_firmware(fw, &idx);
118462306a36Sopenharmony_ci			/* write out instruction to high addr */
118562306a36Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, instr);
118662306a36Sopenharmony_ci			instr = slic_read_dword_from_firmware(fw, &idx);
118762306a36Sopenharmony_ci		}
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	idx = code_start;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	for (sect = 0; sect < numsects; sect++) {
119362306a36Sopenharmony_ci		unsigned int ssize = sectsize[sect] >> 3;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci		instr = slic_read_dword_from_firmware(fw, &idx);
119662306a36Sopenharmony_ci		base = sectstart[sect];
119762306a36Sopenharmony_ci		if (base < 0x8000)
119862306a36Sopenharmony_ci			continue;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci		for (addr = 0; addr < ssize; addr++) {
120162306a36Sopenharmony_ci			/* write out instruction address */
120262306a36Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS,
120362306a36Sopenharmony_ci				   SLIC_WCS_COMPARE | (base + addr));
120462306a36Sopenharmony_ci			/* write out instruction to low addr */
120562306a36Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, instr);
120662306a36Sopenharmony_ci			instr = slic_read_dword_from_firmware(fw, &idx);
120762306a36Sopenharmony_ci			/* write out instruction to high addr */
120862306a36Sopenharmony_ci			slic_write(sdev, SLIC_REG_WCS, instr);
120962306a36Sopenharmony_ci			instr = slic_read_dword_from_firmware(fw, &idx);
121062306a36Sopenharmony_ci		}
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci	slic_flush_write(sdev);
121362306a36Sopenharmony_ci	mdelay(10);
121462306a36Sopenharmony_ci	/* everything OK, kick off the card */
121562306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WCS, SLIC_WCS_START);
121662306a36Sopenharmony_ci	slic_flush_write(sdev);
121762306a36Sopenharmony_ci	/* wait long enough for ucode to init card and reach the mainloop */
121862306a36Sopenharmony_ci	mdelay(20);
121962306a36Sopenharmony_cirelease:
122062306a36Sopenharmony_ci	release_firmware(fw);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	return err;
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cistatic int slic_init_shmem(struct slic_device *sdev)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
122862306a36Sopenharmony_ci	struct slic_shmem_data *sm_data;
122962306a36Sopenharmony_ci	dma_addr_t paddr;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	sm_data = dma_alloc_coherent(&sdev->pdev->dev, sizeof(*sm_data),
123262306a36Sopenharmony_ci				     &paddr, GFP_KERNEL);
123362306a36Sopenharmony_ci	if (!sm_data) {
123462306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to allocate shared memory\n");
123562306a36Sopenharmony_ci		return -ENOMEM;
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	sm->shmem_data = sm_data;
123962306a36Sopenharmony_ci	sm->isr_paddr = paddr;
124062306a36Sopenharmony_ci	sm->link_paddr = paddr + offsetof(struct slic_shmem_data, link);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	return 0;
124362306a36Sopenharmony_ci}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cistatic void slic_free_shmem(struct slic_device *sdev)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
124862306a36Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	dma_free_coherent(&sdev->pdev->dev, sizeof(*sm_data), sm_data,
125162306a36Sopenharmony_ci			  sm->isr_paddr);
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cistatic int slic_init_iface(struct slic_device *sdev)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
125762306a36Sopenharmony_ci	int err;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	sdev->upr_list.pending = false;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	err = slic_init_shmem(sdev);
126262306a36Sopenharmony_ci	if (err) {
126362306a36Sopenharmony_ci		netdev_err(sdev->netdev, "failed to init shared memory\n");
126462306a36Sopenharmony_ci		return err;
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	err = slic_load_firmware(sdev);
126862306a36Sopenharmony_ci	if (err) {
126962306a36Sopenharmony_ci		netdev_err(sdev->netdev, "failed to load firmware\n");
127062306a36Sopenharmony_ci		goto free_sm;
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	err = slic_load_rcvseq_firmware(sdev);
127462306a36Sopenharmony_ci	if (err) {
127562306a36Sopenharmony_ci		netdev_err(sdev->netdev,
127662306a36Sopenharmony_ci			   "failed to load firmware for receive sequencer\n");
127762306a36Sopenharmony_ci		goto free_sm;
127862306a36Sopenharmony_ci	}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF);
128162306a36Sopenharmony_ci	slic_flush_write(sdev);
128262306a36Sopenharmony_ci	mdelay(1);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	err = slic_init_rx_queue(sdev);
128562306a36Sopenharmony_ci	if (err) {
128662306a36Sopenharmony_ci		netdev_err(sdev->netdev, "failed to init rx queue: %u\n", err);
128762306a36Sopenharmony_ci		goto free_sm;
128862306a36Sopenharmony_ci	}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	err = slic_init_tx_queue(sdev);
129162306a36Sopenharmony_ci	if (err) {
129262306a36Sopenharmony_ci		netdev_err(sdev->netdev, "failed to init tx queue: %u\n", err);
129362306a36Sopenharmony_ci		goto free_rxq;
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	err = slic_init_stat_queue(sdev);
129762306a36Sopenharmony_ci	if (err) {
129862306a36Sopenharmony_ci		netdev_err(sdev->netdev, "failed to init status queue: %u\n",
129962306a36Sopenharmony_ci			   err);
130062306a36Sopenharmony_ci		goto free_txq;
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr));
130462306a36Sopenharmony_ci	napi_enable(&sdev->napi);
130562306a36Sopenharmony_ci	/* disable irq mitigation */
130662306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_INTAGG, 0);
130762306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISR, 0);
130862306a36Sopenharmony_ci	slic_flush_write(sdev);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	slic_set_mac_address(sdev);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	spin_lock_bh(&sdev->link_lock);
131362306a36Sopenharmony_ci	sdev->duplex = DUPLEX_UNKNOWN;
131462306a36Sopenharmony_ci	sdev->speed = SPEED_UNKNOWN;
131562306a36Sopenharmony_ci	spin_unlock_bh(&sdev->link_lock);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	slic_set_link_autoneg(sdev);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	err = request_irq(sdev->pdev->irq, slic_irq, IRQF_SHARED, DRV_NAME,
132062306a36Sopenharmony_ci			  sdev);
132162306a36Sopenharmony_ci	if (err) {
132262306a36Sopenharmony_ci		netdev_err(sdev->netdev, "failed to request irq: %u\n", err);
132362306a36Sopenharmony_ci		goto disable_napi;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_ON);
132762306a36Sopenharmony_ci	slic_flush_write(sdev);
132862306a36Sopenharmony_ci	/* request initial link status */
132962306a36Sopenharmony_ci	err = slic_handle_link_change(sdev);
133062306a36Sopenharmony_ci	if (err)
133162306a36Sopenharmony_ci		netdev_warn(sdev->netdev,
133262306a36Sopenharmony_ci			    "failed to set initial link state: %u\n", err);
133362306a36Sopenharmony_ci	return 0;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cidisable_napi:
133662306a36Sopenharmony_ci	napi_disable(&sdev->napi);
133762306a36Sopenharmony_ci	slic_free_stat_queue(sdev);
133862306a36Sopenharmony_cifree_txq:
133962306a36Sopenharmony_ci	slic_free_tx_queue(sdev);
134062306a36Sopenharmony_cifree_rxq:
134162306a36Sopenharmony_ci	slic_free_rx_queue(sdev);
134262306a36Sopenharmony_cifree_sm:
134362306a36Sopenharmony_ci	slic_free_shmem(sdev);
134462306a36Sopenharmony_ci	slic_card_reset(sdev);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	return err;
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic int slic_open(struct net_device *dev)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
135262306a36Sopenharmony_ci	int err;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	netif_carrier_off(dev);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	err = slic_init_iface(sdev);
135762306a36Sopenharmony_ci	if (err) {
135862306a36Sopenharmony_ci		netdev_err(dev, "failed to initialize interface: %i\n", err);
135962306a36Sopenharmony_ci		return err;
136062306a36Sopenharmony_ci	}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	netif_start_queue(dev);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	return 0;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic int slic_close(struct net_device *dev)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
137062306a36Sopenharmony_ci	u32 val;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	netif_stop_queue(dev);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	/* stop irq handling */
137562306a36Sopenharmony_ci	napi_disable(&sdev->napi);
137662306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF);
137762306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISR, 0);
137862306a36Sopenharmony_ci	slic_flush_write(sdev);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	free_irq(sdev->pdev->irq, sdev);
138162306a36Sopenharmony_ci	/* turn off RCV and XMT and power down PHY */
138262306a36Sopenharmony_ci	val = SLIC_GXCR_RESET | SLIC_GXCR_PAUSEEN;
138362306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WXCFG, val);
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	val = SLIC_GRCR_RESET | SLIC_GRCR_CTLEN | SLIC_GRCR_ADDRAEN |
138662306a36Sopenharmony_ci	      SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT;
138762306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WRCFG, val);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	val = MII_BMCR << 16 | BMCR_PDOWN;
139062306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_WPHY, val);
139162306a36Sopenharmony_ci	slic_flush_write(sdev);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	slic_clear_upr_list(&sdev->upr_list);
139462306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_QUIESCE, 0);
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	slic_free_stat_queue(sdev);
139762306a36Sopenharmony_ci	slic_free_tx_queue(sdev);
139862306a36Sopenharmony_ci	slic_free_rx_queue(sdev);
139962306a36Sopenharmony_ci	slic_free_shmem(sdev);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	slic_card_reset(sdev);
140262306a36Sopenharmony_ci	netif_carrier_off(dev);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	return 0;
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_cistatic netdev_tx_t slic_xmit(struct sk_buff *skb, struct net_device *dev)
140862306a36Sopenharmony_ci{
140962306a36Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
141062306a36Sopenharmony_ci	struct slic_tx_queue *txq = &sdev->txq;
141162306a36Sopenharmony_ci	struct slic_tx_buffer *buff;
141262306a36Sopenharmony_ci	struct slic_tx_desc *desc;
141362306a36Sopenharmony_ci	dma_addr_t paddr;
141462306a36Sopenharmony_ci	u32 cbar_val;
141562306a36Sopenharmony_ci	u32 maplen;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	if (unlikely(slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS)) {
141862306a36Sopenharmony_ci		netdev_err(dev, "BUG! not enough tx LEs left: %u\n",
141962306a36Sopenharmony_ci			   slic_get_free_tx_descs(txq));
142062306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
142162306a36Sopenharmony_ci	}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	maplen = skb_headlen(skb);
142462306a36Sopenharmony_ci	paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen,
142562306a36Sopenharmony_ci			       DMA_TO_DEVICE);
142662306a36Sopenharmony_ci	if (dma_mapping_error(&sdev->pdev->dev, paddr)) {
142762306a36Sopenharmony_ci		netdev_err(dev, "failed to map tx buffer\n");
142862306a36Sopenharmony_ci		goto drop_skb;
142962306a36Sopenharmony_ci	}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	buff = &txq->txbuffs[txq->put_idx];
143262306a36Sopenharmony_ci	buff->skb = skb;
143362306a36Sopenharmony_ci	dma_unmap_addr_set(buff, map_addr, paddr);
143462306a36Sopenharmony_ci	dma_unmap_len_set(buff, map_len, maplen);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	desc = buff->desc;
143762306a36Sopenharmony_ci	desc->totlen = cpu_to_le32(maplen);
143862306a36Sopenharmony_ci	desc->paddrl = cpu_to_le32(lower_32_bits(paddr));
143962306a36Sopenharmony_ci	desc->paddrh = cpu_to_le32(upper_32_bits(paddr));
144062306a36Sopenharmony_ci	desc->len = cpu_to_le32(maplen);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	txq->put_idx = slic_next_queue_idx(txq->put_idx, txq->len);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	cbar_val = lower_32_bits(buff->desc_paddr) | 1;
144562306a36Sopenharmony_ci	/* complete writes to RAM and DMA before hardware is informed */
144662306a36Sopenharmony_ci	wmb();
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_CBAR, cbar_val);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	if (slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS)
145162306a36Sopenharmony_ci		netif_stop_queue(dev);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	return NETDEV_TX_OK;
145462306a36Sopenharmony_cidrop_skb:
145562306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	return NETDEV_TX_OK;
145862306a36Sopenharmony_ci}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_cistatic void slic_get_stats(struct net_device *dev,
146162306a36Sopenharmony_ci			   struct rtnl_link_stats64 *lst)
146262306a36Sopenharmony_ci{
146362306a36Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
146462306a36Sopenharmony_ci	struct slic_stats *stats = &sdev->stats;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_packets, stats, rx_packets);
146762306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->tx_packets, stats, tx_packets);
146862306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_bytes, stats, rx_bytes);
146962306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->tx_bytes, stats, tx_bytes);
147062306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_errors, stats, rx_errors);
147162306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_dropped, stats, rx_buff_miss);
147262306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->tx_dropped, stats, tx_dropped);
147362306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->multicast, stats, rx_mcasts);
147462306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_over_errors, stats, rx_buffoflow);
147562306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_crc_errors, stats, rx_crc);
147662306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->rx_fifo_errors, stats, rx_oflow802);
147762306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(lst->tx_carrier_errors, stats, tx_carrier);
147862306a36Sopenharmony_ci}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_cistatic int slic_get_sset_count(struct net_device *dev, int sset)
148162306a36Sopenharmony_ci{
148262306a36Sopenharmony_ci	switch (sset) {
148362306a36Sopenharmony_ci	case ETH_SS_STATS:
148462306a36Sopenharmony_ci		return ARRAY_SIZE(slic_stats_strings);
148562306a36Sopenharmony_ci	default:
148662306a36Sopenharmony_ci		return -EOPNOTSUPP;
148762306a36Sopenharmony_ci	}
148862306a36Sopenharmony_ci}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_cistatic void slic_get_ethtool_stats(struct net_device *dev,
149162306a36Sopenharmony_ci				   struct ethtool_stats *eth_stats, u64 *data)
149262306a36Sopenharmony_ci{
149362306a36Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
149462306a36Sopenharmony_ci	struct slic_stats *stats = &sdev->stats;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[0], stats, rx_packets);
149762306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[1], stats, rx_bytes);
149862306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[2], stats, rx_mcasts);
149962306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[3], stats, rx_errors);
150062306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[4], stats, rx_buff_miss);
150162306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[5], stats, rx_tpcsum);
150262306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[6], stats, rx_tpoflow);
150362306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[7], stats, rx_tphlen);
150462306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[8], stats, rx_ipcsum);
150562306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[9], stats, rx_iplen);
150662306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[10], stats, rx_iphlen);
150762306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[11], stats, rx_early);
150862306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[12], stats, rx_buffoflow);
150962306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[13], stats, rx_lcode);
151062306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[14], stats, rx_drbl);
151162306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[15], stats, rx_crc);
151262306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[16], stats, rx_oflow802);
151362306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[17], stats, rx_uflow802);
151462306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[18], stats, tx_packets);
151562306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[19], stats, tx_bytes);
151662306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[20], stats, tx_carrier);
151762306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[21], stats, tx_dropped);
151862306a36Sopenharmony_ci	SLIC_GET_STATS_COUNTER(data[22], stats, irq_errs);
151962306a36Sopenharmony_ci}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_cistatic void slic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	if (stringset == ETH_SS_STATS)
152462306a36Sopenharmony_ci		memcpy(data, slic_stats_strings, sizeof(slic_stats_strings));
152562306a36Sopenharmony_ci}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_cistatic void slic_get_drvinfo(struct net_device *dev,
152862306a36Sopenharmony_ci			     struct ethtool_drvinfo *info)
152962306a36Sopenharmony_ci{
153062306a36Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
153362306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(sdev->pdev), sizeof(info->bus_info));
153462306a36Sopenharmony_ci}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_cistatic const struct ethtool_ops slic_ethtool_ops = {
153762306a36Sopenharmony_ci	.get_drvinfo		= slic_get_drvinfo,
153862306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
153962306a36Sopenharmony_ci	.get_strings		= slic_get_strings,
154062306a36Sopenharmony_ci	.get_ethtool_stats	= slic_get_ethtool_stats,
154162306a36Sopenharmony_ci	.get_sset_count		= slic_get_sset_count,
154262306a36Sopenharmony_ci};
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_cistatic const struct net_device_ops slic_netdev_ops = {
154562306a36Sopenharmony_ci	.ndo_open		= slic_open,
154662306a36Sopenharmony_ci	.ndo_stop		= slic_close,
154762306a36Sopenharmony_ci	.ndo_start_xmit		= slic_xmit,
154862306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
154962306a36Sopenharmony_ci	.ndo_get_stats64	= slic_get_stats,
155062306a36Sopenharmony_ci	.ndo_set_rx_mode	= slic_set_rx_mode,
155162306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
155262306a36Sopenharmony_ci};
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_cistatic u16 slic_eeprom_csum(unsigned char *eeprom, unsigned int len)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	unsigned char *ptr = eeprom;
155762306a36Sopenharmony_ci	u32 csum = 0;
155862306a36Sopenharmony_ci	__le16 data;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	while (len > 1) {
156162306a36Sopenharmony_ci		memcpy(&data, ptr, sizeof(data));
156262306a36Sopenharmony_ci		csum += le16_to_cpu(data);
156362306a36Sopenharmony_ci		ptr += 2;
156462306a36Sopenharmony_ci		len -= 2;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci	if (len > 0)
156762306a36Sopenharmony_ci		csum += *(u8 *)ptr;
156862306a36Sopenharmony_ci	while (csum >> 16)
156962306a36Sopenharmony_ci		csum = (csum & 0xFFFF) + ((csum >> 16) & 0xFFFF);
157062306a36Sopenharmony_ci	return ~csum;
157162306a36Sopenharmony_ci}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci/* check eeprom size, magic and checksum */
157462306a36Sopenharmony_cistatic bool slic_eeprom_valid(unsigned char *eeprom, unsigned int size)
157562306a36Sopenharmony_ci{
157662306a36Sopenharmony_ci	const unsigned int MAX_SIZE = 128;
157762306a36Sopenharmony_ci	const unsigned int MIN_SIZE = 98;
157862306a36Sopenharmony_ci	__le16 magic;
157962306a36Sopenharmony_ci	__le16 csum;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	if (size < MIN_SIZE || size > MAX_SIZE)
158262306a36Sopenharmony_ci		return false;
158362306a36Sopenharmony_ci	memcpy(&magic, eeprom, sizeof(magic));
158462306a36Sopenharmony_ci	if (le16_to_cpu(magic) != SLIC_EEPROM_MAGIC)
158562306a36Sopenharmony_ci		return false;
158662306a36Sopenharmony_ci	/* cut checksum bytes */
158762306a36Sopenharmony_ci	size -= 2;
158862306a36Sopenharmony_ci	memcpy(&csum, eeprom + size, sizeof(csum));
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	return (le16_to_cpu(csum) == slic_eeprom_csum(eeprom, size));
159162306a36Sopenharmony_ci}
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_cistatic int slic_read_eeprom(struct slic_device *sdev)
159462306a36Sopenharmony_ci{
159562306a36Sopenharmony_ci	unsigned int devfn = PCI_FUNC(sdev->pdev->devfn);
159662306a36Sopenharmony_ci	struct slic_shmem *sm = &sdev->shmem;
159762306a36Sopenharmony_ci	struct slic_shmem_data *sm_data = sm->shmem_data;
159862306a36Sopenharmony_ci	const unsigned int MAX_LOOPS = 5000;
159962306a36Sopenharmony_ci	unsigned int codesize;
160062306a36Sopenharmony_ci	unsigned char *eeprom;
160162306a36Sopenharmony_ci	struct slic_upr *upr;
160262306a36Sopenharmony_ci	unsigned int i = 0;
160362306a36Sopenharmony_ci	dma_addr_t paddr;
160462306a36Sopenharmony_ci	int err = 0;
160562306a36Sopenharmony_ci	u8 *mac[2];
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	eeprom = dma_alloc_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE,
160862306a36Sopenharmony_ci				    &paddr, GFP_KERNEL);
160962306a36Sopenharmony_ci	if (!eeprom)
161062306a36Sopenharmony_ci		return -ENOMEM;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF);
161362306a36Sopenharmony_ci	/* setup ISP temporarily */
161462306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr));
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	err = slic_new_upr(sdev, SLIC_UPR_CONFIG, paddr);
161762306a36Sopenharmony_ci	if (!err) {
161862306a36Sopenharmony_ci		for (i = 0; i < MAX_LOOPS; i++) {
161962306a36Sopenharmony_ci			if (le32_to_cpu(sm_data->isr) & SLIC_ISR_UPC)
162062306a36Sopenharmony_ci				break;
162162306a36Sopenharmony_ci			mdelay(1);
162262306a36Sopenharmony_ci		}
162362306a36Sopenharmony_ci		if (i == MAX_LOOPS) {
162462306a36Sopenharmony_ci			dev_err(&sdev->pdev->dev,
162562306a36Sopenharmony_ci				"timed out while waiting for eeprom data\n");
162662306a36Sopenharmony_ci			err = -ETIMEDOUT;
162762306a36Sopenharmony_ci		}
162862306a36Sopenharmony_ci		upr = slic_dequeue_upr(sdev);
162962306a36Sopenharmony_ci		kfree(upr);
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISP, 0);
163362306a36Sopenharmony_ci	slic_write(sdev, SLIC_REG_ISR, 0);
163462306a36Sopenharmony_ci	slic_flush_write(sdev);
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	if (err)
163762306a36Sopenharmony_ci		goto free_eeprom;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	if (sdev->model == SLIC_MODEL_OASIS) {
164062306a36Sopenharmony_ci		struct slic_oasis_eeprom *oee;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci		oee = (struct slic_oasis_eeprom *)eeprom;
164362306a36Sopenharmony_ci		mac[0] = oee->mac;
164462306a36Sopenharmony_ci		mac[1] = oee->mac2;
164562306a36Sopenharmony_ci		codesize = le16_to_cpu(oee->eeprom_code_size);
164662306a36Sopenharmony_ci	} else {
164762306a36Sopenharmony_ci		struct slic_mojave_eeprom *mee;
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci		mee = (struct slic_mojave_eeprom *)eeprom;
165062306a36Sopenharmony_ci		mac[0] = mee->mac;
165162306a36Sopenharmony_ci		mac[1] = mee->mac2;
165262306a36Sopenharmony_ci		codesize = le16_to_cpu(mee->eeprom_code_size);
165362306a36Sopenharmony_ci	}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	if (!slic_eeprom_valid(eeprom, codesize)) {
165662306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev, "invalid checksum in eeprom\n");
165762306a36Sopenharmony_ci		err = -EINVAL;
165862306a36Sopenharmony_ci		goto free_eeprom;
165962306a36Sopenharmony_ci	}
166062306a36Sopenharmony_ci	/* set mac address */
166162306a36Sopenharmony_ci	eth_hw_addr_set(sdev->netdev, mac[devfn]);
166262306a36Sopenharmony_cifree_eeprom:
166362306a36Sopenharmony_ci	dma_free_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE, eeprom, paddr);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	return err;
166662306a36Sopenharmony_ci}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_cistatic int slic_init(struct slic_device *sdev)
166962306a36Sopenharmony_ci{
167062306a36Sopenharmony_ci	int err;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	spin_lock_init(&sdev->upper_lock);
167362306a36Sopenharmony_ci	spin_lock_init(&sdev->link_lock);
167462306a36Sopenharmony_ci	INIT_LIST_HEAD(&sdev->upr_list.list);
167562306a36Sopenharmony_ci	spin_lock_init(&sdev->upr_list.lock);
167662306a36Sopenharmony_ci	u64_stats_init(&sdev->stats.syncp);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	slic_card_reset(sdev);
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	err = slic_load_firmware(sdev);
168162306a36Sopenharmony_ci	if (err) {
168262306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to load firmware\n");
168362306a36Sopenharmony_ci		return err;
168462306a36Sopenharmony_ci	}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	/* we need the shared memory to read EEPROM so set it up temporarily */
168762306a36Sopenharmony_ci	err = slic_init_shmem(sdev);
168862306a36Sopenharmony_ci	if (err) {
168962306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to init shared memory\n");
169062306a36Sopenharmony_ci		return err;
169162306a36Sopenharmony_ci	}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	err = slic_read_eeprom(sdev);
169462306a36Sopenharmony_ci	if (err) {
169562306a36Sopenharmony_ci		dev_err(&sdev->pdev->dev, "failed to read eeprom\n");
169662306a36Sopenharmony_ci		goto free_sm;
169762306a36Sopenharmony_ci	}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	slic_card_reset(sdev);
170062306a36Sopenharmony_ci	slic_free_shmem(sdev);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	return 0;
170362306a36Sopenharmony_cifree_sm:
170462306a36Sopenharmony_ci	slic_free_shmem(sdev);
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	return err;
170762306a36Sopenharmony_ci}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_cistatic bool slic_is_fiber(unsigned short subdev)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	switch (subdev) {
171262306a36Sopenharmony_ci	/* Mojave */
171362306a36Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_1000X1F:
171462306a36Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SES1001F: fallthrough;
171562306a36Sopenharmony_ci	/* Oasis */
171662306a36Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2002XF:
171762306a36Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2001XF:
171862306a36Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2104EF:
171962306a36Sopenharmony_ci	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2102EF:
172062306a36Sopenharmony_ci		return true;
172162306a36Sopenharmony_ci	}
172262306a36Sopenharmony_ci	return false;
172362306a36Sopenharmony_ci}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_cistatic void slic_configure_pci(struct pci_dev *pdev)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	u16 old;
172862306a36Sopenharmony_ci	u16 cmd;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	pci_read_config_word(pdev, PCI_COMMAND, &old);
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	cmd = old | PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
173362306a36Sopenharmony_ci	if (old != cmd)
173462306a36Sopenharmony_ci		pci_write_config_word(pdev, PCI_COMMAND, cmd);
173562306a36Sopenharmony_ci}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_cistatic int slic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	struct slic_device *sdev;
174062306a36Sopenharmony_ci	struct net_device *dev;
174162306a36Sopenharmony_ci	int err;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	err = pci_enable_device(pdev);
174462306a36Sopenharmony_ci	if (err) {
174562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable PCI device\n");
174662306a36Sopenharmony_ci		return err;
174762306a36Sopenharmony_ci	}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	pci_set_master(pdev);
175062306a36Sopenharmony_ci	pci_try_set_mwi(pdev);
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	slic_configure_pci(pdev);
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
175562306a36Sopenharmony_ci	if (err) {
175662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to setup DMA\n");
175762306a36Sopenharmony_ci		goto disable;
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
176362306a36Sopenharmony_ci	if (err) {
176462306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to obtain PCI regions\n");
176562306a36Sopenharmony_ci		goto disable;
176662306a36Sopenharmony_ci	}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(*sdev));
176962306a36Sopenharmony_ci	if (!dev) {
177062306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to alloc ethernet device\n");
177162306a36Sopenharmony_ci		err = -ENOMEM;
177262306a36Sopenharmony_ci		goto free_regions;
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
177662306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
177762306a36Sopenharmony_ci	dev->irq = pdev->irq;
177862306a36Sopenharmony_ci	dev->netdev_ops = &slic_netdev_ops;
177962306a36Sopenharmony_ci	dev->hw_features = NETIF_F_RXCSUM;
178062306a36Sopenharmony_ci	dev->features |= dev->hw_features;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	dev->ethtool_ops = &slic_ethtool_ops;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	sdev = netdev_priv(dev);
178562306a36Sopenharmony_ci	sdev->model = (pdev->device == PCI_DEVICE_ID_ALACRITECH_OASIS) ?
178662306a36Sopenharmony_ci		      SLIC_MODEL_OASIS : SLIC_MODEL_MOJAVE;
178762306a36Sopenharmony_ci	sdev->is_fiber = slic_is_fiber(pdev->subsystem_device);
178862306a36Sopenharmony_ci	sdev->pdev = pdev;
178962306a36Sopenharmony_ci	sdev->netdev = dev;
179062306a36Sopenharmony_ci	sdev->regs = ioremap(pci_resource_start(pdev, 0),
179162306a36Sopenharmony_ci				     pci_resource_len(pdev, 0));
179262306a36Sopenharmony_ci	if (!sdev->regs) {
179362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to map registers\n");
179462306a36Sopenharmony_ci		err = -ENOMEM;
179562306a36Sopenharmony_ci		goto free_netdev;
179662306a36Sopenharmony_ci	}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	err = slic_init(sdev);
179962306a36Sopenharmony_ci	if (err) {
180062306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to initialize driver\n");
180162306a36Sopenharmony_ci		goto unmap;
180262306a36Sopenharmony_ci	}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	netif_napi_add(dev, &sdev->napi, slic_poll);
180562306a36Sopenharmony_ci	netif_carrier_off(dev);
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	err = register_netdev(dev);
180862306a36Sopenharmony_ci	if (err) {
180962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register net device: %i\n", err);
181062306a36Sopenharmony_ci		goto unmap;
181162306a36Sopenharmony_ci	}
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	return 0;
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ciunmap:
181662306a36Sopenharmony_ci	iounmap(sdev->regs);
181762306a36Sopenharmony_cifree_netdev:
181862306a36Sopenharmony_ci	free_netdev(dev);
181962306a36Sopenharmony_cifree_regions:
182062306a36Sopenharmony_ci	pci_release_regions(pdev);
182162306a36Sopenharmony_cidisable:
182262306a36Sopenharmony_ci	pci_disable_device(pdev);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	return err;
182562306a36Sopenharmony_ci}
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_cistatic void slic_remove(struct pci_dev *pdev)
182862306a36Sopenharmony_ci{
182962306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
183062306a36Sopenharmony_ci	struct slic_device *sdev = netdev_priv(dev);
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	unregister_netdev(dev);
183362306a36Sopenharmony_ci	iounmap(sdev->regs);
183462306a36Sopenharmony_ci	free_netdev(dev);
183562306a36Sopenharmony_ci	pci_release_regions(pdev);
183662306a36Sopenharmony_ci	pci_disable_device(pdev);
183762306a36Sopenharmony_ci}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_cistatic struct pci_driver slic_driver = {
184062306a36Sopenharmony_ci	.name = DRV_NAME,
184162306a36Sopenharmony_ci	.id_table = slic_id_tbl,
184262306a36Sopenharmony_ci	.probe = slic_probe,
184362306a36Sopenharmony_ci	.remove = slic_remove,
184462306a36Sopenharmony_ci};
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cimodule_pci_driver(slic_driver);
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ciMODULE_DESCRIPTION("Alacritech non-accelerated SLIC driver");
184962306a36Sopenharmony_ciMODULE_AUTHOR("Lino Sanfilippo <LinoSanfilippo@gmx.de>");
185062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1851