Linux3.5—IIC學習分析

I2C控制器的設備對象內核已經實現並關聯到platform總線。node

 

I2C控制器的驅動對象內核已經實現。linux

 

 

看mach-tiny4412.hios

/plat-samsung/目錄下算法

/drivers/i2c/   看 *.o 文件數組

看i2c-s3c2410.c   從下往上看。app

.id_table ide

匹配成功後看 probe函數:函數

 

一個I2C控制器對應一個struct s3c24xx_i2c結構體對象:ui

  struct s3c24xx_i2c *i2c; this

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子系統的上報函數上報便可。       

 

驅動代碼:

 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");
ts.c

 

 

至此:i2c 基本瞭解。

相關文章
相關標籤/搜索