zoukankan      html  css  js  c++  java
  • imx6 spi分析

    /**************************************************************************
     *本文主要跟踪imx6 spi设备和驱动的注册过程。
     * 
     *                                    Tony Liu, 2016-4-13, Shenzhen 
     *************************************************************************/                                                                                
                                                                                    
                                                                                    
    kernel/arch/arm/mach-mx6/board-mx6q_sabresd.c                                   
                                                                                    
    MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
        /* Maintainer: Freescale Semiconductor, Inc. */                             
        .boot_params = MX6_PHYS_OFFSET + 0x100,                                     
        .fixup = fixup_mxc_board,                                                   
        .map_io = mx6_map_io,                                                       
        .init_irq = mx6_init_irq,                                                   
        .init_machine = mx6_sabresd_board_init,                                     
        .timer = &mx6_sabresd_timer,         |                                      
        .reserve = mx6q_sabresd_reserve,     |                                      
    MACHINE_END                              |                                      
                                             V                                      
    static void __init mx6_sabresd_board_init(void)                                 
    {                                                                               
        ... ...                                                                     
        imx6q_add_ecspi(0, &mx6q_sabresd_spi_data);                                 
        imx6q_add_ecspi(1, &mx6q_sabresd_spi2_data);                                
        spi_device_init();        --------------------------------------------+     
        ... ...                               |                               |     
    }                                         |                               |     
                                              V                               |     
    static const struct spi_imx_master mx6q_sabresd_spi_data __initconst = {  |     
        .chipselect     = mx6q_sabresd_spi_cs,        --------+               |     
        .num_chipselect = ARRAY_SIZE(mx6q_sabresd_spi_cs),    |               |     
    };                                                        |               |     
                                                              |               |     
    static int mx6q_sabresd_spi_cs[] = {             <--------+               |     
        SABRESD_ECSPI1_CS1,                                                   |     
    };                                                                        |     
    #define SABRESD_ECSPI1_CS1     IMX_GPIO_NR(3, 19)                         |     
                                                                              |     
    //注册平台设备                                                            |    
    #define imx6q_add_ecspi(id, pdata)                                       |     
        imx_add_spi_imx(&imx6q_ecspi_data[id], pdata)  ----+                  |     
                                                           |                  |     
    struct platform_device *__init imx_add_spi_imx(    <---+                  |     
            const struct imx_spi_imx_data *data,                              |     
            const struct spi_imx_master *pdata)                               |     
    {                                                                         |     
        struct resource res[] = {                                             |     
            {                                                                 |     
                .start = data->iobase,                                        |     
                .end = data->iobase + data->iosize - 1,                       |     
                .flags = IORESOURCE_MEM,                                      |     
            }, {                                                              |     
                .start = data->irq,                                           |     
                .end = data->irq,                                             |     
                .flags = IORESOURCE_IRQ,                                      |     
            },                                                                |     
        };                                                                    |     
        return imx_add_platform_device(data->devid, data->id,                 |     
                res, ARRAY_SIZE(res), pdata, sizeof(*pdata));                 |     
    }                                                                         |     
                                                                              |     
    static void spi_device_init(void)                <------------------------+     
    {                                                                               
        spi_register_board_info(imx6_sabresd_spi_nor_device,     -------------+     
                    ARRAY_SIZE(imx6_sabresd_spi_nor_device));                 |     
    }                                            |                            |     
                                                 V                            |     
    static struct spi_board_info imx6_sabresd_spi_nor_device[] __initdata = { |     
    #if 0                                                                     |     
    #if defined(CONFIG_MTD_M25P80)                                            |     
        {                                                                     |     
            .modalias = "m25p80",                                             |     
            .max_speed_hz = 20000000, /* max spi clock (SCK) speed in HZ */   |     
            .bus_num = 0,                                                     |     
            .chip_select = 0,                                                 |     
            .platform_data = &imx6_sabresd__spi_flash_data,                   |     
        },                                                                    |     
    #endif                                                                    |     
    #endif                                                                    |     
        {                                                                     |     
            .modalias = "ar1020-spi",                                         |     
            .max_speed_hz = 50000, /* max spi clock (SCK) speed in HZ */      |     
            .bus_num = 1,                                                     |     
            .chip_select = 0,                                                 |     
            .mode = SPI_MODE_1,                                               |     
            .irq = gpio_to_irq(SABRESD_AR1020_INT),                           |     
                                                                              |     
            //.platform_data = &imx6_sabresd__spi_flash_data,                 |     
        },                                                                    |     
                                                                              |     
    };                                                                        |     
                                                                              |     
    int __init                                                                |     
    spi_register_board_info(struct spi_board_info const *info, unsigned n) <--+     
    {                                                                               
        struct boardinfo *bi;                                                       
        int i;                                                                      
                                                                                    
        bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);                                  
        if (!bi)                                                                    
            return -ENOMEM;                                                         
                                                                                    
        for (i = 0; i < n; i++, bi++, info++) {                                     
            struct spi_master *master;                                              
                                                                                    
            memcpy(&bi->board_info, info, sizeof(*info));                           
            mutex_lock(&board_lock);                                                
            //将所有的spi设备添加到链表中                               
            list_add_tail(&bi->list, &board_list);                                  
            list_for_each_entry(master, &spi_master_list, list)                     
                spi_match_master_to_boardinfo(master, &bi->board_info);             
            mutex_unlock(&board_lock);                                              
        }                                                                           
                                                                                    
        return 0;                                                                   
    }                                                                               
                                                                                    
    kernel/drivers/spi/spi_imx.c                                                    
    static int __init spi_imx_init(void)                                            
    {                                                                               
        return platform_driver_register(&spi_imx_driver);                           
    }                                     |                                         
                                          V                                         
    static struct platform_driver spi_imx_driver = {                                
        .driver = {                                                                 
               .name = DRIVER_NAME,            // "spi_imx"                         
               .owner = THIS_MODULE,                                                
               },                                                                   
        .id_table = spi_imx_devtype,                                                
        .probe = spi_imx_probe,                 -------+                            
        .remove = __devexit_p(spi_imx_remove),         |                            
    };                                                 |                            
                                                       V                            
    static int __devinit spi_imx_probe(struct platform_device *pdev)                
    {                                                                               
        struct spi_imx_master *mxc_platform_info;                                   
        struct spi_master *master;                                                  
        struct spi_imx_data *spi_imx;                                               
        struct resource *res;                                                       
        int i, ret;                                                                 
                                                                                    
        mxc_platform_info = dev_get_platdata(&pdev->dev);                           
        if (!mxc_platform_info) {                                                   
            dev_err(&pdev->dev, "can't get the platform data
    ");                   
            return -EINVAL;                                                         
        }                                                                           
                                                                                    
        master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data));         
        if (!master)                                                                
            return -ENOMEM;                                                         
                                                                                    
        platform_set_drvdata(pdev, master);                                         
                                                                                    
        master->bus_num = pdev->id;                                                 
        master->num_chipselect = mxc_platform_info->num_chipselect;                 
                                                                                    
        spi_imx = spi_master_get_devdata(master);                                   
        spi_imx->bitbang.master = spi_master_get(master);     ---------------------+
        spi_imx->chipselect = mxc_platform_info->chipselect;                       |
        //控制spi的chipselect引脚                                                    |  
        for (i = 0; i < master->num_chipselect; i++) {                             |
            if (spi_imx->chipselect[i] < 0)                                        |
                continue;                                                          |
            ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);               |
            if (ret) {                                                             |
                while (i > 0) {                                                    |
                    i--;                                                           |
                    if (spi_imx->chipselect[i] >= 0)                               |
                        gpio_free(spi_imx->chipselect[i]);                         |
                }                                                                  |
                dev_err(&pdev->dev, "can't get cs gpios
    ");                       |
                goto out_master_put;                                               |
            }                                                                      |
        }                                                                          |
        // spi 对应的操作函数                                                      |  
        spi_imx->bitbang.chipselect = spi_imx_chipselect;                          |
        spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;                       |
        spi_imx->bitbang.txrx_bufs = spi_imx_transfer;                             |
        spi_imx->bitbang.master->setup = spi_imx_setup;                            |
        spi_imx->bitbang.master->cleanup = spi_imx_cleanup;                        |
        spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;    |
                                                                                   |
        init_completion(&spi_imx->xfer_done);                                      |
                                                                                   |
        spi_imx->devtype_data =                                                    |
            spi_imx_devtype_data[pdev->id_entry->driver_data];                     |
                                                                                   |
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);                      |
        if (!res) {                                                                |
            dev_err(&pdev->dev, "can't get platform resource
    ");                  |
            ret = -ENOMEM;                                                         |
            goto out_gpio_free;                                                    |
        }                                                                          |
                                                                                   |
        if (!request_mem_region(res->start, resource_size(res), pdev->name)) {     |
            dev_err(&pdev->dev, "request_mem_region failed
    ");                    |
            ret = -EBUSY;                                                          |
            goto out_gpio_free;                                                    |
        }                                                                          |
                                                                                   |
        spi_imx->base = ioremap(res->start, resource_size(res));                   |
        if (!spi_imx->base) {                                                      |
            ret = -EINVAL;                                                         |
            goto out_release_mem;                                                  |
        }                                                                          |
                                                                                   |
        spi_imx->irq = platform_get_irq(pdev, 0);                                  |
        if (spi_imx->irq < 0) {                                                    |
            ret = -EINVAL;                                                         |
            goto out_iounmap;                                                      |
        }                                                                          |
                                                                                   |
        ret = request_irq(spi_imx->irq, spi_imx_isr, 0, DRIVER_NAME, spi_imx);     |
        if (ret) {                                                                 |
            dev_err(&pdev->dev, "can't get irq%d: %d
    ", spi_imx->irq, ret);       |
            goto out_iounmap;                                                      |
        }                                                                          |
                                                                                   |
        spi_imx->clk = clk_get(&pdev->dev, NULL);                                  |
        if (IS_ERR(spi_imx->clk)) {                                                |
            dev_err(&pdev->dev, "unable to get clock
    ");                          |
            ret = PTR_ERR(spi_imx->clk);                                           |
            goto out_free_irq;                                                     |
        }                                                                          |
                                                                                   |
        clk_enable(spi_imx->clk);                                                  |
        spi_imx->spi_clk = clk_get_rate(spi_imx->clk);                             |
                                                                                   |
        spi_imx->devtype_data.reset(spi_imx);                                      |
                                                                                   |
        spi_imx->devtype_data.intctrl(spi_imx, 0);                                 |
        ret = spi_bitbang_start(&spi_imx->bitbang);                                |
        if (ret) {                                                                 |
            dev_err(&pdev->dev, "bitbang start failed with %d
    ", ret);            |
            goto out_clk_put;                                                      |
        }                                                                          |
        clk_disable(spi_imx->clk);                                                 |
                                                                                   |
        dev_info(&pdev->dev, "probed
    ");                                          |
                                                                                   |
        return ret;                                                                |
                                                                                   |
    out_clk_put:                                                                   |
        clk_disable(spi_imx->clk);                                                 |
        clk_put(spi_imx->clk);                                                     |
    out_free_irq:                                                                  |
        free_irq(spi_imx->irq, spi_imx);                                           |
    out_iounmap:                                                                   |
        iounmap(spi_imx->base);                                                    |
    out_release_mem:                                                               |
        release_mem_region(res->start, resource_size(res));                        |
    out_gpio_free:                                                                 |
        for (i = 0; i < master->num_chipselect; i++)                               |
            if (spi_imx->chipselect[i] >= 0)                                       |
                gpio_free(spi_imx->chipselect[i]);                                 |
    out_master_put:                                                                |
        spi_master_put(master);                                                    |
        kfree(master);                                                             |
        platform_set_drvdata(pdev, NULL);                                          |
        return ret;                                                                |
    }                                                                              |
                                                                                   |
    int spi_bitbang_start(struct spi_bitbang *bitbang)           <-----------------+
    {                                                                               
        int    status;                                                              
                                                                                    
        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->mode_bits)                                            
            bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;      
                                                                                    
        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;                                                         
        if (bitbang->master->transfer == spi_bitbang_transfer &&                    
                !bitbang->setup_transfer)                                           
            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;                                                              
        }                                                                           
                                                                                    
        /* driver may get busy before register() returns, especially                
         * if someone registered boardinfo for devices                              
         */                                                                         
        status = spi_register_master(bitbang->master);  -----+                      
        if (status < 0)                                      |                      
            goto err2;                                       |                      
                                                             |                      
        return status;                                       |                      
                                                             |                      
    err2:                                                    |                      
        destroy_workqueue(bitbang->workqueue);               |                      
    err1:                                                    |                      
        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)                                            
            return -EINVAL;                                                         
                                                                                    
        /* convention:  dynamically assigned bus IDs count down from the max */     
        if (master->bus_num < 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/spi0, /dev/spi1                       
        dev_set_name(&master->dev, "spi%u", master->bus_num);                       
        status = device_add(&master->dev);                                          
        if (status < 0)                                                             
            goto done;                                                              
        dev_dbg(dev, "registered master %s%s
    ", 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)                                  
            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;                                                              
    }                                                                               
  • 相关阅读:
    JS(原生语法)_实现酷酷的动态简历
    Linux外在设备的使用
    查看系统内存信息
    查看CPU信息
    查看系统PCI设备
    配置网络
    Linux分区
    Observer
    Singleton
    Open closed principle
  • 原文地址:https://www.cnblogs.com/helloworldtoyou/p/5387982.html
Copyright © 2011-2022 走看看