zoukankan      html  css  js  c++  java
  • lpc3250spi控制器驱动部分分析

     

    lpc3250spi控制器驱动部分分析

    http://blog.csdn.net/yongan1006/article/details/7187713   

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

    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;

    }



  • 相关阅读:
    MD5 带salt 加密
    生成包含数字和大小写字母的随机码
    多读好代码助于提高
    Winform程序窗体间的跳转
    Sql Server 存储过程
    GDI+的学习
    管理人生的8个危机
    马云语录
    无边框窗体的拖动和拉伸
    安装oracle时遇到 环境变量path的值超过1023字符,无法设置该值
  • 原文地址:https://www.cnblogs.com/yuzaipiaofei/p/4124196.html
Copyright © 2011-2022 走看看