usb: dwc3: add ULPI interface support
Registers DWC3's ULPI interface with the ULPI bus when it's available. Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Acked-by: David Cohen <david.a.cohen@linux.intel.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
parent
a89d977cc0
commit
88bc9d194f
|
@ -11,6 +11,13 @@ config USB_DWC3
|
|||
|
||||
if USB_DWC3
|
||||
|
||||
config USB_DWC3_ULPI
|
||||
bool "Register ULPI PHY Interface"
|
||||
depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_DWC3
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -394,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
|
|||
/**
|
||||
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
|
||||
* @dwc: Pointer to our controller context structure
|
||||
*
|
||||
* Returns 0 on success. The USB PHY interfaces are configured but not
|
||||
* initialized. The PHY interfaces and the PHYs get initialized together with
|
||||
* the core in dwc3_core_init.
|
||||
*/
|
||||
static void dwc3_phy_setup(struct dwc3 *dwc)
|
||||
static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||
|
||||
|
@ -443,13 +448,30 @@ static void 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_warn(dwc->dev, "HSPHY Interface not defined\n");
|
||||
break;
|
||||
|
||||
/* Relying on default value. */
|
||||
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
|
||||
/* Making sure the interface and PHY are operational */
|
||||
ret = dwc3_soft_reset(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
udelay(1);
|
||||
|
||||
ret = dwc3_ulpi_init(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -467,6 +489,8 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
|
|||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -906,7 +930,9 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, dwc);
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
dwc3_phy_setup(dwc);
|
||||
ret = dwc3_phy_setup(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
ret = dwc3_core_get_phy(dwc);
|
||||
if (ret)
|
||||
|
@ -994,6 +1020,7 @@ err2:
|
|||
|
||||
err1:
|
||||
dwc3_free_event_buffers(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
err0:
|
||||
/*
|
||||
|
@ -1029,6 +1056,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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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_ops = {
|
||||
.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_ops);
|
||||
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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue