linux SPI源码剖析

来源:互联网 发布:战龙三国黄忠进阶数据 编辑:程序博客网 时间:2024/05/05 01:10

1.      SPI

1.1.      Linux框架

Spi总线是主从控制方式,总体结构如下:


1.1.1.     Spi bus

<drivers/spi/spi.c>

struct bus_type spi_bus_type = {

         .name               = "spi",

         .dev_attrs       = spi_dev_attrs,

         .match              = spi_match_device,

         .uevent            = spi_uevent,

         .suspend                  =spi_suspend,

         .resume           = spi_resume,

};

/*总线match方法:name匹配方法*/

static int spi_match_device(struct device*dev, struct device_driver *drv)

{

         conststruct spi_device          *spi =to_spi_device(dev);

         conststruct spi_driver  *sdrv =to_spi_driver(drv);

 

         if(sdrv->id_table)/*根据驱动中定义一组name,匹配*/

                   return!!spi_match_id(sdrv->id_table, spi);

 

         returnstrcmp(spi->modalias, drv->name) == 0;/*根据别名进行匹配*/

}

1.1.2.     Spi驱动注册

<drivers/spi/spi.c>

int spi_register_driver(struct spi_driver*sdrv)

{

         sdrv->driver.bus= &spi_bus_type;

         if(sdrv->probe)

                   sdrv->driver.probe= spi_drv_probe;

         if(sdrv->remove)

                   sdrv->driver.remove= spi_drv_remove;

         if(sdrv->shutdown)

                   sdrv->driver.shutdown= spi_drv_shutdown;

         returndriver_register(&sdrv->driver);

}

/*驱动probe方法*/

static int spi_drv_probe(struct device *dev)

{

         conststruct spi_driver           *sdrv =to_spi_driver(dev->driver);

 

         returnsdrv->probe(to_spi_device(dev));//直接调用平台probe方法

}

1.1.3.     Master 控制器注册

<linux/spi/spi.h>

struct spi_master {

         structdevice  dev;

         s16                     bus_num;/*控制器序号*/

         u16                     num_chipselect;/*片选数量*/

         u16                     dma_alignment;

         /*spi_device.mode flags understood by this controller driver */

         u16                     mode_bits;

         /*other constraints relevant to this driver */

         u16                     flags;

#define SPI_MASTER_HALF_DUPLEX         BIT(0)                /*can't do full duplex */

#define SPI_MASTER_NO_RX     BIT(1)                /*can't do buffer read */

#define SPI_MASTER_NO_TX     BIT(2)                /*can't do buffer write */

         /*安装spi设备,配置如mode,clock等*/

         int                       (*setup)(structspi_device *spi);

         /*双向传输*/

         int                       (*transfer)(structspi_device *spi,

                                                        structspi_message *mesg);

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

         void                   (*cleanup)(struct spi_device*spi);

};

 

/*master注册*/

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;

 

         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

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

                    */

                   master->bus_num= atomic_dec_return(&dyn_bus_id);

                   dynamic= 1;

         }

         /*mater的名字为:spi控制器号*/

         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);/*遍历控制器下的spi设备*/

         status= 0;

done:

         returnstatus;

}

static void scan_boardinfo(structspi_master *master)

{

         structboardinfo     *bi;

 

         mutex_lock(&board_lock);

         list_for_each_entry(bi,&board_list,list) {/*spi_register_board_info中入队列*/

                   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);

                   }

         }

         mutex_unlock(&board_lock);

}

1.1.4.     Spi device注册

设备注册时靠在board文件中定义spi_board_info来实现的

struct spi_board_info {//代表spi设备信息

         /*the device name and module name are coupled, like platform_bus;

          * "modalias" is normally the drivername.

          *

          * platform_data goes tospi_device.dev.platform_data,

          * controller_data goes tospi_device.controller_data,

          * irq is copied too

          */

         char          modalias[SPI_NAME_SIZE];

         constvoid       *platform_data;

         void          *controller_data;

         int             irq;

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

         u32           max_speed_hz;

         /*bus_num is board specific and matches the bus_num of some

          * spi_master that will probably be registeredlater.

          *

          * chip_select reflects how this chip is wiredto that master;

          * it's less than num_chipselect.

          */

         u16           bus_num;

         u16           chip_select;

 

         /*mode becomes spi_device.mode, and is essential for chips

          * where the default of SPI_CS_HIGH = 0 iswrong.

          */

         u8              mode;

 

         /*... may need additional spi_device chip config data here.

          * avoid stuff protocol drivers can set; butinclude stuff

          * needed to behave without being bound to adriver:

          *  -quirks like clock rate mattering when not selected

          */

};

