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)会一次性实例化扩展点所有实现,基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍,如果我 ...
随机推荐
- MVC 之Action
下面我要重新温习一下,MVC控制器中的Action方法相关概念: 1.Action方法必须是public,不能是private或者是protected; 2.Action方法不能够被重载: 3.Act ...
- bug list
机型: Samsung Galaxy S GT-I9000 版本: 2.2.1bug: Couldn't create directory for SharedPreferences file xxx ...
- VLOG丨树莓派Raspberry Pi 3安装PLEX并挂载USB硬盘打造最牛的微型家庭影音服务器2018
视频介绍 树莓派3安装目前最流行的PLEX服务器,实现既能最大限度降低功耗,也能随时随地观看分享影片. 一.在树莓派下安装PLEX媒体服务器 1.在终端,将你的树莓派更新至最新 sudo apt up ...
- 分享八:特殊的mysql函数
一:MYSQL自定义排序函数FIELD() MySQL可以通过field()函数自定义排序,格式:field(value,str1,str2,str3,str4),value与str1.str2.st ...
- mkdir命令的-p和-m
mkdir命令是常用的命令,用来建立空目录,它还有2个常用参数: -m, --mode=模式 设定权限 (类似 chmod),而不是 rwxrwxrwx 减 umask -p, --parents 需 ...
- linux修改yum本地源的方法
CentOS 系统下修改yum本地源: mkdir /mnt/cdrom/ cd /etc/yum.repos.d/mv CentOS-Base.repo CentOS-Base.repo.bakvi ...
- http请求头中的Content-Type属性在angular 和 node中的用法
post请求的请求体有以下两种格式: 1. 字符串: 'name=code_bunny&age=12' 这种格式的请求体,需要配置请求头 'Content-Type':'application ...
- Sql Server连接字符串
SQL Server .NET Data Provider 连接字符串包含一个由一些属性名/值对组成的集合.每一个属性/值对都由分号隔开. PropertyName1=Value1; ...
- Rokid开发者社区skill之【历史上的今天】之简介+玩法+设计+实现+心得
Skill简介: 来源:好奇心.探索欲.趣味性: 资源:百度百科: 方式:实时获取,自动更新: 技能玩法: 想要进入历史上的今天这个skill,则对若琪说:若琪,打开历史上的今天. 想要了解某天的历史 ...
- Python实现二叉树及其4种遍历
Python & BinaryTree 1. BinaryTree (二叉树) 二叉树是有限个元素的集合,该集合或者为空.或者有一个称为根节点(root)的元素及两个互不相交的.分别被称为左子 ...