diff options
Diffstat (limited to 'arch/arm/plat-omap/mcbsp.c')
-rw-r--r-- | arch/arm/plat-omap/mcbsp.c | 758 |
1 files changed, 758 insertions, 0 deletions
diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c new file mode 100644 index 000000000000..43567d5edddb --- /dev/null +++ b/arch/arm/plat-omap/mcbsp.c | |||
@@ -0,0 +1,758 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-omap/mcbsp.c | ||
3 | * | ||
4 | * Copyright (C) 2004 Nokia Corporation | ||
5 | * Author: Samuel Ortiz <samuel.ortiz@nokia.com> | ||
6 | * | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Multichannel mode not supported. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/wait.h> | ||
19 | #include <linux/completion.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/err.h> | ||
22 | |||
23 | #include <asm/delay.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/irq.h> | ||
26 | |||
27 | #include <asm/arch/dma.h> | ||
28 | #include <asm/arch/mux.h> | ||
29 | #include <asm/arch/irqs.h> | ||
30 | #include <asm/arch/mcbsp.h> | ||
31 | |||
32 | #include <asm/hardware/clock.h> | ||
33 | |||
34 | #ifdef CONFIG_MCBSP_DEBUG | ||
35 | #define DBG(x...) printk(x) | ||
36 | #else | ||
37 | #define DBG(x...) do { } while (0) | ||
38 | #endif | ||
39 | |||
40 | struct omap_mcbsp { | ||
41 | u32 io_base; | ||
42 | u8 id; | ||
43 | u8 free; | ||
44 | omap_mcbsp_word_length rx_word_length; | ||
45 | omap_mcbsp_word_length tx_word_length; | ||
46 | |||
47 | /* IRQ based TX/RX */ | ||
48 | int rx_irq; | ||
49 | int tx_irq; | ||
50 | |||
51 | /* DMA stuff */ | ||
52 | u8 dma_rx_sync; | ||
53 | short dma_rx_lch; | ||
54 | u8 dma_tx_sync; | ||
55 | short dma_tx_lch; | ||
56 | |||
57 | /* Completion queues */ | ||
58 | struct completion tx_irq_completion; | ||
59 | struct completion rx_irq_completion; | ||
60 | struct completion tx_dma_completion; | ||
61 | struct completion rx_dma_completion; | ||
62 | |||
63 | spinlock_t lock; | ||
64 | }; | ||
65 | |||
66 | static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT]; | ||
67 | static struct clk *mcbsp_dsp_ck = 0; | ||
68 | static struct clk *mcbsp_api_ck = 0; | ||
69 | static struct clk *mcbsp_dspxor_ck = 0; | ||
70 | |||
71 | |||
72 | static void omap_mcbsp_dump_reg(u8 id) | ||
73 | { | ||
74 | DBG("**** MCBSP%d regs ****\n", mcbsp[id].id); | ||
75 | DBG("DRR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR2)); | ||
76 | DBG("DRR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR1)); | ||
77 | DBG("DXR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR2)); | ||
78 | DBG("DXR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR1)); | ||
79 | DBG("SPCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR2)); | ||
80 | DBG("SPCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR1)); | ||
81 | DBG("RCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR2)); | ||
82 | DBG("RCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR1)); | ||
83 | DBG("XCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR2)); | ||
84 | DBG("XCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR1)); | ||
85 | DBG("SRGR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR2)); | ||
86 | DBG("SRGR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR1)); | ||
87 | DBG("PCR0: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, PCR0)); | ||
88 | DBG("***********************\n"); | ||
89 | } | ||
90 | |||
91 | |||
92 | static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
93 | { | ||
94 | struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id); | ||
95 | |||
96 | DBG("TX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2)); | ||
97 | |||
98 | complete(&mcbsp_tx->tx_irq_completion); | ||
99 | return IRQ_HANDLED; | ||
100 | } | ||
101 | |||
102 | static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
103 | { | ||
104 | struct omap_mcbsp * mcbsp_rx = (struct omap_mcbsp *)(dev_id); | ||
105 | |||
106 | DBG("RX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR2)); | ||
107 | |||
108 | complete(&mcbsp_rx->rx_irq_completion); | ||
109 | return IRQ_HANDLED; | ||
110 | } | ||
111 | |||
112 | |||
113 | static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) | ||
114 | { | ||
115 | struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data); | ||
116 | |||
117 | DBG("TX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2)); | ||
118 | |||
119 | /* We can free the channels */ | ||
120 | omap_free_dma(mcbsp_dma_tx->dma_tx_lch); | ||
121 | mcbsp_dma_tx->dma_tx_lch = -1; | ||
122 | |||
123 | complete(&mcbsp_dma_tx->tx_dma_completion); | ||
124 | } | ||
125 | |||
126 | static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data) | ||
127 | { | ||
128 | struct omap_mcbsp * mcbsp_dma_rx = (struct omap_mcbsp *)(data); | ||
129 | |||
130 | DBG("RX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2)); | ||
131 | |||
132 | /* We can free the channels */ | ||
133 | omap_free_dma(mcbsp_dma_rx->dma_rx_lch); | ||
134 | mcbsp_dma_rx->dma_rx_lch = -1; | ||
135 | |||
136 | complete(&mcbsp_dma_rx->rx_dma_completion); | ||
137 | } | ||
138 | |||
139 | |||
140 | /* | ||
141 | * omap_mcbsp_config simply write a config to the | ||
142 | * appropriate McBSP. | ||
143 | * You either call this function or set the McBSP registers | ||
144 | * by yourself before calling omap_mcbsp_start(). | ||
145 | */ | ||
146 | |||
147 | void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config) | ||
148 | { | ||
149 | u32 io_base = mcbsp[id].io_base; | ||
150 | |||
151 | DBG("OMAP-McBSP: McBSP%d io_base: 0x%8x\n", id+1, io_base); | ||
152 | |||
153 | /* We write the given config */ | ||
154 | OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2); | ||
155 | OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1); | ||
156 | OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2); | ||
157 | OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1); | ||
158 | OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2); | ||
159 | OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1); | ||
160 | OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2); | ||
161 | OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1); | ||
162 | OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2); | ||
163 | OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1); | ||
164 | OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0); | ||
165 | } | ||
166 | |||
167 | |||
168 | |||
169 | static int omap_mcbsp_check(unsigned int id) | ||
170 | { | ||
171 | if (cpu_is_omap730()) { | ||
172 | if (id > OMAP_MAX_MCBSP_COUNT - 1) { | ||
173 | printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); | ||
174 | return -1; | ||
175 | } | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | if (cpu_is_omap1510() || cpu_is_omap16xx()) { | ||
180 | if (id > OMAP_MAX_MCBSP_COUNT) { | ||
181 | printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); | ||
182 | return -1; | ||
183 | } | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | return -1; | ||
188 | } | ||
189 | |||
190 | #define EN_XORPCK 1 | ||
191 | #define DSP_RSTCT2 0xe1008014 | ||
192 | |||
193 | static void omap_mcbsp_dsp_request(void) | ||
194 | { | ||
195 | if (cpu_is_omap1510() || cpu_is_omap16xx()) { | ||
196 | clk_use(mcbsp_dsp_ck); | ||
197 | clk_use(mcbsp_api_ck); | ||
198 | |||
199 | /* enable 12MHz clock to mcbsp 1 & 3 */ | ||
200 | clk_use(mcbsp_dspxor_ck); | ||
201 | __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1 << 1, | ||
202 | DSP_RSTCT2); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | static void omap_mcbsp_dsp_free(void) | ||
207 | { | ||
208 | if (cpu_is_omap1510() || cpu_is_omap16xx()) { | ||
209 | clk_unuse(mcbsp_dspxor_ck); | ||
210 | clk_unuse(mcbsp_dsp_ck); | ||
211 | clk_unuse(mcbsp_api_ck); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | int omap_mcbsp_request(unsigned int id) | ||
216 | { | ||
217 | int err; | ||
218 | |||
219 | if (omap_mcbsp_check(id) < 0) | ||
220 | return -EINVAL; | ||
221 | |||
222 | /* | ||
223 | * On 1510, 1610 and 1710, McBSP1 and McBSP3 | ||
224 | * are DSP public peripherals. | ||
225 | */ | ||
226 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | ||
227 | omap_mcbsp_dsp_request(); | ||
228 | |||
229 | spin_lock(&mcbsp[id].lock); | ||
230 | if (!mcbsp[id].free) { | ||
231 | printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); | ||
232 | spin_unlock(&mcbsp[id].lock); | ||
233 | return -1; | ||
234 | } | ||
235 | |||
236 | mcbsp[id].free = 0; | ||
237 | spin_unlock(&mcbsp[id].lock); | ||
238 | |||
239 | /* We need to get IRQs here */ | ||
240 | err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, | ||
241 | "McBSP", | ||
242 | (void *) (&mcbsp[id])); | ||
243 | if (err != 0) { | ||
244 | printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", | ||
245 | mcbsp[id].tx_irq, mcbsp[id].id); | ||
246 | return err; | ||
247 | } | ||
248 | |||
249 | init_completion(&(mcbsp[id].tx_irq_completion)); | ||
250 | |||
251 | |||
252 | err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, | ||
253 | "McBSP", | ||
254 | (void *) (&mcbsp[id])); | ||
255 | if (err != 0) { | ||
256 | printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", | ||
257 | mcbsp[id].rx_irq, mcbsp[id].id); | ||
258 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | ||
259 | return err; | ||
260 | } | ||
261 | |||
262 | init_completion(&(mcbsp[id].rx_irq_completion)); | ||
263 | return 0; | ||
264 | |||
265 | } | ||
266 | |||
267 | void omap_mcbsp_free(unsigned int id) | ||
268 | { | ||
269 | if (omap_mcbsp_check(id) < 0) | ||
270 | return; | ||
271 | |||
272 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | ||
273 | omap_mcbsp_dsp_free(); | ||
274 | |||
275 | spin_lock(&mcbsp[id].lock); | ||
276 | if (mcbsp[id].free) { | ||
277 | printk (KERN_ERR "OMAP-McBSP: McBSP%d was not reserved\n", id + 1); | ||
278 | spin_unlock(&mcbsp[id].lock); | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | mcbsp[id].free = 1; | ||
283 | spin_unlock(&mcbsp[id].lock); | ||
284 | |||
285 | /* Free IRQs */ | ||
286 | free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); | ||
287 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * Here we start the McBSP, by enabling the sample | ||
292 | * generator, both transmitter and receivers, | ||
293 | * and the frame sync. | ||
294 | */ | ||
295 | void omap_mcbsp_start(unsigned int id) | ||
296 | { | ||
297 | u32 io_base; | ||
298 | u16 w; | ||
299 | |||
300 | if (omap_mcbsp_check(id) < 0) | ||
301 | return; | ||
302 | |||
303 | io_base = mcbsp[id].io_base; | ||
304 | |||
305 | mcbsp[id].rx_word_length = ((OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7); | ||
306 | mcbsp[id].tx_word_length = ((OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7); | ||
307 | |||
308 | /* Start the sample generator */ | ||
309 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
310 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | ||
311 | |||
312 | /* Enable transmitter and receiver */ | ||
313 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
314 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); | ||
315 | |||
316 | w = OMAP_MCBSP_READ(io_base, SPCR1); | ||
317 | OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); | ||
318 | |||
319 | udelay(100); | ||
320 | |||
321 | /* Start frame sync */ | ||
322 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
323 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | ||
324 | |||
325 | /* Dump McBSP Regs */ | ||
326 | omap_mcbsp_dump_reg(id); | ||
327 | |||
328 | } | ||
329 | |||
330 | void omap_mcbsp_stop(unsigned int id) | ||
331 | { | ||
332 | u32 io_base; | ||
333 | u16 w; | ||
334 | |||
335 | if (omap_mcbsp_check(id) < 0) | ||
336 | return; | ||
337 | |||
338 | io_base = mcbsp[id].io_base; | ||
339 | |||
340 | /* Reset transmitter */ | ||
341 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
342 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); | ||
343 | |||
344 | /* Reset receiver */ | ||
345 | w = OMAP_MCBSP_READ(io_base, SPCR1); | ||
346 | OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); | ||
347 | |||
348 | /* Reset the sample rate generator */ | ||
349 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
350 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | ||
351 | } | ||
352 | |||
353 | |||
354 | /* polled mcbsp i/o operations */ | ||
355 | int omap_mcbsp_pollwrite(unsigned int id, u16 buf) | ||
356 | { | ||
357 | u32 base = mcbsp[id].io_base; | ||
358 | writew(buf, base + OMAP_MCBSP_REG_DXR1); | ||
359 | /* if frame sync error - clear the error */ | ||
360 | if (readw(base + OMAP_MCBSP_REG_SPCR2) & XSYNC_ERR) { | ||
361 | /* clear error */ | ||
362 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) & (~XSYNC_ERR), | ||
363 | base + OMAP_MCBSP_REG_SPCR2); | ||
364 | /* resend */ | ||
365 | return -1; | ||
366 | } else { | ||
367 | /* wait for transmit confirmation */ | ||
368 | int attemps = 0; | ||
369 | while (!(readw(base + OMAP_MCBSP_REG_SPCR2) & XRDY)) { | ||
370 | if (attemps++ > 1000) { | ||
371 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) & | ||
372 | (~XRST), | ||
373 | base + OMAP_MCBSP_REG_SPCR2); | ||
374 | udelay(10); | ||
375 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) | | ||
376 | (XRST), | ||
377 | base + OMAP_MCBSP_REG_SPCR2); | ||
378 | udelay(10); | ||
379 | printk(KERN_ERR | ||
380 | " Could not write to McBSP Register\n"); | ||
381 | return -2; | ||
382 | } | ||
383 | } | ||
384 | } | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | int omap_mcbsp_pollread(unsigned int id, u16 * buf) | ||
389 | { | ||
390 | u32 base = mcbsp[id].io_base; | ||
391 | /* if frame sync error - clear the error */ | ||
392 | if (readw(base + OMAP_MCBSP_REG_SPCR1) & RSYNC_ERR) { | ||
393 | /* clear error */ | ||
394 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) & (~RSYNC_ERR), | ||
395 | base + OMAP_MCBSP_REG_SPCR1); | ||
396 | /* resend */ | ||
397 | return -1; | ||
398 | } else { | ||
399 | /* wait for recieve confirmation */ | ||
400 | int attemps = 0; | ||
401 | while (!(readw(base + OMAP_MCBSP_REG_SPCR1) & RRDY)) { | ||
402 | if (attemps++ > 1000) { | ||
403 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) & | ||
404 | (~RRST), | ||
405 | base + OMAP_MCBSP_REG_SPCR1); | ||
406 | udelay(10); | ||
407 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) | | ||
408 | (RRST), | ||
409 | base + OMAP_MCBSP_REG_SPCR1); | ||
410 | udelay(10); | ||
411 | printk(KERN_ERR | ||
412 | " Could not read from McBSP Register\n"); | ||
413 | return -2; | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | *buf = readw(base + OMAP_MCBSP_REG_DRR1); | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | /* | ||
422 | * IRQ based word transmission. | ||
423 | */ | ||
424 | void omap_mcbsp_xmit_word(unsigned int id, u32 word) | ||
425 | { | ||
426 | u32 io_base; | ||
427 | omap_mcbsp_word_length word_length = mcbsp[id].tx_word_length; | ||
428 | |||
429 | if (omap_mcbsp_check(id) < 0) | ||
430 | return; | ||
431 | |||
432 | io_base = mcbsp[id].io_base; | ||
433 | |||
434 | wait_for_completion(&(mcbsp[id].tx_irq_completion)); | ||
435 | |||
436 | if (word_length > OMAP_MCBSP_WORD_16) | ||
437 | OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); | ||
438 | OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); | ||
439 | } | ||
440 | |||
441 | u32 omap_mcbsp_recv_word(unsigned int id) | ||
442 | { | ||
443 | u32 io_base; | ||
444 | u16 word_lsb, word_msb = 0; | ||
445 | omap_mcbsp_word_length word_length = mcbsp[id].rx_word_length; | ||
446 | |||
447 | if (omap_mcbsp_check(id) < 0) | ||
448 | return -EINVAL; | ||
449 | |||
450 | io_base = mcbsp[id].io_base; | ||
451 | |||
452 | wait_for_completion(&(mcbsp[id].rx_irq_completion)); | ||
453 | |||
454 | if (word_length > OMAP_MCBSP_WORD_16) | ||
455 | word_msb = OMAP_MCBSP_READ(io_base, DRR2); | ||
456 | word_lsb = OMAP_MCBSP_READ(io_base, DRR1); | ||
457 | |||
458 | return (word_lsb | (word_msb << 16)); | ||
459 | } | ||
460 | |||
461 | |||
462 | /* | ||
463 | * Simple DMA based buffer rx/tx routines. | ||
464 | * Nothing fancy, just a single buffer tx/rx through DMA. | ||
465 | * The DMA resources are released once the transfer is done. | ||
466 | * For anything fancier, you should use your own customized DMA | ||
467 | * routines and callbacks. | ||
468 | */ | ||
469 | int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | ||
470 | { | ||
471 | int dma_tx_ch; | ||
472 | |||
473 | if (omap_mcbsp_check(id) < 0) | ||
474 | return -EINVAL; | ||
475 | |||
476 | if (omap_request_dma(mcbsp[id].dma_tx_sync, "McBSP TX", omap_mcbsp_tx_dma_callback, | ||
477 | &mcbsp[id], | ||
478 | &dma_tx_ch)) { | ||
479 | printk("OMAP-McBSP: Unable to request DMA channel for McBSP%d TX. Trying IRQ based TX\n", id+1); | ||
480 | return -EAGAIN; | ||
481 | } | ||
482 | mcbsp[id].dma_tx_lch = dma_tx_ch; | ||
483 | |||
484 | DBG("TX DMA on channel %d\n", dma_tx_ch); | ||
485 | |||
486 | init_completion(&(mcbsp[id].tx_dma_completion)); | ||
487 | |||
488 | omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, | ||
489 | OMAP_DMA_DATA_TYPE_S16, | ||
490 | length >> 1, 1, | ||
491 | OMAP_DMA_SYNC_ELEMENT); | ||
492 | |||
493 | omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, | ||
494 | OMAP_DMA_PORT_TIPB, | ||
495 | OMAP_DMA_AMODE_CONSTANT, | ||
496 | mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1); | ||
497 | |||
498 | omap_set_dma_src_params(mcbsp[id].dma_tx_lch, | ||
499 | OMAP_DMA_PORT_EMIFF, | ||
500 | OMAP_DMA_AMODE_POST_INC, | ||
501 | buffer); | ||
502 | |||
503 | omap_start_dma(mcbsp[id].dma_tx_lch); | ||
504 | wait_for_completion(&(mcbsp[id].tx_dma_completion)); | ||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | |||
509 | int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | ||
510 | { | ||
511 | int dma_rx_ch; | ||
512 | |||
513 | if (omap_mcbsp_check(id) < 0) | ||
514 | return -EINVAL; | ||
515 | |||
516 | if (omap_request_dma(mcbsp[id].dma_rx_sync, "McBSP RX", omap_mcbsp_rx_dma_callback, | ||
517 | &mcbsp[id], | ||
518 | &dma_rx_ch)) { | ||
519 | printk("Unable to request DMA channel for McBSP%d RX. Trying IRQ based RX\n", id+1); | ||
520 | return -EAGAIN; | ||
521 | } | ||
522 | mcbsp[id].dma_rx_lch = dma_rx_ch; | ||
523 | |||
524 | DBG("RX DMA on channel %d\n", dma_rx_ch); | ||
525 | |||
526 | init_completion(&(mcbsp[id].rx_dma_completion)); | ||
527 | |||
528 | omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, | ||
529 | OMAP_DMA_DATA_TYPE_S16, | ||
530 | length >> 1, 1, | ||
531 | OMAP_DMA_SYNC_ELEMENT); | ||
532 | |||
533 | omap_set_dma_src_params(mcbsp[id].dma_rx_lch, | ||
534 | OMAP_DMA_PORT_TIPB, | ||
535 | OMAP_DMA_AMODE_CONSTANT, | ||
536 | mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1); | ||
537 | |||
538 | omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, | ||
539 | OMAP_DMA_PORT_EMIFF, | ||
540 | OMAP_DMA_AMODE_POST_INC, | ||
541 | buffer); | ||
542 | |||
543 | omap_start_dma(mcbsp[id].dma_rx_lch); | ||
544 | wait_for_completion(&(mcbsp[id].rx_dma_completion)); | ||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | |||
549 | /* | ||
550 | * SPI wrapper. | ||
551 | * Since SPI setup is much simpler than the generic McBSP one, | ||
552 | * this wrapper just need an omap_mcbsp_spi_cfg structure as an input. | ||
553 | * Once this is done, you can call omap_mcbsp_start(). | ||
554 | */ | ||
555 | void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg) | ||
556 | { | ||
557 | struct omap_mcbsp_reg_cfg mcbsp_cfg; | ||
558 | |||
559 | if (omap_mcbsp_check(id) < 0) | ||
560 | return; | ||
561 | |||
562 | memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); | ||
563 | |||
564 | /* SPI has only one frame */ | ||
565 | mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0)); | ||
566 | mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0)); | ||
567 | |||
568 | /* Clock stop mode */ | ||
569 | if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY) | ||
570 | mcbsp_cfg.spcr1 |= (1 << 12); | ||
571 | else | ||
572 | mcbsp_cfg.spcr1 |= (3 << 11); | ||
573 | |||
574 | /* Set clock parities */ | ||
575 | if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING) | ||
576 | mcbsp_cfg.pcr0 |= CLKRP; | ||
577 | else | ||
578 | mcbsp_cfg.pcr0 &= ~CLKRP; | ||
579 | |||
580 | if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING) | ||
581 | mcbsp_cfg.pcr0 &= ~CLKXP; | ||
582 | else | ||
583 | mcbsp_cfg.pcr0 |= CLKXP; | ||
584 | |||
585 | /* Set SCLKME to 0 and CLKSM to 1 */ | ||
586 | mcbsp_cfg.pcr0 &= ~SCLKME; | ||
587 | mcbsp_cfg.srgr2 |= CLKSM; | ||
588 | |||
589 | /* Set FSXP */ | ||
590 | if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH) | ||
591 | mcbsp_cfg.pcr0 &= ~FSXP; | ||
592 | else | ||
593 | mcbsp_cfg.pcr0 |= FSXP; | ||
594 | |||
595 | if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) { | ||
596 | mcbsp_cfg.pcr0 |= CLKXM; | ||
597 | mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div -1); | ||
598 | mcbsp_cfg.pcr0 |= FSXM; | ||
599 | mcbsp_cfg.srgr2 &= ~FSGM; | ||
600 | mcbsp_cfg.xcr2 |= XDATDLY(1); | ||
601 | mcbsp_cfg.rcr2 |= RDATDLY(1); | ||
602 | } | ||
603 | else { | ||
604 | mcbsp_cfg.pcr0 &= ~CLKXM; | ||
605 | mcbsp_cfg.srgr1 |= CLKGDV(1); | ||
606 | mcbsp_cfg.pcr0 &= ~FSXM; | ||
607 | mcbsp_cfg.xcr2 &= ~XDATDLY(3); | ||
608 | mcbsp_cfg.rcr2 &= ~RDATDLY(3); | ||
609 | } | ||
610 | |||
611 | mcbsp_cfg.xcr2 &= ~XPHASE; | ||
612 | mcbsp_cfg.rcr2 &= ~RPHASE; | ||
613 | |||
614 | omap_mcbsp_config(id, &mcbsp_cfg); | ||
615 | } | ||
616 | |||
617 | |||
618 | /* | ||
619 | * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. | ||
620 | * 730 has only 2 McBSP, and both of them are MPU peripherals. | ||
621 | */ | ||
622 | struct omap_mcbsp_info { | ||
623 | u32 virt_base; | ||
624 | u8 dma_rx_sync, dma_tx_sync; | ||
625 | u16 rx_irq, tx_irq; | ||
626 | }; | ||
627 | |||
628 | #ifdef CONFIG_ARCH_OMAP730 | ||
629 | static const struct omap_mcbsp_info mcbsp_730[] = { | ||
630 | [0] = { .virt_base = io_p2v(OMAP730_MCBSP1_BASE), | ||
631 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
632 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
633 | .rx_irq = INT_730_McBSP1RX, | ||
634 | .tx_irq = INT_730_McBSP1TX }, | ||
635 | [1] = { .virt_base = io_p2v(OMAP730_MCBSP2_BASE), | ||
636 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
637 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
638 | .rx_irq = INT_730_McBSP2RX, | ||
639 | .tx_irq = INT_730_McBSP2TX }, | ||
640 | }; | ||
641 | #endif | ||
642 | |||
643 | #ifdef CONFIG_ARCH_OMAP1510 | ||
644 | static const struct omap_mcbsp_info mcbsp_1510[] = { | ||
645 | [0] = { .virt_base = OMAP1510_MCBSP1_BASE, | ||
646 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
647 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
648 | .rx_irq = INT_McBSP1RX, | ||
649 | .tx_irq = INT_McBSP1TX }, | ||
650 | [1] = { .virt_base = io_p2v(OMAP1510_MCBSP2_BASE), | ||
651 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | ||
652 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | ||
653 | .rx_irq = INT_1510_SPI_RX, | ||
654 | .tx_irq = INT_1510_SPI_TX }, | ||
655 | [2] = { .virt_base = OMAP1510_MCBSP3_BASE, | ||
656 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
657 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
658 | .rx_irq = INT_McBSP3RX, | ||
659 | .tx_irq = INT_McBSP3TX }, | ||
660 | }; | ||
661 | #endif | ||
662 | |||
663 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
664 | static const struct omap_mcbsp_info mcbsp_1610[] = { | ||
665 | [0] = { .virt_base = OMAP1610_MCBSP1_BASE, | ||
666 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
667 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
668 | .rx_irq = INT_McBSP1RX, | ||
669 | .tx_irq = INT_McBSP1TX }, | ||
670 | [1] = { .virt_base = io_p2v(OMAP1610_MCBSP2_BASE), | ||
671 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | ||
672 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | ||
673 | .rx_irq = INT_1610_McBSP2_RX, | ||
674 | .tx_irq = INT_1610_McBSP2_TX }, | ||
675 | [2] = { .virt_base = OMAP1610_MCBSP3_BASE, | ||
676 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
677 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
678 | .rx_irq = INT_McBSP3RX, | ||
679 | .tx_irq = INT_McBSP3TX }, | ||
680 | }; | ||
681 | #endif | ||
682 | |||
683 | static int __init omap_mcbsp_init(void) | ||
684 | { | ||
685 | int mcbsp_count = 0, i; | ||
686 | static const struct omap_mcbsp_info *mcbsp_info; | ||
687 | |||
688 | printk("Initializing OMAP McBSP system\n"); | ||
689 | |||
690 | mcbsp_dsp_ck = clk_get(0, "dsp_ck"); | ||
691 | if (IS_ERR(mcbsp_dsp_ck)) { | ||
692 | printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n"); | ||
693 | return PTR_ERR(mcbsp_dsp_ck); | ||
694 | } | ||
695 | mcbsp_api_ck = clk_get(0, "api_ck"); | ||
696 | if (IS_ERR(mcbsp_api_ck)) { | ||
697 | printk(KERN_ERR "mcbsp: could not acquire api_ck handle.\n"); | ||
698 | return PTR_ERR(mcbsp_api_ck); | ||
699 | } | ||
700 | mcbsp_dspxor_ck = clk_get(0, "dspxor_ck"); | ||
701 | if (IS_ERR(mcbsp_dspxor_ck)) { | ||
702 | printk(KERN_ERR "mcbsp: could not acquire dspxor_ck handle.\n"); | ||
703 | return PTR_ERR(mcbsp_dspxor_ck); | ||
704 | } | ||
705 | |||
706 | #ifdef CONFIG_ARCH_OMAP730 | ||
707 | if (cpu_is_omap730()) { | ||
708 | mcbsp_info = mcbsp_730; | ||
709 | mcbsp_count = ARRAY_SIZE(mcbsp_730); | ||
710 | } | ||
711 | #endif | ||
712 | #ifdef CONFIG_ARCH_OMAP1510 | ||
713 | if (cpu_is_omap1510()) { | ||
714 | mcbsp_info = mcbsp_1510; | ||
715 | mcbsp_count = ARRAY_SIZE(mcbsp_1510); | ||
716 | } | ||
717 | #endif | ||
718 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
719 | if (cpu_is_omap16xx()) { | ||
720 | mcbsp_info = mcbsp_1610; | ||
721 | mcbsp_count = ARRAY_SIZE(mcbsp_1610); | ||
722 | } | ||
723 | #endif | ||
724 | for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { | ||
725 | if (i >= mcbsp_count) { | ||
726 | mcbsp[i].io_base = 0; | ||
727 | mcbsp[i].free = 0; | ||
728 | continue; | ||
729 | } | ||
730 | mcbsp[i].id = i + 1; | ||
731 | mcbsp[i].free = 1; | ||
732 | mcbsp[i].dma_tx_lch = -1; | ||
733 | mcbsp[i].dma_rx_lch = -1; | ||
734 | |||
735 | mcbsp[i].io_base = mcbsp_info[i].virt_base; | ||
736 | mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; | ||
737 | mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; | ||
738 | mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; | ||
739 | mcbsp[i].dma_tx_sync = mcbsp_info[i].dma_tx_sync; | ||
740 | spin_lock_init(&mcbsp[i].lock); | ||
741 | } | ||
742 | |||
743 | return 0; | ||
744 | } | ||
745 | |||
746 | |||
747 | arch_initcall(omap_mcbsp_init); | ||
748 | |||
749 | EXPORT_SYMBOL(omap_mcbsp_config); | ||
750 | EXPORT_SYMBOL(omap_mcbsp_request); | ||
751 | EXPORT_SYMBOL(omap_mcbsp_free); | ||
752 | EXPORT_SYMBOL(omap_mcbsp_start); | ||
753 | EXPORT_SYMBOL(omap_mcbsp_stop); | ||
754 | EXPORT_SYMBOL(omap_mcbsp_xmit_word); | ||
755 | EXPORT_SYMBOL(omap_mcbsp_recv_word); | ||
756 | EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); | ||
757 | EXPORT_SYMBOL(omap_mcbsp_recv_buffer); | ||
758 | EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); | ||