Linux3.5—IIC学习分析
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:1; 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[2]; #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[48]; 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 = 2; //尝试次数,最多两次 i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->tx_setup = 50;
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 = 0; 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(100); } 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 != 0) { 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 = 0; i2c->msg_idx = 0; 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 == 0, HZ * 5); ret = i2c->msg_idx; /* having these next two as dev_err() makes life very * noisy when doing an i2cdetect */ if (timeout == 0) 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; //检查400次 while (timeout-- > 0) { iicstat = readl(i2c->regs + S3C2410_IICSTAT); if (!(iicstat & S3C2410_IICSTAT_BUSBUSY)) return 0; msleep(1); } 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) << 1; //7位地址,左移一位。 unsigned long stat; unsigned long iiccon; stat = 0; stat |= S3C2410_IICSTAT_TXRXEN; if (msg->flags & I2C_M_RD) { stat |= S3C2410_IICSTAT_MASTER_RX; // 2<<6,对应配置。
addr |= 1; } else stat |= S3C2410_IICSTAT_MASTER_TX; //3<<6 对应上图数据手册截图 if (msg->flags & I2C_M_REV_DIR_ADDR) addr ^= 1; /* 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) /* -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) == 0) 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 == 0 && id != adap->nr) { status = -EBUSY; idr_remove(&i2c_adapter_idr, id); } mutex_unlock(&core_lock); if (status == -EAGAIN) goto retry; if (status == 0) status = i2c_register_adapter(adap); //下面看这个
return status; }
static int i2c_register_adapter(struct i2c_adapter *adap) { int res = 0; /* 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] == '\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 == 0) 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 0; 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 ? 10 : 7, 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 : 0)); 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 0; 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 0; /* Attempt an OF style match */ if (of_driver_match_device(dev, drv)) return 1; 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 0; }
总结:维护了一个__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 >> 1)), .platform_data = &ft5x0x_pdata, }, };
忘了说,先取消厂家提供的驱动程序。
具体目录如上图;
注意:我们的 从机信息在 i2c 设备注册之前已经注册。
涉及从机也就是触摸屏控制模块对应的驱动对象(struct i2c_driver),当其和从机的对象匹配成功则调用probe,probe完成:
@1 触摸屏作为输入设备,则利用input子系统的机制实现驱动的编写。
@2 外部中断的注册。
@3 中断上下半部的实现。
@4 中断的下半部调用I2C控制器的读写算法所完成的访问总线的函数读取ft5206
所准备好的触摸数据。
拿到数据利用input子系统的上报函数上报即可。
驱动代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/input.h> 4 #include <linux/interrupt.h> 5 #include <linux/slab.h> 6 #include <linux/i2c.h> 7 #include <linux/gpio.h> 8 9 #include <plat/ft5x0x_touch.h> 10 #include <plat/gpio-cfg.h> 11 12 struct prislavedata { 13 struct input_dev *inputdev; 14 struct i2c_client *cli; 15 int gpio; 16 int irqnum; 17 struct work_struct work; 18 }; 19 20 /*中断的下半部处理函数*/ 21 static void do_ts_bh(struct work_struct *work) 22 { 23 #define LEN 31 24 struct prislavedata *tsdev = container_of(work, struct prislavedata, work); 25 char kbuf[LEN]; 26 struct input_dev *idev = tsdev->inputdev; 27 int x, y; 28 29 /*读取从机ft5206内部寄存器的值*/ 30 31 if (i2c_master_recv(tsdev->cli, kbuf, LEN) < 0) { 32 return; 33 } 34 35 if (kbuf[2] < 0) { 36 return; 37 } 38 39 if (!((kbuf[3] >> 6) & 0x3)) { 40 x = ((kbuf[3]&0xf) << 8) | kbuf[4]; 41 y = ((kbuf[5]&0xf) << 8) | kbuf[6]; 42 43 input_report_abs(idev, ABS_X, x); 44 input_report_abs(idev, ABS_Y, y); 45 input_report_abs(idev, ABS_PRESSURE, 1); 46 input_report_key(idev, BTN_TOUCH, 1); 47 input_sync(idev); 48 } else if (((kbuf[3] >> 6) & 0x3) == 0x1){ 49 input_report_abs(idev, ABS_PRESSURE, 0); 50 input_report_key(idev, BTN_TOUCH, 0); 51 input_sync(idev); 52 } else { 53 54 } 55 56 57 enable_irq(tsdev->irqnum); 58 } 59 60 /*中断的上半部处理函数*/ 61 static irqreturn_t do_ts_top(int irqnum, void *data) 62 { 63 struct prislavedata *tsdev = data; 64 65 schedule_work(&tsdev->work); 66 67 disable_irq_nosync(tsdev->irqnum); 68 69 return IRQ_HANDLED; 70 } 71 72 static int ts_probe(struct i2c_client *cli, const struct i2c_device_id *devid) 73 { 74 int ret; 75 struct input_dev *idev; 76 struct prislavedata *tsdev; 77 struct ft5x0x_i2c_platform_data *platdat; 78 79 /* 80 1. 获取到从机的信息的GPIO引脚的编号后设置GPIO为外部中断专用。 81 2. 将GPIO引脚的编号转换为中断号,注册中断,初始化中断的下半部。 82 3. 为输入设备分配空间,设置事件分类、编码、注册输入设备驱动。 83 */ 84 85 86 tsdev = kzalloc(sizeof(struct prislavedata), GFP_KERNEL); 87 88 if (NULL == tsdev) { 89 return -ENOMEM; 90 } 91 92 platdat = cli->dev.platform_data; 93 94 tsdev->cli = cli; 95 tsdev->gpio = platdat->gpio_irq; 96 tsdev->irqnum = gpio_to_irq(platdat->gpio_irq); 97 98 ret = gpio_request(tsdev->gpio, "ft5206irq"); 99 if (ret < 0) { 100 goto error0; 101 } 102 103 /*将GPX1_6设置为外部中断专用*/ 104 s3c_gpio_cfgpin(tsdev->gpio, platdat->irq_cfg); 105 106 /*注册触摸屏中断*/ 107 ret = request_irq(tsdev->irqnum, do_ts_top, IRQF_TRIGGER_FALLING, 108 "ft5206", tsdev); 109 if (ret < 0) { 110 goto error1; 111 } 112 113 /*初始化中断的下半部任务*/ 114 INIT_WORK(&tsdev->work, do_ts_bh); 115 116 tsdev->inputdev = idev = input_allocate_device(); 117 if (!tsdev->inputdev) { 118 goto error2; 119 } 120 121 /*设置事件分类及编码*/ 122 set_bit(EV_ABS, idev->evbit); 123 set_bit(EV_KEY, idev->evbit); 124 125 set_bit(ABS_X, idev->absbit); 126 set_bit(ABS_Y, idev->absbit); 127 set_bit(ABS_PRESSURE, idev->absbit); 128 129 set_bit(BTN_TOUCH, idev->keybit); 130 131 input_set_abs_params(idev, ABS_X, 0, 799, 0, 0); 132 input_set_abs_params(idev, ABS_Y, 0, 479, 0, 0); 133 input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0); 134 135 ret = input_register_device(idev); 136 if (ret < 0) { 137 goto error3; 138 } 139 140 i2c_set_clientdata(cli, tsdev); 141 142 return 0; 143 error3: 144 input_free_device(idev); 145 error2: 146 free_irq(tsdev->irqnum, tsdev); 147 error1: 148 gpio_free(tsdev->gpio); 149 error0: 150 kfree(tsdev); 151 152 return ret; 153 } 154 155 static int ts_remove (struct i2c_client *cli) 156 { 157 struct prislavedata *tsdev = i2c_get_clientdata(cli); 158 159 input_unregister_device(tsdev->inputdev); 160 free_irq(tsdev->irqnum, tsdev); 161 gpio_free(tsdev->gpio); 162 kfree(tsdev); 163 164 return 0; 165 } 166 167 const struct i2c_device_id tables[] = { 168 {"ft5206", }, 169 {"ft5206_ts", }, 170 { }, 171 }; 172 173 static struct i2c_driver ft5206slav = { 174 .probe = ts_probe, 175 .remove = ts_remove, 176 .driver = { 177 .name = "ft5206", 178 }, 179 .id_table = tables, 180 }; 181 182 module_i2c_driver(ft5206slav); 183 184 MODULE_LICENSE("GPL");
至此:i2c 基本了解。