]> git.ipfire.org Git - people/ms/u-boot.git/blame - arch/arm/cpu/armv7/sunxi/usbc.c
sunxi: Add basic A33 basic support
[people/ms/u-boot.git] / arch / arm / cpu / armv7 / sunxi / usbc.c
CommitLineData
0eccec4e
HG
1/*
2 * Sunxi usb-controller code shared between the ehci and musb controllers
3 *
4 * Copyright (C) 2014 Roman Byshko
5 *
6 * Roman Byshko <rbyshko@gmail.com>
7 *
8 * Based on code from
9 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
10 *
11 * SPDX-License-Identifier: GPL-2.0+
12 */
13
14#include <asm/arch/clock.h>
15#include <asm/arch/cpu.h>
16#include <asm/arch/usbc.h>
17#include <asm/gpio.h>
18#include <asm/io.h>
19#include <common.h>
a90e77db
HG
20#ifdef CONFIG_AXP152_POWER
21#include <axp152.h>
22#endif
23#ifdef CONFIG_AXP209_POWER
24#include <axp209.h>
25#endif
26#ifdef CONFIG_AXP221_POWER
27#include <axp221.h>
28#endif
0eccec4e
HG
29
30#define SUNXI_USB_PMU_IRQ_ENABLE 0x800
8c3dacff
VP
31#ifdef CONFIG_MACH_SUN8I_A33
32#define SUNXI_USB_CSR 0x410
33#else
0eccec4e 34#define SUNXI_USB_CSR 0x404
8c3dacff 35#endif
0eccec4e
HG
36#define SUNXI_USB_PASSBY_EN 1
37
38#define SUNXI_EHCI_AHB_ICHR8_EN (1 << 10)
39#define SUNXI_EHCI_AHB_INCR4_BURST_EN (1 << 9)
40#define SUNXI_EHCI_AHB_INCRX_ALIGN_EN (1 << 8)
41#define SUNXI_EHCI_ULPI_BYPASS_EN (1 << 0)
42
43static struct sunxi_usbc_hcd {
44 struct usb_hcd *hcd;
45 int usb_rst_mask;
46 int ahb_clk_mask;
47 int gpio_vbus;
ebd468b2 48 int gpio_vbus_det;
0eccec4e
HG
49 int id;
50} sunxi_usbc_hcd[] = {
4458b7a6
HG
51 {
52 .usb_rst_mask = CCM_USB_CTRL_PHY0_RST | CCM_USB_CTRL_PHY0_CLK,
53 .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB0,
4458b7a6
HG
54 .id = 0,
55 },
0eccec4e
HG
56 {
57 .usb_rst_mask = CCM_USB_CTRL_PHY1_RST | CCM_USB_CTRL_PHY1_CLK,
58 .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0,
0eccec4e
HG
59 .id = 1,
60 },
61#if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1)
62 {
63 .usb_rst_mask = CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK,
64 .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1,
0eccec4e
HG
65 .id = 2,
66 }
67#endif
68};
69
70static int enabled_hcd_count;
71
72void *sunxi_usbc_get_io_base(int index)
73{
74 switch (index) {
75 case 0:
76 return (void *)SUNXI_USB0_BASE;
77 case 1:
78 return (void *)SUNXI_USB1_BASE;
79 case 2:
80 return (void *)SUNXI_USB2_BASE;
81 default:
82 return NULL;
83 }
84}
85
86static int get_vbus_gpio(int index)
87{
88 switch (index) {
4458b7a6 89 case 0: return sunxi_name_to_gpio(CONFIG_USB0_VBUS_PIN);
0eccec4e
HG
90 case 1: return sunxi_name_to_gpio(CONFIG_USB1_VBUS_PIN);
91 case 2: return sunxi_name_to_gpio(CONFIG_USB2_VBUS_PIN);
92 }
93 return -1;
94}
95
ebd468b2
PK
96static int get_vbus_detect_gpio(int index)
97{
98 switch (index) {
99 case 0: return sunxi_name_to_gpio(CONFIG_USB0_VBUS_DET);
100 }
101 return -1;
102}
103
0eccec4e
HG
104static void usb_phy_write(struct sunxi_usbc_hcd *sunxi_usbc, int addr,
105 int data, int len)
106{
107 int j = 0, usbc_bit = 0;
108 void *dest = sunxi_usbc_get_io_base(0) + SUNXI_USB_CSR;
109
8c3dacff
VP
110#ifdef CONFIG_MACH_SUN8I_A33
111 /* CSR needs to be explicitly initialized to 0 on A33 */
112 writel(0, dest);
113#endif
114
0eccec4e
HG
115 usbc_bit = 1 << (sunxi_usbc->id * 2);
116 for (j = 0; j < len; j++) {
117 /* set the bit address to be written */
118 clrbits_le32(dest, 0xff << 8);
119 setbits_le32(dest, (addr + j) << 8);
120
121 clrbits_le32(dest, usbc_bit);
122 /* set data bit */
123 if (data & 0x1)
124 setbits_le32(dest, 1 << 7);
125 else
126 clrbits_le32(dest, 1 << 7);
127
128 setbits_le32(dest, usbc_bit);
129
130 clrbits_le32(dest, usbc_bit);
131
132 data >>= 1;
133 }
134}
135
136static void sunxi_usb_phy_init(struct sunxi_usbc_hcd *sunxi_usbc)
137{
138 /* The following comments are machine
139 * translated from Chinese, you have been warned!
140 */
141
4458b7a6
HG
142 /* Regulation 45 ohms */
143 if (sunxi_usbc->id == 0)
144 usb_phy_write(sunxi_usbc, 0x0c, 0x01, 1);
145
0eccec4e
HG
146 /* adjust PHY's magnitude and rate */
147 usb_phy_write(sunxi_usbc, 0x20, 0x14, 5);
148
149 /* threshold adjustment disconnect */
150#if defined CONFIG_MACH_SUN4I || defined CONFIG_MACH_SUN6I
151 usb_phy_write(sunxi_usbc, 0x2a, 3, 2);
152#else
153 usb_phy_write(sunxi_usbc, 0x2a, 2, 2);
154#endif
155
156 return;
157}
158
159static void sunxi_usb_passby(struct sunxi_usbc_hcd *sunxi_usbc, int enable)
160{
161 unsigned long bits = 0;
162 void *addr = sunxi_usbc_get_io_base(sunxi_usbc->id) +
163 SUNXI_USB_PMU_IRQ_ENABLE;
164
165 bits = SUNXI_EHCI_AHB_ICHR8_EN |
166 SUNXI_EHCI_AHB_INCR4_BURST_EN |
167 SUNXI_EHCI_AHB_INCRX_ALIGN_EN |
168 SUNXI_EHCI_ULPI_BYPASS_EN;
169
170 if (enable)
171 setbits_le32(addr, bits);
172 else
173 clrbits_le32(addr, bits);
174
175 return;
176}
177
246e3b87
HG
178void sunxi_usbc_enable_squelch_detect(int index, int enable)
179{
180 struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
181
182 usb_phy_write(sunxi_usbc, 0x3c, enable ? 0 : 2, 2);
183}
184
0eccec4e
HG
185int sunxi_usbc_request_resources(int index)
186{
4458b7a6 187 struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
ebd468b2 188 int ret = 0;
0eccec4e
HG
189
190 sunxi_usbc->gpio_vbus = get_vbus_gpio(index);
046ea8b3 191 if (sunxi_usbc->gpio_vbus != -1) {
ebd468b2 192 ret |= gpio_request(sunxi_usbc->gpio_vbus, "usbc_vbus");
046ea8b3
HG
193 ret |= gpio_direction_output(sunxi_usbc->gpio_vbus, 0);
194 }
0eccec4e 195
ebd468b2 196 sunxi_usbc->gpio_vbus_det = get_vbus_detect_gpio(index);
046ea8b3 197 if (sunxi_usbc->gpio_vbus_det != -1) {
ebd468b2 198 ret |= gpio_request(sunxi_usbc->gpio_vbus_det, "usbc_vbus_det");
046ea8b3
HG
199 ret |= gpio_direction_input(sunxi_usbc->gpio_vbus_det);
200 }
ebd468b2
PK
201
202 return ret;
0eccec4e
HG
203}
204
205int sunxi_usbc_free_resources(int index)
206{
4458b7a6 207 struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
ebd468b2 208 int ret = 0;
0eccec4e
HG
209
210 if (sunxi_usbc->gpio_vbus != -1)
ebd468b2
PK
211 ret |= gpio_free(sunxi_usbc->gpio_vbus);
212
213 if (sunxi_usbc->gpio_vbus_det != -1)
214 ret |= gpio_free(sunxi_usbc->gpio_vbus_det);
0eccec4e 215
ebd468b2 216 return ret;
0eccec4e
HG
217}
218
219void sunxi_usbc_enable(int index)
220{
4458b7a6 221 struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
0eccec4e
HG
222 struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
223
224 /* enable common PHY only once */
225 if (enabled_hcd_count == 0)
226 setbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE);
227
228 setbits_le32(&ccm->usb_clk_cfg, sunxi_usbc->usb_rst_mask);
229 setbits_le32(&ccm->ahb_gate0, sunxi_usbc->ahb_clk_mask);
44d8ae5b 230#ifdef CONFIG_SUNXI_GEN_SUN6I
0eccec4e
HG
231 setbits_le32(&ccm->ahb_reset0_cfg, sunxi_usbc->ahb_clk_mask);
232#endif
233
234 sunxi_usb_phy_init(sunxi_usbc);
235
4458b7a6
HG
236 if (sunxi_usbc->id != 0)
237 sunxi_usb_passby(sunxi_usbc, SUNXI_USB_PASSBY_EN);
0eccec4e
HG
238
239 enabled_hcd_count++;
240}
241
242void sunxi_usbc_disable(int index)
243{
4458b7a6 244 struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
0eccec4e
HG
245 struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
246
4458b7a6
HG
247 if (sunxi_usbc->id != 0)
248 sunxi_usb_passby(sunxi_usbc, !SUNXI_USB_PASSBY_EN);
0eccec4e 249
44d8ae5b 250#ifdef CONFIG_SUNXI_GEN_SUN6I
0eccec4e
HG
251 clrbits_le32(&ccm->ahb_reset0_cfg, sunxi_usbc->ahb_clk_mask);
252#endif
253 clrbits_le32(&ccm->ahb_gate0, sunxi_usbc->ahb_clk_mask);
254 clrbits_le32(&ccm->usb_clk_cfg, sunxi_usbc->usb_rst_mask);
255
256 /* disable common PHY only once, for the last enabled hcd */
257 if (enabled_hcd_count == 1)
258 clrbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE);
259
260 enabled_hcd_count--;
261}
262
263void sunxi_usbc_vbus_enable(int index)
264{
4458b7a6 265 struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
0eccec4e
HG
266
267 if (sunxi_usbc->gpio_vbus != -1)
046ea8b3 268 gpio_set_value(sunxi_usbc->gpio_vbus, 1);
0eccec4e
HG
269}
270
271void sunxi_usbc_vbus_disable(int index)
272{
4458b7a6 273 struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
0eccec4e
HG
274
275 if (sunxi_usbc->gpio_vbus != -1)
046ea8b3 276 gpio_set_value(sunxi_usbc->gpio_vbus, 0);
0eccec4e 277}
ebd468b2
PK
278
279int sunxi_usbc_vbus_detect(int index)
280{
281 struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
a0e2b1b8 282 int err, retries = 3;
ebd468b2
PK
283
284 if (sunxi_usbc->gpio_vbus_det == -1) {
285 eprintf("Error: invalid vbus detection pin\n");
286 return -1;
287 }
288
a0e2b1b8
HG
289 err = gpio_get_value(sunxi_usbc->gpio_vbus_det);
290 /*
291 * Vbus may have been provided by the board and just been turned of
292 * some milliseconds ago on reset, what we're measuring then is a
293 * residual charge on Vbus, sleep a bit and try again.
294 */
295 while (err > 0 && retries--) {
296 mdelay(100);
297 err = gpio_get_value(sunxi_usbc->gpio_vbus_det);
298 }
299
300 return err;
ebd468b2 301}