]>
Commit | Line | Data |
---|---|---|
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 | ||
43 | static 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 | ||
70 | static int enabled_hcd_count; | |
71 | ||
72 | void *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 | ||
86 | static 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 |
96 | static 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 |
104 | static 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 | ||
136 | static 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 | ||
159 | static 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 |
178 | void 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 |
185 | int 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 | ||
205 | int 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 | ||
219 | void 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 | ||
242 | void 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 | ||
263 | void 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 | ||
271 | void 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 | |
279 | int 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 | } |