162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2010 ASIX Electronics Corporation
462306a36Sopenharmony_ci * Copyright (c) 2020 Samsung Electronics Co., Ltd.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * ASIX AX88796C SPI Fast Ethernet Linux driver
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt)	"ax88796c: " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "ax88796c_main.h"
1262306a36Sopenharmony_ci#include "ax88796c_ioctl.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/bitmap.h>
1562306a36Sopenharmony_ci#include <linux/etherdevice.h>
1662306a36Sopenharmony_ci#include <linux/iopoll.h>
1762306a36Sopenharmony_ci#include <linux/lockdep.h>
1862306a36Sopenharmony_ci#include <linux/mdio.h>
1962306a36Sopenharmony_ci#include <linux/minmax.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/netdevice.h>
2262306a36Sopenharmony_ci#include <linux/of.h>
2362306a36Sopenharmony_ci#include <linux/phy.h>
2462306a36Sopenharmony_ci#include <linux/skbuff.h>
2562306a36Sopenharmony_ci#include <linux/spi/spi.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int comp = IS_ENABLED(CONFIG_SPI_AX88796C_COMPRESSION);
2862306a36Sopenharmony_cistatic int msg_enable = NETIF_MSG_PROBE |
2962306a36Sopenharmony_ci			NETIF_MSG_LINK |
3062306a36Sopenharmony_ci			NETIF_MSG_RX_ERR |
3162306a36Sopenharmony_ci			NETIF_MSG_TX_ERR;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const char *no_regs_list = "80018001,e1918001,8001a001,fc0d0000";
3462306a36Sopenharmony_ciunsigned long ax88796c_no_regs_mask[AX88796C_REGDUMP_LEN / (sizeof(unsigned long) * 8)];
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cimodule_param(msg_enable, int, 0444);
3762306a36Sopenharmony_ciMODULE_PARM_DESC(msg_enable, "Message mask (see linux/netdevice.h for bitmap)");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int ax88796c_soft_reset(struct ax88796c_device *ax_local)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	u16 temp;
4262306a36Sopenharmony_ci	int ret;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, PSR_RESET, P0_PSR);
4762306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, PSR_RESET_CLR, P0_PSR);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	ret = read_poll_timeout(AX_READ, ret,
5062306a36Sopenharmony_ci				(ret & PSR_DEV_READY),
5162306a36Sopenharmony_ci				0, jiffies_to_usecs(160 * HZ / 1000), false,
5262306a36Sopenharmony_ci				&ax_local->ax_spi, P0_PSR);
5362306a36Sopenharmony_ci	if (ret)
5462306a36Sopenharmony_ci		return ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	temp = AX_READ(&ax_local->ax_spi, P4_SPICR);
5762306a36Sopenharmony_ci	if (ax_local->priv_flags & AX_CAP_COMP) {
5862306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi,
5962306a36Sopenharmony_ci			 (temp | SPICR_RCEN | SPICR_QCEN), P4_SPICR);
6062306a36Sopenharmony_ci		ax_local->ax_spi.comp = 1;
6162306a36Sopenharmony_ci	} else {
6262306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi,
6362306a36Sopenharmony_ci			 (temp & ~(SPICR_RCEN | SPICR_QCEN)), P4_SPICR);
6462306a36Sopenharmony_ci		ax_local->ax_spi.comp = 0;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int ax88796c_reload_eeprom(struct ax88796c_device *ax_local)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	int ret;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, EECR_RELOAD, P3_EECR);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ret = read_poll_timeout(AX_READ, ret,
7962306a36Sopenharmony_ci				(ret & PSR_DEV_READY),
8062306a36Sopenharmony_ci				0, jiffies_to_usecs(2 * HZ / 1000), false,
8162306a36Sopenharmony_ci				&ax_local->ax_spi, P0_PSR);
8262306a36Sopenharmony_ci	if (ret) {
8362306a36Sopenharmony_ci		dev_err(&ax_local->spi->dev,
8462306a36Sopenharmony_ci			"timeout waiting for reload eeprom\n");
8562306a36Sopenharmony_ci		return ret;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return 0;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void ax88796c_set_hw_multicast(struct net_device *ndev)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
9462306a36Sopenharmony_ci	int mc_count = netdev_mc_count(ndev);
9562306a36Sopenharmony_ci	u16 rx_ctl = RXCR_AB;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	memset(ax_local->multi_filter, 0, AX_MCAST_FILTER_SIZE);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (ndev->flags & IFF_PROMISC) {
10262306a36Sopenharmony_ci		rx_ctl |= RXCR_PRO;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	} else if (ndev->flags & IFF_ALLMULTI || mc_count > AX_MAX_MCAST) {
10562306a36Sopenharmony_ci		rx_ctl |= RXCR_AMALL;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	} else if (mc_count == 0) {
10862306a36Sopenharmony_ci		/* just broadcast and directed */
10962306a36Sopenharmony_ci	} else {
11062306a36Sopenharmony_ci		u32 crc_bits;
11162306a36Sopenharmony_ci		int i;
11262306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, ndev) {
11562306a36Sopenharmony_ci			crc_bits = ether_crc(ETH_ALEN, ha->addr);
11662306a36Sopenharmony_ci			ax_local->multi_filter[crc_bits >> 29] |=
11762306a36Sopenharmony_ci						(1 << ((crc_bits >> 26) & 7));
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		for (i = 0; i < 4; i++) {
12162306a36Sopenharmony_ci			AX_WRITE(&ax_local->ax_spi,
12262306a36Sopenharmony_ci				 ((ax_local->multi_filter[i * 2 + 1] << 8) |
12362306a36Sopenharmony_ci				  ax_local->multi_filter[i * 2]), P3_MFAR(i));
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, rx_ctl, P2_RXCR);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void ax88796c_set_mac_addr(struct net_device *ndev)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, ((u16)(ndev->dev_addr[4] << 8) |
13762306a36Sopenharmony_ci			(u16)ndev->dev_addr[5]), P3_MACASR0);
13862306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, ((u16)(ndev->dev_addr[2] << 8) |
13962306a36Sopenharmony_ci			(u16)ndev->dev_addr[3]), P3_MACASR1);
14062306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, ((u16)(ndev->dev_addr[0] << 8) |
14162306a36Sopenharmony_ci			(u16)ndev->dev_addr[1]), P3_MACASR2);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void ax88796c_load_mac_addr(struct net_device *ndev)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
14762306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
14862306a36Sopenharmony_ci	u16 temp;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* Try the device tree first */
15362306a36Sopenharmony_ci	if (!platform_get_ethdev_address(&ax_local->spi->dev, ndev) &&
15462306a36Sopenharmony_ci	    is_valid_ether_addr(ndev->dev_addr)) {
15562306a36Sopenharmony_ci		if (netif_msg_probe(ax_local))
15662306a36Sopenharmony_ci			dev_info(&ax_local->spi->dev,
15762306a36Sopenharmony_ci				 "MAC address read from device tree\n");
15862306a36Sopenharmony_ci		return;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Read the MAC address from AX88796C */
16262306a36Sopenharmony_ci	temp = AX_READ(&ax_local->ax_spi, P3_MACASR0);
16362306a36Sopenharmony_ci	addr[5] = (u8)temp;
16462306a36Sopenharmony_ci	addr[4] = (u8)(temp >> 8);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	temp = AX_READ(&ax_local->ax_spi, P3_MACASR1);
16762306a36Sopenharmony_ci	addr[3] = (u8)temp;
16862306a36Sopenharmony_ci	addr[2] = (u8)(temp >> 8);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	temp = AX_READ(&ax_local->ax_spi, P3_MACASR2);
17162306a36Sopenharmony_ci	addr[1] = (u8)temp;
17262306a36Sopenharmony_ci	addr[0] = (u8)(temp >> 8);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (is_valid_ether_addr(addr)) {
17562306a36Sopenharmony_ci		eth_hw_addr_set(ndev, addr);
17662306a36Sopenharmony_ci		if (netif_msg_probe(ax_local))
17762306a36Sopenharmony_ci			dev_info(&ax_local->spi->dev,
17862306a36Sopenharmony_ci				 "MAC address read from ASIX chip\n");
17962306a36Sopenharmony_ci		return;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Use random address if none found */
18362306a36Sopenharmony_ci	if (netif_msg_probe(ax_local))
18462306a36Sopenharmony_ci		dev_info(&ax_local->spi->dev, "Use random MAC address\n");
18562306a36Sopenharmony_ci	eth_hw_addr_random(ndev);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void ax88796c_proc_tx_hdr(struct tx_pkt_info *info, u8 ip_summed)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	u16 pkt_len_bar = (~info->pkt_len & TX_HDR_SOP_PKTLENBAR);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* Prepare SOP header */
19362306a36Sopenharmony_ci	info->sop.flags_len = info->pkt_len |
19462306a36Sopenharmony_ci		((ip_summed == CHECKSUM_NONE) ||
19562306a36Sopenharmony_ci		 (ip_summed == CHECKSUM_UNNECESSARY) ? TX_HDR_SOP_DICF : 0);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	info->sop.seq_lenbar = ((info->seq_num << 11) & TX_HDR_SOP_SEQNUM)
19862306a36Sopenharmony_ci				| pkt_len_bar;
19962306a36Sopenharmony_ci	cpu_to_be16s(&info->sop.flags_len);
20062306a36Sopenharmony_ci	cpu_to_be16s(&info->sop.seq_lenbar);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Prepare Segment header */
20362306a36Sopenharmony_ci	info->seg.flags_seqnum_seglen = TX_HDR_SEG_FS | TX_HDR_SEG_LS
20462306a36Sopenharmony_ci						| info->pkt_len;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	info->seg.eo_so_seglenbar = pkt_len_bar;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	cpu_to_be16s(&info->seg.flags_seqnum_seglen);
20962306a36Sopenharmony_ci	cpu_to_be16s(&info->seg.eo_so_seglenbar);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* Prepare EOP header */
21262306a36Sopenharmony_ci	info->eop.seq_len = ((info->seq_num << 11) &
21362306a36Sopenharmony_ci			     TX_HDR_EOP_SEQNUM) | info->pkt_len;
21462306a36Sopenharmony_ci	info->eop.seqbar_lenbar = ((~info->seq_num << 11) &
21562306a36Sopenharmony_ci				   TX_HDR_EOP_SEQNUMBAR) | pkt_len_bar;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	cpu_to_be16s(&info->eop.seq_len);
21862306a36Sopenharmony_ci	cpu_to_be16s(&info->eop.seqbar_lenbar);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int
22262306a36Sopenharmony_ciax88796c_check_free_pages(struct ax88796c_device *ax_local, u8 need_pages)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	u8 free_pages;
22562306a36Sopenharmony_ci	u16 tmp;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	free_pages = AX_READ(&ax_local->ax_spi, P0_TFBFCR) & TX_FREEBUF_MASK;
23062306a36Sopenharmony_ci	if (free_pages < need_pages) {
23162306a36Sopenharmony_ci		/* schedule free page interrupt */
23262306a36Sopenharmony_ci		tmp = AX_READ(&ax_local->ax_spi, P0_TFBFCR)
23362306a36Sopenharmony_ci				& TFBFCR_SCHE_FREE_PAGE;
23462306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, tmp | TFBFCR_TX_PAGE_SET |
23562306a36Sopenharmony_ci				TFBFCR_SET_FREE_PAGE(need_pages),
23662306a36Sopenharmony_ci				P0_TFBFCR);
23762306a36Sopenharmony_ci		return -ENOMEM;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	return 0;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic struct sk_buff *
24462306a36Sopenharmony_ciax88796c_tx_fixup(struct net_device *ndev, struct sk_buff_head *q)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
24762306a36Sopenharmony_ci	u8 spi_len = ax_local->ax_spi.comp ? 1 : 4;
24862306a36Sopenharmony_ci	struct sk_buff *skb;
24962306a36Sopenharmony_ci	struct tx_pkt_info info;
25062306a36Sopenharmony_ci	struct skb_data *entry;
25162306a36Sopenharmony_ci	u16 pkt_len;
25262306a36Sopenharmony_ci	u8 padlen, seq_num;
25362306a36Sopenharmony_ci	u8 need_pages;
25462306a36Sopenharmony_ci	int headroom;
25562306a36Sopenharmony_ci	int tailroom;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (skb_queue_empty(q))
25862306a36Sopenharmony_ci		return NULL;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	skb = skb_peek(q);
26162306a36Sopenharmony_ci	pkt_len = skb->len;
26262306a36Sopenharmony_ci	need_pages = (pkt_len + TX_OVERHEAD + 127) >> 7;
26362306a36Sopenharmony_ci	if (ax88796c_check_free_pages(ax_local, need_pages) != 0)
26462306a36Sopenharmony_ci		return NULL;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	headroom = skb_headroom(skb);
26762306a36Sopenharmony_ci	tailroom = skb_tailroom(skb);
26862306a36Sopenharmony_ci	padlen = round_up(pkt_len, 4) - pkt_len;
26962306a36Sopenharmony_ci	seq_num = ++ax_local->seq_num & 0x1F;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	info.pkt_len = pkt_len;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (skb_cloned(skb) ||
27462306a36Sopenharmony_ci	    (headroom < (TX_OVERHEAD + spi_len)) ||
27562306a36Sopenharmony_ci	    (tailroom < (padlen + TX_EOP_SIZE))) {
27662306a36Sopenharmony_ci		size_t h = max((TX_OVERHEAD + spi_len) - headroom, 0);
27762306a36Sopenharmony_ci		size_t t = max((padlen + TX_EOP_SIZE) - tailroom, 0);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		if (pskb_expand_head(skb, h, t, GFP_KERNEL))
28062306a36Sopenharmony_ci			return NULL;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	info.seq_num = seq_num;
28462306a36Sopenharmony_ci	ax88796c_proc_tx_hdr(&info, skb->ip_summed);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* SOP and SEG header */
28762306a36Sopenharmony_ci	memcpy(skb_push(skb, TX_OVERHEAD), &info.sop, TX_OVERHEAD);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Write SPI TXQ header */
29062306a36Sopenharmony_ci	memcpy(skb_push(skb, spi_len), ax88796c_tx_cmd_buf, spi_len);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Make 32-bit alignment */
29362306a36Sopenharmony_ci	skb_put(skb, padlen);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* EOP header */
29662306a36Sopenharmony_ci	skb_put_data(skb, &info.eop, TX_EOP_SIZE);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	skb_unlink(skb, q);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	entry = (struct skb_data *)skb->cb;
30162306a36Sopenharmony_ci	memset(entry, 0, sizeof(*entry));
30262306a36Sopenharmony_ci	entry->len = pkt_len;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (netif_msg_pktdata(ax_local)) {
30562306a36Sopenharmony_ci		char pfx[IFNAMSIZ + 7];
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		snprintf(pfx, sizeof(pfx), "%s:     ", ndev->name);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		netdev_info(ndev, "TX packet len %d, total len %d, seq %d\n",
31062306a36Sopenharmony_ci			    pkt_len, skb->len, seq_num);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		netdev_info(ndev, "  SPI Header:\n");
31362306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
31462306a36Sopenharmony_ci			       skb->data, 4, 0);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		netdev_info(ndev, "  TX SOP:\n");
31762306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
31862306a36Sopenharmony_ci			       skb->data + 4, TX_OVERHEAD, 0);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		netdev_info(ndev, "  TX packet:\n");
32162306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
32262306a36Sopenharmony_ci			       skb->data + 4 + TX_OVERHEAD,
32362306a36Sopenharmony_ci			       skb->len - TX_EOP_SIZE - 4 - TX_OVERHEAD, 0);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		netdev_info(ndev, "  TX EOP:\n");
32662306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
32762306a36Sopenharmony_ci			       skb->data + skb->len - 4, 4, 0);
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return skb;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int ax88796c_hard_xmit(struct ax88796c_device *ax_local)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct ax88796c_pcpu_stats *stats;
33662306a36Sopenharmony_ci	struct sk_buff *tx_skb;
33762306a36Sopenharmony_ci	struct skb_data *entry;
33862306a36Sopenharmony_ci	unsigned long flags;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	stats = this_cpu_ptr(ax_local->stats);
34362306a36Sopenharmony_ci	tx_skb = ax88796c_tx_fixup(ax_local->ndev, &ax_local->tx_wait_q);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (!tx_skb) {
34662306a36Sopenharmony_ci		this_cpu_inc(ax_local->stats->tx_dropped);
34762306a36Sopenharmony_ci		return 0;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	entry = (struct skb_data *)tx_skb->cb;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi,
35262306a36Sopenharmony_ci		 (TSNR_TXB_START | TSNR_PKT_CNT(1)), P0_TSNR);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	axspi_write_txq(&ax_local->ax_spi, tx_skb->data, tx_skb->len);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (((AX_READ(&ax_local->ax_spi, P0_TSNR) & TXNR_TXB_IDLE) == 0) ||
35762306a36Sopenharmony_ci	    ((ISR_TXERR & AX_READ(&ax_local->ax_spi, P0_ISR)) != 0)) {
35862306a36Sopenharmony_ci		/* Ack tx error int */
35962306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, ISR_TXERR, P0_ISR);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		this_cpu_inc(ax_local->stats->tx_dropped);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		if (net_ratelimit())
36462306a36Sopenharmony_ci			netif_err(ax_local, tx_err, ax_local->ndev,
36562306a36Sopenharmony_ci				  "TX FIFO error, re-initialize the TX bridge\n");
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		/* Reinitial tx bridge */
36862306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, TXNR_TXB_REINIT |
36962306a36Sopenharmony_ci			AX_READ(&ax_local->ax_spi, P0_TSNR), P0_TSNR);
37062306a36Sopenharmony_ci		ax_local->seq_num = 0;
37162306a36Sopenharmony_ci	} else {
37262306a36Sopenharmony_ci		flags = u64_stats_update_begin_irqsave(&stats->syncp);
37362306a36Sopenharmony_ci		u64_stats_inc(&stats->tx_packets);
37462306a36Sopenharmony_ci		u64_stats_add(&stats->tx_bytes, entry->len);
37562306a36Sopenharmony_ci		u64_stats_update_end_irqrestore(&stats->syncp, flags);
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	entry->state = tx_done;
37962306a36Sopenharmony_ci	dev_kfree_skb(tx_skb);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return 1;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic netdev_tx_t
38562306a36Sopenharmony_ciax88796c_start_xmit(struct sk_buff *skb, struct net_device *ndev)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	skb_queue_tail(&ax_local->tx_wait_q, skb);
39062306a36Sopenharmony_ci	if (skb_queue_len(&ax_local->tx_wait_q) > TX_QUEUE_HIGH_WATER)
39162306a36Sopenharmony_ci		netif_stop_queue(ndev);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	set_bit(EVENT_TX, &ax_local->flags);
39462306a36Sopenharmony_ci	schedule_work(&ax_local->ax_work);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return NETDEV_TX_OK;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void
40062306a36Sopenharmony_ciax88796c_skb_return(struct ax88796c_device *ax_local,
40162306a36Sopenharmony_ci		    struct sk_buff *skb, struct rx_header *rxhdr)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct net_device *ndev = ax_local->ndev;
40462306a36Sopenharmony_ci	struct ax88796c_pcpu_stats *stats;
40562306a36Sopenharmony_ci	unsigned long flags;
40662306a36Sopenharmony_ci	int status;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	stats = this_cpu_ptr(ax_local->stats);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	do {
41162306a36Sopenharmony_ci		if (!(ndev->features & NETIF_F_RXCSUM))
41262306a36Sopenharmony_ci			break;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		/* checksum error bit is set */
41562306a36Sopenharmony_ci		if ((rxhdr->flags & RX_HDR3_L3_ERR) ||
41662306a36Sopenharmony_ci		    (rxhdr->flags & RX_HDR3_L4_ERR))
41762306a36Sopenharmony_ci			break;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		/* Other types may be indicated by more than one bit. */
42062306a36Sopenharmony_ci		if ((rxhdr->flags & RX_HDR3_L4_TYPE_TCP) ||
42162306a36Sopenharmony_ci		    (rxhdr->flags & RX_HDR3_L4_TYPE_UDP))
42262306a36Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
42362306a36Sopenharmony_ci	} while (0);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	flags = u64_stats_update_begin_irqsave(&stats->syncp);
42662306a36Sopenharmony_ci	u64_stats_inc(&stats->rx_packets);
42762306a36Sopenharmony_ci	u64_stats_add(&stats->rx_bytes, skb->len);
42862306a36Sopenharmony_ci	u64_stats_update_end_irqrestore(&stats->syncp, flags);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	skb->dev = ndev;
43162306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, ax_local->ndev);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	netif_info(ax_local, rx_status, ndev, "< rx, len %zu, type 0x%x\n",
43462306a36Sopenharmony_ci		   skb->len + sizeof(struct ethhdr), skb->protocol);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	status = netif_rx(skb);
43762306a36Sopenharmony_ci	if (status != NET_RX_SUCCESS && net_ratelimit())
43862306a36Sopenharmony_ci		netif_info(ax_local, rx_err, ndev,
43962306a36Sopenharmony_ci			   "netif_rx status %d\n", status);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void
44362306a36Sopenharmony_ciax88796c_rx_fixup(struct ax88796c_device *ax_local, struct sk_buff *rx_skb)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct rx_header *rxhdr = (struct rx_header *)rx_skb->data;
44662306a36Sopenharmony_ci	struct net_device *ndev = ax_local->ndev;
44762306a36Sopenharmony_ci	u16 len;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	be16_to_cpus(&rxhdr->flags_len);
45062306a36Sopenharmony_ci	be16_to_cpus(&rxhdr->seq_lenbar);
45162306a36Sopenharmony_ci	be16_to_cpus(&rxhdr->flags);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if ((rxhdr->flags_len & RX_HDR1_PKT_LEN) !=
45462306a36Sopenharmony_ci			 (~rxhdr->seq_lenbar & 0x7FF)) {
45562306a36Sopenharmony_ci		netif_err(ax_local, rx_err, ndev, "Header error\n");
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		this_cpu_inc(ax_local->stats->rx_frame_errors);
45862306a36Sopenharmony_ci		kfree_skb(rx_skb);
45962306a36Sopenharmony_ci		return;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	if ((rxhdr->flags_len & RX_HDR1_MII_ERR) ||
46362306a36Sopenharmony_ci	    (rxhdr->flags_len & RX_HDR1_CRC_ERR)) {
46462306a36Sopenharmony_ci		netif_err(ax_local, rx_err, ndev, "CRC or MII error\n");
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		this_cpu_inc(ax_local->stats->rx_crc_errors);
46762306a36Sopenharmony_ci		kfree_skb(rx_skb);
46862306a36Sopenharmony_ci		return;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	len = rxhdr->flags_len & RX_HDR1_PKT_LEN;
47262306a36Sopenharmony_ci	if (netif_msg_pktdata(ax_local)) {
47362306a36Sopenharmony_ci		char pfx[IFNAMSIZ + 7];
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		snprintf(pfx, sizeof(pfx), "%s:     ", ndev->name);
47662306a36Sopenharmony_ci		netdev_info(ndev, "RX data, total len %d, packet len %d\n",
47762306a36Sopenharmony_ci			    rx_skb->len, len);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		netdev_info(ndev, "  Dump RX packet header:");
48062306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
48162306a36Sopenharmony_ci			       rx_skb->data, sizeof(*rxhdr), 0);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		netdev_info(ndev, "  Dump RX packet:");
48462306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
48562306a36Sopenharmony_ci			       rx_skb->data + sizeof(*rxhdr), len, 0);
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	skb_pull(rx_skb, sizeof(*rxhdr));
48962306a36Sopenharmony_ci	pskb_trim(rx_skb, len);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ax88796c_skb_return(ax_local, rx_skb, rxhdr);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic int ax88796c_receive(struct net_device *ndev)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
49762306a36Sopenharmony_ci	struct skb_data *entry;
49862306a36Sopenharmony_ci	u16 w_count, pkt_len;
49962306a36Sopenharmony_ci	struct sk_buff *skb;
50062306a36Sopenharmony_ci	u8 pkt_cnt;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* check rx packet and total word count */
50562306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, AX_READ(&ax_local->ax_spi, P0_RTWCR)
50662306a36Sopenharmony_ci		  | RTWCR_RX_LATCH, P0_RTWCR);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	pkt_cnt = AX_READ(&ax_local->ax_spi, P0_RXBCR2) & RXBCR2_PKT_MASK;
50962306a36Sopenharmony_ci	if (!pkt_cnt)
51062306a36Sopenharmony_ci		return 0;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	pkt_len = AX_READ(&ax_local->ax_spi, P0_RCPHR) & 0x7FF;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	w_count = round_up(pkt_len + 6, 4) >> 1;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	skb = netdev_alloc_skb(ndev, w_count * 2);
51762306a36Sopenharmony_ci	if (!skb) {
51862306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, RXBCR1_RXB_DISCARD, P0_RXBCR1);
51962306a36Sopenharmony_ci		this_cpu_inc(ax_local->stats->rx_dropped);
52062306a36Sopenharmony_ci		return 0;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci	entry = (struct skb_data *)skb->cb;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, RXBCR1_RXB_START | w_count, P0_RXBCR1);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	axspi_read_rxq(&ax_local->ax_spi,
52762306a36Sopenharmony_ci		       skb_put(skb, w_count * 2), skb->len);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/* Check if rx bridge is idle */
53062306a36Sopenharmony_ci	if ((AX_READ(&ax_local->ax_spi, P0_RXBCR2) & RXBCR2_RXB_IDLE) == 0) {
53162306a36Sopenharmony_ci		if (net_ratelimit())
53262306a36Sopenharmony_ci			netif_err(ax_local, rx_err, ndev,
53362306a36Sopenharmony_ci				  "Rx Bridge is not idle\n");
53462306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, RXBCR2_RXB_REINIT, P0_RXBCR2);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		entry->state = rx_err;
53762306a36Sopenharmony_ci	} else {
53862306a36Sopenharmony_ci		entry->state = rx_done;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, ISR_RXPKT, P0_ISR);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	ax88796c_rx_fixup(ax_local, skb);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return 1;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int ax88796c_process_isr(struct ax88796c_device *ax_local)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct net_device *ndev = ax_local->ndev;
55162306a36Sopenharmony_ci	int todo = 0;
55262306a36Sopenharmony_ci	u16 isr;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	isr = AX_READ(&ax_local->ax_spi, P0_ISR);
55762306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, isr, P0_ISR);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	netif_dbg(ax_local, intr, ndev, "  ISR 0x%04x\n", isr);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (isr & ISR_TXERR) {
56262306a36Sopenharmony_ci		netif_dbg(ax_local, intr, ndev, "  TXERR interrupt\n");
56362306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, TXNR_TXB_REINIT, P0_TSNR);
56462306a36Sopenharmony_ci		ax_local->seq_num = 0x1f;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (isr & ISR_TXPAGES) {
56862306a36Sopenharmony_ci		netif_dbg(ax_local, intr, ndev, "  TXPAGES interrupt\n");
56962306a36Sopenharmony_ci		set_bit(EVENT_TX, &ax_local->flags);
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (isr & ISR_LINK) {
57362306a36Sopenharmony_ci		netif_dbg(ax_local, intr, ndev, "  Link change interrupt\n");
57462306a36Sopenharmony_ci		phy_mac_interrupt(ax_local->ndev->phydev);
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (isr & ISR_RXPKT) {
57862306a36Sopenharmony_ci		netif_dbg(ax_local, intr, ndev, "  RX interrupt\n");
57962306a36Sopenharmony_ci		todo = ax88796c_receive(ax_local->ndev);
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	return todo;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic irqreturn_t ax88796c_interrupt(int irq, void *dev_instance)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct ax88796c_device *ax_local;
58862306a36Sopenharmony_ci	struct net_device *ndev;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	ndev = dev_instance;
59162306a36Sopenharmony_ci	if (!ndev) {
59262306a36Sopenharmony_ci		pr_err("irq %d for unknown device.\n", irq);
59362306a36Sopenharmony_ci		return IRQ_RETVAL(0);
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci	ax_local = to_ax88796c_device(ndev);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	disable_irq_nosync(irq);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	netif_dbg(ax_local, intr, ndev, "Interrupt occurred\n");
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	set_bit(EVENT_INTR, &ax_local->flags);
60262306a36Sopenharmony_ci	schedule_work(&ax_local->ax_work);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return IRQ_HANDLED;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic void ax88796c_work(struct work_struct *work)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct ax88796c_device *ax_local =
61062306a36Sopenharmony_ci			container_of(work, struct ax88796c_device, ax_work);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	mutex_lock(&ax_local->spi_lock);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (test_bit(EVENT_SET_MULTI, &ax_local->flags)) {
61562306a36Sopenharmony_ci		ax88796c_set_hw_multicast(ax_local->ndev);
61662306a36Sopenharmony_ci		clear_bit(EVENT_SET_MULTI, &ax_local->flags);
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (test_bit(EVENT_INTR, &ax_local->flags)) {
62062306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, IMR_MASKALL, P0_IMR);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		while (ax88796c_process_isr(ax_local))
62362306a36Sopenharmony_ci			/* nothing */;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		clear_bit(EVENT_INTR, &ax_local->flags);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, IMR_DEFAULT, P0_IMR);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		enable_irq(ax_local->ndev->irq);
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if (test_bit(EVENT_TX, &ax_local->flags)) {
63362306a36Sopenharmony_ci		while (skb_queue_len(&ax_local->tx_wait_q)) {
63462306a36Sopenharmony_ci			if (!ax88796c_hard_xmit(ax_local))
63562306a36Sopenharmony_ci				break;
63662306a36Sopenharmony_ci		}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		clear_bit(EVENT_TX, &ax_local->flags);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		if (netif_queue_stopped(ax_local->ndev) &&
64162306a36Sopenharmony_ci		    (skb_queue_len(&ax_local->tx_wait_q) < TX_QUEUE_LOW_WATER))
64262306a36Sopenharmony_ci			netif_wake_queue(ax_local->ndev);
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	mutex_unlock(&ax_local->spi_lock);
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic void ax88796c_get_stats64(struct net_device *ndev,
64962306a36Sopenharmony_ci				 struct rtnl_link_stats64 *stats)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
65262306a36Sopenharmony_ci	u32 rx_frame_errors = 0, rx_crc_errors = 0;
65362306a36Sopenharmony_ci	u32 rx_dropped = 0, tx_dropped = 0;
65462306a36Sopenharmony_ci	unsigned int start;
65562306a36Sopenharmony_ci	int cpu;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
65862306a36Sopenharmony_ci		struct ax88796c_pcpu_stats *s;
65962306a36Sopenharmony_ci		u64 rx_packets, rx_bytes;
66062306a36Sopenharmony_ci		u64 tx_packets, tx_bytes;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		s = per_cpu_ptr(ax_local->stats, cpu);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		do {
66562306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&s->syncp);
66662306a36Sopenharmony_ci			rx_packets = u64_stats_read(&s->rx_packets);
66762306a36Sopenharmony_ci			rx_bytes   = u64_stats_read(&s->rx_bytes);
66862306a36Sopenharmony_ci			tx_packets = u64_stats_read(&s->tx_packets);
66962306a36Sopenharmony_ci			tx_bytes   = u64_stats_read(&s->tx_bytes);
67062306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&s->syncp, start));
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		stats->rx_packets += rx_packets;
67362306a36Sopenharmony_ci		stats->rx_bytes   += rx_bytes;
67462306a36Sopenharmony_ci		stats->tx_packets += tx_packets;
67562306a36Sopenharmony_ci		stats->tx_bytes   += tx_bytes;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		rx_dropped      += s->rx_dropped;
67862306a36Sopenharmony_ci		tx_dropped      += s->tx_dropped;
67962306a36Sopenharmony_ci		rx_frame_errors += s->rx_frame_errors;
68062306a36Sopenharmony_ci		rx_crc_errors   += s->rx_crc_errors;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	stats->rx_dropped = rx_dropped;
68462306a36Sopenharmony_ci	stats->tx_dropped = tx_dropped;
68562306a36Sopenharmony_ci	stats->rx_frame_errors = rx_frame_errors;
68662306a36Sopenharmony_ci	stats->rx_crc_errors = rx_crc_errors;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic void ax88796c_set_mac(struct  ax88796c_device *ax_local)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	u16 maccr;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	maccr = (ax_local->link) ? MACCR_RXEN : 0;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	switch (ax_local->speed) {
69662306a36Sopenharmony_ci	case SPEED_100:
69762306a36Sopenharmony_ci		maccr |= MACCR_SPEED_100;
69862306a36Sopenharmony_ci		break;
69962306a36Sopenharmony_ci	case SPEED_10:
70062306a36Sopenharmony_ci	case SPEED_UNKNOWN:
70162306a36Sopenharmony_ci		break;
70262306a36Sopenharmony_ci	default:
70362306a36Sopenharmony_ci		return;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	switch (ax_local->duplex) {
70762306a36Sopenharmony_ci	case DUPLEX_FULL:
70862306a36Sopenharmony_ci		maccr |= MACCR_SPEED_100;
70962306a36Sopenharmony_ci		break;
71062306a36Sopenharmony_ci	case DUPLEX_HALF:
71162306a36Sopenharmony_ci	case DUPLEX_UNKNOWN:
71262306a36Sopenharmony_ci		break;
71362306a36Sopenharmony_ci	default:
71462306a36Sopenharmony_ci		return;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (ax_local->flowctrl & AX_FC_ANEG &&
71862306a36Sopenharmony_ci	    ax_local->phydev->autoneg) {
71962306a36Sopenharmony_ci		maccr |= ax_local->pause ? MACCR_RXFC_ENABLE : 0;
72062306a36Sopenharmony_ci		maccr |= !ax_local->pause != !ax_local->asym_pause ?
72162306a36Sopenharmony_ci			MACCR_TXFC_ENABLE : 0;
72262306a36Sopenharmony_ci	} else {
72362306a36Sopenharmony_ci		maccr |= (ax_local->flowctrl & AX_FC_RX) ? MACCR_RXFC_ENABLE : 0;
72462306a36Sopenharmony_ci		maccr |= (ax_local->flowctrl & AX_FC_TX) ? MACCR_TXFC_ENABLE : 0;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	mutex_lock(&ax_local->spi_lock);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	maccr |= AX_READ(&ax_local->ax_spi, P0_MACCR) &
73062306a36Sopenharmony_ci		~(MACCR_DUPLEX_FULL | MACCR_SPEED_100 |
73162306a36Sopenharmony_ci		  MACCR_TXFC_ENABLE | MACCR_RXFC_ENABLE);
73262306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, maccr, P0_MACCR);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	mutex_unlock(&ax_local->spi_lock);
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic void ax88796c_handle_link_change(struct net_device *ndev)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
74062306a36Sopenharmony_ci	struct phy_device *phydev = ndev->phydev;
74162306a36Sopenharmony_ci	bool update = false;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (phydev->link && (ax_local->speed != phydev->speed ||
74462306a36Sopenharmony_ci			     ax_local->duplex != phydev->duplex ||
74562306a36Sopenharmony_ci			     ax_local->pause != phydev->pause ||
74662306a36Sopenharmony_ci			     ax_local->asym_pause != phydev->asym_pause)) {
74762306a36Sopenharmony_ci		ax_local->speed = phydev->speed;
74862306a36Sopenharmony_ci		ax_local->duplex = phydev->duplex;
74962306a36Sopenharmony_ci		ax_local->pause = phydev->pause;
75062306a36Sopenharmony_ci		ax_local->asym_pause = phydev->asym_pause;
75162306a36Sopenharmony_ci		update = true;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (phydev->link != ax_local->link) {
75562306a36Sopenharmony_ci		if (!phydev->link) {
75662306a36Sopenharmony_ci			ax_local->speed = SPEED_UNKNOWN;
75762306a36Sopenharmony_ci			ax_local->duplex = DUPLEX_UNKNOWN;
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		ax_local->link = phydev->link;
76162306a36Sopenharmony_ci		update = true;
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if (update)
76562306a36Sopenharmony_ci		ax88796c_set_mac(ax_local);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (net_ratelimit())
76862306a36Sopenharmony_ci		phy_print_status(ndev->phydev);
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic void ax88796c_set_csums(struct ax88796c_device *ax_local)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	struct net_device *ndev = ax_local->ndev;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	lockdep_assert_held(&ax_local->spi_lock);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (ndev->features & NETIF_F_RXCSUM) {
77862306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, COERCR0_DEFAULT, P4_COERCR0);
77962306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, COERCR1_DEFAULT, P4_COERCR1);
78062306a36Sopenharmony_ci	} else {
78162306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, 0, P4_COERCR0);
78262306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, 0, P4_COERCR1);
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (ndev->features & NETIF_F_HW_CSUM) {
78662306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, COETCR0_DEFAULT, P4_COETCR0);
78762306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, COETCR1_TXPPPE, P4_COETCR1);
78862306a36Sopenharmony_ci	} else {
78962306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, 0, P4_COETCR0);
79062306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, 0, P4_COETCR1);
79162306a36Sopenharmony_ci	}
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic int
79562306a36Sopenharmony_ciax88796c_open(struct net_device *ndev)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
79862306a36Sopenharmony_ci	unsigned long irq_flag = 0;
79962306a36Sopenharmony_ci	int fc = AX_FC_NONE;
80062306a36Sopenharmony_ci	int ret;
80162306a36Sopenharmony_ci	u16 t;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	ret = request_irq(ndev->irq, ax88796c_interrupt,
80462306a36Sopenharmony_ci			  irq_flag, ndev->name, ndev);
80562306a36Sopenharmony_ci	if (ret) {
80662306a36Sopenharmony_ci		netdev_err(ndev, "unable to get IRQ %d (errno=%d).\n",
80762306a36Sopenharmony_ci			   ndev->irq, ret);
80862306a36Sopenharmony_ci		return ret;
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	mutex_lock(&ax_local->spi_lock);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	ret = ax88796c_soft_reset(ax_local);
81462306a36Sopenharmony_ci	if (ret < 0) {
81562306a36Sopenharmony_ci		free_irq(ndev->irq, ndev);
81662306a36Sopenharmony_ci		mutex_unlock(&ax_local->spi_lock);
81762306a36Sopenharmony_ci		return ret;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci	ax_local->seq_num = 0x1f;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	ax88796c_set_mac_addr(ndev);
82262306a36Sopenharmony_ci	ax88796c_set_csums(ax_local);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	/* Disable stuffing packet */
82562306a36Sopenharmony_ci	t = AX_READ(&ax_local->ax_spi, P1_RXBSPCR);
82662306a36Sopenharmony_ci	t &= ~RXBSPCR_STUF_ENABLE;
82762306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, t, P1_RXBSPCR);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	/* Enable RX packet process */
83062306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, RPPER_RXEN, P1_RPPER);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	t = AX_READ(&ax_local->ax_spi, P0_FER);
83362306a36Sopenharmony_ci	t |= FER_RXEN | FER_TXEN | FER_BSWAP | FER_IRQ_PULL;
83462306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, t, P0_FER);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	/* Setup LED mode */
83762306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi,
83862306a36Sopenharmony_ci		 (LCR_LED0_EN | LCR_LED0_DUPLEX | LCR_LED1_EN |
83962306a36Sopenharmony_ci		 LCR_LED1_100MODE), P2_LCR0);
84062306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi,
84162306a36Sopenharmony_ci		 (AX_READ(&ax_local->ax_spi, P2_LCR1) & LCR_LED2_MASK) |
84262306a36Sopenharmony_ci		 LCR_LED2_EN | LCR_LED2_LINK, P2_LCR1);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/* Disable PHY auto-polling */
84562306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, PCR_PHYID(AX88796C_PHY_ID), P2_PCR);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* Enable MAC interrupts */
84862306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, IMR_DEFAULT, P0_IMR);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	mutex_unlock(&ax_local->spi_lock);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* Setup flow-control configuration */
85362306a36Sopenharmony_ci	phy_support_asym_pause(ax_local->phydev);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
85662306a36Sopenharmony_ci			      ax_local->phydev->advertising) ||
85762306a36Sopenharmony_ci	    linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
85862306a36Sopenharmony_ci			      ax_local->phydev->advertising))
85962306a36Sopenharmony_ci		fc |= AX_FC_ANEG;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	fc |= linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
86262306a36Sopenharmony_ci				ax_local->phydev->advertising) ? AX_FC_RX : 0;
86362306a36Sopenharmony_ci	fc |= (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
86462306a36Sopenharmony_ci				 ax_local->phydev->advertising) !=
86562306a36Sopenharmony_ci	       linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
86662306a36Sopenharmony_ci				 ax_local->phydev->advertising)) ? AX_FC_TX : 0;
86762306a36Sopenharmony_ci	ax_local->flowctrl = fc;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	phy_start(ax_local->ndev->phydev);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	netif_start_queue(ndev);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	spi_message_init(&ax_local->ax_spi.rx_msg);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	return 0;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int
87962306a36Sopenharmony_ciax88796c_close(struct net_device *ndev)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	phy_stop(ndev->phydev);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/* We lock the mutex early not only to protect the device
88662306a36Sopenharmony_ci	 * against concurrent access, but also avoid waking up the
88762306a36Sopenharmony_ci	 * queue in ax88796c_work(). phy_stop() needs to be called
88862306a36Sopenharmony_ci	 * before because it locks the mutex to access SPI.
88962306a36Sopenharmony_ci	 */
89062306a36Sopenharmony_ci	mutex_lock(&ax_local->spi_lock);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	netif_stop_queue(ndev);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* No more work can be scheduled now. Make any pending work,
89562306a36Sopenharmony_ci	 * including one already waiting for the mutex to be unlocked,
89662306a36Sopenharmony_ci	 * NOP.
89762306a36Sopenharmony_ci	 */
89862306a36Sopenharmony_ci	netif_dbg(ax_local, ifdown, ndev, "clearing bits\n");
89962306a36Sopenharmony_ci	clear_bit(EVENT_SET_MULTI, &ax_local->flags);
90062306a36Sopenharmony_ci	clear_bit(EVENT_INTR, &ax_local->flags);
90162306a36Sopenharmony_ci	clear_bit(EVENT_TX, &ax_local->flags);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/* Disable MAC interrupts */
90462306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, IMR_MASKALL, P0_IMR);
90562306a36Sopenharmony_ci	__skb_queue_purge(&ax_local->tx_wait_q);
90662306a36Sopenharmony_ci	ax88796c_soft_reset(ax_local);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	mutex_unlock(&ax_local->spi_lock);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	cancel_work_sync(&ax_local->ax_work);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	free_irq(ndev->irq, ndev);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	return 0;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_cistatic int
91862306a36Sopenharmony_ciax88796c_set_features(struct net_device *ndev, netdev_features_t features)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
92162306a36Sopenharmony_ci	netdev_features_t changed = features ^ ndev->features;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (!(changed & (NETIF_F_RXCSUM | NETIF_F_HW_CSUM)))
92462306a36Sopenharmony_ci		return 0;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	ndev->features = features;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (changed & (NETIF_F_RXCSUM | NETIF_F_HW_CSUM))
92962306a36Sopenharmony_ci		ax88796c_set_csums(ax_local);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	return 0;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic const struct net_device_ops ax88796c_netdev_ops = {
93562306a36Sopenharmony_ci	.ndo_open		= ax88796c_open,
93662306a36Sopenharmony_ci	.ndo_stop		= ax88796c_close,
93762306a36Sopenharmony_ci	.ndo_start_xmit		= ax88796c_start_xmit,
93862306a36Sopenharmony_ci	.ndo_get_stats64	= ax88796c_get_stats64,
93962306a36Sopenharmony_ci	.ndo_eth_ioctl		= ax88796c_ioctl,
94062306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
94162306a36Sopenharmony_ci	.ndo_set_features	= ax88796c_set_features,
94262306a36Sopenharmony_ci};
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_cistatic int ax88796c_hard_reset(struct ax88796c_device *ax_local)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	struct device *dev = (struct device *)&ax_local->spi->dev;
94762306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	/* reset info */
95062306a36Sopenharmony_ci	reset_gpio = gpiod_get(dev, "reset", 0);
95162306a36Sopenharmony_ci	if (IS_ERR(reset_gpio)) {
95262306a36Sopenharmony_ci		dev_err(dev, "Could not get 'reset' GPIO: %ld", PTR_ERR(reset_gpio));
95362306a36Sopenharmony_ci		return PTR_ERR(reset_gpio);
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	/* set reset */
95762306a36Sopenharmony_ci	gpiod_direction_output(reset_gpio, 1);
95862306a36Sopenharmony_ci	msleep(100);
95962306a36Sopenharmony_ci	gpiod_direction_output(reset_gpio, 0);
96062306a36Sopenharmony_ci	gpiod_put(reset_gpio);
96162306a36Sopenharmony_ci	msleep(20);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return 0;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic int ax88796c_probe(struct spi_device *spi)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	char phy_id[MII_BUS_ID_SIZE + 3];
96962306a36Sopenharmony_ci	struct ax88796c_device *ax_local;
97062306a36Sopenharmony_ci	struct net_device *ndev;
97162306a36Sopenharmony_ci	u16 temp;
97262306a36Sopenharmony_ci	int ret;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	ndev = devm_alloc_etherdev(&spi->dev, sizeof(*ax_local));
97562306a36Sopenharmony_ci	if (!ndev)
97662306a36Sopenharmony_ci		return -ENOMEM;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, &spi->dev);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	ax_local = to_ax88796c_device(ndev);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	dev_set_drvdata(&spi->dev, ax_local);
98362306a36Sopenharmony_ci	ax_local->spi = spi;
98462306a36Sopenharmony_ci	ax_local->ax_spi.spi = spi;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	ax_local->stats =
98762306a36Sopenharmony_ci		devm_netdev_alloc_pcpu_stats(&spi->dev,
98862306a36Sopenharmony_ci					     struct ax88796c_pcpu_stats);
98962306a36Sopenharmony_ci	if (!ax_local->stats)
99062306a36Sopenharmony_ci		return -ENOMEM;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	ax_local->ndev = ndev;
99362306a36Sopenharmony_ci	ax_local->priv_flags |= comp ? AX_CAP_COMP : 0;
99462306a36Sopenharmony_ci	ax_local->msg_enable = msg_enable;
99562306a36Sopenharmony_ci	mutex_init(&ax_local->spi_lock);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	ax_local->mdiobus = devm_mdiobus_alloc(&spi->dev);
99862306a36Sopenharmony_ci	if (!ax_local->mdiobus)
99962306a36Sopenharmony_ci		return -ENOMEM;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	ax_local->mdiobus->priv = ax_local;
100262306a36Sopenharmony_ci	ax_local->mdiobus->read = ax88796c_mdio_read;
100362306a36Sopenharmony_ci	ax_local->mdiobus->write = ax88796c_mdio_write;
100462306a36Sopenharmony_ci	ax_local->mdiobus->name = "ax88976c-mdiobus";
100562306a36Sopenharmony_ci	ax_local->mdiobus->phy_mask = (u32)~BIT(AX88796C_PHY_ID);
100662306a36Sopenharmony_ci	ax_local->mdiobus->parent = &spi->dev;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	snprintf(ax_local->mdiobus->id, MII_BUS_ID_SIZE,
100962306a36Sopenharmony_ci		 "ax88796c-%s.%u", dev_name(&spi->dev), spi_get_chipselect(spi, 0));
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	ret = devm_mdiobus_register(&spi->dev, ax_local->mdiobus);
101262306a36Sopenharmony_ci	if (ret < 0) {
101362306a36Sopenharmony_ci		dev_err(&spi->dev, "Could not register MDIO bus\n");
101462306a36Sopenharmony_ci		return ret;
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	if (netif_msg_probe(ax_local)) {
101862306a36Sopenharmony_ci		dev_info(&spi->dev, "AX88796C-SPI Configuration:\n");
101962306a36Sopenharmony_ci		dev_info(&spi->dev, "    Compression : %s\n",
102062306a36Sopenharmony_ci			 ax_local->priv_flags & AX_CAP_COMP ? "ON" : "OFF");
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	ndev->irq = spi->irq;
102462306a36Sopenharmony_ci	ndev->netdev_ops = &ax88796c_netdev_ops;
102562306a36Sopenharmony_ci	ndev->ethtool_ops = &ax88796c_ethtool_ops;
102662306a36Sopenharmony_ci	ndev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
102762306a36Sopenharmony_ci	ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
102862306a36Sopenharmony_ci	ndev->needed_headroom = TX_OVERHEAD;
102962306a36Sopenharmony_ci	ndev->needed_tailroom = TX_EOP_SIZE;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	mutex_lock(&ax_local->spi_lock);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/* ax88796c gpio reset */
103462306a36Sopenharmony_ci	ax88796c_hard_reset(ax_local);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/* Reset AX88796C */
103762306a36Sopenharmony_ci	ret = ax88796c_soft_reset(ax_local);
103862306a36Sopenharmony_ci	if (ret < 0) {
103962306a36Sopenharmony_ci		ret = -ENODEV;
104062306a36Sopenharmony_ci		mutex_unlock(&ax_local->spi_lock);
104162306a36Sopenharmony_ci		goto err;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci	/* Check board revision */
104462306a36Sopenharmony_ci	temp = AX_READ(&ax_local->ax_spi, P2_CRIR);
104562306a36Sopenharmony_ci	if ((temp & 0xF) != 0x0) {
104662306a36Sopenharmony_ci		dev_err(&spi->dev, "spi read failed: %d\n", temp);
104762306a36Sopenharmony_ci		ret = -ENODEV;
104862306a36Sopenharmony_ci		mutex_unlock(&ax_local->spi_lock);
104962306a36Sopenharmony_ci		goto err;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/*Reload EEPROM*/
105362306a36Sopenharmony_ci	ax88796c_reload_eeprom(ax_local);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	ax88796c_load_mac_addr(ndev);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (netif_msg_probe(ax_local))
105862306a36Sopenharmony_ci		dev_info(&spi->dev,
105962306a36Sopenharmony_ci			 "irq %d, MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n",
106062306a36Sopenharmony_ci			 ndev->irq,
106162306a36Sopenharmony_ci			 ndev->dev_addr[0], ndev->dev_addr[1],
106262306a36Sopenharmony_ci			 ndev->dev_addr[2], ndev->dev_addr[3],
106362306a36Sopenharmony_ci			 ndev->dev_addr[4], ndev->dev_addr[5]);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	/* Disable power saving */
106662306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, (AX_READ(&ax_local->ax_spi, P0_PSCR)
106762306a36Sopenharmony_ci				     & PSCR_PS_MASK) | PSCR_PS_D0, P0_PSCR);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	mutex_unlock(&ax_local->spi_lock);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	INIT_WORK(&ax_local->ax_work, ax88796c_work);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	skb_queue_head_init(&ax_local->tx_wait_q);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT,
107662306a36Sopenharmony_ci		 ax_local->mdiobus->id, AX88796C_PHY_ID);
107762306a36Sopenharmony_ci	ax_local->phydev = phy_connect(ax_local->ndev, phy_id,
107862306a36Sopenharmony_ci				       ax88796c_handle_link_change,
107962306a36Sopenharmony_ci				       PHY_INTERFACE_MODE_MII);
108062306a36Sopenharmony_ci	if (IS_ERR(ax_local->phydev)) {
108162306a36Sopenharmony_ci		ret = PTR_ERR(ax_local->phydev);
108262306a36Sopenharmony_ci		goto err;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci	ax_local->phydev->irq = PHY_POLL;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	ret = devm_register_netdev(&spi->dev, ndev);
108762306a36Sopenharmony_ci	if (ret) {
108862306a36Sopenharmony_ci		dev_err(&spi->dev, "failed to register a network device\n");
108962306a36Sopenharmony_ci		goto err_phy_dis;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	netif_info(ax_local, probe, ndev, "%s %s registered\n",
109362306a36Sopenharmony_ci		   dev_driver_string(&spi->dev),
109462306a36Sopenharmony_ci		   dev_name(&spi->dev));
109562306a36Sopenharmony_ci	phy_attached_info(ax_local->phydev);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return 0;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cierr_phy_dis:
110062306a36Sopenharmony_ci	phy_disconnect(ax_local->phydev);
110162306a36Sopenharmony_cierr:
110262306a36Sopenharmony_ci	return ret;
110362306a36Sopenharmony_ci}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cistatic void ax88796c_remove(struct spi_device *spi)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	struct ax88796c_device *ax_local = dev_get_drvdata(&spi->dev);
110862306a36Sopenharmony_ci	struct net_device *ndev = ax_local->ndev;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	phy_disconnect(ndev->phydev);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	netif_info(ax_local, probe, ndev, "removing network device %s %s\n",
111362306a36Sopenharmony_ci		   dev_driver_string(&spi->dev),
111462306a36Sopenharmony_ci		   dev_name(&spi->dev));
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci#ifdef CONFIG_OF
111862306a36Sopenharmony_cistatic const struct of_device_id ax88796c_dt_ids[] = {
111962306a36Sopenharmony_ci	{ .compatible = "asix,ax88796c" },
112062306a36Sopenharmony_ci	{},
112162306a36Sopenharmony_ci};
112262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ax88796c_dt_ids);
112362306a36Sopenharmony_ci#endif
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_cistatic const struct spi_device_id asix_id[] = {
112662306a36Sopenharmony_ci	{ "ax88796c", 0 },
112762306a36Sopenharmony_ci	{ }
112862306a36Sopenharmony_ci};
112962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, asix_id);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic struct spi_driver ax88796c_spi_driver = {
113262306a36Sopenharmony_ci	.driver = {
113362306a36Sopenharmony_ci		.name = DRV_NAME,
113462306a36Sopenharmony_ci		.of_match_table = of_match_ptr(ax88796c_dt_ids),
113562306a36Sopenharmony_ci	},
113662306a36Sopenharmony_ci	.probe = ax88796c_probe,
113762306a36Sopenharmony_ci	.remove = ax88796c_remove,
113862306a36Sopenharmony_ci	.id_table = asix_id,
113962306a36Sopenharmony_ci};
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic __init int ax88796c_spi_init(void)
114262306a36Sopenharmony_ci{
114362306a36Sopenharmony_ci	int ret;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	bitmap_zero(ax88796c_no_regs_mask, AX88796C_REGDUMP_LEN);
114662306a36Sopenharmony_ci	ret = bitmap_parse(no_regs_list, 35,
114762306a36Sopenharmony_ci			   ax88796c_no_regs_mask, AX88796C_REGDUMP_LEN);
114862306a36Sopenharmony_ci	if (ret) {
114962306a36Sopenharmony_ci		bitmap_fill(ax88796c_no_regs_mask, AX88796C_REGDUMP_LEN);
115062306a36Sopenharmony_ci		pr_err("Invalid bitmap description, masking all registers\n");
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	return spi_register_driver(&ax88796c_spi_driver);
115462306a36Sopenharmony_ci}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_cistatic __exit void ax88796c_spi_exit(void)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	spi_unregister_driver(&ax88796c_spi_driver);
115962306a36Sopenharmony_ci}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_cimodule_init(ax88796c_spi_init);
116262306a36Sopenharmony_cimodule_exit(ax88796c_spi_exit);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ciMODULE_AUTHOR("Łukasz Stelmach <l.stelmach@samsung.com>");
116562306a36Sopenharmony_ciMODULE_DESCRIPTION("ASIX AX88796C SPI Ethernet driver");
116662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1167