blob: f8245f25825b9996c25275a61b8b18d672ec6ee8 [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;
31int omap_mcbsp_count;
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{
35 if (cpu_class_is_omap1() || cpu_is_omap2420())
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080036 __raw_writew((u16)val, mcbsp->io_base + reg);
Chandra Shekharb4b58f52008-10-08 10:01:39 +030037 else
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080038 __raw_writel(val, mcbsp->io_base + reg);
Chandra Shekharb4b58f52008-10-08 10:01:39 +030039}
40
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080041int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030042{
43 if (cpu_class_is_omap1() || cpu_is_omap2420())
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080044 return __raw_readw(mcbsp->io_base + reg);
Chandra Shekharb4b58f52008-10-08 10:01:39 +030045 else
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080046 return __raw_readl(mcbsp->io_base + reg);
Chandra Shekharb4b58f52008-10-08 10:01:39 +030047}
48
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080049#define MCBSP_READ(mcbsp, reg) \
50 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg)
51#define MCBSP_WRITE(mcbsp, reg, val) \
52 omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030053
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",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080063 MCBSP_READ(mcbsp, DRR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030064 dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080065 MCBSP_READ(mcbsp, DRR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030066 dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080067 MCBSP_READ(mcbsp, DXR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030068 dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080069 MCBSP_READ(mcbsp, DXR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030070 dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080071 MCBSP_READ(mcbsp, SPCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030072 dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080073 MCBSP_READ(mcbsp, SPCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030074 dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080075 MCBSP_READ(mcbsp, RCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030076 dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080077 MCBSP_READ(mcbsp, RCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030078 dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080079 MCBSP_READ(mcbsp, XCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030080 dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080081 MCBSP_READ(mcbsp, XCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030082 dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080083 MCBSP_READ(mcbsp, SRGR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030084 dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080085 MCBSP_READ(mcbsp, SRGR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030086 dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080087 MCBSP_READ(mcbsp, PCR0));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030088 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
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080096 irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -070097 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 */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800103 MCBSP_WRITE(mcbsp_tx, SPCR2, irqst_spcr2 & ~(XSYNC_ERR));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700104 } else {
105 complete(&mcbsp_tx->tx_irq_completion);
106 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300107
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100108 return IRQ_HANDLED;
109}
110
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700111static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100112{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400113 struct omap_mcbsp *mcbsp_rx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700114 u16 irqst_spcr1;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100115
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800116 irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700117 dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100118
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700119 if (irqst_spcr1 & RSYNC_ERR) {
120 dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n",
121 irqst_spcr1);
122 /* Writing zero to RSYNC_ERR clears the IRQ */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800123 MCBSP_WRITE(mcbsp_rx, SPCR1, irqst_spcr1 & ~(RSYNC_ERR));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700124 } else {
125 complete(&mcbsp_rx->tx_irq_completion);
126 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300127
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100128 return IRQ_HANDLED;
129}
130
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100131static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data)
132{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400133 struct omap_mcbsp *mcbsp_dma_tx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100134
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300135 dev_dbg(mcbsp_dma_tx->dev, "TX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800136 MCBSP_READ(mcbsp_dma_tx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100137
138 /* We can free the channels */
139 omap_free_dma(mcbsp_dma_tx->dma_tx_lch);
140 mcbsp_dma_tx->dma_tx_lch = -1;
141
142 complete(&mcbsp_dma_tx->tx_dma_completion);
143}
144
145static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data)
146{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400147 struct omap_mcbsp *mcbsp_dma_rx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100148
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300149 dev_dbg(mcbsp_dma_rx->dev, "RX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800150 MCBSP_READ(mcbsp_dma_rx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100151
152 /* We can free the channels */
153 omap_free_dma(mcbsp_dma_rx->dma_rx_lch);
154 mcbsp_dma_rx->dma_rx_lch = -1;
155
156 complete(&mcbsp_dma_rx->rx_dma_completion);
157}
158
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100159/*
160 * omap_mcbsp_config simply write a config to the
161 * appropriate McBSP.
162 * You either call this function or set the McBSP registers
163 * by yourself before calling omap_mcbsp_start().
164 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300165void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100166{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300167 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100168
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300169 if (!omap_mcbsp_check_valid_id(id)) {
170 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
171 return;
172 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300173 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300174
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300175 dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
176 mcbsp->id, mcbsp->phys_base);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100177
178 /* We write the given config */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800179 MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
180 MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
181 MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
182 MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
183 MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
184 MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
185 MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
186 MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
187 MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
188 MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
189 MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
Syed Rafiuddina5b92cc2009-07-28 18:57:10 +0530190 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800191 MCBSP_WRITE(mcbsp, XCCR, config->xccr);
192 MCBSP_WRITE(mcbsp, RCCR, config->rccr);
Tony Lindgren3127f8f2009-01-15 13:09:54 +0200193 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100194}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300195EXPORT_SYMBOL(omap_mcbsp_config);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100196
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -0800197#ifdef CONFIG_ARCH_OMAP3
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300198/*
199 * omap_mcbsp_set_tx_threshold configures how to deal
200 * with transmit threshold. the threshold value and handler can be
201 * configure in here.
202 */
203void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
204{
205 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300206
207 if (!cpu_is_omap34xx())
208 return;
209
210 if (!omap_mcbsp_check_valid_id(id)) {
211 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
212 return;
213 }
214 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300215
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800216 MCBSP_WRITE(mcbsp, THRSH2, threshold);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300217}
218EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
219
220/*
221 * omap_mcbsp_set_rx_threshold configures how to deal
222 * with receive threshold. the threshold value and handler can be
223 * configure in here.
224 */
225void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
226{
227 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300228
229 if (!cpu_is_omap34xx())
230 return;
231
232 if (!omap_mcbsp_check_valid_id(id)) {
233 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
234 return;
235 }
236 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300237
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800238 MCBSP_WRITE(mcbsp, THRSH1, threshold);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300239}
240EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
Eduardo Valentina1a56f52009-08-20 16:18:11 +0300241
242/*
243 * omap_mcbsp_get_max_tx_thres just return the current configured
244 * maximum threshold for transmission
245 */
246u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
247{
248 struct omap_mcbsp *mcbsp;
249
250 if (!omap_mcbsp_check_valid_id(id)) {
251 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
252 return -ENODEV;
253 }
254 mcbsp = id_to_mcbsp_ptr(id);
255
256 return mcbsp->max_tx_thres;
257}
258EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
259
260/*
261 * omap_mcbsp_get_max_rx_thres just return the current configured
262 * maximum threshold for reception
263 */
264u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
265{
266 struct omap_mcbsp *mcbsp;
267
268 if (!omap_mcbsp_check_valid_id(id)) {
269 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
270 return -ENODEV;
271 }
272 mcbsp = id_to_mcbsp_ptr(id);
273
274 return mcbsp->max_rx_thres;
275}
276EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300277
278/*
279 * omap_mcbsp_get_dma_op_mode just return the current configured
280 * operating mode for the mcbsp channel
281 */
282int omap_mcbsp_get_dma_op_mode(unsigned int id)
283{
284 struct omap_mcbsp *mcbsp;
285 int dma_op_mode;
286
287 if (!omap_mcbsp_check_valid_id(id)) {
288 printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
289 return -ENODEV;
290 }
291 mcbsp = id_to_mcbsp_ptr(id);
292
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300293 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300294
295 return dma_op_mode;
296}
297EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300298
299static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
300{
301 /*
302 * Enable wakup behavior, smart idle and all wakeups
303 * REVISIT: some wakeups may be unnecessary
304 */
305 if (cpu_is_omap34xx()) {
306 u16 syscon;
307
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800308 syscon = MCBSP_READ(mcbsp, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300309 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eduardo Valentind99a7452009-08-20 16:18:18 +0300310
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300311 if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
312 syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
313 CLOCKACTIVITY(0x02));
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800314 MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300315 } else {
Eduardo Valentind99a7452009-08-20 16:18:18 +0300316 syscon |= SIDLEMODE(0x01);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300317 }
Eduardo Valentind99a7452009-08-20 16:18:18 +0300318
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800319 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300320 }
321}
322
323static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
324{
325 /*
326 * Disable wakup behavior, smart idle and all wakeups
327 */
328 if (cpu_is_omap34xx()) {
329 u16 syscon;
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300330
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800331 syscon = MCBSP_READ(mcbsp, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300332 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300333 /*
334 * HW bug workaround - If no_idle mode is taken, we need to
335 * go to smart_idle before going to always_idle, or the
336 * device will not hit retention anymore.
337 */
338 syscon |= SIDLEMODE(0x02);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800339 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300340
341 syscon &= ~(SIDLEMODE(0x03));
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800342 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300343
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800344 MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300345 }
346}
347#else
348static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
349static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300350#endif
351
Tony Lindgren120db2c2006-04-02 17:46:27 +0100352/*
353 * We can choose between IRQ based or polled IO.
354 * This needs to be called before omap_mcbsp_request().
355 */
356int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type)
357{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300358 struct omap_mcbsp *mcbsp;
359
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300360 if (!omap_mcbsp_check_valid_id(id)) {
361 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
362 return -ENODEV;
363 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300364 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100365
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300366 spin_lock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100367
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300368 if (!mcbsp->free) {
369 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
370 mcbsp->id);
371 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100372 return -EINVAL;
373 }
374
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300375 mcbsp->io_type = io_type;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100376
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300377 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100378
379 return 0;
380}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300381EXPORT_SYMBOL(omap_mcbsp_set_io_type);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100382
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100383int omap_mcbsp_request(unsigned int id)
384{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300385 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100386 int err;
387
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300388 if (!omap_mcbsp_check_valid_id(id)) {
389 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
390 return -ENODEV;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100391 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300392 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300393
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300394 spin_lock(&mcbsp->lock);
395 if (!mcbsp->free) {
396 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
397 mcbsp->id);
398 spin_unlock(&mcbsp->lock);
Russell Kingb820ce42009-01-23 10:26:46 +0000399 return -EBUSY;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100400 }
401
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300402 mcbsp->free = 0;
403 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100404
Russell Kingb820ce42009-01-23 10:26:46 +0000405 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
406 mcbsp->pdata->ops->request(id);
407
408 clk_enable(mcbsp->iclk);
409 clk_enable(mcbsp->fclk);
410
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300411 /* Do procedure specific to omap34xx arch, if applicable */
412 omap34xx_mcbsp_request(mcbsp);
413
Jarkko Nikula5a070552008-10-08 10:01:41 +0300414 /*
415 * Make sure that transmitter, receiver and sample-rate generator are
416 * not running before activating IRQs.
417 */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800418 MCBSP_WRITE(mcbsp, SPCR1, 0);
419 MCBSP_WRITE(mcbsp, SPCR2, 0);
Jarkko Nikula5a070552008-10-08 10:01:41 +0300420
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300421 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
Tony Lindgren120db2c2006-04-02 17:46:27 +0100422 /* We need to get IRQs here */
Jarkko Nikula5a070552008-10-08 10:01:41 +0300423 init_completion(&mcbsp->tx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300424 err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
425 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100426 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300427 dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
428 "for McBSP%d\n", mcbsp->tx_irq,
429 mcbsp->id);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800430 goto error;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100431 }
432
Jarkko Nikula5a070552008-10-08 10:01:41 +0300433 init_completion(&mcbsp->rx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300434 err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler,
435 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100436 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300437 dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
438 "for McBSP%d\n", mcbsp->rx_irq,
439 mcbsp->id);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800440 goto tx_irq;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100441 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100442 }
443
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100444 return 0;
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800445tx_irq:
446 free_irq(mcbsp->tx_irq, (void *)mcbsp);
447error:
448 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
449 mcbsp->pdata->ops->free(id);
450
451 /* Do procedure specific to omap34xx arch, if applicable */
452 omap34xx_mcbsp_free(mcbsp);
453
454 clk_disable(mcbsp->fclk);
455 clk_disable(mcbsp->iclk);
456
457 mcbsp->free = 1;
458
459 return err;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100460}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300461EXPORT_SYMBOL(omap_mcbsp_request);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100462
463void omap_mcbsp_free(unsigned int id)
464{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300465 struct omap_mcbsp *mcbsp;
466
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300467 if (!omap_mcbsp_check_valid_id(id)) {
468 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100469 return;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100470 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300471 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100472
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300473 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
474 mcbsp->pdata->ops->free(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300475
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300476 /* Do procedure specific to omap34xx arch, if applicable */
477 omap34xx_mcbsp_free(mcbsp);
478
Russell Kingb820ce42009-01-23 10:26:46 +0000479 clk_disable(mcbsp->fclk);
480 clk_disable(mcbsp->iclk);
481
482 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
483 /* Free IRQs */
484 free_irq(mcbsp->rx_irq, (void *)mcbsp);
485 free_irq(mcbsp->tx_irq, (void *)mcbsp);
486 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100487
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300488 spin_lock(&mcbsp->lock);
489 if (mcbsp->free) {
490 dev_err(mcbsp->dev, "McBSP%d was not reserved\n",
491 mcbsp->id);
492 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100493 return;
494 }
495
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300496 mcbsp->free = 1;
497 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100498}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300499EXPORT_SYMBOL(omap_mcbsp_free);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100500
501/*
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300502 * Here we start the McBSP, by enabling transmitter, receiver or both.
503 * If no transmitter or receiver is active prior calling, then sample-rate
504 * generator and frame sync are started.
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100505 */
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300506void omap_mcbsp_start(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100507{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300508 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300509 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100510 u16 w;
511
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300512 if (!omap_mcbsp_check_valid_id(id)) {
513 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100514 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300515 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300516 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100517
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800518 mcbsp->rx_word_length = (MCBSP_READ(mcbsp, RCR1) >> 5) & 0x7;
519 mcbsp->tx_word_length = (MCBSP_READ(mcbsp, XCR1) >> 5) & 0x7;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100520
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800521 idle = !((MCBSP_READ(mcbsp, SPCR2) | MCBSP_READ(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300522
523 if (idle) {
524 /* Start the sample generator */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800525 w = MCBSP_READ(mcbsp, SPCR2);
526 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300527 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100528
529 /* Enable transmitter and receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300530 tx &= 1;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800531 w = MCBSP_READ(mcbsp, SPCR2);
532 MCBSP_WRITE(mcbsp, SPCR2, w | tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100533
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300534 rx &= 1;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800535 w = MCBSP_READ(mcbsp, SPCR1);
536 MCBSP_WRITE(mcbsp, SPCR1, w | rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100537
Eduardo Valentin44a63112009-08-20 16:18:09 +0300538 /*
539 * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
540 * REVISIT: 100us may give enough time for two CLKSRG, however
541 * due to some unknown PM related, clock gating etc. reason it
542 * is now at 500us.
543 */
544 udelay(500);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100545
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300546 if (idle) {
547 /* Start frame sync */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800548 w = MCBSP_READ(mcbsp, SPCR2);
549 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300550 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100551
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300552 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
553 /* Release the transmitter and receiver */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800554 w = MCBSP_READ(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300555 w &= ~(tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800556 MCBSP_WRITE(mcbsp, XCCR, w);
557 w = MCBSP_READ(mcbsp, RCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300558 w &= ~(rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800559 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300560 }
561
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100562 /* Dump McBSP Regs */
563 omap_mcbsp_dump_reg(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100564}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300565EXPORT_SYMBOL(omap_mcbsp_start);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100566
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300567void omap_mcbsp_stop(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100568{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300569 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300570 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100571 u16 w;
572
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300573 if (!omap_mcbsp_check_valid_id(id)) {
574 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100575 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300576 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100577
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300578 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100579
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300580 /* Reset transmitter */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300581 tx &= 1;
582 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
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);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300586 }
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800587 w = MCBSP_READ(mcbsp, SPCR2);
588 MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100589
590 /* Reset receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300591 rx &= 1;
592 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800593 w = MCBSP_READ(mcbsp, RCCR);
Jarkko Nikulaa93d4ed2009-10-14 09:56:35 -0700594 w |= (rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800595 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300596 }
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800597 w = MCBSP_READ(mcbsp, SPCR1);
598 MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100599
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800600 idle = !((MCBSP_READ(mcbsp, SPCR2) | MCBSP_READ(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300601
602 if (idle) {
603 /* Reset the sample rate generator */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800604 w = MCBSP_READ(mcbsp, SPCR2);
605 MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300606 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100607}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300608EXPORT_SYMBOL(omap_mcbsp_stop);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100609
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100610/* polled mcbsp i/o operations */
611int omap_mcbsp_pollwrite(unsigned int id, u16 buf)
612{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300613 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300614
615 if (!omap_mcbsp_check_valid_id(id)) {
616 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
617 return -ENODEV;
618 }
619
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300620 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300621
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800622 MCBSP_WRITE(mcbsp, DXR1, buf);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100623 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800624 if (MCBSP_READ(mcbsp, SPCR2) & XSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100625 /* clear error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800626 MCBSP_WRITE(mcbsp, SPCR2,
627 MCBSP_READ(mcbsp, SPCR2) & (~XSYNC_ERR));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100628 /* resend */
629 return -1;
630 } else {
631 /* wait for transmit confirmation */
632 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800633 while (!(MCBSP_READ(mcbsp, SPCR2) & XRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100634 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800635 MCBSP_WRITE(mcbsp, SPCR2,
636 MCBSP_READ(mcbsp, SPCR2) & (~XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100637 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800638 MCBSP_WRITE(mcbsp, SPCR2,
639 MCBSP_READ(mcbsp, SPCR2) | (XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100640 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300641 dev_err(mcbsp->dev, "Could not write to"
642 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100643 return -2;
644 }
645 }
646 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300647
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100648 return 0;
649}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300650EXPORT_SYMBOL(omap_mcbsp_pollwrite);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100651
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300652int omap_mcbsp_pollread(unsigned int id, u16 *buf)
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100653{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300654 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300655
656 if (!omap_mcbsp_check_valid_id(id)) {
657 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
658 return -ENODEV;
659 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300660 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300661
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100662 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800663 if (MCBSP_READ(mcbsp, SPCR1) & RSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100664 /* clear error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800665 MCBSP_WRITE(mcbsp, SPCR1,
666 MCBSP_READ(mcbsp, SPCR1) & (~RSYNC_ERR));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100667 /* resend */
668 return -1;
669 } else {
670 /* wait for recieve confirmation */
671 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800672 while (!(MCBSP_READ(mcbsp, SPCR1) & RRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100673 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800674 MCBSP_WRITE(mcbsp, SPCR1,
675 MCBSP_READ(mcbsp, SPCR1) & (~RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100676 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800677 MCBSP_WRITE(mcbsp, SPCR1,
678 MCBSP_READ(mcbsp, SPCR1) | (RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100679 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300680 dev_err(mcbsp->dev, "Could not read from"
681 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100682 return -2;
683 }
684 }
685 }
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800686 *buf = MCBSP_READ(mcbsp, DRR1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300687
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100688 return 0;
689}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300690EXPORT_SYMBOL(omap_mcbsp_pollread);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100691
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100692/*
693 * IRQ based word transmission.
694 */
695void omap_mcbsp_xmit_word(unsigned int id, u32 word)
696{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300697 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300698 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100699
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300700 if (!omap_mcbsp_check_valid_id(id)) {
701 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100702 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300703 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100704
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300705 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300706 word_length = mcbsp->tx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100707
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300708 wait_for_completion(&mcbsp->tx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100709
710 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800711 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
712 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100713}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300714EXPORT_SYMBOL(omap_mcbsp_xmit_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100715
716u32 omap_mcbsp_recv_word(unsigned int id)
717{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300718 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100719 u16 word_lsb, word_msb = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300720 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100721
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300722 if (!omap_mcbsp_check_valid_id(id)) {
723 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
724 return -ENODEV;
725 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300726 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100727
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300728 word_length = mcbsp->rx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100729
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300730 wait_for_completion(&mcbsp->rx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100731
732 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800733 word_msb = MCBSP_READ(mcbsp, DRR2);
734 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100735
736 return (word_lsb | (word_msb << 16));
737}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300738EXPORT_SYMBOL(omap_mcbsp_recv_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100739
Tony Lindgren120db2c2006-04-02 17:46:27 +0100740int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word)
741{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300742 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300743 omap_mcbsp_word_length tx_word_length;
744 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100745 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
746
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300747 if (!omap_mcbsp_check_valid_id(id)) {
748 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
749 return -ENODEV;
750 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300751 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300752 tx_word_length = mcbsp->tx_word_length;
753 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300754
Tony Lindgren120db2c2006-04-02 17:46:27 +0100755 if (tx_word_length != rx_word_length)
756 return -EINVAL;
757
758 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800759 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100760 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800761 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100762 if (attempts++ > 1000) {
763 /* We must reset the transmitter */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800764 MCBSP_WRITE(mcbsp, SPCR2, spcr2 & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +0100765 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800766 MCBSP_WRITE(mcbsp, SPCR2, spcr2 | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100767 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300768 dev_err(mcbsp->dev, "McBSP%d transmitter not "
769 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100770 return -EAGAIN;
771 }
772 }
773
774 /* Now we can push the data */
775 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800776 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
777 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100778
779 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800780 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100781 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800782 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100783 if (attempts++ > 1000) {
784 /* We must reset the receiver */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800785 MCBSP_WRITE(mcbsp, SPCR1, spcr1 & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +0100786 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800787 MCBSP_WRITE(mcbsp, SPCR1, spcr1 | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100788 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300789 dev_err(mcbsp->dev, "McBSP%d receiver not "
790 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100791 return -EAGAIN;
792 }
793 }
794
795 /* Receiver is ready, let's read the dummy data */
796 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800797 word_msb = MCBSP_READ(mcbsp, DRR2);
798 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100799
800 return 0;
801}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300802EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100803
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300804int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 *word)
Tony Lindgren120db2c2006-04-02 17:46:27 +0100805{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300806 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +0100807 u32 clock_word = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300808 omap_mcbsp_word_length tx_word_length;
809 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100810 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
811
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300812 if (!omap_mcbsp_check_valid_id(id)) {
813 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
814 return -ENODEV;
815 }
816
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300817 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300818
819 tx_word_length = mcbsp->tx_word_length;
820 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300821
Tony Lindgren120db2c2006-04-02 17:46:27 +0100822 if (tx_word_length != rx_word_length)
823 return -EINVAL;
824
825 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800826 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100827 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800828 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100829 if (attempts++ > 1000) {
830 /* We must reset the transmitter */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800831 MCBSP_WRITE(mcbsp, SPCR2, spcr2 & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +0100832 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800833 MCBSP_WRITE(mcbsp, SPCR2, spcr2 | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100834 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300835 dev_err(mcbsp->dev, "McBSP%d transmitter not "
836 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100837 return -EAGAIN;
838 }
839 }
840
841 /* We first need to enable the bus clock */
842 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800843 MCBSP_WRITE(mcbsp, DXR2, clock_word >> 16);
844 MCBSP_WRITE(mcbsp, DXR1, clock_word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100845
846 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800847 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100848 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800849 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100850 if (attempts++ > 1000) {
851 /* We must reset the receiver */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800852 MCBSP_WRITE(mcbsp, SPCR1, spcr1 & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +0100853 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800854 MCBSP_WRITE(mcbsp, SPCR1, spcr1 | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100855 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300856 dev_err(mcbsp->dev, "McBSP%d receiver not "
857 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100858 return -EAGAIN;
859 }
860 }
861
862 /* Receiver is ready, there is something for us */
863 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800864 word_msb = MCBSP_READ(mcbsp, DRR2);
865 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100866
867 word[0] = (word_lsb | (word_msb << 16));
868
869 return 0;
870}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300871EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100872
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100873/*
874 * Simple DMA based buffer rx/tx routines.
875 * Nothing fancy, just a single buffer tx/rx through DMA.
876 * The DMA resources are released once the transfer is done.
877 * For anything fancier, you should use your own customized DMA
878 * routines and callbacks.
879 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300880int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer,
881 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100882{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300883 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100884 int dma_tx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100885 int src_port = 0;
886 int dest_port = 0;
887 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100888
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300889 if (!omap_mcbsp_check_valid_id(id)) {
890 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
891 return -ENODEV;
892 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300893 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100894
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300895 if (omap_request_dma(mcbsp->dma_tx_sync, "McBSP TX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300896 omap_mcbsp_tx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300897 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300898 &dma_tx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300899 dev_err(mcbsp->dev, " Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300900 "McBSP%d TX. Trying IRQ based TX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300901 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100902 return -EAGAIN;
903 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300904 mcbsp->dma_tx_lch = dma_tx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100905
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300906 dev_err(mcbsp->dev, "McBSP%d TX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300907 dma_tx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100908
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300909 init_completion(&mcbsp->tx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100910
Tony Lindgren120db2c2006-04-02 17:46:27 +0100911 if (cpu_class_is_omap1()) {
912 src_port = OMAP_DMA_PORT_TIPB;
913 dest_port = OMAP_DMA_PORT_EMIFF;
914 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300915 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300916 sync_dev = mcbsp->dma_tx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100917
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300918 omap_set_dma_transfer_params(mcbsp->dma_tx_lch,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100919 OMAP_DMA_DATA_TYPE_S16,
920 length >> 1, 1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000921 OMAP_DMA_SYNC_ELEMENT,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100922 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100923
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300924 omap_set_dma_dest_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100925 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100926 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300927 mcbsp->phys_base + OMAP_MCBSP_REG_DXR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000928 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100929
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300930 omap_set_dma_src_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100931 dest_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100932 OMAP_DMA_AMODE_POST_INC,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000933 buffer,
934 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100935
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300936 omap_start_dma(mcbsp->dma_tx_lch);
937 wait_for_completion(&mcbsp->tx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300938
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100939 return 0;
940}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300941EXPORT_SYMBOL(omap_mcbsp_xmit_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100942
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300943int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer,
944 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100945{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300946 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100947 int dma_rx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100948 int src_port = 0;
949 int dest_port = 0;
950 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100951
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300952 if (!omap_mcbsp_check_valid_id(id)) {
953 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
954 return -ENODEV;
955 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300956 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100957
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300958 if (omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300959 omap_mcbsp_rx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300960 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300961 &dma_rx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300962 dev_err(mcbsp->dev, "Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300963 "McBSP%d RX. Trying IRQ based RX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300964 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100965 return -EAGAIN;
966 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300967 mcbsp->dma_rx_lch = dma_rx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100968
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300969 dev_err(mcbsp->dev, "McBSP%d RX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300970 dma_rx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100971
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300972 init_completion(&mcbsp->rx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100973
Tony Lindgren120db2c2006-04-02 17:46:27 +0100974 if (cpu_class_is_omap1()) {
975 src_port = OMAP_DMA_PORT_TIPB;
976 dest_port = OMAP_DMA_PORT_EMIFF;
977 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300978 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300979 sync_dev = mcbsp->dma_rx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100980
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300981 omap_set_dma_transfer_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300982 OMAP_DMA_DATA_TYPE_S16,
983 length >> 1, 1,
984 OMAP_DMA_SYNC_ELEMENT,
985 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100986
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300987 omap_set_dma_src_params(mcbsp->dma_rx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +0100988 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100989 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300990 mcbsp->phys_base + OMAP_MCBSP_REG_DRR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000991 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100992
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300993 omap_set_dma_dest_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300994 dest_port,
995 OMAP_DMA_AMODE_POST_INC,
996 buffer,
997 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100998
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300999 omap_start_dma(mcbsp->dma_rx_lch);
1000 wait_for_completion(&mcbsp->rx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001001
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001002 return 0;
1003}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001004EXPORT_SYMBOL(omap_mcbsp_recv_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001005
1006/*
1007 * SPI wrapper.
1008 * Since SPI setup is much simpler than the generic McBSP one,
1009 * this wrapper just need an omap_mcbsp_spi_cfg structure as an input.
1010 * Once this is done, you can call omap_mcbsp_start().
1011 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001012void omap_mcbsp_set_spi_mode(unsigned int id,
1013 const struct omap_mcbsp_spi_cfg *spi_cfg)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001014{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001015 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001016 struct omap_mcbsp_reg_cfg mcbsp_cfg;
1017
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001018 if (!omap_mcbsp_check_valid_id(id)) {
1019 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001020 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001021 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001022 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001023
1024 memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg));
1025
1026 /* SPI has only one frame */
1027 mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0));
1028 mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0));
1029
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001030 /* Clock stop mode */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001031 if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY)
1032 mcbsp_cfg.spcr1 |= (1 << 12);
1033 else
1034 mcbsp_cfg.spcr1 |= (3 << 11);
1035
1036 /* Set clock parities */
1037 if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1038 mcbsp_cfg.pcr0 |= CLKRP;
1039 else
1040 mcbsp_cfg.pcr0 &= ~CLKRP;
1041
1042 if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1043 mcbsp_cfg.pcr0 &= ~CLKXP;
1044 else
1045 mcbsp_cfg.pcr0 |= CLKXP;
1046
1047 /* Set SCLKME to 0 and CLKSM to 1 */
1048 mcbsp_cfg.pcr0 &= ~SCLKME;
1049 mcbsp_cfg.srgr2 |= CLKSM;
1050
1051 /* Set FSXP */
1052 if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH)
1053 mcbsp_cfg.pcr0 &= ~FSXP;
1054 else
1055 mcbsp_cfg.pcr0 |= FSXP;
1056
1057 if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) {
1058 mcbsp_cfg.pcr0 |= CLKXM;
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001059 mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div - 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001060 mcbsp_cfg.pcr0 |= FSXM;
1061 mcbsp_cfg.srgr2 &= ~FSGM;
1062 mcbsp_cfg.xcr2 |= XDATDLY(1);
1063 mcbsp_cfg.rcr2 |= RDATDLY(1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001064 } else {
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001065 mcbsp_cfg.pcr0 &= ~CLKXM;
1066 mcbsp_cfg.srgr1 |= CLKGDV(1);
1067 mcbsp_cfg.pcr0 &= ~FSXM;
1068 mcbsp_cfg.xcr2 &= ~XDATDLY(3);
1069 mcbsp_cfg.rcr2 &= ~RDATDLY(3);
1070 }
1071
1072 mcbsp_cfg.xcr2 &= ~XPHASE;
1073 mcbsp_cfg.rcr2 &= ~RPHASE;
1074
1075 omap_mcbsp_config(id, &mcbsp_cfg);
1076}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001077EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001078
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001079#ifdef CONFIG_ARCH_OMAP3
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001080#define max_thres(m) (mcbsp->pdata->buffer_size)
1081#define valid_threshold(m, val) ((val) <= max_thres(m))
1082#define THRESHOLD_PROP_BUILDER(prop) \
1083static ssize_t prop##_show(struct device *dev, \
1084 struct device_attribute *attr, char *buf) \
1085{ \
1086 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1087 \
1088 return sprintf(buf, "%u\n", mcbsp->prop); \
1089} \
1090 \
1091static ssize_t prop##_store(struct device *dev, \
1092 struct device_attribute *attr, \
1093 const char *buf, size_t size) \
1094{ \
1095 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1096 unsigned long val; \
1097 int status; \
1098 \
1099 status = strict_strtoul(buf, 0, &val); \
1100 if (status) \
1101 return status; \
1102 \
1103 if (!valid_threshold(mcbsp, val)) \
1104 return -EDOM; \
1105 \
1106 mcbsp->prop = val; \
1107 return size; \
1108} \
1109 \
1110static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
1111
1112THRESHOLD_PROP_BUILDER(max_tx_thres);
1113THRESHOLD_PROP_BUILDER(max_rx_thres);
1114
Jarkko Nikula9b300502009-08-24 17:45:50 +03001115static const char *dma_op_modes[] = {
1116 "element", "threshold", "frame",
1117};
1118
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001119static ssize_t dma_op_mode_show(struct device *dev,
1120 struct device_attribute *attr, char *buf)
1121{
1122 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001123 int dma_op_mode, i = 0;
1124 ssize_t len = 0;
1125 const char * const *s;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001126
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001127 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001128
Jarkko Nikula9b300502009-08-24 17:45:50 +03001129 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
1130 if (dma_op_mode == i)
1131 len += sprintf(buf + len, "[%s] ", *s);
1132 else
1133 len += sprintf(buf + len, "%s ", *s);
1134 }
1135 len += sprintf(buf + len, "\n");
1136
1137 return len;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001138}
1139
1140static ssize_t dma_op_mode_store(struct device *dev,
1141 struct device_attribute *attr,
1142 const char *buf, size_t size)
1143{
1144 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001145 const char * const *s;
1146 int i = 0;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001147
Jarkko Nikula9b300502009-08-24 17:45:50 +03001148 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
1149 if (sysfs_streq(buf, *s))
1150 break;
1151
1152 if (i == ARRAY_SIZE(dma_op_modes))
1153 return -EINVAL;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001154
1155 spin_lock_irq(&mcbsp->lock);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001156 if (!mcbsp->free) {
1157 size = -EBUSY;
1158 goto unlock;
1159 }
Jarkko Nikula9b300502009-08-24 17:45:50 +03001160 mcbsp->dma_op_mode = i;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001161
1162unlock:
1163 spin_unlock_irq(&mcbsp->lock);
1164
1165 return size;
1166}
1167
1168static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
1169
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001170static const struct attribute *additional_attrs[] = {
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001171 &dev_attr_max_tx_thres.attr,
1172 &dev_attr_max_rx_thres.attr,
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001173 &dev_attr_dma_op_mode.attr,
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001174 NULL,
1175};
1176
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001177static const struct attribute_group additional_attr_group = {
1178 .attrs = (struct attribute **)additional_attrs,
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001179};
1180
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001181static inline int __devinit omap_additional_add(struct device *dev)
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001182{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001183 return sysfs_create_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001184}
1185
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001186static inline void __devexit omap_additional_remove(struct device *dev)
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001187{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001188 sysfs_remove_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001189}
1190
1191static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
1192{
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001193 mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001194 if (cpu_is_omap34xx()) {
1195 mcbsp->max_tx_thres = max_thres(mcbsp);
1196 mcbsp->max_rx_thres = max_thres(mcbsp);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001197 /*
1198 * REVISIT: Set dmap_op_mode to THRESHOLD as default
1199 * for mcbsp2 instances.
1200 */
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001201 if (omap_additional_add(mcbsp->dev))
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001202 dev_warn(mcbsp->dev,
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001203 "Unable to create additional controls\n");
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001204 } else {
1205 mcbsp->max_tx_thres = -EINVAL;
1206 mcbsp->max_rx_thres = -EINVAL;
1207 }
1208}
1209
1210static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
1211{
1212 if (cpu_is_omap34xx())
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001213 omap_additional_remove(mcbsp->dev);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001214}
1215#else
1216static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
1217static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001218#endif /* CONFIG_ARCH_OMAP3 */
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001219
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001220/*
1221 * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
1222 * 730 has only 2 McBSP, and both of them are MPU peripherals.
1223 */
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001224static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001225{
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001226 struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001227 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001228 int id = pdev->id - 1;
1229 int ret = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001230
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001231 if (!pdata) {
1232 dev_err(&pdev->dev, "McBSP device initialized without"
1233 "platform data\n");
1234 ret = -EINVAL;
1235 goto exit;
1236 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001237
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001238 dev_dbg(&pdev->dev, "Initializing OMAP McBSP (%d).\n", pdev->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001239
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001240 if (id >= omap_mcbsp_count) {
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001241 dev_err(&pdev->dev, "Invalid McBSP device id (%d)\n", id);
1242 ret = -EINVAL;
1243 goto exit;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001244 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001245
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001246 mcbsp = kzalloc(sizeof(struct omap_mcbsp), GFP_KERNEL);
1247 if (!mcbsp) {
1248 ret = -ENOMEM;
1249 goto exit;
1250 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001251
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001252 spin_lock_init(&mcbsp->lock);
1253 mcbsp->id = id + 1;
1254 mcbsp->free = 1;
1255 mcbsp->dma_tx_lch = -1;
1256 mcbsp->dma_rx_lch = -1;
1257
1258 mcbsp->phys_base = pdata->phys_base;
1259 mcbsp->io_base = ioremap(pdata->phys_base, SZ_4K);
1260 if (!mcbsp->io_base) {
Russell Kingd592dd12008-09-04 14:25:42 +01001261 ret = -ENOMEM;
1262 goto err_ioremap;
1263 }
1264
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001265 /* Default I/O is IRQ based */
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001266 mcbsp->io_type = OMAP_MCBSP_IRQ_IO;
1267 mcbsp->tx_irq = pdata->tx_irq;
1268 mcbsp->rx_irq = pdata->rx_irq;
1269 mcbsp->dma_rx_sync = pdata->dma_rx_sync;
1270 mcbsp->dma_tx_sync = pdata->dma_tx_sync;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001271
Russell Kingb820ce42009-01-23 10:26:46 +00001272 mcbsp->iclk = clk_get(&pdev->dev, "ick");
1273 if (IS_ERR(mcbsp->iclk)) {
1274 ret = PTR_ERR(mcbsp->iclk);
1275 dev_err(&pdev->dev, "unable to get ick: %d\n", ret);
1276 goto err_iclk;
1277 }
Stanley.Miao06151152009-01-29 08:57:12 -08001278
Russell Kingb820ce42009-01-23 10:26:46 +00001279 mcbsp->fclk = clk_get(&pdev->dev, "fck");
1280 if (IS_ERR(mcbsp->fclk)) {
1281 ret = PTR_ERR(mcbsp->fclk);
1282 dev_err(&pdev->dev, "unable to get fck: %d\n", ret);
1283 goto err_fclk;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001284 }
1285
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001286 mcbsp->pdata = pdata;
1287 mcbsp->dev = &pdev->dev;
Russell Kingb820ce42009-01-23 10:26:46 +00001288 mcbsp_ptr[id] = mcbsp;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001289 platform_set_drvdata(pdev, mcbsp);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001290
1291 /* Initialize mcbsp properties for OMAP34XX if needed / applicable */
1292 omap34xx_device_init(mcbsp);
1293
Russell Kingd592dd12008-09-04 14:25:42 +01001294 return 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001295
Russell Kingb820ce42009-01-23 10:26:46 +00001296err_fclk:
1297 clk_put(mcbsp->iclk);
1298err_iclk:
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001299 iounmap(mcbsp->io_base);
Russell Kingd592dd12008-09-04 14:25:42 +01001300err_ioremap:
Russell Kingb820ce42009-01-23 10:26:46 +00001301 kfree(mcbsp);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001302exit:
1303 return ret;
1304}
1305
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001306static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001307{
1308 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
1309
1310 platform_set_drvdata(pdev, NULL);
1311 if (mcbsp) {
1312
1313 if (mcbsp->pdata && mcbsp->pdata->ops &&
1314 mcbsp->pdata->ops->free)
1315 mcbsp->pdata->ops->free(mcbsp->id);
1316
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001317 omap34xx_device_exit(mcbsp);
1318
Russell Kingb820ce42009-01-23 10:26:46 +00001319 clk_disable(mcbsp->fclk);
1320 clk_disable(mcbsp->iclk);
1321 clk_put(mcbsp->fclk);
1322 clk_put(mcbsp->iclk);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001323
Russell Kingd592dd12008-09-04 14:25:42 +01001324 iounmap(mcbsp->io_base);
1325
Russell Kingb820ce42009-01-23 10:26:46 +00001326 mcbsp->fclk = NULL;
1327 mcbsp->iclk = NULL;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001328 mcbsp->free = 0;
1329 mcbsp->dev = NULL;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001330 }
1331
1332 return 0;
1333}
1334
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001335static struct platform_driver omap_mcbsp_driver = {
1336 .probe = omap_mcbsp_probe,
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001337 .remove = __devexit_p(omap_mcbsp_remove),
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001338 .driver = {
1339 .name = "omap-mcbsp",
1340 },
1341};
1342
1343int __init omap_mcbsp_init(void)
1344{
1345 /* Register the McBSP driver */
1346 return platform_driver_register(&omap_mcbsp_driver);
1347}