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(, &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分析的更多相关文章
- imx6 fec分析
/***************************************************************************** * imx6 fec分析 * 本文主要分析 ...
- imx6 framebuffer 分析
分析imx6 framebuffer设备和驱动的注册过程. Tony Liu, 2016-8-31, Shenzhen 相关文件: arch/arm/mach-mx6/board-mx6q_sabre ...
- imx6 i2c分析
本文主要分析: 1. i2c设备注册 2. i2c驱动注册 3. 上层调用过程参考: http://www.cnblogs.com/helloworldtoyou/p/5126618.html 1. ...
- imx6 MfgTool分析
解析freescale的MfgTool中的脚本,了解imx6, android系统的分区情况. 配置文件 1. cfg.ini [profiles] chip = MX6DL Linux Update ...
- I2C(smbus pmbus)和SPI分析
2C和SPI作为两种非常常用的低速外部总线 I2C I2C是以前的飞利浦半导体制定的标准,也就是如今的NXP. I2C总线由一条数据线(SDA)和一条时钟线(SCL)组成.设备分主从,主设备提供时钟, ...
- imx6 uart分析
本文主要记录: 1.uart设备注册 2.uart驱动注册 3.上层应用调用有些地方理解的还不是很透彻,希望指正. 1.uart设备注册过程 MACHINE_START(MX6Q_SABRESD, & ...
- imx6 gpio分析
本文主要介绍如何配置IOMUX寄存器,设置IO复用寄存器,配置为GPIO功能.参考: http://www.jianshu.com/p/3c2053508342 http://www.embest-t ...
- Linux SPI初始化及接口函数代码细究
2012-01-08 22:11:38 目的:我需要掌握spi驱动相关数据结构关系,及在哪部分函数中把这些数值进行底层寄存器赋值的.结合应用层函数完成spi驱动的代码测试.已达到灵活修改的目的. 按顺 ...
- 02_dubbo的SPI
[dubbo为什么不采用JDK自带的SPI] 1.JDK自带的SPI(ServiceLoader)会一次性实例化扩展点所有实现,基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍,如果我 ...
随机推荐
- print to console or file
/*----------------------------------------------------------------------*/ /* Debug for ...
- Android-SQLiteOpenHelper
Android-SQLiteOpenHelper 一 概念 是对SOLiteDatabase的封装.主要用于建立和版本号控制,方便我们去建立库表结构 二 用法 又一次封装一个MySqliteOpenH ...
- 使用 docker 搭建 openvpn,创建、删除用户证书
我自己的配置,服务器:ubuntu16.04 + docker 17.12.0-ce:客户端:win10 + openvpn2.4.5 1 在dockerhub上搜索 openvpn,我是用的是 进去 ...
- spring中事务配置
1 如果在方法.类.接口上使用注解的方式声明事务,需要在配置文件中进行配置,以便通知 Spring 容器对标注 @Transactional 注解的 bean 加工处理. 首先需要引入 tx 命名空间 ...
- 网站的PV UV IP---网站常见软件性能
IP,衡量不同时间段的上网人数.00:00-24:00内相同的地址被计算一次.例:日300W IP,至少300W人访问PV,衡量页面受欢迎程度.每刷新一次,被记录一次(刷pv),网站被访问的页面的数量 ...
- Filter method example
The Scala List class filter method implicitly loops over the List/Seq you supply, tests each element ...
- Makefile学习之路——1
编写makefile,不是一个猛子扎进去试着写一个规则并对之调试,而应该先采用面向依赖关系的思考方法勾勒出makefile要表达怎样的依赖关系,这一点尤为重要.通过不断地练习这种思考方法,才可能达到流 ...
- Path类与Directory类与File类
阅读目录 开始 Path 对路径 字符串进行操作 获得后缀 能合并路径 获取文件名 Directory和DirectoryInfo 对目录进行操作 判断目录是否存在 创建目录 删除目录 获取目录下所 ...
- c# 终止线程
最近在弄一个等待窗口,使用了线程去调用form.在结束线程这边碰到了些问题.调用: thread.Abort();thread.Join();老被ThreadAbortException异常抛出困扰. ...
- 常见JVM监控工具用法介绍
VisualVM是一种集成了多个JDK命令行工具的可视化工具,它能为您提供强大的分析能力.所有这些都是免费的!它囊括的命令行工具包括jps,jstat,jmap,jinfo,jstack,JConso ...