/** * cdns_i2c_master_xfer - The main i2c transfer function * @adap: pointer to the i2c adapter driver instance * @msgs: pointer to the i2c message structure * @num: the number of messages to transfer * * Initiates the send/recv activity based on the transfer message received. * * Return: number of msgs processed on success, negative error otherwise */ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { int ret, count; u32 reg; struct cdns_i2c *id = adap->algo_data; /* Check if the bus is free */ if (msgs->len) if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA) return -EAGAIN; /* * Set the flag to one when multiple messages are to be * processed with a repeated start. */ if (num > 1) { /* * This controller does not give completion interrupt after a * master receive message if HOLD bit is set (repeated start), * resulting in SW timeout. Hence, if a receive message is * followed by any other message, an error is returned * indicating that this sequence is not supported. */ for (count = 0; count < num - 1; count++) { if (msgs[count].flags & I2C_M_RD) { dev_warn(adap->dev.parent, "Can't do repeated start after a receive message\n"); return -EOPNOTSUPP; } } id->bus_hold_flag = 1; reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); reg |= CDNS_I2C_CR_HOLD; cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET); } else { id->bus_hold_flag = 0; } /* Process the msg one by one */ for (count = 0; count < num; count++, msgs++) { if (count == (num - 1)) id->bus_hold_flag = 0; ret = cdns_i2c_process_msg(id, msgs, adap); if (ret) return ret; /* Report the other error interrupts to application */ if (id->err_status) { cdns_i2c_master_reset(adap); if (id->err_status & CDNS_I2C_IXR_NACK) return -ENXIO; return -EIO; } } return num; }
static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg, struct i2c_adapter *adap) { unsigned long time_left; u32 reg; id->p_msg = msg; id->err_status = 0; reinit_completion(&id->xfer_done); /* Check for the TEN Bit mode on each msg */ reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); if (msg->flags & I2C_M_TEN) { if (reg & CDNS_I2C_CR_NEA) cdns_i2c_writereg(reg & ~CDNS_I2C_CR_NEA, CDNS_I2C_CR_OFFSET); } else { if (!(reg & CDNS_I2C_CR_NEA)) cdns_i2c_writereg(reg | CDNS_I2C_CR_NEA, CDNS_I2C_CR_OFFSET); } /* Check for zero lenght - Slave monitor mode */ if (msg->len == 0) cdns_i2c_slvmon(id); /* Check for the R/W flag on each msg */ else if (msg->flags & I2C_M_RD) cdns_i2c_mrecv(id); else cdns_i2c_msend(id); /* Wait for the signal of completion */ time_left = wait_for_completion_timeout(&id->xfer_done, adap->timeout); if (time_left == 0) { cdns_i2c_master_reset(adap); dev_err(id->adap.dev.parent, "timeout waiting on completion\n"); return -ETIMEDOUT; } cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET); /* If it is bus arbitration error, try again */ if (id->err_status & CDNS_I2C_IXR_ARB_LOST) return -EAGAIN; return 0; }
/** * cdns_i2c_master_xfer - The main i2c transfer function * @adap: pointer to the i2c adapter driver instance * @msgs: pointer to the i2c message structure * @num: the number of messages to transfer * * Initiates the send/recv activity based on the transfer message received. * * Return: number of msgs processed on success, negative error otherwise */ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { int ret, count; u32 reg; struct cdns_i2c *id = adap->algo_data; /* Check if the bus is free */ if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA) return -EAGAIN; /* * Set the flag to one when multiple messages are to be * processed with a repeated start. */ if (num > 1) { id->bus_hold_flag = 1; reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); reg |= CDNS_I2C_CR_HOLD; cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET); } else { id->bus_hold_flag = 0; } /* Process the msg one by one */ for (count = 0; count < num; count++, msgs++) { if (count == (num - 1)) id->bus_hold_flag = 0; ret = cdns_i2c_process_msg(id, msgs, adap); if (ret) return ret; /* Report the other error interrupts to application */ if (id->err_status) { cdns_i2c_master_reset(adap); if (id->err_status & CDNS_I2C_IXR_NACK) return -ENXIO; return -EIO; } } return num; }