This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Question Regarding linux McBSP driver on OMAP3530

Other Parts Discussed in Thread: ADS1278, OMAP3530, ADS1287, ADS1274

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.

  • Ooops, my apologies I made a mistake in preparing the post.

    #define MCBSP_ID 3

    should be

    #define MCBSP_ID 2

    This mistake doesn't appear in the actual source code, I just typed the wrong number here.

  • Since you mentioned all you are seing in the receive buffer register is zeros, my first thought is for you to check the configuration settings for the PIN.  Please ensure that the input path is enabled.  There is a bit field called INPUTENABLE which will be part of the Pin Configuration Registers.  This needs to be enabled to allow the propagation of the signals into the device.

    This is indicated in the footnotes of Table 21-2 of the Technical Reference Manual.

  • Brandon,

    Thanks for the response!  Your suggestion was correct, I did not have my MUX settings configured properly.  I admit I was confused by Table 21-2 in the TRM as it says concerning mcbspi_fsx, "This signal is also used as FSR when it is configured as output" leading me to believe I needed to have input disabled in order to tie FSX to FSR.  But that doesn't really make much sense when you think about it.   Thanks again!

  • Hi Joshua,

    I'm pretty new to linux driver development/programming and so I'm a bit stuck here with pretty the same task to fulfill. I've got a OMAP3530 Board from TechNexion (TAO-3530), which has the McBSP 3 port accessible and a ADS1287 Evalution board to connect to that port. My problem is probably pretty simple, if one knows where to start: I've got the complete kernel sources and am able to compile and run the kernel. I found the mcbsp source module in the folder: arch/arm/plat-omap/mcbsp.c which contains the routines that you used in your driver source: e.g. " omap_mcbsp_config" etc...

    Can you send me your complete driver source, that would be really great! My question: How do you compile your driver? Do you tell the Makefile of your kernel sources to also compile your driver? Do you create a kernel Module, or is the driver integrated into the kernel? And even more interesting: How do you access your driver? As a character device? Examples or even further sources would be of great help :)

    My setup: I've got an Ubuntu Linux running and have installed CodeSorcery as Cross Compiler. I already managed to crosscompile some Qt applications with touchscreen support, works like a charm, but when it comes to drivers, I'm a pretty newb!

    Thanks a lot for your help!

  • David,

    I don't think I'll be permitted to share my whole module source code at this time, but to answer a few of your questions:

    1. I use a Makefile to build the module.  The resource I used to construct it and the overall source code framework can be found here

    http://tldp.org/LDP/lkmpg/2.6/html/lkmpg.html

    2. I'm using a kernel module, not a integrated driver.

    3. Data is read into user space via a character device.  I also tested a block device, but the performance was a little worse.  Example character devices abound on the internet, here's the example I used:

    http://tldp.org/LDP/lkmpg/2.6/html/x569.html

    I'm fairly new to kernel programming myself, so this character device may get changed to something  else if we find a need for it, but it is working well enough so far.

    At present, we have nearly completed the driver, but there are still a few tweaks to make.  The code I provided in this thread is substantially similar to what we are still using now.  The main difference is that I had to modify the DMA to use chained channels with packet synchronization(as opposed to frame synchronization here) to get better data throughput.  The OMAP3530 reference manual includes a case study in its DMA section that covers packet synchronization and explains how the registers need to be configured.

    Hope this helps.

  • Thank you for your quick and very informative answer. I'll check it out as soon as I'm back in office, but you already helped a lot!

  • Hi David,

    I'm using the same TechNexion module and am trying to do something similar to you (hooking McBSP3 up to something useful). The way that I've been able to compile the module is using the Make files that TechNexion provided and compiling the driver module in with it. (From the Tao UG, follow the directions from the "make modules" to the end of the section; note that you do not need to compile the entire kernel if you're just wanting to compile your module. If you modify any of the platform libraries, though, you will need to compile the entire kernel which takes a lot longer). Then you need to follow the rest of the directions from there to load the new modules to use on the board.

    I'm having a real hard time getting the DMA to work for TX. When I enable all the DMA interrupts, I see a "transaction error" but there's really no documentation anywhere talking about what this means. Has anyone gotten it to work out there? I see many RX examples but very few TX examples (I saw 1 on this website but haven't managed to get it to work).

    Robin

  • Hi Joshua,

    I got a bit further. I managed to create my own kernel module and to compile it using following command:

    make M=modules-selfmade/ modules

    where "modules-selfmade" is the folder where I've written a simple test.c containing your source code from above. This gives me a test.ko module which I can then copy and run on my board. But I'm still missing an important link: How do I correctly start/load the mcbsp driver. Because after first tests I took a closer look at "mcbsp.c", which came with the kernel sources and then I realized the "omap_mcbsp_init" method which should be called to then correctly load the driver ("platform_driver_register", which then initializes "mcbsp_ptr" Array and so on...) was obviously not called. I found out by directly reading out the register memory after following line of your code:

    omap_mcbsp_config(MCBSP_ID, &mcbsp_cfg);

    The registers were still empty, which must be because "omap_mcbsp_init" from "mcbsp.c" module was not called. From the "mcbsp.c" I saw the name of the driver:

    static struct platform_driver omap_mcbsp_driver = {
        .probe        = omap_mcbsp_probe,
        .remove        = __devexit_p(omap_mcbsp_remove),
        .driver        = {
            .name    = "omap-mcbsp",  <============== THIS ONE
        },
    };

    On my Omap Device I then found a similar entry at following path:

    /sys/bus/platform/drivers/omap-mcbsp

    which contains subfolders for each of the 5 McBSP ports. Is this the driver/device? And again: How is this driver/device and the kernel source (mcbsp.c) behind it correctly accessed?

    What I also did to get your code to work, was to export 2 Symbols which weren't actually exported in "mcbsp.c":

    EXPORT_SYMBOL(mcbsp_ptr);
    EXPORT_SYMBOL(omap_mcbsp_count);

    to make your "int ads1278_dma_setup(void)" method compile and link correctly. That for I had to recompile the kernel of course... But I think that's also not the way it's meant to be done...

    Tanks again for your help and forgive my "not knowing" of this basic linux knowledge :)

     

     

  • I found the problem, my assumption that "omap_mcbsp_init" was not called, was just proven wrong! I had the wrong McBSP channel in use...  I had following define of yours set to the wrong value:


    #define MCBSP_ID 3 instead of #define MCBSP_ID 2

    Now I've got a CLK and a FSYNC signal.

  • If you havnt noticed, there is an error in the 2.6.32 kernel. The patch is given here for reference. This patch is accepted, and have now reached the kernel 2.6.37 in omap3evm psp04.02.00.07 !

     

    diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c
    index e31496e..0c8612f 100644
    --- a/arch/arm/plat-omap/mcbsp.c
    +++ b/arch/arm/plat-omap/mcbsp.c
    @@ -156,7 +156,7 @@ static irqreturn_t omap_mcbsp_rx_irq_handler(int
    irq, void *dev_id)
                    /* Writing zero to RSYNC_ERR clears the IRQ */
                    MCBSP_WRITE(mcbsp_rx, SPCR1,
    MCBSP_READ_CACHE(mcbsp_rx, SPCR1));
            } else {
    -               complete(&mcbsp_rx->tx_irq_completion);
    +               complete(&mcbsp_rx->rx_irq_completion);
            }

            return IRQ_HANDLED;

  • Thanks a lot Vincent, I didn't realize of course.

    For all those trying to achieve a similar goal as I did, here some additional hints:

    I had two major problems which took me quite a long time to solve (many days):

    1. omap_set_dma_dest_params(dma_rx_ch[i],
                                                         dest_port,
                                                         OMAP_DMA_AMODE_DOUBLE_IDX,
                                                         mcbsp_realPhys,a,b);   <------ this mcbsp_realPhys means the real physical (not virtual) address of the buffers I created (see next point)...

     

    2. mcbsp_physbuff[i] = (dma_addr_t)dma_alloc_coherent(NULL,(size_t)(PAGE_SIZE<<pg),&mcbsp_realPhys[i],0);   <------here's the creation of the buffers to be used by the mcbsp buffers in combination with the DMA peripheral. If not allocated with dma_alloc_coherent, we would end up with some ugly caching effects of linux when accessing this driver. Even when the driver was opened like this:

    driver=fopen("/dev/ads1278","r+");
    setvbuf(driver,NULL,_IONBF,0);

    I still got the ugly effect that my driver returned the same values many times before refreshing at a random time to a newer value of my buffers.

    Hope this saves some time for anybody.

  • Hi David,

    Thanks for the tip.

    You have created your own custom transmit handler.!!

     

     

    So, it means that, if I call the omap_mcbsp_xmit_buffer() fn from the plat/mcbsp.c i should pass the buffer like this, right?

    #include <plat/mcbsp.h>

    #include <plat/dma.h>

    #define MCBSP2      1

    ....

    //in the module read fn

    dma_addr_t mybuffer;

    unsigned int mybuffer_phy;

    mybuffer = (dma_addr_t) dma_alloc_coherent (NULL, (size_t)(PAGE_SIZE),  &mybuffer_phy, 0); //am I correct?????

    omap_mcbsp_recv_buffer(MCBSP2, mybuffer_phy, 480);

     

  • Hi Vincent,

    you forgot the "pg" in the "dma_alloc_coherent" call: I used it like this:

    int pg;

    pg=get_order(BUFF_SIZE);

    mcbsp_physbuff[i] = (dma_addr_t)dma_alloc_coherent(NULL,(size_t)(PAGE_SIZE<<pg),&mcbsp_realPhys[i],0);

    I would also suggest either "&mcbsp_realPhys[i]" or just "mcbsp_realPhys" without & and [ ]....

     

    About the "omap_mcbsp_recv_buffer": I didn't use that function, I let the dma_interrupt handle the receiption in the first place, and then I wrote a character device driver to read the buffers into userspace. That way I could link several DMA Accesses together to create a larger intermediate buffer, so that my user space software doesn't have to interact too much, which would be a bit of a performance killer. Here's my DMA Setup routine (watch the red marked callback routine, which gets called at every dma occurance, which reads out the mcbsp Buffer for me. I made some special rearrangement of the "words" received by the mcbsp. Because the stream I receive is 4 channels of a A/D converter which come in the following order: abcdabcdabcd......(25 times abcd) I use the dma to also store the values in a more efficient way concerning further computations: aaaa...bbbb....cccc....dddd (25 times a, 25 times b ...). Furthermore I have 3 circulating buffers, which get filled within 4 dma request, that's why you'll find the i/4 and i%4 computations!)

     

    int ads1278_dma_setup(void)
    {
       

        int sync_dev;  
        int src_port=0;
        int dest_port=0;
        int i;
        //unsigned long mcbsp_physbuff;
        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 = (struct omap_mcbsp *)id_to_mcbsp_ptr(MCBSP_ID);

        for(i=0;i<12;i++)
        {
            if(omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX", ads1278_mcbsp_dma_callback, mcbsp, &dma_rx_ch[i]))
            {
            printk(KERN_ERR "Error: Unable to request DMA channel for McBSP #%d RX.\n", MCBSP_ID);
            return -1;
            }
            if(i==0)
                mcbsp->dma_rx_lch = dma_rx_ch[i];
          
            printk(KERN_INFO "McBSP%d RX DMA(%d) on channel %d\n", mcbsp->id, mcbsp->dma_rx_sync, dma_rx_ch[i]);

            sync_dev = mcbsp->dma_rx_sync;
       
          
            omap_set_dma_transfer_params(dma_rx_ch[i],
                                                                 OMAP_DMA_DATA_TYPE_S32,
                                                                 4, 25,
                                                                 OMAP_DMA_SYNC_BLOCK,
                                                                 sync_dev, 1);

           
            omap_set_dma_src_params(dma_rx_ch[i],
                                                        src_port,
                                                        OMAP_DMA_AMODE_CONSTANT,
                                                        mcbsp->phys_base + OMAP_MCBSP_REG_DRR,
                                                        1, 0);
           
            omap_set_dma_dest_params(dma_rx_ch[i],
                                                         dest_port,
                                                         OMAP_DMA_AMODE_DOUBLE_IDX,
                                                         mcbsp_realPhys[i/4]+(i%4)*100,
                                                         397,-1199);
        }
        omap_dma_link_lch(dma_rx_ch[0],dma_rx_ch[1]);
        omap_dma_link_lch(dma_rx_ch[1],dma_rx_ch[2]);
        omap_dma_link_lch(dma_rx_ch[2],dma_rx_ch[3]);
        omap_dma_link_lch(dma_rx_ch[3],dma_rx_ch[4]);
        omap_dma_link_lch(dma_rx_ch[4],dma_rx_ch[5]);
        omap_dma_link_lch(dma_rx_ch[5],dma_rx_ch[6]);
        omap_dma_link_lch(dma_rx_ch[6],dma_rx_ch[7]);
        omap_dma_link_lch(dma_rx_ch[7],dma_rx_ch[8]);
        omap_dma_link_lch(dma_rx_ch[8],dma_rx_ch[9]);
        omap_dma_link_lch(dma_rx_ch[9],dma_rx_ch[10]);
        omap_dma_link_lch(dma_rx_ch[10],dma_rx_ch[11]);
        omap_dma_link_lch(dma_rx_ch[11],dma_rx_ch[0]);

        omap_start_dma(mcbsp->dma_rx_lch);


        return 0;
    }

    The callback routine is very simple, so it's also not blocking the dma interrupt. Later on I use this callback_timer as a flowcontrol, to know which buffer (of the 3 large ones) is ready for reading....

    static void ads1278_mcbsp_dma_callback(int lch, u16 ch_status, void *data)
    {
        callback_timer++;
    }

  • Hi

    Im also fighting with the same problems at the moment. Im using a ads1274 with the beagleboard. My problem is that the dma callback is never being called, the rest seems to be working.I am using Kernel 3.0.0 with the rt patches.

    Things I have checked:

    - CLKX and FSX are being generated as expected

    - Mux settings in u-boot

     MUX_VAL(CP(MCBSP3_FSX),        (IEN  | PTD | DIS | M0)) /*FSX/\

    MUX_VAL(CP(UART2_CTS),        (IEN  | PTD | DIS | M1)) /*DX*/\

    MUX_VAL(CP(UART2_TX),        (IEN  | PTD | DIS | M1)) /*CLKX*/\

    MUX_VAL(CP(UART2_RTS),        (IEN  | PTD | DIS | M1)) /*MCBSP3_DR*/\

    I have verified that DX, FSX, CLKX are working, so I have to assume that DR is also working.

    - My configuration

        mcbsp_config.rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_24);
        mcbsp_config.rcr1 |= RFRLEN1(0); // Set frame length to 1 word.  For more channels, this number will increase
        mcbsp_config.pcr0 |= CLKRP; // Sample data on rising edge of clk
        mcbsp_config.pcr0 |= FSXM;
       mcbsp_config.pcr0 |= CLKXM;
        mcbsp_config.pcr0 |= CLKXP;
        mcbsp_config.srgr1 |= FWID(0);
        mcbsp_config.srgr1 |= CLKGDV(48); // 96 MHz / 48 = 2 MHz clock
        mcbsp_config.srgr2 |= FPER(130);
        mcbsp_config.srgr2 |= FSGM;
        mcbsp_config.spcr1 |=RINTM(3);
      //  mcbsp_config.srgr2 |= CLKSP;
       // mcbsp_config.spcr2 |= FRST;
        mcbsp_config.spcr2 |= FREE;
        mcbsp_config.rccr |= RDMAEN; // Enable DMA receive requests
        mcbsp_config.rcerc = 0x1;

    - Then the most important part, the dma initialisation, which is imho failing.

    static int mcbsp_dma_setup(void)
    {
     
      int dma_channel, ret;

      pg=get_order(DMA_BLOCK_SIZE);

      mybuffer = (dma_addr_t)dma_alloc_coherent(ads1274_dev.dev,(size_t)(PAGE_SIZE<<pg),&mybuffer_phy,0);
       
       
       rx_data.data = kzalloc(DMA_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);

       if (!rx_data.data)
           return -ENOMEM;

       rx_data.dma_handle = dma_map_single(ads1274_dev.dev, rx_data.data, DMA_BLOCK_SIZE,DMA_FROM_DEVICE);
       if (!rx_data.dma_handle)
           return -ENOMEM;

         printk(KERN_INFO "initialising dma\n");

               if(omap_request_dma(ads1274_dev.dma_rx_sync, "McBSP RX", ads1274_mcbsp_dma_callback, 0, &dma_channel))
        {
             printk(KERN_ERR "Error: Unable to request DMA channel for McBSP #%d RX.\n", MCBSP_ID);
             return -1;
        }
     
     printk(KERN_INFO "DMA Channel %i received\n",dma_channel);
     
      printk(KERN_INFO "DMA Channel Param %i\n",ads1274_dev.dma_rx_sync);
      printk(KERN_INFO "DMA Channel Register Param %x\n",ads1274_dev.rx_reg);
      printk(KERN_INFO "DMA Destination Start Address %x\n",rx_data.dma_handle);
     
         omap_set_dma_transfer_params(dma_channel,OMAP_DMA_DATA_TYPE_S32,100, 1,OMAP_DMA_SYNC_ELEMENT, ads1274_dev.dma_rx_sync, 0);
     
         omap_set_dma_src_params(dma_channel,0,OMAP_DMA_AMODE_CONSTANT,ads1274_dev.rx_reg,0, 0);
     
         omap_set_dma_dest_params(dma_channel,0,OMAP_DMA_AMODE_POST_INC,rx_data.dma_handle,0, 0);
     
        
        
        omap_enable_dma_irq(dma_channel, 0x01);
       
         omap_start_dma(dma_channel);
        
        ads1274_dev.dma_channel = dma_channel;
        printk(KERN_INFO "DMA Running? %i\n", omap_get_dma_active_status(dma_channel));
       
         return 0;
    }

     

    The printk which are coming back indicate that my settings for the addresses are correct:
    [   28.230163] DMA Channel 0 received                                          
    [   28.233825] DMA Channel Param 18                                            
    [   28.237243] DMA Channel Register Param 49024000                             
    [   28.242065] DMA Destination Start Address 8f08dec0                          
    [   28.247131] DMA Running? 1                                                  
    [   28.250030] DMA Setup and Started 

    - The next thing I did was check if data was coming in and RRDY in SPCR1 was asserted so the DMA would start transfer. For this I made a stupid loop which reads out the RBUFFSTAT and the SPCR1 register, here is the output:

    [   28.259887] McBSP Started                                                   
    [   28.262664] Slots left: 21                                                  
    [   28.265594] SPCR1 = 33                                                      
    [   28.268066] Slots left: 0                                                   
    [   28.270843] SPCR1 = 37                                                      
    [   28.273376] Slots left: 0                                                   
    [   28.276153] SPCR1 = 37                                                      
    [   28.278625] Slots left: 0                                                   
    [   28.281463] SPCR1 = 37                                                      
    [   28.283935] Slots left: 0                                                   
    [   28.286773] SPCR1 = 37                                                      
    [   28.289245] Slots left: 0                                                   
    [   28.292022] SPCR1 = 37                                                      
    [   28.294616] Slots left: 0                                                   
    [   28.297393] SPCR1 = 37                                                      
    [   28.299865] Slots left: 0   

     

    This shows me that the buffer is being filled, RBUFFSTAT goes to zero (which means that the buffer is full) but the DMA doesnt come to pickup the data :(

    If I set omap_mcbsp_set_rx_threshold(MCBSP_ID, 100); to 1 instead of 100 the dma callback gets called and it seems to work.

    I attachted the entire file since I want to make it open source anyway, maybe somebody could try it on there configuration / kernel and see if it works there, or even better maybe someone sees my mistake!

    Cheers

    Tom

    PS: The code is a mess, but Ill clean up as soon as it works ;)

    6505.ads1274.zip

     

     

  • Hi Tom,

    have you tried entering 99 instead of 100 in "omap_mcbsp_set_rx_threshold"? Because the register behind this method will add 1 to your value, so only then you'll get your threshhold at 100. What you've got here is 101 as threshold...

     

    Hope this helps

  • 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 thread
    static 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