GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
config USB_DWC3
|
||||
bool "DesignWare USB3 DRD Core Support"
|
||||
depends on USB_HOST || USB_GADGET
|
||||
help
|
||||
Say Y here if your system has a Dual Role SuperSpeed
|
||||
USB controller based on the DesignWare USB3 IP Core.
|
||||
|
||||
if USB_DWC3
|
||||
|
||||
config USB_DWC3_GADGET
|
||||
bool "USB Gadget support for DWC3"
|
||||
default y
|
||||
depends on USB_GADGET
|
||||
select USB_GADGET_DUALSPEED
|
||||
|
||||
comment "Platform Glue Driver Support"
|
||||
|
||||
config USB_DWC3_OMAP
|
||||
bool "Texas Instruments OMAP5 and similar Platforms"
|
||||
help
|
||||
Some platforms from Texas Instruments like OMAP5, DRA7xxx and
|
||||
AM437x use this IP for USB2/3 functionality.
|
||||
|
||||
Say 'Y' here if you have one such device
|
||||
|
||||
config USB_DWC3_GENERIC
|
||||
bool "Generic implementation of a DWC3 wrapper (aka dwc3 glue)"
|
||||
depends on DM_USB && USB_DWC3 && MISC
|
||||
help
|
||||
Select this for Xilinx ZynqMP and similar Platforms.
|
||||
This wrapper supports Host and Peripheral operation modes.
|
||||
|
||||
config USB_DWC3_MESON_G12A
|
||||
bool "Amlogic Meson G12A USB wrapper"
|
||||
depends on DM_USB && USB_DWC3 && ARCH_MESON
|
||||
imply PHY
|
||||
help
|
||||
Select this for Amlogic Meson G12A Platforms.
|
||||
This wrapper supports Host and Peripheral operation modes.
|
||||
|
||||
config USB_DWC3_UNIPHIER
|
||||
bool "DesignWare USB3 Host Support on UniPhier Platforms"
|
||||
depends on ARCH_UNIPHIER && USB_XHCI_DWC3
|
||||
help
|
||||
Support of USB2/3 functionality in Socionext UniPhier platforms.
|
||||
Say 'Y' here if you have one such device.
|
||||
|
||||
menu "PHY Subsystem"
|
||||
|
||||
config USB_DWC3_PHY_OMAP
|
||||
bool "TI OMAP SoC series USB DRD PHY driver"
|
||||
help
|
||||
Enable single driver for both USB2 PHY programming and USB3 PHY
|
||||
programming for TI SoCs.
|
||||
|
||||
config USB_DWC3_PHY_SAMSUNG
|
||||
bool "Exynos5 SoC series USB DRD PHY driver"
|
||||
help
|
||||
Enable USB DRD PHY support for Exynos 5 SoC series.
|
||||
This driver provides PHY interface for USB 3.0 DRD controller
|
||||
present on Exynos5 SoC series.
|
||||
|
||||
endmenu
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3.o
|
||||
|
||||
dwc3-y := core.o
|
||||
|
||||
obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o
|
||||
|
||||
obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
|
||||
obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o
|
||||
obj-$(CONFIG_USB_DWC3_GENERIC) += dwc3-generic.o
|
||||
obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o
|
||||
obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o
|
||||
obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,441 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Generic DWC3 Glue layer
|
||||
*
|
||||
* Copyright (C) 2016 - 2018 Xilinx, Inc.
|
||||
*
|
||||
* Based on dwc3-omap.c.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <asm-generic/io.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dwc3-uboot.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <malloc.h>
|
||||
#include <usb.h>
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include <reset.h>
|
||||
#include <clk.h>
|
||||
#include <usb/xhci.h>
|
||||
|
||||
struct dwc3_generic_plat {
|
||||
fdt_addr_t base;
|
||||
u32 maximum_speed;
|
||||
enum usb_dr_mode dr_mode;
|
||||
};
|
||||
|
||||
struct dwc3_generic_priv {
|
||||
void *base;
|
||||
struct dwc3 dwc3;
|
||||
struct phy *phys;
|
||||
int num_phys;
|
||||
};
|
||||
|
||||
struct dwc3_generic_host_priv {
|
||||
struct xhci_ctrl xhci_ctrl;
|
||||
struct dwc3_generic_priv gen_priv;
|
||||
};
|
||||
|
||||
static int dwc3_generic_probe(struct udevice *dev,
|
||||
struct dwc3_generic_priv *priv)
|
||||
{
|
||||
int rc;
|
||||
struct dwc3_generic_plat *plat = dev_get_platdata(dev);
|
||||
struct dwc3 *dwc3 = &priv->dwc3;
|
||||
|
||||
dwc3->dev = dev;
|
||||
dwc3->maximum_speed = plat->maximum_speed;
|
||||
dwc3->dr_mode = plat->dr_mode;
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
dwc3_of_parse(dwc3);
|
||||
#endif
|
||||
|
||||
rc = dwc3_setup_phy(dev, &priv->phys, &priv->num_phys);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
priv->base = map_physmem(plat->base, DWC3_OTG_REGS_END, MAP_NOCACHE);
|
||||
dwc3->regs = priv->base + DWC3_GLOBALS_REGS_START;
|
||||
|
||||
|
||||
rc = dwc3_init(dwc3);
|
||||
if (rc) {
|
||||
unmap_physmem(priv->base, MAP_NOCACHE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_generic_remove(struct udevice *dev,
|
||||
struct dwc3_generic_priv *priv)
|
||||
{
|
||||
struct dwc3 *dwc3 = &priv->dwc3;
|
||||
|
||||
dwc3_remove(dwc3);
|
||||
dwc3_shutdown_phy(dev, priv->phys, priv->num_phys);
|
||||
unmap_physmem(dwc3->regs, MAP_NOCACHE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_generic_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_generic_plat *plat = dev_get_platdata(dev);
|
||||
int node = dev_of_offset(dev);
|
||||
|
||||
plat->base = devfdt_get_addr(dev);
|
||||
|
||||
plat->maximum_speed = usb_get_maximum_speed(node);
|
||||
if (plat->maximum_speed == USB_SPEED_UNKNOWN) {
|
||||
pr_info("No USB maximum speed specified. Using super speed\n");
|
||||
plat->maximum_speed = USB_SPEED_SUPER;
|
||||
}
|
||||
|
||||
plat->dr_mode = usb_get_dr_mode(node);
|
||||
if (plat->dr_mode == USB_DR_MODE_UNKNOWN) {
|
||||
pr_err("Invalid usb mode setup\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_USB_GADGET)
|
||||
int dm_usb_gadget_handle_interrupts(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_generic_priv *priv = dev_get_priv(dev);
|
||||
struct dwc3 *dwc3 = &priv->dwc3;
|
||||
|
||||
dwc3_gadget_uboot_handle_interrupt(dwc3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_generic_peripheral_probe(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_generic_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return dwc3_generic_probe(dev, priv);
|
||||
}
|
||||
|
||||
static int dwc3_generic_peripheral_remove(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_generic_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return dwc3_generic_remove(dev, priv);
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(dwc3_generic_peripheral) = {
|
||||
.name = "dwc3-generic-peripheral",
|
||||
.id = UCLASS_USB_GADGET_GENERIC,
|
||||
.ofdata_to_platdata = dwc3_generic_ofdata_to_platdata,
|
||||
.probe = dwc3_generic_peripheral_probe,
|
||||
.remove = dwc3_generic_peripheral_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct dwc3_generic_priv),
|
||||
.platdata_auto_alloc_size = sizeof(struct dwc3_generic_plat),
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || !defined(CONFIG_SPL_BUILD)
|
||||
static int dwc3_generic_host_probe(struct udevice *dev)
|
||||
{
|
||||
struct xhci_hcor *hcor;
|
||||
struct xhci_hccr *hccr;
|
||||
struct dwc3_generic_host_priv *priv = dev_get_priv(dev);
|
||||
int rc;
|
||||
|
||||
rc = dwc3_generic_probe(dev, &priv->gen_priv);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
hccr = (struct xhci_hccr *)priv->gen_priv.base;
|
||||
hcor = (struct xhci_hcor *)(priv->gen_priv.base +
|
||||
HC_LENGTH(xhci_readl(&(hccr)->cr_capbase)));
|
||||
|
||||
return xhci_register(dev, hccr, hcor);
|
||||
}
|
||||
|
||||
static int dwc3_generic_host_remove(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_generic_host_priv *priv = dev_get_priv(dev);
|
||||
int rc;
|
||||
|
||||
rc = xhci_deregister(dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return dwc3_generic_remove(dev, &priv->gen_priv);
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(dwc3_generic_host) = {
|
||||
.name = "dwc3-generic-host",
|
||||
.id = UCLASS_USB,
|
||||
.ofdata_to_platdata = dwc3_generic_ofdata_to_platdata,
|
||||
.probe = dwc3_generic_host_probe,
|
||||
.remove = dwc3_generic_host_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct dwc3_generic_host_priv),
|
||||
.platdata_auto_alloc_size = sizeof(struct dwc3_generic_plat),
|
||||
.ops = &xhci_usb_ops,
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
#endif
|
||||
|
||||
struct dwc3_glue_data {
|
||||
struct clk_bulk clks;
|
||||
struct reset_ctl_bulk resets;
|
||||
fdt_addr_t regs;
|
||||
};
|
||||
|
||||
struct dwc3_glue_ops {
|
||||
void (*select_dr_mode)(struct udevice *dev, int index,
|
||||
enum usb_dr_mode mode);
|
||||
};
|
||||
|
||||
void dwc3_ti_select_dr_mode(struct udevice *dev, int index,
|
||||
enum usb_dr_mode mode)
|
||||
{
|
||||
#define USBOTGSS_UTMI_OTG_STATUS 0x0084
|
||||
#define USBOTGSS_UTMI_OTG_OFFSET 0x0480
|
||||
|
||||
/* UTMI_OTG_STATUS REGISTER */
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE BIT(31)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT BIT(9)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE BIT(8)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_IDDIG BIT(4)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_SESSEND BIT(3)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID BIT(2)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID BIT(1)
|
||||
enum dwc3_omap_utmi_mode {
|
||||
DWC3_OMAP_UTMI_MODE_UNKNOWN = 0,
|
||||
DWC3_OMAP_UTMI_MODE_HW,
|
||||
DWC3_OMAP_UTMI_MODE_SW,
|
||||
};
|
||||
|
||||
u32 use_id_pin;
|
||||
u32 host_mode;
|
||||
u32 reg;
|
||||
u32 utmi_mode;
|
||||
u32 utmi_status_offset = USBOTGSS_UTMI_OTG_STATUS;
|
||||
|
||||
struct dwc3_glue_data *glue = dev_get_platdata(dev);
|
||||
void *base = map_physmem(glue->regs, 0x10000, MAP_NOCACHE);
|
||||
|
||||
if (device_is_compatible(dev, "ti,am437x-dwc3"))
|
||||
utmi_status_offset += USBOTGSS_UTMI_OTG_OFFSET;
|
||||
|
||||
utmi_mode = dev_read_u32_default(dev, "utmi-mode",
|
||||
DWC3_OMAP_UTMI_MODE_UNKNOWN);
|
||||
if (utmi_mode != DWC3_OMAP_UTMI_MODE_HW) {
|
||||
debug("%s: OTG is not supported. defaulting to PERIPHERAL\n",
|
||||
dev->name);
|
||||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
use_id_pin = 0;
|
||||
host_mode = 0;
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
use_id_pin = 0;
|
||||
host_mode = 1;
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
default:
|
||||
use_id_pin = 1;
|
||||
host_mode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
reg = readl(base + utmi_status_offset);
|
||||
|
||||
reg &= ~(USBOTGSS_UTMI_OTG_STATUS_SW_MODE);
|
||||
if (!use_id_pin)
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
|
||||
writel(reg, base + utmi_status_offset);
|
||||
|
||||
reg &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSEND |
|
||||
USBOTGSS_UTMI_OTG_STATUS_VBUSVALID |
|
||||
USBOTGSS_UTMI_OTG_STATUS_IDDIG);
|
||||
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID |
|
||||
USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
|
||||
|
||||
if (!host_mode)
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_IDDIG |
|
||||
USBOTGSS_UTMI_OTG_STATUS_VBUSVALID;
|
||||
|
||||
writel(reg, base + utmi_status_offset);
|
||||
|
||||
unmap_physmem(base, MAP_NOCACHE);
|
||||
}
|
||||
|
||||
struct dwc3_glue_ops ti_ops = {
|
||||
.select_dr_mode = dwc3_ti_select_dr_mode,
|
||||
};
|
||||
|
||||
static int dwc3_glue_bind(struct udevice *parent)
|
||||
{
|
||||
const void *fdt = gd->fdt_blob;
|
||||
int node;
|
||||
int ret;
|
||||
|
||||
for (node = fdt_first_subnode(fdt, dev_of_offset(parent)); node > 0;
|
||||
node = fdt_next_subnode(fdt, node)) {
|
||||
const char *name = fdt_get_name(fdt, node, NULL);
|
||||
enum usb_dr_mode dr_mode;
|
||||
struct udevice *dev;
|
||||
const char *driver = NULL;
|
||||
|
||||
debug("%s: subnode name: %s\n", __func__, name);
|
||||
|
||||
dr_mode = usb_get_dr_mode(node);
|
||||
|
||||
switch (dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
#if CONFIG_IS_ENABLED(DM_USB_GADGET)
|
||||
debug("%s: dr_mode: OTG or Peripheral\n", __func__);
|
||||
driver = "dwc3-generic-peripheral";
|
||||
#endif
|
||||
break;
|
||||
#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || !defined(CONFIG_SPL_BUILD)
|
||||
case USB_DR_MODE_HOST:
|
||||
debug("%s: dr_mode: HOST\n", __func__);
|
||||
driver = "dwc3-generic-host";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
debug("%s: unsupported dr_mode\n", __func__);
|
||||
return -ENODEV;
|
||||
};
|
||||
|
||||
if (!driver)
|
||||
continue;
|
||||
|
||||
ret = device_bind_driver_to_node(parent, driver, name,
|
||||
offset_to_ofnode(node), &dev);
|
||||
if (ret) {
|
||||
debug("%s: not able to bind usb device mode\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_glue_reset_init(struct udevice *dev,
|
||||
struct dwc3_glue_data *glue)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = reset_get_bulk(dev, &glue->resets);
|
||||
if (ret == -ENOTSUPP || ret == -ENOENT)
|
||||
return 0;
|
||||
else if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_deassert_bulk(&glue->resets);
|
||||
if (ret) {
|
||||
reset_release_bulk(&glue->resets);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_glue_clk_init(struct udevice *dev,
|
||||
struct dwc3_glue_data *glue)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_get_bulk(dev, &glue->clks);
|
||||
if (ret == -ENOSYS || ret == -ENOENT)
|
||||
return 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
ret = clk_enable_bulk(&glue->clks);
|
||||
if (ret) {
|
||||
clk_release_bulk(&glue->clks);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_glue_probe(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_glue_ops *ops = (struct dwc3_glue_ops *)dev_get_driver_data(dev);
|
||||
struct dwc3_glue_data *glue = dev_get_platdata(dev);
|
||||
struct udevice *child = NULL;
|
||||
int index = 0;
|
||||
int ret;
|
||||
|
||||
glue->regs = dev_read_addr(dev);
|
||||
|
||||
ret = dwc3_glue_clk_init(dev, glue);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_glue_reset_init(dev, glue);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = device_find_first_child(dev, &child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (child) {
|
||||
enum usb_dr_mode dr_mode;
|
||||
|
||||
dr_mode = usb_get_dr_mode(dev_of_offset(child));
|
||||
device_find_next_child(&child);
|
||||
if (ops && ops->select_dr_mode)
|
||||
ops->select_dr_mode(dev, index, dr_mode);
|
||||
index++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_glue_remove(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_glue_data *glue = dev_get_platdata(dev);
|
||||
|
||||
reset_release_bulk(&glue->resets);
|
||||
|
||||
clk_release_bulk(&glue->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id dwc3_glue_ids[] = {
|
||||
{ .compatible = "xlnx,zynqmp-dwc3" },
|
||||
{ .compatible = "ti,keystone-dwc3"},
|
||||
{ .compatible = "ti,dwc3", .data = (ulong)&ti_ops },
|
||||
{ .compatible = "ti,am437x-dwc3", .data = (ulong)&ti_ops },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(dwc3_generic_wrapper) = {
|
||||
.name = "dwc3-generic-wrapper",
|
||||
.id = UCLASS_NOP,
|
||||
.of_match = dwc3_glue_ids,
|
||||
.bind = dwc3_glue_bind,
|
||||
.probe = dwc3_glue_probe,
|
||||
.remove = dwc3_glue_remove,
|
||||
.platdata_auto_alloc_size = sizeof(struct dwc3_glue_data),
|
||||
|
||||
};
|
||||
@@ -0,0 +1,456 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Amlogic G12A DWC3 Glue layer
|
||||
*
|
||||
* Copyright (C) 2019 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm-generic/io.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dwc3-uboot.h>
|
||||
#include <generic-phy.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <malloc.h>
|
||||
#include <regmap.h>
|
||||
#include <usb.h>
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include <reset.h>
|
||||
#include <clk.h>
|
||||
#include <power/regulator.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
/* USB2 Ports Control Registers */
|
||||
|
||||
#define U2P_REG_SIZE 0x20
|
||||
|
||||
#define U2P_R0 0x0
|
||||
#define U2P_R0_HOST_DEVICE BIT(0)
|
||||
#define U2P_R0_POWER_OK BIT(1)
|
||||
#define U2P_R0_HAST_MODE BIT(2)
|
||||
#define U2P_R0_POWER_ON_RESET BIT(3)
|
||||
#define U2P_R0_ID_PULLUP BIT(4)
|
||||
#define U2P_R0_DRV_VBUS BIT(5)
|
||||
|
||||
#define U2P_R1 0x4
|
||||
#define U2P_R1_PHY_READY BIT(0)
|
||||
#define U2P_R1_ID_DIG BIT(1)
|
||||
#define U2P_R1_OTG_SESSION_VALID BIT(2)
|
||||
#define U2P_R1_VBUS_VALID BIT(3)
|
||||
|
||||
/* USB Glue Control Registers */
|
||||
|
||||
#define USB_R0 0x80
|
||||
#define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
|
||||
#define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
|
||||
#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
|
||||
#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
|
||||
#define USB_R0_U2D_ACT BIT(31)
|
||||
|
||||
#define USB_R1 0x84
|
||||
#define USB_R1_U3H_BIGENDIAN_GS BIT(0)
|
||||
#define USB_R1_U3H_PME_ENABLE BIT(1)
|
||||
#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2)
|
||||
#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7)
|
||||
#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 12)
|
||||
#define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16)
|
||||
#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17)
|
||||
#define USB_R1_U3H_HOST_MSI_ENABLE BIT(18)
|
||||
#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
|
||||
#define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
|
||||
|
||||
#define USB_R2 0x88
|
||||
#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
|
||||
#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
|
||||
|
||||
#define USB_R3 0x8c
|
||||
#define USB_R3_P30_SSC_ENABLE BIT(0)
|
||||
#define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
|
||||
#define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
|
||||
#define USB_R3_P30_REF_SSP_EN BIT(13)
|
||||
|
||||
#define USB_R4 0x90
|
||||
#define USB_R4_P21_PORT_RESET_0 BIT(0)
|
||||
#define USB_R4_P21_SLEEP_M0 BIT(1)
|
||||
#define USB_R4_MEM_PD_MASK GENMASK(3, 2)
|
||||
#define USB_R4_P21_ONLY BIT(4)
|
||||
|
||||
#define USB_R5 0x94
|
||||
#define USB_R5_ID_DIG_SYNC BIT(0)
|
||||
#define USB_R5_ID_DIG_REG BIT(1)
|
||||
#define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
|
||||
#define USB_R5_ID_DIG_EN_0 BIT(4)
|
||||
#define USB_R5_ID_DIG_EN_1 BIT(5)
|
||||
#define USB_R5_ID_DIG_CURR BIT(6)
|
||||
#define USB_R5_ID_DIG_IRQ BIT(7)
|
||||
#define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
|
||||
#define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
|
||||
|
||||
enum {
|
||||
USB2_HOST_PHY = 0,
|
||||
USB2_OTG_PHY,
|
||||
USB3_HOST_PHY,
|
||||
PHY_COUNT,
|
||||
};
|
||||
|
||||
static const char *phy_names[PHY_COUNT] = {
|
||||
"usb2-phy0", "usb2-phy1", "usb3-phy0",
|
||||
};
|
||||
|
||||
struct dwc3_meson_g12a {
|
||||
struct udevice *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk clk;
|
||||
struct reset_ctl reset;
|
||||
struct phy phys[PHY_COUNT];
|
||||
enum usb_dr_mode otg_mode;
|
||||
enum usb_dr_mode otg_phy_mode;
|
||||
unsigned int usb2_ports;
|
||||
unsigned int usb3_ports;
|
||||
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||
struct udevice *vbus_supply;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define U2P_REG_SIZE 0x20
|
||||
#define USB_REG_OFFSET 0x80
|
||||
|
||||
static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
|
||||
int i, enum usb_dr_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case USB_DR_MODE_HOST:
|
||||
case USB_DR_MODE_OTG:
|
||||
case USB_DR_MODE_UNKNOWN:
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_HOST_DEVICE,
|
||||
U2P_R0_HOST_DEVICE);
|
||||
break;
|
||||
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_HOST_DEVICE, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
|
||||
priv->otg_phy_mode = USB_DR_MODE_PERIPHERAL;
|
||||
else
|
||||
priv->otg_phy_mode = USB_DR_MODE_HOST;
|
||||
|
||||
for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_POWER_ON_RESET,
|
||||
U2P_R0_POWER_ON_RESET);
|
||||
|
||||
if (i == USB2_OTG_PHY) {
|
||||
regmap_update_bits(priv->regmap,
|
||||
U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
|
||||
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, i,
|
||||
priv->otg_phy_mode);
|
||||
} else
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, i,
|
||||
USB_DR_MODE_HOST);
|
||||
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_POWER_ON_RESET, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
regmap_update_bits(priv->regmap, USB_R3,
|
||||
USB_R3_P30_SSC_RANGE_MASK |
|
||||
USB_R3_P30_REF_SSP_EN,
|
||||
USB_R3_P30_SSC_ENABLE |
|
||||
FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
|
||||
USB_R3_P30_REF_SSP_EN);
|
||||
udelay(2);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R2,
|
||||
USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
|
||||
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R2,
|
||||
USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
|
||||
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
|
||||
|
||||
udelay(2);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
|
||||
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
USB_R1_P30_PCS_TX_SWING_FULL_MASK,
|
||||
FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
|
||||
}
|
||||
|
||||
static void dwc3_meson_g12a_usb_init_mode(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
if (priv->otg_phy_mode == USB_DR_MODE_PERIPHERAL) {
|
||||
regmap_update_bits(priv->regmap, USB_R0,
|
||||
USB_R0_U2D_ACT, USB_R0_U2D_ACT);
|
||||
regmap_update_bits(priv->regmap, USB_R0,
|
||||
USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
|
||||
regmap_update_bits(priv->regmap, USB_R4,
|
||||
USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
|
||||
} else {
|
||||
regmap_update_bits(priv->regmap, USB_R0,
|
||||
USB_R0_U2D_ACT, 0);
|
||||
regmap_update_bits(priv->regmap, USB_R4,
|
||||
USB_R4_P21_SLEEP_M0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dwc3_meson_g12a_usb2_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
|
||||
FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
USB_R5_ID_DIG_EN_0,
|
||||
USB_R5_ID_DIG_EN_0);
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
USB_R5_ID_DIG_EN_1,
|
||||
USB_R5_ID_DIG_EN_1);
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
USB_R5_ID_DIG_TH_MASK,
|
||||
FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
|
||||
|
||||
/* If we have an actual SuperSpeed port, initialize it */
|
||||
if (priv->usb3_ports)
|
||||
dwc3_meson_g12a_usb3_init(priv);
|
||||
|
||||
dwc3_meson_g12a_usb_init_mode(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
|
||||
|
||||
if (!priv)
|
||||
return -EINVAL;
|
||||
|
||||
if (mode != USB_DR_MODE_HOST && mode != USB_DR_MODE_PERIPHERAL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!priv->phys[USB2_OTG_PHY].dev)
|
||||
return -EINVAL;
|
||||
|
||||
if (mode == priv->otg_mode)
|
||||
return 0;
|
||||
|
||||
if (mode == USB_DR_MODE_HOST)
|
||||
debug("%s: switching to Host Mode\n", __func__);
|
||||
else
|
||||
debug("%s: switching to Device Mode\n", __func__);
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||
if (priv->vbus_supply) {
|
||||
int ret = regulator_set_enable(priv->vbus_supply,
|
||||
(mode == USB_DR_MODE_PERIPHERAL));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
priv->otg_phy_mode = mode;
|
||||
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
|
||||
|
||||
dwc3_meson_g12a_usb_init_mode(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = generic_phy_get_by_name(priv->dev, phy_names[i],
|
||||
&priv->phys[i]);
|
||||
if (ret == -ENOENT)
|
||||
continue;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (i == USB3_HOST_PHY)
|
||||
priv->usb3_ports++;
|
||||
else
|
||||
priv->usb2_ports++;
|
||||
}
|
||||
|
||||
debug("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
|
||||
debug("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_reset_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = reset_get_by_index(priv->dev, 0, &priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_assert(&priv->reset);
|
||||
udelay(1);
|
||||
ret |= reset_deassert(&priv->reset);
|
||||
if (ret) {
|
||||
reset_free(&priv->reset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_clk_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_get_by_index(priv->dev, 0, &priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
ret = clk_enable(&priv->clk);
|
||||
if (ret) {
|
||||
clk_free(&priv->clk);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_probe(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
|
||||
int ret, i;
|
||||
|
||||
priv->dev = dev;
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_meson_g12a_clk_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_meson_g12a_reset_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_meson_g12a_get_phys(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||
ret = device_get_supply_regulator(dev, "vbus-supply",
|
||||
&priv->vbus_supply);
|
||||
if (ret && ret != -ENOENT) {
|
||||
pr_err("Failed to get PHY regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (priv->vbus_supply) {
|
||||
ret = regulator_set_enable(priv->vbus_supply, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
|
||||
|
||||
ret = dwc3_meson_g12a_usb_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
ret = generic_phy_init(&priv->phys[i]);
|
||||
if (ret)
|
||||
goto err_phy_init;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_phy_init:
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
generic_phy_exit(&priv->phys[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_remove(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
|
||||
int i;
|
||||
|
||||
reset_release_all(&priv->reset, 1);
|
||||
|
||||
clk_release_all(&priv->clk, 1);
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
generic_phy_exit(&priv->phys[i]);
|
||||
}
|
||||
|
||||
return dm_scan_fdt_dev(dev);
|
||||
}
|
||||
|
||||
static const struct udevice_id dwc3_meson_g12a_ids[] = {
|
||||
{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(dwc3_generic_wrapper) = {
|
||||
.name = "dwc3-meson-g12a",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = dwc3_meson_g12a_ids,
|
||||
.probe = dwc3_meson_g12a_probe,
|
||||
.remove = dwc3_meson_g12a_remove,
|
||||
.platdata_auto_alloc_size = sizeof(struct dwc3_meson_g12a),
|
||||
|
||||
};
|
||||
@@ -0,0 +1,451 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/**
|
||||
* dwc3-omap.c - OMAP Specific Glue layer
|
||||
*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Authors: Felipe Balbi <balbi@ti.com>,
|
||||
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
*
|
||||
* Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/dwc3-omap.c) and ported
|
||||
* to uboot.
|
||||
*
|
||||
* commit 7ee2566ff5 : usb: dwc3: dwc3-omap: get rid of ->prepare()/->complete()
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm.h>
|
||||
#include <dwc3-omap-uboot.h>
|
||||
#include <linux/usb/dwc3-omap.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include "linux-compat.h"
|
||||
|
||||
/*
|
||||
* All these registers belong to OMAP's Wrapper around the
|
||||
* DesignWare USB3 Core.
|
||||
*/
|
||||
|
||||
#define USBOTGSS_REVISION 0x0000
|
||||
#define USBOTGSS_SYSCONFIG 0x0010
|
||||
#define USBOTGSS_IRQ_EOI 0x0020
|
||||
#define USBOTGSS_EOI_OFFSET 0x0008
|
||||
#define USBOTGSS_IRQSTATUS_RAW_0 0x0024
|
||||
#define USBOTGSS_IRQSTATUS_0 0x0028
|
||||
#define USBOTGSS_IRQENABLE_SET_0 0x002c
|
||||
#define USBOTGSS_IRQENABLE_CLR_0 0x0030
|
||||
#define USBOTGSS_IRQ0_OFFSET 0x0004
|
||||
#define USBOTGSS_IRQSTATUS_RAW_1 0x0030
|
||||
#define USBOTGSS_IRQSTATUS_1 0x0034
|
||||
#define USBOTGSS_IRQENABLE_SET_1 0x0038
|
||||
#define USBOTGSS_IRQENABLE_CLR_1 0x003c
|
||||
#define USBOTGSS_IRQSTATUS_RAW_2 0x0040
|
||||
#define USBOTGSS_IRQSTATUS_2 0x0044
|
||||
#define USBOTGSS_IRQENABLE_SET_2 0x0048
|
||||
#define USBOTGSS_IRQENABLE_CLR_2 0x004c
|
||||
#define USBOTGSS_IRQSTATUS_RAW_3 0x0050
|
||||
#define USBOTGSS_IRQSTATUS_3 0x0054
|
||||
#define USBOTGSS_IRQENABLE_SET_3 0x0058
|
||||
#define USBOTGSS_IRQENABLE_CLR_3 0x005c
|
||||
#define USBOTGSS_IRQSTATUS_EOI_MISC 0x0030
|
||||
#define USBOTGSS_IRQSTATUS_RAW_MISC 0x0034
|
||||
#define USBOTGSS_IRQSTATUS_MISC 0x0038
|
||||
#define USBOTGSS_IRQENABLE_SET_MISC 0x003c
|
||||
#define USBOTGSS_IRQENABLE_CLR_MISC 0x0040
|
||||
#define USBOTGSS_IRQMISC_OFFSET 0x03fc
|
||||
#define USBOTGSS_UTMI_OTG_CTRL 0x0080
|
||||
#define USBOTGSS_UTMI_OTG_STATUS 0x0084
|
||||
#define USBOTGSS_UTMI_OTG_OFFSET 0x0480
|
||||
#define USBOTGSS_TXFIFO_DEPTH 0x0508
|
||||
#define USBOTGSS_RXFIFO_DEPTH 0x050c
|
||||
#define USBOTGSS_MMRAM_OFFSET 0x0100
|
||||
#define USBOTGSS_FLADJ 0x0104
|
||||
#define USBOTGSS_DEBUG_CFG 0x0108
|
||||
#define USBOTGSS_DEBUG_DATA 0x010c
|
||||
#define USBOTGSS_DEV_EBC_EN 0x0110
|
||||
#define USBOTGSS_DEBUG_OFFSET 0x0600
|
||||
|
||||
/* SYSCONFIG REGISTER */
|
||||
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
|
||||
|
||||
/* IRQ_EOI REGISTER */
|
||||
#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0)
|
||||
|
||||
/* IRQS0 BITS */
|
||||
#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0)
|
||||
|
||||
/* IRQMISC BITS */
|
||||
#define USBOTGSS_IRQMISC_DMADISABLECLR (1 << 17)
|
||||
#define USBOTGSS_IRQMISC_OEVT (1 << 16)
|
||||
#define USBOTGSS_IRQMISC_DRVVBUS_RISE (1 << 13)
|
||||
#define USBOTGSS_IRQMISC_CHRGVBUS_RISE (1 << 12)
|
||||
#define USBOTGSS_IRQMISC_DISCHRGVBUS_RISE (1 << 11)
|
||||
#define USBOTGSS_IRQMISC_IDPULLUP_RISE (1 << 8)
|
||||
#define USBOTGSS_IRQMISC_DRVVBUS_FALL (1 << 5)
|
||||
#define USBOTGSS_IRQMISC_CHRGVBUS_FALL (1 << 4)
|
||||
#define USBOTGSS_IRQMISC_DISCHRGVBUS_FALL (1 << 3)
|
||||
#define USBOTGSS_IRQMISC_IDPULLUP_FALL (1 << 0)
|
||||
|
||||
#define USBOTGSS_INTERRUPTS (USBOTGSS_IRQMISC_OEVT | \
|
||||
USBOTGSS_IRQMISC_DRVVBUS_RISE | \
|
||||
USBOTGSS_IRQMISC_CHRGVBUS_RISE | \
|
||||
USBOTGSS_IRQMISC_DISCHRGVBUS_RISE | \
|
||||
USBOTGSS_IRQMISC_IDPULLUP_RISE | \
|
||||
USBOTGSS_IRQMISC_DRVVBUS_FALL | \
|
||||
USBOTGSS_IRQMISC_CHRGVBUS_FALL | \
|
||||
USBOTGSS_IRQMISC_DISCHRGVBUS_FALL | \
|
||||
USBOTGSS_IRQMISC_IDPULLUP_FALL)
|
||||
|
||||
/* UTMI_OTG_CTRL REGISTER */
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS (1 << 4)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS (1 << 3)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP (1 << 0)
|
||||
|
||||
/* UTMI_OTG_STATUS REGISTER */
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE (1 << 31)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT (1 << 9)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_IDDIG (1 << 4)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_SESSEND (1 << 3)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID (1 << 2)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1)
|
||||
|
||||
struct dwc3_omap {
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *base;
|
||||
|
||||
u32 utmi_otg_status;
|
||||
u32 utmi_otg_offset;
|
||||
u32 irqmisc_offset;
|
||||
u32 irq_eoi_offset;
|
||||
u32 debug_offset;
|
||||
u32 irq0_offset;
|
||||
|
||||
u32 dma_status:1;
|
||||
struct list_head list;
|
||||
u32 index;
|
||||
};
|
||||
|
||||
static LIST_HEAD(dwc3_omap_list);
|
||||
|
||||
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
static u32 dwc3_omap_read_utmi_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS +
|
||||
omap->utmi_otg_offset);
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_utmi_status(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS +
|
||||
omap->utmi_otg_offset, value);
|
||||
|
||||
}
|
||||
|
||||
static u32 dwc3_omap_read_irq0_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0 -
|
||||
omap->irq0_offset);
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irq0_status(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0 -
|
||||
omap->irq0_offset, value);
|
||||
|
||||
}
|
||||
|
||||
static u32 dwc3_omap_read_irqmisc_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_MISC +
|
||||
omap->irqmisc_offset);
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irqmisc_status(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_MISC +
|
||||
omap->irqmisc_offset, value);
|
||||
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irqmisc_set(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_MISC +
|
||||
omap->irqmisc_offset, value);
|
||||
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0 -
|
||||
omap->irq0_offset, value);
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irqmisc_clr(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_MISC +
|
||||
omap->irqmisc_offset, value);
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irq0_clr(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_0 -
|
||||
omap->irq0_offset, value);
|
||||
}
|
||||
|
||||
static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
|
||||
enum omap_dwc3_vbus_id_status status)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
switch (status) {
|
||||
case OMAP_DWC3_ID_GROUND:
|
||||
dev_dbg(omap->dev, "ID GND\n");
|
||||
|
||||
val = dwc3_omap_read_utmi_status(omap);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_SESSEND);
|
||||
val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
|
||||
dwc3_omap_write_utmi_status(omap, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_VBUS_VALID:
|
||||
dev_dbg(omap->dev, "VBUS Connect\n");
|
||||
|
||||
val = dwc3_omap_read_utmi_status(omap);
|
||||
val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND;
|
||||
val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
|
||||
dwc3_omap_write_utmi_status(omap, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_ID_FLOAT:
|
||||
case OMAP_DWC3_VBUS_OFF:
|
||||
dev_dbg(omap->dev, "VBUS Disconnect\n");
|
||||
|
||||
val = dwc3_omap_read_utmi_status(omap);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT);
|
||||
val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND
|
||||
| USBOTGSS_UTMI_OTG_STATUS_IDDIG;
|
||||
dwc3_omap_write_utmi_status(omap, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(omap->dev, "invalid state\n");
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
{
|
||||
struct dwc3_omap *omap = _omap;
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_omap_read_irqmisc_status(omap);
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_DMADISABLECLR) {
|
||||
dev_dbg(omap->dev, "DMA Disable was Cleared\n");
|
||||
omap->dma_status = false;
|
||||
}
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_OEVT)
|
||||
dev_dbg(omap->dev, "OTG Event\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_DRVVBUS_RISE)
|
||||
dev_dbg(omap->dev, "DRVVBUS Rise\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_CHRGVBUS_RISE)
|
||||
dev_dbg(omap->dev, "CHRGVBUS Rise\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_DISCHRGVBUS_RISE)
|
||||
dev_dbg(omap->dev, "DISCHRGVBUS Rise\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_IDPULLUP_RISE)
|
||||
dev_dbg(omap->dev, "IDPULLUP Rise\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_DRVVBUS_FALL)
|
||||
dev_dbg(omap->dev, "DRVVBUS Fall\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_CHRGVBUS_FALL)
|
||||
dev_dbg(omap->dev, "CHRGVBUS Fall\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_DISCHRGVBUS_FALL)
|
||||
dev_dbg(omap->dev, "DISCHRGVBUS Fall\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_IDPULLUP_FALL)
|
||||
dev_dbg(omap->dev, "IDPULLUP Fall\n");
|
||||
|
||||
dwc3_omap_write_irqmisc_status(omap, reg);
|
||||
|
||||
reg = dwc3_omap_read_irq0_status(omap);
|
||||
|
||||
dwc3_omap_write_irq0_status(omap, reg);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
|
||||
{
|
||||
/* enable all IRQs */
|
||||
dwc3_omap_write_irq0_set(omap, USBOTGSS_IRQO_COREIRQ_ST);
|
||||
|
||||
dwc3_omap_write_irqmisc_set(omap, USBOTGSS_INTERRUPTS);
|
||||
}
|
||||
|
||||
static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
|
||||
{
|
||||
/* disable all IRQs */
|
||||
dwc3_omap_write_irq0_clr(omap, USBOTGSS_IRQO_COREIRQ_ST);
|
||||
|
||||
dwc3_omap_write_irqmisc_clr(omap, USBOTGSS_INTERRUPTS);
|
||||
}
|
||||
|
||||
static void dwc3_omap_map_offset(struct dwc3_omap *omap)
|
||||
{
|
||||
/*
|
||||
* Differentiate between OMAP5 and AM437x.
|
||||
*
|
||||
* For OMAP5(ES2.0) and AM437x wrapper revision is same, even
|
||||
* though there are changes in wrapper register offsets.
|
||||
*
|
||||
* Using dt compatible to differentiate AM437x.
|
||||
*/
|
||||
#ifdef CONFIG_AM43XX
|
||||
omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
|
||||
omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
|
||||
omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
|
||||
omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
|
||||
omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap, int utmi_mode)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_omap_read_utmi_status(omap);
|
||||
|
||||
switch (utmi_mode) {
|
||||
case DWC3_OMAP_UTMI_MODE_SW:
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
case DWC3_OMAP_UTMI_MODE_HW:
|
||||
reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode);
|
||||
}
|
||||
|
||||
dwc3_omap_write_utmi_status(omap, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_omap_uboot_init - dwc3 omap uboot initialization code
|
||||
* @dev: struct dwc3_omap_device containing initialization data
|
||||
*
|
||||
* Entry point for dwc3 omap driver (equivalent to dwc3_omap_probe in linux
|
||||
* kernel driver). Pointer to dwc3_omap_device should be passed containing
|
||||
* base address and other initialization data. Returns '0' on success and
|
||||
* a negative value on failure.
|
||||
*
|
||||
* Generally called from board_usb_init() implemented in board file.
|
||||
*/
|
||||
int dwc3_omap_uboot_init(struct dwc3_omap_device *omap_dev)
|
||||
{
|
||||
u32 reg;
|
||||
struct device *dev = NULL;
|
||||
struct dwc3_omap *omap;
|
||||
|
||||
omap = devm_kzalloc((struct udevice *)dev, sizeof(*omap), GFP_KERNEL);
|
||||
if (!omap)
|
||||
return -ENOMEM;
|
||||
|
||||
omap->base = omap_dev->base;
|
||||
omap->index = omap_dev->index;
|
||||
|
||||
dwc3_omap_map_offset(omap);
|
||||
dwc3_omap_set_utmi_mode(omap, omap_dev->utmi_mode);
|
||||
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
|
||||
|
||||
dwc3_omap_set_mailbox(omap, omap_dev->vbus_id_status);
|
||||
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
list_add_tail(&omap->list, &dwc3_omap_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_omap_uboot_exit - dwc3 omap uboot cleanup code
|
||||
* @index: index of this controller
|
||||
*
|
||||
* Performs cleanup of memory allocated in dwc3_omap_uboot_init
|
||||
* (equivalent to dwc3_omap_remove in linux). index of _this_ controller
|
||||
* should be passed and should match with the index passed in
|
||||
* dwc3_omap_device during init.
|
||||
*
|
||||
* Generally called from board file.
|
||||
*/
|
||||
void dwc3_omap_uboot_exit(int index)
|
||||
{
|
||||
struct dwc3_omap *omap = NULL;
|
||||
|
||||
list_for_each_entry(omap, &dwc3_omap_list, list) {
|
||||
if (omap->index != index)
|
||||
continue;
|
||||
|
||||
dwc3_omap_disable_irqs(omap);
|
||||
list_del(&omap->list);
|
||||
kfree(omap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_omap_uboot_interrupt_status - check the status of interrupt
|
||||
* @index: index of this controller
|
||||
*
|
||||
* Checks the status of interrupts and returns true if an interrupt
|
||||
* is detected or false otherwise.
|
||||
*
|
||||
* Generally called from board file.
|
||||
*/
|
||||
int dwc3_omap_uboot_interrupt_status(int index)
|
||||
{
|
||||
struct dwc3_omap *omap = NULL;
|
||||
|
||||
list_for_each_entry(omap, &dwc3_omap_list, list)
|
||||
if (omap->index == index)
|
||||
return dwc3_omap_interrupt(-1, omap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("platform:omap-dwc3");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer");
|
||||
@@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* UniPhier Specific Glue Layer for DWC3
|
||||
*
|
||||
* Copyright (C) 2016-2017 Socionext Inc.
|
||||
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#define UNIPHIER_PRO4_DWC3_RESET 0x40
|
||||
#define UNIPHIER_PRO4_DWC3_RESET_XIOMMU BIT(5)
|
||||
#define UNIPHIER_PRO4_DWC3_RESET_XLINK BIT(4)
|
||||
#define UNIPHIER_PRO4_DWC3_RESET_PHY_SS BIT(2)
|
||||
|
||||
#define UNIPHIER_PRO5_DWC3_RESET 0x00
|
||||
#define UNIPHIER_PRO5_DWC3_RESET_PHY_S1 BIT(17)
|
||||
#define UNIPHIER_PRO5_DWC3_RESET_PHY_S0 BIT(16)
|
||||
#define UNIPHIER_PRO5_DWC3_RESET_XLINK BIT(15)
|
||||
#define UNIPHIER_PRO5_DWC3_RESET_XIOMMU BIT(14)
|
||||
|
||||
#define UNIPHIER_PXS2_DWC3_RESET 0x00
|
||||
#define UNIPHIER_PXS2_DWC3_RESET_XLINK BIT(15)
|
||||
|
||||
static int uniphier_pro4_dwc3_init(void __iomem *regs)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl(regs + UNIPHIER_PRO4_DWC3_RESET);
|
||||
tmp &= ~UNIPHIER_PRO4_DWC3_RESET_PHY_SS;
|
||||
tmp |= UNIPHIER_PRO4_DWC3_RESET_XIOMMU | UNIPHIER_PRO4_DWC3_RESET_XLINK;
|
||||
writel(tmp, regs + UNIPHIER_PRO4_DWC3_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_pro5_dwc3_init(void __iomem *regs)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl(regs + UNIPHIER_PRO5_DWC3_RESET);
|
||||
tmp &= ~(UNIPHIER_PRO5_DWC3_RESET_PHY_S1 |
|
||||
UNIPHIER_PRO5_DWC3_RESET_PHY_S0);
|
||||
tmp |= UNIPHIER_PRO5_DWC3_RESET_XLINK | UNIPHIER_PRO5_DWC3_RESET_XIOMMU;
|
||||
writel(tmp, regs + UNIPHIER_PRO5_DWC3_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_pxs2_dwc3_init(void __iomem *regs)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl(regs + UNIPHIER_PXS2_DWC3_RESET);
|
||||
tmp |= UNIPHIER_PXS2_DWC3_RESET_XLINK;
|
||||
writel(tmp, regs + UNIPHIER_PXS2_DWC3_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_dwc3_probe(struct udevice *dev)
|
||||
{
|
||||
fdt_addr_t base;
|
||||
void __iomem *regs;
|
||||
int (*init)(void __iomem *regs);
|
||||
int ret;
|
||||
|
||||
base = devfdt_get_addr(dev);
|
||||
if (base == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
regs = ioremap(base, SZ_32K);
|
||||
if (!regs)
|
||||
return -ENOMEM;
|
||||
|
||||
init = (typeof(init))dev_get_driver_data(dev);
|
||||
ret = init(regs);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to init glue layer\n");
|
||||
|
||||
iounmap(regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct udevice_id uniphier_dwc3_match[] = {
|
||||
{
|
||||
.compatible = "socionext,uniphier-pro4-dwc3",
|
||||
.data = (ulong)uniphier_pro4_dwc3_init,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,uniphier-pro5-dwc3",
|
||||
.data = (ulong)uniphier_pro5_dwc3_init,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,uniphier-pxs2-dwc3",
|
||||
.data = (ulong)uniphier_pxs2_dwc3_init,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,uniphier-ld20-dwc3",
|
||||
.data = (ulong)uniphier_pxs2_dwc3_init,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,uniphier-pxs3-dwc3",
|
||||
.data = (ulong)uniphier_pxs2_dwc3_init,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(usb_xhci) = {
|
||||
.name = "uniphier-dwc3",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = uniphier_dwc3_match,
|
||||
.probe = uniphier_dwc3_probe,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,107 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
* gadget.h - DesignWare USB3 DRD Gadget Header
|
||||
*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Authors: Felipe Balbi <balbi@ti.com>,
|
||||
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
*
|
||||
* Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.h) and ported
|
||||
* to uboot.
|
||||
*
|
||||
* commit 7a60855972 : usb: dwc3: gadget: fix set_halt() bug with pending
|
||||
transfers
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_USB_DWC3_GADGET_H
|
||||
#define __DRIVERS_USB_DWC3_GADGET_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include "io.h"
|
||||
|
||||
struct dwc3;
|
||||
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
|
||||
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
|
||||
|
||||
/* DEPCFG parameter 1 */
|
||||
#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
|
||||
#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
|
||||
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
|
||||
#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
|
||||
#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
|
||||
#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
|
||||
#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16)
|
||||
#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
|
||||
#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25)
|
||||
#define DWC3_DEPCFG_BULK_BASED (1 << 30)
|
||||
#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
|
||||
|
||||
/* DEPCFG parameter 0 */
|
||||
#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1)
|
||||
#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3)
|
||||
#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
|
||||
#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
|
||||
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
|
||||
/* This applies for core versions earlier than 1.94a */
|
||||
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
|
||||
/* These apply for core versions 1.94a and later */
|
||||
#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
|
||||
#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30)
|
||||
#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30)
|
||||
|
||||
/* DEPXFERCFG parameter 0 */
|
||||
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
|
||||
|
||||
static inline struct dwc3_request *next_request(struct list_head *list)
|
||||
{
|
||||
if (list_empty(list))
|
||||
return NULL;
|
||||
|
||||
return list_first_entry(list, struct dwc3_request, list);
|
||||
}
|
||||
|
||||
static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
|
||||
{
|
||||
struct dwc3_ep *dep = req->dep;
|
||||
|
||||
req->queued = true;
|
||||
list_move_tail(&req->list, &dep->req_queued);
|
||||
}
|
||||
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
int status);
|
||||
|
||||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event);
|
||||
void dwc3_ep0_out_start(struct dwc3 *dwc);
|
||||
int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
||||
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
||||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags);
|
||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
|
||||
void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
* @dwc: DesignWare USB3 Pointer
|
||||
* @number: DWC endpoint number
|
||||
*
|
||||
* Caller should take care of locking
|
||||
*/
|
||||
static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number)
|
||||
{
|
||||
u32 res_id;
|
||||
|
||||
res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number));
|
||||
|
||||
return DWC3_DEPCMD_GET_RSC_IDX(res_id);
|
||||
}
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
|
||||
@@ -0,0 +1,55 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
* io.h - DesignWare USB3 DRD IO Header
|
||||
*
|
||||
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Authors: Felipe Balbi <balbi@ti.com>,
|
||||
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
*
|
||||
* Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/io.h) and ported
|
||||
* to uboot.
|
||||
*
|
||||
* commit 2c4cbe6e5a : usb: dwc3: add tracepoints to aid debugging
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_USB_DWC3_IO_H
|
||||
#define __DRIVERS_USB_DWC3_IO_H
|
||||
|
||||
#include <cpu_func.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
|
||||
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
unsigned long offs = offset - DWC3_GLOBALS_REGS_START;
|
||||
u32 value;
|
||||
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
value = readl(base + offs);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
unsigned long offs = offset - DWC3_GLOBALS_REGS_START;
|
||||
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
writel(value, base + offs);
|
||||
}
|
||||
|
||||
static inline void dwc3_flush_cache(uintptr_t addr, int length)
|
||||
{
|
||||
flush_dcache_range(addr, addr + ROUND(length, CACHELINE_SIZE));
|
||||
}
|
||||
#endif /* __DRIVERS_USB_DWC3_IO_H */
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
* linux-compat.h - DesignWare USB3 Linux Compatibiltiy Adapter Header
|
||||
*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Authors: Kishon Vijay Abraham I <kishon@ti.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DWC3_LINUX_COMPAT__
|
||||
#define __DWC3_LINUX_COMPAT__
|
||||
|
||||
#define dev_WARN(dev, format, arg...) debug(format, ##arg)
|
||||
|
||||
static inline size_t strlcat(char *dest, const char *src, size_t n)
|
||||
{
|
||||
strcat(dest, src);
|
||||
return strlen(dest) + strlen(src);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/**
|
||||
* samsung_usb_phy.c - DesignWare USB3 (DWC3) PHY handling file
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics
|
||||
*
|
||||
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/power.h>
|
||||
#include <asm/arch/xhci-exynos.h>
|
||||
|
||||
void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Reset USB 3.0 PHY */
|
||||
writel(0x0, &phy->phy_reg0);
|
||||
|
||||
clrbits_le32(&phy->phy_param0,
|
||||
/* Select PHY CLK source */
|
||||
PHYPARAM0_REF_USE_PAD |
|
||||
/* Set Loss-of-Signal Detector sensitivity */
|
||||
PHYPARAM0_REF_LOSLEVEL_MASK);
|
||||
setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL);
|
||||
|
||||
|
||||
writel(0x0, &phy->phy_resume);
|
||||
|
||||
/*
|
||||
* Setting the Frame length Adj value[6:1] to default 0x20
|
||||
* See xHCI 1.0 spec, 5.2.4
|
||||
*/
|
||||
setbits_le32(&phy->link_system,
|
||||
LINKSYSTEM_XHCI_VERSION_CONTROL |
|
||||
LINKSYSTEM_FLADJ(0x20));
|
||||
|
||||
/* Set Tx De-Emphasis level */
|
||||
clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK);
|
||||
setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH);
|
||||
|
||||
setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL);
|
||||
|
||||
/* PHYTEST POWERDOWN Control */
|
||||
clrbits_le32(&phy->phy_test,
|
||||
PHYTEST_POWERDOWN_SSP |
|
||||
PHYTEST_POWERDOWN_HSP);
|
||||
|
||||
/* UTMI Power Control */
|
||||
writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi);
|
||||
|
||||
/* Use core clock from main PLL */
|
||||
reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
|
||||
/* Default 24Mhz crystal clock */
|
||||
PHYCLKRST_FSEL(FSEL_CLKSEL_24M) |
|
||||
PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
|
||||
PHYCLKRST_SSC_REFCLKSEL(0) |
|
||||
/* Force PortReset of PHY */
|
||||
PHYCLKRST_PORTRESET |
|
||||
/* Digital power supply in normal operating mode */
|
||||
PHYCLKRST_RETENABLEN |
|
||||
/* Enable ref clock for SS function */
|
||||
PHYCLKRST_REF_SSP_EN |
|
||||
/* Enable spread spectrum */
|
||||
PHYCLKRST_SSC_EN |
|
||||
/* Power down HS Bias and PLL blocks in suspend mode */
|
||||
PHYCLKRST_COMMONONN;
|
||||
|
||||
writel(reg, &phy->phy_clk_rst);
|
||||
|
||||
/* giving time to Phy clock to settle before resetting */
|
||||
udelay(10);
|
||||
|
||||
reg &= ~PHYCLKRST_PORTRESET;
|
||||
writel(reg, &phy->phy_clk_rst);
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/**
|
||||
* ti_usb_phy.c - USB3 and USB3 PHY programming for dwc3
|
||||
*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Kishon Vijay Abraham I <kishon@ti.com>
|
||||
*
|
||||
* Taken from Linux Kernel v3.16 (drivers/phy/phy-ti-pipe3.c and
|
||||
* drivers/phy/phy-omap-usb2.c) and ported to uboot.
|
||||
*
|
||||
* "commit 56042e : phy: ti-pipe3: Fix suspend/resume and module reload" for
|
||||
* phy-ti-pipe3.c
|
||||
*
|
||||
* "commit eb82a3 : phy: omap-usb2: Balance pm_runtime_enable() on probe failure
|
||||
* and remove" for phy-omap-usb2.c
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <ti-usb-phy-uboot.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#include <dm.h>
|
||||
|
||||
#include "linux-compat.h"
|
||||
|
||||
#define PLL_STATUS 0x00000004
|
||||
#define PLL_GO 0x00000008
|
||||
#define PLL_CONFIGURATION1 0x0000000C
|
||||
#define PLL_CONFIGURATION2 0x00000010
|
||||
#define PLL_CONFIGURATION3 0x00000014
|
||||
#define PLL_CONFIGURATION4 0x00000020
|
||||
|
||||
#define PLL_REGM_MASK 0x001FFE00
|
||||
#define PLL_REGM_SHIFT 0x9
|
||||
#define PLL_REGM_F_MASK 0x0003FFFF
|
||||
#define PLL_REGM_F_SHIFT 0x0
|
||||
#define PLL_REGN_MASK 0x000001FE
|
||||
#define PLL_REGN_SHIFT 0x1
|
||||
#define PLL_SELFREQDCO_MASK 0x0000000E
|
||||
#define PLL_SELFREQDCO_SHIFT 0x1
|
||||
#define PLL_SD_MASK 0x0003FC00
|
||||
#define PLL_SD_SHIFT 10
|
||||
#define SET_PLL_GO 0x1
|
||||
#define PLL_LDOPWDN BIT(15)
|
||||
#define PLL_TICOPWDN BIT(16)
|
||||
#define PLL_LOCK 0x2
|
||||
#define PLL_IDLE 0x1
|
||||
|
||||
#define OMAP_CTRL_DEV_PHY_PD BIT(0)
|
||||
#define OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000
|
||||
#define OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_SHIFT 0xE
|
||||
|
||||
#define OMAP_CTRL_USB3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000
|
||||
#define OMAP_CTRL_USB3_PHY_PWRCTL_CLK_FREQ_SHIFT 0x16
|
||||
|
||||
#define OMAP_CTRL_USB3_PHY_TX_RX_POWERON 0x3
|
||||
#define OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF 0x0
|
||||
|
||||
#define OMAP_CTRL_USB2_PHY_PD BIT(28)
|
||||
|
||||
#define AM437X_CTRL_USB2_PHY_PD BIT(0)
|
||||
#define AM437X_CTRL_USB2_OTG_PD BIT(1)
|
||||
#define AM437X_CTRL_USB2_OTGVDET_EN BIT(19)
|
||||
#define AM437X_CTRL_USB2_OTGSESSEND_EN BIT(20)
|
||||
|
||||
static LIST_HEAD(ti_usb_phy_list);
|
||||
typedef unsigned int u32;
|
||||
|
||||
struct usb3_dpll_params {
|
||||
u16 m;
|
||||
u8 n;
|
||||
u8 freq:3;
|
||||
u8 sd;
|
||||
u32 mf;
|
||||
};
|
||||
|
||||
struct usb3_dpll_map {
|
||||
unsigned long rate;
|
||||
struct usb3_dpll_params params;
|
||||
struct usb3_dpll_map *dpll_map;
|
||||
};
|
||||
|
||||
struct ti_usb_phy {
|
||||
void __iomem *pll_ctrl_base;
|
||||
void __iomem *usb2_phy_power;
|
||||
void __iomem *usb3_phy_power;
|
||||
struct usb3_dpll_map *dpll_map;
|
||||
struct list_head list;
|
||||
int index;
|
||||
};
|
||||
|
||||
static struct usb3_dpll_map dpll_map_usb[] = {
|
||||
{12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */
|
||||
{16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */
|
||||
{19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */
|
||||
{20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */
|
||||
{26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */
|
||||
{38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */
|
||||
{ }, /* Terminator */
|
||||
};
|
||||
|
||||
static inline unsigned int ti_usb3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static inline void ti_usb3_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_AM43XX
|
||||
static struct usb3_dpll_params *ti_usb3_get_dpll_params(struct ti_usb_phy *phy)
|
||||
{
|
||||
unsigned long rate;
|
||||
struct usb3_dpll_map *dpll_map = phy->dpll_map;
|
||||
|
||||
rate = get_sys_clk_freq();
|
||||
|
||||
for (; dpll_map->rate; dpll_map++) {
|
||||
if (rate == dpll_map->rate)
|
||||
return &dpll_map->params;
|
||||
}
|
||||
|
||||
dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ti_usb3_dpll_wait_lock(struct ti_usb_phy *phy)
|
||||
{
|
||||
u32 val;
|
||||
do {
|
||||
val = ti_usb3_readl(phy->pll_ctrl_base, PLL_STATUS);
|
||||
if (val & PLL_LOCK)
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_usb3_dpll_program(struct ti_usb_phy *phy)
|
||||
{
|
||||
u32 val;
|
||||
struct usb3_dpll_params *dpll_params;
|
||||
|
||||
if (!phy->pll_ctrl_base)
|
||||
return -EINVAL;
|
||||
|
||||
dpll_params = ti_usb3_get_dpll_params(phy);
|
||||
if (!dpll_params)
|
||||
return -EINVAL;
|
||||
|
||||
val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
|
||||
val &= ~PLL_REGN_MASK;
|
||||
val |= dpll_params->n << PLL_REGN_SHIFT;
|
||||
ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
|
||||
|
||||
val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
|
||||
val &= ~PLL_SELFREQDCO_MASK;
|
||||
val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
|
||||
ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
|
||||
|
||||
val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
|
||||
val &= ~PLL_REGM_MASK;
|
||||
val |= dpll_params->m << PLL_REGM_SHIFT;
|
||||
ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
|
||||
|
||||
val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
|
||||
val &= ~PLL_REGM_F_MASK;
|
||||
val |= dpll_params->mf << PLL_REGM_F_SHIFT;
|
||||
ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
|
||||
|
||||
val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
|
||||
val &= ~PLL_SD_MASK;
|
||||
val |= dpll_params->sd << PLL_SD_SHIFT;
|
||||
ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
|
||||
|
||||
ti_usb3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
|
||||
|
||||
return ti_usb3_dpll_wait_lock(phy);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ti_usb2_phy_power(struct ti_usb_phy *phy, int on)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(phy->usb2_phy_power);
|
||||
|
||||
if (on) {
|
||||
#if defined(CONFIG_DRA7XX)
|
||||
if (phy->index == 1)
|
||||
val &= ~OMAP_CTRL_USB2_PHY_PD;
|
||||
else
|
||||
val &= ~OMAP_CTRL_DEV_PHY_PD;
|
||||
#elif defined(CONFIG_AM43XX)
|
||||
val &= ~(AM437X_CTRL_USB2_PHY_PD |
|
||||
AM437X_CTRL_USB2_OTG_PD);
|
||||
val |= (AM437X_CTRL_USB2_OTGVDET_EN |
|
||||
AM437X_CTRL_USB2_OTGSESSEND_EN);
|
||||
#endif
|
||||
} else {
|
||||
#if defined(CONFIG_DRA7XX)
|
||||
if (phy->index == 1)
|
||||
val |= OMAP_CTRL_USB2_PHY_PD;
|
||||
else
|
||||
val |= OMAP_CTRL_DEV_PHY_PD;
|
||||
|
||||
#elif defined(CONFIG_AM43XX)
|
||||
val &= ~(AM437X_CTRL_USB2_OTGVDET_EN |
|
||||
AM437X_CTRL_USB2_OTGSESSEND_EN);
|
||||
val |= (AM437X_CTRL_USB2_PHY_PD |
|
||||
AM437X_CTRL_USB2_OTG_PD);
|
||||
#endif
|
||||
}
|
||||
writel(val, phy->usb2_phy_power);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_AM43XX
|
||||
void ti_usb3_phy_power(struct ti_usb_phy *phy, int on)
|
||||
{
|
||||
u32 val;
|
||||
u32 rate;
|
||||
rate = get_sys_clk_freq();
|
||||
rate = rate/1000000;
|
||||
|
||||
if (!phy->usb3_phy_power)
|
||||
return;
|
||||
|
||||
val = readl(phy->usb3_phy_power);
|
||||
if (on) {
|
||||
val &= ~(OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_MASK |
|
||||
OMAP_CTRL_USB3_PHY_PWRCTL_CLK_FREQ_MASK);
|
||||
val |= (OMAP_CTRL_USB3_PHY_TX_RX_POWERON) <<
|
||||
OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_SHIFT;
|
||||
val |= rate <<
|
||||
OMAP_CTRL_USB3_PHY_PWRCTL_CLK_FREQ_SHIFT;
|
||||
} else {
|
||||
val &= ~OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_MASK;
|
||||
val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
|
||||
OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_SHIFT;
|
||||
}
|
||||
writel(val, phy->usb3_phy_power);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ti_usb_phy_uboot_init - usb phy uboot initialization code
|
||||
* @dev: struct ti_usb_phy_device containing initialization data
|
||||
*
|
||||
* Entry point for ti usb phy driver. This driver handles initialization
|
||||
* of both usb2 phy and usb3 phy. Pointer to ti_usb_phy_device should be
|
||||
* passed containing base address and other initialization data.
|
||||
* Returns '0' on success and a negative value on failure.
|
||||
*
|
||||
* Generally called from board_usb_init() implemented in board file.
|
||||
*/
|
||||
int ti_usb_phy_uboot_init(struct ti_usb_phy_device *dev)
|
||||
{
|
||||
struct ti_usb_phy *phy;
|
||||
|
||||
phy = devm_kzalloc(NULL, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
dev_err(NULL, "unable to alloc mem for TI USB3 PHY\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
phy->dpll_map = dpll_map_usb;
|
||||
phy->index = dev->index;
|
||||
phy->pll_ctrl_base = dev->pll_ctrl_base;
|
||||
phy->usb2_phy_power = dev->usb2_phy_power;
|
||||
phy->usb3_phy_power = dev->usb3_phy_power;
|
||||
|
||||
#ifndef CONFIG_AM43XX
|
||||
ti_usb3_dpll_program(phy);
|
||||
ti_usb3_phy_power(phy, 1);
|
||||
#endif
|
||||
ti_usb2_phy_power(phy, 1);
|
||||
mdelay(150);
|
||||
list_add_tail(&phy->list, &ti_usb_phy_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_usb_phy_uboot_exit - usb phy uboot cleanup code
|
||||
* @index: index of this controller
|
||||
*
|
||||
* Performs cleanup of memory allocated in ti_usb_phy_uboot_init.
|
||||
* index of _this_ controller should be passed and should match with
|
||||
* the index passed in ti_usb_phy_device during init.
|
||||
*
|
||||
* Generally called from board file.
|
||||
*/
|
||||
void ti_usb_phy_uboot_exit(int index)
|
||||
{
|
||||
struct ti_usb_phy *phy = NULL;
|
||||
|
||||
list_for_each_entry(phy, &ti_usb_phy_list, list) {
|
||||
if (phy->index != index)
|
||||
continue;
|
||||
|
||||
ti_usb2_phy_power(phy, 0);
|
||||
#ifndef CONFIG_AM43XX
|
||||
ti_usb3_phy_power(phy, 0);
|
||||
#endif
|
||||
list_del(&phy->list);
|
||||
kfree(phy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user