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结构体中,