xref: /openbmc/linux/drivers/net/ethernet/qualcomm/emac/emac-phy.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
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