]>
Commit | Line | Data |
---|---|---|
8d154002 RB |
1 | /* |
2 | * Copyright (C) 2014 Roman Byshko | |
3 | * | |
4 | * Roman Byshko <rbyshko@gmail.com> | |
5 | * | |
6 | * Based on code from | |
7 | * Allwinner Technology Co., Ltd. <www.allwinnertech.com> | |
8 | * | |
9 | * SPDX-License-Identifier: GPL-2.0+ | |
10 | */ | |
11 | ||
12 | #include <asm/arch/clock.h> | |
13 | #include <asm/gpio.h> | |
14 | #include <asm/io.h> | |
15 | #include <common.h> | |
16 | #include "ehci.h" | |
17 | ||
18 | #define SUNXI_USB1_IO_BASE 0x01c14000 | |
19 | #define SUNXI_USB2_IO_BASE 0x01c1c000 | |
20 | ||
21 | #define SUNXI_USB_PMU_IRQ_ENABLE 0x800 | |
22 | #define SUNXI_USB_CSR 0x01c13404 | |
23 | #define SUNXI_USB_PASSBY_EN 1 | |
24 | ||
25 | #define SUNXI_EHCI_AHB_ICHR8_EN (1 << 10) | |
26 | #define SUNXI_EHCI_AHB_INCR4_BURST_EN (1 << 9) | |
27 | #define SUNXI_EHCI_AHB_INCRX_ALIGN_EN (1 << 8) | |
28 | #define SUNXI_EHCI_ULPI_BYPASS_EN (1 << 0) | |
29 | ||
30 | static struct sunxi_ehci_hcd { | |
31 | struct usb_hcd *hcd; | |
32 | int usb_rst_mask; | |
33 | int ahb_clk_mask; | |
34 | int gpio_vbus; | |
35 | void *csr; | |
36 | int irq; | |
37 | int id; | |
38 | } sunxi_echi_hcd[] = { | |
39 | { | |
40 | .usb_rst_mask = CCM_USB_CTRL_PHY1_RST, | |
41 | .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, | |
42 | .gpio_vbus = CONFIG_SUNXI_USB_VBUS0_GPIO, | |
43 | .csr = (void *)SUNXI_USB_CSR, | |
44 | .irq = 39, | |
45 | .id = 1, | |
46 | }, | |
47 | #if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) | |
48 | { | |
49 | .usb_rst_mask = CCM_USB_CTRL_PHY2_RST, | |
50 | .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1, | |
51 | .gpio_vbus = CONFIG_SUNXI_USB_VBUS1_GPIO, | |
52 | .csr = (void *)SUNXI_USB_CSR, | |
53 | .irq = 40, | |
54 | .id = 2, | |
55 | } | |
56 | #endif | |
57 | }; | |
58 | ||
59 | static int enabled_hcd_count; | |
60 | ||
61 | static void *get_io_base(int hcd_id) | |
62 | { | |
63 | if (hcd_id == 1) | |
64 | return (void *)SUNXI_USB1_IO_BASE; | |
65 | else if (hcd_id == 2) | |
66 | return (void *)SUNXI_USB2_IO_BASE; | |
67 | else | |
68 | return NULL; | |
69 | } | |
70 | ||
71 | static void usb_phy_write(struct sunxi_ehci_hcd *sunxi_ehci, int addr, | |
72 | int data, int len) | |
73 | { | |
74 | int j = 0, usbc_bit = 0; | |
75 | void *dest = sunxi_ehci->csr; | |
76 | ||
77 | usbc_bit = 1 << (sunxi_ehci->id * 2); | |
78 | for (j = 0; j < len; j++) { | |
79 | /* set the bit address to be written */ | |
80 | clrbits_le32(dest, 0xff << 8); | |
81 | setbits_le32(dest, (addr + j) << 8); | |
82 | ||
83 | clrbits_le32(dest, usbc_bit); | |
84 | /* set data bit */ | |
85 | if (data & 0x1) | |
86 | setbits_le32(dest, 1 << 7); | |
87 | else | |
88 | clrbits_le32(dest, 1 << 7); | |
89 | ||
90 | setbits_le32(dest, usbc_bit); | |
91 | ||
92 | clrbits_le32(dest, usbc_bit); | |
93 | ||
94 | data >>= 1; | |
95 | } | |
96 | } | |
97 | ||
98 | static void sunxi_usb_phy_init(struct sunxi_ehci_hcd *sunxi_ehci) | |
99 | { | |
100 | /* The following comments are machine | |
101 | * translated from Chinese, you have been warned! | |
102 | */ | |
103 | ||
104 | /* adjust PHY's magnitude and rate */ | |
105 | usb_phy_write(sunxi_ehci, 0x20, 0x14, 5); | |
106 | ||
107 | /* threshold adjustment disconnect */ | |
ed41e62f | 108 | #ifdef CONFIG_MACH_SUN4I |
8d154002 RB |
109 | usb_phy_write(sunxi_ehci, 0x2a, 3, 2); |
110 | #else | |
111 | usb_phy_write(sunxi_ehci, 0x2a, 2, 2); | |
112 | #endif | |
113 | ||
114 | return; | |
115 | } | |
116 | ||
117 | static void sunxi_usb_passby(struct sunxi_ehci_hcd *sunxi_ehci, int enable) | |
118 | { | |
119 | unsigned long bits = 0; | |
120 | void *addr = get_io_base(sunxi_ehci->id) + SUNXI_USB_PMU_IRQ_ENABLE; | |
121 | ||
122 | bits = SUNXI_EHCI_AHB_ICHR8_EN | | |
123 | SUNXI_EHCI_AHB_INCR4_BURST_EN | | |
124 | SUNXI_EHCI_AHB_INCRX_ALIGN_EN | | |
125 | SUNXI_EHCI_ULPI_BYPASS_EN; | |
126 | ||
127 | if (enable) | |
128 | setbits_le32(addr, bits); | |
129 | else | |
130 | clrbits_le32(addr, bits); | |
131 | ||
132 | return; | |
133 | } | |
134 | ||
135 | static void sunxi_ehci_enable(struct sunxi_ehci_hcd *sunxi_ehci) | |
136 | { | |
137 | struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; | |
138 | ||
139 | setbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); | |
140 | setbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); | |
141 | ||
142 | sunxi_usb_phy_init(sunxi_ehci); | |
143 | ||
144 | sunxi_usb_passby(sunxi_ehci, SUNXI_USB_PASSBY_EN); | |
145 | ||
146 | gpio_direction_output(sunxi_ehci->gpio_vbus, 1); | |
147 | } | |
148 | ||
149 | static void sunxi_ehci_disable(struct sunxi_ehci_hcd *sunxi_ehci) | |
150 | { | |
151 | struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; | |
152 | ||
153 | gpio_direction_output(sunxi_ehci->gpio_vbus, 0); | |
154 | ||
155 | sunxi_usb_passby(sunxi_ehci, !SUNXI_USB_PASSBY_EN); | |
156 | ||
157 | clrbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); | |
158 | clrbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); | |
159 | } | |
160 | ||
161 | int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, | |
162 | struct ehci_hcor **hcor) | |
163 | { | |
164 | struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; | |
165 | struct sunxi_ehci_hcd *sunxi_ehci = &sunxi_echi_hcd[index]; | |
44fd5914 | 166 | int err; |
8d154002 RB |
167 | |
168 | /* enable common PHY only once */ | |
169 | if (index == 0) | |
170 | setbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); | |
171 | ||
44fd5914 HG |
172 | err = gpio_request(sunxi_ehci->gpio_vbus, "ehci_vbus"); |
173 | if (err) | |
174 | return err; | |
175 | ||
8d154002 RB |
176 | sunxi_ehci_enable(sunxi_ehci); |
177 | ||
178 | *hccr = get_io_base(sunxi_ehci->id); | |
179 | ||
180 | *hcor = (struct ehci_hcor *)((uint32_t) *hccr | |
181 | + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); | |
182 | ||
183 | debug("sunxi-ehci: init hccr %x and hcor %x hc_length %d\n", | |
184 | (uint32_t)*hccr, (uint32_t)*hcor, | |
185 | (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); | |
186 | ||
187 | enabled_hcd_count++; | |
188 | ||
189 | return 0; | |
190 | } | |
191 | ||
192 | int ehci_hcd_stop(int index) | |
193 | { | |
194 | struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; | |
195 | struct sunxi_ehci_hcd *sunxi_ehci = &sunxi_echi_hcd[index]; | |
44fd5914 | 196 | int err; |
8d154002 RB |
197 | |
198 | sunxi_ehci_disable(sunxi_ehci); | |
199 | ||
44fd5914 HG |
200 | err = gpio_free(sunxi_ehci->gpio_vbus); |
201 | if (err) | |
202 | return err; | |
203 | ||
8d154002 RB |
204 | /* disable common PHY only once, for the last enabled hcd */ |
205 | if (enabled_hcd_count == 1) | |
206 | clrbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); | |
207 | ||
208 | enabled_hcd_count--; | |
209 | ||
210 | return 0; | |
211 | } |