spi流程

開發板爲sbc3530,芯片爲ti的omap3530

Mscpi主設備。/arch/arm/match-omap2/devices.c

static struct omap2_mcspi_platform_configomap2_mcspi1_config = {

       .num_cs         = 4,

};

 

static struct resourceomap2_mcspi1_resources[] = {

       {

              .start              = OMAP2_MCSPI1_BASE,

              .end        = OMAP2_MCSPI1_BASE + 0xff,

              .flags             = IORESOURCE_MEM,

       },

};

 

static struct platform_device omap2_mcspi1= {

       .name             = "omap2_mcspi",

       .id          = 1,

       .num_resources      = ARRAY_SIZE(omap2_mcspi1_resources),

       .resource = omap2_mcspi1_resources,

       .dev        = {

              .platform_data= &omap2_mcspi1_config,

       },

};

 

static struct omap2_mcspi_platform_configomap2_mcspi2_config = {

       .num_cs         = 2,

};

 

static struct resourceomap2_mcspi2_resources[] = {

       {

              .start              = OMAP2_MCSPI2_BASE,

              .end        = OMAP2_MCSPI2_BASE + 0xff,

              .flags             = IORESOURCE_MEM,

       },

};

 

static struct platform_device omap2_mcspi2= {

       .name             = "omap2_mcspi",

       .id          = 2,

       .num_resources      = ARRAY_SIZE(omap2_mcspi2_resources),

       .resource = omap2_mcspi2_resources,

       .dev        = {

              .platform_data= &omap2_mcspi2_config,

       },

};

 

static struct omap2_mcspi_platform_configomap2_mcspi3_config = {

       .num_cs         = 2,

};

 

static struct resourceomap2_mcspi3_resources[] = {

       {

       .start              = OMAP2_MCSPI3_BASE,

       .end        = OMAP2_MCSPI3_BASE + 0xff,

       .flags             = IORESOURCE_MEM,

       },

};

 

static struct platform_device omap2_mcspi3= {

       .name             = "omap2_mcspi",

       .id          = 3,

       .num_resources      = ARRAY_SIZE(omap2_mcspi3_resources),

       .resource = omap2_mcspi3_resources,

       .dev        = {

              .platform_data= &omap2_mcspi3_config,

       },

};

 

static struct omap2_mcspi_platform_configomap2_mcspi4_config = {

       .num_cs         = 1,

};

 

static struct resourceomap2_mcspi4_resources[] = {

       {

              .start              = OMAP2_MCSPI4_BASE,

              .end        = OMAP2_MCSPI4_BASE + 0xff,

              .flags             = IORESOURCE_MEM,

       },

};

 

static struct platform_device omap2_mcspi4= {

       .name             = "omap2_mcspi",

       .id          = 4,

       .num_resources      = ARRAY_SIZE(omap2_mcspi4_resources),

       .resource = omap2_mcspi4_resources,

       .dev        = {

              .platform_data= &omap2_mcspi4_config,

       },

};

 

註冊mcspi設備。

static void omap_init_mcspi(void)

{

       if(cpu_is_omap44xx())

              omap4_mcspi_fixup();

 

       platform_device_register(&omap2_mcspi1);

       platform_device_register(&omap2_mcspi2);

 

       if(cpu_is_omap2430() || cpu_is_omap343x() || cpu_is_omap44xx())

              omap2_mcspi3_init();

 

       if(cpu_is_omap343x() || cpu_is_omap44xx())

              omap2_mcspi4_init();

}

 

 

 

./drive/spi/omap2-mcspi.c

mcSpi驅動                                                                                                      

static struct platform_driveromap2_mcspi_driver = {

       .driver= {

              .name=          "omap2_mcspi",

              .owner= THIS_MODULE,

       },

       .remove=       __exit_p(omap2_mcspi_remove),

};

 

註冊mcSpi驅動

 

static int __init omap2_mcspi_init(void)

{

       omap2_mcspi_wq= create_singlethread_workqueue(

                            omap2_mcspi_driver.driver.name);

       if(omap2_mcspi_wq == NULL)

              return-1;

       returnplatform_driver_probe(&omap2_mcspi_driver, omap2_mcspi_probe);

}

//運行mcspi的probe函數

static int __init omap2_mcspi_probe(structplatform_device *pdev)

