step1:
SPI主控制器驱动属于总线驱动模型当中的平台总线,SPI主控制器驱动在spi_s3c24xx.c当中完成了驱动的注册。代码如下:
.remove = __exit_p(s3c24xx_spi_remove),
.suspend = s3c24xx_spi_suspend,
.resume = s3c24xx_spi_resume,
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
},
};
static int __init s3c24xx_spi_init(void)
{
return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
}
我们追踪一下platform_driver_probe()这个函数。
int __init_or_module platform_driver_probe(struct platform_driver *drv,int (*probe)(struct platform_device *))
{
{
int retval, code;
drv->probe = probe; //对平台驱动添加proble函数
retval = code = platform_driver_register(drv); //对平台驱动进行注册
spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
drv->probe = NULL;
if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
retval = -ENODEV;
drv->driver.probe = platform_drv_probe_fail;
spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);
if (code != retval)
platform_driver_unregister(drv);
return retval;
}
通过追踪我们对platform_driver_probe()这个函数的作用一目了然。主要是添加对平台驱动的proble函数和对平台驱动进行注册。
step2:
现在我们查找一下平台设备的信息是如何添加和注册的。
s3c2440只有两个spi控制器,所以对应linux内核就采用s3c_device_spi0 和s3c_device_spi1这两个结构来描述平台设备。我们ADS8344用的spi0主控制。 s3c_device_spi0 在Devs.c定义。代码如下:
struct platform_device s3c_device_spi0 = {
.name = "s3c2410-spi",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_spi0_resource),
.resource = s3c_spi0_resource,
.dev = {
.dma_mask = &s3c_device_spi0_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
还记得在第一讲我们在机器配置文件当中添加了如下代码:
static struct s3c2410_spi_info s3c2410_spi0_platdata = {
.pin_cs = S3C2410_GPG(2),
.num_cs = 1, //所有的片选信号
.bus_num = 0, //SPI多对应的总线编号
.gpio_setup = s3c24xx_spi_gpiocfg_bus0_gpe11_12_13, //引脚设置函数
};
在tq2440_devices[]平台数组中添加如下代码:
&s3c_device_spi0,
最后在tq2440_machine_init函数中加入如下代码:
s3c_device_spi0.dev.platform_data= &s3c2410_spi0_platdata;
我们添加了把 s3c2410_spi0_platdata 作为平台设备的私有数据添加到s3c_device_spi0这个平台设备当中。同时将
s3c_device_spi0这个平凡设备加入到tq2440_devices[]平台数组。稍后在机器配置文件mach-tq2440.c的初始化函数tq2440_machine_init(void)完成对tq2440_devices[]平台数组上所用平台设备的注册。
static void __init tq2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&tq2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices)); //完成对平台设备的注册
EmbedSky_machine_init();
s3c2410_gpio_setpin(S3C2410_GPG12, 0);
s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);
s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);
}
接着我们继续追踪platform_add_devices()这个函数做什么事情。
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
由以上代码可知调用 platform_device_register(devs[i]);对平台数组内所用的平台设备进行注册。
step3:
现在我们完成了SPI主控制平台驱动和平台设备的注册。平台总线对其进行遍历和匹配,匹配成功后就就会调用SPI主控制平台驱动的probe函数 s3c24xx_spi_probe(struct platform_device *pdev)。现在我们看一下这个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结构体,其中包括s3c24xx_spi结构的内存空间,使用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;
}
/*获得s3c24xx_spi结构,将master.dev.driver_data指向的
struct s3c24xx_spi数据结构的地址赋给指针hw */
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct s3c24xx_spi));
/*将master的地址赋给hw->master*/
hw->master = spi_master_get(master);
/*获取s3c2410_spi_info结构体指针,就是之前你在添加到机器配置文件当中的
s3c2410_spi_info数据结构 */
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;
}
/*将hw的地址赋给pdev.dev.driver_dataz */
platform_set_drvdata(pdev, hw);
init_completion(&hw->done);
master->num_chipselect = hw->pdata->num_cs;
master->bus_num = pdata->bus_num;
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);
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;
}
/*获得平台资源的寄存器的地址。并完成物理地址到虚拟地址的映射
该寄存器为ARM9中控制SPI的寄存器 */
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); /*获取irq号*/
if (hw->irq < 0) {
dev_err(&pdev->dev, "No IRQ specified\n");
err = -ENOENT;
goto err_no_irq;
}
/*申请spi中断,ISR为 s3c24xx_spi_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;
}
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;
}
if (!pdata->set_cs) {
if (pdata->pin_cs < 0) {
dev_err(&pdev->dev, "No chipselect pin\n");
goto err_register;
}
err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));
if (err) {
dev_err(&pdev->dev, "Failed to get gpio for cs\n");
goto err_register;
}
hw->set_cs = s3c24xx_spi_gpiocs;
gpio_direction_output(pdata->pin_cs, 1);
} else
hw->set_cs = pdata->set_cs;
s3c24xx_spi_initialsetup(hw);
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:
if (hw->set_cs == s3c24xx_spi_gpiocs)
gpio_free(pdata->pin_cs);
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;
}
probe函数前半部分主要是对struct s3c24xx_spi *hw; struct spi_master *master;中的hw和master进行分配内存和填充内部的数据结构。主要如下:
{
master.dev.driver_data 指向已分配内存s3c24xx_spi数据类型的地址
hw = master.dev.driver_data
hw->master = master
/*获取s3c2410_spi_info结构体指针,就是之前你在添加到机器配置文件当中的
s3c2410_spi_info数据结构 */
hw->pdata = pdev->dev.platform_data
hw->dev = &pdev->dev
pdev->dev->driver_data = hw
}
使 hw ,master 和设备信息pdev 通过指针对地址的相互赋值,使这三个数据结构发生关联,通过其中的任一都可追寻到其他两个数据结构。
probe函数除了完成以上外还主要完成了注册中断函数request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw); 和调用硬件初始化函数s3c24xx_spi_initialsetup(hw); 和函数spi_bitbang_start(&hw->bitbang)和对hw->bitbang结构数据进行填充。
step4:
(1)
好了我们对上面的三个函数进行分析。先看一下硬件初始化函数s3c24xx_spi_initialsetup(hw)做了什么工作
static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
{
/* for the moment, permanently enable the clock */
clk_enable(hw->clk);
/*设置SPI的寄存器SPPRE,SPPIN,SPCON*/
writeb(0xff, hw->regs + S3C2410_SPPRE);
writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
if (hw->pdata)
{
if (hw->set_cs == s3c24xx_spi_gpiocs)
gpio_direction_output(hw->pdata->pin_cs, 1);
if (hw->pdata->gpio_setup)
hw->pdata->gpio_setup(hw->pdata, 1);
}
}
由以上函数可知,主要是设置SPI的通讯速率为PLCK/257和 将ARM9设置为SPI的master以及设置数据读写方式为
中断方式。
(2)
接着分析函数spi_bitbang_start(&hw->bitbang)
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
intstatus;
if (!bitbang->master || !bitbang->chipselect)
return -EINVAL;
INIT_WORK(&bitbang->work, bitbang_work); //创建工作
spin_lock_init(&bitbang->lock);
INIT_LIST_HEAD(&bitbang->queue);
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(
dev_name(bitbang->master->dev.parent)); //创建工作队列
if (bitbang->workqueue == NULL) {
status = -EBUSY;
goto err1;
}
status = spi_register_master(bitbang->master); //对主控制器进行注册
if (status < 0)
goto err2;
return status;
err2:
destroy_workqueue(bitbang->workqueue);
err1:
return status;
}
spi_bitbang_start(&hw->bitbang)函数完成了对bitbang数据结构内容的填充,创建工作队列和创建工作。以及调用spi_register_master(bitbang->master)函数完成SPI主控制器的注册,spi_register_master()中调用scan_boardinfo(master)。
scan_boardinfo()函数将先遍历board_list链表。(我们在第一讲中我们将s3c2410_spi1_board[] 添加到机器配置文件中,并用spi_register_board_info(s3c2410_spi0_board, ARRAY_SIZE(s3c2410_spi0_board)将s3c2410_spi1_board[] 挂在到board_list链表上。)然后将s3c2410_spi1_board[] 的内容添加到SPI设备信息struct spi_device中。接着调用spi_add_device(proxy)函数对SPI设备进行注册。(SPI设备进行注册
成功,你所写的ADS8344驱动也就能工作了。)
(3)
最后我们分析一下中断函数s3c24xx_spi_irq(int irq, void *dev)
static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
{
struct s3c24xx_spi *hw = dev;
unsigned int spsta = readb(hw->regs + S3C2410_SPSTA); //读寄存器SPSTA,获取状态信息
unsigned int count = hw->count;
/* 通过读寄存器SPSTA获取的状态信息判断是否发生数据故障 */
if (spsta & S3C2410_SPSTA_DCOL) {
dev_dbg(hw->dev, "data-collision\n");
complete(&hw->done);
goto irq_done;
}
if (!(spsta & S3C2410_SPSTA_READY)) {
dev_dbg(hw->dev, "spi not ready for tx?\n");
complete(&hw->done);
goto irq_done;
}
hw->count++;
//从SPRDAT寄存器中读取从SPI外设(如ads8344)发来的数据
if (hw->rx)
hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
count++;
//将要发送给SPI外设的数据或命令写入SPTDAT寄存器,
if (count < hw->len)
writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
else
complete(&hw->done);
irq_done:
return IRQ_HANDLED;
}
由以上代码分析可知中断函数完成了对SPI数据的读写。当是中断何时调用以及你如何通过你的应用程序去读写一个SPI外设和其中的数据是如何传递我们还不清楚。就留给下一讲再进行分析。