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; }