(最新内核3.4)Linux 设备树加载I2C client adapter 的流程(内核3.4 高通)

21 篇文章 0 订阅
17 篇文章 0 订阅

BLSP(BAM Low-Speed Peripheral) , 每一个BLSP含有两个QUP, 每一个QUP可以被配置为I2C, SPI, UART, UIM接口, BLSP是高通对于低速接口的一种管理方式。

    i2c@f9923000 { /* BLSP-1 QUP-1 */
        cell-index = <1>;
        compatible = "qcom,i2c-qup";
        #address-cells = <1>;
        #size-cells = <0>;
        reg-names = "qup_phys_addr";
        reg = <0xf9923000 0x1000>;
        interrupt-names = "qup_err_intr";
        interrupts = <0 95 0>;
        qcom,i2c-bus-freq = <100000>;
        qcom,i2c-src-freq = <19200000>;
        qcom,sda-gpio = <&msmgpio 2 0>;
        qcom,scl-gpio = <&msmgpio 3 0>;
                qcom,master-id = <86>;
    };

    i2c_cdc: i2c@f9927000 { /* BLSP1 QUP5 */
                cell-index = <5>;
                compatible = "qcom,i2c-qup";
                #address-cells = <1>;
                #size-cells = <0>;
                reg-names = "qup_phys_addr";
                reg = <0xf9927000 0x1000>;
                interrupt-names = "qup_err_intr";
                interrupts = <0 99 0>;
                qcom,i2c-bus-freq = <100000>;
                qcom,i2c-src-freq = <19200000>;
                qcom,master-id = <86>;
        };

    i2c: i2c@f9928000 { /* BLSP1 QUP6 */
        cell-index = <6>;
        compatible = "qcom,i2c-qup";
        #address-cells = <1>;
        #size-cells = <0>;
        reg-names = "qup_phys_addr";
        reg = <0xf9928000 0x1000>;
        interrupt-names = "qup_err_intr";
        interrupts = <0 100 0>;
        qcom,i2c-bus-freq = <100000>;
        qcom,i2c-src-freq = <19200000>;
        qcom,sda-gpio = <&msmgpio 16 0>;
        qcom,scl-gpio = <&msmgpio 17 0>;
                qcom,master-id = <86>;
    };


    i2c@f9924000 { /* BLSP-1 QUP-3 */
        cell-index = <2>;
        compatible = "qcom,i2c-qup";
        #address-cells = <1>;
        #size-cells = <0>;
        reg-names = "qup_phys_addr";
        reg = <0xf9924000 0x1000>;
        interrupt-names = "qup_err_intr";
        interrupts = <0 96 0>;
        qcom,i2c-bus-freq = <100000>;
        qcom,i2c-src-freq = <19200000>;
        qcom,sda-gpio = <&msmgpio 8 0>;
        qcom,scl-gpio = <&msmgpio 9 0>;
        qcom,master-id = <86>;
    };

    i2c@f9925000 { /* BLSP-1 QUP-3 */
        cell-index = <0>;
        compatible = "qcom,i2c-qup";
        #address-cells = <1>;
        #size-cells = <0>;
        reg-names = "qup_phys_addr";
        reg = <0xf9925000 0x1000>;
        interrupt-names = "qup_err_intr";
        interrupts = <0 97 0>;
        qcom,i2c-bus-freq = <100000>;
                qcom,i2c-src-freq = <19200000>;
                qcom,sda-gpio = <&msmgpio 10 0>;
                qcom,scl-gpio = <&msmgpio 11 0>;
        qcom,clk-ctl-xfer;
                qcom,master-id = <86>;
    };

以上是I2C的硬件接口描述。

 i2c@f9923000{    //I2C设备挂的两种设备 focaltech  goodix
        focaltech@38{
            compatible = "focaltech,5x06";
            reg = <0x38>;
            interrupt-parent = <&msmgpio>;
            interrupts = <1 0x2008>;
            vdd-supply = <&pm8110_l19>;
            vcc_i2c-supply = <&pm8110_l14>;
            focaltech,name = "ft5x06";
            focaltech,family-id = <0x54>;
            focaltech,reset-gpio = <&msmgpio 0 0x00>;
            focaltech,irq-gpio = <&msmgpio 1 0x00>;
            focaltech,display-coords = <0 0 480 854>;
            focaltech,button-map= <139 172 158>;
            focaltech,no-force-update;
            focaltech,i2c-pull-up;
            focaltech,group-id = <1>;
            focaltech,hard-reset-delay-ms = <20>;
            focaltech,soft-reset-delay-ms = <150>;
            focaltech,num-max-touches = <10>;
            focaltech,fw-name = "5436_Ref_Asus89118_V12_D01_20160712_app.i";
            focaltech,fw-delay-aa-ms = <50>;
            focaltech,fw-delay-55-ms = <30>;
            focaltech,fw-upgrade-id1 = <0x79>;
            focaltech,fw-upgrade-id2 = <0x03>;
            focaltech,fw-delay-readid-ms = <10>;
            focaltech,fw-delay-era-flsh-ms = <2000>;
        };
        goodix@5d {
            compatible = "goodix,gt915l";    //用于.of_match_table = goodix_match_table,
            reg = <0x5d>;           //地址
            interrupt-parent = <&msmgpio>;  //中断块
            interrupts = <1 0x2008>;    //中断地址
            vdd_ana-supply = <&pm8110_l19>; 
            vcc_i2c-supply = <&pm8110_l14>;
            goodix,rst-gpio = <&msmgpio 0 0x00>;    //复位引脚
            goodix,irq-gpio = <&msmgpio 1 0x00>;    //中断引脚
            //goodix,panel-coords = <0 0 540 980>;
            //goodix,display-coords = <0 0 540 960>;
            //goodix,button-map= <139 172 158>;
            //goodix,product-id = "915L";
            //goodix,enable-power-off;
            goodix,cfg-group0 = [
                47 E0 01 56 03 05 34 01 01 05 
                28 08 50 32 03 05 00 00 FF 7F 
                00 11 05 17 18 23 14 8C 2E 0E 
                35 33 0F 0A 00 00 01 BA 33 1D 
                00 01 00 00 00 00 00 0A 10 00 
                2B 2E 55 94 C5 02 00 00 00 04 
                83 31 00 79 37 00 70 3E 00 68 
                46 00 61 50 00 61 18 38 60 00 
                F0 4A 3A EE EE 27 00 00 00 00 
                00 00 00 00 00 00 00 00 00 00 
                00 00 00 00 00 00 0F 19 00 00 
                4B 37 0E 10 12 14 16 18 1A 1C 
                02 04 06 08 0A 0C 00 00 00 00 
                00 00 00 00 00 00 00 00 00 00 
                00 00 2A 29 28 26 24 22 21 20 
                1F 1E 1D 1C 16 18 00 02 04 06 
                08 0A 0C 0F 10 12 13 14 00 00 
                00 00 00 00 00 00 00 00 00 00 
                00 00 00 00 4C 01];
            goodix,cfg-group2 = [
                48 D0 02 00 05 05 75 01 01 0F 24 
                0F 64 3C 03 05 00 00 00 02 00 00 
                00 16 19 1C 14 8C 0E 0E 24 00 31 
                0D 00 00 00 83 33 1D 00 41 00 00 
                3C 0A 14 08 0A 00 2B 1C 3C 94 D5 
                03 08 00 00 04 93 1E 00 82 23 00 
                74 29 00 69 2F 00 5F 37 00 5F 20 
                40 60 00 F0 40 30 55 50 27 00 00 
                00 00 00 00 00 00 00 00 00 00 00 
                00 00 00 00 00 00 00 14 19 00 00 
                50 50 02 04 06 08 0A 0C 0E 10 12 
                14 16 18 1A 1C 00 00 00 00 00 00 
                00 00 00 00 00 00 00 00 00 00 1D 
                1E 1F 20 21 22 24 26 28 29 2A 1C 
                18 16 14 13 12 10 0F 0C 0A 08 06 
                04 02 00 00 00 00 00 00 00 00 00 
                00 00 00 00 00 00 00 00 3C 01];
        };
    };

I2C bus 总线的构建。
在kernel/include/linux/init.h中

#define arch_initcall(fn) 
    #define __define_initcall(level,fn,id) \  
        static initcall_t __initcall_##fn##id __used \  
        __attribute__((__section__(".initcall" level ".init"))) = fn  

