嵌入式微处理器访问SPI设备有两种方式:使用GPIO模拟SPI接口的工作时序或者使用SPI控制器。使用GPIO模拟SPI接口的工作时序是非常容易实现的,但是会导致大量的时间耗费在模拟SPI接口的时序上,访问效率比较低,容易成为系统瓶颈。这里主要分析使用SPI控制器的情况。
在内核的drivers/spi/目录下有两个spi主控制器驱动程序:spi_s3c24xx.c和spi_s3c24xx_gpio.c其中spi_s3c24xx.c是基于s3c24xx下相应的spi接口的驱动程序,spi_s3c24xx_gpio.c运行用户指定3个gpio口分别充当spi_clk、spi_mosi和spi_miso接口,模拟标准的spi总线。UT4412BV01开发板预留了两路的spi接口(spi0和spi1),对于UT4412BV01开发板而言,使用的是spi_s3c64xx.c,也就是硬件SPI,不是软件SPI。注:下面是基于硬件SPI的spi1分析。
1. 定义platform device
kernel3.0.15/arch/arm/mach-exynos/dev-spi.c
static struct resource exynos_spi1_resource[] = {
[0] = {
.start = EXYNOS_PA_SPI1,
.end = EXYNOS_PA_SPI1 + 0x100 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = DMACH_SPI1_TX,
.end = DMACH_SPI1_TX,
.flags = IORESOURCE_DMA,
},
[2] = {
.start = DMACH_SPI1_RX,
.end = DMACH_SPI1_RX,
.flags = IORESOURCE_DMA,
},
[3] = {
.start = IRQ_SPI1,
.end = IRQ_SPI1,
.flags = IORESOURCE_IRQ,
},
};
static struct s3c64xx_spi_info exynos_spi1_pdata = {
.cfg_gpio = exynos_spi_cfg_gpio,
.fifo_lvl_mask = 0x7f,
.rx_lvl_offset = 15,
.high_speed = 1,
.clk_from_cmu = true,
.tx_st_done = 25,
};
struct platform_device exynos_device_spi1 = {
.name = "s3c64xx-spi",
.id = 1,
.num_resources = ARRAY_SIZE(exynos_spi1_resource),
.resource = exynos_spi1_resource,
.dev = {
.dma_mask = &spi_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &exynos_spi1_pdata,
},
};
exynos4412总共定义了三个spi控制器平台设备,实际上UT4412BV01开发板只预留了两个spi控制器(spi0和spi1)。platform设备给出了spi1接口的寄存器地址资源及IRQ资源。
注意其设备名为s3c64xx-spi。
2. 定义platform driver
kernel3.0.15/drivers/spi/spi_s3c64xx.c
static struct platform_driver s3c64xx_spi_driver = {
.driver = {
.name = "s3c64xx-spi",
.owner = THIS_MODULE,
},
.remove = s3c64xx_spi_remove,
.suspend = s3c64xx_spi_suspend,
.resume = s3c64xx_spi_resume,
};
MODULE_ALIAS("platform:s3c64xx-spi");
static int __init s3c64xx_spi_init(void)
{
//设备不可热插拔,所以使用该函数,而不是platform_driver_register
return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
}
subsys_initcall(s3c64xx_spi_init);
static void __exit s3c64xx_spi_exit(void)
{
platform_driver_unregister(&s3c64xx_spi_driver);
}
module_exit(s3c64xx_spi_exit);
MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
MODULE_LICENSE("GPL");
调用了platform_driver_probe注册platform驱动,注册完成以后将会调用platform的s3c64xx_spi_probe函数。
注意:platform驱动的name和platform device的name是相同的。
3. s3c64xx_spi_probe函数
kernel3.0.15/drivers/spi/spi_s3c64xx.c
当exynos_device_spi1中的name与s3c64xx_spi_driver中的name相同时,也就是是设备名字跟驱动名字可以匹配,s3c64xx_spi_probe驱动探测函数被调用,该函数代码如下所示:
static int __init s3c64xx_spi_probe(struct platform_device *pdev)
{
struct resource *mem_res, *dmatx_res, *dmarx_res;
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci;
struct spi_master *master;
int ret;
if (pdev->id < 0) { //pdev->id = 1
dev_err(&pdev->dev,
"Invalid platform device id-%d\n", pdev->id);
return -ENODEV;
}
if (pdev->dev.platform_data == NULL) { //pdev->dev.platform_data = &exynos_spi1_pdata
dev_err(&pdev->dev, "platform_data missing!\n");
return -ENODEV;
}
sci = pdev->dev