1 /* 2 * Freescale i.MX28 USB Host driver 3 * 4 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> 5 * on behalf of DENX Software Engineering GmbH 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22 #include <common.h> 23 #include <asm/io.h> 24 #include <asm/arch/imx-regs.h> 25 #include <errno.h> 26 27 #include "ehci.h" 28 29 /* This DIGCTL register ungates clock to USB */ 30 #define HW_DIGCTL_CTRL 0x8001c000 31 #define HW_DIGCTL_CTRL_USB0_CLKGATE (1 << 2) 32 #define HW_DIGCTL_CTRL_USB1_CLKGATE (1 << 16) 33 34 struct ehci_mxs_port { 35 uint32_t usb_regs; 36 struct mxs_usbphy_regs *phy_regs; 37 38 struct mxs_register_32 *pll; 39 uint32_t pll_en_bits; 40 uint32_t pll_dis_bits; 41 uint32_t gate_bits; 42 }; 43 44 static const struct ehci_mxs_port mxs_port[] = { 45 #ifdef CONFIG_EHCI_MXS_PORT0 46 { 47 MXS_USBCTRL0_BASE, 48 (struct mxs_usbphy_regs *)MXS_USBPHY0_BASE, 49 (struct mxs_register_32 *)(MXS_CLKCTRL_BASE + 50 offsetof(struct mxs_clkctrl_regs, 51 hw_clkctrl_pll0ctrl0_reg)), 52 CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER, 53 CLKCTRL_PLL0CTRL0_EN_USB_CLKS, 54 HW_DIGCTL_CTRL_USB0_CLKGATE, 55 }, 56 #endif 57 #ifdef CONFIG_EHCI_MXS_PORT1 58 { 59 MXS_USBCTRL1_BASE, 60 (struct mxs_usbphy_regs *)MXS_USBPHY1_BASE, 61 (struct mxs_register_32 *)(MXS_CLKCTRL_BASE + 62 offsetof(struct mxs_clkctrl_regs, 63 hw_clkctrl_pll1ctrl0_reg)), 64 CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER, 65 CLKCTRL_PLL1CTRL0_EN_USB_CLKS, 66 HW_DIGCTL_CTRL_USB1_CLKGATE, 67 }, 68 #endif 69 }; 70 71 static int ehci_mxs_toggle_clock(const struct ehci_mxs_port *port, int enable) 72 { 73 struct mxs_register_32 *digctl_ctrl = 74 (struct mxs_register_32 *)HW_DIGCTL_CTRL; 75 int pll_offset, dig_offset; 76 77 if (enable) { 78 pll_offset = offsetof(struct mxs_register_32, reg_set); 79 dig_offset = offsetof(struct mxs_register_32, reg_clr); 80 writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset); 81 writel(port->pll_en_bits, (u32)port->pll + pll_offset); 82 } else { 83 pll_offset = offsetof(struct mxs_register_32, reg_clr); 84 dig_offset = offsetof(struct mxs_register_32, reg_set); 85 writel(port->pll_dis_bits, (u32)port->pll + pll_offset); 86 writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset); 87 } 88 89 return 0; 90 } 91 92 int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) 93 { 94 95 int ret; 96 uint32_t usb_base, cap_base; 97 const struct ehci_mxs_port *port; 98 99 if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) { 100 printf("Invalid port index (index = %d)!\n", index); 101 return -EINVAL; 102 } 103 104 port = &mxs_port[index]; 105 106 /* Reset the PHY block */ 107 writel(USBPHY_CTRL_SFTRST, &port->phy_regs->hw_usbphy_ctrl_set); 108 udelay(10); 109 writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, 110 &port->phy_regs->hw_usbphy_ctrl_clr); 111 112 /* Enable USB clock */ 113 ret = ehci_mxs_toggle_clock(port, 1); 114 if (ret) 115 return ret; 116 117 /* Start USB PHY */ 118 writel(0, &port->phy_regs->hw_usbphy_pwd); 119 120 /* Enable UTMI+ Level 2 and Level 3 compatibility */ 121 writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1, 122 &port->phy_regs->hw_usbphy_ctrl_set); 123 124 usb_base = port->usb_regs + 0x100; 125 *hccr = (struct ehci_hccr *)usb_base; 126 127 cap_base = ehci_readl(&(*hccr)->cr_capbase); 128 *hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base)); 129 130 return 0; 131 } 132 133 int ehci_hcd_stop(int index) 134 { 135 int ret; 136 uint32_t usb_base, cap_base, tmp; 137 struct ehci_hccr *hccr; 138 struct ehci_hcor *hcor; 139 const struct ehci_mxs_port *port; 140 141 if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) { 142 printf("Invalid port index (index = %d)!\n", index); 143 return -EINVAL; 144 } 145 146 port = &mxs_port[index]; 147 148 /* Stop the USB port */ 149 usb_base = port->usb_regs + 0x100; 150 hccr = (struct ehci_hccr *)usb_base; 151 cap_base = ehci_readl(&hccr->cr_capbase); 152 hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base)); 153 154 tmp = ehci_readl(&hcor->or_usbcmd); 155 tmp &= ~CMD_RUN; 156 ehci_writel(tmp, &hcor->or_usbcmd); 157 158 /* Disable the PHY */ 159 tmp = USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF | 160 USBPHY_PWD_RXPWD1PT1 | USBPHY_PWD_RXPWDENV | 161 USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS | 162 USBPHY_PWD_TXPWDFS; 163 writel(tmp, &port->phy_regs->hw_usbphy_pwd); 164 165 /* Disable USB clock */ 166 ret = ehci_mxs_toggle_clock(port, 0); 167 168 return ret; 169 } 170