I2C控制器的设备对象内核已经实现并关联到platform总线。

I2C控制器的驱动对象内核已经实现。

看mach-tiny4412.h

/plat-samsung/目录下

/drivers/i2c/   看 *.o 文件

看i2c-s3c2410.c   从下往上看。

.id_table 

匹配成功后看 probe函数:

一个I2C控制器对应一个struct s3c24xx_i2c结构体对象:

struct s3c24xx_i2c *i2c;

struct s3c24xx_i2c {
wait_queue_head_t wait;
unsigned int quirks;
unsigned int suspended:; struct i2c_msg *msg;       //IIC要传输的数据,
unsigned int msg_num;     //数组元素格式
unsigned int msg_idx;
unsigned int msg_ptr; unsigned int tx_setup;
unsigned int irq;        //中断号 enum s3c24xx_i2c_state state;
unsigned long clkrate; void __iomem *regs;      //通过platform_get_resource拿到物理基地址,映射完后赋值
struct clk *clk;
struct device *dev;
struct resource *ioarea;
struct i2c_adapter adap;      //读写数据的算法 struct s3c2410_platform_i2c *pdata;
int gpios[];
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
};
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
*/
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data; /* data fields that are valid for all devices */
struct rt_mutex bus_lock; int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */ int nr;
char name[];
struct completion dev_released; struct mutex userspace_clients_lock;
struct list_head userspace_clients;
};
/*
* The following structs are for those who like to implement new bus drivers:
* i2c_algorithm is the interface to a class of hardware solutions which can
* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
* to name two of the most common.
*/
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};

tiny4412一共是9个IIC控制器接口,如果都加入的话,probe函数最多可以被调用9次。

来自exynos4412数据手册:

29.2特性12C总线接口的特点是:9频道多主机。

从12C总线接口(通用频道8个,高清多媒体接口专用频道1个)

7位寻址模式串行、8位定向和双向数据传输

支持高达100千位在标准模式支持高达400千位在快速模式。

支持主发送、主接收、从发送和从接收操作

支持中断或轮询事件

probe.c 部分代码:   I2C控制器的初始化,访问总线的读写算法的实现。

        strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm; //I2C控制访问总线的读写算法
i2c->adap.retries = ;               //尝试次数,最多两次
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = ;     

      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  //获取资源

      

     

      i2c->ioarea = request_mem_region(res->start, resource_size(res),
                  pdev->name);

      i2c->regs = ioremap(res->start, resource_size(res));

      

      ret = s3c24xx_i2c_init(i2c);

      

/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/

    i2c->irq = ret = platform_get_irq(pdev, 0);
    if (ret <= 0) {
      dev_err(&pdev->dev, "cannot find IRQ\n");
      goto err_iomap;
    }

    ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
                  dev_name(&pdev->dev), i2c);

    

    ret = i2c_add_numbered_adapter(&i2c->adap); //非常重要,下面有分析
    if (ret < 0) {
      dev_err(&pdev->dev, "failed to add bus to i2c core\n");
      goto err_cpufreq;
    }

s3c24xx_i2c_algorithm中的 .master_xfer
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
int retry;
int ret; pm_runtime_get_sync(&adap->dev);
clk_enable(i2c->clk); for (retry = ; retry < adap->retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs, num); //真正的从总线上收发数据 if (ret != -EAGAIN) {
clk_disable(i2c->clk);
pm_runtime_put_sync(&adap->dev);
return ret;
} dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); udelay();
} clk_disable(i2c->clk);
pm_runtime_put_sync(&adap->dev);
return -EREMOTEIO;
}
/* s3c24xx_i2c_doxfer
*
* this starts an i2c transfer
*/ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
struct i2c_msg *msgs, int num)
{
unsigned long timeout;
int ret; if (i2c->suspended)
return -EIO; ret = s3c24xx_i2c_set_master(i2c);
if (ret != ) {
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
ret = -EAGAIN;
goto out;
} i2c->msg = msgs;
i2c->msg_num = num;
i2c->msg_ptr = ;
i2c->msg_idx = ;
i2c->state = STATE_START; s3c24xx_i2c_enable_irq(i2c);      //使能I2C中断
s3c24xx_i2c_message_start(i2c, msgs); timeout = wait_event_timeout(i2c->wait, i2c->msg_num == , HZ * ); ret = i2c->msg_idx; /* having these next two as dev_err() makes life very
* noisy when doing an i2cdetect */ if (timeout == )
dev_dbg(i2c->dev, "timeout\n");
else if (ret != num)
dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); /* For QUIRK_HDMIPHY, bus is already disabled */
if (i2c->quirks & QUIRK_HDMIPHY)
goto out; s3c24xx_i2c_wait_idle(i2c); out:
return ret;
}
/* s3c24xx_i2c_set_master
*
* get the i2c bus for a master transaction
*/ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
{
unsigned long iicstat;
int timeout = ;        //检查400次 while (timeout-- > ) {
iicstat = readl(i2c->regs + S3C2410_IICSTAT); if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))
return ; msleep();
} return -ETIMEDOUT;
}
 