{

       structspi_master    *master;

       structomap2_mcspi       *mcspi;

       structresource        *r;

       int                 status = 0, i;

       constu8         *rxdma_id, *txdma_id;

       unsigned         num_chipselect;

 

       switch(pdev->id) {

       case1:

              rxdma_id= spi1_rxdma_id;

              txdma_id= spi1_txdma_id;

              num_chipselect= 4;

              break;

       case2:

              rxdma_id= spi2_rxdma_id;

              txdma_id= spi2_txdma_id;

              num_chipselect= 2;

              break;

#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \

       ||defined(CONFIG_ARCH_OMAP4)

       case3:

              rxdma_id= spi3_rxdma_id;

              txdma_id= spi3_txdma_id;

              num_chipselect= 2;

              break;

#endif

#if defined(CONFIG_ARCH_OMAP3) ||defined(CONFIG_ARCH_OMAP4)

       case4:

              rxdma_id= spi4_rxdma_id;

              txdma_id= spi4_txdma_id;

              num_chipselect= 1;

              break;

#endif

       default:

              return-EINVAL;

       }

 

       master= spi_alloc_master(&pdev->dev, sizeof *mcspi);//分master 和mcspi的空間

 

       if(master == NULL) {

              dev_dbg(&pdev->dev,"master allocation failed\n");

              return-ENOMEM;

       }

 

       /*the spi->mode bits understood by this driver: */

       master->mode_bits= SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

 

       if(pdev->id != -1)

              master->bus_num= pdev->id;

 

       master->setup= omap2_mcspi_setup;

       master->transfer= omap2_mcspi_transfer;

       master->cleanup= omap2_mcspi_cleanup;

       master->num_chipselect= num_chipselect;

 

       dev_set_drvdata(&pdev->dev,master);

 

       mcspi= spi_master_get_devdata(master);//由master地址得到mcspi地址

       mcspi->master= master;

 

       r= platform_get_resource(pdev, IORESOURCE_MEM, 0);

       if(r == NULL) {

              status= -ENODEV;

              gotoerr1;

       }

       if(!request_mem_region(r->start, (r->end - r->start) + 1,

                     dev_name(&pdev->dev))){

              status= -EBUSY;

              gotoerr1;

       }

 

       mcspi->phys= r->start;  //spi控制寄存器起始的物理地址

       mcspi->base= ioremap(r->start, r->end - r->start + 1);// spi控制寄存器起始的虛擬地址

 

       if(!mcspi->base) {

              dev_dbg(&pdev->dev,"can't ioremap MCSPI\n");

              status= -ENOMEM;

              gotoerr1aa;

       }

 

       INIT_WORK(&mcspi->work,omap2_mcspi_work);//初始化工作隊列,傳輸的主要函數

 

       spin_lock_init(&mcspi->lock);

       INIT_LIST_HEAD(&mcspi->msg_queue);  //初始化傳輸數據隊列,

       INIT_LIST_HEAD(&omap2_mcspi_ctx[master->bus_num- 1].cs);

 

       mcspi->ick= clk_get(&pdev->dev, "ick"); //時鐘

       if(IS_ERR(mcspi->ick)) {

              dev_dbg(&pdev->dev,"can't get mcspi_ick\n");

              status= PTR_ERR(mcspi->ick);

              gotoerr1a;

       }

       mcspi->fck= clk_get(&pdev->dev, "fck");//時鐘

       if(IS_ERR(mcspi->fck)) {

              dev_dbg(&pdev->dev,"can't get mcspi_fck\n");

              status= PTR_ERR(mcspi->fck);

              gotoerr2;

       }

 

       mcspi->dma_channels= kcalloc(master->num_chipselect,

                     sizeof(structomap2_mcspi_dma),

                     GFP_KERNEL);

 

       if(mcspi->dma_channels == NULL)

              gotoerr3;

 

       for(i = 0; i < num_chipselect; i++) {

              mcspi->dma_channels[i].dma_rx_channel= -1;

              mcspi->dma_channels[i].dma_rx_sync_dev= rxdma_id[i];

              mcspi->dma_channels[i].dma_tx_channel= -1;

              mcspi->dma_channels[i].dma_tx_sync_dev= txdma_id[i];

       }

 

       if(omap2_mcspi_reset(mcspi) < 0)

              gotoerr4;

 

       status= spi_register_master(master);

       if(status < 0)

              gotoerr4;

 

       returnstatus;

 

err4:

       kfree(mcspi->dma_channels);

err3:

       clk_put(mcspi->fck);

err2:

       clk_put(mcspi->ick);

err1a:

       iounmap(mcspi->base);

err1aa:

       release_mem_region(r->start,(r->end - r->start) + 1);

err1:

       spi_master_put(master);

       returnstatus;

}

struct spi_master *spi_alloc_master(structdevice *dev, unsigned size)

{

       structspi_master    *master;

 

       if(!dev)

              returnNULL;

 

       master= kzalloc(size + sizeof *master, GFP_KERNEL);

       if(!master)

              returnNULL;

 

       device_initialize(&master->dev);

       master->dev.class= &spi_master_class;

       master->dev.parent= get_device(dev);

       spi_master_set_devdata(master,&master[1]);

 

       returnmaster;

}

int spi_register_master(struct spi_master*master)

{

       staticatomic_t        dyn_bus_id = ATOMIC_INIT((1<<15)- 1);

       structdevice          *dev =master->dev.parent;

       int                 status = -ENODEV;

       int                 dynamic = 0;

 

       if(!dev)

              return-ENODEV;

 

       /*even if it's just one always-selected device, there must

        * be at least one chipselect

        */

       if(master->num_chipselect == 0)

              return-EINVAL;

 

       /*convention:  dynamically assigned bus IDscount down from the max */

       if(master->bus_num < 0) {

              /*FIXME switch to an IDR based scheme, something like

               * I2Cnow uses, so we can't run out of "dynamic" IDs

               */

              master->bus_num= atomic_dec_return(&dyn_bus_id);

              dynamic= 1;

       }

 

       /*register the device, then userspace will see it.

        * registration fails if the bus ID is in use.

        */

       dev_set_name(&master->dev,"spi%u", master->bus_num);

       status= device_add(&master->dev);

 

       if(status < 0)

              gotodone;

       dev_dbg(dev,"registered master %s%s\n", dev_name(&master->dev),

                     dynamic? " (dynamic)" : "");

 

       /*populate children from any spi device tables */

       scan_boardinfo(master);

       status= 0;

done:

       returnstatus;

}

 

