if (cdns->roles[role]->state == CDNS_ROLE_STATE_ACTIVE) return 0;
mutex_lock(&cdns->mutex);
ret = cdns->roles[role]->start(cdns); if (!ret)
cdns->roles[role]->state = CDNS_ROLE_STATE_ACTIVE;
mutex_unlock(&cdns->mutex);
return ret;
}
staticvoid cdns_role_stop(struct cdns *cdns)
{ enum usb_role role = cdns->role;
if (WARN_ON(role > USB_ROLE_DEVICE)) return;
if (cdns->roles[role]->state == CDNS_ROLE_STATE_INACTIVE) return;
/* * If driver can't read mode by means of usb_get_dr_mode function then * chooses mode according with Kernel configuration. This setting * can be restricted later depending on strap pin configuration.
*/ if (dr_mode == USB_DR_MODE_UNKNOWN) { if (cdns->version == CDNSP_CONTROLLER_V2) { if (IS_ENABLED(CONFIG_USB_CDNSP_HOST) &&
IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
dr_mode = USB_DR_MODE_OTG; elseif (IS_ENABLED(CONFIG_USB_CDNSP_HOST))
dr_mode = USB_DR_MODE_HOST; elseif (IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
dr_mode = USB_DR_MODE_PERIPHERAL;
} else { if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_OTG; elseif (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
dr_mode = USB_DR_MODE_HOST; elseif (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_PERIPHERAL;
}
}
/* * At this point cdns->dr_mode contains strap configuration. * Driver try update this setting considering kernel configuration
*/
best_dr_mode = cdns->dr_mode;
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { if ((cdns->version == CDNSP_CONTROLLER_V2 &&
IS_ENABLED(CONFIG_USB_CDNSP_HOST)) ||
(cdns->version < CDNSP_CONTROLLER_V2 &&
IS_ENABLED(CONFIG_USB_CDNS3_HOST)))
ret = cdns_host_init(cdns); else
ret = -ENXIO;
if (ret) {
dev_err(dev, "Host initialization failed with %d\n",
ret); goto err;
}
}
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { if (cdns->gadget_init)
ret = cdns->gadget_init(cdns); else
ret = -ENXIO;
if (ret) {
dev_err(dev, "Device initialization failed with %d\n",
ret); goto err;
}
}
cdns->dr_mode = dr_mode;
ret = cdns_drd_update_mode(cdns); if (ret) goto err;
/* Initialize idle role to start with */
ret = cdns_role_start(cdns, USB_ROLE_NONE); if (ret) goto err;
switch (cdns->dr_mode) { case USB_DR_MODE_OTG:
ret = cdns_hw_role_switch(cdns); if (ret) goto err; break; case USB_DR_MODE_PERIPHERAL:
ret = cdns_role_start(cdns, USB_ROLE_DEVICE); if (ret) goto err; break; case USB_DR_MODE_HOST:
ret = cdns_role_start(cdns, USB_ROLE_HOST); if (ret) goto err; break; default:
ret = -EINVAL; goto err;
}
/** * cdns_hw_role_state_machine - role switch state machine based on hw events. * @cdns: Pointer to controller structure. * * Returns next role to be entered based on hw events.
*/ staticenum usb_role cdns_hw_role_state_machine(struct cdns *cdns)
{ enum usb_role role = USB_ROLE_NONE; int id, vbus;
if (cdns->dr_mode != USB_DR_MODE_OTG) { if (cdns_is_host(cdns))
role = USB_ROLE_HOST; if (cdns_is_device(cdns))
role = USB_ROLE_DEVICE;
return role;
}
id = cdns_get_id(cdns);
vbus = cdns_get_vbus(cdns);
/* * Role change state machine * Inputs: ID, VBUS * Previous state: cdns->role * Next state: role
*/
role = cdns->role;
switch (role) { case USB_ROLE_NONE: /* * Driver treats USB_ROLE_NONE synonymous to IDLE state from * controller specification.
*/ if (!id)
role = USB_ROLE_HOST; elseif (vbus)
role = USB_ROLE_DEVICE; break; case USB_ROLE_HOST: /* from HOST, we can only change to NONE */ if (id)
role = USB_ROLE_NONE; break; case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/ if (!vbus)
role = USB_ROLE_NONE; break;
}
/** * cdns_hw_role_switch - switch roles based on HW state * @cdns: controller
*/ int cdns_hw_role_switch(struct cdns *cdns)
{ enum usb_role real_role, current_role; int ret = 0;
/* Depends on role switch class */ if (cdns->role_sw) return 0;
/* Do nothing if nothing changed */ if (current_role == real_role) gotoexit;
cdns_role_stop(cdns);
dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
ret = cdns_role_start(cdns, real_role); if (ret) { /* Back to current role */
dev_err(cdns->dev, "set %d has failed, back to %d\n",
real_role, current_role);
ret = cdns_role_start(cdns, current_role); if (ret)
dev_err(cdns->dev, "back to %d failed too\n",
current_role);
} exit:
pm_runtime_put_sync(cdns->dev); return ret;
}
/** * cdns_role_get - get current role of controller. * * @sw: pointer to USB role switch structure * * Returns role
*/ staticenum usb_role cdns_role_get(struct usb_role_switch *sw)
{ struct cdns *cdns = usb_role_switch_get_drvdata(sw);
return cdns->role;
}
/** * cdns_role_set - set current role of controller. * * @sw: pointer to USB role switch structure * @role: the previous role * Handles below events: * - Role switch for dual-role devices * - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
*/ staticint cdns_role_set(struct usb_role_switch *sw, enum usb_role role)
{ struct cdns *cdns = usb_role_switch_get_drvdata(sw); int ret = 0;
pm_runtime_get_sync(cdns->dev);
if (cdns->role == role) goto pm_put;
if (cdns->dr_mode == USB_DR_MODE_HOST) { switch (role) { case USB_ROLE_NONE: case USB_ROLE_HOST: break; default: goto pm_put;
}
}
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) { switch (role) { case USB_ROLE_NONE: case USB_ROLE_DEVICE: break; default: goto pm_put;
}
}
cdns_role_stop(cdns);
ret = cdns_role_start(cdns, role); if (ret)
dev_err(cdns->dev, "set role %d has failed\n", role);
if (pm_runtime_status_suspended(dev))
pm_runtime_resume(dev);
if (cdns->roles[cdns->role]->suspend) {
spin_lock_irqsave(&cdns->lock, flags);
cdns->roles[cdns->role]->suspend(cdns, false);
spin_unlock_irqrestore(&cdns->lock, flags);
}
return 0;
}
EXPORT_SYMBOL_GPL(cdns_suspend);
int cdns_resume(struct cdns *cdns)
{ bool power_lost = cdns_power_is_lost(cdns); enum usb_role real_role; bool role_changed = false; int ret = 0;
if (power_lost) { if (!cdns->role_sw) {
real_role = cdns_hw_role_state_machine(cdns); if (real_role != cdns->role) {
ret = cdns_hw_role_switch(cdns); if (ret) return ret;
role_changed = true;
}
}
if (!role_changed) { if (cdns->role == USB_ROLE_HOST)
ret = cdns_drd_host_on(cdns); elseif (cdns->role == USB_ROLE_DEVICE)
ret = cdns_drd_gadget_on(cdns);
if (ret) return ret;
}
}
if (cdns->roles[cdns->role]->resume)
cdns->roles[cdns->role]->resume(cdns, power_lost);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.