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 PHY Controller driver. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 98c2ecf20Sopenharmony_ci#include <linux/phy.h> 108c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 118c2ecf20Sopenharmony_ci#include <linux/acpi.h> 128c2ecf20Sopenharmony_ci#include "emac.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* EMAC base register offsets */ 158c2ecf20Sopenharmony_ci#define EMAC_MDIO_CTRL 0x001414 168c2ecf20Sopenharmony_ci#define EMAC_PHY_STS 0x001418 178c2ecf20Sopenharmony_ci#define EMAC_MDIO_EX_CTRL 0x001440 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* EMAC_MDIO_CTRL */ 208c2ecf20Sopenharmony_ci#define MDIO_MODE BIT(30) 218c2ecf20Sopenharmony_ci#define MDIO_PR BIT(29) 228c2ecf20Sopenharmony_ci#define MDIO_AP_EN BIT(28) 238c2ecf20Sopenharmony_ci#define MDIO_BUSY BIT(27) 248c2ecf20Sopenharmony_ci#define MDIO_CLK_SEL_BMSK 0x7000000 258c2ecf20Sopenharmony_ci#define MDIO_CLK_SEL_SHFT 24 268c2ecf20Sopenharmony_ci#define MDIO_START BIT(23) 278c2ecf20Sopenharmony_ci#define SUP_PREAMBLE BIT(22) 288c2ecf20Sopenharmony_ci#define MDIO_RD_NWR BIT(21) 298c2ecf20Sopenharmony_ci#define MDIO_REG_ADDR_BMSK 0x1f0000 308c2ecf20Sopenharmony_ci#define MDIO_REG_ADDR_SHFT 16 318c2ecf20Sopenharmony_ci#define MDIO_DATA_BMSK 0xffff 328c2ecf20Sopenharmony_ci#define MDIO_DATA_SHFT 0 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* EMAC_PHY_STS */ 358c2ecf20Sopenharmony_ci#define PHY_ADDR_BMSK 0x1f0000 368c2ecf20Sopenharmony_ci#define PHY_ADDR_SHFT 16 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define MDIO_CLK_25_4 0 398c2ecf20Sopenharmony_ci#define MDIO_CLK_25_28 7 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define MDIO_WAIT_TIMES 1000 428c2ecf20Sopenharmony_ci#define MDIO_STATUS_DELAY_TIME 1 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int emac_mdio_read(struct mii_bus *bus, int addr, int regnum) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct emac_adapter *adpt = bus->priv; 478c2ecf20Sopenharmony_ci u32 reg; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK, 508c2ecf20Sopenharmony_ci (addr << PHY_ADDR_SHFT)); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci reg = SUP_PREAMBLE | 538c2ecf20Sopenharmony_ci ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | 548c2ecf20Sopenharmony_ci ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | 558c2ecf20Sopenharmony_ci MDIO_START | MDIO_RD_NWR; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci writel(reg, adpt->base + EMAC_MDIO_CTRL); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg, 608c2ecf20Sopenharmony_ci !(reg & (MDIO_START | MDIO_BUSY)), 618c2ecf20Sopenharmony_ci MDIO_STATUS_DELAY_TIME, MDIO_WAIT_TIMES * 100)) 628c2ecf20Sopenharmony_ci return -EIO; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return (reg >> MDIO_DATA_SHFT) & MDIO_DATA_BMSK; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct emac_adapter *adpt = bus->priv; 708c2ecf20Sopenharmony_ci u32 reg; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK, 738c2ecf20Sopenharmony_ci (addr << PHY_ADDR_SHFT)); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci reg = SUP_PREAMBLE | 768c2ecf20Sopenharmony_ci ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | 778c2ecf20Sopenharmony_ci ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | 788c2ecf20Sopenharmony_ci ((val << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) | 798c2ecf20Sopenharmony_ci MDIO_START; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci writel(reg, adpt->base + EMAC_MDIO_CTRL); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg, 848c2ecf20Sopenharmony_ci !(reg & (MDIO_START | MDIO_BUSY)), 858c2ecf20Sopenharmony_ci MDIO_STATUS_DELAY_TIME, MDIO_WAIT_TIMES * 100)) 868c2ecf20Sopenharmony_ci return -EIO; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* Configure the MDIO bus and connect the external PHY */ 928c2ecf20Sopenharmony_ciint emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 958c2ecf20Sopenharmony_ci struct mii_bus *mii_bus; 968c2ecf20Sopenharmony_ci int ret; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Create the mii_bus object for talking to the MDIO bus */ 998c2ecf20Sopenharmony_ci adpt->mii_bus = mii_bus = devm_mdiobus_alloc(&pdev->dev); 1008c2ecf20Sopenharmony_ci if (!mii_bus) 1018c2ecf20Sopenharmony_ci return -ENOMEM; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci mii_bus->name = "emac-mdio"; 1048c2ecf20Sopenharmony_ci snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); 1058c2ecf20Sopenharmony_ci mii_bus->read = emac_mdio_read; 1068c2ecf20Sopenharmony_ci mii_bus->write = emac_mdio_write; 1078c2ecf20Sopenharmony_ci mii_bus->parent = &pdev->dev; 1088c2ecf20Sopenharmony_ci mii_bus->priv = adpt; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (has_acpi_companion(&pdev->dev)) { 1118c2ecf20Sopenharmony_ci u32 phy_addr; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = mdiobus_register(mii_bus); 1148c2ecf20Sopenharmony_ci if (ret) { 1158c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not register mdio bus\n"); 1168c2ecf20Sopenharmony_ci return ret; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci ret = device_property_read_u32(&pdev->dev, "phy-channel", 1198c2ecf20Sopenharmony_ci &phy_addr); 1208c2ecf20Sopenharmony_ci if (ret) 1218c2ecf20Sopenharmony_ci /* If we can't read a valid phy address, then assume 1228c2ecf20Sopenharmony_ci * that there is only one phy on this mdio bus. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci adpt->phydev = phy_find_first(mii_bus); 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci adpt->phydev = mdiobus_get_phy(mii_bus, phy_addr); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* of_phy_find_device() claims a reference to the phydev, 1298c2ecf20Sopenharmony_ci * so we do that here manually as well. When the driver 1308c2ecf20Sopenharmony_ci * later unloads, it can unilaterally drop the reference 1318c2ecf20Sopenharmony_ci * without worrying about ACPI vs DT. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci if (adpt->phydev) 1348c2ecf20Sopenharmony_ci get_device(&adpt->phydev->mdio.dev); 1358c2ecf20Sopenharmony_ci } else { 1368c2ecf20Sopenharmony_ci struct device_node *phy_np; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ret = of_mdiobus_register(mii_bus, np); 1398c2ecf20Sopenharmony_ci if (ret) { 1408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not register mdio bus\n"); 1418c2ecf20Sopenharmony_ci return ret; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci phy_np = of_parse_phandle(np, "phy-handle", 0); 1458c2ecf20Sopenharmony_ci adpt->phydev = of_phy_find_device(phy_np); 1468c2ecf20Sopenharmony_ci of_node_put(phy_np); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!adpt->phydev) { 1508c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not find external phy\n"); 1518c2ecf20Sopenharmony_ci mdiobus_unregister(mii_bus); 1528c2ecf20Sopenharmony_ci return -ENODEV; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 157