Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. */ /* Qualcomm Technologies, Inc. EMAC PHY Controller driver. */ #include <linux/of_mdio.h> #include <linux/phy.h> #include <linux/iopoll.h> #include <linux/acpi.h> #include "emac.h" /* EMAC base register offsets */ #define EMAC_MDIO_CTRL 0x001414 #define EMAC_PHY_STS 0x001418 #define EMAC_MDIO_EX_CTRL 0x001440 /* EMAC_MDIO_CTRL */ #define MDIO_MODE BIT(30) #define MDIO_PR BIT(29) #define MDIO_AP_EN BIT(28) #define MDIO_BUSY BIT(27) #define MDIO_CLK_SEL_BMSK 0x7000000 #define MDIO_CLK_SEL_SHFT 24 #define MDIO_START BIT(23) #define SUP_PREAMBLE BIT(22) #define MDIO_RD_NWR BIT(21) #define MDIO_REG_ADDR_BMSK 0x1f0000 #define MDIO_REG_ADDR_SHFT 16 #define MDIO_DATA_BMSK 0xffff #define MDIO_DATA_SHFT 0 /* EMAC_PHY_STS */ #define PHY_ADDR_BMSK 0x1f0000 #define PHY_ADDR_SHFT 16 #define MDIO_CLK_25_4 0 #define MDIO_CLK_25_28 7 #define MDIO_WAIT_TIMES 1000 #define MDIO_STATUS_DELAY_TIME 1 static int emac_mdio_read(struct mii_bus *bus, int addr, int regnum) { struct emac_adapter *adpt = bus->priv; u32 reg; emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK, (addr << PHY_ADDR_SHFT)); reg = SUP_PREAMBLE | ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | MDIO_START | MDIO_RD_NWR; writel(reg, adpt->base + EMAC_MDIO_CTRL); if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg, !(reg & (MDIO_START | MDIO_BUSY)), MDIO_STATUS_DELAY_TIME, MDIO_WAIT_TIMES * 100)) return -EIO; return (reg >> MDIO_DATA_SHFT) & MDIO_DATA_BMSK; } static int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) { struct emac_adapter *adpt = bus->priv; u32 reg; emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK, (addr << PHY_ADDR_SHFT)); reg = SUP_PREAMBLE | ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | ((val << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) | MDIO_START; writel(reg, adpt->base + EMAC_MDIO_CTRL); if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg, !(reg & (MDIO_START | MDIO_BUSY)), MDIO_STATUS_DELAY_TIME, MDIO_WAIT_TIMES * 100)) return -EIO; return 0; } /* Configure the MDIO bus and connect the external PHY */ int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt) { struct device_node *np = pdev->dev.of_node; struct mii_bus *mii_bus; int ret; /* Create the mii_bus object for talking to the MDIO bus */ adpt->mii_bus = mii_bus = devm_mdiobus_alloc(&pdev->dev); if (!mii_bus) return -ENOMEM; mii_bus->name = "emac-mdio"; snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); mii_bus->read = emac_mdio_read; mii_bus->write = emac_mdio_write; mii_bus->parent = &pdev->dev; mii_bus->priv = adpt; if (has_acpi_companion(&pdev->dev)) { u32 phy_addr; ret = mdiobus_register(mii_bus); if (ret) { dev_err(&pdev->dev, "could not register mdio bus\n"); return ret; } ret = device_property_read_u32(&pdev->dev, "phy-channel", &phy_addr); if (ret) /* If we can't read a valid phy address, then assume * that there is only one phy on this mdio bus. */ adpt->phydev = phy_find_first(mii_bus); else adpt->phydev = mdiobus_get_phy(mii_bus, phy_addr); /* of_phy_find_device() claims a reference to the phydev, * so we do that here manually as well. When the driver * later unloads, it can unilaterally drop the reference * without worrying about ACPI vs DT. */ if (adpt->phydev) get_device(&adpt->phydev->mdio.dev); } else { struct device_node *phy_np; ret = of_mdiobus_register(mii_bus, np); if (ret) { dev_err(&pdev->dev, "could not register mdio bus\n"); return ret; } phy_np = of_parse_phandle(np, "phy-handle", 0); adpt->phydev = of_phy_find_device(phy_np); of_node_put(phy_np); } if (!adpt->phydev) { dev_err(&pdev->dev, "could not find external phy\n"); mdiobus_unregister(mii_bus); return -ENODEV; } return 0; } |