2012年(44)
分类: LINUX
2012-08-08 17:14:01
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 //接口对应的总线号
//设备在接口上的编号当然不能超过,接口上设备数。
//设置设备名,设备名后缀设备编号,在同一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;
}