該系列文章將分為四個部分:
第一部分,將對SPI子系統整體進行描述,同時給出SPI的相關數據結構,最後描述SPI總線的注冊。基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(一) http://www.linuxidc.com/Linux/2012-08/68402.htm
第二部分,即本篇文章,該文將對SPI的主控制器(master)驅動進行描述。
第三部分,該文將對SPI設備驅動,也稱protocol 驅動,進行講解。基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(三) http://www.linuxidc.com/Linux/2012-08/68405.htm
第四部分,通過SPI設備驅動留給用戶層的API,我們將從上到下描述數據是如何通過SPI的protocol 驅動,由bitbang中轉,最後由master驅動將數據傳輸出去。 基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(四) http://www.linuxidc.com/Linux/2012-08/68406.htm
本文屬於第二部分。
4. 主控制器驅動程序
4.1 定義 platform device
下列數據結構位於arch/arm/plat-s3c24XX/devs.c
- /* SPI (0) */
-
- static struct resource s3c_spi0_resource[] = {
- [0] = {
- .start = S3C24XX_PA_SPI,
- .end = S3C24XX_PA_SPI + 0x1f,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_SPI0,
- .end = IRQ_SPI0,
- .flags = IORESOURCE_IRQ,
- }
-
- };
-
- static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
-
- 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
- }
- };
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的身影,因此這正式需要我們移植的地方。