main.c revision 3e5ccc29f71b5dfdfb81dac8c19372af83923b7f
17f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan/*
2e7eacd36865ae0707f5efae8e4dda421ffcd1b66Ralph Campbell * Copyright (c) 2013 Johannes Berg <johannes@sipsolutions.net>
37f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *
47f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  This file is free software: you may copy, redistribute and/or modify it
57f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  under the terms of the GNU General Public License as published by the
67f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  Free Software Foundation, either version 2 of the License, or (at your
77f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  option) any later version.
87f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *
97f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  This file is distributed in the hope that it will be useful, but
107f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  WITHOUT ANY WARRANTY; without even the implied warranty of
117f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
127f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  General Public License for more details.
137f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *
147f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  You should have received a copy of the GNU General Public License
157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
167f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *
177f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * This file incorporates work covered by the following copyright and
187f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * permission notice:
197f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *
207f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * Copyright (c) 2012 Qualcomm Atheros, Inc.
217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *
227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * Permission to use, copy, modify, and/or distribute this software for any
237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * purpose with or without fee is hereby granted, provided that the above
247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * copyright notice and this permission notice appear in all copies.
257f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan *
267f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
277f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
287f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
297f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
307f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
317f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan */
347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include <linux/module.h>
367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include <linux/pci.h>
377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include <linux/interrupt.h>
387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include <linux/ip.h>
39124b4dcb1dd3a6fb80051f1785117a732d785f70Dave Olson#include <linux/ipv6.h>
40124b4dcb1dd3a6fb80051f1785117a732d785f70Dave Olson#include <linux/if_vlan.h>
41124b4dcb1dd3a6fb80051f1785117a732d785f70Dave Olson#include <linux/mdio.h>
42f2b9857eee17797541b845782ade4d7a9d50f843Jonathan Corbet#include <linux/aer.h>
437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include <linux/bitops.h>
447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include <linux/netdevice.h>
457f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include <linux/etherdevice.h>
4627b678dd04a636f2c351816f4b3042c8815d4e9dBryan O'Sullivan#include <net/ip6_checksum.h>
47124b4dcb1dd3a6fb80051f1785117a732d785f70Dave Olson#include <linux/crc32.h>
487f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include "alx.h"
497f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include "hw.h"
507f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#include "reg.h"
517f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanconst char alx_drv_name[] = "alx";
53124b4dcb1dd3a6fb80051f1785117a732d785f70Dave Olson
54124b4dcb1dd3a6fb80051f1785117a732d785f70Dave Olson
557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_free_txbuf(struct alx_priv *alx, int entry)
567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_buffer *txb = &alx->txq.bufs[entry];
582b8693c0617e972fc0b2fd1ebf8de97e15b656c3Arjan van de Ven
597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (dma_unmap_len(txb, size)) {
607f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		dma_unmap_single(&alx->hw.pdev->dev,
61124b4dcb1dd3a6fb80051f1785117a732d785f70Dave Olson				 dma_unmap_addr(txb, dma),
627f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				 dma_unmap_len(txb, size),
637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				 DMA_TO_DEVICE);
647f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		dma_unmap_len_set(txb, size, 0);
657f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
667f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (txb->skb) {
680a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell		dev_kfree_skb_any(txb->skb);
690a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell		txb->skb = NULL;
700a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	}
710a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell}
720a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell
730a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbellstatic int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp)
740a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell{
750a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	struct alx_rx_queue *rxq = &alx->rxq;
760a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	struct sk_buff *skb;
770a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	struct alx_buffer *cur_buf;
780a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	dma_addr_t dma;
790a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	u16 cur, next, count = 0;
800a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell
810a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	next = cur = rxq->write_idx;
820a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	if (++next == alx->rx_ringsz)
830a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell		next = 0;
840a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	cur_buf = &rxq->bufs[cur];
850a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell
869929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	while (!cur_buf->skb && next != rxq->read_idx) {
877f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		struct alx_rfd *rfd = &rxq->rfd[cur];
887f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
899929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		skb = __netdev_alloc_skb(alx->dev, alx->rxbuf_size, gfp);
907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		if (!skb)
917f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			break;
927f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		dma = dma_map_single(&alx->hw.pdev->dev,
939929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan				     skb->data, alx->rxbuf_size,
949929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan				     DMA_FROM_DEVICE);
959929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		if (dma_mapping_error(&alx->hw.pdev->dev, dma)) {
969929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			dev_kfree_skb(skb);
979929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			break;
989929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		}
999929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1009929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		/* Unfortunately, RX descriptor buffers must be 4-byte
1019929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		 * aligned, so we can't use IP alignment.
1029929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		 */
1039929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		if (WARN_ON(dma & 3)) {
1049929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			dev_kfree_skb(skb);
1059929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			break;
1067f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		}
1079929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1089929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		cur_buf->skb = skb;
1099929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		dma_unmap_len_set(cur_buf, size, alx->rxbuf_size);
110c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		dma_unmap_addr_set(cur_buf, dma, dma);
1119929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		rfd->addr = cpu_to_le64(dma);
1127f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1139929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		cur = next;
1149929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		if (++next == alx->rx_ringsz)
1157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			next = 0;
1167f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		cur_buf = &rxq->bufs[cur];
1177f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		count++;
1187f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
1197f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1207f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (count) {
1217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		/* flush all updates before updating hardware */
1227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		wmb();
1237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		rxq->write_idx = cur;
1247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_write_mem16(&alx->hw, ALX_RFD_PIDX, cur);
1257f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
1267f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1277f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return count;
1287f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
1297f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1307f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic inline int alx_tpd_avail(struct alx_priv *alx)
1317f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
1327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_tx_queue *txq = &alx->txq;
1337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (txq->write_idx >= txq->read_idx)
1357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return alx->tx_ringsz + txq->read_idx - txq->write_idx - 1;
1367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return txq->read_idx - txq->write_idx - 1;
1377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
1387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic bool alx_clean_tx_irq(struct alx_priv *alx)
1407f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
1419929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct alx_tx_queue *txq = &alx->txq;
1429929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	u16 hw_read_idx, sw_read_idx;
1439929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	unsigned int total_bytes = 0, total_packets = 0;
1447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	int budget = ALX_DEFAULT_TX_WORK;
1457f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1467f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	sw_read_idx = txq->read_idx;
1477f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw_read_idx = alx_read_mem16(&alx->hw, ALX_TPD_PRI0_CIDX);
1487f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1497f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (sw_read_idx != hw_read_idx) {
1507f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		while (sw_read_idx != hw_read_idx && budget > 0) {
1517f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			struct sk_buff *skb;
1527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1537f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			skb = txq->bufs[sw_read_idx].skb;
1547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			if (skb) {
1557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				total_bytes += skb->len;
1567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				total_packets++;
1577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				budget--;
1587f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			}
1597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1607f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			alx_free_txbuf(alx, sw_read_idx);
1617f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1627f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			if (++sw_read_idx == alx->tx_ringsz)
1637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				sw_read_idx = 0;
1647f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		}
1657f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		txq->read_idx = sw_read_idx;
1669929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1679929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		netdev_completed_queue(alx->dev, total_packets, total_bytes);
1687f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
1697f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1709929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (netif_queue_stopped(alx->dev) && netif_carrier_ok(alx->dev) &&
1717f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	    alx_tpd_avail(alx) > alx->tx_ringsz/4)
1727f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		netif_wake_queue(alx->dev);
1737f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1747f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return sw_read_idx == hw_read_idx;
1757f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
1769929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
177e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olsonstatic void alx_schedule_link_check(struct alx_priv *alx)
1789929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan{
1799929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	schedule_work(&alx->link_check_wk);
180a18e26ae442001de62f6b84a923e8613347dc35fRalph Campbell}
1819929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
182e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olsonstatic void alx_schedule_reset(struct alx_priv *alx)
183e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olson{
1849929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	schedule_work(&alx->reset_wk);
1859929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan}
1869929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
187e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olsonstatic bool alx_clean_rx_irq(struct alx_priv *alx, int budget)
1889929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan{
1899929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct alx_rx_queue *rxq = &alx->rxq;
1907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_rrd *rrd;
191e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olson	struct alx_buffer *rxb;
1929929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct sk_buff *skb;
1939929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	u16 length, rfd_cleaned = 0;
194c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
1951d7c2e529fb6d4143d294ade7d99e29cb6b3775fDave Olson	while (budget > 0) {
196c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		rrd = &rxq->rrd[rxq->rrd_read_idx];
197c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		if (!(rrd->word3 & cpu_to_le32(1 << RRD_UPDATED_SHIFT)))
198a18e26ae442001de62f6b84a923e8613347dc35fRalph Campbell			break;
199c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		rrd->word3 &= ~cpu_to_le32(1 << RRD_UPDATED_SHIFT);
200c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
201c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		if (ALX_GET_FIELD(le32_to_cpu(rrd->word0),
202c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				  RRD_SI) != rxq->read_idx ||
2030a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell		    ALX_GET_FIELD(le32_to_cpu(rrd->word0),
204c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				  RRD_NOR) != 1) {
2059929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			alx_schedule_reset(alx);
2060a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell			return 0;
207c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		}
208947d7617a1d876c2c93f73017a734e070c64d43bRalph Campbell
2090a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell		rxb = &rxq->bufs[rxq->read_idx];
210c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		dma_unmap_single(&alx->hw.pdev->dev,
211c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				 dma_unmap_addr(rxb, dma),
212c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				 dma_unmap_len(rxb, size),
213c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				 DMA_FROM_DEVICE);
214c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		dma_unmap_len_set(rxb, size, 0);
215c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		skb = rxb->skb;
216c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		rxb->skb = NULL;
217c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
218c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		if (rrd->word3 & cpu_to_le32(1 << RRD_ERR_RES_SHIFT) ||
219c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		    rrd->word3 & cpu_to_le32(1 << RRD_ERR_LEN_SHIFT)) {
220c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage			rrd->word3 = 0;
221c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage			dev_kfree_skb_any(skb);
222c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage			goto next_pkt;
223c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		}
2249929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
2259929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		length = ALX_GET_FIELD(le32_to_cpu(rrd->word3),
2261bf7724e093cf3071d943d53bfa4de8b8e50426bDave Olson				       RRD_PKTLEN) - ETH_FCS_LEN;
2271bf7724e093cf3071d943d53bfa4de8b8e50426bDave Olson		skb_put(skb, length);
2281bf7724e093cf3071d943d53bfa4de8b8e50426bDave Olson		skb->protocol = eth_type_trans(skb, alx->dev);
2291bf7724e093cf3071d943d53bfa4de8b8e50426bDave Olson
2301bf7724e093cf3071d943d53bfa4de8b8e50426bDave Olson		skb_checksum_none_assert(skb);
2311bf7724e093cf3071d943d53bfa4de8b8e50426bDave Olson		if (alx->dev->features & NETIF_F_RXCSUM &&
2321bf7724e093cf3071d943d53bfa4de8b8e50426bDave Olson		    !(rrd->word3 & (cpu_to_le32(1 << RRD_ERR_L4_SHIFT) |
2337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				    cpu_to_le32(1 << RRD_ERR_IPV4_SHIFT)))) {
2347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			switch (ALX_GET_FIELD(le32_to_cpu(rrd->word2),
2357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan					      RRD_PID)) {
236826d801009fb3c82832f2d92149446cce354bf61Dave Olson			case RRD_PID_IPV6UDP:
237826d801009fb3c82832f2d92149446cce354bf61Dave Olson			case RRD_PID_IPV4UDP:
238826d801009fb3c82832f2d92149446cce354bf61Dave Olson			case RRD_PID_IPV4TCP:
239826d801009fb3c82832f2d92149446cce354bf61Dave Olson			case RRD_PID_IPV6TCP:
240826d801009fb3c82832f2d92149446cce354bf61Dave Olson				skb->ip_summed = CHECKSUM_UNNECESSARY;
241826d801009fb3c82832f2d92149446cce354bf61Dave Olson				break;
2427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			}
2437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		}
2449929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
245eaf6733bc176742fb08def2269441684e963c275Bryan O'Sullivan		napi_gro_receive(&alx->napi, skb);
2467f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		budget--;
2477f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2489929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivannext_pkt:
2499929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		if (++rxq->read_idx == alx->rx_ringsz)
2509929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			rxq->read_idx = 0;
2519929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		if (++rxq->rrd_read_idx == alx->rx_ringsz)
252c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage			rxq->rrd_read_idx = 0;
253c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
2547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		if (++rfd_cleaned > ALX_RX_ALLOC_THRESH)
2557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			rfd_cleaned -= alx_refill_rx_ring(alx, GFP_ATOMIC);
2567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
2577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2587f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (rfd_cleaned)
2597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_refill_rx_ring(alx, GFP_ATOMIC);
2607f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2617f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return budget > 0;
2627f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
2637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2649929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic int alx_poll(struct napi_struct *napi, int budget)
2657f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
2667f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_priv *alx = container_of(napi, struct alx_priv, napi);
2677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
2687f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	bool complete = true;
2697f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	unsigned long flags;
2707f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2717f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	complete = alx_clean_tx_irq(alx) &&
2727f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		   alx_clean_rx_irq(alx, budget);
2737f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2747f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!complete)
2757f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return 1;
2767f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2777f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	napi_complete(&alx->napi);
2787f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2797f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* enable interrupt */
2807f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	spin_lock_irqsave(&alx->irq_lock, flags);
2817f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0;
2827f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_IMR, alx->int_mask);
2837f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	spin_unlock_irqrestore(&alx->irq_lock, flags);
2847f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2857f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_post_write(hw);
2867f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2879929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	return 0;
2887f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
2897f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic irqreturn_t alx_intr_handle(struct alx_priv *alx, u32 intr)
2919929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan{
2927f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
2937f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	bool write_int_mask = false;
2947f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2957f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	spin_lock(&alx->irq_lock);
2967f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
2977f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* ACK interrupt */
2987f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_ISR, intr | ALX_ISR_DIS);
2999929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	intr &= alx->int_mask;
3007f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3017f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (intr & ALX_ISR_FATAL) {
3027f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		netif_warn(alx, hw, alx->dev,
3037f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			   "fatal interrupt 0x%x, resetting\n", intr);
3047f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_schedule_reset(alx);
3057f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		goto out;
3067f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
3077f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3087f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (intr & ALX_ISR_ALERT)
3097f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		netdev_warn(alx->dev, "alert interrupt: 0x%x\n", intr);
3107f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3117f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (intr & ALX_ISR_PHY) {
3127f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		/* suppress PHY interrupt, because the source
3137f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		 * is from PHY internal. only the internal status
3147f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		 * is cleared, the interrupt status could be cleared.
3157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		 */
3169929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		alx->int_mask &= ~ALX_ISR_PHY;
3179929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		write_int_mask = true;
3189929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		alx_schedule_link_check(alx);
3199929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	}
3209929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
3219929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (intr & (ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0)) {
3229929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		napi_schedule(&alx->napi);
3239929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		/* mask rx/tx interrupt, enable them when napi complete */
3249929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		alx->int_mask &= ~ALX_ISR_ALL_QUEUES;
3259929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		write_int_mask = true;
3269929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	}
3279929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
3289929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (write_int_mask)
3299929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		alx_write_mem32(hw, ALX_IMR, alx->int_mask);
3309929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
3319929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_write_mem32(hw, ALX_ISR, 0);
3329929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
3339929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan out:
3347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	spin_unlock(&alx->irq_lock);
3357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return IRQ_HANDLED;
3367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
3377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic irqreturn_t alx_intr_msi(int irq, void *data)
3399929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan{
3409929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct alx_priv *alx = data;
3417f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return alx_intr_handle(alx, alx_read_mem32(&alx->hw, ALX_ISR));
3437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
3447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3457f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic irqreturn_t alx_intr_legacy(int irq, void *data)
3467f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
3477f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_priv *alx = data;
3487f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
3497f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	u32 intr;
3507f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3517f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	intr = alx_read_mem32(hw, ALX_ISR);
3527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3537f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (intr & ALX_ISR_DIS || !(intr & alx->int_mask))
3547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return IRQ_NONE;
3557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return alx_intr_handle(alx, intr);
3577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
3587f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_init_ring_ptrs(struct alx_priv *alx)
3607f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
3617f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
3627f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	u32 addr_hi = ((u64)alx->descmem.dma) >> 32;
3637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3647f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxq.read_idx = 0;
3657f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxq.write_idx = 0;
3667f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxq.rrd_read_idx = 0;
3677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_RX_BASE_ADDR_HI, addr_hi);
3687f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_RRD_ADDR_LO, alx->rxq.rrd_dma);
3697f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_RRD_RING_SZ, alx->rx_ringsz);
3707f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_RFD_ADDR_LO, alx->rxq.rfd_dma);
3717f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_RFD_RING_SZ, alx->rx_ringsz);
3727f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_RFD_BUF_SZ, alx->rxbuf_size);
3737f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3747f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->txq.read_idx = 0;
3757f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->txq.write_idx = 0;
3767f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_TX_BASE_ADDR_HI, addr_hi);
3777f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_TPD_PRI0_ADDR_LO, alx->txq.tpd_dma);
3787f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_TPD_RING_SZ, alx->tx_ringsz);
3797f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3807f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* load these pointers into the chip */
3817f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_SRAM9, ALX_SRAM_LOAD_PTR);
3827f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
3837f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3847f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_free_txring_buf(struct alx_priv *alx)
3857f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
3867f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_tx_queue *txq = &alx->txq;
3877f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	int i;
3887f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3897f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!txq->bufs)
3907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return;
3917f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3927f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	for (i = 0; i < alx->tx_ringsz; i++)
3937f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_free_txbuf(alx, i);
3947f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
3957f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	memset(txq->bufs, 0, alx->tx_ringsz * sizeof(struct alx_buffer));
3967f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	memset(txq->tpd, 0, alx->tx_ringsz * sizeof(struct alx_txd));
3977f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	txq->write_idx = 0;
3987f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	txq->read_idx = 0;
3997f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4009929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	netdev_reset_queue(alx->dev);
4017f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
4029929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
4037f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_free_rxring_buf(struct alx_priv *alx)
4047f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
4051fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	struct alx_rx_queue *rxq = &alx->rxq;
4061fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	struct alx_buffer *cur_buf;
4071fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	u16 i;
4087f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4097f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (rxq == NULL)
4107f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return;
4117f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4121fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	for (i = 0; i < alx->rx_ringsz; i++) {
4137f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		cur_buf = rxq->bufs + i;
4147f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		if (cur_buf->skb) {
4157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			dma_unmap_single(&alx->hw.pdev->dev,
4167f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan					 dma_unmap_addr(cur_buf, dma),
4177f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan					 dma_unmap_len(cur_buf, size),
418f716cdfe57f217966f41a7add190d6f5b9fd0769Joan Eslinger					 DMA_FROM_DEVICE);
419f716cdfe57f217966f41a7add190d6f5b9fd0769Joan Eslinger			dev_kfree_skb(cur_buf->skb);
4207f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			cur_buf->skb = NULL;
4217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			dma_unmap_len_set(cur_buf, size, 0);
4227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			dma_unmap_addr_set(cur_buf, dma, 0);
4237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		}
4247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
4257f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4267f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	rxq->write_idx = 0;
4277f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	rxq->read_idx = 0;
4287f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	rxq->rrd_read_idx = 0;
4297f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
4307f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4317f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_free_buffers(struct alx_priv *alx)
4327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
4337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_free_txring_buf(alx);
4347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_free_rxring_buf(alx);
4357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
4367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_reinit_rings(struct alx_priv *alx)
4387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
4397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_free_buffers(alx);
4407f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4417f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_init_ring_ptrs(alx);
4427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!alx_refill_rx_ring(alx, GFP_KERNEL))
4447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return -ENOMEM;
445f716cdfe57f217966f41a7add190d6f5b9fd0769Joan Eslinger
446f716cdfe57f217966f41a7add190d6f5b9fd0769Joan Eslinger	return 0;
4477f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
4481fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan
4491fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivanstatic void alx_add_mc_addr(struct alx_hw *hw, const u8 *addr, u32 *mc_hash)
4501fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan{
4517f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	u32 crc32, bit, reg;
4527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4537f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	crc32 = ether_crc(ETH_ALEN, addr);
4547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	reg = (crc32 >> 31) & 0x1;
4557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	bit = (crc32 >> 26) & 0x1F;
4567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	mc_hash[reg] |= BIT(bit);
4587f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
4597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4607f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void __alx_set_rx_mode(struct net_device *netdev)
4617f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
4627f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_priv *alx = netdev_priv(netdev);
4637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
4647f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct netdev_hw_addr *ha;
4657f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	u32 mc_hash[2] = {};
4667f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!(netdev->flags & IFF_ALLMULTI)) {
4687f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		netdev_for_each_mc_addr(ha, netdev)
4697f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			alx_add_mc_addr(hw, ha->addr, mc_hash);
4707f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4717f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_write_mem32(hw, ALX_HASH_TBL0, mc_hash[0]);
4727f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_write_mem32(hw, ALX_HASH_TBL1, mc_hash[1]);
4737f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
4747f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4759929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	hw->rx_ctrl &= ~(ALX_MAC_CTRL_MULTIALL_EN | ALX_MAC_CTRL_PROMISC_EN);
4769929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (netdev->flags & IFF_PROMISC)
4779929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		hw->rx_ctrl |= ALX_MAC_CTRL_PROMISC_EN;
4789929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (netdev->flags & IFF_ALLMULTI)
4797f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		hw->rx_ctrl |= ALX_MAC_CTRL_MULTIALL_EN;
4807f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4817f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl);
4827f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
4837f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4847f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_set_rx_mode(struct net_device *netdev)
4857f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
4867f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	__alx_set_rx_mode(netdev);
4877f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
4887f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4897f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_set_mac_address(struct net_device *netdev, void *data)
4907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
4919929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct alx_priv *alx = netdev_priv(netdev);
4927f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
4937f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct sockaddr *addr = data;
4947f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4957f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!is_valid_ether_addr(addr->sa_data))
4967f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return -EADDRNOTAVAIL;
4977f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
4987f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (netdev->addr_assign_type & NET_ADDR_RANDOM)
4997f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		netdev->addr_assign_type ^= NET_ADDR_RANDOM;
5007f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5017f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
5027f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	memcpy(hw->mac_addr, addr->sa_data, netdev->addr_len);
5037f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_set_macaddr(hw, hw->mac_addr);
5047f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5059929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	return 0;
5067f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
5077f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5087f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_alloc_descriptors(struct alx_priv *alx)
5097f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
5107f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->txq.bufs = kcalloc(alx->tx_ringsz,
5117f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				sizeof(struct alx_buffer),
5127f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				GFP_KERNEL);
5137f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!alx->txq.bufs)
5147f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return -ENOMEM;
5157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5167f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxq.bufs = kcalloc(alx->rx_ringsz,
5177f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				sizeof(struct alx_buffer),
5187f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				GFP_KERNEL);
5197f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!alx->rxq.bufs)
5207f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		goto out_free;
5217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* physical tx/rx ring descriptors
5237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	 *
5247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	 * Allocate them as a single chunk because they must not cross a
5257f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	 * 4G boundary (hardware has a single register for high 32 bits
5269929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	 * of addresses only)
5279929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	 */
5289929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx->descmem.size = sizeof(struct alx_txd) * alx->tx_ringsz +
5299929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			    sizeof(struct alx_rrd) * alx->rx_ringsz +
5309929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			    sizeof(struct alx_rfd) * alx->rx_ringsz;
5319929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx->descmem.virt = dma_zalloc_coherent(&alx->hw.pdev->dev,
5329929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan						alx->descmem.size,
5339929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan						&alx->descmem.dma,
5349929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan						GFP_KERNEL);
5359929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (!alx->descmem.virt)
5367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		goto out_free;
5377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->txq.tpd = (void *)alx->descmem.virt;
5397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->txq.tpd_dma = alx->descmem.dma;
5407f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5417f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* alignment requirement for next block */
5427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	BUILD_BUG_ON(sizeof(struct alx_txd) % 8);
5437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxq.rrd =
5457f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		(void *)((u8 *)alx->descmem.virt +
5467f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			 sizeof(struct alx_txd) * alx->tx_ringsz);
5477f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxq.rrd_dma = alx->descmem.dma +
5487f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			   sizeof(struct alx_txd) * alx->tx_ringsz;
5497f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5507f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* alignment requirement for next block */
5517f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	BUILD_BUG_ON(sizeof(struct alx_rrd) % 8);
5527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5537f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxq.rfd =
5547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		(void *)((u8 *)alx->descmem.virt +
5557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			 sizeof(struct alx_txd) * alx->tx_ringsz +
5567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			 sizeof(struct alx_rrd) * alx->rx_ringsz);
5577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxq.rfd_dma = alx->descmem.dma +
5587f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			   sizeof(struct alx_txd) * alx->tx_ringsz +
5597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			   sizeof(struct alx_rrd) * alx->rx_ringsz;
5603ac8c70f74ca67111c570f4ba828cc4b6fc229f4Dave Olson
5613ac8c70f74ca67111c570f4ba828cc4b6fc229f4Dave Olson	return 0;
5623ac8c70f74ca67111c570f4ba828cc4b6fc229f4Dave Olsonout_free:
5637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	kfree(alx->txq.bufs);
56440d97692fbfe52ef68fa771d8121394b2210fd67Pavel Emelyanov	kfree(alx->rxq.bufs);
565f716cdfe57f217966f41a7add190d6f5b9fd0769Joan Eslinger	return -ENOMEM;
566f716cdfe57f217966f41a7add190d6f5b9fd0769Joan Eslinger}
5677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5681fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivanstatic int alx_alloc_rings(struct alx_priv *alx)
5691fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan{
5701fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	int err;
5713ac8c70f74ca67111c570f4ba828cc4b6fc229f4Dave Olson
5727f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	err = alx_alloc_descriptors(alx);
5737f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (err)
5747f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return err;
5757f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5767f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->int_mask &= ~ALX_ISR_ALL_QUEUES;
5777f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0;
5787f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->tx_ringsz = alx->tx_ringsz;
5797f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5807f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	netif_napi_add(alx->dev, &alx->napi, alx_poll, 64);
5817f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5827f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_reinit_rings(alx);
5837f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return 0;
5847f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
5857f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5867f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_free_rings(struct alx_priv *alx)
5877f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
5887f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	netif_napi_del(&alx->napi);
5897f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_free_buffers(alx);
5907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5917f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	kfree(alx->txq.bufs);
5927f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	kfree(alx->rxq.bufs);
5937f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
5947f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	dma_free_coherent(&alx->hw.pdev->dev,
5957f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			  alx->descmem.size,
5967f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			  alx->descmem.virt,
5977f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			  alx->descmem.dma);
5987f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
5997f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6007f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_config_vector_mapping(struct alx_priv *alx)
6017f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
6027f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
6037f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6047f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_MSI_MAP_TBL1, 0);
6057f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_MSI_MAP_TBL2, 0);
6067f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_MSI_ID_MAP, 0);
6077f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
6087f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
60927b678dd04a636f2c351816f4b3042c8815d4e9dBryan O'Sullivanstatic void alx_irq_enable(struct alx_priv *alx)
6107f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
6117f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
6127f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6137f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* level-1 interrupt switch */
6147f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_ISR, 0);
6157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_IMR, alx->int_mask);
6167f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_post_write(hw);
6177f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
6187f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6197f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_irq_disable(struct alx_priv *alx)
6207f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
6217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
6227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_ISR, ALX_ISR_DIS);
6247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_IMR, 0);
6257f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_post_write(hw);
6267f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6277f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	synchronize_irq(alx->hw.pdev->irq);
6287f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
6297f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6307f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_request_irq(struct alx_priv *alx)
6317f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
6327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct pci_dev *pdev = alx->hw.pdev;
6337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
6347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	int err;
6357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	u32 msi_ctrl;
6367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	msi_ctrl = (hw->imt >> 1) << ALX_MSI_RETRANS_TM_SHIFT;
6387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!pci_enable_msi(alx->hw.pdev)) {
6407f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx->msi = true;
6417f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER,
6437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				msi_ctrl | ALX_MSI_MASK_SEL_LINE);
6447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		err = request_irq(pdev->irq, alx_intr_msi, 0,
6457f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				  alx->dev->name, alx);
6467f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		if (!err)
6477f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			goto out;
6487f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		/* fall back to legacy interrupt */
6497f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		pci_disable_msi(alx->hw.pdev);
6507f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
6517f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, 0);
6537f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	err = request_irq(pdev->irq, alx_intr_legacy, IRQF_SHARED,
6547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			  alx->dev->name, alx);
6557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanout:
6567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!err)
6577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_config_vector_mapping(alx);
6587f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return err;
6597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
6607f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6617f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_free_irq(struct alx_priv *alx)
6627f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
6637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct pci_dev *pdev = alx->hw.pdev;
6647f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6657f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	free_irq(pdev->irq, alx);
6667f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (alx->msi) {
6687f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		pci_disable_msi(alx->hw.pdev);
6697f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx->msi = false;
6707f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
6717f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
6727f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6737f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_identify_hw(struct alx_priv *alx)
6747f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
6757f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
6767f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	int rev = alx_hw_revision(hw);
6777f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6787f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (rev > ALX_REV_C0)
6797f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return -EINVAL;
6807f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6817f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->max_dma_chnl = rev >= ALX_REV_B0 ? 4 : 2;
6827f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6837f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return 0;
6847f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
6857f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6867f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_init_sw(struct alx_priv *alx)
6877f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
6887f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct pci_dev *pdev = alx->hw.pdev;
6897f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
6907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	int err;
6917f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6927f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	err = alx_identify_hw(alx);
6937f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (err) {
6947f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		dev_err(&pdev->dev, "unrecognized chip, aborting\n");
6957f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return err;
6967f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
6977f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
6987f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->hw.lnk_patch =
6997f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		pdev->device == ALX_DEV_ID_AR8161 &&
7007f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		pdev->subsystem_vendor == PCI_VENDOR_ID_ATTANSIC &&
7017f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		pdev->subsystem_device == 0x0091 &&
7027f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		pdev->revision == 0;
7037f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7047f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->smb_timer = 400;
7057f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->mtu = alx->dev->mtu;
7067f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxbuf_size = ALIGN(ALX_RAW_MTU(hw->mtu), 8);
7077f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->tx_ringsz = 256;
7087f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rx_ringsz = 512;
7097f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->imt = 200;
7107f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->int_mask = ALX_ISR_MISC;
7117f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->dma_chnl = hw->max_dma_chnl;
7127f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->ith_tpd = alx->tx_ringsz / 3;
7137f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->link_speed = SPEED_UNKNOWN;
7147f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->duplex = DUPLEX_UNKNOWN;
7157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->adv_cfg = ADVERTISED_Autoneg |
7167f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ADVERTISED_10baseT_Half |
7177f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ADVERTISED_10baseT_Full |
7187f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ADVERTISED_100baseT_Full |
7197f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ADVERTISED_100baseT_Half |
7207f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ADVERTISED_1000baseT_Full;
7217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->flowctrl = ALX_FC_ANEG | ALX_FC_RX | ALX_FC_TX;
7227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->rx_ctrl = ALX_MAC_CTRL_WOLSPED_SWEN |
7247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ALX_MAC_CTRL_MHASH_ALG_HI5B |
7257f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ALX_MAC_CTRL_BRD_EN |
7267f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ALX_MAC_CTRL_PCRCE |
7277f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ALX_MAC_CTRL_CRCE |
7287f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ALX_MAC_CTRL_RXFC_EN |
7297f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      ALX_MAC_CTRL_TXFC_EN |
7307f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		      7 << ALX_MAC_CTRL_PRMBLEN_SHIFT;
7317f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return err;
7339929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan}
7347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic netdev_features_t alx_fix_features(struct net_device *netdev,
7377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan					  netdev_features_t features)
7387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
7397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (netdev->mtu > ALX_MAX_TSO_PKT_SIZE)
7409929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
7419929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
7427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return features;
7437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
7447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7459929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic void alx_netif_stop(struct alx_priv *alx)
7467f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
7479929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx->dev->trans_start = jiffies;
7489929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (netif_carrier_ok(alx->dev)) {
7499929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		netif_carrier_off(alx->dev);
7507f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		netif_tx_disable(alx->dev);
7517f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		napi_disable(&alx->napi);
7527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
7537f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
7547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_halt(struct alx_priv *alx)
7567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
7577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
7587f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_netif_stop(alx);
7607f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->link_speed = SPEED_UNKNOWN;
7617f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->duplex = DUPLEX_UNKNOWN;
7627f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_reset_mac(hw);
764c59a80aca0bfc491d90534ed5606d5493eca24a3Ralph Campbell
765c59a80aca0bfc491d90534ed5606d5493eca24a3Ralph Campbell	/* disable l0s/l1 */
766d8274869d742c3d8082e1428de47e54d12104928Dave Olson	alx_enable_aspm(hw, false, false);
7677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_irq_disable(alx);
7687f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_free_buffers(alx);
769d8274869d742c3d8082e1428de47e54d12104928Dave Olson}
7707f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7717f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_configure(struct alx_priv *alx)
7727f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
7737f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
77444f8e3f3f7d8e61b4aafced278403955fe18ad88Roland Dreier
7757f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_configure_basic(hw);
7767f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_disable_rss(hw);
7777f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	__alx_set_rx_mode(alx->dev);
7787f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7797f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl);
7807f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
7817f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7827f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_activate(struct alx_priv *alx)
78344f8e3f3f7d8e61b4aafced278403955fe18ad88Roland Dreier{
7847f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* hardware setting lost, restore it */
7857f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_reinit_rings(alx);
7869929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_configure(alx);
7877f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7887f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* clear old interrupts */
7897f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem32(&alx->hw, ALX_ISR, ~(u32)ALX_ISR_DIS);
7907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7917f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_irq_enable(alx);
7927f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7937f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_schedule_link_check(alx);
7947f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
7957f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
7967f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_reinit(struct alx_priv *alx)
7977f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
7987f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	ASSERT_RTNL();
7997f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8007f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_halt(alx);
8017f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_activate(alx);
8027f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
8037f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8047f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_change_mtu(struct net_device *netdev, int mtu)
8057f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
8067f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_priv *alx = netdev_priv(netdev);
8077f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	int max_frame = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
8087f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8097f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if ((max_frame < ALX_MIN_FRAME_SIZE) ||
8107f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	    (max_frame > ALX_MAX_FRAME_SIZE))
8117f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return -EINVAL;
8127f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8137f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (netdev->mtu == mtu)
8147f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return 0;
8157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8167f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	netdev->mtu = mtu;
8177f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->hw.mtu = mtu;
8187f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx->rxbuf_size = mtu > ALX_DEF_RXBUF_SIZE ?
8197f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			   ALIGN(max_frame, 8) : ALX_DEF_RXBUF_SIZE;
8207f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	netdev_update_features(netdev);
8217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (netif_running(netdev))
8227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_reinit(alx);
8237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return 0;
8247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
8257f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8267f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_netif_start(struct alx_priv *alx)
8277f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
8287f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	netif_tx_wake_all_queues(alx->dev);
8297f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	napi_enable(&alx->napi);
8307f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	netif_carrier_on(alx->dev);
8317f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
8327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int __alx_open(struct alx_priv *alx, bool resume)
8347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
8357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	int err;
8367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!resume)
8387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		netif_carrier_off(alx->dev);
8397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8407f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	err = alx_alloc_rings(alx);
8417f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (err)
8427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return err;
8437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8449929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_configure(alx);
8459929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
8469929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	err = alx_request_irq(alx);
8479929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (err)
8489929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		goto out_free_rings;
8499929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
8509929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	/* clear old interrupts */
8519929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_write_mem32(&alx->hw, ALX_ISR, ~(u32)ALX_ISR_DIS);
8529929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
8539929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_irq_enable(alx);
8549929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
8559929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (!resume)
8569929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		netif_tx_start_all_queues(alx->dev);
8579929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
8589929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_schedule_link_check(alx);
8599929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	return 0;
8609929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
8619929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanout_free_rings:
8629929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_free_rings(alx);
8639929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	return err;
8649929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan}
8659929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
8669929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic void __alx_stop(struct alx_priv *alx)
8679929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan{
8689929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_halt(alx);
8699929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_free_irq(alx);
8709929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_free_rings(alx);
8719929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan}
8729929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
8739929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic const char *alx_speed_desc(struct alx_hw *hw)
8747f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
8757f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	switch (alx_speed_to_ethadv(hw->link_speed, hw->duplex)) {
8767f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	case ADVERTISED_1000baseT_Full:
8777f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return "1 Gbps Full";
8787f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	case ADVERTISED_100baseT_Full:
8797f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return "100 Mbps Full";
8807f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	case ADVERTISED_100baseT_Half:
8817f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return "100 Mbps Half";
8827f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	case ADVERTISED_10baseT_Full:
8837f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return "10 Mbps Full";
8847f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	case ADVERTISED_10baseT_Half:
8857f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return "10 Mbps Half";
8867f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	default:
8877f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return "Unknown speed";
8887f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
8899929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan}
8907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
8917f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_check_link(struct alx_priv *alx)
8920ed9a4a0b6df0548f9ccadb62add2c0155d5262cBryan O'Sullivan{
8930ed9a4a0b6df0548f9ccadb62add2c0155d5262cBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
8940ed9a4a0b6df0548f9ccadb62add2c0155d5262cBryan O'Sullivan	unsigned long flags;
8950ed9a4a0b6df0548f9ccadb62add2c0155d5262cBryan O'Sullivan	int old_speed;
8960ed9a4a0b6df0548f9ccadb62add2c0155d5262cBryan O'Sullivan	u8 old_duplex;
8970ed9a4a0b6df0548f9ccadb62add2c0155d5262cBryan O'Sullivan	int err;
8980ed9a4a0b6df0548f9ccadb62add2c0155d5262cBryan O'Sullivan
8990ed9a4a0b6df0548f9ccadb62add2c0155d5262cBryan O'Sullivan	/* clear PHY internal interrupt status, otherwise the main
9000ed9a4a0b6df0548f9ccadb62add2c0155d5262cBryan O'Sullivan	 * interrupt status will be asserted forever
9017f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	 */
9027f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_clear_phy_intr(hw);
9037f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
90460948a415859cb859e24f0dfe739069d66577466Ralph Campbell	old_speed = hw->link_speed;
9057f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	old_duplex = hw->duplex;
9067f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	err = alx_read_phy_link(hw);
9077f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (err < 0)
9087f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		goto reset;
9099929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
9109929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	spin_lock_irqsave(&alx->irq_lock, flags);
9119929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx->int_mask |= ALX_ISR_PHY;
9129929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx_write_mem32(hw, ALX_IMR, alx->int_mask);
9139929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	spin_unlock_irqrestore(&alx->irq_lock, flags);
9147f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (old_speed == hw->link_speed)
9167f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return;
9177f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9187f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (hw->link_speed != SPEED_UNKNOWN) {
9199929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		netif_info(alx, link, alx->dev,
9209929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			   "NIC Up: %s\n", alx_speed_desc(hw));
9217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_post_phy_link(hw);
9227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_enable_aspm(hw, true, true);
9237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_start_mac(hw);
9247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9257f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		if (old_speed == SPEED_UNKNOWN)
9267f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			alx_netif_start(alx);
9277f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	} else {
9287f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		/* link is now down */
9297f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_netif_stop(alx);
9307f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		netif_info(alx, link, alx->dev, "Link Down\n");
9317f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		err = alx_reset_mac(hw);
9327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		if (err)
9337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			goto reset;
9347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_irq_disable(alx);
9357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		/* MAC reset causes all HW settings to be lost, restore all */
9377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		err = alx_reinit_rings(alx);
9387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		if (err)
9397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			goto reset;
9407f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_configure(alx);
9417f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_enable_aspm(hw, false, true);
9427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_post_phy_link(hw);
9437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		alx_irq_enable(alx);
9447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
9457f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9467f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return;
9477f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
948f716cdfe57f217966f41a7add190d6f5b9fd0769Joan Eslingerreset:
949f716cdfe57f217966f41a7add190d6f5b9fd0769Joan Eslinger	alx_schedule_reset(alx);
9507f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
9517f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_open(struct net_device *netdev)
9537f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
9547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return __alx_open(netdev_priv(netdev), false);
9557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
9567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_stop(struct net_device *netdev)
9587f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
9597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	__alx_stop(netdev_priv(netdev));
960f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	return 0;
9617f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
9627f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_link_check(struct work_struct *work)
9647f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
965f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	struct alx_priv *alx;
9669929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
9677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx = container_of(work, struct alx_priv, link_check_wk);
9687f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9699929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	rtnl_lock();
9707f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_check_link(alx);
9717f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	rtnl_unlock();
9727f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
9737f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
9747f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_reset(struct work_struct *work)
975f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan{
976f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	struct alx_priv *alx = container_of(work, struct alx_priv, reset_wk);
977f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
9781fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	rtnl_lock();
9791fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	alx_reinit(alx);
980f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	rtnl_unlock();
981f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan}
9821fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan
983f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivanstatic int alx_tx_csum(struct sk_buff *skb, struct alx_txd *first)
984f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan{
985f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	u8 cso, css;
986f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
987f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	if (skb->ip_summed != CHECKSUM_PARTIAL)
988f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		return 0;
989f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
990f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	cso = skb_checksum_start_offset(skb);
991f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	if (cso & 1)
992f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		return -EINVAL;
993f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
994f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	css = cso + skb->csum_offset;
995f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	first->word1 |= cpu_to_le32((cso >> 1) << TPD_CXSUMSTART_SHIFT);
996f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	first->word1 |= cpu_to_le32((css >> 1) << TPD_CXSUMOFFSET_SHIFT);
997f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	first->word1 |= cpu_to_le32(1 << TPD_CXSUM_EN_SHIFT);
998f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
999f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	return 0;
1000f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan}
1001f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
1002f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivanstatic int alx_map_tx_skb(struct alx_priv *alx, struct sk_buff *skb)
1003f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan{
1004f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	struct alx_tx_queue *txq = &alx->txq;
10051fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	struct alx_txd *tpd, *first_tpd;
1006f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	dma_addr_t dma;
1007f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	int maplen, f, first_idx = txq->write_idx;
1008f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
10091fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	first_tpd = &txq->tpd[txq->write_idx];
10101fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	tpd = first_tpd;
10111fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan
1012f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	maplen = skb_headlen(skb);
10131fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	dma = dma_map_single(&alx->hw.pdev->dev, skb->data, maplen,
10141fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan			     DMA_TO_DEVICE);
10151fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	if (dma_mapping_error(&alx->hw.pdev->dev, dma))
1016f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		goto err_dma;
1017f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
1018f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen);
1019f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	dma_unmap_addr_set(&txq->bufs[txq->write_idx], dma, dma);
10207f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	tpd->adrl.addr = cpu_to_le64(dma);
10227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	tpd->len = cpu_to_le16(maplen);
10237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) {
10257f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		struct skb_frag_struct *frag;
1026f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
1027f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		frag = &skb_shinfo(skb)->frags[f];
1028f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
1029f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		if (++txq->write_idx == alx->tx_ringsz)
1030f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan			txq->write_idx = 0;
10317f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		tpd = &txq->tpd[txq->write_idx];
10327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		tpd->word1 = first_tpd->word1;
10347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		maplen = skb_frag_size(frag);
10367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		dma = skb_frag_dma_map(&alx->hw.pdev->dev, frag, 0,
10377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				       maplen, DMA_TO_DEVICE);
10387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		if (dma_mapping_error(&alx->hw.pdev->dev, dma))
10397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			goto err_dma;
10407f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen);
10417f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		dma_unmap_addr_set(&txq->bufs[txq->write_idx], dma, dma);
10427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		tpd->adrl.addr = cpu_to_le64(dma);
10447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		tpd->len = cpu_to_le16(maplen);
10457f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
10467f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10477f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* last TPD, set EOP flag and store skb */
10487f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	tpd->word1 |= cpu_to_le32(1 << TPD_EOP_SHIFT);
10497f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	txq->bufs[txq->write_idx].skb = skb;
10509929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
10519929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (++txq->write_idx == alx->tx_ringsz)
10527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		txq->write_idx = 0;
10537f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return 0;
10557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10567f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanerr_dma:
1057f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	f = first_idx;
1058f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	while (f != txq->write_idx) {
1059f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		alx_free_txbuf(alx, f);
1060f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		if (++f == alx->tx_ringsz)
10617f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			f = 0;
10629929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	}
10637f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return -ENOMEM;
10647f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
10657f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10669929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic netdev_tx_t alx_start_xmit(struct sk_buff *skb,
10677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				  struct net_device *netdev)
10687f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
10697f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_priv *alx = netdev_priv(netdev);
10709929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct alx_tx_queue *txq = &alx->txq;
1071f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	struct alx_txd *first;
1072eb9dc6f48dc7537ce53163109625bd992150e0cfBryan O'Sullivan	int tpdreq = skb_shinfo(skb)->nr_frags + 1;
1073eb9dc6f48dc7537ce53163109625bd992150e0cfBryan O'Sullivan
1074eb9dc6f48dc7537ce53163109625bd992150e0cfBryan O'Sullivan	if (alx_tpd_avail(alx) < tpdreq) {
1075eb9dc6f48dc7537ce53163109625bd992150e0cfBryan O'Sullivan		netif_stop_queue(alx->dev);
1076eb9dc6f48dc7537ce53163109625bd992150e0cfBryan O'Sullivan		goto drop;
1077eb9dc6f48dc7537ce53163109625bd992150e0cfBryan O'Sullivan	}
1078eb9dc6f48dc7537ce53163109625bd992150e0cfBryan O'Sullivan
1079367fe711c5dc85dbc3265cf01e34d4d6fbd55f06Bryan O'Sullivan	first = &txq->tpd[txq->write_idx];
1080367fe711c5dc85dbc3265cf01e34d4d6fbd55f06Bryan O'Sullivan	memset(first, 0, sizeof(*first));
1081367fe711c5dc85dbc3265cf01e34d4d6fbd55f06Bryan O'Sullivan
1082367fe711c5dc85dbc3265cf01e34d4d6fbd55f06Bryan O'Sullivan	if (alx_tx_csum(skb, first))
1083f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		goto drop;
10847f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10857f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (alx_map_tx_skb(alx, skb) < 0)
10867f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		goto drop;
10877f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10887f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	netdev_sent_queue(alx->dev, skb->len);
10897f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10907f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* flush updates before updating hardware */
10917f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	wmb();
10927f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_write_mem16(&alx->hw, ALX_TPD_PRI0_PIDX, txq->write_idx);
10937f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10947f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (alx_tpd_avail(alx) < alx->tx_ringsz/8)
10957f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		netif_stop_queue(alx->dev);
10967f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10977f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return NETDEV_TX_OK;
10987f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
10991fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivandrop:
11007f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	dev_kfree_skb(skb);
11017f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return NETDEV_TX_OK;
11027f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
11037f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
11047f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_tx_timeout(struct net_device *dev)
11057f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
11067f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_priv *alx = netdev_priv(dev);
11077f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
11087f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	alx_schedule_reset(alx);
11099929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan}
11107f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
11117f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_mdio_read(struct net_device *netdev,
11127f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			 int prtad, int devad, u16 addr)
11137f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
11147f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_priv *alx = netdev_priv(netdev);
11157f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
11167f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	u16 val;
11177f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	int err;
11187f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
1119f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	if (prtad != hw->mdio.prtad)
1120f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		return -EINVAL;
11217f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
11227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (devad == MDIO_DEVAD_NONE)
11237f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		err = alx_read_phy_reg(hw, addr, &val);
11247f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	else
11251fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan		err = alx_read_phy_ext(hw, devad, addr, &val);
11261fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan
11271fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	if (err)
11287f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return err;
11297f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return val;
11307f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan}
11317f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
11327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_mdio_write(struct net_device *netdev,
11337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			  int prtad, int devad, u16 addr, u16 val)
11347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
11357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_priv *alx = netdev_priv(netdev);
11367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
11379929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
11383c8450860ba9d6279dbc969633eacf99161860d9Nick Piggin	if (prtad != hw->mdio.prtad)
11399929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		return -EINVAL;
11403c8450860ba9d6279dbc969633eacf99161860d9Nick Piggin
11413c8450860ba9d6279dbc969633eacf99161860d9Nick Piggin	if (devad == MDIO_DEVAD_NONE)
11429929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		return alx_write_phy_reg(hw, addr, val);
11433c8450860ba9d6279dbc969633eacf99161860d9Nick Piggin
11449929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	return alx_write_phy_ext(hw, devad, addr, val);
11453c8450860ba9d6279dbc969633eacf99161860d9Nick Piggin}
11469929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
11473c8450860ba9d6279dbc969633eacf99161860d9Nick Pigginstatic int alx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
11489929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan{
11493c8450860ba9d6279dbc969633eacf99161860d9Nick Piggin	struct alx_priv *alx = netdev_priv(netdev);
11503c8450860ba9d6279dbc969633eacf99161860d9Nick Piggin
11513c8450860ba9d6279dbc969633eacf99161860d9Nick Piggin	if (!netif_running(netdev))
11529929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		return -EAGAIN;
11539929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
11549929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	return mdio_mii_ioctl(&alx->hw.mdio, if_mii(ifr), cmd);
11553c8450860ba9d6279dbc969633eacf99161860d9Nick Piggin}
11569929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
11579929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan#ifdef CONFIG_NET_POLL_CONTROLLER
11589929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic void alx_poll_controller(struct net_device *netdev)
11599929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan{
11609929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct alx_priv *alx = netdev_priv(netdev);
11619929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
11629929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (alx->msi)
11639929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		alx_intr_msi(0, alx);
11649929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	else
11650a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell		alx_intr_legacy(0, alx);
11669929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan}
11679929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan#endif
11680a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell
11699929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic struct rtnl_link_stats64 *alx_get_stats64(struct net_device *dev,
11709929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan					struct rtnl_link_stats64 *net_stats)
11719929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan{
11729929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct alx_priv *alx = netdev_priv(dev);
11739929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct alx_hw_stats *hw_stats = &alx->hw.stats;
11749929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1175c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	spin_lock(&alx->stats_lock);
1176c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
1177c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	alx_update_hw_stats(&alx->hw);
11789929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1179c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->tx_bytes   = hw_stats->tx_byte_cnt;
1180c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->rx_bytes   = hw_stats->rx_byte_cnt;
1181c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->multicast  = hw_stats->rx_mcast;
1182c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->collisions = hw_stats->tx_single_col +
1183c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				hw_stats->tx_multi_col +
1184c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				hw_stats->tx_late_col +
1185c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				hw_stats->tx_abort_col;
1186c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
1187c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->rx_errors  = hw_stats->rx_frag +
1188c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				hw_stats->rx_fcs_err +
1189c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				hw_stats->rx_len_err +
1190c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				hw_stats->rx_ov_sz +
1191c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				hw_stats->rx_ov_rrd +
1192c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				hw_stats->rx_align_err +
1193c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage				hw_stats->rx_ov_rxf;
1194c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
1195c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->rx_fifo_errors   = hw_stats->rx_ov_rxf;
1196c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->rx_length_errors = hw_stats->rx_len_err;
1197c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->rx_crc_errors    = hw_stats->rx_fcs_err;
1198c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->rx_frame_errors  = hw_stats->rx_align_err;
1199c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->rx_dropped       = hw_stats->rx_ov_rrd;
1200c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
1201c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->tx_errors = hw_stats->tx_late_col +
1202c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage			       hw_stats->tx_abort_col +
1203c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage			       hw_stats->tx_underrun +
1204c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage			       hw_stats->tx_trunc;
1205c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
1206c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->tx_aborted_errors = hw_stats->tx_abort_col;
1207c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->tx_fifo_errors    = hw_stats->tx_underrun;
1208c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->tx_window_errors  = hw_stats->tx_late_col;
1209c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
1210c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->tx_packets = hw_stats->tx_ok + net_stats->tx_errors;
1211c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	net_stats->rx_packets = hw_stats->rx_ok + net_stats->rx_errors;
1212c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
1213c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	spin_unlock(&alx->stats_lock);
12149929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1215c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	return net_stats;
12169929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan}
12179929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
12189929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic const struct net_device_ops alx_netdev_ops = {
12199929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_open               = alx_open,
12209929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_stop               = alx_stop,
12219929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_start_xmit         = alx_start_xmit,
12229929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_get_stats64        = alx_get_stats64,
12239929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_set_rx_mode        = alx_set_rx_mode,
12249929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_validate_addr      = eth_validate_addr,
12259929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_set_mac_address    = alx_set_mac_address,
12260a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	.ndo_change_mtu         = alx_change_mtu,
12279929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_do_ioctl           = alx_ioctl,
12289929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_tx_timeout         = alx_tx_timeout,
12299929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_fix_features	= alx_fix_features,
12309929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan#ifdef CONFIG_NET_POLL_CONTROLLER
12319929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.ndo_poll_controller    = alx_poll_controller,
12327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#endif
12337f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan};
12347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
12357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
12367f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan{
12377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct net_device *netdev;
12387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_priv *alx;
12397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	struct alx_hw *hw;
12407f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	bool phy_configured;
12417f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	int bars, err;
12427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
12437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	err = pci_enable_device_mem(pdev);
12447f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (err)
12457f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return err;
12467f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
12479929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	/* The alx chip can DMA to 64-bit addresses, but it uses a single
12487f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	 * shared register for the high 32 bits, so only a single, aligned,
12497f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	 * 4 GB physical address range can be used for descriptors.
12507f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	 */
12519929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) &&
12529929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	    !dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) {
12539929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		dev_dbg(&pdev->dev, "DMA to 64-BIT addresses\n");
12549929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	} else {
12557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
1256f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		if (err) {
12577f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			err = dma_set_coherent_mask(&pdev->dev,
12587f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan						    DMA_BIT_MASK(32));
12597f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			if (err) {
12609929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan				dev_err(&pdev->dev,
12619929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan					"No usable DMA config, aborting\n");
12629929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan				goto out_pci_disable;
12639929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			}
12647f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		}
12657f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
12667f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
12677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	bars = pci_select_bars(pdev, IORESOURCE_MEM);
12689929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	err = pci_request_selected_regions(pdev, bars, alx_drv_name);
12699929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (err) {
12707f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		dev_err(&pdev->dev,
12719929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			"pci_request_selected_regions failed(bars:%d)\n", bars);
12729929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		goto out_pci_disable;
12739929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	}
1274f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
12757f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	pci_enable_pcie_error_reporting(pdev);
12769929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	pci_set_master(pdev);
12777f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
12789929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (!pdev->pm_cap) {
12799929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		dev_err(&pdev->dev,
12807f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			"Can't find power management capability, aborting\n");
12819929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		err = -EIO;
12829929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		goto out_pci_release;
12839929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	}
12849929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
12859929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	netdev = alloc_etherdev(sizeof(*alx));
12860a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	if (!netdev) {
12870a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell		err = -ENOMEM;
12880a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell		goto out_pci_release;
12890a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell	}
12909929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
12919929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	SET_NETDEV_DEV(netdev, &pdev->dev);
12929929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx = netdev_priv(netdev);
1293a18e26ae442001de62f6b84a923e8613347dc35fRalph Campbell	spin_lock_init(&alx->hw.mdio_lock);
12949929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	spin_lock_init(&alx->irq_lock);
12959929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	spin_lock_init(&alx->stats_lock);
1296e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olson	alx->dev = netdev;
12979929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx->hw.pdev = pdev;
12989929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	alx->msg_enable = NETIF_MSG_LINK | NETIF_MSG_HW | NETIF_MSG_IFUP |
12999929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			  NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR | NETIF_MSG_WOL;
1300e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olson	hw = &alx->hw;
1301e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olson	pci_set_drvdata(pdev, alx);
13029929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1303e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olson	hw->hw_addr = pci_ioremap_bar(pdev, 0);
13049929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (!hw->hw_addr) {
13059929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		dev_err(&pdev->dev, "cannot map device registers\n");
13069929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		err = -EIO;
13079929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		goto out_free_netdev;
1308e2ab41cae418108f376ad1634d7507f56379f7a2Dave Olson	}
13099929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1310f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	netdev->netdev_ops = &alx_netdev_ops;
13119929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	SET_ETHTOOL_OPS(netdev, &alx_ethtool_ops);
13129929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	netdev->irq = pdev->irq;
13137f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	netdev->watchdog_timeo = ALX_WATCHDOG_TIME;
13149929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
13159929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (ent->driver_data & ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG)
13169929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		pdev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG;
13179929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
13189929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	err = alx_init_sw(alx);
13191fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	if (err) {
13209929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		dev_err(&pdev->dev, "net device private data init failed\n");
13219929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		goto out_unmap;
13227f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	}
13239929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1324f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	alx_reset_pcie(hw);
1325525d0ca1d452ed336c1d907fb20c104467a8a47bBryan O'Sullivan
1326f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	phy_configured = alx_phy_configured(hw);
1327f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
1328f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	if (!phy_configured)
13299929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		alx_reset_phy(hw);
13301fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan
1331f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan	err = alx_reset_mac(hw);
13329929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (err) {
1333f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		dev_err(&pdev->dev, "MAC Reset failed, error = %d\n", err);
1334f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan		goto out_unmap;
13351fd3b40fde3bfacdf742cadfe99cfd47ffd05219Bryan O'Sullivan	}
1336f37bda92461313ad3bbfbf5660adc849c69718bfBryan O'Sullivan
13377f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	/* setup link to put it in a known good starting state */
13387f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!phy_configured) {
13397f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		err = alx_setup_speed_duplex(hw, hw->adv_cfg, hw->flowctrl);
13407f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		if (err) {
13417f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan			dev_err(&pdev->dev,
13427f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				"failed to configure PHY speed/duplex (err=%d)\n",
13437f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan				err);
13449929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan			goto out_unmap;
13459929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		}
13469929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	}
13479929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
13487f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM;
13497f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
13507f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (alx_get_perm_macaddr(hw, hw->perm_addr)) {
135170c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones		dev_warn(&pdev->dev,
135270c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones			 "Invalid permanent address programmed, using random one\n");
135370c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones		eth_hw_addr_random(netdev);
135470c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones		memcpy(hw->perm_addr, netdev->dev_addr, netdev->addr_len);
135570c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	}
135670c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones
135770c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	memcpy(hw->mac_addr, hw->perm_addr, ETH_ALEN);
135870c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	memcpy(netdev->dev_addr, hw->mac_addr, ETH_ALEN);
135970c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	memcpy(netdev->perm_addr, hw->perm_addr, ETH_ALEN);
136070c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones
136170c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	hw->mdio.prtad = 0;
136270c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	hw->mdio.mmds = 0;
136370c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	hw->mdio.dev = netdev;
1364f2d042313e420002b06715675963cfab48ed2597Robert Walsh	hw->mdio.mode_support = MDIO_SUPPORTS_C45 |
1365f2d042313e420002b06715675963cfab48ed2597Robert Walsh				MDIO_SUPPORTS_C22 |
1366f2d042313e420002b06715675963cfab48ed2597Robert Walsh				MDIO_EMULATE_C22;
13677f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	hw->mdio.mdio_read = alx_mdio_read;
1368e35d710d0c5b74bc9833d6a3791706bd577a3724Bryan O'Sullivan	hw->mdio.mdio_write = alx_mdio_write;
13697f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
13707f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (!alx_get_phy_info(hw)) {
13717f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		dev_err(&pdev->dev, "failed to identify PHY\n");
13727f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		err = -EIO;
137370c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones		goto out_unmap;
137470c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	}
137570c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones
13767f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	INIT_WORK(&alx->link_check_wk, alx_link_check);
137770c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	INIT_WORK(&alx->reset_wk, alx_reset);
1378f2d042313e420002b06715675963cfab48ed2597Robert Walsh	netif_carrier_off(netdev);
137970c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones
1380f2d042313e420002b06715675963cfab48ed2597Robert Walsh	err = register_netdev(netdev);
13817f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	if (err) {
1382f2d042313e420002b06715675963cfab48ed2597Robert Walsh		dev_err(&pdev->dev, "register netdevice failed\n");
138370c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones		goto out_unmap;
1384f2d042313e420002b06715675963cfab48ed2597Robert Walsh	}
138570c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones
138670c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	netdev_info(netdev,
1387f2d042313e420002b06715675963cfab48ed2597Robert Walsh		    "Qualcomm Atheros AR816x/AR817x Ethernet [%pM]\n",
1388f2d042313e420002b06715675963cfab48ed2597Robert Walsh		    netdev->dev_addr);
1389f2d042313e420002b06715675963cfab48ed2597Robert Walsh
1390f2d042313e420002b06715675963cfab48ed2597Robert Walsh	return 0;
1391f2d042313e420002b06715675963cfab48ed2597Robert Walsh
1392f2d042313e420002b06715675963cfab48ed2597Robert Walshout_unmap:
1393f2d042313e420002b06715675963cfab48ed2597Robert Walsh	iounmap(hw->hw_addr);
1394f2d042313e420002b06715675963cfab48ed2597Robert Walshout_free_netdev:
1395f2d042313e420002b06715675963cfab48ed2597Robert Walsh	free_netdev(netdev);
1396f2d042313e420002b06715675963cfab48ed2597Robert Walshout_pci_release:
139770c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	pci_release_selected_regions(pdev, bars);
139870c51da2c4f84317bb13a2b564600afdcebd686fArthur Jonesout_pci_disable:
1399f2d042313e420002b06715675963cfab48ed2597Robert Walsh	pci_disable_device(pdev);
1400f2d042313e420002b06715675963cfab48ed2597Robert Walsh	return err;
1401f2d042313e420002b06715675963cfab48ed2597Robert Walsh}
1402f2d042313e420002b06715675963cfab48ed2597Robert Walsh
14037f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic void alx_remove(struct pci_dev *pdev)
140470c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones{
140570c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	struct alx_priv *alx = pci_get_drvdata(pdev);
140670c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	struct alx_hw *hw = &alx->hw;
140770c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones
14087f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	cancel_work_sync(&alx->link_check_wk);
1409c59a80aca0bfc491d90534ed5606d5493eca24a3Ralph Campbell	cancel_work_sync(&alx->reset_wk);
1410c59a80aca0bfc491d90534ed5606d5493eca24a3Ralph Campbell
1411c59a80aca0bfc491d90534ed5606d5493eca24a3Ralph Campbell	/* restore permanent mac address */
1412c59a80aca0bfc491d90534ed5606d5493eca24a3Ralph Campbell	alx_set_macaddr(hw, hw->perm_addr);
1413f2d042313e420002b06715675963cfab48ed2597Robert Walsh
141470c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	unregister_netdev(alx->dev);
1415f2d042313e420002b06715675963cfab48ed2597Robert Walsh	iounmap(hw->hw_addr);
141670c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	pci_release_selected_regions(pdev,
141770c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones				     pci_select_bars(pdev, IORESOURCE_MEM));
14187f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan
141970c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	pci_disable_pcie_error_reporting(pdev);
142070c51da2c4f84317bb13a2b564600afdcebd686fArthur Jones	pci_disable_device(pdev);
1421f2d042313e420002b06715675963cfab48ed2597Robert Walsh
1422d8274869d742c3d8082e1428de47e54d12104928Dave Olson	free_netdev(alx->dev);
1423f2d042313e420002b06715675963cfab48ed2597Robert Walsh}
1424f2d042313e420002b06715675963cfab48ed2597Robert Walsh
1425f2d042313e420002b06715675963cfab48ed2597Robert Walsh#ifdef CONFIG_PM_SLEEP
1426f2d042313e420002b06715675963cfab48ed2597Robert Walshstatic int alx_suspend(struct device *dev)
1427f2d042313e420002b06715675963cfab48ed2597Robert Walsh{
14289929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct pci_dev *pdev = to_pci_dev(dev);
1429f2d042313e420002b06715675963cfab48ed2597Robert Walsh	struct alx_priv *alx = pci_get_drvdata(pdev);
1430f2d042313e420002b06715675963cfab48ed2597Robert Walsh
1431f2d042313e420002b06715675963cfab48ed2597Robert Walsh	if (!netif_running(alx->dev))
14327f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan		return 0;
1433f2d042313e420002b06715675963cfab48ed2597Robert Walsh	netif_device_detach(alx->dev);
14347f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	__alx_stop(alx);
14357f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan	return 0;
1436f2d042313e420002b06715675963cfab48ed2597Robert Walsh}
1437f2d042313e420002b06715675963cfab48ed2597Robert Walsh
1438f2d042313e420002b06715675963cfab48ed2597Robert Walshstatic int alx_resume(struct device *dev)
1439f2d042313e420002b06715675963cfab48ed2597Robert Walsh{
1440f2d042313e420002b06715675963cfab48ed2597Robert Walsh	struct pci_dev *pdev = to_pci_dev(dev);
1441f2d042313e420002b06715675963cfab48ed2597Robert Walsh	struct alx_priv *alx = pci_get_drvdata(pdev);
1442f2d042313e420002b06715675963cfab48ed2597Robert Walsh	struct alx_hw *hw = &alx->hw;
1443f2d042313e420002b06715675963cfab48ed2597Robert Walsh
1444f2d042313e420002b06715675963cfab48ed2597Robert Walsh	alx_reset_phy(hw);
1445f2d042313e420002b06715675963cfab48ed2597Robert Walsh
1446f2d042313e420002b06715675963cfab48ed2597Robert Walsh	if (!netif_running(alx->dev))
1447f2d042313e420002b06715675963cfab48ed2597Robert Walsh		return 0;
1448f2d042313e420002b06715675963cfab48ed2597Robert Walsh	netif_device_attach(alx->dev);
1449f2d042313e420002b06715675963cfab48ed2597Robert Walsh	return __alx_open(alx, true);
1450f2d042313e420002b06715675963cfab48ed2597Robert Walsh}
1451f2d042313e420002b06715675963cfab48ed2597Robert Walsh
14527f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivanstatic SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume);
1453e35d710d0c5b74bc9833d6a3791706bd577a3724Bryan O'Sullivan#define ALX_PM_OPS      (&alx_pm_ops)
14547f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#else
14557f510b46e4771cfb89af134b3aa827d46125a2ceBryan O'Sullivan#define ALX_PM_OPS      NULL
14560df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage#endif
14570df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
14580df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
14590df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbagestatic pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev,
14600df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage					       pci_channel_state_t state)
14610df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage{
14620df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	struct alx_priv *alx = pci_get_drvdata(pdev);
14630df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	struct net_device *netdev = alx->dev;
14640df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	pci_ers_result_t rc = PCI_ERS_RESULT_NEED_RESET;
14650df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
14660df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	dev_info(&pdev->dev, "pci error detected\n");
14670df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
14680df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	rtnl_lock();
14690df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
14700df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	if (netif_running(netdev)) {
14710df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage		netif_device_detach(netdev);
14720df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage		alx_halt(alx);
14730df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	}
14740df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
14750df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	if (state == pci_channel_io_perm_failure)
14760df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage		rc = PCI_ERS_RESULT_DISCONNECT;
14770df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	else
14780df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage		pci_disable_device(pdev);
14790df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
14800df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	rtnl_unlock();
14810df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
14820df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	return rc;
14830df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage}
14840df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
14850df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbagestatic pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev)
14860df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage{
14870df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	struct alx_priv *alx = pci_get_drvdata(pdev);
14889929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	struct alx_hw *hw = &alx->hw;
14899929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	pci_ers_result_t rc = PCI_ERS_RESULT_DISCONNECT;
14909929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
14919929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	dev_info(&pdev->dev, "pci error slot reset\n");
14929929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1493c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	rtnl_lock();
14949929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
14959929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	if (pci_enable_device(pdev)) {
14969929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan		dev_err(&pdev->dev, "Failed to re-enable PCI device after reset\n");
1497bacf4013530e7fc230a8aa0c6ea3c17fc2f47665Mark Debbage		goto out;
14989929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	}
14999929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1500bacf4013530e7fc230a8aa0c6ea3c17fc2f47665Mark Debbage	pci_set_master(pdev);
15019929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1502c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	alx_reset_pcie(hw);
15030df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	if (!alx_reset_mac(hw))
15040df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage		rc = PCI_ERS_RESULT_RECOVERED;
15050df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbageout:
15060df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	pci_cleanup_aer_uncorrect_error_status(pdev);
1507c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
15080df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	rtnl_unlock();
15090df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
15100df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	return rc;
15110df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage}
15120df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
15130df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbagestatic void alx_pci_error_resume(struct pci_dev *pdev)
15140df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage{
15150df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	struct alx_priv *alx = pci_get_drvdata(pdev);
15160df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	struct net_device *netdev = alx->dev;
15170df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage
1518c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	dev_info(&pdev->dev, "pci error resume\n");
1519c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
15200df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	rtnl_lock();
1521c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage
15220df6291c8af2778d05f278d5738eef2c8fafa2ddMark Debbage	if (netif_running(netdev)) {
1523c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		alx_activate(alx);
1524c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage		netif_device_attach(netdev);
1525c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	}
15260a5a83cffc03592c2102ad07b7532b596a16f8cdRalph Campbell
15279929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	rtnl_unlock();
15289929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan}
15299929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
15309929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic const struct pci_error_handlers alx_err_handlers = {
1531c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	.error_detected = alx_pci_error_detected,
1532c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	.slot_reset     = alx_pci_error_slot_reset,
15339929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.resume         = alx_pci_error_resume,
15349929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan};
15359929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
15369929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivanstatic DEFINE_PCI_DEVICE_TABLE(alx_pci_tbl) = {
15379929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8161),
15389929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	  .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
1539c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2200),
15409929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	  .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
15419929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8162),
15429929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	  .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
15439929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8171) },
15449929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8172) },
15459929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	{}
15469929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan};
15479929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1548c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbagestatic struct pci_driver alx_driver = {
15499929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.name        = alx_drv_name,
15509929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.id_table    = alx_pci_tbl,
15519929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.probe       = alx_probe,
15529929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.remove      = alx_remove,
15539929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.err_handler = &alx_err_handlers,
15549929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan	.driver.pm   = ALX_PM_OPS,
15559929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan};
15569929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan
1557947d7617a1d876c2c93f73017a734e070c64d43bRalph Campbellmodule_pci_driver(alx_driver);
1558c7e29ff11f23ec78b3caf691789c2b791bb596bfMark DebbageMODULE_DEVICE_TABLE(pci, alx_pci_tbl);
1559c7e29ff11f23ec78b3caf691789c2b791bb596bfMark DebbageMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
1560c7e29ff11f23ec78b3caf691789c2b791bb596bfMark DebbageMODULE_AUTHOR("Qualcomm Corporation, <nic-devel@qualcomm.com>");
1561c7e29ff11f23ec78b3caf691789c2b791bb596bfMark DebbageMODULE_DESCRIPTION(
1562c7e29ff11f23ec78b3caf691789c2b791bb596bfMark Debbage	"Qualcomm Atheros(R) AR816x/AR817x PCI-E Ethernet Network Driver");
15639929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'SullivanMODULE_LICENSE("GPL");
15649929b0fb0f35f54371e9364bab809bcd753f9d3aBryan O'Sullivan