将i2c-bus的driver init函数放到.initcall3.init 代码段中。
之所以,i2c-bus的driver和i2c 设备的驱动init函数使用的不同的函数放到.initcall代码段,主要原因是后面调用.initcall中的函数执行顺序是按.initcall段的函数顺序进行的(按照initcall的level从0到7依次存放的)。

    #define INIT_CALLS_LEVEL(level)                        \  
            VMLINUX_SYMBOL(__initcall##level##_start) = .;        \  
            *(.initcall##level##.init)                \  
            *(.initcall##level##s.init)      

    #define INIT_CALLS                          \  
            VMLINUX_SYMBOL(__initcall_start) = .;           \  
            *(.initcallearly.init)                  \  
            INIT_CALLS_LEVEL(0)                 \  
            INIT_CALLS_LEVEL(1)                 \  
            INIT_CALLS_LEVEL(2)                 \  
            INIT_CALLS_LEVEL(3)                 \  
            INIT_CALLS_LEVEL(4)                 \  
            INIT_CALLS_LEVEL(5)                 \  
            INIT_CALLS_LEVEL(rootfs)                \  
            INIT_CALLS_LEVEL(6)                 \  
            INIT_CALLS_LEVEL(7)                 \  
            VMLINUX_SYMBOL(__initcall_end) = .;  

如module_init()的驱动初始化是等级66

(kernel/include/linux/init.h)

#define module_init(x)        __initcall(x);

#define __initcall(fn)                               device_initcall(fn)
#define device_initcall(fn)                __define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) /
        static initcall_t __initcall_##fn##id __used /
        __attribute__((__section__(".initcall" level ".init"))) = fn

initcall_ttypedef int (*initcall_t)(void))

这就保证了I2C总线的会提前的注册好,在注册挂载的驱动和设备。

在linux  /init/main.c 中,
static void __init do_initcalls(void)
{
  initcall_t *call;

  call = &__initcall_start;
  do {
   (*call)();
   call++;
  } while (call < &__initcall_end);
  /* Make sure there is no pending stuff from the initcall sequence */
  flush_scheduled_tasks();
} 

通过do_initcalls就可以把__initcall section段的函数调用起來。
kernel/drivers/i2c/busses/i2c-qup.c

/* Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */
/*
 * QUP driver for Qualcomm MSM platforms
 *
 */

/* #define DEBUG */

#include <linux/module.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c/i2c-qup.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_i2c.h>
#include <linux/of_gpio.h>
#include <mach/board.h>
#include <mach/gpiomux.h>
#include <mach/msm_bus_board.h>

MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.2");
MODULE_ALIAS("platform:i2c_qup");

/* QUP Registers */
enum {
    QUP_CONFIG              = 0x0,
    QUP_STATE               = 0x4,
    QUP_IO_MODE             = 0x8,
    QUP_SW_RESET            = 0xC,
    QUP_OPERATIONAL         = 0x18,
    QUP_ERROR_FLAGS         = 0x1C,
    QUP_ERROR_FLAGS_EN      = 0x20,
    QUP_MX_READ_CNT         = 0x208,
    QUP_MX_INPUT_CNT        = 0x200,
    QUP_MX_WR_CNT           = 0x100,
    QUP_OUT_DEBUG           = 0x108,
    QUP_OUT_FIFO_CNT        = 0x10C,
    QUP_OUT_FIFO_BASE       = 0x110,
    QUP_IN_READ_CUR         = 0x20C,
    QUP_IN_DEBUG            = 0x210,
    QUP_IN_FIFO_CNT         = 0x214,
    QUP_IN_FIFO_BASE        = 0x218,
    QUP_I2C_CLK_CTL         = 0x400,
    QUP_I2C_STATUS          = 0x404,
};

/* QUP States and reset values */
enum {
    QUP_RESET_STATE         = 0,
    QUP_RUN_STATE           = 1U,
    QUP_STATE_MASK          = 3U,
    QUP_PAUSE_STATE         = 3U,
    QUP_STATE_VALID         = 1U << 2,
    QUP_I2C_MAST_GEN        = 1U << 4,
    QUP_OPERATIONAL_RESET   = 0xFF0,
    QUP_I2C_STATUS_RESET    = 0xFFFFFC,
};

/* QUP OPERATIONAL FLAGS */
enum {
    QUP_OUT_SVC_FLAG        = 1U << 8,
    QUP_IN_SVC_FLAG         = 1U << 9,
    QUP_MX_INPUT_DONE       = 1U << 11,
};

/* QUP_CONFIG values and flags */
enum {
    I2C_MINI_CORE           = 2U << 8,
    I2C_N_VAL               = 0xF,
    I2C_CORE_CLK_ON_EN      = BIT(13),

};

/* Packing Unpacking words in FIFOs , and IO modes*/
enum {
    QUP_WR_BLK_MODE  = 1U << 10,
    QUP_RD_BLK_MODE  = 1U << 12,
    QUP_UNPACK_EN = 1U << 14,
    QUP_PACK_EN = 1U << 15,
};

/* QUP tags */
enum {
    QUP_OUT_NOP   = 0,
    QUP_OUT_START = 1U << 8,
    QUP_OUT_DATA  = 2U << 8,
    QUP_OUT_STOP  = 3U << 8,
    QUP_OUT_REC   = 4U << 8,
    QUP_IN_DATA   = 5U << 8,
    QUP_IN_STOP   = 6U << 8,
    QUP_IN_NACK   = 7U << 8,
};

/* Status, Error flags */
enum {
    I2C_STATUS_WR_BUFFER_FULL  = 1U << 0,
    I2C_STATUS_BUS_ACTIVE      = 1U << 8,
    I2C_STATUS_BUS_MASTER      = 1U << 9,
    I2C_STATUS_ERROR_MASK      = 0x38000FC,
    QUP_I2C_NACK_FLAG          = 1U << 3,
    QUP_IN_NOT_EMPTY           = 1U << 5,
    QUP_STATUS_ERROR_FLAGS     = 0x7C,
};

/* Master status clock states */
enum {
    I2C_CLK_RESET_BUSIDLE_STATE = 0,
    I2C_CLK_FORCED_LOW_STATE    = 5,
};

enum msm_i2c_state {
    MSM_I2C_PM_ACTIVE,
    MSM_I2C_PM_SUSPENDED,
    MSM_I2C_SYS_SUSPENDING,
    MSM_I2C_SYS_SUSPENDED,
};
#define QUP_MAX_CLK_STATE_RETRIES   300
#define DEFAULT_CLK_RATE        (19200000)
#define I2C_STATUS_CLK_STATE        13
#define QUP_OUT_FIFO_NOT_EMPTY      0x10
#define I2C_GPIOS_DT_CNT        (2)     /* sda and scl */

static char const * const i2c_rsrcs[] = {"i2c_clk", "i2c_sda"};

static struct gpiomux_setting recovery_config = {
    .func = GPIOMUX_FUNC_GPIO,
    .drv = GPIOMUX_DRV_8MA,
    .pull = GPIOMUX_PULL_NONE,
};

/**
 * qup_i2c_clk_path_vote: data to use bus scaling driver for clock path vote
 *
 * @client_hdl when zero, client is not registered with the bus scaling driver,
 *      and bus scaling functionality should not be used. When non zero, it
 *      is a bus scaling client id and may be used to vote for clock path.
 * @reg_err when true, registration error was detected and an error message was
 *      logged. i2c will attempt to re-register but will log error only once.
 *      once registration succeed, the flag is set to false.
 */
struct qup_i2c_clk_path_vote {
    u32                         client_hdl;
    struct msm_bus_scale_pdata *pdata;
    bool                        reg_err;
};

struct qup_i2c_dev {
    struct device                *dev;
    void __iomem                 *base;     /* virtual */
    void __iomem                 *gsbi;     /* virtual */
    int                          in_irq;
    int                          out_irq;
    int                          err_irq;
    int                          num_irqs;
    struct clk                   *clk;
    struct clk                   *pclk;
    struct i2c_adapter           adapter;

    struct i2c_msg               *msg;
    int                          pos;
    int                          cnt;
    int                          err;
    int                          mode;
    int                          clk_ctl;
    int                          one_bit_t;
    int                          out_fifo_sz;
    int                          in_fifo_sz;
    int                          out_blk_sz;
    int                          in_blk_sz;
    int                          wr_sz;
    struct msm_i2c_platform_data *pdata;
    enum msm_i2c_state           pwr_state;
    atomic_t             xfer_progress;
    struct mutex                 mlock;
    void                         *complete;
    int                          i2c_gpios[ARRAY_SIZE(i2c_rsrcs)];
    struct qup_i2c_clk_path_vote clk_path_vote;
};

#ifdef CONFIG_PM
static int i2c_qup_pm_resume_runtime(struct device *device);
#endif

#ifdef DEBUG
static void
qup_print_status(struct qup_i2c_dev *dev)
{
    uint32_t val;
    val = readl_relaxed(dev->base+QUP_CONFIG);
    dev_dbg(dev->dev, "Qup config is :0x%x\n", val);
    val = readl_relaxed(dev->base+QUP_STATE);
    dev_dbg(dev->dev, "Qup state is :0x%x\n", val);
    val = readl_relaxed(dev->base+QUP_IO_MODE);
    dev_dbg(dev->dev, "Qup mode is :0x%x\n", val);
}
#else
static inline void qup_print_status(struct qup_i2c_dev *dev)
{
}
#endif

static irqreturn_t
qup_i2c_interrupt(int irq, void *devid)
{
    struct qup_i2c_dev *dev = devid;
    uint32_t status = 0;
    uint32_t status1 = 0;
    uint32_t op_flgs = 0;
    int err = 0;

    if (atomic_read(&dev->xfer_progress) != 1) {
        dev_err(dev->dev, "irq:%d when PM suspended\n", irq);
        return IRQ_NONE;
    }

    status = readl_relaxed(dev->base + QUP_I2C_STATUS);
    status1 = readl_relaxed(dev->base + QUP_ERROR_FLAGS);
    op_flgs = readl_relaxed(dev->base + QUP_OPERATIONAL);

    if (!dev->msg || !dev->complete) {
        /* Clear Error interrupt if it's a level triggered interrupt*/
        if (dev->num_irqs == 1) {
            writel_relaxed(QUP_RESET_STATE, dev->base+QUP_STATE);
            /* Ensure that state is written before ISR exits */
            mb();
        }
        return IRQ_HANDLED;
    }

    if (status & I2C_STATUS_ERROR_MASK) {
        dev_err(dev->dev, "QUP: I2C status flags :0x%x, irq:%d\n",
            status, irq);
        err = status;
        /* Clear Error interrupt if it's a level triggered interrupt*/
        if (dev->num_irqs == 1) {
            writel_relaxed(QUP_RESET_STATE, dev->base+QUP_STATE);
            /* Ensure that state is written before ISR exits */
            mb();
        }
        goto intr_done;
    }

    if (status1 & 0x7F) {
        dev_err(dev->dev, "QUP: QUP status flags :0x%x\n", status1);
        err = -status1;
        /* Clear Error interrupt if it's a level triggered interrupt*/
        if (dev->num_irqs == 1) {
            writel_relaxed((status1 & QUP_STATUS_ERROR_FLAGS),
                dev->base + QUP_ERROR_FLAGS);
            /* Ensure that error flags are cleared before ISR
             * exits
             */
            mb();
        }
        goto intr_done;
    }

    if ((dev->num_irqs == 3) && (dev->msg->flags == I2C_M_RD)
        && (irq == dev->out_irq))
        return IRQ_HANDLED;
    if (op_flgs & QUP_OUT_SVC_FLAG) {
        writel_relaxed(QUP_OUT_SVC_FLAG, dev->base + QUP_OPERATIONAL);
        /* Ensure that service flag is acknowledged before ISR exits */
        mb();
    }
    if (dev->msg->flags == I2C_M_RD) {
        if ((op_flgs & QUP_MX_INPUT_DONE) ||
            (op_flgs & QUP_IN_SVC_FLAG)) {
            writel_relaxed(QUP_IN_SVC_FLAG, dev->base
                    + QUP_OPERATIONAL);
            /* Ensure that service flag is acknowledged before ISR
             * exits
             */
            mb();
        } else
            return IRQ_HANDLED;
    }

intr_done:
    dev_dbg(dev->dev, "QUP intr= %d, i2c status=0x%x, qup status = 0x%x\n",
            irq, status, status1);
    qup_print_status(dev);
    dev->err = err;
    complete(dev->complete);
    return IRQ_HANDLED;
}

static int
qup_i2c_poll_state(struct qup_i2c_dev *dev, uint32_t req_state, bool only_valid)
{
    uint32_t retries = 0;

    dev_dbg(dev->dev, "Polling for state:0x%x, or valid-only:%d\n",
                req_state, only_valid);

    while (retries != 2000) {
        uint32_t status = readl_relaxed(dev->base + QUP_STATE);

        /*
         * If only valid bit needs to be checked, requested state is
         * 'don't care'
         */
        if (status & QUP_STATE_VALID) {
            if (only_valid)
                return 0;
            else if ((req_state & QUP_I2C_MAST_GEN) &&
                    (status & QUP_I2C_MAST_GEN))
                return 0;
            else if ((status & QUP_STATE_MASK) == req_state)
                return 0;
        }
        if (retries++ == 1000)
            udelay(100);
    }
    return -ETIMEDOUT;
}

static int
qup_update_state(struct qup_i2c_dev *dev, uint32_t state)
{
    if (qup_i2c_poll_state(dev, 0, true) != 0)
        return -EIO;
    writel_relaxed(state, dev->base + QUP_STATE);
    if (qup_i2c_poll_state(dev, state, false) != 0)
        return -EIO;
    return 0;
}

#define MSM_I2C_CLK_PATH_SUSPEND (0)
#define MSM_I2C_CLK_PATH_RESUME  (1)
#define MSM_I2C_CLK_PATH_MAX_BW(dev) ((dev->pdata->src_clk_rate * 8) / 1000)

static int i2c_qup_clk_path_init(struct platform_device *pdev,
                            struct qup_i2c_dev *dev)
{
    struct msm_bus_vectors *paths    = NULL;
    struct msm_bus_paths   *usecases = NULL;

    if (!dev->pdata->master_id)
        return 0;

    dev_dbg(&pdev->dev, "initialises bus-scaling clock voting");

    paths = devm_kzalloc(&pdev->dev, sizeof(*paths) * 2, GFP_KERNEL);
    if (!paths) {
        dev_err(&pdev->dev,
        "msm_bus_paths.paths memory allocation failed");
        return -ENOMEM;
    }

    usecases = devm_kzalloc(&pdev->dev, sizeof(*usecases) * 2, GFP_KERNEL);
    if (!usecases) {
        dev_err(&pdev->dev,
        "msm_bus_scale_pdata.usecases memory allocation failed");
        goto path_init_err;
    }

    dev->clk_path_vote.pdata = devm_kzalloc(&pdev->dev,
                        sizeof(*dev->clk_path_vote.pdata),
                        GFP_KERNEL);
    if (!dev->clk_path_vote.pdata) {
        dev_err(&pdev->dev,
        "msm_bus_scale_pdata memory allocation failed");
        goto path_init_err;
    }

    paths[MSM_I2C_CLK_PATH_SUSPEND] = (struct msm_bus_vectors) {
        dev->pdata->master_id, MSM_BUS_SLAVE_EBI_CH0, 0, 0
    };

    paths[MSM_I2C_CLK_PATH_RESUME]  = (struct msm_bus_vectors) {
        dev->pdata->master_id, MSM_BUS_SLAVE_EBI_CH0, 0,
                        MSM_I2C_CLK_PATH_MAX_BW(dev)
    };

    usecases[MSM_I2C_CLK_PATH_SUSPEND] = (struct msm_bus_paths) {
        .num_paths = 1,
        .vectors   = &paths[MSM_I2C_CLK_PATH_SUSPEND],
    };

    usecases[MSM_I2C_CLK_PATH_RESUME] = (struct msm_bus_paths) {
        .num_paths = 1,
        .vectors   = &paths[MSM_I2C_CLK_PATH_RESUME],
    };

    *dev->clk_path_vote.pdata = (struct msm_bus_scale_pdata) {
        .active_only  = dev->pdata->active_only,
        .name         = pdev->name,
        .num_usecases = 2,
        .usecase      = usecases,
    };

    return 0;

path_init_err:
    devm_kfree(&pdev->dev, paths);
    devm_kfree(&pdev->dev, usecases);
    devm_kfree(&pdev->dev, dev->clk_path_vote.pdata);
    dev->clk_path_vote.pdata = NULL;
    return -ENOMEM;
}

static void i2c_qup_clk_path_teardown(struct qup_i2c_dev *dev)
{
    if (dev->clk_path_vote.client_hdl) {
        msm_bus_scale_unregister_client(dev->clk_path_vote.client_hdl);
        dev->clk_path_vote.client_hdl = 0;
    }
}

static void i2c_qup_clk_path_vote(struct qup_i2c_dev *dev)
{
    if (dev->clk_path_vote.client_hdl)
        msm_bus_scale_client_update_request(
                        dev->clk_path_vote.client_hdl,
                        MSM_I2C_CLK_PATH_RESUME);
}

static void i2c_qup_clk_path_unvote(struct qup_i2c_dev *dev)
{
    if (dev->clk_path_vote.client_hdl)
        msm_bus_scale_client_update_request(
                        dev->clk_path_vote.client_hdl,
                        MSM_I2C_CLK_PATH_SUSPEND);
}

/**
 * i2c_qup_clk_path_postponed_register: reg with bus-scaling after it is probed
 *
 * Workaround: i2c driver may be probed before the bus scaling driver. Thus,
 * this function should be called not from probe but from a later context.
 * This function may be called more then once before register succeed. At
 * this case only one error message will be logged. At boot time all clocks
 * are on, so earlier i2c transactions should succeed.
 */
static void i2c_qup_clk_path_postponed_register(struct qup_i2c_dev *dev)
{
    /*
     * bail out if path voting is diabled (master_id == 0) or if it is
     * already registered (client_hdl != 0)
     */
    if (!dev->pdata->master_id || dev->clk_path_vote.client_hdl)
        return;

    dev->clk_path_vote.client_hdl = msm_bus_scale_register_client(
                        dev->clk_path_vote.pdata);

    if (dev->clk_path_vote.client_hdl) {
        if (dev->clk_path_vote.reg_err) {
            /* log a success message if an error msg was logged */
            dev->clk_path_vote.reg_err = false;
            dev_info(dev->dev,
                "msm_bus_scale_register_client(mstr-id:%d "
                "actv-only:%d):0x%x",
                dev->pdata->master_id, dev->pdata->active_only,
                dev->clk_path_vote.client_hdl);
        }

        if (dev->pdata->active_only)
            i2c_qup_clk_path_vote(dev);
    } else {
        /* guard to log only one error on multiple failure */
        if (!dev->clk_path_vote.reg_err) {
            dev->clk_path_vote.reg_err = true;

            dev_info(dev->dev,
                "msm_bus_scale_register_client(mstr-id:%d "
                "actv-only:%d):0",
                dev->pdata->master_id, dev->pdata->active_only);
        }
    }
}

static int i2c_qup_gpio_request(struct qup_i2c_dev *dev)
{
    int i;
    int result = 0;

    for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
        if (dev->i2c_gpios[i] >= 0) {
            result = gpio_request(dev->i2c_gpios[i], i2c_rsrcs[i]);
            if (result) {
                dev_err(dev->dev,
                    "gpio_request for pin %d failed with error %d\n",
                    dev->i2c_gpios[i], result);
                goto error;
            }
        }
    }
    return 0;

error:
    for (; --i >= 0;) {
        if (dev->i2c_gpios[i] >= 0)
            gpio_free(dev->i2c_gpios[i]);
    }
    return result;
}

static void i2c_qup_gpio_free(struct qup_i2c_dev *dev)
{
    int i;

    for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
        if (dev->i2c_gpios[i] >= 0)
            gpio_free(dev->i2c_gpios[i]);
    }
}

