Commit 0c6a8818447d38f7bb0b0013448659113d37a3e1
Committed by
Greg Kroah-Hartman
1 parent
2ffcdb3bda
Exists in
master
and in
7 other branches
USB: musb: add Blackfin driver to MUSB framework (v2)
- replace MUSB_FIFOSIZE register to MUSB_TXCOUNT, cause no MUSB_FIFOSIZE register on Blackfin - use #ifdef to replace #if defined() Signed-off-by: Bryan Wu <cooloney@kernel.org> Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Showing 2 changed files with 368 additions and 0 deletions Side-by-side Diff
drivers/usb/musb/blackfin.c
1 | +/* | |
2 | + * MUSB OTG controller driver for Blackfin Processors | |
3 | + * | |
4 | + * Copyright 2006-2008 Analog Devices Inc. | |
5 | + * | |
6 | + * Enter bugs at http://blackfin.uclinux.org/ | |
7 | + * | |
8 | + * Licensed under the GPL-2 or later. | |
9 | + */ | |
10 | + | |
11 | +#include <linux/module.h> | |
12 | +#include <linux/kernel.h> | |
13 | +#include <linux/sched.h> | |
14 | +#include <linux/slab.h> | |
15 | +#include <linux/init.h> | |
16 | +#include <linux/list.h> | |
17 | +#include <linux/clk.h> | |
18 | +#include <linux/gpio.h> | |
19 | +#include <linux/io.h> | |
20 | + | |
21 | +#include <asm/cacheflush.h> | |
22 | + | |
23 | +#include "musb_core.h" | |
24 | +#include "blackfin.h" | |
25 | + | |
26 | +/* | |
27 | + * Load an endpoint's FIFO | |
28 | + */ | |
29 | +void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) | |
30 | +{ | |
31 | + void __iomem *fifo = hw_ep->fifo; | |
32 | + void __iomem *epio = hw_ep->regs; | |
33 | + | |
34 | + prefetch((u8 *)src); | |
35 | + | |
36 | + musb_writew(epio, MUSB_TXCOUNT, len); | |
37 | + | |
38 | + DBG(4, "TX ep%d fifo %p count %d buf %p, epio %p\n", | |
39 | + hw_ep->epnum, fifo, len, src, epio); | |
40 | + | |
41 | + dump_fifo_data(src, len); | |
42 | + | |
43 | + if (unlikely((unsigned long)src & 0x01)) | |
44 | + outsw_8(fifo, src, len & 0x01 ? (len >> 1) + 1 : len >> 1); | |
45 | + else | |
46 | + outsw(fifo, src, len & 0x01 ? (len >> 1) + 1 : len >> 1); | |
47 | +} | |
48 | + | |
49 | +/* | |
50 | + * Unload an endpoint's FIFO | |
51 | + */ | |
52 | +void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) | |
53 | +{ | |
54 | + void __iomem *fifo = hw_ep->fifo; | |
55 | + u8 epnum = hw_ep->epnum; | |
56 | + u16 dma_reg = 0; | |
57 | + int i; | |
58 | + u16 *data; | |
59 | + | |
60 | + DBG(4, "%cX ep%d fifo %p count %d buf %p\n", | |
61 | + 'R', hw_ep->epnum, fifo, len, dst); | |
62 | + | |
63 | +#ifdef CONFIG_BF52x | |
64 | + invalidate_dcache_range((unsigned int)dst, | |
65 | + (unsigned int)(dst + len)); | |
66 | + | |
67 | + /* Setup DMA address register */ | |
68 | + dma_reg = (u16) ((u32) dst & 0xFFFF); | |
69 | + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_LOW), dma_reg); | |
70 | + SSYNC(); | |
71 | + | |
72 | + dma_reg = (u16) (((u32) dst >> 16) & 0xFFFF); | |
73 | + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_HIGH), dma_reg); | |
74 | + SSYNC(); | |
75 | + | |
76 | + /* Setup DMA count register */ | |
77 | + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_LOW), len); | |
78 | + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_HIGH), 0); | |
79 | + SSYNC(); | |
80 | + | |
81 | + /* Enable the DMA */ | |
82 | + dma_reg = (epnum << 4) | DMA_ENA | INT_ENA; | |
83 | + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), dma_reg); | |
84 | + SSYNC(); | |
85 | + | |
86 | + /* Wait for compelete */ | |
87 | + while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << epnum))) | |
88 | + cpu_relax(); | |
89 | + | |
90 | + /* acknowledge dma interrupt */ | |
91 | + bfin_write_USB_DMA_INTERRUPT(1 << epnum); | |
92 | + SSYNC(); | |
93 | + | |
94 | + /* Reset DMA */ | |
95 | + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), 0); | |
96 | + SSYNC(); | |
97 | +#else | |
98 | + if (unlikely((unsigned long)dst & 0x01)) | |
99 | + insw_8(fifo, dst, len & 0x01 ? (len >> 1) + 1 : len >> 1); | |
100 | + else | |
101 | + insw(fifo, dst, len & 0x01 ? (len >> 1) + 1 : len >> 1); | |
102 | +#endif | |
103 | + | |
104 | + dump_fifo_data(dst, len); | |
105 | +} | |
106 | + | |
107 | +static irqreturn_t blackfin_interrupt(int irq, void *__hci) | |
108 | +{ | |
109 | + unsigned long flags; | |
110 | + irqreturn_t retval = IRQ_NONE; | |
111 | + struct musb *musb = __hci; | |
112 | + | |
113 | + spin_lock_irqsave(&musb->lock, flags); | |
114 | + | |
115 | + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); | |
116 | + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); | |
117 | + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); | |
118 | + | |
119 | + if (musb->int_usb || musb->int_tx || musb->int_rx) { | |
120 | + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); | |
121 | + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); | |
122 | + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); | |
123 | + retval = musb_interrupt(musb); | |
124 | + } | |
125 | + | |
126 | + spin_unlock_irqrestore(&musb->lock, flags); | |
127 | + | |
128 | + /* REVISIT we sometimes get spurious IRQs on g_ep0 | |
129 | + * not clear why... fall in BF54x too. | |
130 | + */ | |
131 | + if (retval != IRQ_HANDLED) | |
132 | + DBG(5, "spurious?\n"); | |
133 | + | |
134 | + return IRQ_HANDLED; | |
135 | +} | |
136 | + | |
137 | +static void musb_conn_timer_handler(unsigned long _musb) | |
138 | +{ | |
139 | + struct musb *musb = (void *)_musb; | |
140 | + unsigned long flags; | |
141 | + u16 val; | |
142 | + | |
143 | + spin_lock_irqsave(&musb->lock, flags); | |
144 | + switch (musb->xceiv.state) { | |
145 | + case OTG_STATE_A_IDLE: | |
146 | + case OTG_STATE_A_WAIT_BCON: | |
147 | + /* Start a new session */ | |
148 | + val = musb_readw(musb->mregs, MUSB_DEVCTL); | |
149 | + val |= MUSB_DEVCTL_SESSION; | |
150 | + musb_writew(musb->mregs, MUSB_DEVCTL, val); | |
151 | + | |
152 | + val = musb_readw(musb->mregs, MUSB_DEVCTL); | |
153 | + if (!(val & MUSB_DEVCTL_BDEVICE)) { | |
154 | + gpio_set_value(musb->config->gpio_vrsel, 1); | |
155 | + musb->xceiv.state = OTG_STATE_A_WAIT_BCON; | |
156 | + } else { | |
157 | + gpio_set_value(musb->config->gpio_vrsel, 0); | |
158 | + | |
159 | + /* Ignore VBUSERROR and SUSPEND IRQ */ | |
160 | + val = musb_readb(musb->mregs, MUSB_INTRUSBE); | |
161 | + val &= ~MUSB_INTR_VBUSERROR; | |
162 | + musb_writeb(musb->mregs, MUSB_INTRUSBE, val); | |
163 | + | |
164 | + val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR; | |
165 | + musb_writeb(musb->mregs, MUSB_INTRUSB, val); | |
166 | + | |
167 | + val = MUSB_POWER_HSENAB; | |
168 | + musb_writeb(musb->mregs, MUSB_POWER, val); | |
169 | + } | |
170 | + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); | |
171 | + break; | |
172 | + | |
173 | + default: | |
174 | + DBG(1, "%s state not handled\n", otg_state_string(musb)); | |
175 | + break; | |
176 | + } | |
177 | + spin_unlock_irqrestore(&musb->lock, flags); | |
178 | + | |
179 | + DBG(4, "state is %s\n", otg_state_string(musb)); | |
180 | +} | |
181 | + | |
182 | +void musb_platform_enable(struct musb *musb) | |
183 | +{ | |
184 | + if (is_host_enabled(musb)) { | |
185 | + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); | |
186 | + musb->a_wait_bcon = TIMER_DELAY; | |
187 | + } | |
188 | +} | |
189 | + | |
190 | +void musb_platform_disable(struct musb *musb) | |
191 | +{ | |
192 | +} | |
193 | + | |
194 | +static void bfin_vbus_power(struct musb *musb, int is_on, int sleeping) | |
195 | +{ | |
196 | +} | |
197 | + | |
198 | +static void bfin_set_vbus(struct musb *musb, int is_on) | |
199 | +{ | |
200 | + if (is_on) | |
201 | + gpio_set_value(musb->config->gpio_vrsel, 1); | |
202 | + else | |
203 | + gpio_set_value(musb->config->gpio_vrsel, 0); | |
204 | + | |
205 | + DBG(1, "VBUS %s, devctl %02x " | |
206 | + /* otg %3x conf %08x prcm %08x */ "\n", | |
207 | + otg_state_string(musb), | |
208 | + musb_readb(musb->mregs, MUSB_DEVCTL)); | |
209 | +} | |
210 | + | |
211 | +static int bfin_set_power(struct otg_transceiver *x, unsigned mA) | |
212 | +{ | |
213 | + return 0; | |
214 | +} | |
215 | + | |
216 | +void musb_platform_try_idle(struct musb *musb, unsigned long timeout) | |
217 | +{ | |
218 | + if (is_host_enabled(musb)) | |
219 | + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); | |
220 | +} | |
221 | + | |
222 | +int musb_platform_get_vbus_status(struct musb *musb) | |
223 | +{ | |
224 | + return 0; | |
225 | +} | |
226 | + | |
227 | +void musb_platform_set_mode(struct musb *musb, u8 musb_mode) | |
228 | +{ | |
229 | +} | |
230 | + | |
231 | +int __init musb_platform_init(struct musb *musb) | |
232 | +{ | |
233 | + | |
234 | + /* | |
235 | + * Rev 1.0 BF549 EZ-KITs require PE7 to be high for both DEVICE | |
236 | + * and OTG HOST modes, while rev 1.1 and greater require PE7 to | |
237 | + * be low for DEVICE mode and high for HOST mode. We set it high | |
238 | + * here because we are in host mode | |
239 | + */ | |
240 | + | |
241 | + if (gpio_request(musb->config->gpio_vrsel, "USB_VRSEL")) { | |
242 | + printk(KERN_ERR "Failed ro request USB_VRSEL GPIO_%d \n", | |
243 | + musb->config->gpio_vrsel); | |
244 | + return -ENODEV; | |
245 | + } | |
246 | + gpio_direction_output(musb->config->gpio_vrsel, 0); | |
247 | + | |
248 | + /* Anomaly #05000346 */ | |
249 | + bfin_write_USB_APHY_CALIB(0x5411); | |
250 | + SSYNC(); | |
251 | + | |
252 | + /* Anomaly #05000347 */ | |
253 | + bfin_write_USB_APHY_CNTRL(0x0); | |
254 | + SSYNC(); | |
255 | + | |
256 | + /* TODO | |
257 | + * Set SIC-IVG register | |
258 | + */ | |
259 | + | |
260 | + /* Configure PLL oscillator register */ | |
261 | + bfin_write_USB_PLLOSC_CTRL(0x30a8); | |
262 | + SSYNC(); | |
263 | + | |
264 | + bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1); | |
265 | + SSYNC(); | |
266 | + | |
267 | + bfin_write_USB_EP_NI0_RXMAXP(64); | |
268 | + SSYNC(); | |
269 | + | |
270 | + bfin_write_USB_EP_NI0_TXMAXP(64); | |
271 | + SSYNC(); | |
272 | + | |
273 | + /* Route INTRUSB/INTR_RX/INTR_TX to USB_INT0*/ | |
274 | + bfin_write_USB_GLOBINTR(0x7); | |
275 | + SSYNC(); | |
276 | + | |
277 | + bfin_write_USB_GLOBAL_CTL(GLOBAL_ENA | EP1_TX_ENA | EP2_TX_ENA | | |
278 | + EP3_TX_ENA | EP4_TX_ENA | EP5_TX_ENA | | |
279 | + EP6_TX_ENA | EP7_TX_ENA | EP1_RX_ENA | | |
280 | + EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA | | |
281 | + EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA); | |
282 | + SSYNC(); | |
283 | + | |
284 | + if (is_host_enabled(musb)) { | |
285 | + musb->board_set_vbus = bfin_set_vbus; | |
286 | + setup_timer(&musb_conn_timer, | |
287 | + musb_conn_timer_handler, (unsigned long) musb); | |
288 | + } | |
289 | + if (is_peripheral_enabled(musb)) | |
290 | + musb->xceiv.set_power = bfin_set_power; | |
291 | + | |
292 | + musb->isr = blackfin_interrupt; | |
293 | + | |
294 | + return 0; | |
295 | +} | |
296 | + | |
297 | +int musb_platform_suspend(struct musb *musb) | |
298 | +{ | |
299 | + return 0; | |
300 | +} | |
301 | + | |
302 | +int musb_platform_resume(struct musb *musb) | |
303 | +{ | |
304 | + return 0; | |
305 | +} | |
306 | + | |
307 | + | |
308 | +int musb_platform_exit(struct musb *musb) | |
309 | +{ | |
310 | + | |
311 | + bfin_vbus_power(musb, 0 /*off*/, 1); | |
312 | + gpio_free(musb->config->gpio_vrsel); | |
313 | + musb_platform_suspend(musb); | |
314 | + | |
315 | + return 0; | |
316 | +} |
drivers/usb/musb/blackfin.h
1 | +/* | |
2 | + * Copyright (C) 2007 by Analog Devices, Inc. | |
3 | + * | |
4 | + * The Inventra Controller Driver for Linux is free software; you | |
5 | + * can redistribute it and/or modify it under the terms of the GNU | |
6 | + * General Public License version 2 as published by the Free Software | |
7 | + * Foundation. | |
8 | + */ | |
9 | + | |
10 | +#ifndef __MUSB_BLACKFIN_H__ | |
11 | +#define __MUSB_BLACKFIN_H__ | |
12 | + | |
13 | +/* | |
14 | + * Blackfin specific definitions | |
15 | + */ | |
16 | + | |
17 | +#undef DUMP_FIFO_DATA | |
18 | +#ifdef DUMP_FIFO_DATA | |
19 | +static void dump_fifo_data(u8 *buf, u16 len) | |
20 | +{ | |
21 | + u8 *tmp = buf; | |
22 | + int i; | |
23 | + | |
24 | + for (i = 0; i < len; i++) { | |
25 | + if (!(i % 16) && i) | |
26 | + pr_debug("\n"); | |
27 | + pr_debug("%02x ", *tmp++); | |
28 | + } | |
29 | + pr_debug("\n"); | |
30 | +} | |
31 | +#else | |
32 | +#define dump_fifo_data(buf, len) do {} while (0) | |
33 | +#endif | |
34 | + | |
35 | +#ifdef CONFIG_BF52x | |
36 | + | |
37 | +#define USB_DMA_BASE USB_DMA_INTERRUPT | |
38 | +#define USB_DMAx_CTRL 0x04 | |
39 | +#define USB_DMAx_ADDR_LOW 0x08 | |
40 | +#define USB_DMAx_ADDR_HIGH 0x0C | |
41 | +#define USB_DMAx_COUNT_LOW 0x10 | |
42 | +#define USB_DMAx_COUNT_HIGH 0x14 | |
43 | + | |
44 | +#define USB_DMA_REG(ep, reg) (USB_DMA_BASE + 0x20 * ep + reg) | |
45 | +#endif | |
46 | + | |
47 | +/* Almost 1 second */ | |
48 | +#define TIMER_DELAY (1 * HZ) | |
49 | + | |
50 | +static struct timer_list musb_conn_timer; | |
51 | + | |
52 | +#endif /* __MUSB_BLACKFIN_H__ */ |