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 return err; 204 } 205 206 return 0; 207 } 208 EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode); 209 210 int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub) 211 { 212 struct usb_phy_roothub *roothub_entry; 213 struct list_head *head; 214 int err; 215 216 if (!phy_roothub) 217 return 0; 218 219 head = &phy_roothub->list; 220 221 list_for_each_entry(roothub_entry, head, list) { 222 err = phy_calibrate(roothub_entry->phy); 223 if (err) 224 return err; 225 } 226 227 return 0; 228 } 229 EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate); 230 231 /** 232 * usb_phy_roothub_notify_connect() - connect notification 233 * @phy_roothub: the phy of roothub, if the host use a generic phy. 234 * @port: the port index for connect 235 * 236 * If the phy needs to get connection status, the callback can be used. 237 * Returns: %0 if successful, a negative error code otherwise 238 */ 239 int usb_phy_roothub_notify_connect(struct usb_phy_roothub *phy_roothub, int port) 240 { 241 struct usb_phy_roothub *roothub_entry; 242 struct list_head *head; 243 int err; 244 245 if (!phy_roothub) 246 return 0; 247 248 head = &phy_roothub->list; 249 250 list_for_each_entry(roothub_entry, head, list) { 251 err = phy_notify_connect(roothub_entry->phy, port); 252 if (err) 253 return err; 254 } 255 256 return 0; 257 } 258 EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_connect); 259 260 /** 261 * usb_phy_roothub_notify_disconnect() - disconnect notification 262 * @phy_roothub: the phy of roothub, if the host use a generic phy. 263 * @port: the port index for disconnect 264 * 265 * If the phy needs to get connection status, the callback can be used. 266 * Returns: %0 if successful, a negative error code otherwise 267 */ 268 int usb_phy_roothub_notify_disconnect(struct usb_phy_roothub *phy_roothub, int port) 269 { 270 struct usb_phy_roothub *roothub_entry; 271 struct list_head *head; 272 int err; 273 274 if (!phy_roothub) 275 return 0; 276 277 head = &phy_roothub->list; 278 279 list_for_each_entry(roothub_entry, head, list) { 280 err = phy_notify_disconnect(roothub_entry->phy, port); 281 if (err) 282 return err; 283 } 284 285 return 0; 286 } 287 EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_disconnect); 288 289 int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub) 290 { 291 struct usb_phy_roothub *roothub_entry; 292 struct list_head *head; 293 int err; 294 295 if (!phy_roothub) 296 return 0; 297 298 head = &phy_roothub->list; 299 300 list_for_each_entry(roothub_entry, head, list) { 301 err = phy_power_on(roothub_entry->phy); 302 if (err) 303 goto err_out; 304 } 305 306 return 0; 307 308 err_out: 309 list_for_each_entry_continue_reverse(roothub_entry, head, list) 310 phy_power_off(roothub_entry->phy); 311 312 return err; 313 } 314 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_on); 315 316 void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub) 317 { 318 struct usb_phy_roothub *roothub_entry; 319 320 if (!phy_roothub) 321 return; 322 323 list_for_each_entry_reverse(roothub_entry, &phy_roothub->list, list) 324 phy_power_off(roothub_entry->phy); 325 } 326 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off); 327 328 int usb_phy_roothub_suspend(struct device *controller_dev, 329 struct usb_phy_roothub *phy_roothub) 330 { 331 usb_phy_roothub_power_off(phy_roothub); 332 333 /* keep the PHYs initialized so the device can wake up the system */ 334 if (device_may_wakeup(controller_dev)) 335 return 0; 336 337 return usb_phy_roothub_exit(phy_roothub); 338 } 339 EXPORT_SYMBOL_GPL(usb_phy_roothub_suspend); 340 341 int usb_phy_roothub_resume(struct device *controller_dev, 342 struct usb_phy_roothub *phy_roothub) 343 { 344 int err; 345 346 /* if the device can't wake up the system _exit was called */ 347 if (!device_may_wakeup(controller_dev)) { 348 err = usb_phy_roothub_init(phy_roothub); 349 if (err) 350 return err; 351 } 352 353 err = usb_phy_roothub_power_on(phy_roothub); 354 355 /* undo _init if _power_on failed */ 356 if (err && !device_may_wakeup(controller_dev)) 357 usb_phy_roothub_exit(phy_roothub); 358 359 return err; 360 } 361 EXPORT_SYMBOL_GPL(usb_phy_roothub_resume); 362