18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2013 Johannes Berg <johannes@sipsolutions.net> 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is free software: you may copy, redistribute and/or modify it 58c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the 68c2ecf20Sopenharmony_ci * Free Software Foundation, either version 2 of the License, or (at your 78c2ecf20Sopenharmony_ci * option) any later version. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 108c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 118c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 128c2ecf20Sopenharmony_ci * General Public License for more details. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License 158c2ecf20Sopenharmony_ci * along with this program. If not, see <http://www.gnu.org/licenses/>. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * This file incorporates work covered by the following copyright and 188c2ecf20Sopenharmony_ci * permission notice: 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Copyright (c) 2012 Qualcomm Atheros, Inc. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 238c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 248c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 278c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 288c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 298c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 308c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 318c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 328c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/module.h> 368c2ecf20Sopenharmony_ci#include <linux/pci.h> 378c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 388c2ecf20Sopenharmony_ci#include <linux/ip.h> 398c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 408c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 418c2ecf20Sopenharmony_ci#include <linux/mdio.h> 428c2ecf20Sopenharmony_ci#include <linux/aer.h> 438c2ecf20Sopenharmony_ci#include <linux/bitops.h> 448c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 458c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 468c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 478c2ecf20Sopenharmony_ci#include <linux/crc32.h> 488c2ecf20Sopenharmony_ci#include "alx.h" 498c2ecf20Sopenharmony_ci#include "hw.h" 508c2ecf20Sopenharmony_ci#include "reg.h" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic const char alx_drv_name[] = "alx"; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void alx_free_txbuf(struct alx_tx_queue *txq, int entry) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct alx_buffer *txb = &txq->bufs[entry]; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (dma_unmap_len(txb, size)) { 598c2ecf20Sopenharmony_ci dma_unmap_single(txq->dev, 608c2ecf20Sopenharmony_ci dma_unmap_addr(txb, dma), 618c2ecf20Sopenharmony_ci dma_unmap_len(txb, size), 628c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 638c2ecf20Sopenharmony_ci dma_unmap_len_set(txb, size, 0); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (txb->skb) { 678c2ecf20Sopenharmony_ci dev_kfree_skb_any(txb->skb); 688c2ecf20Sopenharmony_ci txb->skb = NULL; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct alx_rx_queue *rxq = alx->qnapi[0]->rxq; 758c2ecf20Sopenharmony_ci struct sk_buff *skb; 768c2ecf20Sopenharmony_ci struct alx_buffer *cur_buf; 778c2ecf20Sopenharmony_ci dma_addr_t dma; 788c2ecf20Sopenharmony_ci u16 cur, next, count = 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci next = cur = rxq->write_idx; 818c2ecf20Sopenharmony_ci if (++next == alx->rx_ringsz) 828c2ecf20Sopenharmony_ci next = 0; 838c2ecf20Sopenharmony_ci cur_buf = &rxq->bufs[cur]; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci while (!cur_buf->skb && next != rxq->read_idx) { 868c2ecf20Sopenharmony_ci struct alx_rfd *rfd = &rxq->rfd[cur]; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* 898c2ecf20Sopenharmony_ci * When DMA RX address is set to something like 908c2ecf20Sopenharmony_ci * 0x....fc0, it will be very likely to cause DMA 918c2ecf20Sopenharmony_ci * RFD overflow issue. 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * To work around it, we apply rx skb with 64 bytes 948c2ecf20Sopenharmony_ci * longer space, and offset the address whenever 958c2ecf20Sopenharmony_ci * 0x....fc0 is detected. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci skb = __netdev_alloc_skb(alx->dev, alx->rxbuf_size + 64, gfp); 988c2ecf20Sopenharmony_ci if (!skb) 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (((unsigned long)skb->data & 0xfff) == 0xfc0) 1028c2ecf20Sopenharmony_ci skb_reserve(skb, 64); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci dma = dma_map_single(&alx->hw.pdev->dev, 1058c2ecf20Sopenharmony_ci skb->data, alx->rxbuf_size, 1068c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 1078c2ecf20Sopenharmony_ci if (dma_mapping_error(&alx->hw.pdev->dev, dma)) { 1088c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Unfortunately, RX descriptor buffers must be 4-byte 1138c2ecf20Sopenharmony_ci * aligned, so we can't use IP alignment. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci if (WARN_ON(dma & 3)) { 1168c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci cur_buf->skb = skb; 1218c2ecf20Sopenharmony_ci dma_unmap_len_set(cur_buf, size, alx->rxbuf_size); 1228c2ecf20Sopenharmony_ci dma_unmap_addr_set(cur_buf, dma, dma); 1238c2ecf20Sopenharmony_ci rfd->addr = cpu_to_le64(dma); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci cur = next; 1268c2ecf20Sopenharmony_ci if (++next == alx->rx_ringsz) 1278c2ecf20Sopenharmony_ci next = 0; 1288c2ecf20Sopenharmony_ci cur_buf = &rxq->bufs[cur]; 1298c2ecf20Sopenharmony_ci count++; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (count) { 1338c2ecf20Sopenharmony_ci /* flush all updates before updating hardware */ 1348c2ecf20Sopenharmony_ci wmb(); 1358c2ecf20Sopenharmony_ci rxq->write_idx = cur; 1368c2ecf20Sopenharmony_ci alx_write_mem16(&alx->hw, ALX_RFD_PIDX, cur); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return count; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic struct alx_tx_queue *alx_tx_queue_mapping(struct alx_priv *alx, 1438c2ecf20Sopenharmony_ci struct sk_buff *skb) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci unsigned int r_idx = skb->queue_mapping; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (r_idx >= alx->num_txq) 1488c2ecf20Sopenharmony_ci r_idx = r_idx % alx->num_txq; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return alx->qnapi[r_idx]->txq; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic struct netdev_queue *alx_get_tx_queue(const struct alx_tx_queue *txq) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci return netdev_get_tx_queue(txq->netdev, txq->queue_idx); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic inline int alx_tpd_avail(struct alx_tx_queue *txq) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci if (txq->write_idx >= txq->read_idx) 1618c2ecf20Sopenharmony_ci return txq->count + txq->read_idx - txq->write_idx - 1; 1628c2ecf20Sopenharmony_ci return txq->read_idx - txq->write_idx - 1; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic bool alx_clean_tx_irq(struct alx_tx_queue *txq) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct alx_priv *alx; 1688c2ecf20Sopenharmony_ci struct netdev_queue *tx_queue; 1698c2ecf20Sopenharmony_ci u16 hw_read_idx, sw_read_idx; 1708c2ecf20Sopenharmony_ci unsigned int total_bytes = 0, total_packets = 0; 1718c2ecf20Sopenharmony_ci int budget = ALX_DEFAULT_TX_WORK; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci alx = netdev_priv(txq->netdev); 1748c2ecf20Sopenharmony_ci tx_queue = alx_get_tx_queue(txq); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci sw_read_idx = txq->read_idx; 1778c2ecf20Sopenharmony_ci hw_read_idx = alx_read_mem16(&alx->hw, txq->c_reg); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (sw_read_idx != hw_read_idx) { 1808c2ecf20Sopenharmony_ci while (sw_read_idx != hw_read_idx && budget > 0) { 1818c2ecf20Sopenharmony_ci struct sk_buff *skb; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci skb = txq->bufs[sw_read_idx].skb; 1848c2ecf20Sopenharmony_ci if (skb) { 1858c2ecf20Sopenharmony_ci total_bytes += skb->len; 1868c2ecf20Sopenharmony_ci total_packets++; 1878c2ecf20Sopenharmony_ci budget--; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci alx_free_txbuf(txq, sw_read_idx); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (++sw_read_idx == txq->count) 1938c2ecf20Sopenharmony_ci sw_read_idx = 0; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci txq->read_idx = sw_read_idx; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci netdev_tx_completed_queue(tx_queue, total_packets, total_bytes); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (netif_tx_queue_stopped(tx_queue) && netif_carrier_ok(alx->dev) && 2018c2ecf20Sopenharmony_ci alx_tpd_avail(txq) > txq->count / 4) 2028c2ecf20Sopenharmony_ci netif_tx_wake_queue(tx_queue); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return sw_read_idx == hw_read_idx; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void alx_schedule_link_check(struct alx_priv *alx) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci schedule_work(&alx->link_check_wk); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void alx_schedule_reset(struct alx_priv *alx) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci schedule_work(&alx->reset_wk); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int alx_clean_rx_irq(struct alx_rx_queue *rxq, int budget) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct alx_priv *alx; 2208c2ecf20Sopenharmony_ci struct alx_rrd *rrd; 2218c2ecf20Sopenharmony_ci struct alx_buffer *rxb; 2228c2ecf20Sopenharmony_ci struct sk_buff *skb; 2238c2ecf20Sopenharmony_ci u16 length, rfd_cleaned = 0; 2248c2ecf20Sopenharmony_ci int work = 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci alx = netdev_priv(rxq->netdev); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci while (work < budget) { 2298c2ecf20Sopenharmony_ci rrd = &rxq->rrd[rxq->rrd_read_idx]; 2308c2ecf20Sopenharmony_ci if (!(rrd->word3 & cpu_to_le32(1 << RRD_UPDATED_SHIFT))) 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci rrd->word3 &= ~cpu_to_le32(1 << RRD_UPDATED_SHIFT); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (ALX_GET_FIELD(le32_to_cpu(rrd->word0), 2358c2ecf20Sopenharmony_ci RRD_SI) != rxq->read_idx || 2368c2ecf20Sopenharmony_ci ALX_GET_FIELD(le32_to_cpu(rrd->word0), 2378c2ecf20Sopenharmony_ci RRD_NOR) != 1) { 2388c2ecf20Sopenharmony_ci alx_schedule_reset(alx); 2398c2ecf20Sopenharmony_ci return work; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci rxb = &rxq->bufs[rxq->read_idx]; 2438c2ecf20Sopenharmony_ci dma_unmap_single(rxq->dev, 2448c2ecf20Sopenharmony_ci dma_unmap_addr(rxb, dma), 2458c2ecf20Sopenharmony_ci dma_unmap_len(rxb, size), 2468c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2478c2ecf20Sopenharmony_ci dma_unmap_len_set(rxb, size, 0); 2488c2ecf20Sopenharmony_ci skb = rxb->skb; 2498c2ecf20Sopenharmony_ci rxb->skb = NULL; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (rrd->word3 & cpu_to_le32(1 << RRD_ERR_RES_SHIFT) || 2528c2ecf20Sopenharmony_ci rrd->word3 & cpu_to_le32(1 << RRD_ERR_LEN_SHIFT)) { 2538c2ecf20Sopenharmony_ci rrd->word3 = 0; 2548c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2558c2ecf20Sopenharmony_ci goto next_pkt; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci length = ALX_GET_FIELD(le32_to_cpu(rrd->word3), 2598c2ecf20Sopenharmony_ci RRD_PKTLEN) - ETH_FCS_LEN; 2608c2ecf20Sopenharmony_ci skb_put(skb, length); 2618c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, rxq->netdev); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 2648c2ecf20Sopenharmony_ci if (alx->dev->features & NETIF_F_RXCSUM && 2658c2ecf20Sopenharmony_ci !(rrd->word3 & (cpu_to_le32(1 << RRD_ERR_L4_SHIFT) | 2668c2ecf20Sopenharmony_ci cpu_to_le32(1 << RRD_ERR_IPV4_SHIFT)))) { 2678c2ecf20Sopenharmony_ci switch (ALX_GET_FIELD(le32_to_cpu(rrd->word2), 2688c2ecf20Sopenharmony_ci RRD_PID)) { 2698c2ecf20Sopenharmony_ci case RRD_PID_IPV6UDP: 2708c2ecf20Sopenharmony_ci case RRD_PID_IPV4UDP: 2718c2ecf20Sopenharmony_ci case RRD_PID_IPV4TCP: 2728c2ecf20Sopenharmony_ci case RRD_PID_IPV6TCP: 2738c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci napi_gro_receive(&rxq->np->napi, skb); 2798c2ecf20Sopenharmony_ci work++; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cinext_pkt: 2828c2ecf20Sopenharmony_ci if (++rxq->read_idx == rxq->count) 2838c2ecf20Sopenharmony_ci rxq->read_idx = 0; 2848c2ecf20Sopenharmony_ci if (++rxq->rrd_read_idx == rxq->count) 2858c2ecf20Sopenharmony_ci rxq->rrd_read_idx = 0; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (++rfd_cleaned > ALX_RX_ALLOC_THRESH) 2888c2ecf20Sopenharmony_ci rfd_cleaned -= alx_refill_rx_ring(alx, GFP_ATOMIC); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (rfd_cleaned) 2928c2ecf20Sopenharmony_ci alx_refill_rx_ring(alx, GFP_ATOMIC); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return work; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int alx_poll(struct napi_struct *napi, int budget) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct alx_napi *np = container_of(napi, struct alx_napi, napi); 3008c2ecf20Sopenharmony_ci struct alx_priv *alx = np->alx; 3018c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 3028c2ecf20Sopenharmony_ci unsigned long flags; 3038c2ecf20Sopenharmony_ci bool tx_complete = true; 3048c2ecf20Sopenharmony_ci int work = 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (np->txq) 3078c2ecf20Sopenharmony_ci tx_complete = alx_clean_tx_irq(np->txq); 3088c2ecf20Sopenharmony_ci if (np->rxq) 3098c2ecf20Sopenharmony_ci work = alx_clean_rx_irq(np->rxq, budget); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!tx_complete || work == budget) 3128c2ecf20Sopenharmony_ci return budget; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci napi_complete_done(&np->napi, work); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* enable interrupt */ 3178c2ecf20Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 3188c2ecf20Sopenharmony_ci alx_mask_msix(hw, np->vec_idx, false); 3198c2ecf20Sopenharmony_ci } else { 3208c2ecf20Sopenharmony_ci spin_lock_irqsave(&alx->irq_lock, flags); 3218c2ecf20Sopenharmony_ci alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0; 3228c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 3238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&alx->irq_lock, flags); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci alx_post_write(hw); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return work; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic bool alx_intr_handle_misc(struct alx_priv *alx, u32 intr) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (intr & ALX_ISR_FATAL) { 3368c2ecf20Sopenharmony_ci netif_warn(alx, hw, alx->dev, 3378c2ecf20Sopenharmony_ci "fatal interrupt 0x%x, resetting\n", intr); 3388c2ecf20Sopenharmony_ci alx_schedule_reset(alx); 3398c2ecf20Sopenharmony_ci return true; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (intr & ALX_ISR_ALERT) 3438c2ecf20Sopenharmony_ci netdev_warn(alx->dev, "alert interrupt: 0x%x\n", intr); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (intr & ALX_ISR_PHY) { 3468c2ecf20Sopenharmony_ci /* suppress PHY interrupt, because the source 3478c2ecf20Sopenharmony_ci * is from PHY internal. only the internal status 3488c2ecf20Sopenharmony_ci * is cleared, the interrupt status could be cleared. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci alx->int_mask &= ~ALX_ISR_PHY; 3518c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 3528c2ecf20Sopenharmony_ci alx_schedule_link_check(alx); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return false; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic irqreturn_t alx_intr_handle(struct alx_priv *alx, u32 intr) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci spin_lock(&alx->irq_lock); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* ACK interrupt */ 3658c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, intr | ALX_ISR_DIS); 3668c2ecf20Sopenharmony_ci intr &= alx->int_mask; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (alx_intr_handle_misc(alx, intr)) 3698c2ecf20Sopenharmony_ci goto out; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (intr & (ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0)) { 3728c2ecf20Sopenharmony_ci napi_schedule(&alx->qnapi[0]->napi); 3738c2ecf20Sopenharmony_ci /* mask rx/tx interrupt, enable them when napi complete */ 3748c2ecf20Sopenharmony_ci alx->int_mask &= ~ALX_ISR_ALL_QUEUES; 3758c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, 0); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci out: 3818c2ecf20Sopenharmony_ci spin_unlock(&alx->irq_lock); 3828c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic irqreturn_t alx_intr_msix_ring(int irq, void *data) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct alx_napi *np = data; 3888c2ecf20Sopenharmony_ci struct alx_hw *hw = &np->alx->hw; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* mask interrupt to ACK chip */ 3918c2ecf20Sopenharmony_ci alx_mask_msix(hw, np->vec_idx, true); 3928c2ecf20Sopenharmony_ci /* clear interrupt status */ 3938c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, np->vec_mask); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci napi_schedule(&np->napi); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic irqreturn_t alx_intr_msix_misc(int irq, void *data) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct alx_priv *alx = data; 4038c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 4048c2ecf20Sopenharmony_ci u32 intr; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* mask interrupt to ACK chip */ 4078c2ecf20Sopenharmony_ci alx_mask_msix(hw, 0, true); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* read interrupt status */ 4108c2ecf20Sopenharmony_ci intr = alx_read_mem32(hw, ALX_ISR); 4118c2ecf20Sopenharmony_ci intr &= (alx->int_mask & ~ALX_ISR_ALL_QUEUES); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (alx_intr_handle_misc(alx, intr)) 4148c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* clear interrupt status */ 4178c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, intr); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* enable interrupt again */ 4208c2ecf20Sopenharmony_ci alx_mask_msix(hw, 0, false); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic irqreturn_t alx_intr_msi(int irq, void *data) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct alx_priv *alx = data; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return alx_intr_handle(alx, alx_read_mem32(&alx->hw, ALX_ISR)); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic irqreturn_t alx_intr_legacy(int irq, void *data) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct alx_priv *alx = data; 4358c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 4368c2ecf20Sopenharmony_ci u32 intr; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci intr = alx_read_mem32(hw, ALX_ISR); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (intr & ALX_ISR_DIS || !(intr & alx->int_mask)) 4418c2ecf20Sopenharmony_ci return IRQ_NONE; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return alx_intr_handle(alx, intr); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic const u16 txring_header_reg[] = {ALX_TPD_PRI0_ADDR_LO, 4478c2ecf20Sopenharmony_ci ALX_TPD_PRI1_ADDR_LO, 4488c2ecf20Sopenharmony_ci ALX_TPD_PRI2_ADDR_LO, 4498c2ecf20Sopenharmony_ci ALX_TPD_PRI3_ADDR_LO}; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic void alx_init_ring_ptrs(struct alx_priv *alx) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 4548c2ecf20Sopenharmony_ci u32 addr_hi = ((u64)alx->descmem.dma) >> 32; 4558c2ecf20Sopenharmony_ci struct alx_napi *np; 4568c2ecf20Sopenharmony_ci int i; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) { 4598c2ecf20Sopenharmony_ci np = alx->qnapi[i]; 4608c2ecf20Sopenharmony_ci if (np->txq) { 4618c2ecf20Sopenharmony_ci np->txq->read_idx = 0; 4628c2ecf20Sopenharmony_ci np->txq->write_idx = 0; 4638c2ecf20Sopenharmony_ci alx_write_mem32(hw, 4648c2ecf20Sopenharmony_ci txring_header_reg[np->txq->queue_idx], 4658c2ecf20Sopenharmony_ci np->txq->tpd_dma); 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (np->rxq) { 4698c2ecf20Sopenharmony_ci np->rxq->read_idx = 0; 4708c2ecf20Sopenharmony_ci np->rxq->write_idx = 0; 4718c2ecf20Sopenharmony_ci np->rxq->rrd_read_idx = 0; 4728c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_RRD_ADDR_LO, np->rxq->rrd_dma); 4738c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_RFD_ADDR_LO, np->rxq->rfd_dma); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_TX_BASE_ADDR_HI, addr_hi); 4788c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_TPD_RING_SZ, alx->tx_ringsz); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_RX_BASE_ADDR_HI, addr_hi); 4818c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_RRD_RING_SZ, alx->rx_ringsz); 4828c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_RFD_RING_SZ, alx->rx_ringsz); 4838c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_RFD_BUF_SZ, alx->rxbuf_size); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* load these pointers into the chip */ 4868c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_SRAM9, ALX_SRAM_LOAD_PTR); 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic void alx_free_txring_buf(struct alx_tx_queue *txq) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci int i; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (!txq->bufs) 4948c2ecf20Sopenharmony_ci return; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci for (i = 0; i < txq->count; i++) 4978c2ecf20Sopenharmony_ci alx_free_txbuf(txq, i); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci memset(txq->bufs, 0, txq->count * sizeof(struct alx_buffer)); 5008c2ecf20Sopenharmony_ci memset(txq->tpd, 0, txq->count * sizeof(struct alx_txd)); 5018c2ecf20Sopenharmony_ci txq->write_idx = 0; 5028c2ecf20Sopenharmony_ci txq->read_idx = 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci netdev_tx_reset_queue(alx_get_tx_queue(txq)); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void alx_free_rxring_buf(struct alx_rx_queue *rxq) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct alx_buffer *cur_buf; 5108c2ecf20Sopenharmony_ci u16 i; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (!rxq->bufs) 5138c2ecf20Sopenharmony_ci return; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci for (i = 0; i < rxq->count; i++) { 5168c2ecf20Sopenharmony_ci cur_buf = rxq->bufs + i; 5178c2ecf20Sopenharmony_ci if (cur_buf->skb) { 5188c2ecf20Sopenharmony_ci dma_unmap_single(rxq->dev, 5198c2ecf20Sopenharmony_ci dma_unmap_addr(cur_buf, dma), 5208c2ecf20Sopenharmony_ci dma_unmap_len(cur_buf, size), 5218c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5228c2ecf20Sopenharmony_ci dev_kfree_skb(cur_buf->skb); 5238c2ecf20Sopenharmony_ci cur_buf->skb = NULL; 5248c2ecf20Sopenharmony_ci dma_unmap_len_set(cur_buf, size, 0); 5258c2ecf20Sopenharmony_ci dma_unmap_addr_set(cur_buf, dma, 0); 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci rxq->write_idx = 0; 5308c2ecf20Sopenharmony_ci rxq->read_idx = 0; 5318c2ecf20Sopenharmony_ci rxq->rrd_read_idx = 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic void alx_free_buffers(struct alx_priv *alx) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci int i; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) 5398c2ecf20Sopenharmony_ci if (alx->qnapi[i] && alx->qnapi[i]->txq) 5408c2ecf20Sopenharmony_ci alx_free_txring_buf(alx->qnapi[i]->txq); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (alx->qnapi[0] && alx->qnapi[0]->rxq) 5438c2ecf20Sopenharmony_ci alx_free_rxring_buf(alx->qnapi[0]->rxq); 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic int alx_reinit_rings(struct alx_priv *alx) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci alx_free_buffers(alx); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci alx_init_ring_ptrs(alx); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (!alx_refill_rx_ring(alx, GFP_KERNEL)) 5538c2ecf20Sopenharmony_ci return -ENOMEM; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic void alx_add_mc_addr(struct alx_hw *hw, const u8 *addr, u32 *mc_hash) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci u32 crc32, bit, reg; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci crc32 = ether_crc(ETH_ALEN, addr); 5638c2ecf20Sopenharmony_ci reg = (crc32 >> 31) & 0x1; 5648c2ecf20Sopenharmony_ci bit = (crc32 >> 26) & 0x1F; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci mc_hash[reg] |= BIT(bit); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic void __alx_set_rx_mode(struct net_device *netdev) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 5728c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 5738c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 5748c2ecf20Sopenharmony_ci u32 mc_hash[2] = {}; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (!(netdev->flags & IFF_ALLMULTI)) { 5778c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) 5788c2ecf20Sopenharmony_ci alx_add_mc_addr(hw, ha->addr, mc_hash); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_HASH_TBL0, mc_hash[0]); 5818c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_HASH_TBL1, mc_hash[1]); 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci hw->rx_ctrl &= ~(ALX_MAC_CTRL_MULTIALL_EN | ALX_MAC_CTRL_PROMISC_EN); 5858c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) 5868c2ecf20Sopenharmony_ci hw->rx_ctrl |= ALX_MAC_CTRL_PROMISC_EN; 5878c2ecf20Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI) 5888c2ecf20Sopenharmony_ci hw->rx_ctrl |= ALX_MAC_CTRL_MULTIALL_EN; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl); 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic void alx_set_rx_mode(struct net_device *netdev) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci __alx_set_rx_mode(netdev); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int alx_set_mac_address(struct net_device *netdev, void *data) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 6018c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 6028c2ecf20Sopenharmony_ci struct sockaddr *addr = data; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 6058c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (netdev->addr_assign_type & NET_ADDR_RANDOM) 6088c2ecf20Sopenharmony_ci netdev->addr_assign_type ^= NET_ADDR_RANDOM; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); 6118c2ecf20Sopenharmony_ci memcpy(hw->mac_addr, addr->sa_data, netdev->addr_len); 6128c2ecf20Sopenharmony_ci alx_set_macaddr(hw, hw->mac_addr); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int alx_alloc_tx_ring(struct alx_priv *alx, struct alx_tx_queue *txq, 6188c2ecf20Sopenharmony_ci int offset) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci txq->bufs = kcalloc(txq->count, sizeof(struct alx_buffer), GFP_KERNEL); 6218c2ecf20Sopenharmony_ci if (!txq->bufs) 6228c2ecf20Sopenharmony_ci return -ENOMEM; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci txq->tpd = alx->descmem.virt + offset; 6258c2ecf20Sopenharmony_ci txq->tpd_dma = alx->descmem.dma + offset; 6268c2ecf20Sopenharmony_ci offset += sizeof(struct alx_txd) * txq->count; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci return offset; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic int alx_alloc_rx_ring(struct alx_priv *alx, struct alx_rx_queue *rxq, 6328c2ecf20Sopenharmony_ci int offset) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci rxq->bufs = kcalloc(rxq->count, sizeof(struct alx_buffer), GFP_KERNEL); 6358c2ecf20Sopenharmony_ci if (!rxq->bufs) 6368c2ecf20Sopenharmony_ci return -ENOMEM; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci rxq->rrd = alx->descmem.virt + offset; 6398c2ecf20Sopenharmony_ci rxq->rrd_dma = alx->descmem.dma + offset; 6408c2ecf20Sopenharmony_ci offset += sizeof(struct alx_rrd) * rxq->count; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci rxq->rfd = alx->descmem.virt + offset; 6438c2ecf20Sopenharmony_ci rxq->rfd_dma = alx->descmem.dma + offset; 6448c2ecf20Sopenharmony_ci offset += sizeof(struct alx_rfd) * rxq->count; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci return offset; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic int alx_alloc_rings(struct alx_priv *alx) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci int i, offset = 0; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* physical tx/rx ring descriptors 6548c2ecf20Sopenharmony_ci * 6558c2ecf20Sopenharmony_ci * Allocate them as a single chunk because they must not cross a 6568c2ecf20Sopenharmony_ci * 4G boundary (hardware has a single register for high 32 bits 6578c2ecf20Sopenharmony_ci * of addresses only) 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci alx->descmem.size = sizeof(struct alx_txd) * alx->tx_ringsz * 6608c2ecf20Sopenharmony_ci alx->num_txq + 6618c2ecf20Sopenharmony_ci sizeof(struct alx_rrd) * alx->rx_ringsz + 6628c2ecf20Sopenharmony_ci sizeof(struct alx_rfd) * alx->rx_ringsz; 6638c2ecf20Sopenharmony_ci alx->descmem.virt = dma_alloc_coherent(&alx->hw.pdev->dev, 6648c2ecf20Sopenharmony_ci alx->descmem.size, 6658c2ecf20Sopenharmony_ci &alx->descmem.dma, GFP_KERNEL); 6668c2ecf20Sopenharmony_ci if (!alx->descmem.virt) 6678c2ecf20Sopenharmony_ci return -ENOMEM; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* alignment requirements */ 6708c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct alx_txd) % 8); 6718c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct alx_rrd) % 8); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) { 6748c2ecf20Sopenharmony_ci offset = alx_alloc_tx_ring(alx, alx->qnapi[i]->txq, offset); 6758c2ecf20Sopenharmony_ci if (offset < 0) { 6768c2ecf20Sopenharmony_ci netdev_err(alx->dev, "Allocation of tx buffer failed!\n"); 6778c2ecf20Sopenharmony_ci return -ENOMEM; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci offset = alx_alloc_rx_ring(alx, alx->qnapi[0]->rxq, offset); 6828c2ecf20Sopenharmony_ci if (offset < 0) { 6838c2ecf20Sopenharmony_ci netdev_err(alx->dev, "Allocation of rx buffer failed!\n"); 6848c2ecf20Sopenharmony_ci return -ENOMEM; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return 0; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic void alx_free_rings(struct alx_priv *alx) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci int i; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci alx_free_buffers(alx); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) 6978c2ecf20Sopenharmony_ci if (alx->qnapi[i] && alx->qnapi[i]->txq) 6988c2ecf20Sopenharmony_ci kfree(alx->qnapi[i]->txq->bufs); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (alx->qnapi[0] && alx->qnapi[0]->rxq) 7018c2ecf20Sopenharmony_ci kfree(alx->qnapi[0]->rxq->bufs); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (alx->descmem.virt) 7048c2ecf20Sopenharmony_ci dma_free_coherent(&alx->hw.pdev->dev, 7058c2ecf20Sopenharmony_ci alx->descmem.size, 7068c2ecf20Sopenharmony_ci alx->descmem.virt, 7078c2ecf20Sopenharmony_ci alx->descmem.dma); 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic void alx_free_napis(struct alx_priv *alx) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct alx_napi *np; 7138c2ecf20Sopenharmony_ci int i; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) { 7168c2ecf20Sopenharmony_ci np = alx->qnapi[i]; 7178c2ecf20Sopenharmony_ci if (!np) 7188c2ecf20Sopenharmony_ci continue; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci netif_napi_del(&np->napi); 7218c2ecf20Sopenharmony_ci kfree(np->txq); 7228c2ecf20Sopenharmony_ci kfree(np->rxq); 7238c2ecf20Sopenharmony_ci kfree(np); 7248c2ecf20Sopenharmony_ci alx->qnapi[i] = NULL; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic const u16 tx_pidx_reg[] = {ALX_TPD_PRI0_PIDX, ALX_TPD_PRI1_PIDX, 7298c2ecf20Sopenharmony_ci ALX_TPD_PRI2_PIDX, ALX_TPD_PRI3_PIDX}; 7308c2ecf20Sopenharmony_cistatic const u16 tx_cidx_reg[] = {ALX_TPD_PRI0_CIDX, ALX_TPD_PRI1_CIDX, 7318c2ecf20Sopenharmony_ci ALX_TPD_PRI2_CIDX, ALX_TPD_PRI3_CIDX}; 7328c2ecf20Sopenharmony_cistatic const u32 tx_vect_mask[] = {ALX_ISR_TX_Q0, ALX_ISR_TX_Q1, 7338c2ecf20Sopenharmony_ci ALX_ISR_TX_Q2, ALX_ISR_TX_Q3}; 7348c2ecf20Sopenharmony_cistatic const u32 rx_vect_mask[] = {ALX_ISR_RX_Q0, ALX_ISR_RX_Q1, 7358c2ecf20Sopenharmony_ci ALX_ISR_RX_Q2, ALX_ISR_RX_Q3, 7368c2ecf20Sopenharmony_ci ALX_ISR_RX_Q4, ALX_ISR_RX_Q5, 7378c2ecf20Sopenharmony_ci ALX_ISR_RX_Q6, ALX_ISR_RX_Q7}; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic int alx_alloc_napis(struct alx_priv *alx) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci struct alx_napi *np; 7428c2ecf20Sopenharmony_ci struct alx_rx_queue *rxq; 7438c2ecf20Sopenharmony_ci struct alx_tx_queue *txq; 7448c2ecf20Sopenharmony_ci int i; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci alx->int_mask &= ~ALX_ISR_ALL_QUEUES; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* allocate alx_napi structures */ 7498c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) { 7508c2ecf20Sopenharmony_ci np = kzalloc(sizeof(struct alx_napi), GFP_KERNEL); 7518c2ecf20Sopenharmony_ci if (!np) 7528c2ecf20Sopenharmony_ci goto err_out; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci np->alx = alx; 7558c2ecf20Sopenharmony_ci netif_napi_add(alx->dev, &np->napi, alx_poll, 64); 7568c2ecf20Sopenharmony_ci alx->qnapi[i] = np; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* allocate tx queues */ 7608c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) { 7618c2ecf20Sopenharmony_ci np = alx->qnapi[i]; 7628c2ecf20Sopenharmony_ci txq = kzalloc(sizeof(*txq), GFP_KERNEL); 7638c2ecf20Sopenharmony_ci if (!txq) 7648c2ecf20Sopenharmony_ci goto err_out; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci np->txq = txq; 7678c2ecf20Sopenharmony_ci txq->p_reg = tx_pidx_reg[i]; 7688c2ecf20Sopenharmony_ci txq->c_reg = tx_cidx_reg[i]; 7698c2ecf20Sopenharmony_ci txq->queue_idx = i; 7708c2ecf20Sopenharmony_ci txq->count = alx->tx_ringsz; 7718c2ecf20Sopenharmony_ci txq->netdev = alx->dev; 7728c2ecf20Sopenharmony_ci txq->dev = &alx->hw.pdev->dev; 7738c2ecf20Sopenharmony_ci np->vec_mask |= tx_vect_mask[i]; 7748c2ecf20Sopenharmony_ci alx->int_mask |= tx_vect_mask[i]; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* allocate rx queues */ 7788c2ecf20Sopenharmony_ci np = alx->qnapi[0]; 7798c2ecf20Sopenharmony_ci rxq = kzalloc(sizeof(*rxq), GFP_KERNEL); 7808c2ecf20Sopenharmony_ci if (!rxq) 7818c2ecf20Sopenharmony_ci goto err_out; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci np->rxq = rxq; 7848c2ecf20Sopenharmony_ci rxq->np = alx->qnapi[0]; 7858c2ecf20Sopenharmony_ci rxq->queue_idx = 0; 7868c2ecf20Sopenharmony_ci rxq->count = alx->rx_ringsz; 7878c2ecf20Sopenharmony_ci rxq->netdev = alx->dev; 7888c2ecf20Sopenharmony_ci rxq->dev = &alx->hw.pdev->dev; 7898c2ecf20Sopenharmony_ci np->vec_mask |= rx_vect_mask[0]; 7908c2ecf20Sopenharmony_ci alx->int_mask |= rx_vect_mask[0]; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci return 0; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cierr_out: 7958c2ecf20Sopenharmony_ci netdev_err(alx->dev, "error allocating internal structures\n"); 7968c2ecf20Sopenharmony_ci alx_free_napis(alx); 7978c2ecf20Sopenharmony_ci return -ENOMEM; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic const int txq_vec_mapping_shift[] = { 8018c2ecf20Sopenharmony_ci 0, ALX_MSI_MAP_TBL1_TXQ0_SHIFT, 8028c2ecf20Sopenharmony_ci 0, ALX_MSI_MAP_TBL1_TXQ1_SHIFT, 8038c2ecf20Sopenharmony_ci 1, ALX_MSI_MAP_TBL2_TXQ2_SHIFT, 8048c2ecf20Sopenharmony_ci 1, ALX_MSI_MAP_TBL2_TXQ3_SHIFT, 8058c2ecf20Sopenharmony_ci}; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic void alx_config_vector_mapping(struct alx_priv *alx) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 8108c2ecf20Sopenharmony_ci u32 tbl[2] = {0, 0}; 8118c2ecf20Sopenharmony_ci int i, vector, idx, shift; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 8148c2ecf20Sopenharmony_ci /* tx mappings */ 8158c2ecf20Sopenharmony_ci for (i = 0, vector = 1; i < alx->num_txq; i++, vector++) { 8168c2ecf20Sopenharmony_ci idx = txq_vec_mapping_shift[i * 2]; 8178c2ecf20Sopenharmony_ci shift = txq_vec_mapping_shift[i * 2 + 1]; 8188c2ecf20Sopenharmony_ci tbl[idx] |= vector << shift; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* rx mapping */ 8228c2ecf20Sopenharmony_ci tbl[0] |= 1 << ALX_MSI_MAP_TBL1_RXQ0_SHIFT; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_MAP_TBL1, tbl[0]); 8268c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_MAP_TBL2, tbl[1]); 8278c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_ID_MAP, 0); 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic int alx_enable_msix(struct alx_priv *alx) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci int err, num_vec, num_txq, num_rxq; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci num_txq = min_t(int, num_online_cpus(), ALX_MAX_TX_QUEUES); 8358c2ecf20Sopenharmony_ci num_rxq = 1; 8368c2ecf20Sopenharmony_ci num_vec = max_t(int, num_txq, num_rxq) + 1; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci err = pci_alloc_irq_vectors(alx->hw.pdev, num_vec, num_vec, 8398c2ecf20Sopenharmony_ci PCI_IRQ_MSIX); 8408c2ecf20Sopenharmony_ci if (err < 0) { 8418c2ecf20Sopenharmony_ci netdev_warn(alx->dev, "Enabling MSI-X interrupts failed!\n"); 8428c2ecf20Sopenharmony_ci return err; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci alx->num_vec = num_vec; 8468c2ecf20Sopenharmony_ci alx->num_napi = num_vec - 1; 8478c2ecf20Sopenharmony_ci alx->num_txq = num_txq; 8488c2ecf20Sopenharmony_ci alx->num_rxq = num_rxq; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci return err; 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic int alx_request_msix(struct alx_priv *alx) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci struct net_device *netdev = alx->dev; 8568c2ecf20Sopenharmony_ci int i, err, vector = 0, free_vector = 0; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci err = request_irq(pci_irq_vector(alx->hw.pdev, 0), alx_intr_msix_misc, 8598c2ecf20Sopenharmony_ci 0, netdev->name, alx); 8608c2ecf20Sopenharmony_ci if (err) 8618c2ecf20Sopenharmony_ci goto out_err; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) { 8648c2ecf20Sopenharmony_ci struct alx_napi *np = alx->qnapi[i]; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci vector++; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (np->txq && np->rxq) 8698c2ecf20Sopenharmony_ci sprintf(np->irq_lbl, "%s-TxRx-%u", netdev->name, 8708c2ecf20Sopenharmony_ci np->txq->queue_idx); 8718c2ecf20Sopenharmony_ci else if (np->txq) 8728c2ecf20Sopenharmony_ci sprintf(np->irq_lbl, "%s-tx-%u", netdev->name, 8738c2ecf20Sopenharmony_ci np->txq->queue_idx); 8748c2ecf20Sopenharmony_ci else if (np->rxq) 8758c2ecf20Sopenharmony_ci sprintf(np->irq_lbl, "%s-rx-%u", netdev->name, 8768c2ecf20Sopenharmony_ci np->rxq->queue_idx); 8778c2ecf20Sopenharmony_ci else 8788c2ecf20Sopenharmony_ci sprintf(np->irq_lbl, "%s-unused", netdev->name); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci np->vec_idx = vector; 8818c2ecf20Sopenharmony_ci err = request_irq(pci_irq_vector(alx->hw.pdev, vector), 8828c2ecf20Sopenharmony_ci alx_intr_msix_ring, 0, np->irq_lbl, np); 8838c2ecf20Sopenharmony_ci if (err) 8848c2ecf20Sopenharmony_ci goto out_free; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci return 0; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ciout_free: 8898c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(alx->hw.pdev, free_vector++), alx); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci vector--; 8928c2ecf20Sopenharmony_ci for (i = 0; i < vector; i++) 8938c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(alx->hw.pdev,free_vector++), 8948c2ecf20Sopenharmony_ci alx->qnapi[i]); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ciout_err: 8978c2ecf20Sopenharmony_ci return err; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic int alx_init_intr(struct alx_priv *alx) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci int ret; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci ret = pci_alloc_irq_vectors(alx->hw.pdev, 1, 1, 9058c2ecf20Sopenharmony_ci PCI_IRQ_MSI | PCI_IRQ_LEGACY); 9068c2ecf20Sopenharmony_ci if (ret < 0) 9078c2ecf20Sopenharmony_ci return ret; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci alx->num_vec = 1; 9108c2ecf20Sopenharmony_ci alx->num_napi = 1; 9118c2ecf20Sopenharmony_ci alx->num_txq = 1; 9128c2ecf20Sopenharmony_ci alx->num_rxq = 1; 9138c2ecf20Sopenharmony_ci return 0; 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_cistatic void alx_irq_enable(struct alx_priv *alx) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 9198c2ecf20Sopenharmony_ci int i; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* level-1 interrupt switch */ 9228c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, 0); 9238c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 9248c2ecf20Sopenharmony_ci alx_post_write(hw); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 9278c2ecf20Sopenharmony_ci /* enable all msix irqs */ 9288c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_vec; i++) 9298c2ecf20Sopenharmony_ci alx_mask_msix(hw, i, false); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic void alx_irq_disable(struct alx_priv *alx) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 9368c2ecf20Sopenharmony_ci int i; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, ALX_ISR_DIS); 9398c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, 0); 9408c2ecf20Sopenharmony_ci alx_post_write(hw); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 9438c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_vec; i++) { 9448c2ecf20Sopenharmony_ci alx_mask_msix(hw, i, true); 9458c2ecf20Sopenharmony_ci synchronize_irq(pci_irq_vector(alx->hw.pdev, i)); 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci } else { 9488c2ecf20Sopenharmony_ci synchronize_irq(pci_irq_vector(alx->hw.pdev, 0)); 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic int alx_realloc_resources(struct alx_priv *alx) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci int err; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci alx_free_rings(alx); 9578c2ecf20Sopenharmony_ci alx_free_napis(alx); 9588c2ecf20Sopenharmony_ci pci_free_irq_vectors(alx->hw.pdev); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci err = alx_init_intr(alx); 9618c2ecf20Sopenharmony_ci if (err) 9628c2ecf20Sopenharmony_ci return err; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci err = alx_alloc_napis(alx); 9658c2ecf20Sopenharmony_ci if (err) 9668c2ecf20Sopenharmony_ci return err; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci err = alx_alloc_rings(alx); 9698c2ecf20Sopenharmony_ci if (err) 9708c2ecf20Sopenharmony_ci return err; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci return 0; 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic int alx_request_irq(struct alx_priv *alx) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct pci_dev *pdev = alx->hw.pdev; 9788c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 9798c2ecf20Sopenharmony_ci int err; 9808c2ecf20Sopenharmony_ci u32 msi_ctrl; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci msi_ctrl = (hw->imt >> 1) << ALX_MSI_RETRANS_TM_SHIFT; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 9858c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, msi_ctrl); 9868c2ecf20Sopenharmony_ci err = alx_request_msix(alx); 9878c2ecf20Sopenharmony_ci if (!err) 9888c2ecf20Sopenharmony_ci goto out; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* msix request failed, realloc resources */ 9918c2ecf20Sopenharmony_ci err = alx_realloc_resources(alx); 9928c2ecf20Sopenharmony_ci if (err) 9938c2ecf20Sopenharmony_ci goto out; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci if (alx->hw.pdev->msi_enabled) { 9978c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, 9988c2ecf20Sopenharmony_ci msi_ctrl | ALX_MSI_MASK_SEL_LINE); 9998c2ecf20Sopenharmony_ci err = request_irq(pci_irq_vector(pdev, 0), alx_intr_msi, 0, 10008c2ecf20Sopenharmony_ci alx->dev->name, alx); 10018c2ecf20Sopenharmony_ci if (!err) 10028c2ecf20Sopenharmony_ci goto out; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* fall back to legacy interrupt */ 10058c2ecf20Sopenharmony_ci pci_free_irq_vectors(alx->hw.pdev); 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, 0); 10098c2ecf20Sopenharmony_ci err = request_irq(pci_irq_vector(pdev, 0), alx_intr_legacy, IRQF_SHARED, 10108c2ecf20Sopenharmony_ci alx->dev->name, alx); 10118c2ecf20Sopenharmony_ciout: 10128c2ecf20Sopenharmony_ci if (!err) 10138c2ecf20Sopenharmony_ci alx_config_vector_mapping(alx); 10148c2ecf20Sopenharmony_ci else 10158c2ecf20Sopenharmony_ci netdev_err(alx->dev, "IRQ registration failed!\n"); 10168c2ecf20Sopenharmony_ci return err; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic void alx_free_irq(struct alx_priv *alx) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci struct pci_dev *pdev = alx->hw.pdev; 10228c2ecf20Sopenharmony_ci int i; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(pdev, 0), alx); 10258c2ecf20Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 10268c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) 10278c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(pdev, i + 1), alx->qnapi[i]); 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci pci_free_irq_vectors(pdev); 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cistatic int alx_identify_hw(struct alx_priv *alx) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 10368c2ecf20Sopenharmony_ci int rev = alx_hw_revision(hw); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (rev > ALX_REV_C0) 10398c2ecf20Sopenharmony_ci return -EINVAL; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci hw->max_dma_chnl = rev >= ALX_REV_B0 ? 4 : 2; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci return 0; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic int alx_init_sw(struct alx_priv *alx) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci struct pci_dev *pdev = alx->hw.pdev; 10498c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 10508c2ecf20Sopenharmony_ci int err; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci err = alx_identify_hw(alx); 10538c2ecf20Sopenharmony_ci if (err) { 10548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unrecognized chip, aborting\n"); 10558c2ecf20Sopenharmony_ci return err; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci alx->hw.lnk_patch = 10598c2ecf20Sopenharmony_ci pdev->device == ALX_DEV_ID_AR8161 && 10608c2ecf20Sopenharmony_ci pdev->subsystem_vendor == PCI_VENDOR_ID_ATTANSIC && 10618c2ecf20Sopenharmony_ci pdev->subsystem_device == 0x0091 && 10628c2ecf20Sopenharmony_ci pdev->revision == 0; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci hw->smb_timer = 400; 10658c2ecf20Sopenharmony_ci hw->mtu = alx->dev->mtu; 10668c2ecf20Sopenharmony_ci alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu); 10678c2ecf20Sopenharmony_ci /* MTU range: 34 - 9256 */ 10688c2ecf20Sopenharmony_ci alx->dev->min_mtu = 34; 10698c2ecf20Sopenharmony_ci alx->dev->max_mtu = ALX_MAX_FRAME_LEN(ALX_MAX_FRAME_SIZE); 10708c2ecf20Sopenharmony_ci alx->tx_ringsz = 256; 10718c2ecf20Sopenharmony_ci alx->rx_ringsz = 512; 10728c2ecf20Sopenharmony_ci hw->imt = 200; 10738c2ecf20Sopenharmony_ci alx->int_mask = ALX_ISR_MISC; 10748c2ecf20Sopenharmony_ci hw->dma_chnl = hw->max_dma_chnl; 10758c2ecf20Sopenharmony_ci hw->ith_tpd = alx->tx_ringsz / 3; 10768c2ecf20Sopenharmony_ci hw->link_speed = SPEED_UNKNOWN; 10778c2ecf20Sopenharmony_ci hw->duplex = DUPLEX_UNKNOWN; 10788c2ecf20Sopenharmony_ci hw->adv_cfg = ADVERTISED_Autoneg | 10798c2ecf20Sopenharmony_ci ADVERTISED_10baseT_Half | 10808c2ecf20Sopenharmony_ci ADVERTISED_10baseT_Full | 10818c2ecf20Sopenharmony_ci ADVERTISED_100baseT_Full | 10828c2ecf20Sopenharmony_ci ADVERTISED_100baseT_Half | 10838c2ecf20Sopenharmony_ci ADVERTISED_1000baseT_Full; 10848c2ecf20Sopenharmony_ci hw->flowctrl = ALX_FC_ANEG | ALX_FC_RX | ALX_FC_TX; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci hw->rx_ctrl = ALX_MAC_CTRL_WOLSPED_SWEN | 10878c2ecf20Sopenharmony_ci ALX_MAC_CTRL_MHASH_ALG_HI5B | 10888c2ecf20Sopenharmony_ci ALX_MAC_CTRL_BRD_EN | 10898c2ecf20Sopenharmony_ci ALX_MAC_CTRL_PCRCE | 10908c2ecf20Sopenharmony_ci ALX_MAC_CTRL_CRCE | 10918c2ecf20Sopenharmony_ci ALX_MAC_CTRL_RXFC_EN | 10928c2ecf20Sopenharmony_ci ALX_MAC_CTRL_TXFC_EN | 10938c2ecf20Sopenharmony_ci 7 << ALX_MAC_CTRL_PRMBLEN_SHIFT; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci return err; 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic netdev_features_t alx_fix_features(struct net_device *netdev, 11008c2ecf20Sopenharmony_ci netdev_features_t features) 11018c2ecf20Sopenharmony_ci{ 11028c2ecf20Sopenharmony_ci if (netdev->mtu > ALX_MAX_TSO_PKT_SIZE) 11038c2ecf20Sopenharmony_ci features &= ~(NETIF_F_TSO | NETIF_F_TSO6); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci return features; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic void alx_netif_stop(struct alx_priv *alx) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci int i; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci netif_trans_update(alx->dev); 11138c2ecf20Sopenharmony_ci if (netif_carrier_ok(alx->dev)) { 11148c2ecf20Sopenharmony_ci netif_carrier_off(alx->dev); 11158c2ecf20Sopenharmony_ci netif_tx_disable(alx->dev); 11168c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) 11178c2ecf20Sopenharmony_ci napi_disable(&alx->qnapi[i]->napi); 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic void alx_halt(struct alx_priv *alx) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci alx_netif_stop(alx); 11268c2ecf20Sopenharmony_ci hw->link_speed = SPEED_UNKNOWN; 11278c2ecf20Sopenharmony_ci hw->duplex = DUPLEX_UNKNOWN; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci alx_reset_mac(hw); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci /* disable l0s/l1 */ 11328c2ecf20Sopenharmony_ci alx_enable_aspm(hw, false, false); 11338c2ecf20Sopenharmony_ci alx_irq_disable(alx); 11348c2ecf20Sopenharmony_ci alx_free_buffers(alx); 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic void alx_configure(struct alx_priv *alx) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci alx_configure_basic(hw); 11428c2ecf20Sopenharmony_ci alx_disable_rss(hw); 11438c2ecf20Sopenharmony_ci __alx_set_rx_mode(alx->dev); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl); 11468c2ecf20Sopenharmony_ci} 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_cistatic void alx_activate(struct alx_priv *alx) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci /* hardware setting lost, restore it */ 11518c2ecf20Sopenharmony_ci alx_reinit_rings(alx); 11528c2ecf20Sopenharmony_ci alx_configure(alx); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* clear old interrupts */ 11558c2ecf20Sopenharmony_ci alx_write_mem32(&alx->hw, ALX_ISR, ~(u32)ALX_ISR_DIS); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci alx_irq_enable(alx); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci alx_schedule_link_check(alx); 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic void alx_reinit(struct alx_priv *alx) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci ASSERT_RTNL(); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci alx_halt(alx); 11678c2ecf20Sopenharmony_ci alx_activate(alx); 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_cistatic int alx_change_mtu(struct net_device *netdev, int mtu) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 11738c2ecf20Sopenharmony_ci int max_frame = ALX_MAX_FRAME_LEN(mtu); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci netdev->mtu = mtu; 11768c2ecf20Sopenharmony_ci alx->hw.mtu = mtu; 11778c2ecf20Sopenharmony_ci alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE); 11788c2ecf20Sopenharmony_ci netdev_update_features(netdev); 11798c2ecf20Sopenharmony_ci if (netif_running(netdev)) 11808c2ecf20Sopenharmony_ci alx_reinit(alx); 11818c2ecf20Sopenharmony_ci return 0; 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cistatic void alx_netif_start(struct alx_priv *alx) 11858c2ecf20Sopenharmony_ci{ 11868c2ecf20Sopenharmony_ci int i; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci netif_tx_wake_all_queues(alx->dev); 11898c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) 11908c2ecf20Sopenharmony_ci napi_enable(&alx->qnapi[i]->napi); 11918c2ecf20Sopenharmony_ci netif_carrier_on(alx->dev); 11928c2ecf20Sopenharmony_ci} 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic int __alx_open(struct alx_priv *alx, bool resume) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci int err; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci err = alx_enable_msix(alx); 11998c2ecf20Sopenharmony_ci if (err < 0) { 12008c2ecf20Sopenharmony_ci err = alx_init_intr(alx); 12018c2ecf20Sopenharmony_ci if (err) 12028c2ecf20Sopenharmony_ci return err; 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (!resume) 12068c2ecf20Sopenharmony_ci netif_carrier_off(alx->dev); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci err = alx_alloc_napis(alx); 12098c2ecf20Sopenharmony_ci if (err) 12108c2ecf20Sopenharmony_ci goto out_disable_adv_intr; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci err = alx_alloc_rings(alx); 12138c2ecf20Sopenharmony_ci if (err) 12148c2ecf20Sopenharmony_ci goto out_free_rings; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci alx_configure(alx); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci err = alx_request_irq(alx); 12198c2ecf20Sopenharmony_ci if (err) 12208c2ecf20Sopenharmony_ci goto out_free_rings; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* must be called after alx_request_irq because the chip stops working 12238c2ecf20Sopenharmony_ci * if we copy the dma addresses in alx_init_ring_ptrs twice when 12248c2ecf20Sopenharmony_ci * requesting msi-x interrupts failed 12258c2ecf20Sopenharmony_ci */ 12268c2ecf20Sopenharmony_ci alx_reinit_rings(alx); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci netif_set_real_num_tx_queues(alx->dev, alx->num_txq); 12298c2ecf20Sopenharmony_ci netif_set_real_num_rx_queues(alx->dev, alx->num_rxq); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci /* clear old interrupts */ 12328c2ecf20Sopenharmony_ci alx_write_mem32(&alx->hw, ALX_ISR, ~(u32)ALX_ISR_DIS); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci alx_irq_enable(alx); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (!resume) 12378c2ecf20Sopenharmony_ci netif_tx_start_all_queues(alx->dev); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci alx_schedule_link_check(alx); 12408c2ecf20Sopenharmony_ci return 0; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ciout_free_rings: 12438c2ecf20Sopenharmony_ci alx_free_rings(alx); 12448c2ecf20Sopenharmony_ci alx_free_napis(alx); 12458c2ecf20Sopenharmony_ciout_disable_adv_intr: 12468c2ecf20Sopenharmony_ci pci_free_irq_vectors(alx->hw.pdev); 12478c2ecf20Sopenharmony_ci return err; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic void __alx_stop(struct alx_priv *alx) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci alx_free_irq(alx); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci cancel_work_sync(&alx->link_check_wk); 12558c2ecf20Sopenharmony_ci cancel_work_sync(&alx->reset_wk); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci alx_halt(alx); 12588c2ecf20Sopenharmony_ci alx_free_rings(alx); 12598c2ecf20Sopenharmony_ci alx_free_napis(alx); 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic const char *alx_speed_desc(struct alx_hw *hw) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci switch (alx_speed_to_ethadv(hw->link_speed, hw->duplex)) { 12658c2ecf20Sopenharmony_ci case ADVERTISED_1000baseT_Full: 12668c2ecf20Sopenharmony_ci return "1 Gbps Full"; 12678c2ecf20Sopenharmony_ci case ADVERTISED_100baseT_Full: 12688c2ecf20Sopenharmony_ci return "100 Mbps Full"; 12698c2ecf20Sopenharmony_ci case ADVERTISED_100baseT_Half: 12708c2ecf20Sopenharmony_ci return "100 Mbps Half"; 12718c2ecf20Sopenharmony_ci case ADVERTISED_10baseT_Full: 12728c2ecf20Sopenharmony_ci return "10 Mbps Full"; 12738c2ecf20Sopenharmony_ci case ADVERTISED_10baseT_Half: 12748c2ecf20Sopenharmony_ci return "10 Mbps Half"; 12758c2ecf20Sopenharmony_ci default: 12768c2ecf20Sopenharmony_ci return "Unknown speed"; 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_cistatic void alx_check_link(struct alx_priv *alx) 12818c2ecf20Sopenharmony_ci{ 12828c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 12838c2ecf20Sopenharmony_ci unsigned long flags; 12848c2ecf20Sopenharmony_ci int old_speed; 12858c2ecf20Sopenharmony_ci int err; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci /* clear PHY internal interrupt status, otherwise the main 12888c2ecf20Sopenharmony_ci * interrupt status will be asserted forever 12898c2ecf20Sopenharmony_ci */ 12908c2ecf20Sopenharmony_ci alx_clear_phy_intr(hw); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci old_speed = hw->link_speed; 12938c2ecf20Sopenharmony_ci err = alx_read_phy_link(hw); 12948c2ecf20Sopenharmony_ci if (err < 0) 12958c2ecf20Sopenharmony_ci goto reset; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci spin_lock_irqsave(&alx->irq_lock, flags); 12988c2ecf20Sopenharmony_ci alx->int_mask |= ALX_ISR_PHY; 12998c2ecf20Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 13008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&alx->irq_lock, flags); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci if (old_speed == hw->link_speed) 13038c2ecf20Sopenharmony_ci return; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci if (hw->link_speed != SPEED_UNKNOWN) { 13068c2ecf20Sopenharmony_ci netif_info(alx, link, alx->dev, 13078c2ecf20Sopenharmony_ci "NIC Up: %s\n", alx_speed_desc(hw)); 13088c2ecf20Sopenharmony_ci alx_post_phy_link(hw); 13098c2ecf20Sopenharmony_ci alx_enable_aspm(hw, true, true); 13108c2ecf20Sopenharmony_ci alx_start_mac(hw); 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (old_speed == SPEED_UNKNOWN) 13138c2ecf20Sopenharmony_ci alx_netif_start(alx); 13148c2ecf20Sopenharmony_ci } else { 13158c2ecf20Sopenharmony_ci /* link is now down */ 13168c2ecf20Sopenharmony_ci alx_netif_stop(alx); 13178c2ecf20Sopenharmony_ci netif_info(alx, link, alx->dev, "Link Down\n"); 13188c2ecf20Sopenharmony_ci err = alx_reset_mac(hw); 13198c2ecf20Sopenharmony_ci if (err) 13208c2ecf20Sopenharmony_ci goto reset; 13218c2ecf20Sopenharmony_ci alx_irq_disable(alx); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci /* MAC reset causes all HW settings to be lost, restore all */ 13248c2ecf20Sopenharmony_ci err = alx_reinit_rings(alx); 13258c2ecf20Sopenharmony_ci if (err) 13268c2ecf20Sopenharmony_ci goto reset; 13278c2ecf20Sopenharmony_ci alx_configure(alx); 13288c2ecf20Sopenharmony_ci alx_enable_aspm(hw, false, true); 13298c2ecf20Sopenharmony_ci alx_post_phy_link(hw); 13308c2ecf20Sopenharmony_ci alx_irq_enable(alx); 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci return; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cireset: 13368c2ecf20Sopenharmony_ci alx_schedule_reset(alx); 13378c2ecf20Sopenharmony_ci} 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic int alx_open(struct net_device *netdev) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci return __alx_open(netdev_priv(netdev), false); 13428c2ecf20Sopenharmony_ci} 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_cistatic int alx_stop(struct net_device *netdev) 13458c2ecf20Sopenharmony_ci{ 13468c2ecf20Sopenharmony_ci __alx_stop(netdev_priv(netdev)); 13478c2ecf20Sopenharmony_ci return 0; 13488c2ecf20Sopenharmony_ci} 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_cistatic void alx_link_check(struct work_struct *work) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci struct alx_priv *alx; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci alx = container_of(work, struct alx_priv, link_check_wk); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci rtnl_lock(); 13578c2ecf20Sopenharmony_ci alx_check_link(alx); 13588c2ecf20Sopenharmony_ci rtnl_unlock(); 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic void alx_reset(struct work_struct *work) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci struct alx_priv *alx = container_of(work, struct alx_priv, reset_wk); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci rtnl_lock(); 13668c2ecf20Sopenharmony_ci alx_reinit(alx); 13678c2ecf20Sopenharmony_ci rtnl_unlock(); 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic int alx_tpd_req(struct sk_buff *skb) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci int num; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci num = skb_shinfo(skb)->nr_frags + 1; 13758c2ecf20Sopenharmony_ci /* we need one extra descriptor for LSOv2 */ 13768c2ecf20Sopenharmony_ci if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) 13778c2ecf20Sopenharmony_ci num++; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci return num; 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_cistatic int alx_tx_csum(struct sk_buff *skb, struct alx_txd *first) 13838c2ecf20Sopenharmony_ci{ 13848c2ecf20Sopenharmony_ci u8 cso, css; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 13878c2ecf20Sopenharmony_ci return 0; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci cso = skb_checksum_start_offset(skb); 13908c2ecf20Sopenharmony_ci if (cso & 1) 13918c2ecf20Sopenharmony_ci return -EINVAL; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci css = cso + skb->csum_offset; 13948c2ecf20Sopenharmony_ci first->word1 |= cpu_to_le32((cso >> 1) << TPD_CXSUMSTART_SHIFT); 13958c2ecf20Sopenharmony_ci first->word1 |= cpu_to_le32((css >> 1) << TPD_CXSUMOFFSET_SHIFT); 13968c2ecf20Sopenharmony_ci first->word1 |= cpu_to_le32(1 << TPD_CXSUM_EN_SHIFT); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci return 0; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic int alx_tso(struct sk_buff *skb, struct alx_txd *first) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci int err; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 14068c2ecf20Sopenharmony_ci return 0; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci if (!skb_is_gso(skb)) 14098c2ecf20Sopenharmony_ci return 0; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci err = skb_cow_head(skb, 0); 14128c2ecf20Sopenharmony_ci if (err < 0) 14138c2ecf20Sopenharmony_ci return err; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 14168c2ecf20Sopenharmony_ci struct iphdr *iph = ip_hdr(skb); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci iph->check = 0; 14198c2ecf20Sopenharmony_ci tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, 14208c2ecf20Sopenharmony_ci 0, IPPROTO_TCP, 0); 14218c2ecf20Sopenharmony_ci first->word1 |= 1 << TPD_IPV4_SHIFT; 14228c2ecf20Sopenharmony_ci } else if (skb_is_gso_v6(skb)) { 14238c2ecf20Sopenharmony_ci tcp_v6_gso_csum_prep(skb); 14248c2ecf20Sopenharmony_ci /* LSOv2: the first TPD only provides the packet length */ 14258c2ecf20Sopenharmony_ci first->adrl.l.pkt_len = skb->len; 14268c2ecf20Sopenharmony_ci first->word1 |= 1 << TPD_LSO_V2_SHIFT; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci first->word1 |= 1 << TPD_LSO_EN_SHIFT; 14308c2ecf20Sopenharmony_ci first->word1 |= (skb_transport_offset(skb) & 14318c2ecf20Sopenharmony_ci TPD_L4HDROFFSET_MASK) << TPD_L4HDROFFSET_SHIFT; 14328c2ecf20Sopenharmony_ci first->word1 |= (skb_shinfo(skb)->gso_size & 14338c2ecf20Sopenharmony_ci TPD_MSS_MASK) << TPD_MSS_SHIFT; 14348c2ecf20Sopenharmony_ci return 1; 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_cistatic int alx_map_tx_skb(struct alx_tx_queue *txq, struct sk_buff *skb) 14388c2ecf20Sopenharmony_ci{ 14398c2ecf20Sopenharmony_ci struct alx_txd *tpd, *first_tpd; 14408c2ecf20Sopenharmony_ci dma_addr_t dma; 14418c2ecf20Sopenharmony_ci int maplen, f, first_idx = txq->write_idx; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci first_tpd = &txq->tpd[txq->write_idx]; 14448c2ecf20Sopenharmony_ci tpd = first_tpd; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci if (tpd->word1 & (1 << TPD_LSO_V2_SHIFT)) { 14478c2ecf20Sopenharmony_ci if (++txq->write_idx == txq->count) 14488c2ecf20Sopenharmony_ci txq->write_idx = 0; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci tpd = &txq->tpd[txq->write_idx]; 14518c2ecf20Sopenharmony_ci tpd->len = first_tpd->len; 14528c2ecf20Sopenharmony_ci tpd->vlan_tag = first_tpd->vlan_tag; 14538c2ecf20Sopenharmony_ci tpd->word1 = first_tpd->word1; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci maplen = skb_headlen(skb); 14578c2ecf20Sopenharmony_ci dma = dma_map_single(txq->dev, skb->data, maplen, 14588c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 14598c2ecf20Sopenharmony_ci if (dma_mapping_error(txq->dev, dma)) 14608c2ecf20Sopenharmony_ci goto err_dma; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen); 14638c2ecf20Sopenharmony_ci dma_unmap_addr_set(&txq->bufs[txq->write_idx], dma, dma); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci tpd->adrl.addr = cpu_to_le64(dma); 14668c2ecf20Sopenharmony_ci tpd->len = cpu_to_le16(maplen); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) { 14698c2ecf20Sopenharmony_ci skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (++txq->write_idx == txq->count) 14728c2ecf20Sopenharmony_ci txq->write_idx = 0; 14738c2ecf20Sopenharmony_ci tpd = &txq->tpd[txq->write_idx]; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci tpd->word1 = first_tpd->word1; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci maplen = skb_frag_size(frag); 14788c2ecf20Sopenharmony_ci dma = skb_frag_dma_map(txq->dev, frag, 0, 14798c2ecf20Sopenharmony_ci maplen, DMA_TO_DEVICE); 14808c2ecf20Sopenharmony_ci if (dma_mapping_error(txq->dev, dma)) 14818c2ecf20Sopenharmony_ci goto err_dma; 14828c2ecf20Sopenharmony_ci dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen); 14838c2ecf20Sopenharmony_ci dma_unmap_addr_set(&txq->bufs[txq->write_idx], dma, dma); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci tpd->adrl.addr = cpu_to_le64(dma); 14868c2ecf20Sopenharmony_ci tpd->len = cpu_to_le16(maplen); 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci /* last TPD, set EOP flag and store skb */ 14908c2ecf20Sopenharmony_ci tpd->word1 |= cpu_to_le32(1 << TPD_EOP_SHIFT); 14918c2ecf20Sopenharmony_ci txq->bufs[txq->write_idx].skb = skb; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci if (++txq->write_idx == txq->count) 14948c2ecf20Sopenharmony_ci txq->write_idx = 0; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci return 0; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_cierr_dma: 14998c2ecf20Sopenharmony_ci f = first_idx; 15008c2ecf20Sopenharmony_ci while (f != txq->write_idx) { 15018c2ecf20Sopenharmony_ci alx_free_txbuf(txq, f); 15028c2ecf20Sopenharmony_ci if (++f == txq->count) 15038c2ecf20Sopenharmony_ci f = 0; 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci return -ENOMEM; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic netdev_tx_t alx_start_xmit_ring(struct sk_buff *skb, 15098c2ecf20Sopenharmony_ci struct alx_tx_queue *txq) 15108c2ecf20Sopenharmony_ci{ 15118c2ecf20Sopenharmony_ci struct alx_priv *alx; 15128c2ecf20Sopenharmony_ci struct alx_txd *first; 15138c2ecf20Sopenharmony_ci int tso; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci alx = netdev_priv(txq->netdev); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (alx_tpd_avail(txq) < alx_tpd_req(skb)) { 15188c2ecf20Sopenharmony_ci netif_tx_stop_queue(alx_get_tx_queue(txq)); 15198c2ecf20Sopenharmony_ci goto drop; 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci first = &txq->tpd[txq->write_idx]; 15238c2ecf20Sopenharmony_ci memset(first, 0, sizeof(*first)); 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci tso = alx_tso(skb, first); 15268c2ecf20Sopenharmony_ci if (tso < 0) 15278c2ecf20Sopenharmony_ci goto drop; 15288c2ecf20Sopenharmony_ci else if (!tso && alx_tx_csum(skb, first)) 15298c2ecf20Sopenharmony_ci goto drop; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci if (alx_map_tx_skb(txq, skb) < 0) 15328c2ecf20Sopenharmony_ci goto drop; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci netdev_tx_sent_queue(alx_get_tx_queue(txq), skb->len); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* flush updates before updating hardware */ 15378c2ecf20Sopenharmony_ci wmb(); 15388c2ecf20Sopenharmony_ci alx_write_mem16(&alx->hw, txq->p_reg, txq->write_idx); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci if (alx_tpd_avail(txq) < txq->count / 8) 15418c2ecf20Sopenharmony_ci netif_tx_stop_queue(alx_get_tx_queue(txq)); 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_cidrop: 15468c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 15478c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 15488c2ecf20Sopenharmony_ci} 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_cistatic netdev_tx_t alx_start_xmit(struct sk_buff *skb, 15518c2ecf20Sopenharmony_ci struct net_device *netdev) 15528c2ecf20Sopenharmony_ci{ 15538c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 15548c2ecf20Sopenharmony_ci return alx_start_xmit_ring(skb, alx_tx_queue_mapping(alx, skb)); 15558c2ecf20Sopenharmony_ci} 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_cistatic void alx_tx_timeout(struct net_device *dev, unsigned int txqueue) 15588c2ecf20Sopenharmony_ci{ 15598c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(dev); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci alx_schedule_reset(alx); 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic int alx_mdio_read(struct net_device *netdev, 15658c2ecf20Sopenharmony_ci int prtad, int devad, u16 addr) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 15688c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 15698c2ecf20Sopenharmony_ci u16 val; 15708c2ecf20Sopenharmony_ci int err; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci if (prtad != hw->mdio.prtad) 15738c2ecf20Sopenharmony_ci return -EINVAL; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci if (devad == MDIO_DEVAD_NONE) 15768c2ecf20Sopenharmony_ci err = alx_read_phy_reg(hw, addr, &val); 15778c2ecf20Sopenharmony_ci else 15788c2ecf20Sopenharmony_ci err = alx_read_phy_ext(hw, devad, addr, &val); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci if (err) 15818c2ecf20Sopenharmony_ci return err; 15828c2ecf20Sopenharmony_ci return val; 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_cistatic int alx_mdio_write(struct net_device *netdev, 15868c2ecf20Sopenharmony_ci int prtad, int devad, u16 addr, u16 val) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 15898c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (prtad != hw->mdio.prtad) 15928c2ecf20Sopenharmony_ci return -EINVAL; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci if (devad == MDIO_DEVAD_NONE) 15958c2ecf20Sopenharmony_ci return alx_write_phy_reg(hw, addr, val); 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci return alx_write_phy_ext(hw, devad, addr, val); 15988c2ecf20Sopenharmony_ci} 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_cistatic int alx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 16018c2ecf20Sopenharmony_ci{ 16028c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci if (!netif_running(netdev)) 16058c2ecf20Sopenharmony_ci return -EAGAIN; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci return mdio_mii_ioctl(&alx->hw.mdio, if_mii(ifr), cmd); 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 16118c2ecf20Sopenharmony_cistatic void alx_poll_controller(struct net_device *netdev) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 16148c2ecf20Sopenharmony_ci int i; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 16178c2ecf20Sopenharmony_ci alx_intr_msix_misc(0, alx); 16188c2ecf20Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) 16198c2ecf20Sopenharmony_ci alx_intr_msix_ring(0, alx->qnapi[i]); 16208c2ecf20Sopenharmony_ci } else if (alx->hw.pdev->msi_enabled) 16218c2ecf20Sopenharmony_ci alx_intr_msi(0, alx); 16228c2ecf20Sopenharmony_ci else 16238c2ecf20Sopenharmony_ci alx_intr_legacy(0, alx); 16248c2ecf20Sopenharmony_ci} 16258c2ecf20Sopenharmony_ci#endif 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_cistatic void alx_get_stats64(struct net_device *dev, 16288c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *net_stats) 16298c2ecf20Sopenharmony_ci{ 16308c2ecf20Sopenharmony_ci struct alx_priv *alx = netdev_priv(dev); 16318c2ecf20Sopenharmony_ci struct alx_hw_stats *hw_stats = &alx->hw.stats; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci spin_lock(&alx->stats_lock); 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci alx_update_hw_stats(&alx->hw); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci net_stats->tx_bytes = hw_stats->tx_byte_cnt; 16388c2ecf20Sopenharmony_ci net_stats->rx_bytes = hw_stats->rx_byte_cnt; 16398c2ecf20Sopenharmony_ci net_stats->multicast = hw_stats->rx_mcast; 16408c2ecf20Sopenharmony_ci net_stats->collisions = hw_stats->tx_single_col + 16418c2ecf20Sopenharmony_ci hw_stats->tx_multi_col + 16428c2ecf20Sopenharmony_ci hw_stats->tx_late_col + 16438c2ecf20Sopenharmony_ci hw_stats->tx_abort_col; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci net_stats->rx_errors = hw_stats->rx_frag + 16468c2ecf20Sopenharmony_ci hw_stats->rx_fcs_err + 16478c2ecf20Sopenharmony_ci hw_stats->rx_len_err + 16488c2ecf20Sopenharmony_ci hw_stats->rx_ov_sz + 16498c2ecf20Sopenharmony_ci hw_stats->rx_ov_rrd + 16508c2ecf20Sopenharmony_ci hw_stats->rx_align_err + 16518c2ecf20Sopenharmony_ci hw_stats->rx_ov_rxf; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci net_stats->rx_fifo_errors = hw_stats->rx_ov_rxf; 16548c2ecf20Sopenharmony_ci net_stats->rx_length_errors = hw_stats->rx_len_err; 16558c2ecf20Sopenharmony_ci net_stats->rx_crc_errors = hw_stats->rx_fcs_err; 16568c2ecf20Sopenharmony_ci net_stats->rx_frame_errors = hw_stats->rx_align_err; 16578c2ecf20Sopenharmony_ci net_stats->rx_dropped = hw_stats->rx_ov_rrd; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci net_stats->tx_errors = hw_stats->tx_late_col + 16608c2ecf20Sopenharmony_ci hw_stats->tx_abort_col + 16618c2ecf20Sopenharmony_ci hw_stats->tx_underrun + 16628c2ecf20Sopenharmony_ci hw_stats->tx_trunc; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci net_stats->tx_aborted_errors = hw_stats->tx_abort_col; 16658c2ecf20Sopenharmony_ci net_stats->tx_fifo_errors = hw_stats->tx_underrun; 16668c2ecf20Sopenharmony_ci net_stats->tx_window_errors = hw_stats->tx_late_col; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci net_stats->tx_packets = hw_stats->tx_ok + net_stats->tx_errors; 16698c2ecf20Sopenharmony_ci net_stats->rx_packets = hw_stats->rx_ok + net_stats->rx_errors; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci spin_unlock(&alx->stats_lock); 16728c2ecf20Sopenharmony_ci} 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_cistatic const struct net_device_ops alx_netdev_ops = { 16758c2ecf20Sopenharmony_ci .ndo_open = alx_open, 16768c2ecf20Sopenharmony_ci .ndo_stop = alx_stop, 16778c2ecf20Sopenharmony_ci .ndo_start_xmit = alx_start_xmit, 16788c2ecf20Sopenharmony_ci .ndo_get_stats64 = alx_get_stats64, 16798c2ecf20Sopenharmony_ci .ndo_set_rx_mode = alx_set_rx_mode, 16808c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 16818c2ecf20Sopenharmony_ci .ndo_set_mac_address = alx_set_mac_address, 16828c2ecf20Sopenharmony_ci .ndo_change_mtu = alx_change_mtu, 16838c2ecf20Sopenharmony_ci .ndo_do_ioctl = alx_ioctl, 16848c2ecf20Sopenharmony_ci .ndo_tx_timeout = alx_tx_timeout, 16858c2ecf20Sopenharmony_ci .ndo_fix_features = alx_fix_features, 16868c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 16878c2ecf20Sopenharmony_ci .ndo_poll_controller = alx_poll_controller, 16888c2ecf20Sopenharmony_ci#endif 16898c2ecf20Sopenharmony_ci}; 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_cistatic int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 16928c2ecf20Sopenharmony_ci{ 16938c2ecf20Sopenharmony_ci struct net_device *netdev; 16948c2ecf20Sopenharmony_ci struct alx_priv *alx; 16958c2ecf20Sopenharmony_ci struct alx_hw *hw; 16968c2ecf20Sopenharmony_ci bool phy_configured; 16978c2ecf20Sopenharmony_ci int err; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci err = pci_enable_device_mem(pdev); 17008c2ecf20Sopenharmony_ci if (err) 17018c2ecf20Sopenharmony_ci return err; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci /* The alx chip can DMA to 64-bit addresses, but it uses a single 17048c2ecf20Sopenharmony_ci * shared register for the high 32 bits, so only a single, aligned, 17058c2ecf20Sopenharmony_ci * 4 GB physical address range can be used for descriptors. 17068c2ecf20Sopenharmony_ci */ 17078c2ecf20Sopenharmony_ci if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) { 17088c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "DMA to 64-BIT addresses\n"); 17098c2ecf20Sopenharmony_ci } else { 17108c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 17118c2ecf20Sopenharmony_ci if (err) { 17128c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No usable DMA config, aborting\n"); 17138c2ecf20Sopenharmony_ci goto out_pci_disable; 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci err = pci_request_mem_regions(pdev, alx_drv_name); 17188c2ecf20Sopenharmony_ci if (err) { 17198c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 17208c2ecf20Sopenharmony_ci "pci_request_mem_regions failed\n"); 17218c2ecf20Sopenharmony_ci goto out_pci_disable; 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci pci_enable_pcie_error_reporting(pdev); 17258c2ecf20Sopenharmony_ci pci_set_master(pdev); 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci if (!pdev->pm_cap) { 17288c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 17298c2ecf20Sopenharmony_ci "Can't find power management capability, aborting\n"); 17308c2ecf20Sopenharmony_ci err = -EIO; 17318c2ecf20Sopenharmony_ci goto out_pci_release; 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci netdev = alloc_etherdev_mqs(sizeof(*alx), 17358c2ecf20Sopenharmony_ci ALX_MAX_TX_QUEUES, 1); 17368c2ecf20Sopenharmony_ci if (!netdev) { 17378c2ecf20Sopenharmony_ci err = -ENOMEM; 17388c2ecf20Sopenharmony_ci goto out_pci_release; 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 17428c2ecf20Sopenharmony_ci alx = netdev_priv(netdev); 17438c2ecf20Sopenharmony_ci spin_lock_init(&alx->hw.mdio_lock); 17448c2ecf20Sopenharmony_ci spin_lock_init(&alx->irq_lock); 17458c2ecf20Sopenharmony_ci spin_lock_init(&alx->stats_lock); 17468c2ecf20Sopenharmony_ci alx->dev = netdev; 17478c2ecf20Sopenharmony_ci alx->hw.pdev = pdev; 17488c2ecf20Sopenharmony_ci alx->msg_enable = NETIF_MSG_LINK | NETIF_MSG_HW | NETIF_MSG_IFUP | 17498c2ecf20Sopenharmony_ci NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR | NETIF_MSG_WOL; 17508c2ecf20Sopenharmony_ci hw = &alx->hw; 17518c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, alx); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci hw->hw_addr = pci_ioremap_bar(pdev, 0); 17548c2ecf20Sopenharmony_ci if (!hw->hw_addr) { 17558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot map device registers\n"); 17568c2ecf20Sopenharmony_ci err = -EIO; 17578c2ecf20Sopenharmony_ci goto out_free_netdev; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci netdev->netdev_ops = &alx_netdev_ops; 17618c2ecf20Sopenharmony_ci netdev->ethtool_ops = &alx_ethtool_ops; 17628c2ecf20Sopenharmony_ci netdev->irq = pci_irq_vector(pdev, 0); 17638c2ecf20Sopenharmony_ci netdev->watchdog_timeo = ALX_WATCHDOG_TIME; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if (ent->driver_data & ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG) 17668c2ecf20Sopenharmony_ci pdev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci err = alx_init_sw(alx); 17698c2ecf20Sopenharmony_ci if (err) { 17708c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "net device private data init failed\n"); 17718c2ecf20Sopenharmony_ci goto out_unmap; 17728c2ecf20Sopenharmony_ci } 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci alx_reset_pcie(hw); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci phy_configured = alx_phy_configured(hw); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci if (!phy_configured) 17798c2ecf20Sopenharmony_ci alx_reset_phy(hw); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci err = alx_reset_mac(hw); 17828c2ecf20Sopenharmony_ci if (err) { 17838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "MAC Reset failed, error = %d\n", err); 17848c2ecf20Sopenharmony_ci goto out_unmap; 17858c2ecf20Sopenharmony_ci } 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci /* setup link to put it in a known good starting state */ 17888c2ecf20Sopenharmony_ci if (!phy_configured) { 17898c2ecf20Sopenharmony_ci err = alx_setup_speed_duplex(hw, hw->adv_cfg, hw->flowctrl); 17908c2ecf20Sopenharmony_ci if (err) { 17918c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 17928c2ecf20Sopenharmony_ci "failed to configure PHY speed/duplex (err=%d)\n", 17938c2ecf20Sopenharmony_ci err); 17948c2ecf20Sopenharmony_ci goto out_unmap; 17958c2ecf20Sopenharmony_ci } 17968c2ecf20Sopenharmony_ci } 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci netdev->hw_features = NETIF_F_SG | 17998c2ecf20Sopenharmony_ci NETIF_F_HW_CSUM | 18008c2ecf20Sopenharmony_ci NETIF_F_RXCSUM | 18018c2ecf20Sopenharmony_ci NETIF_F_TSO | 18028c2ecf20Sopenharmony_ci NETIF_F_TSO6; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci if (alx_get_perm_macaddr(hw, hw->perm_addr)) { 18058c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 18068c2ecf20Sopenharmony_ci "Invalid permanent address programmed, using random one\n"); 18078c2ecf20Sopenharmony_ci eth_hw_addr_random(netdev); 18088c2ecf20Sopenharmony_ci memcpy(hw->perm_addr, netdev->dev_addr, netdev->addr_len); 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci memcpy(hw->mac_addr, hw->perm_addr, ETH_ALEN); 18128c2ecf20Sopenharmony_ci memcpy(netdev->dev_addr, hw->mac_addr, ETH_ALEN); 18138c2ecf20Sopenharmony_ci memcpy(netdev->perm_addr, hw->perm_addr, ETH_ALEN); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci hw->mdio.prtad = 0; 18168c2ecf20Sopenharmony_ci hw->mdio.mmds = 0; 18178c2ecf20Sopenharmony_ci hw->mdio.dev = netdev; 18188c2ecf20Sopenharmony_ci hw->mdio.mode_support = MDIO_SUPPORTS_C45 | 18198c2ecf20Sopenharmony_ci MDIO_SUPPORTS_C22 | 18208c2ecf20Sopenharmony_ci MDIO_EMULATE_C22; 18218c2ecf20Sopenharmony_ci hw->mdio.mdio_read = alx_mdio_read; 18228c2ecf20Sopenharmony_ci hw->mdio.mdio_write = alx_mdio_write; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci if (!alx_get_phy_info(hw)) { 18258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to identify PHY\n"); 18268c2ecf20Sopenharmony_ci err = -EIO; 18278c2ecf20Sopenharmony_ci goto out_unmap; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci INIT_WORK(&alx->link_check_wk, alx_link_check); 18318c2ecf20Sopenharmony_ci INIT_WORK(&alx->reset_wk, alx_reset); 18328c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci err = register_netdev(netdev); 18358c2ecf20Sopenharmony_ci if (err) { 18368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "register netdevice failed\n"); 18378c2ecf20Sopenharmony_ci goto out_unmap; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci netdev_info(netdev, 18418c2ecf20Sopenharmony_ci "Qualcomm Atheros AR816x/AR817x Ethernet [%pM]\n", 18428c2ecf20Sopenharmony_ci netdev->dev_addr); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci return 0; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ciout_unmap: 18478c2ecf20Sopenharmony_ci iounmap(hw->hw_addr); 18488c2ecf20Sopenharmony_ciout_free_netdev: 18498c2ecf20Sopenharmony_ci free_netdev(netdev); 18508c2ecf20Sopenharmony_ciout_pci_release: 18518c2ecf20Sopenharmony_ci pci_release_mem_regions(pdev); 18528c2ecf20Sopenharmony_ci pci_disable_pcie_error_reporting(pdev); 18538c2ecf20Sopenharmony_ciout_pci_disable: 18548c2ecf20Sopenharmony_ci pci_disable_device(pdev); 18558c2ecf20Sopenharmony_ci return err; 18568c2ecf20Sopenharmony_ci} 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_cistatic void alx_remove(struct pci_dev *pdev) 18598c2ecf20Sopenharmony_ci{ 18608c2ecf20Sopenharmony_ci struct alx_priv *alx = pci_get_drvdata(pdev); 18618c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci /* restore permanent mac address */ 18648c2ecf20Sopenharmony_ci alx_set_macaddr(hw, hw->perm_addr); 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci unregister_netdev(alx->dev); 18678c2ecf20Sopenharmony_ci iounmap(hw->hw_addr); 18688c2ecf20Sopenharmony_ci pci_release_mem_regions(pdev); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci pci_disable_pcie_error_reporting(pdev); 18718c2ecf20Sopenharmony_ci pci_disable_device(pdev); 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci free_netdev(alx->dev); 18748c2ecf20Sopenharmony_ci} 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 18778c2ecf20Sopenharmony_cistatic int alx_suspend(struct device *dev) 18788c2ecf20Sopenharmony_ci{ 18798c2ecf20Sopenharmony_ci struct alx_priv *alx = dev_get_drvdata(dev); 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci if (!netif_running(alx->dev)) 18828c2ecf20Sopenharmony_ci return 0; 18838c2ecf20Sopenharmony_ci netif_device_detach(alx->dev); 18848c2ecf20Sopenharmony_ci __alx_stop(alx); 18858c2ecf20Sopenharmony_ci return 0; 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_cistatic int alx_resume(struct device *dev) 18898c2ecf20Sopenharmony_ci{ 18908c2ecf20Sopenharmony_ci struct alx_priv *alx = dev_get_drvdata(dev); 18918c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 18928c2ecf20Sopenharmony_ci int err; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci alx_reset_phy(hw); 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci if (!netif_running(alx->dev)) 18978c2ecf20Sopenharmony_ci return 0; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci rtnl_lock(); 19008c2ecf20Sopenharmony_ci err = __alx_open(alx, true); 19018c2ecf20Sopenharmony_ci rtnl_unlock(); 19028c2ecf20Sopenharmony_ci if (err) 19038c2ecf20Sopenharmony_ci return err; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci netif_device_attach(alx->dev); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci return 0; 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); 19118c2ecf20Sopenharmony_ci#define ALX_PM_OPS (&alx_pm_ops) 19128c2ecf20Sopenharmony_ci#else 19138c2ecf20Sopenharmony_ci#define ALX_PM_OPS NULL 19148c2ecf20Sopenharmony_ci#endif 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_cistatic pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev, 19188c2ecf20Sopenharmony_ci pci_channel_state_t state) 19198c2ecf20Sopenharmony_ci{ 19208c2ecf20Sopenharmony_ci struct alx_priv *alx = pci_get_drvdata(pdev); 19218c2ecf20Sopenharmony_ci struct net_device *netdev = alx->dev; 19228c2ecf20Sopenharmony_ci pci_ers_result_t rc = PCI_ERS_RESULT_NEED_RESET; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "pci error detected\n"); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci rtnl_lock(); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 19298c2ecf20Sopenharmony_ci netif_device_detach(netdev); 19308c2ecf20Sopenharmony_ci alx_halt(alx); 19318c2ecf20Sopenharmony_ci } 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci if (state == pci_channel_io_perm_failure) 19348c2ecf20Sopenharmony_ci rc = PCI_ERS_RESULT_DISCONNECT; 19358c2ecf20Sopenharmony_ci else 19368c2ecf20Sopenharmony_ci pci_disable_device(pdev); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci rtnl_unlock(); 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci return rc; 19418c2ecf20Sopenharmony_ci} 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_cistatic pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev) 19448c2ecf20Sopenharmony_ci{ 19458c2ecf20Sopenharmony_ci struct alx_priv *alx = pci_get_drvdata(pdev); 19468c2ecf20Sopenharmony_ci struct alx_hw *hw = &alx->hw; 19478c2ecf20Sopenharmony_ci pci_ers_result_t rc = PCI_ERS_RESULT_DISCONNECT; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "pci error slot reset\n"); 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci rtnl_lock(); 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) { 19548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to re-enable PCI device after reset\n"); 19558c2ecf20Sopenharmony_ci goto out; 19568c2ecf20Sopenharmony_ci } 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci pci_set_master(pdev); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci alx_reset_pcie(hw); 19618c2ecf20Sopenharmony_ci if (!alx_reset_mac(hw)) 19628c2ecf20Sopenharmony_ci rc = PCI_ERS_RESULT_RECOVERED; 19638c2ecf20Sopenharmony_ciout: 19648c2ecf20Sopenharmony_ci rtnl_unlock(); 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci return rc; 19678c2ecf20Sopenharmony_ci} 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_cistatic void alx_pci_error_resume(struct pci_dev *pdev) 19708c2ecf20Sopenharmony_ci{ 19718c2ecf20Sopenharmony_ci struct alx_priv *alx = pci_get_drvdata(pdev); 19728c2ecf20Sopenharmony_ci struct net_device *netdev = alx->dev; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "pci error resume\n"); 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci rtnl_lock(); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 19798c2ecf20Sopenharmony_ci alx_activate(alx); 19808c2ecf20Sopenharmony_ci netif_device_attach(netdev); 19818c2ecf20Sopenharmony_ci } 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci rtnl_unlock(); 19848c2ecf20Sopenharmony_ci} 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_cistatic const struct pci_error_handlers alx_err_handlers = { 19878c2ecf20Sopenharmony_ci .error_detected = alx_pci_error_detected, 19888c2ecf20Sopenharmony_ci .slot_reset = alx_pci_error_slot_reset, 19898c2ecf20Sopenharmony_ci .resume = alx_pci_error_resume, 19908c2ecf20Sopenharmony_ci}; 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_cistatic const struct pci_device_id alx_pci_tbl[] = { 19938c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8161), 19948c2ecf20Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 19958c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2200), 19968c2ecf20Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 19978c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2400), 19988c2ecf20Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 19998c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2500), 20008c2ecf20Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 20018c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8162), 20028c2ecf20Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 20038c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8171) }, 20048c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8172) }, 20058c2ecf20Sopenharmony_ci {} 20068c2ecf20Sopenharmony_ci}; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_cistatic struct pci_driver alx_driver = { 20098c2ecf20Sopenharmony_ci .name = alx_drv_name, 20108c2ecf20Sopenharmony_ci .id_table = alx_pci_tbl, 20118c2ecf20Sopenharmony_ci .probe = alx_probe, 20128c2ecf20Sopenharmony_ci .remove = alx_remove, 20138c2ecf20Sopenharmony_ci .err_handler = &alx_err_handlers, 20148c2ecf20Sopenharmony_ci .driver.pm = ALX_PM_OPS, 20158c2ecf20Sopenharmony_ci}; 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_cimodule_pci_driver(alx_driver); 20188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, alx_pci_tbl); 20198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); 20208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Qualcomm Corporation, <nic-devel@qualcomm.com>"); 20218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION( 20228c2ecf20Sopenharmony_ci "Qualcomm Atheros(R) AR816x/AR817x PCI-E Ethernet Network Driver"); 20238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2024