1*97fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b9b17debSTimur Tabi /* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
3b9b17debSTimur Tabi */
4b9b17debSTimur Tabi
5b9b17debSTimur Tabi /* Qualcomm Technologies, Inc. EMAC PHY Controller driver.
6b9b17debSTimur Tabi */
7b9b17debSTimur Tabi
8b9b17debSTimur Tabi #include <linux/of_mdio.h>
9b9b17debSTimur Tabi #include <linux/phy.h>
10b9b17debSTimur Tabi #include <linux/iopoll.h>
115f3d3807STimur Tabi #include <linux/acpi.h>
12b9b17debSTimur Tabi #include "emac.h"
13b9b17debSTimur Tabi
14b9b17debSTimur Tabi /* EMAC base register offsets */
15b9b17debSTimur Tabi #define EMAC_MDIO_CTRL 0x001414
16b9b17debSTimur Tabi #define EMAC_PHY_STS 0x001418
17b9b17debSTimur Tabi #define EMAC_MDIO_EX_CTRL 0x001440
18b9b17debSTimur Tabi
19b9b17debSTimur Tabi /* EMAC_MDIO_CTRL */
20b9b17debSTimur Tabi #define MDIO_MODE BIT(30)
21b9b17debSTimur Tabi #define MDIO_PR BIT(29)
22b9b17debSTimur Tabi #define MDIO_AP_EN BIT(28)
23b9b17debSTimur Tabi #define MDIO_BUSY BIT(27)
24b9b17debSTimur Tabi #define MDIO_CLK_SEL_BMSK 0x7000000
25b9b17debSTimur Tabi #define MDIO_CLK_SEL_SHFT 24
26b9b17debSTimur Tabi #define MDIO_START BIT(23)
27b9b17debSTimur Tabi #define SUP_PREAMBLE BIT(22)
28b9b17debSTimur Tabi #define MDIO_RD_NWR BIT(21)
29b9b17debSTimur Tabi #define MDIO_REG_ADDR_BMSK 0x1f0000
30b9b17debSTimur Tabi #define MDIO_REG_ADDR_SHFT 16
31b9b17debSTimur Tabi #define MDIO_DATA_BMSK 0xffff
32b9b17debSTimur Tabi #define MDIO_DATA_SHFT 0
33b9b17debSTimur Tabi
34b9b17debSTimur Tabi /* EMAC_PHY_STS */
35b9b17debSTimur Tabi #define PHY_ADDR_BMSK 0x1f0000
36b9b17debSTimur Tabi #define PHY_ADDR_SHFT 16
37b9b17debSTimur Tabi
38b9b17debSTimur Tabi #define MDIO_CLK_25_4 0
39b9b17debSTimur Tabi #define MDIO_CLK_25_28 7
40b9b17debSTimur Tabi
41b9b17debSTimur Tabi #define MDIO_WAIT_TIMES 1000
42043ee1deSHemanth Puranik #define MDIO_STATUS_DELAY_TIME 1
43b9b17debSTimur Tabi
emac_mdio_read(struct mii_bus * bus,int addr,int regnum)44b9b17debSTimur Tabi static int emac_mdio_read(struct mii_bus *bus, int addr, int regnum)
45b9b17debSTimur Tabi {
46b9b17debSTimur Tabi struct emac_adapter *adpt = bus->priv;
47b9b17debSTimur Tabi u32 reg;
48b9b17debSTimur Tabi
49b9b17debSTimur Tabi emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK,
50b9b17debSTimur Tabi (addr << PHY_ADDR_SHFT));
51b9b17debSTimur Tabi
52b9b17debSTimur Tabi reg = SUP_PREAMBLE |
53b9b17debSTimur Tabi ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
54b9b17debSTimur Tabi ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
55b9b17debSTimur Tabi MDIO_START | MDIO_RD_NWR;
56b9b17debSTimur Tabi
57b9b17debSTimur Tabi writel(reg, adpt->base + EMAC_MDIO_CTRL);
58b9b17debSTimur Tabi
59b9b17debSTimur Tabi if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg,
60b9b17debSTimur Tabi !(reg & (MDIO_START | MDIO_BUSY)),
61043ee1deSHemanth Puranik MDIO_STATUS_DELAY_TIME, MDIO_WAIT_TIMES * 100))
6224609669STimur Tabi return -EIO;
63b9b17debSTimur Tabi
6424609669STimur Tabi return (reg >> MDIO_DATA_SHFT) & MDIO_DATA_BMSK;
65b9b17debSTimur Tabi }
66b9b17debSTimur Tabi
emac_mdio_write(struct mii_bus * bus,int addr,int regnum,u16 val)67b9b17debSTimur Tabi static int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
68b9b17debSTimur Tabi {
69b9b17debSTimur Tabi struct emac_adapter *adpt = bus->priv;
70b9b17debSTimur Tabi u32 reg;
71b9b17debSTimur Tabi
72b9b17debSTimur Tabi emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK,
73b9b17debSTimur Tabi (addr << PHY_ADDR_SHFT));
74b9b17debSTimur Tabi
75b9b17debSTimur Tabi reg = SUP_PREAMBLE |
76b9b17debSTimur Tabi ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
77b9b17debSTimur Tabi ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
78b9b17debSTimur Tabi ((val << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) |
79b9b17debSTimur Tabi MDIO_START;
80b9b17debSTimur Tabi
81b9b17debSTimur Tabi writel(reg, adpt->base + EMAC_MDIO_CTRL);
82b9b17debSTimur Tabi
83b9b17debSTimur Tabi if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg,
84043ee1deSHemanth Puranik !(reg & (MDIO_START | MDIO_BUSY)),
85043ee1deSHemanth Puranik MDIO_STATUS_DELAY_TIME, MDIO_WAIT_TIMES * 100))
8624609669STimur Tabi return -EIO;
87b9b17debSTimur Tabi
8824609669STimur Tabi return 0;
89b9b17debSTimur Tabi }
90b9b17debSTimur Tabi
91b9b17debSTimur Tabi /* Configure the MDIO bus and connect the external PHY */
emac_phy_config(struct platform_device * pdev,struct emac_adapter * adpt)92b9b17debSTimur Tabi int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt)
93b9b17debSTimur Tabi {
94b9b17debSTimur Tabi struct device_node *np = pdev->dev.of_node;
95b9b17debSTimur Tabi struct mii_bus *mii_bus;
96b9b17debSTimur Tabi int ret;
97b9b17debSTimur Tabi
98b9b17debSTimur Tabi /* Create the mii_bus object for talking to the MDIO bus */
99b9b17debSTimur Tabi adpt->mii_bus = mii_bus = devm_mdiobus_alloc(&pdev->dev);
100b9b17debSTimur Tabi if (!mii_bus)
101b9b17debSTimur Tabi return -ENOMEM;
102b9b17debSTimur Tabi
103b9b17debSTimur Tabi mii_bus->name = "emac-mdio";
104b9b17debSTimur Tabi snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
105b9b17debSTimur Tabi mii_bus->read = emac_mdio_read;
106b9b17debSTimur Tabi mii_bus->write = emac_mdio_write;
107b9b17debSTimur Tabi mii_bus->parent = &pdev->dev;
108b9b17debSTimur Tabi mii_bus->priv = adpt;
109b9b17debSTimur Tabi
1105f3d3807STimur Tabi if (has_acpi_companion(&pdev->dev)) {
1115f3d3807STimur Tabi u32 phy_addr;
1125f3d3807STimur Tabi
1135f3d3807STimur Tabi ret = mdiobus_register(mii_bus);
1145f3d3807STimur Tabi if (ret) {
1155f3d3807STimur Tabi dev_err(&pdev->dev, "could not register mdio bus\n");
1165f3d3807STimur Tabi return ret;
1175f3d3807STimur Tabi }
1185f3d3807STimur Tabi ret = device_property_read_u32(&pdev->dev, "phy-channel",
1195f3d3807STimur Tabi &phy_addr);
1205f3d3807STimur Tabi if (ret)
1215f3d3807STimur Tabi /* If we can't read a valid phy address, then assume
1225f3d3807STimur Tabi * that there is only one phy on this mdio bus.
1235f3d3807STimur Tabi */
1245f3d3807STimur Tabi adpt->phydev = phy_find_first(mii_bus);
1255f3d3807STimur Tabi else
1265f3d3807STimur Tabi adpt->phydev = mdiobus_get_phy(mii_bus, phy_addr);
1275f3d3807STimur Tabi
128994c5483STimur Tabi /* of_phy_find_device() claims a reference to the phydev,
129994c5483STimur Tabi * so we do that here manually as well. When the driver
130994c5483STimur Tabi * later unloads, it can unilaterally drop the reference
131994c5483STimur Tabi * without worrying about ACPI vs DT.
132994c5483STimur Tabi */
133994c5483STimur Tabi if (adpt->phydev)
134994c5483STimur Tabi get_device(&adpt->phydev->mdio.dev);
1355f3d3807STimur Tabi } else {
1365f3d3807STimur Tabi struct device_node *phy_np;
1375f3d3807STimur Tabi
138b9b17debSTimur Tabi ret = of_mdiobus_register(mii_bus, np);
139b9b17debSTimur Tabi if (ret) {
140b9b17debSTimur Tabi dev_err(&pdev->dev, "could not register mdio bus\n");
141b9b17debSTimur Tabi return ret;
142b9b17debSTimur Tabi }
143b9b17debSTimur Tabi
144b9b17debSTimur Tabi phy_np = of_parse_phandle(np, "phy-handle", 0);
145b9b17debSTimur Tabi adpt->phydev = of_phy_find_device(phy_np);
1466ffe1c4cSJohan Hovold of_node_put(phy_np);
1475f3d3807STimur Tabi }
1485f3d3807STimur Tabi
149b9b17debSTimur Tabi if (!adpt->phydev) {
150b9b17debSTimur Tabi dev_err(&pdev->dev, "could not find external phy\n");
151b9b17debSTimur Tabi mdiobus_unregister(mii_bus);
152b9b17debSTimur Tabi return -ENODEV;
153b9b17debSTimur Tabi }
154b9b17debSTimur Tabi
155b9b17debSTimur Tabi return 0;
156b9b17debSTimur Tabi }
157