qcom I2C driver i2c-msm-v2.c code analysis

I2C client通过接口逐步的调用到I2C driver的master_xfer函数,I2C driver中实现master_xfer()

static const struct i2c_algorithm i2c_msm_frmwrk_algrtm = {
        .master_xfer    = i2c_msm_frmwrk_xfer,
        .functionality  = i2c_msm_frmwrk_func,
};
static int
i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
        int ret = 0;
        struct i2c_msm_ctrl      *ctrl = i2c_get_adapdata(adap);
        struct i2c_msm_xfer      *xfer = &ctrl->xfer;

        ret = i2c_msm_pm_xfer_start(ctrl);//pin control,enable clock,configuration for i2c
        if (ret)
                return ret;

        /* init xfer */
        xfer->msgs         = msgs;
        xfer->msg_cnt      = num;
        xfer->mode_id      = I2C_MSM_XFER_MODE_NONE;
        xfer->err          = 0;
        xfer->rx_cnt       = 0;
        xfer->tx_cnt       = 0;
        xfer->rx_ovrhd_cnt = 0;
        xfer->tx_ovrhd_cnt = 0;
        atomic_set(&xfer->event_cnt, 0);
        init_completion(&xfer->complete);
        xfer->cur_buf.is_init = false;
        xfer->cur_buf.msg_idx = 0;

        i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_XFER_BEG, num,
                                                                msgs->addr, 0);// write num,addr,0 into ctrl->xfer->event.data0,1,2  msg->addr == client.addr
        //event type seems important,but just print some information ?
        i2c_msm_xfer_scan(ctrl);//is_last_buff, inital buff count...  and add 0x0 or 0x1 to slave addr,ensure read or write. create tag.
        i2c_msm_xfer_calc_timeout(ctrl);//calculate timeout : formula : freq,bit count...
        xfer->mode_id = i2c_msm_qup_choose_mode(ctrl);//block,dma,fifo

        dev_dbg(ctrl->dev, "xfer() mode:%d msg_cnt:%d rx_cbt:%zu tx_cnt:%zu\n",
                xfer->mode_id, xfer->msg_cnt, xfer->rx_cnt, xfer->tx_cnt);

        switch (xfer->mode_id) {
        case I2C_MSM_XFER_MODE_FIFO:
                ret = i2c_msm_fifo_xfer(ctrl);
                break;
        case I2C_MSM_XFER_MODE_BLOCK:
                ret = i2c_msm_blk_xfer(ctrl);
                break;
        case I2C_MSM_XFER_MODE_DMA:
                ret = i2c_msm_dma_xfer(ctrl);
                break;
        default:
                ret = -EINTR;
        };

        i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_SCAN_SUM,
                ((xfer->rx_cnt & 0xff) | ((xfer->rx_ovrhd_cnt & 0xff) << 16)),
                ((xfer->tx_cnt & 0xff) | ((xfer->tx_ovrhd_cnt & 0xff) << 16)),
                ((ctrl->xfer.timeout & 0xfff) | ((xfer->mode_id & 0xf) << 24)));

        ret = i2c_msm_qup_post_xfer(ctrl, ret);//check error : timeout or NACK
        /* on success, return number of messages sent (which is index + 1)*/
        if (!ret)
                ret = xfer->cur_buf.msg_idx + 1;

        i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_XFER_END, ret, xfer->err,
                                                xfer->cur_buf.msg_idx + 1);
        /* process and dump profiling data */
        if (xfer->err || (ctrl->dbgfs.dbg_lvl >= MSM_PROF))
                i2c_msm_prof_evnt_dump(ctrl);

        i2c_msm_pm_xfer_end(ctrl);
        return ret;
}

其中函数i2c_msm_pm_xfer_start(ctrl)完成pin的状态控制(sleep,active),时钟的enable以及I2C的初始化配置等工作,并且enable_irq,当传输的时候发生了错误(NAK,bus error,can’t find slave…),controller会触发该中断,转入中断处理函数,读取寄存器的值将当前发生的是那一种错误取值出来,留做之后的处理。

