Loading

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子系统的上报函数上报即可。       

 

驱动代码:

  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 基本了解。

posted @ 2019-03-13 00:31  dinosaur-  阅读(418)  评论(0编辑  收藏  举报