mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-09-08 22:03:22 +09:00
![Lucas Stach](/assets/img/avatar_default.png)
Kill off ehci-core.h It was used to specify some static controller data. To support more than one controller being active at any time we have to carry the controller data ourselfes. Change the ehci interface accordingly. NOTE: OMAP implemented the ehci stuff a bit backwards and should be fixed to do the same thing as other platforms. But the change for now is at least compile clean. Signed-off-by: Lucas Stach <dev@lynxeye.de> Reviewed-by: Marek Vasut <marex@denx.de>
161 lines
4.6 KiB
C
161 lines
4.6 KiB
C
/*
|
|
* Freescale i.MX28 USB Host driver
|
|
*
|
|
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
|
|
* on behalf of DENX Software Engineering GmbH
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/regs-common.h>
|
|
#include <asm/arch/regs-base.h>
|
|
#include <asm/arch/regs-clkctrl-mx28.h>
|
|
#include <asm/arch/regs-usb.h>
|
|
#include <asm/arch/regs-usbphy.h>
|
|
|
|
#include "ehci.h"
|
|
|
|
#if (CONFIG_EHCI_MXS_PORT != 0) && (CONFIG_EHCI_MXS_PORT != 1)
|
|
#error "MXS EHCI: Invalid port selected!"
|
|
#endif
|
|
|
|
#ifndef CONFIG_EHCI_MXS_PORT
|
|
#error "MXS EHCI: Please define correct port using CONFIG_EHCI_MXS_PORT!"
|
|
#endif
|
|
|
|
static struct ehci_mxs {
|
|
struct mxs_usb_regs *usb_regs;
|
|
struct mxs_usbphy_regs *phy_regs;
|
|
} ehci_mxs;
|
|
|
|
int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port)
|
|
{
|
|
uint32_t usb_base, phy_base;
|
|
switch (port) {
|
|
case 0:
|
|
usb_base = MXS_USBCTRL0_BASE;
|
|
phy_base = MXS_USBPHY0_BASE;
|
|
break;
|
|
case 1:
|
|
usb_base = MXS_USBCTRL1_BASE;
|
|
phy_base = MXS_USBPHY1_BASE;
|
|
break;
|
|
default:
|
|
printf("CONFIG_EHCI_MXS_PORT (port = %d)\n", port);
|
|
return -1;
|
|
}
|
|
|
|
mxs_usb->usb_regs = (struct mxs_usb_regs *)usb_base;
|
|
mxs_usb->phy_regs = (struct mxs_usbphy_regs *)phy_base;
|
|
return 0;
|
|
}
|
|
|
|
/* 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)
|
|
|
|
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
|
{
|
|
|
|
int ret;
|
|
uint32_t usb_base, cap_base;
|
|
struct mxs_register_32 *digctl_ctrl =
|
|
(struct mxs_register_32 *)HW_DIGCTL_CTRL;
|
|
struct mxs_clkctrl_regs *clkctrl_regs =
|
|
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
|
|
|
|
ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Reset the PHY block */
|
|
writel(USBPHY_CTRL_SFTRST, &ehci_mxs.phy_regs->hw_usbphy_ctrl_set);
|
|
udelay(10);
|
|
writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE,
|
|
&ehci_mxs.phy_regs->hw_usbphy_ctrl_clr);
|
|
|
|
/* Enable USB clock */
|
|
writel(CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER,
|
|
&clkctrl_regs->hw_clkctrl_pll0ctrl0_set);
|
|
writel(CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER,
|
|
&clkctrl_regs->hw_clkctrl_pll1ctrl0_set);
|
|
|
|
writel(HW_DIGCTL_CTRL_USB0_CLKGATE | HW_DIGCTL_CTRL_USB1_CLKGATE,
|
|
&digctl_ctrl->reg_clr);
|
|
|
|
/* Start USB PHY */
|
|
writel(0, &ehci_mxs.phy_regs->hw_usbphy_pwd);
|
|
|
|
/* Enable UTMI+ Level 2 and Level 3 compatibility */
|
|
writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1,
|
|
&ehci_mxs.phy_regs->hw_usbphy_ctrl_set);
|
|
|
|
usb_base = ((uint32_t)ehci_mxs.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 mxs_register_32 *digctl_ctrl =
|
|
(struct mxs_register_32 *)HW_DIGCTL_CTRL;
|
|
struct mxs_clkctrl_regs *clkctrl_regs =
|
|
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
|
|
struct ehci_hccr *hccr;
|
|
struct ehci_hcor *hcor;
|
|
|
|
ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Stop the USB port */
|
|
usb_base = ((uint32_t)ehci_mxs.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(tmp, &hcor->or_usbcmd);
|
|
|
|
/* 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, &ehci_mxs.phy_regs->hw_usbphy_pwd);
|
|
|
|
/* Disable USB clock */
|
|
writel(CLKCTRL_PLL0CTRL0_EN_USB_CLKS,
|
|
&clkctrl_regs->hw_clkctrl_pll0ctrl0_clr);
|
|
writel(CLKCTRL_PLL1CTRL0_EN_USB_CLKS,
|
|
&clkctrl_regs->hw_clkctrl_pll1ctrl0_clr);
|
|
|
|
/* Gate off the USB clock */
|
|
writel(HW_DIGCTL_CTRL_USB0_CLKGATE | HW_DIGCTL_CTRL_USB1_CLKGATE,
|
|
&digctl_ctrl->reg_set);
|
|
|
|
return 0;
|
|
}
|