1 /* 2 * Copyright (C) 2005-2007 by Texas Instruments 3 * Some code has been taken from tusb6010.c 4 * Copyrights for that are attributable to: 5 * Copyright (C) 2006 Nokia Corporation 6 * Tony Lindgren <tony@atomide.com> 7 * 8 * This file is part of the Inventra Controller Driver for Linux. 9 * 10 * SPDX-License-Identifier: GPL-2.0 11 */ 12 #include <common.h> 13 #include <dm.h> 14 #include <dm/device-internal.h> 15 #include <dm/lists.h> 16 #include <linux/usb/otg.h> 17 #include <asm/omap_common.h> 18 #include <asm/omap_musb.h> 19 #include <twl4030.h> 20 #include <twl6030.h> 21 #include "linux-compat.h" 22 #include "musb_core.h" 23 #include "omap2430.h" 24 #include "musb_uboot.h" 25 26 static inline void omap2430_low_level_exit(struct musb *musb) 27 { 28 u32 l; 29 30 /* in any role */ 31 l = musb_readl(musb->mregs, OTG_FORCESTDBY); 32 l |= ENABLEFORCE; /* enable MSTANDBY */ 33 musb_writel(musb->mregs, OTG_FORCESTDBY, l); 34 } 35 36 static inline void omap2430_low_level_init(struct musb *musb) 37 { 38 u32 l; 39 40 l = musb_readl(musb->mregs, OTG_FORCESTDBY); 41 l &= ~ENABLEFORCE; /* disable MSTANDBY */ 42 musb_writel(musb->mregs, OTG_FORCESTDBY, l); 43 } 44 45 46 static int omap2430_musb_init(struct musb *musb) 47 { 48 u32 l; 49 int status = 0; 50 unsigned long int start; 51 52 struct omap_musb_board_data *data = 53 (struct omap_musb_board_data *)musb->controller; 54 55 /* Reset the controller */ 56 musb_writel(musb->mregs, OTG_SYSCONFIG, SOFTRST); 57 58 start = get_timer(0); 59 60 while (1) { 61 l = musb_readl(musb->mregs, OTG_SYSCONFIG); 62 if ((l & SOFTRST) == 0) 63 break; 64 65 if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { 66 dev_err(musb->controller, "MUSB reset is taking too long\n"); 67 return -ENODEV; 68 } 69 } 70 71 l = musb_readl(musb->mregs, OTG_INTERFSEL); 72 73 if (data->interface_type == MUSB_INTERFACE_UTMI) { 74 /* OMAP4 uses Internal PHY GS70 which uses UTMI interface */ 75 l &= ~ULPI_12PIN; /* Disable ULPI */ 76 l |= UTMI_8BIT; /* Enable UTMI */ 77 } else { 78 l |= ULPI_12PIN; 79 } 80 81 musb_writel(musb->mregs, OTG_INTERFSEL, l); 82 83 pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, " 84 "sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n", 85 musb_readl(musb->mregs, OTG_REVISION), 86 musb_readl(musb->mregs, OTG_SYSCONFIG), 87 musb_readl(musb->mregs, OTG_SYSSTATUS), 88 musb_readl(musb->mregs, OTG_INTERFSEL), 89 musb_readl(musb->mregs, OTG_SIMENABLE)); 90 return 0; 91 92 err1: 93 return status; 94 } 95 96 static int omap2430_musb_enable(struct musb *musb) 97 { 98 #ifdef CONFIG_TWL4030_USB 99 if (twl4030_usb_ulpi_init()) { 100 serial_printf("ERROR: %s Could not initialize PHY\n", 101 __PRETTY_FUNCTION__); 102 } 103 #endif 104 105 #ifdef CONFIG_TWL6030_POWER 106 twl6030_usb_device_settings(); 107 #endif 108 109 #ifdef CONFIG_OMAP44XX 110 u32 *usbotghs_control = (u32 *)((*ctrl)->control_usbotghs_ctrl); 111 *usbotghs_control = USBOTGHS_CONTROL_AVALID | 112 USBOTGHS_CONTROL_VBUSVALID | USBOTGHS_CONTROL_IDDIG; 113 #endif 114 115 return 0; 116 } 117 118 static void omap2430_musb_disable(struct musb *musb) 119 { 120 121 } 122 123 static int omap2430_musb_exit(struct musb *musb) 124 { 125 del_timer_sync(&musb_idle_timer); 126 127 omap2430_low_level_exit(musb); 128 129 return 0; 130 } 131 132 const struct musb_platform_ops omap2430_ops = { 133 .init = omap2430_musb_init, 134 .exit = omap2430_musb_exit, 135 .enable = omap2430_musb_enable, 136 .disable = omap2430_musb_disable, 137 }; 138 139 #if CONFIG_IS_ENABLED(DM_USB) 140 141 struct omap2430_musb_platdata { 142 void *base; 143 void *ctrl_mod_base; 144 struct musb_hdrc_platform_data plat; 145 struct musb_hdrc_config musb_config; 146 struct omap_musb_board_data otg_board_data; 147 }; 148 149 static int omap2430_musb_ofdata_to_platdata(struct udevice *dev) 150 { 151 struct omap2430_musb_platdata *platdata = dev_get_platdata(dev); 152 const void *fdt = gd->fdt_blob; 153 int node = dev_of_offset(dev); 154 155 platdata->base = (void *)dev_read_addr_ptr(dev); 156 157 platdata->musb_config.multipoint = fdtdec_get_int(fdt, node, 158 "multipoint", 159 -1); 160 if (platdata->musb_config.multipoint < 0) { 161 pr_err("MUSB multipoint DT entry missing\n"); 162 return -ENOENT; 163 } 164 165 platdata->musb_config.dyn_fifo = 1; 166 platdata->musb_config.num_eps = fdtdec_get_int(fdt, node, 167 "num-eps", -1); 168 if (platdata->musb_config.num_eps < 0) { 169 pr_err("MUSB num-eps DT entry missing\n"); 170 return -ENOENT; 171 } 172 173 platdata->musb_config.ram_bits = fdtdec_get_int(fdt, node, 174 "ram-bits", -1); 175 if (platdata->musb_config.ram_bits < 0) { 176 pr_err("MUSB ram-bits DT entry missing\n"); 177 return -ENOENT; 178 } 179 180 platdata->plat.power = fdtdec_get_int(fdt, node, 181 "power", -1); 182 if (platdata->plat.power < 0) { 183 pr_err("MUSB power DT entry missing\n"); 184 return -ENOENT; 185 } 186 187 platdata->otg_board_data.interface_type = fdtdec_get_int(fdt, node, 188 "interface-type", -1); 189 if (platdata->otg_board_data.interface_type < 0) { 190 pr_err("MUSB interface-type DT entry missing\n"); 191 return -ENOENT; 192 } 193 194 #if 0 /* In a perfect world, mode would be set to OTG, mode 3 from DT */ 195 platdata->plat.mode = fdtdec_get_int(fdt, node, 196 "mode", -1); 197 if (platdata->plat.mode < 0) { 198 pr_err("MUSB mode DT entry missing\n"); 199 return -ENOENT; 200 } 201 #else /* MUSB_OTG, it doesn't work */ 202 #ifdef CONFIG_USB_MUSB_HOST /* Host seems to be the only option that works */ 203 platdata->plat.mode = MUSB_HOST; 204 #else /* For that matter, MUSB_PERIPHERAL doesn't either */ 205 platdata->plat.mode = MUSB_PERIPHERAL; 206 #endif 207 #endif 208 platdata->otg_board_data.dev = dev; 209 platdata->plat.config = &platdata->musb_config; 210 platdata->plat.platform_ops = &omap2430_ops; 211 platdata->plat.board_data = &platdata->otg_board_data; 212 return 0; 213 } 214 215 static int omap2430_musb_probe(struct udevice *dev) 216 { 217 #ifdef CONFIG_USB_MUSB_HOST 218 struct musb_host_data *host = dev_get_priv(dev); 219 #endif 220 struct omap2430_musb_platdata *platdata = dev_get_platdata(dev); 221 struct usb_bus_priv *priv = dev_get_uclass_priv(dev); 222 struct omap_musb_board_data *otg_board_data; 223 int ret; 224 void *base = dev_read_addr_ptr(dev); 225 226 priv->desc_before_addr = true; 227 228 otg_board_data = &platdata->otg_board_data; 229 230 #ifdef CONFIG_USB_MUSB_HOST 231 host->host = musb_init_controller(&platdata->plat, 232 (struct device *)otg_board_data, 233 platdata->base); 234 if (!host->host) { 235 return -EIO; 236 } 237 238 ret = musb_lowlevel_init(host); 239 #else 240 ret = musb_register(&platdata->plat, 241 (struct device *)otg_board_data, 242 platdata->base); 243 #endif 244 return ret; 245 } 246 247 static int omap2430_musb_remove(struct udevice *dev) 248 { 249 struct musb_host_data *host = dev_get_priv(dev); 250 251 musb_stop(host->host); 252 253 return 0; 254 } 255 256 static const struct udevice_id omap2430_musb_ids[] = { 257 { .compatible = "ti,omap3-musb" }, 258 { .compatible = "ti,omap4-musb" }, 259 { } 260 }; 261 262 U_BOOT_DRIVER(omap2430_musb) = { 263 .name = "omap2430-musb", 264 #ifdef CONFIG_USB_MUSB_HOST 265 .id = UCLASS_USB, 266 #else 267 .id = UCLASS_USB_GADGET_GENERIC, 268 #endif 269 .of_match = omap2430_musb_ids, 270 .ofdata_to_platdata = omap2430_musb_ofdata_to_platdata, 271 .probe = omap2430_musb_probe, 272 .remove = omap2430_musb_remove, 273 #ifdef CONFIG_USB_MUSB_HOST 274 .ops = &musb_usb_ops, 275 #endif 276 .platdata_auto_alloc_size = sizeof(struct omap2430_musb_platdata), 277 .priv_auto_alloc_size = sizeof(struct musb_host_data), 278 }; 279 280 #endif /* CONFIG_IS_ENABLED(DM_USB) */ 281