lkml.org 
[lkml]   [2015]   [Sep]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: [PATCH v4 1/9] usb: dwc3: add dual-role support
    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@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
    [unhandled content-type:application/pgp-signature]
    \
     
     \ /
      Last update: 2015-09-02 16:41    [W:4.912 / U:0.040 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site