/*****************************************************************************
* am335x i2c分析
* i2c驱动主要关注i2c_algorithm结构体,不同芯片实现自己的master_xfer函数.
* 不同芯片i2c驱动框架都类似。
* 本文主要描述am335x_i2c设备和驱动的注册,提及文件:
* arch/arm/mach-omap2/board-am335xevm.c
* drivers/i2c/busses/i2c-omap.c
* drivers/i2c/i2c-core.c
*                          Tony Liu, 2016-5-2, Shenzhen  
***************************************************************************/
. i2c设备注册,配置引脚复用
arch/arm/mach-omap2/board-am335xevm.c
MACHINE_START(AM335XEVM, "am335xevm")
/* Maintainer: Texas Instruments */
.atag_offset = 0x100,
.map_io = am335x_evm_map_io,
.init_early = am33xx_init_early,
.init_irq = ti81xx_init_irq,
.handle_irq = omap3_intc_handle_irq,
.timer = &omap3_am33xx_timer,
.init_machine = am335x_evm_init,
MACHINE_END static void __init am335x_evm_init(void)
{
am33xx_cpuidle_init();
am33xx_mux_init(board_mux);
omap_serial_init();
am335x_evm_i2c_init(); ---------------------+ //i2c0
omap_sdrc_init(NULL, NULL); |
usb_musb_init(&musb_board_data); |
|
omap_board_config = am335x_evm_config; |
omap_board_config_size = ARRAY_SIZE(am335x_evm_config); |
|
daughter_brd_detected = false; |
setup_xxx_xxxx(); ---------------|--+ //i2c1, i2c2
| |
/*create /proc/boardname to export info to userspace*/ | |
proc_init(); | |
| |
/* Create an alias for icss clock */ | |
if (clk_add_alias("pruss", NULL, "pruss_uart_gclk", NULL)) | |
pr_warn("failed to create an alias: icss_uart_gclk --> pruss\n"); | |
/* Create an alias for gfx/sgx clock */ | |
if (clk_add_alias("sgx_ck", NULL, "gfx_fclk", NULL)) | |
pr_warn("failed to create an alias: gfx_fclk --> sgx_ck\n"); | |
} | |
//初始化i2c 0,由于i2c0引脚的mode0就是i2c功能,所以不需要配置引脚复用 | |
static void __init am335x_evm_i2c_init(void) <-------------+ |
{ |
/* Initially assume General Purpose EVM Config */ |
am335x_evm_id = EVM_SK; |
// i2c 0, speed: 100k |
omap_register_i2c_bus(, , i2c0_boardinfo,ARRAY_SIZE(i2c0_boardinfo)); --+ |
} | | |
| | |
// i2c设备的设备地址 | | |
static struct i2c_board_info i2c0_boardinfo[] = { <-----+ | |
{ | |
I2C_BOARD_INFO("tps65910", TPS65910_I2C_ID1), | |
.platform_data = &am335x_tps65910_info, | |
}, | |
{ | |
I2C_BOARD_INFO("24c02", 0x50), | |
}, | |
}; | |
| |
int __init omap_register_i2c_bus(int bus_id, u32 clkrate, <----+ |
struct i2c_board_info const *info, |
unsigned len) |
{ |
int err; |
|
BUG_ON(bus_id < || bus_id > omap_i2c_nr_ports()); |
|
if (info) { |
err = i2c_register_board_info(bus_id, info, len); |
if (err) |
return err; |
} |
|
if (!i2c_pdata[bus_id - ].clkrate) |
i2c_pdata[bus_id - ].clkrate = clkrate; |
|
i2c_pdata[bus_id - ].clkrate &= ~OMAP_I2C_CMDLINE_SETUP; |
//注册i2c设备 |
return omap_i2c_add_bus(bus_id); |
} |
|
static void setup_xxx_xxxx(void) <-------------+
{ /*which doesn't have Write Protect pin LAN8710A_PHY_ID */
am335x_mmc[].gpio_wp = -EINVAL; int ret; _configure_device(EVM_SK, xxx_xxxx_dev_cfg, PROFILE_NONE); ---+
...... |
} |
|
static struct evm_dev_cfg xxx_xxxx_dev_cfg[] = { <--+
...... ---+
{i2c1_init, DEV_ON_BASEBOARD, PROFILE_ALL}, |
{i2c2_init, DEV_ON_BASEBOARD, PROFILE_ALL}, |
...... |
{NULL, , }, |
}; |
//初始化i2c1 |
static void i2c1_init(int evm_id, int profile) <----+
{
setup_pin_mux(i2c1_pin_mux); //设置i2c引脚复用 -----------+
omap_register_i2c_bus(, , am335x_i2c1_boardinfo2,ARRAY_SIZE(am335x_i2c1_boardinfo2));|
return; |
} |
|
static struct i2c_board_info am335x_i2c1_boardinfo2[] = { |
{ |
I2C_BOARD_INFO("ds1337", 0x68), |
}, |
{ |
I2C_BOARD_INFO("tlv320aic3x", 0x1b), |
}, |
}; |
|
static struct pinmux_config i2c1_pin_mux[] = { <-------+
{"spi0_d1.i2c1_sda", OMAP_MUX_MODE2 | AM33XX_SLEWCTRL_SLOW |
AM33XX_PULL_ENBL | AM33XX_INPUT_EN},
{"spi0_cs0.i2c1_scl", OMAP_MUX_MODE2 | AM33XX_SLEWCTRL_SLOW |
AM33XX_PULL_ENBL | AM33XX_INPUT_EN},
{NULL, },
}; . i2c 驱动注册
drivers/i2c/busses/i2c-omap.c
static int __init
omap_i2c_init_driver(void)
{
return platform_driver_register(&omap_i2c_driver); -----+
} |
subsys_initcall(omap_i2c_init_driver); |
|
static struct platform_driver omap_i2c_driver = { <----+
.probe = omap_i2c_probe, -----+
.remove = omap_i2c_remove, |
.driver = { |
.name = "omap_i2c", |
.owner = THIS_MODULE, |
.pm = OMAP_I2C_PM_OPS, |
}, |
}; |
|
static int __devinit |
omap_i2c_probe(struct platform_device *pdev) <---+
{
struct omap_i2c_dev *dev;
struct i2c_adapter *adap;
struct resource *mem, *irq, *ioarea;
struct omap_i2c_bus_platform_data *pdata = pdev->dev.platform_data;
irq_handler_t isr;
int r;
u32 speed = ; /* NOTE: driver uses the static register mapping */
mem = platform_get_resource(pdev, IORESOURCE_MEM, );
if (!mem) {
dev_err(&pdev->dev, "no mem resource?\n");
return -ENODEV;
}
irq = platform_get_resource(pdev, IORESOURCE_IRQ, );
if (!irq) {
dev_err(&pdev->dev, "no irq resource?\n");
return -ENODEV;
} ioarea = request_mem_region(mem->start, resource_size(mem),
pdev->name);
if (!ioarea) {
dev_err(&pdev->dev, "I2C region already claimed\n");
return -EBUSY;
} dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);
if (!dev) {
r = -ENOMEM;
goto err_release_region;
} if (pdata != NULL) {
speed = pdata->clkrate;
dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
} else {
speed = ; /* Default speed */
dev->set_mpu_wkup_lat = NULL;
} dev->speed = speed;
dev->dev = &pdev->dev;
dev->irq = irq->start;
dev->base = ioremap(mem->start, resource_size(mem));
if (!dev->base) {
r = -ENOMEM;
goto err_free_mem;
} platform_set_drvdata(pdev, dev); dev->reg_shift = (pdata->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & ; if (pdata->rev == OMAP_I2C_IP_VERSION_2)
dev->regs = (u8 *)reg_map_ip_v2;
else
dev->regs = (u8 *)reg_map_ip_v1; pm_runtime_enable(dev->dev);
pm_runtime_get_sync(dev->dev); dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff; if (dev->rev <= OMAP_I2C_REV_ON_3430)
dev->errata |= I2C_OMAP3_1P153; if (!(pdata->flags & OMAP_I2C_FLAG_NO_FIFO)) {
u16 s; /* Set up the fifo size - Get total size */
s = (omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> ) & 0x3;
dev->fifo_size = 0x8 << s; /*
* Set up notification threshold as half the total available
* size. This is to ensure that we can handle the status on int
* call back latencies.
*/ dev->fifo_size = (dev->fifo_size / ); if (dev->rev >= OMAP_I2C_REV_ON_3530_4430)
dev->b_hw = ; /* Disable hardware fixes */
else
dev->b_hw = ; /* Enable hardware fixes */ /* calculate wakeup latency constraint for MPU */
if (dev->set_mpu_wkup_lat != NULL)
dev->latency = ( * dev->fifo_size) /
( * speed / );
} /* reset ASAP, clearing any IRQs */
omap_i2c_init(dev); isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr :
omap_i2c_isr;
r = request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev); if (r) {
dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);
goto err_unuse_clocks;
} dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id,
pdata->rev, dev->rev >> , dev->rev & 0xf, dev->speed); pm_runtime_put(dev->dev); adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_HWMON;
strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
adap->algo = &omap_i2c_algo; //i2c发送和接受的算法函数 ------+
adap->dev.parent = &pdev->dev; |
|
/* i2c device drivers may be active on return from add_adapter() */ |
adap->nr = pdev->id; |
r = i2c_add_numbered_adapter(adap); ----------|--+
if (r) { | |
dev_err(dev->dev, "failure adding adapter\n"); | |
goto err_free_irq; | |
} | |
| |
return ; | |
| |
err_free_irq: | |
free_irq(dev->irq, dev); | |
err_unuse_clocks: | |
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, ); | |
pm_runtime_put(dev->dev); | |
iounmap(dev->base); | |
err_free_mem: | |
platform_set_drvdata(pdev, NULL); | |
kfree(dev); | |
err_release_region: | |
release_mem_region(mem->start, resource_size(mem)); | |
| |
return r; | |
} | |
| |
static const struct i2c_algorithm omap_i2c_algo = { <-----+ |
.master_xfer = omap_i2c_xfer, ------+ |
.functionality = omap_i2c_func, | |
}; | | |
V | |
omap_i2c_func(struct i2c_adapter *adap) | |
{ | |
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); | |
} | |
| |
static int | |
omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) <-+ |
{ |
struct omap_i2c_dev *dev = i2c_get_adapdata(adap); |
int i; |
int r; |
|
pm_runtime_get_sync(dev->dev); |
|
r = omap_i2c_wait_for_bb(dev); |
if (r < ) |
goto out; |
|
if (dev->set_mpu_wkup_lat != NULL) |
dev->set_mpu_wkup_lat(dev->dev, dev->latency); |
|
for (i = ; i < num; i++) { |
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - ))); -----+ |
if (r != ) | |
break; | |
} | |
| |
if (dev->set_mpu_wkup_lat != NULL) | |
dev->set_mpu_wkup_lat(dev->dev, -); | |
| |
if (r == ) | |
r = num; | |
| |
omap_i2c_wait_for_bb(dev); | |
out: | |
pm_runtime_put(dev->dev); | |
return r; | |
} | |
| |
static int omap_i2c_xfer_msg(struct i2c_adapter *adap, <----+ |
struct i2c_msg *msg, int stop) |
{ |
struct omap_i2c_dev *dev = i2c_get_adapdata(adap); |
int r; |
u16 w; |
|
dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", |
msg->addr, msg->len, msg->flags, stop); |
|
if (msg->len == ) |
return -EINVAL; |
//写设备地址 |
omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr); |
|
/* REVISIT: Could the STB bit of I2C_CON be used with probing? */ |
dev->buf = msg->buf; |
dev->buf_len = msg->len; |
//接受数据的长度 |
omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len); |
|
/* Clear the FIFO Buffers */ |
w = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG); |
w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR; |
omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w); |
|
init_completion(&dev->cmd_complete); |
dev->cmd_err = ; |
|
w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT; |
|
/* High speed configuration */ |
if (dev->speed > ) |
w |= OMAP_I2C_CON_OPMODE_HS; |
|
if (msg->flags & I2C_M_TEN) |
w |= OMAP_I2C_CON_XA; |
if (!(msg->flags & I2C_M_RD)) |
w |= OMAP_I2C_CON_TRX; |
|
if (!dev->b_hw && stop) |
w |= OMAP_I2C_CON_STP; |
|
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); |
|
/* |
* Don't write stt and stp together on some hardware. |
*/ |
if (dev->b_hw && stop) { |
unsigned long delay = jiffies + OMAP_I2C_TIMEOUT; |
u16 con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); |
while (con & OMAP_I2C_CON_STT) { |
con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); |
|
/* Let the user know if i2c is in a bad state */ |
if (time_after(jiffies, delay)) { |
dev_err(dev->dev, "controller timed out " |
"waiting for start condition to finish\n"); |
return -ETIMEDOUT; |
} |
cpu_relax(); |
} |
|
w |= OMAP_I2C_CON_STP; |
w &= ~OMAP_I2C_CON_STT; |
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); |
} |
|
/* |
* REVISIT: We should abort the transfer on signals, but the bus goes |
* into arbitration and we're currently unable to recover from it. |
*/ |
r = wait_for_completion_timeout(&dev->cmd_complete, |
OMAP_I2C_TIMEOUT); |
dev->buf_len = ; |
if (r < ) |
return r; |
if (r == ) { |
dev_err(dev->dev, "controller timed out\n"); |
omap_i2c_init(dev); |
return -ETIMEDOUT; |
} |
|
if (likely(!dev->cmd_err)) |
return ; |
|
/* We have an error */ |
if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR | |
OMAP_I2C_STAT_XUDF)) { |
omap_i2c_init(dev); |
return -EIO; |
} |
|
if (dev->cmd_err & OMAP_I2C_STAT_NACK) { |
if (msg->flags & I2C_M_IGNORE_NAK) |
return ; |
if (stop) { |
w = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); |
w |= OMAP_I2C_CON_STP; |
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); |
} |
return -EREMOTEIO; |
} |
return -EIO; |
} |
|
int i2c_add_numbered_adapter(struct i2c_adapter *adap) <------+
{
int id;
int status; if (adap->nr == -) /* -1 means dynamically assign bus id */
return i2c_add_adapter(adap);
if (adap->nr & ~MAX_ID_MASK)
return -EINVAL; retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == )
return -ENOMEM; mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh;
* we need the "equal to" result to force the result
*/
status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
if (status == && id != adap->nr) {
status = -EBUSY;
idr_remove(&i2c_adapter_idr, id);
}
mutex_unlock(&core_lock);
if (status == -EAGAIN)
goto retry; if (status == )
status = i2c_register_adapter(adap); ----+
return status; |
} |
EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); |
|
static int i2c_register_adapter(struct i2c_adapter *adap) <---+
{
int res = ; /* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
} /* Sanity checks */
if (unlikely(adap->name[] == '\0')) {
pr_err("i2c-core: Attempt to register an adapter with "
"no name!\n");
return -EINVAL;
}
if (unlikely(!adap->algo)) {
pr_err("i2c-core: Attempt to register adapter '%s' with "
"no algo!\n", adap->name);
return -EINVAL;
} rt_mutex_init(&adap->bus_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */
if (adap->timeout == )
adap->timeout = HZ; dev_set_name(&adap->dev, "i2c-%d", adap->nr); // i2c 设备名
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
if (res)
goto out_list; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); #ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility class link\n");
#endif /* create pre-declared device nodes */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap); /* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock); return ; out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
}

