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_ForceVbusValidToHigh(__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 |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); 155 reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); 156 musb_writel(base, USBC_REG_o_ISCR, reg_val); 157 } 158 159 static void USBC_ConfigFIFO_Base(void) 160 { 161 u32 reg_value; 162 163 /* config usb fifo, 8kb mode */ 164 reg_value = readl(SUNXI_SRAMC_BASE + 0x04); 165 reg_value &= ~(0x03 << 0); 166 reg_value |= (1 << 0); 167 writel(reg_value, SUNXI_SRAMC_BASE + 0x04); 168 } 169 170 /****************************************************************************** 171 * MUSB Glue code 172 ******************************************************************************/ 173 174 static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) 175 { 176 struct musb *musb = __hci; 177 irqreturn_t retval = IRQ_NONE; 178 179 /* read and flush interrupts */ 180 musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 181 if (musb->int_usb) 182 musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); 183 musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 184 if (musb->int_tx) 185 musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); 186 musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 187 if (musb->int_rx) 188 musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); 189 190 if (musb->int_usb || musb->int_tx || musb->int_rx) 191 retval |= musb_interrupt(musb); 192 193 return retval; 194 } 195 196 static void sunxi_musb_enable(struct musb *musb) 197 { 198 pr_debug("%s():\n", __func__); 199 200 /* select PIO mode */ 201 musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); 202 203 if (is_host_enabled(musb)) { 204 /* port power on */ 205 sunxi_usbc_vbus_enable(0); 206 } 207 } 208 209 static void sunxi_musb_disable(struct musb *musb) 210 { 211 pr_debug("%s():\n", __func__); 212 213 /* Put the controller back in a pristane state for "usb reset" */ 214 if (musb->is_active) { 215 sunxi_usbc_disable(0); 216 sunxi_usbc_enable(0); 217 musb->is_active = 0; 218 } 219 } 220 221 static int sunxi_musb_init(struct musb *musb) 222 { 223 int err; 224 225 pr_debug("%s():\n", __func__); 226 227 err = sunxi_usbc_request_resources(0); 228 if (err) 229 return err; 230 231 musb->isr = sunxi_musb_interrupt; 232 sunxi_usbc_enable(0); 233 234 USBC_ConfigFIFO_Base(); 235 USBC_EnableDpDmPullUp(musb->mregs); 236 USBC_EnableIdPullUp(musb->mregs); 237 238 if (is_host_enabled(musb)) { 239 /* Host mode */ 240 USBC_ForceIdToLow(musb->mregs); 241 } else { 242 /* Peripheral mode */ 243 USBC_ForceIdToHigh(musb->mregs); 244 } 245 USBC_ForceVbusValidToHigh(musb->mregs); 246 247 return 0; 248 } 249 250 static int sunxi_musb_exit(struct musb *musb) 251 { 252 pr_debug("%s():\n", __func__); 253 254 USBC_DisableDpDmPullUp(musb->mregs); 255 USBC_DisableIdPullUp(musb->mregs); 256 sunxi_usbc_vbus_disable(0); 257 sunxi_usbc_disable(0); 258 259 return sunxi_usbc_free_resources(0); 260 } 261 262 const struct musb_platform_ops sunxi_musb_ops = { 263 .init = sunxi_musb_init, 264 .exit = sunxi_musb_exit, 265 266 .enable = sunxi_musb_enable, 267 .disable = sunxi_musb_disable, 268 }; 269