SPI驅動之主控制器驅動程式
嵌入式微處理器訪問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
- staticstruct 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,
- },
- };
- staticstruct 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,
- },
- };
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
- staticstruct 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");
- staticint __init s3c64xx_spi_init(void)
- {
- <span style="white-space: pre;"> </span>//裝置不可熱插拔,所以使用該函式,而不是platform_driver_register
- return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
- }
- subsys_initcall(s3c64xx_spi_init);
- staticvoid __exit s3c64xx_spi_exit(void)
- {
- platform_driver_unregister(&s3c64xx_spi_driver);
- }
- module_exit(s3c64xx_spi_exit);
- MODULE_AUTHOR("Jaswinder Singh <[email protected]>");
- MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
- MODULE_LICENSE("GPL");
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 <[email protected]>");
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驅動探測函式被呼叫,該函式程式碼如下所示:
[cpp] view plain copy print?- staticint __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.platform_data;
- if (!sci->src_clk_name) { //在板級檔案中通過呼叫s3c64xx_spi_set_info()來初始化
- dev_err(&pdev->dev,
- "Board init must call s3c64xx_spi_set_info()\n");
- return -EINVAL;
- }
- /* Check for availability of necessary resource */
- //獲取DMA0資源
- dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (dmatx_res == NULL) {
- dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n");
- return -ENXIO;
- }
- //獲取DMA1資源
- dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (dmarx_res == NULL) {
- dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n");
- return -ENXIO;
- }
- //獲取IO記憶體資源
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (mem_res == NULL) {
- dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
- return -ENXIO;
- }
- /**
- * 通過跟蹤spi_alloc_master相關原始碼可知,
- * 此處分配struct spi_master + struct s3c64xx_spi_driver_data大小的資料,
- * 把s3c64xx_spi_driver_data設為spi_master的私有資料
- */
- master = spi_alloc_master(&pdev->dev,
- sizeof(struct s3c64xx_spi_driver_data));
- if (master == NULL) {
- dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
- return -ENOMEM;
- }
- /**
- * platform_set_drvdata 和 platform_get_drvdata
- * probe函式中定義的區域性變數,如果我想在其他地方使用它怎麼辦呢?
- * 這就需要把它儲存起來。核心提供了這個方法,
- * 使用函式platform_set_drvdata()可以將master儲存成平臺匯流排裝置的私有資料。
- * 以後再要使用它時只需呼叫platform_get_drvdata()就可以了。
- */
- platform_set_drvdata(pdev, master);
- //從master中獲得s3c64xx_spi_driver_data,並初始化相關成員
- sdd = spi_master_get_devdata(master);
- sdd->master = master;
- sdd->cntrlr_info = sci;
- sdd->pdev = pdev;
- sdd->sfr_start = mem_res->start;
- sdd->tx_dmach = dmatx_res->start;
- sdd->rx_dmach = dmarx_res->start;
- sdd->cur_bpw = 8;
- //master相關成員的初始化
- master->bus_num = pdev->id; //匯流排號
- master->setup = s3c64xx_spi_setup;
- master->transfer = s3c64xx_spi_transfer;
- master->num_chipselect = sci->num_cs; //該總線上的裝置數
- master->dma_alignment = 8;
- /* the spi->mode bits understood by this driver: */
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //mode_3
- //申請IO記憶體
- if (request_mem_region(mem_res->start,
- resource_size(mem_res), pdev->name) == NULL) {
- dev_err(&pdev->dev, "Req mem region failed\n");
- ret = -ENXIO;
- goto err0;
- }
- //建立對映
- sdd->regs = ioremap(mem_res->start, resource_size(mem_res));
- if (sdd->regs == NULL) {
- dev_err(&pdev->dev, "Unable to remap IO\n");
- ret = -ENXIO;
- goto err1;
- }
- //SPI的IO管腳配置,將相應的IO管腳設定為SPI功能
- if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) {
- dev_err(&pdev->dev, "Unable to config gpio\n");
- ret = -EBUSY;
- goto err2;
- }
- //使能時鐘
- sdd->clk = clk_get(&pdev->dev, "spi");
- if (IS_ERR(sdd->clk)) {
- dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
- ret = PTR_ERR(sdd->clk);
- goto err3;
- }
- if (clk_enable(sdd->clk)) {
- dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
- ret = -EBUSY;
- goto err4;
- }
- sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
- if (IS_ERR(sdd->src_clk)) {
- dev_err(&pdev->dev,
- "Unable to acquire clock '%s'\n", sci->src_clk_name);
- ret = PTR_ERR(sdd->src_clk);
- goto err5;
- }
- if (clk_enable(sdd->src_clk)) {
- dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",
- sci->src_clk_name);
- ret = -EBUSY;
- goto err6;
- }
- //建立單個執行緒的工作佇列,用於資料收發操作
- sdd->workqueue = create_singlethread_workqueue(
- dev_name(master->dev.parent));
- if (sdd->workqueue == NULL) {
- dev_err(&pdev->dev, "Unable to create workqueue\n");
- ret = -ENOMEM;
- goto err7;
- }
- //硬體初始化,初始化設定暫存器,包括對SPIMOSI、SPIMISO、SPICLK引腳的設定
- s3c64xx_spi_hwinit(sdd, pdev->id);
- //鎖、工作佇列等初始化
- spin_lock_init(&sdd->lock);
- init_completion(&sdd->xfer_completion);
- INIT_WORK(&sdd->work, s3c64xx_spi_work);
- INIT_LIST_HEAD(&sdd->queue);
- if (spi_register_master(master)) {
- dev_err(&pdev->dev, "cannot register SPI master\n");
- ret = -EBUSY;
- goto err8;
- }
- dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
- "with %d Slaves attached\n",
- pdev->id, master->num_chipselect);
- dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
- mem_res->end, mem_res->start,
- sdd->rx_dmach, sdd->tx_dmach);
- return 0;
- err8:
- destroy_workqueue(sdd->workqueue);
- err7:
- clk_disable(sdd->src_clk);
- err6:
- clk_put(sdd->src_clk);
- err5:
- clk_disable(sdd->clk);
- err4:
- clk_put(sdd->clk);
- err3:
- err2:
- iounmap((void *) sdd->regs);
- err1:
- release_mem_region(mem_res->start, resource_size(mem_res));
- err0:
- platform_set_drvdata(pdev, NULL);
- spi_master_put(master);
- return ret;
- }
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.platform_data;
if (!sci->src_clk_name) { //在板級檔案中通過呼叫s3c64xx_spi_set_info()來初始化
dev_err(&pdev->dev,
"Board init must call s3c64xx_spi_set_info()\n");
return -EINVAL;
}
/* Check for availability of necessary resource */
//獲取DMA0資源
dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (dmatx_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n");
return -ENXIO;
}
//獲取DMA1資源
dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (dmarx_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n");
return -ENXIO;
}
//獲取IO記憶體資源
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
return -ENXIO;
}
/**
* 通過跟蹤spi_alloc_master相關原始碼可知,
* 此處分配struct spi_master + struct s3c64xx_spi_driver_data大小的資料,
* 把s3c64xx_spi_driver_data設為spi_master的私有資料
*/
master = spi_alloc_master(&pdev->dev,
sizeof(struct s3c64xx_spi_driver_data));
if (master == NULL) {
dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
return -ENOMEM;
}
/**
* platform_set_drvdata 和 platform_get_drvdata
* probe函式中定義的區域性變數,如果我想在其他地方使用它怎麼辦呢?
* 這就需要把它儲存起來。核心提供了這個方法,
* 使用函式platform_set_drvdata()可以將master儲存成平臺匯流排裝置的私有資料。
* 以後再要使用它時只需呼叫platform_get_drvdata()就可以了。
*/
platform_set_drvdata(pdev, master);
//從master中獲得s3c64xx_spi_driver_data,並初始化相關成員
sdd = spi_master_get_devdata(master);
sdd->master = master;
sdd->cntrlr_info = sci;
sdd->pdev = pdev;
sdd->sfr_start = mem_res->start;
sdd->tx_dmach = dmatx_res->start;
sdd->rx_dmach = dmarx_res->start;
sdd->cur_bpw = 8;
//master相關成員的初始化
master->bus_num = pdev->id; //匯流排號
master->setup = s3c64xx_spi_setup;
master->transfer = s3c64xx_spi_transfer;
master->num_chipselect = sci->num_cs; //該總線上的裝置數
master->dma_alignment = 8;
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //mode_3
//申請IO記憶體
if (request_mem_region(mem_res->start,
resource_size(mem_res), pdev->name) == NULL) {
dev_err(&pdev->dev, "Req mem region failed\n");
ret = -ENXIO;
goto err0;
}
//建立對映
sdd->regs = ioremap(mem_res->start, resource_size(mem_res));
if (sdd->regs == NULL) {
dev_err(&pdev->dev, "Unable to remap IO\n");
ret = -ENXIO;
goto err1;
}
//SPI的IO管腳配置,將相應的IO管腳設定為SPI功能
if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) {
dev_err(&pdev->dev, "Unable to config gpio\n");
ret = -EBUSY;
goto err2;
}
//使能時鐘
sdd->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(sdd->clk)) {
dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
ret = PTR_ERR(sdd->clk);
goto err3;
}
if (clk_enable(sdd->clk)) {
dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
ret = -EBUSY;
goto err4;
}
sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
if (IS_ERR(sdd->src_clk)) {
dev_err(&pdev->dev,
"Unable to acquire clock '%s'\n", sci->src_clk_name);
ret = PTR_ERR(sdd->src_clk);
goto err5;
}
if (clk_enable(sdd->src_clk)) {
dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",
sci->src_clk_name);
ret = -EBUSY;
goto err6;
}
//建立單個執行緒的工作佇列,用於資料收發操作
sdd->workqueue = create_singlethread_workqueue(
dev_name(master->dev.parent));
if (sdd->workqueue == NULL) {
dev_err(&pdev->dev, "Unable to create workqueue\n");
ret = -ENOMEM;
goto err7;
}
//硬體初始化,初始化設定暫存器,包括對SPIMOSI、SPIMISO、SPICLK引腳的設定
s3c64xx_spi_hwinit(sdd, pdev->id);
//鎖、工作佇列等初始化
spin_lock_init(&sdd->lock);
init_completion(&sdd->xfer_completion);
INIT_WORK(&sdd->work, s3c64xx_spi_work);
INIT_LIST_HEAD(&sdd->queue);
if (spi_register_master(master)) {
dev_err(&pdev->dev, "cannot register SPI master\n");
ret = -EBUSY;
goto err8;
}
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
"with %d Slaves attached\n",
pdev->id, master->num_chipselect);
dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
mem_res->end, mem_res->start,
sdd->rx_dmach, sdd->tx_dmach);
return 0;
err8:
destroy_workqueue(sdd->workqueue);
err7:
clk_disable(sdd->src_clk);
err6:
clk_put(sdd->src_clk);
err5:
clk_disable(sdd->clk);
err4:
clk_put(sdd->clk);
err3:
err2:
iounmap((void *) sdd->regs);
err1:
release_mem_region(mem_res->start, resource_size(mem_res));
err0:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
return ret;
}
s3c64xx_spi_probe函式很長,但做的事情卻很簡單,從上面程式碼的註釋可以基本理清整個探測流程。其中用到幾個比較重要的函式,下面來一一解釋。spi_alloc_master(kernel3.0.15/drivers/spi/spi.c)
spi_alloc_master函式用於請求分配一個spi_master。
[cpp] view plain copy print?- struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
- {
- struct spi_master *master;
- if (!dev)
- return NULL;
- /* 分配記憶體,分配的記憶體大小是*master + size,包含了兩部分記憶體 */
- master = kzalloc(size + sizeof *master, GFP_KERNEL);
- if (!master)
- return NULL;
- device_initialize(&master->dev); //裝置模型中的初始裝置函式
- master->dev.class = &spi_master_class; //spi_master_class在SPI子系統初始化的時候就已經註冊好了
- master->dev.parent = get_device(dev); //裝置當前裝置的父裝置,這與裝置模型相關
- spi_master_set_devdata(master, &master[1]); //&master[1]就是master之後的另一部分記憶體的起始地址
- return master;
- }
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
struct spi_master *master;
if (!dev)
return NULL;
/* 分配記憶體,分配的記憶體大小是*master + size,包含了兩部分記憶體 */
master = kzalloc(size + sizeof *master, GFP_KERNEL);
if (!master)
return NULL;
device_initialize(&master->dev); //裝置模型中的初始裝置函式
master->dev.class = &spi_master_class; //spi_master_class在SPI子系統初始化的時候就已經註冊好了
master->dev.parent = get_device(dev); //裝置當前裝置的父裝置,這與裝置模型相關
spi_master_set_devdata(master, &master[1]); //&master[1]就是master之後的另一部分記憶體的起始地址
return master;
}
spi_master_register(kernel3.0.15/drivers/spi/spi.c)
spi_master_register函式用於向核心註冊一個spi_master。
[cpp] view plain copy print?- 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;
- struct boardinfo *bi;
- 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) //一個SPI控制器至少有一個片選,因此片選數為0則出錯
- return -EINVAL;
- /* convention: dynamically assigned bus IDs count down from the max */
- if (master->bus_num < 0) { //如果匯流排號小於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;
- }
- spin_lock_init(&master->bus_lock_spinlock);
- mutex_init(&master->bus_lock_mutex);
- master->bus_lock_flag = 0;
- /* register the device, then userspace will see it.
- * registration fails if the bus ID is in use.
- */
- dev_set_name(&master->dev, "spi%u", master->bus_num); //把master加入到裝置模型中
- status = device_add(&master->dev);
- if (status < 0)
- goto done;
- dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
- dynamic ? " (dynamic)" : "");
- mutex_lock(&board_lock);
- list_add_tail(&master->list, &spi_master_list);
- list_for_each_entry(bi, &board_list, list) //遍歷board_list這個連結串列
- spi_match_master_to_boardinfo(master, &bi->board_info);
- mutex_unlock(&board_lock);
- status = 0;
- /* Register devices from the device tree */
- of_register_spi_devices(master);
- done:
- return status;
- }
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;
struct boardinfo *bi;
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) //一個SPI控制器至少有一個片選,因此片選數為0則出錯
return -EINVAL;
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) { //如果匯流排號小於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;
}
spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex);
master->bus_lock_flag = 0;
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
dev_set_name(&master->dev, "spi%u", master->bus_num); //把master加入到裝置模型中
status = device_add(&master->dev);
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list);
list_for_each_entry(bi, &board_list, list) //遍歷board_list這個連結串列
spi_match_master_to_boardinfo(master, &bi->board_info);
mutex_unlock(&board_lock);
status = 0;
/* Register devices from the device tree */
of_register_spi_devices(master);
done:
return status;
}
spi_match_master_to_boardinfo(kernel3.0.15/drivers/spi/spi.c)
[cpp] view plain copy print?- staticvoid spi_match_master_to_boardinfo(struct spi_master *master,
- struct spi_board_info *bi)
- {
- struct spi_device *dev;
- if (master->bus_num != bi->bus_num) //每找到一個成員就將它的匯流排號與master的匯流排號進行比較,如果相等則呼叫spi_new_device函式建立一個spi裝置
- return;
- dev = spi_new_device(master, bi);
- if (!dev)
- dev_err(master->dev.parent, "can't create new device for %s\n",
- bi->modalias);
- }
static void spi_match_master_to_boardinfo(struct spi_master *master,
struct spi_board_info *bi)
{
struct spi_device *dev;
if (master->bus_num != bi->bus_num) //每找到一個成員就將它的匯流排號與master的匯流排號進行比較,如果相等則呼叫spi_new_device函式建立一個spi裝置
return;
dev = spi_new_device(master, bi);
if (!dev)
dev_err(master->dev.parent, "can't create new device for %s\n",
bi->modalias);
}
spi_new_device(kernel3.0.15/drivers/spi/spi.c)
[cpp] view plain copy print?- 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).
- */
- proxy = spi_alloc_device(master);
- if (!proxy)
- return NULL;
- WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
- 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;
- status = spi_add_device(proxy);
- if (status < 0) {
- spi_dev_put(proxy);
- return NULL;
- }
- return proxy;
- }
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).
*/
proxy = spi_alloc_device(master);
if (!proxy)
return NULL;
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
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;
status = spi_add_device(proxy);
if (status < 0) {
spi_dev_put(proxy);
return NULL;
}
return proxy;
}
spi_alloc_device(kernel3.0.15/drivers/spi/spi.c)
[cpp]
view plain
copy
print?
- struct spi_device *spi_alloc_device(struct spi_master *master)
- {
- struct spi_device *spi;
- struct device *dev = master->dev.parent;
- if (!spi_master_get(master)) //錯誤檢測
- return NULL;
- spi = kzalloc(sizeof *spi, GFP_KERNEL); //分配記憶體
- if (!spi) {
- dev_err(dev, "cannot alloc spi_device\n");
- spi_master_put(master);
- return NULL;
- }
- spi->master = master;
- spi->dev.parent = dev;
- spi->dev.bus = &spi_bus_type; //該spi裝置屬於SPI子系統初始化時註冊的叫“spi”的匯流排
- spi->dev.release = spidev_release;
- device_initialize(&spi->dev); //裝置模型方面的初始化
- return spi;
- }
struct spi_device *spi_alloc_device(struct spi_master *master)
{
struct spi_device *spi;
struct device *dev = master->dev.parent;
if (!spi_master_get(master)) //錯誤檢測
return NULL;
spi = kzalloc(sizeof *spi, GFP_KERNEL); //分配記憶體
if (!spi) {
dev_err(dev, "cannot alloc spi_device\n");
spi_master_put(master);
return NULL;
}
spi->master = master;
spi->dev.parent = dev;
spi->dev.bus = &spi_bus_type; //該spi裝置屬於SPI子系統初始化時註冊的叫“spi”的匯流排
spi->dev.release = spidev_release;
device_initialize(&spi->dev); //裝置模型方面的初始化
return spi;
}
spi_add_device(kernel3.0.15/drivers/spi/spi.c)
[cpp]
view plain
copy
print?
- int spi_add_device(struct spi_device *spi)
- {
- static DEFINE_MUTEX(spi_add_lock);
- struct device *dev = spi->master->dev.parent;
- struct device *d;
- int status;
- /* Chipselects are numbered 0..max; validate. */
- if (spi->chip_select >= spi->master->num_chipselect) { //片選號是從0開始的,如果大於或者等於片選數的話則返回出錯
- dev_err(dev, "cs%d >= max %d\n",
- spi->chip_select,
- spi->master->num_chipselect);
- return -EINVAL;
- }
- /* Set the bus ID string */
- dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
- 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);
- d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); //遍歷spi匯流排,看是否已經註冊過該裝置
- if (d != NULL) {
- dev_err(dev, "chipselect %d already in use\n",
- spi->chip_select);
- put_device(d);
- 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...
- */
- status = spi_setup(spi);
- if (status < 0) {
- dev_err(dev, "can't setup %s, status %d\n",
- dev_name(&spi->dev), 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 add %s, status %d\n",
- dev_name(&spi->dev), status);
- else
- dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
- done:
- mutex_unlock(&spi_add_lock);
- return status;
- }
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct device *dev = spi->master->dev.parent;
struct device *d;
int status;
/* Chipselects are numbered 0..max; validate. */
if (spi->chip_select >= spi->master->num_chipselect) { //片選號是從0開始的,如果大於或者等於片選數的話則返回出錯
dev_err(dev, "cs%d >= max %d\n",
spi->chip_select,
spi->master->num_chipselect);
return -EINVAL;
}
/* Set the bus ID string */
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
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);
d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); //遍歷spi匯流排,看是否已經註冊過該裝置
if (d != NULL) {
dev_err(dev, "chipselect %d already in use\n",