// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2020 NXP */ #define LOG_CATEGORY UCLASS_ETH_PHY #include #include #include #include #include #include #include #include #include #include struct eth_phy_device_priv { struct mii_dev *mdio_bus; struct gpio_desc reset_gpio; u32 reset_assert_delay; u32 reset_deassert_delay; }; int eth_phy_binds_nodes(struct udevice *eth_dev) { ofnode mdio_node, phy_node; const char *node_name; int ret; /* search a subnode named "mdio.*" */ dev_for_each_subnode(mdio_node, eth_dev) { node_name = ofnode_get_name(mdio_node); if (!strncmp(node_name, "mdio", 4)) break; } if (!ofnode_valid(mdio_node)) { dev_dbg(eth_dev, "%s: %s mdio subnode not found!\n", __func__, eth_dev->name); return -ENXIO; } dev_dbg(eth_dev, "%s: %s subnode found!\n", __func__, node_name); ofnode_for_each_subnode(phy_node, mdio_node) { node_name = ofnode_get_name(phy_node); dev_dbg(eth_dev, "* Found child node: '%s'\n", node_name); ret = device_bind_driver_to_node(eth_dev, "eth_phy_generic_drv", node_name, phy_node, NULL); if (ret) { dev_dbg(eth_dev, " - Eth phy binding error: %d\n", ret); continue; } dev_dbg(eth_dev, " - bound phy device: '%s'\n", node_name); } return 0; } int eth_phy_set_mdio_bus(struct udevice *eth_dev, struct mii_dev *mdio_bus) { struct udevice *dev; struct eth_phy_device_priv *uc_priv; for (uclass_first_device(UCLASS_ETH_PHY, &dev); dev; uclass_next_device(&dev)) { if (dev->parent == eth_dev) { uc_priv = (struct eth_phy_device_priv *)(dev_get_uclass_priv(dev)); if (!uc_priv->mdio_bus) uc_priv->mdio_bus = mdio_bus; } } return 0; } struct mii_dev *eth_phy_get_mdio_bus(struct udevice *eth_dev) { int ret; struct udevice *phy_dev; struct eth_phy_device_priv *uc_priv; /* Will probe the parent of phy device, then phy device */ ret = uclass_get_device_by_phandle(UCLASS_ETH_PHY, eth_dev, "phy-handle", &phy_dev); if (!ret) { if (eth_dev != phy_dev->parent) { /* * phy_dev is shared and controlled by * other eth controller */ uc_priv = (struct eth_phy_device_priv *)(dev_get_uclass_priv(phy_dev)); if (uc_priv->mdio_bus) log_notice("Get shared mii bus on %s\n", eth_dev->name); else log_notice("Can't get shared mii bus on %s\n", eth_dev->name); return uc_priv->mdio_bus; } } else { log_debug("Can't find phy-handle for %s\n", eth_dev->name); } return NULL; } int eth_phy_get_addr(struct udevice *dev) { struct ofnode_phandle_args phandle_args; int reg; if (dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0, &phandle_args)) { dev_dbg(dev, "Failed to find phy-handle"); return -ENODEV; } reg = ofnode_read_u32_default(phandle_args.node, "reg", 0); return reg; } /* parsing generic properties of devicetree/bindings/net/ethernet-phy.yaml */ static int eth_phy_of_to_plat(struct udevice *dev) { struct eth_phy_device_priv *uc_priv = dev_get_uclass_priv(dev); int ret; if (!CONFIG_IS_ENABLED(DM_GPIO)) return 0; /* search "reset-gpios" in phy node */ ret = gpio_request_by_name(dev, "reset-gpios", 0, &uc_priv->reset_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); if (ret && ret != -ENOENT) return ret; uc_priv->reset_assert_delay = dev_read_u32_default(dev, "reset-assert-us", 0); uc_priv->reset_deassert_delay = dev_read_u32_default(dev, "reset-deassert-us", 0); /* These are used by some DTs, try these as a fallback. */ if (!uc_priv->reset_assert_delay && !uc_priv->reset_deassert_delay) { uc_priv->reset_assert_delay = dev_read_u32_default(dev, "reset-delay-us", 0); uc_priv->reset_deassert_delay = dev_read_u32_default(dev, "reset-post-delay-us", 0); } return 0; } static void eth_phy_reset(struct udevice *dev, int value) { struct eth_phy_device_priv *uc_priv = dev_get_uclass_priv(dev); u32 delay; if (!CONFIG_IS_ENABLED(DM_GPIO)) return; if (!dm_gpio_is_valid(&uc_priv->reset_gpio)) return; dm_gpio_set_value(&uc_priv->reset_gpio, value); delay = value ? uc_priv->reset_assert_delay : uc_priv->reset_deassert_delay; if (delay) udelay(delay); } static int eth_phy_pre_probe(struct udevice *dev) { /* Assert and deassert the reset signal */ eth_phy_reset(dev, 1); eth_phy_reset(dev, 0); return 0; } UCLASS_DRIVER(eth_phy_generic) = { .id = UCLASS_ETH_PHY, .name = "eth_phy_generic", .per_device_auto = sizeof(struct eth_phy_device_priv), .pre_probe = eth_phy_pre_probe, }; U_BOOT_DRIVER(eth_phy_generic_drv) = { .name = "eth_phy_generic_drv", .id = UCLASS_ETH_PHY, .of_to_plat = eth_phy_of_to_plat, };