u-boot/drivers/usb/host/ehci-vf.c
Simon Glass 8b85dfc675 dm: Avoid accessing seq directly
At present various drivers etc. access the device's 'seq' member directly.
This makes it harder to change the meaning of that member. Change access
to go through a function instead.

The drivers/i2c/lpc32xx_i2c.c file is left unchanged for now.

Signed-off-by: Simon Glass <sjg@chromium.org>
2020-12-18 20:32:21 -07:00

362 lines
8.3 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Sanchayan Maity <sanchayan.maity@toradex.com>
* Copyright (C) 2015 Toradex AG
*
* Based on ehci-mx6 driver
*/
#include <common.h>
#include <dm.h>
#include <log.h>
#include <usb.h>
#include <errno.h>
#include <linux/compiler.h>
#include <asm/io.h>
#include <asm-generic/gpio.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/crm_regs.h>
#include <asm/mach-imx/iomux-v3.h>
#include <asm/mach-imx/regs-usbphy.h>
#include <linux/delay.h>
#include <usb/ehci-ci.h>
#include <linux/libfdt.h>
#include <fdtdec.h>
#include "ehci.h"
#define USB_NC_REG_OFFSET 0x00000800
#define ANADIG_PLL_CTRL_EN_USB_CLKS (1 << 6)
#define UCTRL_OVER_CUR_POL (1 << 8) /* OTG Polarity of Overcurrent */
#define UCTRL_OVER_CUR_DIS (1 << 7) /* Disable OTG Overcurrent Detection */
/* USBCMD */
#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */
#define UCMD_RESET (1 << 1) /* controller reset */
DECLARE_GLOBAL_DATA_PTR;
static const unsigned phy_bases[] = {
USB_PHY0_BASE_ADDR,
USB_PHY1_BASE_ADDR,
};
static const unsigned nc_reg_bases[] = {
USBC0_BASE_ADDR,
USBC1_BASE_ADDR,
};
static void usb_internal_phy_clock_gate(int index)
{
void __iomem *phy_reg;
phy_reg = (void __iomem *)phy_bases[index];
clrbits_le32(phy_reg + USBPHY_CTRL, USBPHY_CTRL_CLKGATE);
}
static void usb_power_config(int index)
{
struct anadig_reg __iomem *anadig =
(struct anadig_reg __iomem *)ANADIG_BASE_ADDR;
void __iomem *pll_ctrl;
switch (index) {
case 0:
pll_ctrl = &anadig->pll3_ctrl;
clrbits_le32(pll_ctrl, ANADIG_PLL3_CTRL_BYPASS);
setbits_le32(pll_ctrl, ANADIG_PLL3_CTRL_ENABLE
| ANADIG_PLL3_CTRL_POWERDOWN
| ANADIG_PLL_CTRL_EN_USB_CLKS);
break;
case 1:
pll_ctrl = &anadig->pll7_ctrl;
clrbits_le32(pll_ctrl, ANADIG_PLL7_CTRL_BYPASS);
setbits_le32(pll_ctrl, ANADIG_PLL7_CTRL_ENABLE
| ANADIG_PLL7_CTRL_POWERDOWN
| ANADIG_PLL_CTRL_EN_USB_CLKS);
break;
default:
return;
}
}
static void usb_phy_enable(int index, struct usb_ehci *ehci)
{
void __iomem *phy_reg;
void __iomem *phy_ctrl;
void __iomem *usb_cmd;
phy_reg = (void __iomem *)phy_bases[index];
phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL);
usb_cmd = (void __iomem *)&ehci->usbcmd;
/* Stop then Reset */
clrbits_le32(usb_cmd, UCMD_RUN_STOP);
while (readl(usb_cmd) & UCMD_RUN_STOP)
;
setbits_le32(usb_cmd, UCMD_RESET);
while (readl(usb_cmd) & UCMD_RESET)
;
/* Reset USBPHY module */
setbits_le32(phy_ctrl, USBPHY_CTRL_SFTRST);
udelay(10);
/* Remove CLKGATE and SFTRST */
clrbits_le32(phy_ctrl, USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST);
udelay(10);
/* Power up the PHY */
writel(0, phy_reg + USBPHY_PWD);
/* Enable FS/LS device */
setbits_le32(phy_ctrl, USBPHY_CTRL_ENUTMILEVEL2 |
USBPHY_CTRL_ENUTMILEVEL3);
}
static void usb_oc_config(int index)
{
void __iomem *ctrl;
ctrl = (void __iomem *)(nc_reg_bases[index] + USB_NC_REG_OFFSET);
setbits_le32(ctrl, UCTRL_OVER_CUR_POL);
setbits_le32(ctrl, UCTRL_OVER_CUR_DIS);
}
int __weak board_usb_phy_mode(int port)
{
return 0;
}
int __weak board_ehci_hcd_init(int port)
{
return 0;
}
int ehci_vf_common_init(struct usb_ehci *ehci, int index)
{
int ret;
/* Do board specific initialisation */
ret = board_ehci_hcd_init(index);
if (ret)
return ret;
usb_power_config(index);
usb_oc_config(index);
usb_internal_phy_clock_gate(index);
usb_phy_enable(index, ehci);
return 0;
}
#if !CONFIG_IS_ENABLED(DM_USB)
int ehci_hcd_init(int index, enum usb_init_type init,
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
{
struct usb_ehci *ehci;
enum usb_init_type type;
int ret;
if (index >= ARRAY_SIZE(nc_reg_bases))
return -EINVAL;
ehci = (struct usb_ehci *)nc_reg_bases[index];
ret = ehci_vf_common_init(index);
if (ret)
return ret;
*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
*hcor = (struct ehci_hcor *)((uint32_t)*hccr +
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
type = board_usb_phy_mode(index);
if (type != init)
return -ENODEV;
if (init == USB_INIT_DEVICE) {
setbits_le32(&ehci->usbmode, CM_DEVICE);
writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc);
setbits_le32(&ehci->portsc, USB_EN);
} else if (init == USB_INIT_HOST) {
setbits_le32(&ehci->usbmode, CM_HOST);
writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc);
setbits_le32(&ehci->portsc, USB_EN);
}
return 0;
}
int ehci_hcd_stop(int index)
{
return 0;
}
#else
/* Possible port types (dual role mode) */
enum dr_mode {
DR_MODE_NONE = 0,
DR_MODE_HOST, /* supports host operation */
DR_MODE_DEVICE, /* supports device operation */
DR_MODE_OTG, /* supports both */
};
struct ehci_vf_priv_data {
struct ehci_ctrl ctrl;
struct usb_ehci *ehci;
struct gpio_desc cdet_gpio;
enum usb_init_type init_type;
enum dr_mode dr_mode;
u32 portnr;
};
static int vf_usb_of_to_plat(struct udevice *dev)
{
struct ehci_vf_priv_data *priv = dev_get_priv(dev);
const void *dt_blob = gd->fdt_blob;
int node = dev_of_offset(dev);
const char *mode;
priv->portnr = dev_seq(dev);
priv->ehci = dev_read_addr_ptr(dev);
mode = fdt_getprop(dt_blob, node, "dr_mode", NULL);
if (mode) {
if (0 == strcmp(mode, "host")) {
priv->dr_mode = DR_MODE_HOST;
priv->init_type = USB_INIT_HOST;
} else if (0 == strcmp(mode, "peripheral")) {
priv->dr_mode = DR_MODE_DEVICE;
priv->init_type = USB_INIT_DEVICE;
} else if (0 == strcmp(mode, "otg")) {
priv->dr_mode = DR_MODE_OTG;
/*
* We set init_type to device by default when OTG
* mode is requested. If a valid gpio is provided
* we will switch the init_type based on the state
* of the gpio pin.
*/
priv->init_type = USB_INIT_DEVICE;
} else {
debug("%s: Cannot decode dr_mode '%s'\n",
__func__, mode);
return -EINVAL;
}
} else {
priv->dr_mode = DR_MODE_HOST;
priv->init_type = USB_INIT_HOST;
}
if (priv->dr_mode == DR_MODE_OTG) {
gpio_request_by_name_nodev(offset_to_ofnode(node),
"fsl,cdet-gpio", 0, &priv->cdet_gpio,
GPIOD_IS_IN);
if (dm_gpio_is_valid(&priv->cdet_gpio)) {
if (dm_gpio_get_value(&priv->cdet_gpio))
priv->init_type = USB_INIT_DEVICE;
else
priv->init_type = USB_INIT_HOST;
}
}
return 0;
}
static int vf_init_after_reset(struct ehci_ctrl *dev)
{
struct ehci_vf_priv_data *priv = dev->priv;
enum usb_init_type type = priv->init_type;
struct usb_ehci *ehci = priv->ehci;
int ret;
ret = ehci_vf_common_init(priv->ehci, priv->portnr);
if (ret)
return ret;
if (type == USB_INIT_DEVICE)
return 0;
setbits_le32(&ehci->usbmode, CM_HOST);
writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc);
setbits_le32(&ehci->portsc, USB_EN);
mdelay(10);
return 0;
}
static const struct ehci_ops vf_ehci_ops = {
.init_after_reset = vf_init_after_reset
};
static int vf_usb_bind(struct udevice *dev)
{
static int num_controllers;
/*
* Without this hack, if we return ENODEV for USB Controller 0, on
* probe for the next controller, USB Controller 1 will be given a
* sequence number of 0. This conflicts with our requirement of
* sequence numbers while initialising the peripherals.
*/
dev->req_seq = num_controllers;
num_controllers++;
return 0;
}
static int ehci_usb_probe(struct udevice *dev)
{
struct usb_plat *plat = dev_get_plat(dev);
struct ehci_vf_priv_data *priv = dev_get_priv(dev);
struct usb_ehci *ehci = priv->ehci;
struct ehci_hccr *hccr;
struct ehci_hcor *hcor;
int ret;
ret = ehci_vf_common_init(ehci, priv->portnr);
if (ret)
return ret;
if (priv->init_type != plat->init_type)
return -ENODEV;
if (priv->init_type == USB_INIT_HOST) {
setbits_le32(&ehci->usbmode, CM_HOST);
writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc);
setbits_le32(&ehci->portsc, USB_EN);
}
mdelay(10);
hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
hcor = (struct ehci_hcor *)((uint32_t)hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
return ehci_register(dev, hccr, hcor, &vf_ehci_ops, 0, priv->init_type);
}
static const struct udevice_id vf_usb_ids[] = {
{ .compatible = "fsl,vf610-usb" },
{ }
};
U_BOOT_DRIVER(usb_ehci) = {
.name = "ehci_vf",
.id = UCLASS_USB,
.of_match = vf_usb_ids,
.bind = vf_usb_bind,
.probe = ehci_usb_probe,
.remove = ehci_deregister,
.ops = &ehci_usb_ops,
.of_to_plat = vf_usb_of_to_plat,
.plat_auto = sizeof(struct usb_plat),
.priv_auto = sizeof(struct ehci_vf_priv_data),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
#endif