blob: e47686e0a63369e816ed965edf153e9667c660de [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
Eero Nurkkalad912fa92010-02-22 12:21:11 +000030#include "../mach-omap2/cm-regbits-34xx.h"
31
Chandra Shekharb4b58f52008-10-08 10:01:39 +030032struct omap_mcbsp **mcbsp_ptr;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080033int omap_mcbsp_count, omap_mcbsp_cache_size;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +030034
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080035void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030036{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080037 if (cpu_class_is_omap1()) {
38 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)] = (u16)val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080039 __raw_writew((u16)val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080040 } else if (cpu_is_omap2420()) {
41 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)] = (u16)val;
42 __raw_writew((u16)val, mcbsp->io_base + reg);
43 } else {
44 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)] = val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080045 __raw_writel(val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080046 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030047}
48
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080049int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030050{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080051 if (cpu_class_is_omap1()) {
52 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
53 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)];
54 } else if (cpu_is_omap2420()) {
55 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
56 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)];
57 } else {
58 return !from_cache ? __raw_readl(mcbsp->io_base + reg) :
59 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)];
60 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030061}
62
Eero Nurkkalad912fa92010-02-22 12:21:11 +000063#ifdef CONFIG_ARCH_OMAP3
64void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
65{
66 __raw_writel(val, mcbsp->st_data->io_base_st + reg);
67}
68
69int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
70{
71 return __raw_readl(mcbsp->st_data->io_base_st + reg);
72}
73#endif
74
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080075#define MCBSP_READ(mcbsp, reg) \
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080076 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080077#define MCBSP_WRITE(mcbsp, reg, val) \
78 omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080079#define MCBSP_READ_CACHE(mcbsp, reg) \
80 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030081
82#define omap_mcbsp_check_valid_id(id) (id < omap_mcbsp_count)
83#define id_to_mcbsp_ptr(id) mcbsp_ptr[id];
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010084
Eero Nurkkalad912fa92010-02-22 12:21:11 +000085#define MCBSP_ST_READ(mcbsp, reg) \
86 omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
87#define MCBSP_ST_WRITE(mcbsp, reg, val) \
88 omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
89
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010090static void omap_mcbsp_dump_reg(u8 id)
91{
Chandra Shekharb4b58f52008-10-08 10:01:39 +030092 struct omap_mcbsp *mcbsp = id_to_mcbsp_ptr(id);
93
94 dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
95 dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080096 MCBSP_READ(mcbsp, DRR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030097 dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080098 MCBSP_READ(mcbsp, DRR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030099 dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800100 MCBSP_READ(mcbsp, DXR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300101 dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800102 MCBSP_READ(mcbsp, DXR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300103 dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800104 MCBSP_READ(mcbsp, SPCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300105 dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800106 MCBSP_READ(mcbsp, SPCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300107 dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800108 MCBSP_READ(mcbsp, RCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300109 dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800110 MCBSP_READ(mcbsp, RCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300111 dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800112 MCBSP_READ(mcbsp, XCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300113 dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800114 MCBSP_READ(mcbsp, XCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300115 dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800116 MCBSP_READ(mcbsp, SRGR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300117 dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800118 MCBSP_READ(mcbsp, SRGR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300119 dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800120 MCBSP_READ(mcbsp, PCR0));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300121 dev_dbg(mcbsp->dev, "***********************\n");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100122}
123
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700124static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100125{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400126 struct omap_mcbsp *mcbsp_tx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700127 u16 irqst_spcr2;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100128
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800129 irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700130 dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100131
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700132 if (irqst_spcr2 & XSYNC_ERR) {
133 dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n",
134 irqst_spcr2);
135 /* Writing zero to XSYNC_ERR clears the IRQ */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800136 MCBSP_WRITE(mcbsp_tx, SPCR2,
137 MCBSP_READ_CACHE(mcbsp_tx, SPCR2) & ~(XSYNC_ERR));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700138 } else {
139 complete(&mcbsp_tx->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
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700145static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100146{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400147 struct omap_mcbsp *mcbsp_rx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700148 u16 irqst_spcr1;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100149
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800150 irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700151 dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100152
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700153 if (irqst_spcr1 & RSYNC_ERR) {
154 dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n",
155 irqst_spcr1);
156 /* Writing zero to RSYNC_ERR clears the IRQ */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800157 MCBSP_WRITE(mcbsp_rx, SPCR1,
158 MCBSP_READ_CACHE(mcbsp_rx, SPCR1) & ~(RSYNC_ERR));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700159 } else {
160 complete(&mcbsp_rx->tx_irq_completion);
161 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300162
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100163 return IRQ_HANDLED;
164}
165
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100166static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data)
167{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400168 struct omap_mcbsp *mcbsp_dma_tx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100169
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300170 dev_dbg(mcbsp_dma_tx->dev, "TX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800171 MCBSP_READ(mcbsp_dma_tx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100172
173 /* We can free the channels */
174 omap_free_dma(mcbsp_dma_tx->dma_tx_lch);
175 mcbsp_dma_tx->dma_tx_lch = -1;
176
177 complete(&mcbsp_dma_tx->tx_dma_completion);
178}
179
180static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data)
181{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400182 struct omap_mcbsp *mcbsp_dma_rx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100183
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300184 dev_dbg(mcbsp_dma_rx->dev, "RX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800185 MCBSP_READ(mcbsp_dma_rx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100186
187 /* We can free the channels */
188 omap_free_dma(mcbsp_dma_rx->dma_rx_lch);
189 mcbsp_dma_rx->dma_rx_lch = -1;
190
191 complete(&mcbsp_dma_rx->rx_dma_completion);
192}
193
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100194/*
195 * omap_mcbsp_config simply write a config to the
196 * appropriate McBSP.
197 * You either call this function or set the McBSP registers
198 * by yourself before calling omap_mcbsp_start().
199 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300200void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100201{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300202 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100203
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300204 if (!omap_mcbsp_check_valid_id(id)) {
205 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
206 return;
207 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300208 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300209
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300210 dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
211 mcbsp->id, mcbsp->phys_base);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100212
213 /* We write the given config */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800214 MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
215 MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
216 MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
217 MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
218 MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
219 MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
220 MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
221 MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
222 MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
223 MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
224 MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
Syed Rafiuddina5b92cc2009-07-28 18:57:10 +0530225 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800226 MCBSP_WRITE(mcbsp, XCCR, config->xccr);
227 MCBSP_WRITE(mcbsp, RCCR, config->rccr);
Tony Lindgren3127f8f2009-01-15 13:09:54 +0200228 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100229}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300230EXPORT_SYMBOL(omap_mcbsp_config);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100231
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -0800232#ifdef CONFIG_ARCH_OMAP3
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000233static void omap_st_on(struct omap_mcbsp *mcbsp)
234{
235 unsigned int w;
236
237 /*
238 * Sidetone uses McBSP ICLK - which must not idle when sidetones
239 * are enabled or sidetones start sounding ugly.
240 */
241 w = cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
242 w &= ~(1 << (mcbsp->id - 2));
243 cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
244
245 /* Enable McBSP Sidetone */
246 w = MCBSP_READ(mcbsp, SSELCR);
247 MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
248
249 w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
250 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
251
252 /* Enable Sidetone from Sidetone Core */
253 w = MCBSP_ST_READ(mcbsp, SSELCR);
254 MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
255}
256
257static void omap_st_off(struct omap_mcbsp *mcbsp)
258{
259 unsigned int w;
260
261 w = MCBSP_ST_READ(mcbsp, SSELCR);
262 MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
263
264 w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
265 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
266
267 w = MCBSP_READ(mcbsp, SSELCR);
268 MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
269
270 w = cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
271 w |= 1 << (mcbsp->id - 2);
272 cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
273}
274
275static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
276{
277 u16 val, i;
278
279 val = MCBSP_ST_READ(mcbsp, SYSCONFIG);
280 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, val & ~(ST_AUTOIDLE));
281
282 val = MCBSP_ST_READ(mcbsp, SSELCR);
283
284 if (val & ST_COEFFWREN)
285 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
286
287 MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
288
289 for (i = 0; i < 128; i++)
290 MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
291
292 i = 0;
293
294 val = MCBSP_ST_READ(mcbsp, SSELCR);
295 while (!(val & ST_COEFFWRDONE) && (++i < 1000))
296 val = MCBSP_ST_READ(mcbsp, SSELCR);
297
298 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
299
300 if (i == 1000)
301 dev_err(mcbsp->dev, "McBSP FIR load error!\n");
302}
303
304static void omap_st_chgain(struct omap_mcbsp *mcbsp)
305{
306 u16 w;
307 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
308
309 w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
310 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
311
312 w = MCBSP_ST_READ(mcbsp, SSELCR);
313
314 MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \
315 ST_CH1GAIN(st_data->ch1gain));
316}
317
318int omap_st_set_chgain(unsigned int id, int channel, s16 chgain)
319{
320 struct omap_mcbsp *mcbsp;
321 struct omap_mcbsp_st_data *st_data;
322 int ret = 0;
323
324 if (!omap_mcbsp_check_valid_id(id)) {
325 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
326 return -ENODEV;
327 }
328
329 mcbsp = id_to_mcbsp_ptr(id);
330 st_data = mcbsp->st_data;
331
332 if (!st_data)
333 return -ENOENT;
334
335 spin_lock_irq(&mcbsp->lock);
336 if (channel == 0)
337 st_data->ch0gain = chgain;
338 else if (channel == 1)
339 st_data->ch1gain = chgain;
340 else
341 ret = -EINVAL;
342
343 if (st_data->enabled)
344 omap_st_chgain(mcbsp);
345 spin_unlock_irq(&mcbsp->lock);
346
347 return ret;
348}
349EXPORT_SYMBOL(omap_st_set_chgain);
350
351int omap_st_get_chgain(unsigned int id, int channel, s16 *chgain)
352{
353 struct omap_mcbsp *mcbsp;
354 struct omap_mcbsp_st_data *st_data;
355 int ret = 0;
356
357 if (!omap_mcbsp_check_valid_id(id)) {
358 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
359 return -ENODEV;
360 }
361
362 mcbsp = id_to_mcbsp_ptr(id);
363 st_data = mcbsp->st_data;
364
365 if (!st_data)
366 return -ENOENT;
367
368 spin_lock_irq(&mcbsp->lock);
369 if (channel == 0)
370 *chgain = st_data->ch0gain;
371 else if (channel == 1)
372 *chgain = st_data->ch1gain;
373 else
374 ret = -EINVAL;
375 spin_unlock_irq(&mcbsp->lock);
376
377 return ret;
378}
379EXPORT_SYMBOL(omap_st_get_chgain);
380
381static int omap_st_start(struct omap_mcbsp *mcbsp)
382{
383 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
384
385 if (st_data && st_data->enabled && !st_data->running) {
386 omap_st_fir_write(mcbsp, st_data->taps);
387 omap_st_chgain(mcbsp);
388
389 if (!mcbsp->free) {
390 omap_st_on(mcbsp);
391 st_data->running = 1;
392 }
393 }
394
395 return 0;
396}
397
398int omap_st_enable(unsigned int id)
399{
400 struct omap_mcbsp *mcbsp;
401 struct omap_mcbsp_st_data *st_data;
402
403 if (!omap_mcbsp_check_valid_id(id)) {
404 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
405 return -ENODEV;
406 }
407
408 mcbsp = id_to_mcbsp_ptr(id);
409 st_data = mcbsp->st_data;
410
411 if (!st_data)
412 return -ENODEV;
413
414 spin_lock_irq(&mcbsp->lock);
415 st_data->enabled = 1;
416 omap_st_start(mcbsp);
417 spin_unlock_irq(&mcbsp->lock);
418
419 return 0;
420}
421EXPORT_SYMBOL(omap_st_enable);
422
423static int omap_st_stop(struct omap_mcbsp *mcbsp)
424{
425 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
426
427 if (st_data && st_data->running) {
428 if (!mcbsp->free) {
429 omap_st_off(mcbsp);
430 st_data->running = 0;
431 }
432 }
433
434 return 0;
435}
436
437int omap_st_disable(unsigned int id)
438{
439 struct omap_mcbsp *mcbsp;
440 struct omap_mcbsp_st_data *st_data;
441 int ret = 0;
442
443 if (!omap_mcbsp_check_valid_id(id)) {
444 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
445 return -ENODEV;
446 }
447
448 mcbsp = id_to_mcbsp_ptr(id);
449 st_data = mcbsp->st_data;
450
451 if (!st_data)
452 return -ENODEV;
453
454 spin_lock_irq(&mcbsp->lock);
455 omap_st_stop(mcbsp);
456 st_data->enabled = 0;
457 spin_unlock_irq(&mcbsp->lock);
458
459 return ret;
460}
461EXPORT_SYMBOL(omap_st_disable);
462
463int omap_st_is_enabled(unsigned int id)
464{
465 struct omap_mcbsp *mcbsp;
466 struct omap_mcbsp_st_data *st_data;
467
468 if (!omap_mcbsp_check_valid_id(id)) {
469 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
470 return -ENODEV;
471 }
472
473 mcbsp = id_to_mcbsp_ptr(id);
474 st_data = mcbsp->st_data;
475
476 if (!st_data)
477 return -ENODEV;
478
479
480 return st_data->enabled;
481}
482EXPORT_SYMBOL(omap_st_is_enabled);
483
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300484/*
485 * omap_mcbsp_set_tx_threshold configures how to deal
486 * with transmit threshold. the threshold value and handler can be
487 * configure in here.
488 */
489void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
490{
491 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300492
493 if (!cpu_is_omap34xx())
494 return;
495
496 if (!omap_mcbsp_check_valid_id(id)) {
497 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
498 return;
499 }
500 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300501
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800502 MCBSP_WRITE(mcbsp, THRSH2, threshold);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300503}
504EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
505
506/*
507 * omap_mcbsp_set_rx_threshold configures how to deal
508 * with receive threshold. the threshold value and handler can be
509 * configure in here.
510 */
511void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
512{
513 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300514
515 if (!cpu_is_omap34xx())
516 return;
517
518 if (!omap_mcbsp_check_valid_id(id)) {
519 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
520 return;
521 }
522 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300523
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800524 MCBSP_WRITE(mcbsp, THRSH1, threshold);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300525}
526EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +0300527
528/*
529 * omap_mcbsp_get_max_tx_thres just return the current configured
530 * maximum threshold for transmission
531 */
532u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
533{
534 struct omap_mcbsp *mcbsp;
535
536 if (!omap_mcbsp_check_valid_id(id)) {
537 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
538 return -ENODEV;
539 }
540 mcbsp = id_to_mcbsp_ptr(id);
541
542 return mcbsp->max_tx_thres;
543}
544EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
545
546/*
547 * omap_mcbsp_get_max_rx_thres just return the current configured
548 * maximum threshold for reception
549 */
550u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
551{
552 struct omap_mcbsp *mcbsp;
553
554 if (!omap_mcbsp_check_valid_id(id)) {
555 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
556 return -ENODEV;
557 }
558 mcbsp = id_to_mcbsp_ptr(id);
559
560 return mcbsp->max_rx_thres;
561}
562EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300563
564/*
565 * omap_mcbsp_get_dma_op_mode just return the current configured
566 * operating mode for the mcbsp channel
567 */
568int omap_mcbsp_get_dma_op_mode(unsigned int id)
569{
570 struct omap_mcbsp *mcbsp;
571 int dma_op_mode;
572
573 if (!omap_mcbsp_check_valid_id(id)) {
574 printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
575 return -ENODEV;
576 }
577 mcbsp = id_to_mcbsp_ptr(id);
578
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300579 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300580
581 return dma_op_mode;
582}
583EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300584
585static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
586{
587 /*
588 * Enable wakup behavior, smart idle and all wakeups
589 * REVISIT: some wakeups may be unnecessary
590 */
591 if (cpu_is_omap34xx()) {
592 u16 syscon;
593
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800594 syscon = MCBSP_READ(mcbsp, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300595 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eduardo Valentind99a7452009-08-20 16:18:18 +0300596
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300597 if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
598 syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
599 CLOCKACTIVITY(0x02));
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800600 MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300601 } else {
Eduardo Valentind99a7452009-08-20 16:18:18 +0300602 syscon |= SIDLEMODE(0x01);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300603 }
Eduardo Valentind99a7452009-08-20 16:18:18 +0300604
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800605 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300606 }
607}
608
609static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
610{
611 /*
612 * Disable wakup behavior, smart idle and all wakeups
613 */
614 if (cpu_is_omap34xx()) {
615 u16 syscon;
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300616
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800617 syscon = MCBSP_READ(mcbsp, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300618 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300619 /*
620 * HW bug workaround - If no_idle mode is taken, we need to
621 * go to smart_idle before going to always_idle, or the
622 * device will not hit retention anymore.
623 */
624 syscon |= SIDLEMODE(0x02);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800625 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300626
627 syscon &= ~(SIDLEMODE(0x03));
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800628 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300629
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800630 MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300631 }
632}
633#else
634static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
635static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000636static inline void omap_st_start(struct omap_mcbsp *mcbsp) {}
637static inline void omap_st_stop(struct omap_mcbsp *mcbsp) {}
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300638#endif
639
Tony Lindgren120db2c2006-04-02 17:46:27 +0100640/*
641 * We can choose between IRQ based or polled IO.
642 * This needs to be called before omap_mcbsp_request().
643 */
644int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type)
645{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300646 struct omap_mcbsp *mcbsp;
647
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300648 if (!omap_mcbsp_check_valid_id(id)) {
649 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
650 return -ENODEV;
651 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300652 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100653
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300654 spin_lock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100655
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300656 if (!mcbsp->free) {
657 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
658 mcbsp->id);
659 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100660 return -EINVAL;
661 }
662
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300663 mcbsp->io_type = io_type;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100664
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300665 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100666
667 return 0;
668}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300669EXPORT_SYMBOL(omap_mcbsp_set_io_type);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100670
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100671int omap_mcbsp_request(unsigned int id)
672{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300673 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800674 void *reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100675 int err;
676
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300677 if (!omap_mcbsp_check_valid_id(id)) {
678 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
679 return -ENODEV;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100680 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300681 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300682
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800683 reg_cache = kzalloc(omap_mcbsp_cache_size, GFP_KERNEL);
684 if (!reg_cache) {
685 return -ENOMEM;
686 }
687
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300688 spin_lock(&mcbsp->lock);
689 if (!mcbsp->free) {
690 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
691 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800692 err = -EBUSY;
693 goto err_kfree;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100694 }
695
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300696 mcbsp->free = 0;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800697 mcbsp->reg_cache = reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300698 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100699
Russell Kingb820ce42009-01-23 10:26:46 +0000700 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
701 mcbsp->pdata->ops->request(id);
702
703 clk_enable(mcbsp->iclk);
704 clk_enable(mcbsp->fclk);
705
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300706 /* Do procedure specific to omap34xx arch, if applicable */
707 omap34xx_mcbsp_request(mcbsp);
708
Jarkko Nikula5a070552008-10-08 10:01:41 +0300709 /*
710 * Make sure that transmitter, receiver and sample-rate generator are
711 * not running before activating IRQs.
712 */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800713 MCBSP_WRITE(mcbsp, SPCR1, 0);
714 MCBSP_WRITE(mcbsp, SPCR2, 0);
Jarkko Nikula5a070552008-10-08 10:01:41 +0300715
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300716 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
Tony Lindgren120db2c2006-04-02 17:46:27 +0100717 /* We need to get IRQs here */
Jarkko Nikula5a070552008-10-08 10:01:41 +0300718 init_completion(&mcbsp->tx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300719 err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
720 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100721 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300722 dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
723 "for McBSP%d\n", mcbsp->tx_irq,
724 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800725 goto err_clk_disable;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100726 }
727
Jarkko Nikula5a070552008-10-08 10:01:41 +0300728 init_completion(&mcbsp->rx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300729 err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler,
730 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100731 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300732 dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
733 "for McBSP%d\n", mcbsp->rx_irq,
734 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800735 goto err_free_irq;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100736 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100737 }
738
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100739 return 0;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800740err_free_irq:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800741 free_irq(mcbsp->tx_irq, (void *)mcbsp);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800742err_clk_disable:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800743 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800744 mcbsp->pdata->ops->free(id);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800745
746 /* Do procedure specific to omap34xx arch, if applicable */
747 omap34xx_mcbsp_free(mcbsp);
748
749 clk_disable(mcbsp->fclk);
750 clk_disable(mcbsp->iclk);
751
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800752 spin_lock(&mcbsp->lock);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800753 mcbsp->free = 1;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800754 mcbsp->reg_cache = NULL;
755err_kfree:
756 spin_unlock(&mcbsp->lock);
757 kfree(reg_cache);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800758
759 return err;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100760}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300761EXPORT_SYMBOL(omap_mcbsp_request);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100762
763void omap_mcbsp_free(unsigned int id)
764{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300765 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800766 void *reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300767
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300768 if (!omap_mcbsp_check_valid_id(id)) {
769 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100770 return;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100771 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300772 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100773
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300774 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
775 mcbsp->pdata->ops->free(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300776
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300777 /* Do procedure specific to omap34xx arch, if applicable */
778 omap34xx_mcbsp_free(mcbsp);
779
Russell Kingb820ce42009-01-23 10:26:46 +0000780 clk_disable(mcbsp->fclk);
781 clk_disable(mcbsp->iclk);
782
783 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
784 /* Free IRQs */
785 free_irq(mcbsp->rx_irq, (void *)mcbsp);
786 free_irq(mcbsp->tx_irq, (void *)mcbsp);
787 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100788
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800789 reg_cache = mcbsp->reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100790
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800791 spin_lock(&mcbsp->lock);
792 if (mcbsp->free)
793 dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
794 else
795 mcbsp->free = 1;
796 mcbsp->reg_cache = NULL;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300797 spin_unlock(&mcbsp->lock);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800798
799 if (reg_cache)
800 kfree(reg_cache);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100801}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300802EXPORT_SYMBOL(omap_mcbsp_free);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100803
804/*
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300805 * Here we start the McBSP, by enabling transmitter, receiver or both.
806 * If no transmitter or receiver is active prior calling, then sample-rate
807 * generator and frame sync are started.
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100808 */
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300809void omap_mcbsp_start(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100810{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300811 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300812 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100813 u16 w;
814
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300815 if (!omap_mcbsp_check_valid_id(id)) {
816 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100817 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300818 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300819 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100820
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000821 if (cpu_is_omap34xx())
822 omap_st_start(mcbsp);
823
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800824 mcbsp->rx_word_length = (MCBSP_READ_CACHE(mcbsp, RCR1) >> 5) & 0x7;
825 mcbsp->tx_word_length = (MCBSP_READ_CACHE(mcbsp, XCR1) >> 5) & 0x7;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100826
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800827 idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
828 MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300829
830 if (idle) {
831 /* Start the sample generator */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800832 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800833 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300834 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100835
836 /* Enable transmitter and receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300837 tx &= 1;
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800838 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800839 MCBSP_WRITE(mcbsp, SPCR2, w | tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100840
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300841 rx &= 1;
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800842 w = MCBSP_READ_CACHE(mcbsp, SPCR1);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800843 MCBSP_WRITE(mcbsp, SPCR1, w | rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100844
Eduardo Valentin44a63112009-08-20 16:18:09 +0300845 /*
846 * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
847 * REVISIT: 100us may give enough time for two CLKSRG, however
848 * due to some unknown PM related, clock gating etc. reason it
849 * is now at 500us.
850 */
851 udelay(500);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100852
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300853 if (idle) {
854 /* Start frame sync */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800855 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800856 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300857 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100858
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300859 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
860 /* Release the transmitter and receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800861 w = MCBSP_READ_CACHE(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300862 w &= ~(tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800863 MCBSP_WRITE(mcbsp, XCCR, w);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800864 w = MCBSP_READ_CACHE(mcbsp, RCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300865 w &= ~(rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800866 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300867 }
868
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100869 /* Dump McBSP Regs */
870 omap_mcbsp_dump_reg(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100871}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300872EXPORT_SYMBOL(omap_mcbsp_start);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100873
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300874void omap_mcbsp_stop(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100875{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300876 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300877 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100878 u16 w;
879
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300880 if (!omap_mcbsp_check_valid_id(id)) {
881 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100882 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300883 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100884
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300885 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100886
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300887 /* Reset transmitter */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300888 tx &= 1;
889 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800890 w = MCBSP_READ_CACHE(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300891 w |= (tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800892 MCBSP_WRITE(mcbsp, XCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300893 }
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800894 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800895 MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100896
897 /* Reset receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300898 rx &= 1;
899 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800900 w = MCBSP_READ_CACHE(mcbsp, RCCR);
Jarkko Nikulaa93d4ed2009-10-14 09:56:35 -0700901 w |= (rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800902 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300903 }
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800904 w = MCBSP_READ_CACHE(mcbsp, SPCR1);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800905 MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100906
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800907 idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
908 MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300909
910 if (idle) {
911 /* Reset the sample rate generator */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800912 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800913 MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300914 }
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000915
916 if (cpu_is_omap34xx())
917 omap_st_stop(mcbsp);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100918}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300919EXPORT_SYMBOL(omap_mcbsp_stop);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100920
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100921/* polled mcbsp i/o operations */
922int omap_mcbsp_pollwrite(unsigned int id, u16 buf)
923{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300924 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300925
926 if (!omap_mcbsp_check_valid_id(id)) {
927 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
928 return -ENODEV;
929 }
930
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300931 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300932
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800933 MCBSP_WRITE(mcbsp, DXR1, buf);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100934 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800935 if (MCBSP_READ(mcbsp, SPCR2) & XSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100936 /* clear error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800937 MCBSP_WRITE(mcbsp, SPCR2,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800938 MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XSYNC_ERR));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100939 /* resend */
940 return -1;
941 } else {
942 /* wait for transmit confirmation */
943 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800944 while (!(MCBSP_READ(mcbsp, SPCR2) & XRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100945 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800946 MCBSP_WRITE(mcbsp, SPCR2,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800947 MCBSP_READ_CACHE(mcbsp, SPCR2) &
948 (~XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100949 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800950 MCBSP_WRITE(mcbsp, SPCR2,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800951 MCBSP_READ_CACHE(mcbsp, SPCR2) |
952 (XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100953 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300954 dev_err(mcbsp->dev, "Could not write to"
955 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100956 return -2;
957 }
958 }
959 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300960
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100961 return 0;
962}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300963EXPORT_SYMBOL(omap_mcbsp_pollwrite);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100964
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300965int omap_mcbsp_pollread(unsigned int id, u16 *buf)
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100966{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300967 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300968
969 if (!omap_mcbsp_check_valid_id(id)) {
970 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
971 return -ENODEV;
972 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300973 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300974
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100975 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800976 if (MCBSP_READ(mcbsp, SPCR1) & RSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100977 /* clear error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800978 MCBSP_WRITE(mcbsp, SPCR1,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800979 MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RSYNC_ERR));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100980 /* resend */
981 return -1;
982 } else {
983 /* wait for recieve confirmation */
984 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800985 while (!(MCBSP_READ(mcbsp, SPCR1) & RRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100986 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800987 MCBSP_WRITE(mcbsp, SPCR1,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800988 MCBSP_READ_CACHE(mcbsp, SPCR1) &
989 (~RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100990 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800991 MCBSP_WRITE(mcbsp, SPCR1,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800992 MCBSP_READ_CACHE(mcbsp, SPCR1) |
993 (RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100994 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300995 dev_err(mcbsp->dev, "Could not read from"
996 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100997 return -2;
998 }
999 }
1000 }
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001001 *buf = MCBSP_READ(mcbsp, DRR1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001002
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001003 return 0;
1004}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001005EXPORT_SYMBOL(omap_mcbsp_pollread);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001006
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001007/*
1008 * IRQ based word transmission.
1009 */
1010void omap_mcbsp_xmit_word(unsigned int id, u32 word)
1011{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001012 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001013 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001014
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001015 if (!omap_mcbsp_check_valid_id(id)) {
1016 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001017 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001018 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001019
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001020 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001021 word_length = mcbsp->tx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001022
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001023 wait_for_completion(&mcbsp->tx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001024
1025 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001026 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
1027 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001028}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001029EXPORT_SYMBOL(omap_mcbsp_xmit_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001030
1031u32 omap_mcbsp_recv_word(unsigned int id)
1032{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001033 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001034 u16 word_lsb, word_msb = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001035 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001036
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001037 if (!omap_mcbsp_check_valid_id(id)) {
1038 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1039 return -ENODEV;
1040 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001041 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001042
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001043 word_length = mcbsp->rx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001044
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001045 wait_for_completion(&mcbsp->rx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001046
1047 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001048 word_msb = MCBSP_READ(mcbsp, DRR2);
1049 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001050
1051 return (word_lsb | (word_msb << 16));
1052}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001053EXPORT_SYMBOL(omap_mcbsp_recv_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001054
Tony Lindgren120db2c2006-04-02 17:46:27 +01001055int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word)
1056{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001057 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001058 omap_mcbsp_word_length tx_word_length;
1059 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001060 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
1061
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001062 if (!omap_mcbsp_check_valid_id(id)) {
1063 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1064 return -ENODEV;
1065 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001066 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001067 tx_word_length = mcbsp->tx_word_length;
1068 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001069
Tony Lindgren120db2c2006-04-02 17:46:27 +01001070 if (tx_word_length != rx_word_length)
1071 return -EINVAL;
1072
1073 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001074 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001075 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001076 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001077 if (attempts++ > 1000) {
1078 /* We must reset the transmitter */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001079 MCBSP_WRITE(mcbsp, SPCR2,
1080 MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001081 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001082 MCBSP_WRITE(mcbsp, SPCR2,
1083 MCBSP_READ_CACHE(mcbsp, SPCR2) | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001084 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001085 dev_err(mcbsp->dev, "McBSP%d transmitter not "
1086 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001087 return -EAGAIN;
1088 }
1089 }
1090
1091 /* Now we can push the data */
1092 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001093 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
1094 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001095
1096 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001097 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001098 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001099 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001100 if (attempts++ > 1000) {
1101 /* We must reset the receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001102 MCBSP_WRITE(mcbsp, SPCR1,
1103 MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001104 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001105 MCBSP_WRITE(mcbsp, SPCR1,
1106 MCBSP_READ_CACHE(mcbsp, SPCR1) | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001107 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001108 dev_err(mcbsp->dev, "McBSP%d receiver not "
1109 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001110 return -EAGAIN;
1111 }
1112 }
1113
1114 /* Receiver is ready, let's read the dummy data */
1115 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001116 word_msb = MCBSP_READ(mcbsp, DRR2);
1117 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001118
1119 return 0;
1120}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001121EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001122
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001123int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 *word)
Tony Lindgren120db2c2006-04-02 17:46:27 +01001124{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001125 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +01001126 u32 clock_word = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001127 omap_mcbsp_word_length tx_word_length;
1128 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001129 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
1130
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001131 if (!omap_mcbsp_check_valid_id(id)) {
1132 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1133 return -ENODEV;
1134 }
1135
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001136 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001137
1138 tx_word_length = mcbsp->tx_word_length;
1139 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001140
Tony Lindgren120db2c2006-04-02 17:46:27 +01001141 if (tx_word_length != rx_word_length)
1142 return -EINVAL;
1143
1144 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001145 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001146 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001147 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001148 if (attempts++ > 1000) {
1149 /* We must reset the transmitter */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001150 MCBSP_WRITE(mcbsp, SPCR2,
1151 MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001152 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001153 MCBSP_WRITE(mcbsp, SPCR2,
1154 MCBSP_READ_CACHE(mcbsp, SPCR2) | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001155 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001156 dev_err(mcbsp->dev, "McBSP%d transmitter not "
1157 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001158 return -EAGAIN;
1159 }
1160 }
1161
1162 /* We first need to enable the bus clock */
1163 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001164 MCBSP_WRITE(mcbsp, DXR2, clock_word >> 16);
1165 MCBSP_WRITE(mcbsp, DXR1, clock_word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001166
1167 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001168 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001169 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001170 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001171 if (attempts++ > 1000) {
1172 /* We must reset the receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001173 MCBSP_WRITE(mcbsp, SPCR1,
1174 MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001175 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001176 MCBSP_WRITE(mcbsp, SPCR1,
1177 MCBSP_READ_CACHE(mcbsp, SPCR1) | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001178 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001179 dev_err(mcbsp->dev, "McBSP%d receiver not "
1180 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001181 return -EAGAIN;
1182 }
1183 }
1184
1185 /* Receiver is ready, there is something for us */
1186 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001187 word_msb = MCBSP_READ(mcbsp, DRR2);
1188 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001189
1190 word[0] = (word_lsb | (word_msb << 16));
1191
1192 return 0;
1193}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001194EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001195
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001196/*
1197 * Simple DMA based buffer rx/tx routines.
1198 * Nothing fancy, just a single buffer tx/rx through DMA.
1199 * The DMA resources are released once the transfer is done.
1200 * For anything fancier, you should use your own customized DMA
1201 * routines and callbacks.
1202 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001203int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer,
1204 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001205{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001206 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001207 int dma_tx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001208 int src_port = 0;
1209 int dest_port = 0;
1210 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001211
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001212 if (!omap_mcbsp_check_valid_id(id)) {
1213 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1214 return -ENODEV;
1215 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001216 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001217
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001218 if (omap_request_dma(mcbsp->dma_tx_sync, "McBSP TX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001219 omap_mcbsp_tx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001220 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001221 &dma_tx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001222 dev_err(mcbsp->dev, " Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001223 "McBSP%d TX. Trying IRQ based TX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001224 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001225 return -EAGAIN;
1226 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001227 mcbsp->dma_tx_lch = dma_tx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001228
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001229 dev_err(mcbsp->dev, "McBSP%d TX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001230 dma_tx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001231
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001232 init_completion(&mcbsp->tx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001233
Tony Lindgren120db2c2006-04-02 17:46:27 +01001234 if (cpu_class_is_omap1()) {
1235 src_port = OMAP_DMA_PORT_TIPB;
1236 dest_port = OMAP_DMA_PORT_EMIFF;
1237 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001238 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001239 sync_dev = mcbsp->dma_tx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001240
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001241 omap_set_dma_transfer_params(mcbsp->dma_tx_lch,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001242 OMAP_DMA_DATA_TYPE_S16,
1243 length >> 1, 1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001244 OMAP_DMA_SYNC_ELEMENT,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001245 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001246
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001247 omap_set_dma_dest_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001248 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001249 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001250 mcbsp->phys_base + OMAP_MCBSP_REG_DXR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001251 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001252
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001253 omap_set_dma_src_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001254 dest_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001255 OMAP_DMA_AMODE_POST_INC,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001256 buffer,
1257 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001258
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001259 omap_start_dma(mcbsp->dma_tx_lch);
1260 wait_for_completion(&mcbsp->tx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001261
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001262 return 0;
1263}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001264EXPORT_SYMBOL(omap_mcbsp_xmit_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001265
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001266int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer,
1267 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001268{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001269 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001270 int dma_rx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001271 int src_port = 0;
1272 int dest_port = 0;
1273 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001274
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001275 if (!omap_mcbsp_check_valid_id(id)) {
1276 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1277 return -ENODEV;
1278 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001279 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001280
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001281 if (omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001282 omap_mcbsp_rx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001283 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001284 &dma_rx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001285 dev_err(mcbsp->dev, "Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001286 "McBSP%d RX. Trying IRQ based RX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001287 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001288 return -EAGAIN;
1289 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001290 mcbsp->dma_rx_lch = dma_rx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001291
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001292 dev_err(mcbsp->dev, "McBSP%d RX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001293 dma_rx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001294
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001295 init_completion(&mcbsp->rx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001296
Tony Lindgren120db2c2006-04-02 17:46:27 +01001297 if (cpu_class_is_omap1()) {
1298 src_port = OMAP_DMA_PORT_TIPB;
1299 dest_port = OMAP_DMA_PORT_EMIFF;
1300 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001301 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001302 sync_dev = mcbsp->dma_rx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001303
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001304 omap_set_dma_transfer_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001305 OMAP_DMA_DATA_TYPE_S16,
1306 length >> 1, 1,
1307 OMAP_DMA_SYNC_ELEMENT,
1308 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001309
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001310 omap_set_dma_src_params(mcbsp->dma_rx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001311 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001312 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001313 mcbsp->phys_base + OMAP_MCBSP_REG_DRR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001314 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001315
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001316 omap_set_dma_dest_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001317 dest_port,
1318 OMAP_DMA_AMODE_POST_INC,
1319 buffer,
1320 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001321
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001322 omap_start_dma(mcbsp->dma_rx_lch);
1323 wait_for_completion(&mcbsp->rx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001324
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001325 return 0;
1326}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001327EXPORT_SYMBOL(omap_mcbsp_recv_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001328
1329/*
1330 * SPI wrapper.
1331 * Since SPI setup is much simpler than the generic McBSP one,
1332 * this wrapper just need an omap_mcbsp_spi_cfg structure as an input.
1333 * Once this is done, you can call omap_mcbsp_start().
1334 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001335void omap_mcbsp_set_spi_mode(unsigned int id,
1336 const struct omap_mcbsp_spi_cfg *spi_cfg)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001337{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001338 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001339 struct omap_mcbsp_reg_cfg mcbsp_cfg;
1340
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001341 if (!omap_mcbsp_check_valid_id(id)) {
1342 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001343 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001344 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001345 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001346
1347 memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg));
1348
1349 /* SPI has only one frame */
1350 mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0));
1351 mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0));
1352
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001353 /* Clock stop mode */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001354 if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY)
1355 mcbsp_cfg.spcr1 |= (1 << 12);
1356 else
1357 mcbsp_cfg.spcr1 |= (3 << 11);
1358
1359 /* Set clock parities */
1360 if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1361 mcbsp_cfg.pcr0 |= CLKRP;
1362 else
1363 mcbsp_cfg.pcr0 &= ~CLKRP;
1364
1365 if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1366 mcbsp_cfg.pcr0 &= ~CLKXP;
1367 else
1368 mcbsp_cfg.pcr0 |= CLKXP;
1369
1370 /* Set SCLKME to 0 and CLKSM to 1 */
1371 mcbsp_cfg.pcr0 &= ~SCLKME;
1372 mcbsp_cfg.srgr2 |= CLKSM;
1373
1374 /* Set FSXP */
1375 if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH)
1376 mcbsp_cfg.pcr0 &= ~FSXP;
1377 else
1378 mcbsp_cfg.pcr0 |= FSXP;
1379
1380 if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) {
1381 mcbsp_cfg.pcr0 |= CLKXM;
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001382 mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div - 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001383 mcbsp_cfg.pcr0 |= FSXM;
1384 mcbsp_cfg.srgr2 &= ~FSGM;
1385 mcbsp_cfg.xcr2 |= XDATDLY(1);
1386 mcbsp_cfg.rcr2 |= RDATDLY(1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001387 } else {
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001388 mcbsp_cfg.pcr0 &= ~CLKXM;
1389 mcbsp_cfg.srgr1 |= CLKGDV(1);
1390 mcbsp_cfg.pcr0 &= ~FSXM;
1391 mcbsp_cfg.xcr2 &= ~XDATDLY(3);
1392 mcbsp_cfg.rcr2 &= ~RDATDLY(3);
1393 }
1394
1395 mcbsp_cfg.xcr2 &= ~XPHASE;
1396 mcbsp_cfg.rcr2 &= ~RPHASE;
1397
1398 omap_mcbsp_config(id, &mcbsp_cfg);
1399}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001400EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001401
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001402#ifdef CONFIG_ARCH_OMAP3
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001403#define max_thres(m) (mcbsp->pdata->buffer_size)
1404#define valid_threshold(m, val) ((val) <= max_thres(m))
1405#define THRESHOLD_PROP_BUILDER(prop) \
1406static ssize_t prop##_show(struct device *dev, \
1407 struct device_attribute *attr, char *buf) \
1408{ \
1409 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1410 \
1411 return sprintf(buf, "%u\n", mcbsp->prop); \
1412} \
1413 \
1414static ssize_t prop##_store(struct device *dev, \
1415 struct device_attribute *attr, \
1416 const char *buf, size_t size) \
1417{ \
1418 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1419 unsigned long val; \
1420 int status; \
1421 \
1422 status = strict_strtoul(buf, 0, &val); \
1423 if (status) \
1424 return status; \
1425 \
1426 if (!valid_threshold(mcbsp, val)) \
1427 return -EDOM; \
1428 \
1429 mcbsp->prop = val; \
1430 return size; \
1431} \
1432 \
1433static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
1434
1435THRESHOLD_PROP_BUILDER(max_tx_thres);
1436THRESHOLD_PROP_BUILDER(max_rx_thres);
1437
Jarkko Nikula9b300502009-08-24 17:45:50 +03001438static const char *dma_op_modes[] = {
1439 "element", "threshold", "frame",
1440};
1441
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001442static ssize_t dma_op_mode_show(struct device *dev,
1443 struct device_attribute *attr, char *buf)
1444{
1445 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001446 int dma_op_mode, i = 0;
1447 ssize_t len = 0;
1448 const char * const *s;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001449
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001450 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001451
Jarkko Nikula9b300502009-08-24 17:45:50 +03001452 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
1453 if (dma_op_mode == i)
1454 len += sprintf(buf + len, "[%s] ", *s);
1455 else
1456 len += sprintf(buf + len, "%s ", *s);
1457 }
1458 len += sprintf(buf + len, "\n");
1459
1460 return len;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001461}
1462
1463static ssize_t dma_op_mode_store(struct device *dev,
1464 struct device_attribute *attr,
1465 const char *buf, size_t size)
1466{
1467 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001468 const char * const *s;
1469 int i = 0;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001470
Jarkko Nikula9b300502009-08-24 17:45:50 +03001471 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
1472 if (sysfs_streq(buf, *s))
1473 break;
1474
1475 if (i == ARRAY_SIZE(dma_op_modes))
1476 return -EINVAL;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001477
1478 spin_lock_irq(&mcbsp->lock);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001479 if (!mcbsp->free) {
1480 size = -EBUSY;
1481 goto unlock;
1482 }
Jarkko Nikula9b300502009-08-24 17:45:50 +03001483 mcbsp->dma_op_mode = i;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001484
1485unlock:
1486 spin_unlock_irq(&mcbsp->lock);
1487
1488 return size;
1489}
1490
1491static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
1492
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001493static ssize_t st_taps_show(struct device *dev,
1494 struct device_attribute *attr, char *buf)
1495{
1496 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1497 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1498 ssize_t status = 0;
1499 int i;
1500
1501 spin_lock_irq(&mcbsp->lock);
1502 for (i = 0; i < st_data->nr_taps; i++)
1503 status += sprintf(&buf[status], (i ? ", %d" : "%d"),
1504 st_data->taps[i]);
1505 if (i)
1506 status += sprintf(&buf[status], "\n");
1507 spin_unlock_irq(&mcbsp->lock);
1508
1509 return status;
1510}
1511
1512static ssize_t st_taps_store(struct device *dev,
1513 struct device_attribute *attr,
1514 const char *buf, size_t size)
1515{
1516 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1517 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1518 int val, tmp, status, i = 0;
1519
1520 spin_lock_irq(&mcbsp->lock);
1521 memset(st_data->taps, 0, sizeof(st_data->taps));
1522 st_data->nr_taps = 0;
1523
1524 do {
1525 status = sscanf(buf, "%d%n", &val, &tmp);
1526 if (status < 0 || status == 0) {
1527 size = -EINVAL;
1528 goto out;
1529 }
1530 if (val < -32768 || val > 32767) {
1531 size = -EINVAL;
1532 goto out;
1533 }
1534 st_data->taps[i++] = val;
1535 buf += tmp;
1536 if (*buf != ',')
1537 break;
1538 buf++;
1539 } while (1);
1540
1541 st_data->nr_taps = i;
1542
1543out:
1544 spin_unlock_irq(&mcbsp->lock);
1545
1546 return size;
1547}
1548
1549static DEVICE_ATTR(st_taps, 0644, st_taps_show, st_taps_store);
1550
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001551static const struct attribute *additional_attrs[] = {
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001552 &dev_attr_max_tx_thres.attr,
1553 &dev_attr_max_rx_thres.attr,
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001554 &dev_attr_dma_op_mode.attr,
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001555 NULL,
1556};
1557
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001558static const struct attribute_group additional_attr_group = {
1559 .attrs = (struct attribute **)additional_attrs,
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001560};
1561
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001562static inline int __devinit omap_additional_add(struct device *dev)
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001563{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001564 return sysfs_create_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001565}
1566
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001567static inline void __devexit omap_additional_remove(struct device *dev)
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001568{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001569 sysfs_remove_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001570}
1571
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001572static const struct attribute *sidetone_attrs[] = {
1573 &dev_attr_st_taps.attr,
1574 NULL,
1575};
1576
1577static const struct attribute_group sidetone_attr_group = {
1578 .attrs = (struct attribute **)sidetone_attrs,
1579};
1580
1581int __devinit omap_st_add(struct omap_mcbsp *mcbsp)
1582{
1583 struct omap_mcbsp_platform_data *pdata = mcbsp->pdata;
1584 struct omap_mcbsp_st_data *st_data;
1585 int err;
1586
1587 st_data = kzalloc(sizeof(*mcbsp->st_data), GFP_KERNEL);
1588 if (!st_data) {
1589 err = -ENOMEM;
1590 goto err1;
1591 }
1592
1593 st_data->io_base_st = ioremap(pdata->phys_base_st, SZ_4K);
1594 if (!st_data->io_base_st) {
1595 err = -ENOMEM;
1596 goto err2;
1597 }
1598
1599 err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
1600 if (err)
1601 goto err3;
1602
1603 mcbsp->st_data = st_data;
1604 return 0;
1605
1606err3:
1607 iounmap(st_data->io_base_st);
1608err2:
1609 kfree(st_data);
1610err1:
1611 return err;
1612
1613}
1614
1615static void __devexit omap_st_remove(struct omap_mcbsp *mcbsp)
1616{
1617 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1618
1619 if (st_data) {
1620 sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
1621 iounmap(st_data->io_base_st);
1622 kfree(st_data);
1623 }
1624}
1625
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001626static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
1627{
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001628 mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001629 if (cpu_is_omap34xx()) {
1630 mcbsp->max_tx_thres = max_thres(mcbsp);
1631 mcbsp->max_rx_thres = max_thres(mcbsp);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001632 /*
1633 * REVISIT: Set dmap_op_mode to THRESHOLD as default
1634 * for mcbsp2 instances.
1635 */
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001636 if (omap_additional_add(mcbsp->dev))
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001637 dev_warn(mcbsp->dev,
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001638 "Unable to create additional controls\n");
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001639
1640 if (mcbsp->id == 2 || mcbsp->id == 3)
1641 if (omap_st_add(mcbsp))
1642 dev_warn(mcbsp->dev,
1643 "Unable to create sidetone controls\n");
1644
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001645 } else {
1646 mcbsp->max_tx_thres = -EINVAL;
1647 mcbsp->max_rx_thres = -EINVAL;
1648 }
1649}
1650
1651static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
1652{
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001653 if (cpu_is_omap34xx()) {
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001654 omap_additional_remove(mcbsp->dev);
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001655
1656 if (mcbsp->id == 2 || mcbsp->id == 3)
1657 omap_st_remove(mcbsp);
1658 }
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001659}
1660#else
1661static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
1662static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001663#endif /* CONFIG_ARCH_OMAP3 */
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001664
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001665/*
1666 * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
1667 * 730 has only 2 McBSP, and both of them are MPU peripherals.
1668 */
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001669static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001670{
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001671 struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001672 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001673 int id = pdev->id - 1;
1674 int ret = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001675
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001676 if (!pdata) {
1677 dev_err(&pdev->dev, "McBSP device initialized without"
1678 "platform data\n");
1679 ret = -EINVAL;
1680 goto exit;
1681 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001682
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001683 dev_dbg(&pdev->dev, "Initializing OMAP McBSP (%d).\n", pdev->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001684
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001685 if (id >= omap_mcbsp_count) {
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001686 dev_err(&pdev->dev, "Invalid McBSP device id (%d)\n", id);
1687 ret = -EINVAL;
1688 goto exit;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001689 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001690
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001691 mcbsp = kzalloc(sizeof(struct omap_mcbsp), GFP_KERNEL);
1692 if (!mcbsp) {
1693 ret = -ENOMEM;
1694 goto exit;
1695 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001696
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001697 spin_lock_init(&mcbsp->lock);
1698 mcbsp->id = id + 1;
1699 mcbsp->free = 1;
1700 mcbsp->dma_tx_lch = -1;
1701 mcbsp->dma_rx_lch = -1;
1702
1703 mcbsp->phys_base = pdata->phys_base;
1704 mcbsp->io_base = ioremap(pdata->phys_base, SZ_4K);
1705 if (!mcbsp->io_base) {
Russell Kingd592dd12008-09-04 14:25:42 +01001706 ret = -ENOMEM;
1707 goto err_ioremap;
1708 }
1709
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001710 /* Default I/O is IRQ based */
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001711 mcbsp->io_type = OMAP_MCBSP_IRQ_IO;
1712 mcbsp->tx_irq = pdata->tx_irq;
1713 mcbsp->rx_irq = pdata->rx_irq;
1714 mcbsp->dma_rx_sync = pdata->dma_rx_sync;
1715 mcbsp->dma_tx_sync = pdata->dma_tx_sync;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001716
Russell Kingb820ce42009-01-23 10:26:46 +00001717 mcbsp->iclk = clk_get(&pdev->dev, "ick");
1718 if (IS_ERR(mcbsp->iclk)) {
1719 ret = PTR_ERR(mcbsp->iclk);
1720 dev_err(&pdev->dev, "unable to get ick: %d\n", ret);
1721 goto err_iclk;
1722 }
Stanley.Miao06151152009-01-29 08:57:12 -08001723
Russell Kingb820ce42009-01-23 10:26:46 +00001724 mcbsp->fclk = clk_get(&pdev->dev, "fck");
1725 if (IS_ERR(mcbsp->fclk)) {
1726 ret = PTR_ERR(mcbsp->fclk);
1727 dev_err(&pdev->dev, "unable to get fck: %d\n", ret);
1728 goto err_fclk;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001729 }
1730
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001731 mcbsp->pdata = pdata;
1732 mcbsp->dev = &pdev->dev;
Russell Kingb820ce42009-01-23 10:26:46 +00001733 mcbsp_ptr[id] = mcbsp;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001734 platform_set_drvdata(pdev, mcbsp);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001735
1736 /* Initialize mcbsp properties for OMAP34XX if needed / applicable */
1737 omap34xx_device_init(mcbsp);
1738
Russell Kingd592dd12008-09-04 14:25:42 +01001739 return 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001740
Russell Kingb820ce42009-01-23 10:26:46 +00001741err_fclk:
1742 clk_put(mcbsp->iclk);
1743err_iclk:
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001744 iounmap(mcbsp->io_base);
Russell Kingd592dd12008-09-04 14:25:42 +01001745err_ioremap:
Russell Kingb820ce42009-01-23 10:26:46 +00001746 kfree(mcbsp);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001747exit:
1748 return ret;
1749}
1750
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001751static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001752{
1753 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
1754
1755 platform_set_drvdata(pdev, NULL);
1756 if (mcbsp) {
1757
1758 if (mcbsp->pdata && mcbsp->pdata->ops &&
1759 mcbsp->pdata->ops->free)
1760 mcbsp->pdata->ops->free(mcbsp->id);
1761
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001762 omap34xx_device_exit(mcbsp);
1763
Russell Kingb820ce42009-01-23 10:26:46 +00001764 clk_disable(mcbsp->fclk);
1765 clk_disable(mcbsp->iclk);
1766 clk_put(mcbsp->fclk);
1767 clk_put(mcbsp->iclk);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001768
Russell Kingd592dd12008-09-04 14:25:42 +01001769 iounmap(mcbsp->io_base);
1770
Russell Kingb820ce42009-01-23 10:26:46 +00001771 mcbsp->fclk = NULL;
1772 mcbsp->iclk = NULL;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001773 mcbsp->free = 0;
1774 mcbsp->dev = NULL;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001775 }
1776
1777 return 0;
1778}
1779
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001780static struct platform_driver omap_mcbsp_driver = {
1781 .probe = omap_mcbsp_probe,
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001782 .remove = __devexit_p(omap_mcbsp_remove),
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001783 .driver = {
1784 .name = "omap-mcbsp",
1785 },
1786};
1787
1788int __init omap_mcbsp_init(void)
1789{
1790 /* Register the McBSP driver */
1791 return platform_driver_register(&omap_mcbsp_driver);
1792}