static void i2c_qup_pm_suspend_clk(struct qup_i2c_dev *dev)
{
    uint32_t status;

    /* reset core and enable conditional dynamic clock gating */
    qup_update_state(dev, QUP_RESET_STATE);
    status = readl_relaxed(dev->base + QUP_CONFIG);
    status |= I2C_CORE_CLK_ON_EN;
    writel_relaxed(status, dev->base + QUP_CONFIG);
    /* ensure that write has really gone through */
    mb();

    clk_disable_unprepare(dev->clk);
    if (!dev->pdata->keep_ahb_clk_on)
        clk_disable_unprepare(dev->pclk);
}

static void i2c_qup_pm_resume_clk(struct qup_i2c_dev *dev)
{
    clk_prepare_enable(dev->clk);
    if (!dev->pdata->keep_ahb_clk_on)
        clk_prepare_enable(dev->pclk);
}

static void i2c_qup_pm_suspend(struct qup_i2c_dev *dev)
{
    if (dev->pwr_state == MSM_I2C_PM_SUSPENDED) {
        dev_err(dev->dev, "attempt to suspend when suspended\n");
        return;
    }

    if (!dev->pdata->clk_ctl_xfer)
        i2c_qup_pm_suspend_clk(dev);

    if (!dev->pdata->active_only)
        i2c_qup_clk_path_unvote(dev);

    i2c_qup_gpio_free(dev);
    dev->pwr_state = MSM_I2C_PM_SUSPENDED;
}

static void i2c_qup_pm_resume(struct qup_i2c_dev *dev)
{
    if (dev->pwr_state == MSM_I2C_PM_ACTIVE)
        return;

    i2c_qup_gpio_request(dev);

    i2c_qup_clk_path_postponed_register(dev);
    if (!dev->pdata->active_only)
        i2c_qup_clk_path_vote(dev);

    if (!dev->pdata->clk_ctl_xfer)
        i2c_qup_pm_resume_clk(dev);
    dev->pwr_state = MSM_I2C_PM_ACTIVE;
}

static int
qup_i2c_poll_writeready(struct qup_i2c_dev *dev, int rem)
{
    uint32_t retries = 0;

    while (retries != 2000) {
        uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);

        if (!(status & I2C_STATUS_WR_BUFFER_FULL)) {
            if (((dev->msg->flags & I2C_M_RD) || (rem == 0)) &&
                !(status & I2C_STATUS_BUS_ACTIVE))
                return 0;
            else if ((dev->msg->flags == 0) && (rem > 0))
                return 0;
            else /* 1-bit delay before we check for bus busy */
                udelay(dev->one_bit_t);
        }
        if (retries++ == 1000) {
            /*
             * Wait for FIFO number of bytes to be absolutely sure
             * that I2C write state machine is not idle. Each byte
             * takes 9 clock cycles. (8 bits + 1 ack)
             */
            usleep_range((dev->one_bit_t * (dev->out_fifo_sz * 9)),
                (dev->one_bit_t * (dev->out_fifo_sz * 9)));
        }
    }
    qup_print_status(dev);
    return -ETIMEDOUT;
}

static int qup_i2c_poll_clock_ready(struct qup_i2c_dev *dev)
{
    uint32_t retries = 0;
    uint32_t op_flgs = -1, clk_state = -1;

    /*
     * Wait for the clock state to transition to either IDLE or FORCED
     * LOW.  This will usually happen within one cycle of the i2c clock.
     */

    while (retries++ < QUP_MAX_CLK_STATE_RETRIES) {
        uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
        clk_state = (status >> I2C_STATUS_CLK_STATE) & 0x7;
        /* Read the operational register */
        op_flgs = readl_relaxed(dev->base +
            QUP_OPERATIONAL) & QUP_OUT_FIFO_NOT_EMPTY;

        /*
         * In very corner case when slave do clock stretching and
         * output fifo will have 1 block of data space empty at
         * the same time.  So i2c qup will get output service
         * interrupt and as it doesn't have more data to be written.
         * This can lead to issue where output fifo is not empty.
        */
        if (op_flgs == 0 &&
            (clk_state == I2C_CLK_RESET_BUSIDLE_STATE ||
            clk_state == I2C_CLK_FORCED_LOW_STATE)){
            dev_dbg(dev->dev, "clk_state 0x%x op_flgs [%x]\n",
                clk_state, op_flgs);
            return 0;
        }

        /* 1-bit delay before we check again */
        udelay(dev->one_bit_t);
    }

    dev_err(dev->dev, "Error waiting for clk ready clk_state: 0x%x op_flgs: 0x%x\n",
        clk_state, op_flgs);
    return -ETIMEDOUT;
}

#ifdef DEBUG
static void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
                uint32_t addr, int rdwr)
{
    if (rdwr)
        dev_dbg(dev->dev, "RD:Wrote 0x%x to out_ff:0x%x\n", val, addr);
    else
        dev_dbg(dev->dev, "WR:Wrote 0x%x to out_ff:0x%x\n", val, addr);
}
#else
static inline void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
                uint32_t addr, int rdwr)
{
}
#endif

static void
qup_issue_read(struct qup_i2c_dev *dev, struct i2c_msg *msg, int *idx,
        uint32_t carry_over)
{
    uint16_t addr = (msg->addr << 1) | 1;
    /* QUP limit 256 bytes per read. By HW design, 0 in the 8-bit field
     * is treated as 256 byte read.
     */
    uint16_t rd_len = ((dev->cnt == 256) ? 0 : dev->cnt);

    if (*idx % 4) {
        writel_relaxed(carry_over | ((QUP_OUT_START | addr) << 16),
        dev->base + QUP_OUT_FIFO_BASE);/* + (*idx-2)); */

        qup_verify_fifo(dev, carry_over |
            ((QUP_OUT_START | addr) << 16), (uint32_t)dev->base
            + QUP_OUT_FIFO_BASE + (*idx - 2), 1);
        writel_relaxed((QUP_OUT_REC | rd_len),
            dev->base + QUP_OUT_FIFO_BASE);/* + (*idx+2)); */

        qup_verify_fifo(dev, (QUP_OUT_REC | rd_len),
        (uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx + 2), 1);
    } else {
        writel_relaxed(((QUP_OUT_REC | rd_len) << 16)
            | QUP_OUT_START | addr,
            dev->base + QUP_OUT_FIFO_BASE);/* + (*idx)); */

        qup_verify_fifo(dev, QUP_OUT_REC << 16 | rd_len << 16 |
        QUP_OUT_START | addr,
        (uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx), 1);
    }
    *idx += 4;
}

