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