1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2011 Ilya Yanok, Emcraft Systems 4 * (C) Copyright 2004-2008 5 * Texas Instruments, <www.ti.com> 6 * 7 * Derived from Beagle Board code by 8 * Sunil Kumar <sunilsaini05@gmail.com> 9 * Shashi Ranjan <shashiranjanmca05@gmail.com> 10 * 11 */ 12 13#include <common.h> 14#include <log.h> 15#include <usb.h> 16#include <linux/delay.h> 17#include <usb/ulpi.h> 18#include <errno.h> 19#include <asm/io.h> 20#include <asm/gpio.h> 21#include <asm/arch/ehci.h> 22#include <asm/ehci-omap.h> 23#include <dm.h> 24#include <dm/device-internal.h> 25#include <dm/lists.h> 26#include <power/regulator.h> 27 28#include "ehci.h" 29 30static struct omap_uhh *const uhh = (struct omap_uhh *)OMAP_UHH_BASE; 31static struct omap_usbtll *const usbtll = (struct omap_usbtll *)OMAP_USBTLL_BASE; 32static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE; 33 34static int omap_uhh_reset(void) 35{ 36 int timeout = 0; 37 u32 rev; 38 39 rev = readl(&uhh->rev); 40 41 /* Soft RESET */ 42 writel(OMAP_UHH_SYSCONFIG_SOFTRESET, &uhh->sysc); 43 44 switch (rev) { 45 case OMAP_USBHS_REV1: 46 /* Wait for soft RESET to complete */ 47 while (!(readl(&uhh->syss) & 0x1)) { 48 if (timeout > 100) { 49 printf("%s: RESET timeout\n", __func__); 50 return -1; 51 } 52 udelay(10); 53 timeout++; 54 } 55 56 /* Set No-Idle, No-Standby */ 57 writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); 58 break; 59 60 default: /* Rev. 2 onwards */ 61 62 udelay(2); /* Need to wait before accessing SYSCONFIG back */ 63 64 /* Wait for soft RESET to complete */ 65 while ((readl(&uhh->sysc) & 0x1)) { 66 if (timeout > 100) { 67 printf("%s: RESET timeout\n", __func__); 68 return -1; 69 } 70 udelay(10); 71 timeout++; 72 } 73 74 writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); 75 break; 76 } 77 78 return 0; 79} 80 81static int omap_ehci_tll_reset(void) 82{ 83 unsigned long init = get_timer(0); 84 85 /* perform TLL soft reset, and wait until reset is complete */ 86 writel(OMAP_USBTLL_SYSCONFIG_SOFTRESET, &usbtll->sysc); 87 88 /* Wait for TLL reset to complete */ 89 while (!(readl(&usbtll->syss) & OMAP_USBTLL_SYSSTATUS_RESETDONE)) 90 if (get_timer(init) > CONFIG_SYS_HZ) { 91 debug("OMAP EHCI error: timeout resetting TLL\n"); 92 return -EL3RST; 93 } 94 95 return 0; 96} 97 98static void omap_usbhs_hsic_init(int port) 99{ 100 unsigned int reg; 101 102 /* Enable channels now */ 103 reg = readl(&usbtll->channel_conf + port); 104 105 setbits_le32(®, (OMAP_TLL_CHANNEL_CONF_CHANMODE_TRANSPARENT_UTMI 106 | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF 107 | OMAP_TLL_CHANNEL_CONF_DRVVBUS 108 | OMAP_TLL_CHANNEL_CONF_CHRGVBUS 109 | OMAP_TLL_CHANNEL_CONF_CHANEN)); 110 111 writel(reg, &usbtll->channel_conf + port); 112} 113 114#ifdef CONFIG_USB_ULPI 115static void omap_ehci_soft_phy_reset(int port) 116{ 117 struct ulpi_viewport ulpi_vp; 118 119 ulpi_vp.viewport_addr = (u32)&ehci->insreg05_utmi_ulpi; 120 ulpi_vp.port_num = port; 121 122 ulpi_reset(&ulpi_vp); 123} 124#else 125static void omap_ehci_soft_phy_reset(int port) 126{ 127 return; 128} 129#endif 130 131struct ehci_omap_priv_data { 132 struct ehci_ctrl ctrl; 133 struct omap_ehci *ehci; 134#ifdef CONFIG_DM_REGULATOR 135 struct udevice *vbus_supply; 136#endif 137 enum usb_init_type init_type; 138 int portnr; 139 struct phy phy[OMAP_HS_USB_PORTS]; 140 int nports; 141}; 142 143/* 144 * Initialize the OMAP EHCI controller and PHY. 145 * Based on "drivers/usb/host/ehci-omap.c" from Linux 3.1 146 * See there for additional Copyrights. 147 */ 148static int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata, 149 struct udevice *dev) 150{ 151 int ret; 152 unsigned int i, reg = 0, rev = 0; 153 154 debug("Initializing OMAP EHCI\n"); 155 156 ret = board_usb_init(index, USB_INIT_HOST); 157 if (ret < 0) 158 return ret; 159 160 /* Hold the PHY in RESET for enough time till DIR is high */ 161 /* Refer: ISSUE1 */ 162 udelay(10); 163 164 ret = omap_uhh_reset(); 165 if (ret < 0) 166 return ret; 167 168 ret = omap_ehci_tll_reset(); 169 if (ret) 170 return ret; 171 172 writel(OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | 173 OMAP_USBTLL_SYSCONFIG_SIDLEMODE | 174 OMAP_USBTLL_SYSCONFIG_CACTIVITY, &usbtll->sysc); 175 176 /* Put UHH in NoIdle/NoStandby mode */ 177 writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); 178 179 /* setup ULPI bypass and burst configurations */ 180 clrsetbits_le32(®, OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN, 181 (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN | 182 OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN | 183 OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN)); 184 185 rev = readl(&uhh->rev); 186 if (rev == OMAP_USBHS_REV1) { 187 if (is_ehci_phy_mode(usbhs_pdata->port_mode[0])) 188 clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); 189 else 190 setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); 191 192 if (is_ehci_phy_mode(usbhs_pdata->port_mode[1])) 193 clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS); 194 else 195 setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS); 196 197 if (is_ehci_phy_mode(usbhs_pdata->port_mode[2])) 198 clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS); 199 else 200 setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS); 201 } else if (rev == OMAP_USBHS_REV2) { 202 203 clrsetbits_le32(®, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR), 204 OMAP4_UHH_HOSTCONFIG_APP_START_CLK); 205 206 /* Clear port mode fields for PHY mode */ 207 208 if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0])) 209 setbits_le32(®, OMAP_P1_MODE_HSIC); 210 211 if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1])) 212 setbits_le32(®, OMAP_P2_MODE_HSIC); 213 214 } else if (rev == OMAP_USBHS_REV2_1) { 215 216 clrsetbits_le32(®, 217 (OMAP_P1_MODE_CLEAR | 218 OMAP_P2_MODE_CLEAR | 219 OMAP_P3_MODE_CLEAR), 220 OMAP4_UHH_HOSTCONFIG_APP_START_CLK); 221 222 /* Clear port mode fields for PHY mode */ 223 224 if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0])) 225 setbits_le32(®, OMAP_P1_MODE_HSIC); 226 227 if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1])) 228 setbits_le32(®, OMAP_P2_MODE_HSIC); 229 230 if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2])) 231 setbits_le32(®, OMAP_P3_MODE_HSIC); 232 } 233 234 debug("OMAP UHH_REVISION 0x%x\n", rev); 235 writel(reg, &uhh->hostconfig); 236 237 for (i = 0; i < OMAP_HS_USB_PORTS; i++) 238 if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i])) 239 omap_usbhs_hsic_init(i); 240 241 /* 242 * Refer ISSUE1: 243 * Hold the PHY in RESET for enough time till 244 * PHY is settled and ready 245 */ 246 udelay(10); 247 248 /* 249 * An undocumented "feature" in the OMAP3 EHCI controller, 250 * causes suspended ports to be taken out of suspend when 251 * the USBCMD.Run/Stop bit is cleared (for example when 252 * we do ehci_bus_suspend). 253 * This breaks suspend-resume if the root-hub is allowed 254 * to suspend. Writing 1 to this undocumented register bit 255 * disables this feature and restores normal behavior. 256 */ 257 writel(EHCI_INSNREG04_DISABLE_UNSUSPEND, &ehci->insreg04); 258 259 for (i = 0; i < OMAP_HS_USB_PORTS; i++) 260 if (is_ehci_phy_mode(usbhs_pdata->port_mode[i])) 261 omap_ehci_soft_phy_reset(i); 262 263 debug("OMAP EHCI init done\n"); 264 return 0; 265} 266 267static struct omap_usbhs_board_data usbhs_bdata = { 268 .port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED, 269 .port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED, 270 .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED, 271}; 272 273static void omap_usbhs_set_mode(u8 index, const char *mode) 274{ 275 if (!strcmp(mode, "ehci-phy")) 276 usbhs_bdata.port_mode[index] = OMAP_EHCI_PORT_MODE_PHY; 277 else if (!strcmp(mode, "ehci-tll")) 278 usbhs_bdata.port_mode[index] = OMAP_EHCI_PORT_MODE_TLL; 279 else if (!strcmp(mode, "ehci-hsic")) 280 usbhs_bdata.port_mode[index] = OMAP_EHCI_PORT_MODE_HSIC; 281} 282 283static int omap_usbhs_probe(struct udevice *dev) 284{ 285 u8 i; 286 const char *mode; 287 char prop[11]; 288 289 /* Go through each port portX-mode to determing phy mode */ 290 for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 291 snprintf(prop, sizeof(prop), "port%d-mode", i + 1); 292 mode = dev_read_string(dev, prop); 293 294 /* If the portX-mode exists, set the mode */ 295 if (mode) 296 omap_usbhs_set_mode(i, mode); 297 } 298 299 return 0; 300} 301 302static const struct udevice_id omap_usbhs_dt_ids[] = { 303 { .compatible = "ti,usbhs-host" }, 304 { } 305}; 306 307U_BOOT_DRIVER(usb_omaphs_host) = { 308 .name = "usbhs-host", 309 .id = UCLASS_SIMPLE_BUS, 310 .of_match = omap_usbhs_dt_ids, 311 .probe = omap_usbhs_probe, 312 .flags = DM_FLAG_ALLOC_PRIV_DMA, 313}; 314 315static int ehci_usb_of_to_plat(struct udevice *dev) 316{ 317 struct usb_plat *plat = dev_get_plat(dev); 318 319 plat->init_type = USB_INIT_HOST; 320 321 return 0; 322} 323 324/* 325 * This driver references phys based on the USB port. If 326 * the port is unused, the corresponding phy is listed as NULL 327 * which generic_phy_init_bulk treats as an error, so we need 328 * a custom one that tolerates empty phys 329 */ 330static int omap_ehci_phy_get(struct udevice *dev) 331{ 332 struct ehci_omap_priv_data *priv = dev_get_priv(dev); 333 int i, ret; 334 335 for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 336 ret = generic_phy_get_by_index(dev, i, &priv->phy[i]); 337 if (ret && ret != -ENOENT) 338 return ret; 339 }; 340 341 return 0; 342}; 343 344static int omap_ehci_probe(struct udevice *dev) 345{ 346 struct usb_plat *plat = dev_get_plat(dev); 347 struct ehci_omap_priv_data *priv = dev_get_priv(dev); 348 struct ehci_hccr *hccr; 349 struct ehci_hcor *hcor; 350 int ret; 351 352 priv->ehci = dev_read_addr_ptr(dev); 353 priv->portnr = dev_seq(dev); 354 priv->init_type = plat->init_type; 355 356 hccr = (struct ehci_hccr *)&priv->ehci->hccapbase; 357 hcor = (struct ehci_hcor *)&priv->ehci->usbcmd; 358 359 /* Identify Phys */ 360 ret = omap_ehci_phy_get(dev); 361 if (ret) { 362 printf("Failed to get phys\n"); 363 return ret; 364 } 365 366 /* Register the EHCI */ 367 ret = ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST); 368 if (ret) { 369 printf("Failed to register EHCI\n"); 370 return ret; 371 } 372 373 ret = omap_ehci_hcd_init(0, &usbhs_bdata, dev); 374 if (ret) 375 return ret; 376 377 return ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST); 378} 379 380static const struct udevice_id omap_ehci_dt_ids[] = { 381 { .compatible = "ti,ehci-omap" }, 382 { } 383}; 384 385U_BOOT_DRIVER(usb_omap_ehci) = { 386 .name = "omap-ehci", 387 .id = UCLASS_USB, 388 .of_match = omap_ehci_dt_ids, 389 .probe = omap_ehci_probe, 390 .of_to_plat = ehci_usb_of_to_plat, 391 .plat_auto = sizeof(struct usb_plat), 392 .priv_auto = sizeof(struct ehci_omap_priv_data), 393 .remove = ehci_deregister, 394 .ops = &ehci_usb_ops, 395 .flags = DM_FLAG_ALLOC_PRIV_DMA, 396}; 397