162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci/* Qualcomm Technologies, Inc. EMAC Gigabit Ethernet Driver */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/if_ether.h> 862306a36Sopenharmony_ci#include <linux/if_vlan.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_net.h> 1462306a36Sopenharmony_ci#include <linux/phy.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/acpi.h> 1762306a36Sopenharmony_ci#include "emac.h" 1862306a36Sopenharmony_ci#include "emac-mac.h" 1962306a36Sopenharmony_ci#include "emac-phy.h" 2062306a36Sopenharmony_ci#include "emac-sgmii.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define EMAC_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ 2362306a36Sopenharmony_ci NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define EMAC_RRD_SIZE 4 2662306a36Sopenharmony_ci/* The RRD size if timestamping is enabled: */ 2762306a36Sopenharmony_ci#define EMAC_TS_RRD_SIZE 6 2862306a36Sopenharmony_ci#define EMAC_TPD_SIZE 4 2962306a36Sopenharmony_ci#define EMAC_RFD_SIZE 2 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define REG_MAC_RX_STATUS_BIN EMAC_RXMAC_STATC_REG0 3262306a36Sopenharmony_ci#define REG_MAC_RX_STATUS_END EMAC_RXMAC_STATC_REG22 3362306a36Sopenharmony_ci#define REG_MAC_TX_STATUS_BIN EMAC_TXMAC_STATC_REG0 3462306a36Sopenharmony_ci#define REG_MAC_TX_STATUS_END EMAC_TXMAC_STATC_REG24 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define RXQ0_NUM_RFD_PREF_DEF 8 3762306a36Sopenharmony_ci#define TXQ0_NUM_TPD_PREF_DEF 5 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define EMAC_PREAMBLE_DEF 7 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define DMAR_DLY_CNT_DEF 15 4262306a36Sopenharmony_ci#define DMAW_DLY_CNT_DEF 4 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define IMR_NORMAL_MASK (ISR_ERROR | ISR_OVER | ISR_TX_PKT) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define ISR_TX_PKT (\ 4762306a36Sopenharmony_ci TX_PKT_INT |\ 4862306a36Sopenharmony_ci TX_PKT_INT1 |\ 4962306a36Sopenharmony_ci TX_PKT_INT2 |\ 5062306a36Sopenharmony_ci TX_PKT_INT3) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define ISR_OVER (\ 5362306a36Sopenharmony_ci RFD0_UR_INT |\ 5462306a36Sopenharmony_ci RFD1_UR_INT |\ 5562306a36Sopenharmony_ci RFD2_UR_INT |\ 5662306a36Sopenharmony_ci RFD3_UR_INT |\ 5762306a36Sopenharmony_ci RFD4_UR_INT |\ 5862306a36Sopenharmony_ci RXF_OF_INT |\ 5962306a36Sopenharmony_ci TXF_UR_INT) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define ISR_ERROR (\ 6262306a36Sopenharmony_ci DMAR_TO_INT |\ 6362306a36Sopenharmony_ci DMAW_TO_INT |\ 6462306a36Sopenharmony_ci TXQ_TO_INT) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* in sync with enum emac_clk_id */ 6762306a36Sopenharmony_cistatic const char * const emac_clk_name[] = { 6862306a36Sopenharmony_ci "axi_clk", "cfg_ahb_clk", "high_speed_clk", "mdio_clk", "tx_clk", 6962306a36Sopenharmony_ci "rx_clk", "sys_clk" 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_civoid emac_reg_update32(void __iomem *addr, u32 mask, u32 val) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci u32 data = readl(addr); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci writel(((data & ~mask) | val), addr); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* reinitialize */ 8062306a36Sopenharmony_ciint emac_reinit_locked(struct emac_adapter *adpt) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci int ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci mutex_lock(&adpt->reset_lock); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci emac_mac_down(adpt); 8762306a36Sopenharmony_ci emac_sgmii_reset(adpt); 8862306a36Sopenharmony_ci ret = emac_mac_up(adpt); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci mutex_unlock(&adpt->reset_lock); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return ret; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* NAPI */ 9662306a36Sopenharmony_cistatic int emac_napi_rtx(struct napi_struct *napi, int budget) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct emac_rx_queue *rx_q = 9962306a36Sopenharmony_ci container_of(napi, struct emac_rx_queue, napi); 10062306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(rx_q->netdev); 10162306a36Sopenharmony_ci struct emac_irq *irq = rx_q->irq; 10262306a36Sopenharmony_ci int work_done = 0; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci emac_mac_rx_process(adpt, rx_q, &work_done, budget); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (work_done < budget) { 10762306a36Sopenharmony_ci napi_complete_done(napi, work_done); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci irq->mask |= rx_q->intr; 11062306a36Sopenharmony_ci writel(irq->mask, adpt->base + EMAC_INT_MASK); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return work_done; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* Transmit the packet */ 11762306a36Sopenharmony_cistatic netdev_tx_t emac_start_xmit(struct sk_buff *skb, 11862306a36Sopenharmony_ci struct net_device *netdev) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return emac_mac_tx_buf_send(adpt, &adpt->tx_q, skb); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic irqreturn_t emac_isr(int _irq, void *data) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct emac_irq *irq = data; 12862306a36Sopenharmony_ci struct emac_adapter *adpt = 12962306a36Sopenharmony_ci container_of(irq, struct emac_adapter, irq); 13062306a36Sopenharmony_ci struct emac_rx_queue *rx_q = &adpt->rx_q; 13162306a36Sopenharmony_ci u32 isr, status; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* disable the interrupt */ 13462306a36Sopenharmony_ci writel(0, adpt->base + EMAC_INT_MASK); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci isr = readl_relaxed(adpt->base + EMAC_INT_STATUS); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci status = isr & irq->mask; 13962306a36Sopenharmony_ci if (status == 0) 14062306a36Sopenharmony_ci goto exit; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (status & ISR_ERROR) { 14362306a36Sopenharmony_ci net_err_ratelimited("%s: error interrupt 0x%lx\n", 14462306a36Sopenharmony_ci adpt->netdev->name, status & ISR_ERROR); 14562306a36Sopenharmony_ci /* reset MAC */ 14662306a36Sopenharmony_ci schedule_work(&adpt->work_thread); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Schedule the napi for receive queue with interrupt 15062306a36Sopenharmony_ci * status bit set 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci if (status & rx_q->intr) { 15362306a36Sopenharmony_ci if (napi_schedule_prep(&rx_q->napi)) { 15462306a36Sopenharmony_ci irq->mask &= ~rx_q->intr; 15562306a36Sopenharmony_ci __napi_schedule(&rx_q->napi); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (status & TX_PKT_INT) 16062306a36Sopenharmony_ci emac_mac_tx_process(adpt, &adpt->tx_q); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (status & ISR_OVER) 16362306a36Sopenharmony_ci net_warn_ratelimited("%s: TX/RX overflow interrupt\n", 16462306a36Sopenharmony_ci adpt->netdev->name); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ciexit: 16762306a36Sopenharmony_ci /* enable the interrupt */ 16862306a36Sopenharmony_ci writel(irq->mask, adpt->base + EMAC_INT_MASK); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return IRQ_HANDLED; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* Configure VLAN tag strip/insert feature */ 17462306a36Sopenharmony_cistatic int emac_set_features(struct net_device *netdev, 17562306a36Sopenharmony_ci netdev_features_t features) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci netdev_features_t changed = features ^ netdev->features; 17862306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* We only need to reprogram the hardware if the VLAN tag features 18162306a36Sopenharmony_ci * have changed, and if it's already running. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX))) 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!netif_running(netdev)) 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* emac_mac_mode_config() uses netdev->features to configure the EMAC, 19062306a36Sopenharmony_ci * so make sure it's set first. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci netdev->features = features; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return emac_reinit_locked(adpt); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* Configure Multicast and Promiscuous modes */ 19862306a36Sopenharmony_cistatic void emac_rx_mode_set(struct net_device *netdev) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 20162306a36Sopenharmony_ci struct netdev_hw_addr *ha; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci emac_mac_mode_config(adpt); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* update multicast address filtering */ 20662306a36Sopenharmony_ci emac_mac_multicast_addr_clear(adpt); 20762306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) 20862306a36Sopenharmony_ci emac_mac_multicast_addr_set(adpt, ha->addr); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* Change the Maximum Transfer Unit (MTU) */ 21262306a36Sopenharmony_cistatic int emac_change_mtu(struct net_device *netdev, int new_mtu) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci netif_dbg(adpt, hw, adpt->netdev, 21762306a36Sopenharmony_ci "changing MTU from %d to %d\n", netdev->mtu, 21862306a36Sopenharmony_ci new_mtu); 21962306a36Sopenharmony_ci netdev->mtu = new_mtu; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (netif_running(netdev)) 22262306a36Sopenharmony_ci return emac_reinit_locked(adpt); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/* Called when the network interface is made active */ 22862306a36Sopenharmony_cistatic int emac_open(struct net_device *netdev) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 23162306a36Sopenharmony_ci struct emac_irq *irq = &adpt->irq; 23262306a36Sopenharmony_ci int ret; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ret = request_irq(irq->irq, emac_isr, 0, "emac-core0", irq); 23562306a36Sopenharmony_ci if (ret) { 23662306a36Sopenharmony_ci netdev_err(adpt->netdev, "could not request emac-core0 irq\n"); 23762306a36Sopenharmony_ci return ret; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* allocate rx/tx dma buffer & descriptors */ 24162306a36Sopenharmony_ci ret = emac_mac_rx_tx_rings_alloc_all(adpt); 24262306a36Sopenharmony_ci if (ret) { 24362306a36Sopenharmony_ci netdev_err(adpt->netdev, "error allocating rx/tx rings\n"); 24462306a36Sopenharmony_ci free_irq(irq->irq, irq); 24562306a36Sopenharmony_ci return ret; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = emac_sgmii_open(adpt); 24962306a36Sopenharmony_ci if (ret) { 25062306a36Sopenharmony_ci emac_mac_rx_tx_rings_free_all(adpt); 25162306a36Sopenharmony_ci free_irq(irq->irq, irq); 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ret = emac_mac_up(adpt); 25662306a36Sopenharmony_ci if (ret) { 25762306a36Sopenharmony_ci emac_mac_rx_tx_rings_free_all(adpt); 25862306a36Sopenharmony_ci free_irq(irq->irq, irq); 25962306a36Sopenharmony_ci emac_sgmii_close(adpt); 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* Called when the network interface is disabled */ 26762306a36Sopenharmony_cistatic int emac_close(struct net_device *netdev) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci mutex_lock(&adpt->reset_lock); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci emac_sgmii_close(adpt); 27462306a36Sopenharmony_ci emac_mac_down(adpt); 27562306a36Sopenharmony_ci emac_mac_rx_tx_rings_free_all(adpt); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci free_irq(adpt->irq.irq, &adpt->irq); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci mutex_unlock(&adpt->reset_lock); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* Respond to a TX hang */ 28562306a36Sopenharmony_cistatic void emac_tx_timeout(struct net_device *netdev, unsigned int txqueue) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci schedule_work(&adpt->work_thread); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/** 29362306a36Sopenharmony_ci * emac_update_hw_stats - read the EMAC stat registers 29462306a36Sopenharmony_ci * @adpt: pointer to adapter struct 29562306a36Sopenharmony_ci * 29662306a36Sopenharmony_ci * Reads the stats registers and write the values to adpt->stats. 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * adpt->stats.lock must be held while calling this function, 29962306a36Sopenharmony_ci * and while reading from adpt->stats. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_civoid emac_update_hw_stats(struct emac_adapter *adpt) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct emac_stats *stats = &adpt->stats; 30462306a36Sopenharmony_ci u64 *stats_itr = &adpt->stats.rx_ok; 30562306a36Sopenharmony_ci void __iomem *base = adpt->base; 30662306a36Sopenharmony_ci unsigned int addr; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci addr = REG_MAC_RX_STATUS_BIN; 30962306a36Sopenharmony_ci while (addr <= REG_MAC_RX_STATUS_END) { 31062306a36Sopenharmony_ci *stats_itr += readl_relaxed(base + addr); 31162306a36Sopenharmony_ci stats_itr++; 31262306a36Sopenharmony_ci addr += sizeof(u32); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* additional rx status */ 31662306a36Sopenharmony_ci stats->rx_crc_align += readl_relaxed(base + EMAC_RXMAC_STATC_REG23); 31762306a36Sopenharmony_ci stats->rx_jabbers += readl_relaxed(base + EMAC_RXMAC_STATC_REG24); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* update tx status */ 32062306a36Sopenharmony_ci addr = REG_MAC_TX_STATUS_BIN; 32162306a36Sopenharmony_ci stats_itr = &stats->tx_ok; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci while (addr <= REG_MAC_TX_STATUS_END) { 32462306a36Sopenharmony_ci *stats_itr += readl_relaxed(base + addr); 32562306a36Sopenharmony_ci stats_itr++; 32662306a36Sopenharmony_ci addr += sizeof(u32); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* additional tx status */ 33062306a36Sopenharmony_ci stats->tx_col += readl_relaxed(base + EMAC_TXMAC_STATC_REG25); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* Provide network statistics info for the interface */ 33462306a36Sopenharmony_cistatic void emac_get_stats64(struct net_device *netdev, 33562306a36Sopenharmony_ci struct rtnl_link_stats64 *net_stats) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 33862306a36Sopenharmony_ci struct emac_stats *stats = &adpt->stats; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci spin_lock(&stats->lock); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci emac_update_hw_stats(adpt); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* return parsed statistics */ 34562306a36Sopenharmony_ci net_stats->rx_packets = stats->rx_ok; 34662306a36Sopenharmony_ci net_stats->tx_packets = stats->tx_ok; 34762306a36Sopenharmony_ci net_stats->rx_bytes = stats->rx_byte_cnt; 34862306a36Sopenharmony_ci net_stats->tx_bytes = stats->tx_byte_cnt; 34962306a36Sopenharmony_ci net_stats->multicast = stats->rx_mcast; 35062306a36Sopenharmony_ci net_stats->collisions = stats->tx_1_col + stats->tx_2_col * 2 + 35162306a36Sopenharmony_ci stats->tx_late_col + stats->tx_abort_col; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci net_stats->rx_errors = stats->rx_frag + stats->rx_fcs_err + 35462306a36Sopenharmony_ci stats->rx_len_err + stats->rx_sz_ov + 35562306a36Sopenharmony_ci stats->rx_align_err; 35662306a36Sopenharmony_ci net_stats->rx_fifo_errors = stats->rx_rxf_ov; 35762306a36Sopenharmony_ci net_stats->rx_length_errors = stats->rx_len_err; 35862306a36Sopenharmony_ci net_stats->rx_crc_errors = stats->rx_fcs_err; 35962306a36Sopenharmony_ci net_stats->rx_frame_errors = stats->rx_align_err; 36062306a36Sopenharmony_ci net_stats->rx_over_errors = stats->rx_rxf_ov; 36162306a36Sopenharmony_ci net_stats->rx_missed_errors = stats->rx_rxf_ov; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci net_stats->tx_errors = stats->tx_late_col + stats->tx_abort_col + 36462306a36Sopenharmony_ci stats->tx_underrun + stats->tx_trunc; 36562306a36Sopenharmony_ci net_stats->tx_fifo_errors = stats->tx_underrun; 36662306a36Sopenharmony_ci net_stats->tx_aborted_errors = stats->tx_abort_col; 36762306a36Sopenharmony_ci net_stats->tx_window_errors = stats->tx_late_col; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci spin_unlock(&stats->lock); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic const struct net_device_ops emac_netdev_ops = { 37362306a36Sopenharmony_ci .ndo_open = emac_open, 37462306a36Sopenharmony_ci .ndo_stop = emac_close, 37562306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 37662306a36Sopenharmony_ci .ndo_start_xmit = emac_start_xmit, 37762306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 37862306a36Sopenharmony_ci .ndo_change_mtu = emac_change_mtu, 37962306a36Sopenharmony_ci .ndo_eth_ioctl = phy_do_ioctl_running, 38062306a36Sopenharmony_ci .ndo_tx_timeout = emac_tx_timeout, 38162306a36Sopenharmony_ci .ndo_get_stats64 = emac_get_stats64, 38262306a36Sopenharmony_ci .ndo_set_features = emac_set_features, 38362306a36Sopenharmony_ci .ndo_set_rx_mode = emac_rx_mode_set, 38462306a36Sopenharmony_ci}; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* Watchdog task routine, called to reinitialize the EMAC */ 38762306a36Sopenharmony_cistatic void emac_work_thread(struct work_struct *work) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct emac_adapter *adpt = 39062306a36Sopenharmony_ci container_of(work, struct emac_adapter, work_thread); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci emac_reinit_locked(adpt); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* Initialize various data structures */ 39662306a36Sopenharmony_cistatic void emac_init_adapter(struct emac_adapter *adpt) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci u32 reg; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci adpt->rrd_size = EMAC_RRD_SIZE; 40162306a36Sopenharmony_ci adpt->tpd_size = EMAC_TPD_SIZE; 40262306a36Sopenharmony_ci adpt->rfd_size = EMAC_RFD_SIZE; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* descriptors */ 40562306a36Sopenharmony_ci adpt->tx_desc_cnt = EMAC_DEF_TX_DESCS; 40662306a36Sopenharmony_ci adpt->rx_desc_cnt = EMAC_DEF_RX_DESCS; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* dma */ 40962306a36Sopenharmony_ci adpt->dma_order = emac_dma_ord_out; 41062306a36Sopenharmony_ci adpt->dmar_block = emac_dma_req_4096; 41162306a36Sopenharmony_ci adpt->dmaw_block = emac_dma_req_128; 41262306a36Sopenharmony_ci adpt->dmar_dly_cnt = DMAR_DLY_CNT_DEF; 41362306a36Sopenharmony_ci adpt->dmaw_dly_cnt = DMAW_DLY_CNT_DEF; 41462306a36Sopenharmony_ci adpt->tpd_burst = TXQ0_NUM_TPD_PREF_DEF; 41562306a36Sopenharmony_ci adpt->rfd_burst = RXQ0_NUM_RFD_PREF_DEF; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* irq moderator */ 41862306a36Sopenharmony_ci reg = ((EMAC_DEF_RX_IRQ_MOD >> 1) << IRQ_MODERATOR2_INIT_SHFT) | 41962306a36Sopenharmony_ci ((EMAC_DEF_TX_IRQ_MOD >> 1) << IRQ_MODERATOR_INIT_SHFT); 42062306a36Sopenharmony_ci adpt->irq_mod = reg; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* others */ 42362306a36Sopenharmony_ci adpt->preamble = EMAC_PREAMBLE_DEF; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* default to automatic flow control */ 42662306a36Sopenharmony_ci adpt->automatic = true; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Disable single-pause-frame mode by default */ 42962306a36Sopenharmony_ci adpt->single_pause_mode = false; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci/* Get the clock */ 43362306a36Sopenharmony_cistatic int emac_clks_get(struct platform_device *pdev, 43462306a36Sopenharmony_ci struct emac_adapter *adpt) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci unsigned int i; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci for (i = 0; i < EMAC_CLK_CNT; i++) { 43962306a36Sopenharmony_ci struct clk *clk = devm_clk_get(&pdev->dev, emac_clk_name[i]); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (IS_ERR(clk)) { 44262306a36Sopenharmony_ci dev_err(&pdev->dev, 44362306a36Sopenharmony_ci "could not claim clock %s (error=%li)\n", 44462306a36Sopenharmony_ci emac_clk_name[i], PTR_ERR(clk)); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return PTR_ERR(clk); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci adpt->clk[i] = clk; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/* Initialize clocks */ 45662306a36Sopenharmony_cistatic int emac_clks_phase1_init(struct platform_device *pdev, 45762306a36Sopenharmony_ci struct emac_adapter *adpt) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci int ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* On ACPI platforms, clocks are controlled by firmware and/or 46262306a36Sopenharmony_ci * ACPI, not by drivers. 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_ci if (has_acpi_companion(&pdev->dev)) 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ret = emac_clks_get(pdev, adpt); 46862306a36Sopenharmony_ci if (ret) 46962306a36Sopenharmony_ci return ret; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_AXI]); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_CFG_AHB]); 47662306a36Sopenharmony_ci if (ret) 47762306a36Sopenharmony_ci goto disable_clk_axi; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci ret = clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 19200000); 48062306a36Sopenharmony_ci if (ret) 48162306a36Sopenharmony_ci goto disable_clk_cfg_ahb; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_HIGH_SPEED]); 48462306a36Sopenharmony_ci if (ret) 48562306a36Sopenharmony_ci goto disable_clk_cfg_ahb; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cidisable_clk_cfg_ahb: 49062306a36Sopenharmony_ci clk_disable_unprepare(adpt->clk[EMAC_CLK_CFG_AHB]); 49162306a36Sopenharmony_cidisable_clk_axi: 49262306a36Sopenharmony_ci clk_disable_unprepare(adpt->clk[EMAC_CLK_AXI]); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/* Enable clocks; needs emac_clks_phase1_init to be called before */ 49862306a36Sopenharmony_cistatic int emac_clks_phase2_init(struct platform_device *pdev, 49962306a36Sopenharmony_ci struct emac_adapter *adpt) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci int ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (has_acpi_companion(&pdev->dev)) 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = clk_set_rate(adpt->clk[EMAC_CLK_TX], 125000000); 50762306a36Sopenharmony_ci if (ret) 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_TX]); 51162306a36Sopenharmony_ci if (ret) 51262306a36Sopenharmony_ci return ret; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci ret = clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 125000000); 51562306a36Sopenharmony_ci if (ret) 51662306a36Sopenharmony_ci return ret; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci ret = clk_set_rate(adpt->clk[EMAC_CLK_MDIO], 25000000); 51962306a36Sopenharmony_ci if (ret) 52062306a36Sopenharmony_ci return ret; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_MDIO]); 52362306a36Sopenharmony_ci if (ret) 52462306a36Sopenharmony_ci return ret; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_RX]); 52762306a36Sopenharmony_ci if (ret) 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return clk_prepare_enable(adpt->clk[EMAC_CLK_SYS]); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void emac_clks_teardown(struct emac_adapter *adpt) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci unsigned int i; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci for (i = 0; i < EMAC_CLK_CNT; i++) 53962306a36Sopenharmony_ci clk_disable_unprepare(adpt->clk[i]); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci/* Get the resources */ 54362306a36Sopenharmony_cistatic int emac_probe_resources(struct platform_device *pdev, 54462306a36Sopenharmony_ci struct emac_adapter *adpt) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct net_device *netdev = adpt->netdev; 54762306a36Sopenharmony_ci int ret = 0; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* get mac address */ 55062306a36Sopenharmony_ci if (device_get_ethdev_address(&pdev->dev, netdev)) 55162306a36Sopenharmony_ci eth_hw_addr_random(netdev); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Core 0 interrupt */ 55462306a36Sopenharmony_ci ret = platform_get_irq(pdev, 0); 55562306a36Sopenharmony_ci if (ret < 0) 55662306a36Sopenharmony_ci return ret; 55762306a36Sopenharmony_ci adpt->irq.irq = ret; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* base register address */ 56062306a36Sopenharmony_ci adpt->base = devm_platform_ioremap_resource(pdev, 0); 56162306a36Sopenharmony_ci if (IS_ERR(adpt->base)) 56262306a36Sopenharmony_ci return PTR_ERR(adpt->base); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* CSR register address */ 56562306a36Sopenharmony_ci adpt->csr = devm_platform_ioremap_resource(pdev, 1); 56662306a36Sopenharmony_ci if (IS_ERR(adpt->csr)) 56762306a36Sopenharmony_ci return PTR_ERR(adpt->csr); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci netdev->base_addr = (unsigned long)adpt->base; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic const struct of_device_id emac_dt_match[] = { 57562306a36Sopenharmony_ci { 57662306a36Sopenharmony_ci .compatible = "qcom,fsm9900-emac", 57762306a36Sopenharmony_ci }, 57862306a36Sopenharmony_ci {} 57962306a36Sopenharmony_ci}; 58062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, emac_dt_match); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI) 58362306a36Sopenharmony_cistatic const struct acpi_device_id emac_acpi_match[] = { 58462306a36Sopenharmony_ci { 58562306a36Sopenharmony_ci .id = "QCOM8070", 58662306a36Sopenharmony_ci }, 58762306a36Sopenharmony_ci {} 58862306a36Sopenharmony_ci}; 58962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, emac_acpi_match); 59062306a36Sopenharmony_ci#endif 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int emac_probe(struct platform_device *pdev) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct net_device *netdev; 59562306a36Sopenharmony_ci struct emac_adapter *adpt; 59662306a36Sopenharmony_ci struct emac_sgmii *phy; 59762306a36Sopenharmony_ci u16 devid, revid; 59862306a36Sopenharmony_ci u32 reg; 59962306a36Sopenharmony_ci int ret; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* The TPD buffer address is limited to: 60262306a36Sopenharmony_ci * 1. PTP: 45bits. (Driver doesn't support yet.) 60362306a36Sopenharmony_ci * 2. NON-PTP: 46bits. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(46)); 60662306a36Sopenharmony_ci if (ret) { 60762306a36Sopenharmony_ci dev_err(&pdev->dev, "could not set DMA mask\n"); 60862306a36Sopenharmony_ci return ret; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct emac_adapter)); 61262306a36Sopenharmony_ci if (!netdev) 61362306a36Sopenharmony_ci return -ENOMEM; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, netdev); 61662306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 61762306a36Sopenharmony_ci emac_set_ethtool_ops(netdev); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci adpt = netdev_priv(netdev); 62062306a36Sopenharmony_ci adpt->netdev = netdev; 62162306a36Sopenharmony_ci adpt->msg_enable = EMAC_MSG_DEFAULT; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci phy = &adpt->phy; 62462306a36Sopenharmony_ci atomic_set(&phy->decode_error_count, 0); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci mutex_init(&adpt->reset_lock); 62762306a36Sopenharmony_ci spin_lock_init(&adpt->stats.lock); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci adpt->irq.mask = RX_PKT_INT0 | IMR_NORMAL_MASK; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci ret = emac_probe_resources(pdev, adpt); 63262306a36Sopenharmony_ci if (ret) 63362306a36Sopenharmony_ci goto err_undo_netdev; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* initialize clocks */ 63662306a36Sopenharmony_ci ret = emac_clks_phase1_init(pdev, adpt); 63762306a36Sopenharmony_ci if (ret) { 63862306a36Sopenharmony_ci dev_err(&pdev->dev, "could not initialize clocks\n"); 63962306a36Sopenharmony_ci goto err_undo_netdev; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci netdev->watchdog_timeo = EMAC_WATCHDOG_TIME; 64362306a36Sopenharmony_ci netdev->irq = adpt->irq.irq; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci netdev->netdev_ops = &emac_netdev_ops; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci emac_init_adapter(adpt); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* init external phy */ 65062306a36Sopenharmony_ci ret = emac_phy_config(pdev, adpt); 65162306a36Sopenharmony_ci if (ret) 65262306a36Sopenharmony_ci goto err_undo_clocks; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* init internal sgmii phy */ 65562306a36Sopenharmony_ci ret = emac_sgmii_config(pdev, adpt); 65662306a36Sopenharmony_ci if (ret) 65762306a36Sopenharmony_ci goto err_undo_mdiobus; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* enable clocks */ 66062306a36Sopenharmony_ci ret = emac_clks_phase2_init(pdev, adpt); 66162306a36Sopenharmony_ci if (ret) { 66262306a36Sopenharmony_ci dev_err(&pdev->dev, "could not initialize clocks\n"); 66362306a36Sopenharmony_ci goto err_undo_mdiobus; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* set hw features */ 66762306a36Sopenharmony_ci netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM | 66862306a36Sopenharmony_ci NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_RX | 66962306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX; 67062306a36Sopenharmony_ci netdev->hw_features = netdev->features; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci netdev->vlan_features |= NETIF_F_SG | NETIF_F_HW_CSUM | 67362306a36Sopenharmony_ci NETIF_F_TSO | NETIF_F_TSO6; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* MTU range: 46 - 9194 */ 67662306a36Sopenharmony_ci netdev->min_mtu = EMAC_MIN_ETH_FRAME_SIZE - 67762306a36Sopenharmony_ci (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); 67862306a36Sopenharmony_ci netdev->max_mtu = EMAC_MAX_ETH_FRAME_SIZE - 67962306a36Sopenharmony_ci (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci INIT_WORK(&adpt->work_thread, emac_work_thread); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* Initialize queues */ 68462306a36Sopenharmony_ci emac_mac_rx_tx_ring_init_all(pdev, adpt); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci netif_napi_add(netdev, &adpt->rx_q.napi, emac_napi_rtx); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci ret = register_netdev(netdev); 68962306a36Sopenharmony_ci if (ret) { 69062306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register net device\n"); 69162306a36Sopenharmony_ci goto err_undo_napi; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci reg = readl_relaxed(adpt->base + EMAC_DMA_MAS_CTRL); 69562306a36Sopenharmony_ci devid = (reg & DEV_ID_NUM_BMSK) >> DEV_ID_NUM_SHFT; 69662306a36Sopenharmony_ci revid = (reg & DEV_REV_NUM_BMSK) >> DEV_REV_NUM_SHFT; 69762306a36Sopenharmony_ci reg = readl_relaxed(adpt->base + EMAC_CORE_HW_VERSION); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci netif_info(adpt, probe, netdev, 70062306a36Sopenharmony_ci "hardware id %d.%d, hardware version %d.%d.%d\n", 70162306a36Sopenharmony_ci devid, revid, 70262306a36Sopenharmony_ci (reg & MAJOR_BMSK) >> MAJOR_SHFT, 70362306a36Sopenharmony_ci (reg & MINOR_BMSK) >> MINOR_SHFT, 70462306a36Sopenharmony_ci (reg & STEP_BMSK) >> STEP_SHFT); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return 0; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cierr_undo_napi: 70962306a36Sopenharmony_ci netif_napi_del(&adpt->rx_q.napi); 71062306a36Sopenharmony_cierr_undo_mdiobus: 71162306a36Sopenharmony_ci put_device(&adpt->phydev->mdio.dev); 71262306a36Sopenharmony_ci mdiobus_unregister(adpt->mii_bus); 71362306a36Sopenharmony_cierr_undo_clocks: 71462306a36Sopenharmony_ci emac_clks_teardown(adpt); 71562306a36Sopenharmony_cierr_undo_netdev: 71662306a36Sopenharmony_ci free_netdev(netdev); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci return ret; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic int emac_remove(struct platform_device *pdev) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(&pdev->dev); 72462306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci netif_carrier_off(netdev); 72762306a36Sopenharmony_ci netif_tx_disable(netdev); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci unregister_netdev(netdev); 73062306a36Sopenharmony_ci netif_napi_del(&adpt->rx_q.napi); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci free_irq(adpt->irq.irq, &adpt->irq); 73362306a36Sopenharmony_ci cancel_work_sync(&adpt->work_thread); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci emac_clks_teardown(adpt); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci put_device(&adpt->phydev->mdio.dev); 73862306a36Sopenharmony_ci mdiobus_unregister(adpt->mii_bus); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (adpt->phy.digital) 74162306a36Sopenharmony_ci iounmap(adpt->phy.digital); 74262306a36Sopenharmony_ci iounmap(adpt->phy.base); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci free_netdev(netdev); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return 0; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic void emac_shutdown(struct platform_device *pdev) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(&pdev->dev); 75262306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (netdev->flags & IFF_UP) { 75562306a36Sopenharmony_ci /* Closing the SGMII turns off its interrupts */ 75662306a36Sopenharmony_ci emac_sgmii_close(adpt); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* Resetting the MAC turns off all DMA and its interrupts */ 75962306a36Sopenharmony_ci emac_mac_reset(adpt); 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic struct platform_driver emac_platform_driver = { 76462306a36Sopenharmony_ci .probe = emac_probe, 76562306a36Sopenharmony_ci .remove = emac_remove, 76662306a36Sopenharmony_ci .driver = { 76762306a36Sopenharmony_ci .name = "qcom-emac", 76862306a36Sopenharmony_ci .of_match_table = emac_dt_match, 76962306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(emac_acpi_match), 77062306a36Sopenharmony_ci }, 77162306a36Sopenharmony_ci .shutdown = emac_shutdown, 77262306a36Sopenharmony_ci}; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cimodule_platform_driver(emac_platform_driver); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 77762306a36Sopenharmony_ciMODULE_ALIAS("platform:qcom-emac"); 778