162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2013, 2021 Johannes Berg <johannes@sipsolutions.net> 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This file is free software: you may copy, redistribute and/or modify it 562306a36Sopenharmony_ci * under the terms of the GNU General Public License as published by the 662306a36Sopenharmony_ci * Free Software Foundation, either version 2 of the License, or (at your 762306a36Sopenharmony_ci * option) any later version. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 1062306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1162306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1262306a36Sopenharmony_ci * General Public License for more details. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 1562306a36Sopenharmony_ci * along with this program. If not, see <http://www.gnu.org/licenses/>. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * This file incorporates work covered by the following copyright and 1862306a36Sopenharmony_ci * permission notice: 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Copyright (c) 2012 Qualcomm Atheros, Inc. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 2362306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 2462306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 2762306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 2862306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 2962306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 3062306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 3162306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 3262306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/module.h> 3662306a36Sopenharmony_ci#include <linux/pci.h> 3762306a36Sopenharmony_ci#include <linux/interrupt.h> 3862306a36Sopenharmony_ci#include <linux/ip.h> 3962306a36Sopenharmony_ci#include <linux/ipv6.h> 4062306a36Sopenharmony_ci#include <linux/if_vlan.h> 4162306a36Sopenharmony_ci#include <linux/mdio.h> 4262306a36Sopenharmony_ci#include <linux/bitops.h> 4362306a36Sopenharmony_ci#include <linux/netdevice.h> 4462306a36Sopenharmony_ci#include <linux/etherdevice.h> 4562306a36Sopenharmony_ci#include <net/ip6_checksum.h> 4662306a36Sopenharmony_ci#include <linux/crc32.h> 4762306a36Sopenharmony_ci#include "alx.h" 4862306a36Sopenharmony_ci#include "hw.h" 4962306a36Sopenharmony_ci#include "reg.h" 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic const char alx_drv_name[] = "alx"; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void alx_free_txbuf(struct alx_tx_queue *txq, int entry) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct alx_buffer *txb = &txq->bufs[entry]; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (dma_unmap_len(txb, size)) { 5862306a36Sopenharmony_ci dma_unmap_single(txq->dev, 5962306a36Sopenharmony_ci dma_unmap_addr(txb, dma), 6062306a36Sopenharmony_ci dma_unmap_len(txb, size), 6162306a36Sopenharmony_ci DMA_TO_DEVICE); 6262306a36Sopenharmony_ci dma_unmap_len_set(txb, size, 0); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (txb->skb) { 6662306a36Sopenharmony_ci dev_kfree_skb_any(txb->skb); 6762306a36Sopenharmony_ci txb->skb = NULL; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct alx_rx_queue *rxq = alx->qnapi[0]->rxq; 7462306a36Sopenharmony_ci struct sk_buff *skb; 7562306a36Sopenharmony_ci struct alx_buffer *cur_buf; 7662306a36Sopenharmony_ci dma_addr_t dma; 7762306a36Sopenharmony_ci u16 cur, next, count = 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci next = cur = rxq->write_idx; 8062306a36Sopenharmony_ci if (++next == alx->rx_ringsz) 8162306a36Sopenharmony_ci next = 0; 8262306a36Sopenharmony_ci cur_buf = &rxq->bufs[cur]; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci while (!cur_buf->skb && next != rxq->read_idx) { 8562306a36Sopenharmony_ci struct alx_rfd *rfd = &rxq->rfd[cur]; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * When DMA RX address is set to something like 8962306a36Sopenharmony_ci * 0x....fc0, it will be very likely to cause DMA 9062306a36Sopenharmony_ci * RFD overflow issue. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * To work around it, we apply rx skb with 64 bytes 9362306a36Sopenharmony_ci * longer space, and offset the address whenever 9462306a36Sopenharmony_ci * 0x....fc0 is detected. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci skb = __netdev_alloc_skb(alx->dev, alx->rxbuf_size + 64, gfp); 9762306a36Sopenharmony_ci if (!skb) 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (((unsigned long)skb->data & 0xfff) == 0xfc0) 10162306a36Sopenharmony_ci skb_reserve(skb, 64); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci dma = dma_map_single(&alx->hw.pdev->dev, 10462306a36Sopenharmony_ci skb->data, alx->rxbuf_size, 10562306a36Sopenharmony_ci DMA_FROM_DEVICE); 10662306a36Sopenharmony_ci if (dma_mapping_error(&alx->hw.pdev->dev, dma)) { 10762306a36Sopenharmony_ci dev_kfree_skb(skb); 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* Unfortunately, RX descriptor buffers must be 4-byte 11262306a36Sopenharmony_ci * aligned, so we can't use IP alignment. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci if (WARN_ON(dma & 3)) { 11562306a36Sopenharmony_ci dev_kfree_skb(skb); 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci cur_buf->skb = skb; 12062306a36Sopenharmony_ci dma_unmap_len_set(cur_buf, size, alx->rxbuf_size); 12162306a36Sopenharmony_ci dma_unmap_addr_set(cur_buf, dma, dma); 12262306a36Sopenharmony_ci rfd->addr = cpu_to_le64(dma); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci cur = next; 12562306a36Sopenharmony_ci if (++next == alx->rx_ringsz) 12662306a36Sopenharmony_ci next = 0; 12762306a36Sopenharmony_ci cur_buf = &rxq->bufs[cur]; 12862306a36Sopenharmony_ci count++; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (count) { 13262306a36Sopenharmony_ci /* flush all updates before updating hardware */ 13362306a36Sopenharmony_ci wmb(); 13462306a36Sopenharmony_ci rxq->write_idx = cur; 13562306a36Sopenharmony_ci alx_write_mem16(&alx->hw, ALX_RFD_PIDX, cur); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return count; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic struct alx_tx_queue *alx_tx_queue_mapping(struct alx_priv *alx, 14262306a36Sopenharmony_ci struct sk_buff *skb) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci unsigned int r_idx = skb->queue_mapping; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (r_idx >= alx->num_txq) 14762306a36Sopenharmony_ci r_idx = r_idx % alx->num_txq; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return alx->qnapi[r_idx]->txq; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct netdev_queue *alx_get_tx_queue(const struct alx_tx_queue *txq) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci return netdev_get_tx_queue(txq->netdev, txq->queue_idx); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic inline int alx_tpd_avail(struct alx_tx_queue *txq) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci if (txq->write_idx >= txq->read_idx) 16062306a36Sopenharmony_ci return txq->count + txq->read_idx - txq->write_idx - 1; 16162306a36Sopenharmony_ci return txq->read_idx - txq->write_idx - 1; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic bool alx_clean_tx_irq(struct alx_tx_queue *txq) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct alx_priv *alx; 16762306a36Sopenharmony_ci struct netdev_queue *tx_queue; 16862306a36Sopenharmony_ci u16 hw_read_idx, sw_read_idx; 16962306a36Sopenharmony_ci unsigned int total_bytes = 0, total_packets = 0; 17062306a36Sopenharmony_ci int budget = ALX_DEFAULT_TX_WORK; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci alx = netdev_priv(txq->netdev); 17362306a36Sopenharmony_ci tx_queue = alx_get_tx_queue(txq); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci sw_read_idx = txq->read_idx; 17662306a36Sopenharmony_ci hw_read_idx = alx_read_mem16(&alx->hw, txq->c_reg); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (sw_read_idx != hw_read_idx) { 17962306a36Sopenharmony_ci while (sw_read_idx != hw_read_idx && budget > 0) { 18062306a36Sopenharmony_ci struct sk_buff *skb; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci skb = txq->bufs[sw_read_idx].skb; 18362306a36Sopenharmony_ci if (skb) { 18462306a36Sopenharmony_ci total_bytes += skb->len; 18562306a36Sopenharmony_ci total_packets++; 18662306a36Sopenharmony_ci budget--; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci alx_free_txbuf(txq, sw_read_idx); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (++sw_read_idx == txq->count) 19262306a36Sopenharmony_ci sw_read_idx = 0; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci txq->read_idx = sw_read_idx; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci netdev_tx_completed_queue(tx_queue, total_packets, total_bytes); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (netif_tx_queue_stopped(tx_queue) && netif_carrier_ok(alx->dev) && 20062306a36Sopenharmony_ci alx_tpd_avail(txq) > txq->count / 4) 20162306a36Sopenharmony_ci netif_tx_wake_queue(tx_queue); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return sw_read_idx == hw_read_idx; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void alx_schedule_link_check(struct alx_priv *alx) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci schedule_work(&alx->link_check_wk); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic void alx_schedule_reset(struct alx_priv *alx) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci schedule_work(&alx->reset_wk); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int alx_clean_rx_irq(struct alx_rx_queue *rxq, int budget) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct alx_priv *alx; 21962306a36Sopenharmony_ci struct alx_rrd *rrd; 22062306a36Sopenharmony_ci struct alx_buffer *rxb; 22162306a36Sopenharmony_ci struct sk_buff *skb; 22262306a36Sopenharmony_ci u16 length, rfd_cleaned = 0; 22362306a36Sopenharmony_ci int work = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci alx = netdev_priv(rxq->netdev); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci while (work < budget) { 22862306a36Sopenharmony_ci rrd = &rxq->rrd[rxq->rrd_read_idx]; 22962306a36Sopenharmony_ci if (!(rrd->word3 & cpu_to_le32(1 << RRD_UPDATED_SHIFT))) 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci rrd->word3 &= ~cpu_to_le32(1 << RRD_UPDATED_SHIFT); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (ALX_GET_FIELD(le32_to_cpu(rrd->word0), 23462306a36Sopenharmony_ci RRD_SI) != rxq->read_idx || 23562306a36Sopenharmony_ci ALX_GET_FIELD(le32_to_cpu(rrd->word0), 23662306a36Sopenharmony_ci RRD_NOR) != 1) { 23762306a36Sopenharmony_ci alx_schedule_reset(alx); 23862306a36Sopenharmony_ci return work; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci rxb = &rxq->bufs[rxq->read_idx]; 24262306a36Sopenharmony_ci dma_unmap_single(rxq->dev, 24362306a36Sopenharmony_ci dma_unmap_addr(rxb, dma), 24462306a36Sopenharmony_ci dma_unmap_len(rxb, size), 24562306a36Sopenharmony_ci DMA_FROM_DEVICE); 24662306a36Sopenharmony_ci dma_unmap_len_set(rxb, size, 0); 24762306a36Sopenharmony_ci skb = rxb->skb; 24862306a36Sopenharmony_ci rxb->skb = NULL; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (rrd->word3 & cpu_to_le32(1 << RRD_ERR_RES_SHIFT) || 25162306a36Sopenharmony_ci rrd->word3 & cpu_to_le32(1 << RRD_ERR_LEN_SHIFT)) { 25262306a36Sopenharmony_ci rrd->word3 = 0; 25362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 25462306a36Sopenharmony_ci goto next_pkt; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci length = ALX_GET_FIELD(le32_to_cpu(rrd->word3), 25862306a36Sopenharmony_ci RRD_PKTLEN) - ETH_FCS_LEN; 25962306a36Sopenharmony_ci skb_put(skb, length); 26062306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, rxq->netdev); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci skb_checksum_none_assert(skb); 26362306a36Sopenharmony_ci if (alx->dev->features & NETIF_F_RXCSUM && 26462306a36Sopenharmony_ci !(rrd->word3 & (cpu_to_le32(1 << RRD_ERR_L4_SHIFT) | 26562306a36Sopenharmony_ci cpu_to_le32(1 << RRD_ERR_IPV4_SHIFT)))) { 26662306a36Sopenharmony_ci switch (ALX_GET_FIELD(le32_to_cpu(rrd->word2), 26762306a36Sopenharmony_ci RRD_PID)) { 26862306a36Sopenharmony_ci case RRD_PID_IPV6UDP: 26962306a36Sopenharmony_ci case RRD_PID_IPV4UDP: 27062306a36Sopenharmony_ci case RRD_PID_IPV4TCP: 27162306a36Sopenharmony_ci case RRD_PID_IPV6TCP: 27262306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci napi_gro_receive(&rxq->np->napi, skb); 27862306a36Sopenharmony_ci work++; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cinext_pkt: 28162306a36Sopenharmony_ci if (++rxq->read_idx == rxq->count) 28262306a36Sopenharmony_ci rxq->read_idx = 0; 28362306a36Sopenharmony_ci if (++rxq->rrd_read_idx == rxq->count) 28462306a36Sopenharmony_ci rxq->rrd_read_idx = 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (++rfd_cleaned > ALX_RX_ALLOC_THRESH) 28762306a36Sopenharmony_ci rfd_cleaned -= alx_refill_rx_ring(alx, GFP_ATOMIC); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (rfd_cleaned) 29162306a36Sopenharmony_ci alx_refill_rx_ring(alx, GFP_ATOMIC); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return work; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int alx_poll(struct napi_struct *napi, int budget) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct alx_napi *np = container_of(napi, struct alx_napi, napi); 29962306a36Sopenharmony_ci struct alx_priv *alx = np->alx; 30062306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 30162306a36Sopenharmony_ci unsigned long flags; 30262306a36Sopenharmony_ci bool tx_complete = true; 30362306a36Sopenharmony_ci int work = 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (np->txq) 30662306a36Sopenharmony_ci tx_complete = alx_clean_tx_irq(np->txq); 30762306a36Sopenharmony_ci if (np->rxq) 30862306a36Sopenharmony_ci work = alx_clean_rx_irq(np->rxq, budget); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!tx_complete || work == budget) 31162306a36Sopenharmony_ci return budget; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci napi_complete_done(&np->napi, work); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* enable interrupt */ 31662306a36Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 31762306a36Sopenharmony_ci alx_mask_msix(hw, np->vec_idx, false); 31862306a36Sopenharmony_ci } else { 31962306a36Sopenharmony_ci spin_lock_irqsave(&alx->irq_lock, flags); 32062306a36Sopenharmony_ci alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0; 32162306a36Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 32262306a36Sopenharmony_ci spin_unlock_irqrestore(&alx->irq_lock, flags); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci alx_post_write(hw); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return work; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic bool alx_intr_handle_misc(struct alx_priv *alx, u32 intr) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (intr & ALX_ISR_FATAL) { 33562306a36Sopenharmony_ci netif_warn(alx, hw, alx->dev, 33662306a36Sopenharmony_ci "fatal interrupt 0x%x, resetting\n", intr); 33762306a36Sopenharmony_ci alx_schedule_reset(alx); 33862306a36Sopenharmony_ci return true; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (intr & ALX_ISR_ALERT) 34262306a36Sopenharmony_ci netdev_warn(alx->dev, "alert interrupt: 0x%x\n", intr); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (intr & ALX_ISR_PHY) { 34562306a36Sopenharmony_ci /* suppress PHY interrupt, because the source 34662306a36Sopenharmony_ci * is from PHY internal. only the internal status 34762306a36Sopenharmony_ci * is cleared, the interrupt status could be cleared. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci alx->int_mask &= ~ALX_ISR_PHY; 35062306a36Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 35162306a36Sopenharmony_ci alx_schedule_link_check(alx); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return false; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic irqreturn_t alx_intr_handle(struct alx_priv *alx, u32 intr) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci spin_lock(&alx->irq_lock); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* ACK interrupt */ 36462306a36Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, intr | ALX_ISR_DIS); 36562306a36Sopenharmony_ci intr &= alx->int_mask; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (alx_intr_handle_misc(alx, intr)) 36862306a36Sopenharmony_ci goto out; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (intr & (ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0)) { 37162306a36Sopenharmony_ci napi_schedule(&alx->qnapi[0]->napi); 37262306a36Sopenharmony_ci /* mask rx/tx interrupt, enable them when napi complete */ 37362306a36Sopenharmony_ci alx->int_mask &= ~ALX_ISR_ALL_QUEUES; 37462306a36Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, 0); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci out: 38062306a36Sopenharmony_ci spin_unlock(&alx->irq_lock); 38162306a36Sopenharmony_ci return IRQ_HANDLED; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic irqreturn_t alx_intr_msix_ring(int irq, void *data) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct alx_napi *np = data; 38762306a36Sopenharmony_ci struct alx_hw *hw = &np->alx->hw; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* mask interrupt to ACK chip */ 39062306a36Sopenharmony_ci alx_mask_msix(hw, np->vec_idx, true); 39162306a36Sopenharmony_ci /* clear interrupt status */ 39262306a36Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, np->vec_mask); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci napi_schedule(&np->napi); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return IRQ_HANDLED; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic irqreturn_t alx_intr_msix_misc(int irq, void *data) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct alx_priv *alx = data; 40262306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 40362306a36Sopenharmony_ci u32 intr; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* mask interrupt to ACK chip */ 40662306a36Sopenharmony_ci alx_mask_msix(hw, 0, true); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* read interrupt status */ 40962306a36Sopenharmony_ci intr = alx_read_mem32(hw, ALX_ISR); 41062306a36Sopenharmony_ci intr &= (alx->int_mask & ~ALX_ISR_ALL_QUEUES); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (alx_intr_handle_misc(alx, intr)) 41362306a36Sopenharmony_ci return IRQ_HANDLED; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* clear interrupt status */ 41662306a36Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, intr); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* enable interrupt again */ 41962306a36Sopenharmony_ci alx_mask_msix(hw, 0, false); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return IRQ_HANDLED; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic irqreturn_t alx_intr_msi(int irq, void *data) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct alx_priv *alx = data; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return alx_intr_handle(alx, alx_read_mem32(&alx->hw, ALX_ISR)); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic irqreturn_t alx_intr_legacy(int irq, void *data) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct alx_priv *alx = data; 43462306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 43562306a36Sopenharmony_ci u32 intr; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci intr = alx_read_mem32(hw, ALX_ISR); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (intr & ALX_ISR_DIS || !(intr & alx->int_mask)) 44062306a36Sopenharmony_ci return IRQ_NONE; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return alx_intr_handle(alx, intr); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic const u16 txring_header_reg[] = {ALX_TPD_PRI0_ADDR_LO, 44662306a36Sopenharmony_ci ALX_TPD_PRI1_ADDR_LO, 44762306a36Sopenharmony_ci ALX_TPD_PRI2_ADDR_LO, 44862306a36Sopenharmony_ci ALX_TPD_PRI3_ADDR_LO}; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic void alx_init_ring_ptrs(struct alx_priv *alx) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 45362306a36Sopenharmony_ci u32 addr_hi = ((u64)alx->descmem.dma) >> 32; 45462306a36Sopenharmony_ci struct alx_napi *np; 45562306a36Sopenharmony_ci int i; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) { 45862306a36Sopenharmony_ci np = alx->qnapi[i]; 45962306a36Sopenharmony_ci if (np->txq) { 46062306a36Sopenharmony_ci np->txq->read_idx = 0; 46162306a36Sopenharmony_ci np->txq->write_idx = 0; 46262306a36Sopenharmony_ci alx_write_mem32(hw, 46362306a36Sopenharmony_ci txring_header_reg[np->txq->queue_idx], 46462306a36Sopenharmony_ci np->txq->tpd_dma); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (np->rxq) { 46862306a36Sopenharmony_ci np->rxq->read_idx = 0; 46962306a36Sopenharmony_ci np->rxq->write_idx = 0; 47062306a36Sopenharmony_ci np->rxq->rrd_read_idx = 0; 47162306a36Sopenharmony_ci alx_write_mem32(hw, ALX_RRD_ADDR_LO, np->rxq->rrd_dma); 47262306a36Sopenharmony_ci alx_write_mem32(hw, ALX_RFD_ADDR_LO, np->rxq->rfd_dma); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci alx_write_mem32(hw, ALX_TX_BASE_ADDR_HI, addr_hi); 47762306a36Sopenharmony_ci alx_write_mem32(hw, ALX_TPD_RING_SZ, alx->tx_ringsz); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci alx_write_mem32(hw, ALX_RX_BASE_ADDR_HI, addr_hi); 48062306a36Sopenharmony_ci alx_write_mem32(hw, ALX_RRD_RING_SZ, alx->rx_ringsz); 48162306a36Sopenharmony_ci alx_write_mem32(hw, ALX_RFD_RING_SZ, alx->rx_ringsz); 48262306a36Sopenharmony_ci alx_write_mem32(hw, ALX_RFD_BUF_SZ, alx->rxbuf_size); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* load these pointers into the chip */ 48562306a36Sopenharmony_ci alx_write_mem32(hw, ALX_SRAM9, ALX_SRAM_LOAD_PTR); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void alx_free_txring_buf(struct alx_tx_queue *txq) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci int i; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (!txq->bufs) 49362306a36Sopenharmony_ci return; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci for (i = 0; i < txq->count; i++) 49662306a36Sopenharmony_ci alx_free_txbuf(txq, i); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci memset(txq->bufs, 0, txq->count * sizeof(struct alx_buffer)); 49962306a36Sopenharmony_ci memset(txq->tpd, 0, txq->count * sizeof(struct alx_txd)); 50062306a36Sopenharmony_ci txq->write_idx = 0; 50162306a36Sopenharmony_ci txq->read_idx = 0; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci netdev_tx_reset_queue(alx_get_tx_queue(txq)); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic void alx_free_rxring_buf(struct alx_rx_queue *rxq) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct alx_buffer *cur_buf; 50962306a36Sopenharmony_ci u16 i; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (!rxq->bufs) 51262306a36Sopenharmony_ci return; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci for (i = 0; i < rxq->count; i++) { 51562306a36Sopenharmony_ci cur_buf = rxq->bufs + i; 51662306a36Sopenharmony_ci if (cur_buf->skb) { 51762306a36Sopenharmony_ci dma_unmap_single(rxq->dev, 51862306a36Sopenharmony_ci dma_unmap_addr(cur_buf, dma), 51962306a36Sopenharmony_ci dma_unmap_len(cur_buf, size), 52062306a36Sopenharmony_ci DMA_FROM_DEVICE); 52162306a36Sopenharmony_ci dev_kfree_skb(cur_buf->skb); 52262306a36Sopenharmony_ci cur_buf->skb = NULL; 52362306a36Sopenharmony_ci dma_unmap_len_set(cur_buf, size, 0); 52462306a36Sopenharmony_ci dma_unmap_addr_set(cur_buf, dma, 0); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci rxq->write_idx = 0; 52962306a36Sopenharmony_ci rxq->read_idx = 0; 53062306a36Sopenharmony_ci rxq->rrd_read_idx = 0; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void alx_free_buffers(struct alx_priv *alx) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci int i; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) 53862306a36Sopenharmony_ci if (alx->qnapi[i] && alx->qnapi[i]->txq) 53962306a36Sopenharmony_ci alx_free_txring_buf(alx->qnapi[i]->txq); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (alx->qnapi[0] && alx->qnapi[0]->rxq) 54262306a36Sopenharmony_ci alx_free_rxring_buf(alx->qnapi[0]->rxq); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int alx_reinit_rings(struct alx_priv *alx) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci alx_free_buffers(alx); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci alx_init_ring_ptrs(alx); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (!alx_refill_rx_ring(alx, GFP_KERNEL)) 55262306a36Sopenharmony_ci return -ENOMEM; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic void alx_add_mc_addr(struct alx_hw *hw, const u8 *addr, u32 *mc_hash) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci u32 crc32, bit, reg; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci crc32 = ether_crc(ETH_ALEN, addr); 56262306a36Sopenharmony_ci reg = (crc32 >> 31) & 0x1; 56362306a36Sopenharmony_ci bit = (crc32 >> 26) & 0x1F; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci mc_hash[reg] |= BIT(bit); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic void __alx_set_rx_mode(struct net_device *netdev) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 57162306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 57262306a36Sopenharmony_ci struct netdev_hw_addr *ha; 57362306a36Sopenharmony_ci u32 mc_hash[2] = {}; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (!(netdev->flags & IFF_ALLMULTI)) { 57662306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) 57762306a36Sopenharmony_ci alx_add_mc_addr(hw, ha->addr, mc_hash); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci alx_write_mem32(hw, ALX_HASH_TBL0, mc_hash[0]); 58062306a36Sopenharmony_ci alx_write_mem32(hw, ALX_HASH_TBL1, mc_hash[1]); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci hw->rx_ctrl &= ~(ALX_MAC_CTRL_MULTIALL_EN | ALX_MAC_CTRL_PROMISC_EN); 58462306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) 58562306a36Sopenharmony_ci hw->rx_ctrl |= ALX_MAC_CTRL_PROMISC_EN; 58662306a36Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI) 58762306a36Sopenharmony_ci hw->rx_ctrl |= ALX_MAC_CTRL_MULTIALL_EN; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl); 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic void alx_set_rx_mode(struct net_device *netdev) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci __alx_set_rx_mode(netdev); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic int alx_set_mac_address(struct net_device *netdev, void *data) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 60062306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 60162306a36Sopenharmony_ci struct sockaddr *addr = data; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 60462306a36Sopenharmony_ci return -EADDRNOTAVAIL; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (netdev->addr_assign_type & NET_ADDR_RANDOM) 60762306a36Sopenharmony_ci netdev->addr_assign_type ^= NET_ADDR_RANDOM; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr->sa_data); 61062306a36Sopenharmony_ci memcpy(hw->mac_addr, addr->sa_data, netdev->addr_len); 61162306a36Sopenharmony_ci alx_set_macaddr(hw, hw->mac_addr); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic int alx_alloc_tx_ring(struct alx_priv *alx, struct alx_tx_queue *txq, 61762306a36Sopenharmony_ci int offset) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci txq->bufs = kcalloc(txq->count, sizeof(struct alx_buffer), GFP_KERNEL); 62062306a36Sopenharmony_ci if (!txq->bufs) 62162306a36Sopenharmony_ci return -ENOMEM; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci txq->tpd = alx->descmem.virt + offset; 62462306a36Sopenharmony_ci txq->tpd_dma = alx->descmem.dma + offset; 62562306a36Sopenharmony_ci offset += sizeof(struct alx_txd) * txq->count; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return offset; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic int alx_alloc_rx_ring(struct alx_priv *alx, struct alx_rx_queue *rxq, 63162306a36Sopenharmony_ci int offset) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci rxq->bufs = kcalloc(rxq->count, sizeof(struct alx_buffer), GFP_KERNEL); 63462306a36Sopenharmony_ci if (!rxq->bufs) 63562306a36Sopenharmony_ci return -ENOMEM; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci rxq->rrd = alx->descmem.virt + offset; 63862306a36Sopenharmony_ci rxq->rrd_dma = alx->descmem.dma + offset; 63962306a36Sopenharmony_ci offset += sizeof(struct alx_rrd) * rxq->count; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci rxq->rfd = alx->descmem.virt + offset; 64262306a36Sopenharmony_ci rxq->rfd_dma = alx->descmem.dma + offset; 64362306a36Sopenharmony_ci offset += sizeof(struct alx_rfd) * rxq->count; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return offset; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic int alx_alloc_rings(struct alx_priv *alx) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci int i, offset = 0; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* physical tx/rx ring descriptors 65362306a36Sopenharmony_ci * 65462306a36Sopenharmony_ci * Allocate them as a single chunk because they must not cross a 65562306a36Sopenharmony_ci * 4G boundary (hardware has a single register for high 32 bits 65662306a36Sopenharmony_ci * of addresses only) 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci alx->descmem.size = sizeof(struct alx_txd) * alx->tx_ringsz * 65962306a36Sopenharmony_ci alx->num_txq + 66062306a36Sopenharmony_ci sizeof(struct alx_rrd) * alx->rx_ringsz + 66162306a36Sopenharmony_ci sizeof(struct alx_rfd) * alx->rx_ringsz; 66262306a36Sopenharmony_ci alx->descmem.virt = dma_alloc_coherent(&alx->hw.pdev->dev, 66362306a36Sopenharmony_ci alx->descmem.size, 66462306a36Sopenharmony_ci &alx->descmem.dma, GFP_KERNEL); 66562306a36Sopenharmony_ci if (!alx->descmem.virt) 66662306a36Sopenharmony_ci return -ENOMEM; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* alignment requirements */ 66962306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct alx_txd) % 8); 67062306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct alx_rrd) % 8); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) { 67362306a36Sopenharmony_ci offset = alx_alloc_tx_ring(alx, alx->qnapi[i]->txq, offset); 67462306a36Sopenharmony_ci if (offset < 0) { 67562306a36Sopenharmony_ci netdev_err(alx->dev, "Allocation of tx buffer failed!\n"); 67662306a36Sopenharmony_ci return -ENOMEM; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci offset = alx_alloc_rx_ring(alx, alx->qnapi[0]->rxq, offset); 68162306a36Sopenharmony_ci if (offset < 0) { 68262306a36Sopenharmony_ci netdev_err(alx->dev, "Allocation of rx buffer failed!\n"); 68362306a36Sopenharmony_ci return -ENOMEM; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic void alx_free_rings(struct alx_priv *alx) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci int i; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci alx_free_buffers(alx); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) 69662306a36Sopenharmony_ci if (alx->qnapi[i] && alx->qnapi[i]->txq) 69762306a36Sopenharmony_ci kfree(alx->qnapi[i]->txq->bufs); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (alx->qnapi[0] && alx->qnapi[0]->rxq) 70062306a36Sopenharmony_ci kfree(alx->qnapi[0]->rxq->bufs); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (alx->descmem.virt) 70362306a36Sopenharmony_ci dma_free_coherent(&alx->hw.pdev->dev, 70462306a36Sopenharmony_ci alx->descmem.size, 70562306a36Sopenharmony_ci alx->descmem.virt, 70662306a36Sopenharmony_ci alx->descmem.dma); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic void alx_free_napis(struct alx_priv *alx) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct alx_napi *np; 71262306a36Sopenharmony_ci int i; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) { 71562306a36Sopenharmony_ci np = alx->qnapi[i]; 71662306a36Sopenharmony_ci if (!np) 71762306a36Sopenharmony_ci continue; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci netif_napi_del(&np->napi); 72062306a36Sopenharmony_ci kfree(np->txq); 72162306a36Sopenharmony_ci kfree(np->rxq); 72262306a36Sopenharmony_ci kfree(np); 72362306a36Sopenharmony_ci alx->qnapi[i] = NULL; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic const u16 tx_pidx_reg[] = {ALX_TPD_PRI0_PIDX, ALX_TPD_PRI1_PIDX, 72862306a36Sopenharmony_ci ALX_TPD_PRI2_PIDX, ALX_TPD_PRI3_PIDX}; 72962306a36Sopenharmony_cistatic const u16 tx_cidx_reg[] = {ALX_TPD_PRI0_CIDX, ALX_TPD_PRI1_CIDX, 73062306a36Sopenharmony_ci ALX_TPD_PRI2_CIDX, ALX_TPD_PRI3_CIDX}; 73162306a36Sopenharmony_cistatic const u32 tx_vect_mask[] = {ALX_ISR_TX_Q0, ALX_ISR_TX_Q1, 73262306a36Sopenharmony_ci ALX_ISR_TX_Q2, ALX_ISR_TX_Q3}; 73362306a36Sopenharmony_cistatic const u32 rx_vect_mask[] = {ALX_ISR_RX_Q0, ALX_ISR_RX_Q1, 73462306a36Sopenharmony_ci ALX_ISR_RX_Q2, ALX_ISR_RX_Q3, 73562306a36Sopenharmony_ci ALX_ISR_RX_Q4, ALX_ISR_RX_Q5, 73662306a36Sopenharmony_ci ALX_ISR_RX_Q6, ALX_ISR_RX_Q7}; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int alx_alloc_napis(struct alx_priv *alx) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct alx_napi *np; 74162306a36Sopenharmony_ci struct alx_rx_queue *rxq; 74262306a36Sopenharmony_ci struct alx_tx_queue *txq; 74362306a36Sopenharmony_ci int i; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci alx->int_mask &= ~ALX_ISR_ALL_QUEUES; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* allocate alx_napi structures */ 74862306a36Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) { 74962306a36Sopenharmony_ci np = kzalloc(sizeof(struct alx_napi), GFP_KERNEL); 75062306a36Sopenharmony_ci if (!np) 75162306a36Sopenharmony_ci goto err_out; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci np->alx = alx; 75462306a36Sopenharmony_ci netif_napi_add(alx->dev, &np->napi, alx_poll); 75562306a36Sopenharmony_ci alx->qnapi[i] = np; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* allocate tx queues */ 75962306a36Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) { 76062306a36Sopenharmony_ci np = alx->qnapi[i]; 76162306a36Sopenharmony_ci txq = kzalloc(sizeof(*txq), GFP_KERNEL); 76262306a36Sopenharmony_ci if (!txq) 76362306a36Sopenharmony_ci goto err_out; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci np->txq = txq; 76662306a36Sopenharmony_ci txq->p_reg = tx_pidx_reg[i]; 76762306a36Sopenharmony_ci txq->c_reg = tx_cidx_reg[i]; 76862306a36Sopenharmony_ci txq->queue_idx = i; 76962306a36Sopenharmony_ci txq->count = alx->tx_ringsz; 77062306a36Sopenharmony_ci txq->netdev = alx->dev; 77162306a36Sopenharmony_ci txq->dev = &alx->hw.pdev->dev; 77262306a36Sopenharmony_ci np->vec_mask |= tx_vect_mask[i]; 77362306a36Sopenharmony_ci alx->int_mask |= tx_vect_mask[i]; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci /* allocate rx queues */ 77762306a36Sopenharmony_ci np = alx->qnapi[0]; 77862306a36Sopenharmony_ci rxq = kzalloc(sizeof(*rxq), GFP_KERNEL); 77962306a36Sopenharmony_ci if (!rxq) 78062306a36Sopenharmony_ci goto err_out; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci np->rxq = rxq; 78362306a36Sopenharmony_ci rxq->np = alx->qnapi[0]; 78462306a36Sopenharmony_ci rxq->queue_idx = 0; 78562306a36Sopenharmony_ci rxq->count = alx->rx_ringsz; 78662306a36Sopenharmony_ci rxq->netdev = alx->dev; 78762306a36Sopenharmony_ci rxq->dev = &alx->hw.pdev->dev; 78862306a36Sopenharmony_ci np->vec_mask |= rx_vect_mask[0]; 78962306a36Sopenharmony_ci alx->int_mask |= rx_vect_mask[0]; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cierr_out: 79462306a36Sopenharmony_ci netdev_err(alx->dev, "error allocating internal structures\n"); 79562306a36Sopenharmony_ci alx_free_napis(alx); 79662306a36Sopenharmony_ci return -ENOMEM; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic const int txq_vec_mapping_shift[] = { 80062306a36Sopenharmony_ci 0, ALX_MSI_MAP_TBL1_TXQ0_SHIFT, 80162306a36Sopenharmony_ci 0, ALX_MSI_MAP_TBL1_TXQ1_SHIFT, 80262306a36Sopenharmony_ci 1, ALX_MSI_MAP_TBL2_TXQ2_SHIFT, 80362306a36Sopenharmony_ci 1, ALX_MSI_MAP_TBL2_TXQ3_SHIFT, 80462306a36Sopenharmony_ci}; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic void alx_config_vector_mapping(struct alx_priv *alx) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 80962306a36Sopenharmony_ci u32 tbl[2] = {0, 0}; 81062306a36Sopenharmony_ci int i, vector, idx, shift; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 81362306a36Sopenharmony_ci /* tx mappings */ 81462306a36Sopenharmony_ci for (i = 0, vector = 1; i < alx->num_txq; i++, vector++) { 81562306a36Sopenharmony_ci idx = txq_vec_mapping_shift[i * 2]; 81662306a36Sopenharmony_ci shift = txq_vec_mapping_shift[i * 2 + 1]; 81762306a36Sopenharmony_ci tbl[idx] |= vector << shift; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* rx mapping */ 82162306a36Sopenharmony_ci tbl[0] |= 1 << ALX_MSI_MAP_TBL1_RXQ0_SHIFT; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_MAP_TBL1, tbl[0]); 82562306a36Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_MAP_TBL2, tbl[1]); 82662306a36Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_ID_MAP, 0); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int alx_enable_msix(struct alx_priv *alx) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci int err, num_vec, num_txq, num_rxq; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci num_txq = min_t(int, num_online_cpus(), ALX_MAX_TX_QUEUES); 83462306a36Sopenharmony_ci num_rxq = 1; 83562306a36Sopenharmony_ci num_vec = max_t(int, num_txq, num_rxq) + 1; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci err = pci_alloc_irq_vectors(alx->hw.pdev, num_vec, num_vec, 83862306a36Sopenharmony_ci PCI_IRQ_MSIX); 83962306a36Sopenharmony_ci if (err < 0) { 84062306a36Sopenharmony_ci netdev_warn(alx->dev, "Enabling MSI-X interrupts failed!\n"); 84162306a36Sopenharmony_ci return err; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci alx->num_vec = num_vec; 84562306a36Sopenharmony_ci alx->num_napi = num_vec - 1; 84662306a36Sopenharmony_ci alx->num_txq = num_txq; 84762306a36Sopenharmony_ci alx->num_rxq = num_rxq; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return err; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic int alx_request_msix(struct alx_priv *alx) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct net_device *netdev = alx->dev; 85562306a36Sopenharmony_ci int i, err, vector = 0, free_vector = 0; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci err = request_irq(pci_irq_vector(alx->hw.pdev, 0), alx_intr_msix_misc, 85862306a36Sopenharmony_ci 0, netdev->name, alx); 85962306a36Sopenharmony_ci if (err) 86062306a36Sopenharmony_ci goto out_err; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) { 86362306a36Sopenharmony_ci struct alx_napi *np = alx->qnapi[i]; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci vector++; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (np->txq && np->rxq) 86862306a36Sopenharmony_ci sprintf(np->irq_lbl, "%s-TxRx-%u", netdev->name, 86962306a36Sopenharmony_ci np->txq->queue_idx); 87062306a36Sopenharmony_ci else if (np->txq) 87162306a36Sopenharmony_ci sprintf(np->irq_lbl, "%s-tx-%u", netdev->name, 87262306a36Sopenharmony_ci np->txq->queue_idx); 87362306a36Sopenharmony_ci else if (np->rxq) 87462306a36Sopenharmony_ci sprintf(np->irq_lbl, "%s-rx-%u", netdev->name, 87562306a36Sopenharmony_ci np->rxq->queue_idx); 87662306a36Sopenharmony_ci else 87762306a36Sopenharmony_ci sprintf(np->irq_lbl, "%s-unused", netdev->name); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci np->vec_idx = vector; 88062306a36Sopenharmony_ci err = request_irq(pci_irq_vector(alx->hw.pdev, vector), 88162306a36Sopenharmony_ci alx_intr_msix_ring, 0, np->irq_lbl, np); 88262306a36Sopenharmony_ci if (err) 88362306a36Sopenharmony_ci goto out_free; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci return 0; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ciout_free: 88862306a36Sopenharmony_ci free_irq(pci_irq_vector(alx->hw.pdev, free_vector++), alx); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci vector--; 89162306a36Sopenharmony_ci for (i = 0; i < vector; i++) 89262306a36Sopenharmony_ci free_irq(pci_irq_vector(alx->hw.pdev,free_vector++), 89362306a36Sopenharmony_ci alx->qnapi[i]); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ciout_err: 89662306a36Sopenharmony_ci return err; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic int alx_init_intr(struct alx_priv *alx) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci int ret; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci ret = pci_alloc_irq_vectors(alx->hw.pdev, 1, 1, 90462306a36Sopenharmony_ci PCI_IRQ_MSI | PCI_IRQ_LEGACY); 90562306a36Sopenharmony_ci if (ret < 0) 90662306a36Sopenharmony_ci return ret; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci alx->num_vec = 1; 90962306a36Sopenharmony_ci alx->num_napi = 1; 91062306a36Sopenharmony_ci alx->num_txq = 1; 91162306a36Sopenharmony_ci alx->num_rxq = 1; 91262306a36Sopenharmony_ci return 0; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic void alx_irq_enable(struct alx_priv *alx) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 91862306a36Sopenharmony_ci int i; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* level-1 interrupt switch */ 92162306a36Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, 0); 92262306a36Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 92362306a36Sopenharmony_ci alx_post_write(hw); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 92662306a36Sopenharmony_ci /* enable all msix irqs */ 92762306a36Sopenharmony_ci for (i = 0; i < alx->num_vec; i++) 92862306a36Sopenharmony_ci alx_mask_msix(hw, i, false); 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic void alx_irq_disable(struct alx_priv *alx) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 93562306a36Sopenharmony_ci int i; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci alx_write_mem32(hw, ALX_ISR, ALX_ISR_DIS); 93862306a36Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, 0); 93962306a36Sopenharmony_ci alx_post_write(hw); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 94262306a36Sopenharmony_ci for (i = 0; i < alx->num_vec; i++) { 94362306a36Sopenharmony_ci alx_mask_msix(hw, i, true); 94462306a36Sopenharmony_ci synchronize_irq(pci_irq_vector(alx->hw.pdev, i)); 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci } else { 94762306a36Sopenharmony_ci synchronize_irq(pci_irq_vector(alx->hw.pdev, 0)); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int alx_realloc_resources(struct alx_priv *alx) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci int err; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci alx_free_rings(alx); 95662306a36Sopenharmony_ci alx_free_napis(alx); 95762306a36Sopenharmony_ci pci_free_irq_vectors(alx->hw.pdev); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci err = alx_init_intr(alx); 96062306a36Sopenharmony_ci if (err) 96162306a36Sopenharmony_ci return err; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci err = alx_alloc_napis(alx); 96462306a36Sopenharmony_ci if (err) 96562306a36Sopenharmony_ci return err; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci err = alx_alloc_rings(alx); 96862306a36Sopenharmony_ci if (err) 96962306a36Sopenharmony_ci return err; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic int alx_request_irq(struct alx_priv *alx) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci struct pci_dev *pdev = alx->hw.pdev; 97762306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 97862306a36Sopenharmony_ci int err; 97962306a36Sopenharmony_ci u32 msi_ctrl; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci msi_ctrl = (hw->imt >> 1) << ALX_MSI_RETRANS_TM_SHIFT; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 98462306a36Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, msi_ctrl); 98562306a36Sopenharmony_ci err = alx_request_msix(alx); 98662306a36Sopenharmony_ci if (!err) 98762306a36Sopenharmony_ci goto out; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* msix request failed, realloc resources */ 99062306a36Sopenharmony_ci err = alx_realloc_resources(alx); 99162306a36Sopenharmony_ci if (err) 99262306a36Sopenharmony_ci goto out; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (alx->hw.pdev->msi_enabled) { 99662306a36Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, 99762306a36Sopenharmony_ci msi_ctrl | ALX_MSI_MASK_SEL_LINE); 99862306a36Sopenharmony_ci err = request_irq(pci_irq_vector(pdev, 0), alx_intr_msi, 0, 99962306a36Sopenharmony_ci alx->dev->name, alx); 100062306a36Sopenharmony_ci if (!err) 100162306a36Sopenharmony_ci goto out; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* fall back to legacy interrupt */ 100462306a36Sopenharmony_ci pci_free_irq_vectors(alx->hw.pdev); 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, 0); 100862306a36Sopenharmony_ci err = request_irq(pci_irq_vector(pdev, 0), alx_intr_legacy, IRQF_SHARED, 100962306a36Sopenharmony_ci alx->dev->name, alx); 101062306a36Sopenharmony_ciout: 101162306a36Sopenharmony_ci if (!err) 101262306a36Sopenharmony_ci alx_config_vector_mapping(alx); 101362306a36Sopenharmony_ci else 101462306a36Sopenharmony_ci netdev_err(alx->dev, "IRQ registration failed!\n"); 101562306a36Sopenharmony_ci return err; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cistatic void alx_free_irq(struct alx_priv *alx) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci struct pci_dev *pdev = alx->hw.pdev; 102162306a36Sopenharmony_ci int i; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, 0), alx); 102462306a36Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 102562306a36Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) 102662306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, i + 1), alx->qnapi[i]); 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic int alx_identify_hw(struct alx_priv *alx) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 103562306a36Sopenharmony_ci int rev = alx_hw_revision(hw); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (rev > ALX_REV_C0) 103862306a36Sopenharmony_ci return -EINVAL; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci hw->max_dma_chnl = rev >= ALX_REV_B0 ? 4 : 2; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return 0; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic int alx_init_sw(struct alx_priv *alx) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct pci_dev *pdev = alx->hw.pdev; 104862306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 104962306a36Sopenharmony_ci int err; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci err = alx_identify_hw(alx); 105262306a36Sopenharmony_ci if (err) { 105362306a36Sopenharmony_ci dev_err(&pdev->dev, "unrecognized chip, aborting\n"); 105462306a36Sopenharmony_ci return err; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci alx->hw.lnk_patch = 105862306a36Sopenharmony_ci pdev->device == ALX_DEV_ID_AR8161 && 105962306a36Sopenharmony_ci pdev->subsystem_vendor == PCI_VENDOR_ID_ATTANSIC && 106062306a36Sopenharmony_ci pdev->subsystem_device == 0x0091 && 106162306a36Sopenharmony_ci pdev->revision == 0; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci hw->smb_timer = 400; 106462306a36Sopenharmony_ci hw->mtu = alx->dev->mtu; 106562306a36Sopenharmony_ci alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu); 106662306a36Sopenharmony_ci /* MTU range: 34 - 9256 */ 106762306a36Sopenharmony_ci alx->dev->min_mtu = 34; 106862306a36Sopenharmony_ci alx->dev->max_mtu = ALX_MAX_FRAME_LEN(ALX_MAX_FRAME_SIZE); 106962306a36Sopenharmony_ci alx->tx_ringsz = 256; 107062306a36Sopenharmony_ci alx->rx_ringsz = 512; 107162306a36Sopenharmony_ci hw->imt = 200; 107262306a36Sopenharmony_ci alx->int_mask = ALX_ISR_MISC; 107362306a36Sopenharmony_ci hw->dma_chnl = hw->max_dma_chnl; 107462306a36Sopenharmony_ci hw->ith_tpd = alx->tx_ringsz / 3; 107562306a36Sopenharmony_ci hw->link_speed = SPEED_UNKNOWN; 107662306a36Sopenharmony_ci hw->duplex = DUPLEX_UNKNOWN; 107762306a36Sopenharmony_ci hw->adv_cfg = ADVERTISED_Autoneg | 107862306a36Sopenharmony_ci ADVERTISED_10baseT_Half | 107962306a36Sopenharmony_ci ADVERTISED_10baseT_Full | 108062306a36Sopenharmony_ci ADVERTISED_100baseT_Full | 108162306a36Sopenharmony_ci ADVERTISED_100baseT_Half | 108262306a36Sopenharmony_ci ADVERTISED_1000baseT_Full; 108362306a36Sopenharmony_ci hw->flowctrl = ALX_FC_ANEG | ALX_FC_RX | ALX_FC_TX; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci hw->rx_ctrl = ALX_MAC_CTRL_WOLSPED_SWEN | 108662306a36Sopenharmony_ci ALX_MAC_CTRL_MHASH_ALG_HI5B | 108762306a36Sopenharmony_ci ALX_MAC_CTRL_BRD_EN | 108862306a36Sopenharmony_ci ALX_MAC_CTRL_PCRCE | 108962306a36Sopenharmony_ci ALX_MAC_CTRL_CRCE | 109062306a36Sopenharmony_ci ALX_MAC_CTRL_RXFC_EN | 109162306a36Sopenharmony_ci ALX_MAC_CTRL_TXFC_EN | 109262306a36Sopenharmony_ci 7 << ALX_MAC_CTRL_PRMBLEN_SHIFT; 109362306a36Sopenharmony_ci mutex_init(&alx->mtx); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci return 0; 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic netdev_features_t alx_fix_features(struct net_device *netdev, 110062306a36Sopenharmony_ci netdev_features_t features) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci if (netdev->mtu > ALX_MAX_TSO_PKT_SIZE) 110362306a36Sopenharmony_ci features &= ~(NETIF_F_TSO | NETIF_F_TSO6); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci return features; 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_cistatic void alx_netif_stop(struct alx_priv *alx) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci int i; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci netif_trans_update(alx->dev); 111362306a36Sopenharmony_ci if (netif_carrier_ok(alx->dev)) { 111462306a36Sopenharmony_ci netif_carrier_off(alx->dev); 111562306a36Sopenharmony_ci netif_tx_disable(alx->dev); 111662306a36Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) 111762306a36Sopenharmony_ci napi_disable(&alx->qnapi[i]->napi); 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistatic void alx_halt(struct alx_priv *alx) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci lockdep_assert_held(&alx->mtx); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci alx_netif_stop(alx); 112862306a36Sopenharmony_ci hw->link_speed = SPEED_UNKNOWN; 112962306a36Sopenharmony_ci hw->duplex = DUPLEX_UNKNOWN; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci alx_reset_mac(hw); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci /* disable l0s/l1 */ 113462306a36Sopenharmony_ci alx_enable_aspm(hw, false, false); 113562306a36Sopenharmony_ci alx_irq_disable(alx); 113662306a36Sopenharmony_ci alx_free_buffers(alx); 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic void alx_configure(struct alx_priv *alx) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci alx_configure_basic(hw); 114462306a36Sopenharmony_ci alx_disable_rss(hw); 114562306a36Sopenharmony_ci __alx_set_rx_mode(alx->dev); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl); 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cistatic void alx_activate(struct alx_priv *alx) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci lockdep_assert_held(&alx->mtx); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci /* hardware setting lost, restore it */ 115562306a36Sopenharmony_ci alx_reinit_rings(alx); 115662306a36Sopenharmony_ci alx_configure(alx); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* clear old interrupts */ 115962306a36Sopenharmony_ci alx_write_mem32(&alx->hw, ALX_ISR, ~(u32)ALX_ISR_DIS); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci alx_irq_enable(alx); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci alx_schedule_link_check(alx); 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_cistatic void alx_reinit(struct alx_priv *alx) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci lockdep_assert_held(&alx->mtx); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci alx_halt(alx); 117162306a36Sopenharmony_ci alx_activate(alx); 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_cistatic int alx_change_mtu(struct net_device *netdev, int mtu) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 117762306a36Sopenharmony_ci int max_frame = ALX_MAX_FRAME_LEN(mtu); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci netdev->mtu = mtu; 118062306a36Sopenharmony_ci alx->hw.mtu = mtu; 118162306a36Sopenharmony_ci alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE); 118262306a36Sopenharmony_ci netdev_update_features(netdev); 118362306a36Sopenharmony_ci if (netif_running(netdev)) { 118462306a36Sopenharmony_ci mutex_lock(&alx->mtx); 118562306a36Sopenharmony_ci alx_reinit(alx); 118662306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci return 0; 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_cistatic void alx_netif_start(struct alx_priv *alx) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci int i; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci netif_tx_wake_all_queues(alx->dev); 119662306a36Sopenharmony_ci for (i = 0; i < alx->num_napi; i++) 119762306a36Sopenharmony_ci napi_enable(&alx->qnapi[i]->napi); 119862306a36Sopenharmony_ci netif_carrier_on(alx->dev); 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_cistatic int __alx_open(struct alx_priv *alx, bool resume) 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci int err; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci err = alx_enable_msix(alx); 120662306a36Sopenharmony_ci if (err < 0) { 120762306a36Sopenharmony_ci err = alx_init_intr(alx); 120862306a36Sopenharmony_ci if (err) 120962306a36Sopenharmony_ci return err; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (!resume) 121362306a36Sopenharmony_ci netif_carrier_off(alx->dev); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci err = alx_alloc_napis(alx); 121662306a36Sopenharmony_ci if (err) 121762306a36Sopenharmony_ci goto out_disable_adv_intr; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci err = alx_alloc_rings(alx); 122062306a36Sopenharmony_ci if (err) 122162306a36Sopenharmony_ci goto out_free_rings; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci alx_configure(alx); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci err = alx_request_irq(alx); 122662306a36Sopenharmony_ci if (err) 122762306a36Sopenharmony_ci goto out_free_rings; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* must be called after alx_request_irq because the chip stops working 123062306a36Sopenharmony_ci * if we copy the dma addresses in alx_init_ring_ptrs twice when 123162306a36Sopenharmony_ci * requesting msi-x interrupts failed 123262306a36Sopenharmony_ci */ 123362306a36Sopenharmony_ci alx_reinit_rings(alx); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci netif_set_real_num_tx_queues(alx->dev, alx->num_txq); 123662306a36Sopenharmony_ci netif_set_real_num_rx_queues(alx->dev, alx->num_rxq); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* clear old interrupts */ 123962306a36Sopenharmony_ci alx_write_mem32(&alx->hw, ALX_ISR, ~(u32)ALX_ISR_DIS); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci alx_irq_enable(alx); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (!resume) 124462306a36Sopenharmony_ci netif_tx_start_all_queues(alx->dev); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci alx_schedule_link_check(alx); 124762306a36Sopenharmony_ci return 0; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ciout_free_rings: 125062306a36Sopenharmony_ci alx_free_rings(alx); 125162306a36Sopenharmony_ci alx_free_napis(alx); 125262306a36Sopenharmony_ciout_disable_adv_intr: 125362306a36Sopenharmony_ci pci_free_irq_vectors(alx->hw.pdev); 125462306a36Sopenharmony_ci return err; 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cistatic void __alx_stop(struct alx_priv *alx) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci lockdep_assert_held(&alx->mtx); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci alx_free_irq(alx); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci cancel_work_sync(&alx->link_check_wk); 126462306a36Sopenharmony_ci cancel_work_sync(&alx->reset_wk); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci alx_halt(alx); 126762306a36Sopenharmony_ci alx_free_rings(alx); 126862306a36Sopenharmony_ci alx_free_napis(alx); 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_cistatic const char *alx_speed_desc(struct alx_hw *hw) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci switch (alx_speed_to_ethadv(hw->link_speed, hw->duplex)) { 127462306a36Sopenharmony_ci case ADVERTISED_1000baseT_Full: 127562306a36Sopenharmony_ci return "1 Gbps Full"; 127662306a36Sopenharmony_ci case ADVERTISED_100baseT_Full: 127762306a36Sopenharmony_ci return "100 Mbps Full"; 127862306a36Sopenharmony_ci case ADVERTISED_100baseT_Half: 127962306a36Sopenharmony_ci return "100 Mbps Half"; 128062306a36Sopenharmony_ci case ADVERTISED_10baseT_Full: 128162306a36Sopenharmony_ci return "10 Mbps Full"; 128262306a36Sopenharmony_ci case ADVERTISED_10baseT_Half: 128362306a36Sopenharmony_ci return "10 Mbps Half"; 128462306a36Sopenharmony_ci default: 128562306a36Sopenharmony_ci return "Unknown speed"; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic void alx_check_link(struct alx_priv *alx) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 129262306a36Sopenharmony_ci unsigned long flags; 129362306a36Sopenharmony_ci int old_speed; 129462306a36Sopenharmony_ci int err; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci lockdep_assert_held(&alx->mtx); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci /* clear PHY internal interrupt status, otherwise the main 129962306a36Sopenharmony_ci * interrupt status will be asserted forever 130062306a36Sopenharmony_ci */ 130162306a36Sopenharmony_ci alx_clear_phy_intr(hw); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci old_speed = hw->link_speed; 130462306a36Sopenharmony_ci err = alx_read_phy_link(hw); 130562306a36Sopenharmony_ci if (err < 0) 130662306a36Sopenharmony_ci goto reset; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci spin_lock_irqsave(&alx->irq_lock, flags); 130962306a36Sopenharmony_ci alx->int_mask |= ALX_ISR_PHY; 131062306a36Sopenharmony_ci alx_write_mem32(hw, ALX_IMR, alx->int_mask); 131162306a36Sopenharmony_ci spin_unlock_irqrestore(&alx->irq_lock, flags); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci if (old_speed == hw->link_speed) 131462306a36Sopenharmony_ci return; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (hw->link_speed != SPEED_UNKNOWN) { 131762306a36Sopenharmony_ci netif_info(alx, link, alx->dev, 131862306a36Sopenharmony_ci "NIC Up: %s\n", alx_speed_desc(hw)); 131962306a36Sopenharmony_ci alx_post_phy_link(hw); 132062306a36Sopenharmony_ci alx_enable_aspm(hw, true, true); 132162306a36Sopenharmony_ci alx_start_mac(hw); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (old_speed == SPEED_UNKNOWN) 132462306a36Sopenharmony_ci alx_netif_start(alx); 132562306a36Sopenharmony_ci } else { 132662306a36Sopenharmony_ci /* link is now down */ 132762306a36Sopenharmony_ci alx_netif_stop(alx); 132862306a36Sopenharmony_ci netif_info(alx, link, alx->dev, "Link Down\n"); 132962306a36Sopenharmony_ci err = alx_reset_mac(hw); 133062306a36Sopenharmony_ci if (err) 133162306a36Sopenharmony_ci goto reset; 133262306a36Sopenharmony_ci alx_irq_disable(alx); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* MAC reset causes all HW settings to be lost, restore all */ 133562306a36Sopenharmony_ci err = alx_reinit_rings(alx); 133662306a36Sopenharmony_ci if (err) 133762306a36Sopenharmony_ci goto reset; 133862306a36Sopenharmony_ci alx_configure(alx); 133962306a36Sopenharmony_ci alx_enable_aspm(hw, false, true); 134062306a36Sopenharmony_ci alx_post_phy_link(hw); 134162306a36Sopenharmony_ci alx_irq_enable(alx); 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci return; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cireset: 134762306a36Sopenharmony_ci alx_schedule_reset(alx); 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_cistatic int alx_open(struct net_device *netdev) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 135362306a36Sopenharmony_ci int ret; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci mutex_lock(&alx->mtx); 135662306a36Sopenharmony_ci ret = __alx_open(alx, false); 135762306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci return ret; 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cistatic int alx_stop(struct net_device *netdev) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci mutex_lock(&alx->mtx); 136762306a36Sopenharmony_ci __alx_stop(alx); 136862306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci return 0; 137162306a36Sopenharmony_ci} 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cistatic void alx_link_check(struct work_struct *work) 137462306a36Sopenharmony_ci{ 137562306a36Sopenharmony_ci struct alx_priv *alx; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci alx = container_of(work, struct alx_priv, link_check_wk); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci mutex_lock(&alx->mtx); 138062306a36Sopenharmony_ci alx_check_link(alx); 138162306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic void alx_reset(struct work_struct *work) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci struct alx_priv *alx = container_of(work, struct alx_priv, reset_wk); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci mutex_lock(&alx->mtx); 138962306a36Sopenharmony_ci alx_reinit(alx); 139062306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic int alx_tpd_req(struct sk_buff *skb) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci int num; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci num = skb_shinfo(skb)->nr_frags + 1; 139862306a36Sopenharmony_ci /* we need one extra descriptor for LSOv2 */ 139962306a36Sopenharmony_ci if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) 140062306a36Sopenharmony_ci num++; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci return num; 140362306a36Sopenharmony_ci} 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_cistatic int alx_tx_csum(struct sk_buff *skb, struct alx_txd *first) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci u8 cso, css; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 141062306a36Sopenharmony_ci return 0; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci cso = skb_checksum_start_offset(skb); 141362306a36Sopenharmony_ci if (cso & 1) 141462306a36Sopenharmony_ci return -EINVAL; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci css = cso + skb->csum_offset; 141762306a36Sopenharmony_ci first->word1 |= cpu_to_le32((cso >> 1) << TPD_CXSUMSTART_SHIFT); 141862306a36Sopenharmony_ci first->word1 |= cpu_to_le32((css >> 1) << TPD_CXSUMOFFSET_SHIFT); 141962306a36Sopenharmony_ci first->word1 |= cpu_to_le32(1 << TPD_CXSUM_EN_SHIFT); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci return 0; 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cistatic int alx_tso(struct sk_buff *skb, struct alx_txd *first) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci int err; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 142962306a36Sopenharmony_ci return 0; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (!skb_is_gso(skb)) 143262306a36Sopenharmony_ci return 0; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci err = skb_cow_head(skb, 0); 143562306a36Sopenharmony_ci if (err < 0) 143662306a36Sopenharmony_ci return err; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 143962306a36Sopenharmony_ci struct iphdr *iph = ip_hdr(skb); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci iph->check = 0; 144262306a36Sopenharmony_ci tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, 144362306a36Sopenharmony_ci 0, IPPROTO_TCP, 0); 144462306a36Sopenharmony_ci first->word1 |= 1 << TPD_IPV4_SHIFT; 144562306a36Sopenharmony_ci } else if (skb_is_gso_v6(skb)) { 144662306a36Sopenharmony_ci tcp_v6_gso_csum_prep(skb); 144762306a36Sopenharmony_ci /* LSOv2: the first TPD only provides the packet length */ 144862306a36Sopenharmony_ci first->adrl.l.pkt_len = skb->len; 144962306a36Sopenharmony_ci first->word1 |= 1 << TPD_LSO_V2_SHIFT; 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci first->word1 |= 1 << TPD_LSO_EN_SHIFT; 145362306a36Sopenharmony_ci first->word1 |= (skb_transport_offset(skb) & 145462306a36Sopenharmony_ci TPD_L4HDROFFSET_MASK) << TPD_L4HDROFFSET_SHIFT; 145562306a36Sopenharmony_ci first->word1 |= (skb_shinfo(skb)->gso_size & 145662306a36Sopenharmony_ci TPD_MSS_MASK) << TPD_MSS_SHIFT; 145762306a36Sopenharmony_ci return 1; 145862306a36Sopenharmony_ci} 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_cistatic int alx_map_tx_skb(struct alx_tx_queue *txq, struct sk_buff *skb) 146162306a36Sopenharmony_ci{ 146262306a36Sopenharmony_ci struct alx_txd *tpd, *first_tpd; 146362306a36Sopenharmony_ci dma_addr_t dma; 146462306a36Sopenharmony_ci int maplen, f, first_idx = txq->write_idx; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci first_tpd = &txq->tpd[txq->write_idx]; 146762306a36Sopenharmony_ci tpd = first_tpd; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci if (tpd->word1 & (1 << TPD_LSO_V2_SHIFT)) { 147062306a36Sopenharmony_ci if (++txq->write_idx == txq->count) 147162306a36Sopenharmony_ci txq->write_idx = 0; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci tpd = &txq->tpd[txq->write_idx]; 147462306a36Sopenharmony_ci tpd->len = first_tpd->len; 147562306a36Sopenharmony_ci tpd->vlan_tag = first_tpd->vlan_tag; 147662306a36Sopenharmony_ci tpd->word1 = first_tpd->word1; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci maplen = skb_headlen(skb); 148062306a36Sopenharmony_ci dma = dma_map_single(txq->dev, skb->data, maplen, 148162306a36Sopenharmony_ci DMA_TO_DEVICE); 148262306a36Sopenharmony_ci if (dma_mapping_error(txq->dev, dma)) 148362306a36Sopenharmony_ci goto err_dma; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen); 148662306a36Sopenharmony_ci dma_unmap_addr_set(&txq->bufs[txq->write_idx], dma, dma); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci tpd->adrl.addr = cpu_to_le64(dma); 148962306a36Sopenharmony_ci tpd->len = cpu_to_le16(maplen); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) { 149262306a36Sopenharmony_ci skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci if (++txq->write_idx == txq->count) 149562306a36Sopenharmony_ci txq->write_idx = 0; 149662306a36Sopenharmony_ci tpd = &txq->tpd[txq->write_idx]; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci tpd->word1 = first_tpd->word1; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci maplen = skb_frag_size(frag); 150162306a36Sopenharmony_ci dma = skb_frag_dma_map(txq->dev, frag, 0, 150262306a36Sopenharmony_ci maplen, DMA_TO_DEVICE); 150362306a36Sopenharmony_ci if (dma_mapping_error(txq->dev, dma)) 150462306a36Sopenharmony_ci goto err_dma; 150562306a36Sopenharmony_ci dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen); 150662306a36Sopenharmony_ci dma_unmap_addr_set(&txq->bufs[txq->write_idx], dma, dma); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci tpd->adrl.addr = cpu_to_le64(dma); 150962306a36Sopenharmony_ci tpd->len = cpu_to_le16(maplen); 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci /* last TPD, set EOP flag and store skb */ 151362306a36Sopenharmony_ci tpd->word1 |= cpu_to_le32(1 << TPD_EOP_SHIFT); 151462306a36Sopenharmony_ci txq->bufs[txq->write_idx].skb = skb; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci if (++txq->write_idx == txq->count) 151762306a36Sopenharmony_ci txq->write_idx = 0; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci return 0; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_cierr_dma: 152262306a36Sopenharmony_ci f = first_idx; 152362306a36Sopenharmony_ci while (f != txq->write_idx) { 152462306a36Sopenharmony_ci alx_free_txbuf(txq, f); 152562306a36Sopenharmony_ci if (++f == txq->count) 152662306a36Sopenharmony_ci f = 0; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci return -ENOMEM; 152962306a36Sopenharmony_ci} 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_cistatic netdev_tx_t alx_start_xmit_ring(struct sk_buff *skb, 153262306a36Sopenharmony_ci struct alx_tx_queue *txq) 153362306a36Sopenharmony_ci{ 153462306a36Sopenharmony_ci struct alx_priv *alx; 153562306a36Sopenharmony_ci struct alx_txd *first; 153662306a36Sopenharmony_ci int tso; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci alx = netdev_priv(txq->netdev); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci if (alx_tpd_avail(txq) < alx_tpd_req(skb)) { 154162306a36Sopenharmony_ci netif_tx_stop_queue(alx_get_tx_queue(txq)); 154262306a36Sopenharmony_ci goto drop; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci first = &txq->tpd[txq->write_idx]; 154662306a36Sopenharmony_ci memset(first, 0, sizeof(*first)); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci tso = alx_tso(skb, first); 154962306a36Sopenharmony_ci if (tso < 0) 155062306a36Sopenharmony_ci goto drop; 155162306a36Sopenharmony_ci else if (!tso && alx_tx_csum(skb, first)) 155262306a36Sopenharmony_ci goto drop; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci if (alx_map_tx_skb(txq, skb) < 0) 155562306a36Sopenharmony_ci goto drop; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci netdev_tx_sent_queue(alx_get_tx_queue(txq), skb->len); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci /* flush updates before updating hardware */ 156062306a36Sopenharmony_ci wmb(); 156162306a36Sopenharmony_ci alx_write_mem16(&alx->hw, txq->p_reg, txq->write_idx); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci if (alx_tpd_avail(txq) < txq->count / 8) 156462306a36Sopenharmony_ci netif_tx_stop_queue(alx_get_tx_queue(txq)); 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci return NETDEV_TX_OK; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_cidrop: 156962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 157062306a36Sopenharmony_ci return NETDEV_TX_OK; 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic netdev_tx_t alx_start_xmit(struct sk_buff *skb, 157462306a36Sopenharmony_ci struct net_device *netdev) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 157762306a36Sopenharmony_ci return alx_start_xmit_ring(skb, alx_tx_queue_mapping(alx, skb)); 157862306a36Sopenharmony_ci} 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_cistatic void alx_tx_timeout(struct net_device *dev, unsigned int txqueue) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(dev); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci alx_schedule_reset(alx); 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic int alx_mdio_read(struct net_device *netdev, 158862306a36Sopenharmony_ci int prtad, int devad, u16 addr) 158962306a36Sopenharmony_ci{ 159062306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 159162306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 159262306a36Sopenharmony_ci u16 val; 159362306a36Sopenharmony_ci int err; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (prtad != hw->mdio.prtad) 159662306a36Sopenharmony_ci return -EINVAL; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci if (devad == MDIO_DEVAD_NONE) 159962306a36Sopenharmony_ci err = alx_read_phy_reg(hw, addr, &val); 160062306a36Sopenharmony_ci else 160162306a36Sopenharmony_ci err = alx_read_phy_ext(hw, devad, addr, &val); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if (err) 160462306a36Sopenharmony_ci return err; 160562306a36Sopenharmony_ci return val; 160662306a36Sopenharmony_ci} 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic int alx_mdio_write(struct net_device *netdev, 160962306a36Sopenharmony_ci int prtad, int devad, u16 addr, u16 val) 161062306a36Sopenharmony_ci{ 161162306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 161262306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci if (prtad != hw->mdio.prtad) 161562306a36Sopenharmony_ci return -EINVAL; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if (devad == MDIO_DEVAD_NONE) 161862306a36Sopenharmony_ci return alx_write_phy_reg(hw, addr, val); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci return alx_write_phy_ext(hw, devad, addr, val); 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_cistatic int alx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 162462306a36Sopenharmony_ci{ 162562306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci if (!netif_running(netdev)) 162862306a36Sopenharmony_ci return -EAGAIN; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci return mdio_mii_ioctl(&alx->hw.mdio, if_mii(ifr), cmd); 163162306a36Sopenharmony_ci} 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 163462306a36Sopenharmony_cistatic void alx_poll_controller(struct net_device *netdev) 163562306a36Sopenharmony_ci{ 163662306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(netdev); 163762306a36Sopenharmony_ci int i; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci if (alx->hw.pdev->msix_enabled) { 164062306a36Sopenharmony_ci alx_intr_msix_misc(0, alx); 164162306a36Sopenharmony_ci for (i = 0; i < alx->num_txq; i++) 164262306a36Sopenharmony_ci alx_intr_msix_ring(0, alx->qnapi[i]); 164362306a36Sopenharmony_ci } else if (alx->hw.pdev->msi_enabled) 164462306a36Sopenharmony_ci alx_intr_msi(0, alx); 164562306a36Sopenharmony_ci else 164662306a36Sopenharmony_ci alx_intr_legacy(0, alx); 164762306a36Sopenharmony_ci} 164862306a36Sopenharmony_ci#endif 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_cistatic void alx_get_stats64(struct net_device *dev, 165162306a36Sopenharmony_ci struct rtnl_link_stats64 *net_stats) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci struct alx_priv *alx = netdev_priv(dev); 165462306a36Sopenharmony_ci struct alx_hw_stats *hw_stats = &alx->hw.stats; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci spin_lock(&alx->stats_lock); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci alx_update_hw_stats(&alx->hw); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci net_stats->tx_bytes = hw_stats->tx_byte_cnt; 166162306a36Sopenharmony_ci net_stats->rx_bytes = hw_stats->rx_byte_cnt; 166262306a36Sopenharmony_ci net_stats->multicast = hw_stats->rx_mcast; 166362306a36Sopenharmony_ci net_stats->collisions = hw_stats->tx_single_col + 166462306a36Sopenharmony_ci hw_stats->tx_multi_col + 166562306a36Sopenharmony_ci hw_stats->tx_late_col + 166662306a36Sopenharmony_ci hw_stats->tx_abort_col; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci net_stats->rx_errors = hw_stats->rx_frag + 166962306a36Sopenharmony_ci hw_stats->rx_fcs_err + 167062306a36Sopenharmony_ci hw_stats->rx_len_err + 167162306a36Sopenharmony_ci hw_stats->rx_ov_sz + 167262306a36Sopenharmony_ci hw_stats->rx_ov_rrd + 167362306a36Sopenharmony_ci hw_stats->rx_align_err + 167462306a36Sopenharmony_ci hw_stats->rx_ov_rxf; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci net_stats->rx_fifo_errors = hw_stats->rx_ov_rxf; 167762306a36Sopenharmony_ci net_stats->rx_length_errors = hw_stats->rx_len_err; 167862306a36Sopenharmony_ci net_stats->rx_crc_errors = hw_stats->rx_fcs_err; 167962306a36Sopenharmony_ci net_stats->rx_frame_errors = hw_stats->rx_align_err; 168062306a36Sopenharmony_ci net_stats->rx_dropped = hw_stats->rx_ov_rrd; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci net_stats->tx_errors = hw_stats->tx_late_col + 168362306a36Sopenharmony_ci hw_stats->tx_abort_col + 168462306a36Sopenharmony_ci hw_stats->tx_underrun + 168562306a36Sopenharmony_ci hw_stats->tx_trunc; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci net_stats->tx_aborted_errors = hw_stats->tx_abort_col; 168862306a36Sopenharmony_ci net_stats->tx_fifo_errors = hw_stats->tx_underrun; 168962306a36Sopenharmony_ci net_stats->tx_window_errors = hw_stats->tx_late_col; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci net_stats->tx_packets = hw_stats->tx_ok + net_stats->tx_errors; 169262306a36Sopenharmony_ci net_stats->rx_packets = hw_stats->rx_ok + net_stats->rx_errors; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci spin_unlock(&alx->stats_lock); 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_cistatic const struct net_device_ops alx_netdev_ops = { 169862306a36Sopenharmony_ci .ndo_open = alx_open, 169962306a36Sopenharmony_ci .ndo_stop = alx_stop, 170062306a36Sopenharmony_ci .ndo_start_xmit = alx_start_xmit, 170162306a36Sopenharmony_ci .ndo_get_stats64 = alx_get_stats64, 170262306a36Sopenharmony_ci .ndo_set_rx_mode = alx_set_rx_mode, 170362306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 170462306a36Sopenharmony_ci .ndo_set_mac_address = alx_set_mac_address, 170562306a36Sopenharmony_ci .ndo_change_mtu = alx_change_mtu, 170662306a36Sopenharmony_ci .ndo_eth_ioctl = alx_ioctl, 170762306a36Sopenharmony_ci .ndo_tx_timeout = alx_tx_timeout, 170862306a36Sopenharmony_ci .ndo_fix_features = alx_fix_features, 170962306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 171062306a36Sopenharmony_ci .ndo_poll_controller = alx_poll_controller, 171162306a36Sopenharmony_ci#endif 171262306a36Sopenharmony_ci}; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_cistatic int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 171562306a36Sopenharmony_ci{ 171662306a36Sopenharmony_ci struct net_device *netdev; 171762306a36Sopenharmony_ci struct alx_priv *alx; 171862306a36Sopenharmony_ci struct alx_hw *hw; 171962306a36Sopenharmony_ci bool phy_configured; 172062306a36Sopenharmony_ci int err; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci err = pci_enable_device_mem(pdev); 172362306a36Sopenharmony_ci if (err) 172462306a36Sopenharmony_ci return err; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* The alx chip can DMA to 64-bit addresses, but it uses a single 172762306a36Sopenharmony_ci * shared register for the high 32 bits, so only a single, aligned, 172862306a36Sopenharmony_ci * 4 GB physical address range can be used for descriptors. 172962306a36Sopenharmony_ci */ 173062306a36Sopenharmony_ci if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) { 173162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "DMA to 64-BIT addresses\n"); 173262306a36Sopenharmony_ci } else { 173362306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 173462306a36Sopenharmony_ci if (err) { 173562306a36Sopenharmony_ci dev_err(&pdev->dev, "No usable DMA config, aborting\n"); 173662306a36Sopenharmony_ci goto out_pci_disable; 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci } 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci err = pci_request_mem_regions(pdev, alx_drv_name); 174162306a36Sopenharmony_ci if (err) { 174262306a36Sopenharmony_ci dev_err(&pdev->dev, 174362306a36Sopenharmony_ci "pci_request_mem_regions failed\n"); 174462306a36Sopenharmony_ci goto out_pci_disable; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci pci_set_master(pdev); 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci if (!pdev->pm_cap) { 175062306a36Sopenharmony_ci dev_err(&pdev->dev, 175162306a36Sopenharmony_ci "Can't find power management capability, aborting\n"); 175262306a36Sopenharmony_ci err = -EIO; 175362306a36Sopenharmony_ci goto out_pci_release; 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci netdev = alloc_etherdev_mqs(sizeof(*alx), 175762306a36Sopenharmony_ci ALX_MAX_TX_QUEUES, 1); 175862306a36Sopenharmony_ci if (!netdev) { 175962306a36Sopenharmony_ci err = -ENOMEM; 176062306a36Sopenharmony_ci goto out_pci_release; 176162306a36Sopenharmony_ci } 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 176462306a36Sopenharmony_ci alx = netdev_priv(netdev); 176562306a36Sopenharmony_ci spin_lock_init(&alx->hw.mdio_lock); 176662306a36Sopenharmony_ci spin_lock_init(&alx->irq_lock); 176762306a36Sopenharmony_ci spin_lock_init(&alx->stats_lock); 176862306a36Sopenharmony_ci alx->dev = netdev; 176962306a36Sopenharmony_ci alx->hw.pdev = pdev; 177062306a36Sopenharmony_ci alx->msg_enable = NETIF_MSG_LINK | NETIF_MSG_HW | NETIF_MSG_IFUP | 177162306a36Sopenharmony_ci NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR | NETIF_MSG_WOL; 177262306a36Sopenharmony_ci hw = &alx->hw; 177362306a36Sopenharmony_ci pci_set_drvdata(pdev, alx); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci hw->hw_addr = pci_ioremap_bar(pdev, 0); 177662306a36Sopenharmony_ci if (!hw->hw_addr) { 177762306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot map device registers\n"); 177862306a36Sopenharmony_ci err = -EIO; 177962306a36Sopenharmony_ci goto out_free_netdev; 178062306a36Sopenharmony_ci } 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci netdev->netdev_ops = &alx_netdev_ops; 178362306a36Sopenharmony_ci netdev->ethtool_ops = &alx_ethtool_ops; 178462306a36Sopenharmony_ci netdev->irq = pci_irq_vector(pdev, 0); 178562306a36Sopenharmony_ci netdev->watchdog_timeo = ALX_WATCHDOG_TIME; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci if (ent->driver_data & ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG) 178862306a36Sopenharmony_ci pdev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci err = alx_init_sw(alx); 179162306a36Sopenharmony_ci if (err) { 179262306a36Sopenharmony_ci dev_err(&pdev->dev, "net device private data init failed\n"); 179362306a36Sopenharmony_ci goto out_unmap; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci mutex_lock(&alx->mtx); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci alx_reset_pcie(hw); 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci phy_configured = alx_phy_configured(hw); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci if (!phy_configured) 180362306a36Sopenharmony_ci alx_reset_phy(hw); 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci err = alx_reset_mac(hw); 180662306a36Sopenharmony_ci if (err) { 180762306a36Sopenharmony_ci dev_err(&pdev->dev, "MAC Reset failed, error = %d\n", err); 180862306a36Sopenharmony_ci goto out_unlock; 180962306a36Sopenharmony_ci } 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci /* setup link to put it in a known good starting state */ 181262306a36Sopenharmony_ci if (!phy_configured) { 181362306a36Sopenharmony_ci err = alx_setup_speed_duplex(hw, hw->adv_cfg, hw->flowctrl); 181462306a36Sopenharmony_ci if (err) { 181562306a36Sopenharmony_ci dev_err(&pdev->dev, 181662306a36Sopenharmony_ci "failed to configure PHY speed/duplex (err=%d)\n", 181762306a36Sopenharmony_ci err); 181862306a36Sopenharmony_ci goto out_unlock; 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci } 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci netdev->hw_features = NETIF_F_SG | 182362306a36Sopenharmony_ci NETIF_F_HW_CSUM | 182462306a36Sopenharmony_ci NETIF_F_RXCSUM | 182562306a36Sopenharmony_ci NETIF_F_TSO | 182662306a36Sopenharmony_ci NETIF_F_TSO6; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci if (alx_get_perm_macaddr(hw, hw->perm_addr)) { 182962306a36Sopenharmony_ci dev_warn(&pdev->dev, 183062306a36Sopenharmony_ci "Invalid permanent address programmed, using random one\n"); 183162306a36Sopenharmony_ci eth_hw_addr_random(netdev); 183262306a36Sopenharmony_ci memcpy(hw->perm_addr, netdev->dev_addr, netdev->addr_len); 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci memcpy(hw->mac_addr, hw->perm_addr, ETH_ALEN); 183662306a36Sopenharmony_ci eth_hw_addr_set(netdev, hw->mac_addr); 183762306a36Sopenharmony_ci memcpy(netdev->perm_addr, hw->perm_addr, ETH_ALEN); 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci hw->mdio.prtad = 0; 184062306a36Sopenharmony_ci hw->mdio.mmds = 0; 184162306a36Sopenharmony_ci hw->mdio.dev = netdev; 184262306a36Sopenharmony_ci hw->mdio.mode_support = MDIO_SUPPORTS_C45 | 184362306a36Sopenharmony_ci MDIO_SUPPORTS_C22 | 184462306a36Sopenharmony_ci MDIO_EMULATE_C22; 184562306a36Sopenharmony_ci hw->mdio.mdio_read = alx_mdio_read; 184662306a36Sopenharmony_ci hw->mdio.mdio_write = alx_mdio_write; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci if (!alx_get_phy_info(hw)) { 184962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to identify PHY\n"); 185062306a36Sopenharmony_ci err = -EIO; 185162306a36Sopenharmony_ci goto out_unlock; 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci INIT_WORK(&alx->link_check_wk, alx_link_check); 185762306a36Sopenharmony_ci INIT_WORK(&alx->reset_wk, alx_reset); 185862306a36Sopenharmony_ci netif_carrier_off(netdev); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci err = register_netdev(netdev); 186162306a36Sopenharmony_ci if (err) { 186262306a36Sopenharmony_ci dev_err(&pdev->dev, "register netdevice failed\n"); 186362306a36Sopenharmony_ci goto out_unmap; 186462306a36Sopenharmony_ci } 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci netdev_info(netdev, 186762306a36Sopenharmony_ci "Qualcomm Atheros AR816x/AR817x Ethernet [%pM]\n", 186862306a36Sopenharmony_ci netdev->dev_addr); 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci return 0; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ciout_unlock: 187362306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 187462306a36Sopenharmony_ciout_unmap: 187562306a36Sopenharmony_ci iounmap(hw->hw_addr); 187662306a36Sopenharmony_ciout_free_netdev: 187762306a36Sopenharmony_ci free_netdev(netdev); 187862306a36Sopenharmony_ciout_pci_release: 187962306a36Sopenharmony_ci pci_release_mem_regions(pdev); 188062306a36Sopenharmony_ciout_pci_disable: 188162306a36Sopenharmony_ci pci_disable_device(pdev); 188262306a36Sopenharmony_ci return err; 188362306a36Sopenharmony_ci} 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_cistatic void alx_remove(struct pci_dev *pdev) 188662306a36Sopenharmony_ci{ 188762306a36Sopenharmony_ci struct alx_priv *alx = pci_get_drvdata(pdev); 188862306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci /* restore permanent mac address */ 189162306a36Sopenharmony_ci alx_set_macaddr(hw, hw->perm_addr); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci unregister_netdev(alx->dev); 189462306a36Sopenharmony_ci iounmap(hw->hw_addr); 189562306a36Sopenharmony_ci pci_release_mem_regions(pdev); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci pci_disable_device(pdev); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci mutex_destroy(&alx->mtx); 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci free_netdev(alx->dev); 190262306a36Sopenharmony_ci} 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_cistatic int alx_suspend(struct device *dev) 190562306a36Sopenharmony_ci{ 190662306a36Sopenharmony_ci struct alx_priv *alx = dev_get_drvdata(dev); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci if (!netif_running(alx->dev)) 190962306a36Sopenharmony_ci return 0; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci rtnl_lock(); 191262306a36Sopenharmony_ci netif_device_detach(alx->dev); 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci mutex_lock(&alx->mtx); 191562306a36Sopenharmony_ci __alx_stop(alx); 191662306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 191762306a36Sopenharmony_ci rtnl_unlock(); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci return 0; 192062306a36Sopenharmony_ci} 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_cistatic int alx_resume(struct device *dev) 192362306a36Sopenharmony_ci{ 192462306a36Sopenharmony_ci struct alx_priv *alx = dev_get_drvdata(dev); 192562306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 192662306a36Sopenharmony_ci int err; 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci rtnl_lock(); 192962306a36Sopenharmony_ci mutex_lock(&alx->mtx); 193062306a36Sopenharmony_ci alx_reset_phy(hw); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci if (!netif_running(alx->dev)) { 193362306a36Sopenharmony_ci err = 0; 193462306a36Sopenharmony_ci goto unlock; 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci err = __alx_open(alx, true); 193862306a36Sopenharmony_ci if (err) 193962306a36Sopenharmony_ci goto unlock; 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci netif_device_attach(alx->dev); 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ciunlock: 194462306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 194562306a36Sopenharmony_ci rtnl_unlock(); 194662306a36Sopenharmony_ci return err; 194762306a36Sopenharmony_ci} 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_cistatic pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev, 195262306a36Sopenharmony_ci pci_channel_state_t state) 195362306a36Sopenharmony_ci{ 195462306a36Sopenharmony_ci struct alx_priv *alx = pci_get_drvdata(pdev); 195562306a36Sopenharmony_ci struct net_device *netdev = alx->dev; 195662306a36Sopenharmony_ci pci_ers_result_t rc = PCI_ERS_RESULT_NEED_RESET; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci dev_info(&pdev->dev, "pci error detected\n"); 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci mutex_lock(&alx->mtx); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci if (netif_running(netdev)) { 196362306a36Sopenharmony_ci netif_device_detach(netdev); 196462306a36Sopenharmony_ci alx_halt(alx); 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci if (state == pci_channel_io_perm_failure) 196862306a36Sopenharmony_ci rc = PCI_ERS_RESULT_DISCONNECT; 196962306a36Sopenharmony_ci else 197062306a36Sopenharmony_ci pci_disable_device(pdev); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci return rc; 197562306a36Sopenharmony_ci} 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_cistatic pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev) 197862306a36Sopenharmony_ci{ 197962306a36Sopenharmony_ci struct alx_priv *alx = pci_get_drvdata(pdev); 198062306a36Sopenharmony_ci struct alx_hw *hw = &alx->hw; 198162306a36Sopenharmony_ci pci_ers_result_t rc = PCI_ERS_RESULT_DISCONNECT; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci dev_info(&pdev->dev, "pci error slot reset\n"); 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci mutex_lock(&alx->mtx); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci if (pci_enable_device(pdev)) { 198862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to re-enable PCI device after reset\n"); 198962306a36Sopenharmony_ci goto out; 199062306a36Sopenharmony_ci } 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci pci_set_master(pdev); 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci alx_reset_pcie(hw); 199562306a36Sopenharmony_ci if (!alx_reset_mac(hw)) 199662306a36Sopenharmony_ci rc = PCI_ERS_RESULT_RECOVERED; 199762306a36Sopenharmony_ciout: 199862306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci return rc; 200162306a36Sopenharmony_ci} 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_cistatic void alx_pci_error_resume(struct pci_dev *pdev) 200462306a36Sopenharmony_ci{ 200562306a36Sopenharmony_ci struct alx_priv *alx = pci_get_drvdata(pdev); 200662306a36Sopenharmony_ci struct net_device *netdev = alx->dev; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci dev_info(&pdev->dev, "pci error resume\n"); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci mutex_lock(&alx->mtx); 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci if (netif_running(netdev)) { 201362306a36Sopenharmony_ci alx_activate(alx); 201462306a36Sopenharmony_ci netif_device_attach(netdev); 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci mutex_unlock(&alx->mtx); 201862306a36Sopenharmony_ci} 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_cistatic const struct pci_error_handlers alx_err_handlers = { 202162306a36Sopenharmony_ci .error_detected = alx_pci_error_detected, 202262306a36Sopenharmony_ci .slot_reset = alx_pci_error_slot_reset, 202362306a36Sopenharmony_ci .resume = alx_pci_error_resume, 202462306a36Sopenharmony_ci}; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_cistatic const struct pci_device_id alx_pci_tbl[] = { 202762306a36Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8161), 202862306a36Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 202962306a36Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2200), 203062306a36Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 203162306a36Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2400), 203262306a36Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 203362306a36Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2500), 203462306a36Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 203562306a36Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8162), 203662306a36Sopenharmony_ci .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, 203762306a36Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8171) }, 203862306a36Sopenharmony_ci { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8172) }, 203962306a36Sopenharmony_ci {} 204062306a36Sopenharmony_ci}; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_cistatic struct pci_driver alx_driver = { 204362306a36Sopenharmony_ci .name = alx_drv_name, 204462306a36Sopenharmony_ci .id_table = alx_pci_tbl, 204562306a36Sopenharmony_ci .probe = alx_probe, 204662306a36Sopenharmony_ci .remove = alx_remove, 204762306a36Sopenharmony_ci .err_handler = &alx_err_handlers, 204862306a36Sopenharmony_ci .driver.pm = pm_sleep_ptr(&alx_pm_ops), 204962306a36Sopenharmony_ci}; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_cimodule_pci_driver(alx_driver); 205262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, alx_pci_tbl); 205362306a36Sopenharmony_ciMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); 205462306a36Sopenharmony_ciMODULE_AUTHOR("Qualcomm Corporation"); 205562306a36Sopenharmony_ciMODULE_DESCRIPTION( 205662306a36Sopenharmony_ci "Qualcomm Atheros(R) AR816x/AR817x PCI-E Ethernet Network Driver"); 205762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2058