根据上文中的移植步骤,可以看出主要工作由两方面,
一是定义SPI控制器(struct platform_device s3c_device_spi0,struct s3c2410_spi_info s3c2410_spi0_platdata)。
二是定义SPI从设备(spi_board_info s3c2410_spi0_board[]),因为SPI总线上可以接多个从设备,所以是个结构体数组。
其实还有两个方面就是编写SPI控制器的驱动和编写SPI从设备驱动,只是内核中已经包含了这两部分的代码,前者在
spi_s3c24xx.c和spi_bitbang.c中,后者在spidev.c中,所以只需要make menuconfig配置下,将这几个文件编译进内核即可。
我们之所以要定义SPI控制器就是为了将SPI控制器的参数,如寄存器地址,中断号等内容保存起来,之后SPI控制器的驱动程序
就会读取这些参数,根据这些参数来进行收发数据。举个例子,s3c_device_spi0中定义了中断号,s3c24xx_spi_probe就会通
过platform_get_irq获取到该中断号,并向该中断注册中断处理函数s3c24xx_spi_irq。s3c2410_spi0_platdata定义了cs引脚,
s3c24xx_spi_probe就会通过s3c_device_spi0.dev.platform_data获取并设置该CS引脚。
下面首先会分析SPI控制器端的代码,再分析SPI从设备端的代码,最后分析
测试程序spidev_test.c发送数据时的内核执行流程,从中了解SPI驱动的框架。
需要注意的是SPI总线有可能接的是flash,那么该flash的驱动就不会像spidev.c中那么简单了。
一、SPI控制器端
可见spi控制器设备是作为platform设备向内核进行注册的,其驱动也是作为platform驱动进行注册。
以后也会看到I2C控制器也是以这样的方式注册。
platform设备和驱动的注册过程分析请参见之前的文章,这里只讲和SPI有关的部分。主要是驱动中的probe函数。
3.probe函数
代码是在spidev.c中,
1.spidev_init
spi_drv_probe()函数是驱动注册时发现到匹配的设备后调用的函数。
一是定义SPI控制器(struct platform_device s3c_device_spi0,struct s3c2410_spi_info s3c2410_spi0_platdata)。
二是定义SPI从设备(spi_board_info s3c2410_spi0_board[]),因为SPI总线上可以接多个从设备,所以是个结构体数组。
其实还有两个方面就是编写SPI控制器的驱动和编写SPI从设备驱动,只是内核中已经包含了这两部分的代码,前者在
spi_s3c24xx.c和spi_bitbang.c中,后者在spidev.c中,所以只需要make menuconfig配置下,将这几个文件编译进内核即可。
我们之所以要定义SPI控制器就是为了将SPI控制器的参数,如寄存器地址,中断号等内容保存起来,之后SPI控制器的驱动程序
就会读取这些参数,根据这些参数来进行收发数据。举个例子,s3c_device_spi0中定义了中断号,s3c24xx_spi_probe就会通
过platform_get_irq获取到该中断号,并向该中断注册中断处理函数s3c24xx_spi_irq。s3c2410_spi0_platdata定义了cs引脚,
s3c24xx_spi_probe就会通过s3c_device_spi0.dev.platform_data获取并设置该CS引脚。
下面首先会分析SPI控制器端的代码,再分析SPI从设备端的代码,最后分析
测试程序spidev_test.c发送数据时的内核执行流程,从中了解SPI驱动的框架。
需要注意的是SPI总线有可能接的是flash,那么该flash的驱动就不会像spidev.c中那么简单了。
一、SPI控制器端
1.SPI控制器设备注册
static void __init smdk2410_init(void)
{
/*将spi控制器设备的部分参数存入dev.platform_data,驱动注册时会读取这些参数*/
s3c_device_spi0.dev.platform_data= &s3c2410_spi0_platdata;
/*注册从设备信息*/
spi_register_board_info(s3c2410_spi0_board, ARRAY_SIZE(s3c2410_spi0_board));
...
/*该函数会循环调用platform_device_register()注册CPU集成的各种外设控制器,包括SPI控制器*/
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
}
2.SPI控制器驱动注册
static int __init s3c24xx_spi_init(void)
{
return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
}
platform_driver_probe会调用platform_driver_register()注册驱动。
可见spi控制器设备是作为platform设备向内核进行注册的,其驱动也是作为platform驱动进行注册。
以后也会看到I2C控制器也是以这样的方式注册。
platform设备和驱动的注册过程分析请参见之前的文章,这里只讲和SPI有关的部分。主要是驱动中的probe函数。
3.probe函数
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;
/*分配两个连在一起的空间,master指向第一块,
*并将master->dev->driver_data指向第二块*/
master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
if (master == NULL) {
dev_err(&pdev->dev, "No memory for spi_master\n");
err = -ENOMEM;
goto err_nomem;
}
/*hw指向第二块空间*/
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct s3c24xx_spi));
/*hw->master指向master,即建立master和hw之间的联系*/
hw->master = spi_master_get(master);
/*pdev就是移植时定义的s3c_device_spi0,platform_data则是s3c2410_spi0_platdata*/
hw->pdata = pdata = pdev->dev.platform_data;
hw->dev = &pdev->dev;
if (pdata == NULL) {
dev_err(&pdev->dev, "No platform data supplied\n");
err = -ENOENT;
goto err_no_pdata;
}
/*将s3c_device_spi0.dev.driver_data也指向hw*/
platform_set_drvdata(pdev, hw);
/*定义一个completion,用于数据收发*/
init_completion(&hw->done);
/* setup the master state. */
master->num_chipselect = hw->pdata->num_cs;
master->bus_num = pdata->bus_num;
/* setup the state for the bitbang driver */
/*下列函数会在分析数据发送过程时分析*/
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
hw->bitbang.chipselect = s3c24xx_spi_chipsel;
hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
hw->bitbang.master->setup = s3c24xx_spi_setup;
dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
/* find and map our resources */
/*获取移植时定义的各种resouse,包括寄存器地址,终端号*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
err = -ENOENT;
goto err_no_iores;
}
hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,
pdev->name);
if (hw->ioarea == NULL) {
dev_err(&pdev->dev, "Cannot reserve region\n");
err = -ENXIO;
goto err_no_iores;
}
hw->regs = ioremap(res->start, (res->end - res->start)+1);
if (hw->regs == NULL) {
dev_err(&pdev->dev, "Cannot map IO\n");
err = -ENXIO;
goto err_no_iomap;
}
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq < 0) {
dev_err(&pdev->dev, "No IRQ specified\n");
err = -ENOENT;
goto err_no_irq;
}
err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);
if (err) {
dev_err(&pdev->dev, "Cannot claim IRQ\n");
goto err_no_irq;
}
/*获取时钟,暂不分析,是内核中提供的一套机制,可以获取和enable,disable特定的clk,
*其他的还有gpio*/
hw->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(hw->clk)) {
dev_err(&pdev->dev, "No clock for device\n");
err = PTR_ERR(hw->clk);
goto err_no_clk;
}
/*SPI控制器硬件相关的初始化*/
s3c24xx_spi_initialsetup(hw);
/* setup any gpio we can */
/*设置cs脚的函数,在收发数据时会用到*/
if (!pdata->set_cs) {
hw->set_cs = s3c24xx_spi_gpiocs;
s3c2410_gpio_setpin(pdata->pin_cs, 1);
s3c2410_gpio_cfgpin(pdata->pin_cs, S3C2410_GPIO_OUTPUT);
} else
hw->set_cs = pdata->set_cs;
/* register our spi controller */
/*4*/
err = spi_bitbang_start(&hw->bitbang);
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master\n");
goto err_register;
}
return 0;
err_register:
clk_disable(hw->clk);
clk_put(hw->clk);
err_no_clk:
free_irq(hw->irq, hw);
err_no_irq:
iounmap(hw->regs);
err_no_iomap:
release_resource(hw->ioarea);
kfree(hw->ioarea);
err_no_iores:
err_no_pdata:
spi_master_put(hw->master);;
err_nomem:
return err;
}
4.spi_bitbang_start
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
int status;
if (!bitbang->master || !bitbang->chipselect)
return -EINVAL;
/*稍后分析*/
INIT_WORK(&bitbang->work, bitbang_work);
spin_lock_init(&bitbang->lock);
INIT_LIST_HEAD(&bitbang->queue);
/*设置bitbang->master->transfer */
if (!bitbang->master->transfer)
bitbang->master->transfer = spi_bitbang_transfer;
/*不为空,跳过*/
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!bitbang->master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer =
spi_bitbang_setup_transfer;
bitbang->master->setup = spi_bitbang_setup;
bitbang->master->cleanup = spi_bitbang_cleanup;
}
} else if (!bitbang->master->setup)
return -EINVAL;
/* this task is the only thing to touch the SPI bits */
bitbang->busy = 0;
bitbang->workqueue = create_singlethread_workqueue(
bitbang->master->dev.parent->bus_id);
if (bitbang->workqueue == NULL) {
status = -EBUSY;
goto err1;
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
/*注册SPI控制器 5*/
status = spi_register_master(bitbang->master);
if (status < 0)
goto err2;
return status;
err2:
destroy_workqueue(bitbang->workqueue);
err1:
return status;
}
5.spi_register_master
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;
if (!dev)
return -ENODEV;
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (master->num_chipselect == 0)
return -EINVAL;
/* convention: dynamically assigned bus IDs count 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;
}
/*设置bus_id,spi0,并注册入内核*/
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
snprintf(master->dev.bus_id, sizeof master->dev.bus_id,
"spi%u", master->bus_num);
status = device_add(&master->dev);
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", master->dev.bus_id,
dynamic ? " (dynamic)" : "");
/*6*/
/* populate children from any spi device tables */
scan_boardinfo(master);
status = 0;
done:
return status;
}
6.scan_boardinfo
static void scan_boardinfo(struct spi_master *master)
{
struct boardinfo *bi;
/*第1步中spi_register_board_info已将信息添加到board_list中,
*此时找出来,并注册入内核
*/
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;
/* NOTE: this relies on spi_new_device to
* issue diagnostics when given bogus inputs
*/
/*7*/
(void) spi_new_device(master, chip);
}
}
mutex_unlock(&board_lock);
}
7.spi_new_device
struct spi_device *spi_new_device(struct spi_master *master,
struct spi_board_info *chip)
{
struct spi_device *proxy;
int status;
/* NOTE: caller did any chip->bus_num checks necessary.
*
* Also, unless we change the return value convention to use
* error-or-pointer (not NULL-or-pointer), troubleshootability
* suggests syslogged diagnostics are best here (ugh).
*/
/*分配spi_device*/
proxy = spi_alloc_device(master);
if (!proxy)
return NULL;
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
/*将获取到数据保存到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;
/*8*/
status = spi_add_device(proxy);
if (status < 0) {
spi_dev_put(proxy);
return NULL;
}
return proxy;
}
8.spi_add_device
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct device *dev = spi->master->dev.parent;
int status;
/*这里要做一些判断,可以理解chip_select,num_chipselect的含义*/
/* 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;
}
/*设置bus_id,spi0.0,可以理解bus_id,chip_select的含义
*可以在/sys/bus/spi/下找到spidev0.0的文件
*/
/* Set the bus ID string */
snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id,
"%s.%u", spi->master->dev.bus_id,
spi->chip_select);
/* We need to make sure there's no other device with this
* chipselect **BEFORE** we call setup(), else we'll trash
* its configuration. Lock against concurrent add() calls.
*/
mutex_lock(&spi_add_lock);
if (bus_find_device_by_name(&spi_bus_type, NULL, spi->dev.bus_id)
!= NULL) {
dev_err(dev, "chipselect %d already in use\n",
spi->chip_select);
status = -EBUSY;
goto done;
}
/* Drivers may modify this initial i/o setup, but will
* normally rely on the device being setup. Devices
* using SPI_CS_HIGH can't coexist well otherwise...
*/
/*调用之前设置的s3c24xx_spi_setup函数*/
status = spi->master->setup(spi);
if (status < 0) {
dev_err(dev, "can't %s %s, status %d\n",
"setup", spi->dev.bus_id, status);
goto done;
}
/* 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", spi->dev.bus_id, status);
else
dev_dbg(dev, "registered child %s\n", spi->dev.bus_id);
done:
mutex_unlock(&spi_add_lock);
return status;
}
二、SPI从设备端
代码是在spidev.c中,
1.spidev_init
static int __init spidev_init(void)
{
int status;
/* Claim our 256 reserved device numbers. Then register a class
* that will key udev/mdev to add/remove /dev nodes. Last, register
* the driver which manages those device numbers.
*/
BUILD_BUG_ON(N_SPI_MINORS > 256);
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "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);
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
}
return status;
}
里面需要分析是spi_register_driver函数
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;
return driver_register(&sdrv->driver);
}
注意spi_bus_type是在spi_init()函数中建立的,对应的是/sys/bus/spi目录。
spi_drv_probe()函数是驱动注册时发现到匹配的设备后调用的函数。
static int spi_drv_probe(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
return sdrv->probe(to_spi_device(dev));
}
该函数会通过container_of方法,找到spi_device,再作为spidev_probe()函数的参数。
2.spidev_probe
static int spidev_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
int status;
unsigned long minor;
/*创建spidev_data*/
/* Allocate driver data */
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
if (!spidev)
return -ENOMEM;
/*指向(一)中的spi_device*/
/* Initialize the driver data */
spidev->spi = spi;
spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry);
/* If we can allocate a minor number, hook up this device.
* Reusing minors is fine so long as udev or mdev is working.
*/
mutex_lock(&device_list_lock);
/*设置devt,并创建spidev0.0设备文件*/
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;
}
/*将创建的spidev连接到device_list中
*在open函数中会查找到该spidev*/
if (status == 0) {
set_bit(minor, minors);
list_add(&spidev->device_entry, &device_list);
}
mutex_unlock(&device_list_lock);
if (status == 0)
spi_set_drvdata(spi, spidev);
else
kfree(spidev);
return status;
}