Add role switch class support for chipidea

-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEDaZUZmFxRG/wNThrSFkpgVDWcbsFAl1uCRoACgkQSFkpgVDW
 cbv5swf/cHomRBrTFoH0r/qNr9M7flAbrZiwNmWdxef2PJSsc+2kUU3CpY7OGiGa
 7/9bngHno8EpJuPiGsKKtOOMvsUz2vzreFuh7Rgss+no1Ct3B7bdkke/n06S52Rj
 0SX3xqyuNL+J4IVrgByEcsrgxbSpwY5Yzx+7rAF4NJU0P6wrZht1VflYvhpRdE0y
 RfrWdV9jKxEuE16FPZhu56u4FynuhO850+urixp+sqp8zQghVJOm0BTZCfI5WxAK
 DZ42f9PnrhrIcYJ4JNLDE7QKTFHp08GVqLGkna1cwtUMq7INNW3ovXQk6r0Mu9Kv
 Il0DIIW34kI5x+4gdUFQnnFhyb/clQ==
 =wMNU
 -----END PGP SIGNATURE-----

Merge tag 'usb-ci-v5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb into usb-next

Peter writes:

Add role switch class support for chipidea

* tag 'usb-ci-v5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb:
  usb: chipidea: msm: Use device-managed registration API
  usb: chipidea: add role switch class support
  dt-binding: usb: usbmisc-imx: add imx7ulp compatible
  dt-binding: usb: ci-hdrc-usb2: add imx7ulp compatible
This commit is contained in:
Greg Kroah-Hartman 2019-09-05 10:02:07 +02:00
commit fb9617edf6
6 changed files with 102 additions and 4 deletions

View file

@ -10,6 +10,7 @@ Required properties:
"fsl,imx6sx-usb"
"fsl,imx6ul-usb"
"fsl,imx7d-usb"
"fsl,imx7ulp-usb"
"lsi,zevio-usb"
"qcom,ci-hdrc"
"chipidea,usb2"

View file

@ -7,6 +7,7 @@ Required properties:
"fsl,vf610-usbmisc" for Vybrid vf610
"fsl,imx6sx-usbmisc" for imx6sx
"fsl,imx7d-usbmisc" for imx7d
"fsl,imx7ulp-usbmisc" for imx7ulp
- reg: Should contain registers location and length
Examples:

View file

@ -6,6 +6,7 @@ config USB_CHIPIDEA
select EXTCON
select RESET_CONTROLLER
select USB_ULPI_BUS
select USB_ROLE_SWITCH
help
Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports:

View file

