Chinaunix首页 | 论坛 | 博客
  • 博客访问: 175760
  • 博文数量: 44
  • 博客积分: 627
  • 博客等级: 中士
  • 技术积分: 345
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-20 21:55
文章分类

全部博文(44)

文章存档

2012年(44)

分类: LINUX

2012-08-08 17:14:01

SPI设备建立过程  

2010-08-01 10:59:55|  分类: arm linux设备驱 |  标签: |字号 

//S3C2440A中集成了两个SPI接口,SPI接口作为平台设备添加到内核。

static int __init s3c24xx_spi_probe(struct platform_device *pdev)
{
 struct s3c2410_spi_info *pdata;
 struct s3c24xx_spi *hw;
 struct spi_master *master;
 struct resource *res;
 int err = 0;

hw->pdata = pdata = pdev->dev.platform_data; //platform_data是驱动移植时要实现的重要结构体

。。。。。。

//一个接口对应一个master,一个master对应一条SPI总线,一条总线上可能挂有多个设备,num_chipselect 就表示该总线上的设备

//数

 master->num_chipselect = hw->pdata->num_cs; 

//master->bus_num记录的是总线号
 master->bus_num = pdata->bus_num;

//spi_master代表了一个SPI接口。在一个设备驱动中都有一个与硬件紧密相关的文件,在这样的文件中都有一个

//重要的结构体,在硬件设备工作起来时这个结构体收集相关硬件信息,状态信息,和建立重要结构体等,

//在SPI驱动中在文件linux/drivers/spi/spi_s3c24xx.c中就有这样一个结构体s3c24xx_spi。

//此处便是为结构体spi_master和结构体s3c24xx_spi分配存储空间,并将s3c24xx_spi设为spi_master的drvdata。

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

//结构体bitbang中包含了一些硬件操作函数和完成数据传输的工作队列。

 hw->bitbang.master         = hw->master;
。。。。。。

 err = spi_bitbang_start(&hw->bitbang);  @@@@
。。。。。。
 return err;
}

/************************************************************************************************************/

int spi_bitbang_start(struct spi_bitbang *bitbang)
{

。。。。。。

初始化结构体spi_bitbang的各字段。
。。。。。。
 status = spi_register_master(bitbang->master);@@@@
。。。。。。
 return status;
}

/******************************************************************************************************/

int spi_register_master(struct spi_master *master)
{
 static atomic_t  dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
 struct device  *dev = master->dev.parent;
 int   status = -ENODEV;
 int   dynamic = 0;

。。。。。。

//将设备master->dev添加到内核,它代表一个SPI接口也对应着一条总线。
 dev_set_name(&master->dev, "spi%u", master->bus_num);
 status = device_add(&master->dev);
 。。。。。。
 scan_boardinfo(master);@@@@
 status = 0;
done:
 return status;
}

/******************************************************************************************************/

/*

要让设备模型建立工作继续我们必须认识两个结构体。

//结构体boardinfo管理多个结构体spi_board_info,结构体spi_board_info中挂在SPI总线上的设备的平台信息。

//一个结构体spi_board_info对应着一个SPI设备spi_device。

struct boardinfo {
 struct list_head list;  //用于在注册后挂接到链表board_list上。
 unsigned  n_board_info;  //该结构体所管理的spi_board_info的数目
 struct spi_board_info board_info[0];
};

//在设备移植时填充结构体spi_board_info时移植的重要工作,同时还要调用函数spi_register_board_info()将

//结构体boardinfo添加到链表board_list上。函数spi_register_board_info()在文件spi.c中实现。

int __init
spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
 struct boardinfo *bi;

 bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);  //为结构体boardinfo 分配内存空间
 if (!bi)
  return -ENOMEM;
 bi->n_board_info = n;   //boardinfo所管理的spi_board_info的数目
 memcpy(bi->board_info, info, n * sizeof *info); //为这些spi_board_info分配内存空间

 mutex_lock(&board_lock);
 list_add_tail(&bi->list, &board_list);     //将boardinfo添加到链表board_list。
 mutex_unlock(&board_lock);
 return 0;
}

//结构体n_board_info的原型如下,该结构体描述了一个SPI设备spi_device,结构体spi_board_info中的各字段

//都将用于初始化SPI设备结构体spi_device。

struct spi_board_info {
 char  modalias[32];  //设备spi_device在SPI总线spi_bus_type上匹配驱动的唯一标识。
 const void *platform_data;
 void  *controller_data;
 int  irq;
 u32  max_speed_hz; //SPI设备工作时的波特率
 u16  bus_num;   //该SPI设备所在总线的总线号
 u16  chip_select; //该该SPI设备在该条SPI总线上的设备号的唯一标识,
 u8  mode;
};

*/

//结构体master代表一个SPI接口,一个接口对应一条SPI总线,master->bus_num则记录了这个总线号。

//在接口被注册时,它会到链表board_list上找出所有总线号与该接口master->bus_num匹配的设备(spi_board_info),并

