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:; 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学习分析的更多相关文章
- 《LINUX3.0内核源代码分析》第二章:中断和异常 【转】
转自:http://blog.chinaunix.net/uid-25845340-id-2982887.html 摘要:第二章主要讲述linux如何处理ARM cortex A9多核处理器的中断.异 ...
- NAACL 2019 字词表示学习分析
NAACL 2019 表示学习分析 为要找出字.词.文档等实体表示学习相关的文章. word embedding 搜索关键词 word embedding Vector of Locally-Aggr ...
- IIC驱动分析
IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中.在现代电子系统中,有很多的IIC设备需要进行相互之间通信 IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接 ...
- CISCN love_math和roarctf的easy_clac学习分析
Love_math 题目源码: <?php error_reporting(0); //听说你很喜欢数学,不知道你是否爱它胜过爱flag if(!isset($_GET['c'])){ show ...
- web兼容学习分析笔记-margin 和padding浏览器解析差异
二.margin 和padding浏览器解析差异 只有默认margin的元素 <body>margin:8px margin:15px 10px 15px 10px(IE7) <b ...
- Full GC有关问题学习分析(转载)
网站持久代引发Full GC问题分析 现状: Dragoon(监控系统)的日报显示trade_us_wholelsale(美国wholesale集群),日均Young GC次数25w次左右,应用暂停2 ...
- IIC学习
1 概述: IIC是用两条双向的线,一条SDA(serial data line),一条SCL(serial clock). SCL:上升沿将数据输入到每个EEPROM器件中,下降沿驱动EEPROM器 ...
- Kernel的IIC驱动分析
涉及到的文件: drivers/i2c/i2c-core.c drivers/i2c/i2c-dev.c drivers/i2c/busses/i2c-imx.c 等等 在下面分析的代码中,不想关或者 ...
- Mininet实验 MAC地址学习分析
拓扑图 学习过程分析 首先交换机A和交换机B一开始的MAC地址表都是空的. 此时主机11向主机33发送一个数据帧. 数据帧会先到达交换机A,交换机A会获得主机11的MAC地址和端口号.(此时交换机A的 ...
随机推荐
- html的文件控件<input type="file">样式的改变
一直以来,<input type="file">上传文件标签默认样式都是让人不爽的,使用它多要给它整整容什么的,当然如果用ui插件还比较方便,不能就自己来操刀实践一下! ...
- Linux->Jdk1.8安装
一.下载jdk 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 二.解 ...
- 【Leetcode】【Medium】Best Time to Buy and Sell Stock II
Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...
- DOS 保存系统日志
1.导出系统日志,以当前日期命名 @echo offset nowDate=%date%set tmp=%nowDate:~0,-3%set file=%tmp:/=-%系统日志.evtx echo ...
- 优秀 Java 程序员写代码的风格
往 期 精 彩 推 荐 [1]Java Web技术经验总结 [2]15个顶级Java多线程面试题及答案,快来看看吧 [3]面试官最喜欢问的十道java面试题 [4]从零讲JAVA ,给你一条清晰 ...
- 为网页元素增加resize事件
默认只有window支持resize事件,但有时我们需要为div等元素添加resize事件 代码见下面,原理是在元素内添加一个内嵌html,然后监听这个内嵌html的resize事件 import { ...
- AngularJs学习笔记--IE Compatibility 兼容老版本IE
原版地址:http://docs.angularjs.org/guide/ie Internet Explorer Compatibility 一.总括 这文章描述Internet Explorer( ...
- mac环境下安装posgreSQL,postGIS,pgrouting方法
费了九牛二虎之力,终于安装成功...都是mac的坑,好好的window环境多好,非要换mac环境,导致软件配置极其的麻烦,window的环境下配置会少很多事,自己惹的祸自己担着吧还是.换mac要慎重, ...
- Intellij IDEA 格式化代码
- UVa 247 - Calling Circles(Floyd求有向图的传递闭包)
链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...