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\n");                   
        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\n");                       |
            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\n");                  |
        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\n");                    |
        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\n", 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\n");                          |
        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\n", ret);            |
        goto out_clk_put;                                                      |
    }                                                                          |
    clk_disable(spi_imx->clk);                                                 |
                                                                               |
    dev_info(&pdev->dev, "probed\n");                                          |
                                                                               |
    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\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)                                  
        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;                                                              
}                                                                               

 

posted @ 2016-04-13 17:34  SuperTao1024  阅读(4802)  评论(0编辑  收藏  举报