//为该设备创建一个spi_device结构体,添加到内核。

static void scan_boardinfo(struct spi_master *master)
{
 struct boardinfo *bi;

 mutex_lock(&board_lock);
 list_for_each_entry(bi, &board_list, list) {
  struct spi_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;

   (void) spi_new_device(master, chip);@@@@
  }
 }
 mutex_unlock(&board_lock);
}

/******************************************************************************************************/

struct spi_device *spi_new_device(struct spi_master *master,
      struct spi_board_info *chip)
{
 struct spi_device *proxy;
 int   status;

/*

struct spi_device *spi_alloc_device(struct spi_master *master)
{
 struct spi_device *spi;
 struct device  *dev = master->dev.parent;

 spi = kzalloc(sizeof *spi, GFP_KERNEL);
。。。。。。

 spi->master = master;
 spi->dev.parent = dev;
 spi->dev.bus = &spi_bus_type;   //总线类型为SPI总线spi_bus_type。
 spi->dev.release = spidev_release;
 device_initialize(&spi->dev);
 return spi;
}

//以上便是函数spi_alloc_device()的具体实现,他的主要工作是分配spi_device结构体并初始化spi->dev的一些字段。

*/

 proxy = spi_alloc_device(master);
 if (!proxy)
  return NULL;

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

//一下是用结构提spi_board_info中的信息初始化SPI设备spi_device的一些字段

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

 return proxy;
}

/******************************************************************************************************/

int spi_add_device(struct spi_device *spi)
{
 static DEFINE_MUTEX(spi_add_lock);
 struct device *dev = spi->master->dev.parent;
 int status;

 //master->num_chipselect字段是用结构体s3c2410_spi_info 的num_cs 成员初始化的。该字段表示接口上的SPI设备数目。

//结构体s3c2410_spi_info是移植时要实现的重要结构体。它SPI平台设备的platform_data被添加和调用的,它用来初始化master,

 //master->num_chipselect //接口上的设备数目
// master->bus_num  //接口对应的总线号                  

//设备在接口上的编号当然不能超过,接口上设备数。


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

//设置设备名,设备名后缀设备编号,在同一SPI总线上设备名是不能重复的。
 dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
   spi->chip_select);
 mutex_lock(&spi_add_lock);

//新加入的设备是不能与SPI总线spi_bus_type上已有的设备同名。

 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;
  goto done;
 }
 status = spi->master->setup(spi);
 if (status < 0) {
  dev_err(dev, "can't %s %s, status %d\n",
    "setup", dev_name(&spi->dev), status);
  goto done;
 }
 status = device_add(&spi->dev); //将SPI设备添加到内核。
 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);
 return status;
}

/******************************************************************************************************/

设备添加到了总线上当然要与该总线上对应的驱动相匹配。

在spi_bus_type总线上设备和驱动的匹配函数的实现如下。可以看出SPI设备的spi->modalias字段是

匹配的唯一标识。字段spi->modalias是由就够提spi_board_info的字段modalias来初始化的。

static int spi_match_device(struct device *dev, struct device_driver *drv)
{
 const struct spi_device *spi = to_spi_device(dev);

 return strcmp(spi->modalias, drv->name) == 0;
}

/******************************************************************************************************/

static int spidev_probe(struct spi_device *spi)
{
。。。。。。

//在匹配成功后找出一个空闲的设备号,以SPI设备为父设备创建一个设备节点。
 minor = find_first_zero_bit(minors, N_SPI_MINORS);
 if (minor < N_SPI_MINORS) {
  struct device *dev;

  spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
  dev = device_create(spidev_class, &spi->dev, spidev->devt,
        spidev, "spidev%d.%d",
        spi->master->bus_num, spi->chip_select);
  status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
 } else {
  dev_dbg(&spi->dev, "no minor number available!\n");
  status = -ENODEV;
 }
。。。。。。

 return status;
}

/******************************************************************************************************/

所有的SPI设备都有一个共同的主设备号

#define SPIDEV_MAJOR   153 /* assigned */

这个主设备号在文件spidev.c 中申请。

static int __init spidev_init(void)
{
 int status;

//申请一个设备号SPIDEV_MAJOR并关联一个操作函数集spidev_fops该函数集在文件spidev.c中实现。
 status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
 if (status < 0)
  return status;

 spidev_class = class_create(THIS_MODULE,"spidev");  //创建一个名为"spidev" 的类。
 if (IS_ERR(spidev_class)) {
  unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
  return PTR_ERR(spidev_class);
 }

 status = spi_register_driver(&spidev_spi); //注册SPI设备驱动。
 if (status < 0) {
  class_destroy(spidev_class);
  unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
 }
 return status;
}

阅读(2142) | 评论(0) | 转发(0) |
0

上一篇:DMA原理

下一篇:RTC实时时钟驱动程序分析

给主人留下些什么吧!~~