/**************************************************************************
*本文主要跟踪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(, &mx6q_sabresd_spi_data);
imx6q_add_ecspi(, &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 - , |
.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 = , /* max spi clock (SCK) speed in HZ */ |
.bus_num = , |
.chip_select = , |
.platform_data = &imx6_sabresd__spi_flash_data, |
}, |
#endif |
#endif |
{ |
.modalias = "ar1020-spi", |
.max_speed_hz = , /* max spi clock (SCK) speed in HZ */ |
.bus_num = , |
.chip_select = , |
.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 = ; 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 ;
} 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 = ; i < master->num_chipselect; i++) { |
if (spi_imx->chipselect[i] < ) |
continue; |
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME); |
if (ret) { |
while (i > ) { |
i--; |
if (spi_imx->chipselect[i] >= ) |
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, ); |
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, ); |
if (spi_imx->irq < ) { |
ret = -EINVAL; |
goto out_iounmap; |
} |
|
ret = request_irq(spi_imx->irq, spi_imx_isr, , 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, ); |
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 = ; i < master->num_chipselect; i++) |
if (spi_imx->chipselect[i] >= ) |
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 = ;
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 = ;
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 < ) |
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((<<) - );
struct device *dev = master->dev.parent;
struct boardinfo *bi;
int status = -ENODEV;
int dynamic = ; if (!dev)
return -ENODEV; /* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (master->num_chipselect == )
return -EINVAL; /* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < ) {
/* 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 = ;
} spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex);
master->bus_lock_flag = ; /* 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 < )
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 = ; /* Register devices from the device tree */
of_register_spi_devices(master);
done:
return status;
}

imx6 spi分析的更多相关文章

  1. imx6 fec分析

    /***************************************************************************** * imx6 fec分析 * 本文主要分析 ...

  2. imx6 framebuffer 分析

    分析imx6 framebuffer设备和驱动的注册过程. Tony Liu, 2016-8-31, Shenzhen 相关文件: arch/arm/mach-mx6/board-mx6q_sabre ...

  3. imx6 i2c分析

    本文主要分析: 1. i2c设备注册 2. i2c驱动注册 3. 上层调用过程参考: http://www.cnblogs.com/helloworldtoyou/p/5126618.html 1. ...

  4. imx6 MfgTool分析

    解析freescale的MfgTool中的脚本,了解imx6, android系统的分区情况. 配置文件 1. cfg.ini [profiles] chip = MX6DL Linux Update ...

  5. I2C(smbus pmbus)和SPI分析

    2C和SPI作为两种非常常用的低速外部总线 I2C I2C是以前的飞利浦半导体制定的标准,也就是如今的NXP. I2C总线由一条数据线(SDA)和一条时钟线(SCL)组成.设备分主从,主设备提供时钟, ...

  6. imx6 uart分析

    本文主要记录: 1.uart设备注册 2.uart驱动注册 3.上层应用调用有些地方理解的还不是很透彻,希望指正. 1.uart设备注册过程 MACHINE_START(MX6Q_SABRESD, & ...

  7. imx6 gpio分析

    本文主要介绍如何配置IOMUX寄存器,设置IO复用寄存器,配置为GPIO功能.参考: http://www.jianshu.com/p/3c2053508342 http://www.embest-t ...

  8. Linux SPI初始化及接口函数代码细究

    2012-01-08 22:11:38 目的:我需要掌握spi驱动相关数据结构关系,及在哪部分函数中把这些数值进行底层寄存器赋值的.结合应用层函数完成spi驱动的代码测试.已达到灵活修改的目的. 按顺 ...

  9. 02_dubbo的SPI

    [dubbo为什么不采用JDK自带的SPI] 1.JDK自带的SPI(ServiceLoader)会一次性实例化扩展点所有实现,基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍,如果我 ...

随机推荐

  1. Linux时间子系统(四) timekeeping

    一.前言 timekeeping模块是一个提供时间服务的基础模块.Linux内核提供各种time line,real time clock,monotonic clock.monotonic raw ...

  2. 实例讲解Nginx下的rewrite规则(转)

    一.正则表达式匹配,其中:* ~ 为区分大小写匹配* ~* 为不区分大小写匹配* !~和!~*分别为区分大小写不匹配及不区分大小写不匹配二.文件及目录匹配,其中:* -f和!-f用来判断是否存在文件* ...

  3. Shiro整合SSH开发3:配置Shiro认证后页面地址跳转问题(和详述不配置须要注意的问题)

         在视频教程中讲请求认证成功后跳转页面的问题是一笔带过的,可是我认为有必要单独写一篇相应的文章进行叙述.      我用了SSH来整合Shiro,在开发后验证的过程中,每次登陆后Shiro都会 ...

  4. 每日英语:Cyclists Live Six Years Longer

    Cycling does the body good. New data from Tour de France cyclists finds that those athletes live an ...

  5. 八、Java的可变参数例子

    1.在Java中什么是可变参数 可变参数是在Java1.5中引入的特性.它准许一个方法 public static void main(String[] args) { print("a&q ...

  6. Win7  CMD大全

    Win7  CMD大全  compmgmt.msc---计算机管理 conf—-启动 netmeeting charmap–-启动字符映射表 calc—-启动计算器 chkdsk.exe–-Chkds ...

  7. .balignl 16,0xdeadbeef浅析

    http://zqwt.012.blog.163.com/blog/static/12044684201031102956976/ 最近在分析u-boot的源代码,看到这一行:        .bal ...

  8. kafka之partition分区及副本replica升级

    修改kafka的partition分区 bin/kafka-topics.sh --zookeeper datacollect-2:2181 --alter --partitions 3 --topi ...

  9. 【转】java中&和&&的区别和联系

    [转]http://www.cnblogs.com/hongten/p/hongten_java_yu.html 电路问题总结: 对于:&   -- >  不管怎样,都会执行" ...

  10. FreeRTOS 低功耗之待机模式

    STM32F103 如何进入待机模式在 FreeRTOS 系统中,让 STM32 进入待机模式比较容易,调用固件库函数PWR_EnterSTANDBYMode 即可. STM32F103 如何退出待机 ...