static void
qup_issue_write(struct qup_i2c_dev *dev, struct i2c_msg *msg, int rem,
            int *idx, uint32_t *carry_over)
{
    int entries = dev->cnt;
    int empty_sl = dev->wr_sz - ((*idx) >> 1);
    int i = 0;
    uint32_t val = 0;
    uint32_t last_entry = 0;
    uint16_t addr = msg->addr << 1;

    if (dev->pos == 0) {
        if (*idx % 4) {
            writel_relaxed(*carry_over | ((QUP_OUT_START |
                            addr) << 16),
                    dev->base + QUP_OUT_FIFO_BASE);

            qup_verify_fifo(dev, *carry_over | QUP_OUT_START << 16 |
                addr << 16, (uint32_t)dev->base +
                QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
        } else
            val = QUP_OUT_START | addr;
        *idx += 2;
        i++;
        entries++;
    } else {
        /* Avoid setp time issue by adding 1 NOP when number of bytes
         * are more than FIFO/BLOCK size. setup time issue can't appear
         * otherwise since next byte to be written will always be ready
         */
        val = (QUP_OUT_NOP | 1);
        *idx += 2;
        i++;
        entries++;
    }
    if (entries > empty_sl)
        entries = empty_sl;

    for (; i < (entries - 1); i++) {
        if (*idx % 4) {
            writel_relaxed(val | ((QUP_OUT_DATA |
                msg->buf[dev->pos]) << 16),
                dev->base + QUP_OUT_FIFO_BASE);

            qup_verify_fifo(dev, val | QUP_OUT_DATA << 16 |
                msg->buf[dev->pos] << 16, (uint32_t)dev->base +
                QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
        } else
            val = QUP_OUT_DATA | msg->buf[dev->pos];
        (*idx) += 2;
        dev->pos++;
    }
    if (dev->pos < (msg->len - 1))
        last_entry = QUP_OUT_DATA;
    else if (rem > 1) /* not last array entry */
        last_entry = QUP_OUT_DATA;
    else
        last_entry = QUP_OUT_STOP;
    if ((*idx % 4) == 0) {
        /*
         * If read-start and read-command end up in different fifos, it
         * may result in extra-byte being read due to extra-read cycle.
         * Avoid that by inserting NOP as the last entry of fifo only
         * if write command(s) leave 1 space in fifo.
         */
        if (rem > 1) {
            struct i2c_msg *next = msg + 1;
            if (next->addr == msg->addr && (next->flags & I2C_M_RD)
                && *idx == ((dev->wr_sz*2) - 4)) {
                writel_relaxed(((last_entry |
                    msg->buf[dev->pos]) |
                    ((1 | QUP_OUT_NOP) << 16)), dev->base +
                    QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */

                qup_verify_fifo(dev,
                    ((last_entry | msg->buf[dev->pos]) |
                    ((1 | QUP_OUT_NOP) << 16)),
                    (uint32_t)dev->base +
                    QUP_OUT_FIFO_BASE + (*idx), 0);
                *idx += 2;
            } else if ((dev->pos == msg->len - 1)
                    && *idx < (dev->wr_sz*2) &&
                    (next->addr != msg->addr)) {
                /* Last byte of an intermittent write */
                writel_relaxed((QUP_OUT_STOP |
                        msg->buf[dev->pos]),
                    dev->base + QUP_OUT_FIFO_BASE);

                qup_verify_fifo(dev,
                    QUP_OUT_STOP | msg->buf[dev->pos],
                    (uint32_t)dev->base +
                    QUP_OUT_FIFO_BASE + (*idx), 0);
                *idx += 2;
            } else
                *carry_over = (last_entry | msg->buf[dev->pos]);
        } else {
            writel_relaxed((last_entry | msg->buf[dev->pos]),
            dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */

            qup_verify_fifo(dev, last_entry | msg->buf[dev->pos],
            (uint32_t)dev->base + QUP_OUT_FIFO_BASE +
            (*idx), 0);
        }
    } else {
        writel_relaxed(val | ((last_entry | msg->buf[dev->pos]) << 16),
        dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */

        qup_verify_fifo(dev, val | (last_entry << 16) |
        (msg->buf[dev->pos] << 16), (uint32_t)dev->base +
        QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
    }

    *idx += 2;
    dev->pos++;
    dev->cnt = msg->len - dev->pos;
}

static void
qup_set_read_mode(struct qup_i2c_dev *dev, int rd_len)
{
    uint32_t wr_mode = (dev->wr_sz < dev->out_fifo_sz) ?
                QUP_WR_BLK_MODE : 0;
    if (rd_len > 256) {
        dev_dbg(dev->dev, "HW limit: Breaking reads in chunk of 256\n");
        rd_len = 256;
    }
    if (rd_len <= dev->in_fifo_sz) {
        writel_relaxed(wr_mode | QUP_PACK_EN | QUP_UNPACK_EN,
            dev->base + QUP_IO_MODE);
        writel_relaxed(rd_len, dev->base + QUP_MX_READ_CNT);
    } else {
        writel_relaxed(wr_mode | QUP_RD_BLK_MODE |
            QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
        writel_relaxed(rd_len, dev->base + QUP_MX_INPUT_CNT);
    }
}

static int
qup_set_wr_mode(struct qup_i2c_dev *dev, int rem)
{
    int total_len = 0;
    int ret = 0;
    int len = dev->msg->len;
    struct i2c_msg *next = NULL;
    if (rem > 1)
        next = dev->msg + 1;
    while (rem > 1 && next->flags == 0 && (next->addr == dev->msg->addr)) {
        len += next->len + 1;
        next = next + 1;
        rem--;
    }
    if (len >= (dev->out_fifo_sz - 1)) {
        total_len = len + 1 + (len/(dev->out_blk_sz-1));

        writel_relaxed(QUP_WR_BLK_MODE | QUP_PACK_EN | QUP_UNPACK_EN,
            dev->base + QUP_IO_MODE);
        dev->wr_sz = dev->out_blk_sz;
    } else
        writel_relaxed(QUP_PACK_EN | QUP_UNPACK_EN,
            dev->base + QUP_IO_MODE);

    if (rem > 1) {
        if (next->addr == dev->msg->addr &&
            next->flags == I2C_M_RD) {
            qup_set_read_mode(dev, next->len);
            /* make sure read start & read command are in 1 blk */
            if ((total_len % dev->out_blk_sz) ==
                (dev->out_blk_sz - 1))
                total_len += 3;
            else
                total_len += 2;
        }
    }
    /* WRITE COUNT register valid/used only in block mode */
    if (dev->wr_sz == dev->out_blk_sz)
        writel_relaxed(total_len, dev->base + QUP_MX_WR_CNT);
    return ret;
}


static void qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev)
{
    int i;
    int gpio_clk;
    int gpio_dat;
    bool gpio_clk_status = false;
    uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
    struct gpiomux_setting old_gpio_setting[ARRAY_SIZE(i2c_rsrcs)];

    if (dev->pdata->msm_i2c_config_gpio)
        return;

    if (!(status & (I2C_STATUS_BUS_ACTIVE)) ||
        (status & (I2C_STATUS_BUS_MASTER)))
        return;

    gpio_clk = dev->i2c_gpios[0];
    gpio_dat = dev->i2c_gpios[1];

    if ((gpio_clk == -1) && (gpio_dat == -1)) {
        dev_err(dev->dev, "Recovery failed due to undefined GPIO's\n");
        return;
    }

    disable_irq(dev->err_irq);
    for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
        if (msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
                &recovery_config, &old_gpio_setting[i])) {
            dev_err(dev->dev, "GPIO pins have no active setting\n");
            goto recovery_end;
        }
    }

    dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n",
         gpio_get_value(gpio_clk), gpio_get_value(gpio_dat));

    for (i = 0; i < 9; i++) {
        if (gpio_get_value(gpio_dat) && gpio_clk_status)
            break;
        gpio_direction_output(gpio_clk, 0);
        udelay(5);
        gpio_direction_output(gpio_dat, 0);
        udelay(5);
        gpio_direction_input(gpio_clk);
        udelay(5);
        if (!gpio_get_value(gpio_clk))
            udelay(20);
        if (!gpio_get_value(gpio_clk))
            usleep_range(10000, 10000);
        gpio_clk_status = gpio_get_value(gpio_clk);
        gpio_direction_input(gpio_dat);
        udelay(5);
    }

    /* Configure ALT funciton to QUP I2C*/
    for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
        msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
                &old_gpio_setting[i], NULL);
    }

    udelay(10);

    status = readl_relaxed(dev->base + QUP_I2C_STATUS);
    if (!(status & I2C_STATUS_BUS_ACTIVE)) {
        dev_info(dev->dev, "Bus busy cleared after %d clock cycles, "
             "status %x\n",
             i, status);
        goto recovery_end;
    }

    dev_warn(dev->dev, "Bus still busy, status %x\n", status);

recovery_end:
    enable_irq(dev->err_irq);
}

static int
qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
    DECLARE_COMPLETION_ONSTACK(complete);
    struct qup_i2c_dev *dev = i2c_get_adapdata(adap);
    int ret;
    int rem = num;
    long timeout;
    int err;

    /*
     * If all slaves of this controller behave as expected, they will
     * implement suspend and won't call any transaction if they are
     * suspended. Since controller is its parent, controller's suspend
     * will be called only AFTER alls slaves are suspended.
     * However reality is differe and some slave don't implement suspend
     * If a slave tries to initiate transfer when we are suspended,
     * pm_runtime_enabled is set to false by system-pm.
     * Make sure we return error when transaction is initiated while
     * we are in suspended state
     */
    mutex_lock(&dev->mlock);
    if (dev->pwr_state >= MSM_I2C_SYS_SUSPENDING) {
        dev_err(dev->dev,
            "xfer not allowed when ctrl is suspended addr:0x%x\n",
            msgs->addr);
        mutex_unlock(&dev->mlock);
        return -EIO;
    }
    if (!pm_runtime_enabled(dev->dev)) {
        dev_dbg(dev->dev, "Runtime PM FEATURE is disabled\n");
        i2c_qup_pm_resume(dev);
    } else {
        pm_runtime_get_sync(dev->dev);
    }


    if (dev->pdata->clk_ctl_xfer)
        i2c_qup_pm_resume_clk(dev);

    atomic_set(&dev->xfer_progress, 1);
    /* Initialize QUP registers during first transfer */
    if (dev->clk_ctl == 0) {
        int fs_div;
        int hs_div;
        uint32_t fifo_reg;

        if (dev->gsbi) {
            writel_relaxed(0x2 << 4, dev->gsbi);
            /* GSBI memory is not in the same 1K region as other
             * QUP registers. mb() here ensures that the GSBI
             * register is updated in correct order and that the
             * write has gone through before programming QUP core
             * registers
             */
            mb();
        }

        fs_div = ((dev->pdata->src_clk_rate
                / dev->pdata->clk_freq) / 2) - 3;
        hs_div = 3;
        dev->clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
        fifo_reg = readl_relaxed(dev->base + QUP_IO_MODE);
        if (fifo_reg & 0x3)
            dev->out_blk_sz = (fifo_reg & 0x3) * 16;
        else
            dev->out_blk_sz = 16;
        if (fifo_reg & 0x60)
            dev->in_blk_sz = ((fifo_reg & 0x60) >> 5) * 16;
        else
            dev->in_blk_sz = 16;
        /*
         * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
         * associated with each byte written/received
         */
        dev->out_blk_sz /= 2;
        dev->in_blk_sz /= 2;
        dev->out_fifo_sz = dev->out_blk_sz *
                    (2 << ((fifo_reg & 0x1C) >> 2));
        dev->in_fifo_sz = dev->in_blk_sz *
                    (2 << ((fifo_reg & 0x380) >> 7));
        dev_dbg(dev->dev, "QUP IN:bl:%d, ff:%d, OUT:bl:%d, ff:%d\n",
                dev->in_blk_sz, dev->in_fifo_sz,
                dev->out_blk_sz, dev->out_fifo_sz);
    }

    writel_relaxed(1, dev->base + QUP_SW_RESET);
    ret = qup_i2c_poll_state(dev, QUP_RESET_STATE, false);
    if (ret) {
        dev_err(dev->dev, "QUP Busy:Trying to recover\n");
        goto out_err;
    }

    if (dev->num_irqs == 3) {
        enable_irq(dev->in_irq);
        enable_irq(dev->out_irq);
    }
    enable_irq(dev->err_irq);

    /* Initialize QUP registers */
    writel_relaxed(0, dev->base + QUP_CONFIG);
    writel_relaxed(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL);
    writel_relaxed(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN);

    writel_relaxed(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG);

    /* Initialize I2C mini core registers */
    writel_relaxed(0, dev->base + QUP_I2C_CLK_CTL);
    writel_relaxed(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS);

    while (rem) {
        bool filled = false;

        dev->cnt = msgs->len - dev->pos;
        dev->msg = msgs;

        dev->wr_sz = dev->out_fifo_sz;
        dev->err = 0;
        dev->complete = &complete;

        if (qup_i2c_poll_state(dev, QUP_I2C_MAST_GEN, false) != 0) {
            ret = -EIO;
            goto out_err;
        }

        qup_print_status(dev);
        /* HW limits Read upto 256 bytes in 1 read without stop */
        if (dev->msg->flags & I2C_M_RD) {
            qup_set_read_mode(dev, dev->cnt);
            if (dev->cnt > 256)
                dev->cnt = 256;
        } else {
            ret = qup_set_wr_mode(dev, rem);
            if (ret != 0)
                goto out_err;
            /* Don't fill block till we get interrupt */
            if (dev->wr_sz == dev->out_blk_sz)
                filled = true;
        }

        err = qup_update_state(dev, QUP_RUN_STATE);
        if (err < 0) {
            ret = err;
            goto out_err;
        }

        qup_print_status(dev);
        writel_relaxed(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL);
        /* CLK_CTL register is not in the same 1K region as other QUP
         * registers. Ensure that clock control is written before
         * programming other QUP registers
         */
        mb();

        do {
            int idx = 0;
            uint32_t carry_over = 0;

            /* Transition to PAUSE state only possible from RUN */
            err = qup_update_state(dev, QUP_PAUSE_STATE);
            if (err < 0) {
                ret = err;
                goto out_err;
            }

            qup_print_status(dev);
            /* This operation is Write, check the next operation
             * and decide mode
             */
            while (filled == false) {
                if ((msgs->flags & I2C_M_RD))
                    qup_issue_read(dev, msgs, &idx,
                            carry_over);
                else if (!(msgs->flags & I2C_M_RD))
                    qup_issue_write(dev, msgs, rem, &idx,
                            &carry_over);
                if (idx >= (dev->wr_sz << 1))
                    filled = true;
                /* Start new message */
                if (filled == false) {
                    if (msgs->flags & I2C_M_RD)
                            filled = true;
                    else if (rem > 1) {
                        /* Only combine operations with
                         * same address
                         */
                        struct i2c_msg *next = msgs + 1;
                        if (next->addr != msgs->addr)
                            filled = true;
                        else {
                            rem--;
                            msgs++;
                            dev->msg = msgs;
                            dev->pos = 0;
                            dev->cnt = msgs->len;
                            if (msgs->len > 256)
                                dev->cnt = 256;
                        }
                    } else
                        filled = true;
                }
            }
            err = qup_update_state(dev, QUP_RUN_STATE);
            if (err < 0) {
                ret = err;
                goto out_err;
            }
            dev_dbg(dev->dev, "idx:%d, rem:%d, num:%d, mode:%d\n",
                idx, rem, num, dev->mode);

            qup_print_status(dev);
            timeout = wait_for_completion_timeout(&complete,
                    msecs_to_jiffies(dev->out_fifo_sz));
            if (!timeout) {
                uint32_t istatus = readl_relaxed(dev->base +
                            QUP_I2C_STATUS);
                uint32_t qstatus = readl_relaxed(dev->base +
                            QUP_ERROR_FLAGS);
                uint32_t op_flgs = readl_relaxed(dev->base +
                            QUP_OPERATIONAL);

                /*
                 * Dont wait for 1 sec if i2c sees the bus
                 * active and controller is not master.
                 * A slave has pulled line low. Try to recover
                 */
                if (!(istatus & I2C_STATUS_BUS_ACTIVE) ||
                    (istatus & I2C_STATUS_BUS_MASTER)) {
                    timeout =
                    wait_for_completion_timeout(&complete,
                                    HZ);
                    if (timeout)
                        goto timeout_err;
                }
                qup_i2c_recover_bus_busy(dev);
                dev_err(dev->dev,
                    "Transaction timed out, SL-AD = 0x%x\n",
                    dev->msg->addr);

                dev_err(dev->dev, "I2C Status: %x\n", istatus);
                dev_err(dev->dev, "QUP Status: %x\n", qstatus);
                dev_err(dev->dev, "OP Flags: %x\n", op_flgs);
                writel_relaxed(1, dev->base + QUP_SW_RESET);
                /* Make sure that the write has gone through
                 * before returning from the function
                 */
                mb();
                ret = -ETIMEDOUT;
                goto out_err;
            }
timeout_err:
            if (dev->err) {
                if (dev->err > 0 &&
                    dev->err & QUP_I2C_NACK_FLAG) {
                    dev_err(dev->dev,
                    "I2C slave addr:0x%x not connected\n",
                    dev->msg->addr);
                    dev->err = ENOTCONN;
                } else if (dev->err < 0) {
                    dev_err(dev->dev,
                    "QUP data xfer error %d\n", dev->err);
                    ret = dev->err;
                    goto out_err;
                } else if (dev->err > 0) {
                    /*
                     * ISR returns +ve error if error code
                     * is I2C related, e.g. unexpected start
                     * So you may call recover-bus-busy when
                     * this error happens
                     */
                    qup_i2c_recover_bus_busy(dev);
                }
                ret = -dev->err;
                goto out_err;
            }
            if (dev->msg->flags & I2C_M_RD) {
                int i;
                uint32_t dval = 0;
                for (i = 0; dev->pos < dev->msg->len; i++,
                        dev->pos++) {
                    uint32_t rd_status =
                        readl_relaxed(dev->base
                            + QUP_OPERATIONAL);
                    if (i % 2 == 0) {
                        if ((rd_status &
                            QUP_IN_NOT_EMPTY) == 0)
                            break;
                        dval = readl_relaxed(dev->base +
                            QUP_IN_FIFO_BASE);
                        dev->msg->buf[dev->pos] =
                            dval & 0xFF;
                    } else
                        dev->msg->buf[dev->pos] =
                            ((dval & 0xFF0000) >>
                             16);
                }
                dev->cnt -= i;
            } else
                filled = false; /* refill output FIFO */
            dev_dbg(dev->dev, "pos:%d, len:%d, cnt:%d\n",
                    dev->pos, msgs->len, dev->cnt);
        } while (dev->cnt > 0);
        if (dev->cnt == 0) {
            if (msgs->len == dev->pos) {
                rem--;
                msgs++;
                dev->pos = 0;
            }
            if (rem) {
                err = qup_i2c_poll_clock_ready(dev);
                if (err < 0) {
                    ret = err;
                    goto out_err;
                }
                err = qup_update_state(dev, QUP_RESET_STATE);
                if (err < 0) {
                    ret = err;
                    goto out_err;
                }
            }
        }
        /* Wait for I2C bus to be idle */
        ret = qup_i2c_poll_writeready(dev, rem);
        if (ret) {
            dev_err(dev->dev,
                "Error waiting for write ready\n");
            goto out_err;
        }
    }

    ret = num;
 out_err:
    disable_irq(dev->err_irq);
    if (dev->num_irqs == 3) {
        disable_irq(dev->in_irq);
        disable_irq(dev->out_irq);
    }
    dev->complete = NULL;
    dev->msg = NULL;
    dev->pos = 0;
    dev->err = 0;
    dev->cnt = 0;
    if (dev->pdata->clk_ctl_xfer)
        i2c_qup_pm_suspend_clk(dev);
    atomic_set(&dev->xfer_progress, 0);
    mutex_unlock(&dev->mlock);
    pm_runtime_mark_last_busy(dev->dev);
    pm_runtime_put_autosuspend(dev->dev);
    return ret;
}

