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 * The Inventra Controller Driver for Linux is free software; you 18 * can redistribute it and/or modify it under the terms of the GNU 19 * General Public License version 2 as published by the Free Software 20 * Foundation. 21 * 22 */ 23 #include <common.h> 24 #include <asm/arch/cpu.h> 25 #include <asm/arch/gpio.h> 26 #include <asm/arch/usbc.h> 27 #include <asm-generic/gpio.h> 28 #include "linux-compat.h" 29 #include "musb_core.h" 30 31 /****************************************************************************** 32 ****************************************************************************** 33 * From the Allwinner driver 34 ****************************************************************************** 35 ******************************************************************************/ 36 37 /****************************************************************************** 38 * From include/sunxi_usb_bsp.h 39 ******************************************************************************/ 40 41 /* reg offsets */ 42 #define USBC_REG_o_ISCR 0x0400 43 #define USBC_REG_o_PHYCTL 0x0404 44 #define USBC_REG_o_PHYBIST 0x0408 45 #define USBC_REG_o_PHYTUNE 0x040c 46 47 #define USBC_REG_o_VEND0 0x0043 48 49 /* Interface Status and Control */ 50 #define USBC_BP_ISCR_VBUS_VALID_FROM_DATA 30 51 #define USBC_BP_ISCR_VBUS_VALID_FROM_VBUS 29 52 #define USBC_BP_ISCR_EXT_ID_STATUS 28 53 #define USBC_BP_ISCR_EXT_DM_STATUS 27 54 #define USBC_BP_ISCR_EXT_DP_STATUS 26 55 #define USBC_BP_ISCR_MERGED_VBUS_STATUS 25 56 #define USBC_BP_ISCR_MERGED_ID_STATUS 24 57 58 #define USBC_BP_ISCR_ID_PULLUP_EN 17 59 #define USBC_BP_ISCR_DPDM_PULLUP_EN 16 60 #define USBC_BP_ISCR_FORCE_ID 14 61 #define USBC_BP_ISCR_FORCE_VBUS_VALID 12 62 #define USBC_BP_ISCR_VBUS_VALID_SRC 10 63 64 #define USBC_BP_ISCR_HOSC_EN 7 65 #define USBC_BP_ISCR_VBUS_CHANGE_DETECT 6 66 #define USBC_BP_ISCR_ID_CHANGE_DETECT 5 67 #define USBC_BP_ISCR_DPDM_CHANGE_DETECT 4 68 #define USBC_BP_ISCR_IRQ_ENABLE 3 69 #define USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN 2 70 #define USBC_BP_ISCR_ID_CHANGE_DETECT_EN 1 71 #define USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN 0 72 73 /****************************************************************************** 74 * From usbc/usbc.c 75 ******************************************************************************/ 76 77 static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val) 78 { 79 u32 temp = reg_val; 80 81 temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT); 82 temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT); 83 temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT); 84 85 return temp; 86 } 87 88 static void USBC_EnableIdPullUp(__iomem void *base) 89 { 90 u32 reg_val; 91 92 reg_val = musb_readl(base, USBC_REG_o_ISCR); 93 reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN); 94 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 95 musb_writel(base, USBC_REG_o_ISCR, reg_val); 96 } 97 98 static void USBC_DisableIdPullUp(__iomem void *base) 99 { 100 u32 reg_val; 101 102 reg_val = musb_readl(base, USBC_REG_o_ISCR); 103 reg_val &= ~(1 << USBC_BP_ISCR_ID_PULLUP_EN); 104 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 105 musb_writel(base, USBC_REG_o_ISCR, reg_val); 106 } 107 108 static void USBC_EnableDpDmPullUp(__iomem void *base) 109 { 110 u32 reg_val; 111 112 reg_val = musb_readl(base, USBC_REG_o_ISCR); 113 reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN); 114 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 115 musb_writel(base, USBC_REG_o_ISCR, reg_val); 116 } 117 118 static void USBC_DisableDpDmPullUp(__iomem void *base) 119 { 120 u32 reg_val; 121 122 reg_val = musb_readl(base, USBC_REG_o_ISCR); 123 reg_val &= ~(1 << USBC_BP_ISCR_DPDM_PULLUP_EN); 124 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 125 musb_writel(base, USBC_REG_o_ISCR, reg_val); 126 } 127 128 static void USBC_ForceIdToLow(__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 |= (0x02 << 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_ForceIdToHigh(__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_ID); 145 reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); 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 * MUSB Glue code 174 ******************************************************************************/ 175 176 static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) 177 { 178 struct musb *musb = __hci; 179 irqreturn_t retval = IRQ_NONE; 180 181 /* read and flush interrupts */ 182 musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 183 if (musb->int_usb) 184 musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); 185 musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 186 if (musb->int_tx) 187 musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); 188 musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 189 if (musb->int_rx) 190 musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); 191 192 if (musb->int_usb || musb->int_tx || musb->int_rx) 193 retval |= musb_interrupt(musb); 194 195 return retval; 196 } 197 198 static void sunxi_musb_enable(struct musb *musb) 199 { 200 pr_debug("%s():\n", __func__); 201 202 /* select PIO mode */ 203 musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); 204 205 if (is_host_enabled(musb)) { 206 /* port power on */ 207 sunxi_usbc_vbus_enable(0); 208 } 209 } 210 211 static void sunxi_musb_disable(struct musb *musb) 212 { 213 pr_debug("%s():\n", __func__); 214 215 /* Put the controller back in a pristane state for "usb reset" */ 216 if (musb->is_active) { 217 sunxi_usbc_disable(0); 218 sunxi_usbc_enable(0); 219 musb->is_active = 0; 220 } 221 } 222 223 static int sunxi_musb_init(struct musb *musb) 224 { 225 int err; 226 227 pr_debug("%s():\n", __func__); 228 229 if (is_host_enabled(musb)) { 230 int vbus_det = sunxi_name_to_gpio(CONFIG_USB0_VBUS_DET); 231 if (vbus_det == -1) { 232 eprintf("Error invalid Vusb-det pin\n"); 233 return -EINVAL; 234 } 235 236 err = gpio_request(vbus_det, "vbus0_det"); 237 if (err) 238 return err; 239 240 err = gpio_direction_input(vbus_det); 241 if (err) { 242 gpio_free(vbus_det); 243 return err; 244 } 245 246 err = gpio_get_value(vbus_det); 247 if (err) { 248 eprintf("Error: A charger is plugged into the OTG\n"); 249 gpio_free(vbus_det); 250 return -EIO; 251 } 252 253 gpio_free(vbus_det); 254 } 255 256 err = sunxi_usbc_request_resources(0); 257 if (err) 258 return err; 259 260 musb->isr = sunxi_musb_interrupt; 261 sunxi_usbc_enable(0); 262 263 USBC_ConfigFIFO_Base(); 264 USBC_EnableDpDmPullUp(musb->mregs); 265 USBC_EnableIdPullUp(musb->mregs); 266 267 if (is_host_enabled(musb)) { 268 /* Host mode */ 269 USBC_ForceIdToLow(musb->mregs); 270 } else { 271 /* Peripheral mode */ 272 USBC_ForceIdToHigh(musb->mregs); 273 } 274 USBC_ForceVbusValidToHigh(musb->mregs); 275 276 return 0; 277 } 278 279 static int sunxi_musb_exit(struct musb *musb) 280 { 281 pr_debug("%s():\n", __func__); 282 283 USBC_DisableDpDmPullUp(musb->mregs); 284 USBC_DisableIdPullUp(musb->mregs); 285 sunxi_usbc_vbus_disable(0); 286 sunxi_usbc_disable(0); 287 288 return sunxi_usbc_free_resources(0); 289 } 290 291 const struct musb_platform_ops sunxi_musb_ops = { 292 .init = sunxi_musb_init, 293 .exit = sunxi_musb_exit, 294 295 .enable = sunxi_musb_enable, 296 .disable = sunxi_musb_disable, 297 }; 298