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