基于S3C2440的嵌入式Linux驱动——SPI子系统解读(二)
该系列文章将分为四个部分:
第二部分,即本篇文章,该文将对SPI的主控制器(master)驱动进行描述。
本文属于第二部分。
4. 主控制器驱动程序
4.1 定义 platform device
下列数据结构位于arch/arm/plat-s3c24XX/devs.c
- /* SPI (0) */
- static struct resource s3c_spi0_resource[] = {
- [0] = {
- .start = S3C24XX_PA_SPI,
platform设备给出了spi0接口的寄存器地址资源以及IRQ资源。注意其设备名为s3c2410-spi。
4.2 定义platform driver
下列函数位于deivers/spi/s3c24xx.c。
- MODULE_ALIAS("platform:s3c2410-spi");
- 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_register
- }
- static void __exit s3c24xx_spi_exit(void)
- {
- platform_driver_unregister(&s3c24xx_spi_driver);
- }
- module_init(s3c24xx_spi_init);
- module_exit(s3c24xx_spi_exit);
调用了platform_driver_probe注册platform驱动,注册完成以后将会调用platform的s3c24xx_spi_probe函数。
NOTE:platform驱动的name和platform device的name是相同的。
4.2.1 s3c24xx_spi_probe函数
下列函数位于deivers/spi/s3c24xx.c。
- 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结构,并清0该结构*/
- hw = spi_master_get_devdata(master);
- memset(hw, 0, sizeof(struct s3c24xx_spi));
- hw->master = spi_master_get(master); /*保存master结构体,同时增加引用计数*/
- hw->pdata = pdata = pdev->dev.platform_data; /*获取s3c2410_spi_info结构体指针*/
- hw->dev = &pdev->dev; /*保存platform设备的dev*/
- if (pdata == NULL) {
- dev_err(&pdev->dev, "No platform data supplied\n");
- err = -ENOENT;
- goto err_no_pdata;
- }
- platform_set_drvdata(pdev, hw); /*让platform_device.dev.driver_data 指向 s3c24xx_spi*/
- init_completion(&hw->done); /*初始化completion*/
- /* setup the master state. */ /*填充master结构体的两个字段*/
- master->num_chipselect = hw->pdata->num_cs;
- master->bus_num = pdata->bus_num;
- /* setup the state for the bitbang driver */ /*填充bitbang字段*/
- 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 */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /*获取IO资源*/
- 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, /*申请IO内存*/
- 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); /*获取irq号*/
- 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); /*申请spi中断,ISR为 s3c24xx_spi_irq*/
- if (err) {
- dev_err(&pdev->dev, "Cannot claim IRQ\n");
- goto err_no_irq;
- }
- hw->clk = clk_get(&pdev->dev, "spi"); /*获取spi时钟*/
- if (IS_ERR(hw->clk)) {
- dev_err(&pdev->dev, "No clock for device\n");
- err = PTR_ERR(hw->clk);
- goto err_no_clk;
- }
- /* setup any gpio we can */
- if (!pdata->set_cs) { /*没有定义分配CS管脚的函数*/
- if (pdata->pin_cs < 0) { /*pin_cs为cs管脚*/
- dev_err(&pdev->dev, "No chipselect pin\n");
- goto err_register;
- }
- err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));/*申请IO地址*/
- if (err) {
- dev_err(&pdev->dev, "Failed to get gpio for cs\n");
- goto err_register;
- }
- hw->set_cs = s3c24xx_spi_gpiocs; /*给出分配cs管脚函数*/
- gpio_direction_output(pdata->pin_cs, 1);/*设置该管脚为输出模式*/
- } else
- hw->set_cs = pdata->set_cs;
- s3c24xx_spi_initialsetup(hw); /*spi控制器初始化*/
- /* register our spi controller */
- 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;
- }
该函数首先为spi_master结构体以及s3c24xx_spi结构体分配了空间,同时,spi_master.dev.driver_data指向了s3c24xx_spi。
s3c24xx_spi结构如下:
- struct s3c24xx_spi {
- /* bitbang has to be first */
- struct spi_bitbang bitbang;
- struct completion done;
- void __iomem *regs;
- int irq;
- int len;
- int count;
- void (*set_cs)(struct s3c2410_spi_info *spi,
- int cs, int pol);
- /* data buffers */
- const unsigned char *tx;
- unsigned char *rx;
- struct clk *clk;
- struct resource *ioarea;
- struct spi_master *master;
- struct spi_device *curdev;
- struct device *dev;
- struct s3c2410_spi_info *pdata;
- };
接着执行了该条语句:
hw->pdata = pdata = pdev->dev.platform_data; /*获取s3c2410_spi_info结构体指针*/
NOTE:在这里获取platform_device.dev.platform_data,也就是平台设备的相关数据,而在4.1小结中的arch/arm/plat-s3c24XX/devs.c文件中并没有发现platform_data的身影,因此这正式需要我们移植的地方。