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; 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 phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL); 128 if (!phy_roothub) 129 return ERR_PTR(-ENOMEM); 130 131 INIT_LIST_HEAD(&phy_roothub->list); 132 133 if (!usb_phy_roothub_add_phy_by_name(dev, "usb3-phy", &phy_roothub->list)) 134 return phy_roothub; 135 136 return NULL; 137 } 138 EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc_usb3_phy); 139 140 int usb_phy_roothub_init(struct usb_phy_roothub *phy_roothub) 141 { 142 struct usb_phy_roothub *roothub_entry; 143 struct list_head *head; 144 int err; 145 146 if (!phy_roothub) 147 return 0; 148 149 head = &phy_roothub->list; 150 151 list_for_each_entry(roothub_entry, head, list) { 152 err = phy_init(roothub_entry->phy); 153 if (err) 154 goto err_exit_phys; 155 } 156 157 return 0; 158 159 err_exit_phys: 160 list_for_each_entry_continue_reverse(roothub_entry, head, list) 161 phy_exit(roothub_entry->phy); 162 163 return err; 164 } 165 EXPORT_SYMBOL_GPL(usb_phy_roothub_init); 166 167 int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub) 168 { 169 struct usb_phy_roothub *roothub_entry; 170 struct list_head *head; 171 int err, ret = 0; 172 173 if (!phy_roothub) 174 return 0; 175 176 head = &phy_roothub->list; 177 178 list_for_each_entry(roothub_entry, head, list) { 179 err = phy_exit(roothub_entry->phy); 180 if (err) 181 ret = err; 182 } 183 184 return ret; 185 } 186 EXPORT_SYMBOL_GPL(usb_phy_roothub_exit); 187 188 int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub, 189 enum phy_mode mode) 190 { 191 struct usb_phy_roothub *roothub_entry; 192 struct list_head *head; 193 int err; 194 195 if (!phy_roothub) 196 return 0; 197 198 head = &phy_roothub->list; 199 200 list_for_each_entry(roothub_entry, head, list) { 201 err = phy_set_mode(roothub_entry->phy, mode); 202 if (err) 203 goto err_out; 204 } 205 206 return 0; 207 208 err_out: 209 list_for_each_entry_continue_reverse(roothub_entry, head, list) 210 phy_power_off(roothub_entry->phy); 211 212 return err; 213 } 214 EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode); 215 216 int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub) 217 { 218 struct usb_phy_roothub *roothub_entry; 219 struct list_head *head; 220 int err; 221 222 if (!phy_roothub) 223 return 0; 224 225 head = &phy_roothub->list; 226 227 list_for_each_entry(roothub_entry, head, list) { 228 err = phy_calibrate(roothub_entry->phy); 229 if (err) 230 return err; 231 } 232 233 return 0; 234 } 235 EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate); 236 237 /** 238 * usb_phy_roothub_notify_connect() - connect notification 239 * @phy_roothub: the phy of roothub, if the host use a generic phy. 240 * @port: the port index for connect 241 * 242 * If the phy needs to get connection status, the callback can be used. 243 * Returns: %0 if successful, a negative error code otherwise 244 */ 245 int usb_phy_roothub_notify_connect(struct usb_phy_roothub *phy_roothub, int port) 246 { 247 struct usb_phy_roothub *roothub_entry; 248 struct list_head *head; 249 int err; 250 251 if (!phy_roothub) 252 return 0; 253 254 head = &phy_roothub->list; 255 256 list_for_each_entry(roothub_entry, head, list) { 257 err = phy_notify_connect(roothub_entry->phy, port); 258 if (err) 259 return err; 260 } 261 262 return 0; 263 } 264 EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_connect); 265 266 /** 267 * usb_phy_roothub_notify_disconnect() - disconnect notification 268 * @phy_roothub: the phy of roothub, if the host use a generic phy. 269 * @port: the port index for disconnect 270 * 271 * If the phy needs to get connection status, the callback can be used. 272 * Returns: %0 if successful, a negative error code otherwise 273 */ 274 int usb_phy_roothub_notify_disconnect(struct usb_phy_roothub *phy_roothub, int port) 275 { 276 struct usb_phy_roothub *roothub_entry; 277 struct list_head *head; 278 int err; 279 280 if (!phy_roothub) 281 return 0; 282 283 head = &phy_roothub->list; 284 285 list_for_each_entry(roothub_entry, head, list) { 286 err = phy_notify_disconnect(roothub_entry->phy, port); 287 if (err) 288 return err; 289 } 290 291 return 0; 292 } 293 EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_disconnect); 294 295 int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub) 296 { 297 struct usb_phy_roothub *roothub_entry; 298 struct list_head *head; 299 int err; 300 301 if (!phy_roothub) 302 return 0; 303 304 head = &phy_roothub->list; 305 306 list_for_each_entry(roothub_entry, head, list) { 307 err = phy_power_on(roothub_entry->phy); 308 if (err) 309 goto err_out; 310 } 311 312 return 0; 313 314 err_out: 315 list_for_each_entry_continue_reverse(roothub_entry, head, list) 316 phy_power_off(roothub_entry->phy); 317 318 return err; 319 } 320 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_on); 321 322 void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub) 323 { 324 struct usb_phy_roothub *roothub_entry; 325 326 if (!phy_roothub) 327 return; 328 329 list_for_each_entry_reverse(roothub_entry, &phy_roothub->list, list) 330 phy_power_off(roothub_entry->phy); 331 } 332 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off); 333 334 int usb_phy_roothub_suspend(struct device *controller_dev, 335 struct usb_phy_roothub *phy_roothub) 336 { 337 usb_phy_roothub_power_off(phy_roothub); 338 339 /* keep the PHYs initialized so the device can wake up the system */ 340 if (device_may_wakeup(controller_dev)) 341 return 0; 342 343 return usb_phy_roothub_exit(phy_roothub); 344 } 345 EXPORT_SYMBOL_GPL(usb_phy_roothub_suspend); 346 347 int usb_phy_roothub_resume(struct device *controller_dev, 348 struct usb_phy_roothub *phy_roothub) 349 { 350 int err; 351 352 /* if the device can't wake up the system _exit was called */ 353 if (!device_may_wakeup(controller_dev)) { 354 err = usb_phy_roothub_init(phy_roothub); 355 if (err) 356 return err; 357 } 358 359 err = usb_phy_roothub_power_on(phy_roothub); 360 361 /* undo _init if _power_on failed */ 362 if (err && !device_may_wakeup(controller_dev)) 363 usb_phy_roothub_exit(phy_roothub); 364 365 return err; 366 } 367 EXPORT_SYMBOL_GPL(usb_phy_roothub_resume); 368