am335x i2c分析的更多相关文章

  1. am335x uart分析

    /************************************************************ * am335x uart分析 * 本文记录am335x uart驱动的注册 ...

  2. imx6 i2c分析

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

  3. I2C分析三

    1 引言 IIC (Inter-Integrated Circuit1总线是一种由Philips公司开发的2线式串行总线,用于连接微控制器及其外围设备.它是同步通信的一种特殊形式,具有接口线少.控制方 ...

  4. 基于at91rm9200的i2c分析(DS1307实时时钟芯片)

    board-ek.c 构造i2c_board_info结构体 static struct i2c_board_info __initdata ek_i2c_devices[] = {     {    ...

  5. am335x gpio分析

    /************************************************************************ * am335x_gpio * 本文主要记录am33 ...

  6. TM1680的I2C的51例程

    搞到一个例程,虽然是51的, 但是我的ST版本也是用的模拟I2C, 分析一下吧: unsigned char i=0;TM1680start();  //I2C起始信号 TM1680SendByte( ...

  7. 2019第十二届全国大学生信息安全实践创新赛线上赛Writeup

    本文章来自https://www.cnblogs.com/iAmSoScArEd/p/10780242.html  未经允许不得转载! 1.MISC-签到 下载附件后,看到readme.txt打开后提 ...

  8. am335x uboot启动流程分析

    基本指令含义 .globl _start .globl指示告诉汇编器,_start这个符号要被链接器用到,所以要在目标文件的符号表中标记它是一个全局符号 b,bl b是不带返回的跳转  bl带返回的跳 ...

  9. [I2C]I2C架构分析

    转自:http://blog.csdn.net/wangpengqi/article/details/17711165 1. I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一 ...

随机推荐

  1. GDI+ 怎样将图片绘制成圆形的图片

    大概意思就是不生成新的图片,而是将图片转换为圆形图片. 实现代码例如以下: private Image CutEllipse(Image img, Rectangle rec, Size size) ...

  2. Ubuntu下设置环境变量

    Ubuntu下设置环境变量有三种方法,一种用于当前终端,一种用于当前用户,一种用于所有用户:   一:用于当前终端: 在当前终端中输入:export PATH=$PATH:<你的要加入的路径&g ...

  3. vue开发环境搭建win10

    需要安装nodejs, webpack@2.2.1, babel-cli,  vue-cli 1 安装nodejs 现在版本默认会安装nodejs 和 npm包 和 配置环境 2 检查是否安装成功,在 ...

  4. java的多线程(一)

    我们知道我们打开个程序(或者说运行一款软件)其实也就是创建了一个进程,只不过程序是静态指令的集合,而进程是正在系统中运行的指令集合,进程是系统进行资源分配与调度的一个独立单位.进程具有独立性,动态性, ...

  5. mysql特殊语句学习

    一.Mysql ON子句和USING子句 Mysql 中联接SQL语句中,ON子句的语法格式为:table1.column_name = table2.column_name. 当模式设计对联接表的列 ...

  6. DPDK

    Intel DPDK 全面解读   高性能网络技术 随着云计算产业的异军突起,网络技术的不断创新,越来越多的网络设备基础架构逐步向基于通用处理器平台的架构方向融合,从传统的物理网络到虚拟网络,从扁平化 ...

  7. ControlExtensionTest(一)

    #ifndef __CCCONTROLSCENE_H__ #define __CCCONTROLSCENE_H__ #include "cocos2d.h" #include &q ...

  8. sudo和man的tab自动补全

    要加入sudo和man的tab自动补全功能,只需在~/.bashrc中加入: #Enabling tab-completioncomplete -cf sudocomplete -cf man

  9. SQL中AND与OR的优先级

    突然发现,把基础给忘了,AND的优先级大于OR,试验如下: Oracle --Y ; --Y ) ; --No value ); 附,Oracle文档: http://docs.oracle.com/ ...

  10. 五、String在Java中是传“引用”

    这个是Java的经典问题.许多类似的问题在stackoverflow被提问,有很多不正确或不完备的答案.如果不想太多你会认为这个问题比较简单.( The question is simple if y ...