diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/usb/host/ehci-mxc.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'drivers/usb/host/ehci-mxc.c')
-rw-r--r-- | drivers/usb/host/ehci-mxc.c | 100 |
1 files changed, 69 insertions, 31 deletions
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index a8ad8ac120a2..0c058be35a38 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c | |||
@@ -21,17 +21,17 @@ | |||
21 | #include <linux/clk.h> | 21 | #include <linux/clk.h> |
22 | #include <linux/delay.h> | 22 | #include <linux/delay.h> |
23 | #include <linux/usb/otg.h> | 23 | #include <linux/usb/otg.h> |
24 | #include <linux/usb/ulpi.h> | ||
24 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
25 | 26 | ||
26 | #include <mach/mxc_ehci.h> | 27 | #include <mach/mxc_ehci.h> |
27 | 28 | ||
29 | #include <asm/mach-types.h> | ||
30 | |||
28 | #define ULPI_VIEWPORT_OFFSET 0x170 | 31 | #define ULPI_VIEWPORT_OFFSET 0x170 |
29 | #define PORTSC_OFFSET 0x184 | ||
30 | #define USBMODE_OFFSET 0x1a8 | ||
31 | #define USBMODE_CM_HOST 3 | ||
32 | 32 | ||
33 | struct ehci_mxc_priv { | 33 | struct ehci_mxc_priv { |
34 | struct clk *usbclk, *ahbclk; | 34 | struct clk *usbclk, *ahbclk, *phy1clk; |
35 | struct usb_hcd *hcd; | 35 | struct usb_hcd *hcd; |
36 | }; | 36 | }; |
37 | 37 | ||
@@ -41,16 +41,14 @@ static int ehci_mxc_setup(struct usb_hcd *hcd) | |||
41 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | 41 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
42 | int retval; | 42 | int retval; |
43 | 43 | ||
44 | /* EHCI registers start at offset 0x100 */ | ||
45 | ehci->caps = hcd->regs + 0x100; | ||
46 | ehci->regs = hcd->regs + 0x100 + | ||
47 | HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); | ||
48 | dbg_hcs_params(ehci, "reset"); | 44 | dbg_hcs_params(ehci, "reset"); |
49 | dbg_hcc_params(ehci, "reset"); | 45 | dbg_hcc_params(ehci, "reset"); |
50 | 46 | ||
51 | /* cache this readonly data; minimize chip reads */ | 47 | /* cache this readonly data; minimize chip reads */ |
52 | ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); | 48 | ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); |
53 | 49 | ||
50 | hcd->has_tt = 1; | ||
51 | |||
54 | retval = ehci_halt(ehci); | 52 | retval = ehci_halt(ehci); |
55 | if (retval) | 53 | if (retval) |
56 | return retval; | 54 | return retval; |
@@ -60,8 +58,6 @@ static int ehci_mxc_setup(struct usb_hcd *hcd) | |||
60 | if (retval) | 58 | if (retval) |
61 | return retval; | 59 | return retval; |
62 | 60 | ||
63 | hcd->has_tt = 1; | ||
64 | |||
65 | ehci->sbrn = 0x20; | 61 | ehci->sbrn = 0x20; |
66 | 62 | ||
67 | ehci_reset(ehci); | 63 | ehci_reset(ehci); |
@@ -95,6 +91,7 @@ static const struct hc_driver ehci_mxc_hc_driver = { | |||
95 | .urb_enqueue = ehci_urb_enqueue, | 91 | .urb_enqueue = ehci_urb_enqueue, |
96 | .urb_dequeue = ehci_urb_dequeue, | 92 | .urb_dequeue = ehci_urb_dequeue, |
97 | .endpoint_disable = ehci_endpoint_disable, | 93 | .endpoint_disable = ehci_endpoint_disable, |
94 | .endpoint_reset = ehci_endpoint_reset, | ||
98 | 95 | ||
99 | /* | 96 | /* |
100 | * scheduling support | 97 | * scheduling support |
@@ -110,6 +107,8 @@ static const struct hc_driver ehci_mxc_hc_driver = { | |||
110 | .bus_resume = ehci_bus_resume, | 107 | .bus_resume = ehci_bus_resume, |
111 | .relinquish_port = ehci_relinquish_port, | 108 | .relinquish_port = ehci_relinquish_port, |
112 | .port_handed_over = ehci_port_handed_over, | 109 | .port_handed_over = ehci_port_handed_over, |
110 | |||
111 | .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, | ||
113 | }; | 112 | }; |
114 | 113 | ||
115 | static int ehci_mxc_drv_probe(struct platform_device *pdev) | 114 | static int ehci_mxc_drv_probe(struct platform_device *pdev) |
@@ -117,9 +116,11 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) | |||
117 | struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; | 116 | struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; |
118 | struct usb_hcd *hcd; | 117 | struct usb_hcd *hcd; |
119 | struct resource *res; | 118 | struct resource *res; |
120 | int irq, ret, temp; | 119 | int irq, ret; |
120 | unsigned int flags; | ||
121 | struct ehci_mxc_priv *priv; | 121 | struct ehci_mxc_priv *priv; |
122 | struct device *dev = &pdev->dev; | 122 | struct device *dev = &pdev->dev; |
123 | struct ehci_hcd *ehci; | ||
123 | 124 | ||
124 | dev_info(&pdev->dev, "initializing i.MX USB Controller\n"); | 125 | dev_info(&pdev->dev, "initializing i.MX USB Controller\n"); |
125 | 126 | ||
@@ -163,17 +164,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) | |||
163 | goto err_ioremap; | 164 | goto err_ioremap; |
164 | } | 165 | } |
165 | 166 | ||
166 | /* call platform specific init function */ | ||
167 | if (pdata->init) { | ||
168 | ret = pdata->init(pdev); | ||
169 | if (ret) { | ||
170 | dev_err(dev, "platform init failed\n"); | ||
171 | goto err_init; | ||
172 | } | ||
173 | /* platforms need some time to settle changed IO settings */ | ||
174 | mdelay(10); | ||
175 | } | ||
176 | |||
177 | /* enable clocks */ | 167 | /* enable clocks */ |
178 | priv->usbclk = clk_get(dev, "usb"); | 168 | priv->usbclk = clk_get(dev, "usb"); |
179 | if (IS_ERR(priv->usbclk)) { | 169 | if (IS_ERR(priv->usbclk)) { |
@@ -191,18 +181,40 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) | |||
191 | clk_enable(priv->ahbclk); | 181 | clk_enable(priv->ahbclk); |
192 | } | 182 | } |
193 | 183 | ||
194 | /* set USBMODE to host mode */ | 184 | /* "dr" device has its own clock on i.MX51 */ |
195 | temp = readl(hcd->regs + USBMODE_OFFSET); | 185 | if (cpu_is_mx51() && (pdev->id == 0)) { |
196 | writel(temp | USBMODE_CM_HOST, hcd->regs + USBMODE_OFFSET); | 186 | priv->phy1clk = clk_get(dev, "usb_phy1"); |
187 | if (IS_ERR(priv->phy1clk)) { | ||
188 | ret = PTR_ERR(priv->phy1clk); | ||
189 | goto err_clk_phy; | ||
190 | } | ||
191 | clk_enable(priv->phy1clk); | ||
192 | } | ||
193 | |||
194 | |||
195 | /* call platform specific init function */ | ||
196 | if (pdata->init) { | ||
197 | ret = pdata->init(pdev); | ||
198 | if (ret) { | ||
199 | dev_err(dev, "platform init failed\n"); | ||
200 | goto err_init; | ||
201 | } | ||
202 | /* platforms need some time to settle changed IO settings */ | ||
203 | mdelay(10); | ||
204 | } | ||
205 | |||
206 | ehci = hcd_to_ehci(hcd); | ||
207 | |||
208 | /* EHCI registers start at offset 0x100 */ | ||
209 | ehci->caps = hcd->regs + 0x100; | ||
210 | ehci->regs = hcd->regs + 0x100 + | ||
211 | HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); | ||
197 | 212 | ||
198 | /* set up the PORTSCx register */ | 213 | /* set up the PORTSCx register */ |
199 | writel(pdata->portsc, hcd->regs + PORTSC_OFFSET); | 214 | ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]); |
200 | mdelay(10); | ||
201 | 215 | ||
202 | /* setup specific usb hw */ | 216 | /* is this really needed? */ |
203 | ret = mxc_initialize_usb_hw(pdev->id, pdata->flags); | 217 | msleep(10); |
204 | if (ret < 0) | ||
205 | goto err_init; | ||
206 | 218 | ||
207 | /* Initialize the transceiver */ | 219 | /* Initialize the transceiver */ |
208 | if (pdata->otg) { | 220 | if (pdata->otg) { |
@@ -227,12 +239,34 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) | |||
227 | if (ret) | 239 | if (ret) |
228 | goto err_add; | 240 | goto err_add; |
229 | 241 | ||
242 | if (pdata->otg) { | ||
243 | /* | ||
244 | * efikamx and efikasb have some hardware bug which is | ||
245 | * preventing usb to work unless CHRGVBUS is set. | ||
246 | * It's in violation of USB specs | ||
247 | */ | ||
248 | if (machine_is_mx51_efikamx() || machine_is_mx51_efikasb()) { | ||
249 | flags = otg_io_read(pdata->otg, ULPI_OTG_CTRL); | ||
250 | flags |= ULPI_OTG_CTRL_CHRGVBUS; | ||
251 | ret = otg_io_write(pdata->otg, flags, ULPI_OTG_CTRL); | ||
252 | if (ret) { | ||
253 | dev_err(dev, "unable to set CHRVBUS\n"); | ||
254 | goto err_add; | ||
255 | } | ||
256 | } | ||
257 | } | ||
258 | |||
230 | return 0; | 259 | return 0; |
231 | 260 | ||
232 | err_add: | 261 | err_add: |
233 | if (pdata && pdata->exit) | 262 | if (pdata && pdata->exit) |
234 | pdata->exit(pdev); | 263 | pdata->exit(pdev); |
235 | err_init: | 264 | err_init: |
265 | if (priv->phy1clk) { | ||
266 | clk_disable(priv->phy1clk); | ||
267 | clk_put(priv->phy1clk); | ||
268 | } | ||
269 | err_clk_phy: | ||
236 | if (priv->ahbclk) { | 270 | if (priv->ahbclk) { |
237 | clk_disable(priv->ahbclk); | 271 | clk_disable(priv->ahbclk); |
238 | clk_put(priv->ahbclk); | 272 | clk_put(priv->ahbclk); |
@@ -276,6 +310,10 @@ static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) | |||
276 | clk_disable(priv->ahbclk); | 310 | clk_disable(priv->ahbclk); |
277 | clk_put(priv->ahbclk); | 311 | clk_put(priv->ahbclk); |
278 | } | 312 | } |
313 | if (priv->phy1clk) { | ||
314 | clk_disable(priv->phy1clk); | ||
315 | clk_put(priv->phy1clk); | ||
316 | } | ||
279 | 317 | ||
280 | kfree(priv); | 318 | kfree(priv); |
281 | 319 | ||