mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-26 13:44:29 +08:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
174 lines
4.2 KiB
C
174 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Freescale i.MX28 USB Host driver
|
|
*
|
|
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
|
|
* on behalf of DENX Software Engineering GmbH
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/imx-regs.h>
|
|
#include <errno.h>
|
|
|
|
#include "ehci.h"
|
|
|
|
/* This DIGCTL register ungates clock to USB */
|
|
#define HW_DIGCTL_CTRL 0x8001c000
|
|
#define HW_DIGCTL_CTRL_USB0_CLKGATE (1 << 2)
|
|
#define HW_DIGCTL_CTRL_USB1_CLKGATE (1 << 16)
|
|
|
|
struct ehci_mxs_port {
|
|
uint32_t usb_regs;
|
|
struct mxs_usbphy_regs *phy_regs;
|
|
|
|
struct mxs_register_32 *pll;
|
|
uint32_t pll_en_bits;
|
|
uint32_t pll_dis_bits;
|
|
uint32_t gate_bits;
|
|
};
|
|
|
|
static const struct ehci_mxs_port mxs_port[] = {
|
|
#ifdef CONFIG_EHCI_MXS_PORT0
|
|
{
|
|
MXS_USBCTRL0_BASE,
|
|
(struct mxs_usbphy_regs *)MXS_USBPHY0_BASE,
|
|
(struct mxs_register_32 *)(MXS_CLKCTRL_BASE +
|
|
offsetof(struct mxs_clkctrl_regs,
|
|
hw_clkctrl_pll0ctrl0_reg)),
|
|
CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER,
|
|
CLKCTRL_PLL0CTRL0_EN_USB_CLKS,
|
|
HW_DIGCTL_CTRL_USB0_CLKGATE,
|
|
},
|
|
#endif
|
|
#ifdef CONFIG_EHCI_MXS_PORT1
|
|
{
|
|
MXS_USBCTRL1_BASE,
|
|
(struct mxs_usbphy_regs *)MXS_USBPHY1_BASE,
|
|
(struct mxs_register_32 *)(MXS_CLKCTRL_BASE +
|
|
offsetof(struct mxs_clkctrl_regs,
|
|
hw_clkctrl_pll1ctrl0_reg)),
|
|
CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER,
|
|
CLKCTRL_PLL1CTRL0_EN_USB_CLKS,
|
|
HW_DIGCTL_CTRL_USB1_CLKGATE,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
static int ehci_mxs_toggle_clock(const struct ehci_mxs_port *port, int enable)
|
|
{
|
|
struct mxs_register_32 *digctl_ctrl =
|
|
(struct mxs_register_32 *)HW_DIGCTL_CTRL;
|
|
int pll_offset, dig_offset;
|
|
|
|
if (enable) {
|
|
pll_offset = offsetof(struct mxs_register_32, reg_set);
|
|
dig_offset = offsetof(struct mxs_register_32, reg_clr);
|
|
writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset);
|
|
writel(port->pll_en_bits, (u32)port->pll + pll_offset);
|
|
} else {
|
|
pll_offset = offsetof(struct mxs_register_32, reg_clr);
|
|
dig_offset = offsetof(struct mxs_register_32, reg_set);
|
|
writel(port->pll_dis_bits, (u32)port->pll + pll_offset);
|
|
writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int __weak board_ehci_hcd_init(int port)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int __weak board_ehci_hcd_exit(int port)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int ehci_hcd_init(int index, enum usb_init_type init,
|
|
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
|
{
|
|
|
|
int ret;
|
|
uint32_t usb_base, cap_base;
|
|
const struct ehci_mxs_port *port;
|
|
|
|
if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) {
|
|
printf("Invalid port index (index = %d)!\n", index);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = board_ehci_hcd_init(index);
|
|
if (ret)
|
|
return ret;
|
|
|
|
port = &mxs_port[index];
|
|
|
|
/* Reset the PHY block */
|
|
writel(USBPHY_CTRL_SFTRST, &port->phy_regs->hw_usbphy_ctrl_set);
|
|
udelay(10);
|
|
writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE,
|
|
&port->phy_regs->hw_usbphy_ctrl_clr);
|
|
|
|
/* Enable USB clock */
|
|
ret = ehci_mxs_toggle_clock(port, 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Start USB PHY */
|
|
writel(0, &port->phy_regs->hw_usbphy_pwd);
|
|
|
|
/* Enable UTMI+ Level 2 and Level 3 compatibility */
|
|
writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1,
|
|
&port->phy_regs->hw_usbphy_ctrl_set);
|
|
|
|
usb_base = port->usb_regs + 0x100;
|
|
*hccr = (struct ehci_hccr *)usb_base;
|
|
|
|
cap_base = ehci_readl(&(*hccr)->cr_capbase);
|
|
*hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ehci_hcd_stop(int index)
|
|
{
|
|
int ret;
|
|
uint32_t usb_base, cap_base, tmp;
|
|
struct ehci_hccr *hccr;
|
|
struct ehci_hcor *hcor;
|
|
const struct ehci_mxs_port *port;
|
|
|
|
if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) {
|
|
printf("Invalid port index (index = %d)!\n", index);
|
|
return -EINVAL;
|
|
}
|
|
|
|
port = &mxs_port[index];
|
|
|
|
/* Stop the USB port */
|
|
usb_base = port->usb_regs + 0x100;
|
|
hccr = (struct ehci_hccr *)usb_base;
|
|
cap_base = ehci_readl(&hccr->cr_capbase);
|
|
hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base));
|
|
|
|
tmp = ehci_readl(&hcor->or_usbcmd);
|
|
tmp &= ~CMD_RUN;
|
|
ehci_writel(&hcor->or_usbcmd, tmp);
|
|
|
|
/* Disable the PHY */
|
|
tmp = USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF |
|
|
USBPHY_PWD_RXPWD1PT1 | USBPHY_PWD_RXPWDENV |
|
|
USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS |
|
|
USBPHY_PWD_TXPWDFS;
|
|
writel(tmp, &port->phy_regs->hw_usbphy_pwd);
|
|
|
|
/* Disable USB clock */
|
|
ret = ehci_mxs_toggle_clock(port, 0);
|
|
|
|
board_ehci_hcd_exit(index);
|
|
|
|
return ret;
|
|
}
|