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 <rog...@ti.com>
> ---
>  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

Reply via email to