/* s3c24xx_i2c_message_start
*
* put the start of a message onto the bus
*/ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << ;    //7位地址,左移一位。
unsigned long stat;
unsigned long iiccon; stat = ;
stat |= S3C2410_IICSTAT_TXRXEN; if (msg->flags & I2C_M_RD) {
stat |= S3C2410_IICSTAT_MASTER_RX;    // 2<<6,对应配置。
                addr |= ;
} else
stat |= S3C2410_IICSTAT_MASTER_TX;  //3<<6 对应上图数据手册截图 if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= ; /* todo - check for whether ack wanted or not */
s3c24xx_i2c_enable_ack(i2c);  //使能ACK iiccon = readl(i2c->regs + S3C2410_IICCON);
writel(stat, i2c->regs + S3C2410_IICSTAT); dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
writeb(addr, i2c->regs + S3C2410_IICDS); /* delay here to ensure the data byte has gotten onto the bus
* before the transaction is started */ ndelay(i2c->tx_setup); dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
writel(iiccon, i2c->regs + S3C2410_IICCON); stat |= S3C2410_IICSTAT_START; //1<<5
        writel(stat, i2c->regs + S3C2410_IICSTAT);
}

1:struct i2c_board_info xx = {};

   i2c_register_board_info();

2:匹配busnum ,

3: 生成:struct i2c_client{

      .name = xxx

     }/**

 * i2c_add_numbered_adapter - declare i2c adapter, use static bus number
* @adap: the adapter to register (with adap->nr initialized)
* Context: can sleep
*
* This routine is used to declare an I2C adapter when its bus number
* matters. For example, use it for I2C adapters from system-on-chip CPUs,
* or otherwise built in to the system's mainboard, and where i2c_board_info
* is used to properly configure I2C devices.
*
* If the requested bus number is set to -1, then this function will behave
* identically to i2c_add_adapter, and will dynamically assign a bus number.
*
* If no devices have pre-been declared for this bus, then be sure to
* register the adapter before any dynamically allocated ones. Otherwise
* the required bus ID may not be available.
*
* When this returns zero, the specified adapter became available for
* clients using the bus number provided in adap->nr. Also, the table
* of I2C devices pre-declared using i2c_register_board_info() is scanned,
* and the appropriate driver model device nodes are created. Otherwise, a
* negative errno value is returned.
*/
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_IDR_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;
}
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);
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;
}
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo; down_read(&__i2c_board_lock);      //LIST_HEAD(__i2c_board_list); 全局可访问,头结点
        list_for_each_entry(devinfo, &__i2c_board_list, list) {   //遍历链表
      //遍历一个一个从机准备的信息,匹配busnum ,成功后调用
i2c_new_device来创建 i2c_client
      //i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
      //s3c24xx_i2c_probe 函数中赋值 i2c->adap.nr = i2c->pdata->bus_num;if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
/**
 * i2c_new_device - instantiate an i2c device
* @adap: the adapter managing the device
* @info: describes one I2C device; bus_num is ignored
* Context: can sleep
*
* Create an i2c device. Binding is handled through driver model
* probe()/remove() methods. A driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe hotplugging will
* load the driver module). This call is not appropriate for use by mainboard
* initialization logic, which usually runs during an arch_initcall() long
* before any i2c_adapter could exist.
*
* This returns the new i2c client, which may be saved for later use with
* i2c_unregister_device(); or NULL to indicate an error.
*/

struct i2c_client {
  unsigned short flags;    /* div., see below */
  unsigned short addr;   /* chip address - NOTE: 7bit */
                /* addresses are stored in the */
                /* _LOWER_ 7 bits */
  char name[I2C_NAME_SIZE];
  struct i2c_adapter *adapter; /* the adapter we sit on */
  struct i2c_driver *driver; /* and our access routines */
  struct device dev; /* the device structure */
  int irq; /* irq issued by device */
  struct list_head detected;
};

 

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status; client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL; client->adapter = adap;
                      //info 从机真正的信息            
client->dev.platform_data = info->platform_data; if (info->archdata)
client->dev.archdata = *info->archdata; client->flags = info->flags;
client->addr = info->addr;  //从机地址
client->irq = info->irq;    //从机对应的外部中断号或者外部中断对应的GPIO strlcpy(client->name, info->type, sizeof(client->name));  //与匹配相关的名字 /* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? : , client->addr);
goto out_err_silent;
} /* Check for address business */
status = i2c_check_addr_busy(adap, client->addr);
if (status)
goto out_err; client->dev.parent = &client->adapter->dev;

