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
|
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)),)
|
|||||||
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)
|
|||||||
/**
|
/**
|
||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 @@ err2:
|
|||||||
|
|
||||||
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)
|
|||||||
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 @@
|
|||||||
#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 {
|
|||||||
* @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 {
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
#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 */
|
||||||
|
91
drivers/usb/dwc3/ulpi.c
Normal file
91
drivers/usb/dwc3/ulpi.c
Normal file
@ -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…
x
Reference in New Issue
Block a user