註冊spi drive.

static void scan_boardinfo(structspi_master *master)

{

       structboardinfo      *bi;

 

       mutex_lock(&board_lock);

       list_for_each_entry(bi,&board_list, list) {

              structspi_board_info     *chip =bi->board_info;

              unsigned         n;

 

              for(n = bi->n_board_info; n > 0; n--, chip++) {

                     if(chip->bus_num != master->bus_num)

                            continue;

                     /*NOTE: this relies on spi_new_device to

                      * issue diagnostics when given bogus inputs

                      */

                     (void)spi_new_device(master, chip);

                     printk(KERN_ALERT"chip%s master is %d \n",chip->modalias,master->bus_num);//yuanye

                    

              }

       }

       mutex_unlock(&board_lock);

}

 

struct spi_device *spi_new_device(structspi_master *master,

                              struct spi_board_info *chip)

{

       structspi_device     *proxy;

       int                 status;

 

       /*NOTE:  caller did any chip->bus_numchecks necessary.

        *

        * Also, unless we change the return valueconvention to use

        * error-or-pointer (not NULL-or-pointer),troubleshootability

        * suggests syslogged diagnostics are best here(ugh).

        */

 

       proxy= spi_alloc_device(master);

       if(!proxy)

              returnNULL;

 

       WARN_ON(strlen(chip->modalias)>= sizeof(proxy->modalias));

 

       proxy->chip_select= chip->chip_select;

       proxy->max_speed_hz= chip->max_speed_hz;

       proxy->mode= chip->mode;

       proxy->irq= chip->irq;

       strlcpy(proxy->modalias,chip->modalias, sizeof(proxy->modalias));

       proxy->dev.platform_data= (void *) chip->platform_data;

       proxy->controller_data= chip->controller_data;

       proxy->controller_state= NULL;

 

       status= spi_add_device(proxy);

       if(status < 0) {

              spi_dev_put(proxy);

              return NULL;

       }

 

       returnproxy;

}

int spi_add_device(struct spi_device *spi)

{

       staticDEFINE_MUTEX(spi_add_lock);

       structdevice *dev = spi->master->dev.parent;

       intstatus;

 

       /*Chipselects are numbered 0..max; validate. */

       if(spi->chip_select >= spi->master->num_chipselect) {

              dev_err(dev,"cs%d >= max %d\n",

                     spi->chip_select,

                     spi->master->num_chipselect);

              return-EINVAL;

       }

 

       /*Set the bus ID string */

       dev_set_name(&spi->dev,"%s.%u", dev_name(&spi->master->dev),

                     spi->chip_select);

 

 

       /*We need to make sure there's no other device with this

        * chipselect **BEFORE** we call setup(), elsewe'll trash

        * its configuration.  Lock against concurrent add() calls.

        */

       mutex_lock(&spi_add_lock);

 

       if(bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev))

                     !=NULL) {

              dev_err(dev,"chipselect %d already in use\n",

                            spi->chip_select);

              status= -EBUSY;

              gotodone;

       }

 

       /*Drivers may modify this initial i/o setup, but will

        * normally rely on the device beingsetup.  Devices

        * using SPI_CS_HIGH can't coexist wellotherwise...

        */

       status= spi_setup(spi);

       if(status < 0) {

              dev_err(dev,"can't %s %s, status %d\n",

                            "setup",dev_name(&spi->dev), status);

              gotodone;

       }

 

       /*Device may be bound to an active driver when this returns */

       status= device_add(&spi->dev);

       if(status < 0)

              dev_err(dev,"can't %s %s, status %d\n",

                            "add",dev_name(&spi->dev), status);

       else

              dev_dbg(dev,"registered child %s\n", dev_name(&spi->dev));

 

done:

       mutex_unlock(&spi_add_lock);

       returnstatus;

}

int spi_setup(struct spi_device *spi)

{

       unsigned  bad_bits;

       int          status;

 

       /*help drivers fail *cleanly* when they need options

        * that aren't supported with their currentmaster

        */

       bad_bits= spi->mode & ~spi->master->mode_bits;

       if(bad_bits) {

              dev_dbg(&spi->dev,"setup: unsupported mode bits %x\n",

                     bad_bits);

              return-EINVAL;

       }

 

       if(!spi->bits_per_word)

              spi->bits_per_word= 8;

 

       status= spi->master->setup(spi);

 

       dev_dbg(&spi->dev,"setup mode %d, %s%s%s%s"

                            "%ubits/w, %u Hz max --> %d\n",

                     (int)(spi->mode & (SPI_CPOL | SPI_CPHA)),

                     (spi->mode& SPI_CS_HIGH) ? "cs_high, " : "",

                     (spi->mode& SPI_LSB_FIRST) ? "lsb, " : "",

                     (spi->mode& SPI_3WIRE) ? "3wire, " : "",

                     (spi->mode& SPI_LOOP) ? "loopback, " : "",

                     spi->bits_per_word,spi->max_speed_hz,

                     status);

 

       returnstatus;

} static int omap2_mcspi_setup(structspi_device *spi)

