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 #ifdef CONFIG_AXP152_POWER 31 #include <axp152.h> 32 #endif 33 #ifdef CONFIG_AXP209_POWER 34 #include <axp209.h> 35 #endif 36 #ifdef CONFIG_AXP221_POWER 37 #include <axp221.h> 38 #endif 39 40 /****************************************************************************** 41 ****************************************************************************** 42 * From the Allwinner driver 43 ****************************************************************************** 44 ******************************************************************************/ 45 46 /****************************************************************************** 47 * From include/sunxi_usb_bsp.h 48 ******************************************************************************/ 49 50 /* reg offsets */ 51 #define USBC_REG_o_ISCR 0x0400 52 #define USBC_REG_o_PHYCTL 0x0404 53 #define USBC_REG_o_PHYBIST 0x0408 54 #define USBC_REG_o_PHYTUNE 0x040c 55 56 #define USBC_REG_o_VEND0 0x0043 57 58 /* Interface Status and Control */ 59 #define USBC_BP_ISCR_VBUS_VALID_FROM_DATA 30 60 #define USBC_BP_ISCR_VBUS_VALID_FROM_VBUS 29 61 #define USBC_BP_ISCR_EXT_ID_STATUS 28 62 #define USBC_BP_ISCR_EXT_DM_STATUS 27 63 #define USBC_BP_ISCR_EXT_DP_STATUS 26 64 #define USBC_BP_ISCR_MERGED_VBUS_STATUS 25 65 #define USBC_BP_ISCR_MERGED_ID_STATUS 24 66 67 #define USBC_BP_ISCR_ID_PULLUP_EN 17 68 #define USBC_BP_ISCR_DPDM_PULLUP_EN 16 69 #define USBC_BP_ISCR_FORCE_ID 14 70 #define USBC_BP_ISCR_FORCE_VBUS_VALID 12 71 #define USBC_BP_ISCR_VBUS_VALID_SRC 10 72 73 #define USBC_BP_ISCR_HOSC_EN 7 74 #define USBC_BP_ISCR_VBUS_CHANGE_DETECT 6 75 #define USBC_BP_ISCR_ID_CHANGE_DETECT 5 76 #define USBC_BP_ISCR_DPDM_CHANGE_DETECT 4 77 #define USBC_BP_ISCR_IRQ_ENABLE 3 78 #define USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN 2 79 #define USBC_BP_ISCR_ID_CHANGE_DETECT_EN 1 80 #define USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN 0 81 82 /****************************************************************************** 83 * From usbc/usbc.c 84 ******************************************************************************/ 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_DisableIdPullUp(__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_ID_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_EnableDpDmPullUp(__iomem void *base) 118 { 119 u32 reg_val; 120 121 reg_val = musb_readl(base, USBC_REG_o_ISCR); 122 reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN); 123 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 124 musb_writel(base, USBC_REG_o_ISCR, reg_val); 125 } 126 127 static void USBC_DisableDpDmPullUp(__iomem void *base) 128 { 129 u32 reg_val; 130 131 reg_val = musb_readl(base, USBC_REG_o_ISCR); 132 reg_val &= ~(1 << USBC_BP_ISCR_DPDM_PULLUP_EN); 133 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 134 musb_writel(base, USBC_REG_o_ISCR, reg_val); 135 } 136 137 static void USBC_ForceIdToLow(__iomem void *base) 138 { 139 u32 reg_val; 140 141 reg_val = musb_readl(base, USBC_REG_o_ISCR); 142 reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); 143 reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); 144 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 145 musb_writel(base, USBC_REG_o_ISCR, reg_val); 146 } 147 148 static void USBC_ForceIdToHigh(__iomem void *base) 149 { 150 u32 reg_val; 151 152 reg_val = musb_readl(base, USBC_REG_o_ISCR); 153 reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); 154 reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); 155 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 156 musb_writel(base, USBC_REG_o_ISCR, reg_val); 157 } 158 159 static void USBC_ForceVbusValidToHigh(__iomem void *base) 160 { 161 u32 reg_val; 162 163 reg_val = musb_readl(base, USBC_REG_o_ISCR); 164 reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); 165 reg_val |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); 166 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 167 musb_writel(base, USBC_REG_o_ISCR, reg_val); 168 } 169 170 static void USBC_ConfigFIFO_Base(void) 171 { 172 u32 reg_value; 173 174 /* config usb fifo, 8kb mode */ 175 reg_value = readl(SUNXI_SRAMC_BASE + 0x04); 176 reg_value &= ~(0x03 << 0); 177 reg_value |= (1 << 0); 178 writel(reg_value, SUNXI_SRAMC_BASE + 0x04); 179 } 180 181 /****************************************************************************** 182 * MUSB Glue code 183 ******************************************************************************/ 184 185 static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) 186 { 187 struct musb *musb = __hci; 188 irqreturn_t retval = IRQ_NONE; 189 190 /* read and flush interrupts */ 191 musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 192 if (musb->int_usb) 193 musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); 194 musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 195 if (musb->int_tx) 196 musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); 197 musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 198 if (musb->int_rx) 199 musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); 200 201 if (musb->int_usb || musb->int_tx || musb->int_rx) 202 retval |= musb_interrupt(musb); 203 204 return retval; 205 } 206 207 static void sunxi_musb_enable(struct musb *musb) 208 { 209 pr_debug("%s():\n", __func__); 210 211 /* select PIO mode */ 212 musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); 213 214 if (is_host_enabled(musb)) { 215 /* port power on */ 216 sunxi_usbc_vbus_enable(0); 217 } 218 } 219 220 static void sunxi_musb_disable(struct musb *musb) 221 { 222 pr_debug("%s():\n", __func__); 223 224 /* Put the controller back in a pristane state for "usb reset" */ 225 if (musb->is_active) { 226 sunxi_usbc_disable(0); 227 sunxi_usbc_enable(0); 228 musb->is_active = 0; 229 } 230 } 231 232 static int sunxi_musb_init(struct musb *musb) 233 { 234 int err; 235 236 pr_debug("%s():\n", __func__); 237 238 if (is_host_enabled(musb)) { 239 int vbus_det = sunxi_name_to_gpio(CONFIG_USB0_VBUS_DET); 240 241 #ifdef AXP_VBUS_DETECT 242 if (!strcmp(CONFIG_USB0_VBUS_DET, "axp_vbus_detect")) { 243 err = axp_get_vbus(); 244 if (err < 0) 245 return err; 246 } else { 247 #endif 248 if (vbus_det == -1) { 249 eprintf("Error invalid Vusb-det pin\n"); 250 return -EINVAL; 251 } 252 253 err = gpio_request(vbus_det, "vbus0_det"); 254 if (err) 255 return err; 256 257 err = gpio_direction_input(vbus_det); 258 if (err) { 259 gpio_free(vbus_det); 260 return err; 261 } 262 263 err = gpio_get_value(vbus_det); 264 if (err < 0) { 265 gpio_free(vbus_det); 266 return -EIO; 267 } 268 269 gpio_free(vbus_det); 270 #ifdef AXP_VBUS_DETECT 271 } 272 #endif 273 274 if (err) { 275 eprintf("Error: A charger is plugged into the OTG\n"); 276 return -EIO; 277 } 278 } 279 280 err = sunxi_usbc_request_resources(0); 281 if (err) 282 return err; 283 284 musb->isr = sunxi_musb_interrupt; 285 sunxi_usbc_enable(0); 286 287 USBC_ConfigFIFO_Base(); 288 USBC_EnableDpDmPullUp(musb->mregs); 289 USBC_EnableIdPullUp(musb->mregs); 290 291 if (is_host_enabled(musb)) { 292 /* Host mode */ 293 USBC_ForceIdToLow(musb->mregs); 294 } else { 295 /* Peripheral mode */ 296 USBC_ForceIdToHigh(musb->mregs); 297 } 298 USBC_ForceVbusValidToHigh(musb->mregs); 299 300 return 0; 301 } 302 303 static int sunxi_musb_exit(struct musb *musb) 304 { 305 pr_debug("%s():\n", __func__); 306 307 USBC_DisableDpDmPullUp(musb->mregs); 308 USBC_DisableIdPullUp(musb->mregs); 309 sunxi_usbc_vbus_disable(0); 310 sunxi_usbc_disable(0); 311 312 return sunxi_usbc_free_resources(0); 313 } 314 315 const struct musb_platform_ops sunxi_musb_ops = { 316 .init = sunxi_musb_init, 317 .exit = sunxi_musb_exit, 318 319 .enable = sunxi_musb_enable, 320 .disable = sunxi_musb_disable, 321 }; 322