Commit | Line | Data |
---|---|---|
54ab2b02 FB |
1 | /* |
2 | * ehci-omap.c - driver for USBHOST on OMAP 34xx processor | |
3 | * | |
4 | * Bus Glue for OMAP34xx USBHOST 3 port EHCI controller | |
5 | * Tested on OMAP3430 ES2.0 SDP | |
6 | * | |
7 | * Copyright (C) 2007-2008 Texas Instruments, Inc. | |
8 | * Author: Vikram Pandita <vikram.pandita@ti.com> | |
9 | * | |
10 | * Copyright (C) 2009 Nokia Corporation | |
11 | * Contact: Felipe Balbi <felipe.balbi@nokia.com> | |
12 | * | |
13 | * Based on "ehci-fsl.c" and "ehci-au1xxx.c" ehci glue layers | |
14 | * | |
15 | * This program is free software; you can redistribute it and/or modify | |
16 | * it under the terms of the GNU General Public License as published by | |
17 | * the Free Software Foundation; either version 2 of the License, or | |
18 | * (at your option) any later version. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | * GNU General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU General Public License | |
26 | * along with this program; if not, write to the Free Software | |
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
28 | * | |
bdee2d84 | 29 | * TODO (last updated Feb 12, 2010): |
54ab2b02 FB |
30 | * - add kernel-doc |
31 | * - enable AUTOIDLE | |
54ab2b02 FB |
32 | * - add suspend/resume |
33 | * - move workarounds to board-files | |
34 | */ | |
35 | ||
36 | #include <linux/platform_device.h> | |
37 | #include <linux/clk.h> | |
38 | #include <linux/gpio.h> | |
88114266 | 39 | #include <linux/regulator/consumer.h> |
5a0e3ad6 | 40 | #include <linux/slab.h> |
c76f782c | 41 | #include <plat/usb.h> |
54ab2b02 FB |
42 | |
43 | /* | |
44 | * OMAP USBHOST Register addresses: VIRTUAL ADDRESSES | |
45 | * Use ehci_omap_readl()/ehci_omap_writel() functions | |
46 | */ | |
47 | ||
48 | /* TLL Register Set */ | |
49 | #define OMAP_USBTLL_REVISION (0x00) | |
50 | #define OMAP_USBTLL_SYSCONFIG (0x10) | |
51 | #define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8) | |
52 | #define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3) | |
53 | #define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2) | |
54 | #define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1) | |
55 | #define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0) | |
56 | ||
57 | #define OMAP_USBTLL_SYSSTATUS (0x14) | |
58 | #define OMAP_USBTLL_SYSSTATUS_RESETDONE (1 << 0) | |
59 | ||
60 | #define OMAP_USBTLL_IRQSTATUS (0x18) | |
61 | #define OMAP_USBTLL_IRQENABLE (0x1C) | |
62 | ||
63 | #define OMAP_TLL_SHARED_CONF (0x30) | |
64 | #define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN (1 << 6) | |
65 | #define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN (1 << 5) | |
66 | #define OMAP_TLL_SHARED_CONF_USB_DIVRATION (1 << 2) | |
67 | #define OMAP_TLL_SHARED_CONF_FCLK_REQ (1 << 1) | |
68 | #define OMAP_TLL_SHARED_CONF_FCLK_IS_ON (1 << 0) | |
69 | ||
70 | #define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) | |
71 | #define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) | |
72 | #define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) | |
73 | #define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) | |
74 | #define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) | |
75 | #define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) | |
76 | ||
77 | #define OMAP_TLL_ULPI_FUNCTION_CTRL(num) (0x804 + 0x100 * num) | |
78 | #define OMAP_TLL_ULPI_INTERFACE_CTRL(num) (0x807 + 0x100 * num) | |
79 | #define OMAP_TLL_ULPI_OTG_CTRL(num) (0x80A + 0x100 * num) | |
80 | #define OMAP_TLL_ULPI_INT_EN_RISE(num) (0x80D + 0x100 * num) | |
81 | #define OMAP_TLL_ULPI_INT_EN_FALL(num) (0x810 + 0x100 * num) | |
82 | #define OMAP_TLL_ULPI_INT_STATUS(num) (0x813 + 0x100 * num) | |
83 | #define OMAP_TLL_ULPI_INT_LATCH(num) (0x814 + 0x100 * num) | |
84 | #define OMAP_TLL_ULPI_DEBUG(num) (0x815 + 0x100 * num) | |
85 | #define OMAP_TLL_ULPI_SCRATCH_REGISTER(num) (0x816 + 0x100 * num) | |
86 | ||
87 | #define OMAP_TLL_CHANNEL_COUNT 3 | |
88 | #define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 1) | |
89 | #define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 2) | |
90 | #define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 4) | |
91 | ||
92 | /* UHH Register Set */ | |
93 | #define OMAP_UHH_REVISION (0x00) | |
94 | #define OMAP_UHH_SYSCONFIG (0x10) | |
95 | #define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12) | |
96 | #define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8) | |
97 | #define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3) | |
98 | #define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2) | |
99 | #define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1) | |
100 | #define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0) | |
101 | ||
102 | #define OMAP_UHH_SYSSTATUS (0x14) | |
103 | #define OMAP_UHH_HOSTCONFIG (0x40) | |
104 | #define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0) | |
105 | #define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0) | |
106 | #define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11) | |
107 | #define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12) | |
108 | #define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2) | |
109 | #define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3) | |
110 | #define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4) | |
111 | #define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5) | |
112 | #define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8) | |
113 | #define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9) | |
114 | #define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10) | |
115 | ||
116 | #define OMAP_UHH_DEBUG_CSR (0x44) | |
117 | ||
118 | /* EHCI Register Set */ | |
119 | #define EHCI_INSNREG05_ULPI (0xA4) | |
120 | #define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31 | |
121 | #define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24 | |
122 | #define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22 | |
123 | #define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16 | |
124 | #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 | |
125 | #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 | |
126 | ||
127 | /*-------------------------------------------------------------------------*/ | |
128 | ||
129 | static inline void ehci_omap_writel(void __iomem *base, u32 reg, u32 val) | |
130 | { | |
131 | __raw_writel(val, base + reg); | |
132 | } | |
133 | ||
134 | static inline u32 ehci_omap_readl(void __iomem *base, u32 reg) | |
135 | { | |
136 | return __raw_readl(base + reg); | |
137 | } | |
138 | ||
139 | static inline void ehci_omap_writeb(void __iomem *base, u8 reg, u8 val) | |
140 | { | |
141 | __raw_writeb(val, base + reg); | |
142 | } | |
143 | ||
144 | static inline u8 ehci_omap_readb(void __iomem *base, u8 reg) | |
145 | { | |
146 | return __raw_readb(base + reg); | |
147 | } | |
148 | ||
149 | /*-------------------------------------------------------------------------*/ | |
150 | ||
151 | struct ehci_hcd_omap { | |
152 | struct ehci_hcd *ehci; | |
153 | struct device *dev; | |
154 | ||
155 | struct clk *usbhost_ick; | |
156 | struct clk *usbhost2_120m_fck; | |
157 | struct clk *usbhost1_48m_fck; | |
158 | struct clk *usbtll_fck; | |
159 | struct clk *usbtll_ick; | |
160 | ||
161 | /* FIXME the following two workarounds are | |
162 | * board specific not silicon-specific so these | |
163 | * should be moved to board-file instead. | |
164 | * | |
165 | * Maybe someone from TI will know better which | |
166 | * board is affected and needs the workarounds | |
167 | * to be applied | |
168 | */ | |
169 | ||
170 | /* gpio for resetting phy */ | |
171 | int reset_gpio_port[OMAP3_HS_USB_PORTS]; | |
172 | ||
173 | /* phy reset workaround */ | |
174 | int phy_reset; | |
175 | ||
176 | /* desired phy_mode: TLL, PHY */ | |
177 | enum ehci_hcd_omap_mode port_mode[OMAP3_HS_USB_PORTS]; | |
178 | ||
179 | void __iomem *uhh_base; | |
180 | void __iomem *tll_base; | |
181 | void __iomem *ehci_base; | |
88114266 AKG |
182 | |
183 | /* Regulators for USB PHYs. | |
a8cd4561 | 184 | * Each PHY can have a separate regulator. |
88114266 AKG |
185 | */ |
186 | struct regulator *regulator[OMAP3_HS_USB_PORTS]; | |
54ab2b02 FB |
187 | }; |
188 | ||
189 | /*-------------------------------------------------------------------------*/ | |
190 | ||
191 | static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask) | |
192 | { | |
193 | unsigned reg; | |
194 | int i; | |
195 | ||
196 | /* Program the 3 TLL channels upfront */ | |
197 | for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { | |
198 | reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); | |
199 | ||
200 | /* Disable AutoIdle, BitStuffing and use SDR Mode */ | |
201 | reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE | |
202 | | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF | |
203 | | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); | |
204 | ehci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg); | |
205 | } | |
206 | ||
207 | /* Program Common TLL register */ | |
208 | reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_SHARED_CONF); | |
209 | reg |= (OMAP_TLL_SHARED_CONF_FCLK_IS_ON | |
210 | | OMAP_TLL_SHARED_CONF_USB_DIVRATION | |
211 | | OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN); | |
212 | reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN; | |
213 | ||
214 | ehci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg); | |
215 | ||
216 | /* Enable channels now */ | |
217 | for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { | |
218 | reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); | |
219 | ||
220 | /* Enable only the reg that is needed */ | |
221 | if (!(tll_channel_mask & 1<<i)) | |
222 | continue; | |
223 | ||
224 | reg |= OMAP_TLL_CHANNEL_CONF_CHANEN; | |
225 | ehci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg); | |
226 | ||
227 | ehci_omap_writeb(omap->tll_base, | |
228 | OMAP_TLL_ULPI_SCRATCH_REGISTER(i), 0xbe); | |
229 | dev_dbg(omap->dev, "ULPI_SCRATCH_REG[ch=%d]= 0x%02x\n", | |
230 | i+1, ehci_omap_readb(omap->tll_base, | |
231 | OMAP_TLL_ULPI_SCRATCH_REGISTER(i))); | |
232 | } | |
233 | } | |
234 | ||
235 | /*-------------------------------------------------------------------------*/ | |
236 | ||
237 | /* omap_start_ehc | |
238 | * - Start the TI USBHOST controller | |
239 | */ | |
240 | static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) | |
241 | { | |
242 | unsigned long timeout = jiffies + msecs_to_jiffies(1000); | |
243 | u8 tll_ch_mask = 0; | |
244 | unsigned reg = 0; | |
245 | int ret = 0; | |
246 | ||
247 | dev_dbg(omap->dev, "starting TI EHCI USB Controller\n"); | |
248 | ||
249 | /* Enable Clocks for USBHOST */ | |
250 | omap->usbhost_ick = clk_get(omap->dev, "usbhost_ick"); | |
251 | if (IS_ERR(omap->usbhost_ick)) { | |
252 | ret = PTR_ERR(omap->usbhost_ick); | |
253 | goto err_host_ick; | |
254 | } | |
255 | clk_enable(omap->usbhost_ick); | |
256 | ||
257 | omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck"); | |
258 | if (IS_ERR(omap->usbhost2_120m_fck)) { | |
259 | ret = PTR_ERR(omap->usbhost2_120m_fck); | |
260 | goto err_host_120m_fck; | |
261 | } | |
262 | clk_enable(omap->usbhost2_120m_fck); | |
263 | ||
264 | omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck"); | |
265 | if (IS_ERR(omap->usbhost1_48m_fck)) { | |
266 | ret = PTR_ERR(omap->usbhost1_48m_fck); | |
267 | goto err_host_48m_fck; | |
268 | } | |
269 | clk_enable(omap->usbhost1_48m_fck); | |
270 | ||
271 | if (omap->phy_reset) { | |
272 | /* Refer: ISSUE1 */ | |
273 | if (gpio_is_valid(omap->reset_gpio_port[0])) { | |
274 | gpio_request(omap->reset_gpio_port[0], | |
275 | "USB1 PHY reset"); | |
276 | gpio_direction_output(omap->reset_gpio_port[0], 0); | |
277 | } | |
278 | ||
279 | if (gpio_is_valid(omap->reset_gpio_port[1])) { | |
280 | gpio_request(omap->reset_gpio_port[1], | |
281 | "USB2 PHY reset"); | |
282 | gpio_direction_output(omap->reset_gpio_port[1], 0); | |
283 | } | |
284 | ||
285 | /* Hold the PHY in RESET for enough time till DIR is high */ | |
286 | udelay(10); | |
287 | } | |
288 | ||
289 | /* Configure TLL for 60Mhz clk for ULPI */ | |
290 | omap->usbtll_fck = clk_get(omap->dev, "usbtll_fck"); | |
291 | if (IS_ERR(omap->usbtll_fck)) { | |
292 | ret = PTR_ERR(omap->usbtll_fck); | |
293 | goto err_tll_fck; | |
294 | } | |
295 | clk_enable(omap->usbtll_fck); | |
296 | ||
297 | omap->usbtll_ick = clk_get(omap->dev, "usbtll_ick"); | |
298 | if (IS_ERR(omap->usbtll_ick)) { | |
299 | ret = PTR_ERR(omap->usbtll_ick); | |
300 | goto err_tll_ick; | |
301 | } | |
302 | clk_enable(omap->usbtll_ick); | |
303 | ||
304 | /* perform TLL soft reset, and wait until reset is complete */ | |
305 | ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, | |
306 | OMAP_USBTLL_SYSCONFIG_SOFTRESET); | |
307 | ||
308 | /* Wait for TLL reset to complete */ | |
309 | while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS) | |
310 | & OMAP_USBTLL_SYSSTATUS_RESETDONE)) { | |
311 | cpu_relax(); | |
312 | ||
313 | if (time_after(jiffies, timeout)) { | |
314 | dev_dbg(omap->dev, "operation timed out\n"); | |
315 | ret = -EINVAL; | |
316 | goto err_sys_status; | |
317 | } | |
318 | } | |
319 | ||
320 | dev_dbg(omap->dev, "TLL RESET DONE\n"); | |
321 | ||
322 | /* (1<<3) = no idle mode only for initial debugging */ | |
323 | ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, | |
324 | OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | | |
325 | OMAP_USBTLL_SYSCONFIG_SIDLEMODE | | |
326 | OMAP_USBTLL_SYSCONFIG_CACTIVITY); | |
327 | ||
328 | ||
329 | /* Put UHH in NoIdle/NoStandby mode */ | |
330 | reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG); | |
331 | reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP | |
332 | | OMAP_UHH_SYSCONFIG_SIDLEMODE | |
333 | | OMAP_UHH_SYSCONFIG_CACTIVITY | |
334 | | OMAP_UHH_SYSCONFIG_MIDLEMODE); | |
335 | reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; | |
336 | ||
337 | ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); | |
338 | ||
339 | reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG); | |
340 | ||
341 | /* setup ULPI bypass and burst configurations */ | |
342 | reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN | |
343 | | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN | |
344 | | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN); | |
345 | reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; | |
346 | ||
347 | if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) | |
348 | reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; | |
349 | if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) | |
350 | reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; | |
351 | if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) | |
352 | reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; | |
353 | ||
354 | /* Bypass the TLL module for PHY mode operation */ | |
355 | if (omap_rev() <= OMAP3430_REV_ES2_1) { | |
356 | dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1 \n"); | |
357 | if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || | |
358 | (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || | |
359 | (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) | |
360 | reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; | |
361 | else | |
362 | reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; | |
363 | } else { | |
364 | dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n"); | |
365 | if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) | |
366 | reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; | |
367 | else if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) | |
368 | reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; | |
369 | ||
370 | if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) | |
371 | reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; | |
372 | else if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) | |
373 | reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; | |
374 | ||
375 | if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY) | |
376 | reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; | |
377 | else if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) | |
378 | reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; | |
379 | ||
380 | } | |
381 | ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); | |
382 | dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg); | |
383 | ||
384 | ||
385 | if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) || | |
386 | (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) || | |
387 | (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) { | |
388 | ||
389 | if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) | |
390 | tll_ch_mask |= OMAP_TLL_CHANNEL_1_EN_MASK; | |
391 | if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) | |
392 | tll_ch_mask |= OMAP_TLL_CHANNEL_2_EN_MASK; | |
393 | if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) | |
394 | tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK; | |
395 | ||
396 | /* Enable UTMI mode for required TLL channels */ | |
397 | omap_usb_utmi_init(omap, tll_ch_mask); | |
398 | } | |
399 | ||
400 | if (omap->phy_reset) { | |
401 | /* Refer ISSUE1: | |
402 | * Hold the PHY in RESET for enough time till | |
403 | * PHY is settled and ready | |
404 | */ | |
405 | udelay(10); | |
406 | ||
407 | if (gpio_is_valid(omap->reset_gpio_port[0])) | |
408 | gpio_set_value(omap->reset_gpio_port[0], 1); | |
409 | ||
410 | if (gpio_is_valid(omap->reset_gpio_port[1])) | |
411 | gpio_set_value(omap->reset_gpio_port[1], 1); | |
412 | } | |
413 | ||
414 | return 0; | |
415 | ||
416 | err_sys_status: | |
417 | clk_disable(omap->usbtll_ick); | |
418 | clk_put(omap->usbtll_ick); | |
419 | ||
420 | err_tll_ick: | |
421 | clk_disable(omap->usbtll_fck); | |
422 | clk_put(omap->usbtll_fck); | |
423 | ||
424 | err_tll_fck: | |
425 | clk_disable(omap->usbhost1_48m_fck); | |
426 | clk_put(omap->usbhost1_48m_fck); | |
427 | ||
428 | if (omap->phy_reset) { | |
429 | if (gpio_is_valid(omap->reset_gpio_port[0])) | |
430 | gpio_free(omap->reset_gpio_port[0]); | |
431 | ||
432 | if (gpio_is_valid(omap->reset_gpio_port[1])) | |
433 | gpio_free(omap->reset_gpio_port[1]); | |
434 | } | |
435 | ||
436 | err_host_48m_fck: | |
437 | clk_disable(omap->usbhost2_120m_fck); | |
438 | clk_put(omap->usbhost2_120m_fck); | |
439 | ||
440 | err_host_120m_fck: | |
441 | clk_disable(omap->usbhost_ick); | |
442 | clk_put(omap->usbhost_ick); | |
443 | ||
444 | err_host_ick: | |
445 | return ret; | |
446 | } | |
447 | ||
448 | static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) | |
449 | { | |
450 | unsigned long timeout = jiffies + msecs_to_jiffies(100); | |
451 | ||
452 | dev_dbg(omap->dev, "stopping TI EHCI USB Controller\n"); | |
453 | ||
454 | /* Reset OMAP modules for insmod/rmmod to work */ | |
455 | ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, | |
456 | OMAP_UHH_SYSCONFIG_SOFTRESET); | |
457 | while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) | |
458 | & (1 << 0))) { | |
459 | cpu_relax(); | |
460 | ||
461 | if (time_after(jiffies, timeout)) | |
462 | dev_dbg(omap->dev, "operation timed out\n"); | |
463 | } | |
464 | ||
465 | while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) | |
466 | & (1 << 1))) { | |
467 | cpu_relax(); | |
468 | ||
469 | if (time_after(jiffies, timeout)) | |
470 | dev_dbg(omap->dev, "operation timed out\n"); | |
471 | } | |
472 | ||
473 | while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) | |
474 | & (1 << 2))) { | |
475 | cpu_relax(); | |
476 | ||
477 | if (time_after(jiffies, timeout)) | |
478 | dev_dbg(omap->dev, "operation timed out\n"); | |
479 | } | |
480 | ||
481 | ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1)); | |
482 | ||
483 | while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS) | |
484 | & (1 << 0))) { | |
485 | cpu_relax(); | |
486 | ||
487 | if (time_after(jiffies, timeout)) | |
488 | dev_dbg(omap->dev, "operation timed out\n"); | |
489 | } | |
490 | ||
491 | if (omap->usbtll_fck != NULL) { | |
492 | clk_disable(omap->usbtll_fck); | |
493 | clk_put(omap->usbtll_fck); | |
494 | omap->usbtll_fck = NULL; | |
495 | } | |
496 | ||
497 | if (omap->usbhost_ick != NULL) { | |
498 | clk_disable(omap->usbhost_ick); | |
499 | clk_put(omap->usbhost_ick); | |
500 | omap->usbhost_ick = NULL; | |
501 | } | |
502 | ||
503 | if (omap->usbhost1_48m_fck != NULL) { | |
504 | clk_disable(omap->usbhost1_48m_fck); | |
505 | clk_put(omap->usbhost1_48m_fck); | |
506 | omap->usbhost1_48m_fck = NULL; | |
507 | } | |
508 | ||
509 | if (omap->usbhost2_120m_fck != NULL) { | |
510 | clk_disable(omap->usbhost2_120m_fck); | |
511 | clk_put(omap->usbhost2_120m_fck); | |
512 | omap->usbhost2_120m_fck = NULL; | |
513 | } | |
514 | ||
515 | if (omap->usbtll_ick != NULL) { | |
516 | clk_disable(omap->usbtll_ick); | |
517 | clk_put(omap->usbtll_ick); | |
518 | omap->usbtll_ick = NULL; | |
519 | } | |
520 | ||
521 | if (omap->phy_reset) { | |
522 | if (gpio_is_valid(omap->reset_gpio_port[0])) | |
523 | gpio_free(omap->reset_gpio_port[0]); | |
524 | ||
525 | if (gpio_is_valid(omap->reset_gpio_port[1])) | |
526 | gpio_free(omap->reset_gpio_port[1]); | |
527 | } | |
528 | ||
529 | dev_dbg(omap->dev, "Clock to USB host has been disabled\n"); | |
530 | } | |
531 | ||
532 | /*-------------------------------------------------------------------------*/ | |
533 | ||
534 | static const struct hc_driver ehci_omap_hc_driver; | |
535 | ||
536 | /* configure so an HC device and id are always provided */ | |
537 | /* always called with process context; sleeping is OK */ | |
538 | ||
539 | /** | |
540 | * ehci_hcd_omap_probe - initialize TI-based HCDs | |
541 | * | |
542 | * Allocates basic resources for this USB host controller, and | |
543 | * then invokes the start() method for the HCD associated with it | |
544 | * through the hotplug entry's driver_data. | |
545 | */ | |
546 | static int ehci_hcd_omap_probe(struct platform_device *pdev) | |
547 | { | |
548 | struct ehci_hcd_omap_platform_data *pdata = pdev->dev.platform_data; | |
549 | struct ehci_hcd_omap *omap; | |
550 | struct resource *res; | |
551 | struct usb_hcd *hcd; | |
552 | ||
553 | int irq = platform_get_irq(pdev, 0); | |
554 | int ret = -ENODEV; | |
88114266 AKG |
555 | int i; |
556 | char supply[7]; | |
54ab2b02 FB |
557 | |
558 | if (!pdata) { | |
559 | dev_dbg(&pdev->dev, "missing platform_data\n"); | |
560 | goto err_pdata; | |
561 | } | |
562 | ||
563 | if (usb_disabled()) | |
564 | goto err_disabled; | |
565 | ||
566 | omap = kzalloc(sizeof(*omap), GFP_KERNEL); | |
567 | if (!omap) { | |
568 | ret = -ENOMEM; | |
b2b60809 | 569 | goto err_disabled; |
54ab2b02 FB |
570 | } |
571 | ||
572 | hcd = usb_create_hcd(&ehci_omap_hc_driver, &pdev->dev, | |
573 | dev_name(&pdev->dev)); | |
574 | if (!hcd) { | |
575 | dev_dbg(&pdev->dev, "failed to create hcd with err %d\n", ret); | |
576 | ret = -ENOMEM; | |
577 | goto err_create_hcd; | |
578 | } | |
579 | ||
580 | platform_set_drvdata(pdev, omap); | |
581 | omap->dev = &pdev->dev; | |
582 | omap->phy_reset = pdata->phy_reset; | |
583 | omap->reset_gpio_port[0] = pdata->reset_gpio_port[0]; | |
584 | omap->reset_gpio_port[1] = pdata->reset_gpio_port[1]; | |
585 | omap->reset_gpio_port[2] = pdata->reset_gpio_port[2]; | |
586 | omap->port_mode[0] = pdata->port_mode[0]; | |
587 | omap->port_mode[1] = pdata->port_mode[1]; | |
588 | omap->port_mode[2] = pdata->port_mode[2]; | |
589 | omap->ehci = hcd_to_ehci(hcd); | |
590 | omap->ehci->sbrn = 0x20; | |
591 | ||
592 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
593 | ||
594 | hcd->rsrc_start = res->start; | |
595 | hcd->rsrc_len = resource_size(res); | |
596 | ||
597 | hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); | |
598 | if (!hcd->regs) { | |
599 | dev_err(&pdev->dev, "EHCI ioremap failed\n"); | |
600 | ret = -ENOMEM; | |
601 | goto err_ioremap; | |
602 | } | |
603 | ||
604 | /* we know this is the memory we want, no need to ioremap again */ | |
605 | omap->ehci->caps = hcd->regs; | |
606 | omap->ehci_base = hcd->regs; | |
607 | ||
608 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | |
609 | omap->uhh_base = ioremap(res->start, resource_size(res)); | |
610 | if (!omap->uhh_base) { | |
611 | dev_err(&pdev->dev, "UHH ioremap failed\n"); | |
612 | ret = -ENOMEM; | |
613 | goto err_uhh_ioremap; | |
614 | } | |
615 | ||
616 | res = platform_get_resource(pdev, IORESOURCE_MEM, 2); | |
617 | omap->tll_base = ioremap(res->start, resource_size(res)); | |
618 | if (!omap->tll_base) { | |
619 | dev_err(&pdev->dev, "TLL ioremap failed\n"); | |
620 | ret = -ENOMEM; | |
621 | goto err_tll_ioremap; | |
622 | } | |
623 | ||
88114266 AKG |
624 | /* get ehci regulator and enable */ |
625 | for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) { | |
626 | if (omap->port_mode[i] != EHCI_HCD_OMAP_MODE_PHY) { | |
627 | omap->regulator[i] = NULL; | |
628 | continue; | |
629 | } | |
630 | snprintf(supply, sizeof(supply), "hsusb%d", i); | |
631 | omap->regulator[i] = regulator_get(omap->dev, supply); | |
632 | if (IS_ERR(omap->regulator[i])) | |
633 | dev_dbg(&pdev->dev, | |
634 | "failed to get ehci port%d regulator\n", i); | |
635 | else | |
636 | regulator_enable(omap->regulator[i]); | |
637 | } | |
638 | ||
54ab2b02 FB |
639 | ret = omap_start_ehc(omap, hcd); |
640 | if (ret) { | |
641 | dev_dbg(&pdev->dev, "failed to start ehci\n"); | |
642 | goto err_start; | |
643 | } | |
644 | ||
645 | omap->ehci->regs = hcd->regs | |
646 | + HC_LENGTH(readl(&omap->ehci->caps->hc_capbase)); | |
647 | ||
bdb581bd AG |
648 | dbg_hcs_params(omap->ehci, "reset"); |
649 | dbg_hcc_params(omap->ehci, "reset"); | |
650 | ||
54ab2b02 FB |
651 | /* cache this readonly data; minimize chip reads */ |
652 | omap->ehci->hcs_params = readl(&omap->ehci->caps->hcs_params); | |
653 | ||
54ab2b02 FB |
654 | ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); |
655 | if (ret) { | |
656 | dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); | |
657 | goto err_add_hcd; | |
658 | } | |
659 | ||
660 | return 0; | |
661 | ||
662 | err_add_hcd: | |
663 | omap_stop_ehc(omap, hcd); | |
664 | ||
665 | err_start: | |
88114266 AKG |
666 | for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) { |
667 | if (omap->regulator[i]) { | |
668 | regulator_disable(omap->regulator[i]); | |
669 | regulator_put(omap->regulator[i]); | |
670 | } | |
671 | } | |
54ab2b02 FB |
672 | iounmap(omap->tll_base); |
673 | ||
674 | err_tll_ioremap: | |
675 | iounmap(omap->uhh_base); | |
676 | ||
677 | err_uhh_ioremap: | |
678 | iounmap(hcd->regs); | |
679 | ||
680 | err_ioremap: | |
681 | usb_put_hcd(hcd); | |
682 | ||
683 | err_create_hcd: | |
b2b60809 | 684 | kfree(omap); |
54ab2b02 FB |
685 | err_disabled: |
686 | err_pdata: | |
687 | return ret; | |
688 | } | |
689 | ||
690 | /* may be called without controller electrically present */ | |
691 | /* may be called with controller, bus, and devices active */ | |
692 | ||
693 | /** | |
694 | * ehci_hcd_omap_remove - shutdown processing for EHCI HCDs | |
695 | * @pdev: USB Host Controller being removed | |
696 | * | |
697 | * Reverses the effect of usb_ehci_hcd_omap_probe(), first invoking | |
698 | * the HCD's stop() method. It is always called from a thread | |
699 | * context, normally "rmmod", "apmd", or something similar. | |
700 | */ | |
701 | static int ehci_hcd_omap_remove(struct platform_device *pdev) | |
702 | { | |
703 | struct ehci_hcd_omap *omap = platform_get_drvdata(pdev); | |
704 | struct usb_hcd *hcd = ehci_to_hcd(omap->ehci); | |
88114266 | 705 | int i; |
54ab2b02 FB |
706 | |
707 | usb_remove_hcd(hcd); | |
708 | omap_stop_ehc(omap, hcd); | |
709 | iounmap(hcd->regs); | |
88114266 AKG |
710 | for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) { |
711 | if (omap->regulator[i]) { | |
712 | regulator_disable(omap->regulator[i]); | |
713 | regulator_put(omap->regulator[i]); | |
714 | } | |
715 | } | |
54ab2b02 FB |
716 | iounmap(omap->tll_base); |
717 | iounmap(omap->uhh_base); | |
718 | usb_put_hcd(hcd); | |
d3ae8562 | 719 | kfree(omap); |
54ab2b02 FB |
720 | |
721 | return 0; | |
722 | } | |
723 | ||
724 | static void ehci_hcd_omap_shutdown(struct platform_device *pdev) | |
725 | { | |
726 | struct ehci_hcd_omap *omap = platform_get_drvdata(pdev); | |
727 | struct usb_hcd *hcd = ehci_to_hcd(omap->ehci); | |
728 | ||
729 | if (hcd->driver->shutdown) | |
730 | hcd->driver->shutdown(hcd); | |
731 | } | |
732 | ||
733 | static struct platform_driver ehci_hcd_omap_driver = { | |
734 | .probe = ehci_hcd_omap_probe, | |
735 | .remove = ehci_hcd_omap_remove, | |
736 | .shutdown = ehci_hcd_omap_shutdown, | |
737 | /*.suspend = ehci_hcd_omap_suspend, */ | |
738 | /*.resume = ehci_hcd_omap_resume, */ | |
739 | .driver = { | |
740 | .name = "ehci-omap", | |
741 | } | |
742 | }; | |
743 | ||
744 | /*-------------------------------------------------------------------------*/ | |
745 | ||
746 | static const struct hc_driver ehci_omap_hc_driver = { | |
747 | .description = hcd_name, | |
748 | .product_desc = "OMAP-EHCI Host Controller", | |
749 | .hcd_priv_size = sizeof(struct ehci_hcd), | |
750 | ||
751 | /* | |
752 | * generic hardware linkage | |
753 | */ | |
754 | .irq = ehci_irq, | |
755 | .flags = HCD_MEMORY | HCD_USB2, | |
756 | ||
757 | /* | |
758 | * basic lifecycle operations | |
759 | */ | |
760 | .reset = ehci_init, | |
761 | .start = ehci_run, | |
762 | .stop = ehci_stop, | |
763 | .shutdown = ehci_shutdown, | |
764 | ||
765 | /* | |
766 | * managing i/o requests and associated device resources | |
767 | */ | |
768 | .urb_enqueue = ehci_urb_enqueue, | |
769 | .urb_dequeue = ehci_urb_dequeue, | |
770 | .endpoint_disable = ehci_endpoint_disable, | |
771 | .endpoint_reset = ehci_endpoint_reset, | |
772 | ||
773 | /* | |
774 | * scheduling support | |
775 | */ | |
776 | .get_frame_number = ehci_get_frame, | |
777 | ||
778 | /* | |
779 | * root hub support | |
780 | */ | |
781 | .hub_status_data = ehci_hub_status_data, | |
782 | .hub_control = ehci_hub_control, | |
783 | .bus_suspend = ehci_bus_suspend, | |
784 | .bus_resume = ehci_bus_resume, | |
785 | ||
786 | .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, | |
787 | }; | |
788 | ||
789 | MODULE_ALIAS("platform:omap-ehci"); | |
790 | MODULE_AUTHOR("Texas Instruments, Inc."); | |
791 | MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>"); | |
792 |