{

       int                 ret;

       structomap2_mcspi       *mcspi;

       structomap2_mcspi_dma      *mcspi_dma;

       structomap2_mcspi_cs  *cs =spi->controller_state;

 

       if(spi->bits_per_word < 4 || spi->bits_per_word > 32) {

              dev_dbg(&spi->dev,"setup: unsupported %d bit words\n",

                     spi->bits_per_word);

              return-EINVAL;

       }

 

       mcspi= spi_master_get_devdata(spi->master);

       mcspi_dma= &mcspi->dma_channels[spi->chip_select];

 

       if(!cs) {

              cs= kzalloc(sizeof *cs, GFP_KERNEL);

              if(!cs)

                     return-ENOMEM;

              cs->base= mcspi->base + spi->chip_select * 0x14;

              cs->phys= mcspi->phys + spi->chip_select * 0x14;

              cs->chconf0= 0;

              spi->controller_state= cs;

              /*Link this to context save list */

              list_add_tail(&cs->node,

                     &omap2_mcspi_ctx[mcspi->master->bus_num- 1].cs);

       }

 

       if(mcspi_dma->dma_rx_channel == -1

                     ||mcspi_dma->dma_tx_channel == -1) {

              ret= omap2_mcspi_request_dma(spi);

              if(ret < 0)

                     returnret;

       }

 

       if(omap2_mcspi_enable_clocks(mcspi))

              return-ENODEV;

 

       ret= omap2_mcspi_setup_transfer(spi, NULL);

       omap2_mcspi_disable_clocks(mcspi);

 

       returnret;

}

 

對static intomap2_mcspi_request_dma(struct spi_device *spi)進行分析。

static int omap2_mcspi_request_dma(structspi_device *spi)

{

       structspi_master    *master = spi->master;

       structomap2_mcspi       *mcspi;

       structomap2_mcspi_dma      *mcspi_dma;

 

       mcspi= spi_master_get_devdata(master);

       mcspi_dma= mcspi->dma_channels + spi->chip_select;

       printk(KERN_ALERT"mcspi_dma->dma_rx_sync_devis %d\n",mcspi_dma->dma_rx_sync_dev);

       printk(KERN_ALERT"mcspi_dma->dma_tx_sync_devis %d\n",mcspi_dma->dma_tx_sync_dev)  ;

       if(omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX",

                     omap2_mcspi_dma_rx_callback,spi,

                     &mcspi_dma->dma_rx_channel)){

              dev_err(&spi->dev,"no RX DMA channel for McSPI\n");

              return-EAGAIN;

       }

 

       if(omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",

                     omap2_mcspi_dma_tx_callback,spi,

                     &mcspi_dma->dma_tx_channel)){

              omap_free_dma(mcspi_dma->dma_rx_channel);

              mcspi_dma->dma_rx_channel= -1;

              dev_err(&spi->dev,"no TX DMA channel for McSPI\n");

              return-EAGAIN;

       }

 

       init_completion(&mcspi_dma->dma_rx_completion);

       init_completion(&mcspi_dma->dma_tx_completion);

 

       return0;

}

主要分析omap_request_dma()函數。

int omap_request_dma(int dev_id, const char*dev_name,

                   void (*callback)(int lch, u16 ch_status,void *data),

                   void *data, int *dma_ch_out)

{

       intch, free_ch = -1;

       unsignedlong flags;

       structomap_dma_lch *chan;

 

       spin_lock_irqsave(&dma_chan_lock,flags);

       for(ch = 0; ch < dma_chan_count; ch++) {

              if(free_ch == -1 && dma_chan[ch].dev_id == -1) {//尋找空閒的DMA chanel

                     free_ch= ch;

                     if(dev_id == 0)

                            break;

              }

       }

       if(free_ch == -1) {

              spin_unlock_irqrestore(&dma_chan_lock,flags);

              return-EBUSY;

       }

       chan= dma_chan + free_ch;

       chan->dev_id= dev_id;

 

       if(cpu_class_is_omap1())

              clear_lch_regs(free_ch);

 

       if(cpu_class_is_omap2())

              omap_clear_dma(free_ch);//初始化所找到的chanel所對應的寄存器

 

       spin_unlock_irqrestore(&dma_chan_lock,flags);

 

       chan->dev_name= dev_name;

       chan->callback= callback;

       chan->data= data;

       chan->flags= 0;

 

#ifndef CONFIG_ARCH_OMAP1

       if(cpu_class_is_omap2()) {

              chan->chain_id= -1;

              chan->next_linked_ch= -1;

       }

#endif

 

       chan->enabled_irqs= OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ;

 

       if(cpu_class_is_omap1())

              chan->enabled_irqs|= OMAP1_DMA_TOUT_IRQ;

       elseif (cpu_class_is_omap2())

              chan->enabled_irqs|= OMAP2_DMA_MISALIGNED_ERR_IRQ |

                     OMAP2_DMA_TRANS_ERR_IRQ;

 

       if(cpu_is_omap16xx()) {

              /*If the sync device is set, configure it dynamically. */

              if(dev_id != 0) {

                     set_gdma_dev(free_ch+ 1, dev_id);

                     dev_id= free_ch + 1;

              }

              /*

               * Disable the 1510 compatibility mode and setthe sync device

               * id.

               */

              dma_write(dev_id| (1 << 10), CCR(free_ch));

       }else if (cpu_is_omap7xx() || cpu_is_omap15xx()) {

              dma_write(dev_id,CCR(free_ch));

       }

 

       if(cpu_class_is_omap2()) {

              omap2_enable_irq_lch(free_ch);//使能free_ch所對應信道號上的中斷

              omap_enable_channel_irq(free_ch);//free_ch  所對應的chanel狀態復位

              /*Clear the CSR register and IRQ status register */

              dma_write(OMAP2_DMA_CSR_CLEAR_MASK,CSR(free_ch));

              dma_write(1<< free_ch, IRQSTATUS_L0);

       }

 

       *dma_ch_out= free_ch;

 

       return0;

}註冊流程:

omap2_mcspi_probe()-->spi_register_master()-->scan_boardinfo()-->spi_new_device()-->spi_add_device()-->spi_setup()-->omap2_mcspi_setup()

 

 

 

 

驅動註冊完成後,主要的數據結構:

 

structspi_master *master{

       master ->dev.class =&spi_master_class;

       master ->dev.parent =get_device(dev);//platform設備的dev

       master->dev.p-> driver_data-> mcspi;

master->bus_num = pdev->id;//此處爲3,爲spi3模塊

master->setup = omap2_mcspi_setup;

master->transfer = omap2_mcspi_transfer;

master->cleanup = omap2_mcspi_cleanup;

      master->num_chipselect =num_chipselect;//此處爲2

}

 

structomap2_mcspi *mcspi{

      mcspi->master = master;

mcspi->phys = r->start;//控制寄存器初始物理地址

      mcspi->base = ioremap(r->start,r->end - r->start + 1);//虛擬地址

      mcspi->ick = clk_get(&pdev->dev,"ick");

      mcspi->fck = clk_get(&pdev->dev,"fck");

mcspi->dma_channels[0].dma_rx_channel= -1;

           mcspi->dma_channels[0].dma_rx_sync_dev= rxdma_id[0];

      //OMAP24XX_DMA_SPI3_RX0         16  DMA請求源序號+1

           mcspi->dma_channels[0].dma_tx_channel= -1;// /爲申請到的DMA信號號

 

           mcspi->dma_channels[0].dma_tx_sync_dev= txdma_id[0];

           // OMAP24XX_DMA_SPI3_TX0 15 DMA 請求源序號+1

           mcspi->dma_channels[1].dma_rx_sync_dev= rxdma_id[1];

      //OMAP24XX_DMA_SPI3_RX1         24  DMA請求源序號+1

           mcspi->dma_channels[1].dma_tx_channel= -1;//爲申請到的DMA信號號

           mcspi->dma_channels[0].dma_tx_sync_dev= txdma_id[1];

           // OMAP24XX_DMA_SPI3_TX1 23 DMA 請求源序號+1

}

structspi_device *proxy{

      proxy ->master = master;

      proxy ->dev.parent =master->dev.parent;;

      proxy ->dev.bus = &spi_bus_type;

      proxy ->dev.release = spidev_release; proxy->chip_select= 0;

      proxy->max_speed_hz = 1500000

      proxy->mode = 0

      proxy->irq =180

      sproxy->modalias=” spidev;

      proxy->dev.platform_data =NULL

      proxy->controller_data = NULL

      proxy->controller_state = NULL;

proxy ->bits_per_word = 8;

cs = kzalloc(sizeof *cs, GFP_KERNEL);         

cs->base = mcspi->base;

           cs->phys = mcspi->phys;

           cs->chconf0 = 0;

      proxy ->controller_state = cs;

      }

//註冊的spi設備

struct spi_board_infoomap3stalker_spi_board_info[] = {

       [0]= {

              .modalias        = “spidev”,

              .bus_num              = 1,

              .chip_select            = 0,

              .max_speed_hz              = 1500000,

              .irq                =OMAP_GPIO_IRQ(OMAP3_STALKER_TS_GPIO),//irq=180

                     },

};

 

 

 

//註冊的spi驅動

static struct spi_driver spidev_spi = {

       .driver= {

              .name=          "spidev",

              .owner= THIS_MODULE,

       },

       .probe=  spidev_probe,

       .remove=       __devexit_p(spidev_remove),

 

       /*NOTE:  suspend/resume methods are notnecessary here.

        * We don't do anything except pass the requeststo/from

        * the underlying controller.  The refrigerator handles

        * most issues; the controller driver handlesthe rest.

        */

};

//分析一下SPI寫的驅動

首先調用

static ssize_t

spidev_write(struct file *filp, const char__user *buf,

              size_tcount, loff_t *f_pos)

{

       structspidev_data   *spidev;

       ssize_t                   status = 0;

       unsignedlong        missing;

 

       /*chipselect only toggles at start or end of operation */

       if(count > bufsiz)

              return-EMSGSIZE;

 

       spidev= filp->private_data;

 

       mutex_lock(&spidev->buf_lock);

       missing= copy_from_user(spidev->buffer, buf, count);//把數據從用戶空間拷到內核空間

       if(missing == 0) {

              status= spidev_sync_write(spidev, count);

       }else

              status = -EFAULT;

       mutex_unlock(&spidev->buf_lock);

 

       returnstatus;

}

接着調用

static inline ssize_t

spidev_sync_write(struct spidev_data*spidev, size_t len)

{

       structspi_transfer   t = {

                     .tx_buf           = spidev->buffer,

                     .len         = len,

              };

       structspi_message  m;

 

       spi_message_init(&m);//初始化m爲0,初始化m->transfers鏈表

       spi_message_add_tail(&t,&m);// 將t->transfer_list加入m->transfers

       returnspidev_sync(spidev, &m);

}

繼續調用

static ssize_t

spidev_sync(struct spidev_data *spidev,struct spi_message *message)

{

       DECLARE_COMPLETION_ONSTACK(done);//聲明並初始化struct completion done

       intstatus;

 

       message->complete= spidev_complete;

       message->context= &done;

 

       spin_lock_irq(&spidev->spi_lock);

       if(spidev->spi == NULL)

              status= -ESHUTDOWN;

       else

              status= spi_async(spidev->spi, message);

       spin_unlock_irq(&spidev->spi_lock);

 

       if(status == 0) {

              wait_for_completion(&done);

              status= message->status;

              if(status == 0)

                     status= message->actual_length;

       }

       returnstatus;

}

 

接着分析

int spi_async(struct spi_device *spi,struct spi_message *message)

{

       structspi_master *master = spi->master;

 

       /*Half-duplex links include original MicroWire, and ones with

        * only one data pin like SPI_3WIRE (switchesdirection) or where

        * either MOSI or MISO is missing.  They can also be caused by

        * software limitations.

        */

       if((master->flags & SPI_MASTER_HALF_DUPLEX)

                     ||(spi->mode & SPI_3WIRE)) {

              structspi_transfer *xfer;

              unsignedflags = master->flags;

 

              list_for_each_entry(xfer,&message->transfers, transfer_list) {

                     if(xfer->rx_buf && xfer->tx_buf)

                            return-EINVAL;

                     if((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)

                            return-EINVAL;

                     if((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)

                            return-EINVAL;

              }

       }

 

       message->spi= spi;

       message->status= -EINPROGRESS;

       returnmaster->transfer(spi, message);

}

調用master->transfer(spi,message),由前面分析,master->transfer = omap2_mcspi_transfer;

static int omap2_mcspi_transfer(structspi_device *spi, struct spi_message *m)

{

       structomap2_mcspi       *mcspi;

       unsignedlong        flags;

       structspi_transfer   *t;

 

       m->actual_length= 0;

       m->status= 0;

 

       /*reject invalid messages and transfers */

       if(list_empty(&m->transfers) || !m->complete)

              return-EINVAL;

       list_for_each_entry(t,&m->transfers, transfer_list) {

              constvoid       *tx_buf = t->tx_buf;

              void        *rx_buf = t->rx_buf;

              unsigned  len = t->len;

 

              if(t->speed_hz > OMAP2_MCSPI_MAX_FREQ

                            ||(len && !(rx_buf || tx_buf))

                            ||(t->bits_per_word &&

                                   (  t->bits_per_word < 4

                                   ||t->bits_per_word > 32))) {

                     dev_dbg(&spi->dev,"transfer: %d Hz, %d %s%s, %d bpw\n",

                                   t->speed_hz,

                                   len,

                                   tx_buf? "tx" : "",

                                   rx_buf? "rx" : "",

                                   t->bits_per_word);

                     return-EINVAL;

              }

              if(t->speed_hz && t->speed_hz <OMAP2_MCSPI_MAX_FREQ/(1<<16)) {

                     dev_dbg(&spi->dev,"%d Hz max exceeds %d\n",

                                   t->speed_hz,

                                   OMAP2_MCSPI_MAX_FREQ/(1<<16));

                     return-EINVAL;

              }

 

              if(m->is_dma_mapped || len < DMA_MIN_BYTES)

                     continue;

 

              /*Do DMA mapping "early" for better error reporting and

               * dcache use. Note that if dma_unmap_single() ever starts

               * to do real work on ARM, we'd need to cleanup mappings

               * for previous transfers on *ALL* exits ofthis loop...

               */

              if(tx_buf != NULL) {

                     //dma內存映射,用於DMA傳輸

                     t->tx_dma= dma_map_single(&spi->dev, (void *) tx_buf,

                                   len,DMA_TO_DEVICE);

                     if(dma_mapping_error(&spi->dev, t->tx_dma)) {

                            dev_dbg(&spi->dev,"dma %cX %d bytes error\n",

                                          'T',len);

                            return-EINVAL;

                     }

              }

              if(rx_buf != NULL) {

                     t->rx_dma= dma_map_single(&spi->dev, rx_buf, t->len,

                                   DMA_FROM_DEVICE);

                     if(dma_mapping_error(&spi->dev, t->rx_dma)) {

                            dev_dbg(&spi->dev,"dma %cX %d bytes error\n",

                                          'R',len);

                            if(tx_buf != NULL)

                                   dma_unmap_single(NULL,t->tx_dma,

                                                 len,DMA_TO_DEVICE);

                            return-EINVAL;

                     }

              }

       }

 

       mcspi= spi_master_get_devdata(spi->master);

 

       spin_lock_irqsave(&mcspi->lock,flags);

       list_add_tail(&m->queue,&mcspi->msg_queue);//加入mcspi->msg_queue鏈表

       queue_work(omap2_mcspi_wq,&mcspi->work);//調度工作隊列

       spin_unlock_irqrestore(&mcspi->lock,flags);

 

       return0;

}

由前面分析,INIT_WORK(&mcspi->work,omap2_mcspi_work),將執行omap2_mcspi_work

 

static void omap2_mcspi_work(structwork_struct *work)

{

       structomap2_mcspi       *mcspi;

 

       mcspi= container_of(work, struct omap2_mcspi, work);

       spin_lock_irq(&mcspi->lock);

 

       if(omap2_mcspi_enable_clocks(mcspi))

              gotoout;

 

       /*We only enable one channel at a time -- the one whose message is

        * at the head of the queue -- although thiscontroller would gladly

        * arbitrate among multiple channels.  This corresponds to "single

        * channel" master mode.  As a side effect, we need to manage the

        * chipselect with the FORCE bit ... CS !=channel enable.

        */

       while(!list_empty(&mcspi->msg_queue)) {// mcspi->msg_queue顯然不爲空

              structspi_message         *m;

              structspi_device            *spi;

              structspi_transfer          *t = NULL;

              int                        cs_active = 0;

              structomap2_mcspi_cs         *cs;

              int                        par_override = 0;

              int                        status = 0;

              u32                       chconf;

 

              m= container_of(mcspi->msg_queue.next, struct spi_message,

                             queue);

 

              list_del_init(&m->queue);

              spin_unlock_irq(&mcspi->lock);

 

              spi= m->spi;

              cs= spi->controller_state;

 

              omap2_mcspi_set_enable(spi,1);

              list_for_each_entry(t,&m->transfers, transfer_list) {

                     if(t->tx_buf == NULL && t->rx_buf == NULL && t->len) {

                            status= -EINVAL;

                            break;

                     }

                     if(par_override || t->speed_hz || t->bits_per_word) {

                            par_override= 1;

                            status= omap2_mcspi_setup_transfer(spi, t);

                            if(status < 0)

                                   break;

                            if(!t->speed_hz && !t->bits_per_word)

                                   par_override= 0;

                     }

 

                     if(!cs_active) {

                            omap2_mcspi_force_cs(spi,1);

                            cs_active= 1;

                     }

 

                     chconf= mcspi_cached_chconf0(spi);

                     chconf&= ~OMAP2_MCSPI_CHCONF_TRM_MASK;

                     if(t->tx_buf == NULL)

                            chconf|= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;

                     elseif (t->rx_buf == NULL)

                            chconf|= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;

                     mcspi_write_chconf0(spi,chconf);

 

                     if(t->len) {

                            unsigned  count;

 

                            /*RX_ONLY mode needs dummy data in TX reg */

                            if(t->tx_buf == NULL)

                                   __raw_writel(0,cs->base

                                                 +OMAP2_MCSPI_TX0);

 

                            if(m->is_dma_mapped || t->len >= DMA_MIN_BYTES)

                                   count= omap2_mcspi_txrx_dma(spi, t);

                            else

                                   count= omap2_mcspi_txrx_pio(spi, t);

                            m->actual_length+= count;

 

                            if(count != t->len) {

                                   status= -EIO;

                                   break;

                            }

                     }

 

                     if(t->delay_usecs)

                            udelay(t->delay_usecs);

 

                     /*ignore the "leave it on after last xfer" hint */

                     if(t->cs_change) {

                            omap2_mcspi_force_cs(spi,0);

                            cs_active= 0;

                     }

              }

 

              /*Restore defaults if they were overriden */

              if(par_override) {

                     par_override= 0;

                     status= omap2_mcspi_setup_transfer(spi, NULL);

              }

 

              if(cs_active)

                     omap2_mcspi_force_cs(spi,0);

 

              omap2_mcspi_set_enable(spi,0);

 

              m->status= status;

              m->complete(m->context);

 

              spin_lock_irq(&mcspi->lock);

       }

 

       omap2_mcspi_disable_clocks(mcspi);

 

out:

       spin_unlock_irq(&mcspi->lock);

}

省略分析中間設置寄存器的過程,分析關鍵的函數:if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)

                                   count= omap2_mcspi_txrx_dma(spi, t);

                            else

                                   count= omap2_mcspi_txrx_pio(spi, t);

 

omap2_mcspi_txrx_pio(spi, t)函數很簡單,只分析count =omap2_mcspi_txrx_dma(spi, t)

起定義如下:

 

static unsigned

omap2_mcspi_txrx_dma(struct spi_device*spi, struct spi_transfer *xfer)

{

       structomap2_mcspi       *mcspi;

       structomap2_mcspi_cs  *cs =spi->controller_state;

       structomap2_mcspi_dma  *mcspi_dma;

       unsignedint           count, c;

       unsignedlong        base, tx_reg, rx_reg;

       int                 word_len, data_type,element_count;

       u8                  * rx;

       constu8         * tx;

 

       mcspi= spi_master_get_devdata(spi->master);

       mcspi_dma= &mcspi->dma_channels[spi->chip_select];

 

       count= xfer->len;

       c= count;

       word_len= cs->word_len;

 

       base= cs->phys;

       tx_reg= base + OMAP2_MCSPI_TX0;

       rx_reg= base + OMAP2_MCSPI_RX0;

       rx= xfer->rx_buf;

       tx= xfer->tx_buf;

 

       if(word_len <= 8) {

              data_type= OMAP_DMA_DATA_TYPE_S8;

              element_count= count;

       }else if (word_len <= 16) {

              data_type= OMAP_DMA_DATA_TYPE_S16;

              element_count= count >> 1;

       }else /* word_len <= 32 */ {

              data_type= OMAP_DMA_DATA_TYPE_S32;

              element_count= count >> 2;

       }

 

       if(tx != NULL) {

       //設置DMA傳輸參數,具體可以參照芯片資料的sDMA模塊

              omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,

                            data_type,element_count, 1,

                            OMAP_DMA_SYNC_ELEMENT,

                            mcspi_dma->dma_tx_sync_dev,0);

 

              omap_set_dma_dest_params(mcspi_dma->dma_tx_channel,0,

                            OMAP_DMA_AMODE_CONSTANT,

                            tx_reg,0, 0);

 

              omap_set_dma_src_params(mcspi_dma->dma_tx_channel,0,

                            OMAP_DMA_AMODE_POST_INC,

                            xfer->tx_dma,0, 0);

       }

 

       if(rx != NULL) {

              omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel,

                            data_type,element_count - 1, 1,

                            OMAP_DMA_SYNC_ELEMENT,

                            mcspi_dma->dma_rx_sync_dev,1);

 

              omap_set_dma_src_params(mcspi_dma->dma_rx_channel,0,

                            OMAP_DMA_AMODE_CONSTANT,

                            rx_reg,0, 0);

 

              omap_set_dma_dest_params(mcspi_dma->dma_rx_channel,0,

                            OMAP_DMA_AMODE_POST_INC,

                            xfer->rx_dma,0, 0);

       }

 

       if(tx != NULL) {

              omap_start_dma(mcspi_dma->dma_tx_channel);

              omap2_mcspi_set_dma_req(spi,0, 1);

       }

 

       if(rx != NULL) {

              omap_start_dma(mcspi_dma->dma_rx_channel);

              omap2_mcspi_set_dma_req(spi,1, 1);

       }

 

       if(tx != NULL) {

              wait_for_completion(&mcspi_dma->dma_tx_completion);//等待傳輸完成

              dma_unmap_single(NULL,xfer->tx_dma, count, DMA_TO_DEVICE);//釋放申請的DMA內存

       }

 

       if(rx != NULL) {

              wait_for_completion(&mcspi_dma->dma_rx_completion);

              dma_unmap_single(NULL,xfer->rx_dma, count, DMA_FROM_DEVICE);

              omap2_mcspi_set_enable(spi,0);

              if(likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)

                            &OMAP2_MCSPI_CHSTAT_RXS)) {

                     u32w;

 

                     w= mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);

                     if(word_len <= 8)

                            ((u8*)xfer->rx_buf)[element_count - 1] = w;

                     elseif (word_len <= 16)

                            ((u16*)xfer->rx_buf)[element_count - 1] = w;

                     else/* word_len <= 32 */

                            ((u32*)xfer->rx_buf)[element_count - 1] = w;

              }else {

                     dev_err(&spi->dev,"DMA RX last word empty");

                     count-= (word_len <= 8)  ? 1 :

                             (word_len <= 16) ? 2 :

                            /* word_len <= 32 */ 4;

              }

              omap2_mcspi_set_enable(spi,1);

       }

       returncount;

}

主要分析,設置好DMA參數後,進行DMA傳輸:

if (tx != NULL) {

              omap_start_dma(mcspi_dma->dma_tx_channel);//使能對應chanel上的中斷

              omap2_mcspi_set_dma_req(spi,0, 1);

       }

 

if (tx != NULL) {

              wait_for_completion(&mcspi_dma->dma_tx_completion);//等待傳輸完成

              dma_unmap_single(NULL,xfer->tx_dma, count, DMA_TO_DEVICE);//釋放申請的DMA內存

wait_for_completion(&mcspi_dma->dma_tx_completion);//等待傳輸完成,此時進程重新調度,進入等待隊列。下面中斷處理函數喚醒等待隊列,此時傳輸已經完成

static void omap2_mcspi_dma_tx_callback(intlch, u16 ch_status, void *data)

{

       structspi_device     *spi = data;

       structomap2_mcspi       *mcspi;

       structomap2_mcspi_dma      *mcspi_dma;

       mcspi= spi_master_get_devdata(spi->master);

       mcspi_dma= &(mcspi->dma_channels[spi->chip_select]);

       complete(&mcspi_dma->dma_tx_completion);

       /*We must disable the DMA TX request */

       omap2_mcspi_set_dma_req(spi,0, 0);

}

簡單測試程序:

#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#include <fcntl.h>

#include <sys/mman.h>

int main(int argc ,char **argv)

{

       int fbfd=0;

       unsigned int i,num;

       for(i=0;i<100;i++)

       buf[i]=0x5a;

       fbfd = open("/dev/spidev3.0",O_RDWR);

       if(!fbfd)

       {

                printf("openfail\n");

                exit(1);

       }

       while(1)

{  

num=write(fbfd,buf,100);

 

                     printf("thenum is %d \n",num);

    sleep(1);

}

close(fbfd);

  return 0;

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章