xref: /openbmc/u-boot/drivers/usb/musb-new/sunxi.c (revision 86a390d3)
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