1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Cadence USBSS DRD Driver. 4 * 5 * Copyright (C) 2018-2019 Cadence. 6 * Copyright (C) 2017-2018 NXP 7 * Copyright (C) 2019 Texas Instruments 8 * 9 * Author: Peter Chen <peter.chen@nxp.com> 10 * Pawel Laszczak <pawell@cadence.com> 11 * Roger Quadros <rogerq@ti.com> 12 */ 13 14#include <common.h> 15#include <dm.h> 16#include <log.h> 17#include <dm/device-internal.h> 18#include <dm/device_compat.h> 19#include <dm/devres.h> 20#include <dm/lists.h> 21#include <linux/bug.h> 22#include <linux/kernel.h> 23#include <linux/io.h> 24#include <usb.h> 25#include <usb/xhci.h> 26 27#include "core.h" 28#include "host-export.h" 29#include "gadget-export.h" 30#include "drd.h" 31 32static int cdns3_idle_init(struct cdns3 *cdns); 33 34struct cdns3_host_priv { 35 struct xhci_ctrl xhci_ctrl; 36 struct cdns3 cdns; 37}; 38 39struct cdns3_gadget_priv { 40 struct cdns3 cdns; 41}; 42 43static inline 44struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns) 45{ 46 WARN_ON(!cdns->roles[cdns->role]); 47 return cdns->roles[cdns->role]; 48} 49 50static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role) 51{ 52 int ret; 53 54 if (WARN_ON(role > USB_ROLE_DEVICE)) 55 return 0; 56 57 mutex_lock(&cdns->mutex); 58 cdns->role = role; 59 mutex_unlock(&cdns->mutex); 60 61 if (!cdns->roles[role]) 62 return -ENXIO; 63 64 if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE) 65 return 0; 66 67 mutex_lock(&cdns->mutex); 68 ret = cdns->roles[role]->start(cdns); 69 if (!ret) 70 cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE; 71 mutex_unlock(&cdns->mutex); 72 73 return ret; 74} 75 76static void cdns3_role_stop(struct cdns3 *cdns) 77{ 78 enum usb_role role = cdns->role; 79 80 if (WARN_ON(role > USB_ROLE_DEVICE)) 81 return; 82 83 if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE) 84 return; 85 86 mutex_lock(&cdns->mutex); 87 cdns->roles[role]->stop(cdns); 88 cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE; 89 mutex_unlock(&cdns->mutex); 90} 91 92static void cdns3_exit_roles(struct cdns3 *cdns) 93{ 94 cdns3_role_stop(cdns); 95 cdns3_drd_exit(cdns); 96} 97 98static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns); 99 100/** 101 * cdns3_core_init_role - initialize role of operation 102 * @cdns: Pointer to cdns3 structure 103 * 104 * Returns 0 on success otherwise negative errno 105 */ 106static int cdns3_core_init_role(struct cdns3 *cdns) 107{ 108 struct udevice *dev = cdns->dev; 109 enum usb_dr_mode best_dr_mode; 110 enum usb_dr_mode dr_mode; 111 int ret = 0; 112 113 dr_mode = usb_get_dr_mode(dev_ofnode(dev)); 114 cdns->role = USB_ROLE_NONE; 115 116 /* 117 * If driver can't read mode by means of usb_get_dr_mode function then 118 * chooses mode according with Kernel configuration. This setting 119 * can be restricted later depending on strap pin configuration. 120 */ 121 if (dr_mode == USB_DR_MODE_UNKNOWN) { 122 if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) && 123 IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) 124 dr_mode = USB_DR_MODE_OTG; 125 else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST)) 126 dr_mode = USB_DR_MODE_HOST; 127 else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) 128 dr_mode = USB_DR_MODE_PERIPHERAL; 129 } 130 131 /* 132 * At this point cdns->dr_mode contains strap configuration. 133 * Driver try update this setting considering kernel configuration 134 */ 135 best_dr_mode = cdns->dr_mode; 136 137 ret = cdns3_idle_init(cdns); 138 if (ret) 139 return ret; 140 141 if (dr_mode == USB_DR_MODE_OTG) { 142 best_dr_mode = cdns->dr_mode; 143 } else if (cdns->dr_mode == USB_DR_MODE_OTG) { 144 best_dr_mode = dr_mode; 145 } else if (cdns->dr_mode != dr_mode) { 146 dev_err(dev, "Incorrect DRD configuration\n"); 147 return -EINVAL; 148 } 149 150 dr_mode = best_dr_mode; 151 152#if defined(CONFIG_SPL_USB_HOST) || !defined(CONFIG_SPL_BUILD) 153 if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { 154 ret = cdns3_host_init(cdns); 155 if (ret) { 156 dev_err(dev, "Host initialization failed with %d\n", 157 ret); 158 goto err; 159 } 160 } 161#endif 162 163#if CONFIG_IS_ENABLED(DM_USB_GADGET) 164 if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { 165 ret = cdns3_gadget_init(cdns); 166 if (ret) { 167 dev_err(dev, "Device initialization failed with %d\n", 168 ret); 169 goto err; 170 } 171 } 172#endif 173 174 cdns->dr_mode = dr_mode; 175 176 ret = cdns3_drd_update_mode(cdns); 177 if (ret) 178 goto err; 179 180 if (cdns->dr_mode != USB_DR_MODE_OTG) { 181 ret = cdns3_hw_role_switch(cdns); 182 if (ret) 183 goto err; 184 } 185 186 return ret; 187err: 188 cdns3_exit_roles(cdns); 189 return ret; 190} 191 192/** 193 * cdsn3_hw_role_state_machine - role switch state machine based on hw events 194 * @cdns: Pointer to controller structure. 195 * 196 * Returns next role to be entered based on hw events. 197 */ 198static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns) 199{ 200 enum usb_role role; 201 int id, vbus; 202 203 if (cdns->dr_mode != USB_DR_MODE_OTG) 204 goto not_otg; 205 206 id = cdns3_get_id(cdns); 207 vbus = cdns3_get_vbus(cdns); 208 209 /* 210 * Role change state machine 211 * Inputs: ID, VBUS 212 * Previous state: cdns->role 213 * Next state: role 214 */ 215 role = cdns->role; 216 217 switch (role) { 218 case USB_ROLE_NONE: 219 /* 220 * Driver treats USB_ROLE_NONE synonymous to IDLE state from 221 * controller specification. 222 */ 223 if (!id) 224 role = USB_ROLE_HOST; 225 else if (vbus) 226 role = USB_ROLE_DEVICE; 227 break; 228 case USB_ROLE_HOST: /* from HOST, we can only change to NONE */ 229 if (id) 230 role = USB_ROLE_NONE; 231 break; 232 case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/ 233 if (!vbus) 234 role = USB_ROLE_NONE; 235 break; 236 } 237 238 dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role); 239 240 return role; 241 242not_otg: 243 if (cdns3_is_host(cdns)) 244 role = USB_ROLE_HOST; 245 if (cdns3_is_device(cdns)) 246 role = USB_ROLE_DEVICE; 247 248 return role; 249} 250 251static int cdns3_idle_role_start(struct cdns3 *cdns) 252{ 253 return 0; 254} 255 256static void cdns3_idle_role_stop(struct cdns3 *cdns) 257{ 258 /* Program Lane swap and bring PHY out of RESET */ 259 generic_phy_reset(&cdns->usb3_phy); 260} 261 262static int cdns3_idle_init(struct cdns3 *cdns) 263{ 264 struct cdns3_role_driver *rdrv; 265 266 rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); 267 if (!rdrv) 268 return -ENOMEM; 269 270 rdrv->start = cdns3_idle_role_start; 271 rdrv->stop = cdns3_idle_role_stop; 272 rdrv->state = CDNS3_ROLE_STATE_INACTIVE; 273 rdrv->suspend = NULL; 274 rdrv->resume = NULL; 275 rdrv->name = "idle"; 276 277 cdns->roles[USB_ROLE_NONE] = rdrv; 278 279 return 0; 280} 281 282/** 283 * cdns3_hw_role_switch - switch roles based on HW state 284 * @cdns3: controller 285 */ 286int cdns3_hw_role_switch(struct cdns3 *cdns) 287{ 288 enum usb_role real_role, current_role; 289 int ret = 0; 290 291 /* Do nothing if role based on syfs. */ 292 if (cdns->role_override) 293 return 0; 294 295 current_role = cdns->role; 296 real_role = cdsn3_hw_role_state_machine(cdns); 297 298 /* Do nothing if nothing changed */ 299 if (current_role == real_role) 300 goto exit; 301 302 cdns3_role_stop(cdns); 303 304 dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role); 305 306 ret = cdns3_role_start(cdns, real_role); 307 if (ret) { 308 /* Back to current role */ 309 dev_err(cdns->dev, "set %d has failed, back to %d\n", 310 real_role, current_role); 311 ret = cdns3_role_start(cdns, current_role); 312 if (ret) 313 dev_err(cdns->dev, "back to %d failed too\n", 314 current_role); 315 } 316exit: 317 return ret; 318} 319 320static int cdns3_probe(struct cdns3 *cdns) 321{ 322 struct udevice *dev = cdns->dev; 323 int ret; 324 325 cdns->xhci_regs = dev_remap_addr_name(dev, "xhci"); 326 if (!cdns->xhci_regs) 327 return -EINVAL; 328 329 cdns->dev_regs = dev_remap_addr_name(dev, "dev"); 330 if (!cdns->dev_regs) 331 return -EINVAL; 332 333 mutex_init(&cdns->mutex); 334 335 ret = generic_phy_get_by_name(dev, "cdns3,usb2-phy", &cdns->usb2_phy); 336 if (!ret) { 337 ret = generic_phy_init(&cdns->usb2_phy); 338 if (ret) { 339 dev_err(dev, "USB2 PHY init failed: %d\n", ret); 340 return ret; 341 } 342 } else if (ret != -ENOENT && ret != -ENODATA) { 343 dev_err(dev, "Couldn't get USB2 PHY: %d\n", ret); 344 return ret; 345 } 346 347 ret = generic_phy_get_by_name(dev, "cdns3,usb3-phy", &cdns->usb3_phy); 348 if (!ret) { 349 ret = generic_phy_init(&cdns->usb3_phy); 350 if (ret) { 351 dev_err(dev, "USB3 PHY init failed: %d\n", ret); 352 return ret; 353 } 354 } else if (ret != -ENOENT && ret != -ENODATA) { 355 dev_err(dev, "Couldn't get USB3 PHY: %d\n", ret); 356 return ret; 357 } 358 359 ret = generic_phy_power_on(&cdns->usb2_phy); 360 if (ret) 361 return ret; 362 363 ret = generic_phy_power_on(&cdns->usb3_phy); 364 if (ret) 365 return ret; 366 367 ret = cdns3_drd_init(cdns); 368 if (ret) 369 return ret; 370 371 ret = cdns3_core_init_role(cdns); 372 if (ret) 373 return ret; 374 375 dev_dbg(dev, "Cadence USB3 core: probe succeed\n"); 376 377 return 0; 378} 379 380static int cdns3_remove(struct cdns3 *cdns) 381{ 382 cdns3_exit_roles(cdns); 383 generic_phy_power_off(&cdns->usb2_phy); 384 generic_phy_power_off(&cdns->usb3_phy); 385 generic_phy_exit(&cdns->usb2_phy); 386 generic_phy_exit(&cdns->usb3_phy); 387 return 0; 388} 389 390static const struct udevice_id cdns3_ids[] = { 391 { .compatible = "cdns,usb3" }, 392 { }, 393}; 394 395int cdns3_bind(struct udevice *parent) 396{ 397 enum usb_dr_mode dr_mode; 398 struct udevice *dev; 399 const char *driver; 400 const char *name; 401 ofnode node; 402 int ret; 403 404 node = ofnode_by_compatible(dev_ofnode(parent), "cdns,usb3"); 405 if (!ofnode_valid(node)) { 406 ret = -ENODEV; 407 goto fail; 408 } 409 410 name = ofnode_get_name(node); 411 dr_mode = usb_get_dr_mode(node); 412 413 switch (dr_mode) { 414#if defined(CONFIG_SPL_USB_HOST) || \ 415 (!defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST)) 416 case USB_DR_MODE_HOST: 417 debug("%s: dr_mode: HOST\n", __func__); 418 driver = "cdns-usb3-host"; 419 break; 420#endif 421#if CONFIG_IS_ENABLED(DM_USB_GADGET) 422 case USB_DR_MODE_PERIPHERAL: 423 debug("%s: dr_mode: PERIPHERAL\n", __func__); 424 driver = "cdns-usb3-peripheral"; 425 break; 426#endif 427 default: 428 printf("%s: unsupported dr_mode\n", __func__); 429 ret = -ENODEV; 430 goto fail; 431 }; 432 433 ret = device_bind_driver_to_node(parent, driver, name, node, &dev); 434 if (ret) { 435 printf("%s: not able to bind usb device mode\n", 436 __func__); 437 goto fail; 438 } 439 440 return 0; 441 442fail: 443 /* do not return an error: failing to bind would hang the board */ 444 return 0; 445} 446 447#if CONFIG_IS_ENABLED(DM_USB_GADGET) 448static int cdns3_gadget_probe(struct udevice *dev) 449{ 450 struct cdns3_gadget_priv *priv = dev_get_priv(dev); 451 struct cdns3 *cdns = &priv->cdns; 452 453 cdns->dev = dev; 454 455 return cdns3_probe(cdns); 456} 457 458static int cdns3_gadget_remove(struct udevice *dev) 459{ 460 struct cdns3_gadget_priv *priv = dev_get_priv(dev); 461 struct cdns3 *cdns = &priv->cdns; 462 463 return cdns3_remove(cdns); 464} 465 466U_BOOT_DRIVER(cdns_usb3_peripheral) = { 467 .name = "cdns-usb3-peripheral", 468 .id = UCLASS_USB_GADGET_GENERIC, 469 .of_match = cdns3_ids, 470 .probe = cdns3_gadget_probe, 471 .remove = cdns3_gadget_remove, 472 .priv_auto = sizeof(struct cdns3_gadget_priv), 473 .flags = DM_FLAG_ALLOC_PRIV_DMA, 474}; 475#endif 476 477#if defined(CONFIG_SPL_USB_HOST) || \ 478 (!defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST)) 479static int cdns3_host_probe(struct udevice *dev) 480{ 481 struct cdns3_host_priv *priv = dev_get_priv(dev); 482 struct cdns3 *cdns = &priv->cdns; 483 484 cdns->dev = dev; 485 486 return cdns3_probe(cdns); 487} 488 489static int cdns3_host_remove(struct udevice *dev) 490{ 491 struct cdns3_host_priv *priv = dev_get_priv(dev); 492 struct cdns3 *cdns = &priv->cdns; 493 494 return cdns3_remove(cdns); 495} 496 497U_BOOT_DRIVER(cdns_usb3_host) = { 498 .name = "cdns-usb3-host", 499 .id = UCLASS_USB, 500 .of_match = cdns3_ids, 501 .probe = cdns3_host_probe, 502 .remove = cdns3_host_remove, 503 .priv_auto = sizeof(struct cdns3_host_priv), 504 .ops = &xhci_usb_ops, 505 .flags = DM_FLAG_ALLOC_PRIV_DMA, 506}; 507#endif 508