diff options
Diffstat (limited to 'drivers/usb/chipidea/core.c')
| -rw-r--r-- | drivers/usb/chipidea/core.c | 97 | 
1 files changed, 85 insertions, 12 deletions
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 26062d610c20..98ee575ee500 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -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; @@ -903,10 +971,7 @@ static struct attribute *ci_attrs[] = {  	&dev_attr_role.attr,  	NULL,  }; - -static const struct attribute_group ci_attr_group = { -	.attrs = ci_attrs, -}; +ATTRIBUTE_GROUPS(ci);  static int ci_hdrc_probe(struct platform_device *pdev)  { @@ -1008,7 +1073,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)  	ci->irq = platform_get_irq(pdev, 0);  	if (ci->irq < 0) { -		dev_err(dev, "missing IRQ\n");  		ret = ci->irq;  		goto deinit_phy;  	} @@ -1051,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); @@ -1106,15 +1179,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)  	device_set_wakeup_capable(&pdev->dev, true);  	dbg_create_files(ci); -	ret = sysfs_create_group(&dev->kobj, &ci_attr_group); -	if (ret) -		goto remove_debug; -  	return 0; -remove_debug: -	dbg_remove_files(ci);  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: @@ -1133,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); @@ -1140,7 +1213,6 @@ static int ci_hdrc_remove(struct platform_device *pdev)  	}  	dbg_remove_files(ci); -	sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);  	ci_role_destroy(ci);  	ci_hdrc_enter_lpm(ci, true);  	ci_usb_phy_exit(ci); @@ -1319,6 +1391,7 @@ static struct platform_driver ci_hdrc_driver = {  	.driver	= {  		.name	= "ci_hdrc",  		.pm	= &ci_pm_ops, +		.dev_groups = ci_groups,  	},  };  |