Synopsys DesignWareI2C master 数据的发送和接收

来源:互联网 发布:java设计模式场景 编辑:程序博客网 时间:2024/06/06 00:47
在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;
}

原创粉丝点击