ARM9 ADS8344 SPI驱动移植 (三)

来源:互联网 发布:北京程序员 编辑:程序博客网 时间:2024/06/05 07:13


step1:

     SPI主控制器驱动属于总线驱动模型当中的平台总线,SPI主控制器驱动在spi_s3c24xx.c当中完成了驱动的注册。代码如下:


static struct platform_driver s3c24xx_spi_driver = {
.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_spi0s3c_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外设和其中的数据是如何传递我们还不清楚。就留给下一讲再进行分析



0 0