static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl)
{
        int ret;
        struct i2c_msm_xfer *xfer = &ctrl->xfer;
        mutex_lock(&ctrl->xfer.mtx);

        /* if system is suspended just bail out */
        /*if the system state of power was suspend,then return.but if the state of power was inactive.then active it by excute function xx_resume*/
        if (ctrl->pwr_state == I2C_MSM_PM_SYS_SUSPENDED) {
                struct i2c_msg *msgs = xfer->msgs + xfer->cur_buf.msg_idx;
                dev_err(ctrl->dev,
                                "slave:0x%x is calling xfer when system is suspended\n",
                                msgs->addr);
                mutex_unlock(&ctrl->xfer.mtx);
                return -EIO;
        }

        pm_runtime_get_sync(ctrl->dev);
        /*
         * if runtime PM callback was not invoked (when both runtime-pm
         * and systme-pm are in transition concurrently)
         */
        if (ctrl->pwr_state != I2C_MSM_PM_RT_ACTIVE) {
                dev_info(ctrl->dev, "Runtime PM-callback was not invoked.\n");
                i2c_msm_pm_resume(ctrl->dev);
        }

        ret = i2c_msm_pm_clk_prepare_enable(ctrl);
        if (ret) {
                mutex_unlock(&ctrl->xfer.mtx);
                return ret;
        }
        i2c_msm_qup_init(ctrl);//configuration for i2c status.

        /* Set xfer to active state (efectively enabling our ISR)*/
        atomic_set(&ctrl->xfer.is_active, 1);//I think it's just a flag.

        enable_irq(ctrl->rsrcs.irq);
        return 0;
}
ret = request_irq(irq, i2c_msm_qup_isr, IRQF_TRIGGER_HIGH,
                                                "i2c-msm-v2-irq", ctrl);
static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
{
        struct i2c_msm_ctrl *ctrl = devid;
        void __iomem        *base = ctrl->rsrcs.base;
        struct i2c_msm_xfer *xfer = &ctrl->xfer;
        struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk;
        u32  i2c_status = 0;
        u32  err_flags  = 0;
        u32  qup_op     = 0;
        u32  clr_flds   = 0;
        bool log_event       = false;
        bool signal_complete = false;
        bool need_wmb        = false;

        i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_IRQ_BGN, irq, 0, 0);//zero for corl->xfer->event.data

        if (!atomic_read(&ctrl->xfer.is_active)) {
                dev_info(ctrl->dev, "irq:%d when no active transfer\n", irq);
                return IRQ_HANDLED;
        }
        i2c_status  = readl_relaxed(base + QUP_I2C_STATUS);
        err_flags   = readl_relaxed(base + QUP_ERROR_FLAGS);
        qup_op      = readl_relaxed(base + QUP_OPERATIONAL);

        if (i2c_status & QUP_MSTR_STTS_ERR_MASK) {//has been generated a interrupt : invalid write,arbition lost,packet nacked,bus error.
                signal_complete = true;
                log_event       = true;
                /*
                 * If there is more than 1 error here, last one sticks.
                 * The order of the error set here matters.
                 */
                if (i2c_status & QUP_ARB_LOST)//when the controller loses arbitration for the bus.
                        ctrl->xfer.err = I2C_MSM_ERR_ARB_LOST;

                if (i2c_status & QUP_BUS_ERROR)//when an unexpected START or STOP condition is detected. 
                        ctrl->xfer.err = I2C_MSM_ERR_BUS_ERR;

                if (i2c_status & QUP_PACKET_NACKED)//when a NACK is received from a slave
                        ctrl->xfer.err = I2C_MSM_ERR_NACK;
        }

        /* check for FIFO over/under runs error */
        if (err_flags & QUP_ERR_FLGS_MASK)//input or output was full or empty in specfic situation.
                ctrl->xfer.err = I2C_MSM_ERR_OVR_UNDR_RUN;

        /* Dump the register values before reset the core */
        if (ctrl->xfer.err && ctrl->dbgfs.dbg_lvl >= MSM_DBG)
                i2c_msm_dbg_qup_reg_dump(ctrl);

        /* clear interrupts fields */
        clr_flds = i2c_status & QUP_MSTR_STTS_ERR_MASK;//except for error bit,the other bit is cleard.  why?
        if (clr_flds) {
                writel_relaxed(clr_flds, base + QUP_I2C_STATUS);
                need_wmb = true;
        }

        clr_flds = err_flags & QUP_ERR_FLGS_MASK;
        if (clr_flds) {
                writel_relaxed(clr_flds,  base + QUP_ERROR_FLAGS);
                need_wmb = true;
        }

        clr_flds = qup_op & (QUP_OUTPUT_SERVICE_FLAG | QUP_INPUT_SERVICE_FLAG);
        if (clr_flds) {
                writel_relaxed(clr_flds, base + QUP_OPERATIONAL);
                need_wmb = true;
        }

        if (need_wmb)
                /*
                 * flush writes that clear the interrupt flags before changing
                 * state to reset.
                 */
                wmb();

        /* Reset and bail out on error */
        if (ctrl->xfer.err) {
                /* Flush for the tags in case of an error and DMA Mode*/
                if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_DMA) {
                        writel_relaxed(QUP_I2C_FLUSH, ctrl->rsrcs.base
                                                                + QUP_STATE);
                        /*
                         * Ensure that QUP_I2C_FLUSH is written before
                         * State reset
                         */
                        wmb();
                }

                /* HW workaround: when interrupt is level triggerd, more
                 * than one interrupt may fire in error cases. Thus we
                 * change the QUP core state to Reset immediately in the
                 * ISR to ward off the next interrupt.
                 */
                //ward off : avoid
                writel_relaxed(QUP_STATE_RESET, ctrl->rsrcs.base + QUP_STATE);

                signal_complete = true;
                log_event       = true;
                goto isr_end;
        }

        /* handle data completion */
        if (xfer->mode_id == I2C_MSM_XFER_MODE_BLOCK) {
                /* block ready for writing */
                if (qup_op & QUP_OUTPUT_SERVICE_FLAG) {
                        log_event = true;
                        if (qup_op & QUP_OUT_BLOCK_WRITE_REQ)//QUP output FIFO needs BLOCK_SIZE_OUTPUT amount of data to be written
                                complete(&blk->wait_tx_blk);

                        if ((qup_op & blk->complete_mask)
                                        == blk->complete_mask) {
                                log_event       = true;
                                signal_complete = true;
                        }
                }
                /* block ready for reading */
                if (qup_op & QUP_INPUT_SERVICE_FLAG) {//software will read the data.
                        log_event = true;
                        complete(&blk->wait_rx_blk);
                }
        } else {
                /* for FIFO/DMA Mode*/
                if (qup_op & QUP_MAX_INPUT_DONE_FLAG) {//When set by hardware, indicates QUP input FIFO has reached the programmed QUP_MX_INPUT_COUNT value.
                        log_event = true;
                        /*
                         * If last transaction is an input then the entire
                         * transfer is done
                         */
                        if (ctrl->xfer.last_is_rx)//full and ready to read from input fifo.
                                signal_complete = true;
                }
                /*
                 * Ideally, would like to check QUP_MAX_OUTPUT_DONE_FLAG.
                 * However, QUP_MAX_OUTPUT_DONE_FLAG is lagging behind
                 * QUP_OUTPUT_SERVICE_FLAG. The only reason for
                 * QUP_OUTPUT_SERVICE_FLAG to be set in FIFO mode is
                 * QUP_MAX_OUTPUT_DONE_FLAG condition. The code checking
                 * here QUP_OUTPUT_SERVICE_FLAG and assumes that
                 * QUP_MAX_OUTPUT_DONE_FLAG.
                 */
                if (qup_op & (QUP_OUTPUT_SERVICE_FLAG |
                                                QUP_MAX_OUTPUT_DONE_FLAG)) {//will read the data | reached the max programed amount.
                        log_event = true;
                        /*
                         * If last transaction is an output then the
                         * entire transfer is done
                         */
                        if (!ctrl->xfer.last_is_rx)//have already full, so if last is still rx,and then signal_complete = true.
                                signal_complete = true;
                }//when fifo is full,we should read the data from fifo.
        }

isr_end:
        if (ctrl->xfer.err || (ctrl->dbgfs.dbg_lvl >= MSM_DBG))
                i2c_msm_dbg_dump_diag(ctrl, true, i2c_status, qup_op);

        if (log_event || (ctrl->dbgfs.dbg_lvl >= MSM_DBG))
                i2c_msm_prof_evnt_add(ctrl, MSM_PROF,
                                        I2C_MSM_IRQ_END,
                                        i2c_status, qup_op, err_flags);

        if (signal_complete)
                complete(&ctrl->xfer.complete);

        return IRQ_HANDLED;
}

将传递进来的msg信息保存在xfer结构体中,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值