您的位置:首页 > 运维架构

Synopsys DesignWareI2C master 数据的发送和接收

2017-06-20 09:32 155 查看
在i2c_dw_probe 中会对i2c_adapter的重要成员变量algo赋值

int i2c_dw_probe(struct dw_i2c_dev *dev)

{

    struct i2c_adapter *adap = &dev->adapter;

    int r;

    init_completion(&dev->cmd_complete);

    r = i2c_dw_init(dev);

    if (r)

        return r;

    snprintf(adap->name, sizeof(adap->name),

         "Synopsys DesignWare I2C adapter");

    adap->retries = 3;

    adap->algo = &i2c_dw_algo;

    adap->dev.parent = dev->dev;

    i2c_set_adapdata(adap, dev);

}

这里的i2c_dw_algo 定义如下:

static struct i2c_algorithm i2c_dw_algo = {

    .master_xfer    = i2c_dw_xfer,

    .functionality    = i2c_dw_func,

};

这样在i2c_detect_address 中就会调用i2c_default_probe->i2c_smbus_xfer->i2c_smbus_xfer_emulated->i2c_transfer->__i2c_transfer

int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

{

    unsigned long orig_jiffies;

    int ret, try;

    if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))

        return -EOPNOTSUPP;

    /* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets

     * enabled.  This is an efficient way of keeping the for-loop from

     * being executed when not needed.

     */

    if (static_key_false(&i2c_trace_msg)) {

        int i;

        for (i = 0; i < num; i++)

            if (msgs[i].flags & I2C_M_RD)

                trace_i2c_read(adap, &msgs[i], i);

            else

                trace_i2c_write(adap, &msgs[i], i);

    }

    /* Retry automatically on arbitration loss */

    orig_jiffies = jiffies;

    for (ret = 0, try = 0; try <= adap->retries; try++) {

//关键的一句这里的algo->master_xfer 就是对应i2c_dw_algo中的master_xfer

        ret = adap->algo->master_xfer(adap, msgs, num);

        if (ret != -EAGAIN)

            break;

        if (time_after(jiffies, orig_jiffies + adap->timeout))

            break;

    }

}

所以在__i2c_transfer 中最终调用master_xfer来发送数据

static int

i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)

{

    struct dw_i2c_dev *dev = i2c_get_adapdata(adap);

    int ret;

    dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);

    pm_runtime_get_sync(dev->dev);

//重新初始化完成量,从这里也可以看出完成量是一次性的,第二次使用之前必须调用reinit_completion 来重新初始化

    reinit_completion(&dev->cmd_complete);

    dev->msgs = msgs;

    dev->msgs_num = num;

    dev->cmd_err = 0;

    dev->msg_write_idx = 0;

    dev->msg_read_idx = 0;

    dev->msg_err = 0;

    dev->status = STATUS_IDLE;

    dev->abort_source = 0;

    dev->rx_outstanding = 0;

    ret = i2c_dw_acquire_lock(dev);

    if (ret)

        goto done_nolock;

    ret = i2c_dw_wait_bus_not_busy(dev);

    if (ret < 0)

        goto done;

// 发送数据,这里主要设置平台相关的寄存器

    /* start the transfers */

    i2c_dw_xfer_init(dev);

//等待释放完成量

    /* wait for tx to complete */

    if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) {

        dev_err(dev->dev, "controller timed out\n");

        /* i2c_dw_init implicitly disables the adapter */

        i2c_dw_init(dev);

        ret = -ETIMEDOUT;

        goto done;

    }

    /*

     * We must disable the adapter before returning and signaling the end

     * of the current transfer. Otherwise the hardware might continue

     * generating interrupts which in turn causes a race condition with

     * the following transfer.  Needs some more investigation if the

     * additional interrupts are a hardware bug or this driver doesn't

     * handle them correctly yet.

     */

    __i2c_dw_enable(dev, false);

    if (dev->msg_err) {

        ret = dev->msg_err;

        goto done;

    }

    return ret;

}

那完成量是在哪里释放的呢?

答案是在中断的处理函数i2c_dw_isr

static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)

{

    struct dw_i2c_dev *dev = dev_id;

    u32 stat, enabled;

    enabled = dw_readl(dev, DW_IC_ENABLE);

    stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);

    dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__, enabled, stat);

    if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))

        return IRQ_NONE;

    stat = i2c_dw_read_clear_intrbits(dev);

    if (stat & DW_IC_INTR_TX_ABRT) {

        dev->cmd_err |= DW_IC_ERR_TX_ABRT;

        dev->status = STATUS_IDLE;

        /*

         * Anytime TX_ABRT is set, the contents of the tx/rx

         * buffers are flushed.  Make sure to skip them.

         */

        dw_writel(dev, 0, DW_IC_INTR_MASK);

        goto tx_aborted;

    }

    if (stat & DW_IC_INTR_RX_FULL)

        i2c_dw_read(dev);

    if (stat & DW_IC_INTR_TX_EMPTY)

        i2c_dw_xfer_msg(dev);

    /*

     * No need to modify or disable the interrupt mask here.

     * i2c_dw_xfer_msg() will take care of it according to

     * the current transmit status.

     */

tx_aborted:

    //正常情况下这里就会释放完成量

    if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)

        complete(&dev->cmd_complete);

    else if (unlikely(dev->accessor_flags & ACCESS_INTR_MASK)) {

        /* workaround to trigger pending interrupt */

        stat = dw_readl(dev, DW_IC_INTR_MASK);

        i2c_dw_disable_int(dev);

        dw_writel(dev, stat, DW_IC_INTR_MASK);

    }

    return IRQ_HANDLED;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: