]>
Commit | Line | Data |
---|---|---|
5e1c5ff4 TL |
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> | |
f8ce2547 | 22 | #include <linux/clk.h> |
5e1c5ff4 TL |
23 | |
24 | #include <asm/delay.h> | |
25 | #include <asm/io.h> | |
26 | #include <asm/irq.h> | |
27 | ||
28 | #include <asm/arch/dma.h> | |
29 | #include <asm/arch/mux.h> | |
30 | #include <asm/arch/irqs.h> | |
92105bb7 | 31 | #include <asm/arch/dsp_common.h> |
5e1c5ff4 TL |
32 | #include <asm/arch/mcbsp.h> |
33 | ||
5e1c5ff4 TL |
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; | |
bb13b5fd | 69 | static struct clk *mcbsp_dspxor_ck = 0; |
5e1c5ff4 TL |
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 | ||
bb13b5fd | 179 | if (cpu_is_omap1510() || cpu_is_omap16xx()) { |
5e1c5ff4 TL |
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 | ||
5e1c5ff4 TL |
190 | static void omap_mcbsp_dsp_request(void) |
191 | { | |
bb13b5fd TL |
192 | if (cpu_is_omap1510() || cpu_is_omap16xx()) { |
193 | clk_use(mcbsp_dsp_ck); | |
194 | clk_use(mcbsp_api_ck); | |
5e1c5ff4 TL |
195 | |
196 | /* enable 12MHz clock to mcbsp 1 & 3 */ | |
bb13b5fd | 197 | clk_use(mcbsp_dspxor_ck); |
92105bb7 TL |
198 | |
199 | /* | |
200 | * DSP external peripheral reset | |
201 | * FIXME: This should be moved to dsp code | |
202 | */ | |
5e1c5ff4 TL |
203 | __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1 << 1, |
204 | DSP_RSTCT2); | |
205 | } | |
206 | } | |
207 | ||
208 | static void omap_mcbsp_dsp_free(void) | |
209 | { | |
bb13b5fd TL |
210 | if (cpu_is_omap1510() || cpu_is_omap16xx()) { |
211 | clk_unuse(mcbsp_dspxor_ck); | |
212 | clk_unuse(mcbsp_dsp_ck); | |
213 | clk_unuse(mcbsp_api_ck); | |
214 | } | |
5e1c5ff4 TL |
215 | } |
216 | ||
5e1c5ff4 TL |
217 | int omap_mcbsp_request(unsigned int id) |
218 | { | |
219 | int err; | |
220 | ||
221 | if (omap_mcbsp_check(id) < 0) | |
222 | return -EINVAL; | |
223 | ||
224 | /* | |
225 | * On 1510, 1610 and 1710, McBSP1 and McBSP3 | |
226 | * are DSP public peripherals. | |
227 | */ | |
228 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | |
229 | omap_mcbsp_dsp_request(); | |
230 | ||
231 | spin_lock(&mcbsp[id].lock); | |
232 | if (!mcbsp[id].free) { | |
233 | printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); | |
234 | spin_unlock(&mcbsp[id].lock); | |
235 | return -1; | |
236 | } | |
237 | ||
238 | mcbsp[id].free = 0; | |
239 | spin_unlock(&mcbsp[id].lock); | |
240 | ||
241 | /* We need to get IRQs here */ | |
242 | err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, | |
243 | "McBSP", | |
244 | (void *) (&mcbsp[id])); | |
245 | if (err != 0) { | |
246 | printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", | |
247 | mcbsp[id].tx_irq, mcbsp[id].id); | |
248 | return err; | |
249 | } | |
250 | ||
251 | init_completion(&(mcbsp[id].tx_irq_completion)); | |
252 | ||
253 | ||
254 | err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, | |
255 | "McBSP", | |
256 | (void *) (&mcbsp[id])); | |
257 | if (err != 0) { | |
258 | printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", | |
259 | mcbsp[id].rx_irq, mcbsp[id].id); | |
260 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | |
261 | return err; | |
262 | } | |
263 | ||
264 | init_completion(&(mcbsp[id].rx_irq_completion)); | |
265 | return 0; | |
266 | ||
267 | } | |
268 | ||
269 | void omap_mcbsp_free(unsigned int id) | |
270 | { | |
271 | if (omap_mcbsp_check(id) < 0) | |
272 | return; | |
273 | ||
274 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | |
275 | omap_mcbsp_dsp_free(); | |
276 | ||
277 | spin_lock(&mcbsp[id].lock); | |
278 | if (mcbsp[id].free) { | |
279 | printk (KERN_ERR "OMAP-McBSP: McBSP%d was not reserved\n", id + 1); | |
280 | spin_unlock(&mcbsp[id].lock); | |
281 | return; | |
282 | } | |
283 | ||
284 | mcbsp[id].free = 1; | |
285 | spin_unlock(&mcbsp[id].lock); | |
286 | ||
287 | /* Free IRQs */ | |
288 | free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); | |
289 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | |
290 | } | |
291 | ||
292 | /* | |
293 | * Here we start the McBSP, by enabling the sample | |
294 | * generator, both transmitter and receivers, | |
295 | * and the frame sync. | |
296 | */ | |
297 | void omap_mcbsp_start(unsigned int id) | |
298 | { | |
299 | u32 io_base; | |
300 | u16 w; | |
301 | ||
302 | if (omap_mcbsp_check(id) < 0) | |
303 | return; | |
304 | ||
305 | io_base = mcbsp[id].io_base; | |
306 | ||
307 | mcbsp[id].rx_word_length = ((OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7); | |
308 | mcbsp[id].tx_word_length = ((OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7); | |
309 | ||
310 | /* Start the sample generator */ | |
311 | w = OMAP_MCBSP_READ(io_base, SPCR2); | |
312 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | |
313 | ||
314 | /* Enable transmitter and receiver */ | |
315 | w = OMAP_MCBSP_READ(io_base, SPCR2); | |
316 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); | |
317 | ||
318 | w = OMAP_MCBSP_READ(io_base, SPCR1); | |
319 | OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); | |
320 | ||
321 | udelay(100); | |
322 | ||
323 | /* Start frame sync */ | |
324 | w = OMAP_MCBSP_READ(io_base, SPCR2); | |
325 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | |
326 | ||
327 | /* Dump McBSP Regs */ | |
328 | omap_mcbsp_dump_reg(id); | |
329 | ||
330 | } | |
331 | ||
332 | void omap_mcbsp_stop(unsigned int id) | |
333 | { | |
334 | u32 io_base; | |
335 | u16 w; | |
336 | ||
337 | if (omap_mcbsp_check(id) < 0) | |
338 | return; | |
339 | ||
340 | io_base = mcbsp[id].io_base; | |
341 | ||
342 | /* Reset transmitter */ | |
343 | w = OMAP_MCBSP_READ(io_base, SPCR2); | |
344 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); | |
345 | ||
346 | /* Reset receiver */ | |
347 | w = OMAP_MCBSP_READ(io_base, SPCR1); | |
348 | OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); | |
349 | ||
350 | /* Reset the sample rate generator */ | |
351 | w = OMAP_MCBSP_READ(io_base, SPCR2); | |
352 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | |
353 | } | |
354 | ||
355 | ||
bb13b5fd TL |
356 | /* polled mcbsp i/o operations */ |
357 | int omap_mcbsp_pollwrite(unsigned int id, u16 buf) | |
358 | { | |
359 | u32 base = mcbsp[id].io_base; | |
360 | writew(buf, base + OMAP_MCBSP_REG_DXR1); | |
361 | /* if frame sync error - clear the error */ | |
362 | if (readw(base + OMAP_MCBSP_REG_SPCR2) & XSYNC_ERR) { | |
363 | /* clear error */ | |
364 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) & (~XSYNC_ERR), | |
365 | base + OMAP_MCBSP_REG_SPCR2); | |
366 | /* resend */ | |
367 | return -1; | |
368 | } else { | |
369 | /* wait for transmit confirmation */ | |
370 | int attemps = 0; | |
371 | while (!(readw(base + OMAP_MCBSP_REG_SPCR2) & XRDY)) { | |
372 | if (attemps++ > 1000) { | |
373 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) & | |
374 | (~XRST), | |
375 | base + OMAP_MCBSP_REG_SPCR2); | |
376 | udelay(10); | |
377 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) | | |
378 | (XRST), | |
379 | base + OMAP_MCBSP_REG_SPCR2); | |
380 | udelay(10); | |
381 | printk(KERN_ERR | |
382 | " Could not write to McBSP Register\n"); | |
383 | return -2; | |
384 | } | |
385 | } | |
386 | } | |
387 | return 0; | |
388 | } | |
389 | ||
390 | int omap_mcbsp_pollread(unsigned int id, u16 * buf) | |
391 | { | |
392 | u32 base = mcbsp[id].io_base; | |
393 | /* if frame sync error - clear the error */ | |
394 | if (readw(base + OMAP_MCBSP_REG_SPCR1) & RSYNC_ERR) { | |
395 | /* clear error */ | |
396 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) & (~RSYNC_ERR), | |
397 | base + OMAP_MCBSP_REG_SPCR1); | |
398 | /* resend */ | |
399 | return -1; | |
400 | } else { | |
401 | /* wait for recieve confirmation */ | |
402 | int attemps = 0; | |
403 | while (!(readw(base + OMAP_MCBSP_REG_SPCR1) & RRDY)) { | |
404 | if (attemps++ > 1000) { | |
405 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) & | |
406 | (~RRST), | |
407 | base + OMAP_MCBSP_REG_SPCR1); | |
408 | udelay(10); | |
409 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) | | |
410 | (RRST), | |
411 | base + OMAP_MCBSP_REG_SPCR1); | |
412 | udelay(10); | |
413 | printk(KERN_ERR | |
414 | " Could not read from McBSP Register\n"); | |
415 | return -2; | |
416 | } | |
417 | } | |
418 | } | |
419 | *buf = readw(base + OMAP_MCBSP_REG_DRR1); | |
420 | return 0; | |
421 | } | |
422 | ||
5e1c5ff4 TL |
423 | /* |
424 | * IRQ based word transmission. | |
425 | */ | |
426 | void omap_mcbsp_xmit_word(unsigned int id, u32 word) | |
427 | { | |
428 | u32 io_base; | |
429 | omap_mcbsp_word_length word_length = mcbsp[id].tx_word_length; | |
430 | ||
431 | if (omap_mcbsp_check(id) < 0) | |
432 | return; | |
433 | ||
434 | io_base = mcbsp[id].io_base; | |
435 | ||
436 | wait_for_completion(&(mcbsp[id].tx_irq_completion)); | |
437 | ||
438 | if (word_length > OMAP_MCBSP_WORD_16) | |
439 | OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); | |
440 | OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); | |
441 | } | |
442 | ||
443 | u32 omap_mcbsp_recv_word(unsigned int id) | |
444 | { | |
445 | u32 io_base; | |
446 | u16 word_lsb, word_msb = 0; | |
447 | omap_mcbsp_word_length word_length = mcbsp[id].rx_word_length; | |
448 | ||
449 | if (omap_mcbsp_check(id) < 0) | |
450 | return -EINVAL; | |
451 | ||
452 | io_base = mcbsp[id].io_base; | |
453 | ||
454 | wait_for_completion(&(mcbsp[id].rx_irq_completion)); | |
455 | ||
456 | if (word_length > OMAP_MCBSP_WORD_16) | |
457 | word_msb = OMAP_MCBSP_READ(io_base, DRR2); | |
458 | word_lsb = OMAP_MCBSP_READ(io_base, DRR1); | |
459 | ||
460 | return (word_lsb | (word_msb << 16)); | |
461 | } | |
462 | ||
463 | ||
464 | /* | |
465 | * Simple DMA based buffer rx/tx routines. | |
466 | * Nothing fancy, just a single buffer tx/rx through DMA. | |
467 | * The DMA resources are released once the transfer is done. | |
468 | * For anything fancier, you should use your own customized DMA | |
469 | * routines and callbacks. | |
470 | */ | |
471 | int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | |
472 | { | |
473 | int dma_tx_ch; | |
474 | ||
475 | if (omap_mcbsp_check(id) < 0) | |
476 | return -EINVAL; | |
477 | ||
478 | if (omap_request_dma(mcbsp[id].dma_tx_sync, "McBSP TX", omap_mcbsp_tx_dma_callback, | |
479 | &mcbsp[id], | |
480 | &dma_tx_ch)) { | |
481 | printk("OMAP-McBSP: Unable to request DMA channel for McBSP%d TX. Trying IRQ based TX\n", id+1); | |
482 | return -EAGAIN; | |
483 | } | |
484 | mcbsp[id].dma_tx_lch = dma_tx_ch; | |
485 | ||
486 | DBG("TX DMA on channel %d\n", dma_tx_ch); | |
487 | ||
488 | init_completion(&(mcbsp[id].tx_dma_completion)); | |
489 | ||
490 | omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, | |
491 | OMAP_DMA_DATA_TYPE_S16, | |
492 | length >> 1, 1, | |
1a8bfa1e TL |
493 | OMAP_DMA_SYNC_ELEMENT, |
494 | 0, 0); | |
5e1c5ff4 TL |
495 | |
496 | omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, | |
497 | OMAP_DMA_PORT_TIPB, | |
498 | OMAP_DMA_AMODE_CONSTANT, | |
1a8bfa1e TL |
499 | mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1, |
500 | 0, 0); | |
5e1c5ff4 TL |
501 | |
502 | omap_set_dma_src_params(mcbsp[id].dma_tx_lch, | |
503 | OMAP_DMA_PORT_EMIFF, | |
504 | OMAP_DMA_AMODE_POST_INC, | |
1a8bfa1e TL |
505 | buffer, |
506 | 0, 0); | |
5e1c5ff4 TL |
507 | |
508 | omap_start_dma(mcbsp[id].dma_tx_lch); | |
509 | wait_for_completion(&(mcbsp[id].tx_dma_completion)); | |
510 | return 0; | |
511 | } | |
512 | ||
513 | ||
514 | int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | |
515 | { | |
516 | int dma_rx_ch; | |
517 | ||
518 | if (omap_mcbsp_check(id) < 0) | |
519 | return -EINVAL; | |
520 | ||
521 | if (omap_request_dma(mcbsp[id].dma_rx_sync, "McBSP RX", omap_mcbsp_rx_dma_callback, | |
522 | &mcbsp[id], | |
523 | &dma_rx_ch)) { | |
524 | printk("Unable to request DMA channel for McBSP%d RX. Trying IRQ based RX\n", id+1); | |
525 | return -EAGAIN; | |
526 | } | |
527 | mcbsp[id].dma_rx_lch = dma_rx_ch; | |
528 | ||
529 | DBG("RX DMA on channel %d\n", dma_rx_ch); | |
530 | ||
531 | init_completion(&(mcbsp[id].rx_dma_completion)); | |
532 | ||
533 | omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, | |
534 | OMAP_DMA_DATA_TYPE_S16, | |
535 | length >> 1, 1, | |
1a8bfa1e TL |
536 | OMAP_DMA_SYNC_ELEMENT, |
537 | 0, 0); | |
5e1c5ff4 TL |
538 | |
539 | omap_set_dma_src_params(mcbsp[id].dma_rx_lch, | |
540 | OMAP_DMA_PORT_TIPB, | |
541 | OMAP_DMA_AMODE_CONSTANT, | |
1a8bfa1e TL |
542 | mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1, |
543 | 0, 0); | |
5e1c5ff4 TL |
544 | |
545 | omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, | |
546 | OMAP_DMA_PORT_EMIFF, | |
547 | OMAP_DMA_AMODE_POST_INC, | |
1a8bfa1e TL |
548 | buffer, |
549 | 0, 0); | |
5e1c5ff4 TL |
550 | |
551 | omap_start_dma(mcbsp[id].dma_rx_lch); | |
552 | wait_for_completion(&(mcbsp[id].rx_dma_completion)); | |
553 | return 0; | |
554 | } | |
555 | ||
556 | ||
557 | /* | |
558 | * SPI wrapper. | |
559 | * Since SPI setup is much simpler than the generic McBSP one, | |
560 | * this wrapper just need an omap_mcbsp_spi_cfg structure as an input. | |
561 | * Once this is done, you can call omap_mcbsp_start(). | |
562 | */ | |
563 | void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg) | |
564 | { | |
565 | struct omap_mcbsp_reg_cfg mcbsp_cfg; | |
566 | ||
567 | if (omap_mcbsp_check(id) < 0) | |
568 | return; | |
569 | ||
570 | memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); | |
571 | ||
572 | /* SPI has only one frame */ | |
573 | mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0)); | |
574 | mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0)); | |
575 | ||
576 | /* Clock stop mode */ | |
577 | if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY) | |
578 | mcbsp_cfg.spcr1 |= (1 << 12); | |
579 | else | |
580 | mcbsp_cfg.spcr1 |= (3 << 11); | |
581 | ||
582 | /* Set clock parities */ | |
583 | if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING) | |
584 | mcbsp_cfg.pcr0 |= CLKRP; | |
585 | else | |
586 | mcbsp_cfg.pcr0 &= ~CLKRP; | |
587 | ||
588 | if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING) | |
589 | mcbsp_cfg.pcr0 &= ~CLKXP; | |
590 | else | |
591 | mcbsp_cfg.pcr0 |= CLKXP; | |
592 | ||
593 | /* Set SCLKME to 0 and CLKSM to 1 */ | |
594 | mcbsp_cfg.pcr0 &= ~SCLKME; | |
595 | mcbsp_cfg.srgr2 |= CLKSM; | |
596 | ||
597 | /* Set FSXP */ | |
598 | if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH) | |
599 | mcbsp_cfg.pcr0 &= ~FSXP; | |
600 | else | |
601 | mcbsp_cfg.pcr0 |= FSXP; | |
602 | ||
603 | if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) { | |
604 | mcbsp_cfg.pcr0 |= CLKXM; | |
605 | mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div -1); | |
606 | mcbsp_cfg.pcr0 |= FSXM; | |
607 | mcbsp_cfg.srgr2 &= ~FSGM; | |
608 | mcbsp_cfg.xcr2 |= XDATDLY(1); | |
609 | mcbsp_cfg.rcr2 |= RDATDLY(1); | |
610 | } | |
611 | else { | |
612 | mcbsp_cfg.pcr0 &= ~CLKXM; | |
613 | mcbsp_cfg.srgr1 |= CLKGDV(1); | |
614 | mcbsp_cfg.pcr0 &= ~FSXM; | |
615 | mcbsp_cfg.xcr2 &= ~XDATDLY(3); | |
616 | mcbsp_cfg.rcr2 &= ~RDATDLY(3); | |
617 | } | |
618 | ||
619 | mcbsp_cfg.xcr2 &= ~XPHASE; | |
620 | mcbsp_cfg.rcr2 &= ~RPHASE; | |
621 | ||
622 | omap_mcbsp_config(id, &mcbsp_cfg); | |
623 | } | |
624 | ||
625 | ||
626 | /* | |
627 | * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. | |
628 | * 730 has only 2 McBSP, and both of them are MPU peripherals. | |
629 | */ | |
630 | struct omap_mcbsp_info { | |
631 | u32 virt_base; | |
632 | u8 dma_rx_sync, dma_tx_sync; | |
633 | u16 rx_irq, tx_irq; | |
634 | }; | |
635 | ||
636 | #ifdef CONFIG_ARCH_OMAP730 | |
637 | static const struct omap_mcbsp_info mcbsp_730[] = { | |
638 | [0] = { .virt_base = io_p2v(OMAP730_MCBSP1_BASE), | |
639 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | |
640 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | |
641 | .rx_irq = INT_730_McBSP1RX, | |
642 | .tx_irq = INT_730_McBSP1TX }, | |
643 | [1] = { .virt_base = io_p2v(OMAP730_MCBSP2_BASE), | |
644 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | |
645 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | |
646 | .rx_irq = INT_730_McBSP2RX, | |
647 | .tx_irq = INT_730_McBSP2TX }, | |
648 | }; | |
649 | #endif | |
650 | ||
1a8bfa1e | 651 | #ifdef CONFIG_ARCH_OMAP15XX |
5e1c5ff4 TL |
652 | static const struct omap_mcbsp_info mcbsp_1510[] = { |
653 | [0] = { .virt_base = OMAP1510_MCBSP1_BASE, | |
654 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | |
655 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | |
656 | .rx_irq = INT_McBSP1RX, | |
657 | .tx_irq = INT_McBSP1TX }, | |
658 | [1] = { .virt_base = io_p2v(OMAP1510_MCBSP2_BASE), | |
659 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | |
660 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | |
661 | .rx_irq = INT_1510_SPI_RX, | |
662 | .tx_irq = INT_1510_SPI_TX }, | |
663 | [2] = { .virt_base = OMAP1510_MCBSP3_BASE, | |
664 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | |
665 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | |
666 | .rx_irq = INT_McBSP3RX, | |
667 | .tx_irq = INT_McBSP3TX }, | |
668 | }; | |
669 | #endif | |
670 | ||
671 | #if defined(CONFIG_ARCH_OMAP16XX) | |
672 | static const struct omap_mcbsp_info mcbsp_1610[] = { | |
673 | [0] = { .virt_base = OMAP1610_MCBSP1_BASE, | |
674 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | |
675 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | |
676 | .rx_irq = INT_McBSP1RX, | |
677 | .tx_irq = INT_McBSP1TX }, | |
678 | [1] = { .virt_base = io_p2v(OMAP1610_MCBSP2_BASE), | |
679 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | |
680 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | |
681 | .rx_irq = INT_1610_McBSP2_RX, | |
682 | .tx_irq = INT_1610_McBSP2_TX }, | |
683 | [2] = { .virt_base = OMAP1610_MCBSP3_BASE, | |
684 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | |
685 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | |
686 | .rx_irq = INT_McBSP3RX, | |
687 | .tx_irq = INT_McBSP3TX }, | |
688 | }; | |
689 | #endif | |
690 | ||
691 | static int __init omap_mcbsp_init(void) | |
692 | { | |
693 | int mcbsp_count = 0, i; | |
694 | static const struct omap_mcbsp_info *mcbsp_info; | |
695 | ||
696 | printk("Initializing OMAP McBSP system\n"); | |
697 | ||
698 | mcbsp_dsp_ck = clk_get(0, "dsp_ck"); | |
699 | if (IS_ERR(mcbsp_dsp_ck)) { | |
700 | printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n"); | |
701 | return PTR_ERR(mcbsp_dsp_ck); | |
702 | } | |
703 | mcbsp_api_ck = clk_get(0, "api_ck"); | |
bb13b5fd | 704 | if (IS_ERR(mcbsp_api_ck)) { |
5e1c5ff4 TL |
705 | printk(KERN_ERR "mcbsp: could not acquire api_ck handle.\n"); |
706 | return PTR_ERR(mcbsp_api_ck); | |
707 | } | |
bb13b5fd TL |
708 | mcbsp_dspxor_ck = clk_get(0, "dspxor_ck"); |
709 | if (IS_ERR(mcbsp_dspxor_ck)) { | |
710 | printk(KERN_ERR "mcbsp: could not acquire dspxor_ck handle.\n"); | |
711 | return PTR_ERR(mcbsp_dspxor_ck); | |
712 | } | |
5e1c5ff4 TL |
713 | |
714 | #ifdef CONFIG_ARCH_OMAP730 | |
715 | if (cpu_is_omap730()) { | |
716 | mcbsp_info = mcbsp_730; | |
717 | mcbsp_count = ARRAY_SIZE(mcbsp_730); | |
718 | } | |
719 | #endif | |
1a8bfa1e | 720 | #ifdef CONFIG_ARCH_OMAP15XX |
5e1c5ff4 TL |
721 | if (cpu_is_omap1510()) { |
722 | mcbsp_info = mcbsp_1510; | |
723 | mcbsp_count = ARRAY_SIZE(mcbsp_1510); | |
724 | } | |
725 | #endif | |
726 | #if defined(CONFIG_ARCH_OMAP16XX) | |
bb13b5fd | 727 | if (cpu_is_omap16xx()) { |
5e1c5ff4 TL |
728 | mcbsp_info = mcbsp_1610; |
729 | mcbsp_count = ARRAY_SIZE(mcbsp_1610); | |
730 | } | |
731 | #endif | |
732 | for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { | |
733 | if (i >= mcbsp_count) { | |
734 | mcbsp[i].io_base = 0; | |
735 | mcbsp[i].free = 0; | |
736 | continue; | |
737 | } | |
738 | mcbsp[i].id = i + 1; | |
739 | mcbsp[i].free = 1; | |
740 | mcbsp[i].dma_tx_lch = -1; | |
741 | mcbsp[i].dma_rx_lch = -1; | |
742 | ||
743 | mcbsp[i].io_base = mcbsp_info[i].virt_base; | |
744 | mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; | |
745 | mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; | |
746 | mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; | |
747 | mcbsp[i].dma_tx_sync = mcbsp_info[i].dma_tx_sync; | |
748 | spin_lock_init(&mcbsp[i].lock); | |
749 | } | |
750 | ||
751 | return 0; | |
752 | } | |
753 | ||
754 | ||
755 | arch_initcall(omap_mcbsp_init); | |
756 | ||
757 | EXPORT_SYMBOL(omap_mcbsp_config); | |
758 | EXPORT_SYMBOL(omap_mcbsp_request); | |
759 | EXPORT_SYMBOL(omap_mcbsp_free); | |
760 | EXPORT_SYMBOL(omap_mcbsp_start); | |
761 | EXPORT_SYMBOL(omap_mcbsp_stop); | |
762 | EXPORT_SYMBOL(omap_mcbsp_xmit_word); | |
763 | EXPORT_SYMBOL(omap_mcbsp_recv_word); | |
764 | EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); | |
765 | EXPORT_SYMBOL(omap_mcbsp_recv_buffer); | |
766 | EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); |