@ -16,6 +16,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
#include <linux/usb/otg.h>
#include <linux/usb/role.h>
#include <linux/ulpi/interface.h>
/******************************************************************************
@ -217,6 +218,7 @@ struct ci_hdrc {
ktime_t hr_timeouts[NUM_OTG_FSM_TIMERS];
unsigned enabled_otg_timer_bits;
enum otg_fsm_timer next_otg_timer;
struct usb_role_switch *role_switch;
struct work_struct work;
struct workqueue_struct *wq;
@ -290,6 +292,16 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
ci->roles[role]->stop(ci);
}
static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci)
{
if (ci->role == CI_ROLE_HOST)
return USB_ROLE_HOST;
else if (ci->role == CI_ROLE_GADGET && ci->vbus_active)
return USB_ROLE_DEVICE;
else
return USB_ROLE_NONE;
}
/**
* hw_read_id_reg: reads from a identification register
* @ci: the controller

View file

@ -600,6 +600,71 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
return NOTIFY_DONE;
}
static enum usb_role ci_usb_role_switch_get(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
enum usb_role role;
unsigned long flags;
spin_lock_irqsave(&ci->lock, flags);
role = ci_role_to_usb_role(ci);
spin_unlock_irqrestore(&ci->lock, flags);
return role;
}
static int ci_usb_role_switch_set(struct device *dev, enum usb_role role)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
struct ci_hdrc_cable *cable = NULL;
enum usb_role current_role = ci_role_to_usb_role(ci);
unsigned long flags;
if (current_role == role)
return 0;
pm_runtime_get_sync(ci->dev);
/* Stop current role */
spin_lock_irqsave(&ci->lock, flags);
if (current_role == USB_ROLE_DEVICE)
cable = &ci->platdata->vbus_extcon;
else if (current_role == USB_ROLE_HOST)
cable = &ci->platdata->id_extcon;
if (cable) {
cable->changed = true;
cable->connected = false;
ci_irq(ci->irq, ci);
spin_unlock_irqrestore(&ci->lock, flags);
if (ci->wq && role != USB_ROLE_NONE)
flush_workqueue(ci->wq);
spin_lock_irqsave(&ci->lock, flags);
}
cable = NULL;
/* Start target role */
if (role == USB_ROLE_DEVICE)
cable = &ci->platdata->vbus_extcon;
else if (role == USB_ROLE_HOST)
cable = &ci->platdata->id_extcon;
if (cable) {
cable->changed = true;
cable->connected = true;
ci_irq(ci->irq, ci);
}
spin_unlock_irqrestore(&ci->lock, flags);
pm_runtime_put_sync(ci->dev);
return 0;
}
static struct usb_role_switch_desc ci_role_switch = {
.set = ci_usb_role_switch_set,
.get = ci_usb_role_switch_get,
};
static int ci_get_platdata(struct device *dev,
struct ci_hdrc_platform_data *platdata)
{
@ -726,6 +791,9 @@ static int ci_get_platdata(struct device *dev,
cable->connected = false;
}
if (device_property_read_bool(dev, "usb-role-switch"))
ci_role_switch.fwnode = dev->fwnode;
platdata->pctl = devm_pinctrl_get(dev);
if (!IS_ERR(platdata->pctl)) {
struct pinctrl_state *p;
@ -1047,6 +1115,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
}
}
if (ci_role_switch.fwnode) {
ci->role_switch = usb_role_switch_register(dev,
&ci_role_switch);
if (IS_ERR(ci->role_switch)) {
ret = PTR_ERR(ci->role_switch);
goto deinit_otg;
}
}
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
if (ci->is_otg) {
ci->role = ci_otg_role(ci);
@ -1105,6 +1182,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return 0;
stop:
if (ci->role_switch)
usb_role_switch_unregister(ci->role_switch);
deinit_otg:
if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
ci_hdrc_otg_destroy(ci);
deinit_gadget:
@ -1123,6 +1203,9 @@ static int ci_hdrc_remove(struct platform_device *pdev)
{
struct ci_hdrc *ci = platform_get_drvdata(pdev);
if (ci->role_switch)
usb_role_switch_unregister(ci->role_switch);
if (ci->supports_runtime_pm) {
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);

View file

@ -35,7 +35,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
* detection overwrite OTGSC register value
*/
cable = &ci->platdata->vbus_extcon;
if (!IS_ERR(cable->edev)) {
if (!IS_ERR(cable->edev) || ci->role_switch) {
if (cable->changed)
val |= OTGSC_BSVIS;
else
@ -53,7 +53,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
}
cable = &ci->platdata->id_extcon;
if (!IS_ERR(cable->edev)) {
if (!IS_ERR(cable->edev) || ci->role_switch) {
if (cable->changed)
val |= OTGSC_IDIS;
else
@ -83,7 +83,7 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
struct ci_hdrc_cable *cable;
cable = &ci->platdata->vbus_extcon;
if (!IS_ERR(cable->edev)) {
if (!IS_ERR(cable->edev) || ci->role_switch) {
if (data & mask & OTGSC_BSVIS)
cable->changed = false;
@ -97,7 +97,7 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
}
cable = &ci->platdata->id_extcon;
if (!IS_ERR(cable->edev)) {
if (!IS_ERR(cable->edev) || ci->role_switch) {
if (data & mask & OTGSC_IDIS)
cable->changed = false;