/* struct bus_type i2c_bus_type = {
  .name = "i2c",
  .match = i2c_device_match,
  .probe = i2c_device_probe,
  .remove = i2c_device_remove,
  .shutdown = i2c_device_shutdown,
  .pm = &i2c_device_pm_ops,
  };   */

        client->dev.bus = &i2c_bus_type;    //确实是“i2c”   下面查看bus_type i2c_bus_type中的i2c_device_probe  查看匹配规则   后面列出
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node; /* For 10-bit clients, add an arbitrary offset to avoid collisions */
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr | ((client->flags & I2C_CLIENT_TEN)
? 0xa000 : ));
status = device_register(&client->dev);    //注册
if (status)
goto out_err; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev)); return client; out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}
//使得i2c_client 和 i2c_driver 关联起来
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status; if (!client)
return ; driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n"); status = driver->probe(client, i2c_match_id(driver->id_table, client)); //匹配的最后一个有个哨兵
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver; if (!client)
return ; /* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return ; driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL; return ;
}

总结:维护了一个__i2c_board_list为头结点的双向循环链表

下面以我tiny4412上使用的S70LCD触摸屏驱动为例:

  从机硬件信息往全局的循环链表__i2c_board_list里注册的时刻必须在 i2c 控制器的设备对象和 i2c控制器的驱动对象由platform总线的匹配规则匹配之前成功注册好。

这里在mach-tiny4412.c里的smdk4x12_machine_init() 来完成。

通过查看原理图,连接的是IIC控制器1;

//触摸屏控制模块ft5206从机的硬件信息的注册在这里。

s3c_i2c1_set_platdata(&tiny4412_i2c1_data);
    i2c_register_board_info(1, smdk4x12_i2c_devs1, ARRAY_SIZE(smdk4x12_i2c_devs1));


#include <plat/ft5x0x_touch.h>
static struct ft5x0x_i2c_platform_data ft5x0x_pdata = {
    .gpio_irq = EXYNOS4_GPX1(6),    //中断外部引脚为EINT14的GPX1(6)
    .irq_cfg = S3C_GPIO_SFN(0xf),   //配置引脚为中断模式
    .screen_max_x = 800,
    .screen_max_y = 1280,
    .pressure_max = 255,
};

static struct i2c_board_info smdk4x12_i2c_devs1[] __initdata = {
{                       //0x38
I2C_BOARD_INFO("ft5x0x_ts", (0x70 >> )),
.platform_data = &ft5x0x_pdata,
},
};

忘了说,先取消厂家提供的驱动程序。

具体目录如上图;

注意:我们的 从机信息在 i2c 设备注册之前已经注册。

涉及从机也就是触摸屏控制模块对应的驱动对象(struct i2c_driver),当其和从机的对象匹配成功则调用probe,probe完成:

@1 触摸屏作为输入设备,则利用input子系统的机制实现驱动的编写。

@2 外部中断的注册。

@3 中断上下半部的实现。

@4 中断的下半部调用I2C控制器的读写算法所完成的访问总线的函数读取ft5206
所准备好的触摸数据。

拿到数据利用input子系统的上报函数上报即可。

驱动代码:

 #include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/gpio.h> #include <plat/ft5x0x_touch.h>
#include <plat/gpio-cfg.h> struct prislavedata {
struct input_dev *inputdev;
struct i2c_client *cli;
int gpio;
int irqnum;
struct work_struct work;
}; /*中断的下半部处理函数*/
static void do_ts_bh(struct work_struct *work)
{
#define LEN 31
struct prislavedata *tsdev = container_of(work, struct prislavedata, work);
char kbuf[LEN];
struct input_dev *idev = tsdev->inputdev;
int x, y; /*读取从机ft5206内部寄存器的值*/ if (i2c_master_recv(tsdev->cli, kbuf, LEN) < ) {
return;
} if (kbuf[] < ) {
return;
} if (!((kbuf[] >> ) & 0x3)) {
x = ((kbuf[]&0xf) << ) | kbuf[];
y = ((kbuf[]&0xf) << ) | kbuf[]; input_report_abs(idev, ABS_X, x);
input_report_abs(idev, ABS_Y, y);
input_report_abs(idev, ABS_PRESSURE, );
input_report_key(idev, BTN_TOUCH, );
input_sync(idev);
} else if (((kbuf[] >> ) & 0x3) == 0x1){
input_report_abs(idev, ABS_PRESSURE, );
input_report_key(idev, BTN_TOUCH, );
input_sync(idev);
} else { } enable_irq(tsdev->irqnum);
} /*中断的上半部处理函数*/
static irqreturn_t do_ts_top(int irqnum, void *data)
{
struct prislavedata *tsdev = data; schedule_work(&tsdev->work); disable_irq_nosync(tsdev->irqnum); return IRQ_HANDLED;
} static int ts_probe(struct i2c_client *cli, const struct i2c_device_id *devid)
{
int ret;
struct input_dev *idev;
struct prislavedata *tsdev;
struct ft5x0x_i2c_platform_data *platdat; /*
1. 获取到从机的信息的GPIO引脚的编号后设置GPIO为外部中断专用。
2. 将GPIO引脚的编号转换为中断号,注册中断,初始化中断的下半部。
3. 为输入设备分配空间,设置事件分类、编码、注册输入设备驱动。
*/ tsdev = kzalloc(sizeof(struct prislavedata), GFP_KERNEL); if (NULL == tsdev) {
return -ENOMEM;
} platdat = cli->dev.platform_data; tsdev->cli = cli;
tsdev->gpio = platdat->gpio_irq;
tsdev->irqnum = gpio_to_irq(platdat->gpio_irq); ret = gpio_request(tsdev->gpio, "ft5206irq");
if (ret < ) {
goto error0;
} /*将GPX1_6设置为外部中断专用*/
s3c_gpio_cfgpin(tsdev->gpio, platdat->irq_cfg); /*注册触摸屏中断*/
ret = request_irq(tsdev->irqnum, do_ts_top, IRQF_TRIGGER_FALLING,
"ft5206", tsdev);
if (ret < ) {
goto error1;
} /*初始化中断的下半部任务*/
INIT_WORK(&tsdev->work, do_ts_bh); tsdev->inputdev = idev = input_allocate_device();
if (!tsdev->inputdev) {
goto error2;
} /*设置事件分类及编码*/
set_bit(EV_ABS, idev->evbit);
set_bit(EV_KEY, idev->evbit); set_bit(ABS_X, idev->absbit);
set_bit(ABS_Y, idev->absbit);
set_bit(ABS_PRESSURE, idev->absbit); set_bit(BTN_TOUCH, idev->keybit); input_set_abs_params(idev, ABS_X, , , , );
input_set_abs_params(idev, ABS_Y, , , , );
input_set_abs_params(idev, ABS_PRESSURE, , , , ); ret = input_register_device(idev);
if (ret < ) {
goto error3;
} i2c_set_clientdata(cli, tsdev); return ;
error3:
input_free_device(idev);
error2:
free_irq(tsdev->irqnum, tsdev);
error1:
gpio_free(tsdev->gpio);
error0:
kfree(tsdev); return ret;
} static int ts_remove (struct i2c_client *cli)
{
struct prislavedata *tsdev = i2c_get_clientdata(cli); input_unregister_device(tsdev->inputdev);
free_irq(tsdev->irqnum, tsdev);
gpio_free(tsdev->gpio);
kfree(tsdev); return ;
} const struct i2c_device_id tables[] = {
{"ft5206", },
{"ft5206_ts", },
{ },
}; static struct i2c_driver ft5206slav = {
.probe = ts_probe,
.remove = ts_remove,
.driver = {
.name = "ft5206",
},
.id_table = tables,
}; module_i2c_driver(ft5206slav); MODULE_LICENSE("GPL");