/*注册board信息*/

int __init spi_register_board_info(structspi_board_info const *info, unsigned n)

{

         structboardinfo     *bi;

 

         bi= kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);

         if(!bi)

                   return-ENOMEM;

         bi->n_board_info= n;

         memcpy(bi->board_info,info, n * sizeof *info);

 

         mutex_lock(&board_lock);

         list_add_tail(&bi->list,&board_list);//加入到此列表中,等待master注册时遍历

         mutex_unlock(&board_lock);

         return0;

}

 

struct spi_device {

         structdevice           dev;

         structspi_master  *master;/*所属控制器*/

         u32                     max_speed_hz;

         u8                       chip_select;

         u8                       mode;

#define   SPI_CPHA        0x01                            /*clock phase */

#define   SPI_CPOL        0x02                            /*clock polarity */

#define   SPI_MODE_0 (0|0)                           /*(original MicroWire) */

#define   SPI_MODE_1 (0|SPI_CPHA)

#define   SPI_MODE_2 (SPI_CPOL|0)

#define   SPI_MODE_3 (SPI_CPOL|SPI_CPHA)

#define   SPI_CS_HIGH 0x04                            /*chipselect active high? */

#define   SPI_LSB_FIRST        0x08                            /*per-word bits-on-wire */

#define   SPI_3WIRE      0x10                            /*SI/SO signals shared */

#define   SPI_LOOP        0x20                            /*loopback mode */

#define   SPI_NO_CS     0x40                            /*1 dev/bus, no chipselect */

#define   SPI_READY      0x80                            /*slave pulls low to pause */

         u8                       bits_per_word;

         int                       irq;

         void                   *controller_state;

         void                   *controller_data;

         char                   modalias[SPI_NAME_SIZE];/*别名*/

 

         /*

          * likely need more hooks for more protocoloptions affecting how

          * the controller talks to each chip, like:

          *  -memory packing (12 bit samples into low bits, others zeroed)

          *  -priority

          *  -drop chipselect after each word

          *  -chipselect delays

          *  - ...

          */

};

 

/*添加新的spi设备到系统中*/

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);//组建spi_device

         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);

                   returnNULL;

         }

 

         returnproxy;

}

/*安装及初始化spi设备*/

int spi_add_device(struct spi_device *spi)

{

         staticDEFINE_MUTEX(spi_add_lock);

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

         structdevice *d;

         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);

 

         /*根据spi设备的名字,从总线上找驱动*/

         d= bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev));

         if(d != NULL) {

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

                                     spi->chip_select);

                   put_device(d);

                   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);/*安装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;

}

/*调用master的setup函数*/

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;

}

 

1.1.5.     User space访问接口

<linux/spi/spidev.h>

#define SPI_CPHA                   0x01         //极性

#define SPI_CPOL          0x02         //相位

 

#define SPI_MODE_0            (0|0)

#define SPI_MODE_1            (0|SPI_CPHA)

#define SPI_MODE_2            (SPI_CPOL|0)

#define SPI_MODE_3            (SPI_CPOL|SPI_CPHA)

 

SCLK极性和相位关系

 

<drivers/spi/spidev.c>

/*probe驱动,spi*/

static struct spi_driver spidev_spi_driver ={

         .driver= {

                   .name=           "spidev",//在board文件中定义,同spi 设备匹配

                   .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 therequests to/from

          * the underlying controller.  The refrigerator handles

          * most issues; the controller driver handlesthe rest.

          */

};

 

static int __init spidev_init(void)

{

         intstatus;

 

         /*Claim our 256 reserved 0device numbers. Then register a class

          * that will key udev/mdev to add/remove /devnodes.  Last, register

          * the driver which manages those devicenumbers.

          */

         BUILD_BUG_ON(N_SPI_MINORS> 256);

         status= register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);//注册为字符设备

         if(status < 0)

                   returnstatus;

 

         spidev_class= class_create(THIS_MODULE, "spidev");

         if(IS_ERR(spidev_class)) {

                   unregister_chrdev(SPIDEV_MAJOR,spidev_spi_driver.driver.name);

                   returnPTR_ERR(spidev_class);

         }

 

         status= spi_register_driver(&spidev_spi_driver);

         if(status < 0) {

                   class_destroy(spidev_class);

                   unregister_chrdev(SPIDEV_MAJOR,spidev_spi_driver.driver.name);

         }

         returnstatus;

}

 

1.1.6.     bitbang软件模拟spi

bitbang为软件模拟spi时序

<drivers/spi/bitbang.c>

 

 


原创粉丝点击