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/usbc.h> 26 #include "linux-compat.h" 27 #include "musb_core.h" 28 29 /****************************************************************************** 30 ****************************************************************************** 31 * From the Allwinner driver 32 ****************************************************************************** 33 ******************************************************************************/ 34 35 /****************************************************************************** 36 * From include/sunxi_usb_bsp.h 37 ******************************************************************************/ 38 39 /* reg offsets */ 40 #define USBC_REG_o_ISCR 0x0400 41 #define USBC_REG_o_PHYCTL 0x0404 42 #define USBC_REG_o_PHYBIST 0x0408 43 #define USBC_REG_o_PHYTUNE 0x040c 44 45 #define USBC_REG_o_VEND0 0x0043 46 47 /* Interface Status and Control */ 48 #define USBC_BP_ISCR_VBUS_VALID_FROM_DATA 30 49 #define USBC_BP_ISCR_VBUS_VALID_FROM_VBUS 29 50 #define USBC_BP_ISCR_EXT_ID_STATUS 28 51 #define USBC_BP_ISCR_EXT_DM_STATUS 27 52 #define USBC_BP_ISCR_EXT_DP_STATUS 26 53 #define USBC_BP_ISCR_MERGED_VBUS_STATUS 25 54 #define USBC_BP_ISCR_MERGED_ID_STATUS 24 55 56 #define USBC_BP_ISCR_ID_PULLUP_EN 17 57 #define USBC_BP_ISCR_DPDM_PULLUP_EN 16 58 #define USBC_BP_ISCR_FORCE_ID 14 59 #define USBC_BP_ISCR_FORCE_VBUS_VALID 12 60 #define USBC_BP_ISCR_VBUS_VALID_SRC 10 61 62 #define USBC_BP_ISCR_HOSC_EN 7 63 #define USBC_BP_ISCR_VBUS_CHANGE_DETECT 6 64 #define USBC_BP_ISCR_ID_CHANGE_DETECT 5 65 #define USBC_BP_ISCR_DPDM_CHANGE_DETECT 4 66 #define USBC_BP_ISCR_IRQ_ENABLE 3 67 #define USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN 2 68 #define USBC_BP_ISCR_ID_CHANGE_DETECT_EN 1 69 #define USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN 0 70 71 /****************************************************************************** 72 * From usbc/usbc.c 73 ******************************************************************************/ 74 75 static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val) 76 { 77 u32 temp = reg_val; 78 79 temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT); 80 temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT); 81 temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT); 82 83 return temp; 84 } 85 86 static void USBC_EnableIdPullUp(__iomem void *base) 87 { 88 u32 reg_val; 89 90 reg_val = musb_readl(base, USBC_REG_o_ISCR); 91 reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN); 92 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 93 musb_writel(base, USBC_REG_o_ISCR, reg_val); 94 } 95 96 static void USBC_DisableIdPullUp(__iomem void *base) 97 { 98 u32 reg_val; 99 100 reg_val = musb_readl(base, USBC_REG_o_ISCR); 101 reg_val &= ~(1 << USBC_BP_ISCR_ID_PULLUP_EN); 102 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 103 musb_writel(base, USBC_REG_o_ISCR, reg_val); 104 } 105 106 static void USBC_EnableDpDmPullUp(__iomem void *base) 107 { 108 u32 reg_val; 109 110 reg_val = musb_readl(base, USBC_REG_o_ISCR); 111 reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN); 112 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 113 musb_writel(base, USBC_REG_o_ISCR, reg_val); 114 } 115 116 static void USBC_DisableDpDmPullUp(__iomem void *base) 117 { 118 u32 reg_val; 119 120 reg_val = musb_readl(base, USBC_REG_o_ISCR); 121 reg_val &= ~(1 << USBC_BP_ISCR_DPDM_PULLUP_EN); 122 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 123 musb_writel(base, USBC_REG_o_ISCR, reg_val); 124 } 125 126 static void USBC_ForceIdToLow(__iomem void *base) 127 { 128 u32 reg_val; 129 130 reg_val = musb_readl(base, USBC_REG_o_ISCR); 131 reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); 132 reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); 133 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 134 musb_writel(base, USBC_REG_o_ISCR, reg_val); 135 } 136 137 static void USBC_ForceIdToHigh(__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 |= (0x03 << 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_ForceVbusValidDisable(__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_VBUS_VALID); 154 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 155 musb_writel(base, USBC_REG_o_ISCR, reg_val); 156 } 157 158 static void USBC_ForceVbusValidToHigh(__iomem void *base) 159 { 160 u32 reg_val; 161 162 reg_val = musb_readl(base, USBC_REG_o_ISCR); 163 reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); 164 reg_val |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); 165 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 166 musb_writel(base, USBC_REG_o_ISCR, reg_val); 167 } 168 169 static void USBC_ConfigFIFO_Base(void) 170 { 171 u32 reg_value; 172 173 /* config usb fifo, 8kb mode */ 174 reg_value = readl(SUNXI_SRAMC_BASE + 0x04); 175 reg_value &= ~(0x03 << 0); 176 reg_value |= (1 << 0); 177 writel(reg_value, SUNXI_SRAMC_BASE + 0x04); 178 } 179 180 /****************************************************************************** 181 * MUSB Glue code 182 ******************************************************************************/ 183 184 static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) 185 { 186 struct musb *musb = __hci; 187 irqreturn_t retval = IRQ_NONE; 188 189 /* read and flush interrupts */ 190 musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 191 if (musb->int_usb) 192 musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); 193 musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 194 if (musb->int_tx) 195 musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); 196 musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 197 if (musb->int_rx) 198 musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); 199 200 if (musb->int_usb || musb->int_tx || musb->int_rx) 201 retval |= musb_interrupt(musb); 202 203 return retval; 204 } 205 206 static void sunxi_musb_enable(struct musb *musb) 207 { 208 pr_debug("%s():\n", __func__); 209 210 /* select PIO mode */ 211 musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); 212 213 if (is_host_enabled(musb)) { 214 /* port power on */ 215 sunxi_usbc_vbus_enable(0); 216 } 217 } 218 219 static void sunxi_musb_disable(struct musb *musb) 220 { 221 pr_debug("%s():\n", __func__); 222 223 /* Put the controller back in a pristane state for "usb reset" */ 224 if (musb->is_active) { 225 sunxi_usbc_disable(0); 226 sunxi_usbc_enable(0); 227 musb->is_active = 0; 228 } 229 } 230 231 static int sunxi_musb_init(struct musb *musb) 232 { 233 int err; 234 235 pr_debug("%s():\n", __func__); 236 237 err = sunxi_usbc_request_resources(0); 238 if (err) 239 return err; 240 241 musb->isr = sunxi_musb_interrupt; 242 sunxi_usbc_enable(0); 243 244 USBC_ConfigFIFO_Base(); 245 USBC_EnableDpDmPullUp(musb->mregs); 246 USBC_EnableIdPullUp(musb->mregs); 247 248 if (is_host_enabled(musb)) { 249 /* Host mode */ 250 USBC_ForceIdToLow(musb->mregs); 251 USBC_ForceVbusValidToHigh(musb->mregs); 252 } else { 253 /* Peripheral mode */ 254 USBC_ForceIdToHigh(musb->mregs); 255 USBC_ForceVbusValidDisable(musb->mregs); 256 } 257 258 return 0; 259 } 260 261 static int sunxi_musb_exit(struct musb *musb) 262 { 263 pr_debug("%s():\n", __func__); 264 265 USBC_DisableDpDmPullUp(musb->mregs); 266 USBC_DisableIdPullUp(musb->mregs); 267 sunxi_usbc_vbus_disable(0); 268 sunxi_usbc_disable(0); 269 270 return sunxi_usbc_free_resources(0); 271 } 272 273 const struct musb_platform_ops sunxi_musb_ops = { 274 .init = sunxi_musb_init, 275 .exit = sunxi_musb_exit, 276 277 .enable = sunxi_musb_enable, 278 .disable = sunxi_musb_disable, 279 }; 280