18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci */ 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci/* Qualcomm Technologies, Inc. EMAC Gigabit Ethernet Driver */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 88c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_net.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/phy.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/acpi.h> 188c2ecf20Sopenharmony_ci#include "emac.h" 198c2ecf20Sopenharmony_ci#include "emac-mac.h" 208c2ecf20Sopenharmony_ci#include "emac-phy.h" 218c2ecf20Sopenharmony_ci#include "emac-sgmii.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define EMAC_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ 248c2ecf20Sopenharmony_ci NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define EMAC_RRD_SIZE 4 278c2ecf20Sopenharmony_ci/* The RRD size if timestamping is enabled: */ 288c2ecf20Sopenharmony_ci#define EMAC_TS_RRD_SIZE 6 298c2ecf20Sopenharmony_ci#define EMAC_TPD_SIZE 4 308c2ecf20Sopenharmony_ci#define EMAC_RFD_SIZE 2 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define REG_MAC_RX_STATUS_BIN EMAC_RXMAC_STATC_REG0 338c2ecf20Sopenharmony_ci#define REG_MAC_RX_STATUS_END EMAC_RXMAC_STATC_REG22 348c2ecf20Sopenharmony_ci#define REG_MAC_TX_STATUS_BIN EMAC_TXMAC_STATC_REG0 358c2ecf20Sopenharmony_ci#define REG_MAC_TX_STATUS_END EMAC_TXMAC_STATC_REG24 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define RXQ0_NUM_RFD_PREF_DEF 8 388c2ecf20Sopenharmony_ci#define TXQ0_NUM_TPD_PREF_DEF 5 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define EMAC_PREAMBLE_DEF 7 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define DMAR_DLY_CNT_DEF 15 438c2ecf20Sopenharmony_ci#define DMAW_DLY_CNT_DEF 4 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define IMR_NORMAL_MASK (ISR_ERROR | ISR_OVER | ISR_TX_PKT) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define ISR_TX_PKT (\ 488c2ecf20Sopenharmony_ci TX_PKT_INT |\ 498c2ecf20Sopenharmony_ci TX_PKT_INT1 |\ 508c2ecf20Sopenharmony_ci TX_PKT_INT2 |\ 518c2ecf20Sopenharmony_ci TX_PKT_INT3) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define ISR_OVER (\ 548c2ecf20Sopenharmony_ci RFD0_UR_INT |\ 558c2ecf20Sopenharmony_ci RFD1_UR_INT |\ 568c2ecf20Sopenharmony_ci RFD2_UR_INT |\ 578c2ecf20Sopenharmony_ci RFD3_UR_INT |\ 588c2ecf20Sopenharmony_ci RFD4_UR_INT |\ 598c2ecf20Sopenharmony_ci RXF_OF_INT |\ 608c2ecf20Sopenharmony_ci TXF_UR_INT) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define ISR_ERROR (\ 638c2ecf20Sopenharmony_ci DMAR_TO_INT |\ 648c2ecf20Sopenharmony_ci DMAW_TO_INT |\ 658c2ecf20Sopenharmony_ci TXQ_TO_INT) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* in sync with enum emac_clk_id */ 688c2ecf20Sopenharmony_cistatic const char * const emac_clk_name[] = { 698c2ecf20Sopenharmony_ci "axi_clk", "cfg_ahb_clk", "high_speed_clk", "mdio_clk", "tx_clk", 708c2ecf20Sopenharmony_ci "rx_clk", "sys_clk" 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_civoid emac_reg_update32(void __iomem *addr, u32 mask, u32 val) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci u32 data = readl(addr); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci writel(((data & ~mask) | val), addr); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* reinitialize */ 818c2ecf20Sopenharmony_ciint emac_reinit_locked(struct emac_adapter *adpt) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int ret; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci mutex_lock(&adpt->reset_lock); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci emac_mac_down(adpt); 888c2ecf20Sopenharmony_ci emac_sgmii_reset(adpt); 898c2ecf20Sopenharmony_ci ret = emac_mac_up(adpt); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci mutex_unlock(&adpt->reset_lock); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return ret; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* NAPI */ 978c2ecf20Sopenharmony_cistatic int emac_napi_rtx(struct napi_struct *napi, int budget) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct emac_rx_queue *rx_q = 1008c2ecf20Sopenharmony_ci container_of(napi, struct emac_rx_queue, napi); 1018c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(rx_q->netdev); 1028c2ecf20Sopenharmony_ci struct emac_irq *irq = rx_q->irq; 1038c2ecf20Sopenharmony_ci int work_done = 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci emac_mac_rx_process(adpt, rx_q, &work_done, budget); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (work_done < budget) { 1088c2ecf20Sopenharmony_ci napi_complete_done(napi, work_done); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci irq->mask |= rx_q->intr; 1118c2ecf20Sopenharmony_ci writel(irq->mask, adpt->base + EMAC_INT_MASK); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return work_done; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* Transmit the packet */ 1188c2ecf20Sopenharmony_cistatic netdev_tx_t emac_start_xmit(struct sk_buff *skb, 1198c2ecf20Sopenharmony_ci struct net_device *netdev) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return emac_mac_tx_buf_send(adpt, &adpt->tx_q, skb); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic irqreturn_t emac_isr(int _irq, void *data) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct emac_irq *irq = data; 1298c2ecf20Sopenharmony_ci struct emac_adapter *adpt = 1308c2ecf20Sopenharmony_ci container_of(irq, struct emac_adapter, irq); 1318c2ecf20Sopenharmony_ci struct emac_rx_queue *rx_q = &adpt->rx_q; 1328c2ecf20Sopenharmony_ci u32 isr, status; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* disable the interrupt */ 1358c2ecf20Sopenharmony_ci writel(0, adpt->base + EMAC_INT_MASK); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci isr = readl_relaxed(adpt->base + EMAC_INT_STATUS); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci status = isr & irq->mask; 1408c2ecf20Sopenharmony_ci if (status == 0) 1418c2ecf20Sopenharmony_ci goto exit; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (status & ISR_ERROR) { 1448c2ecf20Sopenharmony_ci net_err_ratelimited("%s: error interrupt 0x%lx\n", 1458c2ecf20Sopenharmony_ci adpt->netdev->name, status & ISR_ERROR); 1468c2ecf20Sopenharmony_ci /* reset MAC */ 1478c2ecf20Sopenharmony_ci schedule_work(&adpt->work_thread); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Schedule the napi for receive queue with interrupt 1518c2ecf20Sopenharmony_ci * status bit set 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci if (status & rx_q->intr) { 1548c2ecf20Sopenharmony_ci if (napi_schedule_prep(&rx_q->napi)) { 1558c2ecf20Sopenharmony_ci irq->mask &= ~rx_q->intr; 1568c2ecf20Sopenharmony_ci __napi_schedule(&rx_q->napi); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (status & TX_PKT_INT) 1618c2ecf20Sopenharmony_ci emac_mac_tx_process(adpt, &adpt->tx_q); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (status & ISR_OVER) 1648c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: TX/RX overflow interrupt\n", 1658c2ecf20Sopenharmony_ci adpt->netdev->name); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ciexit: 1688c2ecf20Sopenharmony_ci /* enable the interrupt */ 1698c2ecf20Sopenharmony_ci writel(irq->mask, adpt->base + EMAC_INT_MASK); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* Configure VLAN tag strip/insert feature */ 1758c2ecf20Sopenharmony_cistatic int emac_set_features(struct net_device *netdev, 1768c2ecf20Sopenharmony_ci netdev_features_t features) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci netdev_features_t changed = features ^ netdev->features; 1798c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* We only need to reprogram the hardware if the VLAN tag features 1828c2ecf20Sopenharmony_ci * have changed, and if it's already running. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX))) 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!netif_running(netdev)) 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* emac_mac_mode_config() uses netdev->features to configure the EMAC, 1918c2ecf20Sopenharmony_ci * so make sure it's set first. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci netdev->features = features; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return emac_reinit_locked(adpt); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* Configure Multicast and Promiscuous modes */ 1998c2ecf20Sopenharmony_cistatic void emac_rx_mode_set(struct net_device *netdev) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 2028c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci emac_mac_mode_config(adpt); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* update multicast address filtering */ 2078c2ecf20Sopenharmony_ci emac_mac_multicast_addr_clear(adpt); 2088c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) 2098c2ecf20Sopenharmony_ci emac_mac_multicast_addr_set(adpt, ha->addr); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* Change the Maximum Transfer Unit (MTU) */ 2138c2ecf20Sopenharmony_cistatic int emac_change_mtu(struct net_device *netdev, int new_mtu) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci netif_dbg(adpt, hw, adpt->netdev, 2188c2ecf20Sopenharmony_ci "changing MTU from %d to %d\n", netdev->mtu, 2198c2ecf20Sopenharmony_ci new_mtu); 2208c2ecf20Sopenharmony_ci netdev->mtu = new_mtu; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (netif_running(netdev)) 2238c2ecf20Sopenharmony_ci return emac_reinit_locked(adpt); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* Called when the network interface is made active */ 2298c2ecf20Sopenharmony_cistatic int emac_open(struct net_device *netdev) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 2328c2ecf20Sopenharmony_ci struct emac_irq *irq = &adpt->irq; 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = request_irq(irq->irq, emac_isr, 0, "emac-core0", irq); 2368c2ecf20Sopenharmony_ci if (ret) { 2378c2ecf20Sopenharmony_ci netdev_err(adpt->netdev, "could not request emac-core0 irq\n"); 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* allocate rx/tx dma buffer & descriptors */ 2428c2ecf20Sopenharmony_ci ret = emac_mac_rx_tx_rings_alloc_all(adpt); 2438c2ecf20Sopenharmony_ci if (ret) { 2448c2ecf20Sopenharmony_ci netdev_err(adpt->netdev, "error allocating rx/tx rings\n"); 2458c2ecf20Sopenharmony_ci free_irq(irq->irq, irq); 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ret = emac_sgmii_open(adpt); 2508c2ecf20Sopenharmony_ci if (ret) { 2518c2ecf20Sopenharmony_ci emac_mac_rx_tx_rings_free_all(adpt); 2528c2ecf20Sopenharmony_ci free_irq(irq->irq, irq); 2538c2ecf20Sopenharmony_ci return ret; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ret = emac_mac_up(adpt); 2578c2ecf20Sopenharmony_ci if (ret) { 2588c2ecf20Sopenharmony_ci emac_mac_rx_tx_rings_free_all(adpt); 2598c2ecf20Sopenharmony_ci free_irq(irq->irq, irq); 2608c2ecf20Sopenharmony_ci emac_sgmii_close(adpt); 2618c2ecf20Sopenharmony_ci return ret; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* Called when the network interface is disabled */ 2688c2ecf20Sopenharmony_cistatic int emac_close(struct net_device *netdev) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci mutex_lock(&adpt->reset_lock); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci emac_sgmii_close(adpt); 2758c2ecf20Sopenharmony_ci emac_mac_down(adpt); 2768c2ecf20Sopenharmony_ci emac_mac_rx_tx_rings_free_all(adpt); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci free_irq(adpt->irq.irq, &adpt->irq); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci mutex_unlock(&adpt->reset_lock); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* Respond to a TX hang */ 2868c2ecf20Sopenharmony_cistatic void emac_tx_timeout(struct net_device *netdev, unsigned int txqueue) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci schedule_work(&adpt->work_thread); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/** 2948c2ecf20Sopenharmony_ci * emac_update_hw_stats - read the EMAC stat registers 2958c2ecf20Sopenharmony_ci * @adpt: pointer to adapter struct 2968c2ecf20Sopenharmony_ci * 2978c2ecf20Sopenharmony_ci * Reads the stats registers and write the values to adpt->stats. 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * adpt->stats.lock must be held while calling this function, 3008c2ecf20Sopenharmony_ci * and while reading from adpt->stats. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_civoid emac_update_hw_stats(struct emac_adapter *adpt) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct emac_stats *stats = &adpt->stats; 3058c2ecf20Sopenharmony_ci u64 *stats_itr = &adpt->stats.rx_ok; 3068c2ecf20Sopenharmony_ci void __iomem *base = adpt->base; 3078c2ecf20Sopenharmony_ci unsigned int addr; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci addr = REG_MAC_RX_STATUS_BIN; 3108c2ecf20Sopenharmony_ci while (addr <= REG_MAC_RX_STATUS_END) { 3118c2ecf20Sopenharmony_ci *stats_itr += readl_relaxed(base + addr); 3128c2ecf20Sopenharmony_ci stats_itr++; 3138c2ecf20Sopenharmony_ci addr += sizeof(u32); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* additional rx status */ 3178c2ecf20Sopenharmony_ci stats->rx_crc_align += readl_relaxed(base + EMAC_RXMAC_STATC_REG23); 3188c2ecf20Sopenharmony_ci stats->rx_jabbers += readl_relaxed(base + EMAC_RXMAC_STATC_REG24); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* update tx status */ 3218c2ecf20Sopenharmony_ci addr = REG_MAC_TX_STATUS_BIN; 3228c2ecf20Sopenharmony_ci stats_itr = &stats->tx_ok; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci while (addr <= REG_MAC_TX_STATUS_END) { 3258c2ecf20Sopenharmony_ci *stats_itr += readl_relaxed(base + addr); 3268c2ecf20Sopenharmony_ci stats_itr++; 3278c2ecf20Sopenharmony_ci addr += sizeof(u32); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* additional tx status */ 3318c2ecf20Sopenharmony_ci stats->tx_col += readl_relaxed(base + EMAC_TXMAC_STATC_REG25); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/* Provide network statistics info for the interface */ 3358c2ecf20Sopenharmony_cistatic void emac_get_stats64(struct net_device *netdev, 3368c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *net_stats) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 3398c2ecf20Sopenharmony_ci struct emac_stats *stats = &adpt->stats; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci spin_lock(&stats->lock); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci emac_update_hw_stats(adpt); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* return parsed statistics */ 3468c2ecf20Sopenharmony_ci net_stats->rx_packets = stats->rx_ok; 3478c2ecf20Sopenharmony_ci net_stats->tx_packets = stats->tx_ok; 3488c2ecf20Sopenharmony_ci net_stats->rx_bytes = stats->rx_byte_cnt; 3498c2ecf20Sopenharmony_ci net_stats->tx_bytes = stats->tx_byte_cnt; 3508c2ecf20Sopenharmony_ci net_stats->multicast = stats->rx_mcast; 3518c2ecf20Sopenharmony_ci net_stats->collisions = stats->tx_1_col + stats->tx_2_col * 2 + 3528c2ecf20Sopenharmony_ci stats->tx_late_col + stats->tx_abort_col; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci net_stats->rx_errors = stats->rx_frag + stats->rx_fcs_err + 3558c2ecf20Sopenharmony_ci stats->rx_len_err + stats->rx_sz_ov + 3568c2ecf20Sopenharmony_ci stats->rx_align_err; 3578c2ecf20Sopenharmony_ci net_stats->rx_fifo_errors = stats->rx_rxf_ov; 3588c2ecf20Sopenharmony_ci net_stats->rx_length_errors = stats->rx_len_err; 3598c2ecf20Sopenharmony_ci net_stats->rx_crc_errors = stats->rx_fcs_err; 3608c2ecf20Sopenharmony_ci net_stats->rx_frame_errors = stats->rx_align_err; 3618c2ecf20Sopenharmony_ci net_stats->rx_over_errors = stats->rx_rxf_ov; 3628c2ecf20Sopenharmony_ci net_stats->rx_missed_errors = stats->rx_rxf_ov; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci net_stats->tx_errors = stats->tx_late_col + stats->tx_abort_col + 3658c2ecf20Sopenharmony_ci stats->tx_underrun + stats->tx_trunc; 3668c2ecf20Sopenharmony_ci net_stats->tx_fifo_errors = stats->tx_underrun; 3678c2ecf20Sopenharmony_ci net_stats->tx_aborted_errors = stats->tx_abort_col; 3688c2ecf20Sopenharmony_ci net_stats->tx_window_errors = stats->tx_late_col; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci spin_unlock(&stats->lock); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic const struct net_device_ops emac_netdev_ops = { 3748c2ecf20Sopenharmony_ci .ndo_open = emac_open, 3758c2ecf20Sopenharmony_ci .ndo_stop = emac_close, 3768c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 3778c2ecf20Sopenharmony_ci .ndo_start_xmit = emac_start_xmit, 3788c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 3798c2ecf20Sopenharmony_ci .ndo_change_mtu = emac_change_mtu, 3808c2ecf20Sopenharmony_ci .ndo_do_ioctl = phy_do_ioctl_running, 3818c2ecf20Sopenharmony_ci .ndo_tx_timeout = emac_tx_timeout, 3828c2ecf20Sopenharmony_ci .ndo_get_stats64 = emac_get_stats64, 3838c2ecf20Sopenharmony_ci .ndo_set_features = emac_set_features, 3848c2ecf20Sopenharmony_ci .ndo_set_rx_mode = emac_rx_mode_set, 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/* Watchdog task routine, called to reinitialize the EMAC */ 3888c2ecf20Sopenharmony_cistatic void emac_work_thread(struct work_struct *work) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct emac_adapter *adpt = 3918c2ecf20Sopenharmony_ci container_of(work, struct emac_adapter, work_thread); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci emac_reinit_locked(adpt); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/* Initialize various data structures */ 3978c2ecf20Sopenharmony_cistatic void emac_init_adapter(struct emac_adapter *adpt) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci u32 reg; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci adpt->rrd_size = EMAC_RRD_SIZE; 4028c2ecf20Sopenharmony_ci adpt->tpd_size = EMAC_TPD_SIZE; 4038c2ecf20Sopenharmony_ci adpt->rfd_size = EMAC_RFD_SIZE; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* descriptors */ 4068c2ecf20Sopenharmony_ci adpt->tx_desc_cnt = EMAC_DEF_TX_DESCS; 4078c2ecf20Sopenharmony_ci adpt->rx_desc_cnt = EMAC_DEF_RX_DESCS; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* dma */ 4108c2ecf20Sopenharmony_ci adpt->dma_order = emac_dma_ord_out; 4118c2ecf20Sopenharmony_ci adpt->dmar_block = emac_dma_req_4096; 4128c2ecf20Sopenharmony_ci adpt->dmaw_block = emac_dma_req_128; 4138c2ecf20Sopenharmony_ci adpt->dmar_dly_cnt = DMAR_DLY_CNT_DEF; 4148c2ecf20Sopenharmony_ci adpt->dmaw_dly_cnt = DMAW_DLY_CNT_DEF; 4158c2ecf20Sopenharmony_ci adpt->tpd_burst = TXQ0_NUM_TPD_PREF_DEF; 4168c2ecf20Sopenharmony_ci adpt->rfd_burst = RXQ0_NUM_RFD_PREF_DEF; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* irq moderator */ 4198c2ecf20Sopenharmony_ci reg = ((EMAC_DEF_RX_IRQ_MOD >> 1) << IRQ_MODERATOR2_INIT_SHFT) | 4208c2ecf20Sopenharmony_ci ((EMAC_DEF_TX_IRQ_MOD >> 1) << IRQ_MODERATOR_INIT_SHFT); 4218c2ecf20Sopenharmony_ci adpt->irq_mod = reg; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* others */ 4248c2ecf20Sopenharmony_ci adpt->preamble = EMAC_PREAMBLE_DEF; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* default to automatic flow control */ 4278c2ecf20Sopenharmony_ci adpt->automatic = true; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Disable single-pause-frame mode by default */ 4308c2ecf20Sopenharmony_ci adpt->single_pause_mode = false; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/* Get the clock */ 4348c2ecf20Sopenharmony_cistatic int emac_clks_get(struct platform_device *pdev, 4358c2ecf20Sopenharmony_ci struct emac_adapter *adpt) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci unsigned int i; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci for (i = 0; i < EMAC_CLK_CNT; i++) { 4408c2ecf20Sopenharmony_ci struct clk *clk = devm_clk_get(&pdev->dev, emac_clk_name[i]); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 4438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 4448c2ecf20Sopenharmony_ci "could not claim clock %s (error=%li)\n", 4458c2ecf20Sopenharmony_ci emac_clk_name[i], PTR_ERR(clk)); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return PTR_ERR(clk); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci adpt->clk[i] = clk; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* Initialize clocks */ 4578c2ecf20Sopenharmony_cistatic int emac_clks_phase1_init(struct platform_device *pdev, 4588c2ecf20Sopenharmony_ci struct emac_adapter *adpt) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci int ret; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* On ACPI platforms, clocks are controlled by firmware and/or 4638c2ecf20Sopenharmony_ci * ACPI, not by drivers. 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci if (has_acpi_companion(&pdev->dev)) 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ret = emac_clks_get(pdev, adpt); 4698c2ecf20Sopenharmony_ci if (ret) 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_AXI]); 4738c2ecf20Sopenharmony_ci if (ret) 4748c2ecf20Sopenharmony_ci return ret; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_CFG_AHB]); 4778c2ecf20Sopenharmony_ci if (ret) 4788c2ecf20Sopenharmony_ci goto disable_clk_axi; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ret = clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 19200000); 4818c2ecf20Sopenharmony_ci if (ret) 4828c2ecf20Sopenharmony_ci goto disable_clk_cfg_ahb; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_HIGH_SPEED]); 4858c2ecf20Sopenharmony_ci if (ret) 4868c2ecf20Sopenharmony_ci goto disable_clk_cfg_ahb; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cidisable_clk_cfg_ahb: 4918c2ecf20Sopenharmony_ci clk_disable_unprepare(adpt->clk[EMAC_CLK_CFG_AHB]); 4928c2ecf20Sopenharmony_cidisable_clk_axi: 4938c2ecf20Sopenharmony_ci clk_disable_unprepare(adpt->clk[EMAC_CLK_AXI]); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return ret; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* Enable clocks; needs emac_clks_phase1_init to be called before */ 4998c2ecf20Sopenharmony_cistatic int emac_clks_phase2_init(struct platform_device *pdev, 5008c2ecf20Sopenharmony_ci struct emac_adapter *adpt) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci int ret; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (has_acpi_companion(&pdev->dev)) 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci ret = clk_set_rate(adpt->clk[EMAC_CLK_TX], 125000000); 5088c2ecf20Sopenharmony_ci if (ret) 5098c2ecf20Sopenharmony_ci return ret; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_TX]); 5128c2ecf20Sopenharmony_ci if (ret) 5138c2ecf20Sopenharmony_ci return ret; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci ret = clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 125000000); 5168c2ecf20Sopenharmony_ci if (ret) 5178c2ecf20Sopenharmony_ci return ret; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci ret = clk_set_rate(adpt->clk[EMAC_CLK_MDIO], 25000000); 5208c2ecf20Sopenharmony_ci if (ret) 5218c2ecf20Sopenharmony_ci return ret; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_MDIO]); 5248c2ecf20Sopenharmony_ci if (ret) 5258c2ecf20Sopenharmony_ci return ret; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ret = clk_prepare_enable(adpt->clk[EMAC_CLK_RX]); 5288c2ecf20Sopenharmony_ci if (ret) 5298c2ecf20Sopenharmony_ci return ret; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return clk_prepare_enable(adpt->clk[EMAC_CLK_SYS]); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic void emac_clks_teardown(struct emac_adapter *adpt) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci unsigned int i; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci for (i = 0; i < EMAC_CLK_CNT; i++) 5408c2ecf20Sopenharmony_ci clk_disable_unprepare(adpt->clk[i]); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/* Get the resources */ 5448c2ecf20Sopenharmony_cistatic int emac_probe_resources(struct platform_device *pdev, 5458c2ecf20Sopenharmony_ci struct emac_adapter *adpt) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct net_device *netdev = adpt->netdev; 5488c2ecf20Sopenharmony_ci char maddr[ETH_ALEN]; 5498c2ecf20Sopenharmony_ci int ret = 0; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* get mac address */ 5528c2ecf20Sopenharmony_ci if (device_get_mac_address(&pdev->dev, maddr, ETH_ALEN)) 5538c2ecf20Sopenharmony_ci ether_addr_copy(netdev->dev_addr, maddr); 5548c2ecf20Sopenharmony_ci else 5558c2ecf20Sopenharmony_ci eth_hw_addr_random(netdev); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Core 0 interrupt */ 5588c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 0); 5598c2ecf20Sopenharmony_ci if (ret < 0) 5608c2ecf20Sopenharmony_ci return ret; 5618c2ecf20Sopenharmony_ci adpt->irq.irq = ret; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci /* base register address */ 5648c2ecf20Sopenharmony_ci adpt->base = devm_platform_ioremap_resource(pdev, 0); 5658c2ecf20Sopenharmony_ci if (IS_ERR(adpt->base)) 5668c2ecf20Sopenharmony_ci return PTR_ERR(adpt->base); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* CSR register address */ 5698c2ecf20Sopenharmony_ci adpt->csr = devm_platform_ioremap_resource(pdev, 1); 5708c2ecf20Sopenharmony_ci if (IS_ERR(adpt->csr)) 5718c2ecf20Sopenharmony_ci return PTR_ERR(adpt->csr); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci netdev->base_addr = (unsigned long)adpt->base; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return 0; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic const struct of_device_id emac_dt_match[] = { 5798c2ecf20Sopenharmony_ci { 5808c2ecf20Sopenharmony_ci .compatible = "qcom,fsm9900-emac", 5818c2ecf20Sopenharmony_ci }, 5828c2ecf20Sopenharmony_ci {} 5838c2ecf20Sopenharmony_ci}; 5848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, emac_dt_match); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI) 5878c2ecf20Sopenharmony_cistatic const struct acpi_device_id emac_acpi_match[] = { 5888c2ecf20Sopenharmony_ci { 5898c2ecf20Sopenharmony_ci .id = "QCOM8070", 5908c2ecf20Sopenharmony_ci }, 5918c2ecf20Sopenharmony_ci {} 5928c2ecf20Sopenharmony_ci}; 5938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, emac_acpi_match); 5948c2ecf20Sopenharmony_ci#endif 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int emac_probe(struct platform_device *pdev) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct net_device *netdev; 5998c2ecf20Sopenharmony_ci struct emac_adapter *adpt; 6008c2ecf20Sopenharmony_ci struct emac_sgmii *phy; 6018c2ecf20Sopenharmony_ci u16 devid, revid; 6028c2ecf20Sopenharmony_ci u32 reg; 6038c2ecf20Sopenharmony_ci int ret; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* The TPD buffer address is limited to: 6068c2ecf20Sopenharmony_ci * 1. PTP: 45bits. (Driver doesn't support yet.) 6078c2ecf20Sopenharmony_ci * 2. NON-PTP: 46bits. 6088c2ecf20Sopenharmony_ci */ 6098c2ecf20Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(46)); 6108c2ecf20Sopenharmony_ci if (ret) { 6118c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not set DMA mask\n"); 6128c2ecf20Sopenharmony_ci return ret; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct emac_adapter)); 6168c2ecf20Sopenharmony_ci if (!netdev) 6178c2ecf20Sopenharmony_ci return -ENOMEM; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, netdev); 6208c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 6218c2ecf20Sopenharmony_ci emac_set_ethtool_ops(netdev); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci adpt = netdev_priv(netdev); 6248c2ecf20Sopenharmony_ci adpt->netdev = netdev; 6258c2ecf20Sopenharmony_ci adpt->msg_enable = EMAC_MSG_DEFAULT; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci phy = &adpt->phy; 6288c2ecf20Sopenharmony_ci atomic_set(&phy->decode_error_count, 0); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci mutex_init(&adpt->reset_lock); 6318c2ecf20Sopenharmony_ci spin_lock_init(&adpt->stats.lock); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci adpt->irq.mask = RX_PKT_INT0 | IMR_NORMAL_MASK; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci ret = emac_probe_resources(pdev, adpt); 6368c2ecf20Sopenharmony_ci if (ret) 6378c2ecf20Sopenharmony_ci goto err_undo_netdev; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* initialize clocks */ 6408c2ecf20Sopenharmony_ci ret = emac_clks_phase1_init(pdev, adpt); 6418c2ecf20Sopenharmony_ci if (ret) { 6428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not initialize clocks\n"); 6438c2ecf20Sopenharmony_ci goto err_undo_netdev; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci netdev->watchdog_timeo = EMAC_WATCHDOG_TIME; 6478c2ecf20Sopenharmony_ci netdev->irq = adpt->irq.irq; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci netdev->netdev_ops = &emac_netdev_ops; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci emac_init_adapter(adpt); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* init external phy */ 6548c2ecf20Sopenharmony_ci ret = emac_phy_config(pdev, adpt); 6558c2ecf20Sopenharmony_ci if (ret) 6568c2ecf20Sopenharmony_ci goto err_undo_clocks; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* init internal sgmii phy */ 6598c2ecf20Sopenharmony_ci ret = emac_sgmii_config(pdev, adpt); 6608c2ecf20Sopenharmony_ci if (ret) 6618c2ecf20Sopenharmony_ci goto err_undo_mdiobus; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* enable clocks */ 6648c2ecf20Sopenharmony_ci ret = emac_clks_phase2_init(pdev, adpt); 6658c2ecf20Sopenharmony_ci if (ret) { 6668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not initialize clocks\n"); 6678c2ecf20Sopenharmony_ci goto err_undo_mdiobus; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* set hw features */ 6718c2ecf20Sopenharmony_ci netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM | 6728c2ecf20Sopenharmony_ci NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_RX | 6738c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX; 6748c2ecf20Sopenharmony_ci netdev->hw_features = netdev->features; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci netdev->vlan_features |= NETIF_F_SG | NETIF_F_HW_CSUM | 6778c2ecf20Sopenharmony_ci NETIF_F_TSO | NETIF_F_TSO6; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* MTU range: 46 - 9194 */ 6808c2ecf20Sopenharmony_ci netdev->min_mtu = EMAC_MIN_ETH_FRAME_SIZE - 6818c2ecf20Sopenharmony_ci (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); 6828c2ecf20Sopenharmony_ci netdev->max_mtu = EMAC_MAX_ETH_FRAME_SIZE - 6838c2ecf20Sopenharmony_ci (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci INIT_WORK(&adpt->work_thread, emac_work_thread); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* Initialize queues */ 6888c2ecf20Sopenharmony_ci emac_mac_rx_tx_ring_init_all(pdev, adpt); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci netif_napi_add(netdev, &adpt->rx_q.napi, emac_napi_rtx, 6918c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci ret = register_netdev(netdev); 6948c2ecf20Sopenharmony_ci if (ret) { 6958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not register net device\n"); 6968c2ecf20Sopenharmony_ci goto err_undo_napi; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci reg = readl_relaxed(adpt->base + EMAC_DMA_MAS_CTRL); 7008c2ecf20Sopenharmony_ci devid = (reg & DEV_ID_NUM_BMSK) >> DEV_ID_NUM_SHFT; 7018c2ecf20Sopenharmony_ci revid = (reg & DEV_REV_NUM_BMSK) >> DEV_REV_NUM_SHFT; 7028c2ecf20Sopenharmony_ci reg = readl_relaxed(adpt->base + EMAC_CORE_HW_VERSION); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci netif_info(adpt, probe, netdev, 7058c2ecf20Sopenharmony_ci "hardware id %d.%d, hardware version %d.%d.%d\n", 7068c2ecf20Sopenharmony_ci devid, revid, 7078c2ecf20Sopenharmony_ci (reg & MAJOR_BMSK) >> MAJOR_SHFT, 7088c2ecf20Sopenharmony_ci (reg & MINOR_BMSK) >> MINOR_SHFT, 7098c2ecf20Sopenharmony_ci (reg & STEP_BMSK) >> STEP_SHFT); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cierr_undo_napi: 7148c2ecf20Sopenharmony_ci netif_napi_del(&adpt->rx_q.napi); 7158c2ecf20Sopenharmony_cierr_undo_mdiobus: 7168c2ecf20Sopenharmony_ci put_device(&adpt->phydev->mdio.dev); 7178c2ecf20Sopenharmony_ci mdiobus_unregister(adpt->mii_bus); 7188c2ecf20Sopenharmony_cierr_undo_clocks: 7198c2ecf20Sopenharmony_ci emac_clks_teardown(adpt); 7208c2ecf20Sopenharmony_cierr_undo_netdev: 7218c2ecf20Sopenharmony_ci free_netdev(netdev); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci return ret; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic int emac_remove(struct platform_device *pdev) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(&pdev->dev); 7298c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 7328c2ecf20Sopenharmony_ci netif_tx_disable(netdev); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci unregister_netdev(netdev); 7358c2ecf20Sopenharmony_ci netif_napi_del(&adpt->rx_q.napi); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci free_irq(adpt->irq.irq, &adpt->irq); 7388c2ecf20Sopenharmony_ci cancel_work_sync(&adpt->work_thread); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci emac_clks_teardown(adpt); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci put_device(&adpt->phydev->mdio.dev); 7438c2ecf20Sopenharmony_ci mdiobus_unregister(adpt->mii_bus); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (adpt->phy.digital) 7468c2ecf20Sopenharmony_ci iounmap(adpt->phy.digital); 7478c2ecf20Sopenharmony_ci iounmap(adpt->phy.base); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci free_netdev(netdev); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return 0; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic void emac_shutdown(struct platform_device *pdev) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(&pdev->dev); 7578c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (netdev->flags & IFF_UP) { 7608c2ecf20Sopenharmony_ci /* Closing the SGMII turns off its interrupts */ 7618c2ecf20Sopenharmony_ci emac_sgmii_close(adpt); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci /* Resetting the MAC turns off all DMA and its interrupts */ 7648c2ecf20Sopenharmony_ci emac_mac_reset(adpt); 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic struct platform_driver emac_platform_driver = { 7698c2ecf20Sopenharmony_ci .probe = emac_probe, 7708c2ecf20Sopenharmony_ci .remove = emac_remove, 7718c2ecf20Sopenharmony_ci .driver = { 7728c2ecf20Sopenharmony_ci .name = "qcom-emac", 7738c2ecf20Sopenharmony_ci .of_match_table = emac_dt_match, 7748c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(emac_acpi_match), 7758c2ecf20Sopenharmony_ci }, 7768c2ecf20Sopenharmony_ci .shutdown = emac_shutdown, 7778c2ecf20Sopenharmony_ci}; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cimodule_platform_driver(emac_platform_driver); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7828c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:qcom-emac"); 783