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> | |
bc5d0c89 | 18 | #include <linux/platform_device.h> |
5e1c5ff4 TL |
19 | #include <linux/wait.h> |
20 | #include <linux/completion.h> | |
21 | #include <linux/interrupt.h> | |
22 | #include <linux/err.h> | |
f8ce2547 | 23 | #include <linux/clk.h> |
04fbf6a2 | 24 | #include <linux/delay.h> |
fb78d808 | 25 | #include <linux/io.h> |
5e1c5ff4 | 26 | |
a09e64fb RK |
27 | #include <mach/dma.h> |
28 | #include <mach/mcbsp.h> | |
5e1c5ff4 | 29 | |
b4b58f58 CS |
30 | struct omap_mcbsp **mcbsp_ptr; |
31 | int omap_mcbsp_count; | |
bc5d0c89 | 32 | |
b4b58f58 CS |
33 | void omap_mcbsp_write(void __iomem *io_base, u16 reg, u32 val) |
34 | { | |
35 | if (cpu_class_is_omap1() || cpu_is_omap2420()) | |
36 | __raw_writew((u16)val, io_base + reg); | |
37 | else | |
38 | __raw_writel(val, io_base + reg); | |
39 | } | |
40 | ||
41 | int omap_mcbsp_read(void __iomem *io_base, u16 reg) | |
42 | { | |
43 | if (cpu_class_is_omap1() || cpu_is_omap2420()) | |
44 | return __raw_readw(io_base + reg); | |
45 | else | |
46 | return __raw_readl(io_base + reg); | |
47 | } | |
48 | ||
49 | #define OMAP_MCBSP_READ(base, reg) \ | |
50 | omap_mcbsp_read(base, OMAP_MCBSP_REG_##reg) | |
51 | #define OMAP_MCBSP_WRITE(base, reg, val) \ | |
52 | omap_mcbsp_write(base, OMAP_MCBSP_REG_##reg, val) | |
53 | ||
54 | #define omap_mcbsp_check_valid_id(id) (id < omap_mcbsp_count) | |
55 | #define id_to_mcbsp_ptr(id) mcbsp_ptr[id]; | |
5e1c5ff4 TL |
56 | |
57 | static void omap_mcbsp_dump_reg(u8 id) | |
58 | { | |
b4b58f58 CS |
59 | struct omap_mcbsp *mcbsp = id_to_mcbsp_ptr(id); |
60 | ||
61 | dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); | |
62 | dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", | |
63 | OMAP_MCBSP_READ(mcbsp->io_base, DRR2)); | |
64 | dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", | |
65 | OMAP_MCBSP_READ(mcbsp->io_base, DRR1)); | |
66 | dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", | |
67 | OMAP_MCBSP_READ(mcbsp->io_base, DXR2)); | |
68 | dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", | |
69 | OMAP_MCBSP_READ(mcbsp->io_base, DXR1)); | |
70 | dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", | |
71 | OMAP_MCBSP_READ(mcbsp->io_base, SPCR2)); | |
72 | dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", | |
73 | OMAP_MCBSP_READ(mcbsp->io_base, SPCR1)); | |
74 | dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", | |
75 | OMAP_MCBSP_READ(mcbsp->io_base, RCR2)); | |
76 | dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", | |
77 | OMAP_MCBSP_READ(mcbsp->io_base, RCR1)); | |
78 | dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", | |
79 | OMAP_MCBSP_READ(mcbsp->io_base, XCR2)); | |
80 | dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", | |
81 | OMAP_MCBSP_READ(mcbsp->io_base, XCR1)); | |
82 | dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", | |
83 | OMAP_MCBSP_READ(mcbsp->io_base, SRGR2)); | |
84 | dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", | |
85 | OMAP_MCBSP_READ(mcbsp->io_base, SRGR1)); | |
86 | dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", | |
87 | OMAP_MCBSP_READ(mcbsp->io_base, PCR0)); | |
88 | dev_dbg(mcbsp->dev, "***********************\n"); | |
5e1c5ff4 TL |
89 | } |
90 | ||
0cd61b68 | 91 | static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id) |
5e1c5ff4 | 92 | { |
e8f2af17 | 93 | struct omap_mcbsp *mcbsp_tx = dev_id; |
d6d834b0 | 94 | u16 irqst_spcr2; |
5e1c5ff4 | 95 | |
d6d834b0 EN |
96 | irqst_spcr2 = OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2); |
97 | dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); | |
5e1c5ff4 | 98 | |
d6d834b0 EN |
99 | if (irqst_spcr2 & XSYNC_ERR) { |
100 | dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n", | |
101 | irqst_spcr2); | |
102 | /* Writing zero to XSYNC_ERR clears the IRQ */ | |
103 | OMAP_MCBSP_WRITE(mcbsp_tx->io_base, SPCR2, | |
104 | irqst_spcr2 & ~(XSYNC_ERR)); | |
105 | } else { | |
106 | complete(&mcbsp_tx->tx_irq_completion); | |
107 | } | |
fb78d808 | 108 | |
5e1c5ff4 TL |
109 | return IRQ_HANDLED; |
110 | } | |
111 | ||
0cd61b68 | 112 | static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id) |
5e1c5ff4 | 113 | { |
e8f2af17 | 114 | struct omap_mcbsp *mcbsp_rx = dev_id; |
d6d834b0 EN |
115 | u16 irqst_spcr1; |
116 | ||
117 | irqst_spcr1 = OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR1); | |
118 | dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); | |
119 | ||
120 | if (irqst_spcr1 & RSYNC_ERR) { | |
121 | dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n", | |
122 | irqst_spcr1); | |
123 | /* Writing zero to RSYNC_ERR clears the IRQ */ | |
124 | OMAP_MCBSP_WRITE(mcbsp_rx->io_base, SPCR1, | |
125 | irqst_spcr1 & ~(RSYNC_ERR)); | |
126 | } else { | |
127 | complete(&mcbsp_rx->tx_irq_completion); | |
128 | } | |
fb78d808 | 129 | |
5e1c5ff4 TL |
130 | return IRQ_HANDLED; |
131 | } | |
132 | ||
5e1c5ff4 TL |
133 | static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) |
134 | { | |
e8f2af17 | 135 | struct omap_mcbsp *mcbsp_dma_tx = data; |
5e1c5ff4 | 136 | |
bc5d0c89 EV |
137 | dev_dbg(mcbsp_dma_tx->dev, "TX DMA callback : 0x%x\n", |
138 | OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2)); | |
5e1c5ff4 TL |
139 | |
140 | /* We can free the channels */ | |
141 | omap_free_dma(mcbsp_dma_tx->dma_tx_lch); | |
142 | mcbsp_dma_tx->dma_tx_lch = -1; | |
143 | ||
144 | complete(&mcbsp_dma_tx->tx_dma_completion); | |
145 | } | |
146 | ||
147 | static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data) | |
148 | { | |
e8f2af17 | 149 | struct omap_mcbsp *mcbsp_dma_rx = data; |
5e1c5ff4 | 150 | |
bc5d0c89 EV |
151 | dev_dbg(mcbsp_dma_rx->dev, "RX DMA callback : 0x%x\n", |
152 | OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2)); | |
5e1c5ff4 TL |
153 | |
154 | /* We can free the channels */ | |
155 | omap_free_dma(mcbsp_dma_rx->dma_rx_lch); | |
156 | mcbsp_dma_rx->dma_rx_lch = -1; | |
157 | ||
158 | complete(&mcbsp_dma_rx->rx_dma_completion); | |
159 | } | |
160 | ||
5e1c5ff4 TL |
161 | /* |
162 | * omap_mcbsp_config simply write a config to the | |
163 | * appropriate McBSP. | |
164 | * You either call this function or set the McBSP registers | |
165 | * by yourself before calling omap_mcbsp_start(). | |
166 | */ | |
fb78d808 | 167 | void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config) |
5e1c5ff4 | 168 | { |
b4b58f58 | 169 | struct omap_mcbsp *mcbsp; |
d592dd1a | 170 | void __iomem *io_base; |
5e1c5ff4 | 171 | |
bc5d0c89 EV |
172 | if (!omap_mcbsp_check_valid_id(id)) { |
173 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
174 | return; | |
175 | } | |
b4b58f58 | 176 | mcbsp = id_to_mcbsp_ptr(id); |
bc5d0c89 | 177 | |
b4b58f58 CS |
178 | io_base = mcbsp->io_base; |
179 | dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", | |
180 | mcbsp->id, mcbsp->phys_base); | |
5e1c5ff4 TL |
181 | |
182 | /* We write the given config */ | |
183 | OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2); | |
184 | OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1); | |
185 | OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2); | |
186 | OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1); | |
187 | OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2); | |
188 | OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1); | |
189 | OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2); | |
190 | OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1); | |
191 | OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2); | |
192 | OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1); | |
193 | OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0); | |
a5b92cc3 | 194 | if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) { |
3127f8f8 TL |
195 | OMAP_MCBSP_WRITE(io_base, XCCR, config->xccr); |
196 | OMAP_MCBSP_WRITE(io_base, RCCR, config->rccr); | |
197 | } | |
5e1c5ff4 | 198 | } |
fb78d808 | 199 | EXPORT_SYMBOL(omap_mcbsp_config); |
5e1c5ff4 | 200 | |
120db2cb TL |
201 | /* |
202 | * We can choose between IRQ based or polled IO. | |
203 | * This needs to be called before omap_mcbsp_request(). | |
204 | */ | |
205 | int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type) | |
206 | { | |
b4b58f58 CS |
207 | struct omap_mcbsp *mcbsp; |
208 | ||
bc5d0c89 EV |
209 | if (!omap_mcbsp_check_valid_id(id)) { |
210 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
211 | return -ENODEV; | |
212 | } | |
b4b58f58 | 213 | mcbsp = id_to_mcbsp_ptr(id); |
120db2cb | 214 | |
b4b58f58 | 215 | spin_lock(&mcbsp->lock); |
120db2cb | 216 | |
b4b58f58 CS |
217 | if (!mcbsp->free) { |
218 | dev_err(mcbsp->dev, "McBSP%d is currently in use\n", | |
219 | mcbsp->id); | |
220 | spin_unlock(&mcbsp->lock); | |
120db2cb TL |
221 | return -EINVAL; |
222 | } | |
223 | ||
b4b58f58 | 224 | mcbsp->io_type = io_type; |
120db2cb | 225 | |
b4b58f58 | 226 | spin_unlock(&mcbsp->lock); |
120db2cb TL |
227 | |
228 | return 0; | |
229 | } | |
fb78d808 | 230 | EXPORT_SYMBOL(omap_mcbsp_set_io_type); |
5e1c5ff4 | 231 | |
5e1c5ff4 TL |
232 | int omap_mcbsp_request(unsigned int id) |
233 | { | |
b4b58f58 | 234 | struct omap_mcbsp *mcbsp; |
5e1c5ff4 TL |
235 | int err; |
236 | ||
bc5d0c89 EV |
237 | if (!omap_mcbsp_check_valid_id(id)) { |
238 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
239 | return -ENODEV; | |
120db2cb | 240 | } |
b4b58f58 | 241 | mcbsp = id_to_mcbsp_ptr(id); |
bc5d0c89 | 242 | |
b4b58f58 CS |
243 | spin_lock(&mcbsp->lock); |
244 | if (!mcbsp->free) { | |
245 | dev_err(mcbsp->dev, "McBSP%d is currently in use\n", | |
246 | mcbsp->id); | |
247 | spin_unlock(&mcbsp->lock); | |
b820ce4e | 248 | return -EBUSY; |
5e1c5ff4 TL |
249 | } |
250 | ||
b4b58f58 CS |
251 | mcbsp->free = 0; |
252 | spin_unlock(&mcbsp->lock); | |
5e1c5ff4 | 253 | |
b820ce4e RK |
254 | if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) |
255 | mcbsp->pdata->ops->request(id); | |
256 | ||
257 | clk_enable(mcbsp->iclk); | |
258 | clk_enable(mcbsp->fclk); | |
259 | ||
5a07055a JN |
260 | /* |
261 | * Make sure that transmitter, receiver and sample-rate generator are | |
262 | * not running before activating IRQs. | |
263 | */ | |
264 | OMAP_MCBSP_WRITE(mcbsp->io_base, SPCR1, 0); | |
265 | OMAP_MCBSP_WRITE(mcbsp->io_base, SPCR2, 0); | |
266 | ||
b4b58f58 | 267 | if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) { |
120db2cb | 268 | /* We need to get IRQs here */ |
5a07055a | 269 | init_completion(&mcbsp->tx_irq_completion); |
b4b58f58 CS |
270 | err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, |
271 | 0, "McBSP", (void *)mcbsp); | |
120db2cb | 272 | if (err != 0) { |
b4b58f58 CS |
273 | dev_err(mcbsp->dev, "Unable to request TX IRQ %d " |
274 | "for McBSP%d\n", mcbsp->tx_irq, | |
275 | mcbsp->id); | |
120db2cb TL |
276 | return err; |
277 | } | |
5e1c5ff4 | 278 | |
5a07055a | 279 | init_completion(&mcbsp->rx_irq_completion); |
b4b58f58 CS |
280 | err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, |
281 | 0, "McBSP", (void *)mcbsp); | |
120db2cb | 282 | if (err != 0) { |
b4b58f58 CS |
283 | dev_err(mcbsp->dev, "Unable to request RX IRQ %d " |
284 | "for McBSP%d\n", mcbsp->rx_irq, | |
285 | mcbsp->id); | |
286 | free_irq(mcbsp->tx_irq, (void *)mcbsp); | |
120db2cb TL |
287 | return err; |
288 | } | |
5e1c5ff4 TL |
289 | } |
290 | ||
5e1c5ff4 | 291 | return 0; |
5e1c5ff4 | 292 | } |
fb78d808 | 293 | EXPORT_SYMBOL(omap_mcbsp_request); |
5e1c5ff4 TL |
294 | |
295 | void omap_mcbsp_free(unsigned int id) | |
296 | { | |
b4b58f58 CS |
297 | struct omap_mcbsp *mcbsp; |
298 | ||
bc5d0c89 EV |
299 | if (!omap_mcbsp_check_valid_id(id)) { |
300 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
5e1c5ff4 | 301 | return; |
120db2cb | 302 | } |
b4b58f58 | 303 | mcbsp = id_to_mcbsp_ptr(id); |
bc5d0c89 | 304 | |
b4b58f58 CS |
305 | if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) |
306 | mcbsp->pdata->ops->free(id); | |
bc5d0c89 | 307 | |
b820ce4e RK |
308 | clk_disable(mcbsp->fclk); |
309 | clk_disable(mcbsp->iclk); | |
310 | ||
311 | if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) { | |
312 | /* Free IRQs */ | |
313 | free_irq(mcbsp->rx_irq, (void *)mcbsp); | |
314 | free_irq(mcbsp->tx_irq, (void *)mcbsp); | |
315 | } | |
5e1c5ff4 | 316 | |
b4b58f58 CS |
317 | spin_lock(&mcbsp->lock); |
318 | if (mcbsp->free) { | |
319 | dev_err(mcbsp->dev, "McBSP%d was not reserved\n", | |
320 | mcbsp->id); | |
321 | spin_unlock(&mcbsp->lock); | |
5e1c5ff4 TL |
322 | return; |
323 | } | |
324 | ||
b4b58f58 CS |
325 | mcbsp->free = 1; |
326 | spin_unlock(&mcbsp->lock); | |
5e1c5ff4 | 327 | } |
fb78d808 | 328 | EXPORT_SYMBOL(omap_mcbsp_free); |
5e1c5ff4 TL |
329 | |
330 | /* | |
331 | * Here we start the McBSP, by enabling the sample | |
332 | * generator, both transmitter and receivers, | |
333 | * and the frame sync. | |
334 | */ | |
335 | void omap_mcbsp_start(unsigned int id) | |
336 | { | |
b4b58f58 | 337 | struct omap_mcbsp *mcbsp; |
d592dd1a | 338 | void __iomem *io_base; |
5e1c5ff4 TL |
339 | u16 w; |
340 | ||
bc5d0c89 EV |
341 | if (!omap_mcbsp_check_valid_id(id)) { |
342 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
5e1c5ff4 | 343 | return; |
bc5d0c89 | 344 | } |
b4b58f58 CS |
345 | mcbsp = id_to_mcbsp_ptr(id); |
346 | io_base = mcbsp->io_base; | |
5e1c5ff4 | 347 | |
b4b58f58 CS |
348 | mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7; |
349 | mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7; | |
5e1c5ff4 TL |
350 | |
351 | /* Start the sample generator */ | |
352 | w = OMAP_MCBSP_READ(io_base, SPCR2); | |
353 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | |
354 | ||
355 | /* Enable transmitter and receiver */ | |
356 | w = OMAP_MCBSP_READ(io_base, SPCR2); | |
357 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); | |
358 | ||
359 | w = OMAP_MCBSP_READ(io_base, SPCR1); | |
360 | OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); | |
361 | ||
362 | udelay(100); | |
363 | ||
364 | /* Start frame sync */ | |
365 | w = OMAP_MCBSP_READ(io_base, SPCR2); | |
366 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | |
367 | ||
368 | /* Dump McBSP Regs */ | |
369 | omap_mcbsp_dump_reg(id); | |
5e1c5ff4 | 370 | } |
fb78d808 | 371 | EXPORT_SYMBOL(omap_mcbsp_start); |
5e1c5ff4 TL |
372 | |
373 | void omap_mcbsp_stop(unsigned int id) | |
374 | { | |
b4b58f58 | 375 | struct omap_mcbsp *mcbsp; |
d592dd1a | 376 | void __iomem *io_base; |
5e1c5ff4 TL |
377 | u16 w; |
378 | ||
bc5d0c89 EV |
379 | if (!omap_mcbsp_check_valid_id(id)) { |
380 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
5e1c5ff4 | 381 | return; |
bc5d0c89 | 382 | } |
5e1c5ff4 | 383 | |
b4b58f58 CS |
384 | mcbsp = id_to_mcbsp_ptr(id); |
385 | io_base = mcbsp->io_base; | |
5e1c5ff4 | 386 | |
fb78d808 | 387 | /* Reset transmitter */ |
5e1c5ff4 TL |
388 | w = OMAP_MCBSP_READ(io_base, SPCR2); |
389 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); | |
390 | ||
391 | /* Reset receiver */ | |
392 | w = OMAP_MCBSP_READ(io_base, SPCR1); | |
393 | OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); | |
394 | ||
395 | /* Reset the sample rate generator */ | |
396 | w = OMAP_MCBSP_READ(io_base, SPCR2); | |
397 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | |
398 | } | |
fb78d808 | 399 | EXPORT_SYMBOL(omap_mcbsp_stop); |
5e1c5ff4 | 400 | |
bb13b5fd TL |
401 | /* polled mcbsp i/o operations */ |
402 | int omap_mcbsp_pollwrite(unsigned int id, u16 buf) | |
403 | { | |
b4b58f58 | 404 | struct omap_mcbsp *mcbsp; |
d592dd1a | 405 | void __iomem *base; |
bc5d0c89 EV |
406 | |
407 | if (!omap_mcbsp_check_valid_id(id)) { | |
408 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
409 | return -ENODEV; | |
410 | } | |
411 | ||
b4b58f58 CS |
412 | mcbsp = id_to_mcbsp_ptr(id); |
413 | base = mcbsp->io_base; | |
414 | ||
bb13b5fd TL |
415 | writew(buf, base + OMAP_MCBSP_REG_DXR1); |
416 | /* if frame sync error - clear the error */ | |
417 | if (readw(base + OMAP_MCBSP_REG_SPCR2) & XSYNC_ERR) { | |
418 | /* clear error */ | |
419 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) & (~XSYNC_ERR), | |
420 | base + OMAP_MCBSP_REG_SPCR2); | |
421 | /* resend */ | |
422 | return -1; | |
423 | } else { | |
424 | /* wait for transmit confirmation */ | |
425 | int attemps = 0; | |
426 | while (!(readw(base + OMAP_MCBSP_REG_SPCR2) & XRDY)) { | |
427 | if (attemps++ > 1000) { | |
428 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) & | |
429 | (~XRST), | |
430 | base + OMAP_MCBSP_REG_SPCR2); | |
431 | udelay(10); | |
432 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) | | |
433 | (XRST), | |
434 | base + OMAP_MCBSP_REG_SPCR2); | |
435 | udelay(10); | |
b4b58f58 CS |
436 | dev_err(mcbsp->dev, "Could not write to" |
437 | " McBSP%d Register\n", mcbsp->id); | |
bb13b5fd TL |
438 | return -2; |
439 | } | |
440 | } | |
441 | } | |
fb78d808 | 442 | |
bb13b5fd TL |
443 | return 0; |
444 | } | |
fb78d808 | 445 | EXPORT_SYMBOL(omap_mcbsp_pollwrite); |
bb13b5fd | 446 | |
fb78d808 | 447 | int omap_mcbsp_pollread(unsigned int id, u16 *buf) |
bb13b5fd | 448 | { |
b4b58f58 | 449 | struct omap_mcbsp *mcbsp; |
d592dd1a | 450 | void __iomem *base; |
bc5d0c89 EV |
451 | |
452 | if (!omap_mcbsp_check_valid_id(id)) { | |
453 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
454 | return -ENODEV; | |
455 | } | |
b4b58f58 | 456 | mcbsp = id_to_mcbsp_ptr(id); |
bc5d0c89 | 457 | |
b4b58f58 | 458 | base = mcbsp->io_base; |
bb13b5fd TL |
459 | /* if frame sync error - clear the error */ |
460 | if (readw(base + OMAP_MCBSP_REG_SPCR1) & RSYNC_ERR) { | |
461 | /* clear error */ | |
462 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) & (~RSYNC_ERR), | |
463 | base + OMAP_MCBSP_REG_SPCR1); | |
464 | /* resend */ | |
465 | return -1; | |
466 | } else { | |
467 | /* wait for recieve confirmation */ | |
468 | int attemps = 0; | |
469 | while (!(readw(base + OMAP_MCBSP_REG_SPCR1) & RRDY)) { | |
470 | if (attemps++ > 1000) { | |
471 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) & | |
472 | (~RRST), | |
473 | base + OMAP_MCBSP_REG_SPCR1); | |
474 | udelay(10); | |
475 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) | | |
476 | (RRST), | |
477 | base + OMAP_MCBSP_REG_SPCR1); | |
478 | udelay(10); | |
b4b58f58 CS |
479 | dev_err(mcbsp->dev, "Could not read from" |
480 | " McBSP%d Register\n", mcbsp->id); | |
bb13b5fd TL |
481 | return -2; |
482 | } | |
483 | } | |
484 | } | |
485 | *buf = readw(base + OMAP_MCBSP_REG_DRR1); | |
fb78d808 | 486 | |
bb13b5fd TL |
487 | return 0; |
488 | } | |
fb78d808 | 489 | EXPORT_SYMBOL(omap_mcbsp_pollread); |
bb13b5fd | 490 | |
5e1c5ff4 TL |
491 | /* |
492 | * IRQ based word transmission. | |
493 | */ | |
494 | void omap_mcbsp_xmit_word(unsigned int id, u32 word) | |
495 | { | |
b4b58f58 | 496 | struct omap_mcbsp *mcbsp; |
d592dd1a | 497 | void __iomem *io_base; |
bc5d0c89 | 498 | omap_mcbsp_word_length word_length; |
5e1c5ff4 | 499 | |
bc5d0c89 EV |
500 | if (!omap_mcbsp_check_valid_id(id)) { |
501 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
5e1c5ff4 | 502 | return; |
bc5d0c89 | 503 | } |
5e1c5ff4 | 504 | |
b4b58f58 CS |
505 | mcbsp = id_to_mcbsp_ptr(id); |
506 | io_base = mcbsp->io_base; | |
507 | word_length = mcbsp->tx_word_length; | |
5e1c5ff4 | 508 | |
b4b58f58 | 509 | wait_for_completion(&mcbsp->tx_irq_completion); |
5e1c5ff4 TL |
510 | |
511 | if (word_length > OMAP_MCBSP_WORD_16) | |
512 | OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); | |
513 | OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); | |
514 | } | |
fb78d808 | 515 | EXPORT_SYMBOL(omap_mcbsp_xmit_word); |
5e1c5ff4 TL |
516 | |
517 | u32 omap_mcbsp_recv_word(unsigned int id) | |
518 | { | |
b4b58f58 | 519 | struct omap_mcbsp *mcbsp; |
d592dd1a | 520 | void __iomem *io_base; |
5e1c5ff4 | 521 | u16 word_lsb, word_msb = 0; |
bc5d0c89 | 522 | omap_mcbsp_word_length word_length; |
5e1c5ff4 | 523 | |
bc5d0c89 EV |
524 | if (!omap_mcbsp_check_valid_id(id)) { |
525 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
526 | return -ENODEV; | |
527 | } | |
b4b58f58 | 528 | mcbsp = id_to_mcbsp_ptr(id); |
5e1c5ff4 | 529 | |
b4b58f58 CS |
530 | word_length = mcbsp->rx_word_length; |
531 | io_base = mcbsp->io_base; | |
5e1c5ff4 | 532 | |
b4b58f58 | 533 | wait_for_completion(&mcbsp->rx_irq_completion); |
5e1c5ff4 TL |
534 | |
535 | if (word_length > OMAP_MCBSP_WORD_16) | |
536 | word_msb = OMAP_MCBSP_READ(io_base, DRR2); | |
537 | word_lsb = OMAP_MCBSP_READ(io_base, DRR1); | |
538 | ||
539 | return (word_lsb | (word_msb << 16)); | |
540 | } | |
fb78d808 | 541 | EXPORT_SYMBOL(omap_mcbsp_recv_word); |
5e1c5ff4 | 542 | |
120db2cb TL |
543 | int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word) |
544 | { | |
b4b58f58 | 545 | struct omap_mcbsp *mcbsp; |
d592dd1a | 546 | void __iomem *io_base; |
bc5d0c89 EV |
547 | omap_mcbsp_word_length tx_word_length; |
548 | omap_mcbsp_word_length rx_word_length; | |
120db2cb TL |
549 | u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0; |
550 | ||
bc5d0c89 EV |
551 | if (!omap_mcbsp_check_valid_id(id)) { |
552 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
553 | return -ENODEV; | |
554 | } | |
b4b58f58 CS |
555 | mcbsp = id_to_mcbsp_ptr(id); |
556 | io_base = mcbsp->io_base; | |
557 | tx_word_length = mcbsp->tx_word_length; | |
558 | rx_word_length = mcbsp->rx_word_length; | |
bc5d0c89 | 559 | |
120db2cb TL |
560 | if (tx_word_length != rx_word_length) |
561 | return -EINVAL; | |
562 | ||
563 | /* First we wait for the transmitter to be ready */ | |
564 | spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); | |
565 | while (!(spcr2 & XRDY)) { | |
566 | spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); | |
567 | if (attempts++ > 1000) { | |
568 | /* We must reset the transmitter */ | |
569 | OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 & (~XRST)); | |
570 | udelay(10); | |
571 | OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 | XRST); | |
572 | udelay(10); | |
b4b58f58 CS |
573 | dev_err(mcbsp->dev, "McBSP%d transmitter not " |
574 | "ready\n", mcbsp->id); | |
120db2cb TL |
575 | return -EAGAIN; |
576 | } | |
577 | } | |
578 | ||
579 | /* Now we can push the data */ | |
580 | if (tx_word_length > OMAP_MCBSP_WORD_16) | |
581 | OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); | |
582 | OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); | |
583 | ||
584 | /* We wait for the receiver to be ready */ | |
585 | spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); | |
586 | while (!(spcr1 & RRDY)) { | |
587 | spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); | |
588 | if (attempts++ > 1000) { | |
589 | /* We must reset the receiver */ | |
590 | OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 & (~RRST)); | |
591 | udelay(10); | |
592 | OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 | RRST); | |
593 | udelay(10); | |
b4b58f58 CS |
594 | dev_err(mcbsp->dev, "McBSP%d receiver not " |
595 | "ready\n", mcbsp->id); | |
120db2cb TL |
596 | return -EAGAIN; |
597 | } | |
598 | } | |
599 | ||
600 | /* Receiver is ready, let's read the dummy data */ | |
601 | if (rx_word_length > OMAP_MCBSP_WORD_16) | |
602 | word_msb = OMAP_MCBSP_READ(io_base, DRR2); | |
603 | word_lsb = OMAP_MCBSP_READ(io_base, DRR1); | |
604 | ||
605 | return 0; | |
606 | } | |
fb78d808 | 607 | EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll); |
120db2cb | 608 | |
fb78d808 | 609 | int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 *word) |
120db2cb | 610 | { |
b4b58f58 | 611 | struct omap_mcbsp *mcbsp; |
d592dd1a RK |
612 | u32 clock_word = 0; |
613 | void __iomem *io_base; | |
bc5d0c89 EV |
614 | omap_mcbsp_word_length tx_word_length; |
615 | omap_mcbsp_word_length rx_word_length; | |
120db2cb TL |
616 | u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0; |
617 | ||
bc5d0c89 EV |
618 | if (!omap_mcbsp_check_valid_id(id)) { |
619 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
620 | return -ENODEV; | |
621 | } | |
622 | ||
b4b58f58 CS |
623 | mcbsp = id_to_mcbsp_ptr(id); |
624 | io_base = mcbsp->io_base; | |
625 | ||
626 | tx_word_length = mcbsp->tx_word_length; | |
627 | rx_word_length = mcbsp->rx_word_length; | |
bc5d0c89 | 628 | |
120db2cb TL |
629 | if (tx_word_length != rx_word_length) |
630 | return -EINVAL; | |
631 | ||
632 | /* First we wait for the transmitter to be ready */ | |
633 | spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); | |
634 | while (!(spcr2 & XRDY)) { | |
635 | spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); | |
636 | if (attempts++ > 1000) { | |
637 | /* We must reset the transmitter */ | |
638 | OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 & (~XRST)); | |
639 | udelay(10); | |
640 | OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 | XRST); | |
641 | udelay(10); | |
b4b58f58 CS |
642 | dev_err(mcbsp->dev, "McBSP%d transmitter not " |
643 | "ready\n", mcbsp->id); | |
120db2cb TL |
644 | return -EAGAIN; |
645 | } | |
646 | } | |
647 | ||
648 | /* We first need to enable the bus clock */ | |
649 | if (tx_word_length > OMAP_MCBSP_WORD_16) | |
650 | OMAP_MCBSP_WRITE(io_base, DXR2, clock_word >> 16); | |
651 | OMAP_MCBSP_WRITE(io_base, DXR1, clock_word & 0xffff); | |
652 | ||
653 | /* We wait for the receiver to be ready */ | |
654 | spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); | |
655 | while (!(spcr1 & RRDY)) { | |
656 | spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); | |
657 | if (attempts++ > 1000) { | |
658 | /* We must reset the receiver */ | |
659 | OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 & (~RRST)); | |
660 | udelay(10); | |
661 | OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 | RRST); | |
662 | udelay(10); | |
b4b58f58 CS |
663 | dev_err(mcbsp->dev, "McBSP%d receiver not " |
664 | "ready\n", mcbsp->id); | |
120db2cb TL |
665 | return -EAGAIN; |
666 | } | |
667 | } | |
668 | ||
669 | /* Receiver is ready, there is something for us */ | |
670 | if (rx_word_length > OMAP_MCBSP_WORD_16) | |
671 | word_msb = OMAP_MCBSP_READ(io_base, DRR2); | |
672 | word_lsb = OMAP_MCBSP_READ(io_base, DRR1); | |
673 | ||
674 | word[0] = (word_lsb | (word_msb << 16)); | |
675 | ||
676 | return 0; | |
677 | } | |
fb78d808 | 678 | EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll); |
120db2cb | 679 | |
5e1c5ff4 TL |
680 | /* |
681 | * Simple DMA based buffer rx/tx routines. | |
682 | * Nothing fancy, just a single buffer tx/rx through DMA. | |
683 | * The DMA resources are released once the transfer is done. | |
684 | * For anything fancier, you should use your own customized DMA | |
685 | * routines and callbacks. | |
686 | */ | |
fb78d808 EV |
687 | int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, |
688 | unsigned int length) | |
5e1c5ff4 | 689 | { |
b4b58f58 | 690 | struct omap_mcbsp *mcbsp; |
5e1c5ff4 | 691 | int dma_tx_ch; |
120db2cb TL |
692 | int src_port = 0; |
693 | int dest_port = 0; | |
694 | int sync_dev = 0; | |
5e1c5ff4 | 695 | |
bc5d0c89 EV |
696 | if (!omap_mcbsp_check_valid_id(id)) { |
697 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
698 | return -ENODEV; | |
699 | } | |
b4b58f58 | 700 | mcbsp = id_to_mcbsp_ptr(id); |
5e1c5ff4 | 701 | |
b4b58f58 | 702 | if (omap_request_dma(mcbsp->dma_tx_sync, "McBSP TX", |
fb78d808 | 703 | omap_mcbsp_tx_dma_callback, |
b4b58f58 | 704 | mcbsp, |
fb78d808 | 705 | &dma_tx_ch)) { |
b4b58f58 | 706 | dev_err(mcbsp->dev, " Unable to request DMA channel for " |
bc5d0c89 | 707 | "McBSP%d TX. Trying IRQ based TX\n", |
b4b58f58 | 708 | mcbsp->id); |
5e1c5ff4 TL |
709 | return -EAGAIN; |
710 | } | |
b4b58f58 | 711 | mcbsp->dma_tx_lch = dma_tx_ch; |
5e1c5ff4 | 712 | |
b4b58f58 | 713 | dev_err(mcbsp->dev, "McBSP%d TX DMA on channel %d\n", mcbsp->id, |
bc5d0c89 | 714 | dma_tx_ch); |
5e1c5ff4 | 715 | |
b4b58f58 | 716 | init_completion(&mcbsp->tx_dma_completion); |
5e1c5ff4 | 717 | |
120db2cb TL |
718 | if (cpu_class_is_omap1()) { |
719 | src_port = OMAP_DMA_PORT_TIPB; | |
720 | dest_port = OMAP_DMA_PORT_EMIFF; | |
721 | } | |
bc5d0c89 | 722 | if (cpu_class_is_omap2()) |
b4b58f58 | 723 | sync_dev = mcbsp->dma_tx_sync; |
120db2cb | 724 | |
b4b58f58 | 725 | omap_set_dma_transfer_params(mcbsp->dma_tx_lch, |
5e1c5ff4 TL |
726 | OMAP_DMA_DATA_TYPE_S16, |
727 | length >> 1, 1, | |
1a8bfa1e | 728 | OMAP_DMA_SYNC_ELEMENT, |
120db2cb | 729 | sync_dev, 0); |
5e1c5ff4 | 730 | |
b4b58f58 | 731 | omap_set_dma_dest_params(mcbsp->dma_tx_lch, |
120db2cb | 732 | src_port, |
5e1c5ff4 | 733 | OMAP_DMA_AMODE_CONSTANT, |
b4b58f58 | 734 | mcbsp->phys_base + OMAP_MCBSP_REG_DXR1, |
1a8bfa1e | 735 | 0, 0); |
5e1c5ff4 | 736 | |
b4b58f58 | 737 | omap_set_dma_src_params(mcbsp->dma_tx_lch, |
120db2cb | 738 | dest_port, |
5e1c5ff4 | 739 | OMAP_DMA_AMODE_POST_INC, |
1a8bfa1e TL |
740 | buffer, |
741 | 0, 0); | |
5e1c5ff4 | 742 | |
b4b58f58 CS |
743 | omap_start_dma(mcbsp->dma_tx_lch); |
744 | wait_for_completion(&mcbsp->tx_dma_completion); | |
fb78d808 | 745 | |
5e1c5ff4 TL |
746 | return 0; |
747 | } | |
fb78d808 | 748 | EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); |
5e1c5ff4 | 749 | |
fb78d808 EV |
750 | int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, |
751 | unsigned int length) | |
5e1c5ff4 | 752 | { |
b4b58f58 | 753 | struct omap_mcbsp *mcbsp; |
5e1c5ff4 | 754 | int dma_rx_ch; |
120db2cb TL |
755 | int src_port = 0; |
756 | int dest_port = 0; | |
757 | int sync_dev = 0; | |
5e1c5ff4 | 758 | |
bc5d0c89 EV |
759 | if (!omap_mcbsp_check_valid_id(id)) { |
760 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
761 | return -ENODEV; | |
762 | } | |
b4b58f58 | 763 | mcbsp = id_to_mcbsp_ptr(id); |
5e1c5ff4 | 764 | |
b4b58f58 | 765 | if (omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX", |
fb78d808 | 766 | omap_mcbsp_rx_dma_callback, |
b4b58f58 | 767 | mcbsp, |
fb78d808 | 768 | &dma_rx_ch)) { |
b4b58f58 | 769 | dev_err(mcbsp->dev, "Unable to request DMA channel for " |
bc5d0c89 | 770 | "McBSP%d RX. Trying IRQ based RX\n", |
b4b58f58 | 771 | mcbsp->id); |
5e1c5ff4 TL |
772 | return -EAGAIN; |
773 | } | |
b4b58f58 | 774 | mcbsp->dma_rx_lch = dma_rx_ch; |
5e1c5ff4 | 775 | |
b4b58f58 | 776 | dev_err(mcbsp->dev, "McBSP%d RX DMA on channel %d\n", mcbsp->id, |
bc5d0c89 | 777 | dma_rx_ch); |
5e1c5ff4 | 778 | |
b4b58f58 | 779 | init_completion(&mcbsp->rx_dma_completion); |
5e1c5ff4 | 780 | |
120db2cb TL |
781 | if (cpu_class_is_omap1()) { |
782 | src_port = OMAP_DMA_PORT_TIPB; | |
783 | dest_port = OMAP_DMA_PORT_EMIFF; | |
784 | } | |
bc5d0c89 | 785 | if (cpu_class_is_omap2()) |
b4b58f58 | 786 | sync_dev = mcbsp->dma_rx_sync; |
120db2cb | 787 | |
b4b58f58 | 788 | omap_set_dma_transfer_params(mcbsp->dma_rx_lch, |
fb78d808 EV |
789 | OMAP_DMA_DATA_TYPE_S16, |
790 | length >> 1, 1, | |
791 | OMAP_DMA_SYNC_ELEMENT, | |
792 | sync_dev, 0); | |
5e1c5ff4 | 793 | |
b4b58f58 | 794 | omap_set_dma_src_params(mcbsp->dma_rx_lch, |
120db2cb | 795 | src_port, |
5e1c5ff4 | 796 | OMAP_DMA_AMODE_CONSTANT, |
b4b58f58 | 797 | mcbsp->phys_base + OMAP_MCBSP_REG_DRR1, |
1a8bfa1e | 798 | 0, 0); |
5e1c5ff4 | 799 | |
b4b58f58 | 800 | omap_set_dma_dest_params(mcbsp->dma_rx_lch, |
fb78d808 EV |
801 | dest_port, |
802 | OMAP_DMA_AMODE_POST_INC, | |
803 | buffer, | |
804 | 0, 0); | |
5e1c5ff4 | 805 | |
b4b58f58 CS |
806 | omap_start_dma(mcbsp->dma_rx_lch); |
807 | wait_for_completion(&mcbsp->rx_dma_completion); | |
fb78d808 | 808 | |
5e1c5ff4 TL |
809 | return 0; |
810 | } | |
fb78d808 | 811 | EXPORT_SYMBOL(omap_mcbsp_recv_buffer); |
5e1c5ff4 TL |
812 | |
813 | /* | |
814 | * SPI wrapper. | |
815 | * Since SPI setup is much simpler than the generic McBSP one, | |
816 | * this wrapper just need an omap_mcbsp_spi_cfg structure as an input. | |
817 | * Once this is done, you can call omap_mcbsp_start(). | |
818 | */ | |
fb78d808 EV |
819 | void omap_mcbsp_set_spi_mode(unsigned int id, |
820 | const struct omap_mcbsp_spi_cfg *spi_cfg) | |
5e1c5ff4 | 821 | { |
b4b58f58 | 822 | struct omap_mcbsp *mcbsp; |
5e1c5ff4 TL |
823 | struct omap_mcbsp_reg_cfg mcbsp_cfg; |
824 | ||
bc5d0c89 EV |
825 | if (!omap_mcbsp_check_valid_id(id)) { |
826 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | |
5e1c5ff4 | 827 | return; |
bc5d0c89 | 828 | } |
b4b58f58 | 829 | mcbsp = id_to_mcbsp_ptr(id); |
5e1c5ff4 TL |
830 | |
831 | memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); | |
832 | ||
833 | /* SPI has only one frame */ | |
834 | mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0)); | |
835 | mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0)); | |
836 | ||
fb78d808 | 837 | /* Clock stop mode */ |
5e1c5ff4 TL |
838 | if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY) |
839 | mcbsp_cfg.spcr1 |= (1 << 12); | |
840 | else | |
841 | mcbsp_cfg.spcr1 |= (3 << 11); | |
842 | ||
843 | /* Set clock parities */ | |
844 | if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING) | |
845 | mcbsp_cfg.pcr0 |= CLKRP; | |
846 | else | |
847 | mcbsp_cfg.pcr0 &= ~CLKRP; | |
848 | ||
849 | if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING) | |
850 | mcbsp_cfg.pcr0 &= ~CLKXP; | |
851 | else | |
852 | mcbsp_cfg.pcr0 |= CLKXP; | |
853 | ||
854 | /* Set SCLKME to 0 and CLKSM to 1 */ | |
855 | mcbsp_cfg.pcr0 &= ~SCLKME; | |
856 | mcbsp_cfg.srgr2 |= CLKSM; | |
857 | ||
858 | /* Set FSXP */ | |
859 | if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH) | |
860 | mcbsp_cfg.pcr0 &= ~FSXP; | |
861 | else | |
862 | mcbsp_cfg.pcr0 |= FSXP; | |
863 | ||
864 | if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) { | |
865 | mcbsp_cfg.pcr0 |= CLKXM; | |
fb78d808 | 866 | mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div - 1); |
5e1c5ff4 TL |
867 | mcbsp_cfg.pcr0 |= FSXM; |
868 | mcbsp_cfg.srgr2 &= ~FSGM; | |
869 | mcbsp_cfg.xcr2 |= XDATDLY(1); | |
870 | mcbsp_cfg.rcr2 |= RDATDLY(1); | |
fb78d808 | 871 | } else { |
5e1c5ff4 TL |
872 | mcbsp_cfg.pcr0 &= ~CLKXM; |
873 | mcbsp_cfg.srgr1 |= CLKGDV(1); | |
874 | mcbsp_cfg.pcr0 &= ~FSXM; | |
875 | mcbsp_cfg.xcr2 &= ~XDATDLY(3); | |
876 | mcbsp_cfg.rcr2 &= ~RDATDLY(3); | |
877 | } | |
878 | ||
879 | mcbsp_cfg.xcr2 &= ~XPHASE; | |
880 | mcbsp_cfg.rcr2 &= ~RPHASE; | |
881 | ||
882 | omap_mcbsp_config(id, &mcbsp_cfg); | |
883 | } | |
fb78d808 | 884 | EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); |
5e1c5ff4 TL |
885 | |
886 | /* | |
887 | * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. | |
888 | * 730 has only 2 McBSP, and both of them are MPU peripherals. | |
889 | */ | |
25cef225 | 890 | static int __devinit omap_mcbsp_probe(struct platform_device *pdev) |
bc5d0c89 EV |
891 | { |
892 | struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data; | |
b4b58f58 | 893 | struct omap_mcbsp *mcbsp; |
bc5d0c89 EV |
894 | int id = pdev->id - 1; |
895 | int ret = 0; | |
5e1c5ff4 | 896 | |
bc5d0c89 EV |
897 | if (!pdata) { |
898 | dev_err(&pdev->dev, "McBSP device initialized without" | |
899 | "platform data\n"); | |
900 | ret = -EINVAL; | |
901 | goto exit; | |
902 | } | |
903 | ||
904 | dev_dbg(&pdev->dev, "Initializing OMAP McBSP (%d).\n", pdev->id); | |
905 | ||
b4b58f58 | 906 | if (id >= omap_mcbsp_count) { |
bc5d0c89 EV |
907 | dev_err(&pdev->dev, "Invalid McBSP device id (%d)\n", id); |
908 | ret = -EINVAL; | |
909 | goto exit; | |
910 | } | |
911 | ||
b4b58f58 CS |
912 | mcbsp = kzalloc(sizeof(struct omap_mcbsp), GFP_KERNEL); |
913 | if (!mcbsp) { | |
914 | ret = -ENOMEM; | |
915 | goto exit; | |
916 | } | |
b4b58f58 CS |
917 | |
918 | spin_lock_init(&mcbsp->lock); | |
919 | mcbsp->id = id + 1; | |
920 | mcbsp->free = 1; | |
921 | mcbsp->dma_tx_lch = -1; | |
922 | mcbsp->dma_rx_lch = -1; | |
bc5d0c89 | 923 | |
b4b58f58 CS |
924 | mcbsp->phys_base = pdata->phys_base; |
925 | mcbsp->io_base = ioremap(pdata->phys_base, SZ_4K); | |
926 | if (!mcbsp->io_base) { | |
d592dd1a RK |
927 | ret = -ENOMEM; |
928 | goto err_ioremap; | |
929 | } | |
930 | ||
bc5d0c89 | 931 | /* Default I/O is IRQ based */ |
b4b58f58 CS |
932 | mcbsp->io_type = OMAP_MCBSP_IRQ_IO; |
933 | mcbsp->tx_irq = pdata->tx_irq; | |
934 | mcbsp->rx_irq = pdata->rx_irq; | |
935 | mcbsp->dma_rx_sync = pdata->dma_rx_sync; | |
936 | mcbsp->dma_tx_sync = pdata->dma_tx_sync; | |
bc5d0c89 | 937 | |
b820ce4e RK |
938 | mcbsp->iclk = clk_get(&pdev->dev, "ick"); |
939 | if (IS_ERR(mcbsp->iclk)) { | |
940 | ret = PTR_ERR(mcbsp->iclk); | |
941 | dev_err(&pdev->dev, "unable to get ick: %d\n", ret); | |
942 | goto err_iclk; | |
943 | } | |
06151158 | 944 | |
b820ce4e RK |
945 | mcbsp->fclk = clk_get(&pdev->dev, "fck"); |
946 | if (IS_ERR(mcbsp->fclk)) { | |
947 | ret = PTR_ERR(mcbsp->fclk); | |
948 | dev_err(&pdev->dev, "unable to get fck: %d\n", ret); | |
949 | goto err_fclk; | |
bc5d0c89 EV |
950 | } |
951 | ||
b4b58f58 CS |
952 | mcbsp->pdata = pdata; |
953 | mcbsp->dev = &pdev->dev; | |
b820ce4e | 954 | mcbsp_ptr[id] = mcbsp; |
b4b58f58 | 955 | platform_set_drvdata(pdev, mcbsp); |
d592dd1a | 956 | return 0; |
bc5d0c89 | 957 | |
b820ce4e RK |
958 | err_fclk: |
959 | clk_put(mcbsp->iclk); | |
960 | err_iclk: | |
b4b58f58 | 961 | iounmap(mcbsp->io_base); |
d592dd1a | 962 | err_ioremap: |
b820ce4e | 963 | kfree(mcbsp); |
bc5d0c89 EV |
964 | exit: |
965 | return ret; | |
966 | } | |
120db2cb | 967 | |
25cef225 | 968 | static int __devexit omap_mcbsp_remove(struct platform_device *pdev) |
5e1c5ff4 | 969 | { |
bc5d0c89 | 970 | struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); |
5e1c5ff4 | 971 | |
bc5d0c89 EV |
972 | platform_set_drvdata(pdev, NULL); |
973 | if (mcbsp) { | |
5e1c5ff4 | 974 | |
bc5d0c89 EV |
975 | if (mcbsp->pdata && mcbsp->pdata->ops && |
976 | mcbsp->pdata->ops->free) | |
977 | mcbsp->pdata->ops->free(mcbsp->id); | |
5e1c5ff4 | 978 | |
b820ce4e RK |
979 | clk_disable(mcbsp->fclk); |
980 | clk_disable(mcbsp->iclk); | |
981 | clk_put(mcbsp->fclk); | |
982 | clk_put(mcbsp->iclk); | |
bc5d0c89 | 983 | |
d592dd1a RK |
984 | iounmap(mcbsp->io_base); |
985 | ||
b820ce4e RK |
986 | mcbsp->fclk = NULL; |
987 | mcbsp->iclk = NULL; | |
bc5d0c89 EV |
988 | mcbsp->free = 0; |
989 | mcbsp->dev = NULL; | |
5e1c5ff4 TL |
990 | } |
991 | ||
992 | return 0; | |
993 | } | |
994 | ||
bc5d0c89 EV |
995 | static struct platform_driver omap_mcbsp_driver = { |
996 | .probe = omap_mcbsp_probe, | |
25cef225 | 997 | .remove = __devexit_p(omap_mcbsp_remove), |
bc5d0c89 EV |
998 | .driver = { |
999 | .name = "omap-mcbsp", | |
1000 | }, | |
1001 | }; | |
1002 | ||
1003 | int __init omap_mcbsp_init(void) | |
1004 | { | |
1005 | /* Register the McBSP driver */ | |
1006 | return platform_driver_register(&omap_mcbsp_driver); | |
1007 | } |