1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * A wrapper for multiple PHYs which passes all phy_* function calls to 4 * multiple (actual) PHY devices. This is comes handy when initializing 5 * all PHYs on a HCD and to keep them all in the same state. 6 * 7 * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com> 8 */ 9 10 #include <linux/device.h> 11 #include <linux/list.h> 12 #include <linux/phy/phy.h> 13 #include <linux/of.h> 14 15 #include "phy.h" 16 17 struct usb_phy_roothub { 18 struct phy *phy; 19 struct list_head list; 20 }; 21 22 /* Allocate the roothub_entry by specific name of phy */ 23 static int usb_phy_roothub_add_phy_by_name(struct device *dev, const char *name, 24 struct list_head *list) 25 { 26 struct usb_phy_roothub *roothub_entry; 27 struct phy *phy; 28 29 phy = devm_of_phy_get(dev, dev->of_node, name); 30 if (IS_ERR(phy)) 31 return PTR_ERR(phy); 32 33 roothub_entry = devm_kzalloc(dev, sizeof(*roothub_entry), GFP_KERNEL); 34 if (!roothub_entry) 35 return -ENOMEM; 36 37 INIT_LIST_HEAD(&roothub_entry->list); 38 39 roothub_entry->phy = phy; 40 41 list_add_tail(&roothub_entry->list, list); 42 43 return 0; 44 } 45 46 static int usb_phy_roothub_add_phy(struct device *dev, int index, 47 struct list_head *list) 48 { 49 struct usb_phy_roothub *roothub_entry; 50 struct phy *phy; 51 52 phy = devm_of_phy_get_by_index(dev, dev->of_node, index); 53 if (IS_ERR(phy)) { 54 if (PTR_ERR(phy) == -ENODEV) 55 return 0; 56 else 57 return PTR_ERR(phy); 58 } 59 60 roothub_entry = devm_kzalloc(dev, sizeof(*roothub_entry), GFP_KERNEL); 61 if (!roothub_entry) 62 return -ENOMEM; 63 64 INIT_LIST_HEAD(&roothub_entry->list); 65 66 roothub_entry->phy = phy; 67 68 list_add_tail(&roothub_entry->list, list); 69 70 return 0; 71 } 72 73 struct usb_phy_roothub *usb_phy_roothub_alloc(struct device *dev) 74 { 75 struct usb_phy_roothub *phy_roothub; 76 int i, num_phys, err; 77 78 if (!IS_ENABLED(CONFIG_GENERIC_PHY)) 79 return NULL; 80 81 num_phys = of_count_phandle_with_args(dev->of_node, "phys", 82 "#phy-cells"); 83 if (num_phys <= 0) 84 return NULL; 85 86 phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL); 87 if (!phy_roothub) 88 return ERR_PTR(-ENOMEM); 89 90 INIT_LIST_HEAD(&phy_roothub->list); 91 92 if (!usb_phy_roothub_add_phy_by_name(dev, "usb2-phy", &phy_roothub->list)) 93 return phy_roothub; 94 95 for (i = 0; i < num_phys; i++) { 96 err = usb_phy_roothub_add_phy(dev, i, &phy_roothub->list); 97 if (err) 98 return ERR_PTR(err); 99 } 100 101 return phy_roothub; 102 } 103 EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc); 104 105 /** 106 * usb_phy_roothub_alloc_usb3_phy - alloc the roothub 107 * @dev: the device of the host controller 108 * 109 * Allocate the usb phy roothub if the host use a generic usb3-phy. 110 * 111 * Return: On success, a pointer to the usb_phy_roothub. Otherwise, 112 * %NULL if no use usb3 phy or %-ENOMEM if out of memory. 113 */ 114 struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev) 115 { 116 struct usb_phy_roothub *phy_roothub; 117 int num_phys, usb2_phy_index; 118 119 if (!IS_ENABLED(CONFIG_GENERIC_PHY)) 120 return NULL; 121 122 num_phys = of_count_phandle_with_args(dev->of_node, "phys", 123 "#phy-cells"); 124 if (num_phys <= 0) 125 return NULL; 126 127 /* 128 * If 'usb2-phy' is not present, usb_phy_roothub_alloc() added 129 * all PHYs to the primary HCD's phy_roothub already, so skip 130 * adding 'usb3-phy' here to avoid double use of that. 131 */ 132 usb2_phy_index = of_property_match_string(dev->of_node, "phy-names", 133 "usb2-phy"); 134 if (usb2_phy_index < 0) 135 return NULL; 136 137 phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL); 138 if (!phy_roothub) 139 return ERR_PTR(-ENOMEM); 140 141 INIT_LIST_HEAD(&phy_roothub->list); 142 143 if (!usb_phy_roothub_add_phy_by_name(dev, "usb3-phy", &phy_roothub->list)) 144 return phy_roothub; 145 146 return NULL; 147 } 148 EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc_usb3_phy); 149 150 int usb_phy_roothub_init(struct usb_phy_roothub *phy_roothub) 151 { 152 struct usb_phy_roothub *roothub_entry; 153 struct list_head *head; 154 int err; 155 156 if (!phy_roothub) 157 return 0; 158 159 head = &phy_roothub->list; 160 161 list_for_each_entry(roothub_entry, head, list) { 162 err = phy_init(roothub_entry->phy); 163 if (err) 164 goto err_exit_phys; 165 } 166 167 return 0; 168 169 err_exit_phys: 170 list_for_each_entry_continue_reverse(roothub_entry, head, list) 171 phy_exit(roothub_entry->phy); 172 173 return err; 174 } 175 EXPORT_SYMBOL_GPL(usb_phy_roothub_init); 176 177 int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub) 178 { 179 struct usb_phy_roothub *roothub_entry; 180 struct list_head *head; 181 int err, ret = 0; 182 183 if (!phy_roothub) 184 return 0; 185 186 head = &phy_roothub->list; 187 188 list_for_each_entry(roothub_entry, head, list) { 189 err = phy_exit(roothub_entry->phy); 190 if (err) 191 ret = err; 192 } 193 194 return ret; 195 } 196 EXPORT_SYMBOL_GPL(usb_phy_roothub_exit); 197 198 int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub, 199 enum phy_mode mode) 200 { 201 struct usb_phy_roothub *roothub_entry; 202 struct list_head *head; 203 int err; 204 205 if (!phy_roothub) 206 return 0; 207 208 head = &phy_roothub->list; 209 210 list_for_each_entry(roothub_entry, head, list) { 211 err = phy_set_mode(roothub_entry->phy, mode); 212 if (err) 213 return err; 214 } 215 216 return 0; 217 } 218 EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode); 219 220 int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub) 221 { 222 struct usb_phy_roothub *roothub_entry; 223 struct list_head *head; 224 int err; 225 226 if (!phy_roothub) 227 return 0; 228 229 head = &phy_roothub->list; 230 231 list_for_each_entry(roothub_entry, head, list) { 232 err = phy_calibrate(roothub_entry->phy); 233 if (err) 234 return err; 235 } 236 237 return 0; 238 } 239 EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate); 240 241 /** 242 * usb_phy_roothub_notify_connect() - connect notification 243 * @phy_roothub: the phy of roothub, if the host use a generic phy. 244 * @port: the port index for connect 245 * 246 * If the phy needs to get connection status, the callback can be used. 247 * Returns: %0 if successful, a negative error code otherwise 248 */ 249 int usb_phy_roothub_notify_connect(struct usb_phy_roothub *phy_roothub, int port) 250 { 251 struct usb_phy_roothub *roothub_entry; 252 struct list_head *head; 253 int err; 254 255 if (!phy_roothub) 256 return 0; 257 258 head = &phy_roothub->list; 259 260 list_for_each_entry(roothub_entry, head, list) { 261 err = phy_notify_connect(roothub_entry->phy, port); 262 if (err) 263 return err; 264 } 265 266 return 0; 267 } 268 EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_connect); 269 270 /** 271 * usb_phy_roothub_notify_disconnect() - disconnect notification 272 * @phy_roothub: the phy of roothub, if the host use a generic phy. 273 * @port: the port index for disconnect 274 * 275 * If the phy needs to get connection status, the callback can be used. 276 * Returns: %0 if successful, a negative error code otherwise 277 */ 278 int usb_phy_roothub_notify_disconnect(struct usb_phy_roothub *phy_roothub, int port) 279 { 280 struct usb_phy_roothub *roothub_entry; 281 struct list_head *head; 282 int err; 283 284 if (!phy_roothub) 285 return 0; 286 287 head = &phy_roothub->list; 288 289 list_for_each_entry(roothub_entry, head, list) { 290 err = phy_notify_disconnect(roothub_entry->phy, port); 291 if (err) 292 return err; 293 } 294 295 return 0; 296 } 297 EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_disconnect); 298 299 int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub) 300 { 301 struct usb_phy_roothub *roothub_entry; 302 struct list_head *head; 303 int err; 304 305 if (!phy_roothub) 306 return 0; 307 308 head = &phy_roothub->list; 309 310 list_for_each_entry(roothub_entry, head, list) { 311 err = phy_power_on(roothub_entry->phy); 312 if (err) 313 goto err_out; 314 } 315 316 return 0; 317 318 err_out: 319 list_for_each_entry_continue_reverse(roothub_entry, head, list) 320 phy_power_off(roothub_entry->phy); 321 322 return err; 323 } 324 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_on); 325 326 void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub) 327 { 328 struct usb_phy_roothub *roothub_entry; 329 330 if (!phy_roothub) 331 return; 332 333 list_for_each_entry_reverse(roothub_entry, &phy_roothub->list, list) 334 phy_power_off(roothub_entry->phy); 335 } 336 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off); 337 338 int usb_phy_roothub_suspend(struct device *controller_dev, 339 struct usb_phy_roothub *phy_roothub) 340 { 341 usb_phy_roothub_power_off(phy_roothub); 342 343 /* keep the PHYs initialized so the device can wake up the system */ 344 if (device_may_wakeup(controller_dev)) 345 return 0; 346 347 return usb_phy_roothub_exit(phy_roothub); 348 } 349 EXPORT_SYMBOL_GPL(usb_phy_roothub_suspend); 350 351 int usb_phy_roothub_resume(struct device *controller_dev, 352 struct usb_phy_roothub *phy_roothub) 353 { 354 int err; 355 356 /* if the device can't wake up the system _exit was called */ 357 if (!device_may_wakeup(controller_dev)) { 358 err = usb_phy_roothub_init(phy_roothub); 359 if (err) 360 return err; 361 } 362 363 err = usb_phy_roothub_power_on(phy_roothub); 364 365 /* undo _init if _power_on failed */ 366 if (err && !device_may_wakeup(controller_dev)) 367 usb_phy_roothub_exit(phy_roothub); 368 369 return err; 370 } 371 EXPORT_SYMBOL_GPL(usb_phy_roothub_resume); 372