blob: 61e440a0f7cd0c25dcad35f9a9bbf55145399290 [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
Tony Lindgrence491cf2009-10-20 09:40:47 -070027#include <plat/dma.h>
28#include <plat/mcbsp.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010029
Chandra Shekharb4b58f52008-10-08 10:01:39 +030030struct omap_mcbsp **mcbsp_ptr;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080031int omap_mcbsp_count, omap_mcbsp_cache_size;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +030032
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080033void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030034{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080035 if (cpu_class_is_omap1()) {
36 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)] = (u16)val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080037 __raw_writew((u16)val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080038 } else if (cpu_is_omap2420()) {
39 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)] = (u16)val;
40 __raw_writew((u16)val, mcbsp->io_base + reg);
41 } else {
42 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)] = val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080043 __raw_writel(val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080044 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030045}
46
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080047int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030048{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080049 if (cpu_class_is_omap1()) {
50 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
51 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)];
52 } else if (cpu_is_omap2420()) {
53 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
54 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)];
55 } else {
56 return !from_cache ? __raw_readl(mcbsp->io_base + reg) :
57 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)];
58 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030059}
60
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080061#define MCBSP_READ(mcbsp, reg) \
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080062 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080063#define MCBSP_WRITE(mcbsp, reg, val) \
64 omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080065#define MCBSP_READ_CACHE(mcbsp, reg) \
66 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030067
68#define omap_mcbsp_check_valid_id(id) (id < omap_mcbsp_count)
69#define id_to_mcbsp_ptr(id) mcbsp_ptr[id];
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010070
71static void omap_mcbsp_dump_reg(u8 id)
72{
Chandra Shekharb4b58f52008-10-08 10:01:39 +030073 struct omap_mcbsp *mcbsp = id_to_mcbsp_ptr(id);
74
75 dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
76 dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080077 MCBSP_READ(mcbsp, DRR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030078 dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080079 MCBSP_READ(mcbsp, DRR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030080 dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080081 MCBSP_READ(mcbsp, DXR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030082 dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080083 MCBSP_READ(mcbsp, DXR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030084 dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080085 MCBSP_READ(mcbsp, SPCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030086 dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080087 MCBSP_READ(mcbsp, SPCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030088 dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080089 MCBSP_READ(mcbsp, RCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030090 dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080091 MCBSP_READ(mcbsp, RCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030092 dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080093 MCBSP_READ(mcbsp, XCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030094 dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080095 MCBSP_READ(mcbsp, XCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030096 dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080097 MCBSP_READ(mcbsp, SRGR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030098 dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080099 MCBSP_READ(mcbsp, SRGR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300100 dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800101 MCBSP_READ(mcbsp, PCR0));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300102 dev_dbg(mcbsp->dev, "***********************\n");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100103}
104
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700105static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100106{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400107 struct omap_mcbsp *mcbsp_tx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700108 u16 irqst_spcr2;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100109
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800110 irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700111 dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100112
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700113 if (irqst_spcr2 & XSYNC_ERR) {
114 dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n",
115 irqst_spcr2);
116 /* Writing zero to XSYNC_ERR clears the IRQ */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800117 MCBSP_WRITE(mcbsp_tx, SPCR2, irqst_spcr2 & ~(XSYNC_ERR));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700118 } else {
119 complete(&mcbsp_tx->tx_irq_completion);
120 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300121
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100122 return IRQ_HANDLED;
123}
124
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700125static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100126{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400127 struct omap_mcbsp *mcbsp_rx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700128 u16 irqst_spcr1;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100129
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800130 irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700131 dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100132
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700133 if (irqst_spcr1 & RSYNC_ERR) {
134 dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n",
135 irqst_spcr1);
136 /* Writing zero to RSYNC_ERR clears the IRQ */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800137 MCBSP_WRITE(mcbsp_rx, SPCR1, irqst_spcr1 & ~(RSYNC_ERR));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700138 } else {
139 complete(&mcbsp_rx->tx_irq_completion);
140 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300141
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100142 return IRQ_HANDLED;
143}
144
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100145static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data)
146{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400147 struct omap_mcbsp *mcbsp_dma_tx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100148
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300149 dev_dbg(mcbsp_dma_tx->dev, "TX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800150 MCBSP_READ(mcbsp_dma_tx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100151
152 /* We can free the channels */
153 omap_free_dma(mcbsp_dma_tx->dma_tx_lch);
154 mcbsp_dma_tx->dma_tx_lch = -1;
155
156 complete(&mcbsp_dma_tx->tx_dma_completion);
157}
158
159static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data)
160{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400161 struct omap_mcbsp *mcbsp_dma_rx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100162
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300163 dev_dbg(mcbsp_dma_rx->dev, "RX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800164 MCBSP_READ(mcbsp_dma_rx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100165
166 /* We can free the channels */
167 omap_free_dma(mcbsp_dma_rx->dma_rx_lch);
168 mcbsp_dma_rx->dma_rx_lch = -1;
169
170 complete(&mcbsp_dma_rx->rx_dma_completion);
171}
172
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100173/*
174 * omap_mcbsp_config simply write a config to the
175 * appropriate McBSP.
176 * You either call this function or set the McBSP registers
177 * by yourself before calling omap_mcbsp_start().
178 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300179void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100180{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300181 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100182
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300183 if (!omap_mcbsp_check_valid_id(id)) {
184 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
185 return;
186 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300187 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300188
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300189 dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
190 mcbsp->id, mcbsp->phys_base);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100191
192 /* We write the given config */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800193 MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
194 MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
195 MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
196 MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
197 MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
198 MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
199 MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
200 MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
201 MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
202 MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
203 MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
Syed Rafiuddina5b92cc2009-07-28 18:57:10 +0530204 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800205 MCBSP_WRITE(mcbsp, XCCR, config->xccr);
206 MCBSP_WRITE(mcbsp, RCCR, config->rccr);
Tony Lindgren3127f8f2009-01-15 13:09:54 +0200207 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100208}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300209EXPORT_SYMBOL(omap_mcbsp_config);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100210
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -0800211#ifdef CONFIG_ARCH_OMAP3
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300212/*
213 * omap_mcbsp_set_tx_threshold configures how to deal
214 * with transmit threshold. the threshold value and handler can be
215 * configure in here.
216 */
217void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
218{
219 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300220
221 if (!cpu_is_omap34xx())
222 return;
223
224 if (!omap_mcbsp_check_valid_id(id)) {
225 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
226 return;
227 }
228 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300229
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800230 MCBSP_WRITE(mcbsp, THRSH2, threshold);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300231}
232EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
233
234/*
235 * omap_mcbsp_set_rx_threshold configures how to deal
236 * with receive threshold. the threshold value and handler can be
237 * configure in here.
238 */
239void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
240{
241 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300242
243 if (!cpu_is_omap34xx())
244 return;
245
246 if (!omap_mcbsp_check_valid_id(id)) {
247 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
248 return;
249 }
250 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300251
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800252 MCBSP_WRITE(mcbsp, THRSH1, threshold);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300253}
254EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
Eduardo Valentina1a56f52009-08-20 16:18:11 +0300255
256/*
257 * omap_mcbsp_get_max_tx_thres just return the current configured
258 * maximum threshold for transmission
259 */
260u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
261{
262 struct omap_mcbsp *mcbsp;
263
264 if (!omap_mcbsp_check_valid_id(id)) {
265 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
266 return -ENODEV;
267 }
268 mcbsp = id_to_mcbsp_ptr(id);
269
270 return mcbsp->max_tx_thres;
271}
272EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
273
274/*
275 * omap_mcbsp_get_max_rx_thres just return the current configured
276 * maximum threshold for reception
277 */
278u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
279{
280 struct omap_mcbsp *mcbsp;
281
282 if (!omap_mcbsp_check_valid_id(id)) {
283 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
284 return -ENODEV;
285 }
286 mcbsp = id_to_mcbsp_ptr(id);
287
288 return mcbsp->max_rx_thres;
289}
290EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300291
292/*
293 * omap_mcbsp_get_dma_op_mode just return the current configured
294 * operating mode for the mcbsp channel
295 */
296int omap_mcbsp_get_dma_op_mode(unsigned int id)
297{
298 struct omap_mcbsp *mcbsp;
299 int dma_op_mode;
300
301 if (!omap_mcbsp_check_valid_id(id)) {
302 printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
303 return -ENODEV;
304 }
305 mcbsp = id_to_mcbsp_ptr(id);
306
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300307 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300308
309 return dma_op_mode;
310}
311EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300312
313static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
314{
315 /*
316 * Enable wakup behavior, smart idle and all wakeups
317 * REVISIT: some wakeups may be unnecessary
318 */
319 if (cpu_is_omap34xx()) {
320 u16 syscon;
321
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800322 syscon = MCBSP_READ(mcbsp, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300323 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eduardo Valentind99a7452009-08-20 16:18:18 +0300324
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300325 if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
326 syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
327 CLOCKACTIVITY(0x02));
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800328 MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300329 } else {
Eduardo Valentind99a7452009-08-20 16:18:18 +0300330 syscon |= SIDLEMODE(0x01);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300331 }
Eduardo Valentind99a7452009-08-20 16:18:18 +0300332
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800333 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300334 }
335}
336
337static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
338{
339 /*
340 * Disable wakup behavior, smart idle and all wakeups
341 */
342 if (cpu_is_omap34xx()) {
343 u16 syscon;
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300344
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800345 syscon = MCBSP_READ(mcbsp, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300346 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300347 /*
348 * HW bug workaround - If no_idle mode is taken, we need to
349 * go to smart_idle before going to always_idle, or the
350 * device will not hit retention anymore.
351 */
352 syscon |= SIDLEMODE(0x02);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800353 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300354
355 syscon &= ~(SIDLEMODE(0x03));
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800356 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300357
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800358 MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300359 }
360}
361#else
362static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
363static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300364#endif
365
Tony Lindgren120db2c2006-04-02 17:46:27 +0100366/*
367 * We can choose between IRQ based or polled IO.
368 * This needs to be called before omap_mcbsp_request().
369 */
370int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type)
371{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300372 struct omap_mcbsp *mcbsp;
373
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300374 if (!omap_mcbsp_check_valid_id(id)) {
375 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
376 return -ENODEV;
377 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300378 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100379
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300380 spin_lock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100381
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300382 if (!mcbsp->free) {
383 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
384 mcbsp->id);
385 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100386 return -EINVAL;
387 }
388
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300389 mcbsp->io_type = io_type;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100390
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300391 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100392
393 return 0;
394}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300395EXPORT_SYMBOL(omap_mcbsp_set_io_type);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100396
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100397int omap_mcbsp_request(unsigned int id)
398{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300399 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800400 void *reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100401 int err;
402
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300403 if (!omap_mcbsp_check_valid_id(id)) {
404 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
405 return -ENODEV;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100406 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300407 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300408
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800409 reg_cache = kzalloc(omap_mcbsp_cache_size, GFP_KERNEL);
410 if (!reg_cache) {
411 return -ENOMEM;
412 }
413
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300414 spin_lock(&mcbsp->lock);
415 if (!mcbsp->free) {
416 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
417 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800418 err = -EBUSY;
419 goto err_kfree;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100420 }
421
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300422 mcbsp->free = 0;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800423 mcbsp->reg_cache = reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300424 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100425
Russell Kingb820ce42009-01-23 10:26:46 +0000426 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
427 mcbsp->pdata->ops->request(id);
428
429 clk_enable(mcbsp->iclk);
430 clk_enable(mcbsp->fclk);
431
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300432 /* Do procedure specific to omap34xx arch, if applicable */
433 omap34xx_mcbsp_request(mcbsp);
434
Jarkko Nikula5a070552008-10-08 10:01:41 +0300435 /*
436 * Make sure that transmitter, receiver and sample-rate generator are
437 * not running before activating IRQs.
438 */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800439 MCBSP_WRITE(mcbsp, SPCR1, 0);
440 MCBSP_WRITE(mcbsp, SPCR2, 0);
Jarkko Nikula5a070552008-10-08 10:01:41 +0300441
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300442 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
Tony Lindgren120db2c2006-04-02 17:46:27 +0100443 /* We need to get IRQs here */
Jarkko Nikula5a070552008-10-08 10:01:41 +0300444 init_completion(&mcbsp->tx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300445 err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
446 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100447 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300448 dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
449 "for McBSP%d\n", mcbsp->tx_irq,
450 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800451 goto err_clk_disable;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100452 }
453
Jarkko Nikula5a070552008-10-08 10:01:41 +0300454 init_completion(&mcbsp->rx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300455 err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler,
456 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100457 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300458 dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
459 "for McBSP%d\n", mcbsp->rx_irq,
460 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800461 goto err_free_irq;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100462 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100463 }
464
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100465 return 0;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800466err_free_irq:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800467 free_irq(mcbsp->tx_irq, (void *)mcbsp);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800468err_clk_disable:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800469 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800470 mcbsp->pdata->ops->free(id);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800471
472 /* Do procedure specific to omap34xx arch, if applicable */
473 omap34xx_mcbsp_free(mcbsp);
474
475 clk_disable(mcbsp->fclk);
476 clk_disable(mcbsp->iclk);
477
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800478 spin_lock(&mcbsp->lock);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800479 mcbsp->free = 1;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800480 mcbsp->reg_cache = NULL;
481err_kfree:
482 spin_unlock(&mcbsp->lock);
483 kfree(reg_cache);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800484
485 return err;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100486}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300487EXPORT_SYMBOL(omap_mcbsp_request);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100488
489void omap_mcbsp_free(unsigned int id)
490{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300491 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800492 void *reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300493
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300494 if (!omap_mcbsp_check_valid_id(id)) {
495 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100496 return;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100497 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300498 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100499
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300500 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
501 mcbsp->pdata->ops->free(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300502
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300503 /* Do procedure specific to omap34xx arch, if applicable */
504 omap34xx_mcbsp_free(mcbsp);
505
Russell Kingb820ce42009-01-23 10:26:46 +0000506 clk_disable(mcbsp->fclk);
507 clk_disable(mcbsp->iclk);
508
509 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
510 /* Free IRQs */
511 free_irq(mcbsp->rx_irq, (void *)mcbsp);
512 free_irq(mcbsp->tx_irq, (void *)mcbsp);
513 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100514
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800515 reg_cache = mcbsp->reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100516
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800517 spin_lock(&mcbsp->lock);
518 if (mcbsp->free)
519 dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
520 else
521 mcbsp->free = 1;
522 mcbsp->reg_cache = NULL;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300523 spin_unlock(&mcbsp->lock);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800524
525 if (reg_cache)
526 kfree(reg_cache);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100527}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300528EXPORT_SYMBOL(omap_mcbsp_free);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100529
530/*
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300531 * Here we start the McBSP, by enabling transmitter, receiver or both.
532 * If no transmitter or receiver is active prior calling, then sample-rate
533 * generator and frame sync are started.
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100534 */
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300535void omap_mcbsp_start(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100536{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300537 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300538 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100539 u16 w;
540
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300541 if (!omap_mcbsp_check_valid_id(id)) {
542 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100543 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300544 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300545 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100546
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800547 mcbsp->rx_word_length = (MCBSP_READ(mcbsp, RCR1) >> 5) & 0x7;
548 mcbsp->tx_word_length = (MCBSP_READ(mcbsp, XCR1) >> 5) & 0x7;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100549
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800550 idle = !((MCBSP_READ(mcbsp, SPCR2) | MCBSP_READ(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300551
552 if (idle) {
553 /* Start the sample generator */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800554 w = MCBSP_READ(mcbsp, SPCR2);
555 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300556 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100557
558 /* Enable transmitter and receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300559 tx &= 1;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800560 w = MCBSP_READ(mcbsp, SPCR2);
561 MCBSP_WRITE(mcbsp, SPCR2, w | tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100562
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300563 rx &= 1;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800564 w = MCBSP_READ(mcbsp, SPCR1);
565 MCBSP_WRITE(mcbsp, SPCR1, w | rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100566
Eduardo Valentin44a63112009-08-20 16:18:09 +0300567 /*
568 * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
569 * REVISIT: 100us may give enough time for two CLKSRG, however
570 * due to some unknown PM related, clock gating etc. reason it
571 * is now at 500us.
572 */
573 udelay(500);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100574
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300575 if (idle) {
576 /* Start frame sync */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800577 w = MCBSP_READ(mcbsp, SPCR2);
578 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300579 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100580
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300581 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
582 /* Release the transmitter and receiver */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800583 w = MCBSP_READ(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300584 w &= ~(tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800585 MCBSP_WRITE(mcbsp, XCCR, w);
586 w = MCBSP_READ(mcbsp, RCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300587 w &= ~(rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800588 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300589 }
590
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100591 /* Dump McBSP Regs */
592 omap_mcbsp_dump_reg(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100593}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300594EXPORT_SYMBOL(omap_mcbsp_start);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100595
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300596void omap_mcbsp_stop(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100597{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300598 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300599 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100600 u16 w;
601
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300602 if (!omap_mcbsp_check_valid_id(id)) {
603 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100604 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300605 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100606
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300607 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100608
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300609 /* Reset transmitter */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300610 tx &= 1;
611 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800612 w = MCBSP_READ(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300613 w |= (tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800614 MCBSP_WRITE(mcbsp, XCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300615 }
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800616 w = MCBSP_READ(mcbsp, SPCR2);
617 MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100618
619 /* Reset receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300620 rx &= 1;
621 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800622 w = MCBSP_READ(mcbsp, RCCR);
Jarkko Nikulaa93d4ed2009-10-14 09:56:35 -0700623 w |= (rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800624 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300625 }
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800626 w = MCBSP_READ(mcbsp, SPCR1);
627 MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100628
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800629 idle = !((MCBSP_READ(mcbsp, SPCR2) | MCBSP_READ(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300630
631 if (idle) {
632 /* Reset the sample rate generator */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800633 w = MCBSP_READ(mcbsp, SPCR2);
634 MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300635 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100636}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300637EXPORT_SYMBOL(omap_mcbsp_stop);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100638
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100639/* polled mcbsp i/o operations */
640int omap_mcbsp_pollwrite(unsigned int id, u16 buf)
641{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300642 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300643
644 if (!omap_mcbsp_check_valid_id(id)) {
645 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
646 return -ENODEV;
647 }
648
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300649 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300650
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800651 MCBSP_WRITE(mcbsp, DXR1, buf);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100652 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800653 if (MCBSP_READ(mcbsp, SPCR2) & XSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100654 /* clear error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800655 MCBSP_WRITE(mcbsp, SPCR2,
656 MCBSP_READ(mcbsp, SPCR2) & (~XSYNC_ERR));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100657 /* resend */
658 return -1;
659 } else {
660 /* wait for transmit confirmation */
661 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800662 while (!(MCBSP_READ(mcbsp, SPCR2) & XRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100663 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800664 MCBSP_WRITE(mcbsp, SPCR2,
665 MCBSP_READ(mcbsp, SPCR2) & (~XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100666 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800667 MCBSP_WRITE(mcbsp, SPCR2,
668 MCBSP_READ(mcbsp, SPCR2) | (XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100669 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300670 dev_err(mcbsp->dev, "Could not write to"
671 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100672 return -2;
673 }
674 }
675 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300676
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100677 return 0;
678}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300679EXPORT_SYMBOL(omap_mcbsp_pollwrite);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100680
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300681int omap_mcbsp_pollread(unsigned int id, u16 *buf)
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100682{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300683 struct omap_mcbsp *mcbsp;
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
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100691 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800692 if (MCBSP_READ(mcbsp, SPCR1) & RSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100693 /* clear error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800694 MCBSP_WRITE(mcbsp, SPCR1,
695 MCBSP_READ(mcbsp, SPCR1) & (~RSYNC_ERR));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100696 /* resend */
697 return -1;
698 } else {
699 /* wait for recieve confirmation */
700 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800701 while (!(MCBSP_READ(mcbsp, SPCR1) & RRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100702 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800703 MCBSP_WRITE(mcbsp, SPCR1,
704 MCBSP_READ(mcbsp, SPCR1) & (~RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100705 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800706 MCBSP_WRITE(mcbsp, SPCR1,
707 MCBSP_READ(mcbsp, SPCR1) | (RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100708 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300709 dev_err(mcbsp->dev, "Could not read from"
710 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100711 return -2;
712 }
713 }
714 }
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800715 *buf = MCBSP_READ(mcbsp, DRR1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300716
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100717 return 0;
718}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300719EXPORT_SYMBOL(omap_mcbsp_pollread);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100720
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100721/*
722 * IRQ based word transmission.
723 */
724void omap_mcbsp_xmit_word(unsigned int id, u32 word)
725{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300726 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300727 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100728
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300729 if (!omap_mcbsp_check_valid_id(id)) {
730 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100731 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300732 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100733
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300734 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300735 word_length = mcbsp->tx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100736
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300737 wait_for_completion(&mcbsp->tx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100738
739 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800740 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
741 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100742}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300743EXPORT_SYMBOL(omap_mcbsp_xmit_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100744
745u32 omap_mcbsp_recv_word(unsigned int id)
746{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300747 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100748 u16 word_lsb, word_msb = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300749 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100750
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300751 if (!omap_mcbsp_check_valid_id(id)) {
752 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
753 return -ENODEV;
754 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300755 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100756
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300757 word_length = mcbsp->rx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100758
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300759 wait_for_completion(&mcbsp->rx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100760
761 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800762 word_msb = MCBSP_READ(mcbsp, DRR2);
763 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100764
765 return (word_lsb | (word_msb << 16));
766}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300767EXPORT_SYMBOL(omap_mcbsp_recv_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100768
Tony Lindgren120db2c2006-04-02 17:46:27 +0100769int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word)
770{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300771 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300772 omap_mcbsp_word_length tx_word_length;
773 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100774 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
775
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300776 if (!omap_mcbsp_check_valid_id(id)) {
777 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
778 return -ENODEV;
779 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300780 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300781 tx_word_length = mcbsp->tx_word_length;
782 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300783
Tony Lindgren120db2c2006-04-02 17:46:27 +0100784 if (tx_word_length != rx_word_length)
785 return -EINVAL;
786
787 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800788 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100789 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800790 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100791 if (attempts++ > 1000) {
792 /* We must reset the transmitter */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800793 MCBSP_WRITE(mcbsp, SPCR2, spcr2 & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +0100794 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800795 MCBSP_WRITE(mcbsp, SPCR2, spcr2 | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100796 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300797 dev_err(mcbsp->dev, "McBSP%d transmitter not "
798 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100799 return -EAGAIN;
800 }
801 }
802
803 /* Now we can push the data */
804 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800805 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
806 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100807
808 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800809 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100810 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800811 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100812 if (attempts++ > 1000) {
813 /* We must reset the receiver */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800814 MCBSP_WRITE(mcbsp, SPCR1, spcr1 & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +0100815 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800816 MCBSP_WRITE(mcbsp, SPCR1, spcr1 | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100817 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300818 dev_err(mcbsp->dev, "McBSP%d receiver not "
819 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100820 return -EAGAIN;
821 }
822 }
823
824 /* Receiver is ready, let's read the dummy data */
825 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800826 word_msb = MCBSP_READ(mcbsp, DRR2);
827 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100828
829 return 0;
830}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300831EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100832
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300833int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 *word)
Tony Lindgren120db2c2006-04-02 17:46:27 +0100834{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300835 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100836 u32 clock_word = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300837 omap_mcbsp_word_length tx_word_length;
838 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100839 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
840
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300841 if (!omap_mcbsp_check_valid_id(id)) {
842 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
843 return -ENODEV;
844 }
845
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300846 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300847
848 tx_word_length = mcbsp->tx_word_length;
849 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300850
Tony Lindgren120db2c2006-04-02 17:46:27 +0100851 if (tx_word_length != rx_word_length)
852 return -EINVAL;
853
854 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800855 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100856 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800857 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100858 if (attempts++ > 1000) {
859 /* We must reset the transmitter */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800860 MCBSP_WRITE(mcbsp, SPCR2, spcr2 & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +0100861 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800862 MCBSP_WRITE(mcbsp, SPCR2, spcr2 | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100863 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300864 dev_err(mcbsp->dev, "McBSP%d transmitter not "
865 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100866 return -EAGAIN;
867 }
868 }
869
870 /* We first need to enable the bus clock */
871 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800872 MCBSP_WRITE(mcbsp, DXR2, clock_word >> 16);
873 MCBSP_WRITE(mcbsp, DXR1, clock_word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100874
875 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800876 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100877 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800878 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100879 if (attempts++ > 1000) {
880 /* We must reset the receiver */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800881 MCBSP_WRITE(mcbsp, SPCR1, spcr1 & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +0100882 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800883 MCBSP_WRITE(mcbsp, SPCR1, spcr1 | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100884 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300885 dev_err(mcbsp->dev, "McBSP%d receiver not "
886 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100887 return -EAGAIN;
888 }
889 }
890
891 /* Receiver is ready, there is something for us */
892 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800893 word_msb = MCBSP_READ(mcbsp, DRR2);
894 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100895
896 word[0] = (word_lsb | (word_msb << 16));
897
898 return 0;
899}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300900EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100901
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100902/*
903 * Simple DMA based buffer rx/tx routines.
904 * Nothing fancy, just a single buffer tx/rx through DMA.
905 * The DMA resources are released once the transfer is done.
906 * For anything fancier, you should use your own customized DMA
907 * routines and callbacks.
908 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300909int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer,
910 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100911{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300912 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100913 int dma_tx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100914 int src_port = 0;
915 int dest_port = 0;
916 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100917
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300918 if (!omap_mcbsp_check_valid_id(id)) {
919 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
920 return -ENODEV;
921 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300922 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100923
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300924 if (omap_request_dma(mcbsp->dma_tx_sync, "McBSP TX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300925 omap_mcbsp_tx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300926 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300927 &dma_tx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300928 dev_err(mcbsp->dev, " Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300929 "McBSP%d TX. Trying IRQ based TX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300930 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100931 return -EAGAIN;
932 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300933 mcbsp->dma_tx_lch = dma_tx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100934
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300935 dev_err(mcbsp->dev, "McBSP%d TX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300936 dma_tx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100937
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300938 init_completion(&mcbsp->tx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100939
Tony Lindgren120db2c2006-04-02 17:46:27 +0100940 if (cpu_class_is_omap1()) {
941 src_port = OMAP_DMA_PORT_TIPB;
942 dest_port = OMAP_DMA_PORT_EMIFF;
943 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300944 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300945 sync_dev = mcbsp->dma_tx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100946
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300947 omap_set_dma_transfer_params(mcbsp->dma_tx_lch,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100948 OMAP_DMA_DATA_TYPE_S16,
949 length >> 1, 1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000950 OMAP_DMA_SYNC_ELEMENT,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100951 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100952
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300953 omap_set_dma_dest_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100954 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100955 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300956 mcbsp->phys_base + OMAP_MCBSP_REG_DXR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000957 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100958
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300959 omap_set_dma_src_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100960 dest_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100961 OMAP_DMA_AMODE_POST_INC,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000962 buffer,
963 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100964
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300965 omap_start_dma(mcbsp->dma_tx_lch);
966 wait_for_completion(&mcbsp->tx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300967
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100968 return 0;
969}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300970EXPORT_SYMBOL(omap_mcbsp_xmit_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100971
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300972int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer,
973 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100974{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300975 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100976 int dma_rx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100977 int src_port = 0;
978 int dest_port = 0;
979 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100980
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300981 if (!omap_mcbsp_check_valid_id(id)) {
982 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
983 return -ENODEV;
984 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300985 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100986
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300987 if (omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300988 omap_mcbsp_rx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300989 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300990 &dma_rx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300991 dev_err(mcbsp->dev, "Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300992 "McBSP%d RX. Trying IRQ based RX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300993 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100994 return -EAGAIN;
995 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300996 mcbsp->dma_rx_lch = dma_rx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100997
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300998 dev_err(mcbsp->dev, "McBSP%d RX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300999 dma_rx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001000
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001001 init_completion(&mcbsp->rx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001002
Tony Lindgren120db2c2006-04-02 17:46:27 +01001003 if (cpu_class_is_omap1()) {
1004 src_port = OMAP_DMA_PORT_TIPB;
1005 dest_port = OMAP_DMA_PORT_EMIFF;
1006 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001007 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001008 sync_dev = mcbsp->dma_rx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001009
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001010 omap_set_dma_transfer_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001011 OMAP_DMA_DATA_TYPE_S16,
1012 length >> 1, 1,
1013 OMAP_DMA_SYNC_ELEMENT,
1014 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001015
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001016 omap_set_dma_src_params(mcbsp->dma_rx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001017 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001018 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001019 mcbsp->phys_base + OMAP_MCBSP_REG_DRR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001020 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001021
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001022 omap_set_dma_dest_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001023 dest_port,
1024 OMAP_DMA_AMODE_POST_INC,
1025 buffer,
1026 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001027
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001028 omap_start_dma(mcbsp->dma_rx_lch);
1029 wait_for_completion(&mcbsp->rx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001030
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001031 return 0;
1032}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001033EXPORT_SYMBOL(omap_mcbsp_recv_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001034
1035/*
1036 * SPI wrapper.
1037 * Since SPI setup is much simpler than the generic McBSP one,
1038 * this wrapper just need an omap_mcbsp_spi_cfg structure as an input.
1039 * Once this is done, you can call omap_mcbsp_start().
1040 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001041void omap_mcbsp_set_spi_mode(unsigned int id,
1042 const struct omap_mcbsp_spi_cfg *spi_cfg)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001043{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001044 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001045 struct omap_mcbsp_reg_cfg mcbsp_cfg;
1046
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001047 if (!omap_mcbsp_check_valid_id(id)) {
1048 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001049 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001050 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001051 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001052
1053 memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg));
1054
1055 /* SPI has only one frame */
1056 mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0));
1057 mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0));
1058
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001059 /* Clock stop mode */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001060 if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY)
1061 mcbsp_cfg.spcr1 |= (1 << 12);
1062 else
1063 mcbsp_cfg.spcr1 |= (3 << 11);
1064
1065 /* Set clock parities */
1066 if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1067 mcbsp_cfg.pcr0 |= CLKRP;
1068 else
1069 mcbsp_cfg.pcr0 &= ~CLKRP;
1070
1071 if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1072 mcbsp_cfg.pcr0 &= ~CLKXP;
1073 else
1074 mcbsp_cfg.pcr0 |= CLKXP;
1075
1076 /* Set SCLKME to 0 and CLKSM to 1 */
1077 mcbsp_cfg.pcr0 &= ~SCLKME;
1078 mcbsp_cfg.srgr2 |= CLKSM;
1079
1080 /* Set FSXP */
1081 if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH)
1082 mcbsp_cfg.pcr0 &= ~FSXP;
1083 else
1084 mcbsp_cfg.pcr0 |= FSXP;
1085
1086 if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) {
1087 mcbsp_cfg.pcr0 |= CLKXM;
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001088 mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div - 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001089 mcbsp_cfg.pcr0 |= FSXM;
1090 mcbsp_cfg.srgr2 &= ~FSGM;
1091 mcbsp_cfg.xcr2 |= XDATDLY(1);
1092 mcbsp_cfg.rcr2 |= RDATDLY(1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001093 } else {
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001094 mcbsp_cfg.pcr0 &= ~CLKXM;
1095 mcbsp_cfg.srgr1 |= CLKGDV(1);
1096 mcbsp_cfg.pcr0 &= ~FSXM;
1097 mcbsp_cfg.xcr2 &= ~XDATDLY(3);
1098 mcbsp_cfg.rcr2 &= ~RDATDLY(3);
1099 }
1100
1101 mcbsp_cfg.xcr2 &= ~XPHASE;
1102 mcbsp_cfg.rcr2 &= ~RPHASE;
1103
1104 omap_mcbsp_config(id, &mcbsp_cfg);
1105}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001106EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001107
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001108#ifdef CONFIG_ARCH_OMAP3
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001109#define max_thres(m) (mcbsp->pdata->buffer_size)
1110#define valid_threshold(m, val) ((val) <= max_thres(m))
1111#define THRESHOLD_PROP_BUILDER(prop) \
1112static ssize_t prop##_show(struct device *dev, \
1113 struct device_attribute *attr, char *buf) \
1114{ \
1115 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1116 \
1117 return sprintf(buf, "%u\n", mcbsp->prop); \
1118} \
1119 \
1120static ssize_t prop##_store(struct device *dev, \
1121 struct device_attribute *attr, \
1122 const char *buf, size_t size) \
1123{ \
1124 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1125 unsigned long val; \
1126 int status; \
1127 \
1128 status = strict_strtoul(buf, 0, &val); \
1129 if (status) \
1130 return status; \
1131 \
1132 if (!valid_threshold(mcbsp, val)) \
1133 return -EDOM; \
1134 \
1135 mcbsp->prop = val; \
1136 return size; \
1137} \
1138 \
1139static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
1140
1141THRESHOLD_PROP_BUILDER(max_tx_thres);
1142THRESHOLD_PROP_BUILDER(max_rx_thres);
1143
Jarkko Nikula9b300502009-08-24 17:45:50 +03001144static const char *dma_op_modes[] = {
1145 "element", "threshold", "frame",
1146};
1147
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001148static ssize_t dma_op_mode_show(struct device *dev,
1149 struct device_attribute *attr, char *buf)
1150{
1151 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001152 int dma_op_mode, i = 0;
1153 ssize_t len = 0;
1154 const char * const *s;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001155
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001156 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001157
Jarkko Nikula9b300502009-08-24 17:45:50 +03001158 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
1159 if (dma_op_mode == i)
1160 len += sprintf(buf + len, "[%s] ", *s);
1161 else
1162 len += sprintf(buf + len, "%s ", *s);
1163 }
1164 len += sprintf(buf + len, "\n");
1165
1166 return len;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001167}
1168
1169static ssize_t dma_op_mode_store(struct device *dev,
1170 struct device_attribute *attr,
1171 const char *buf, size_t size)
1172{
1173 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001174 const char * const *s;
1175 int i = 0;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001176
Jarkko Nikula9b300502009-08-24 17:45:50 +03001177 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
1178 if (sysfs_streq(buf, *s))
1179 break;
1180
1181 if (i == ARRAY_SIZE(dma_op_modes))
1182 return -EINVAL;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001183
1184 spin_lock_irq(&mcbsp->lock);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001185 if (!mcbsp->free) {
1186 size = -EBUSY;
1187 goto unlock;
1188 }
Jarkko Nikula9b300502009-08-24 17:45:50 +03001189 mcbsp->dma_op_mode = i;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001190
1191unlock:
1192 spin_unlock_irq(&mcbsp->lock);
1193
1194 return size;
1195}
1196
1197static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
1198
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001199static const struct attribute *additional_attrs[] = {
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001200 &dev_attr_max_tx_thres.attr,
1201 &dev_attr_max_rx_thres.attr,
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001202 &dev_attr_dma_op_mode.attr,
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001203 NULL,
1204};
1205
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001206static const struct attribute_group additional_attr_group = {
1207 .attrs = (struct attribute **)additional_attrs,
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001208};
1209
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001210static inline int __devinit omap_additional_add(struct device *dev)
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001211{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001212 return sysfs_create_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001213}
1214
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001215static inline void __devexit omap_additional_remove(struct device *dev)
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001216{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001217 sysfs_remove_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001218}
1219
1220static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
1221{
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001222 mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001223 if (cpu_is_omap34xx()) {
1224 mcbsp->max_tx_thres = max_thres(mcbsp);
1225 mcbsp->max_rx_thres = max_thres(mcbsp);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001226 /*
1227 * REVISIT: Set dmap_op_mode to THRESHOLD as default
1228 * for mcbsp2 instances.
1229 */
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001230 if (omap_additional_add(mcbsp->dev))
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001231 dev_warn(mcbsp->dev,
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001232 "Unable to create additional controls\n");
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001233 } else {
1234 mcbsp->max_tx_thres = -EINVAL;
1235 mcbsp->max_rx_thres = -EINVAL;
1236 }
1237}
1238
1239static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
1240{
1241 if (cpu_is_omap34xx())
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001242 omap_additional_remove(mcbsp->dev);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001243}
1244#else
1245static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
1246static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001247#endif /* CONFIG_ARCH_OMAP3 */
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001248
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001249/*
1250 * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
1251 * 730 has only 2 McBSP, and both of them are MPU peripherals.
1252 */
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001253static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001254{
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001255 struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001256 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001257 int id = pdev->id - 1;
1258 int ret = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001259
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001260 if (!pdata) {
1261 dev_err(&pdev->dev, "McBSP device initialized without"
1262 "platform data\n");
1263 ret = -EINVAL;
1264 goto exit;
1265 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001266
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001267 dev_dbg(&pdev->dev, "Initializing OMAP McBSP (%d).\n", pdev->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001268
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001269 if (id >= omap_mcbsp_count) {
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001270 dev_err(&pdev->dev, "Invalid McBSP device id (%d)\n", id);
1271 ret = -EINVAL;
1272 goto exit;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001273 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001274
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001275 mcbsp = kzalloc(sizeof(struct omap_mcbsp), GFP_KERNEL);
1276 if (!mcbsp) {
1277 ret = -ENOMEM;
1278 goto exit;
1279 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001280
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001281 spin_lock_init(&mcbsp->lock);
1282 mcbsp->id = id + 1;
1283 mcbsp->free = 1;
1284 mcbsp->dma_tx_lch = -1;
1285 mcbsp->dma_rx_lch = -1;
1286
1287 mcbsp->phys_base = pdata->phys_base;
1288 mcbsp->io_base = ioremap(pdata->phys_base, SZ_4K);
1289 if (!mcbsp->io_base) {
Russell Kingd592dd12008-09-04 14:25:42 +01001290 ret = -ENOMEM;
1291 goto err_ioremap;
1292 }
1293
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001294 /* Default I/O is IRQ based */
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001295 mcbsp->io_type = OMAP_MCBSP_IRQ_IO;
1296 mcbsp->tx_irq = pdata->tx_irq;
1297 mcbsp->rx_irq = pdata->rx_irq;
1298 mcbsp->dma_rx_sync = pdata->dma_rx_sync;
1299 mcbsp->dma_tx_sync = pdata->dma_tx_sync;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001300
Russell Kingb820ce42009-01-23 10:26:46 +00001301 mcbsp->iclk = clk_get(&pdev->dev, "ick");
1302 if (IS_ERR(mcbsp->iclk)) {
1303 ret = PTR_ERR(mcbsp->iclk);
1304 dev_err(&pdev->dev, "unable to get ick: %d\n", ret);
1305 goto err_iclk;
1306 }
Stanley.Miao06151152009-01-29 08:57:12 -08001307
Russell Kingb820ce42009-01-23 10:26:46 +00001308 mcbsp->fclk = clk_get(&pdev->dev, "fck");
1309 if (IS_ERR(mcbsp->fclk)) {
1310 ret = PTR_ERR(mcbsp->fclk);
1311 dev_err(&pdev->dev, "unable to get fck: %d\n", ret);
1312 goto err_fclk;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001313 }
1314
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001315 mcbsp->pdata = pdata;
1316 mcbsp->dev = &pdev->dev;
Russell Kingb820ce42009-01-23 10:26:46 +00001317 mcbsp_ptr[id] = mcbsp;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001318 platform_set_drvdata(pdev, mcbsp);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001319
1320 /* Initialize mcbsp properties for OMAP34XX if needed / applicable */
1321 omap34xx_device_init(mcbsp);
1322
Russell Kingd592dd12008-09-04 14:25:42 +01001323 return 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001324
Russell Kingb820ce42009-01-23 10:26:46 +00001325err_fclk:
1326 clk_put(mcbsp->iclk);
1327err_iclk:
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001328 iounmap(mcbsp->io_base);
Russell Kingd592dd12008-09-04 14:25:42 +01001329err_ioremap:
Russell Kingb820ce42009-01-23 10:26:46 +00001330 kfree(mcbsp);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001331exit:
1332 return ret;
1333}
1334
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001335static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001336{
1337 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
1338
1339 platform_set_drvdata(pdev, NULL);
1340 if (mcbsp) {
1341
1342 if (mcbsp->pdata && mcbsp->pdata->ops &&
1343 mcbsp->pdata->ops->free)
1344 mcbsp->pdata->ops->free(mcbsp->id);
1345
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001346 omap34xx_device_exit(mcbsp);
1347
Russell Kingb820ce42009-01-23 10:26:46 +00001348 clk_disable(mcbsp->fclk);
1349 clk_disable(mcbsp->iclk);
1350 clk_put(mcbsp->fclk);
1351 clk_put(mcbsp->iclk);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001352
Russell Kingd592dd12008-09-04 14:25:42 +01001353 iounmap(mcbsp->io_base);
1354
Russell Kingb820ce42009-01-23 10:26:46 +00001355 mcbsp->fclk = NULL;
1356 mcbsp->iclk = NULL;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001357 mcbsp->free = 0;
1358 mcbsp->dev = NULL;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001359 }
1360
1361 return 0;
1362}
1363
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001364static struct platform_driver omap_mcbsp_driver = {
1365 .probe = omap_mcbsp_probe,
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001366 .remove = __devexit_p(omap_mcbsp_remove),
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001367 .driver = {
1368 .name = "omap-mcbsp",
1369 },
1370};
1371
1372int __init omap_mcbsp_init(void)
1373{
1374 /* Register the McBSP driver */
1375 return platform_driver_register(&omap_mcbsp_driver);
1376}