xref: /openbmc/linux/drivers/net/ethernet/atheros/alx/main.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1ab69bde6SJohannes Berg /*
24a5fe57eSJohannes Berg  * Copyright (c) 2013, 2021 Johannes Berg <johannes@sipsolutions.net>
3ab69bde6SJohannes Berg  *
4ab69bde6SJohannes Berg  *  This file is free software: you may copy, redistribute and/or modify it
5ab69bde6SJohannes Berg  *  under the terms of the GNU General Public License as published by the
6ab69bde6SJohannes Berg  *  Free Software Foundation, either version 2 of the License, or (at your
7ab69bde6SJohannes Berg  *  option) any later version.
8ab69bde6SJohannes Berg  *
9ab69bde6SJohannes Berg  *  This file is distributed in the hope that it will be useful, but
10ab69bde6SJohannes Berg  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11ab69bde6SJohannes Berg  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12ab69bde6SJohannes Berg  *  General Public License for more details.
13ab69bde6SJohannes Berg  *
14ab69bde6SJohannes Berg  *  You should have received a copy of the GNU General Public License
15ab69bde6SJohannes Berg  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16ab69bde6SJohannes Berg  *
17ab69bde6SJohannes Berg  * This file incorporates work covered by the following copyright and
18ab69bde6SJohannes Berg  * permission notice:
19ab69bde6SJohannes Berg  *
20ab69bde6SJohannes Berg  * Copyright (c) 2012 Qualcomm Atheros, Inc.
21ab69bde6SJohannes Berg  *
22ab69bde6SJohannes Berg  * Permission to use, copy, modify, and/or distribute this software for any
23ab69bde6SJohannes Berg  * purpose with or without fee is hereby granted, provided that the above
24ab69bde6SJohannes Berg  * copyright notice and this permission notice appear in all copies.
25ab69bde6SJohannes Berg  *
26ab69bde6SJohannes Berg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
27ab69bde6SJohannes Berg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
28ab69bde6SJohannes Berg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
29ab69bde6SJohannes Berg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
30ab69bde6SJohannes Berg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
31ab69bde6SJohannes Berg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32ab69bde6SJohannes Berg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33ab69bde6SJohannes Berg  */
34ab69bde6SJohannes Berg 
35ab69bde6SJohannes Berg #include <linux/module.h>
36ab69bde6SJohannes Berg #include <linux/pci.h>
37ab69bde6SJohannes Berg #include <linux/interrupt.h>
38ab69bde6SJohannes Berg #include <linux/ip.h>
39ab69bde6SJohannes Berg #include <linux/ipv6.h>
40ab69bde6SJohannes Berg #include <linux/if_vlan.h>
41ab69bde6SJohannes Berg #include <linux/mdio.h>
42ab69bde6SJohannes Berg #include <linux/bitops.h>
43ab69bde6SJohannes Berg #include <linux/netdevice.h>
44ab69bde6SJohannes Berg #include <linux/etherdevice.h>
45ab69bde6SJohannes Berg #include <net/ip6_checksum.h>
46ab69bde6SJohannes Berg #include <linux/crc32.h>
47ab69bde6SJohannes Berg #include "alx.h"
48ab69bde6SJohannes Berg #include "hw.h"
49ab69bde6SJohannes Berg #include "reg.h"
50ab69bde6SJohannes Berg 
5171311931SRasmus Villemoes static const char alx_drv_name[] = "alx";
52ab69bde6SJohannes Berg 
alx_free_txbuf(struct alx_tx_queue * txq,int entry)53702e8418STobias Regnery static void alx_free_txbuf(struct alx_tx_queue *txq, int entry)
54ab69bde6SJohannes Berg {
55702e8418STobias Regnery 	struct alx_buffer *txb = &txq->bufs[entry];
56ab69bde6SJohannes Berg 
57ab69bde6SJohannes Berg 	if (dma_unmap_len(txb, size)) {
58702e8418STobias Regnery 		dma_unmap_single(txq->dev,
59ab69bde6SJohannes Berg 				 dma_unmap_addr(txb, dma),
60ab69bde6SJohannes Berg 				 dma_unmap_len(txb, size),
61ab69bde6SJohannes Berg 				 DMA_TO_DEVICE);
62ab69bde6SJohannes Berg 		dma_unmap_len_set(txb, size, 0);
63ab69bde6SJohannes Berg 	}
64ab69bde6SJohannes Berg 
65ab69bde6SJohannes Berg 	if (txb->skb) {
66ab69bde6SJohannes Berg 		dev_kfree_skb_any(txb->skb);
67ab69bde6SJohannes Berg 		txb->skb = NULL;
68ab69bde6SJohannes Berg 	}
69ab69bde6SJohannes Berg }
70ab69bde6SJohannes Berg 
alx_refill_rx_ring(struct alx_priv * alx,gfp_t gfp)71ab69bde6SJohannes Berg static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp)
72ab69bde6SJohannes Berg {
73702e8418STobias Regnery 	struct alx_rx_queue *rxq = alx->qnapi[0]->rxq;
74ab69bde6SJohannes Berg 	struct sk_buff *skb;
75ab69bde6SJohannes Berg 	struct alx_buffer *cur_buf;
76ab69bde6SJohannes Berg 	dma_addr_t dma;
77ab69bde6SJohannes Berg 	u16 cur, next, count = 0;
78ab69bde6SJohannes Berg 
79ab69bde6SJohannes Berg 	next = cur = rxq->write_idx;
80ab69bde6SJohannes Berg 	if (++next == alx->rx_ringsz)
81ab69bde6SJohannes Berg 		next = 0;
82ab69bde6SJohannes Berg 	cur_buf = &rxq->bufs[cur];
83ab69bde6SJohannes Berg 
84ab69bde6SJohannes Berg 	while (!cur_buf->skb && next != rxq->read_idx) {
85ab69bde6SJohannes Berg 		struct alx_rfd *rfd = &rxq->rfd[cur];
86ab69bde6SJohannes Berg 
87881d0327SFeng Tang 		/*
88881d0327SFeng Tang 		 * When DMA RX address is set to something like
89881d0327SFeng Tang 		 * 0x....fc0, it will be very likely to cause DMA
90881d0327SFeng Tang 		 * RFD overflow issue.
91881d0327SFeng Tang 		 *
92881d0327SFeng Tang 		 * To work around it, we apply rx skb with 64 bytes
93881d0327SFeng Tang 		 * longer space, and offset the address whenever
94881d0327SFeng Tang 		 * 0x....fc0 is detected.
95881d0327SFeng Tang 		 */
96881d0327SFeng Tang 		skb = __netdev_alloc_skb(alx->dev, alx->rxbuf_size + 64, gfp);
97ab69bde6SJohannes Berg 		if (!skb)
98ab69bde6SJohannes Berg 			break;
99881d0327SFeng Tang 
100881d0327SFeng Tang 		if (((unsigned long)skb->data & 0xfff) == 0xfc0)
101881d0327SFeng Tang 			skb_reserve(skb, 64);
102881d0327SFeng Tang 
103ab69bde6SJohannes Berg 		dma = dma_map_single(&alx->hw.pdev->dev,
104ab69bde6SJohannes Berg 				     skb->data, alx->rxbuf_size,
105ab69bde6SJohannes Berg 				     DMA_FROM_DEVICE);
106ab69bde6SJohannes Berg 		if (dma_mapping_error(&alx->hw.pdev->dev, dma)) {
107ab69bde6SJohannes Berg 			dev_kfree_skb(skb);
108ab69bde6SJohannes Berg 			break;
109ab69bde6SJohannes Berg 		}
110ab69bde6SJohannes Berg 
111ab69bde6SJohannes Berg 		/* Unfortunately, RX descriptor buffers must be 4-byte
112ab69bde6SJohannes Berg 		 * aligned, so we can't use IP alignment.
113ab69bde6SJohannes Berg 		 */
114ab69bde6SJohannes Berg 		if (WARN_ON(dma & 3)) {
115ab69bde6SJohannes Berg 			dev_kfree_skb(skb);
116ab69bde6SJohannes Berg 			break;
117ab69bde6SJohannes Berg 		}
118ab69bde6SJohannes Berg 
119ab69bde6SJohannes Berg 		cur_buf->skb = skb;
120ab69bde6SJohannes Berg 		dma_unmap_len_set(cur_buf, size, alx->rxbuf_size);
121ab69bde6SJohannes Berg 		dma_unmap_addr_set(cur_buf, dma, dma);
122ab69bde6SJohannes Berg 		rfd->addr = cpu_to_le64(dma);
123ab69bde6SJohannes Berg 
124ab69bde6SJohannes Berg 		cur = next;
125ab69bde6SJohannes Berg 		if (++next == alx->rx_ringsz)
126ab69bde6SJohannes Berg 			next = 0;
127ab69bde6SJohannes Berg 		cur_buf = &rxq->bufs[cur];
128ab69bde6SJohannes Berg 		count++;
129ab69bde6SJohannes Berg 	}
130ab69bde6SJohannes Berg 
131ab69bde6SJohannes Berg 	if (count) {
132ab69bde6SJohannes Berg 		/* flush all updates before updating hardware */
133ab69bde6SJohannes Berg 		wmb();
134ab69bde6SJohannes Berg 		rxq->write_idx = cur;
135ab69bde6SJohannes Berg 		alx_write_mem16(&alx->hw, ALX_RFD_PIDX, cur);
136ab69bde6SJohannes Berg 	}
137ab69bde6SJohannes Berg 
138ab69bde6SJohannes Berg 	return count;
139ab69bde6SJohannes Berg }
140ab69bde6SJohannes Berg 
alx_tx_queue_mapping(struct alx_priv * alx,struct sk_buff * skb)1412e06826bSTobias Regnery static struct alx_tx_queue *alx_tx_queue_mapping(struct alx_priv *alx,
1422e06826bSTobias Regnery 						 struct sk_buff *skb)
1432e06826bSTobias Regnery {
1442e06826bSTobias Regnery 	unsigned int r_idx = skb->queue_mapping;
1452e06826bSTobias Regnery 
1462e06826bSTobias Regnery 	if (r_idx >= alx->num_txq)
1472e06826bSTobias Regnery 		r_idx = r_idx % alx->num_txq;
1482e06826bSTobias Regnery 
1492e06826bSTobias Regnery 	return alx->qnapi[r_idx]->txq;
1502e06826bSTobias Regnery }
1512e06826bSTobias Regnery 
alx_get_tx_queue(const struct alx_tx_queue * txq)1522e06826bSTobias Regnery static struct netdev_queue *alx_get_tx_queue(const struct alx_tx_queue *txq)
1532e06826bSTobias Regnery {
1542e06826bSTobias Regnery 	return netdev_get_tx_queue(txq->netdev, txq->queue_idx);
1552e06826bSTobias Regnery }
1562e06826bSTobias Regnery 
alx_tpd_avail(struct alx_tx_queue * txq)157702e8418STobias Regnery static inline int alx_tpd_avail(struct alx_tx_queue *txq)
158ab69bde6SJohannes Berg {
159ab69bde6SJohannes Berg 	if (txq->write_idx >= txq->read_idx)
160702e8418STobias Regnery 		return txq->count + txq->read_idx - txq->write_idx - 1;
161ab69bde6SJohannes Berg 	return txq->read_idx - txq->write_idx - 1;
162ab69bde6SJohannes Berg }
163ab69bde6SJohannes Berg 
alx_clean_tx_irq(struct alx_tx_queue * txq)164702e8418STobias Regnery static bool alx_clean_tx_irq(struct alx_tx_queue *txq)
165ab69bde6SJohannes Berg {
166702e8418STobias Regnery 	struct alx_priv *alx;
1672e06826bSTobias Regnery 	struct netdev_queue *tx_queue;
168ab69bde6SJohannes Berg 	u16 hw_read_idx, sw_read_idx;
169ab69bde6SJohannes Berg 	unsigned int total_bytes = 0, total_packets = 0;
170ab69bde6SJohannes Berg 	int budget = ALX_DEFAULT_TX_WORK;
171ab69bde6SJohannes Berg 
172702e8418STobias Regnery 	alx = netdev_priv(txq->netdev);
1732e06826bSTobias Regnery 	tx_queue = alx_get_tx_queue(txq);
174702e8418STobias Regnery 
175ab69bde6SJohannes Berg 	sw_read_idx = txq->read_idx;
1762e06826bSTobias Regnery 	hw_read_idx = alx_read_mem16(&alx->hw, txq->c_reg);
177ab69bde6SJohannes Berg 
178ab69bde6SJohannes Berg 	if (sw_read_idx != hw_read_idx) {
179ab69bde6SJohannes Berg 		while (sw_read_idx != hw_read_idx && budget > 0) {
180ab69bde6SJohannes Berg 			struct sk_buff *skb;
181ab69bde6SJohannes Berg 
182ab69bde6SJohannes Berg 			skb = txq->bufs[sw_read_idx].skb;
183ab69bde6SJohannes Berg 			if (skb) {
184ab69bde6SJohannes Berg 				total_bytes += skb->len;
185ab69bde6SJohannes Berg 				total_packets++;
186ab69bde6SJohannes Berg 				budget--;
187ab69bde6SJohannes Berg 			}
188ab69bde6SJohannes Berg 
189702e8418STobias Regnery 			alx_free_txbuf(txq, sw_read_idx);
190ab69bde6SJohannes Berg 
191702e8418STobias Regnery 			if (++sw_read_idx == txq->count)
192ab69bde6SJohannes Berg 				sw_read_idx = 0;
193ab69bde6SJohannes Berg 		}
194ab69bde6SJohannes Berg 		txq->read_idx = sw_read_idx;
195ab69bde6SJohannes Berg 
1962e06826bSTobias Regnery 		netdev_tx_completed_queue(tx_queue, total_packets, total_bytes);
197ab69bde6SJohannes Berg 	}
198ab69bde6SJohannes Berg 
1992e06826bSTobias Regnery 	if (netif_tx_queue_stopped(tx_queue) && netif_carrier_ok(alx->dev) &&
200702e8418STobias Regnery 	    alx_tpd_avail(txq) > txq->count / 4)
2012e06826bSTobias Regnery 		netif_tx_wake_queue(tx_queue);
202ab69bde6SJohannes Berg 
203ab69bde6SJohannes Berg 	return sw_read_idx == hw_read_idx;
204ab69bde6SJohannes Berg }
205ab69bde6SJohannes Berg 
alx_schedule_link_check(struct alx_priv * alx)206ab69bde6SJohannes Berg static void alx_schedule_link_check(struct alx_priv *alx)
207ab69bde6SJohannes Berg {
208ab69bde6SJohannes Berg 	schedule_work(&alx->link_check_wk);
209ab69bde6SJohannes Berg }
210ab69bde6SJohannes Berg 
alx_schedule_reset(struct alx_priv * alx)211ab69bde6SJohannes Berg static void alx_schedule_reset(struct alx_priv *alx)
212ab69bde6SJohannes Berg {
213ab69bde6SJohannes Berg 	schedule_work(&alx->reset_wk);
214ab69bde6SJohannes Berg }
215ab69bde6SJohannes Berg 
alx_clean_rx_irq(struct alx_rx_queue * rxq,int budget)216702e8418STobias Regnery static int alx_clean_rx_irq(struct alx_rx_queue *rxq, int budget)
217ab69bde6SJohannes Berg {
218702e8418STobias Regnery 	struct alx_priv *alx;
219ab69bde6SJohannes Berg 	struct alx_rrd *rrd;
220ab69bde6SJohannes Berg 	struct alx_buffer *rxb;
221ab69bde6SJohannes Berg 	struct sk_buff *skb;
222ab69bde6SJohannes Berg 	u16 length, rfd_cleaned = 0;
2237a05dc64SEric Dumazet 	int work = 0;
224ab69bde6SJohannes Berg 
225702e8418STobias Regnery 	alx = netdev_priv(rxq->netdev);
226702e8418STobias Regnery 
2277a05dc64SEric Dumazet 	while (work < budget) {
228ab69bde6SJohannes Berg 		rrd = &rxq->rrd[rxq->rrd_read_idx];
229ab69bde6SJohannes Berg 		if (!(rrd->word3 & cpu_to_le32(1 << RRD_UPDATED_SHIFT)))
230ab69bde6SJohannes Berg 			break;
231ab69bde6SJohannes Berg 		rrd->word3 &= ~cpu_to_le32(1 << RRD_UPDATED_SHIFT);
232ab69bde6SJohannes Berg 
233ab69bde6SJohannes Berg 		if (ALX_GET_FIELD(le32_to_cpu(rrd->word0),
234ab69bde6SJohannes Berg 				  RRD_SI) != rxq->read_idx ||
235ab69bde6SJohannes Berg 		    ALX_GET_FIELD(le32_to_cpu(rrd->word0),
236ab69bde6SJohannes Berg 				  RRD_NOR) != 1) {
237ab69bde6SJohannes Berg 			alx_schedule_reset(alx);
2387a05dc64SEric Dumazet 			return work;
239ab69bde6SJohannes Berg 		}
240ab69bde6SJohannes Berg 
241ab69bde6SJohannes Berg 		rxb = &rxq->bufs[rxq->read_idx];
242702e8418STobias Regnery 		dma_unmap_single(rxq->dev,
243ab69bde6SJohannes Berg 				 dma_unmap_addr(rxb, dma),
244ab69bde6SJohannes Berg 				 dma_unmap_len(rxb, size),
245ab69bde6SJohannes Berg 				 DMA_FROM_DEVICE);
246ab69bde6SJohannes Berg 		dma_unmap_len_set(rxb, size, 0);
247ab69bde6SJohannes Berg 		skb = rxb->skb;
248ab69bde6SJohannes Berg 		rxb->skb = NULL;
249ab69bde6SJohannes Berg 
250ab69bde6SJohannes Berg 		if (rrd->word3 & cpu_to_le32(1 << RRD_ERR_RES_SHIFT) ||
251ab69bde6SJohannes Berg 		    rrd->word3 & cpu_to_le32(1 << RRD_ERR_LEN_SHIFT)) {
252ab69bde6SJohannes Berg 			rrd->word3 = 0;
253ab69bde6SJohannes Berg 			dev_kfree_skb_any(skb);
254ab69bde6SJohannes Berg 			goto next_pkt;
255ab69bde6SJohannes Berg 		}
256ab69bde6SJohannes Berg 
257ab69bde6SJohannes Berg 		length = ALX_GET_FIELD(le32_to_cpu(rrd->word3),
258ab69bde6SJohannes Berg 				       RRD_PKTLEN) - ETH_FCS_LEN;
259ab69bde6SJohannes Berg 		skb_put(skb, length);
260702e8418STobias Regnery 		skb->protocol = eth_type_trans(skb, rxq->netdev);
261ab69bde6SJohannes Berg 
262ab69bde6SJohannes Berg 		skb_checksum_none_assert(skb);
263ab69bde6SJohannes Berg 		if (alx->dev->features & NETIF_F_RXCSUM &&
264ab69bde6SJohannes Berg 		    !(rrd->word3 & (cpu_to_le32(1 << RRD_ERR_L4_SHIFT) |
265ab69bde6SJohannes Berg 				    cpu_to_le32(1 << RRD_ERR_IPV4_SHIFT)))) {
266ab69bde6SJohannes Berg 			switch (ALX_GET_FIELD(le32_to_cpu(rrd->word2),
267ab69bde6SJohannes Berg 					      RRD_PID)) {
268ab69bde6SJohannes Berg 			case RRD_PID_IPV6UDP:
269ab69bde6SJohannes Berg 			case RRD_PID_IPV4UDP:
270ab69bde6SJohannes Berg 			case RRD_PID_IPV4TCP:
271ab69bde6SJohannes Berg 			case RRD_PID_IPV6TCP:
272ab69bde6SJohannes Berg 				skb->ip_summed = CHECKSUM_UNNECESSARY;
273ab69bde6SJohannes Berg 				break;
274ab69bde6SJohannes Berg 			}
275ab69bde6SJohannes Berg 		}
276ab69bde6SJohannes Berg 
277702e8418STobias Regnery 		napi_gro_receive(&rxq->np->napi, skb);
2787a05dc64SEric Dumazet 		work++;
279ab69bde6SJohannes Berg 
280ab69bde6SJohannes Berg next_pkt:
281702e8418STobias Regnery 		if (++rxq->read_idx == rxq->count)
282ab69bde6SJohannes Berg 			rxq->read_idx = 0;
283702e8418STobias Regnery 		if (++rxq->rrd_read_idx == rxq->count)
284ab69bde6SJohannes Berg 			rxq->rrd_read_idx = 0;
285ab69bde6SJohannes Berg 
286ab69bde6SJohannes Berg 		if (++rfd_cleaned > ALX_RX_ALLOC_THRESH)
287ab69bde6SJohannes Berg 			rfd_cleaned -= alx_refill_rx_ring(alx, GFP_ATOMIC);
288ab69bde6SJohannes Berg 	}
289ab69bde6SJohannes Berg 
290ab69bde6SJohannes Berg 	if (rfd_cleaned)
291ab69bde6SJohannes Berg 		alx_refill_rx_ring(alx, GFP_ATOMIC);
292ab69bde6SJohannes Berg 
2937a05dc64SEric Dumazet 	return work;
294ab69bde6SJohannes Berg }
295ab69bde6SJohannes Berg 
alx_poll(struct napi_struct * napi,int budget)296ab69bde6SJohannes Berg static int alx_poll(struct napi_struct *napi, int budget)
297ab69bde6SJohannes Berg {
298702e8418STobias Regnery 	struct alx_napi *np = container_of(napi, struct alx_napi, napi);
299702e8418STobias Regnery 	struct alx_priv *alx = np->alx;
300ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
301ab69bde6SJohannes Berg 	unsigned long flags;
302e0eac254STobias Regnery 	bool tx_complete = true;
303e0eac254STobias Regnery 	int work = 0;
304ab69bde6SJohannes Berg 
305e0eac254STobias Regnery 	if (np->txq)
306702e8418STobias Regnery 		tx_complete = alx_clean_tx_irq(np->txq);
307e0eac254STobias Regnery 	if (np->rxq)
308702e8418STobias Regnery 		work = alx_clean_rx_irq(np->rxq, budget);
309ab69bde6SJohannes Berg 
3107a05dc64SEric Dumazet 	if (!tx_complete || work == budget)
3117a05dc64SEric Dumazet 		return budget;
312ab69bde6SJohannes Berg 
3136ad20165SEric Dumazet 	napi_complete_done(&np->napi, work);
314ab69bde6SJohannes Berg 
315ab69bde6SJohannes Berg 	/* enable interrupt */
316f3297f68SChristoph Hellwig 	if (alx->hw.pdev->msix_enabled) {
317e0eac254STobias Regnery 		alx_mask_msix(hw, np->vec_idx, false);
318dc39a78bSTobias Regnery 	} else {
319ab69bde6SJohannes Berg 		spin_lock_irqsave(&alx->irq_lock, flags);
320ab69bde6SJohannes Berg 		alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0;
321ab69bde6SJohannes Berg 		alx_write_mem32(hw, ALX_IMR, alx->int_mask);
322ab69bde6SJohannes Berg 		spin_unlock_irqrestore(&alx->irq_lock, flags);
323dc39a78bSTobias Regnery 	}
324ab69bde6SJohannes Berg 
325ab69bde6SJohannes Berg 	alx_post_write(hw);
326ab69bde6SJohannes Berg 
3277a05dc64SEric Dumazet 	return work;
328ab69bde6SJohannes Berg }
329ab69bde6SJohannes Berg 
alx_intr_handle_misc(struct alx_priv * alx,u32 intr)330a0373aefSTobias Regnery static bool alx_intr_handle_misc(struct alx_priv *alx, u32 intr)
331ab69bde6SJohannes Berg {
332ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
333ab69bde6SJohannes Berg 
334ab69bde6SJohannes Berg 	if (intr & ALX_ISR_FATAL) {
335ab69bde6SJohannes Berg 		netif_warn(alx, hw, alx->dev,
336ab69bde6SJohannes Berg 			   "fatal interrupt 0x%x, resetting\n", intr);
337ab69bde6SJohannes Berg 		alx_schedule_reset(alx);
338a0373aefSTobias Regnery 		return true;
339ab69bde6SJohannes Berg 	}
340ab69bde6SJohannes Berg 
341ab69bde6SJohannes Berg 	if (intr & ALX_ISR_ALERT)
342ab69bde6SJohannes Berg 		netdev_warn(alx->dev, "alert interrupt: 0x%x\n", intr);
343ab69bde6SJohannes Berg 
344ab69bde6SJohannes Berg 	if (intr & ALX_ISR_PHY) {
345ab69bde6SJohannes Berg 		/* suppress PHY interrupt, because the source
346ab69bde6SJohannes Berg 		 * is from PHY internal. only the internal status
347ab69bde6SJohannes Berg 		 * is cleared, the interrupt status could be cleared.
348ab69bde6SJohannes Berg 		 */
349ab69bde6SJohannes Berg 		alx->int_mask &= ~ALX_ISR_PHY;
350a0373aefSTobias Regnery 		alx_write_mem32(hw, ALX_IMR, alx->int_mask);
351ab69bde6SJohannes Berg 		alx_schedule_link_check(alx);
352ab69bde6SJohannes Berg 	}
353ab69bde6SJohannes Berg 
354a0373aefSTobias Regnery 	return false;
355a0373aefSTobias Regnery }
356a0373aefSTobias Regnery 
alx_intr_handle(struct alx_priv * alx,u32 intr)357a0373aefSTobias Regnery static irqreturn_t alx_intr_handle(struct alx_priv *alx, u32 intr)
358a0373aefSTobias Regnery {
359a0373aefSTobias Regnery 	struct alx_hw *hw = &alx->hw;
360a0373aefSTobias Regnery 
361a0373aefSTobias Regnery 	spin_lock(&alx->irq_lock);
362a0373aefSTobias Regnery 
363a0373aefSTobias Regnery 	/* ACK interrupt */
364a0373aefSTobias Regnery 	alx_write_mem32(hw, ALX_ISR, intr | ALX_ISR_DIS);
365a0373aefSTobias Regnery 	intr &= alx->int_mask;
366a0373aefSTobias Regnery 
367a0373aefSTobias Regnery 	if (alx_intr_handle_misc(alx, intr))
368a0373aefSTobias Regnery 		goto out;
369a0373aefSTobias Regnery 
370ab69bde6SJohannes Berg 	if (intr & (ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0)) {
371702e8418STobias Regnery 		napi_schedule(&alx->qnapi[0]->napi);
372ab69bde6SJohannes Berg 		/* mask rx/tx interrupt, enable them when napi complete */
373ab69bde6SJohannes Berg 		alx->int_mask &= ~ALX_ISR_ALL_QUEUES;
374ab69bde6SJohannes Berg 		alx_write_mem32(hw, ALX_IMR, alx->int_mask);
375a0373aefSTobias Regnery 	}
376ab69bde6SJohannes Berg 
377ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_ISR, 0);
378ab69bde6SJohannes Berg 
379ab69bde6SJohannes Berg  out:
380ab69bde6SJohannes Berg 	spin_unlock(&alx->irq_lock);
381ab69bde6SJohannes Berg 	return IRQ_HANDLED;
382ab69bde6SJohannes Berg }
383ab69bde6SJohannes Berg 
alx_intr_msix_ring(int irq,void * data)384dc39a78bSTobias Regnery static irqreturn_t alx_intr_msix_ring(int irq, void *data)
385dc39a78bSTobias Regnery {
386702e8418STobias Regnery 	struct alx_napi *np = data;
387702e8418STobias Regnery 	struct alx_hw *hw = &np->alx->hw;
388dc39a78bSTobias Regnery 
389dc39a78bSTobias Regnery 	/* mask interrupt to ACK chip */
390e0eac254STobias Regnery 	alx_mask_msix(hw, np->vec_idx, true);
391dc39a78bSTobias Regnery 	/* clear interrupt status */
392e0eac254STobias Regnery 	alx_write_mem32(hw, ALX_ISR, np->vec_mask);
393dc39a78bSTobias Regnery 
394702e8418STobias Regnery 	napi_schedule(&np->napi);
395dc39a78bSTobias Regnery 
396dc39a78bSTobias Regnery 	return IRQ_HANDLED;
397dc39a78bSTobias Regnery }
398dc39a78bSTobias Regnery 
alx_intr_msix_misc(int irq,void * data)399dc39a78bSTobias Regnery static irqreturn_t alx_intr_msix_misc(int irq, void *data)
400dc39a78bSTobias Regnery {
401dc39a78bSTobias Regnery 	struct alx_priv *alx = data;
402dc39a78bSTobias Regnery 	struct alx_hw *hw = &alx->hw;
403dc39a78bSTobias Regnery 	u32 intr;
404dc39a78bSTobias Regnery 
405dc39a78bSTobias Regnery 	/* mask interrupt to ACK chip */
406dc39a78bSTobias Regnery 	alx_mask_msix(hw, 0, true);
407dc39a78bSTobias Regnery 
408dc39a78bSTobias Regnery 	/* read interrupt status */
409dc39a78bSTobias Regnery 	intr = alx_read_mem32(hw, ALX_ISR);
410dc39a78bSTobias Regnery 	intr &= (alx->int_mask & ~ALX_ISR_ALL_QUEUES);
411dc39a78bSTobias Regnery 
412dc39a78bSTobias Regnery 	if (alx_intr_handle_misc(alx, intr))
413dc39a78bSTobias Regnery 		return IRQ_HANDLED;
414dc39a78bSTobias Regnery 
415dc39a78bSTobias Regnery 	/* clear interrupt status */
416dc39a78bSTobias Regnery 	alx_write_mem32(hw, ALX_ISR, intr);
417dc39a78bSTobias Regnery 
418dc39a78bSTobias Regnery 	/* enable interrupt again */
419dc39a78bSTobias Regnery 	alx_mask_msix(hw, 0, false);
420dc39a78bSTobias Regnery 
421dc39a78bSTobias Regnery 	return IRQ_HANDLED;
422dc39a78bSTobias Regnery }
423dc39a78bSTobias Regnery 
alx_intr_msi(int irq,void * data)424ab69bde6SJohannes Berg static irqreturn_t alx_intr_msi(int irq, void *data)
425ab69bde6SJohannes Berg {
426ab69bde6SJohannes Berg 	struct alx_priv *alx = data;
427ab69bde6SJohannes Berg 
428ab69bde6SJohannes Berg 	return alx_intr_handle(alx, alx_read_mem32(&alx->hw, ALX_ISR));
429ab69bde6SJohannes Berg }
430ab69bde6SJohannes Berg 
alx_intr_legacy(int irq,void * data)431ab69bde6SJohannes Berg static irqreturn_t alx_intr_legacy(int irq, void *data)
432ab69bde6SJohannes Berg {
433ab69bde6SJohannes Berg 	struct alx_priv *alx = data;
434ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
435ab69bde6SJohannes Berg 	u32 intr;
436ab69bde6SJohannes Berg 
437ab69bde6SJohannes Berg 	intr = alx_read_mem32(hw, ALX_ISR);
438ab69bde6SJohannes Berg 
439ab69bde6SJohannes Berg 	if (intr & ALX_ISR_DIS || !(intr & alx->int_mask))
440ab69bde6SJohannes Berg 		return IRQ_NONE;
441ab69bde6SJohannes Berg 
442ab69bde6SJohannes Berg 	return alx_intr_handle(alx, intr);
443ab69bde6SJohannes Berg }
444ab69bde6SJohannes Berg 
445a4076d34STobias Regnery static const u16 txring_header_reg[] = {ALX_TPD_PRI0_ADDR_LO,
446a4076d34STobias Regnery 					ALX_TPD_PRI1_ADDR_LO,
447a4076d34STobias Regnery 					ALX_TPD_PRI2_ADDR_LO,
448a4076d34STobias Regnery 					ALX_TPD_PRI3_ADDR_LO};
449a4076d34STobias Regnery 
alx_init_ring_ptrs(struct alx_priv * alx)450ab69bde6SJohannes Berg static void alx_init_ring_ptrs(struct alx_priv *alx)
451ab69bde6SJohannes Berg {
452ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
453ab69bde6SJohannes Berg 	u32 addr_hi = ((u64)alx->descmem.dma) >> 32;
454a4076d34STobias Regnery 	struct alx_napi *np;
455a4076d34STobias Regnery 	int i;
456ab69bde6SJohannes Berg 
457a4076d34STobias Regnery 	for (i = 0; i < alx->num_napi; i++) {
458a4076d34STobias Regnery 		np = alx->qnapi[i];
459a4076d34STobias Regnery 		if (np->txq) {
460a4076d34STobias Regnery 			np->txq->read_idx = 0;
461a4076d34STobias Regnery 			np->txq->write_idx = 0;
462a4076d34STobias Regnery 			alx_write_mem32(hw,
463a4076d34STobias Regnery 					txring_header_reg[np->txq->queue_idx],
464a4076d34STobias Regnery 					np->txq->tpd_dma);
465a4076d34STobias Regnery 		}
466a4076d34STobias Regnery 
467a4076d34STobias Regnery 		if (np->rxq) {
468702e8418STobias Regnery 			np->rxq->read_idx = 0;
469702e8418STobias Regnery 			np->rxq->write_idx = 0;
470702e8418STobias Regnery 			np->rxq->rrd_read_idx = 0;
471702e8418STobias Regnery 			alx_write_mem32(hw, ALX_RRD_ADDR_LO, np->rxq->rrd_dma);
472702e8418STobias Regnery 			alx_write_mem32(hw, ALX_RFD_ADDR_LO, np->rxq->rfd_dma);
473a4076d34STobias Regnery 		}
474a4076d34STobias Regnery 	}
475a4076d34STobias Regnery 
476a4076d34STobias Regnery 	alx_write_mem32(hw, ALX_TX_BASE_ADDR_HI, addr_hi);
477a4076d34STobias Regnery 	alx_write_mem32(hw, ALX_TPD_RING_SZ, alx->tx_ringsz);
478a4076d34STobias Regnery 
479a4076d34STobias Regnery 	alx_write_mem32(hw, ALX_RX_BASE_ADDR_HI, addr_hi);
480a4076d34STobias Regnery 	alx_write_mem32(hw, ALX_RRD_RING_SZ, alx->rx_ringsz);
481ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_RFD_RING_SZ, alx->rx_ringsz);
482ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_RFD_BUF_SZ, alx->rxbuf_size);
483ab69bde6SJohannes Berg 
484ab69bde6SJohannes Berg 	/* load these pointers into the chip */
485ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_SRAM9, ALX_SRAM_LOAD_PTR);
486ab69bde6SJohannes Berg }
487ab69bde6SJohannes Berg 
alx_free_txring_buf(struct alx_tx_queue * txq)488702e8418STobias Regnery static void alx_free_txring_buf(struct alx_tx_queue *txq)
489ab69bde6SJohannes Berg {
490ab69bde6SJohannes Berg 	int i;
491ab69bde6SJohannes Berg 
492ab69bde6SJohannes Berg 	if (!txq->bufs)
493ab69bde6SJohannes Berg 		return;
494ab69bde6SJohannes Berg 
495702e8418STobias Regnery 	for (i = 0; i < txq->count; i++)
496702e8418STobias Regnery 		alx_free_txbuf(txq, i);
497ab69bde6SJohannes Berg 
498702e8418STobias Regnery 	memset(txq->bufs, 0, txq->count * sizeof(struct alx_buffer));
499702e8418STobias Regnery 	memset(txq->tpd, 0, txq->count * sizeof(struct alx_txd));
500ab69bde6SJohannes Berg 	txq->write_idx = 0;
501ab69bde6SJohannes Berg 	txq->read_idx = 0;
502ab69bde6SJohannes Berg 
5032e06826bSTobias Regnery 	netdev_tx_reset_queue(alx_get_tx_queue(txq));
504ab69bde6SJohannes Berg }
505ab69bde6SJohannes Berg 
alx_free_rxring_buf(struct alx_rx_queue * rxq)506702e8418STobias Regnery static void alx_free_rxring_buf(struct alx_rx_queue *rxq)
507ab69bde6SJohannes Berg {
508ab69bde6SJohannes Berg 	struct alx_buffer *cur_buf;
509ab69bde6SJohannes Berg 	u16 i;
510ab69bde6SJohannes Berg 
511a4076d34STobias Regnery 	if (!rxq->bufs)
512ab69bde6SJohannes Berg 		return;
513ab69bde6SJohannes Berg 
514702e8418STobias Regnery 	for (i = 0; i < rxq->count; i++) {
515ab69bde6SJohannes Berg 		cur_buf = rxq->bufs + i;
516ab69bde6SJohannes Berg 		if (cur_buf->skb) {
517702e8418STobias Regnery 			dma_unmap_single(rxq->dev,
518ab69bde6SJohannes Berg 					 dma_unmap_addr(cur_buf, dma),
519ab69bde6SJohannes Berg 					 dma_unmap_len(cur_buf, size),
520ab69bde6SJohannes Berg 					 DMA_FROM_DEVICE);
521ab69bde6SJohannes Berg 			dev_kfree_skb(cur_buf->skb);
522ab69bde6SJohannes Berg 			cur_buf->skb = NULL;
523ab69bde6SJohannes Berg 			dma_unmap_len_set(cur_buf, size, 0);
524ab69bde6SJohannes Berg 			dma_unmap_addr_set(cur_buf, dma, 0);
525ab69bde6SJohannes Berg 		}
526ab69bde6SJohannes Berg 	}
527ab69bde6SJohannes Berg 
528ab69bde6SJohannes Berg 	rxq->write_idx = 0;
529ab69bde6SJohannes Berg 	rxq->read_idx = 0;
530ab69bde6SJohannes Berg 	rxq->rrd_read_idx = 0;
531ab69bde6SJohannes Berg }
532ab69bde6SJohannes Berg 
alx_free_buffers(struct alx_priv * alx)533ab69bde6SJohannes Berg static void alx_free_buffers(struct alx_priv *alx)
534ab69bde6SJohannes Berg {
535a4076d34STobias Regnery 	int i;
536a4076d34STobias Regnery 
537a4076d34STobias Regnery 	for (i = 0; i < alx->num_txq; i++)
538a4076d34STobias Regnery 		if (alx->qnapi[i] && alx->qnapi[i]->txq)
539a4076d34STobias Regnery 			alx_free_txring_buf(alx->qnapi[i]->txq);
540a4076d34STobias Regnery 
541a4076d34STobias Regnery 	if (alx->qnapi[0] && alx->qnapi[0]->rxq)
542702e8418STobias Regnery 		alx_free_rxring_buf(alx->qnapi[0]->rxq);
543ab69bde6SJohannes Berg }
544ab69bde6SJohannes Berg 
alx_reinit_rings(struct alx_priv * alx)545ab69bde6SJohannes Berg static int alx_reinit_rings(struct alx_priv *alx)
546ab69bde6SJohannes Berg {
547ab69bde6SJohannes Berg 	alx_free_buffers(alx);
548ab69bde6SJohannes Berg 
549ab69bde6SJohannes Berg 	alx_init_ring_ptrs(alx);
550ab69bde6SJohannes Berg 
551ab69bde6SJohannes Berg 	if (!alx_refill_rx_ring(alx, GFP_KERNEL))
552ab69bde6SJohannes Berg 		return -ENOMEM;
553ab69bde6SJohannes Berg 
554ab69bde6SJohannes Berg 	return 0;
555ab69bde6SJohannes Berg }
556ab69bde6SJohannes Berg 
alx_add_mc_addr(struct alx_hw * hw,const u8 * addr,u32 * mc_hash)557ab69bde6SJohannes Berg static void alx_add_mc_addr(struct alx_hw *hw, const u8 *addr, u32 *mc_hash)
558ab69bde6SJohannes Berg {
559ab69bde6SJohannes Berg 	u32 crc32, bit, reg;
560ab69bde6SJohannes Berg 
561ab69bde6SJohannes Berg 	crc32 = ether_crc(ETH_ALEN, addr);
562ab69bde6SJohannes Berg 	reg = (crc32 >> 31) & 0x1;
563ab69bde6SJohannes Berg 	bit = (crc32 >> 26) & 0x1F;
564ab69bde6SJohannes Berg 
565ab69bde6SJohannes Berg 	mc_hash[reg] |= BIT(bit);
566ab69bde6SJohannes Berg }
567ab69bde6SJohannes Berg 
__alx_set_rx_mode(struct net_device * netdev)568ab69bde6SJohannes Berg static void __alx_set_rx_mode(struct net_device *netdev)
569ab69bde6SJohannes Berg {
570ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
571ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
572ab69bde6SJohannes Berg 	struct netdev_hw_addr *ha;
573ab69bde6SJohannes Berg 	u32 mc_hash[2] = {};
574ab69bde6SJohannes Berg 
575ab69bde6SJohannes Berg 	if (!(netdev->flags & IFF_ALLMULTI)) {
576ab69bde6SJohannes Berg 		netdev_for_each_mc_addr(ha, netdev)
577ab69bde6SJohannes Berg 			alx_add_mc_addr(hw, ha->addr, mc_hash);
578ab69bde6SJohannes Berg 
579ab69bde6SJohannes Berg 		alx_write_mem32(hw, ALX_HASH_TBL0, mc_hash[0]);
580ab69bde6SJohannes Berg 		alx_write_mem32(hw, ALX_HASH_TBL1, mc_hash[1]);
581ab69bde6SJohannes Berg 	}
582ab69bde6SJohannes Berg 
583ab69bde6SJohannes Berg 	hw->rx_ctrl &= ~(ALX_MAC_CTRL_MULTIALL_EN | ALX_MAC_CTRL_PROMISC_EN);
584ab69bde6SJohannes Berg 	if (netdev->flags & IFF_PROMISC)
585ab69bde6SJohannes Berg 		hw->rx_ctrl |= ALX_MAC_CTRL_PROMISC_EN;
586ab69bde6SJohannes Berg 	if (netdev->flags & IFF_ALLMULTI)
587ab69bde6SJohannes Berg 		hw->rx_ctrl |= ALX_MAC_CTRL_MULTIALL_EN;
588ab69bde6SJohannes Berg 
589ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl);
590ab69bde6SJohannes Berg }
591ab69bde6SJohannes Berg 
alx_set_rx_mode(struct net_device * netdev)592ab69bde6SJohannes Berg static void alx_set_rx_mode(struct net_device *netdev)
593ab69bde6SJohannes Berg {
594ab69bde6SJohannes Berg 	__alx_set_rx_mode(netdev);
595ab69bde6SJohannes Berg }
596ab69bde6SJohannes Berg 
alx_set_mac_address(struct net_device * netdev,void * data)597ab69bde6SJohannes Berg static int alx_set_mac_address(struct net_device *netdev, void *data)
598ab69bde6SJohannes Berg {
599ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
600ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
601ab69bde6SJohannes Berg 	struct sockaddr *addr = data;
602ab69bde6SJohannes Berg 
603ab69bde6SJohannes Berg 	if (!is_valid_ether_addr(addr->sa_data))
604ab69bde6SJohannes Berg 		return -EADDRNOTAVAIL;
605ab69bde6SJohannes Berg 
606ab69bde6SJohannes Berg 	if (netdev->addr_assign_type & NET_ADDR_RANDOM)
607ab69bde6SJohannes Berg 		netdev->addr_assign_type ^= NET_ADDR_RANDOM;
608ab69bde6SJohannes Berg 
609a05e4c0aSJakub Kicinski 	eth_hw_addr_set(netdev, addr->sa_data);
610ab69bde6SJohannes Berg 	memcpy(hw->mac_addr, addr->sa_data, netdev->addr_len);
611ab69bde6SJohannes Berg 	alx_set_macaddr(hw, hw->mac_addr);
612ab69bde6SJohannes Berg 
613ab69bde6SJohannes Berg 	return 0;
614ab69bde6SJohannes Berg }
615ab69bde6SJohannes Berg 
alx_alloc_tx_ring(struct alx_priv * alx,struct alx_tx_queue * txq,int offset)6168c2a4c8eSTobias Regnery static int alx_alloc_tx_ring(struct alx_priv *alx, struct alx_tx_queue *txq,
6178c2a4c8eSTobias Regnery 			     int offset)
618ab69bde6SJohannes Berg {
619702e8418STobias Regnery 	txq->bufs = kcalloc(txq->count, sizeof(struct alx_buffer), GFP_KERNEL);
6208c2a4c8eSTobias Regnery 	if (!txq->bufs)
621ab69bde6SJohannes Berg 		return -ENOMEM;
622ab69bde6SJohannes Berg 
6238c2a4c8eSTobias Regnery 	txq->tpd = alx->descmem.virt + offset;
6248c2a4c8eSTobias Regnery 	txq->tpd_dma = alx->descmem.dma + offset;
625702e8418STobias Regnery 	offset += sizeof(struct alx_txd) * txq->count;
6268c2a4c8eSTobias Regnery 
6278c2a4c8eSTobias Regnery 	return offset;
6288c2a4c8eSTobias Regnery }
6298c2a4c8eSTobias Regnery 
alx_alloc_rx_ring(struct alx_priv * alx,struct alx_rx_queue * rxq,int offset)6308c2a4c8eSTobias Regnery static int alx_alloc_rx_ring(struct alx_priv *alx, struct alx_rx_queue *rxq,
6318c2a4c8eSTobias Regnery 			     int offset)
6328c2a4c8eSTobias Regnery {
633702e8418STobias Regnery 	rxq->bufs = kcalloc(rxq->count, sizeof(struct alx_buffer), GFP_KERNEL);
6348c2a4c8eSTobias Regnery 	if (!rxq->bufs)
6358c2a4c8eSTobias Regnery 		return -ENOMEM;
6368c2a4c8eSTobias Regnery 
6378c2a4c8eSTobias Regnery 	rxq->rrd = alx->descmem.virt + offset;
6388c2a4c8eSTobias Regnery 	rxq->rrd_dma = alx->descmem.dma + offset;
639702e8418STobias Regnery 	offset += sizeof(struct alx_rrd) * rxq->count;
6408c2a4c8eSTobias Regnery 
6418c2a4c8eSTobias Regnery 	rxq->rfd = alx->descmem.virt + offset;
6428c2a4c8eSTobias Regnery 	rxq->rfd_dma = alx->descmem.dma + offset;
643702e8418STobias Regnery 	offset += sizeof(struct alx_rfd) * rxq->count;
6448c2a4c8eSTobias Regnery 
6458c2a4c8eSTobias Regnery 	return offset;
6468c2a4c8eSTobias Regnery }
6478c2a4c8eSTobias Regnery 
alx_alloc_rings(struct alx_priv * alx)6488c2a4c8eSTobias Regnery static int alx_alloc_rings(struct alx_priv *alx)
6498c2a4c8eSTobias Regnery {
650a4076d34STobias Regnery 	int i, offset = 0;
651ab69bde6SJohannes Berg 
652ab69bde6SJohannes Berg 	/* physical tx/rx ring descriptors
653ab69bde6SJohannes Berg 	 *
654ab69bde6SJohannes Berg 	 * Allocate them as a single chunk because they must not cross a
655ab69bde6SJohannes Berg 	 * 4G boundary (hardware has a single register for high 32 bits
656ab69bde6SJohannes Berg 	 * of addresses only)
657ab69bde6SJohannes Berg 	 */
658a4076d34STobias Regnery 	alx->descmem.size = sizeof(struct alx_txd) * alx->tx_ringsz *
659a4076d34STobias Regnery 			    alx->num_txq +
660ab69bde6SJohannes Berg 			    sizeof(struct alx_rrd) * alx->rx_ringsz +
661ab69bde6SJohannes Berg 			    sizeof(struct alx_rfd) * alx->rx_ringsz;
662750afb08SLuis Chamberlain 	alx->descmem.virt = dma_alloc_coherent(&alx->hw.pdev->dev,
663ab69bde6SJohannes Berg 					       alx->descmem.size,
664750afb08SLuis Chamberlain 					       &alx->descmem.dma, GFP_KERNEL);
665ab69bde6SJohannes Berg 	if (!alx->descmem.virt)
6668c2a4c8eSTobias Regnery 		return -ENOMEM;
667ab69bde6SJohannes Berg 
6688c2a4c8eSTobias Regnery 	/* alignment requirements */
669ab69bde6SJohannes Berg 	BUILD_BUG_ON(sizeof(struct alx_txd) % 8);
670ab69bde6SJohannes Berg 	BUILD_BUG_ON(sizeof(struct alx_rrd) % 8);
671ab69bde6SJohannes Berg 
672a4076d34STobias Regnery 	for (i = 0; i < alx->num_txq; i++) {
673a4076d34STobias Regnery 		offset = alx_alloc_tx_ring(alx, alx->qnapi[i]->txq, offset);
6748c2a4c8eSTobias Regnery 		if (offset < 0) {
6758c2a4c8eSTobias Regnery 			netdev_err(alx->dev, "Allocation of tx buffer failed!\n");
676b0999223STobias Regnery 			return -ENOMEM;
677ab69bde6SJohannes Berg 		}
678a4076d34STobias Regnery 	}
679ab69bde6SJohannes Berg 
680702e8418STobias Regnery 	offset = alx_alloc_rx_ring(alx, alx->qnapi[0]->rxq, offset);
6818c2a4c8eSTobias Regnery 	if (offset < 0) {
6828c2a4c8eSTobias Regnery 		netdev_err(alx->dev, "Allocation of rx buffer failed!\n");
683b0999223STobias Regnery 		return -ENOMEM;
6848c2a4c8eSTobias Regnery 	}
685ab69bde6SJohannes Berg 
686ab69bde6SJohannes Berg 	return 0;
687ab69bde6SJohannes Berg }
688ab69bde6SJohannes Berg 
alx_free_rings(struct alx_priv * alx)689ab69bde6SJohannes Berg static void alx_free_rings(struct alx_priv *alx)
690ab69bde6SJohannes Berg {
691a4076d34STobias Regnery 	int i;
692702e8418STobias Regnery 
693ab69bde6SJohannes Berg 	alx_free_buffers(alx);
694ab69bde6SJohannes Berg 
695a4076d34STobias Regnery 	for (i = 0; i < alx->num_txq; i++)
696a4076d34STobias Regnery 		if (alx->qnapi[i] && alx->qnapi[i]->txq)
697a4076d34STobias Regnery 			kfree(alx->qnapi[i]->txq->bufs);
698a4076d34STobias Regnery 
699a4076d34STobias Regnery 	if (alx->qnapi[0] && alx->qnapi[0]->rxq)
700702e8418STobias Regnery 		kfree(alx->qnapi[0]->rxq->bufs);
701ab69bde6SJohannes Berg 
702f1db5c10STobias Regnery 	if (alx->descmem.virt)
703ab69bde6SJohannes Berg 		dma_free_coherent(&alx->hw.pdev->dev,
704ab69bde6SJohannes Berg 				  alx->descmem.size,
705ab69bde6SJohannes Berg 				  alx->descmem.virt,
706ab69bde6SJohannes Berg 				  alx->descmem.dma);
707ab69bde6SJohannes Berg }
708ab69bde6SJohannes Berg 
alx_free_napis(struct alx_priv * alx)709b0999223STobias Regnery static void alx_free_napis(struct alx_priv *alx)
710b0999223STobias Regnery {
711b0999223STobias Regnery 	struct alx_napi *np;
712a4076d34STobias Regnery 	int i;
713b0999223STobias Regnery 
714a4076d34STobias Regnery 	for (i = 0; i < alx->num_napi; i++) {
715a4076d34STobias Regnery 		np = alx->qnapi[i];
716b0999223STobias Regnery 		if (!np)
717a4076d34STobias Regnery 			continue;
718b0999223STobias Regnery 
719702e8418STobias Regnery 		netif_napi_del(&np->napi);
720b0999223STobias Regnery 		kfree(np->txq);
721b0999223STobias Regnery 		kfree(np->rxq);
722b0999223STobias Regnery 		kfree(np);
723a4076d34STobias Regnery 		alx->qnapi[i] = NULL;
724a4076d34STobias Regnery 	}
725b0999223STobias Regnery }
726b0999223STobias Regnery 
7272e06826bSTobias Regnery static const u16 tx_pidx_reg[] = {ALX_TPD_PRI0_PIDX, ALX_TPD_PRI1_PIDX,
7282e06826bSTobias Regnery 				  ALX_TPD_PRI2_PIDX, ALX_TPD_PRI3_PIDX};
7292e06826bSTobias Regnery static const u16 tx_cidx_reg[] = {ALX_TPD_PRI0_CIDX, ALX_TPD_PRI1_CIDX,
7302e06826bSTobias Regnery 				  ALX_TPD_PRI2_CIDX, ALX_TPD_PRI3_CIDX};
731e0eac254STobias Regnery static const u32 tx_vect_mask[] = {ALX_ISR_TX_Q0, ALX_ISR_TX_Q1,
732e0eac254STobias Regnery 				   ALX_ISR_TX_Q2, ALX_ISR_TX_Q3};
733e0eac254STobias Regnery static const u32 rx_vect_mask[] = {ALX_ISR_RX_Q0, ALX_ISR_RX_Q1,
734e0eac254STobias Regnery 				   ALX_ISR_RX_Q2, ALX_ISR_RX_Q3,
735e0eac254STobias Regnery 				   ALX_ISR_RX_Q4, ALX_ISR_RX_Q5,
736e0eac254STobias Regnery 				   ALX_ISR_RX_Q6, ALX_ISR_RX_Q7};
737e0eac254STobias Regnery 
alx_alloc_napis(struct alx_priv * alx)738b0999223STobias Regnery static int alx_alloc_napis(struct alx_priv *alx)
739b0999223STobias Regnery {
740b0999223STobias Regnery 	struct alx_napi *np;
741b0999223STobias Regnery 	struct alx_rx_queue *rxq;
742b0999223STobias Regnery 	struct alx_tx_queue *txq;
743a4076d34STobias Regnery 	int i;
744b0999223STobias Regnery 
745b0999223STobias Regnery 	alx->int_mask &= ~ALX_ISR_ALL_QUEUES;
746b0999223STobias Regnery 
747b0999223STobias Regnery 	/* allocate alx_napi structures */
748a4076d34STobias Regnery 	for (i = 0; i < alx->num_napi; i++) {
749b0999223STobias Regnery 		np = kzalloc(sizeof(struct alx_napi), GFP_KERNEL);
750b0999223STobias Regnery 		if (!np)
751b0999223STobias Regnery 			goto err_out;
752b0999223STobias Regnery 
753b0999223STobias Regnery 		np->alx = alx;
754b48b89f9SJakub Kicinski 		netif_napi_add(alx->dev, &np->napi, alx_poll);
755a4076d34STobias Regnery 		alx->qnapi[i] = np;
756a4076d34STobias Regnery 	}
757b0999223STobias Regnery 
758b0999223STobias Regnery 	/* allocate tx queues */
759a4076d34STobias Regnery 	for (i = 0; i < alx->num_txq; i++) {
760a4076d34STobias Regnery 		np = alx->qnapi[i];
761b0999223STobias Regnery 		txq = kzalloc(sizeof(*txq), GFP_KERNEL);
762b0999223STobias Regnery 		if (!txq)
763b0999223STobias Regnery 			goto err_out;
764b0999223STobias Regnery 
765b0999223STobias Regnery 		np->txq = txq;
7662e06826bSTobias Regnery 		txq->p_reg = tx_pidx_reg[i];
7672e06826bSTobias Regnery 		txq->c_reg = tx_cidx_reg[i];
768a4076d34STobias Regnery 		txq->queue_idx = i;
769b0999223STobias Regnery 		txq->count = alx->tx_ringsz;
770b0999223STobias Regnery 		txq->netdev = alx->dev;
771b0999223STobias Regnery 		txq->dev = &alx->hw.pdev->dev;
772a4076d34STobias Regnery 		np->vec_mask |= tx_vect_mask[i];
773a4076d34STobias Regnery 		alx->int_mask |= tx_vect_mask[i];
774a4076d34STobias Regnery 	}
775b0999223STobias Regnery 
776b0999223STobias Regnery 	/* allocate rx queues */
777b0999223STobias Regnery 	np = alx->qnapi[0];
778b0999223STobias Regnery 	rxq = kzalloc(sizeof(*rxq), GFP_KERNEL);
779b0999223STobias Regnery 	if (!rxq)
780b0999223STobias Regnery 		goto err_out;
781b0999223STobias Regnery 
782b0999223STobias Regnery 	np->rxq = rxq;
783b0999223STobias Regnery 	rxq->np = alx->qnapi[0];
784e0eac254STobias Regnery 	rxq->queue_idx = 0;
785b0999223STobias Regnery 	rxq->count = alx->rx_ringsz;
786b0999223STobias Regnery 	rxq->netdev = alx->dev;
787b0999223STobias Regnery 	rxq->dev = &alx->hw.pdev->dev;
788e0eac254STobias Regnery 	np->vec_mask |= rx_vect_mask[0];
789e0eac254STobias Regnery 	alx->int_mask |= rx_vect_mask[0];
790b0999223STobias Regnery 
791b0999223STobias Regnery 	return 0;
792b0999223STobias Regnery 
793b0999223STobias Regnery err_out:
794b0999223STobias Regnery 	netdev_err(alx->dev, "error allocating internal structures\n");
795b0999223STobias Regnery 	alx_free_napis(alx);
796b0999223STobias Regnery 	return -ENOMEM;
797b0999223STobias Regnery }
798b0999223STobias Regnery 
799e0eac254STobias Regnery static const int txq_vec_mapping_shift[] = {
800e0eac254STobias Regnery 	0, ALX_MSI_MAP_TBL1_TXQ0_SHIFT,
801e0eac254STobias Regnery 	0, ALX_MSI_MAP_TBL1_TXQ1_SHIFT,
802e0eac254STobias Regnery 	1, ALX_MSI_MAP_TBL2_TXQ2_SHIFT,
803e0eac254STobias Regnery 	1, ALX_MSI_MAP_TBL2_TXQ3_SHIFT,
804e0eac254STobias Regnery };
805e0eac254STobias Regnery 
alx_config_vector_mapping(struct alx_priv * alx)806ab69bde6SJohannes Berg static void alx_config_vector_mapping(struct alx_priv *alx)
807ab69bde6SJohannes Berg {
808ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
809e0eac254STobias Regnery 	u32 tbl[2] = {0, 0};
810e0eac254STobias Regnery 	int i, vector, idx, shift;
811ab69bde6SJohannes Berg 
812f3297f68SChristoph Hellwig 	if (alx->hw.pdev->msix_enabled) {
813e0eac254STobias Regnery 		/* tx mappings */
814e0eac254STobias Regnery 		for (i = 0, vector = 1; i < alx->num_txq; i++, vector++) {
815e0eac254STobias Regnery 			idx = txq_vec_mapping_shift[i * 2];
816e0eac254STobias Regnery 			shift = txq_vec_mapping_shift[i * 2 + 1];
817e0eac254STobias Regnery 			tbl[idx] |= vector << shift;
818dc39a78bSTobias Regnery 		}
819dc39a78bSTobias Regnery 
820e0eac254STobias Regnery 		/* rx mapping */
821e0eac254STobias Regnery 		tbl[0] |= 1 << ALX_MSI_MAP_TBL1_RXQ0_SHIFT;
822e0eac254STobias Regnery 	}
823e0eac254STobias Regnery 
824e0eac254STobias Regnery 	alx_write_mem32(hw, ALX_MSI_MAP_TBL1, tbl[0]);
825e0eac254STobias Regnery 	alx_write_mem32(hw, ALX_MSI_MAP_TBL2, tbl[1]);
826ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_MSI_ID_MAP, 0);
827ab69bde6SJohannes Berg }
828ab69bde6SJohannes Berg 
alx_enable_msix(struct alx_priv * alx)829f3297f68SChristoph Hellwig static int alx_enable_msix(struct alx_priv *alx)
830dc39a78bSTobias Regnery {
831f3297f68SChristoph Hellwig 	int err, num_vec, num_txq, num_rxq;
832e0eac254STobias Regnery 
833d768319cSTobias Regnery 	num_txq = min_t(int, num_online_cpus(), ALX_MAX_TX_QUEUES);
834e0eac254STobias Regnery 	num_rxq = 1;
835e0eac254STobias Regnery 	num_vec = max_t(int, num_txq, num_rxq) + 1;
836dc39a78bSTobias Regnery 
837f3297f68SChristoph Hellwig 	err = pci_alloc_irq_vectors(alx->hw.pdev, num_vec, num_vec,
838f3297f68SChristoph Hellwig 			PCI_IRQ_MSIX);
8392c041afcSRakesh Pandit 	if (err < 0) {
840dc39a78bSTobias Regnery 		netdev_warn(alx->dev, "Enabling MSI-X interrupts failed!\n");
841f3297f68SChristoph Hellwig 		return err;
842dc39a78bSTobias Regnery 	}
843dc39a78bSTobias Regnery 
844dc39a78bSTobias Regnery 	alx->num_vec = num_vec;
845e0eac254STobias Regnery 	alx->num_napi = num_vec - 1;
846e0eac254STobias Regnery 	alx->num_txq = num_txq;
847e0eac254STobias Regnery 	alx->num_rxq = num_rxq;
848e0eac254STobias Regnery 
849f3297f68SChristoph Hellwig 	return err;
850dc39a78bSTobias Regnery }
851dc39a78bSTobias Regnery 
alx_request_msix(struct alx_priv * alx)852dc39a78bSTobias Regnery static int alx_request_msix(struct alx_priv *alx)
853dc39a78bSTobias Regnery {
854dc39a78bSTobias Regnery 	struct net_device *netdev = alx->dev;
855dc39a78bSTobias Regnery 	int i, err, vector = 0, free_vector = 0;
856dc39a78bSTobias Regnery 
857f3297f68SChristoph Hellwig 	err = request_irq(pci_irq_vector(alx->hw.pdev, 0), alx_intr_msix_misc,
858dc39a78bSTobias Regnery 			  0, netdev->name, alx);
859dc39a78bSTobias Regnery 	if (err)
860dc39a78bSTobias Regnery 		goto out_err;
861dc39a78bSTobias Regnery 
862e0eac254STobias Regnery 	for (i = 0; i < alx->num_napi; i++) {
863e0eac254STobias Regnery 		struct alx_napi *np = alx->qnapi[i];
864dc39a78bSTobias Regnery 
865e0eac254STobias Regnery 		vector++;
866e0eac254STobias Regnery 
867e0eac254STobias Regnery 		if (np->txq && np->rxq)
868e0eac254STobias Regnery 			sprintf(np->irq_lbl, "%s-TxRx-%u", netdev->name,
869e0eac254STobias Regnery 				np->txq->queue_idx);
870e0eac254STobias Regnery 		else if (np->txq)
871e0eac254STobias Regnery 			sprintf(np->irq_lbl, "%s-tx-%u", netdev->name,
872e0eac254STobias Regnery 				np->txq->queue_idx);
873e0eac254STobias Regnery 		else if (np->rxq)
874e0eac254STobias Regnery 			sprintf(np->irq_lbl, "%s-rx-%u", netdev->name,
875e0eac254STobias Regnery 				np->rxq->queue_idx);
876e0eac254STobias Regnery 		else
877e0eac254STobias Regnery 			sprintf(np->irq_lbl, "%s-unused", netdev->name);
878e0eac254STobias Regnery 
879e0eac254STobias Regnery 		np->vec_idx = vector;
880f3297f68SChristoph Hellwig 		err = request_irq(pci_irq_vector(alx->hw.pdev, vector),
881702e8418STobias Regnery 				  alx_intr_msix_ring, 0, np->irq_lbl, np);
882dc39a78bSTobias Regnery 		if (err)
883dc39a78bSTobias Regnery 			goto out_free;
884e0eac254STobias Regnery 	}
885dc39a78bSTobias Regnery 	return 0;
886dc39a78bSTobias Regnery 
887dc39a78bSTobias Regnery out_free:
888f3297f68SChristoph Hellwig 	free_irq(pci_irq_vector(alx->hw.pdev, free_vector++), alx);
889dc39a78bSTobias Regnery 
890dc39a78bSTobias Regnery 	vector--;
891dc39a78bSTobias Regnery 	for (i = 0; i < vector; i++)
892f3297f68SChristoph Hellwig 		free_irq(pci_irq_vector(alx->hw.pdev,free_vector++),
893e0eac254STobias Regnery 			 alx->qnapi[i]);
894dc39a78bSTobias Regnery 
895dc39a78bSTobias Regnery out_err:
896dc39a78bSTobias Regnery 	return err;
897dc39a78bSTobias Regnery }
898dc39a78bSTobias Regnery 
alx_init_intr(struct alx_priv * alx)899f3297f68SChristoph Hellwig static int alx_init_intr(struct alx_priv *alx)
9009ee7b683STobias Regnery {
901f3297f68SChristoph Hellwig 	int ret;
902dc39a78bSTobias Regnery 
903f3297f68SChristoph Hellwig 	ret = pci_alloc_irq_vectors(alx->hw.pdev, 1, 1,
904f3297f68SChristoph Hellwig 			PCI_IRQ_MSI | PCI_IRQ_LEGACY);
9052c041afcSRakesh Pandit 	if (ret < 0)
906f3297f68SChristoph Hellwig 		return ret;
907f3297f68SChristoph Hellwig 
908dc39a78bSTobias Regnery 	alx->num_vec = 1;
909e0eac254STobias Regnery 	alx->num_napi = 1;
910e0eac254STobias Regnery 	alx->num_txq = 1;
911e0eac254STobias Regnery 	alx->num_rxq = 1;
912f3297f68SChristoph Hellwig 	return 0;
9139ee7b683STobias Regnery }
9149ee7b683STobias Regnery 
alx_irq_enable(struct alx_priv * alx)915ab69bde6SJohannes Berg static void alx_irq_enable(struct alx_priv *alx)
916ab69bde6SJohannes Berg {
917ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
918dc39a78bSTobias Regnery 	int i;
919ab69bde6SJohannes Berg 
920ab69bde6SJohannes Berg 	/* level-1 interrupt switch */
921ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_ISR, 0);
922ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_IMR, alx->int_mask);
923ab69bde6SJohannes Berg 	alx_post_write(hw);
924dc39a78bSTobias Regnery 
925f3297f68SChristoph Hellwig 	if (alx->hw.pdev->msix_enabled) {
926dc39a78bSTobias Regnery 		/* enable all msix irqs */
927dc39a78bSTobias Regnery 		for (i = 0; i < alx->num_vec; i++)
928dc39a78bSTobias Regnery 			alx_mask_msix(hw, i, false);
929ab69bde6SJohannes Berg 	}
930f3297f68SChristoph Hellwig }
931ab69bde6SJohannes Berg 
alx_irq_disable(struct alx_priv * alx)932ab69bde6SJohannes Berg static void alx_irq_disable(struct alx_priv *alx)
933ab69bde6SJohannes Berg {
934ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
935dc39a78bSTobias Regnery 	int i;
936ab69bde6SJohannes Berg 
937ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_ISR, ALX_ISR_DIS);
938ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_IMR, 0);
939ab69bde6SJohannes Berg 	alx_post_write(hw);
940ab69bde6SJohannes Berg 
941f3297f68SChristoph Hellwig 	if (alx->hw.pdev->msix_enabled) {
942dc39a78bSTobias Regnery 		for (i = 0; i < alx->num_vec; i++) {
943dc39a78bSTobias Regnery 			alx_mask_msix(hw, i, true);
944f3297f68SChristoph Hellwig 			synchronize_irq(pci_irq_vector(alx->hw.pdev, i));
945dc39a78bSTobias Regnery 		}
946dc39a78bSTobias Regnery 	} else {
947f3297f68SChristoph Hellwig 		synchronize_irq(pci_irq_vector(alx->hw.pdev, 0));
948ab69bde6SJohannes Berg 	}
949dc39a78bSTobias Regnery }
950ab69bde6SJohannes Berg 
alx_realloc_resources(struct alx_priv * alx)951e0eac254STobias Regnery static int alx_realloc_resources(struct alx_priv *alx)
952e0eac254STobias Regnery {
953e0eac254STobias Regnery 	int err;
954e0eac254STobias Regnery 
955e0eac254STobias Regnery 	alx_free_rings(alx);
956e0eac254STobias Regnery 	alx_free_napis(alx);
957f3297f68SChristoph Hellwig 	pci_free_irq_vectors(alx->hw.pdev);
958f3297f68SChristoph Hellwig 
959f3297f68SChristoph Hellwig 	err = alx_init_intr(alx);
960f3297f68SChristoph Hellwig 	if (err)
961f3297f68SChristoph Hellwig 		return err;
962e0eac254STobias Regnery 
963e0eac254STobias Regnery 	err = alx_alloc_napis(alx);
964e0eac254STobias Regnery 	if (err)
965e0eac254STobias Regnery 		return err;
966e0eac254STobias Regnery 
967e0eac254STobias Regnery 	err = alx_alloc_rings(alx);
968e0eac254STobias Regnery 	if (err)
969e0eac254STobias Regnery 		return err;
970e0eac254STobias Regnery 
971e0eac254STobias Regnery 	return 0;
972e0eac254STobias Regnery }
973e0eac254STobias Regnery 
alx_request_irq(struct alx_priv * alx)974ab69bde6SJohannes Berg static int alx_request_irq(struct alx_priv *alx)
975ab69bde6SJohannes Berg {
976ab69bde6SJohannes Berg 	struct pci_dev *pdev = alx->hw.pdev;
977ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
978ab69bde6SJohannes Berg 	int err;
979ab69bde6SJohannes Berg 	u32 msi_ctrl;
980ab69bde6SJohannes Berg 
981ab69bde6SJohannes Berg 	msi_ctrl = (hw->imt >> 1) << ALX_MSI_RETRANS_TM_SHIFT;
982ab69bde6SJohannes Berg 
983f3297f68SChristoph Hellwig 	if (alx->hw.pdev->msix_enabled) {
984dc39a78bSTobias Regnery 		alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, msi_ctrl);
985dc39a78bSTobias Regnery 		err = alx_request_msix(alx);
986dc39a78bSTobias Regnery 		if (!err)
987dc39a78bSTobias Regnery 			goto out;
988dc39a78bSTobias Regnery 
989dc39a78bSTobias Regnery 		/* msix request failed, realloc resources */
990e0eac254STobias Regnery 		err = alx_realloc_resources(alx);
991e0eac254STobias Regnery 		if (err)
992e0eac254STobias Regnery 			goto out;
993dc39a78bSTobias Regnery 	}
994dc39a78bSTobias Regnery 
995f3297f68SChristoph Hellwig 	if (alx->hw.pdev->msi_enabled) {
996ab69bde6SJohannes Berg 		alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER,
997ab69bde6SJohannes Berg 				msi_ctrl | ALX_MSI_MASK_SEL_LINE);
998f3297f68SChristoph Hellwig 		err = request_irq(pci_irq_vector(pdev, 0), alx_intr_msi, 0,
999ab69bde6SJohannes Berg 				  alx->dev->name, alx);
1000ab69bde6SJohannes Berg 		if (!err)
1001ab69bde6SJohannes Berg 			goto out;
1002f3297f68SChristoph Hellwig 
1003ab69bde6SJohannes Berg 		/* fall back to legacy interrupt */
1004f3297f68SChristoph Hellwig 		pci_free_irq_vectors(alx->hw.pdev);
1005ab69bde6SJohannes Berg 	}
1006ab69bde6SJohannes Berg 
1007ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, 0);
1008f3297f68SChristoph Hellwig 	err = request_irq(pci_irq_vector(pdev, 0), alx_intr_legacy, IRQF_SHARED,
1009ab69bde6SJohannes Berg 			  alx->dev->name, alx);
1010ab69bde6SJohannes Berg out:
1011ab69bde6SJohannes Berg 	if (!err)
1012ab69bde6SJohannes Berg 		alx_config_vector_mapping(alx);
1013dc39a78bSTobias Regnery 	else
1014dc39a78bSTobias Regnery 		netdev_err(alx->dev, "IRQ registration failed!\n");
1015ab69bde6SJohannes Berg 	return err;
1016ab69bde6SJohannes Berg }
1017ab69bde6SJohannes Berg 
alx_free_irq(struct alx_priv * alx)1018ab69bde6SJohannes Berg static void alx_free_irq(struct alx_priv *alx)
1019ab69bde6SJohannes Berg {
1020ab69bde6SJohannes Berg 	struct pci_dev *pdev = alx->hw.pdev;
1021f3297f68SChristoph Hellwig 	int i;
1022ab69bde6SJohannes Berg 
1023f3297f68SChristoph Hellwig 	free_irq(pci_irq_vector(pdev, 0), alx);
1024f3297f68SChristoph Hellwig 	if (alx->hw.pdev->msix_enabled) {
1025e0eac254STobias Regnery 		for (i = 0; i < alx->num_napi; i++)
1026f3297f68SChristoph Hellwig 			free_irq(pci_irq_vector(pdev, i + 1), alx->qnapi[i]);
1027dc39a78bSTobias Regnery 	}
1028ab69bde6SJohannes Berg 
1029f3297f68SChristoph Hellwig 	pci_free_irq_vectors(pdev);
1030ab69bde6SJohannes Berg }
1031ab69bde6SJohannes Berg 
alx_identify_hw(struct alx_priv * alx)1032ab69bde6SJohannes Berg static int alx_identify_hw(struct alx_priv *alx)
1033ab69bde6SJohannes Berg {
1034ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
1035ab69bde6SJohannes Berg 	int rev = alx_hw_revision(hw);
1036ab69bde6SJohannes Berg 
1037ab69bde6SJohannes Berg 	if (rev > ALX_REV_C0)
1038ab69bde6SJohannes Berg 		return -EINVAL;
1039ab69bde6SJohannes Berg 
1040ab69bde6SJohannes Berg 	hw->max_dma_chnl = rev >= ALX_REV_B0 ? 4 : 2;
1041ab69bde6SJohannes Berg 
1042ab69bde6SJohannes Berg 	return 0;
1043ab69bde6SJohannes Berg }
1044ab69bde6SJohannes Berg 
alx_init_sw(struct alx_priv * alx)1045ab69bde6SJohannes Berg static int alx_init_sw(struct alx_priv *alx)
1046ab69bde6SJohannes Berg {
1047ab69bde6SJohannes Berg 	struct pci_dev *pdev = alx->hw.pdev;
1048ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
1049ab69bde6SJohannes Berg 	int err;
1050ab69bde6SJohannes Berg 
1051ab69bde6SJohannes Berg 	err = alx_identify_hw(alx);
1052ab69bde6SJohannes Berg 	if (err) {
1053ab69bde6SJohannes Berg 		dev_err(&pdev->dev, "unrecognized chip, aborting\n");
1054ab69bde6SJohannes Berg 		return err;
1055ab69bde6SJohannes Berg 	}
1056ab69bde6SJohannes Berg 
1057ab69bde6SJohannes Berg 	alx->hw.lnk_patch =
1058ab69bde6SJohannes Berg 		pdev->device == ALX_DEV_ID_AR8161 &&
1059ab69bde6SJohannes Berg 		pdev->subsystem_vendor == PCI_VENDOR_ID_ATTANSIC &&
1060ab69bde6SJohannes Berg 		pdev->subsystem_device == 0x0091 &&
1061ab69bde6SJohannes Berg 		pdev->revision == 0;
1062ab69bde6SJohannes Berg 
1063ab69bde6SJohannes Berg 	hw->smb_timer = 400;
1064ab69bde6SJohannes Berg 	hw->mtu = alx->dev->mtu;
1065c406700cSJarod Wilson 	alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu);
106667bef942SJarod Wilson 	/* MTU range: 34 - 9256 */
106767bef942SJarod Wilson 	alx->dev->min_mtu = 34;
106867bef942SJarod Wilson 	alx->dev->max_mtu = ALX_MAX_FRAME_LEN(ALX_MAX_FRAME_SIZE);
1069ab69bde6SJohannes Berg 	alx->tx_ringsz = 256;
1070ab69bde6SJohannes Berg 	alx->rx_ringsz = 512;
1071ab69bde6SJohannes Berg 	hw->imt = 200;
1072ab69bde6SJohannes Berg 	alx->int_mask = ALX_ISR_MISC;
1073ab69bde6SJohannes Berg 	hw->dma_chnl = hw->max_dma_chnl;
1074ab69bde6SJohannes Berg 	hw->ith_tpd = alx->tx_ringsz / 3;
1075ab69bde6SJohannes Berg 	hw->link_speed = SPEED_UNKNOWN;
1076a5b87cc9SJohannes Berg 	hw->duplex = DUPLEX_UNKNOWN;
1077ab69bde6SJohannes Berg 	hw->adv_cfg = ADVERTISED_Autoneg |
1078ab69bde6SJohannes Berg 		      ADVERTISED_10baseT_Half |
1079ab69bde6SJohannes Berg 		      ADVERTISED_10baseT_Full |
1080ab69bde6SJohannes Berg 		      ADVERTISED_100baseT_Full |
1081ab69bde6SJohannes Berg 		      ADVERTISED_100baseT_Half |
1082ab69bde6SJohannes Berg 		      ADVERTISED_1000baseT_Full;
1083ab69bde6SJohannes Berg 	hw->flowctrl = ALX_FC_ANEG | ALX_FC_RX | ALX_FC_TX;
1084ab69bde6SJohannes Berg 
1085ab69bde6SJohannes Berg 	hw->rx_ctrl = ALX_MAC_CTRL_WOLSPED_SWEN |
1086ab69bde6SJohannes Berg 		      ALX_MAC_CTRL_MHASH_ALG_HI5B |
1087ab69bde6SJohannes Berg 		      ALX_MAC_CTRL_BRD_EN |
1088ab69bde6SJohannes Berg 		      ALX_MAC_CTRL_PCRCE |
1089ab69bde6SJohannes Berg 		      ALX_MAC_CTRL_CRCE |
1090ab69bde6SJohannes Berg 		      ALX_MAC_CTRL_RXFC_EN |
1091ab69bde6SJohannes Berg 		      ALX_MAC_CTRL_TXFC_EN |
1092ab69bde6SJohannes Berg 		      7 << ALX_MAC_CTRL_PRMBLEN_SHIFT;
10934a5fe57eSJohannes Berg 	mutex_init(&alx->mtx);
1094ab69bde6SJohannes Berg 
10954a5fe57eSJohannes Berg 	return 0;
1096ab69bde6SJohannes Berg }
1097ab69bde6SJohannes Berg 
1098ab69bde6SJohannes Berg 
alx_fix_features(struct net_device * netdev,netdev_features_t features)1099ab69bde6SJohannes Berg static netdev_features_t alx_fix_features(struct net_device *netdev,
1100ab69bde6SJohannes Berg 					  netdev_features_t features)
1101ab69bde6SJohannes Berg {
1102ab69bde6SJohannes Berg 	if (netdev->mtu > ALX_MAX_TSO_PKT_SIZE)
1103ab69bde6SJohannes Berg 		features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
1104ab69bde6SJohannes Berg 
1105ab69bde6SJohannes Berg 	return features;
1106ab69bde6SJohannes Berg }
1107ab69bde6SJohannes Berg 
alx_netif_stop(struct alx_priv * alx)1108ab69bde6SJohannes Berg static void alx_netif_stop(struct alx_priv *alx)
1109ab69bde6SJohannes Berg {
1110a4076d34STobias Regnery 	int i;
1111a4076d34STobias Regnery 
1112860e9538SFlorian Westphal 	netif_trans_update(alx->dev);
1113ab69bde6SJohannes Berg 	if (netif_carrier_ok(alx->dev)) {
1114ab69bde6SJohannes Berg 		netif_carrier_off(alx->dev);
1115ab69bde6SJohannes Berg 		netif_tx_disable(alx->dev);
1116a4076d34STobias Regnery 		for (i = 0; i < alx->num_napi; i++)
1117a4076d34STobias Regnery 			napi_disable(&alx->qnapi[i]->napi);
1118ab69bde6SJohannes Berg 	}
1119ab69bde6SJohannes Berg }
1120ab69bde6SJohannes Berg 
alx_halt(struct alx_priv * alx)1121ab69bde6SJohannes Berg static void alx_halt(struct alx_priv *alx)
1122ab69bde6SJohannes Berg {
1123ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
1124ab69bde6SJohannes Berg 
11254a5fe57eSJohannes Berg 	lockdep_assert_held(&alx->mtx);
11264a5fe57eSJohannes Berg 
1127ab69bde6SJohannes Berg 	alx_netif_stop(alx);
1128ab69bde6SJohannes Berg 	hw->link_speed = SPEED_UNKNOWN;
1129a5b87cc9SJohannes Berg 	hw->duplex = DUPLEX_UNKNOWN;
1130ab69bde6SJohannes Berg 
1131ab69bde6SJohannes Berg 	alx_reset_mac(hw);
1132ab69bde6SJohannes Berg 
1133ab69bde6SJohannes Berg 	/* disable l0s/l1 */
1134ab69bde6SJohannes Berg 	alx_enable_aspm(hw, false, false);
1135ab69bde6SJohannes Berg 	alx_irq_disable(alx);
1136ab69bde6SJohannes Berg 	alx_free_buffers(alx);
1137ab69bde6SJohannes Berg }
1138ab69bde6SJohannes Berg 
alx_configure(struct alx_priv * alx)1139ab69bde6SJohannes Berg static void alx_configure(struct alx_priv *alx)
1140ab69bde6SJohannes Berg {
1141ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
1142ab69bde6SJohannes Berg 
1143ab69bde6SJohannes Berg 	alx_configure_basic(hw);
1144ab69bde6SJohannes Berg 	alx_disable_rss(hw);
1145ab69bde6SJohannes Berg 	__alx_set_rx_mode(alx->dev);
1146ab69bde6SJohannes Berg 
1147ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl);
1148ab69bde6SJohannes Berg }
1149ab69bde6SJohannes Berg 
alx_activate(struct alx_priv * alx)1150ab69bde6SJohannes Berg static void alx_activate(struct alx_priv *alx)
1151ab69bde6SJohannes Berg {
11524a5fe57eSJohannes Berg 	lockdep_assert_held(&alx->mtx);
11534a5fe57eSJohannes Berg 
1154ab69bde6SJohannes Berg 	/* hardware setting lost, restore it */
1155ab69bde6SJohannes Berg 	alx_reinit_rings(alx);
1156ab69bde6SJohannes Berg 	alx_configure(alx);
1157ab69bde6SJohannes Berg 
1158ab69bde6SJohannes Berg 	/* clear old interrupts */
1159ab69bde6SJohannes Berg 	alx_write_mem32(&alx->hw, ALX_ISR, ~(u32)ALX_ISR_DIS);
1160ab69bde6SJohannes Berg 
1161ab69bde6SJohannes Berg 	alx_irq_enable(alx);
1162ab69bde6SJohannes Berg 
1163ab69bde6SJohannes Berg 	alx_schedule_link_check(alx);
1164ab69bde6SJohannes Berg }
1165ab69bde6SJohannes Berg 
alx_reinit(struct alx_priv * alx)1166ab69bde6SJohannes Berg static void alx_reinit(struct alx_priv *alx)
1167ab69bde6SJohannes Berg {
11684a5fe57eSJohannes Berg 	lockdep_assert_held(&alx->mtx);
1169ab69bde6SJohannes Berg 
1170ab69bde6SJohannes Berg 	alx_halt(alx);
1171ab69bde6SJohannes Berg 	alx_activate(alx);
1172ab69bde6SJohannes Berg }
1173ab69bde6SJohannes Berg 
alx_change_mtu(struct net_device * netdev,int mtu)1174ab69bde6SJohannes Berg static int alx_change_mtu(struct net_device *netdev, int mtu)
1175ab69bde6SJohannes Berg {
1176ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
1177c406700cSJarod Wilson 	int max_frame = ALX_MAX_FRAME_LEN(mtu);
1178ab69bde6SJohannes Berg 
1179ab69bde6SJohannes Berg 	netdev->mtu = mtu;
1180ab69bde6SJohannes Berg 	alx->hw.mtu = mtu;
1181c406700cSJarod Wilson 	alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE);
1182ab69bde6SJohannes Berg 	netdev_update_features(netdev);
118346b348fdSNiels Dossche 	if (netif_running(netdev)) {
118446b348fdSNiels Dossche 		mutex_lock(&alx->mtx);
1185ab69bde6SJohannes Berg 		alx_reinit(alx);
118646b348fdSNiels Dossche 		mutex_unlock(&alx->mtx);
118746b348fdSNiels Dossche 	}
1188ab69bde6SJohannes Berg 	return 0;
1189ab69bde6SJohannes Berg }
1190ab69bde6SJohannes Berg 
alx_netif_start(struct alx_priv * alx)1191ab69bde6SJohannes Berg static void alx_netif_start(struct alx_priv *alx)
1192ab69bde6SJohannes Berg {
1193a4076d34STobias Regnery 	int i;
1194a4076d34STobias Regnery 
1195ab69bde6SJohannes Berg 	netif_tx_wake_all_queues(alx->dev);
1196a4076d34STobias Regnery 	for (i = 0; i < alx->num_napi; i++)
1197a4076d34STobias Regnery 		napi_enable(&alx->qnapi[i]->napi);
1198ab69bde6SJohannes Berg 	netif_carrier_on(alx->dev);
1199ab69bde6SJohannes Berg }
1200ab69bde6SJohannes Berg 
__alx_open(struct alx_priv * alx,bool resume)1201ab69bde6SJohannes Berg static int __alx_open(struct alx_priv *alx, bool resume)
1202ab69bde6SJohannes Berg {
1203ab69bde6SJohannes Berg 	int err;
1204ab69bde6SJohannes Berg 
1205f3297f68SChristoph Hellwig 	err = alx_enable_msix(alx);
1206f3297f68SChristoph Hellwig 	if (err < 0) {
1207f3297f68SChristoph Hellwig 		err = alx_init_intr(alx);
1208f3297f68SChristoph Hellwig 		if (err)
1209f3297f68SChristoph Hellwig 			return err;
1210f3297f68SChristoph Hellwig 	}
12119ee7b683STobias Regnery 
1212ab69bde6SJohannes Berg 	if (!resume)
1213ab69bde6SJohannes Berg 		netif_carrier_off(alx->dev);
1214ab69bde6SJohannes Berg 
1215b0999223STobias Regnery 	err = alx_alloc_napis(alx);
1216ab69bde6SJohannes Berg 	if (err)
12170ca4e20bSTobias Regnery 		goto out_disable_adv_intr;
1218ab69bde6SJohannes Berg 
1219b0999223STobias Regnery 	err = alx_alloc_rings(alx);
1220b0999223STobias Regnery 	if (err)
1221b0999223STobias Regnery 		goto out_free_rings;
1222b0999223STobias Regnery 
1223ab69bde6SJohannes Berg 	alx_configure(alx);
1224ab69bde6SJohannes Berg 
1225ab69bde6SJohannes Berg 	err = alx_request_irq(alx);
1226ab69bde6SJohannes Berg 	if (err)
1227ab69bde6SJohannes Berg 		goto out_free_rings;
1228ab69bde6SJohannes Berg 
1229185aceefSTobias Regnery 	/* must be called after alx_request_irq because the chip stops working
1230185aceefSTobias Regnery 	 * if we copy the dma addresses in alx_init_ring_ptrs twice when
1231185aceefSTobias Regnery 	 * requesting msi-x interrupts failed
1232185aceefSTobias Regnery 	 */
1233185aceefSTobias Regnery 	alx_reinit_rings(alx);
1234185aceefSTobias Regnery 
1235d768319cSTobias Regnery 	netif_set_real_num_tx_queues(alx->dev, alx->num_txq);
1236d768319cSTobias Regnery 	netif_set_real_num_rx_queues(alx->dev, alx->num_rxq);
1237d768319cSTobias Regnery 
1238ab69bde6SJohannes Berg 	/* clear old interrupts */
1239ab69bde6SJohannes Berg 	alx_write_mem32(&alx->hw, ALX_ISR, ~(u32)ALX_ISR_DIS);
1240ab69bde6SJohannes Berg 
1241ab69bde6SJohannes Berg 	alx_irq_enable(alx);
1242ab69bde6SJohannes Berg 
1243ab69bde6SJohannes Berg 	if (!resume)
1244ab69bde6SJohannes Berg 		netif_tx_start_all_queues(alx->dev);
1245ab69bde6SJohannes Berg 
1246ab69bde6SJohannes Berg 	alx_schedule_link_check(alx);
1247ab69bde6SJohannes Berg 	return 0;
1248ab69bde6SJohannes Berg 
1249ab69bde6SJohannes Berg out_free_rings:
1250ab69bde6SJohannes Berg 	alx_free_rings(alx);
1251b0999223STobias Regnery 	alx_free_napis(alx);
12520ca4e20bSTobias Regnery out_disable_adv_intr:
1253f3297f68SChristoph Hellwig 	pci_free_irq_vectors(alx->hw.pdev);
1254ab69bde6SJohannes Berg 	return err;
1255ab69bde6SJohannes Berg }
1256ab69bde6SJohannes Berg 
__alx_stop(struct alx_priv * alx)1257ab69bde6SJohannes Berg static void __alx_stop(struct alx_priv *alx)
1258ab69bde6SJohannes Berg {
12594a5fe57eSJohannes Berg 	lockdep_assert_held(&alx->mtx);
12604a5fe57eSJohannes Berg 
1261ab69bde6SJohannes Berg 	alx_free_irq(alx);
1262e89df5c4SZekun Shen 
1263e89df5c4SZekun Shen 	cancel_work_sync(&alx->link_check_wk);
1264e89df5c4SZekun Shen 	cancel_work_sync(&alx->reset_wk);
1265e89df5c4SZekun Shen 
1266e89df5c4SZekun Shen 	alx_halt(alx);
1267ab69bde6SJohannes Berg 	alx_free_rings(alx);
1268b0999223STobias Regnery 	alx_free_napis(alx);
1269ab69bde6SJohannes Berg }
1270ab69bde6SJohannes Berg 
alx_speed_desc(struct alx_hw * hw)1271a5b87cc9SJohannes Berg static const char *alx_speed_desc(struct alx_hw *hw)
1272ab69bde6SJohannes Berg {
1273a5b87cc9SJohannes Berg 	switch (alx_speed_to_ethadv(hw->link_speed, hw->duplex)) {
1274a5b87cc9SJohannes Berg 	case ADVERTISED_1000baseT_Full:
1275ab69bde6SJohannes Berg 		return "1 Gbps Full";
1276a5b87cc9SJohannes Berg 	case ADVERTISED_100baseT_Full:
1277ab69bde6SJohannes Berg 		return "100 Mbps Full";
1278a5b87cc9SJohannes Berg 	case ADVERTISED_100baseT_Half:
1279ab69bde6SJohannes Berg 		return "100 Mbps Half";
1280a5b87cc9SJohannes Berg 	case ADVERTISED_10baseT_Full:
1281ab69bde6SJohannes Berg 		return "10 Mbps Full";
1282a5b87cc9SJohannes Berg 	case ADVERTISED_10baseT_Half:
1283ab69bde6SJohannes Berg 		return "10 Mbps Half";
1284ab69bde6SJohannes Berg 	default:
1285ab69bde6SJohannes Berg 		return "Unknown speed";
1286ab69bde6SJohannes Berg 	}
1287ab69bde6SJohannes Berg }
1288ab69bde6SJohannes Berg 
alx_check_link(struct alx_priv * alx)1289ab69bde6SJohannes Berg static void alx_check_link(struct alx_priv *alx)
1290ab69bde6SJohannes Berg {
1291ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
1292ab69bde6SJohannes Berg 	unsigned long flags;
1293a5b87cc9SJohannes Berg 	int old_speed;
1294ab69bde6SJohannes Berg 	int err;
1295ab69bde6SJohannes Berg 
12964a5fe57eSJohannes Berg 	lockdep_assert_held(&alx->mtx);
12974a5fe57eSJohannes Berg 
1298ab69bde6SJohannes Berg 	/* clear PHY internal interrupt status, otherwise the main
1299ab69bde6SJohannes Berg 	 * interrupt status will be asserted forever
1300ab69bde6SJohannes Berg 	 */
1301ab69bde6SJohannes Berg 	alx_clear_phy_intr(hw);
1302ab69bde6SJohannes Berg 
1303a5b87cc9SJohannes Berg 	old_speed = hw->link_speed;
1304a5b87cc9SJohannes Berg 	err = alx_read_phy_link(hw);
1305ab69bde6SJohannes Berg 	if (err < 0)
1306ab69bde6SJohannes Berg 		goto reset;
1307ab69bde6SJohannes Berg 
1308ab69bde6SJohannes Berg 	spin_lock_irqsave(&alx->irq_lock, flags);
1309ab69bde6SJohannes Berg 	alx->int_mask |= ALX_ISR_PHY;
1310ab69bde6SJohannes Berg 	alx_write_mem32(hw, ALX_IMR, alx->int_mask);
1311ab69bde6SJohannes Berg 	spin_unlock_irqrestore(&alx->irq_lock, flags);
1312ab69bde6SJohannes Berg 
1313a5b87cc9SJohannes Berg 	if (old_speed == hw->link_speed)
1314ab69bde6SJohannes Berg 		return;
1315ab69bde6SJohannes Berg 
1316a5b87cc9SJohannes Berg 	if (hw->link_speed != SPEED_UNKNOWN) {
1317ab69bde6SJohannes Berg 		netif_info(alx, link, alx->dev,
1318a5b87cc9SJohannes Berg 			   "NIC Up: %s\n", alx_speed_desc(hw));
1319ab69bde6SJohannes Berg 		alx_post_phy_link(hw);
1320ab69bde6SJohannes Berg 		alx_enable_aspm(hw, true, true);
1321ab69bde6SJohannes Berg 		alx_start_mac(hw);
1322ab69bde6SJohannes Berg 
1323ab69bde6SJohannes Berg 		if (old_speed == SPEED_UNKNOWN)
1324ab69bde6SJohannes Berg 			alx_netif_start(alx);
1325ab69bde6SJohannes Berg 	} else {
1326ab69bde6SJohannes Berg 		/* link is now down */
1327ab69bde6SJohannes Berg 		alx_netif_stop(alx);
1328ab69bde6SJohannes Berg 		netif_info(alx, link, alx->dev, "Link Down\n");
1329ab69bde6SJohannes Berg 		err = alx_reset_mac(hw);
1330ab69bde6SJohannes Berg 		if (err)
1331ab69bde6SJohannes Berg 			goto reset;
1332ab69bde6SJohannes Berg 		alx_irq_disable(alx);
1333ab69bde6SJohannes Berg 
1334ab69bde6SJohannes Berg 		/* MAC reset causes all HW settings to be lost, restore all */
1335ab69bde6SJohannes Berg 		err = alx_reinit_rings(alx);
1336ab69bde6SJohannes Berg 		if (err)
1337ab69bde6SJohannes Berg 			goto reset;
1338ab69bde6SJohannes Berg 		alx_configure(alx);
1339ab69bde6SJohannes Berg 		alx_enable_aspm(hw, false, true);
1340ab69bde6SJohannes Berg 		alx_post_phy_link(hw);
1341ab69bde6SJohannes Berg 		alx_irq_enable(alx);
1342ab69bde6SJohannes Berg 	}
1343ab69bde6SJohannes Berg 
1344ab69bde6SJohannes Berg 	return;
1345ab69bde6SJohannes Berg 
1346ab69bde6SJohannes Berg reset:
1347ab69bde6SJohannes Berg 	alx_schedule_reset(alx);
1348ab69bde6SJohannes Berg }
1349ab69bde6SJohannes Berg 
alx_open(struct net_device * netdev)1350ab69bde6SJohannes Berg static int alx_open(struct net_device *netdev)
1351ab69bde6SJohannes Berg {
13524a5fe57eSJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
13534a5fe57eSJohannes Berg 	int ret;
13544a5fe57eSJohannes Berg 
13554a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
13564a5fe57eSJohannes Berg 	ret = __alx_open(alx, false);
13574a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
13584a5fe57eSJohannes Berg 
13594a5fe57eSJohannes Berg 	return ret;
1360ab69bde6SJohannes Berg }
1361ab69bde6SJohannes Berg 
alx_stop(struct net_device * netdev)1362ab69bde6SJohannes Berg static int alx_stop(struct net_device *netdev)
1363ab69bde6SJohannes Berg {
13644a5fe57eSJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
13654a5fe57eSJohannes Berg 
13664a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
13674a5fe57eSJohannes Berg 	__alx_stop(alx);
13684a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
13694a5fe57eSJohannes Berg 
1370ab69bde6SJohannes Berg 	return 0;
1371ab69bde6SJohannes Berg }
1372ab69bde6SJohannes Berg 
alx_link_check(struct work_struct * work)1373ab69bde6SJohannes Berg static void alx_link_check(struct work_struct *work)
1374ab69bde6SJohannes Berg {
1375ab69bde6SJohannes Berg 	struct alx_priv *alx;
1376ab69bde6SJohannes Berg 
1377ab69bde6SJohannes Berg 	alx = container_of(work, struct alx_priv, link_check_wk);
1378ab69bde6SJohannes Berg 
13794a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
1380ab69bde6SJohannes Berg 	alx_check_link(alx);
13814a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
1382ab69bde6SJohannes Berg }
1383ab69bde6SJohannes Berg 
alx_reset(struct work_struct * work)1384ab69bde6SJohannes Berg static void alx_reset(struct work_struct *work)
1385ab69bde6SJohannes Berg {
1386ab69bde6SJohannes Berg 	struct alx_priv *alx = container_of(work, struct alx_priv, reset_wk);
1387ab69bde6SJohannes Berg 
13884a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
1389ab69bde6SJohannes Berg 	alx_reinit(alx);
13904a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
1391ab69bde6SJohannes Berg }
1392ab69bde6SJohannes Berg 
alx_tpd_req(struct sk_buff * skb)1393ab725983STobias Regnery static int alx_tpd_req(struct sk_buff *skb)
1394ab725983STobias Regnery {
1395ab725983STobias Regnery 	int num;
1396ab725983STobias Regnery 
1397ab725983STobias Regnery 	num = skb_shinfo(skb)->nr_frags + 1;
1398ab725983STobias Regnery 	/* we need one extra descriptor for LSOv2 */
1399ab725983STobias Regnery 	if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
1400ab725983STobias Regnery 		num++;
1401ab725983STobias Regnery 
1402ab725983STobias Regnery 	return num;
1403ab725983STobias Regnery }
1404ab725983STobias Regnery 
alx_tx_csum(struct sk_buff * skb,struct alx_txd * first)1405ab69bde6SJohannes Berg static int alx_tx_csum(struct sk_buff *skb, struct alx_txd *first)
1406ab69bde6SJohannes Berg {
1407ab69bde6SJohannes Berg 	u8 cso, css;
1408ab69bde6SJohannes Berg 
1409ab69bde6SJohannes Berg 	if (skb->ip_summed != CHECKSUM_PARTIAL)
1410ab69bde6SJohannes Berg 		return 0;
1411ab69bde6SJohannes Berg 
1412ab69bde6SJohannes Berg 	cso = skb_checksum_start_offset(skb);
1413ab69bde6SJohannes Berg 	if (cso & 1)
1414ab69bde6SJohannes Berg 		return -EINVAL;
1415ab69bde6SJohannes Berg 
1416ab69bde6SJohannes Berg 	css = cso + skb->csum_offset;
1417ab69bde6SJohannes Berg 	first->word1 |= cpu_to_le32((cso >> 1) << TPD_CXSUMSTART_SHIFT);
1418ab69bde6SJohannes Berg 	first->word1 |= cpu_to_le32((css >> 1) << TPD_CXSUMOFFSET_SHIFT);
1419ab69bde6SJohannes Berg 	first->word1 |= cpu_to_le32(1 << TPD_CXSUM_EN_SHIFT);
1420ab69bde6SJohannes Berg 
1421ab69bde6SJohannes Berg 	return 0;
1422ab69bde6SJohannes Berg }
1423ab69bde6SJohannes Berg 
alx_tso(struct sk_buff * skb,struct alx_txd * first)1424ab725983STobias Regnery static int alx_tso(struct sk_buff *skb, struct alx_txd *first)
1425ab725983STobias Regnery {
1426ab725983STobias Regnery 	int err;
1427ab725983STobias Regnery 
1428ab725983STobias Regnery 	if (skb->ip_summed != CHECKSUM_PARTIAL)
1429ab725983STobias Regnery 		return 0;
1430ab725983STobias Regnery 
1431ab725983STobias Regnery 	if (!skb_is_gso(skb))
1432ab725983STobias Regnery 		return 0;
1433ab725983STobias Regnery 
1434ab725983STobias Regnery 	err = skb_cow_head(skb, 0);
1435ab725983STobias Regnery 	if (err < 0)
1436ab725983STobias Regnery 		return err;
1437ab725983STobias Regnery 
1438ab725983STobias Regnery 	if (skb->protocol == htons(ETH_P_IP)) {
1439ab725983STobias Regnery 		struct iphdr *iph = ip_hdr(skb);
1440ab725983STobias Regnery 
1441ab725983STobias Regnery 		iph->check = 0;
1442ab725983STobias Regnery 		tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
1443ab725983STobias Regnery 							 0, IPPROTO_TCP, 0);
1444ab725983STobias Regnery 		first->word1 |= 1 << TPD_IPV4_SHIFT;
1445ab725983STobias Regnery 	} else if (skb_is_gso_v6(skb)) {
14464d4c3783SHeiner Kallweit 		tcp_v6_gso_csum_prep(skb);
1447ab725983STobias Regnery 		/* LSOv2: the first TPD only provides the packet length */
1448ab725983STobias Regnery 		first->adrl.l.pkt_len = skb->len;
1449ab725983STobias Regnery 		first->word1 |= 1 << TPD_LSO_V2_SHIFT;
1450ab725983STobias Regnery 	}
1451ab725983STobias Regnery 
1452ab725983STobias Regnery 	first->word1 |= 1 << TPD_LSO_EN_SHIFT;
1453ab725983STobias Regnery 	first->word1 |= (skb_transport_offset(skb) &
1454ab725983STobias Regnery 			 TPD_L4HDROFFSET_MASK) << TPD_L4HDROFFSET_SHIFT;
1455ab725983STobias Regnery 	first->word1 |= (skb_shinfo(skb)->gso_size &
1456ab725983STobias Regnery 			 TPD_MSS_MASK) << TPD_MSS_SHIFT;
1457ab725983STobias Regnery 	return 1;
1458ab725983STobias Regnery }
1459ab725983STobias Regnery 
alx_map_tx_skb(struct alx_tx_queue * txq,struct sk_buff * skb)1460702e8418STobias Regnery static int alx_map_tx_skb(struct alx_tx_queue *txq, struct sk_buff *skb)
1461ab69bde6SJohannes Berg {
1462ab69bde6SJohannes Berg 	struct alx_txd *tpd, *first_tpd;
1463ab69bde6SJohannes Berg 	dma_addr_t dma;
1464ab69bde6SJohannes Berg 	int maplen, f, first_idx = txq->write_idx;
1465ab69bde6SJohannes Berg 
1466ab69bde6SJohannes Berg 	first_tpd = &txq->tpd[txq->write_idx];
1467ab69bde6SJohannes Berg 	tpd = first_tpd;
1468ab69bde6SJohannes Berg 
1469ab725983STobias Regnery 	if (tpd->word1 & (1 << TPD_LSO_V2_SHIFT)) {
1470702e8418STobias Regnery 		if (++txq->write_idx == txq->count)
1471ab725983STobias Regnery 			txq->write_idx = 0;
1472ab725983STobias Regnery 
1473ab725983STobias Regnery 		tpd = &txq->tpd[txq->write_idx];
1474ab725983STobias Regnery 		tpd->len = first_tpd->len;
1475ab725983STobias Regnery 		tpd->vlan_tag = first_tpd->vlan_tag;
1476ab725983STobias Regnery 		tpd->word1 = first_tpd->word1;
1477ab725983STobias Regnery 	}
1478ab725983STobias Regnery 
1479ab69bde6SJohannes Berg 	maplen = skb_headlen(skb);
1480702e8418STobias Regnery 	dma = dma_map_single(txq->dev, skb->data, maplen,
1481ab69bde6SJohannes Berg 			     DMA_TO_DEVICE);
1482702e8418STobias Regnery 	if (dma_mapping_error(txq->dev, dma))
1483ab69bde6SJohannes Berg 		goto err_dma;
1484ab69bde6SJohannes Berg 
1485ab69bde6SJohannes Berg 	dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen);
1486ab69bde6SJohannes Berg 	dma_unmap_addr_set(&txq->bufs[txq->write_idx], dma, dma);
1487ab69bde6SJohannes Berg 
1488ab69bde6SJohannes Berg 	tpd->adrl.addr = cpu_to_le64(dma);
1489ab69bde6SJohannes Berg 	tpd->len = cpu_to_le16(maplen);
1490ab69bde6SJohannes Berg 
1491ab69bde6SJohannes Berg 	for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) {
1492d7840976SMatthew Wilcox (Oracle) 		skb_frag_t *frag = &skb_shinfo(skb)->frags[f];
1493ab69bde6SJohannes Berg 
1494702e8418STobias Regnery 		if (++txq->write_idx == txq->count)
1495ab69bde6SJohannes Berg 			txq->write_idx = 0;
1496ab69bde6SJohannes Berg 		tpd = &txq->tpd[txq->write_idx];
1497ab69bde6SJohannes Berg 
1498ab69bde6SJohannes Berg 		tpd->word1 = first_tpd->word1;
1499ab69bde6SJohannes Berg 
1500ab69bde6SJohannes Berg 		maplen = skb_frag_size(frag);
1501702e8418STobias Regnery 		dma = skb_frag_dma_map(txq->dev, frag, 0,
1502ab69bde6SJohannes Berg 				       maplen, DMA_TO_DEVICE);
1503702e8418STobias Regnery 		if (dma_mapping_error(txq->dev, dma))
1504ab69bde6SJohannes Berg 			goto err_dma;
1505ab69bde6SJohannes Berg 		dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen);
1506ab69bde6SJohannes Berg 		dma_unmap_addr_set(&txq->bufs[txq->write_idx], dma, dma);
1507ab69bde6SJohannes Berg 
1508ab69bde6SJohannes Berg 		tpd->adrl.addr = cpu_to_le64(dma);
1509ab69bde6SJohannes Berg 		tpd->len = cpu_to_le16(maplen);
1510ab69bde6SJohannes Berg 	}
1511ab69bde6SJohannes Berg 
1512ab69bde6SJohannes Berg 	/* last TPD, set EOP flag and store skb */
1513ab69bde6SJohannes Berg 	tpd->word1 |= cpu_to_le32(1 << TPD_EOP_SHIFT);
1514ab69bde6SJohannes Berg 	txq->bufs[txq->write_idx].skb = skb;
1515ab69bde6SJohannes Berg 
1516702e8418STobias Regnery 	if (++txq->write_idx == txq->count)
1517ab69bde6SJohannes Berg 		txq->write_idx = 0;
1518ab69bde6SJohannes Berg 
1519ab69bde6SJohannes Berg 	return 0;
1520ab69bde6SJohannes Berg 
1521ab69bde6SJohannes Berg err_dma:
1522ab69bde6SJohannes Berg 	f = first_idx;
1523ab69bde6SJohannes Berg 	while (f != txq->write_idx) {
1524702e8418STobias Regnery 		alx_free_txbuf(txq, f);
1525702e8418STobias Regnery 		if (++f == txq->count)
1526ab69bde6SJohannes Berg 			f = 0;
1527ab69bde6SJohannes Berg 	}
1528ab69bde6SJohannes Berg 	return -ENOMEM;
1529ab69bde6SJohannes Berg }
1530ab69bde6SJohannes Berg 
alx_start_xmit_ring(struct sk_buff * skb,struct alx_tx_queue * txq)15312e06826bSTobias Regnery static netdev_tx_t alx_start_xmit_ring(struct sk_buff *skb,
15322e06826bSTobias Regnery 				       struct alx_tx_queue *txq)
1533ab69bde6SJohannes Berg {
15342e06826bSTobias Regnery 	struct alx_priv *alx;
1535ab69bde6SJohannes Berg 	struct alx_txd *first;
1536ab725983STobias Regnery 	int tso;
1537ab69bde6SJohannes Berg 
15382e06826bSTobias Regnery 	alx = netdev_priv(txq->netdev);
15392e06826bSTobias Regnery 
1540702e8418STobias Regnery 	if (alx_tpd_avail(txq) < alx_tpd_req(skb)) {
15412e06826bSTobias Regnery 		netif_tx_stop_queue(alx_get_tx_queue(txq));
1542ab69bde6SJohannes Berg 		goto drop;
1543ab69bde6SJohannes Berg 	}
1544ab69bde6SJohannes Berg 
1545ab69bde6SJohannes Berg 	first = &txq->tpd[txq->write_idx];
1546ab69bde6SJohannes Berg 	memset(first, 0, sizeof(*first));
1547ab69bde6SJohannes Berg 
1548ab725983STobias Regnery 	tso = alx_tso(skb, first);
1549ab725983STobias Regnery 	if (tso < 0)
1550ab725983STobias Regnery 		goto drop;
1551ab725983STobias Regnery 	else if (!tso && alx_tx_csum(skb, first))
1552ab69bde6SJohannes Berg 		goto drop;
1553ab69bde6SJohannes Berg 
1554702e8418STobias Regnery 	if (alx_map_tx_skb(txq, skb) < 0)
1555ab69bde6SJohannes Berg 		goto drop;
1556ab69bde6SJohannes Berg 
15572e06826bSTobias Regnery 	netdev_tx_sent_queue(alx_get_tx_queue(txq), skb->len);
1558ab69bde6SJohannes Berg 
1559ab69bde6SJohannes Berg 	/* flush updates before updating hardware */
1560ab69bde6SJohannes Berg 	wmb();
15612e06826bSTobias Regnery 	alx_write_mem16(&alx->hw, txq->p_reg, txq->write_idx);
1562ab69bde6SJohannes Berg 
1563702e8418STobias Regnery 	if (alx_tpd_avail(txq) < txq->count / 8)
15642e06826bSTobias Regnery 		netif_tx_stop_queue(alx_get_tx_queue(txq));
1565ab69bde6SJohannes Berg 
1566ab69bde6SJohannes Berg 	return NETDEV_TX_OK;
1567ab69bde6SJohannes Berg 
1568ab69bde6SJohannes Berg drop:
1569548ff1edSEric W. Biederman 	dev_kfree_skb_any(skb);
1570ab69bde6SJohannes Berg 	return NETDEV_TX_OK;
1571ab69bde6SJohannes Berg }
1572ab69bde6SJohannes Berg 
alx_start_xmit(struct sk_buff * skb,struct net_device * netdev)15732e06826bSTobias Regnery static netdev_tx_t alx_start_xmit(struct sk_buff *skb,
15742e06826bSTobias Regnery 				  struct net_device *netdev)
15752e06826bSTobias Regnery {
15762e06826bSTobias Regnery 	struct alx_priv *alx = netdev_priv(netdev);
15772e06826bSTobias Regnery 	return alx_start_xmit_ring(skb, alx_tx_queue_mapping(alx, skb));
15782e06826bSTobias Regnery }
15792e06826bSTobias Regnery 
alx_tx_timeout(struct net_device * dev,unsigned int txqueue)15800290bd29SMichael S. Tsirkin static void alx_tx_timeout(struct net_device *dev, unsigned int txqueue)
1581ab69bde6SJohannes Berg {
1582ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(dev);
1583ab69bde6SJohannes Berg 
1584ab69bde6SJohannes Berg 	alx_schedule_reset(alx);
1585ab69bde6SJohannes Berg }
1586ab69bde6SJohannes Berg 
alx_mdio_read(struct net_device * netdev,int prtad,int devad,u16 addr)1587ab69bde6SJohannes Berg static int alx_mdio_read(struct net_device *netdev,
1588ab69bde6SJohannes Berg 			 int prtad, int devad, u16 addr)
1589ab69bde6SJohannes Berg {
1590ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
1591ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
1592ab69bde6SJohannes Berg 	u16 val;
1593ab69bde6SJohannes Berg 	int err;
1594ab69bde6SJohannes Berg 
1595ab69bde6SJohannes Berg 	if (prtad != hw->mdio.prtad)
1596ab69bde6SJohannes Berg 		return -EINVAL;
1597ab69bde6SJohannes Berg 
1598ab69bde6SJohannes Berg 	if (devad == MDIO_DEVAD_NONE)
1599ab69bde6SJohannes Berg 		err = alx_read_phy_reg(hw, addr, &val);
1600ab69bde6SJohannes Berg 	else
1601ab69bde6SJohannes Berg 		err = alx_read_phy_ext(hw, devad, addr, &val);
1602ab69bde6SJohannes Berg 
1603ab69bde6SJohannes Berg 	if (err)
1604ab69bde6SJohannes Berg 		return err;
1605ab69bde6SJohannes Berg 	return val;
1606ab69bde6SJohannes Berg }
1607ab69bde6SJohannes Berg 
alx_mdio_write(struct net_device * netdev,int prtad,int devad,u16 addr,u16 val)1608ab69bde6SJohannes Berg static int alx_mdio_write(struct net_device *netdev,
1609ab69bde6SJohannes Berg 			  int prtad, int devad, u16 addr, u16 val)
1610ab69bde6SJohannes Berg {
1611ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
1612ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
1613ab69bde6SJohannes Berg 
1614ab69bde6SJohannes Berg 	if (prtad != hw->mdio.prtad)
1615ab69bde6SJohannes Berg 		return -EINVAL;
1616ab69bde6SJohannes Berg 
1617ab69bde6SJohannes Berg 	if (devad == MDIO_DEVAD_NONE)
1618ab69bde6SJohannes Berg 		return alx_write_phy_reg(hw, addr, val);
1619ab69bde6SJohannes Berg 
1620ab69bde6SJohannes Berg 	return alx_write_phy_ext(hw, devad, addr, val);
1621ab69bde6SJohannes Berg }
1622ab69bde6SJohannes Berg 
alx_ioctl(struct net_device * netdev,struct ifreq * ifr,int cmd)1623ab69bde6SJohannes Berg static int alx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
1624ab69bde6SJohannes Berg {
1625ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
1626ab69bde6SJohannes Berg 
1627ab69bde6SJohannes Berg 	if (!netif_running(netdev))
1628ab69bde6SJohannes Berg 		return -EAGAIN;
1629ab69bde6SJohannes Berg 
1630ab69bde6SJohannes Berg 	return mdio_mii_ioctl(&alx->hw.mdio, if_mii(ifr), cmd);
1631ab69bde6SJohannes Berg }
1632ab69bde6SJohannes Berg 
1633ab69bde6SJohannes Berg #ifdef CONFIG_NET_POLL_CONTROLLER
alx_poll_controller(struct net_device * netdev)1634ab69bde6SJohannes Berg static void alx_poll_controller(struct net_device *netdev)
1635ab69bde6SJohannes Berg {
1636ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
1637e0eac254STobias Regnery 	int i;
1638ab69bde6SJohannes Berg 
1639f3297f68SChristoph Hellwig 	if (alx->hw.pdev->msix_enabled) {
1640dc39a78bSTobias Regnery 		alx_intr_msix_misc(0, alx);
1641e0eac254STobias Regnery 		for (i = 0; i < alx->num_txq; i++)
1642e0eac254STobias Regnery 			alx_intr_msix_ring(0, alx->qnapi[i]);
1643f3297f68SChristoph Hellwig 	} else if (alx->hw.pdev->msi_enabled)
1644ab69bde6SJohannes Berg 		alx_intr_msi(0, alx);
1645ab69bde6SJohannes Berg 	else
1646ab69bde6SJohannes Berg 		alx_intr_legacy(0, alx);
1647ab69bde6SJohannes Berg }
1648ab69bde6SJohannes Berg #endif
1649ab69bde6SJohannes Berg 
alx_get_stats64(struct net_device * dev,struct rtnl_link_stats64 * net_stats)1650bc1f4470Sstephen hemminger static void alx_get_stats64(struct net_device *dev,
1651f1b6b106SSabrina Dubroca 			    struct rtnl_link_stats64 *net_stats)
1652f1b6b106SSabrina Dubroca {
1653f1b6b106SSabrina Dubroca 	struct alx_priv *alx = netdev_priv(dev);
1654f1b6b106SSabrina Dubroca 	struct alx_hw_stats *hw_stats = &alx->hw.stats;
1655f1b6b106SSabrina Dubroca 
1656f1b6b106SSabrina Dubroca 	spin_lock(&alx->stats_lock);
1657f1b6b106SSabrina Dubroca 
1658f1b6b106SSabrina Dubroca 	alx_update_hw_stats(&alx->hw);
1659f1b6b106SSabrina Dubroca 
1660f1b6b106SSabrina Dubroca 	net_stats->tx_bytes   = hw_stats->tx_byte_cnt;
1661f1b6b106SSabrina Dubroca 	net_stats->rx_bytes   = hw_stats->rx_byte_cnt;
1662f1b6b106SSabrina Dubroca 	net_stats->multicast  = hw_stats->rx_mcast;
1663f1b6b106SSabrina Dubroca 	net_stats->collisions = hw_stats->tx_single_col +
1664f1b6b106SSabrina Dubroca 				hw_stats->tx_multi_col +
1665f1b6b106SSabrina Dubroca 				hw_stats->tx_late_col +
1666f1b6b106SSabrina Dubroca 				hw_stats->tx_abort_col;
1667f1b6b106SSabrina Dubroca 
1668f1b6b106SSabrina Dubroca 	net_stats->rx_errors  = hw_stats->rx_frag +
1669f1b6b106SSabrina Dubroca 				hw_stats->rx_fcs_err +
1670f1b6b106SSabrina Dubroca 				hw_stats->rx_len_err +
1671f1b6b106SSabrina Dubroca 				hw_stats->rx_ov_sz +
1672f1b6b106SSabrina Dubroca 				hw_stats->rx_ov_rrd +
1673f1b6b106SSabrina Dubroca 				hw_stats->rx_align_err +
1674f1b6b106SSabrina Dubroca 				hw_stats->rx_ov_rxf;
1675f1b6b106SSabrina Dubroca 
1676f1b6b106SSabrina Dubroca 	net_stats->rx_fifo_errors   = hw_stats->rx_ov_rxf;
1677f1b6b106SSabrina Dubroca 	net_stats->rx_length_errors = hw_stats->rx_len_err;
1678f1b6b106SSabrina Dubroca 	net_stats->rx_crc_errors    = hw_stats->rx_fcs_err;
1679f1b6b106SSabrina Dubroca 	net_stats->rx_frame_errors  = hw_stats->rx_align_err;
1680f1b6b106SSabrina Dubroca 	net_stats->rx_dropped       = hw_stats->rx_ov_rrd;
1681f1b6b106SSabrina Dubroca 
1682f1b6b106SSabrina Dubroca 	net_stats->tx_errors = hw_stats->tx_late_col +
1683f1b6b106SSabrina Dubroca 			       hw_stats->tx_abort_col +
1684f1b6b106SSabrina Dubroca 			       hw_stats->tx_underrun +
1685f1b6b106SSabrina Dubroca 			       hw_stats->tx_trunc;
1686f1b6b106SSabrina Dubroca 
1687f1b6b106SSabrina Dubroca 	net_stats->tx_aborted_errors = hw_stats->tx_abort_col;
1688f1b6b106SSabrina Dubroca 	net_stats->tx_fifo_errors    = hw_stats->tx_underrun;
1689f1b6b106SSabrina Dubroca 	net_stats->tx_window_errors  = hw_stats->tx_late_col;
1690f1b6b106SSabrina Dubroca 
1691f1b6b106SSabrina Dubroca 	net_stats->tx_packets = hw_stats->tx_ok + net_stats->tx_errors;
1692f1b6b106SSabrina Dubroca 	net_stats->rx_packets = hw_stats->rx_ok + net_stats->rx_errors;
1693f1b6b106SSabrina Dubroca 
1694f1b6b106SSabrina Dubroca 	spin_unlock(&alx->stats_lock);
1695f1b6b106SSabrina Dubroca }
1696f1b6b106SSabrina Dubroca 
1697ab69bde6SJohannes Berg static const struct net_device_ops alx_netdev_ops = {
1698ab69bde6SJohannes Berg 	.ndo_open               = alx_open,
1699ab69bde6SJohannes Berg 	.ndo_stop               = alx_stop,
1700ab69bde6SJohannes Berg 	.ndo_start_xmit         = alx_start_xmit,
1701f1b6b106SSabrina Dubroca 	.ndo_get_stats64        = alx_get_stats64,
1702ab69bde6SJohannes Berg 	.ndo_set_rx_mode        = alx_set_rx_mode,
1703ab69bde6SJohannes Berg 	.ndo_validate_addr      = eth_validate_addr,
1704ab69bde6SJohannes Berg 	.ndo_set_mac_address    = alx_set_mac_address,
1705ab69bde6SJohannes Berg 	.ndo_change_mtu         = alx_change_mtu,
1706a7605370SArnd Bergmann 	.ndo_eth_ioctl           = alx_ioctl,
1707ab69bde6SJohannes Berg 	.ndo_tx_timeout         = alx_tx_timeout,
1708ab69bde6SJohannes Berg 	.ndo_fix_features	= alx_fix_features,
1709ab69bde6SJohannes Berg #ifdef CONFIG_NET_POLL_CONTROLLER
1710ab69bde6SJohannes Berg 	.ndo_poll_controller    = alx_poll_controller,
1711ab69bde6SJohannes Berg #endif
1712ab69bde6SJohannes Berg };
1713ab69bde6SJohannes Berg 
alx_probe(struct pci_dev * pdev,const struct pci_device_id * ent)1714ab69bde6SJohannes Berg static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
1715ab69bde6SJohannes Berg {
1716ab69bde6SJohannes Berg 	struct net_device *netdev;
1717ab69bde6SJohannes Berg 	struct alx_priv *alx;
1718ab69bde6SJohannes Berg 	struct alx_hw *hw;
1719ab69bde6SJohannes Berg 	bool phy_configured;
1720caa8e932SJohannes Thumshirn 	int err;
1721ab69bde6SJohannes Berg 
1722ab69bde6SJohannes Berg 	err = pci_enable_device_mem(pdev);
1723ab69bde6SJohannes Berg 	if (err)
1724ab69bde6SJohannes Berg 		return err;
1725ab69bde6SJohannes Berg 
1726ab69bde6SJohannes Berg 	/* The alx chip can DMA to 64-bit addresses, but it uses a single
1727ab69bde6SJohannes Berg 	 * shared register for the high 32 bits, so only a single, aligned,
1728ab69bde6SJohannes Berg 	 * 4 GB physical address range can be used for descriptors.
1729ab69bde6SJohannes Berg 	 */
17308d7f1fbfSPeter Senna Tschudin 	if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) {
1731ab69bde6SJohannes Berg 		dev_dbg(&pdev->dev, "DMA to 64-BIT addresses\n");
1732ab69bde6SJohannes Berg 	} else {
17338d7f1fbfSPeter Senna Tschudin 		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
1734ab69bde6SJohannes Berg 		if (err) {
17358d7f1fbfSPeter Senna Tschudin 			dev_err(&pdev->dev, "No usable DMA config, aborting\n");
1736ab69bde6SJohannes Berg 			goto out_pci_disable;
1737ab69bde6SJohannes Berg 		}
1738ab69bde6SJohannes Berg 	}
1739ab69bde6SJohannes Berg 
1740caa8e932SJohannes Thumshirn 	err = pci_request_mem_regions(pdev, alx_drv_name);
1741ab69bde6SJohannes Berg 	if (err) {
1742ab69bde6SJohannes Berg 		dev_err(&pdev->dev,
1743caa8e932SJohannes Thumshirn 			"pci_request_mem_regions failed\n");
1744ab69bde6SJohannes Berg 		goto out_pci_disable;
1745ab69bde6SJohannes Berg 	}
1746ab69bde6SJohannes Berg 
1747ab69bde6SJohannes Berg 	pci_set_master(pdev);
1748ab69bde6SJohannes Berg 
1749c3eb7a77SYijing Wang 	if (!pdev->pm_cap) {
1750ab69bde6SJohannes Berg 		dev_err(&pdev->dev,
1751ab69bde6SJohannes Berg 			"Can't find power management capability, aborting\n");
1752ab69bde6SJohannes Berg 		err = -EIO;
1753ab69bde6SJohannes Berg 		goto out_pci_release;
1754ab69bde6SJohannes Berg 	}
1755ab69bde6SJohannes Berg 
1756d768319cSTobias Regnery 	netdev = alloc_etherdev_mqs(sizeof(*alx),
1757d768319cSTobias Regnery 				    ALX_MAX_TX_QUEUES, 1);
1758ab69bde6SJohannes Berg 	if (!netdev) {
1759ab69bde6SJohannes Berg 		err = -ENOMEM;
1760ab69bde6SJohannes Berg 		goto out_pci_release;
1761ab69bde6SJohannes Berg 	}
1762ab69bde6SJohannes Berg 
1763ab69bde6SJohannes Berg 	SET_NETDEV_DEV(netdev, &pdev->dev);
1764ab69bde6SJohannes Berg 	alx = netdev_priv(netdev);
1765a8798a5cSMaarten Lankhorst 	spin_lock_init(&alx->hw.mdio_lock);
1766a8798a5cSMaarten Lankhorst 	spin_lock_init(&alx->irq_lock);
17673e5ccc29SJohn Greene 	spin_lock_init(&alx->stats_lock);
1768ab69bde6SJohannes Berg 	alx->dev = netdev;
1769ab69bde6SJohannes Berg 	alx->hw.pdev = pdev;
1770ab69bde6SJohannes Berg 	alx->msg_enable = NETIF_MSG_LINK | NETIF_MSG_HW | NETIF_MSG_IFUP |
1771ab69bde6SJohannes Berg 			  NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR | NETIF_MSG_WOL;
1772ab69bde6SJohannes Berg 	hw = &alx->hw;
1773ab69bde6SJohannes Berg 	pci_set_drvdata(pdev, alx);
1774ab69bde6SJohannes Berg 
1775ab69bde6SJohannes Berg 	hw->hw_addr = pci_ioremap_bar(pdev, 0);
1776ab69bde6SJohannes Berg 	if (!hw->hw_addr) {
1777ab69bde6SJohannes Berg 		dev_err(&pdev->dev, "cannot map device registers\n");
1778ab69bde6SJohannes Berg 		err = -EIO;
1779ab69bde6SJohannes Berg 		goto out_free_netdev;
1780ab69bde6SJohannes Berg 	}
1781ab69bde6SJohannes Berg 
1782ab69bde6SJohannes Berg 	netdev->netdev_ops = &alx_netdev_ops;
17837ad24ea4SWilfried Klaebe 	netdev->ethtool_ops = &alx_ethtool_ops;
1784f3297f68SChristoph Hellwig 	netdev->irq = pci_irq_vector(pdev, 0);
1785ab69bde6SJohannes Berg 	netdev->watchdog_timeo = ALX_WATCHDOG_TIME;
1786ab69bde6SJohannes Berg 
1787ab69bde6SJohannes Berg 	if (ent->driver_data & ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG)
1788ab69bde6SJohannes Berg 		pdev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG;
1789ab69bde6SJohannes Berg 
1790ab69bde6SJohannes Berg 	err = alx_init_sw(alx);
1791ab69bde6SJohannes Berg 	if (err) {
1792ab69bde6SJohannes Berg 		dev_err(&pdev->dev, "net device private data init failed\n");
1793ab69bde6SJohannes Berg 		goto out_unmap;
1794ab69bde6SJohannes Berg 	}
1795ab69bde6SJohannes Berg 
17964a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
17974a5fe57eSJohannes Berg 
1798ab69bde6SJohannes Berg 	alx_reset_pcie(hw);
1799ab69bde6SJohannes Berg 
1800ab69bde6SJohannes Berg 	phy_configured = alx_phy_configured(hw);
1801ab69bde6SJohannes Berg 
1802ab69bde6SJohannes Berg 	if (!phy_configured)
1803ab69bde6SJohannes Berg 		alx_reset_phy(hw);
1804ab69bde6SJohannes Berg 
1805ab69bde6SJohannes Berg 	err = alx_reset_mac(hw);
1806ab69bde6SJohannes Berg 	if (err) {
1807ab69bde6SJohannes Berg 		dev_err(&pdev->dev, "MAC Reset failed, error = %d\n", err);
18084a5fe57eSJohannes Berg 		goto out_unlock;
1809ab69bde6SJohannes Berg 	}
1810ab69bde6SJohannes Berg 
1811ab69bde6SJohannes Berg 	/* setup link to put it in a known good starting state */
1812ab69bde6SJohannes Berg 	if (!phy_configured) {
1813ab69bde6SJohannes Berg 		err = alx_setup_speed_duplex(hw, hw->adv_cfg, hw->flowctrl);
1814ab69bde6SJohannes Berg 		if (err) {
1815ab69bde6SJohannes Berg 			dev_err(&pdev->dev,
1816ab69bde6SJohannes Berg 				"failed to configure PHY speed/duplex (err=%d)\n",
1817ab69bde6SJohannes Berg 				err);
18184a5fe57eSJohannes Berg 			goto out_unlock;
1819ab69bde6SJohannes Berg 		}
1820ab69bde6SJohannes Berg 	}
1821ab69bde6SJohannes Berg 
1822ab725983STobias Regnery 	netdev->hw_features = NETIF_F_SG |
1823ab725983STobias Regnery 			      NETIF_F_HW_CSUM |
18243a7f75e5STobias Regnery 			      NETIF_F_RXCSUM |
1825ab725983STobias Regnery 			      NETIF_F_TSO |
1826ab725983STobias Regnery 			      NETIF_F_TSO6;
1827ab69bde6SJohannes Berg 
1828ab69bde6SJohannes Berg 	if (alx_get_perm_macaddr(hw, hw->perm_addr)) {
1829ab69bde6SJohannes Berg 		dev_warn(&pdev->dev,
1830ab69bde6SJohannes Berg 			 "Invalid permanent address programmed, using random one\n");
1831ab69bde6SJohannes Berg 		eth_hw_addr_random(netdev);
1832ab69bde6SJohannes Berg 		memcpy(hw->perm_addr, netdev->dev_addr, netdev->addr_len);
1833ab69bde6SJohannes Berg 	}
1834ab69bde6SJohannes Berg 
1835ab69bde6SJohannes Berg 	memcpy(hw->mac_addr, hw->perm_addr, ETH_ALEN);
1836a96d317fSJakub Kicinski 	eth_hw_addr_set(netdev, hw->mac_addr);
1837ab69bde6SJohannes Berg 	memcpy(netdev->perm_addr, hw->perm_addr, ETH_ALEN);
1838ab69bde6SJohannes Berg 
1839ab69bde6SJohannes Berg 	hw->mdio.prtad = 0;
1840ab69bde6SJohannes Berg 	hw->mdio.mmds = 0;
1841ab69bde6SJohannes Berg 	hw->mdio.dev = netdev;
1842ab69bde6SJohannes Berg 	hw->mdio.mode_support = MDIO_SUPPORTS_C45 |
1843ab69bde6SJohannes Berg 				MDIO_SUPPORTS_C22 |
1844ab69bde6SJohannes Berg 				MDIO_EMULATE_C22;
1845ab69bde6SJohannes Berg 	hw->mdio.mdio_read = alx_mdio_read;
1846ab69bde6SJohannes Berg 	hw->mdio.mdio_write = alx_mdio_write;
1847ab69bde6SJohannes Berg 
1848ab69bde6SJohannes Berg 	if (!alx_get_phy_info(hw)) {
1849ab69bde6SJohannes Berg 		dev_err(&pdev->dev, "failed to identify PHY\n");
1850ab69bde6SJohannes Berg 		err = -EIO;
18514a5fe57eSJohannes Berg 		goto out_unlock;
1852ab69bde6SJohannes Berg 	}
1853ab69bde6SJohannes Berg 
18544a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
18554a5fe57eSJohannes Berg 
1856ab69bde6SJohannes Berg 	INIT_WORK(&alx->link_check_wk, alx_link_check);
1857ab69bde6SJohannes Berg 	INIT_WORK(&alx->reset_wk, alx_reset);
1858ab69bde6SJohannes Berg 	netif_carrier_off(netdev);
1859ab69bde6SJohannes Berg 
1860ab69bde6SJohannes Berg 	err = register_netdev(netdev);
1861ab69bde6SJohannes Berg 	if (err) {
1862ab69bde6SJohannes Berg 		dev_err(&pdev->dev, "register netdevice failed\n");
18639d8a29aeSDan Carpenter 		goto out_unmap;
1864ab69bde6SJohannes Berg 	}
1865ab69bde6SJohannes Berg 
1866ab69bde6SJohannes Berg 	netdev_info(netdev,
1867ab69bde6SJohannes Berg 		    "Qualcomm Atheros AR816x/AR817x Ethernet [%pM]\n",
1868ab69bde6SJohannes Berg 		    netdev->dev_addr);
1869ab69bde6SJohannes Berg 
1870ab69bde6SJohannes Berg 	return 0;
1871ab69bde6SJohannes Berg 
18724a5fe57eSJohannes Berg out_unlock:
18734a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
1874ab69bde6SJohannes Berg out_unmap:
1875ab69bde6SJohannes Berg 	iounmap(hw->hw_addr);
1876ab69bde6SJohannes Berg out_free_netdev:
1877ab69bde6SJohannes Berg 	free_netdev(netdev);
1878ab69bde6SJohannes Berg out_pci_release:
1879caa8e932SJohannes Thumshirn 	pci_release_mem_regions(pdev);
1880ab69bde6SJohannes Berg out_pci_disable:
1881ab69bde6SJohannes Berg 	pci_disable_device(pdev);
1882ab69bde6SJohannes Berg 	return err;
1883ab69bde6SJohannes Berg }
1884ab69bde6SJohannes Berg 
alx_remove(struct pci_dev * pdev)1885ab69bde6SJohannes Berg static void alx_remove(struct pci_dev *pdev)
1886ab69bde6SJohannes Berg {
1887ab69bde6SJohannes Berg 	struct alx_priv *alx = pci_get_drvdata(pdev);
1888ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
1889ab69bde6SJohannes Berg 
1890ab69bde6SJohannes Berg 	/* restore permanent mac address */
1891ab69bde6SJohannes Berg 	alx_set_macaddr(hw, hw->perm_addr);
1892ab69bde6SJohannes Berg 
1893ab69bde6SJohannes Berg 	unregister_netdev(alx->dev);
1894ab69bde6SJohannes Berg 	iounmap(hw->hw_addr);
1895caa8e932SJohannes Thumshirn 	pci_release_mem_regions(pdev);
1896ab69bde6SJohannes Berg 
1897ab69bde6SJohannes Berg 	pci_disable_device(pdev);
1898ab69bde6SJohannes Berg 
18994a5fe57eSJohannes Berg 	mutex_destroy(&alx->mtx);
19004a5fe57eSJohannes Berg 
1901ab69bde6SJohannes Berg 	free_netdev(alx->dev);
1902ab69bde6SJohannes Berg }
1903ab69bde6SJohannes Berg 
alx_suspend(struct device * dev)1904ab69bde6SJohannes Berg static int alx_suspend(struct device *dev)
1905ab69bde6SJohannes Berg {
1906f54b0fc8SChuhong Yuan 	struct alx_priv *alx = dev_get_drvdata(dev);
1907ab69bde6SJohannes Berg 
1908bc2bebe8SJohannes Berg 	if (!netif_running(alx->dev))
1909bc2bebe8SJohannes Berg 		return 0;
19106ad1c94eSJakub Kicinski 
19116ad1c94eSJakub Kicinski 	rtnl_lock();
1912bc2bebe8SJohannes Berg 	netif_device_detach(alx->dev);
19134a5fe57eSJohannes Berg 
19144a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
1915bc2bebe8SJohannes Berg 	__alx_stop(alx);
19164a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
19176ad1c94eSJakub Kicinski 	rtnl_unlock();
19184a5fe57eSJohannes Berg 
1919ab69bde6SJohannes Berg 	return 0;
1920ab69bde6SJohannes Berg }
1921ab69bde6SJohannes Berg 
alx_resume(struct device * dev)1922ab69bde6SJohannes Berg static int alx_resume(struct device *dev)
1923ab69bde6SJohannes Berg {
1924f54b0fc8SChuhong Yuan 	struct alx_priv *alx = dev_get_drvdata(dev);
1925b54629e2Shahnjo 	struct alx_hw *hw = &alx->hw;
1926bc800e8bSSabrina Dubroca 	int err;
1927b54629e2Shahnjo 
19286ad1c94eSJakub Kicinski 	rtnl_lock();
19294a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
1930b54629e2Shahnjo 	alx_reset_phy(hw);
1931ab69bde6SJohannes Berg 
19324a5fe57eSJohannes Berg 	if (!netif_running(alx->dev)) {
19334a5fe57eSJohannes Berg 		err = 0;
19344a5fe57eSJohannes Berg 		goto unlock;
19354a5fe57eSJohannes Berg 	}
1936bc800e8bSSabrina Dubroca 
1937bc800e8bSSabrina Dubroca 	err = __alx_open(alx, true);
1938a4dcfbc4SJakub Kicinski 	if (err)
19394a5fe57eSJohannes Berg 		goto unlock;
1940a4dcfbc4SJakub Kicinski 
1941a4dcfbc4SJakub Kicinski 	netif_device_attach(alx->dev);
1942a4dcfbc4SJakub Kicinski 
19434a5fe57eSJohannes Berg unlock:
19444a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
19456ad1c94eSJakub Kicinski 	rtnl_unlock();
19464a5fe57eSJohannes Berg 	return err;
1947ab69bde6SJohannes Berg }
1948ab69bde6SJohannes Berg 
1949*1d330d4fSChristoph Heiss static DEFINE_SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume);
1950bc2bebe8SJohannes Berg 
alx_pci_error_detected(struct pci_dev * pdev,pci_channel_state_t state)1951ab69bde6SJohannes Berg static pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev,
1952ab69bde6SJohannes Berg 					       pci_channel_state_t state)
1953ab69bde6SJohannes Berg {
1954ab69bde6SJohannes Berg 	struct alx_priv *alx = pci_get_drvdata(pdev);
1955ab69bde6SJohannes Berg 	struct net_device *netdev = alx->dev;
1956ab69bde6SJohannes Berg 	pci_ers_result_t rc = PCI_ERS_RESULT_NEED_RESET;
1957ab69bde6SJohannes Berg 
1958ab69bde6SJohannes Berg 	dev_info(&pdev->dev, "pci error detected\n");
1959ab69bde6SJohannes Berg 
19604a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
1961ab69bde6SJohannes Berg 
1962ab69bde6SJohannes Berg 	if (netif_running(netdev)) {
1963ab69bde6SJohannes Berg 		netif_device_detach(netdev);
1964ab69bde6SJohannes Berg 		alx_halt(alx);
1965ab69bde6SJohannes Berg 	}
1966ab69bde6SJohannes Berg 
1967ab69bde6SJohannes Berg 	if (state == pci_channel_io_perm_failure)
1968ab69bde6SJohannes Berg 		rc = PCI_ERS_RESULT_DISCONNECT;
1969ab69bde6SJohannes Berg 	else
1970ab69bde6SJohannes Berg 		pci_disable_device(pdev);
1971ab69bde6SJohannes Berg 
19724a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
1973ab69bde6SJohannes Berg 
1974ab69bde6SJohannes Berg 	return rc;
1975ab69bde6SJohannes Berg }
1976ab69bde6SJohannes Berg 
alx_pci_error_slot_reset(struct pci_dev * pdev)1977ab69bde6SJohannes Berg static pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev)
1978ab69bde6SJohannes Berg {
1979ab69bde6SJohannes Berg 	struct alx_priv *alx = pci_get_drvdata(pdev);
1980ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
1981ab69bde6SJohannes Berg 	pci_ers_result_t rc = PCI_ERS_RESULT_DISCONNECT;
1982ab69bde6SJohannes Berg 
1983ab69bde6SJohannes Berg 	dev_info(&pdev->dev, "pci error slot reset\n");
1984ab69bde6SJohannes Berg 
19854a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
1986ab69bde6SJohannes Berg 
1987ab69bde6SJohannes Berg 	if (pci_enable_device(pdev)) {
1988ab69bde6SJohannes Berg 		dev_err(&pdev->dev, "Failed to re-enable PCI device after reset\n");
1989ab69bde6SJohannes Berg 		goto out;
1990ab69bde6SJohannes Berg 	}
1991ab69bde6SJohannes Berg 
1992ab69bde6SJohannes Berg 	pci_set_master(pdev);
1993ab69bde6SJohannes Berg 
1994ab69bde6SJohannes Berg 	alx_reset_pcie(hw);
1995ab69bde6SJohannes Berg 	if (!alx_reset_mac(hw))
1996ab69bde6SJohannes Berg 		rc = PCI_ERS_RESULT_RECOVERED;
1997ab69bde6SJohannes Berg out:
19984a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
1999ab69bde6SJohannes Berg 
2000ab69bde6SJohannes Berg 	return rc;
2001ab69bde6SJohannes Berg }
2002ab69bde6SJohannes Berg 
alx_pci_error_resume(struct pci_dev * pdev)2003ab69bde6SJohannes Berg static void alx_pci_error_resume(struct pci_dev *pdev)
2004ab69bde6SJohannes Berg {
2005ab69bde6SJohannes Berg 	struct alx_priv *alx = pci_get_drvdata(pdev);
2006ab69bde6SJohannes Berg 	struct net_device *netdev = alx->dev;
2007ab69bde6SJohannes Berg 
2008ab69bde6SJohannes Berg 	dev_info(&pdev->dev, "pci error resume\n");
2009ab69bde6SJohannes Berg 
20104a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
2011ab69bde6SJohannes Berg 
2012ab69bde6SJohannes Berg 	if (netif_running(netdev)) {
2013ab69bde6SJohannes Berg 		alx_activate(alx);
2014ab69bde6SJohannes Berg 		netif_device_attach(netdev);
2015ab69bde6SJohannes Berg 	}
2016ab69bde6SJohannes Berg 
20174a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
2018ab69bde6SJohannes Berg }
2019ab69bde6SJohannes Berg 
2020ab69bde6SJohannes Berg static const struct pci_error_handlers alx_err_handlers = {
2021ab69bde6SJohannes Berg 	.error_detected = alx_pci_error_detected,
2022ab69bde6SJohannes Berg 	.slot_reset     = alx_pci_error_slot_reset,
2023ab69bde6SJohannes Berg 	.resume         = alx_pci_error_resume,
2024ab69bde6SJohannes Berg };
2025ab69bde6SJohannes Berg 
20269baa3c34SBenoit Taine static const struct pci_device_id alx_pci_tbl[] = {
2027ab69bde6SJohannes Berg 	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8161),
2028ab69bde6SJohannes Berg 	  .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
2029ab69bde6SJohannes Berg 	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2200),
2030ab69bde6SJohannes Berg 	  .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
20310208e951SBen Pope 	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2400),
20320208e951SBen Pope 	  .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
2033b99b43bbSOwen Lin 	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2500),
2034b99b43bbSOwen Lin 	  .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
2035ab69bde6SJohannes Berg 	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8162),
2036ab69bde6SJohannes Berg 	  .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
2037ab69bde6SJohannes Berg 	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8171) },
2038ab69bde6SJohannes Berg 	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8172) },
2039ab69bde6SJohannes Berg 	{}
2040ab69bde6SJohannes Berg };
2041ab69bde6SJohannes Berg 
2042ab69bde6SJohannes Berg static struct pci_driver alx_driver = {
2043ab69bde6SJohannes Berg 	.name        = alx_drv_name,
2044ab69bde6SJohannes Berg 	.id_table    = alx_pci_tbl,
2045ab69bde6SJohannes Berg 	.probe       = alx_probe,
2046ab69bde6SJohannes Berg 	.remove      = alx_remove,
2047ab69bde6SJohannes Berg 	.err_handler = &alx_err_handlers,
2048*1d330d4fSChristoph Heiss 	.driver.pm   = pm_sleep_ptr(&alx_pm_ops),
2049ab69bde6SJohannes Berg };
2050ab69bde6SJohannes Berg 
2051ab69bde6SJohannes Berg module_pci_driver(alx_driver);
2052ab69bde6SJohannes Berg MODULE_DEVICE_TABLE(pci, alx_pci_tbl);
2053ab69bde6SJohannes Berg MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
2054a57d3d48SJohannes Berg MODULE_AUTHOR("Qualcomm Corporation");
2055ab69bde6SJohannes Berg MODULE_DESCRIPTION(
2056ab69bde6SJohannes Berg 	"Qualcomm Atheros(R) AR816x/AR817x PCI-E Ethernet Network Driver");
2057ab69bde6SJohannes Berg MODULE_LICENSE("GPL");
2058