lkml.org 
[lkml]   [2015]   [Mar]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCHv3 10/12] usb: dwc3: add ULPI interface support
    Date
    Registers DWC3's ULPI interface with the ULPI bus when it's
    available.

    Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
    ---
    drivers/usb/dwc3/Kconfig | 7 ++++
    drivers/usb/dwc3/Makefile | 4 +++
    drivers/usb/dwc3/core.c | 14 ++++++++
    drivers/usb/dwc3/core.h | 14 ++++++++
    drivers/usb/dwc3/ulpi.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++
    5 files changed, 130 insertions(+)
    create mode 100644 drivers/usb/dwc3/ulpi.c

    diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
    index 827c4f8..6f92b36 100644
    --- a/drivers/usb/dwc3/Kconfig
    +++ b/drivers/usb/dwc3/Kconfig
    @@ -11,6 +11,13 @@ config USB_DWC3

    if USB_DWC3

    +config USB_DWC3_ULPI
    + bool "Register ULPI PHY Interface"
    + depends on USB_ULPI_BUS
    + help
    + Select this if you have ULPI type PHY attached to your DWC3
    + controller.
    +
    choice
    bool "DWC3 Mode Selection"
    default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET)
    diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
    index 46172f4..c7076e3 100644
    --- a/drivers/usb/dwc3/Makefile
    +++ b/drivers/usb/dwc3/Makefile
    @@ -15,6 +15,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
    dwc3-y += gadget.o ep0.o
    endif

    +ifneq ($(CONFIG_USB_DWC3_ULPI),)
    + dwc3-y += ulpi.o
    +endif
    +
    ifneq ($(CONFIG_DEBUG_FS),)
    dwc3-y += debugfs.o
    endif
    diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
    index b229ba1..4dce614 100644
    --- a/drivers/usb/dwc3/core.c
    +++ b/drivers/usb/dwc3/core.c
    @@ -444,13 +444,25 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
    case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI:
    if (!strncmp(dwc->hsphy_interface, "utmi", 4)) {
    reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI;
    + break;
    } else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
    reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
    + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
    } else {
    dev_err(dwc->dev, "HSPHY Interface not defined\n");
    return -EINVAL;
    }
    /* FALLTHROUGH */
    + case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
    + /* Soft reset here to sync the clocks */
    + ret = dwc3_soft_reset(dwc);
    + if (ret)
    + return ret;
    +
    + ret = dwc3_ulpi_init(dwc);
    + if (ret)
    + return ret;
    + /* FALLTHROUGH */
    default:
    break;
    }
    @@ -999,6 +1011,7 @@ err2:

    err1:
    dwc3_free_event_buffers(dwc);
    + dwc3_ulpi_exit(dwc);

    err0:
    /*
    @@ -1034,6 +1047,7 @@ static int dwc3_remove(struct platform_device *pdev)
    phy_power_off(dwc->usb3_generic_phy);

    dwc3_core_exit(dwc);
    + dwc3_ulpi_exit(dwc);

    pm_runtime_put_sync(&pdev->dev);
    pm_runtime_disable(&pdev->dev);
    diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
    index 7b3ab64..d70da20 100644
    --- a/drivers/usb/dwc3/core.h
    +++ b/drivers/usb/dwc3/core.h
    @@ -30,6 +30,7 @@
    #include <linux/usb/ch9.h>
    #include <linux/usb/gadget.h>
    #include <linux/usb/otg.h>
    +#include <linux/ulpi/interface.h>

    #include <linux/phy/phy.h>

    @@ -661,6 +662,7 @@ struct dwc3_scratchpad_array {
    * @usb3_phy: pointer to USB3 PHY
    * @usb2_generic_phy: pointer to USB2 PHY
    * @usb3_generic_phy: pointer to USB3 PHY
    + * @ulpi: pointer to ulpi interface
    * @dcfg: saved contents of DCFG register
    * @gctl: saved contents of GCTL register
    * @isoch_delay: wValue from Set Isochronous Delay request;
    @@ -749,6 +751,8 @@ struct dwc3 {
    struct phy *usb2_generic_phy;
    struct phy *usb3_generic_phy;

    + struct ulpi *ulpi;
    +
    void __iomem *regs;
    size_t regs_size;

    @@ -1047,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
    }
    #endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */

    +#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
    +int dwc3_ulpi_init(struct dwc3 *dwc);
    +void dwc3_ulpi_exit(struct dwc3 *dwc);
    +#else
    +static inline int dwc3_ulpi_init(struct dwc3 *dwc)
    +{ return 0; }
    +static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
    +{ }
    +#endif
    +
    #endif /* __DRIVERS_USB_DWC3_CORE_H */
    diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c
    new file mode 100644
    index 0000000..4dfcc81
    --- /dev/null
    +++ b/drivers/usb/dwc3/ulpi.c
    @@ -0,0 +1,91 @@
    +/**
    + * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface
    + *
    + * Copyright (C) 2015 Intel Corporation
    + *
    + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License version 2 as
    + * published by the Free Software Foundation.
    + */
    +
    +#include <linux/ulpi/regs.h>
    +
    +#include "core.h"
    +#include "io.h"
    +
    +#define DWC3_ULPI_ADDR(a) \
    + ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \
    + DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \
    + DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a))
    +
    +static int dwc3_ulpi_busyloop(struct dwc3 *dwc)
    +{
    + unsigned count = 1000;
    + u32 reg;
    +
    + while (count--) {
    + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
    + if (!(reg & DWC3_GUSB2PHYACC_BUSY))
    + return 0;
    + cpu_relax();
    + }
    +
    + return -ETIMEDOUT;
    +}
    +
    +static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr)
    +{
    + struct dwc3 *dwc = dev_get_drvdata(ops->dev);
    + u32 reg;
    + int ret;
    +
    + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
    + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
    +
    + ret = dwc3_ulpi_busyloop(dwc);
    + if (ret)
    + return ret;
    +
    + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
    +
    + return DWC3_GUSB2PHYACC_DATA(reg);
    +}
    +
    +static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val)
    +{
    + struct dwc3 *dwc = dev_get_drvdata(ops->dev);
    + u32 reg;
    +
    + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
    + reg |= DWC3_GUSB2PHYACC_WRITE | val;
    + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
    +
    + return dwc3_ulpi_busyloop(dwc);
    +}
    +
    +static struct ulpi_ops dwc3_ulpi = {
    + .read = dwc3_ulpi_read,
    + .write = dwc3_ulpi_write,
    +};
    +
    +int dwc3_ulpi_init(struct dwc3 *dwc)
    +{
    + /* Register the interface */
    + dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi);
    + if (IS_ERR(dwc->ulpi)) {
    + dev_err(dwc->dev, "failed to register ULPI interface");
    + return PTR_ERR(dwc->ulpi);
    + }
    +
    + return 0;
    +}
    +
    +void dwc3_ulpi_exit(struct dwc3 *dwc)
    +{
    + if (dwc->ulpi) {
    + ulpi_unregister_interface(dwc->ulpi);
    + dwc->ulpi = NULL;
    + }
    +}
    --
    2.1.4


    \
     
     \ /
      Last update: 2015-03-20 13:41    [W:3.721 / U:0.040 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site