ts.c

至此:i2c 基本了解。

Linux3.5—IIC学习分析的更多相关文章

  1. 《LINUX3.0内核源代码分析》第二章:中断和异常 【转】

    转自:http://blog.chinaunix.net/uid-25845340-id-2982887.html 摘要:第二章主要讲述linux如何处理ARM cortex A9多核处理器的中断.异 ...

  2. NAACL 2019 字词表示学习分析

    NAACL 2019 表示学习分析 为要找出字.词.文档等实体表示学习相关的文章. word embedding 搜索关键词 word embedding Vector of Locally-Aggr ...

  3. IIC驱动分析

    IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中.在现代电子系统中,有很多的IIC设备需要进行相互之间通信 IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接 ...

  4. CISCN love_math和roarctf的easy_clac学习分析

    Love_math 题目源码: <?php error_reporting(0); //听说你很喜欢数学,不知道你是否爱它胜过爱flag if(!isset($_GET['c'])){ show ...

  5. web兼容学习分析笔记-margin 和padding浏览器解析差异

    二.margin 和padding浏览器解析差异 只有默认margin的元素 <body>margin:8px  margin:15px 10px 15px 10px(IE7) <b ...

  6. Full GC有关问题学习分析(转载)

    网站持久代引发Full GC问题分析 现状: Dragoon(监控系统)的日报显示trade_us_wholelsale(美国wholesale集群),日均Young GC次数25w次左右,应用暂停2 ...

  7. IIC学习

    1 概述: IIC是用两条双向的线,一条SDA(serial data line),一条SCL(serial clock). SCL:上升沿将数据输入到每个EEPROM器件中,下降沿驱动EEPROM器 ...

  8. Kernel的IIC驱动分析

    涉及到的文件: drivers/i2c/i2c-core.c drivers/i2c/i2c-dev.c drivers/i2c/busses/i2c-imx.c 等等 在下面分析的代码中,不想关或者 ...

  9. Mininet实验 MAC地址学习分析

    拓扑图 学习过程分析 首先交换机A和交换机B一开始的MAC地址表都是空的. 此时主机11向主机33发送一个数据帧. 数据帧会先到达交换机A,交换机A会获得主机11的MAC地址和端口号.(此时交换机A的 ...

随机推荐

  1. SQL Server ->> 斐波那契数列(Fibonacci sequence)

    斐波那契数列(Fibonacci sequence)的T-SQL实现 ;WITH T AS ( AS BIGINT) AS curr, CAST(NULL AS BIGINT) AS prv UNIO ...

  2. Linux ->> VMWare Workstation虚拟机里的UBuntu系统安装VMWare-tools

    1) mkdir创建一个临时目录 2)复制gz压缩包到临时目录下 3)解压到当前目录 4)运行.pl文件安装 root@ubuntu:/# root@ubuntu:/# cd /tmp/ root@u ...

  3. 【Leetcode】【Medium】Sqrt(x)

    Implement int sqrt(int x). Compute and return the square root of x. 解题思路1,o(log(n)): 像这种从初始遍历查找匹配的任务 ...

  4. laravel where筛选会判断类型吗?

    laravel where筛选会判断类型吗? laravel where筛选会判断类型吗? laravel where筛选会判断类型吗? 这个说会判断不对,说不会判断也不对. 当字符串'1'和数值1是 ...

  5. Memory Leak Detection in Embedded Systems

    One of the problems with developing embedded systems is the detection of memory leaks; I've found th ...

  6. 前端面试题总结(一)HTML篇

    前端面试题总结(一)HTML篇 一.iframe的优缺点? 缺点: 1.会阻塞主页面的onload事件(iframe和主页面共享链接池,而浏览器对相同域的链接有限制,所以会影响页面的并行加载). 解决 ...

  7. 理解Underscore中的uniq函数

    uniq函数,是Underscore中的一个数组去重函数,给它传递一个数组,它将会返回该数组的去重副本. 1 ES6版本去重 在ES6版本中,引入了一个新的数据结构——set,这是一种类似数组的数据结 ...

  8. hibernate 映射 数据库number 映射为 double 为空 报错问题

    将 数据库表 映射成hibernate 实体类时 将number --- double 如果数据库中number 为空,查找数据时将报错 Can not set double field ***** ...

  9. python 时间和时间戳的转化

    时间戳与时间之间的转换,需要一个中间过程,即将先将时间或时间戳先转为时间元组! 1.时间转时间戳: import datetime, time s = datetime.datetime(2016,6 ...

  10. tp3.2中的 I () 方法

    I('get.id'); // 相当于 $_GET['id']