18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * OF helpers for the MDIO (Ethernet PHY) API
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2009 Secret Lab Technologies, Ltd.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file provides helper functions for extracting PHY device information
88c2ecf20Sopenharmony_ci * out of the OpenFirmware device tree and using it to populate an mii_bus.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/device.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
188c2ecf20Sopenharmony_ci#include <linux/of_mdio.h>
198c2ecf20Sopenharmony_ci#include <linux/of_net.h>
208c2ecf20Sopenharmony_ci#include <linux/phy.h>
218c2ecf20Sopenharmony_ci#include <linux/phy_fixed.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define DEFAULT_GPIO_RESET_DELAY	10	/* in microseconds */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Extract the clause 22 phy ID from the compatible string of the form
298c2ecf20Sopenharmony_ci * ethernet-phy-idAAAA.BBBB */
308c2ecf20Sopenharmony_cistatic int of_get_phy_id(struct device_node *device, u32 *phy_id)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct property *prop;
338c2ecf20Sopenharmony_ci	const char *cp;
348c2ecf20Sopenharmony_ci	unsigned int upper, lower;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	of_property_for_each_string(device, "compatible", prop, cp) {
378c2ecf20Sopenharmony_ci		if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) == 2) {
388c2ecf20Sopenharmony_ci			*phy_id = ((upper & 0xFFFF) << 16) | (lower & 0xFFFF);
398c2ecf20Sopenharmony_ci			return 0;
408c2ecf20Sopenharmony_ci		}
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci	return -EINVAL;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic struct mii_timestamper *of_find_mii_timestamper(struct device_node *node)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct of_phandle_args arg;
488c2ecf20Sopenharmony_ci	int err;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	err = of_parse_phandle_with_fixed_args(node, "timestamper", 1, 0, &arg);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (err == -ENOENT)
538c2ecf20Sopenharmony_ci		return NULL;
548c2ecf20Sopenharmony_ci	else if (err)
558c2ecf20Sopenharmony_ci		return ERR_PTR(err);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (arg.args_count != 1)
588c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return register_mii_timestamper(arg.np, arg.args[0]);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciint of_mdiobus_phy_device_register(struct mii_bus *mdio, struct phy_device *phy,
648c2ecf20Sopenharmony_ci			      struct device_node *child, u32 addr)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	int rc;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	rc = of_irq_get(child, 0);
698c2ecf20Sopenharmony_ci	if (rc == -EPROBE_DEFER)
708c2ecf20Sopenharmony_ci		return rc;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (rc > 0) {
738c2ecf20Sopenharmony_ci		phy->irq = rc;
748c2ecf20Sopenharmony_ci		mdio->irq[addr] = rc;
758c2ecf20Sopenharmony_ci	} else {
768c2ecf20Sopenharmony_ci		phy->irq = mdio->irq[addr];
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (of_property_read_bool(child, "broken-turn-around"))
808c2ecf20Sopenharmony_ci		mdio->phy_ignore_ta_mask |= 1 << addr;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	of_property_read_u32(child, "reset-assert-us",
838c2ecf20Sopenharmony_ci			     &phy->mdio.reset_assert_delay);
848c2ecf20Sopenharmony_ci	of_property_read_u32(child, "reset-deassert-us",
858c2ecf20Sopenharmony_ci			     &phy->mdio.reset_deassert_delay);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* Associate the OF node with the device structure so it
888c2ecf20Sopenharmony_ci	 * can be looked up later */
898c2ecf20Sopenharmony_ci	of_node_get(child);
908c2ecf20Sopenharmony_ci	phy->mdio.dev.of_node = child;
918c2ecf20Sopenharmony_ci	phy->mdio.dev.fwnode = of_fwnode_handle(child);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* All data is now stored in the phy struct;
948c2ecf20Sopenharmony_ci	 * register it */
958c2ecf20Sopenharmony_ci	rc = phy_device_register(phy);
968c2ecf20Sopenharmony_ci	if (rc) {
978c2ecf20Sopenharmony_ci		of_node_put(child);
988c2ecf20Sopenharmony_ci		return rc;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	dev_dbg(&mdio->dev, "registered phy %pOFn at address %i\n",
1028c2ecf20Sopenharmony_ci		child, addr);
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_mdiobus_phy_device_register);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int of_mdiobus_register_phy(struct mii_bus *mdio,
1088c2ecf20Sopenharmony_ci				    struct device_node *child, u32 addr)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct mii_timestamper *mii_ts;
1118c2ecf20Sopenharmony_ci	struct phy_device *phy;
1128c2ecf20Sopenharmony_ci	bool is_c45;
1138c2ecf20Sopenharmony_ci	int rc;
1148c2ecf20Sopenharmony_ci	u32 phy_id;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	mii_ts = of_find_mii_timestamper(child);
1178c2ecf20Sopenharmony_ci	if (IS_ERR(mii_ts))
1188c2ecf20Sopenharmony_ci		return PTR_ERR(mii_ts);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	is_c45 = of_device_is_compatible(child,
1218c2ecf20Sopenharmony_ci					 "ethernet-phy-ieee802.3-c45");
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (!is_c45 && !of_get_phy_id(child, &phy_id))
1248c2ecf20Sopenharmony_ci		phy = phy_device_create(mdio, addr, phy_id, 0, NULL);
1258c2ecf20Sopenharmony_ci	else
1268c2ecf20Sopenharmony_ci		phy = get_phy_device(mdio, addr, is_c45);
1278c2ecf20Sopenharmony_ci	if (IS_ERR(phy)) {
1288c2ecf20Sopenharmony_ci		if (mii_ts)
1298c2ecf20Sopenharmony_ci			unregister_mii_timestamper(mii_ts);
1308c2ecf20Sopenharmony_ci		return PTR_ERR(phy);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	rc = of_mdiobus_phy_device_register(mdio, phy, child, addr);
1348c2ecf20Sopenharmony_ci	if (rc) {
1358c2ecf20Sopenharmony_ci		if (mii_ts)
1368c2ecf20Sopenharmony_ci			unregister_mii_timestamper(mii_ts);
1378c2ecf20Sopenharmony_ci		phy_device_free(phy);
1388c2ecf20Sopenharmony_ci		return rc;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* phy->mii_ts may already be defined by the PHY driver. A
1428c2ecf20Sopenharmony_ci	 * mii_timestamper probed via the device tree will still have
1438c2ecf20Sopenharmony_ci	 * precedence.
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	if (mii_ts)
1468c2ecf20Sopenharmony_ci		phy->mii_ts = mii_ts;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int of_mdiobus_register_device(struct mii_bus *mdio,
1528c2ecf20Sopenharmony_ci				      struct device_node *child, u32 addr)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct mdio_device *mdiodev;
1558c2ecf20Sopenharmony_ci	int rc;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	mdiodev = mdio_device_create(mdio, addr);
1588c2ecf20Sopenharmony_ci	if (IS_ERR(mdiodev))
1598c2ecf20Sopenharmony_ci		return PTR_ERR(mdiodev);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* Associate the OF node with the device structure so it
1628c2ecf20Sopenharmony_ci	 * can be looked up later.
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_ci	of_node_get(child);
1658c2ecf20Sopenharmony_ci	mdiodev->dev.of_node = child;
1668c2ecf20Sopenharmony_ci	mdiodev->dev.fwnode = of_fwnode_handle(child);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* All data is now stored in the mdiodev struct; register it. */
1698c2ecf20Sopenharmony_ci	rc = mdio_device_register(mdiodev);
1708c2ecf20Sopenharmony_ci	if (rc) {
1718c2ecf20Sopenharmony_ci		mdio_device_free(mdiodev);
1728c2ecf20Sopenharmony_ci		of_node_put(child);
1738c2ecf20Sopenharmony_ci		return rc;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	dev_dbg(&mdio->dev, "registered mdio device %pOFn at address %i\n",
1778c2ecf20Sopenharmony_ci		child, addr);
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* The following is a list of PHY compatible strings which appear in
1828c2ecf20Sopenharmony_ci * some DTBs. The compatible string is never matched against a PHY
1838c2ecf20Sopenharmony_ci * driver, so is pointless. We only expect devices which are not PHYs
1848c2ecf20Sopenharmony_ci * to have a compatible string, so they can be matched to an MDIO
1858c2ecf20Sopenharmony_ci * driver.  Encourage users to upgrade their DT blobs to remove these.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_cistatic const struct of_device_id whitelist_phys[] = {
1888c2ecf20Sopenharmony_ci	{ .compatible = "brcm,40nm-ephy" },
1898c2ecf20Sopenharmony_ci	{ .compatible = "broadcom,bcm5241" },
1908c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88E1111", },
1918c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88e1116", },
1928c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88e1118", },
1938c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88e1145", },
1948c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88e1149r", },
1958c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88e1310", },
1968c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88E1510", },
1978c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88E1514", },
1988c2ecf20Sopenharmony_ci	{ .compatible = "moxa,moxart-rtl8201cp", },
1998c2ecf20Sopenharmony_ci	{}
2008c2ecf20Sopenharmony_ci};
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/*
2038c2ecf20Sopenharmony_ci * Return true if the child node is for a phy. It must either:
2048c2ecf20Sopenharmony_ci * o Compatible string of "ethernet-phy-idX.X"
2058c2ecf20Sopenharmony_ci * o Compatible string of "ethernet-phy-ieee802.3-c45"
2068c2ecf20Sopenharmony_ci * o Compatible string of "ethernet-phy-ieee802.3-c22"
2078c2ecf20Sopenharmony_ci * o In the white list above (and issue a warning)
2088c2ecf20Sopenharmony_ci * o No compatibility string
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * A device which is not a phy is expected to have a compatible string
2118c2ecf20Sopenharmony_ci * indicating what sort of device it is.
2128c2ecf20Sopenharmony_ci */
2138c2ecf20Sopenharmony_cibool of_mdiobus_child_is_phy(struct device_node *child)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	u32 phy_id;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (of_get_phy_id(child, &phy_id) != -EINVAL)
2188c2ecf20Sopenharmony_ci		return true;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c45"))
2218c2ecf20Sopenharmony_ci		return true;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c22"))
2248c2ecf20Sopenharmony_ci		return true;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (of_match_node(whitelist_phys, child)) {
2278c2ecf20Sopenharmony_ci		pr_warn(FW_WARN
2288c2ecf20Sopenharmony_ci			"%pOF: Whitelisted compatible string. Please remove\n",
2298c2ecf20Sopenharmony_ci			child);
2308c2ecf20Sopenharmony_ci		return true;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (!of_find_property(child, "compatible", NULL))
2348c2ecf20Sopenharmony_ci		return true;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return false;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_mdiobus_child_is_phy);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/**
2418c2ecf20Sopenharmony_ci * __of_mdiobus_register - Register mii_bus and create PHYs from the device tree
2428c2ecf20Sopenharmony_ci * @mdio: pointer to mii_bus structure
2438c2ecf20Sopenharmony_ci * @np: pointer to device_node of MDIO bus.
2448c2ecf20Sopenharmony_ci * @owner: module owning the @mdio object.
2458c2ecf20Sopenharmony_ci *
2468c2ecf20Sopenharmony_ci * This function registers the mii_bus structure and registers a phy_device
2478c2ecf20Sopenharmony_ci * for each child node of @np.
2488c2ecf20Sopenharmony_ci */
2498c2ecf20Sopenharmony_ciint __of_mdiobus_register(struct mii_bus *mdio, struct device_node *np,
2508c2ecf20Sopenharmony_ci			  struct module *owner)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct device_node *child;
2538c2ecf20Sopenharmony_ci	bool scanphys = false;
2548c2ecf20Sopenharmony_ci	int addr, rc;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (!np)
2578c2ecf20Sopenharmony_ci		return __mdiobus_register(mdio, owner);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* Do not continue if the node is disabled */
2608c2ecf20Sopenharmony_ci	if (!of_device_is_available(np))
2618c2ecf20Sopenharmony_ci		return -ENODEV;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* Mask out all PHYs from auto probing.  Instead the PHYs listed in
2648c2ecf20Sopenharmony_ci	 * the device tree are populated after the bus has been registered */
2658c2ecf20Sopenharmony_ci	mdio->phy_mask = ~0;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	mdio->dev.of_node = np;
2688c2ecf20Sopenharmony_ci	mdio->dev.fwnode = of_fwnode_handle(np);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* Get bus level PHY reset GPIO details */
2718c2ecf20Sopenharmony_ci	mdio->reset_delay_us = DEFAULT_GPIO_RESET_DELAY;
2728c2ecf20Sopenharmony_ci	of_property_read_u32(np, "reset-delay-us", &mdio->reset_delay_us);
2738c2ecf20Sopenharmony_ci	mdio->reset_post_delay_us = 0;
2748c2ecf20Sopenharmony_ci	of_property_read_u32(np, "reset-post-delay-us", &mdio->reset_post_delay_us);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* Register the MDIO bus */
2778c2ecf20Sopenharmony_ci	rc = __mdiobus_register(mdio, owner);
2788c2ecf20Sopenharmony_ci	if (rc)
2798c2ecf20Sopenharmony_ci		return rc;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* Loop over the child nodes and register a phy_device for each phy */
2828c2ecf20Sopenharmony_ci	for_each_available_child_of_node(np, child) {
2838c2ecf20Sopenharmony_ci		addr = of_mdio_parse_addr(&mdio->dev, child);
2848c2ecf20Sopenharmony_ci		if (addr < 0) {
2858c2ecf20Sopenharmony_ci			scanphys = true;
2868c2ecf20Sopenharmony_ci			continue;
2878c2ecf20Sopenharmony_ci		}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		if (of_mdiobus_child_is_phy(child))
2908c2ecf20Sopenharmony_ci			rc = of_mdiobus_register_phy(mdio, child, addr);
2918c2ecf20Sopenharmony_ci		else
2928c2ecf20Sopenharmony_ci			rc = of_mdiobus_register_device(mdio, child, addr);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		if (rc == -ENODEV)
2958c2ecf20Sopenharmony_ci			dev_err(&mdio->dev,
2968c2ecf20Sopenharmony_ci				"MDIO device at address %d is missing.\n",
2978c2ecf20Sopenharmony_ci				addr);
2988c2ecf20Sopenharmony_ci		else if (rc)
2998c2ecf20Sopenharmony_ci			goto unregister;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (!scanphys)
3038c2ecf20Sopenharmony_ci		return 0;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* auto scan for PHYs with empty reg property */
3068c2ecf20Sopenharmony_ci	for_each_available_child_of_node(np, child) {
3078c2ecf20Sopenharmony_ci		/* Skip PHYs with reg property set */
3088c2ecf20Sopenharmony_ci		if (of_find_property(child, "reg", NULL))
3098c2ecf20Sopenharmony_ci			continue;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
3128c2ecf20Sopenharmony_ci			/* skip already registered PHYs */
3138c2ecf20Sopenharmony_ci			if (mdiobus_is_registered_device(mdio, addr))
3148c2ecf20Sopenharmony_ci				continue;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci			/* be noisy to encourage people to set reg property */
3178c2ecf20Sopenharmony_ci			dev_info(&mdio->dev, "scan phy %pOFn at address %i\n",
3188c2ecf20Sopenharmony_ci				 child, addr);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci			if (of_mdiobus_child_is_phy(child)) {
3218c2ecf20Sopenharmony_ci				/* -ENODEV is the return code that PHYLIB has
3228c2ecf20Sopenharmony_ci				 * standardized on to indicate that bus
3238c2ecf20Sopenharmony_ci				 * scanning should continue.
3248c2ecf20Sopenharmony_ci				 */
3258c2ecf20Sopenharmony_ci				rc = of_mdiobus_register_phy(mdio, child, addr);
3268c2ecf20Sopenharmony_ci				if (!rc)
3278c2ecf20Sopenharmony_ci					break;
3288c2ecf20Sopenharmony_ci				if (rc != -ENODEV)
3298c2ecf20Sopenharmony_ci					goto unregister;
3308c2ecf20Sopenharmony_ci			}
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ciunregister:
3378c2ecf20Sopenharmony_ci	of_node_put(child);
3388c2ecf20Sopenharmony_ci	mdiobus_unregister(mdio);
3398c2ecf20Sopenharmony_ci	return rc;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__of_mdiobus_register);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci/**
3448c2ecf20Sopenharmony_ci * of_mdio_find_device - Given a device tree node, find the mdio_device
3458c2ecf20Sopenharmony_ci * @np: pointer to the mdio_device's device tree node
3468c2ecf20Sopenharmony_ci *
3478c2ecf20Sopenharmony_ci * If successful, returns a pointer to the mdio_device with the embedded
3488c2ecf20Sopenharmony_ci * struct device refcount incremented by one, or NULL on failure.
3498c2ecf20Sopenharmony_ci * The caller should call put_device() on the mdio_device after its use
3508c2ecf20Sopenharmony_ci */
3518c2ecf20Sopenharmony_cistruct mdio_device *of_mdio_find_device(struct device_node *np)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct device *d;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (!np)
3568c2ecf20Sopenharmony_ci		return NULL;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	d = bus_find_device_by_of_node(&mdio_bus_type, np);
3598c2ecf20Sopenharmony_ci	if (!d)
3608c2ecf20Sopenharmony_ci		return NULL;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return to_mdio_device(d);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_mdio_find_device);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci/**
3678c2ecf20Sopenharmony_ci * of_phy_find_device - Give a PHY node, find the phy_device
3688c2ecf20Sopenharmony_ci * @phy_np: Pointer to the phy's device tree node
3698c2ecf20Sopenharmony_ci *
3708c2ecf20Sopenharmony_ci * If successful, returns a pointer to the phy_device with the embedded
3718c2ecf20Sopenharmony_ci * struct device refcount incremented by one, or NULL on failure.
3728c2ecf20Sopenharmony_ci */
3738c2ecf20Sopenharmony_cistruct phy_device *of_phy_find_device(struct device_node *phy_np)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct mdio_device *mdiodev;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	mdiodev = of_mdio_find_device(phy_np);
3788c2ecf20Sopenharmony_ci	if (!mdiodev)
3798c2ecf20Sopenharmony_ci		return NULL;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY)
3828c2ecf20Sopenharmony_ci		return to_phy_device(&mdiodev->dev);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	put_device(&mdiodev->dev);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return NULL;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_phy_find_device);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci/**
3918c2ecf20Sopenharmony_ci * of_phy_connect - Connect to the phy described in the device tree
3928c2ecf20Sopenharmony_ci * @dev: pointer to net_device claiming the phy
3938c2ecf20Sopenharmony_ci * @phy_np: Pointer to device tree node for the PHY
3948c2ecf20Sopenharmony_ci * @hndlr: Link state callback for the network device
3958c2ecf20Sopenharmony_ci * @flags: flags to pass to the PHY
3968c2ecf20Sopenharmony_ci * @iface: PHY data interface type
3978c2ecf20Sopenharmony_ci *
3988c2ecf20Sopenharmony_ci * If successful, returns a pointer to the phy_device with the embedded
3998c2ecf20Sopenharmony_ci * struct device refcount incremented by one, or NULL on failure. The
4008c2ecf20Sopenharmony_ci * refcount must be dropped by calling phy_disconnect() or phy_detach().
4018c2ecf20Sopenharmony_ci */
4028c2ecf20Sopenharmony_cistruct phy_device *of_phy_connect(struct net_device *dev,
4038c2ecf20Sopenharmony_ci				  struct device_node *phy_np,
4048c2ecf20Sopenharmony_ci				  void (*hndlr)(struct net_device *), u32 flags,
4058c2ecf20Sopenharmony_ci				  phy_interface_t iface)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct phy_device *phy = of_phy_find_device(phy_np);
4088c2ecf20Sopenharmony_ci	int ret;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (!phy)
4118c2ecf20Sopenharmony_ci		return NULL;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	phy->dev_flags |= flags;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	ret = phy_connect_direct(dev, phy, hndlr, iface);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* refcount is held by phy_connect_direct() on success */
4188c2ecf20Sopenharmony_ci	put_device(&phy->mdio.dev);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return ret ? NULL : phy;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_phy_connect);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci/**
4258c2ecf20Sopenharmony_ci * of_phy_get_and_connect
4268c2ecf20Sopenharmony_ci * - Get phy node and connect to the phy described in the device tree
4278c2ecf20Sopenharmony_ci * @dev: pointer to net_device claiming the phy
4288c2ecf20Sopenharmony_ci * @np: Pointer to device tree node for the net_device claiming the phy
4298c2ecf20Sopenharmony_ci * @hndlr: Link state callback for the network device
4308c2ecf20Sopenharmony_ci *
4318c2ecf20Sopenharmony_ci * If successful, returns a pointer to the phy_device with the embedded
4328c2ecf20Sopenharmony_ci * struct device refcount incremented by one, or NULL on failure. The
4338c2ecf20Sopenharmony_ci * refcount must be dropped by calling phy_disconnect() or phy_detach().
4348c2ecf20Sopenharmony_ci */
4358c2ecf20Sopenharmony_cistruct phy_device *of_phy_get_and_connect(struct net_device *dev,
4368c2ecf20Sopenharmony_ci					  struct device_node *np,
4378c2ecf20Sopenharmony_ci					  void (*hndlr)(struct net_device *))
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	phy_interface_t iface;
4408c2ecf20Sopenharmony_ci	struct device_node *phy_np;
4418c2ecf20Sopenharmony_ci	struct phy_device *phy;
4428c2ecf20Sopenharmony_ci	int ret;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	ret = of_get_phy_mode(np, &iface);
4458c2ecf20Sopenharmony_ci	if (ret)
4468c2ecf20Sopenharmony_ci		return NULL;
4478c2ecf20Sopenharmony_ci	if (of_phy_is_fixed_link(np)) {
4488c2ecf20Sopenharmony_ci		ret = of_phy_register_fixed_link(np);
4498c2ecf20Sopenharmony_ci		if (ret < 0) {
4508c2ecf20Sopenharmony_ci			netdev_err(dev, "broken fixed-link specification\n");
4518c2ecf20Sopenharmony_ci			return NULL;
4528c2ecf20Sopenharmony_ci		}
4538c2ecf20Sopenharmony_ci		phy_np = of_node_get(np);
4548c2ecf20Sopenharmony_ci	} else {
4558c2ecf20Sopenharmony_ci		phy_np = of_parse_phandle(np, "phy-handle", 0);
4568c2ecf20Sopenharmony_ci		if (!phy_np)
4578c2ecf20Sopenharmony_ci			return NULL;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	phy = of_phy_connect(dev, phy_np, hndlr, 0, iface);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	of_node_put(phy_np);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	return phy;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_phy_get_and_connect);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci/**
4698c2ecf20Sopenharmony_ci * of_phy_attach - Attach to a PHY without starting the state machine
4708c2ecf20Sopenharmony_ci * @dev: pointer to net_device claiming the phy
4718c2ecf20Sopenharmony_ci * @phy_np: Node pointer for the PHY
4728c2ecf20Sopenharmony_ci * @flags: flags to pass to the PHY
4738c2ecf20Sopenharmony_ci * @iface: PHY data interface type
4748c2ecf20Sopenharmony_ci *
4758c2ecf20Sopenharmony_ci * If successful, returns a pointer to the phy_device with the embedded
4768c2ecf20Sopenharmony_ci * struct device refcount incremented by one, or NULL on failure. The
4778c2ecf20Sopenharmony_ci * refcount must be dropped by calling phy_disconnect() or phy_detach().
4788c2ecf20Sopenharmony_ci */
4798c2ecf20Sopenharmony_cistruct phy_device *of_phy_attach(struct net_device *dev,
4808c2ecf20Sopenharmony_ci				 struct device_node *phy_np, u32 flags,
4818c2ecf20Sopenharmony_ci				 phy_interface_t iface)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct phy_device *phy = of_phy_find_device(phy_np);
4848c2ecf20Sopenharmony_ci	int ret;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (!phy)
4878c2ecf20Sopenharmony_ci		return NULL;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	ret = phy_attach_direct(dev, phy, flags, iface);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* refcount is held by phy_attach_direct() on success */
4928c2ecf20Sopenharmony_ci	put_device(&phy->mdio.dev);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	return ret ? NULL : phy;
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_phy_attach);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci/*
4998c2ecf20Sopenharmony_ci * of_phy_is_fixed_link() and of_phy_register_fixed_link() must
5008c2ecf20Sopenharmony_ci * support two DT bindings:
5018c2ecf20Sopenharmony_ci * - the old DT binding, where 'fixed-link' was a property with 5
5028c2ecf20Sopenharmony_ci *   cells encoding various informations about the fixed PHY
5038c2ecf20Sopenharmony_ci * - the new DT binding, where 'fixed-link' is a sub-node of the
5048c2ecf20Sopenharmony_ci *   Ethernet device.
5058c2ecf20Sopenharmony_ci */
5068c2ecf20Sopenharmony_cibool of_phy_is_fixed_link(struct device_node *np)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	struct device_node *dn;
5098c2ecf20Sopenharmony_ci	int len, err;
5108c2ecf20Sopenharmony_ci	const char *managed;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	/* New binding */
5138c2ecf20Sopenharmony_ci	dn = of_get_child_by_name(np, "fixed-link");
5148c2ecf20Sopenharmony_ci	if (dn) {
5158c2ecf20Sopenharmony_ci		of_node_put(dn);
5168c2ecf20Sopenharmony_ci		return true;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	err = of_property_read_string(np, "managed", &managed);
5208c2ecf20Sopenharmony_ci	if (err == 0 && strcmp(managed, "auto") != 0)
5218c2ecf20Sopenharmony_ci		return true;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* Old binding */
5248c2ecf20Sopenharmony_ci	if (of_get_property(np, "fixed-link", &len) &&
5258c2ecf20Sopenharmony_ci	    len == (5 * sizeof(__be32)))
5268c2ecf20Sopenharmony_ci		return true;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	return false;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_phy_is_fixed_link);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ciint of_phy_register_fixed_link(struct device_node *np)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct fixed_phy_status status = {};
5358c2ecf20Sopenharmony_ci	struct device_node *fixed_link_node;
5368c2ecf20Sopenharmony_ci	u32 fixed_link_prop[5];
5378c2ecf20Sopenharmony_ci	const char *managed;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (of_property_read_string(np, "managed", &managed) == 0 &&
5408c2ecf20Sopenharmony_ci	    strcmp(managed, "in-band-status") == 0) {
5418c2ecf20Sopenharmony_ci		/* status is zeroed, namely its .link member */
5428c2ecf20Sopenharmony_ci		goto register_phy;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* New binding */
5468c2ecf20Sopenharmony_ci	fixed_link_node = of_get_child_by_name(np, "fixed-link");
5478c2ecf20Sopenharmony_ci	if (fixed_link_node) {
5488c2ecf20Sopenharmony_ci		status.link = 1;
5498c2ecf20Sopenharmony_ci		status.duplex = of_property_read_bool(fixed_link_node,
5508c2ecf20Sopenharmony_ci						      "full-duplex");
5518c2ecf20Sopenharmony_ci		if (of_property_read_u32(fixed_link_node, "speed",
5528c2ecf20Sopenharmony_ci					 &status.speed)) {
5538c2ecf20Sopenharmony_ci			of_node_put(fixed_link_node);
5548c2ecf20Sopenharmony_ci			return -EINVAL;
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci		status.pause = of_property_read_bool(fixed_link_node, "pause");
5578c2ecf20Sopenharmony_ci		status.asym_pause = of_property_read_bool(fixed_link_node,
5588c2ecf20Sopenharmony_ci							  "asym-pause");
5598c2ecf20Sopenharmony_ci		of_node_put(fixed_link_node);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		goto register_phy;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/* Old binding */
5658c2ecf20Sopenharmony_ci	if (of_property_read_u32_array(np, "fixed-link", fixed_link_prop,
5668c2ecf20Sopenharmony_ci				       ARRAY_SIZE(fixed_link_prop)) == 0) {
5678c2ecf20Sopenharmony_ci		status.link = 1;
5688c2ecf20Sopenharmony_ci		status.duplex = fixed_link_prop[1];
5698c2ecf20Sopenharmony_ci		status.speed  = fixed_link_prop[2];
5708c2ecf20Sopenharmony_ci		status.pause  = fixed_link_prop[3];
5718c2ecf20Sopenharmony_ci		status.asym_pause = fixed_link_prop[4];
5728c2ecf20Sopenharmony_ci		goto register_phy;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return -ENODEV;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ciregister_phy:
5788c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(fixed_phy_register(PHY_POLL, &status, np));
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_phy_register_fixed_link);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_civoid of_phy_deregister_fixed_link(struct device_node *np)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct phy_device *phydev;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	phydev = of_phy_find_device(np);
5878c2ecf20Sopenharmony_ci	if (!phydev)
5888c2ecf20Sopenharmony_ci		return;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	fixed_phy_unregister(phydev);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	put_device(&phydev->mdio.dev);	/* of_phy_find_device() */
5938c2ecf20Sopenharmony_ci	phy_device_free(phydev);	/* fixed_phy_register() */
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_phy_deregister_fixed_link);
596