Re: [PATCH v4 1/9] usb: dwc3: add dual-role support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On Wed, Sep 02, 2015 at 05:24:16PM +0300, Roger Quadros wrote:
> Register with the USB OTG core. Since we don't support
> OTG yet we just work as a dual-role device even
> if device tree says "otg".
> 
> Use extcon framework to get VBUS/ID cable events and
> kick the OTG state machine.
> 
> Signed-off-by: Roger Quadros <rogerq@xxxxxx>
> ---
>  drivers/usb/dwc3/core.c          | 174 ++++++++++++++++++++++++++++++++++++++-
>  drivers/usb/dwc3/core.h          |   7 ++
>  drivers/usb/dwc3/platform_data.h |   1 +
>  3 files changed, 181 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index 064123e..2e36a9b 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -704,6 +704,152 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
>  	return 0;
>  }
>  
> +/* --------------------- Dual-Role management ------------------------------- */
> +
> +static void dwc3_drd_fsm_sync(struct dwc3 *dwc)
> +{
> +	int id, vbus;
> +
> +	/* get ID */
> +	id = extcon_get_cable_state(dwc->edev, "USB-HOST");
> +	/* Host means ID == 0 */
> +	id = !id;
> +
> +	/* get VBUS */
> +	vbus = extcon_get_cable_state(dwc->edev, "USB");
> +	dev_dbg(dwc->dev, "id %d vbus %d\n", id, vbus);

tracepoint please. We don't want this driver to use dev_(v)?db anymore.
Ditto to all others.

> +
> +	dwc->fsm->id = id;
> +	dwc->fsm->b_sess_vld = vbus;
> +	usb_otg_sync_inputs(dwc->fsm);
> +}
> +
> +static int dwc3_drd_start_host(struct otg_fsm *fsm, int on)
> +{
> +	struct device *dev = usb_otg_fsm_to_dev(fsm);
> +	struct dwc3 *dwc = dev_get_drvdata(dev);

how about adding a usb_otg_get_drvdata(fsm) ?

> +	dev_dbg(dwc->dev, "%s: %d\n", __func__, on);
> +	if (on) {
> +		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
> +		/* start the HCD */
> +		usb_otg_start_host(fsm, true);
> +	} else {
> +		/* stop the HCD */
> +		usb_otg_start_host(fsm, false);
> +	}

This can be simplified.

	if (on)
		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);

	usb_otg_start_host(fsm, on);

