/* * If the transfer needs to abort for some reason, we'll try to * force a stop condition to clear any pending bus conditions
*/
writel(LPC24XX_STO, i2c->base + LPC24XX_I2CONSET);
/* Wait for status change */ while (readl(i2c->base + LPC24XX_I2STAT) != M_I2C_IDLE) { if (time_after(jiffies, timeout)) { /* Bus was not idle, try to reset adapter */
i2c_lpc2k_reset(i2c); return -EBUSY;
}
/* * I2C in the LPC2xxx series is basically a state machine. * Just run through the steps based on the current status.
*/
status = readl(i2c->base + LPC24XX_I2STAT);
switch (status) { case M_START: case M_REPSTART: /* Start bit was just sent out, send out addr and dir */
data = i2c_8bit_addr_from_msg(i2c->msg);
case MX_ADDR_W_ACK: case MX_DATA_W_ACK: /* * Address or data was sent out with an ACK. If there is more * data to send, send it now
*/ if (i2c->msg_idx < i2c->msg->len) {
writel(i2c->msg->buf[i2c->msg_idx],
i2c->base + LPC24XX_I2DAT);
} elseif (i2c->is_last) { /* Last message, send stop */
writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET);
writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
i2c->msg_status = 0;
disable_irq_nosync(i2c->irq);
} else {
i2c->msg_status = 0;
disable_irq_nosync(i2c->irq);
}
i2c->msg_idx++; break;
case MR_ADDR_R_ACK: /* Receive first byte from target */ if (i2c->msg->len == 1) { /* Last byte, return NACK */
writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONCLR);
} else { /* Not last byte, return ACK */
writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONSET);
}
case MR_DATA_R_NACK: /* * The I2C shows NACK status on reads, so we need to accept * the NACK as an ACK here. This should be ok, as the real * BACK would of been caught on the address write.
*/ case MR_DATA_R_ACK: /* Data was received */ if (i2c->msg_idx < i2c->msg->len) {
i2c->msg->buf[i2c->msg_idx] =
readl(i2c->base + LPC24XX_I2DAT);
}
/* If transfer is done, send STOP */ if (i2c->msg_idx >= i2c->msg->len - 1 && i2c->is_last) {
writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET);
writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
i2c->msg_status = 0;
}
/* Message is done */ if (i2c->msg_idx >= i2c->msg->len - 1) {
i2c->msg_status = 0;
disable_irq_nosync(i2c->irq);
}
/* * One pre-last data input, send NACK to tell the target that * this is going to be the last data byte to be transferred.
*/ if (i2c->msg_idx >= i2c->msg->len - 2) { /* One byte left to receive - NACK */
writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONCLR);
} else { /* More than one byte left to receive - ACK */
writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONSET);
}
/* Exit on failure or all bytes transferred */ if (i2c->msg_status != -EBUSY)
wake_up(&i2c->wait);
/* * If `msg_status` is zero, then `lpc2k_process_msg()` * is responsible for clearing the SI flag.
*/ if (i2c->msg_status != 0)
writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
}
staticint lpc2k_process_msg(struct lpc2k_i2c *i2c, int msgidx)
{ /* A new transfer is kicked off by initiating a start condition */ if (!msgidx) {
writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONSET);
} else { /* * A multi-message I2C transfer continues where the * previous I2C transfer left off and uses the * current condition of the I2C adapter.
*/ if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) {
WARN_ON(i2c->msg->len == 0);
if (!(i2c->msg->flags & I2C_M_RD)) { /* Start transmit of data */
writel(i2c->msg->buf[0],
i2c->base + LPC24XX_I2DAT);
i2c->msg_idx++;
}
} else { /* Start or repeated start */
writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONSET);
}
/* Wait for transfer completion */ if (wait_event_timeout(i2c->wait, i2c->msg_status != -EBUSY,
msecs_to_jiffies(1000)) == 0) {
disable_irq_nosync(i2c->irq);
return -ETIMEDOUT;
}
return i2c->msg_status;
}
staticint i2c_lpc2k_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int msg_num)
{ struct lpc2k_i2c *i2c = i2c_get_adapdata(adap); int ret, i;
u32 stat;
/* Check for bus idle condition */
stat = readl(i2c->base + LPC24XX_I2STAT); if (stat != M_I2C_IDLE) { /* Something is holding the bus, try to clear it */ return i2c_lpc2k_clear_arb(i2c);
}
/* Process a single message at a time */ for (i = 0; i < msg_num; i++) { /* Save message pointer and current message data index */
i2c->msg = &msgs[i];
i2c->msg_idx = 0;
i2c->msg_status = -EBUSY;
i2c->is_last = (i == (msg_num - 1));
ret = lpc2k_process_msg(i2c, i); if (ret) return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.