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.
Hi David,
Thanks a lot. I have one doubt. How did you manage to call the macro,
mcbsp = (struct omap_mcbsp *)id_to_mcbsp_ptr(MCBSP_ID);
from your loadable module. Or have u added your fns inside the plat-omap/mcbsp.c itself?
Also if possible could u shed some light on the driver read() part with us.
Hi,
Can u try setting the buffer as max_thresh where,
max_thresh = omap_mcbsp_get_max_rx_threshold(busid)
omap_mcbsp_set_rx_threshold(MCBSP_ID, max_thresh);
Then, does it work?
Hi, Can u try setting the buffer as max_thresh where, max_thresh = omap_mcbsp_get_max_rx_threshold(busid) omap_mcbsp_set_rx_threshold(MCBSP_ID, max_thresh); Then, does it work?
Hi Tom,
Are you able to communicate using McBSP ?
I am also writing a McBSP driver to communicate using McBSP1 to AD7266 chip. I am not seeing dma_callback
Can you share your kernel module so that I can use that as an example to accomplish my task ?
Thanks in advance,
Ramgopal Kota
I am trying do the same and your driver is very cool! I was trying a lot of configures mcbsp, but dma callback doesn't calling.( When i set rx treshold to 0 and dma_block_size to 1, callback works, but it calls in random time and returns the same data many times.
The last config was:
mcbsp_config.rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_24); //Recive frame length: 0 == 1 word, For more channels, this number will increase mcbsp_config.rcr1 |= RFRLEN1(0); //New mcbsp_config.rcr1 |= RDATDLY(1); //mcbsp_config.rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_24); //mcbsp_config.rcr2 |= RDATDLY(1); //Pin control //Recive flow polarity, Sample data on rising edge of clk mcbsp_config.pcr0 |= CLKRP; //Transmit frame sync mcbsp_config.pcr0 |= FSXM; //Transmit clock mode (Internal) mcbsp_config.pcr0 |= CLKXM; //Transmit flow polarity mcbsp_config.pcr0 |= CLKXP; //Recive frame sync //mcbsp_config.pcr0 |= FSRM; //Recive clock mode (Internal) //mcbsp_config.pcr0 |= CLKRM; //Sample rate generator 1 //Frame width mcbsp_config.srgr1 |= FWID(0); //Clock divider 96 MHz Default, 48 div = 2MHz mcbsp_config.srgr1 |= CLKGDV(48); //New mcbsp_config.srgr1 |= CLKSM; mcbsp_config.srgr1 |= CLKSP; //Sample rate generator 2 //Frame period mcbsp_config.srgr2 |= FPER(511); //Sample rate generator mcbsp_config.srgr2 |= FSGM; //Serial port control 1 //Recive interrupt mode RSyncErr mcbsp_config.spcr1 |=RINTM(3); //New //mcbsp_config.spcr1 |= RJUST(1); //Serial port control 2 //mcbsp_config.srgr2 |= CLKSP; //mcbsp_config.spcr2 |= FRST; mcbsp_config.spcr2 |= FREE; //mcbsp_config.spcr1 |= ALB; //Recive conf control //Recive DNA bit mcbsp_config.rccr |= RDMAEN; // Enable DMA receive requests //mcbsp_config.rccr |= RFULL_CYCLE; //Recive channel //mcbsp_config.rcera = 0x3; //Transmit control 1 //mcbsp_config.xcr1 |= XFRLEN1(OMAP_MCBSP_WORD_16); //mcbsp_config.xcr1 |= XWDLEN1(0); //Transmit conf control //Loopback //mcbsp_config.xccr |= DLB; //Dma enable bit //mcbsp_config.xccr |= XDMAEN; //Transmit channel //mcbsp_config.xcerc = 0x1;
I also try to read data through recv and pool, but nothing works:
//Read threadstatic int readF(void *data){ int j = 0; int pg = 0; pg=get_order(dma_size * 4); rx_data.data = kzalloc(dma_size * 4, GFP_KERNEL | GFP_DMA); memset(rx_data.data, 0xFF, dma_size * 4); if (!rx_data.data) return -ENOMEM; //rx_data.dma_handle = dma_map_single(ads1274_dev.dev, rx_data.data, dma_size * 4,DMA_FROM_DEVICE); rx_data.dma_handle = (dma_addr_t)dma_alloc_coherent(NULL,(size_t)(PAGE_SIZE<<pg), rx_data.data,0); if (!rx_data.dma_handle) return -ENOMEM; for (j = 0; j < 100; j++) { u32 buf = 1; //buf = omap_mcbsp_recv_word(MCBSP_ID); //if (omap_mcbsp_pollread(MCBSP_ID, &buf)) { //buf = omap_mcbsp_recv_word(MCBSP_ID); //if (buf == 0) { //if (omap_mcbsp_recv_buffer(MCBSP_ID, rx_data.dma_handle, 1)){ if (omap_mcbsp_spi_master_recv_word_poll(MCBSP_ID, &buf)) { printk(KERN_INFO "Read Err\n"); continue; } //int dat = 0; //memcpy(&dat, rx_data.data, 4); printk(KERN_INFO "Read OK Buf 0x%04x\n", buf); //printk(KERN_INFO "Read OK HANDLE 0x%04x\n", rx_data.dma_handle); //printk(KERN_INFO "Read OK DATA 0x%04x\n", *(u32 *)rx_data.data); } return 0;}
If anybody has any suggestions please write!
Thank you.
Alex