> +
> +	return 0;
> +}
> +
> +static int dwc3_drd_start_gadget(struct otg_fsm *fsm, int on)
> +{
> +	struct device *dev = usb_otg_fsm_to_dev(fsm);
> +	struct dwc3 *dwc = dev_get_drvdata(dev);
> +
> +	dev_dbg(dwc->dev, "%s: %d\n", __func__, on);
> +	if (on) {
> +		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
> +		dwc3_event_buffers_setup(dwc);
> +
> +		/* start the UDC */
> +		usb_otg_start_gadget(fsm, true);
> +	} else {
> +		/* stop the UDC */
> +		usb_otg_start_gadget(fsm, false);
> +	}

likewise:

	if (on) {
		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
		dwc3_event_buffers_setup(dwc);
	}

	usb_otg_start_gadget(fsm, on);

> +	return 0;
> +}
> +
> +static struct otg_fsm_ops dwc3_drd_ops = {
> +	.start_host = dwc3_drd_start_host,
> +	.start_gadget = dwc3_drd_start_gadget,
> +};
> +
> +static int dwc3_drd_notifier(struct notifier_block *nb,
> +			     unsigned long event, void *ptr)
> +{
> +	struct dwc3 *dwc = container_of(nb, struct dwc3, otg_nb);
> +
> +	dwc3_drd_fsm_sync(dwc);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static int dwc3_drd_init(struct dwc3 *dwc)
> +{
> +	int ret, id, vbus;
> +	struct usb_otg_caps *otgcaps = &dwc->otg_config.otg_caps;
> +
> +	otgcaps->otg_rev = 0;
> +	otgcaps->hnp_support = false;
> +	otgcaps->srp_support = false;
> +	otgcaps->adp_support = false;
> +	dwc->otg_config.fsm_ops = &dwc3_drd_ops;
> +
> +	if (!dwc->edev) {
> +		dev_err(dwc->dev, "No extcon device found for OTG mode\n");
> +		return -ENODEV;
> +	}
> +
> +	dwc->otg_nb.notifier_call = dwc3_drd_notifier;
> +	ret = extcon_register_notifier(dwc->edev, EXTCON_USB, &dwc->otg_nb);
> +	if (ret < 0) {
> +		dev_err(dwc->dev, "Couldn't register USB cable notifier\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
> +				       &dwc->otg_nb);
> +	if (ret < 0) {
> +		dev_err(dwc->dev, "Couldn't register USB-HOST cable notifier\n");
> +		ret = -ENODEV;
> +		goto extcon_fail;
> +	}
> +
> +	/* sanity check id & vbus states */
> +	id = extcon_get_cable_state(dwc->edev, "USB-HOST");
> +	vbus = extcon_get_cable_state(dwc->edev, "USB");
> +	if (id < 0 || vbus < 0) {
> +		dev_err(dwc->dev, "Invalid USB cable state. id %d, vbus %d\n",
> +			id, vbus);
> +		ret = -ENODEV;
> +		goto fail;
> +	}
> +
> +	/* register as DRD device with OTG core */
> +	dwc->fsm = usb_otg_register(dwc->dev, &dwc->otg_config);
> +	if (IS_ERR(dwc->fsm)) {
> +		ret = PTR_ERR(dwc->fsm);
> +		if (ret == -ENOTSUPP)
> +			dev_err(dwc->dev, "CONFIG_USB_OTG needed for dual-role\n");
> +		else
> +			dev_err(dwc->dev, "Failed to register with OTG core\n");

do you need to cope with EPROBE_DEFER ?

> +
> +		goto fail;
> +	}
> +
> +	dwc3_drd_fsm_sync(dwc);
> +
> +	return 0;
> +fail:
> +	extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, &dwc->otg_nb);
> +extcon_fail:
> +	extcon_unregister_notifier(dwc->edev, EXTCON_USB, &dwc->otg_nb);
> +
> +	return ret;
> +}
> +
> +static void dwc3_drd_exit(struct dwc3 *dwc)
> +{
> +	usb_otg_unregister(dwc->dev);
> +	extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, &dwc->otg_nb);
> +	extcon_unregister_notifier(dwc->edev, EXTCON_USB, &dwc->otg_nb);
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
>  static int dwc3_core_init_mode(struct dwc3 *dwc)
>  {
>  	struct device *dev = dwc->dev;
> @@ -727,13 +873,21 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
>  		}
>  		break;
>  	case USB_DR_MODE_OTG:
> -		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
> +		ret = dwc3_drd_init(dwc);
> +		if (ret) {
> +			dev_err(dev, "limiting to peripheral only\n");
> +			dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
> +			dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
> +			goto gadget_init;
> +		}
> +
>  		ret = dwc3_host_init(dwc);
>  		if (ret) {
>  			dev_err(dev, "failed to initialize host\n");
>  			return ret;
>  		}
>  
> +gadget_init:
>  		ret = dwc3_gadget_init(dwc);
>  		if (ret) {
>  			dev_err(dev, "failed to initialize gadget\n");
> @@ -760,6 +914,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
>  	case USB_DR_MODE_OTG:
>  		dwc3_host_exit(dwc);
>  		dwc3_gadget_exit(dwc);
> +		dwc3_drd_exit(dwc);
>  		break;
>  	default:
>  		/* do nothing */
> @@ -843,6 +998,16 @@ static int dwc3_probe(struct platform_device *pdev)
>  	hird_threshold = 12;
>  
>  	if (node) {
> +		if (of_property_read_bool(node, "extcon"))
> +			dwc->edev = extcon_get_edev_by_phandle(dev, 0);
> +		else if (of_property_read_bool(dev->parent->of_node, "extcon"))
> +			dwc->edev = extcon_get_edev_by_phandle(dev->parent, 0);

why do you need to check the parent ? Why isn't that done on the glue
layer ?

> +
> +		if (IS_ERR(dwc->edev)) {
> +			dev_vdbg(dev, "couldn't get extcon device\n");

dev_err() ??

> +			return -EPROBE_DEFER;

this could make us reschedule probe forever.

> +		}
> +
>  		dwc->maximum_speed = of_usb_get_maximum_speed(node);
>  		dwc->has_lpm_erratum = of_property_read_bool(node,
>  				"snps,has-lpm-erratum");
> @@ -887,6 +1052,13 @@ static int dwc3_probe(struct platform_device *pdev)
>  		of_property_read_string(node, "snps,hsphy_interface",
>  					&dwc->hsphy_interface);
>  	} else if (pdata) {
> +		if (pdata->extcon) {
> +			dwc->edev = extcon_get_extcon_dev(pdata->extcon);
> +			if (!dwc->edev) {
> +				dev_vdbg(dev, "couldn't get extcon device\n");
> +				return -EPROBE_DEFER;

ditto

> +			}
> +		}
>  		dwc->maximum_speed = pdata->maximum_speed;
>  		dwc->has_lpm_erratum = pdata->has_lpm_erratum;
>  		if (pdata->lpm_nyet_threshold)
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> index 0447788..5ca2b25 100644
> --- a/drivers/usb/dwc3/core.h
> +++ b/drivers/usb/dwc3/core.h
> @@ -31,8 +31,10 @@
>  #include <linux/usb/gadget.h>
>  #include <linux/usb/otg.h>
>  #include <linux/ulpi/interface.h>
> +#include <linux/usb/otg-fsm.h>
>  
>  #include <linux/phy/phy.h>
> +#include <linux/extcon.h>
>  
>  #define DWC3_MSG_MAX	500
>  
> @@ -753,6 +755,11 @@ struct dwc3 {
>  
>  	struct ulpi		*ulpi;
>  
> +	struct extcon_dev	*edev;	/* USB cable events ID & VBUS */
> +	struct notifier_block	otg_nb;	/* notifier for USB cable events */
> +	struct otg_fsm		*fsm;
> +	struct usb_otg_config	otg_config;
> +
>  	void __iomem		*regs;
>  	size_t			regs_size;
>  
> diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
> index d3614ec..b3b245c 100644
> --- a/drivers/usb/dwc3/platform_data.h
> +++ b/drivers/usb/dwc3/platform_data.h
> @@ -47,4 +47,5 @@ struct dwc3_platform_data {
>  	unsigned tx_de_emphasis:2;
>  
>  	const char *hsphy_interface;
> +	const char *extcon;	/* extcon name for USB cable events ID/VBUS */
>  };
> -- 
> 2.1.4
> 

-- 
balbi

Attachment: signature.asc
Description: Digital signature


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux