lpc3250spi控制器驱动部分分析

本人是初学者,只为备忘。

SPI驱动分SPI控制器驱动和SPI设备驱动

SPI控制器驱动:以下是代码在arch-lpc32xx.c中。LPC3250有两个SSP控制器(可配置成两个SPI控制器)。它将两个控制器注册成平台设备,但两个控制器使用一个驱动.只有id不一样。

#if defined(CONFIG_SPI_LPC32XX)

#ifdefined(CONFIG_MACH_LPC32XX_SSP0_ENABLE)

static struct resource ssp0_resources[] = {

       [0]= {

              .start       = SSP0_BASE,

              .end = SSP0_BASE + SZ_4K - 1,

              .flags      = IORESOURCE_MEM,

       },

       [1]= {

              .start       = IRQ_SSP0,

              .end = IRQ_SSP0,

              .flags      = IORESOURCE_IRQ,

       },

 

};

static struct platform_device ssp0_device ={

       .name             = "spi_lpc32xx",

       .id           = 0,

       .dev        = {

              .platform_data = &lpc32xx_spi1data,

       },

       .num_resources     = ARRAY_SIZE(ssp0_resources),

       .resource = ssp0_resources,

};

#endif

#ifdefined(CONFIG_MACH_LPC32XX_SSP1_ENABLE)

static struct resource ssp1_resources[] = {

       [0]= {

              .start       = SSP1_BASE,

              .end = SSP1_BASE + SZ_4K - 1,

              .flags      = IORESOURCE_MEM,

       },

       [1]= {

              .start       = IRQ_SSP1,

              .end = IRQ_SSP1,

              .flags      = IORESOURCE_IRQ,

       },

 

};

static struct platform_device ssp1_device ={

       .name             = "spi_lpc32xx",

       .id           = 1,

       .dev        = {

              .platform_data = &lpc32xx_spi2data,

       },

       .num_resources     = ARRAY_SIZE(ssp1_resources),

       .resource = ssp1_resources,

};

#endif

#endif

通过以上代码,SPI控制器会在系统初始化的时候,通过platform_device_register将它注册进系统。

以上注册完了SPI控制器的device。下一步注册SPI控制器的driver

static struct platform_driverlpc32xx_spi_driver = {

       .probe            = lpc32xx_spi_probe,

       .remove          = __devexit_p(lpc32xx_spi_remove),

       .driver            = {

              .name      = "spi_lpc32xx",

              .owner    = THIS_MODULE,

       },

};

在Linux 中,每一个类型的驱动都会有一个相应的结构体来描述,这里的spi_master就是

用来描述SPI 主机控制器驱动的,其主要成员是bus_num,cs,spi 模式和时钟设置用到的

函数,数据传输用到的函数等。

分配、注册和注销SPI 主机驱动结构体的API 由SPI 核心层提供:

struct spi_master * spi_alloc_master(structdevice *host, unsigned size);

int spi_register_master(struct spi_master*master);

void spi_unregister_master(structspi_master *master);

 

使用到platform 总线API 注册到platform

总线上的控制器设备和驱动,都是common 的部分。specific 的部分,会在platform driver

注册后,在probe 函数里面基于注册的common 部分的资源信息来具体实现。称之为

spi_master 的注册部分。

spi_master结构

1.    structspi_master { 

2.    struct device dev; 

3.     

4.    s16   bus_num;//总线编号,从零开始  

5.     

6.    u16   num_chipselect;//支持的片选的数量。从设备的片选号不能大于这个数  

7.     

8.    /* setup mode and clock, etc (spi driver may call many times) */ 

9.    int   (*setup)(struct spi_device *spi);//根据spi设备更新硬件配置。   

10.    

11.   int   (*transfer)(struct spi_device *spi,struct spi_message *mesg);//添加消息到队列的方法。这个函数不可睡眠。它的职责是安排发生的传送并且调用注册的回调函数complete()。  

12.    

13.   /* called on release() to free memory provided by spi_master */ 

14.   void   (*cleanup)(struct spi_device*spi);//cleanup函数会在spidev_release函数中被调用,spidev_release被登记为spi dev的release函数。  

15.   }; 

 

 

spi的设备驱动

SPI 设备要挂载到SPI 总线上,这个过程是由SPIcore 提供的一些API 来完成的,

spi_register_driver(),spi_register_device()。

module init 函数中使用spi_register_driver 注册外设驱动,并注册char 设备,导出SPI 操作

API。

1.    structspi_driver { 

2.    int   (*probe)(struct spi_device *spi);//和spi匹配成功之后会调用这个方法。因此这个方法需要对设备和私有数据进行初始化。  

3.    int   (*remove)(struct spi_device*spi);//解除spi_device和spi_driver的绑定,释放probe申请的资源。  

4.    void   (*shutdown)(struct spi_device*spi);//关闭  

5.    int   (*suspend)(struct spi_device *spi,pm_message_t mesg);//挂起  

6.    int   (*resume)(struct spi_device*spi);//恢复  

7.    struct device_driver driver; 

8.    }

 

 

1.    structspi_device {  

2.    struct device  dev; 

3.    struct spi_master *master;//对应的控制器指针  

4.    u32   max_speed_hz;//spi通信时钟  

5.    u8   chip_select;//片选号,用来区分同一主控制器上的设备。  

6.    u8   mode;//各位的定义如下,主要是传输模式、片选极性。  

7.    #defineSPI_CPHA 0x01   /* clock phase */  

8.    #defineSPI_CPOL 0x02   /* clock polarity */  

9.    #defineSPI_MODE_0 (0|0)   /* (originalMicroWire) */  

10.   #defineSPI_MODE_1 (0|SPI_CPHA)  

11.   #defineSPI_MODE_2 (SPI_CPOL|0)  

12.   #defineSPI_MODE_3 (SPI_CPOL|SPI_CPHA)  

13.   #defineSPI_CS_HIGH 0x04   /* chipselect activehigh? */片选电位为高  

14.   #defineSPI_LSB_FIRST 0x08   /* per-wordbits-on-wire */先输出低比特  

15.   #defineSPI_3WIRE 0x10   /* SI/SO signals shared*/输入输出共享接口,此时只能做半双工。  

16.   #defineSPI_LOOP 0x20   /* loopback mode */回写/回显模式  

17.   u8   bits_per_word;//每个字长的比特数。  

18.   int   irq;//使用到的中断  

19.   void   *controller_state; 

20.   void   *controller_data; 

21.   char   modalias[32];//名字。  

22.   }; 

23.    

24.   ~~~~~~~~~~~~~~~~~~~~~~~ 

25.   这个结构体描述设备的信息。 

26.   structspi_board_info { 

27.   char  modalias[32];//设备名  

28.   const void *platform_data;//平台数据  

29.   void  *controller_data; 

30.   int  irq;//中断  

31.    

32.   /* slower signaling on noisy or low voltage boards */ 

33.   u32  max_speed_hz;//通信时钟  

34.    

35.   u16  bus_num;//总线号  

36.   u16  chip_select;//片选号  

37.    

38.   u8  mode;//参考spi_device中的成员  

39.   }; 


以下是部分spi-lpc32xx.c驱动分析

static int __init lpc32xx_spi_probe(structplatform_device *pdev)

{

       structspi_master *master;

       structlpc32xxspi *spidat;

       structresource *res;

       charclkname[16];

       intret, irq, i;

 

       /*Get required resources */

       res= platform_get_resource(pdev, IORESOURCE_MEM, 0);

       irq= platform_get_irq(pdev, 0);

//通过以上语句获得了相应的内存和中断资源。

       if((!res) || (irq < 0) | (irq >= NR_IRQS))

       {

              return-EBUSY;

       }

 

       master= spi_alloc_master(&pdev->dev, sizeof(struct lpc32xxspi));

//第二个参数指明要为dev.driver_data分配的空间

       if(!master)

       {

              return-ENODEV;

       }

       spidat= spi_master_get_devdata(master);

//将spi->master->dev->driver_data 的空间指针传递给spidata

 

       platform_set_drvdata(pdev,master);

//将pdev->dev->driver_data赋为master

//pdev作为spi控制器的common部分,master作为spi控制器的specific部分。

       /*Save ID for this device */

       spidat->id= pdev->id;

//id指明了使用哪一个控制器

       spidat->irq= irq;

       spin_lock_init(&spidat->lock);

 

       INIT_WORK(&spidat->work,lpc32xx_work);

//初始化工作队列

       INIT_LIST_HEAD(&spidat->queue);

       init_waitqueue_head(&spidat->waitq);

//初始化等待队列

       spidat->workqueue= create_singlethread_workqueue(master->dev.parent->bus_id);

//创建一个单线程的工作队列

       if(!spidat->workqueue)

       {

              ret= -ENOMEM;

              gotoerrout;

       }

 

       /*Generate clock name and get clock */

       snprintf(clkname,10, "spi%d_ck", spidat->id);

//根据控制器id产生一个时钟名,如spi0_ck

       spidat->clk= clk_get(&pdev->dev, clkname);

       if(IS_ERR(spidat->clk)) {

              ret= -ENODEV;

              gotoerrout_qdel;

       }

       clk_enable(spidat->clk);

 

       /*Save IO resources */

       spidat->membase= ioremap(res->start, res->end - res->start + 1);

       if(!spidat->membase)

       {

              ret= -EBUSY;

              gotoerrout2;

       }

 

       ret= request_irq(spidat->irq, lpc32xx_spi_irq,

              IRQF_DISABLED,"spiirq", spidat);

       if(ret)

       {

              ret= -EBUSY;

              gotoerrout3;

       }

//中断号spidat->irq,这个参数是由pdev传入,要执行的函数指针lpc32xx_spi_irq,

//IRQF_DISABLED,执行中断函数期间,irq被屏蔽

//最后是一个void*类型的指针。

       disable_irq(spidat->irq);

 

       master->bus_num= spidat->id;

       master->setup= lpc32xx_spi_setup;

       master->transfer= lpc32xx_spi_transfer;

 

       /*Is a board specific configuration available? */

       spidat->psspcfg= (struct lpc32xx_spi_cfg *) pdev->dev.platform_data;

//platform_data中的数据就是配置数据,但是配置数据放在两个地方,一个在//dev.platform_data中,另一个就是本文件中对struct lpc32xx_spi_cfg的显式初始化。//psspcfg->num_cs代表此时spi控制器中携带的从设备数。

       if(spidat->psspcfg == NULL)

       {

              spidat->psspcfg= &lpc32xx_stdspi_cfg;

       }

       if(spidat->psspcfg->num_cs < 1)

       {

              spidat->psspcfg= &lpc32xx_stdspi_cfg;

       }

 

       master->num_chipselect= spidat->psspcfg->num_cs;

 

       /*Initialize each chip select and set chip select low */

       for(i = 0; i < spidat->psspcfg->num_cs; i++)

       {

              if(spidat->psspcfg->spi_cs_setup != NULL)

              {

                     spidat->psspcfg->spi_cs_setup(i);

//通过追踪得知,它调用的是static void smartarm3250_spi_cs_setup(int cs)

//此函数仅是将ssel这个引脚置(GPIO_05)为高电平。

              }

              if(spidat->psspcfg->spi_cs_set != NULL)

              {

                     spidat->psspcfg->spi_cs_set(i,0);

//通过追踪得知,它调用的是static int smartarm3250_spi_cs_set(int cs, int state)

//函数认为cs=1时,设备不存在。state的电平和ssel电平同步。

              }

       }

 

       /*Initial setup of SPI */

       lpc32xx_spi_prep(spidat);

 

       /*Keep the SSP clock off until a transfer is performed to save power */

       clk_disable(spidat->clk);

 

       ret= spi_register_master(master);

       if(ret)

       {

              gotoerrout4;

       }

 

       return0;

 

errout4:

       free_irq(spidat->irq,pdev);

errout3:

       iounmap(spidat->membase);

errout2:

       clk_disable(spidat->clk);

       clk_put(spidat->clk);

errout_qdel:

       destroy_workqueue(spidat->workqueue);

errout:

       platform_set_drvdata(pdev,NULL);

       spi_master_put(master);

 

       returnret;

}

 

static void lpc32xx_spi_prep(structlpc32xxspi *spidat)

{

       u32tmp;

 

       /*Clear and mask SSP interrupts */

       __raw_writel((SSP_ICR_RORIC| SSP_ICR_RTIC), SSP_ICR(spidat->membase));

       __raw_writel(0,SSP_IMSC(spidat->membase));

 

       /*Setup default SPI mode */

       __raw_writel((SSP_CR0_DSS(16)| SSP_CR0_FRF_SPI | SSP_CR0_CPOL(0) |

              SSP_CR0_CPHA(0)| SSP_CR0_SCR(0)), SSP_CR0(spidat->membase));

       __raw_writel(SSP_CR1_SSP_ENABLE,SSP_CR1(spidat->membase));

       __raw_writel(SSP_CPSR_CPDVSR(2),SSP_CPSR(spidat->membase));

 

//默认的格式为16位格式,第一种SPI传输模式,主机模式,ssp使能,频率为pclk的2分//频

 

       /*Flush FIFO */

       while(__raw_readl(SSP_SR(spidat->membase)) & SSP_SR_RNE)

       {

              tmp= __raw_readl(SSP_DATA(spidat->membase));

       }

//等待接收FIFO为空

       /*Controller stays disabled until a transfer occurs */

}

 

static void lpc32xx_cs_set_state(structspi_device *spi, unsigned int cs,

                            unsignedint active, unsigned int delay_ns)

{

       structlpc32xxspi *spidat = spi_master_get_devdata(spi->master);

       intval = (spi->mode & SPI_CS_HIGH) ? active : !active;

 

       if(spidat->psspcfg->spi_cs_set != NULL)

       {

              spidat->psspcfg->spi_cs_set(cs,val);

       }

 

       ndelay(delay_ns);

}

 

//如果选择了高电平有效,则active和state状态相同,如果没有选择则状态相反。

static int lpc32xx_spi_setup(structspi_device *spi)

{

       structlpc32xxspi *spidat = spi_master_get_devdata(spi->master);

       unsignedlong flags;

       unsignedint bits = spi->bits_per_word;

       u32tmp;

 

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

       {

              dev_dbg(&spi->dev,

                     "setup:invalid chipselect %u (%u defined)\n",

                     spi->chip_select,spi->master->num_chipselect);

              return-EINVAL;

       }

 

       if(bits == 0)

       {

              bits= 8;

       }

       if((bits < 4) || (bits > 16))

       {

              dev_dbg(&spi->dev,

                     "setup:invalid bits_per_word %u (8 to 16)\n", bits);

              return-EINVAL;

       }

 

       if(spi->mode & ~MODEBITS)

       {

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

                     spi->mode& ~MODEBITS);

              return-EINVAL;

       }

 

       spin_lock_irqsave(&spidat->lock,flags);

       clk_enable(spidat->clk);

 

       /*Setup CR0 register */

       tmp= SSP_CR0_FRF_SPI;

       if(spi->mode & SPI_CPOL)

       {

              tmp|= SSP_CR0_CPOL(1);

       }

       if(spi->mode & SPI_CPHA)

       {

              tmp|= SSP_CR0_CPHA(1);

       }

 

       __raw_writel(tmp,SSP_CR0(spidat->membase));

//写入spi控制器的mode

       lpc32xx_update_spi_dwidth(spidat,bits);

//写入每帧的位数

       lpc32xx_update_spi_clock(spidat,spi->max_speed_hz);

//设置时钟频率

       lpc32xx_cs_set_state(spi,spi->chip_select, 0, 0);

//spi->chip_select表示是选中哪一个从机。目前只支持spi->chip_select=0;

//第三个参数为与cs显示的电平关联,最后一个参数是延时。

       clk_disable(spidat->clk);

       spin_unlock_irqrestore(&spidat->lock,flags);

 

#if defined (SSP_DEBUG)

       dev_dbg(&spi->dev,"SSP (%d) prog rate = %d / "

              "actualrate = %d, bits = %d\n", spi->chip_select,

              spi->max_speed_hz,spidat->current_speed_hz, spidat->current_bits_wd);

#endif

 

       return0;

}

 

static irqreturn_t lpc32xx_spi_irq(int irq,void *dev_id)

{

       structlpc32xxspi *spidat = dev_id;

 

       /*Disable interrupts for now, do not clear the interrupt states */

       __raw_writel(0,SSP_IMSC(spidat->membase));

//禁能中断

       wake_up(&spidat->waitq);

//在中断中,唤醒等待队列。

       returnIRQ_HANDLED;

}






你可能感兴趣的:(lpc3250spi控制器驱动部分分析)