18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci */ 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci/* Qualcomm Technologies, Inc. EMAC SGMII Controller driver. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <linux/of_device.h> 128c2ecf20Sopenharmony_ci#include "emac.h" 138c2ecf20Sopenharmony_ci#include "emac-mac.h" 148c2ecf20Sopenharmony_ci#include "emac-sgmii.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* EMAC_SGMII register offsets */ 178c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_AUTONEG_CFG2 0x0048 188c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_SPEED_CFG1 0x0074 198c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_IRQ_CMD 0x00ac 208c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_INTERRUPT_CLEAR 0x00b0 218c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x00b4 228c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_INTERRUPT_STATUS 0x00b8 238c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_RX_CHK_STATUS 0x00d4 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define FORCE_AN_TX_CFG BIT(5) 268c2ecf20Sopenharmony_ci#define FORCE_AN_RX_CFG BIT(4) 278c2ecf20Sopenharmony_ci#define AN_ENABLE BIT(0) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define DUPLEX_MODE BIT(4) 308c2ecf20Sopenharmony_ci#define SPDMODE_1000 BIT(1) 318c2ecf20Sopenharmony_ci#define SPDMODE_100 BIT(0) 328c2ecf20Sopenharmony_ci#define SPDMODE_10 0 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define CDR_ALIGN_DET BIT(6) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define IRQ_GLOBAL_CLEAR BIT(0) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define DECODE_CODE_ERR BIT(7) 398c2ecf20Sopenharmony_ci#define DECODE_DISP_ERR BIT(6) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define SGMII_PHY_IRQ_CLR_WAIT_TIME 10 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define SGMII_PHY_INTERRUPT_ERR (DECODE_CODE_ERR | DECODE_DISP_ERR) 448c2ecf20Sopenharmony_ci#define SGMII_ISR_MASK (SGMII_PHY_INTERRUPT_ERR) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define SERDES_START_WAIT_TIMES 100 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciint emac_sgmii_init(struct emac_adapter *adpt) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->init)) 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return adpt->phy.sgmii_ops->init(adpt); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ciint emac_sgmii_open(struct emac_adapter *adpt) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->open)) 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return adpt->phy.sgmii_ops->open(adpt); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_civoid emac_sgmii_close(struct emac_adapter *adpt) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->close)) 678c2ecf20Sopenharmony_ci return; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci adpt->phy.sgmii_ops->close(adpt); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciint emac_sgmii_link_change(struct emac_adapter *adpt, bool link_state) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->link_change)) 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return adpt->phy.sgmii_ops->link_change(adpt, link_state); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_civoid emac_sgmii_reset(struct emac_adapter *adpt) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->reset)) 838c2ecf20Sopenharmony_ci return; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci adpt->phy.sgmii_ops->reset(adpt); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Initialize the SGMII link between the internal and external PHYs. */ 898c2ecf20Sopenharmony_cistatic void emac_sgmii_link_init(struct emac_adapter *adpt) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct emac_sgmii *phy = &adpt->phy; 928c2ecf20Sopenharmony_ci u32 val; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Always use autonegotiation. It works no matter how the external 958c2ecf20Sopenharmony_ci * PHY is configured. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci val = readl(phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2); 988c2ecf20Sopenharmony_ci val &= ~(FORCE_AN_RX_CFG | FORCE_AN_TX_CFG); 998c2ecf20Sopenharmony_ci val |= AN_ENABLE; 1008c2ecf20Sopenharmony_ci writel(val, phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int emac_sgmii_irq_clear(struct emac_adapter *adpt, u8 irq_bits) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct emac_sgmii *phy = &adpt->phy; 1068c2ecf20Sopenharmony_ci u8 status; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci writel_relaxed(irq_bits, phy->base + EMAC_SGMII_PHY_INTERRUPT_CLEAR); 1098c2ecf20Sopenharmony_ci writel_relaxed(IRQ_GLOBAL_CLEAR, phy->base + EMAC_SGMII_PHY_IRQ_CMD); 1108c2ecf20Sopenharmony_ci /* Ensure interrupt clear command is written to HW */ 1118c2ecf20Sopenharmony_ci wmb(); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* After set the IRQ_GLOBAL_CLEAR bit, the status clearing must 1148c2ecf20Sopenharmony_ci * be confirmed before clearing the bits in other registers. 1158c2ecf20Sopenharmony_ci * It takes a few cycles for hw to clear the interrupt status. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci if (readl_poll_timeout_atomic(phy->base + 1188c2ecf20Sopenharmony_ci EMAC_SGMII_PHY_INTERRUPT_STATUS, 1198c2ecf20Sopenharmony_ci status, !(status & irq_bits), 1, 1208c2ecf20Sopenharmony_ci SGMII_PHY_IRQ_CLR_WAIT_TIME)) { 1218c2ecf20Sopenharmony_ci net_err_ratelimited("%s: failed to clear SGMII irq: status:0x%x bits:0x%x\n", 1228c2ecf20Sopenharmony_ci adpt->netdev->name, status, irq_bits); 1238c2ecf20Sopenharmony_ci return -EIO; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Finalize clearing procedure */ 1278c2ecf20Sopenharmony_ci writel_relaxed(0, phy->base + EMAC_SGMII_PHY_IRQ_CMD); 1288c2ecf20Sopenharmony_ci writel_relaxed(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_CLEAR); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Ensure that clearing procedure finalization is written to HW */ 1318c2ecf20Sopenharmony_ci wmb(); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* The number of decode errors that triggers a reset */ 1378c2ecf20Sopenharmony_ci#define DECODE_ERROR_LIMIT 2 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic irqreturn_t emac_sgmii_interrupt(int irq, void *data) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct emac_adapter *adpt = data; 1428c2ecf20Sopenharmony_ci struct emac_sgmii *phy = &adpt->phy; 1438c2ecf20Sopenharmony_ci u8 status; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci status = readl(phy->base + EMAC_SGMII_PHY_INTERRUPT_STATUS); 1468c2ecf20Sopenharmony_ci status &= SGMII_ISR_MASK; 1478c2ecf20Sopenharmony_ci if (!status) 1488c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* If we get a decoding error and CDR is not locked, then try 1518c2ecf20Sopenharmony_ci * resetting the internal PHY. The internal PHY uses an embedded 1528c2ecf20Sopenharmony_ci * clock with Clock and Data Recovery (CDR) to recover the 1538c2ecf20Sopenharmony_ci * clock and data. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci if (status & SGMII_PHY_INTERRUPT_ERR) { 1568c2ecf20Sopenharmony_ci int count; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* The SGMII is capable of recovering from some decode 1598c2ecf20Sopenharmony_ci * errors automatically. However, if we get multiple 1608c2ecf20Sopenharmony_ci * decode errors in a row, then assume that something 1618c2ecf20Sopenharmony_ci * is wrong and reset the interface. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci count = atomic_inc_return(&phy->decode_error_count); 1648c2ecf20Sopenharmony_ci if (count == DECODE_ERROR_LIMIT) { 1658c2ecf20Sopenharmony_ci schedule_work(&adpt->work_thread); 1668c2ecf20Sopenharmony_ci atomic_set(&phy->decode_error_count, 0); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci } else { 1698c2ecf20Sopenharmony_ci /* We only care about consecutive decode errors. */ 1708c2ecf20Sopenharmony_ci atomic_set(&phy->decode_error_count, 0); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (emac_sgmii_irq_clear(adpt, status)) 1748c2ecf20Sopenharmony_ci schedule_work(&adpt->work_thread); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void emac_sgmii_reset_prepare(struct emac_adapter *adpt) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct emac_sgmii *phy = &adpt->phy; 1828c2ecf20Sopenharmony_ci u32 val; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Reset PHY */ 1858c2ecf20Sopenharmony_ci val = readl(phy->base + EMAC_EMAC_WRAPPER_CSR2); 1868c2ecf20Sopenharmony_ci writel(((val & ~PHY_RESET) | PHY_RESET), phy->base + 1878c2ecf20Sopenharmony_ci EMAC_EMAC_WRAPPER_CSR2); 1888c2ecf20Sopenharmony_ci /* Ensure phy-reset command is written to HW before the release cmd */ 1898c2ecf20Sopenharmony_ci msleep(50); 1908c2ecf20Sopenharmony_ci val = readl(phy->base + EMAC_EMAC_WRAPPER_CSR2); 1918c2ecf20Sopenharmony_ci writel((val & ~PHY_RESET), phy->base + EMAC_EMAC_WRAPPER_CSR2); 1928c2ecf20Sopenharmony_ci /* Ensure phy-reset release command is written to HW before initializing 1938c2ecf20Sopenharmony_ci * SGMII 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci msleep(50); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void emac_sgmii_common_reset(struct emac_adapter *adpt) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci emac_sgmii_reset_prepare(adpt); 2038c2ecf20Sopenharmony_ci emac_sgmii_link_init(adpt); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = emac_sgmii_init(adpt); 2068c2ecf20Sopenharmony_ci if (ret) 2078c2ecf20Sopenharmony_ci netdev_err(adpt->netdev, 2088c2ecf20Sopenharmony_ci "could not reinitialize internal PHY (error=%i)\n", 2098c2ecf20Sopenharmony_ci ret); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int emac_sgmii_common_open(struct emac_adapter *adpt) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct emac_sgmii *sgmii = &adpt->phy; 2158c2ecf20Sopenharmony_ci int ret; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (sgmii->irq) { 2188c2ecf20Sopenharmony_ci /* Make sure interrupts are cleared and disabled first */ 2198c2ecf20Sopenharmony_ci ret = emac_sgmii_irq_clear(adpt, 0xff); 2208c2ecf20Sopenharmony_ci if (ret) 2218c2ecf20Sopenharmony_ci return ret; 2228c2ecf20Sopenharmony_ci writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ret = request_irq(sgmii->irq, emac_sgmii_interrupt, 0, 2258c2ecf20Sopenharmony_ci "emac-sgmii", adpt); 2268c2ecf20Sopenharmony_ci if (ret) { 2278c2ecf20Sopenharmony_ci netdev_err(adpt->netdev, 2288c2ecf20Sopenharmony_ci "could not register handler for internal PHY\n"); 2298c2ecf20Sopenharmony_ci return ret; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void emac_sgmii_common_close(struct emac_adapter *adpt) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct emac_sgmii *sgmii = &adpt->phy; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Make sure interrupts are disabled */ 2418c2ecf20Sopenharmony_ci writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK); 2428c2ecf20Sopenharmony_ci free_irq(sgmii->irq, adpt); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* The error interrupts are only valid after the link is up */ 2468c2ecf20Sopenharmony_cistatic int emac_sgmii_common_link_change(struct emac_adapter *adpt, bool linkup) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct emac_sgmii *sgmii = &adpt->phy; 2498c2ecf20Sopenharmony_ci int ret; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (linkup) { 2528c2ecf20Sopenharmony_ci /* Clear and enable interrupts */ 2538c2ecf20Sopenharmony_ci ret = emac_sgmii_irq_clear(adpt, 0xff); 2548c2ecf20Sopenharmony_ci if (ret) 2558c2ecf20Sopenharmony_ci return ret; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci writel(SGMII_ISR_MASK, 2588c2ecf20Sopenharmony_ci sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK); 2598c2ecf20Sopenharmony_ci } else { 2608c2ecf20Sopenharmony_ci /* Disable interrupts */ 2618c2ecf20Sopenharmony_ci writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK); 2628c2ecf20Sopenharmony_ci synchronize_irq(sgmii->irq); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic struct sgmii_ops fsm9900_ops = { 2698c2ecf20Sopenharmony_ci .init = emac_sgmii_init_fsm9900, 2708c2ecf20Sopenharmony_ci .open = emac_sgmii_common_open, 2718c2ecf20Sopenharmony_ci .close = emac_sgmii_common_close, 2728c2ecf20Sopenharmony_ci .link_change = emac_sgmii_common_link_change, 2738c2ecf20Sopenharmony_ci .reset = emac_sgmii_common_reset, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic struct sgmii_ops qdf2432_ops = { 2778c2ecf20Sopenharmony_ci .init = emac_sgmii_init_qdf2432, 2788c2ecf20Sopenharmony_ci .open = emac_sgmii_common_open, 2798c2ecf20Sopenharmony_ci .close = emac_sgmii_common_close, 2808c2ecf20Sopenharmony_ci .link_change = emac_sgmii_common_link_change, 2818c2ecf20Sopenharmony_ci .reset = emac_sgmii_common_reset, 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 2858c2ecf20Sopenharmony_cistatic struct sgmii_ops qdf2400_ops = { 2868c2ecf20Sopenharmony_ci .init = emac_sgmii_init_qdf2400, 2878c2ecf20Sopenharmony_ci .open = emac_sgmii_common_open, 2888c2ecf20Sopenharmony_ci .close = emac_sgmii_common_close, 2898c2ecf20Sopenharmony_ci .link_change = emac_sgmii_common_link_change, 2908c2ecf20Sopenharmony_ci .reset = emac_sgmii_common_reset, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci#endif 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int emac_sgmii_acpi_match(struct device *dev, void *data) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 2978c2ecf20Sopenharmony_ci static const struct acpi_device_id match_table[] = { 2988c2ecf20Sopenharmony_ci { 2998c2ecf20Sopenharmony_ci .id = "QCOM8071", 3008c2ecf20Sopenharmony_ci }, 3018c2ecf20Sopenharmony_ci {} 3028c2ecf20Sopenharmony_ci }; 3038c2ecf20Sopenharmony_ci const struct acpi_device_id *id = acpi_match_device(match_table, dev); 3048c2ecf20Sopenharmony_ci struct sgmii_ops **ops = data; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (id) { 3078c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 3088c2ecf20Sopenharmony_ci unsigned long long hrv; 3098c2ecf20Sopenharmony_ci acpi_status status; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv); 3128c2ecf20Sopenharmony_ci if (status) { 3138c2ecf20Sopenharmony_ci if (status == AE_NOT_FOUND) 3148c2ecf20Sopenharmony_ci /* Older versions of the QDF2432 ACPI tables do 3158c2ecf20Sopenharmony_ci * not have an _HRV property. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci hrv = 1; 3188c2ecf20Sopenharmony_ci else 3198c2ecf20Sopenharmony_ci /* Something is wrong with the tables */ 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci switch (hrv) { 3248c2ecf20Sopenharmony_ci case 1: 3258c2ecf20Sopenharmony_ci *ops = &qdf2432_ops; 3268c2ecf20Sopenharmony_ci return 1; 3278c2ecf20Sopenharmony_ci case 2: 3288c2ecf20Sopenharmony_ci *ops = &qdf2400_ops; 3298c2ecf20Sopenharmony_ci return 1; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci#endif 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic const struct of_device_id emac_sgmii_dt_match[] = { 3388c2ecf20Sopenharmony_ci { 3398c2ecf20Sopenharmony_ci .compatible = "qcom,fsm9900-emac-sgmii", 3408c2ecf20Sopenharmony_ci .data = &fsm9900_ops, 3418c2ecf20Sopenharmony_ci }, 3428c2ecf20Sopenharmony_ci { 3438c2ecf20Sopenharmony_ci .compatible = "qcom,qdf2432-emac-sgmii", 3448c2ecf20Sopenharmony_ci .data = &qdf2432_ops, 3458c2ecf20Sopenharmony_ci }, 3468c2ecf20Sopenharmony_ci {} 3478c2ecf20Sopenharmony_ci}; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ciint emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct platform_device *sgmii_pdev = NULL; 3528c2ecf20Sopenharmony_ci struct emac_sgmii *phy = &adpt->phy; 3538c2ecf20Sopenharmony_ci struct resource *res; 3548c2ecf20Sopenharmony_ci int ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (has_acpi_companion(&pdev->dev)) { 3578c2ecf20Sopenharmony_ci struct device *dev; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci dev = device_find_child(&pdev->dev, &phy->sgmii_ops, 3608c2ecf20Sopenharmony_ci emac_sgmii_acpi_match); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (!dev) { 3638c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "cannot find internal phy node\n"); 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci sgmii_pdev = to_platform_device(dev); 3688c2ecf20Sopenharmony_ci } else { 3698c2ecf20Sopenharmony_ci const struct of_device_id *match; 3708c2ecf20Sopenharmony_ci struct device_node *np; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci np = of_parse_phandle(pdev->dev.of_node, "internal-phy", 0); 3738c2ecf20Sopenharmony_ci if (!np) { 3748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing internal-phy property\n"); 3758c2ecf20Sopenharmony_ci return -ENODEV; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci sgmii_pdev = of_find_device_by_node(np); 3798c2ecf20Sopenharmony_ci of_node_put(np); 3808c2ecf20Sopenharmony_ci if (!sgmii_pdev) { 3818c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid internal-phy property\n"); 3828c2ecf20Sopenharmony_ci return -ENODEV; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci match = of_match_device(emac_sgmii_dt_match, &sgmii_pdev->dev); 3868c2ecf20Sopenharmony_ci if (!match) { 3878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unrecognized internal phy node\n"); 3888c2ecf20Sopenharmony_ci ret = -ENODEV; 3898c2ecf20Sopenharmony_ci goto error_put_device; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci phy->sgmii_ops = (struct sgmii_ops *)match->data; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* Base address is the first address */ 3968c2ecf20Sopenharmony_ci res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0); 3978c2ecf20Sopenharmony_ci if (!res) { 3988c2ecf20Sopenharmony_ci ret = -EINVAL; 3998c2ecf20Sopenharmony_ci goto error_put_device; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci phy->base = ioremap(res->start, resource_size(res)); 4038c2ecf20Sopenharmony_ci if (!phy->base) { 4048c2ecf20Sopenharmony_ci ret = -ENOMEM; 4058c2ecf20Sopenharmony_ci goto error_put_device; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* v2 SGMII has a per-lane digital digital, so parse it if it exists */ 4098c2ecf20Sopenharmony_ci res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 1); 4108c2ecf20Sopenharmony_ci if (res) { 4118c2ecf20Sopenharmony_ci phy->digital = ioremap(res->start, resource_size(res)); 4128c2ecf20Sopenharmony_ci if (!phy->digital) { 4138c2ecf20Sopenharmony_ci ret = -ENOMEM; 4148c2ecf20Sopenharmony_ci goto error_unmap_base; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ret = emac_sgmii_init(adpt); 4198c2ecf20Sopenharmony_ci if (ret) 4208c2ecf20Sopenharmony_ci goto error; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci emac_sgmii_link_init(adpt); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci ret = platform_get_irq(sgmii_pdev, 0); 4258c2ecf20Sopenharmony_ci if (ret > 0) 4268c2ecf20Sopenharmony_ci phy->irq = ret; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* We've remapped the addresses, so we don't need the device any 4298c2ecf20Sopenharmony_ci * more. of_find_device_by_node() says we should release it. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci put_device(&sgmii_pdev->dev); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cierror: 4368c2ecf20Sopenharmony_ci if (phy->digital) 4378c2ecf20Sopenharmony_ci iounmap(phy->digital); 4388c2ecf20Sopenharmony_cierror_unmap_base: 4398c2ecf20Sopenharmony_ci iounmap(phy->base); 4408c2ecf20Sopenharmony_cierror_put_device: 4418c2ecf20Sopenharmony_ci put_device(&sgmii_pdev->dev); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return ret; 4448c2ecf20Sopenharmony_ci} 445