enum msm_i2c_dt_entry_status {
    DT_REQUIRED,
    DT_SUGGESTED,
    DT_OPTIONAL,
};

enum msm_i2c_dt_entry_type {
    DT_U32,
    DT_GPIO,
    DT_BOOL,
};

struct msm_i2c_dt_to_pdata_map {
    const char                  *dt_name;
    void                        *ptr_data;
    enum msm_i2c_dt_entry_status status;
    enum msm_i2c_dt_entry_type   type;
    int                          default_val;
};

int __devinit msm_i2c_rsrcs_dt_to_pdata_map(struct platform_device *pdev,
                struct msm_i2c_platform_data *pdata, int *gpios)
{
    int  ret, err = 0;
    struct device_node *node = pdev->dev.of_node;
    struct msm_i2c_dt_to_pdata_map *itr;
    struct msm_i2c_dt_to_pdata_map  map[] = {
    {"qcom,i2c-bus-freq", &pdata->clk_freq,     DT_REQUIRED,  DT_U32,   0},
    {"cell-index",        &pdev->id,            DT_REQUIRED,  DT_U32,  -1},
    {"qcom,i2c-src-freq", &pdata->src_clk_rate, DT_SUGGESTED, DT_U32,   0},
    {"qcom,master-id",    &pdata->master_id,    DT_SUGGESTED, DT_U32,   0},
    {"qcom,scl-gpio",      gpios,               DT_OPTIONAL,  DT_GPIO, -1},
    {"qcom,sda-gpio",      gpios + 1,           DT_OPTIONAL,  DT_GPIO, -1},
    {"qcom,clk-ctl-xfer", &pdata->clk_ctl_xfer, DT_OPTIONAL,  DT_BOOL, -1},
    {"qcom,active-only",  &pdata->active_only,  DT_OPTIONAL,  DT_BOOL,  0},
    {NULL,                 NULL,                0,            0,        0},
    };

    for (itr = map; itr->dt_name ; ++itr) {
        switch (itr->type) {
        case DT_GPIO:
            ret = of_get_named_gpio(node, itr->dt_name, 0);
            if (ret >= 0) {
                *((int *) itr->ptr_data) = ret;
                ret = 0;
            }
            break;
        case DT_U32:
            ret = of_property_read_u32(node, itr->dt_name,
                             (u32 *) itr->ptr_data);
            break;
        case DT_BOOL:
            *((bool *) itr->ptr_data) =
                of_property_read_bool(node, itr->dt_name);
            ret = 0;
            break;
        default:
            dev_err(&pdev->dev, "%d is an unknown DT entry type\n",
                                itr->type);
            ret = -EBADE;
        }

        dev_dbg(&pdev->dev, "DT entry ret:%d name:%s val:%d\n",
                ret, itr->dt_name, *((int *)itr->ptr_data));

        if (ret) {
            *((int *)itr->ptr_data) = itr->default_val;

            if (itr->status < DT_OPTIONAL) {
                dev_err(&pdev->dev, "Missing '%s' DT entry\n",
                                itr->dt_name);

                /* cont on err to dump all missing entries */
                if (itr->status == DT_REQUIRED && !err)
                    err = ret;
            }
        }
    }

    return err;
}

static u32
qup_i2c_func(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}

static const struct i2c_algorithm qup_i2c_algo = {
    .master_xfer    = qup_i2c_xfer,
    .functionality  = qup_i2c_func,
};

static int __devinit
qup_i2c_probe(struct platform_device *pdev)
{
    struct qup_i2c_dev  *dev;
    struct resource         *qup_mem, *gsbi_mem, *qup_io, *gsbi_io, *res;
    struct resource     *in_irq, *out_irq, *err_irq;
    struct clk         *clk, *pclk;
    int  ret = 0;
    int  i;
    int  dt_gpios[I2C_GPIOS_DT_CNT];
    bool use_device_tree = pdev->dev.of_node;
    struct msm_i2c_platform_data *pdata;

    gsbi_mem = NULL;
    dev_dbg(&pdev->dev, "qup_i2c_probe\n");

    if (use_device_tree) {
        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
            return -ENOMEM;

        ret = msm_i2c_rsrcs_dt_to_pdata_map(pdev, pdata, dt_gpios);
        if (ret)
            goto get_res_failed;
    } else
        pdata = pdev->dev.platform_data;

    if (!pdata) {
        dev_err(&pdev->dev, "platform data not initialized\n");
        return -ENOSYS;
    }
    qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                        "qup_phys_addr");
    if (!qup_mem) {
        dev_err(&pdev->dev,
            "platform_get_resource_byname(qup_phys_addr) failed\n");
        ret = -ENODEV;
        goto get_res_failed;
    }

    /*
     * We only have 1 interrupt for new hardware targets and in_irq,
     * out_irq will be NULL for those platforms
     */
    in_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                        "qup_in_intr");

    out_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                        "qup_out_intr");

    err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                        "qup_err_intr");
    if (!err_irq) {
        dev_err(&pdev->dev, "no error irq resource?\n");
        ret = -ENODEV;
        goto get_res_failed;
    }

    qup_io = request_mem_region(qup_mem->start, resource_size(qup_mem),
                    pdev->name);
    if (!qup_io) {
        dev_err(&pdev->dev, "QUP region already claimed\n");
        ret = -EBUSY;
        goto get_res_failed;
    }
    if (!pdata->use_gsbi_shared_mode) {
        gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                            "gsbi_qup_i2c_addr");
        if (!gsbi_mem) {
            dev_dbg(&pdev->dev, "Assume BLSP\n");
            /*
             * BLSP core does not need protocol programming so this
             * resource is not expected
             */
            goto blsp_core_init;
        }
        gsbi_io = request_mem_region(gsbi_mem->start,
                        resource_size(gsbi_mem),
                        pdev->name);
        if (!gsbi_io) {
            dev_err(&pdev->dev, "GSBI region already claimed\n");
            ret = -EBUSY;
            goto err_res_failed;
        }
    }

blsp_core_init:
    clk = clk_get(&pdev->dev, "core_clk");
    if (IS_ERR(clk)) {
        dev_err(&pdev->dev, "Could not get core_clk\n");
        ret = PTR_ERR(clk);
        goto err_clk_get_failed;
    }

    pclk = clk_get(&pdev->dev, "iface_clk");
    if (IS_ERR(pclk)) {
        dev_err(&pdev->dev, "Could not get iface_clk\n");
        ret = PTR_ERR(pclk);
        clk_put(clk);
        goto err_clk_get_failed;
    }

    /* We support frequencies upto FAST Mode(400KHz) */
    if (pdata->clk_freq <= 0 ||
            pdata->clk_freq > 400000) {
        dev_err(&pdev->dev, "clock frequency not supported\n");
        ret = -EIO;
        goto err_config_failed;
    }

    dev = kzalloc(sizeof(struct qup_i2c_dev), GFP_KERNEL);
    if (!dev) {
        ret = -ENOMEM;
        goto err_alloc_dev_failed;
    }

    dev->dev = &pdev->dev;
    if (in_irq)
        dev->in_irq = in_irq->start;
    if (out_irq)
        dev->out_irq = out_irq->start;
    dev->err_irq = err_irq->start;
    if (in_irq && out_irq)
        dev->num_irqs = 3;
    else
        dev->num_irqs = 1;
    dev->clk = clk;
    dev->pclk = pclk;
    dev->base = ioremap(qup_mem->start, resource_size(qup_mem));
    if (!dev->base) {
        ret = -ENOMEM;
        goto err_ioremap_failed;
    }

    /* Configure GSBI block to use I2C functionality */
    if (gsbi_mem) {
        dev->gsbi = ioremap(gsbi_mem->start, resource_size(gsbi_mem));
        if (!dev->gsbi) {
            ret = -ENOMEM;
            goto err_gsbi_failed;
        }
    }

    for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
        if (use_device_tree && i < I2C_GPIOS_DT_CNT) {
            dev->i2c_gpios[i] = dt_gpios[i];
        } else {
            res = platform_get_resource_byname(pdev, IORESOURCE_IO,
                               i2c_rsrcs[i]);
            dev->i2c_gpios[i] = res ? res->start : -1;
        }
    }

    platform_set_drvdata(pdev, dev);

    dev->one_bit_t = (USEC_PER_SEC/pdata->clk_freq) + 1;
    dev->pdata = pdata;
    dev->clk_ctl = 0;
    dev->pos = 0;

    ret = i2c_qup_clk_path_init(pdev, dev);
    if (ret) {
        dev_err(&pdev->dev,
        "Failed to init clock path-voting data structs. err:%d", ret);
        /* disable i2c_qup_clk_path_xxx() functionality */
        dev->pdata->master_id = 0;
    }

    if (dev->pdata->src_clk_rate <= 0) {
        dev_info(&pdev->dev,
            "No src_clk_rate specified in platfrom data\n");
        dev_info(&pdev->dev, "Using default clock rate %dHz\n",
                            DEFAULT_CLK_RATE);
        dev->pdata->src_clk_rate = DEFAULT_CLK_RATE;
    }

    ret = clk_set_rate(dev->clk, dev->pdata->src_clk_rate);
    if (ret)
        dev_info(&pdev->dev, "clk_set_rate(core_clk, %dHz):%d\n",
                    dev->pdata->src_clk_rate, ret);

    clk_prepare_enable(dev->clk);
    clk_prepare_enable(dev->pclk);
    /*
     * If bootloaders leave a pending interrupt on certain GSBI's,
     * then we reset the core before registering for interrupts.
     */
    writel_relaxed(1, dev->base + QUP_SW_RESET);
    if (qup_i2c_poll_state(dev, 0, true) != 0)
        goto err_reset_failed;
    clk_disable_unprepare(dev->clk);
    clk_disable_unprepare(dev->pclk);

    /*
     * We use num_irqs to also indicate if we got 3 interrupts or just 1.
     * If we have just 1, we use err_irq as the general purpose irq
     * and handle the changes in ISR accordingly
     * Per Hardware guidelines, if we have 3 interrupts, they are always
     * edge triggering, and if we have 1, it's always level-triggering
     */
    if (dev->num_irqs == 3) {
        ret = request_irq(dev->in_irq, qup_i2c_interrupt,
                IRQF_TRIGGER_RISING, "qup_in_intr", dev);
        if (ret) {
            dev_err(&pdev->dev, "request_in_irq failed\n");
            goto err_request_irq_failed;
        }
        /*
         * We assume out_irq exists if in_irq does since platform
         * configuration either has 3 interrupts assigned to QUP or 1
         */
        ret = request_irq(dev->out_irq, qup_i2c_interrupt,
                IRQF_TRIGGER_RISING, "qup_out_intr", dev);
        if (ret) {
            dev_err(&pdev->dev, "request_out_irq failed\n");
            free_irq(dev->in_irq, dev);
            goto err_request_irq_failed;
        }
        ret = request_irq(dev->err_irq, qup_i2c_interrupt,
                IRQF_TRIGGER_RISING, "qup_err_intr", dev);
        if (ret) {
            dev_err(&pdev->dev, "request_err_irq failed\n");
            free_irq(dev->out_irq, dev);
            free_irq(dev->in_irq, dev);
            goto err_request_irq_failed;
        }
    } else {
        ret = request_irq(dev->err_irq, qup_i2c_interrupt,
                IRQF_TRIGGER_HIGH, "qup_err_intr", dev);
        if (ret) {
            dev_err(&pdev->dev, "request_err_irq failed\n");
            goto err_request_irq_failed;
        }
    }
    disable_irq(dev->err_irq);
    if (dev->num_irqs == 3) {
        disable_irq(dev->in_irq);
        disable_irq(dev->out_irq);
    }
    i2c_set_adapdata(&dev->adapter, dev);
    dev->adapter.algo = &qup_i2c_algo;
    strlcpy(dev->adapter.name,
        "QUP I2C adapter",
        sizeof(dev->adapter.name));
    dev->adapter.nr = pdev->id;
    dev->adapter.dev.parent = &pdev->dev;
    if (pdata->msm_i2c_config_gpio)
        pdata->msm_i2c_config_gpio(dev->adapter.nr, 1);

    mutex_init(&dev->mlock);
    dev->pwr_state = MSM_I2C_PM_SUSPENDED;
    atomic_set(&dev->xfer_progress, 0);
    /* If the same AHB clock is used on Modem side
     * switch it on here itself and don't switch it
     * on and off during suspend and resume.
     */
    if (dev->pdata->keep_ahb_clk_on)
        clk_prepare_enable(dev->pclk);

    ret = i2c_add_numbered_adapter(&dev->adapter);
    if (ret) {
        dev_err(&pdev->dev, "i2c_add_adapter failed\n");
        if (dev->num_irqs == 3) {
            free_irq(dev->out_irq, dev);
            free_irq(dev->in_irq, dev);
        }
        free_irq(dev->err_irq, dev);
    } else {
        if (dev->dev->of_node) {
            dev->adapter.dev.of_node = pdev->dev.of_node;
            of_i2c_register_devices(&dev->adapter);
        }

        pm_runtime_set_autosuspend_delay(&pdev->dev, MSEC_PER_SEC);
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
        return 0;
    }


err_request_irq_failed:
    if (dev->gsbi)
        iounmap(dev->gsbi);
err_reset_failed:
    clk_disable_unprepare(dev->clk);
    clk_disable_unprepare(dev->pclk);
    i2c_qup_clk_path_teardown(dev);
err_gsbi_failed:
    iounmap(dev->base);
err_ioremap_failed:
    kfree(dev);
err_alloc_dev_failed:
err_config_failed:
    clk_put(clk);
    clk_put(pclk);
err_clk_get_failed:
    if (gsbi_mem)
        release_mem_region(gsbi_mem->start, resource_size(gsbi_mem));
err_res_failed:
    release_mem_region(qup_mem->start, resource_size(qup_mem));
get_res_failed:
    if (pdev->dev.of_node)
        kfree(pdata);
    return ret;
}

static void qup_i2c_mem_release(struct platform_device *pdev, const char *name)
{
    struct resource *res =
        platform_get_resource_byname(pdev, IORESOURCE_MEM, name);

    if (res)
        release_mem_region(res->start, resource_size(res));
    else
        dev_dbg(&pdev->dev,
            "platform_get_resource_byname(%s) failed\n", name);
}

static int __devexit
qup_i2c_remove(struct platform_device *pdev)
{
    struct qup_i2c_dev *dev = platform_get_drvdata(pdev);

    /* Grab mutex to ensure ongoing transaction is over */
    mutex_lock(&dev->mlock);
    dev->pwr_state = MSM_I2C_SYS_SUSPENDING;
    mutex_unlock(&dev->mlock);
    i2c_qup_pm_suspend(dev);
    dev->pwr_state = MSM_I2C_SYS_SUSPENDED;
    mutex_destroy(&dev->mlock);
    platform_set_drvdata(pdev, NULL);
    if (dev->num_irqs == 3) {
        free_irq(dev->out_irq, dev);
        free_irq(dev->in_irq, dev);
    }
    free_irq(dev->err_irq, dev);
    i2c_del_adapter(&dev->adapter);
    if (!dev->pdata->keep_ahb_clk_on) {
        clk_put(dev->pclk);
    }
    clk_put(dev->clk);

    if (dev->pdata->active_only)
        i2c_qup_clk_path_unvote(dev);
    i2c_qup_clk_path_teardown(dev);

    if (dev->gsbi)
        iounmap(dev->gsbi);
    iounmap(dev->base);

    pm_runtime_disable(&pdev->dev);
    pm_runtime_set_suspended(&pdev->dev);

    if (!(dev->pdata->use_gsbi_shared_mode))
        qup_i2c_mem_release(pdev, "gsbi_qup_i2c_addr");

    qup_i2c_mem_release(pdev, "qup_phys_addr");

    if (dev->dev->of_node)
        kfree(dev->pdata);
    kfree(dev);
    return 0;
}

#ifdef CONFIG_PM
static int i2c_qup_pm_suspend_runtime(struct device *device)
{
    struct platform_device *pdev = to_platform_device(device);
    struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
    dev_dbg(device, "pm_runtime: suspending...\n");
    i2c_qup_pm_suspend(dev);
    return 0;
}

static int i2c_qup_pm_resume_runtime(struct device *device)
{
    struct platform_device *pdev = to_platform_device(device);
    struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
    dev_dbg(device, "pm_runtime: resuming...\n");
    i2c_qup_pm_resume(dev);
    return 0;
}

static int i2c_qup_pm_suspend_sys(struct device *device)
{
    struct platform_device *pdev = to_platform_device(device);
    struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
    /* Acquire mutex to ensure current transaction is over */
    mutex_lock(&dev->mlock);
    dev->pwr_state = MSM_I2C_SYS_SUSPENDING;
    mutex_unlock(&dev->mlock);
    if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
        dev_dbg(device, "system suspend\n");
        i2c_qup_pm_suspend(dev);
        /*
         * set the device's runtime PM status to 'suspended'
         */
        pm_runtime_disable(device);
        pm_runtime_set_suspended(device);
        pm_runtime_enable(device);
    }
    dev->pwr_state = MSM_I2C_SYS_SUSPENDED;
    return 0;
}

static int i2c_qup_pm_resume_sys(struct device *device)
{
    struct platform_device *pdev = to_platform_device(device);
    struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
    /*
     * Rely on runtime-PM to call resume in case it is enabled
     * Even if it's not enabled, rely on 1st client transaction to do
     * clock ON and gpio configuration
     */
    dev_dbg(device, "system resume\n");
    dev->pwr_state = MSM_I2C_PM_SUSPENDED;
    return 0;
}
#endif /* CONFIG_PM */

static const struct dev_pm_ops i2c_qup_dev_pm_ops = {
    SET_SYSTEM_SLEEP_PM_OPS(
        i2c_qup_pm_suspend_sys,
        i2c_qup_pm_resume_sys
    )
    SET_RUNTIME_PM_OPS(
        i2c_qup_pm_suspend_runtime,
        i2c_qup_pm_resume_runtime,
        NULL
    )
};

static struct of_device_id i2c_qup_dt_match[] = {
    {
        .compatible = "qcom,i2c-qup",
    },
    {}
};

static struct platform_driver qup_i2c_driver = {
    .probe      = qup_i2c_probe,
    .remove     = __devexit_p(qup_i2c_remove),
    .driver     = {
        .name   = "qup_i2c",
        .owner  = THIS_MODULE,
        .pm = &i2c_qup_dev_pm_ops,
        .of_match_table = i2c_qup_dt_match,
    },
};

/* QUP may be needed to bring up other drivers */
int __init qup_i2c_init_driver(void)
{
    static bool initialized;

    if (initialized)
        return 0;
    else
        initialized = true;

    return platform_driver_register(&qup_i2c_driver);
}
EXPORT_SYMBOL(qup_i2c_init_driver);
arch_initcall(qup_i2c_init_driver);

static void __exit qup_i2c_exit_driver(void)
{
    platform_driver_unregister(&qup_i2c_driver);
}
module_exit(qup_i2c_exit_driver);

使用中注意设备树中配成相应的

static struct of_device_id i2c_qup_dt_match[] = {
    {
        .compatible = "qcom,i2c-qup",
    },
    {}
};

第一:
arch_initcall(qup_i2c_init_driver);
第二:
platform_driver_register(&qup_i2c_driver);

static struct platform_driver qup_i2c_driver = {
    .probe      = qup_i2c_probe,
    .remove     = __devexit_p(qup_i2c_remove),
    .driver     = {
        .name   = "qup_i2c",
        .owner  = THIS_MODULE,
        .pm = &i2c_qup_dev_pm_ops,
        .of_match_table = i2c_qup_dt_match,
    },
};

在probe中,先提取platform_device 的设备信息填充i2c平台数据(struct msm_i2c_platform_data) *pdata;
struct device_node *node = pdev->dev.of_node;

compatible = “qcom,i2c-qup”; 用于与驱动match的
reg-names = “qup_phys_addr”;
reg = <0xf9923000 0x1000>;
qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
“qup_phys_addr”);
这个用于一块的IORESOURCE_MEM (resource)的数据结构(struct resource *qup_mem)
interrupt-names = “qup_err_intr”;
interrupts = <0 95 0>;
err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
“qup_err_intr”);
用于读取IORESOURCE_IRQ资源,

ret = of_get_named_gpio(node, itr->dt_name, 0);
static inline int of_get_named_gpio(struct device_node *np,const char *propname, int index)
{
return of_get_named_gpio_flags(np, propname, index, NULL);*返回的是GPIO的有效值,使用Linux通用GPIO的API,或者是一个错误

*值在错误条件。如果“标记”不为空,则函数也填充

对于GPIO标志*。
}
ret = of_property_read_u32(node, itr->dt_name,(u32 *) itr->ptr_data);
static inline int of_property_read_u32(const struct device_node *np,const char *propname, u32 *out_value)
{
return of_property_read_u32_array(np, propname, out_value, 1);*在设备节点中搜索一个属性,并从中读取32位值到out_value(S)

*。成功执行将返回0
}
((bool ) itr->ptr_data) =of_property_read_bool(node, itr->dt_name);等
还有将

    qup_io = request_mem_region(qup_mem->start,resource_size(qup_mem),pdev->name);

request_mem_region 是申请 I/O 内存用的 . 申请了之后 , 还需要使用 ioremap 或者 ioremap_nocache 函数来映射 .对于 request_region, 三个参数 start,n,name 表示你想使用从 start 开始的 size 为 n 的 I/O port 资源 ,name 自然就是你的名字。
这样i2c的一个接口设备驱动就完成了,基础就有了。
重要的平台驱动注册。platform_driver_register
platform_driver_register()的注册过程:

1 platform_driver_register(&qup_i2c_driver)

2 driver_register(&drv->driver)

3 bus_add_driver(drv)

4 driver_attach(drv)

5 bus_for_each_dev(drv->bus, NULL, drv,__driver_attach)

6 __driver_attach(struct device * dev, void * data)

7 driver_probe_device(drv, dev)

8 really_probe(dev, drv)

在really_probe()中:为设备指派管理该设备的驱动:dev->driver = drv, 调用probe()函数初始化设备:drv->probe(dev)

这是完成平台驱动的注册和设备的probe。

sys/bus/platfrom的模型

//platform设备声明
struct device platform_bus = {
    .bus_id        = "platform",
};
EXPORT_SYMBOL_GPL(platform_bus);

//platform总线设备声明
struct bus_type platform_bus_type = {
    .name        = "platform",
    .dev_attrs    = platform_dev_attrs,
    .match        = platform_match,
    .uevent        = platform_uevent,
    .suspend    = platform_suspend,
    .suspend_late    = platform_suspend_late,
    .resume_early    = platform_resume_early,
    .resume        = platform_resume,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

这里定义了两个数据结构分别是
/sys/devices/platfrom/
/sys/bus/platfrom/


int __init platform_bus_init(void)
{
    int error;

    error = device_register(&platform_bus);//注册了"platform"的设备
    if (error)
        return error;
    error = bus_register(&platform_bus_type);//注册了叫"platform"的总线
    if (error)
        device_unregister(&platform_bus);
    return error;
}

有了总线就可以挂设备了。

//常用的platform_device_register,内部调用了platform_device_add,将设备挂在了platform总线上
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*/
int platform_device_register(struct platform_device *pdev)
{
    device_initialize(&pdev->dev);
    return platform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);

//这里在platform总线上挂设备
int platform_device_add(struct platform_device *pdev)
{
    int i, ret = 0;

    if (!pdev)
        return -EINVAL;

    if (!pdev->dev.parent)
        pdev->dev.parent = &platform_bus;//父设备设置为platform_bus

    pdev->dev.bus = &platform_bus_type;//设置挂在platform总线上

    if (pdev->id != -1)
        snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
             pdev->id);
    else
        strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

    for (i = 0; i < pdev->num_resources; i++) {
        struct resource *p, *r = &pdev->resource[i];

        if (r->name == NULL)
            r->name = pdev->dev.bus_id;

        p = r->parent;
        if (!p) {
            if (r->flags & IORESOURCE_MEM)
                p = &iomem_resource;
            else if (r->flags & IORESOURCE_IO)
                p = &ioport_resource;
        }

        if (p && insert_resource(p, r)) {
            printk(KERN_ERR
                   "%s: failed to claim resource %d\n",
                   pdev->dev.bus_id, i);
            ret = -EBUSY;
            goto failed;
        }
    }

    pr_debug("Registering platform device '%s'. Parent at %s\n",
         pdev->dev.bus_id, pdev->dev.parent->bus_id);

    ret = device_add(&pdev->dev);
    if (ret == 0)
        return ret;

failed:
    while (--i >= 0)
        if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
            release_resource(&pdev->resource[i]);
    return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);

blsp_core_init(blsp的初始化)
if (!pdata->use_gsbi_shared_mode)
判断是否使用gsbi
gsbi是物理上实际存在的部件,它可以模拟成i2c、gpio、spi、uart、sdio等,此i2c即是gsbi模拟的。这里顺便提下i2c adapter和i2c总线,两者都是物理上必须存在的,哪怕是其他部件模拟的,例如GSBI模拟I2C,那么GSBI这个部件是实际存在的。GSBI的走线会与所有i2c设备互连,GSBI的接口即是i2c adapter,与其他i2c设备互连的走线即是i2c总线。如果GSBI模拟成uart,那么与uart的rx/tx pin连接的走线即是uart串口线。这是高通平台的设计,换作其他平台,也一定会有一个i2c adapter或者说可以模拟成i2c adapter的物理部件存在。猜测i2c adapter位于片上系统中,类似I/O接口的东西,没有固件,驱动它的代码即是各平台相关的i2c adapter driver。GSBI的地址以及模拟成的qup i2c/uart的地址都是编址好的,在arch/arm/mach-msm/devices-msm8x60.c最开头可以看到这些物理地址。我们知道ARM是统一编址的,在代码中要用ioremap把它映射到虚拟内存,这样就可以访问了。

        gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                            "gsbi_qup_i2c_addr");
        if (!gsbi_mem) {
            dev_dbg(&pdev->dev, "Assume BLSP\n");
            /*
             * BLSP core does not need protocol programming so this
             * resource is not expected
             */
            goto blsp_core_init;
        }

也就是不是Gsbi时goto blsp_core_init;

  1. 获取clk;
  2. 获取pclk;
  3. 实例化一个dev(struct qup_i2c_dev)
  4. dev->dev = &pdev->dev;
  5. dev->err_irq = err_irq->start;
  6. dev->clk = clk;
  7. dev->pclk = pclk;
  8. dev->base = ioremap(qup_mem->start, resource_size(qup_mem));//映射到虚拟地址
  9. dev->i2c_gpios[i] = dt_gpios[i];//获取sda scl;
  10. platform_set_drvdata(pdev, dev);
    pdev是我们在probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方法,使用函数platform_set_drvdata()可以将pdev保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了。
  11. pdata = pdev->dev.platform_data;dev->pdata = pdata;
  12. ret = i2c_qup_clk_path_init(pdev, dev);
  13. ret = request_irq(dev->err_irq, qup_i2c_interrupt,IRQF_TRIGGER_HIGH, “qup_err_intr”, dev);
  14. disable_irq(dev->err_irq);
  15. i2c_set_adapdata(&dev->adapter, dev);
  16. dev->adapter.algo = &qup_i2c_algo;//等等
  17. ret = i2c_add_numbered_adapter(&dev->adapter); //i2c-core.c添加适配器是core层做的事。主要i2c_register_adapter(adap);
    关于i2c_register_adapter(adap)详解一下;
    适配器的存在就是一个设备的存在,按照希望的过程是在/sys/bus/下构建一个i2c的驱动模型框架。设备模型是希望一个功能可以通过某个设备和某个设备驱动probe成功,让驱动完成与应用层的交互。devices这个数据结构就是一个抽象(可以说是个类的概念)
    它就有着device_register(struct device *),它与sysfs的体现就是构建了devices的管理路径.
    INIT_LIST_HEAD(&adap->userspace_clients);// 初始化一个clients的双向链表;
    dev_set_name(&adap->dev, “i2c-%d”, adap->nr);//设置一个具体的适配器设备文件名。
    adap->dev.bus = &i2c_bus_type;
struct bus_type i2c_bus_type = {
    .name       = "i2c",// /sys/bus/i2c  设备模型的框
    .match      = i2c_device_match,//这个设备match函数
    .probe      = i2c_device_probe,//这个设备的probe函数
    .remove     = i2c_device_remove,//驱动与设备的移除
    .shutdown   = i2c_device_shutdown,//设备关闭
    .pm     = &i2c_device_pm_ops,//电源管理
};

adap->dev.type = &i2c_adapter_type;//这个设备(i2c适配器)的sysfs的接口应用

static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);
static struct attribute *i2c_adapter_attrs[] = {
    &dev_attr_name.attr,
    &dev_attr_new_device.attr,
    &dev_attr_delete_device.attr,
    NULL
};
static struct attribute_group i2c_adapter_attr_group = {
    .attrs      = i2c_adapter_attrs,
};
static const struct attribute_group *i2c_adapter_attr_groups[] = {
    &i2c_adapter_attr_group,
    NULL
};
struct device_type i2c_adapter_type = {
    .groups     = i2c_adapter_attr_groups,
    .release    = i2c_adapter_dev_release,
};

res = device_register(&adap->dev); //设备的注册
(难点)

    /* 相应的代码位于i2c-core.c:create pre-declared device nodes */
    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);.......
struct i2c_devinfo {
    struct list_head    list;
    int         busnum;
    struct i2c_board_info   board_info;
};
struct i2c_board_info {
    char        type[I2C_NAME_SIZE];
    unsigned short  flags;
    unsigned short  addr;
    void        *platform_data;
    struct dev_archdata *archdata;
    struct device_node *of_node;
    int     irq;
};      
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
    struct i2c_devinfo  *devinfo;
    down_read(&__i2c_board_lock);
    list_for_each_entry(devinfo, &__i2c_board_list, list) {
        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_scan_static_board_info()函数中遍历I2C设备链表__i2c_board_list,设备的总线号和adapter(nr)的总线号相等,
回顾下在设备树中获取总线号的方法,当然也是配置的方法:
{“cell-index”, &pdev->id, DT_REQUIRED, DT_U32, -1},
adapter->nr = pdev->id;
dev_set_name(&adap->dev, “i2c-%d”, adap->nr);
至于I2C设备链表的创建和填充是在

int of_platform_populate(struct device_node *root,
            const struct of_device_id *matches,
            const struct of_dev_auxdata *lookup,
            struct device *parent)
{
    struct device_node *child;
    int rc = 0;

    root = root ? of_node_get(root) : of_find_node_by_path("/");
    if (!root)
        return -EINVAL;
    for_each_child_of_node(root, child) {   //遍历设备树
        rc = of_platform_bus_create(child, matches, lookup, parent, true);
        if (rc)
            break;
    }
    of_node_put(root);
    return rc;
}
*创建一个platform_device为device_node
*递归为所有子节点创建设备。
static int of_platform_bus_create(struct device_node *bus,
                  const struct of_device_id *matches,
                  const struct of_dev_auxdata *lookup,
                  struct device *parent, bool strict)
{
    const struct of_dev_auxdata *auxdata;
    struct device_node *child;
    struct platform_device *dev;
    const char *bus_id = NULL;
    void *platform_data = NULL;
    int rc = 0;

    /* Make sure it has a compatible property */
    if (strict && (!of_get_property(bus, "compatible", NULL))) {
        pr_debug("%s() - skipping %s, no compatible prop\n",
             __func__, bus->full_name);
        return 0;
    }

    auxdata = of_dev_lookup(lookup, bus);
    if (auxdata) {
        bus_id = auxdata->name;
        platform_data = auxdata->platform_data;
    }

    if (of_device_is_compatible(bus, "arm,primecell")) {
        of_amba_device_create(bus, bus_id, platform_data, parent);
        return 0;
    }

    dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
    if (!dev || !of_match_node(matches, bus))
        return 0;

    for_each_child_of_node(bus, child) {
        pr_debug("   create child: %s\n", child->full_name);
        rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
        if (rc) {
            of_node_put(child);
            break;
        }
    }
    return rc;
}
在适配器probe时如果是设备树的方式注册。
if (dev->dev->of_node) {
            dev->adapter.dev.of_node = pdev->dev.of_node;
            of_i2c_register_devices(&dev->adapter);
        }
void of_i2c_register_devices(struct i2c_adapter *adap)
{
    void *result;
    struct device_node *node;

    /* Only register child devices if the adapter has a node pointer set */
    if (!adap->dev.of_node)
        return;

    dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");

    for_each_child_of_node(adap->dev.of_node, node) {
        struct i2c_board_info info = {};
        struct dev_archdata dev_ad = {};
        const __be32 *addr;
        int len;

        dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);

        if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
            dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
                node->full_name);
            continue;
        }

        addr = of_get_property(node, "reg", &len);
        if (!addr || (len < sizeof(int))) {
            dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
                node->full_name);
            continue;
        }

        info.addr = be32_to_cpup(addr);
        if (info.addr > (1 << 10) - 1) {
            dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
                info.addr, node->full_name);
            continue;
        }
        info.irq = irq_of_parse_and_map(node, 0);
        info.of_node = of_node_get(node);
        info.archdata = &dev_ad;
        request_module("%s%s", I2C_MODULE_PREFIX, info.type);
        result = i2c_new_device(adap, &info);
        if (result == NULL) {
            dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
                    node->full_name);
            of_node_put(node);
            irq_dispose_mapping(info.irq);
            continue;
        }
    }
}

如果使用board_info 的方式则是遍历devices链表,当设备的busnum等于这个i2c的总线号就确认这个设备是挂在这i2c的总线上的。
最后都是使用函数i2c_new_device()创建该设备。

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;

    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;

    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;
    client->dev.bus = &i2c_bus_type;
    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;
}

通过struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);
传入的适配器和i2c_board_info构建成返回一个i2c_client,(如果返回NULL 一般就是地址的有误或冲突)
client->dev.parent = &client->adapter->dev;//将一个设备的父容器dev设置为其适配器的设备容器
client->dev.bus = &i2c_bus_type;//设备总线描述(在bus/i2c/{下的设备}devices/)
client->dev.type = &i2c_client_type;//设备类型描述(att『属性』,uevent,release等)
client->dev.of_node = info->of_node;//设备树的节点,类似pdate,
dev_set_name(&client->dev, “%d-%04x”, i2c_adapter_id(adap),
client->addr | ((client->flags & I2C_CLIENT_TEN)? 0xa000 : 0));//设备的命名 如:
/sys/bus/i2c/devices/1-0038/
通过设备容器的嵌套:1-0038 -> ../../../devices/f9923000.i2c/i2c-1/1-0038
也就是实际上sys/bus/i2c/devices/* 是/sys/devices/的链接文件。
status = device_register(&client->dev);//完成上面的设置。
这就完成了一个client。而这个client构建的时候也是具有了适配器,算法,等。

注意:
*bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
新的适配器加入内核时调用函数 bus_for_each_drv时调用的函数。
完成当一个新的适配器加入内核时,会在总线类型为i2c_bus_type的驱动中找与之匹配的,
并将驱动支持的设备插入新加入的这个适配器。
函数bus_for_each_dev() 和 bus_for_each_drv()分别用于遍历bus上devices和drivers链表中的所有元素。*
这部分在已经废弃了,不在建议使用
/ *通知BUS的机制,一个新的公共bus已经出现,它即将
*删除。你应该避免使用这个,不久的将来它将被删除
* /
int (attach_adapter)(struct i2c_adapter ) __deprecated;
int (detach_adapter)(struct i2c_adapter ) __deprecated;

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_driver {
    unsigned int class;

    /* Notifies the driver that a new bus has appeared or is about to be
     * removed. You should avoid using this, it will be removed in a
     * near future.
     */
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;
    int (*detach_adapter)(struct i2c_adapter *) __deprecated;

    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);

    /* driver model interfaces that don't relate to enumeration  */
    void (*shutdown)(struct i2c_client *);
    int (*suspend)(struct i2c_client *, pm_message_t mesg);
    int (*resume)(struct i2c_client *);

    /* Alert callback, for example for the SMBus alert protocol.
     * The format and meaning of the data value depends on the protocol.
     * For the SMBus alert protocol, there is a single bit of data passed
     * as the alert response's low bit ("event flag").
     */
    void (*alert)(struct i2c_client *, unsigned int data);

    /* a ioctl like command that can be used to perform specific functions
     * with the device.
     */
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

    struct device_driver driver;
    const struct i2c_device_id *id_table;

    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
};
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;
};

struct i2c_device_id {
    char name[I2C_NAME_SIZE];    //它由于对比具体的client的 name于 设备的驱动probe
    kernel_ulong_t driver_data  /* Data private to the driver */
            __attribute__((aligned(sizeof(kernel_ulong_t))));
};

一、关于client与设备驱动的匹配
到内核,这些设备client的name在该驱动的id_table中。
老的内核还是通过函数driver->detect便是填充结构体i2c_board_info,用i2c_board_info去初始化
client,驱动id_table中的name就是i2c_board_info->type带入client的。
该驱动与设备就是根据这个name相匹配的,匹配后调用函数probe执行一些初始化或是设备探测。
当然该驱动也是可以不携带地址信息的,而是注册后与内核中已存在的client匹配执行probe()。
“legacy”driver是不携带地址信息的,它的主要任务是调用函数driver->detach_adapter为新加入
的适配器,或是与之匹配的适配器创建一个可依附的设备节点,或者是做其他初始化工作。
新的内核就通过设备树的方式,将一个适配器和它附属的设备顺序的完成clinet的创建。
驱动实际只需要要匹配设备name就可以probe,和使用设备依赖的适配器的算法和设备数据。
编写一个设备驱动就简单的很多。
如:
module_init(goodix_ts_init); //调用等级为7.

static struct i2c_driver goodix_ts_driver = {
    .probe      = goodix_ts_probe,
    .remove     = goodix_ts_remove,
    .id_table   = goodix_ts_id,   //I2C设备支持的驱动程序列表 /sys/bus/i2c/drivers/XXXX
    .driver = {
        .name     = GTP_I2C_NAME,
        .owner    = THIS_MODULE,
#ifdef CONFIG_OF
        .of_match_table = goodix_match_table,
#endif
#if !defined(CONFIG_FB) && defined(CONFIG_PM)
        .pm       = &gtp_pm_ops,
#endif
    },
};

定义一个i2c_driver的数据结构;

static const struct of_device_id goodix_match_table[] = {
        {.compatible = "goodix,gt915l",}, 
        { },
};

of_device_id为了实现于client的probe。(client->name)///sys/bus/i2c/devices/1-005d/name
驱动可以驱动多个设备,但是设备被一个驱动服务。
ret = i2c_add_driver(&goodix_ts_driver);//调用一个add函数,它具体完成i2c设备的内核注册。

#define i2c_add_driver(driver) \
    i2c_register_driver(THIS_MODULE, driver)//他是core层代码。

driver->driver.owner = owner; //THIS_MODULE
driver->driver.bus = &i2c_bus_type; //sys/bus/i2c/driver/XXXX
res = driver_register(&driver->driver); //类似device_register();
INIT_LIST_HEAD(&driver->clients); //初始化client的链表头。
i2c_for_each_dev(driver, __process_new_driver); //find已经存在的适配器 //新的内核已经废弃。
static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
client :传递的适配器。
id :传递驱动(name ,data)。
probe成功后只需要保存这个client的数据结构,就可以操作具体的设备了。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值