Commit 88bc9d19 authored by Heikki Krogerus's avatar Heikki Krogerus Committed by Felipe Balbi

usb: dwc3: add ULPI interface support

Registers DWC3's ULPI interface with the ULPI bus when it's
available.
Signed-off-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Acked-by: default avatarDavid Cohen <david.a.cohen@linux.intel.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent a89d977c
...@@ -11,6 +11,13 @@ config USB_DWC3 ...@@ -11,6 +11,13 @@ config USB_DWC3
if 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 choice
bool "DWC3 Mode Selection" bool "DWC3 Mode Selection"
default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET) 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)),) ...@@ -15,6 +15,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
dwc3-y += gadget.o ep0.o dwc3-y += gadget.o ep0.o
endif endif
ifneq ($(CONFIG_USB_DWC3_ULPI),)
dwc3-y += ulpi.o
endif
ifneq ($(CONFIG_DEBUG_FS),) ifneq ($(CONFIG_DEBUG_FS),)
dwc3-y += debugfs.o dwc3-y += debugfs.o
endif endif
......
...@@ -394,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) ...@@ -394,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
/** /**
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
* @dwc: Pointer to our controller context structure * @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; u32 reg;
int ret;
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
...@@ -443,13 +448,30 @@ static void dwc3_phy_setup(struct dwc3 *dwc) ...@@ -443,13 +448,30 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI: case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI:
if (!strncmp(dwc->hsphy_interface, "utmi", 4)) { if (!strncmp(dwc->hsphy_interface, "utmi", 4)) {
reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI; reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI;
break;
} else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) { } else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
} else { } else {
dev_warn(dwc->dev, "HSPHY Interface not defined\n"); dev_warn(dwc->dev, "HSPHY Interface not defined\n");
/* Relying on default value. */
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
break; break;
} }
/* FALLTHROUGH */ /* 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: default:
break; break;
} }
...@@ -467,6 +489,8 @@ static void dwc3_phy_setup(struct dwc3 *dwc) ...@@ -467,6 +489,8 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
return 0;
} }
/** /**
...@@ -906,7 +930,9 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -906,7 +930,9 @@ static int dwc3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dwc); platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc); dwc3_cache_hwparams(dwc);
dwc3_phy_setup(dwc); ret = dwc3_phy_setup(dwc);
if (ret)
goto err0;
ret = dwc3_core_get_phy(dwc); ret = dwc3_core_get_phy(dwc);
if (ret) if (ret)
...@@ -994,6 +1020,7 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -994,6 +1020,7 @@ static int dwc3_probe(struct platform_device *pdev)
err1: err1:
dwc3_free_event_buffers(dwc); dwc3_free_event_buffers(dwc);
dwc3_ulpi_exit(dwc);
err0: err0:
/* /*
...@@ -1029,6 +1056,7 @@ static int dwc3_remove(struct platform_device *pdev) ...@@ -1029,6 +1056,7 @@ static int dwc3_remove(struct platform_device *pdev)
phy_power_off(dwc->usb3_generic_phy); phy_power_off(dwc->usb3_generic_phy);
dwc3_core_exit(dwc); dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/otg.h> #include <linux/usb/otg.h>
#include <linux/ulpi/interface.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
...@@ -661,6 +662,7 @@ struct dwc3_scratchpad_array { ...@@ -661,6 +662,7 @@ struct dwc3_scratchpad_array {
* @usb3_phy: pointer to USB3 PHY * @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY * @usb2_generic_phy: pointer to USB2 PHY
* @usb3_generic_phy: pointer to USB3 PHY * @usb3_generic_phy: pointer to USB3 PHY
* @ulpi: pointer to ulpi interface
* @dcfg: saved contents of DCFG register * @dcfg: saved contents of DCFG register
* @gctl: saved contents of GCTL register * @gctl: saved contents of GCTL register
* @isoch_delay: wValue from Set Isochronous Delay request; * @isoch_delay: wValue from Set Isochronous Delay request;
...@@ -749,6 +751,8 @@ struct dwc3 { ...@@ -749,6 +751,8 @@ struct dwc3 {
struct phy *usb2_generic_phy; struct phy *usb2_generic_phy;
struct phy *usb3_generic_phy; struct phy *usb3_generic_phy;
struct ulpi *ulpi;
void __iomem *regs; void __iomem *regs;
size_t regs_size; size_t regs_size;
...@@ -1047,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc) ...@@ -1047,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
} }
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ #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 */ #endif /* __DRIVERS_USB_DWC3_CORE_H */
/**
* 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;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment