1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * fwnode helpers for the MDIO (Ethernet PHY) API 4 * 5 * This file provides helper functions for extracting PHY device information 6 * out of the fwnode and using it to populate an mii_bus. 7 */ 8 9 #include <linux/acpi.h> 10 #include <linux/acpi_mdio.h> 11 #include <linux/fwnode_mdio.h> 12 #include <linux/of.h> 13 #include <linux/of_mdio.h> 14 #include <linux/phy.h> 15 16 MODULE_AUTHOR("Calvin Johnson <calvin.johnson@oss.nxp.com>"); 17 MODULE_LICENSE("GPL"); 18 19 static struct mii_timestamper * 20 fwnode_find_mii_timestamper(struct fwnode_handle *fwnode) 21 { 22 struct of_phandle_args arg; 23 int err; 24 25 if (is_acpi_node(fwnode)) 26 return NULL; 27 28 err = of_parse_phandle_with_fixed_args(to_of_node(fwnode), 29 "timestamper", 1, 0, &arg); 30 if (err == -ENOENT) 31 return NULL; 32 else if (err) 33 return ERR_PTR(err); 34 35 if (arg.args_count != 1) 36 return ERR_PTR(-EINVAL); 37 38 return register_mii_timestamper(arg.np, arg.args[0]); 39 } 40 41 int fwnode_mdiobus_phy_device_register(struct mii_bus *mdio, 42 struct phy_device *phy, 43 struct fwnode_handle *child, u32 addr) 44 { 45 int rc; 46 47 rc = fwnode_irq_get(child, 0); 48 if (rc == -EPROBE_DEFER) 49 return rc; 50 51 if (rc > 0) { 52 phy->irq = rc; 53 mdio->irq[addr] = rc; 54 } else { 55 phy->irq = mdio->irq[addr]; 56 } 57 58 if (fwnode_property_read_bool(child, "broken-turn-around")) 59 mdio->phy_ignore_ta_mask |= 1 << addr; 60 61 fwnode_property_read_u32(child, "reset-assert-us", 62 &phy->mdio.reset_assert_delay); 63 fwnode_property_read_u32(child, "reset-deassert-us", 64 &phy->mdio.reset_deassert_delay); 65 66 /* Associate the fwnode with the device structure so it 67 * can be looked up later 68 */ 69 fwnode_handle_get(child); 70 device_set_node(&phy->mdio.dev, child); 71 72 /* All data is now stored in the phy struct; 73 * register it 74 */ 75 rc = phy_device_register(phy); 76 if (rc) { 77 fwnode_handle_put(child); 78 return rc; 79 } 80 81 dev_dbg(&mdio->dev, "registered phy %p fwnode at address %i\n", 82 child, addr); 83 return 0; 84 } 85 EXPORT_SYMBOL(fwnode_mdiobus_phy_device_register); 86 87 int fwnode_mdiobus_register_phy(struct mii_bus *bus, 88 struct fwnode_handle *child, u32 addr) 89 { 90 struct mii_timestamper *mii_ts = NULL; 91 struct phy_device *phy; 92 bool is_c45 = false; 93 u32 phy_id; 94 int rc; 95 96 mii_ts = fwnode_find_mii_timestamper(child); 97 if (IS_ERR(mii_ts)) 98 return PTR_ERR(mii_ts); 99 100 rc = fwnode_property_match_string(child, "compatible", 101 "ethernet-phy-ieee802.3-c45"); 102 if (rc >= 0) 103 is_c45 = true; 104 105 if (is_c45 || fwnode_get_phy_id(child, &phy_id)) 106 phy = get_phy_device(bus, addr, is_c45); 107 else 108 phy = phy_device_create(bus, addr, phy_id, 0, NULL); 109 if (IS_ERR(phy)) { 110 unregister_mii_timestamper(mii_ts); 111 return PTR_ERR(phy); 112 } 113 114 if (is_acpi_node(child)) { 115 phy->irq = bus->irq[addr]; 116 117 /* Associate the fwnode with the device structure so it 118 * can be looked up later. 119 */ 120 phy->mdio.dev.fwnode = child; 121 122 /* All data is now stored in the phy struct, so register it */ 123 rc = phy_device_register(phy); 124 if (rc) { 125 phy_device_free(phy); 126 fwnode_handle_put(phy->mdio.dev.fwnode); 127 return rc; 128 } 129 } else if (is_of_node(child)) { 130 rc = fwnode_mdiobus_phy_device_register(bus, phy, child, addr); 131 if (rc) { 132 unregister_mii_timestamper(mii_ts); 133 phy_device_free(phy); 134 return rc; 135 } 136 } 137 138 /* phy->mii_ts may already be defined by the PHY driver. A 139 * mii_timestamper probed via the device tree will still have 140 * precedence. 141 */ 142 if (mii_ts) 143 phy->mii_ts = mii_ts; 144 return 0; 145 } 146 EXPORT_SYMBOL(fwnode_mdiobus_register_phy); 147 148 /** 149 * fwnode_mdiobus_register - bring up all the PHYs on a given MDIO bus and 150 * attach them to it. 151 * @bus: Target MDIO bus. 152 * @fwnode: Pointer to fwnode of the MDIO controller. 153 * 154 * Return values are determined accordingly to acpi_/of_ mdiobus_register() 155 * operation. 156 */ 157 int fwnode_mdiobus_register(struct mii_bus *bus, struct fwnode_handle *fwnode) 158 { 159 if (is_acpi_node(fwnode)) 160 return acpi_mdiobus_register(bus, fwnode); 161 else if (is_of_node(fwnode)) 162 return of_mdiobus_register(bus, to_of_node(fwnode)); 163 else 164 return -EINVAL; 165 } 166 EXPORT_SYMBOL(fwnode_mdiobus_register); 167