• 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 ");
          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 ");
          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)
    ", 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)
    ", 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
    ");
            else if (ret != num)
                    dev_dbg(i2c->dev, "incomplete xfer (%d)
    ", 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
    ", 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
    ", 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] == '')) {
                    pr_err("i2c-core: Attempt to register an adapter with "
                           "no name!
    ");
                    return -EINVAL;
            }
            if (unlikely(!adap->algo)) {
                    pr_err("i2c-core: Attempt to register adapter '%s' with "
                           "no algo!
    ", 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
    ", 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
    ");
    #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
    ",
                                    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
    ",
                            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
    ",
                    client->name, dev_name(&client->dev));
    
            return client;
    
    out_err:
            dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
                    "(%d)
    ", 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 "); 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 基本了解。

  • 相关阅读:
    (64)通信协议之一xml
    (63)通信协议之一json
    (61)C语言预处理命令详解
    (60) 结构体指针、结构体变量嵌套、结构体指针嵌套、函数指针、数组指针、指针数组、typedef 综合运用
    (59)Linux操作系统深入应用
    (58)PHP开发
    (57)Linux驱动开发之三Linux字符设备驱动
    (56)Linux驱动开发之二
    (55)Linux驱动开发之一驱动概述
    (54)LINUX应用编程和网络编程之九Linux网络通信实践
  • 原文地址:https://www.cnblogs.com/jason-linux/p/10520466.html
Copyright © 2020-2023  润新知