Hello!
I am attempting to write a linux driver to interface with the TI ADS1278 analog to digital converter using McBSP3 with SDMA. To do this, I need the McBSP to supply both clock and frame-sync signals. Since I am using McBSP3, it is necessary for these signals to come from FSX and CLKX as FSR and CLKR are connected internally to FSX and CLKX.
The problem is that transmit appears to be functional but nothing is being received. I have successfully configured FSX and CLKX to provide the necessary signals, and can see them clearly on the scope along with the data from the ADS1278, but the RBUFFSTAT register remains zero at all times and no SDMA requests are being issued. It is almost like the receiver is not using FSX and CLKX properly like it should, but I simply cannot see where I am misconfiguring the registers.
I am curious about the omap_mcbsp_set_io_type() function. Since I am using SDMA, I do not want the CPU to poll for data completion nor do I want an interrupt to be requested until the SDMA issues a callback. So I am not sure which option I should choose. I went with OMAP_MCBSP_POLL_IO but that may not be the correct choice.
Any help would be appreciated! Thanks!
I am using linux 2.6.32 built by openembedded.
Here is my McBSP initialization code:
#define MCBSP_ID 3
....
// Initialize McBSP
status = omap_mcbsp_set_io_type(MCBSP_ID, OMAP_MCBSP_POLL_IO);
if(status)
{
printk(KERN_ERR "Error: Could not configure McBSP3. Error Code: %d.\n", status);
return -1;
}
status = omap_mcbsp_request(MCBSP_ID);
if(status)
{
printk(KERN_ERR "Error: Could not request McBSP3. Error Code: %d.\n", status);
return -1;
}
// Register configuration.
memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); // Set all register values to zero before configuration.
mcbsp_cfg.rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_24);
mcbsp_cfg.rcr1 |= RFRLEN1(0); // Set frame length to 1 word. For more channels, this number will increase
mcbsp_cfg.pcr0 |= CLKRP; // Sample data on rising edge of clk
mcbsp_cfg.pcr0 |= FSXM;
mcbsp_cfg.pcr0 |= CLKXM;
mcbsp_cfg.pcr0 |= CLKXP;
mcbsp_cfg.srgr1 |= FWID(0);
mcbsp_cfg.srgr1 |= CLKGDV(48); // 96 MHz / 48 = 2 MHz clock
mcbsp_cfg.srgr2 |= FPER(160);
mcbsp_cfg.srgr2 |= FSGM;
mcbsp_cfg.srgr2 |= CLKSP;
mcbsp_cfg.spcr2 |= FRST;
mcbsp_cfg.spcr2 |= FREE;
mcbsp_cfg.rccr |= RDMAEN; // Enable DMA receive requests
mcbsp_cfg.rcerc = 0x1;
omap_mcbsp_enable_clks(MCBSP_ID);
omap_mcbsp_config(MCBSP_ID, &mcbsp_cfg); // Configures the McBSP registers
// Ensure that the necessary clock is enabled.
DEVCONF1 = (u32 *) ioremap(OMAP3530_GENERAL_BASE+OMAP3530_CONTROL_DEVCONF1, 4);
reg = *DEVCONF1;
reg &= 0xFFFFFFFE;
*DEVCONF1 = reg;
iounmap((void *) DEVCONF1);
omap_mcbsp_set_rx_threshold(MCBSP_ID, 100);
mcbsp_physbuff = (dma_addr_t) kmalloc(150*4, GFP_KERNEL | GFP_DMA);
/* Some non-mcbsp, non-DMA stuff */
ads1278_dma_setup();
omap_mcbsp_start(MCBSP_ID, 1, 1);
And my DMA configuration code is as follows:
int ads1278_dma_setup(void)
{
if(!omap_mcbsp_check_valid_id(MCBSP_ID))
{
printk(KERN_ERR "Error: Invalid McBSP id(%d) specified in DMA setup.\n", MCBSP_ID);
return -1;
}
mcbsp = id_to_mcbsp_ptr(MCBSP_ID);
if(omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX", ads1278_mcbsp_dma_callback, mcbsp, &dma_rx_ch))
{
printk(KERN_ERR "Error: Unable to request DMA channel for McBSP #%d RX.\n", MCBSP_ID);
return -1;
}
mcbsp->dma_rx_lch = dma_rx_ch;
printk(KERN_INFO "McBSP%d RX DMA(%d) on channel %d\n", mcbsp->id, mcbsp->dma_rx_sync, dma_rx_ch);
sync_dev = mcbsp->dma_rx_sync;
omap_enable_dma_irq(mcbsp->dma_rx_lch, 0x01);
omap_set_dma_transfer_params(mcbsp->dma_rx_lch,
OMAP_DMA_DATA_TYPE_S32,
100, 1,
OMAP_DMA_SYNC_ELEMENT,
sync_dev, 1);
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_CONSTANT,
mcbsp_physbuff,
0, 0);
omap_start_dma(mcbsp->dma_rx_lch);
return 0;
}
I relied pretty heavily on omap_mcbsp_recv_buffer in mcbsp.c when writing my DMA setup function. When I try to run omap_mcbsp_recv_buffer directly, it waits forever for completion.
My DMA callback function is pretty simple at present:
static void ads1278_mcbsp_dma_callback(int lch, u16 ch_status, void *data)
{
callback_timer++;
}
I can ping callback_timer whenever I want, which is how I know that the callback is never being called.