1 /* 2 * Allwinner SUNXI "glue layer" 3 * 4 * Copyright © 2015 Hans de Goede <hdegoede@redhat.com> 5 * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi> 6 * 7 * Based on the sw_usb "Allwinner OTG Dual Role Controller" code. 8 * Copyright 2007-2012 (C) Allwinner Technology Co., Ltd. 9 * javen <javen@allwinnertech.com> 10 * 11 * Based on the DA8xx "glue layer" code. 12 * Copyright (c) 2008-2009 MontaVista Software, Inc. <source@mvista.com> 13 * Copyright (C) 2005-2006 by Texas Instruments 14 * 15 * This file is part of the Inventra Controller Driver for Linux. 16 * 17 * SPDX-License-Identifier: GPL-2.0 18 */ 19 #include <common.h> 20 #include <dm.h> 21 #include <asm/arch/cpu.h> 22 #include <asm/arch/clock.h> 23 #include <asm/arch/gpio.h> 24 #include <asm/arch/usb_phy.h> 25 #include <asm-generic/gpio.h> 26 #include <dm/lists.h> 27 #include <dm/root.h> 28 #include <linux/usb/musb.h> 29 #include "linux-compat.h" 30 #include "musb_core.h" 31 #include "musb_uboot.h" 32 33 /****************************************************************************** 34 ****************************************************************************** 35 * From the Allwinner driver 36 ****************************************************************************** 37 ******************************************************************************/ 38 39 /****************************************************************************** 40 * From include/sunxi_usb_bsp.h 41 ******************************************************************************/ 42 43 /* reg offsets */ 44 #define USBC_REG_o_ISCR 0x0400 45 #define USBC_REG_o_PHYCTL 0x0404 46 #define USBC_REG_o_PHYBIST 0x0408 47 #define USBC_REG_o_PHYTUNE 0x040c 48 49 #define USBC_REG_o_VEND0 0x0043 50 51 /* Interface Status and Control */ 52 #define USBC_BP_ISCR_VBUS_VALID_FROM_DATA 30 53 #define USBC_BP_ISCR_VBUS_VALID_FROM_VBUS 29 54 #define USBC_BP_ISCR_EXT_ID_STATUS 28 55 #define USBC_BP_ISCR_EXT_DM_STATUS 27 56 #define USBC_BP_ISCR_EXT_DP_STATUS 26 57 #define USBC_BP_ISCR_MERGED_VBUS_STATUS 25 58 #define USBC_BP_ISCR_MERGED_ID_STATUS 24 59 60 #define USBC_BP_ISCR_ID_PULLUP_EN 17 61 #define USBC_BP_ISCR_DPDM_PULLUP_EN 16 62 #define USBC_BP_ISCR_FORCE_ID 14 63 #define USBC_BP_ISCR_FORCE_VBUS_VALID 12 64 #define USBC_BP_ISCR_VBUS_VALID_SRC 10 65 66 #define USBC_BP_ISCR_HOSC_EN 7 67 #define USBC_BP_ISCR_VBUS_CHANGE_DETECT 6 68 #define USBC_BP_ISCR_ID_CHANGE_DETECT 5 69 #define USBC_BP_ISCR_DPDM_CHANGE_DETECT 4 70 #define USBC_BP_ISCR_IRQ_ENABLE 3 71 #define USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN 2 72 #define USBC_BP_ISCR_ID_CHANGE_DETECT_EN 1 73 #define USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN 0 74 75 /****************************************************************************** 76 * From usbc/usbc.c 77 ******************************************************************************/ 78 79 struct sunxi_glue { 80 struct musb_host_data mdata; 81 struct sunxi_ccm_reg *ccm; 82 struct device dev; 83 }; 84 #define to_sunxi_glue(d) container_of(d, struct sunxi_glue, dev) 85 86 static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val) 87 { 88 u32 temp = reg_val; 89 90 temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT); 91 temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT); 92 temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT); 93 94 return temp; 95 } 96 97 static void USBC_EnableIdPullUp(__iomem void *base) 98 { 99 u32 reg_val; 100 101 reg_val = musb_readl(base, USBC_REG_o_ISCR); 102 reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN); 103 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 104 musb_writel(base, USBC_REG_o_ISCR, reg_val); 105 } 106 107 static void USBC_EnableDpDmPullUp(__iomem void *base) 108 { 109 u32 reg_val; 110 111 reg_val = musb_readl(base, USBC_REG_o_ISCR); 112 reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN); 113 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 114 musb_writel(base, USBC_REG_o_ISCR, reg_val); 115 } 116 117 static void USBC_ForceIdToLow(__iomem void *base) 118 { 119 u32 reg_val; 120 121 reg_val = musb_readl(base, USBC_REG_o_ISCR); 122 reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); 123 reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); 124 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 125 musb_writel(base, USBC_REG_o_ISCR, reg_val); 126 } 127 128 static void USBC_ForceIdToHigh(__iomem void *base) 129 { 130 u32 reg_val; 131 132 reg_val = musb_readl(base, USBC_REG_o_ISCR); 133 reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); 134 reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); 135 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 136 musb_writel(base, USBC_REG_o_ISCR, reg_val); 137 } 138 139 static void USBC_ForceVbusValidToLow(__iomem void *base) 140 { 141 u32 reg_val; 142 143 reg_val = musb_readl(base, USBC_REG_o_ISCR); 144 reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); 145 reg_val |= (0x02 << USBC_BP_ISCR_FORCE_VBUS_VALID); 146 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 147 musb_writel(base, USBC_REG_o_ISCR, reg_val); 148 } 149 150 static void USBC_ForceVbusValidToHigh(__iomem void *base) 151 { 152 u32 reg_val; 153 154 reg_val = musb_readl(base, USBC_REG_o_ISCR); 155 reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); 156 reg_val |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); 157 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 158 musb_writel(base, USBC_REG_o_ISCR, reg_val); 159 } 160 161 static void USBC_ConfigFIFO_Base(void) 162 { 163 u32 reg_value; 164 165 /* config usb fifo, 8kb mode */ 166 reg_value = readl(SUNXI_SRAMC_BASE + 0x04); 167 reg_value &= ~(0x03 << 0); 168 reg_value |= (1 << 0); 169 writel(reg_value, SUNXI_SRAMC_BASE + 0x04); 170 } 171 172 /****************************************************************************** 173 * Needed for the DFU polling magic 174 ******************************************************************************/ 175 176 static u8 last_int_usb; 177 178 bool dfu_usb_get_reset(void) 179 { 180 return !!(last_int_usb & MUSB_INTR_RESET); 181 } 182 183 /****************************************************************************** 184 * MUSB Glue code 185 ******************************************************************************/ 186 187 static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) 188 { 189 struct musb *musb = __hci; 190 irqreturn_t retval = IRQ_NONE; 191 192 /* read and flush interrupts */ 193 musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 194 last_int_usb = musb->int_usb; 195 if (musb->int_usb) 196 musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); 197 musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 198 if (musb->int_tx) 199 musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); 200 musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 201 if (musb->int_rx) 202 musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); 203 204 if (musb->int_usb || musb->int_tx || musb->int_rx) 205 retval |= musb_interrupt(musb); 206 207 return retval; 208 } 209 210 /* musb_core does not call enable / disable in a balanced manner <sigh> */ 211 static bool enabled = false; 212 213 static int sunxi_musb_enable(struct musb *musb) 214 { 215 int ret; 216 217 pr_debug("%s():\n", __func__); 218 219 musb_ep_select(musb->mregs, 0); 220 musb_writeb(musb->mregs, MUSB_FADDR, 0); 221 222 if (enabled) 223 return 0; 224 225 /* select PIO mode */ 226 musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); 227 228 if (is_host_enabled(musb)) { 229 ret = sunxi_usb_phy_vbus_detect(0); 230 if (ret == 1) { 231 printf("A charger is plugged into the OTG: "); 232 return -ENODEV; 233 } 234 ret = sunxi_usb_phy_id_detect(0); 235 if (ret == 1) { 236 printf("No host cable detected: "); 237 return -ENODEV; 238 } 239 sunxi_usb_phy_power_on(0); /* port power on */ 240 } 241 242 USBC_ForceVbusValidToHigh(musb->mregs); 243 244 enabled = true; 245 return 0; 246 } 247 248 static void sunxi_musb_disable(struct musb *musb) 249 { 250 pr_debug("%s():\n", __func__); 251 252 if (!enabled) 253 return; 254 255 if (is_host_enabled(musb)) 256 sunxi_usb_phy_power_off(0); /* port power off */ 257 258 USBC_ForceVbusValidToLow(musb->mregs); 259 mdelay(200); /* Wait for the current session to timeout */ 260 261 enabled = false; 262 } 263 264 static int sunxi_musb_init(struct musb *musb) 265 { 266 struct sunxi_glue *glue = to_sunxi_glue(musb->controller); 267 268 pr_debug("%s():\n", __func__); 269 270 musb->isr = sunxi_musb_interrupt; 271 272 setbits_le32(&glue->ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); 273 #ifdef CONFIG_SUNXI_GEN_SUN6I 274 setbits_le32(&glue->ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0); 275 #endif 276 sunxi_usb_phy_init(0); 277 278 USBC_ConfigFIFO_Base(); 279 USBC_EnableDpDmPullUp(musb->mregs); 280 USBC_EnableIdPullUp(musb->mregs); 281 282 if (is_host_enabled(musb)) { 283 /* Host mode */ 284 USBC_ForceIdToLow(musb->mregs); 285 } else { 286 /* Peripheral mode */ 287 USBC_ForceIdToHigh(musb->mregs); 288 } 289 USBC_ForceVbusValidToHigh(musb->mregs); 290 291 return 0; 292 } 293 294 static const struct musb_platform_ops sunxi_musb_ops = { 295 .init = sunxi_musb_init, 296 .enable = sunxi_musb_enable, 297 .disable = sunxi_musb_disable, 298 }; 299 300 static struct musb_hdrc_config musb_config = { 301 .multipoint = 1, 302 .dyn_fifo = 1, 303 .num_eps = 6, 304 .ram_bits = 11, 305 }; 306 307 static struct musb_hdrc_platform_data musb_plat = { 308 #if defined(CONFIG_USB_MUSB_HOST) 309 .mode = MUSB_HOST, 310 #else 311 .mode = MUSB_PERIPHERAL, 312 #endif 313 .config = &musb_config, 314 .power = 250, 315 .platform_ops = &sunxi_musb_ops, 316 }; 317 318 static int musb_usb_probe(struct udevice *dev) 319 { 320 struct sunxi_glue *glue = dev_get_priv(dev); 321 struct musb_host_data *host = &glue->mdata; 322 struct usb_bus_priv *priv = dev_get_uclass_priv(dev); 323 void *base = dev_read_addr_ptr(dev); 324 int ret; 325 326 if (!base) 327 return -EINVAL; 328 329 glue->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 330 if (IS_ERR(glue->ccm)) 331 return PTR_ERR(glue->ccm); 332 333 priv->desc_before_addr = true; 334 335 #ifdef CONFIG_USB_MUSB_HOST 336 host->host = musb_init_controller(&musb_plat, &glue->dev, base); 337 if (!host->host) 338 return -EIO; 339 340 ret = musb_lowlevel_init(host); 341 if (!ret) 342 printf("Allwinner mUSB OTG (Host)\n"); 343 #else 344 ret = musb_register(&musb_plat, &glue->dev, base); 345 if (!ret) 346 printf("Allwinner mUSB OTG (Peripheral)\n"); 347 #endif 348 349 return ret; 350 } 351 352 static int musb_usb_remove(struct udevice *dev) 353 { 354 struct sunxi_glue *glue = dev_get_priv(dev); 355 struct musb_host_data *host = &glue->mdata; 356 357 musb_stop(host->host); 358 359 sunxi_usb_phy_exit(0); 360 #ifdef CONFIG_SUNXI_GEN_SUN6I 361 clrbits_le32(&glue->ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0); 362 #endif 363 clrbits_le32(&glue->ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); 364 365 free(host->host); 366 host->host = NULL; 367 368 return 0; 369 } 370 371 static const struct udevice_id sunxi_musb_ids[] = { 372 { .compatible = "allwinner,sun4i-a10-musb" }, 373 { .compatible = "allwinner,sun6i-a31-musb" }, 374 { .compatible = "allwinner,sun8i-a33-musb" }, 375 { .compatible = "allwinner,sun8i-h3-musb" }, 376 { } 377 }; 378 379 U_BOOT_DRIVER(usb_musb) = { 380 .name = "sunxi-musb", 381 #ifdef CONFIG_USB_MUSB_HOST 382 .id = UCLASS_USB, 383 #else 384 .id = UCLASS_USB_GADGET_GENERIC, 385 #endif 386 .of_match = sunxi_musb_ids, 387 .probe = musb_usb_probe, 388 .remove = musb_usb_remove, 389 #ifdef CONFIG_USB_MUSB_HOST 390 .ops = &musb_usb_ops, 391 #endif 392 .platdata_auto_alloc_size = sizeof(struct usb_platdata), 393 .priv_auto_alloc_size = sizeof(struct sunxi_glue), 394 }; 395