1a97c69baSŁukasz Stelmach // SPDX-License-Identifier: GPL-2.0-only
2a97c69baSŁukasz Stelmach /*
3a97c69baSŁukasz Stelmach  * Copyright (c) 2010 ASIX Electronics Corporation
4a97c69baSŁukasz Stelmach  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
5a97c69baSŁukasz Stelmach  *
6a97c69baSŁukasz Stelmach  * ASIX AX88796C SPI Fast Ethernet Linux driver
7a97c69baSŁukasz Stelmach  */
8a97c69baSŁukasz Stelmach 
9a97c69baSŁukasz Stelmach #define pr_fmt(fmt)	"ax88796c: " fmt
10a97c69baSŁukasz Stelmach 
11a97c69baSŁukasz Stelmach #include "ax88796c_main.h"
12a97c69baSŁukasz Stelmach #include "ax88796c_ioctl.h"
13a97c69baSŁukasz Stelmach 
14a97c69baSŁukasz Stelmach #include <linux/bitmap.h>
15a97c69baSŁukasz Stelmach #include <linux/etherdevice.h>
16a97c69baSŁukasz Stelmach #include <linux/iopoll.h>
17a97c69baSŁukasz Stelmach #include <linux/lockdep.h>
18a97c69baSŁukasz Stelmach #include <linux/mdio.h>
19a97c69baSŁukasz Stelmach #include <linux/minmax.h>
20a97c69baSŁukasz Stelmach #include <linux/module.h>
21a97c69baSŁukasz Stelmach #include <linux/netdevice.h>
22a97c69baSŁukasz Stelmach #include <linux/of.h>
23a97c69baSŁukasz Stelmach #include <linux/phy.h>
24a97c69baSŁukasz Stelmach #include <linux/skbuff.h>
25a97c69baSŁukasz Stelmach #include <linux/spi/spi.h>
26a97c69baSŁukasz Stelmach 
27a97c69baSŁukasz Stelmach static int comp = IS_ENABLED(CONFIG_SPI_AX88796C_COMPRESSION);
28a97c69baSŁukasz Stelmach static int msg_enable = NETIF_MSG_PROBE |
29a97c69baSŁukasz Stelmach 			NETIF_MSG_LINK |
30a97c69baSŁukasz Stelmach 			NETIF_MSG_RX_ERR |
31a97c69baSŁukasz Stelmach 			NETIF_MSG_TX_ERR;
32a97c69baSŁukasz Stelmach 
33a97c69baSŁukasz Stelmach static const char *no_regs_list = "80018001,e1918001,8001a001,fc0d0000";
34a97c69baSŁukasz Stelmach unsigned long ax88796c_no_regs_mask[AX88796C_REGDUMP_LEN / (sizeof(unsigned long) * 8)];
35a97c69baSŁukasz Stelmach 
36a97c69baSŁukasz Stelmach module_param(msg_enable, int, 0444);
37a97c69baSŁukasz Stelmach MODULE_PARM_DESC(msg_enable, "Message mask (see linux/netdevice.h for bitmap)");
38a97c69baSŁukasz Stelmach 
ax88796c_soft_reset(struct ax88796c_device * ax_local)39a97c69baSŁukasz Stelmach static int ax88796c_soft_reset(struct ax88796c_device *ax_local)
40a97c69baSŁukasz Stelmach {
41a97c69baSŁukasz Stelmach 	u16 temp;
42a97c69baSŁukasz Stelmach 	int ret;
43a97c69baSŁukasz Stelmach 
44a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
45a97c69baSŁukasz Stelmach 
46a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, PSR_RESET, P0_PSR);
47a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, PSR_RESET_CLR, P0_PSR);
48a97c69baSŁukasz Stelmach 
49a97c69baSŁukasz Stelmach 	ret = read_poll_timeout(AX_READ, ret,
50a97c69baSŁukasz Stelmach 				(ret & PSR_DEV_READY),
51a97c69baSŁukasz Stelmach 				0, jiffies_to_usecs(160 * HZ / 1000), false,
52a97c69baSŁukasz Stelmach 				&ax_local->ax_spi, P0_PSR);
53a97c69baSŁukasz Stelmach 	if (ret)
54a97c69baSŁukasz Stelmach 		return ret;
55a97c69baSŁukasz Stelmach 
56a97c69baSŁukasz Stelmach 	temp = AX_READ(&ax_local->ax_spi, P4_SPICR);
57a97c69baSŁukasz Stelmach 	if (ax_local->priv_flags & AX_CAP_COMP) {
58a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi,
59a97c69baSŁukasz Stelmach 			 (temp | SPICR_RCEN | SPICR_QCEN), P4_SPICR);
60a97c69baSŁukasz Stelmach 		ax_local->ax_spi.comp = 1;
61a97c69baSŁukasz Stelmach 	} else {
62a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi,
63a97c69baSŁukasz Stelmach 			 (temp & ~(SPICR_RCEN | SPICR_QCEN)), P4_SPICR);
64a97c69baSŁukasz Stelmach 		ax_local->ax_spi.comp = 0;
65a97c69baSŁukasz Stelmach 	}
66a97c69baSŁukasz Stelmach 
67a97c69baSŁukasz Stelmach 	return 0;
68a97c69baSŁukasz Stelmach }
69a97c69baSŁukasz Stelmach 
ax88796c_reload_eeprom(struct ax88796c_device * ax_local)70a97c69baSŁukasz Stelmach static int ax88796c_reload_eeprom(struct ax88796c_device *ax_local)
71a97c69baSŁukasz Stelmach {
72a97c69baSŁukasz Stelmach 	int ret;
73a97c69baSŁukasz Stelmach 
74a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
75a97c69baSŁukasz Stelmach 
76a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, EECR_RELOAD, P3_EECR);
77a97c69baSŁukasz Stelmach 
78a97c69baSŁukasz Stelmach 	ret = read_poll_timeout(AX_READ, ret,
79a97c69baSŁukasz Stelmach 				(ret & PSR_DEV_READY),
80a97c69baSŁukasz Stelmach 				0, jiffies_to_usecs(2 * HZ / 1000), false,
81a97c69baSŁukasz Stelmach 				&ax_local->ax_spi, P0_PSR);
82a97c69baSŁukasz Stelmach 	if (ret) {
83a97c69baSŁukasz Stelmach 		dev_err(&ax_local->spi->dev,
84a97c69baSŁukasz Stelmach 			"timeout waiting for reload eeprom\n");
85a97c69baSŁukasz Stelmach 		return ret;
86a97c69baSŁukasz Stelmach 	}
87a97c69baSŁukasz Stelmach 
88a97c69baSŁukasz Stelmach 	return 0;
89a97c69baSŁukasz Stelmach }
90a97c69baSŁukasz Stelmach 
ax88796c_set_hw_multicast(struct net_device * ndev)91a97c69baSŁukasz Stelmach static void ax88796c_set_hw_multicast(struct net_device *ndev)
92a97c69baSŁukasz Stelmach {
93a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
94a97c69baSŁukasz Stelmach 	int mc_count = netdev_mc_count(ndev);
95a97c69baSŁukasz Stelmach 	u16 rx_ctl = RXCR_AB;
96a97c69baSŁukasz Stelmach 
97a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
98a97c69baSŁukasz Stelmach 
99a97c69baSŁukasz Stelmach 	memset(ax_local->multi_filter, 0, AX_MCAST_FILTER_SIZE);
100a97c69baSŁukasz Stelmach 
101a97c69baSŁukasz Stelmach 	if (ndev->flags & IFF_PROMISC) {
102a97c69baSŁukasz Stelmach 		rx_ctl |= RXCR_PRO;
103a97c69baSŁukasz Stelmach 
104a97c69baSŁukasz Stelmach 	} else if (ndev->flags & IFF_ALLMULTI || mc_count > AX_MAX_MCAST) {
105a97c69baSŁukasz Stelmach 		rx_ctl |= RXCR_AMALL;
106a97c69baSŁukasz Stelmach 
107a97c69baSŁukasz Stelmach 	} else if (mc_count == 0) {
108a97c69baSŁukasz Stelmach 		/* just broadcast and directed */
109a97c69baSŁukasz Stelmach 	} else {
110a97c69baSŁukasz Stelmach 		u32 crc_bits;
111a97c69baSŁukasz Stelmach 		int i;
112a97c69baSŁukasz Stelmach 		struct netdev_hw_addr *ha;
113a97c69baSŁukasz Stelmach 
114a97c69baSŁukasz Stelmach 		netdev_for_each_mc_addr(ha, ndev) {
115a97c69baSŁukasz Stelmach 			crc_bits = ether_crc(ETH_ALEN, ha->addr);
116a97c69baSŁukasz Stelmach 			ax_local->multi_filter[crc_bits >> 29] |=
117a97c69baSŁukasz Stelmach 						(1 << ((crc_bits >> 26) & 7));
118a97c69baSŁukasz Stelmach 		}
119a97c69baSŁukasz Stelmach 
120a97c69baSŁukasz Stelmach 		for (i = 0; i < 4; i++) {
121a97c69baSŁukasz Stelmach 			AX_WRITE(&ax_local->ax_spi,
122a97c69baSŁukasz Stelmach 				 ((ax_local->multi_filter[i * 2 + 1] << 8) |
123a97c69baSŁukasz Stelmach 				  ax_local->multi_filter[i * 2]), P3_MFAR(i));
124a97c69baSŁukasz Stelmach 		}
125a97c69baSŁukasz Stelmach 	}
126a97c69baSŁukasz Stelmach 
127a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, rx_ctl, P2_RXCR);
128a97c69baSŁukasz Stelmach }
129a97c69baSŁukasz Stelmach 
ax88796c_set_mac_addr(struct net_device * ndev)130a97c69baSŁukasz Stelmach static void ax88796c_set_mac_addr(struct net_device *ndev)
131a97c69baSŁukasz Stelmach {
132a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
133a97c69baSŁukasz Stelmach 
134a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
135a97c69baSŁukasz Stelmach 
136a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, ((u16)(ndev->dev_addr[4] << 8) |
137a97c69baSŁukasz Stelmach 			(u16)ndev->dev_addr[5]), P3_MACASR0);
138a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, ((u16)(ndev->dev_addr[2] << 8) |
139a97c69baSŁukasz Stelmach 			(u16)ndev->dev_addr[3]), P3_MACASR1);
140a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, ((u16)(ndev->dev_addr[0] << 8) |
141a97c69baSŁukasz Stelmach 			(u16)ndev->dev_addr[1]), P3_MACASR2);
142a97c69baSŁukasz Stelmach }
143a97c69baSŁukasz Stelmach 
ax88796c_load_mac_addr(struct net_device * ndev)144a97c69baSŁukasz Stelmach static void ax88796c_load_mac_addr(struct net_device *ndev)
145a97c69baSŁukasz Stelmach {
146a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
147e291422cSJakub Kicinski 	u8 addr[ETH_ALEN];
148a97c69baSŁukasz Stelmach 	u16 temp;
149a97c69baSŁukasz Stelmach 
150a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
151a97c69baSŁukasz Stelmach 
152a97c69baSŁukasz Stelmach 	/* Try the device tree first */
153e291422cSJakub Kicinski 	if (!platform_get_ethdev_address(&ax_local->spi->dev, ndev) &&
154a97c69baSŁukasz Stelmach 	    is_valid_ether_addr(ndev->dev_addr)) {
155a97c69baSŁukasz Stelmach 		if (netif_msg_probe(ax_local))
156a97c69baSŁukasz Stelmach 			dev_info(&ax_local->spi->dev,
157a97c69baSŁukasz Stelmach 				 "MAC address read from device tree\n");
158a97c69baSŁukasz Stelmach 		return;
159a97c69baSŁukasz Stelmach 	}
160a97c69baSŁukasz Stelmach 
161a97c69baSŁukasz Stelmach 	/* Read the MAC address from AX88796C */
162a97c69baSŁukasz Stelmach 	temp = AX_READ(&ax_local->ax_spi, P3_MACASR0);
163e291422cSJakub Kicinski 	addr[5] = (u8)temp;
164e291422cSJakub Kicinski 	addr[4] = (u8)(temp >> 8);
165a97c69baSŁukasz Stelmach 
166a97c69baSŁukasz Stelmach 	temp = AX_READ(&ax_local->ax_spi, P3_MACASR1);
167e291422cSJakub Kicinski 	addr[3] = (u8)temp;
168e291422cSJakub Kicinski 	addr[2] = (u8)(temp >> 8);
169a97c69baSŁukasz Stelmach 
170a97c69baSŁukasz Stelmach 	temp = AX_READ(&ax_local->ax_spi, P3_MACASR2);
171e291422cSJakub Kicinski 	addr[1] = (u8)temp;
172e291422cSJakub Kicinski 	addr[0] = (u8)(temp >> 8);
173a97c69baSŁukasz Stelmach 
174e291422cSJakub Kicinski 	if (is_valid_ether_addr(addr)) {
175e291422cSJakub Kicinski 		eth_hw_addr_set(ndev, addr);
176a97c69baSŁukasz Stelmach 		if (netif_msg_probe(ax_local))
177a97c69baSŁukasz Stelmach 			dev_info(&ax_local->spi->dev,
178a97c69baSŁukasz Stelmach 				 "MAC address read from ASIX chip\n");
179a97c69baSŁukasz Stelmach 		return;
180a97c69baSŁukasz Stelmach 	}
181a97c69baSŁukasz Stelmach 
182a97c69baSŁukasz Stelmach 	/* Use random address if none found */
183a97c69baSŁukasz Stelmach 	if (netif_msg_probe(ax_local))
184a97c69baSŁukasz Stelmach 		dev_info(&ax_local->spi->dev, "Use random MAC address\n");
185a97c69baSŁukasz Stelmach 	eth_hw_addr_random(ndev);
186a97c69baSŁukasz Stelmach }
187a97c69baSŁukasz Stelmach 
ax88796c_proc_tx_hdr(struct tx_pkt_info * info,u8 ip_summed)188a97c69baSŁukasz Stelmach static void ax88796c_proc_tx_hdr(struct tx_pkt_info *info, u8 ip_summed)
189a97c69baSŁukasz Stelmach {
190a97c69baSŁukasz Stelmach 	u16 pkt_len_bar = (~info->pkt_len & TX_HDR_SOP_PKTLENBAR);
191a97c69baSŁukasz Stelmach 
192a97c69baSŁukasz Stelmach 	/* Prepare SOP header */
193a97c69baSŁukasz Stelmach 	info->sop.flags_len = info->pkt_len |
194a97c69baSŁukasz Stelmach 		((ip_summed == CHECKSUM_NONE) ||
195a97c69baSŁukasz Stelmach 		 (ip_summed == CHECKSUM_UNNECESSARY) ? TX_HDR_SOP_DICF : 0);
196a97c69baSŁukasz Stelmach 
197a97c69baSŁukasz Stelmach 	info->sop.seq_lenbar = ((info->seq_num << 11) & TX_HDR_SOP_SEQNUM)
198a97c69baSŁukasz Stelmach 				| pkt_len_bar;
199a97c69baSŁukasz Stelmach 	cpu_to_be16s(&info->sop.flags_len);
200a97c69baSŁukasz Stelmach 	cpu_to_be16s(&info->sop.seq_lenbar);
201a97c69baSŁukasz Stelmach 
202a97c69baSŁukasz Stelmach 	/* Prepare Segment header */
203a97c69baSŁukasz Stelmach 	info->seg.flags_seqnum_seglen = TX_HDR_SEG_FS | TX_HDR_SEG_LS
204a97c69baSŁukasz Stelmach 						| info->pkt_len;
205a97c69baSŁukasz Stelmach 
206a97c69baSŁukasz Stelmach 	info->seg.eo_so_seglenbar = pkt_len_bar;
207a97c69baSŁukasz Stelmach 
208a97c69baSŁukasz Stelmach 	cpu_to_be16s(&info->seg.flags_seqnum_seglen);
209a97c69baSŁukasz Stelmach 	cpu_to_be16s(&info->seg.eo_so_seglenbar);
210a97c69baSŁukasz Stelmach 
211a97c69baSŁukasz Stelmach 	/* Prepare EOP header */
212a97c69baSŁukasz Stelmach 	info->eop.seq_len = ((info->seq_num << 11) &
213a97c69baSŁukasz Stelmach 			     TX_HDR_EOP_SEQNUM) | info->pkt_len;
214a97c69baSŁukasz Stelmach 	info->eop.seqbar_lenbar = ((~info->seq_num << 11) &
215a97c69baSŁukasz Stelmach 				   TX_HDR_EOP_SEQNUMBAR) | pkt_len_bar;
216a97c69baSŁukasz Stelmach 
217a97c69baSŁukasz Stelmach 	cpu_to_be16s(&info->eop.seq_len);
218a97c69baSŁukasz Stelmach 	cpu_to_be16s(&info->eop.seqbar_lenbar);
219a97c69baSŁukasz Stelmach }
220a97c69baSŁukasz Stelmach 
221a97c69baSŁukasz Stelmach static int
ax88796c_check_free_pages(struct ax88796c_device * ax_local,u8 need_pages)222a97c69baSŁukasz Stelmach ax88796c_check_free_pages(struct ax88796c_device *ax_local, u8 need_pages)
223a97c69baSŁukasz Stelmach {
224a97c69baSŁukasz Stelmach 	u8 free_pages;
225a97c69baSŁukasz Stelmach 	u16 tmp;
226a97c69baSŁukasz Stelmach 
227a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
228a97c69baSŁukasz Stelmach 
229a97c69baSŁukasz Stelmach 	free_pages = AX_READ(&ax_local->ax_spi, P0_TFBFCR) & TX_FREEBUF_MASK;
230a97c69baSŁukasz Stelmach 	if (free_pages < need_pages) {
231a97c69baSŁukasz Stelmach 		/* schedule free page interrupt */
232a97c69baSŁukasz Stelmach 		tmp = AX_READ(&ax_local->ax_spi, P0_TFBFCR)
233a97c69baSŁukasz Stelmach 				& TFBFCR_SCHE_FREE_PAGE;
234a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, tmp | TFBFCR_TX_PAGE_SET |
235a97c69baSŁukasz Stelmach 				TFBFCR_SET_FREE_PAGE(need_pages),
236a97c69baSŁukasz Stelmach 				P0_TFBFCR);
237a97c69baSŁukasz Stelmach 		return -ENOMEM;
238a97c69baSŁukasz Stelmach 	}
239a97c69baSŁukasz Stelmach 
240a97c69baSŁukasz Stelmach 	return 0;
241a97c69baSŁukasz Stelmach }
242a97c69baSŁukasz Stelmach 
243a97c69baSŁukasz Stelmach static struct sk_buff *
ax88796c_tx_fixup(struct net_device * ndev,struct sk_buff_head * q)244a97c69baSŁukasz Stelmach ax88796c_tx_fixup(struct net_device *ndev, struct sk_buff_head *q)
245a97c69baSŁukasz Stelmach {
246a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
247a97c69baSŁukasz Stelmach 	u8 spi_len = ax_local->ax_spi.comp ? 1 : 4;
248a97c69baSŁukasz Stelmach 	struct sk_buff *skb;
249a97c69baSŁukasz Stelmach 	struct tx_pkt_info info;
250a97c69baSŁukasz Stelmach 	struct skb_data *entry;
251a97c69baSŁukasz Stelmach 	u16 pkt_len;
252a97c69baSŁukasz Stelmach 	u8 padlen, seq_num;
253a97c69baSŁukasz Stelmach 	u8 need_pages;
254a97c69baSŁukasz Stelmach 	int headroom;
255a97c69baSŁukasz Stelmach 	int tailroom;
256a97c69baSŁukasz Stelmach 
257a97c69baSŁukasz Stelmach 	if (skb_queue_empty(q))
258a97c69baSŁukasz Stelmach 		return NULL;
259a97c69baSŁukasz Stelmach 
260a97c69baSŁukasz Stelmach 	skb = skb_peek(q);
261a97c69baSŁukasz Stelmach 	pkt_len = skb->len;
262a97c69baSŁukasz Stelmach 	need_pages = (pkt_len + TX_OVERHEAD + 127) >> 7;
263a97c69baSŁukasz Stelmach 	if (ax88796c_check_free_pages(ax_local, need_pages) != 0)
264a97c69baSŁukasz Stelmach 		return NULL;
265a97c69baSŁukasz Stelmach 
266a97c69baSŁukasz Stelmach 	headroom = skb_headroom(skb);
267a97c69baSŁukasz Stelmach 	tailroom = skb_tailroom(skb);
268a97c69baSŁukasz Stelmach 	padlen = round_up(pkt_len, 4) - pkt_len;
269a97c69baSŁukasz Stelmach 	seq_num = ++ax_local->seq_num & 0x1F;
270a97c69baSŁukasz Stelmach 
271a97c69baSŁukasz Stelmach 	info.pkt_len = pkt_len;
272a97c69baSŁukasz Stelmach 
273a97c69baSŁukasz Stelmach 	if (skb_cloned(skb) ||
274a97c69baSŁukasz Stelmach 	    (headroom < (TX_OVERHEAD + spi_len)) ||
275a97c69baSŁukasz Stelmach 	    (tailroom < (padlen + TX_EOP_SIZE))) {
276a97c69baSŁukasz Stelmach 		size_t h = max((TX_OVERHEAD + spi_len) - headroom, 0);
277a97c69baSŁukasz Stelmach 		size_t t = max((padlen + TX_EOP_SIZE) - tailroom, 0);
278a97c69baSŁukasz Stelmach 
279a97c69baSŁukasz Stelmach 		if (pskb_expand_head(skb, h, t, GFP_KERNEL))
280a97c69baSŁukasz Stelmach 			return NULL;
281a97c69baSŁukasz Stelmach 	}
282a97c69baSŁukasz Stelmach 
283a97c69baSŁukasz Stelmach 	info.seq_num = seq_num;
284a97c69baSŁukasz Stelmach 	ax88796c_proc_tx_hdr(&info, skb->ip_summed);
285a97c69baSŁukasz Stelmach 
286a97c69baSŁukasz Stelmach 	/* SOP and SEG header */
287*2a626448SDmitry Antipov 	memcpy(skb_push(skb, TX_OVERHEAD), &info.tx_overhead, TX_OVERHEAD);
288a97c69baSŁukasz Stelmach 
289a97c69baSŁukasz Stelmach 	/* Write SPI TXQ header */
290a97c69baSŁukasz Stelmach 	memcpy(skb_push(skb, spi_len), ax88796c_tx_cmd_buf, spi_len);
291a97c69baSŁukasz Stelmach 
292a97c69baSŁukasz Stelmach 	/* Make 32-bit alignment */
293a97c69baSŁukasz Stelmach 	skb_put(skb, padlen);
294a97c69baSŁukasz Stelmach 
295a97c69baSŁukasz Stelmach 	/* EOP header */
29685e69a7dSShang XiaoJing 	skb_put_data(skb, &info.eop, TX_EOP_SIZE);
297a97c69baSŁukasz Stelmach 
298a97c69baSŁukasz Stelmach 	skb_unlink(skb, q);
299a97c69baSŁukasz Stelmach 
300a97c69baSŁukasz Stelmach 	entry = (struct skb_data *)skb->cb;
301a97c69baSŁukasz Stelmach 	memset(entry, 0, sizeof(*entry));
302a97c69baSŁukasz Stelmach 	entry->len = pkt_len;
303a97c69baSŁukasz Stelmach 
304a97c69baSŁukasz Stelmach 	if (netif_msg_pktdata(ax_local)) {
305a97c69baSŁukasz Stelmach 		char pfx[IFNAMSIZ + 7];
306a97c69baSŁukasz Stelmach 
307a97c69baSŁukasz Stelmach 		snprintf(pfx, sizeof(pfx), "%s:     ", ndev->name);
308a97c69baSŁukasz Stelmach 
309a97c69baSŁukasz Stelmach 		netdev_info(ndev, "TX packet len %d, total len %d, seq %d\n",
310a97c69baSŁukasz Stelmach 			    pkt_len, skb->len, seq_num);
311a97c69baSŁukasz Stelmach 
312a97c69baSŁukasz Stelmach 		netdev_info(ndev, "  SPI Header:\n");
313a97c69baSŁukasz Stelmach 		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
314a97c69baSŁukasz Stelmach 			       skb->data, 4, 0);
315a97c69baSŁukasz Stelmach 
316a97c69baSŁukasz Stelmach 		netdev_info(ndev, "  TX SOP:\n");
317a97c69baSŁukasz Stelmach 		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
318a97c69baSŁukasz Stelmach 			       skb->data + 4, TX_OVERHEAD, 0);
319a97c69baSŁukasz Stelmach 
320a97c69baSŁukasz Stelmach 		netdev_info(ndev, "  TX packet:\n");
321a97c69baSŁukasz Stelmach 		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
322a97c69baSŁukasz Stelmach 			       skb->data + 4 + TX_OVERHEAD,
323a97c69baSŁukasz Stelmach 			       skb->len - TX_EOP_SIZE - 4 - TX_OVERHEAD, 0);
324a97c69baSŁukasz Stelmach 
325a97c69baSŁukasz Stelmach 		netdev_info(ndev, "  TX EOP:\n");
326a97c69baSŁukasz Stelmach 		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
327a97c69baSŁukasz Stelmach 			       skb->data + skb->len - 4, 4, 0);
328a97c69baSŁukasz Stelmach 	}
329a97c69baSŁukasz Stelmach 
330a97c69baSŁukasz Stelmach 	return skb;
331a97c69baSŁukasz Stelmach }
332a97c69baSŁukasz Stelmach 
ax88796c_hard_xmit(struct ax88796c_device * ax_local)333a97c69baSŁukasz Stelmach static int ax88796c_hard_xmit(struct ax88796c_device *ax_local)
334a97c69baSŁukasz Stelmach {
335a97c69baSŁukasz Stelmach 	struct ax88796c_pcpu_stats *stats;
336a97c69baSŁukasz Stelmach 	struct sk_buff *tx_skb;
337a97c69baSŁukasz Stelmach 	struct skb_data *entry;
338a97c69baSŁukasz Stelmach 	unsigned long flags;
339a97c69baSŁukasz Stelmach 
340a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
341a97c69baSŁukasz Stelmach 
342a97c69baSŁukasz Stelmach 	stats = this_cpu_ptr(ax_local->stats);
343a97c69baSŁukasz Stelmach 	tx_skb = ax88796c_tx_fixup(ax_local->ndev, &ax_local->tx_wait_q);
344a97c69baSŁukasz Stelmach 
345a97c69baSŁukasz Stelmach 	if (!tx_skb) {
346a97c69baSŁukasz Stelmach 		this_cpu_inc(ax_local->stats->tx_dropped);
347a97c69baSŁukasz Stelmach 		return 0;
348a97c69baSŁukasz Stelmach 	}
349a97c69baSŁukasz Stelmach 	entry = (struct skb_data *)tx_skb->cb;
350a97c69baSŁukasz Stelmach 
351a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi,
352a97c69baSŁukasz Stelmach 		 (TSNR_TXB_START | TSNR_PKT_CNT(1)), P0_TSNR);
353a97c69baSŁukasz Stelmach 
354a97c69baSŁukasz Stelmach 	axspi_write_txq(&ax_local->ax_spi, tx_skb->data, tx_skb->len);
355a97c69baSŁukasz Stelmach 
356a97c69baSŁukasz Stelmach 	if (((AX_READ(&ax_local->ax_spi, P0_TSNR) & TXNR_TXB_IDLE) == 0) ||
357a97c69baSŁukasz Stelmach 	    ((ISR_TXERR & AX_READ(&ax_local->ax_spi, P0_ISR)) != 0)) {
358a97c69baSŁukasz Stelmach 		/* Ack tx error int */
359a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, ISR_TXERR, P0_ISR);
360a97c69baSŁukasz Stelmach 
361a97c69baSŁukasz Stelmach 		this_cpu_inc(ax_local->stats->tx_dropped);
362a97c69baSŁukasz Stelmach 
363a97c69baSŁukasz Stelmach 		if (net_ratelimit())
364a97c69baSŁukasz Stelmach 			netif_err(ax_local, tx_err, ax_local->ndev,
365a97c69baSŁukasz Stelmach 				  "TX FIFO error, re-initialize the TX bridge\n");
366a97c69baSŁukasz Stelmach 
367a97c69baSŁukasz Stelmach 		/* Reinitial tx bridge */
368a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, TXNR_TXB_REINIT |
369a97c69baSŁukasz Stelmach 			AX_READ(&ax_local->ax_spi, P0_TSNR), P0_TSNR);
370a97c69baSŁukasz Stelmach 		ax_local->seq_num = 0;
371a97c69baSŁukasz Stelmach 	} else {
372a97c69baSŁukasz Stelmach 		flags = u64_stats_update_begin_irqsave(&stats->syncp);
373a97c69baSŁukasz Stelmach 		u64_stats_inc(&stats->tx_packets);
374a97c69baSŁukasz Stelmach 		u64_stats_add(&stats->tx_bytes, entry->len);
375a97c69baSŁukasz Stelmach 		u64_stats_update_end_irqrestore(&stats->syncp, flags);
376a97c69baSŁukasz Stelmach 	}
377a97c69baSŁukasz Stelmach 
378a97c69baSŁukasz Stelmach 	entry->state = tx_done;
379a97c69baSŁukasz Stelmach 	dev_kfree_skb(tx_skb);
380a97c69baSŁukasz Stelmach 
381a97c69baSŁukasz Stelmach 	return 1;
382a97c69baSŁukasz Stelmach }
383a97c69baSŁukasz Stelmach 
384fcb7c210SNathan Huckleberry static netdev_tx_t
ax88796c_start_xmit(struct sk_buff * skb,struct net_device * ndev)385a97c69baSŁukasz Stelmach ax88796c_start_xmit(struct sk_buff *skb, struct net_device *ndev)
386a97c69baSŁukasz Stelmach {
387a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
388a97c69baSŁukasz Stelmach 
389a97c69baSŁukasz Stelmach 	skb_queue_tail(&ax_local->tx_wait_q, skb);
390a97c69baSŁukasz Stelmach 	if (skb_queue_len(&ax_local->tx_wait_q) > TX_QUEUE_HIGH_WATER)
391a97c69baSŁukasz Stelmach 		netif_stop_queue(ndev);
392a97c69baSŁukasz Stelmach 
393a97c69baSŁukasz Stelmach 	set_bit(EVENT_TX, &ax_local->flags);
394a97c69baSŁukasz Stelmach 	schedule_work(&ax_local->ax_work);
395a97c69baSŁukasz Stelmach 
396a97c69baSŁukasz Stelmach 	return NETDEV_TX_OK;
397a97c69baSŁukasz Stelmach }
398a97c69baSŁukasz Stelmach 
399a97c69baSŁukasz Stelmach static void
ax88796c_skb_return(struct ax88796c_device * ax_local,struct sk_buff * skb,struct rx_header * rxhdr)400a97c69baSŁukasz Stelmach ax88796c_skb_return(struct ax88796c_device *ax_local,
401a97c69baSŁukasz Stelmach 		    struct sk_buff *skb, struct rx_header *rxhdr)
402a97c69baSŁukasz Stelmach {
403a97c69baSŁukasz Stelmach 	struct net_device *ndev = ax_local->ndev;
404a97c69baSŁukasz Stelmach 	struct ax88796c_pcpu_stats *stats;
405a97c69baSŁukasz Stelmach 	unsigned long flags;
406a97c69baSŁukasz Stelmach 	int status;
407a97c69baSŁukasz Stelmach 
408a97c69baSŁukasz Stelmach 	stats = this_cpu_ptr(ax_local->stats);
409a97c69baSŁukasz Stelmach 
410a97c69baSŁukasz Stelmach 	do {
411a97c69baSŁukasz Stelmach 		if (!(ndev->features & NETIF_F_RXCSUM))
412a97c69baSŁukasz Stelmach 			break;
413a97c69baSŁukasz Stelmach 
414a97c69baSŁukasz Stelmach 		/* checksum error bit is set */
415a97c69baSŁukasz Stelmach 		if ((rxhdr->flags & RX_HDR3_L3_ERR) ||
416a97c69baSŁukasz Stelmach 		    (rxhdr->flags & RX_HDR3_L4_ERR))
417a97c69baSŁukasz Stelmach 			break;
418a97c69baSŁukasz Stelmach 
419a97c69baSŁukasz Stelmach 		/* Other types may be indicated by more than one bit. */
420a97c69baSŁukasz Stelmach 		if ((rxhdr->flags & RX_HDR3_L4_TYPE_TCP) ||
421a97c69baSŁukasz Stelmach 		    (rxhdr->flags & RX_HDR3_L4_TYPE_UDP))
422a97c69baSŁukasz Stelmach 			skb->ip_summed = CHECKSUM_UNNECESSARY;
423a97c69baSŁukasz Stelmach 	} while (0);
424a97c69baSŁukasz Stelmach 
425a97c69baSŁukasz Stelmach 	flags = u64_stats_update_begin_irqsave(&stats->syncp);
426a97c69baSŁukasz Stelmach 	u64_stats_inc(&stats->rx_packets);
427a97c69baSŁukasz Stelmach 	u64_stats_add(&stats->rx_bytes, skb->len);
428a97c69baSŁukasz Stelmach 	u64_stats_update_end_irqrestore(&stats->syncp, flags);
429a97c69baSŁukasz Stelmach 
430a97c69baSŁukasz Stelmach 	skb->dev = ndev;
431a97c69baSŁukasz Stelmach 	skb->protocol = eth_type_trans(skb, ax_local->ndev);
432a97c69baSŁukasz Stelmach 
433a97c69baSŁukasz Stelmach 	netif_info(ax_local, rx_status, ndev, "< rx, len %zu, type 0x%x\n",
434a97c69baSŁukasz Stelmach 		   skb->len + sizeof(struct ethhdr), skb->protocol);
435a97c69baSŁukasz Stelmach 
43690f77c1cSSebastian Andrzej Siewior 	status = netif_rx(skb);
437a97c69baSŁukasz Stelmach 	if (status != NET_RX_SUCCESS && net_ratelimit())
438a97c69baSŁukasz Stelmach 		netif_info(ax_local, rx_err, ndev,
439a97c69baSŁukasz Stelmach 			   "netif_rx status %d\n", status);
440a97c69baSŁukasz Stelmach }
441a97c69baSŁukasz Stelmach 
442a97c69baSŁukasz Stelmach static void
ax88796c_rx_fixup(struct ax88796c_device * ax_local,struct sk_buff * rx_skb)443a97c69baSŁukasz Stelmach ax88796c_rx_fixup(struct ax88796c_device *ax_local, struct sk_buff *rx_skb)
444a97c69baSŁukasz Stelmach {
445a97c69baSŁukasz Stelmach 	struct rx_header *rxhdr = (struct rx_header *)rx_skb->data;
446a97c69baSŁukasz Stelmach 	struct net_device *ndev = ax_local->ndev;
447a97c69baSŁukasz Stelmach 	u16 len;
448a97c69baSŁukasz Stelmach 
449a97c69baSŁukasz Stelmach 	be16_to_cpus(&rxhdr->flags_len);
450a97c69baSŁukasz Stelmach 	be16_to_cpus(&rxhdr->seq_lenbar);
451a97c69baSŁukasz Stelmach 	be16_to_cpus(&rxhdr->flags);
452a97c69baSŁukasz Stelmach 
453a97c69baSŁukasz Stelmach 	if ((rxhdr->flags_len & RX_HDR1_PKT_LEN) !=
454a97c69baSŁukasz Stelmach 			 (~rxhdr->seq_lenbar & 0x7FF)) {
455a97c69baSŁukasz Stelmach 		netif_err(ax_local, rx_err, ndev, "Header error\n");
456a97c69baSŁukasz Stelmach 
457a97c69baSŁukasz Stelmach 		this_cpu_inc(ax_local->stats->rx_frame_errors);
458a97c69baSŁukasz Stelmach 		kfree_skb(rx_skb);
459a97c69baSŁukasz Stelmach 		return;
460a97c69baSŁukasz Stelmach 	}
461a97c69baSŁukasz Stelmach 
462a97c69baSŁukasz Stelmach 	if ((rxhdr->flags_len & RX_HDR1_MII_ERR) ||
463a97c69baSŁukasz Stelmach 	    (rxhdr->flags_len & RX_HDR1_CRC_ERR)) {
464a97c69baSŁukasz Stelmach 		netif_err(ax_local, rx_err, ndev, "CRC or MII error\n");
465a97c69baSŁukasz Stelmach 
466a97c69baSŁukasz Stelmach 		this_cpu_inc(ax_local->stats->rx_crc_errors);
467a97c69baSŁukasz Stelmach 		kfree_skb(rx_skb);
468a97c69baSŁukasz Stelmach 		return;
469a97c69baSŁukasz Stelmach 	}
470a97c69baSŁukasz Stelmach 
471a97c69baSŁukasz Stelmach 	len = rxhdr->flags_len & RX_HDR1_PKT_LEN;
472a97c69baSŁukasz Stelmach 	if (netif_msg_pktdata(ax_local)) {
473a97c69baSŁukasz Stelmach 		char pfx[IFNAMSIZ + 7];
474a97c69baSŁukasz Stelmach 
475a97c69baSŁukasz Stelmach 		snprintf(pfx, sizeof(pfx), "%s:     ", ndev->name);
476a97c69baSŁukasz Stelmach 		netdev_info(ndev, "RX data, total len %d, packet len %d\n",
477a97c69baSŁukasz Stelmach 			    rx_skb->len, len);
478a97c69baSŁukasz Stelmach 
479a97c69baSŁukasz Stelmach 		netdev_info(ndev, "  Dump RX packet header:");
480a97c69baSŁukasz Stelmach 		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
481a97c69baSŁukasz Stelmach 			       rx_skb->data, sizeof(*rxhdr), 0);
482a97c69baSŁukasz Stelmach 
483a97c69baSŁukasz Stelmach 		netdev_info(ndev, "  Dump RX packet:");
484a97c69baSŁukasz Stelmach 		print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
485a97c69baSŁukasz Stelmach 			       rx_skb->data + sizeof(*rxhdr), len, 0);
486a97c69baSŁukasz Stelmach 	}
487a97c69baSŁukasz Stelmach 
488a97c69baSŁukasz Stelmach 	skb_pull(rx_skb, sizeof(*rxhdr));
489a97c69baSŁukasz Stelmach 	pskb_trim(rx_skb, len);
490a97c69baSŁukasz Stelmach 
491a97c69baSŁukasz Stelmach 	ax88796c_skb_return(ax_local, rx_skb, rxhdr);
492a97c69baSŁukasz Stelmach }
493a97c69baSŁukasz Stelmach 
ax88796c_receive(struct net_device * ndev)494a97c69baSŁukasz Stelmach static int ax88796c_receive(struct net_device *ndev)
495a97c69baSŁukasz Stelmach {
496a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
497a97c69baSŁukasz Stelmach 	struct skb_data *entry;
498a97c69baSŁukasz Stelmach 	u16 w_count, pkt_len;
499a97c69baSŁukasz Stelmach 	struct sk_buff *skb;
500a97c69baSŁukasz Stelmach 	u8 pkt_cnt;
501a97c69baSŁukasz Stelmach 
502a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
503a97c69baSŁukasz Stelmach 
504a97c69baSŁukasz Stelmach 	/* check rx packet and total word count */
505a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, AX_READ(&ax_local->ax_spi, P0_RTWCR)
506a97c69baSŁukasz Stelmach 		  | RTWCR_RX_LATCH, P0_RTWCR);
507a97c69baSŁukasz Stelmach 
508a97c69baSŁukasz Stelmach 	pkt_cnt = AX_READ(&ax_local->ax_spi, P0_RXBCR2) & RXBCR2_PKT_MASK;
509a97c69baSŁukasz Stelmach 	if (!pkt_cnt)
510a97c69baSŁukasz Stelmach 		return 0;
511a97c69baSŁukasz Stelmach 
512a97c69baSŁukasz Stelmach 	pkt_len = AX_READ(&ax_local->ax_spi, P0_RCPHR) & 0x7FF;
513a97c69baSŁukasz Stelmach 
514a97c69baSŁukasz Stelmach 	w_count = round_up(pkt_len + 6, 4) >> 1;
515a97c69baSŁukasz Stelmach 
516a97c69baSŁukasz Stelmach 	skb = netdev_alloc_skb(ndev, w_count * 2);
517a97c69baSŁukasz Stelmach 	if (!skb) {
518a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, RXBCR1_RXB_DISCARD, P0_RXBCR1);
519a97c69baSŁukasz Stelmach 		this_cpu_inc(ax_local->stats->rx_dropped);
520a97c69baSŁukasz Stelmach 		return 0;
521a97c69baSŁukasz Stelmach 	}
522a97c69baSŁukasz Stelmach 	entry = (struct skb_data *)skb->cb;
523a97c69baSŁukasz Stelmach 
524a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, RXBCR1_RXB_START | w_count, P0_RXBCR1);
525a97c69baSŁukasz Stelmach 
526a97c69baSŁukasz Stelmach 	axspi_read_rxq(&ax_local->ax_spi,
527a97c69baSŁukasz Stelmach 		       skb_put(skb, w_count * 2), skb->len);
528a97c69baSŁukasz Stelmach 
529a97c69baSŁukasz Stelmach 	/* Check if rx bridge is idle */
530a97c69baSŁukasz Stelmach 	if ((AX_READ(&ax_local->ax_spi, P0_RXBCR2) & RXBCR2_RXB_IDLE) == 0) {
531a97c69baSŁukasz Stelmach 		if (net_ratelimit())
532a97c69baSŁukasz Stelmach 			netif_err(ax_local, rx_err, ndev,
533a97c69baSŁukasz Stelmach 				  "Rx Bridge is not idle\n");
534a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, RXBCR2_RXB_REINIT, P0_RXBCR2);
535a97c69baSŁukasz Stelmach 
536a97c69baSŁukasz Stelmach 		entry->state = rx_err;
537a97c69baSŁukasz Stelmach 	} else {
538a97c69baSŁukasz Stelmach 		entry->state = rx_done;
539a97c69baSŁukasz Stelmach 	}
540a97c69baSŁukasz Stelmach 
541a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, ISR_RXPKT, P0_ISR);
542a97c69baSŁukasz Stelmach 
543a97c69baSŁukasz Stelmach 	ax88796c_rx_fixup(ax_local, skb);
544a97c69baSŁukasz Stelmach 
545a97c69baSŁukasz Stelmach 	return 1;
546a97c69baSŁukasz Stelmach }
547a97c69baSŁukasz Stelmach 
ax88796c_process_isr(struct ax88796c_device * ax_local)548a97c69baSŁukasz Stelmach static int ax88796c_process_isr(struct ax88796c_device *ax_local)
549a97c69baSŁukasz Stelmach {
550a97c69baSŁukasz Stelmach 	struct net_device *ndev = ax_local->ndev;
551a97c69baSŁukasz Stelmach 	int todo = 0;
552a97c69baSŁukasz Stelmach 	u16 isr;
553a97c69baSŁukasz Stelmach 
554a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
555a97c69baSŁukasz Stelmach 
556a97c69baSŁukasz Stelmach 	isr = AX_READ(&ax_local->ax_spi, P0_ISR);
557a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, isr, P0_ISR);
558a97c69baSŁukasz Stelmach 
559a97c69baSŁukasz Stelmach 	netif_dbg(ax_local, intr, ndev, "  ISR 0x%04x\n", isr);
560a97c69baSŁukasz Stelmach 
561a97c69baSŁukasz Stelmach 	if (isr & ISR_TXERR) {
562a97c69baSŁukasz Stelmach 		netif_dbg(ax_local, intr, ndev, "  TXERR interrupt\n");
563a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, TXNR_TXB_REINIT, P0_TSNR);
564a97c69baSŁukasz Stelmach 		ax_local->seq_num = 0x1f;
565a97c69baSŁukasz Stelmach 	}
566a97c69baSŁukasz Stelmach 
567a97c69baSŁukasz Stelmach 	if (isr & ISR_TXPAGES) {
568a97c69baSŁukasz Stelmach 		netif_dbg(ax_local, intr, ndev, "  TXPAGES interrupt\n");
569a97c69baSŁukasz Stelmach 		set_bit(EVENT_TX, &ax_local->flags);
570a97c69baSŁukasz Stelmach 	}
571a97c69baSŁukasz Stelmach 
572a97c69baSŁukasz Stelmach 	if (isr & ISR_LINK) {
573a97c69baSŁukasz Stelmach 		netif_dbg(ax_local, intr, ndev, "  Link change interrupt\n");
574a97c69baSŁukasz Stelmach 		phy_mac_interrupt(ax_local->ndev->phydev);
575a97c69baSŁukasz Stelmach 	}
576a97c69baSŁukasz Stelmach 
577a97c69baSŁukasz Stelmach 	if (isr & ISR_RXPKT) {
578a97c69baSŁukasz Stelmach 		netif_dbg(ax_local, intr, ndev, "  RX interrupt\n");
579a97c69baSŁukasz Stelmach 		todo = ax88796c_receive(ax_local->ndev);
580a97c69baSŁukasz Stelmach 	}
581a97c69baSŁukasz Stelmach 
582a97c69baSŁukasz Stelmach 	return todo;
583a97c69baSŁukasz Stelmach }
584a97c69baSŁukasz Stelmach 
ax88796c_interrupt(int irq,void * dev_instance)585a97c69baSŁukasz Stelmach static irqreturn_t ax88796c_interrupt(int irq, void *dev_instance)
586a97c69baSŁukasz Stelmach {
587a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local;
588a97c69baSŁukasz Stelmach 	struct net_device *ndev;
589a97c69baSŁukasz Stelmach 
590a97c69baSŁukasz Stelmach 	ndev = dev_instance;
591a97c69baSŁukasz Stelmach 	if (!ndev) {
592a97c69baSŁukasz Stelmach 		pr_err("irq %d for unknown device.\n", irq);
593a97c69baSŁukasz Stelmach 		return IRQ_RETVAL(0);
594a97c69baSŁukasz Stelmach 	}
595a97c69baSŁukasz Stelmach 	ax_local = to_ax88796c_device(ndev);
596a97c69baSŁukasz Stelmach 
597a97c69baSŁukasz Stelmach 	disable_irq_nosync(irq);
598a97c69baSŁukasz Stelmach 
599a97c69baSŁukasz Stelmach 	netif_dbg(ax_local, intr, ndev, "Interrupt occurred\n");
600a97c69baSŁukasz Stelmach 
601a97c69baSŁukasz Stelmach 	set_bit(EVENT_INTR, &ax_local->flags);
602a97c69baSŁukasz Stelmach 	schedule_work(&ax_local->ax_work);
603a97c69baSŁukasz Stelmach 
604a97c69baSŁukasz Stelmach 	return IRQ_HANDLED;
605a97c69baSŁukasz Stelmach }
606a97c69baSŁukasz Stelmach 
ax88796c_work(struct work_struct * work)607a97c69baSŁukasz Stelmach static void ax88796c_work(struct work_struct *work)
608a97c69baSŁukasz Stelmach {
609a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local =
610a97c69baSŁukasz Stelmach 			container_of(work, struct ax88796c_device, ax_work);
611a97c69baSŁukasz Stelmach 
612a97c69baSŁukasz Stelmach 	mutex_lock(&ax_local->spi_lock);
613a97c69baSŁukasz Stelmach 
614a97c69baSŁukasz Stelmach 	if (test_bit(EVENT_SET_MULTI, &ax_local->flags)) {
615a97c69baSŁukasz Stelmach 		ax88796c_set_hw_multicast(ax_local->ndev);
616a97c69baSŁukasz Stelmach 		clear_bit(EVENT_SET_MULTI, &ax_local->flags);
617a97c69baSŁukasz Stelmach 	}
618a97c69baSŁukasz Stelmach 
619a97c69baSŁukasz Stelmach 	if (test_bit(EVENT_INTR, &ax_local->flags)) {
620a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, IMR_MASKALL, P0_IMR);
621a97c69baSŁukasz Stelmach 
622a97c69baSŁukasz Stelmach 		while (ax88796c_process_isr(ax_local))
623a97c69baSŁukasz Stelmach 			/* nothing */;
624a97c69baSŁukasz Stelmach 
625a97c69baSŁukasz Stelmach 		clear_bit(EVENT_INTR, &ax_local->flags);
626a97c69baSŁukasz Stelmach 
627a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, IMR_DEFAULT, P0_IMR);
628a97c69baSŁukasz Stelmach 
629a97c69baSŁukasz Stelmach 		enable_irq(ax_local->ndev->irq);
630a97c69baSŁukasz Stelmach 	}
631a97c69baSŁukasz Stelmach 
632a97c69baSŁukasz Stelmach 	if (test_bit(EVENT_TX, &ax_local->flags)) {
633a97c69baSŁukasz Stelmach 		while (skb_queue_len(&ax_local->tx_wait_q)) {
634a97c69baSŁukasz Stelmach 			if (!ax88796c_hard_xmit(ax_local))
635a97c69baSŁukasz Stelmach 				break;
636a97c69baSŁukasz Stelmach 		}
637a97c69baSŁukasz Stelmach 
638a97c69baSŁukasz Stelmach 		clear_bit(EVENT_TX, &ax_local->flags);
639a97c69baSŁukasz Stelmach 
640a97c69baSŁukasz Stelmach 		if (netif_queue_stopped(ax_local->ndev) &&
641a97c69baSŁukasz Stelmach 		    (skb_queue_len(&ax_local->tx_wait_q) < TX_QUEUE_LOW_WATER))
642a97c69baSŁukasz Stelmach 			netif_wake_queue(ax_local->ndev);
643a97c69baSŁukasz Stelmach 	}
644a97c69baSŁukasz Stelmach 
645a97c69baSŁukasz Stelmach 	mutex_unlock(&ax_local->spi_lock);
646a97c69baSŁukasz Stelmach }
647a97c69baSŁukasz Stelmach 
ax88796c_get_stats64(struct net_device * ndev,struct rtnl_link_stats64 * stats)648a97c69baSŁukasz Stelmach static void ax88796c_get_stats64(struct net_device *ndev,
649a97c69baSŁukasz Stelmach 				 struct rtnl_link_stats64 *stats)
650a97c69baSŁukasz Stelmach {
651a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
652a97c69baSŁukasz Stelmach 	u32 rx_frame_errors = 0, rx_crc_errors = 0;
653a97c69baSŁukasz Stelmach 	u32 rx_dropped = 0, tx_dropped = 0;
654a97c69baSŁukasz Stelmach 	unsigned int start;
655a97c69baSŁukasz Stelmach 	int cpu;
656a97c69baSŁukasz Stelmach 
657a97c69baSŁukasz Stelmach 	for_each_possible_cpu(cpu) {
658a97c69baSŁukasz Stelmach 		struct ax88796c_pcpu_stats *s;
659a97c69baSŁukasz Stelmach 		u64 rx_packets, rx_bytes;
660a97c69baSŁukasz Stelmach 		u64 tx_packets, tx_bytes;
661a97c69baSŁukasz Stelmach 
662a97c69baSŁukasz Stelmach 		s = per_cpu_ptr(ax_local->stats, cpu);
663a97c69baSŁukasz Stelmach 
664a97c69baSŁukasz Stelmach 		do {
665068c38adSThomas Gleixner 			start = u64_stats_fetch_begin(&s->syncp);
666a97c69baSŁukasz Stelmach 			rx_packets = u64_stats_read(&s->rx_packets);
667a97c69baSŁukasz Stelmach 			rx_bytes   = u64_stats_read(&s->rx_bytes);
668a97c69baSŁukasz Stelmach 			tx_packets = u64_stats_read(&s->tx_packets);
669a97c69baSŁukasz Stelmach 			tx_bytes   = u64_stats_read(&s->tx_bytes);
670068c38adSThomas Gleixner 		} while (u64_stats_fetch_retry(&s->syncp, start));
671a97c69baSŁukasz Stelmach 
672a97c69baSŁukasz Stelmach 		stats->rx_packets += rx_packets;
673a97c69baSŁukasz Stelmach 		stats->rx_bytes   += rx_bytes;
674a97c69baSŁukasz Stelmach 		stats->tx_packets += tx_packets;
675a97c69baSŁukasz Stelmach 		stats->tx_bytes   += tx_bytes;
676a97c69baSŁukasz Stelmach 
677fd559a94SAlexander Lobakin 		rx_dropped      += s->rx_dropped;
678fd559a94SAlexander Lobakin 		tx_dropped      += s->tx_dropped;
679fd559a94SAlexander Lobakin 		rx_frame_errors += s->rx_frame_errors;
680fd559a94SAlexander Lobakin 		rx_crc_errors   += s->rx_crc_errors;
681a97c69baSŁukasz Stelmach 	}
682a97c69baSŁukasz Stelmach 
683a97c69baSŁukasz Stelmach 	stats->rx_dropped = rx_dropped;
684a97c69baSŁukasz Stelmach 	stats->tx_dropped = tx_dropped;
685a97c69baSŁukasz Stelmach 	stats->rx_frame_errors = rx_frame_errors;
686a97c69baSŁukasz Stelmach 	stats->rx_crc_errors = rx_crc_errors;
687a97c69baSŁukasz Stelmach }
688a97c69baSŁukasz Stelmach 
ax88796c_set_mac(struct ax88796c_device * ax_local)689a97c69baSŁukasz Stelmach static void ax88796c_set_mac(struct  ax88796c_device *ax_local)
690a97c69baSŁukasz Stelmach {
691a97c69baSŁukasz Stelmach 	u16 maccr;
692a97c69baSŁukasz Stelmach 
693a97c69baSŁukasz Stelmach 	maccr = (ax_local->link) ? MACCR_RXEN : 0;
694a97c69baSŁukasz Stelmach 
695a97c69baSŁukasz Stelmach 	switch (ax_local->speed) {
696a97c69baSŁukasz Stelmach 	case SPEED_100:
697a97c69baSŁukasz Stelmach 		maccr |= MACCR_SPEED_100;
6983c554881SNathan Chancellor 		break;
699a97c69baSŁukasz Stelmach 	case SPEED_10:
700a97c69baSŁukasz Stelmach 	case SPEED_UNKNOWN:
701a97c69baSŁukasz Stelmach 		break;
702a97c69baSŁukasz Stelmach 	default:
703a97c69baSŁukasz Stelmach 		return;
704a97c69baSŁukasz Stelmach 	}
705a97c69baSŁukasz Stelmach 
706a97c69baSŁukasz Stelmach 	switch (ax_local->duplex) {
707a97c69baSŁukasz Stelmach 	case DUPLEX_FULL:
708a97c69baSŁukasz Stelmach 		maccr |= MACCR_SPEED_100;
7093c554881SNathan Chancellor 		break;
710a97c69baSŁukasz Stelmach 	case DUPLEX_HALF:
711a97c69baSŁukasz Stelmach 	case DUPLEX_UNKNOWN:
712a97c69baSŁukasz Stelmach 		break;
713a97c69baSŁukasz Stelmach 	default:
714a97c69baSŁukasz Stelmach 		return;
715a97c69baSŁukasz Stelmach 	}
716a97c69baSŁukasz Stelmach 
717a97c69baSŁukasz Stelmach 	if (ax_local->flowctrl & AX_FC_ANEG &&
718a97c69baSŁukasz Stelmach 	    ax_local->phydev->autoneg) {
719a97c69baSŁukasz Stelmach 		maccr |= ax_local->pause ? MACCR_RXFC_ENABLE : 0;
720a97c69baSŁukasz Stelmach 		maccr |= !ax_local->pause != !ax_local->asym_pause ?
721a97c69baSŁukasz Stelmach 			MACCR_TXFC_ENABLE : 0;
722a97c69baSŁukasz Stelmach 	} else {
723a97c69baSŁukasz Stelmach 		maccr |= (ax_local->flowctrl & AX_FC_RX) ? MACCR_RXFC_ENABLE : 0;
724a97c69baSŁukasz Stelmach 		maccr |= (ax_local->flowctrl & AX_FC_TX) ? MACCR_TXFC_ENABLE : 0;
725a97c69baSŁukasz Stelmach 	}
726a97c69baSŁukasz Stelmach 
727a97c69baSŁukasz Stelmach 	mutex_lock(&ax_local->spi_lock);
728a97c69baSŁukasz Stelmach 
729a97c69baSŁukasz Stelmach 	maccr |= AX_READ(&ax_local->ax_spi, P0_MACCR) &
730a97c69baSŁukasz Stelmach 		~(MACCR_DUPLEX_FULL | MACCR_SPEED_100 |
731a97c69baSŁukasz Stelmach 		  MACCR_TXFC_ENABLE | MACCR_RXFC_ENABLE);
732a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, maccr, P0_MACCR);
733a97c69baSŁukasz Stelmach 
734a97c69baSŁukasz Stelmach 	mutex_unlock(&ax_local->spi_lock);
735a97c69baSŁukasz Stelmach }
736a97c69baSŁukasz Stelmach 
ax88796c_handle_link_change(struct net_device * ndev)737a97c69baSŁukasz Stelmach static void ax88796c_handle_link_change(struct net_device *ndev)
738a97c69baSŁukasz Stelmach {
739a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
740a97c69baSŁukasz Stelmach 	struct phy_device *phydev = ndev->phydev;
741a97c69baSŁukasz Stelmach 	bool update = false;
742a97c69baSŁukasz Stelmach 
743a97c69baSŁukasz Stelmach 	if (phydev->link && (ax_local->speed != phydev->speed ||
744a97c69baSŁukasz Stelmach 			     ax_local->duplex != phydev->duplex ||
745a97c69baSŁukasz Stelmach 			     ax_local->pause != phydev->pause ||
746a97c69baSŁukasz Stelmach 			     ax_local->asym_pause != phydev->asym_pause)) {
747a97c69baSŁukasz Stelmach 		ax_local->speed = phydev->speed;
748a97c69baSŁukasz Stelmach 		ax_local->duplex = phydev->duplex;
749a97c69baSŁukasz Stelmach 		ax_local->pause = phydev->pause;
750a97c69baSŁukasz Stelmach 		ax_local->asym_pause = phydev->asym_pause;
751a97c69baSŁukasz Stelmach 		update = true;
752a97c69baSŁukasz Stelmach 	}
753a97c69baSŁukasz Stelmach 
754a97c69baSŁukasz Stelmach 	if (phydev->link != ax_local->link) {
755a97c69baSŁukasz Stelmach 		if (!phydev->link) {
756a97c69baSŁukasz Stelmach 			ax_local->speed = SPEED_UNKNOWN;
757a97c69baSŁukasz Stelmach 			ax_local->duplex = DUPLEX_UNKNOWN;
758a97c69baSŁukasz Stelmach 		}
759a97c69baSŁukasz Stelmach 
760a97c69baSŁukasz Stelmach 		ax_local->link = phydev->link;
761a97c69baSŁukasz Stelmach 		update = true;
762a97c69baSŁukasz Stelmach 	}
763a97c69baSŁukasz Stelmach 
764a97c69baSŁukasz Stelmach 	if (update)
765a97c69baSŁukasz Stelmach 		ax88796c_set_mac(ax_local);
766a97c69baSŁukasz Stelmach 
767a97c69baSŁukasz Stelmach 	if (net_ratelimit())
768a97c69baSŁukasz Stelmach 		phy_print_status(ndev->phydev);
769a97c69baSŁukasz Stelmach }
770a97c69baSŁukasz Stelmach 
ax88796c_set_csums(struct ax88796c_device * ax_local)771a97c69baSŁukasz Stelmach static void ax88796c_set_csums(struct ax88796c_device *ax_local)
772a97c69baSŁukasz Stelmach {
773a97c69baSŁukasz Stelmach 	struct net_device *ndev = ax_local->ndev;
774a97c69baSŁukasz Stelmach 
775a97c69baSŁukasz Stelmach 	lockdep_assert_held(&ax_local->spi_lock);
776a97c69baSŁukasz Stelmach 
777a97c69baSŁukasz Stelmach 	if (ndev->features & NETIF_F_RXCSUM) {
778a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, COERCR0_DEFAULT, P4_COERCR0);
779a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, COERCR1_DEFAULT, P4_COERCR1);
780a97c69baSŁukasz Stelmach 	} else {
781a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, 0, P4_COERCR0);
782a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, 0, P4_COERCR1);
783a97c69baSŁukasz Stelmach 	}
784a97c69baSŁukasz Stelmach 
785a97c69baSŁukasz Stelmach 	if (ndev->features & NETIF_F_HW_CSUM) {
786a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, COETCR0_DEFAULT, P4_COETCR0);
787a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, COETCR1_TXPPPE, P4_COETCR1);
788a97c69baSŁukasz Stelmach 	} else {
789a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, 0, P4_COETCR0);
790a97c69baSŁukasz Stelmach 		AX_WRITE(&ax_local->ax_spi, 0, P4_COETCR1);
791a97c69baSŁukasz Stelmach 	}
792a97c69baSŁukasz Stelmach }
793a97c69baSŁukasz Stelmach 
794a97c69baSŁukasz Stelmach static int
ax88796c_open(struct net_device * ndev)795a97c69baSŁukasz Stelmach ax88796c_open(struct net_device *ndev)
796a97c69baSŁukasz Stelmach {
797a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
798a97c69baSŁukasz Stelmach 	unsigned long irq_flag = 0;
799a97c69baSŁukasz Stelmach 	int fc = AX_FC_NONE;
800a97c69baSŁukasz Stelmach 	int ret;
801a97c69baSŁukasz Stelmach 	u16 t;
802a97c69baSŁukasz Stelmach 
803a97c69baSŁukasz Stelmach 	ret = request_irq(ndev->irq, ax88796c_interrupt,
804a97c69baSŁukasz Stelmach 			  irq_flag, ndev->name, ndev);
805a97c69baSŁukasz Stelmach 	if (ret) {
806a97c69baSŁukasz Stelmach 		netdev_err(ndev, "unable to get IRQ %d (errno=%d).\n",
807a97c69baSŁukasz Stelmach 			   ndev->irq, ret);
808a97c69baSŁukasz Stelmach 		return ret;
809a97c69baSŁukasz Stelmach 	}
810a97c69baSŁukasz Stelmach 
811a97c69baSŁukasz Stelmach 	mutex_lock(&ax_local->spi_lock);
812a97c69baSŁukasz Stelmach 
813a97c69baSŁukasz Stelmach 	ret = ax88796c_soft_reset(ax_local);
814a97c69baSŁukasz Stelmach 	if (ret < 0) {
815a97c69baSŁukasz Stelmach 		free_irq(ndev->irq, ndev);
816a97c69baSŁukasz Stelmach 		mutex_unlock(&ax_local->spi_lock);
817a97c69baSŁukasz Stelmach 		return ret;
818a97c69baSŁukasz Stelmach 	}
819a97c69baSŁukasz Stelmach 	ax_local->seq_num = 0x1f;
820a97c69baSŁukasz Stelmach 
821a97c69baSŁukasz Stelmach 	ax88796c_set_mac_addr(ndev);
822a97c69baSŁukasz Stelmach 	ax88796c_set_csums(ax_local);
823a97c69baSŁukasz Stelmach 
824a97c69baSŁukasz Stelmach 	/* Disable stuffing packet */
825a97c69baSŁukasz Stelmach 	t = AX_READ(&ax_local->ax_spi, P1_RXBSPCR);
826a97c69baSŁukasz Stelmach 	t &= ~RXBSPCR_STUF_ENABLE;
827a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, t, P1_RXBSPCR);
828a97c69baSŁukasz Stelmach 
829a97c69baSŁukasz Stelmach 	/* Enable RX packet process */
830a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, RPPER_RXEN, P1_RPPER);
831a97c69baSŁukasz Stelmach 
832a97c69baSŁukasz Stelmach 	t = AX_READ(&ax_local->ax_spi, P0_FER);
833a97c69baSŁukasz Stelmach 	t |= FER_RXEN | FER_TXEN | FER_BSWAP | FER_IRQ_PULL;
834a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, t, P0_FER);
835a97c69baSŁukasz Stelmach 
836a97c69baSŁukasz Stelmach 	/* Setup LED mode */
837a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi,
838a97c69baSŁukasz Stelmach 		 (LCR_LED0_EN | LCR_LED0_DUPLEX | LCR_LED1_EN |
839a97c69baSŁukasz Stelmach 		 LCR_LED1_100MODE), P2_LCR0);
840a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi,
841a97c69baSŁukasz Stelmach 		 (AX_READ(&ax_local->ax_spi, P2_LCR1) & LCR_LED2_MASK) |
842a97c69baSŁukasz Stelmach 		 LCR_LED2_EN | LCR_LED2_LINK, P2_LCR1);
843a97c69baSŁukasz Stelmach 
844a97c69baSŁukasz Stelmach 	/* Disable PHY auto-polling */
845a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, PCR_PHYID(AX88796C_PHY_ID), P2_PCR);
846a97c69baSŁukasz Stelmach 
847a97c69baSŁukasz Stelmach 	/* Enable MAC interrupts */
848a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, IMR_DEFAULT, P0_IMR);
849a97c69baSŁukasz Stelmach 
850a97c69baSŁukasz Stelmach 	mutex_unlock(&ax_local->spi_lock);
851a97c69baSŁukasz Stelmach 
852a97c69baSŁukasz Stelmach 	/* Setup flow-control configuration */
853a97c69baSŁukasz Stelmach 	phy_support_asym_pause(ax_local->phydev);
854a97c69baSŁukasz Stelmach 
855971f5c40SNathan Chancellor 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
856a97c69baSŁukasz Stelmach 			      ax_local->phydev->advertising) ||
857a97c69baSŁukasz Stelmach 	    linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
858971f5c40SNathan Chancellor 			      ax_local->phydev->advertising))
859a97c69baSŁukasz Stelmach 		fc |= AX_FC_ANEG;
860a97c69baSŁukasz Stelmach 
861a97c69baSŁukasz Stelmach 	fc |= linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
862a97c69baSŁukasz Stelmach 				ax_local->phydev->advertising) ? AX_FC_RX : 0;
863a97c69baSŁukasz Stelmach 	fc |= (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
864a97c69baSŁukasz Stelmach 				 ax_local->phydev->advertising) !=
865a97c69baSŁukasz Stelmach 	       linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
866a97c69baSŁukasz Stelmach 				 ax_local->phydev->advertising)) ? AX_FC_TX : 0;
867a97c69baSŁukasz Stelmach 	ax_local->flowctrl = fc;
868a97c69baSŁukasz Stelmach 
869a97c69baSŁukasz Stelmach 	phy_start(ax_local->ndev->phydev);
870a97c69baSŁukasz Stelmach 
871a97c69baSŁukasz Stelmach 	netif_start_queue(ndev);
872a97c69baSŁukasz Stelmach 
873a97c69baSŁukasz Stelmach 	spi_message_init(&ax_local->ax_spi.rx_msg);
874a97c69baSŁukasz Stelmach 
875a97c69baSŁukasz Stelmach 	return 0;
876a97c69baSŁukasz Stelmach }
877a97c69baSŁukasz Stelmach 
878a97c69baSŁukasz Stelmach static int
ax88796c_close(struct net_device * ndev)879a97c69baSŁukasz Stelmach ax88796c_close(struct net_device *ndev)
880a97c69baSŁukasz Stelmach {
881a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
882a97c69baSŁukasz Stelmach 
883a97c69baSŁukasz Stelmach 	phy_stop(ndev->phydev);
884a97c69baSŁukasz Stelmach 
885a97c69baSŁukasz Stelmach 	/* We lock the mutex early not only to protect the device
886a97c69baSŁukasz Stelmach 	 * against concurrent access, but also avoid waking up the
887a97c69baSŁukasz Stelmach 	 * queue in ax88796c_work(). phy_stop() needs to be called
888a97c69baSŁukasz Stelmach 	 * before because it locks the mutex to access SPI.
889a97c69baSŁukasz Stelmach 	 */
890a97c69baSŁukasz Stelmach 	mutex_lock(&ax_local->spi_lock);
891a97c69baSŁukasz Stelmach 
892a97c69baSŁukasz Stelmach 	netif_stop_queue(ndev);
893a97c69baSŁukasz Stelmach 
894a97c69baSŁukasz Stelmach 	/* No more work can be scheduled now. Make any pending work,
895a97c69baSŁukasz Stelmach 	 * including one already waiting for the mutex to be unlocked,
896a97c69baSŁukasz Stelmach 	 * NOP.
897a97c69baSŁukasz Stelmach 	 */
898a97c69baSŁukasz Stelmach 	netif_dbg(ax_local, ifdown, ndev, "clearing bits\n");
899a97c69baSŁukasz Stelmach 	clear_bit(EVENT_SET_MULTI, &ax_local->flags);
900a97c69baSŁukasz Stelmach 	clear_bit(EVENT_INTR, &ax_local->flags);
901a97c69baSŁukasz Stelmach 	clear_bit(EVENT_TX, &ax_local->flags);
902a97c69baSŁukasz Stelmach 
903a97c69baSŁukasz Stelmach 	/* Disable MAC interrupts */
904a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, IMR_MASKALL, P0_IMR);
905a97c69baSŁukasz Stelmach 	__skb_queue_purge(&ax_local->tx_wait_q);
906a97c69baSŁukasz Stelmach 	ax88796c_soft_reset(ax_local);
907a97c69baSŁukasz Stelmach 
908a97c69baSŁukasz Stelmach 	mutex_unlock(&ax_local->spi_lock);
909a97c69baSŁukasz Stelmach 
910a97c69baSŁukasz Stelmach 	cancel_work_sync(&ax_local->ax_work);
911a97c69baSŁukasz Stelmach 
912a97c69baSŁukasz Stelmach 	free_irq(ndev->irq, ndev);
913a97c69baSŁukasz Stelmach 
914a97c69baSŁukasz Stelmach 	return 0;
915a97c69baSŁukasz Stelmach }
916a97c69baSŁukasz Stelmach 
917a97c69baSŁukasz Stelmach static int
ax88796c_set_features(struct net_device * ndev,netdev_features_t features)918a97c69baSŁukasz Stelmach ax88796c_set_features(struct net_device *ndev, netdev_features_t features)
919a97c69baSŁukasz Stelmach {
920a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
921a97c69baSŁukasz Stelmach 	netdev_features_t changed = features ^ ndev->features;
922a97c69baSŁukasz Stelmach 
923a97c69baSŁukasz Stelmach 	if (!(changed & (NETIF_F_RXCSUM | NETIF_F_HW_CSUM)))
924a97c69baSŁukasz Stelmach 		return 0;
925a97c69baSŁukasz Stelmach 
926a97c69baSŁukasz Stelmach 	ndev->features = features;
927a97c69baSŁukasz Stelmach 
928a97c69baSŁukasz Stelmach 	if (changed & (NETIF_F_RXCSUM | NETIF_F_HW_CSUM))
929a97c69baSŁukasz Stelmach 		ax88796c_set_csums(ax_local);
930a97c69baSŁukasz Stelmach 
931a97c69baSŁukasz Stelmach 	return 0;
932a97c69baSŁukasz Stelmach }
933a97c69baSŁukasz Stelmach 
934a97c69baSŁukasz Stelmach static const struct net_device_ops ax88796c_netdev_ops = {
935a97c69baSŁukasz Stelmach 	.ndo_open		= ax88796c_open,
936a97c69baSŁukasz Stelmach 	.ndo_stop		= ax88796c_close,
937a97c69baSŁukasz Stelmach 	.ndo_start_xmit		= ax88796c_start_xmit,
938a97c69baSŁukasz Stelmach 	.ndo_get_stats64	= ax88796c_get_stats64,
9399dcc0071SArnd Bergmann 	.ndo_eth_ioctl		= ax88796c_ioctl,
940a97c69baSŁukasz Stelmach 	.ndo_set_mac_address	= eth_mac_addr,
941a97c69baSŁukasz Stelmach 	.ndo_set_features	= ax88796c_set_features,
942a97c69baSŁukasz Stelmach };
943a97c69baSŁukasz Stelmach 
ax88796c_hard_reset(struct ax88796c_device * ax_local)944a97c69baSŁukasz Stelmach static int ax88796c_hard_reset(struct ax88796c_device *ax_local)
945a97c69baSŁukasz Stelmach {
946a97c69baSŁukasz Stelmach 	struct device *dev = (struct device *)&ax_local->spi->dev;
947a97c69baSŁukasz Stelmach 	struct gpio_desc *reset_gpio;
948a97c69baSŁukasz Stelmach 
949a97c69baSŁukasz Stelmach 	/* reset info */
950a97c69baSŁukasz Stelmach 	reset_gpio = gpiod_get(dev, "reset", 0);
951a97c69baSŁukasz Stelmach 	if (IS_ERR(reset_gpio)) {
952a97c69baSŁukasz Stelmach 		dev_err(dev, "Could not get 'reset' GPIO: %ld", PTR_ERR(reset_gpio));
953a97c69baSŁukasz Stelmach 		return PTR_ERR(reset_gpio);
954a97c69baSŁukasz Stelmach 	}
955a97c69baSŁukasz Stelmach 
956a97c69baSŁukasz Stelmach 	/* set reset */
957a97c69baSŁukasz Stelmach 	gpiod_direction_output(reset_gpio, 1);
958a97c69baSŁukasz Stelmach 	msleep(100);
959a97c69baSŁukasz Stelmach 	gpiod_direction_output(reset_gpio, 0);
960a97c69baSŁukasz Stelmach 	gpiod_put(reset_gpio);
961a97c69baSŁukasz Stelmach 	msleep(20);
962a97c69baSŁukasz Stelmach 
963a97c69baSŁukasz Stelmach 	return 0;
964a97c69baSŁukasz Stelmach }
965a97c69baSŁukasz Stelmach 
ax88796c_probe(struct spi_device * spi)966a97c69baSŁukasz Stelmach static int ax88796c_probe(struct spi_device *spi)
967a97c69baSŁukasz Stelmach {
968a97c69baSŁukasz Stelmach 	char phy_id[MII_BUS_ID_SIZE + 3];
969a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local;
970a97c69baSŁukasz Stelmach 	struct net_device *ndev;
971a97c69baSŁukasz Stelmach 	u16 temp;
972a97c69baSŁukasz Stelmach 	int ret;
973a97c69baSŁukasz Stelmach 
974a97c69baSŁukasz Stelmach 	ndev = devm_alloc_etherdev(&spi->dev, sizeof(*ax_local));
975a97c69baSŁukasz Stelmach 	if (!ndev)
976a97c69baSŁukasz Stelmach 		return -ENOMEM;
977a97c69baSŁukasz Stelmach 
978a97c69baSŁukasz Stelmach 	SET_NETDEV_DEV(ndev, &spi->dev);
979a97c69baSŁukasz Stelmach 
980a97c69baSŁukasz Stelmach 	ax_local = to_ax88796c_device(ndev);
981a97c69baSŁukasz Stelmach 
982a97c69baSŁukasz Stelmach 	dev_set_drvdata(&spi->dev, ax_local);
983a97c69baSŁukasz Stelmach 	ax_local->spi = spi;
984a97c69baSŁukasz Stelmach 	ax_local->ax_spi.spi = spi;
985a97c69baSŁukasz Stelmach 
986a97c69baSŁukasz Stelmach 	ax_local->stats =
987a97c69baSŁukasz Stelmach 		devm_netdev_alloc_pcpu_stats(&spi->dev,
988a97c69baSŁukasz Stelmach 					     struct ax88796c_pcpu_stats);
989a97c69baSŁukasz Stelmach 	if (!ax_local->stats)
990a97c69baSŁukasz Stelmach 		return -ENOMEM;
991a97c69baSŁukasz Stelmach 
992a97c69baSŁukasz Stelmach 	ax_local->ndev = ndev;
993a97c69baSŁukasz Stelmach 	ax_local->priv_flags |= comp ? AX_CAP_COMP : 0;
994a97c69baSŁukasz Stelmach 	ax_local->msg_enable = msg_enable;
995a97c69baSŁukasz Stelmach 	mutex_init(&ax_local->spi_lock);
996a97c69baSŁukasz Stelmach 
997a97c69baSŁukasz Stelmach 	ax_local->mdiobus = devm_mdiobus_alloc(&spi->dev);
998a97c69baSŁukasz Stelmach 	if (!ax_local->mdiobus)
999a97c69baSŁukasz Stelmach 		return -ENOMEM;
1000a97c69baSŁukasz Stelmach 
1001a97c69baSŁukasz Stelmach 	ax_local->mdiobus->priv = ax_local;
1002a97c69baSŁukasz Stelmach 	ax_local->mdiobus->read = ax88796c_mdio_read;
1003a97c69baSŁukasz Stelmach 	ax_local->mdiobus->write = ax88796c_mdio_write;
1004a97c69baSŁukasz Stelmach 	ax_local->mdiobus->name = "ax88976c-mdiobus";
1005a97c69baSŁukasz Stelmach 	ax_local->mdiobus->phy_mask = (u32)~BIT(AX88796C_PHY_ID);
1006a97c69baSŁukasz Stelmach 	ax_local->mdiobus->parent = &spi->dev;
1007a97c69baSŁukasz Stelmach 
1008a97c69baSŁukasz Stelmach 	snprintf(ax_local->mdiobus->id, MII_BUS_ID_SIZE,
100925fd0550SAmit Kumar Mahapatra 		 "ax88796c-%s.%u", dev_name(&spi->dev), spi_get_chipselect(spi, 0));
1010a97c69baSŁukasz Stelmach 
1011a97c69baSŁukasz Stelmach 	ret = devm_mdiobus_register(&spi->dev, ax_local->mdiobus);
1012a97c69baSŁukasz Stelmach 	if (ret < 0) {
1013a97c69baSŁukasz Stelmach 		dev_err(&spi->dev, "Could not register MDIO bus\n");
1014a97c69baSŁukasz Stelmach 		return ret;
1015a97c69baSŁukasz Stelmach 	}
1016a97c69baSŁukasz Stelmach 
1017a97c69baSŁukasz Stelmach 	if (netif_msg_probe(ax_local)) {
1018a97c69baSŁukasz Stelmach 		dev_info(&spi->dev, "AX88796C-SPI Configuration:\n");
1019a97c69baSŁukasz Stelmach 		dev_info(&spi->dev, "    Compression : %s\n",
1020a97c69baSŁukasz Stelmach 			 ax_local->priv_flags & AX_CAP_COMP ? "ON" : "OFF");
1021a97c69baSŁukasz Stelmach 	}
1022a97c69baSŁukasz Stelmach 
1023a97c69baSŁukasz Stelmach 	ndev->irq = spi->irq;
1024a97c69baSŁukasz Stelmach 	ndev->netdev_ops = &ax88796c_netdev_ops;
1025a97c69baSŁukasz Stelmach 	ndev->ethtool_ops = &ax88796c_ethtool_ops;
1026a97c69baSŁukasz Stelmach 	ndev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
1027a97c69baSŁukasz Stelmach 	ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
1028a97c69baSŁukasz Stelmach 	ndev->needed_headroom = TX_OVERHEAD;
1029a97c69baSŁukasz Stelmach 	ndev->needed_tailroom = TX_EOP_SIZE;
1030a97c69baSŁukasz Stelmach 
1031a97c69baSŁukasz Stelmach 	mutex_lock(&ax_local->spi_lock);
1032a97c69baSŁukasz Stelmach 
1033a97c69baSŁukasz Stelmach 	/* ax88796c gpio reset */
1034a97c69baSŁukasz Stelmach 	ax88796c_hard_reset(ax_local);
1035a97c69baSŁukasz Stelmach 
1036a97c69baSŁukasz Stelmach 	/* Reset AX88796C */
1037a97c69baSŁukasz Stelmach 	ret = ax88796c_soft_reset(ax_local);
1038a97c69baSŁukasz Stelmach 	if (ret < 0) {
1039a97c69baSŁukasz Stelmach 		ret = -ENODEV;
1040a97c69baSŁukasz Stelmach 		mutex_unlock(&ax_local->spi_lock);
1041a97c69baSŁukasz Stelmach 		goto err;
1042a97c69baSŁukasz Stelmach 	}
1043a97c69baSŁukasz Stelmach 	/* Check board revision */
1044a97c69baSŁukasz Stelmach 	temp = AX_READ(&ax_local->ax_spi, P2_CRIR);
1045a97c69baSŁukasz Stelmach 	if ((temp & 0xF) != 0x0) {
1046a97c69baSŁukasz Stelmach 		dev_err(&spi->dev, "spi read failed: %d\n", temp);
1047a97c69baSŁukasz Stelmach 		ret = -ENODEV;
1048a97c69baSŁukasz Stelmach 		mutex_unlock(&ax_local->spi_lock);
1049a97c69baSŁukasz Stelmach 		goto err;
1050a97c69baSŁukasz Stelmach 	}
1051a97c69baSŁukasz Stelmach 
1052a97c69baSŁukasz Stelmach 	/*Reload EEPROM*/
1053a97c69baSŁukasz Stelmach 	ax88796c_reload_eeprom(ax_local);
1054a97c69baSŁukasz Stelmach 
1055a97c69baSŁukasz Stelmach 	ax88796c_load_mac_addr(ndev);
1056a97c69baSŁukasz Stelmach 
1057a97c69baSŁukasz Stelmach 	if (netif_msg_probe(ax_local))
1058a97c69baSŁukasz Stelmach 		dev_info(&spi->dev,
1059a97c69baSŁukasz Stelmach 			 "irq %d, MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n",
1060a97c69baSŁukasz Stelmach 			 ndev->irq,
1061a97c69baSŁukasz Stelmach 			 ndev->dev_addr[0], ndev->dev_addr[1],
1062a97c69baSŁukasz Stelmach 			 ndev->dev_addr[2], ndev->dev_addr[3],
1063a97c69baSŁukasz Stelmach 			 ndev->dev_addr[4], ndev->dev_addr[5]);
1064a97c69baSŁukasz Stelmach 
1065a97c69baSŁukasz Stelmach 	/* Disable power saving */
1066a97c69baSŁukasz Stelmach 	AX_WRITE(&ax_local->ax_spi, (AX_READ(&ax_local->ax_spi, P0_PSCR)
1067a97c69baSŁukasz Stelmach 				     & PSCR_PS_MASK) | PSCR_PS_D0, P0_PSCR);
1068a97c69baSŁukasz Stelmach 
1069a97c69baSŁukasz Stelmach 	mutex_unlock(&ax_local->spi_lock);
1070a97c69baSŁukasz Stelmach 
1071a97c69baSŁukasz Stelmach 	INIT_WORK(&ax_local->ax_work, ax88796c_work);
1072a97c69baSŁukasz Stelmach 
1073a97c69baSŁukasz Stelmach 	skb_queue_head_init(&ax_local->tx_wait_q);
1074a97c69baSŁukasz Stelmach 
1075a97c69baSŁukasz Stelmach 	snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT,
1076a97c69baSŁukasz Stelmach 		 ax_local->mdiobus->id, AX88796C_PHY_ID);
1077a97c69baSŁukasz Stelmach 	ax_local->phydev = phy_connect(ax_local->ndev, phy_id,
1078a97c69baSŁukasz Stelmach 				       ax88796c_handle_link_change,
1079a97c69baSŁukasz Stelmach 				       PHY_INTERFACE_MODE_MII);
1080a97c69baSŁukasz Stelmach 	if (IS_ERR(ax_local->phydev)) {
1081a97c69baSŁukasz Stelmach 		ret = PTR_ERR(ax_local->phydev);
1082a97c69baSŁukasz Stelmach 		goto err;
1083a97c69baSŁukasz Stelmach 	}
1084a97c69baSŁukasz Stelmach 	ax_local->phydev->irq = PHY_POLL;
1085a97c69baSŁukasz Stelmach 
1086a97c69baSŁukasz Stelmach 	ret = devm_register_netdev(&spi->dev, ndev);
1087a97c69baSŁukasz Stelmach 	if (ret) {
1088a97c69baSŁukasz Stelmach 		dev_err(&spi->dev, "failed to register a network device\n");
1089a97c69baSŁukasz Stelmach 		goto err_phy_dis;
1090a97c69baSŁukasz Stelmach 	}
1091a97c69baSŁukasz Stelmach 
1092a97c69baSŁukasz Stelmach 	netif_info(ax_local, probe, ndev, "%s %s registered\n",
1093a97c69baSŁukasz Stelmach 		   dev_driver_string(&spi->dev),
1094a97c69baSŁukasz Stelmach 		   dev_name(&spi->dev));
1095a97c69baSŁukasz Stelmach 	phy_attached_info(ax_local->phydev);
1096a97c69baSŁukasz Stelmach 
1097a97c69baSŁukasz Stelmach 	return 0;
1098a97c69baSŁukasz Stelmach 
1099a97c69baSŁukasz Stelmach err_phy_dis:
1100a97c69baSŁukasz Stelmach 	phy_disconnect(ax_local->phydev);
1101a97c69baSŁukasz Stelmach err:
1102a97c69baSŁukasz Stelmach 	return ret;
1103a97c69baSŁukasz Stelmach }
1104a97c69baSŁukasz Stelmach 
ax88796c_remove(struct spi_device * spi)1105a0386bbaSUwe Kleine-König static void ax88796c_remove(struct spi_device *spi)
1106a97c69baSŁukasz Stelmach {
1107a97c69baSŁukasz Stelmach 	struct ax88796c_device *ax_local = dev_get_drvdata(&spi->dev);
1108a97c69baSŁukasz Stelmach 	struct net_device *ndev = ax_local->ndev;
1109a97c69baSŁukasz Stelmach 
1110a97c69baSŁukasz Stelmach 	phy_disconnect(ndev->phydev);
1111a97c69baSŁukasz Stelmach 
1112a97c69baSŁukasz Stelmach 	netif_info(ax_local, probe, ndev, "removing network device %s %s\n",
1113a97c69baSŁukasz Stelmach 		   dev_driver_string(&spi->dev),
1114a97c69baSŁukasz Stelmach 		   dev_name(&spi->dev));
1115a97c69baSŁukasz Stelmach }
1116a97c69baSŁukasz Stelmach 
11176789a4c0SJakub Kicinski #ifdef CONFIG_OF
1118a97c69baSŁukasz Stelmach static const struct of_device_id ax88796c_dt_ids[] = {
1119a97c69baSŁukasz Stelmach 	{ .compatible = "asix,ax88796c" },
1120a97c69baSŁukasz Stelmach 	{},
1121a97c69baSŁukasz Stelmach };
1122a97c69baSŁukasz Stelmach MODULE_DEVICE_TABLE(of, ax88796c_dt_ids);
11236789a4c0SJakub Kicinski #endif
1124a97c69baSŁukasz Stelmach 
1125a97c69baSŁukasz Stelmach static const struct spi_device_id asix_id[] = {
1126a97c69baSŁukasz Stelmach 	{ "ax88796c", 0 },
1127a97c69baSŁukasz Stelmach 	{ }
1128a97c69baSŁukasz Stelmach };
1129a97c69baSŁukasz Stelmach MODULE_DEVICE_TABLE(spi, asix_id);
1130a97c69baSŁukasz Stelmach 
1131a97c69baSŁukasz Stelmach static struct spi_driver ax88796c_spi_driver = {
1132a97c69baSŁukasz Stelmach 	.driver = {
1133a97c69baSŁukasz Stelmach 		.name = DRV_NAME,
1134a97c69baSŁukasz Stelmach 		.of_match_table = of_match_ptr(ax88796c_dt_ids),
1135a97c69baSŁukasz Stelmach 	},
1136a97c69baSŁukasz Stelmach 	.probe = ax88796c_probe,
1137a97c69baSŁukasz Stelmach 	.remove = ax88796c_remove,
1138a97c69baSŁukasz Stelmach 	.id_table = asix_id,
1139a97c69baSŁukasz Stelmach };
1140a97c69baSŁukasz Stelmach 
ax88796c_spi_init(void)1141a97c69baSŁukasz Stelmach static __init int ax88796c_spi_init(void)
1142a97c69baSŁukasz Stelmach {
1143a97c69baSŁukasz Stelmach 	int ret;
1144a97c69baSŁukasz Stelmach 
1145a97c69baSŁukasz Stelmach 	bitmap_zero(ax88796c_no_regs_mask, AX88796C_REGDUMP_LEN);
1146a97c69baSŁukasz Stelmach 	ret = bitmap_parse(no_regs_list, 35,
1147a97c69baSŁukasz Stelmach 			   ax88796c_no_regs_mask, AX88796C_REGDUMP_LEN);
1148a97c69baSŁukasz Stelmach 	if (ret) {
1149a97c69baSŁukasz Stelmach 		bitmap_fill(ax88796c_no_regs_mask, AX88796C_REGDUMP_LEN);
1150a97c69baSŁukasz Stelmach 		pr_err("Invalid bitmap description, masking all registers\n");
1151a97c69baSŁukasz Stelmach 	}
1152a97c69baSŁukasz Stelmach 
1153a97c69baSŁukasz Stelmach 	return spi_register_driver(&ax88796c_spi_driver);
1154a97c69baSŁukasz Stelmach }
1155a97c69baSŁukasz Stelmach 
ax88796c_spi_exit(void)1156a97c69baSŁukasz Stelmach static __exit void ax88796c_spi_exit(void)
1157a97c69baSŁukasz Stelmach {
1158a97c69baSŁukasz Stelmach 	spi_unregister_driver(&ax88796c_spi_driver);
1159a97c69baSŁukasz Stelmach }
1160a97c69baSŁukasz Stelmach 
1161a97c69baSŁukasz Stelmach module_init(ax88796c_spi_init);
1162a97c69baSŁukasz Stelmach module_exit(ax88796c_spi_exit);
1163a97c69baSŁukasz Stelmach 
1164a97c69baSŁukasz Stelmach MODULE_AUTHOR("Łukasz Stelmach <l.stelmach@samsung.com>");
1165a97c69baSŁukasz Stelmach MODULE_DESCRIPTION("ASIX AX88796C SPI Ethernet driver");
1166a97c69baSŁukasz Stelmach MODULE_LICENSE("GPL");
1167