aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/plat-omap/mcbsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-omap/mcbsp.c')
-rw-r--r--arch/arm/plat-omap/mcbsp.c604
1 files changed, 569 insertions, 35 deletions
diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c
index 6c62af10871..5587acf0eb2 100644
--- a/arch/arm/plat-omap/mcbsp.c
+++ b/arch/arm/plat-omap/mcbsp.c
@@ -16,6 +16,8 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/wait.h>
+#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/clk.h>
@@ -23,6 +25,7 @@
#include <linux/io.h>
#include <linux/slab.h>
+#include <plat/dma.h>
#include <plat/mcbsp.h>
#include <plat/omap_device.h>
#include <linux/pm_runtime.h>
@@ -133,6 +136,8 @@ static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
irqst_spcr2);
/* Writing zero to XSYNC_ERR clears the IRQ */
MCBSP_WRITE(mcbsp_tx, SPCR2, MCBSP_READ_CACHE(mcbsp_tx, SPCR2));
+ } else {
+ complete(&mcbsp_tx->tx_irq_completion);
}
return IRQ_HANDLED;
@@ -151,11 +156,41 @@ static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
irqst_spcr1);
/* Writing zero to RSYNC_ERR clears the IRQ */
MCBSP_WRITE(mcbsp_rx, SPCR1, MCBSP_READ_CACHE(mcbsp_rx, SPCR1));
+ } else {
+ complete(&mcbsp_rx->rx_irq_completion);
}
return IRQ_HANDLED;
}
+static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data)
+{
+ struct omap_mcbsp *mcbsp_dma_tx = data;
+
+ dev_dbg(mcbsp_dma_tx->dev, "TX DMA callback : 0x%x\n",
+ MCBSP_READ(mcbsp_dma_tx, SPCR2));
+
+ /* We can free the channels */
+ omap_free_dma(mcbsp_dma_tx->dma_tx_lch);
+ mcbsp_dma_tx->dma_tx_lch = -1;
+
+ complete(&mcbsp_dma_tx->tx_dma_completion);
+}
+
+static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data)
+{
+ struct omap_mcbsp *mcbsp_dma_rx = data;
+
+ dev_dbg(mcbsp_dma_rx->dev, "RX DMA callback : 0x%x\n",
+ MCBSP_READ(mcbsp_dma_rx, SPCR2));
+
+ /* We can free the channels */
+ omap_free_dma(mcbsp_dma_rx->dma_rx_lch);
+ mcbsp_dma_rx->dma_rx_lch = -1;
+
+ complete(&mcbsp_dma_rx->rx_dma_completion);
+}
+
/*
* omap_mcbsp_config simply write a config to the
* appropriate McBSP.
@@ -723,6 +758,37 @@ static inline void omap_st_start(struct omap_mcbsp *mcbsp) {}
static inline void omap_st_stop(struct omap_mcbsp *mcbsp) {}
#endif
+/*
+ * We can choose between IRQ based or polled IO.
+ * This needs to be called before omap_mcbsp_request().
+ */
+int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type)
+{
+ struct omap_mcbsp *mcbsp;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ spin_lock(&mcbsp->lock);
+
+ if (!mcbsp->free) {
+ dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
+ mcbsp->id);
+ spin_unlock(&mcbsp->lock);
+ return -EINVAL;
+ }
+
+ mcbsp->io_type = io_type;
+
+ spin_unlock(&mcbsp->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_mcbsp_set_io_type);
+
int omap_mcbsp_request(unsigned int id)
{
struct omap_mcbsp *mcbsp;
@@ -767,24 +833,29 @@ int omap_mcbsp_request(unsigned int id)
MCBSP_WRITE(mcbsp, SPCR1, 0);
MCBSP_WRITE(mcbsp, SPCR2, 0);
- err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
- 0, "McBSP", (void *)mcbsp);
- if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
- "for McBSP%d\n", mcbsp->tx_irq,
- mcbsp->id);
- goto err_clk_disable;
- }
-
- if (mcbsp->rx_irq) {
- err = request_irq(mcbsp->rx_irq,
- omap_mcbsp_rx_irq_handler,
- 0, "McBSP", (void *)mcbsp);
+ if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
+ /* We need to get IRQs here */
+ init_completion(&mcbsp->tx_irq_completion);
+ err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
+ 0, "McBSP", (void *)mcbsp);
if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
- "for McBSP%d\n", mcbsp->rx_irq,
+ dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
+ "for McBSP%d\n", mcbsp->tx_irq,
mcbsp->id);
- goto err_free_irq;
+ goto err_clk_disable;
+ }
+
+ if (mcbsp->rx_irq) {
+ init_completion(&mcbsp->rx_irq_completion);
+ err = request_irq(mcbsp->rx_irq,
+ omap_mcbsp_rx_irq_handler,
+ 0, "McBSP", (void *)mcbsp);
+ if (err != 0) {
+ dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
+ "for McBSP%d\n", mcbsp->rx_irq,
+ mcbsp->id);
+ goto err_free_irq;
+ }
}
}
@@ -830,9 +901,12 @@ void omap_mcbsp_free(unsigned int id)
pm_runtime_put_sync(mcbsp->dev);
- if (mcbsp->rx_irq)
- free_irq(mcbsp->rx_irq, (void *)mcbsp);
- free_irq(mcbsp->tx_irq, (void *)mcbsp);
+ if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
+ /* Free IRQs */
+ if (mcbsp->rx_irq)
+ free_irq(mcbsp->rx_irq, (void *)mcbsp);
+ free_irq(mcbsp->tx_irq, (void *)mcbsp);
+ }
reg_cache = mcbsp->reg_cache;
@@ -869,6 +943,9 @@ void omap_mcbsp_start(unsigned int id, int tx, int rx)
if (cpu_is_omap34xx())
omap_st_start(mcbsp);
+ mcbsp->rx_word_length = (MCBSP_READ_CACHE(mcbsp, RCR1) >> 5) & 0x7;
+ mcbsp->tx_word_length = (MCBSP_READ_CACHE(mcbsp, XCR1) >> 5) & 0x7;
+
/* Only enable SRG, if McBSP is master */
w = MCBSP_READ_CACHE(mcbsp, PCR0);
if (w & (FSXM | FSRM | CLKXM | CLKRM))
@@ -966,32 +1043,484 @@ void omap_mcbsp_stop(unsigned int id, int tx, int rx)
}
EXPORT_SYMBOL(omap_mcbsp_stop);
+/* polled mcbsp i/o operations */
+int omap_mcbsp_pollwrite(unsigned int id, u16 buf)
+{
+ struct omap_mcbsp *mcbsp;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ MCBSP_WRITE(mcbsp, DXR1, buf);
+ /* if frame sync error - clear the error */
+ if (MCBSP_READ(mcbsp, SPCR2) & XSYNC_ERR) {
+ /* clear error */
+ MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2));
+ /* resend */
+ return -1;
+ } else {
+ /* wait for transmit confirmation */
+ int attemps = 0;
+ while (!(MCBSP_READ(mcbsp, SPCR2) & XRDY)) {
+ if (attemps++ > 1000) {
+ MCBSP_WRITE(mcbsp, SPCR2,
+ MCBSP_READ_CACHE(mcbsp, SPCR2) &
+ (~XRST));
+ udelay(10);
+ MCBSP_WRITE(mcbsp, SPCR2,
+ MCBSP_READ_CACHE(mcbsp, SPCR2) |
+ (XRST));
+ udelay(10);
+ dev_err(mcbsp->dev, "Could not write to"
+ " McBSP%d Register\n", mcbsp->id);
+ return -2;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_mcbsp_pollwrite);
+
+int omap_mcbsp_pollread(unsigned int id, u16 *buf)
+{
+ struct omap_mcbsp *mcbsp;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ /* if frame sync error - clear the error */
+ if (MCBSP_READ(mcbsp, SPCR1) & RSYNC_ERR) {
+ /* clear error */
+ MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1));
+ /* resend */
+ return -1;
+ } else {
+ /* wait for receive confirmation */
+ int attemps = 0;
+ while (!(MCBSP_READ(mcbsp, SPCR1) & RRDY)) {
+ if (attemps++ > 1000) {
+ MCBSP_WRITE(mcbsp, SPCR1,
+ MCBSP_READ_CACHE(mcbsp, SPCR1) &
+ (~RRST));
+ udelay(10);
+ MCBSP_WRITE(mcbsp, SPCR1,
+ MCBSP_READ_CACHE(mcbsp, SPCR1) |
+ (RRST));
+ udelay(10);
+ dev_err(mcbsp->dev, "Could not read from"
+ " McBSP%d Register\n", mcbsp->id);
+ return -2;
+ }
+ }
+ }
+ *buf = MCBSP_READ(mcbsp, DRR1);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_mcbsp_pollread);
+
+/*
+ * IRQ based word transmission.
+ */
+void omap_mcbsp_xmit_word(unsigned int id, u32 word)
+{
+ struct omap_mcbsp *mcbsp;
+ omap_mcbsp_word_length word_length;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+ word_length = mcbsp->tx_word_length;
+
+ wait_for_completion(&mcbsp->tx_irq_completion);
+
+ if (word_length > OMAP_MCBSP_WORD_16)
+ MCBSP_WRITE(mcbsp, DXR2, word >> 16);
+ MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
+}
+EXPORT_SYMBOL(omap_mcbsp_xmit_word);
+
+u32 omap_mcbsp_recv_word(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+ u16 word_lsb, word_msb = 0;
+ omap_mcbsp_word_length word_length;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ word_length = mcbsp->rx_word_length;
+
+ wait_for_completion(&mcbsp->rx_irq_completion);
+
+ if (word_length > OMAP_MCBSP_WORD_16)
+ word_msb = MCBSP_READ(mcbsp, DRR2);
+ word_lsb = MCBSP_READ(mcbsp, DRR1);
+
+ return (word_lsb | (word_msb << 16));
+}
+EXPORT_SYMBOL(omap_mcbsp_recv_word);
+
+int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word)
+{
+ struct omap_mcbsp *mcbsp;
+ omap_mcbsp_word_length tx_word_length;
+ omap_mcbsp_word_length rx_word_length;
+ u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+ tx_word_length = mcbsp->tx_word_length;
+ rx_word_length = mcbsp->rx_word_length;
+
+ if (tx_word_length != rx_word_length)
+ return -EINVAL;
+
+ /* First we wait for the transmitter to be ready */
+ spcr2 = MCBSP_READ(mcbsp, SPCR2);
+ while (!(spcr2 & XRDY)) {
+ spcr2 = MCBSP_READ(mcbsp, SPCR2);
+ if (attempts++ > 1000) {
+ /* We must reset the transmitter */
+ MCBSP_WRITE(mcbsp, SPCR2,
+ MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XRST));
+ udelay(10);
+ MCBSP_WRITE(mcbsp, SPCR2,
+ MCBSP_READ_CACHE(mcbsp, SPCR2) | XRST);
+ udelay(10);
+ dev_err(mcbsp->dev, "McBSP%d transmitter not "
+ "ready\n", mcbsp->id);
+ return -EAGAIN;
+ }
+ }
+
+ /* Now we can push the data */
+ if (tx_word_length > OMAP_MCBSP_WORD_16)
+ MCBSP_WRITE(mcbsp, DXR2, word >> 16);
+ MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
+
+ /* We wait for the receiver to be ready */
+ spcr1 = MCBSP_READ(mcbsp, SPCR1);
+ while (!(spcr1 & RRDY)) {
+ spcr1 = MCBSP_READ(mcbsp, SPCR1);
+ if (attempts++ > 1000) {
+ /* We must reset the receiver */
+ MCBSP_WRITE(mcbsp, SPCR1,
+ MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RRST));
+ udelay(10);
+ MCBSP_WRITE(mcbsp, SPCR1,
+ MCBSP_READ_CACHE(mcbsp, SPCR1) | RRST);
+ udelay(10);
+ dev_err(mcbsp->dev, "McBSP%d receiver not "
+ "ready\n", mcbsp->id);
+ return -EAGAIN;
+ }
+ }
+
+ /* Receiver is ready, let's read the dummy data */
+ if (rx_word_length > OMAP_MCBSP_WORD_16)
+ word_msb = MCBSP_READ(mcbsp, DRR2);
+ word_lsb = MCBSP_READ(mcbsp, DRR1);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll);
+
+int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 *word)
+{
+ struct omap_mcbsp *mcbsp;
+ u32 clock_word = 0;
+ omap_mcbsp_word_length tx_word_length;
+ omap_mcbsp_word_length rx_word_length;
+ u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ tx_word_length = mcbsp->tx_word_length;
+ rx_word_length = mcbsp->rx_word_length;
+
+ if (tx_word_length != rx_word_length)
+ return -EINVAL;
+
+ /* First we wait for the transmitter to be ready */
+ spcr2 = MCBSP_READ(mcbsp, SPCR2);
+ while (!(spcr2 & XRDY)) {
+ spcr2 = MCBSP_READ(mcbsp, SPCR2);
+ if (attempts++ > 1000) {
+ /* We must reset the transmitter */
+ MCBSP_WRITE(mcbsp, SPCR2,
+ MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XRST));
+ udelay(10);
+ MCBSP_WRITE(mcbsp, SPCR2,
+ MCBSP_READ_CACHE(mcbsp, SPCR2) | XRST);
+ udelay(10);
+ dev_err(mcbsp->dev, "McBSP%d transmitter not "
+ "ready\n", mcbsp->id);
+ return -EAGAIN;
+ }
+ }
+
+ /* We first need to enable the bus clock */
+ if (tx_word_length > OMAP_MCBSP_WORD_16)
+ MCBSP_WRITE(mcbsp, DXR2, clock_word >> 16);
+ MCBSP_WRITE(mcbsp, DXR1, clock_word & 0xffff);
+
+ /* We wait for the receiver to be ready */
+ spcr1 = MCBSP_READ(mcbsp, SPCR1);
+ while (!(spcr1 & RRDY)) {
+ spcr1 = MCBSP_READ(mcbsp, SPCR1);
+ if (attempts++ > 1000) {
+ /* We must reset the receiver */
+ MCBSP_WRITE(mcbsp, SPCR1,
+ MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RRST));
+ udelay(10);
+ MCBSP_WRITE(mcbsp, SPCR1,
+ MCBSP_READ_CACHE(mcbsp, SPCR1) | RRST);
+ udelay(10);
+ dev_err(mcbsp->dev, "McBSP%d receiver not "
+ "ready\n", mcbsp->id);
+ return -EAGAIN;
+ }
+ }
+
+ /* Receiver is ready, there is something for us */
+ if (rx_word_length > OMAP_MCBSP_WORD_16)
+ word_msb = MCBSP_READ(mcbsp, DRR2);
+ word_lsb = MCBSP_READ(mcbsp, DRR1);
+
+ word[0] = (word_lsb | (word_msb << 16));
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll);
+
/*
- * The following functions are only required on an OMAP1-only build.
- * mach-omap2/mcbsp.c contains the real functions
+ * Simple DMA based buffer rx/tx routines.
+ * Nothing fancy, just a single buffer tx/rx through DMA.
+ * The DMA resources are released once the transfer is done.
+ * For anything fancier, you should use your own customized DMA
+ * routines and callbacks.
*/
-#ifndef CONFIG_ARCH_OMAP2PLUS
-int omap2_mcbsp_set_clks_src(u8 id, u8 fck_src_id)
+int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer,
+ unsigned int length)
{
- WARN(1, "%s: should never be called on an OMAP1-only kernel\n",
- __func__);
- return -EINVAL;
+ struct omap_mcbsp *mcbsp;
+ int dma_tx_ch;
+ int src_port = 0;
+ int dest_port = 0;
+ int sync_dev = 0;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ if (omap_request_dma(mcbsp->dma_tx_sync, "McBSP TX",
+ omap_mcbsp_tx_dma_callback,
+ mcbsp,
+ &dma_tx_ch)) {
+ dev_err(mcbsp->dev, " Unable to request DMA channel for "
+ "McBSP%d TX. Trying IRQ based TX\n",
+ mcbsp->id);
+ return -EAGAIN;
+ }
+ mcbsp->dma_tx_lch = dma_tx_ch;
+
+ dev_err(mcbsp->dev, "McBSP%d TX DMA on channel %d\n", mcbsp->id,
+ dma_tx_ch);
+
+ init_completion(&mcbsp->tx_dma_completion);
+
+ if (cpu_class_is_omap1()) {
+ src_port = OMAP_DMA_PORT_TIPB;
+ dest_port = OMAP_DMA_PORT_EMIFF;
+ }
+ if (cpu_class_is_omap2())
+ sync_dev = mcbsp->dma_tx_sync;
+
+ omap_set_dma_transfer_params(mcbsp->dma_tx_lch,
+ OMAP_DMA_DATA_TYPE_S16,
+ length >> 1, 1,
+ OMAP_DMA_SYNC_ELEMENT,
+ sync_dev, 0);
+
+ omap_set_dma_dest_params(mcbsp->dma_tx_lch,
+ src_port,
+ OMAP_DMA_AMODE_CONSTANT,
+ mcbsp->phys_base + OMAP_MCBSP_REG_DXR1,
+ 0, 0);
+
+ omap_set_dma_src_params(mcbsp->dma_tx_lch,
+ dest_port,
+ OMAP_DMA_AMODE_POST_INC,
+ buffer,
+ 0, 0);
+
+ omap_start_dma(mcbsp->dma_tx_lch);
+ wait_for_completion(&mcbsp->tx_dma_completion);
+
+ return 0;
}
+EXPORT_SYMBOL(omap_mcbsp_xmit_buffer);
-void omap2_mcbsp1_mux_clkr_src(u8 mux)
+int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer,
+ unsigned int length)
{
- WARN(1, "%s: should never be called on an OMAP1-only kernel\n",
- __func__);
- return;
+ struct omap_mcbsp *mcbsp;
+ int dma_rx_ch;
+ int src_port = 0;
+ int dest_port = 0;
+ int sync_dev = 0;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ if (omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX",
+ omap_mcbsp_rx_dma_callback,
+ mcbsp,
+ &dma_rx_ch)) {
+ dev_err(mcbsp->dev, "Unable to request DMA channel for "
+ "McBSP%d RX. Trying IRQ based RX\n",
+ mcbsp->id);
+ return -EAGAIN;
+ }
+ mcbsp->dma_rx_lch = dma_rx_ch;
+
+ dev_err(mcbsp->dev, "McBSP%d RX DMA on channel %d\n", mcbsp->id,
+ dma_rx_ch);
+
+ init_completion(&mcbsp->rx_dma_completion);
+
+ if (cpu_class_is_omap1()) {
+ src_port = OMAP_DMA_PORT_TIPB;
+ dest_port = OMAP_DMA_PORT_EMIFF;
+ }
+ if (cpu_class_is_omap2())
+ sync_dev = mcbsp->dma_rx_sync;
+
+ omap_set_dma_transfer_params(mcbsp->dma_rx_lch,
+ OMAP_DMA_DATA_TYPE_S16,
+ length >> 1, 1,
+ OMAP_DMA_SYNC_ELEMENT,
+ sync_dev, 0);
+
+ omap_set_dma_src_params(mcbsp->dma_rx_lch,
+ src_port,
+ OMAP_DMA_AMODE_CONSTANT,
+ mcbsp->phys_base + OMAP_MCBSP_REG_DRR1,
+ 0, 0);
+
+ omap_set_dma_dest_params(mcbsp->dma_rx_lch,
+ dest_port,
+ OMAP_DMA_AMODE_POST_INC,
+ buffer,
+ 0, 0);
+
+ omap_start_dma(mcbsp->dma_rx_lch);
+ wait_for_completion(&mcbsp->rx_dma_completion);
+
+ return 0;
}
+EXPORT_SYMBOL(omap_mcbsp_recv_buffer);
-void omap2_mcbsp1_mux_fsr_src(u8 mux)
+/*
+ * SPI wrapper.
+ * Since SPI setup is much simpler than the generic McBSP one,
+ * this wrapper just need an omap_mcbsp_spi_cfg structure as an input.
+ * Once this is done, you can call omap_mcbsp_start().
+ */
+void omap_mcbsp_set_spi_mode(unsigned int id,
+ const struct omap_mcbsp_spi_cfg *spi_cfg)
{
- WARN(1, "%s: should never be called on an OMAP1-only kernel\n",
- __func__);
- return;
+ struct omap_mcbsp *mcbsp;
+ struct omap_mcbsp_reg_cfg mcbsp_cfg;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg));
+
+ /* SPI has only one frame */
+ mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0));
+ mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0));
+
+ /* Clock stop mode */
+ if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY)
+ mcbsp_cfg.spcr1 |= (1 << 12);
+ else
+ mcbsp_cfg.spcr1 |= (3 << 11);
+
+ /* Set clock parities */
+ if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING)
+ mcbsp_cfg.pcr0 |= CLKRP;
+ else
+ mcbsp_cfg.pcr0 &= ~CLKRP;
+
+ if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING)
+ mcbsp_cfg.pcr0 &= ~CLKXP;
+ else
+ mcbsp_cfg.pcr0 |= CLKXP;
+
+ /* Set SCLKME to 0 and CLKSM to 1 */
+ mcbsp_cfg.pcr0 &= ~SCLKME;
+ mcbsp_cfg.srgr2 |= CLKSM;
+
+ /* Set FSXP */
+ if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH)
+ mcbsp_cfg.pcr0 &= ~FSXP;
+ else
+ mcbsp_cfg.pcr0 |= FSXP;
+
+ if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) {
+ mcbsp_cfg.pcr0 |= CLKXM;
+ mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div - 1);
+ mcbsp_cfg.pcr0 |= FSXM;
+ mcbsp_cfg.srgr2 &= ~FSGM;
+ mcbsp_cfg.xcr2 |= XDATDLY(1);
+ mcbsp_cfg.rcr2 |= RDATDLY(1);
+ } else {
+ mcbsp_cfg.pcr0 &= ~CLKXM;
+ mcbsp_cfg.srgr1 |= CLKGDV(1);
+ mcbsp_cfg.pcr0 &= ~FSXM;
+ mcbsp_cfg.xcr2 &= ~XDATDLY(3);
+ mcbsp_cfg.rcr2 &= ~RDATDLY(3);
+ }
+
+ mcbsp_cfg.xcr2 &= ~XPHASE;
+ mcbsp_cfg.rcr2 &= ~RPHASE;
+
+ omap_mcbsp_config(id, &mcbsp_cfg);
}
-#endif
+EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
#ifdef CONFIG_ARCH_OMAP3
#define max_thres(m) (mcbsp->pdata->buffer_size)
@@ -1304,6 +1833,8 @@ static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
spin_lock_init(&mcbsp->lock);
mcbsp->id = id + 1;
mcbsp->free = true;
+ mcbsp->dma_tx_lch = -1;
+ mcbsp->dma_rx_lch = -1;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
if (!res) {
@@ -1329,6 +1860,9 @@ static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
else
mcbsp->phys_dma_base = res->start;
+ /* Default I/O is IRQ based */
+ mcbsp->io_type = OMAP_MCBSP_IRQ_IO;
+
mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");