blob: 56a56887dedcf291c179c7a2df669bed3e49ac41 [file] [log] [blame]
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001/*
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>
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +030018#include <linux/platform_device.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010019#include <linux/wait.h>
20#include <linux/completion.h>
21#include <linux/interrupt.h>
22#include <linux/err.h>
Russell Kingf8ce2542006-01-07 16:15:52 +000023#include <linux/clk.h>
Tony Lindgren04fbf6a2007-02-12 10:50:53 -080024#include <linux/delay.h>
Eduardo Valentinfb78d802008-07-03 12:24:39 +030025#include <linux/io.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010026
Russell Kinga09e64f2008-08-05 16:14:15 +010027#include <mach/dma.h>
28#include <mach/mcbsp.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010029
Chandra Shekharb4b58f52008-10-08 10:01:39 +030030struct omap_mcbsp **mcbsp_ptr;
31int omap_mcbsp_count;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +030032
Chandra Shekharb4b58f52008-10-08 10:01:39 +030033void 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
41int 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];
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010056
57static void omap_mcbsp_dump_reg(u8 id)
58{
Chandra Shekharb4b58f52008-10-08 10:01:39 +030059 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");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010089}
90
Linus Torvalds0cd61b62006-10-06 10:53:39 -070091static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010092{
Jeff Garzike8f2af12007-10-26 05:40:25 -040093 struct omap_mcbsp *mcbsp_tx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -070094 u16 irqst_spcr2;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010095
Eero Nurkkalad6d834b2009-05-25 11:08:42 -070096 irqst_spcr2 = OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2);
97 dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010098
Eero Nurkkalad6d834b2009-05-25 11:08:42 -070099 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 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300108
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100109 return IRQ_HANDLED;
110}
111
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700112static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100113{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400114 struct omap_mcbsp *mcbsp_rx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700115 u16 irqst_spcr1;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100116
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700117 irqst_spcr1 = OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR1);
118 dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100119
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700120 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 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300129
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100130 return IRQ_HANDLED;
131}
132
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100133static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data)
134{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400135 struct omap_mcbsp *mcbsp_dma_tx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100136
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300137 dev_dbg(mcbsp_dma_tx->dev, "TX DMA callback : 0x%x\n",
138 OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100139
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
147static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data)
148{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400149 struct omap_mcbsp *mcbsp_dma_rx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100150
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300151 dev_dbg(mcbsp_dma_rx->dev, "RX DMA callback : 0x%x\n",
152 OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100153
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
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100161/*
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 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300167void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100168{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300169 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100170 void __iomem *io_base;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100171
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300172 if (!omap_mcbsp_check_valid_id(id)) {
173 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
174 return;
175 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300176 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300177
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300178 io_base = mcbsp->io_base;
179 dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
180 mcbsp->id, mcbsp->phys_base);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100181
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);
Tony Lindgren3127f8f2009-01-15 13:09:54 +0200194 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
195 OMAP_MCBSP_WRITE(io_base, XCCR, config->xccr);
196 OMAP_MCBSP_WRITE(io_base, RCCR, config->rccr);
197 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100198}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300199EXPORT_SYMBOL(omap_mcbsp_config);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100200
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300201#ifdef CONFIG_ARCH_OMAP34XX
202/*
203 * omap_mcbsp_set_tx_threshold configures how to deal
204 * with transmit threshold. the threshold value and handler can be
205 * configure in here.
206 */
207void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
208{
209 struct omap_mcbsp *mcbsp;
210 void __iomem *io_base;
211
212 if (!cpu_is_omap34xx())
213 return;
214
215 if (!omap_mcbsp_check_valid_id(id)) {
216 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
217 return;
218 }
219 mcbsp = id_to_mcbsp_ptr(id);
220 io_base = mcbsp->io_base;
221
222 OMAP_MCBSP_WRITE(io_base, THRSH2, threshold);
223}
224EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
225
226/*
227 * omap_mcbsp_set_rx_threshold configures how to deal
228 * with receive threshold. the threshold value and handler can be
229 * configure in here.
230 */
231void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
232{
233 struct omap_mcbsp *mcbsp;
234 void __iomem *io_base;
235
236 if (!cpu_is_omap34xx())
237 return;
238
239 if (!omap_mcbsp_check_valid_id(id)) {
240 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
241 return;
242 }
243 mcbsp = id_to_mcbsp_ptr(id);
244 io_base = mcbsp->io_base;
245
246 OMAP_MCBSP_WRITE(io_base, THRSH1, threshold);
247}
248EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
Eduardo Valentina1a56f52009-08-20 16:18:11 +0300249
250/*
251 * omap_mcbsp_get_max_tx_thres just return the current configured
252 * maximum threshold for transmission
253 */
254u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
255{
256 struct omap_mcbsp *mcbsp;
257
258 if (!omap_mcbsp_check_valid_id(id)) {
259 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
260 return -ENODEV;
261 }
262 mcbsp = id_to_mcbsp_ptr(id);
263
264 return mcbsp->max_tx_thres;
265}
266EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
267
268/*
269 * omap_mcbsp_get_max_rx_thres just return the current configured
270 * maximum threshold for reception
271 */
272u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
273{
274 struct omap_mcbsp *mcbsp;
275
276 if (!omap_mcbsp_check_valid_id(id)) {
277 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
278 return -ENODEV;
279 }
280 mcbsp = id_to_mcbsp_ptr(id);
281
282 return mcbsp->max_rx_thres;
283}
284EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300285
286/*
287 * omap_mcbsp_get_dma_op_mode just return the current configured
288 * operating mode for the mcbsp channel
289 */
290int omap_mcbsp_get_dma_op_mode(unsigned int id)
291{
292 struct omap_mcbsp *mcbsp;
293 int dma_op_mode;
294
295 if (!omap_mcbsp_check_valid_id(id)) {
296 printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
297 return -ENODEV;
298 }
299 mcbsp = id_to_mcbsp_ptr(id);
300
301 spin_lock_irq(&mcbsp->lock);
302 dma_op_mode = mcbsp->dma_op_mode;
303 spin_unlock_irq(&mcbsp->lock);
304
305 return dma_op_mode;
306}
307EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300308
309static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
310{
311 /*
312 * Enable wakup behavior, smart idle and all wakeups
313 * REVISIT: some wakeups may be unnecessary
314 */
315 if (cpu_is_omap34xx()) {
316 u16 syscon;
317
318 syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300319 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eduardo Valentind99a7452009-08-20 16:18:18 +0300320
321 spin_lock_irq(&mcbsp->lock);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300322 if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
323 syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
324 CLOCKACTIVITY(0x02));
325 OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN,
326 XRDYEN | RRDYEN);
327 } else {
Eduardo Valentind99a7452009-08-20 16:18:18 +0300328 syscon |= SIDLEMODE(0x01);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300329 }
Eduardo Valentind99a7452009-08-20 16:18:18 +0300330 spin_unlock_irq(&mcbsp->lock);
331
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300332 OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300333 }
334}
335
336static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
337{
338 /*
339 * Disable wakup behavior, smart idle and all wakeups
340 */
341 if (cpu_is_omap34xx()) {
342 u16 syscon;
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300343
344 syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300345 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300346 OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
347
Eduardo Valentind9a9b3f2009-08-20 16:18:16 +0300348 OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN, 0);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300349 }
350}
351#else
352static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
353static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300354#endif
355
Tony Lindgren120db2c2006-04-02 17:46:27 +0100356/*
357 * We can choose between IRQ based or polled IO.
358 * This needs to be called before omap_mcbsp_request().
359 */
360int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type)
361{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300362 struct omap_mcbsp *mcbsp;
363
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300364 if (!omap_mcbsp_check_valid_id(id)) {
365 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
366 return -ENODEV;
367 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300368 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100369
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300370 spin_lock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100371
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300372 if (!mcbsp->free) {
373 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
374 mcbsp->id);
375 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100376 return -EINVAL;
377 }
378
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300379 mcbsp->io_type = io_type;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100380
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300381 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100382
383 return 0;
384}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300385EXPORT_SYMBOL(omap_mcbsp_set_io_type);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100386
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100387int omap_mcbsp_request(unsigned int id)
388{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300389 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100390 int err;
391
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300392 if (!omap_mcbsp_check_valid_id(id)) {
393 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
394 return -ENODEV;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100395 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300396 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300397
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300398 spin_lock(&mcbsp->lock);
399 if (!mcbsp->free) {
400 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
401 mcbsp->id);
402 spin_unlock(&mcbsp->lock);
Russell Kingb820ce42009-01-23 10:26:46 +0000403 return -EBUSY;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100404 }
405
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300406 mcbsp->free = 0;
407 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100408
Russell Kingb820ce42009-01-23 10:26:46 +0000409 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
410 mcbsp->pdata->ops->request(id);
411
412 clk_enable(mcbsp->iclk);
413 clk_enable(mcbsp->fclk);
414
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300415 /* Do procedure specific to omap34xx arch, if applicable */
416 omap34xx_mcbsp_request(mcbsp);
417
Jarkko Nikula5a070552008-10-08 10:01:41 +0300418 /*
419 * Make sure that transmitter, receiver and sample-rate generator are
420 * not running before activating IRQs.
421 */
422 OMAP_MCBSP_WRITE(mcbsp->io_base, SPCR1, 0);
423 OMAP_MCBSP_WRITE(mcbsp->io_base, SPCR2, 0);
424
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300425 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
Tony Lindgren120db2c2006-04-02 17:46:27 +0100426 /* We need to get IRQs here */
Jarkko Nikula5a070552008-10-08 10:01:41 +0300427 init_completion(&mcbsp->tx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300428 err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
429 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100430 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300431 dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
432 "for McBSP%d\n", mcbsp->tx_irq,
433 mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100434 return err;
435 }
436
Jarkko Nikula5a070552008-10-08 10:01:41 +0300437 init_completion(&mcbsp->rx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300438 err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler,
439 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100440 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300441 dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
442 "for McBSP%d\n", mcbsp->rx_irq,
443 mcbsp->id);
444 free_irq(mcbsp->tx_irq, (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100445 return err;
446 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100447 }
448
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100449 return 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100450}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300451EXPORT_SYMBOL(omap_mcbsp_request);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100452
453void omap_mcbsp_free(unsigned int id)
454{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300455 struct omap_mcbsp *mcbsp;
456
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300457 if (!omap_mcbsp_check_valid_id(id)) {
458 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100459 return;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100460 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300461 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100462
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300463 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
464 mcbsp->pdata->ops->free(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300465
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300466 /* Do procedure specific to omap34xx arch, if applicable */
467 omap34xx_mcbsp_free(mcbsp);
468
Russell Kingb820ce42009-01-23 10:26:46 +0000469 clk_disable(mcbsp->fclk);
470 clk_disable(mcbsp->iclk);
471
472 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
473 /* Free IRQs */
474 free_irq(mcbsp->rx_irq, (void *)mcbsp);
475 free_irq(mcbsp->tx_irq, (void *)mcbsp);
476 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100477
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300478 spin_lock(&mcbsp->lock);
479 if (mcbsp->free) {
480 dev_err(mcbsp->dev, "McBSP%d was not reserved\n",
481 mcbsp->id);
482 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100483 return;
484 }
485
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300486 mcbsp->free = 1;
487 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100488}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300489EXPORT_SYMBOL(omap_mcbsp_free);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100490
491/*
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300492 * Here we start the McBSP, by enabling transmitter, receiver or both.
493 * If no transmitter or receiver is active prior calling, then sample-rate
494 * generator and frame sync are started.
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100495 */
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300496void omap_mcbsp_start(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100497{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300498 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100499 void __iomem *io_base;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300500 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100501 u16 w;
502
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300503 if (!omap_mcbsp_check_valid_id(id)) {
504 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100505 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300506 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300507 mcbsp = id_to_mcbsp_ptr(id);
508 io_base = mcbsp->io_base;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100509
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300510 mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7;
511 mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100512
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300513 idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
514 OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
515
516 if (idle) {
517 /* Start the sample generator */
518 w = OMAP_MCBSP_READ(io_base, SPCR2);
519 OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
520 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100521
522 /* Enable transmitter and receiver */
523 w = OMAP_MCBSP_READ(io_base, SPCR2);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300524 OMAP_MCBSP_WRITE(io_base, SPCR2, w | (tx & 1));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100525
526 w = OMAP_MCBSP_READ(io_base, SPCR1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300527 OMAP_MCBSP_WRITE(io_base, SPCR1, w | (rx & 1));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100528
Eduardo Valentin44a63112009-08-20 16:18:09 +0300529 /*
530 * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
531 * REVISIT: 100us may give enough time for two CLKSRG, however
532 * due to some unknown PM related, clock gating etc. reason it
533 * is now at 500us.
534 */
535 udelay(500);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100536
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300537 if (idle) {
538 /* Start frame sync */
539 w = OMAP_MCBSP_READ(io_base, SPCR2);
540 OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
541 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100542
543 /* Dump McBSP Regs */
544 omap_mcbsp_dump_reg(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100545}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300546EXPORT_SYMBOL(omap_mcbsp_start);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100547
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300548void omap_mcbsp_stop(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100549{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300550 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100551 void __iomem *io_base;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300552 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100553 u16 w;
554
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300555 if (!omap_mcbsp_check_valid_id(id)) {
556 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100557 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300558 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100559
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300560 mcbsp = id_to_mcbsp_ptr(id);
561 io_base = mcbsp->io_base;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100562
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300563 /* Reset transmitter */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100564 w = OMAP_MCBSP_READ(io_base, SPCR2);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300565 OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(tx & 1));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100566
567 /* Reset receiver */
568 w = OMAP_MCBSP_READ(io_base, SPCR1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300569 OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(rx & 1));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100570
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300571 idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
572 OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
573
574 if (idle) {
575 /* Reset the sample rate generator */
576 w = OMAP_MCBSP_READ(io_base, SPCR2);
577 OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
578 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100579}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300580EXPORT_SYMBOL(omap_mcbsp_stop);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100581
Eero Nurkkala9abea082009-08-20 16:18:07 +0300582void omap_mcbsp_xmit_enable(unsigned int id, u8 enable)
583{
584 struct omap_mcbsp *mcbsp;
585 void __iomem *io_base;
586 u16 w;
587
588 if (!(cpu_is_omap2430() || cpu_is_omap34xx()))
589 return;
590
591 if (!omap_mcbsp_check_valid_id(id)) {
592 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
593 return;
594 }
595
596 mcbsp = id_to_mcbsp_ptr(id);
597 io_base = mcbsp->io_base;
598
599 w = OMAP_MCBSP_READ(io_base, XCCR);
600
601 if (enable)
602 OMAP_MCBSP_WRITE(io_base, XCCR, w & ~(XDISABLE));
603 else
604 OMAP_MCBSP_WRITE(io_base, XCCR, w | XDISABLE);
605}
606EXPORT_SYMBOL(omap_mcbsp_xmit_enable);
607
608void omap_mcbsp_recv_enable(unsigned int id, u8 enable)
609{
610 struct omap_mcbsp *mcbsp;
611 void __iomem *io_base;
612 u16 w;
613
614 if (!(cpu_is_omap2430() || cpu_is_omap34xx()))
615 return;
616
617 if (!omap_mcbsp_check_valid_id(id)) {
618 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
619 return;
620 }
621
622 mcbsp = id_to_mcbsp_ptr(id);
623 io_base = mcbsp->io_base;
624
625 w = OMAP_MCBSP_READ(io_base, RCCR);
626
627 if (enable)
628 OMAP_MCBSP_WRITE(io_base, RCCR, w & ~(RDISABLE));
629 else
630 OMAP_MCBSP_WRITE(io_base, RCCR, w | RDISABLE);
631}
632EXPORT_SYMBOL(omap_mcbsp_recv_enable);
633
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100634/* polled mcbsp i/o operations */
635int omap_mcbsp_pollwrite(unsigned int id, u16 buf)
636{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300637 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100638 void __iomem *base;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300639
640 if (!omap_mcbsp_check_valid_id(id)) {
641 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
642 return -ENODEV;
643 }
644
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300645 mcbsp = id_to_mcbsp_ptr(id);
646 base = mcbsp->io_base;
647
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100648 writew(buf, base + OMAP_MCBSP_REG_DXR1);
649 /* if frame sync error - clear the error */
650 if (readw(base + OMAP_MCBSP_REG_SPCR2) & XSYNC_ERR) {
651 /* clear error */
652 writew(readw(base + OMAP_MCBSP_REG_SPCR2) & (~XSYNC_ERR),
653 base + OMAP_MCBSP_REG_SPCR2);
654 /* resend */
655 return -1;
656 } else {
657 /* wait for transmit confirmation */
658 int attemps = 0;
659 while (!(readw(base + OMAP_MCBSP_REG_SPCR2) & XRDY)) {
660 if (attemps++ > 1000) {
661 writew(readw(base + OMAP_MCBSP_REG_SPCR2) &
662 (~XRST),
663 base + OMAP_MCBSP_REG_SPCR2);
664 udelay(10);
665 writew(readw(base + OMAP_MCBSP_REG_SPCR2) |
666 (XRST),
667 base + OMAP_MCBSP_REG_SPCR2);
668 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300669 dev_err(mcbsp->dev, "Could not write to"
670 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100671 return -2;
672 }
673 }
674 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300675
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100676 return 0;
677}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300678EXPORT_SYMBOL(omap_mcbsp_pollwrite);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100679
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300680int omap_mcbsp_pollread(unsigned int id, u16 *buf)
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100681{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300682 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100683 void __iomem *base;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300684
685 if (!omap_mcbsp_check_valid_id(id)) {
686 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
687 return -ENODEV;
688 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300689 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300690
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300691 base = mcbsp->io_base;
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100692 /* if frame sync error - clear the error */
693 if (readw(base + OMAP_MCBSP_REG_SPCR1) & RSYNC_ERR) {
694 /* clear error */
695 writew(readw(base + OMAP_MCBSP_REG_SPCR1) & (~RSYNC_ERR),
696 base + OMAP_MCBSP_REG_SPCR1);
697 /* resend */
698 return -1;
699 } else {
700 /* wait for recieve confirmation */
701 int attemps = 0;
702 while (!(readw(base + OMAP_MCBSP_REG_SPCR1) & RRDY)) {
703 if (attemps++ > 1000) {
704 writew(readw(base + OMAP_MCBSP_REG_SPCR1) &
705 (~RRST),
706 base + OMAP_MCBSP_REG_SPCR1);
707 udelay(10);
708 writew(readw(base + OMAP_MCBSP_REG_SPCR1) |
709 (RRST),
710 base + OMAP_MCBSP_REG_SPCR1);
711 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300712 dev_err(mcbsp->dev, "Could not read from"
713 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100714 return -2;
715 }
716 }
717 }
718 *buf = readw(base + OMAP_MCBSP_REG_DRR1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300719
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100720 return 0;
721}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300722EXPORT_SYMBOL(omap_mcbsp_pollread);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100723
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100724/*
725 * IRQ based word transmission.
726 */
727void omap_mcbsp_xmit_word(unsigned int id, u32 word)
728{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300729 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100730 void __iomem *io_base;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300731 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100732
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300733 if (!omap_mcbsp_check_valid_id(id)) {
734 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100735 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300736 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100737
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300738 mcbsp = id_to_mcbsp_ptr(id);
739 io_base = mcbsp->io_base;
740 word_length = mcbsp->tx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100741
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300742 wait_for_completion(&mcbsp->tx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100743
744 if (word_length > OMAP_MCBSP_WORD_16)
745 OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16);
746 OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff);
747}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300748EXPORT_SYMBOL(omap_mcbsp_xmit_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100749
750u32 omap_mcbsp_recv_word(unsigned int id)
751{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300752 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100753 void __iomem *io_base;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100754 u16 word_lsb, word_msb = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300755 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100756
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300757 if (!omap_mcbsp_check_valid_id(id)) {
758 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
759 return -ENODEV;
760 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300761 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100762
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300763 word_length = mcbsp->rx_word_length;
764 io_base = mcbsp->io_base;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100765
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300766 wait_for_completion(&mcbsp->rx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100767
768 if (word_length > OMAP_MCBSP_WORD_16)
769 word_msb = OMAP_MCBSP_READ(io_base, DRR2);
770 word_lsb = OMAP_MCBSP_READ(io_base, DRR1);
771
772 return (word_lsb | (word_msb << 16));
773}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300774EXPORT_SYMBOL(omap_mcbsp_recv_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100775
Tony Lindgren120db2c2006-04-02 17:46:27 +0100776int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word)
777{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300778 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100779 void __iomem *io_base;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300780 omap_mcbsp_word_length tx_word_length;
781 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100782 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
783
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300784 if (!omap_mcbsp_check_valid_id(id)) {
785 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
786 return -ENODEV;
787 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300788 mcbsp = id_to_mcbsp_ptr(id);
789 io_base = mcbsp->io_base;
790 tx_word_length = mcbsp->tx_word_length;
791 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300792
Tony Lindgren120db2c2006-04-02 17:46:27 +0100793 if (tx_word_length != rx_word_length)
794 return -EINVAL;
795
796 /* First we wait for the transmitter to be ready */
797 spcr2 = OMAP_MCBSP_READ(io_base, SPCR2);
798 while (!(spcr2 & XRDY)) {
799 spcr2 = OMAP_MCBSP_READ(io_base, SPCR2);
800 if (attempts++ > 1000) {
801 /* We must reset the transmitter */
802 OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 & (~XRST));
803 udelay(10);
804 OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 | XRST);
805 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300806 dev_err(mcbsp->dev, "McBSP%d transmitter not "
807 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100808 return -EAGAIN;
809 }
810 }
811
812 /* Now we can push the data */
813 if (tx_word_length > OMAP_MCBSP_WORD_16)
814 OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16);
815 OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff);
816
817 /* We wait for the receiver to be ready */
818 spcr1 = OMAP_MCBSP_READ(io_base, SPCR1);
819 while (!(spcr1 & RRDY)) {
820 spcr1 = OMAP_MCBSP_READ(io_base, SPCR1);
821 if (attempts++ > 1000) {
822 /* We must reset the receiver */
823 OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 & (~RRST));
824 udelay(10);
825 OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 | RRST);
826 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300827 dev_err(mcbsp->dev, "McBSP%d receiver not "
828 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100829 return -EAGAIN;
830 }
831 }
832
833 /* Receiver is ready, let's read the dummy data */
834 if (rx_word_length > OMAP_MCBSP_WORD_16)
835 word_msb = OMAP_MCBSP_READ(io_base, DRR2);
836 word_lsb = OMAP_MCBSP_READ(io_base, DRR1);
837
838 return 0;
839}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300840EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100841
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300842int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 *word)
Tony Lindgren120db2c2006-04-02 17:46:27 +0100843{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300844 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100845 u32 clock_word = 0;
846 void __iomem *io_base;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300847 omap_mcbsp_word_length tx_word_length;
848 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100849 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
850
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300851 if (!omap_mcbsp_check_valid_id(id)) {
852 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
853 return -ENODEV;
854 }
855
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300856 mcbsp = id_to_mcbsp_ptr(id);
857 io_base = mcbsp->io_base;
858
859 tx_word_length = mcbsp->tx_word_length;
860 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300861
Tony Lindgren120db2c2006-04-02 17:46:27 +0100862 if (tx_word_length != rx_word_length)
863 return -EINVAL;
864
865 /* First we wait for the transmitter to be ready */
866 spcr2 = OMAP_MCBSP_READ(io_base, SPCR2);
867 while (!(spcr2 & XRDY)) {
868 spcr2 = OMAP_MCBSP_READ(io_base, SPCR2);
869 if (attempts++ > 1000) {
870 /* We must reset the transmitter */
871 OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 & (~XRST));
872 udelay(10);
873 OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 | XRST);
874 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300875 dev_err(mcbsp->dev, "McBSP%d transmitter not "
876 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100877 return -EAGAIN;
878 }
879 }
880
881 /* We first need to enable the bus clock */
882 if (tx_word_length > OMAP_MCBSP_WORD_16)
883 OMAP_MCBSP_WRITE(io_base, DXR2, clock_word >> 16);
884 OMAP_MCBSP_WRITE(io_base, DXR1, clock_word & 0xffff);
885
886 /* We wait for the receiver to be ready */
887 spcr1 = OMAP_MCBSP_READ(io_base, SPCR1);
888 while (!(spcr1 & RRDY)) {
889 spcr1 = OMAP_MCBSP_READ(io_base, SPCR1);
890 if (attempts++ > 1000) {
891 /* We must reset the receiver */
892 OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 & (~RRST));
893 udelay(10);
894 OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 | RRST);
895 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300896 dev_err(mcbsp->dev, "McBSP%d receiver not "
897 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100898 return -EAGAIN;
899 }
900 }
901
902 /* Receiver is ready, there is something for us */
903 if (rx_word_length > OMAP_MCBSP_WORD_16)
904 word_msb = OMAP_MCBSP_READ(io_base, DRR2);
905 word_lsb = OMAP_MCBSP_READ(io_base, DRR1);
906
907 word[0] = (word_lsb | (word_msb << 16));
908
909 return 0;
910}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300911EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100912
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100913/*
914 * Simple DMA based buffer rx/tx routines.
915 * Nothing fancy, just a single buffer tx/rx through DMA.
916 * The DMA resources are released once the transfer is done.
917 * For anything fancier, you should use your own customized DMA
918 * routines and callbacks.
919 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300920int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer,
921 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100922{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300923 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100924 int dma_tx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100925 int src_port = 0;
926 int dest_port = 0;
927 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100928
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300929 if (!omap_mcbsp_check_valid_id(id)) {
930 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
931 return -ENODEV;
932 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300933 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100934
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300935 if (omap_request_dma(mcbsp->dma_tx_sync, "McBSP TX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300936 omap_mcbsp_tx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300937 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300938 &dma_tx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300939 dev_err(mcbsp->dev, " Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300940 "McBSP%d TX. Trying IRQ based TX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300941 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100942 return -EAGAIN;
943 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300944 mcbsp->dma_tx_lch = dma_tx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100945
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300946 dev_err(mcbsp->dev, "McBSP%d TX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300947 dma_tx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100948
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300949 init_completion(&mcbsp->tx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100950
Tony Lindgren120db2c2006-04-02 17:46:27 +0100951 if (cpu_class_is_omap1()) {
952 src_port = OMAP_DMA_PORT_TIPB;
953 dest_port = OMAP_DMA_PORT_EMIFF;
954 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300955 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300956 sync_dev = mcbsp->dma_tx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100957
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300958 omap_set_dma_transfer_params(mcbsp->dma_tx_lch,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100959 OMAP_DMA_DATA_TYPE_S16,
960 length >> 1, 1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000961 OMAP_DMA_SYNC_ELEMENT,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100962 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100963
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300964 omap_set_dma_dest_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100965 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100966 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300967 mcbsp->phys_base + OMAP_MCBSP_REG_DXR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000968 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100969
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300970 omap_set_dma_src_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100971 dest_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100972 OMAP_DMA_AMODE_POST_INC,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000973 buffer,
974 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100975
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300976 omap_start_dma(mcbsp->dma_tx_lch);
977 wait_for_completion(&mcbsp->tx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300978
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100979 return 0;
980}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300981EXPORT_SYMBOL(omap_mcbsp_xmit_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100982
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300983int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer,
984 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100985{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300986 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100987 int dma_rx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100988 int src_port = 0;
989 int dest_port = 0;
990 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100991
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300992 if (!omap_mcbsp_check_valid_id(id)) {
993 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
994 return -ENODEV;
995 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300996 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100997
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300998 if (omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300999 omap_mcbsp_rx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001000 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001001 &dma_rx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001002 dev_err(mcbsp->dev, "Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001003 "McBSP%d RX. Trying IRQ based RX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001004 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001005 return -EAGAIN;
1006 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001007 mcbsp->dma_rx_lch = dma_rx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001008
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001009 dev_err(mcbsp->dev, "McBSP%d RX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001010 dma_rx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001011
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001012 init_completion(&mcbsp->rx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001013
Tony Lindgren120db2c2006-04-02 17:46:27 +01001014 if (cpu_class_is_omap1()) {
1015 src_port = OMAP_DMA_PORT_TIPB;
1016 dest_port = OMAP_DMA_PORT_EMIFF;
1017 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001018 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001019 sync_dev = mcbsp->dma_rx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001020
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001021 omap_set_dma_transfer_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001022 OMAP_DMA_DATA_TYPE_S16,
1023 length >> 1, 1,
1024 OMAP_DMA_SYNC_ELEMENT,
1025 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001026
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001027 omap_set_dma_src_params(mcbsp->dma_rx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001028 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001029 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001030 mcbsp->phys_base + OMAP_MCBSP_REG_DRR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001031 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001032
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001033 omap_set_dma_dest_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001034 dest_port,
1035 OMAP_DMA_AMODE_POST_INC,
1036 buffer,
1037 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001038
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001039 omap_start_dma(mcbsp->dma_rx_lch);
1040 wait_for_completion(&mcbsp->rx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001041
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001042 return 0;
1043}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001044EXPORT_SYMBOL(omap_mcbsp_recv_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001045
1046/*
1047 * SPI wrapper.
1048 * Since SPI setup is much simpler than the generic McBSP one,
1049 * this wrapper just need an omap_mcbsp_spi_cfg structure as an input.
1050 * Once this is done, you can call omap_mcbsp_start().
1051 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001052void omap_mcbsp_set_spi_mode(unsigned int id,
1053 const struct omap_mcbsp_spi_cfg *spi_cfg)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001054{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001055 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001056 struct omap_mcbsp_reg_cfg mcbsp_cfg;
1057
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001058 if (!omap_mcbsp_check_valid_id(id)) {
1059 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001060 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001061 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001062 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001063
1064 memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg));
1065
1066 /* SPI has only one frame */
1067 mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0));
1068 mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0));
1069
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001070 /* Clock stop mode */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001071 if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY)
1072 mcbsp_cfg.spcr1 |= (1 << 12);
1073 else
1074 mcbsp_cfg.spcr1 |= (3 << 11);
1075
1076 /* Set clock parities */
1077 if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1078 mcbsp_cfg.pcr0 |= CLKRP;
1079 else
1080 mcbsp_cfg.pcr0 &= ~CLKRP;
1081
1082 if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1083 mcbsp_cfg.pcr0 &= ~CLKXP;
1084 else
1085 mcbsp_cfg.pcr0 |= CLKXP;
1086
1087 /* Set SCLKME to 0 and CLKSM to 1 */
1088 mcbsp_cfg.pcr0 &= ~SCLKME;
1089 mcbsp_cfg.srgr2 |= CLKSM;
1090
1091 /* Set FSXP */
1092 if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH)
1093 mcbsp_cfg.pcr0 &= ~FSXP;
1094 else
1095 mcbsp_cfg.pcr0 |= FSXP;
1096
1097 if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) {
1098 mcbsp_cfg.pcr0 |= CLKXM;
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001099 mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div - 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001100 mcbsp_cfg.pcr0 |= FSXM;
1101 mcbsp_cfg.srgr2 &= ~FSGM;
1102 mcbsp_cfg.xcr2 |= XDATDLY(1);
1103 mcbsp_cfg.rcr2 |= RDATDLY(1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001104 } else {
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001105 mcbsp_cfg.pcr0 &= ~CLKXM;
1106 mcbsp_cfg.srgr1 |= CLKGDV(1);
1107 mcbsp_cfg.pcr0 &= ~FSXM;
1108 mcbsp_cfg.xcr2 &= ~XDATDLY(3);
1109 mcbsp_cfg.rcr2 &= ~RDATDLY(3);
1110 }
1111
1112 mcbsp_cfg.xcr2 &= ~XPHASE;
1113 mcbsp_cfg.rcr2 &= ~RPHASE;
1114
1115 omap_mcbsp_config(id, &mcbsp_cfg);
1116}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001117EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001118
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001119#ifdef CONFIG_ARCH_OMAP34XX
1120#define max_thres(m) (mcbsp->pdata->buffer_size)
1121#define valid_threshold(m, val) ((val) <= max_thres(m))
1122#define THRESHOLD_PROP_BUILDER(prop) \
1123static ssize_t prop##_show(struct device *dev, \
1124 struct device_attribute *attr, char *buf) \
1125{ \
1126 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1127 \
1128 return sprintf(buf, "%u\n", mcbsp->prop); \
1129} \
1130 \
1131static ssize_t prop##_store(struct device *dev, \
1132 struct device_attribute *attr, \
1133 const char *buf, size_t size) \
1134{ \
1135 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1136 unsigned long val; \
1137 int status; \
1138 \
1139 status = strict_strtoul(buf, 0, &val); \
1140 if (status) \
1141 return status; \
1142 \
1143 if (!valid_threshold(mcbsp, val)) \
1144 return -EDOM; \
1145 \
1146 mcbsp->prop = val; \
1147 return size; \
1148} \
1149 \
1150static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
1151
1152THRESHOLD_PROP_BUILDER(max_tx_thres);
1153THRESHOLD_PROP_BUILDER(max_rx_thres);
1154
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001155static ssize_t dma_op_mode_show(struct device *dev,
1156 struct device_attribute *attr, char *buf)
1157{
1158 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1159 int dma_op_mode;
1160
1161 spin_lock_irq(&mcbsp->lock);
1162 dma_op_mode = mcbsp->dma_op_mode;
1163 spin_unlock_irq(&mcbsp->lock);
1164
1165 return sprintf(buf, "current mode: %d\n"
1166 "possible mode values are:\n"
1167 "%d - %s\n"
1168 "%d - %s\n"
1169 "%d - %s\n",
1170 dma_op_mode,
1171 MCBSP_DMA_MODE_ELEMENT, "element mode",
1172 MCBSP_DMA_MODE_THRESHOLD, "threshold mode",
1173 MCBSP_DMA_MODE_FRAME, "frame mode");
1174}
1175
1176static ssize_t dma_op_mode_store(struct device *dev,
1177 struct device_attribute *attr,
1178 const char *buf, size_t size)
1179{
1180 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1181 unsigned long val;
1182 int status;
1183
1184 status = strict_strtoul(buf, 0, &val);
1185 if (status)
1186 return status;
1187
1188 spin_lock_irq(&mcbsp->lock);
1189
1190 if (!mcbsp->free) {
1191 size = -EBUSY;
1192 goto unlock;
1193 }
1194
1195 if (val > MCBSP_DMA_MODE_FRAME || val < MCBSP_DMA_MODE_ELEMENT) {
1196 size = -EINVAL;
1197 goto unlock;
1198 }
1199
1200 mcbsp->dma_op_mode = val;
1201
1202unlock:
1203 spin_unlock_irq(&mcbsp->lock);
1204
1205 return size;
1206}
1207
1208static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
1209
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001210static const struct attribute *additional_attrs[] = {
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001211 &dev_attr_max_tx_thres.attr,
1212 &dev_attr_max_rx_thres.attr,
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001213 &dev_attr_dma_op_mode.attr,
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001214 NULL,
1215};
1216
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001217static const struct attribute_group additional_attr_group = {
1218 .attrs = (struct attribute **)additional_attrs,
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001219};
1220
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001221static inline int __devinit omap_additional_add(struct device *dev)
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001222{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001223 return sysfs_create_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001224}
1225
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001226static inline void __devexit omap_additional_remove(struct device *dev)
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001227{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001228 sysfs_remove_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001229}
1230
1231static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
1232{
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001233 mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001234 if (cpu_is_omap34xx()) {
1235 mcbsp->max_tx_thres = max_thres(mcbsp);
1236 mcbsp->max_rx_thres = max_thres(mcbsp);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001237 /*
1238 * REVISIT: Set dmap_op_mode to THRESHOLD as default
1239 * for mcbsp2 instances.
1240 */
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001241 if (omap_additional_add(mcbsp->dev))
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001242 dev_warn(mcbsp->dev,
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001243 "Unable to create additional controls\n");
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001244 } else {
1245 mcbsp->max_tx_thres = -EINVAL;
1246 mcbsp->max_rx_thres = -EINVAL;
1247 }
1248}
1249
1250static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
1251{
1252 if (cpu_is_omap34xx())
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001253 omap_additional_remove(mcbsp->dev);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001254}
1255#else
1256static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
1257static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
1258#endif /* CONFIG_ARCH_OMAP34XX */
1259
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001260/*
1261 * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
1262 * 730 has only 2 McBSP, and both of them are MPU peripherals.
1263 */
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001264static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001265{
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001266 struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001267 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001268 int id = pdev->id - 1;
1269 int ret = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001270
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001271 if (!pdata) {
1272 dev_err(&pdev->dev, "McBSP device initialized without"
1273 "platform data\n");
1274 ret = -EINVAL;
1275 goto exit;
1276 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001277
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001278 dev_dbg(&pdev->dev, "Initializing OMAP McBSP (%d).\n", pdev->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001279
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001280 if (id >= omap_mcbsp_count) {
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001281 dev_err(&pdev->dev, "Invalid McBSP device id (%d)\n", id);
1282 ret = -EINVAL;
1283 goto exit;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001284 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001285
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001286 mcbsp = kzalloc(sizeof(struct omap_mcbsp), GFP_KERNEL);
1287 if (!mcbsp) {
1288 ret = -ENOMEM;
1289 goto exit;
1290 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001291
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001292 spin_lock_init(&mcbsp->lock);
1293 mcbsp->id = id + 1;
1294 mcbsp->free = 1;
1295 mcbsp->dma_tx_lch = -1;
1296 mcbsp->dma_rx_lch = -1;
1297
1298 mcbsp->phys_base = pdata->phys_base;
1299 mcbsp->io_base = ioremap(pdata->phys_base, SZ_4K);
1300 if (!mcbsp->io_base) {
Russell Kingd592dd12008-09-04 14:25:42 +01001301 ret = -ENOMEM;
1302 goto err_ioremap;
1303 }
1304
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001305 /* Default I/O is IRQ based */
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001306 mcbsp->io_type = OMAP_MCBSP_IRQ_IO;
1307 mcbsp->tx_irq = pdata->tx_irq;
1308 mcbsp->rx_irq = pdata->rx_irq;
1309 mcbsp->dma_rx_sync = pdata->dma_rx_sync;
1310 mcbsp->dma_tx_sync = pdata->dma_tx_sync;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001311
Russell Kingb820ce42009-01-23 10:26:46 +00001312 mcbsp->iclk = clk_get(&pdev->dev, "ick");
1313 if (IS_ERR(mcbsp->iclk)) {
1314 ret = PTR_ERR(mcbsp->iclk);
1315 dev_err(&pdev->dev, "unable to get ick: %d\n", ret);
1316 goto err_iclk;
1317 }
Stanley.Miao06151152009-01-29 08:57:12 -08001318
Russell Kingb820ce42009-01-23 10:26:46 +00001319 mcbsp->fclk = clk_get(&pdev->dev, "fck");
1320 if (IS_ERR(mcbsp->fclk)) {
1321 ret = PTR_ERR(mcbsp->fclk);
1322 dev_err(&pdev->dev, "unable to get fck: %d\n", ret);
1323 goto err_fclk;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001324 }
1325
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001326 mcbsp->pdata = pdata;
1327 mcbsp->dev = &pdev->dev;
Russell Kingb820ce42009-01-23 10:26:46 +00001328 mcbsp_ptr[id] = mcbsp;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001329 platform_set_drvdata(pdev, mcbsp);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001330
1331 /* Initialize mcbsp properties for OMAP34XX if needed / applicable */
1332 omap34xx_device_init(mcbsp);
1333
Russell Kingd592dd12008-09-04 14:25:42 +01001334 return 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001335
Russell Kingb820ce42009-01-23 10:26:46 +00001336err_fclk:
1337 clk_put(mcbsp->iclk);
1338err_iclk:
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001339 iounmap(mcbsp->io_base);
Russell Kingd592dd12008-09-04 14:25:42 +01001340err_ioremap:
Russell Kingb820ce42009-01-23 10:26:46 +00001341 kfree(mcbsp);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001342exit:
1343 return ret;
1344}
1345
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001346static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001347{
1348 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
1349
1350 platform_set_drvdata(pdev, NULL);
1351 if (mcbsp) {
1352
1353 if (mcbsp->pdata && mcbsp->pdata->ops &&
1354 mcbsp->pdata->ops->free)
1355 mcbsp->pdata->ops->free(mcbsp->id);
1356
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001357 omap34xx_device_exit(mcbsp);
1358
Russell Kingb820ce42009-01-23 10:26:46 +00001359 clk_disable(mcbsp->fclk);
1360 clk_disable(mcbsp->iclk);
1361 clk_put(mcbsp->fclk);
1362 clk_put(mcbsp->iclk);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001363
Russell Kingd592dd12008-09-04 14:25:42 +01001364 iounmap(mcbsp->io_base);
1365
Russell Kingb820ce42009-01-23 10:26:46 +00001366 mcbsp->fclk = NULL;
1367 mcbsp->iclk = NULL;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001368 mcbsp->free = 0;
1369 mcbsp->dev = NULL;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001370 }
1371
1372 return 0;
1373}
1374
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001375static struct platform_driver omap_mcbsp_driver = {
1376 .probe = omap_mcbsp_probe,
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001377 .remove = __devexit_p(omap_mcbsp_remove),
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001378 .driver = {
1379 .name = "omap-mcbsp",
1380 },
1381};
1382
1383int __init omap_mcbsp_init(void)
1384{
1385 /* Register the McBSP driver */
1386 return platform_driver_register(&omap_mcbsp_driver);
1387}