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/dev_printk.h> 11 #include <linux/fwnode_mdio.h> 12 #include <linux/of.h> 13 #include <linux/phy.h> 14 #include <linux/pse-pd/pse.h> 15 16 MODULE_AUTHOR("Calvin Johnson <calvin.johnson@oss.nxp.com>"); 17 MODULE_LICENSE("GPL"); 18 MODULE_DESCRIPTION("FWNODE MDIO bus (Ethernet PHY) accessors"); 19 20 static struct pse_control * 21 fwnode_find_pse_control(struct fwnode_handle *fwnode) 22 { 23 struct pse_control *psec; 24 struct device_node *np; 25 26 if (!IS_ENABLED(CONFIG_PSE_CONTROLLER)) 27 return NULL; 28 29 np = to_of_node(fwnode); 30 if (!np) 31 return NULL; 32 33 psec = of_pse_control_get(np); 34 if (PTR_ERR(psec) == -ENOENT) 35 return NULL; 36 37 return psec; 38 } 39 40 static struct mii_timestamper * 41 fwnode_find_mii_timestamper(struct fwnode_handle *fwnode) 42 { 43 struct mii_timestamper *mii_ts; 44 struct of_phandle_args arg; 45 int err; 46 47 if (is_acpi_node(fwnode)) 48 return NULL; 49 50 err = of_parse_phandle_with_fixed_args(to_of_node(fwnode), 51 "timestamper", 1, 0, &arg); 52 if (err == -ENOENT) 53 return NULL; 54 else if (err) 55 return ERR_PTR(err); 56 57 if (arg.args_count != 1) { 58 mii_ts = ERR_PTR(-EINVAL); 59 goto put_node; 60 } 61 62 mii_ts = register_mii_timestamper(arg.np, arg.args[0]); 63 64 put_node: 65 of_node_put(arg.np); 66 return mii_ts; 67 } 68 69 int fwnode_mdiobus_phy_device_register(struct mii_bus *mdio, 70 struct phy_device *phy, 71 struct fwnode_handle *child, u32 addr) 72 { 73 int rc; 74 75 rc = fwnode_irq_get(child, 0); 76 /* Don't wait forever if the IRQ provider doesn't become available, 77 * just fall back to poll mode 78 */ 79 if (rc == -EPROBE_DEFER) 80 rc = driver_deferred_probe_check_state(&phy->mdio.dev); 81 if (rc == -EPROBE_DEFER) 82 return rc; 83 84 if (rc > 0) { 85 phy->irq = rc; 86 mdio->irq[addr] = rc; 87 } else { 88 phy->irq = mdio->irq[addr]; 89 } 90 91 if (fwnode_property_read_bool(child, "broken-turn-around")) 92 mdio->phy_ignore_ta_mask |= 1 << addr; 93 94 fwnode_property_read_u32(child, "reset-assert-us", 95 &phy->mdio.reset_assert_delay); 96 fwnode_property_read_u32(child, "reset-deassert-us", 97 &phy->mdio.reset_deassert_delay); 98 99 /* Associate the fwnode with the device structure so it 100 * can be looked up later 101 */ 102 fwnode_handle_get(child); 103 device_set_node(&phy->mdio.dev, child); 104 105 /* All data is now stored in the phy struct; 106 * register it 107 */ 108 rc = phy_device_register(phy); 109 if (rc) { 110 device_set_node(&phy->mdio.dev, NULL); 111 fwnode_handle_put(child); 112 return rc; 113 } 114 115 dev_dbg(&mdio->dev, "registered phy fwnode %pfw at address %i\n", 116 child, addr); 117 return 0; 118 } 119 EXPORT_SYMBOL(fwnode_mdiobus_phy_device_register); 120 121 int fwnode_mdiobus_register_phy(struct mii_bus *bus, 122 struct fwnode_handle *child, u32 addr) 123 { 124 struct mii_timestamper *mii_ts = NULL; 125 struct pse_control *psec = NULL; 126 struct phy_device *phy; 127 bool is_c45; 128 u32 phy_id; 129 int rc; 130 131 psec = fwnode_find_pse_control(child); 132 if (IS_ERR(psec)) 133 return PTR_ERR(psec); 134 135 mii_ts = fwnode_find_mii_timestamper(child); 136 if (IS_ERR(mii_ts)) { 137 rc = PTR_ERR(mii_ts); 138 goto clean_pse; 139 } 140 141 is_c45 = fwnode_device_is_compatible(child, "ethernet-phy-ieee802.3-c45"); 142 if (is_c45 || fwnode_get_phy_id(child, &phy_id)) 143 phy = get_phy_device(bus, addr, is_c45); 144 else 145 phy = phy_device_create(bus, addr, phy_id, 0, NULL); 146 if (IS_ERR(phy)) { 147 rc = PTR_ERR(phy); 148 goto clean_mii_ts; 149 } 150 151 if (is_acpi_node(child)) { 152 phy->irq = bus->irq[addr]; 153 154 /* Associate the fwnode with the device structure so it 155 * can be looked up later. 156 */ 157 phy->mdio.dev.fwnode = fwnode_handle_get(child); 158 159 /* All data is now stored in the phy struct, so register it */ 160 rc = phy_device_register(phy); 161 if (rc) { 162 phy->mdio.dev.fwnode = NULL; 163 fwnode_handle_put(child); 164 goto clean_phy; 165 } 166 } else if (is_of_node(child)) { 167 rc = fwnode_mdiobus_phy_device_register(bus, phy, child, addr); 168 if (rc) 169 goto clean_phy; 170 } 171 172 phy->psec = psec; 173 174 /* phy->mii_ts may already be defined by the PHY driver. A 175 * mii_timestamper probed via the device tree will still have 176 * precedence. 177 */ 178 if (mii_ts) 179 phy->mii_ts = mii_ts; 180 181 return 0; 182 183 clean_phy: 184 phy_device_free(phy); 185 clean_mii_ts: 186 unregister_mii_timestamper(mii_ts); 187 clean_pse: 188 pse_control_put(psec); 189 190 return rc; 191 } 192 EXPORT_SYMBOL(fwnode_mdiobus_register_phy); 193