diff options
author | Guennadi Liakhovetski <lg@denx.de> | 2009-04-15 08:25:33 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 00:44:47 -0400 |
commit | 54e4026b64a970303349b952866641a7804ef594 (patch) | |
tree | a6410500a7218dfe8380e617dad58faf6572a221 | |
parent | 568d422e9cf52b7b26d2e026ae1617971f62b560 (diff) |
USB: gadget: Add i.MX3x support to the fsl_usb2_udc driver
This patch adds support for i.MX3x (only tested with i.MX31 so far) ARM
SoCs to the fsl_usb2_udc driver. It also moves PHY configuration before
controller reset, because otherwise an ULPI PHY doesn't get a reset and
doesn't function after a reboot. The problem with longer control transfers
is still not fixed. The patch renames the fsl_usb2_udc.c file to
fsl_udc_core.c to preserve the same module name for user-space
backwards compatibility.
Signed-off-by: Guennadi Liakhovetski <lg@denx.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/gadget/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/fsl_mx3_udc.c | 95 | ||||
-rw-r--r-- | drivers/usb/gadget/fsl_udc_core.c (renamed from drivers/usb/gadget/fsl_usb2_udc.c) | 69 | ||||
-rw-r--r-- | drivers/usb/gadget/fsl_usb2_udc.h | 18 |
5 files changed, 164 insertions, 24 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 772dd3b9bab9..5de9b4f21713 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig | |||
@@ -156,7 +156,7 @@ config USB_ATMEL_USBA | |||
156 | 156 | ||
157 | config USB_GADGET_FSL_USB2 | 157 | config USB_GADGET_FSL_USB2 |
158 | boolean "Freescale Highspeed USB DR Peripheral Controller" | 158 | boolean "Freescale Highspeed USB DR Peripheral Controller" |
159 | depends on FSL_SOC | 159 | depends on FSL_SOC || ARCH_MXC |
160 | select USB_GADGET_DUALSPEED | 160 | select USB_GADGET_DUALSPEED |
161 | help | 161 | help |
162 | Some of Freescale PowerPC processors have a High Speed | 162 | Some of Freescale PowerPC processors have a High Speed |
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 39a51d746cb7..c3fe06396b74 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile | |||
@@ -18,6 +18,10 @@ obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o | |||
18 | obj-$(CONFIG_USB_AT91) += at91_udc.o | 18 | obj-$(CONFIG_USB_AT91) += at91_udc.o |
19 | obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o | 19 | obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o |
20 | obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o | 20 | obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o |
21 | fsl_usb2_udc-objs := fsl_udc_core.o | ||
22 | ifeq ($(CONFIG_ARCH_MXC),y) | ||
23 | fsl_usb2_udc-objs += fsl_mx3_udc.o | ||
24 | endif | ||
21 | obj-$(CONFIG_USB_M66592) += m66592-udc.o | 25 | obj-$(CONFIG_USB_M66592) += m66592-udc.o |
22 | obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o | 26 | obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o |
23 | obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o | 27 | obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o |
diff --git a/drivers/usb/gadget/fsl_mx3_udc.c b/drivers/usb/gadget/fsl_mx3_udc.c new file mode 100644 index 000000000000..4bc2bf3d602e --- /dev/null +++ b/drivers/usb/gadget/fsl_mx3_udc.c | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 | ||
3 | * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> | ||
4 | * | ||
5 | * Description: | ||
6 | * Helper routines for i.MX3x SoCs from Freescale, needed by the fsl_usb2_udc.c | ||
7 | * driver to function correctly on these systems. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | */ | ||
14 | #include <linux/clk.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/fsl_devices.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | |||
20 | static struct clk *mxc_ahb_clk; | ||
21 | static struct clk *mxc_usb_clk; | ||
22 | |||
23 | int fsl_udc_clk_init(struct platform_device *pdev) | ||
24 | { | ||
25 | struct fsl_usb2_platform_data *pdata; | ||
26 | unsigned long freq; | ||
27 | int ret; | ||
28 | |||
29 | pdata = pdev->dev.platform_data; | ||
30 | |||
31 | mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb"); | ||
32 | if (IS_ERR(mxc_ahb_clk)) | ||
33 | return PTR_ERR(mxc_ahb_clk); | ||
34 | |||
35 | ret = clk_enable(mxc_ahb_clk); | ||
36 | if (ret < 0) { | ||
37 | dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n"); | ||
38 | goto eenahb; | ||
39 | } | ||
40 | |||
41 | /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */ | ||
42 | mxc_usb_clk = clk_get(&pdev->dev, "usb"); | ||
43 | if (IS_ERR(mxc_usb_clk)) { | ||
44 | dev_err(&pdev->dev, "clk_get(\"usb\") failed\n"); | ||
45 | ret = PTR_ERR(mxc_usb_clk); | ||
46 | goto egusb; | ||
47 | } | ||
48 | |||
49 | freq = clk_get_rate(mxc_usb_clk); | ||
50 | if (pdata->phy_mode != FSL_USB2_PHY_ULPI && | ||
51 | (freq < 59999000 || freq > 60001000)) { | ||
52 | dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq); | ||
53 | goto eclkrate; | ||
54 | } | ||
55 | |||
56 | ret = clk_enable(mxc_usb_clk); | ||
57 | if (ret < 0) { | ||
58 | dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n"); | ||
59 | goto eenusb; | ||
60 | } | ||
61 | |||
62 | return 0; | ||
63 | |||
64 | eenusb: | ||
65 | eclkrate: | ||
66 | clk_put(mxc_usb_clk); | ||
67 | mxc_usb_clk = NULL; | ||
68 | egusb: | ||
69 | clk_disable(mxc_ahb_clk); | ||
70 | eenahb: | ||
71 | clk_put(mxc_ahb_clk); | ||
72 | return ret; | ||
73 | } | ||
74 | |||
75 | void fsl_udc_clk_finalize(struct platform_device *pdev) | ||
76 | { | ||
77 | struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; | ||
78 | |||
79 | /* ULPI transceivers don't need usbpll */ | ||
80 | if (pdata->phy_mode == FSL_USB2_PHY_ULPI) { | ||
81 | clk_disable(mxc_usb_clk); | ||
82 | clk_put(mxc_usb_clk); | ||
83 | mxc_usb_clk = NULL; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | void fsl_udc_clk_release(void) | ||
88 | { | ||
89 | if (mxc_usb_clk) { | ||
90 | clk_disable(mxc_usb_clk); | ||
91 | clk_put(mxc_usb_clk); | ||
92 | } | ||
93 | clk_disable(mxc_ahb_clk); | ||
94 | clk_put(mxc_ahb_clk); | ||
95 | } | ||
diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_udc_core.c index 9d7b95d4e3d2..42a74b8a0bb8 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_udc_core.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/platform_device.h> | 38 | #include <linux/platform_device.h> |
39 | #include <linux/fsl_devices.h> | 39 | #include <linux/fsl_devices.h> |
40 | #include <linux/dmapool.h> | 40 | #include <linux/dmapool.h> |
41 | #include <linux/delay.h> | ||
41 | 42 | ||
42 | #include <asm/byteorder.h> | 43 | #include <asm/byteorder.h> |
43 | #include <asm/io.h> | 44 | #include <asm/io.h> |
@@ -57,7 +58,9 @@ static const char driver_name[] = "fsl-usb2-udc"; | |||
57 | static const char driver_desc[] = DRIVER_DESC; | 58 | static const char driver_desc[] = DRIVER_DESC; |
58 | 59 | ||
59 | static struct usb_dr_device *dr_regs; | 60 | static struct usb_dr_device *dr_regs; |
61 | #ifndef CONFIG_ARCH_MXC | ||
60 | static struct usb_sys_interface *usb_sys_regs; | 62 | static struct usb_sys_interface *usb_sys_regs; |
63 | #endif | ||
61 | 64 | ||
62 | /* it is initialized in probe() */ | 65 | /* it is initialized in probe() */ |
63 | static struct fsl_udc *udc_controller = NULL; | 66 | static struct fsl_udc *udc_controller = NULL; |
@@ -174,10 +177,34 @@ static void nuke(struct fsl_ep *ep, int status) | |||
174 | 177 | ||
175 | static int dr_controller_setup(struct fsl_udc *udc) | 178 | static int dr_controller_setup(struct fsl_udc *udc) |
176 | { | 179 | { |
177 | unsigned int tmp = 0, portctrl = 0, ctrl = 0; | 180 | unsigned int tmp, portctrl; |
181 | #ifndef CONFIG_ARCH_MXC | ||
182 | unsigned int ctrl; | ||
183 | #endif | ||
178 | unsigned long timeout; | 184 | unsigned long timeout; |
179 | #define FSL_UDC_RESET_TIMEOUT 1000 | 185 | #define FSL_UDC_RESET_TIMEOUT 1000 |
180 | 186 | ||
187 | /* Config PHY interface */ | ||
188 | portctrl = fsl_readl(&dr_regs->portsc1); | ||
189 | portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); | ||
190 | switch (udc->phy_mode) { | ||
191 | case FSL_USB2_PHY_ULPI: | ||
192 | portctrl |= PORTSCX_PTS_ULPI; | ||
193 | break; | ||
194 | case FSL_USB2_PHY_UTMI_WIDE: | ||
195 | portctrl |= PORTSCX_PTW_16BIT; | ||
196 | /* fall through */ | ||
197 | case FSL_USB2_PHY_UTMI: | ||
198 | portctrl |= PORTSCX_PTS_UTMI; | ||
199 | break; | ||
200 | case FSL_USB2_PHY_SERIAL: | ||
201 | portctrl |= PORTSCX_PTS_FSLS; | ||
202 | break; | ||
203 | default: | ||
204 | return -EINVAL; | ||
205 | } | ||
206 | fsl_writel(portctrl, &dr_regs->portsc1); | ||
207 | |||
181 | /* Stop and reset the usb controller */ | 208 | /* Stop and reset the usb controller */ |
182 | tmp = fsl_readl(&dr_regs->usbcmd); | 209 | tmp = fsl_readl(&dr_regs->usbcmd); |
183 | tmp &= ~USB_CMD_RUN_STOP; | 210 | tmp &= ~USB_CMD_RUN_STOP; |
@@ -215,31 +242,12 @@ static int dr_controller_setup(struct fsl_udc *udc) | |||
215 | udc->ep_qh, (int)tmp, | 242 | udc->ep_qh, (int)tmp, |
216 | fsl_readl(&dr_regs->endpointlistaddr)); | 243 | fsl_readl(&dr_regs->endpointlistaddr)); |
217 | 244 | ||
218 | /* Config PHY interface */ | ||
219 | portctrl = fsl_readl(&dr_regs->portsc1); | ||
220 | portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); | ||
221 | switch (udc->phy_mode) { | ||
222 | case FSL_USB2_PHY_ULPI: | ||
223 | portctrl |= PORTSCX_PTS_ULPI; | ||
224 | break; | ||
225 | case FSL_USB2_PHY_UTMI_WIDE: | ||
226 | portctrl |= PORTSCX_PTW_16BIT; | ||
227 | /* fall through */ | ||
228 | case FSL_USB2_PHY_UTMI: | ||
229 | portctrl |= PORTSCX_PTS_UTMI; | ||
230 | break; | ||
231 | case FSL_USB2_PHY_SERIAL: | ||
232 | portctrl |= PORTSCX_PTS_FSLS; | ||
233 | break; | ||
234 | default: | ||
235 | return -EINVAL; | ||
236 | } | ||
237 | fsl_writel(portctrl, &dr_regs->portsc1); | ||
238 | |||
239 | /* Config control enable i/o output, cpu endian register */ | 245 | /* Config control enable i/o output, cpu endian register */ |
246 | #ifndef CONFIG_ARCH_MXC | ||
240 | ctrl = __raw_readl(&usb_sys_regs->control); | 247 | ctrl = __raw_readl(&usb_sys_regs->control); |
241 | ctrl |= USB_CTRL_IOENB; | 248 | ctrl |= USB_CTRL_IOENB; |
242 | __raw_writel(ctrl, &usb_sys_regs->control); | 249 | __raw_writel(ctrl, &usb_sys_regs->control); |
250 | #endif | ||
243 | 251 | ||
244 | #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) | 252 | #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) |
245 | /* Turn on cache snooping hardware, since some PowerPC platforms | 253 | /* Turn on cache snooping hardware, since some PowerPC platforms |
@@ -2043,6 +2051,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, | |||
2043 | size -= t; | 2051 | size -= t; |
2044 | next += t; | 2052 | next += t; |
2045 | 2053 | ||
2054 | #ifndef CONFIG_ARCH_MXC | ||
2046 | tmp_reg = usb_sys_regs->snoop1; | 2055 | tmp_reg = usb_sys_regs->snoop1; |
2047 | t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); | 2056 | t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); |
2048 | size -= t; | 2057 | size -= t; |
@@ -2053,6 +2062,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, | |||
2053 | tmp_reg); | 2062 | tmp_reg); |
2054 | size -= t; | 2063 | size -= t; |
2055 | next += t; | 2064 | next += t; |
2065 | #endif | ||
2056 | 2066 | ||
2057 | /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */ | 2067 | /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */ |
2058 | ep = &udc->eps[0]; | 2068 | ep = &udc->eps[0]; |
@@ -2263,14 +2273,21 @@ static int __init fsl_udc_probe(struct platform_device *pdev) | |||
2263 | goto err_kfree; | 2273 | goto err_kfree; |
2264 | } | 2274 | } |
2265 | 2275 | ||
2266 | dr_regs = ioremap(res->start, res->end - res->start + 1); | 2276 | dr_regs = ioremap(res->start, resource_size(res)); |
2267 | if (!dr_regs) { | 2277 | if (!dr_regs) { |
2268 | ret = -ENOMEM; | 2278 | ret = -ENOMEM; |
2269 | goto err_release_mem_region; | 2279 | goto err_release_mem_region; |
2270 | } | 2280 | } |
2271 | 2281 | ||
2282 | #ifndef CONFIG_ARCH_MXC | ||
2272 | usb_sys_regs = (struct usb_sys_interface *) | 2283 | usb_sys_regs = (struct usb_sys_interface *) |
2273 | ((u32)dr_regs + USB_DR_SYS_OFFSET); | 2284 | ((u32)dr_regs + USB_DR_SYS_OFFSET); |
2285 | #endif | ||
2286 | |||
2287 | /* Initialize USB clocks */ | ||
2288 | ret = fsl_udc_clk_init(pdev); | ||
2289 | if (ret < 0) | ||
2290 | goto err_iounmap_noclk; | ||
2274 | 2291 | ||
2275 | /* Read Device Controller Capability Parameters register */ | 2292 | /* Read Device Controller Capability Parameters register */ |
2276 | dccparams = fsl_readl(&dr_regs->dccparams); | 2293 | dccparams = fsl_readl(&dr_regs->dccparams); |
@@ -2308,6 +2325,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev) | |||
2308 | * leave usbintr reg untouched */ | 2325 | * leave usbintr reg untouched */ |
2309 | dr_controller_setup(udc_controller); | 2326 | dr_controller_setup(udc_controller); |
2310 | 2327 | ||
2328 | fsl_udc_clk_finalize(pdev); | ||
2329 | |||
2311 | /* Setup gadget structure */ | 2330 | /* Setup gadget structure */ |
2312 | udc_controller->gadget.ops = &fsl_gadget_ops; | 2331 | udc_controller->gadget.ops = &fsl_gadget_ops; |
2313 | udc_controller->gadget.is_dualspeed = 1; | 2332 | udc_controller->gadget.is_dualspeed = 1; |
@@ -2362,6 +2381,8 @@ err_unregister: | |||
2362 | err_free_irq: | 2381 | err_free_irq: |
2363 | free_irq(udc_controller->irq, udc_controller); | 2382 | free_irq(udc_controller->irq, udc_controller); |
2364 | err_iounmap: | 2383 | err_iounmap: |
2384 | fsl_udc_clk_release(); | ||
2385 | err_iounmap_noclk: | ||
2365 | iounmap(dr_regs); | 2386 | iounmap(dr_regs); |
2366 | err_release_mem_region: | 2387 | err_release_mem_region: |
2367 | release_mem_region(res->start, res->end - res->start + 1); | 2388 | release_mem_region(res->start, res->end - res->start + 1); |
@@ -2384,6 +2405,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) | |||
2384 | return -ENODEV; | 2405 | return -ENODEV; |
2385 | udc_controller->done = &done; | 2406 | udc_controller->done = &done; |
2386 | 2407 | ||
2408 | fsl_udc_clk_release(); | ||
2409 | |||
2387 | /* DR has been stopped in usb_gadget_unregister_driver() */ | 2410 | /* DR has been stopped in usb_gadget_unregister_driver() */ |
2388 | remove_proc_file(); | 2411 | remove_proc_file(); |
2389 | 2412 | ||
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index e63ef12645f5..20aeceed48c7 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h | |||
@@ -563,4 +563,22 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) | |||
563 | * 2 + ((windex & USB_DIR_IN) ? 1 : 0)) | 563 | * 2 + ((windex & USB_DIR_IN) ? 1 : 0)) |
564 | #define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP)) | 564 | #define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP)) |
565 | 565 | ||
566 | struct platform_device; | ||
567 | #ifdef CONFIG_ARCH_MXC | ||
568 | int fsl_udc_clk_init(struct platform_device *pdev); | ||
569 | void fsl_udc_clk_finalize(struct platform_device *pdev); | ||
570 | void fsl_udc_clk_release(void); | ||
571 | #else | ||
572 | static inline int fsl_udc_clk_init(struct platform_device *pdev) | ||
573 | { | ||
574 | return 0; | ||
575 | } | ||
576 | static inline void fsl_udc_clk_finalize(struct platform_device *pdev) | ||
577 | { | ||
578 | } | ||
579 | static inline void fsl_udc_clk_release(void) | ||
580 | { | ||
581 | } | ||
582 | #endif | ||
583 | |||
566 | #endif | 584 | #endif |