drivers/i2c/busses/i2c-lpc2k.c

Source file repositories/reference/linux-study-clean/drivers/i2c/busses/i2c-lpc2k.c

File Facts

System
Linux kernel
Corpus path
drivers/i2c/busses/i2c-lpc2k.c
Extension
.c
Size
11928 bytes
Lines
483
Domain
Driver Families
Bucket
drivers/i2c
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct lpc2k_i2c {
	void __iomem		*base;
	struct clk		*clk;
	int			irq;
	wait_queue_head_t	wait;
	struct i2c_adapter	adap;
	struct i2c_msg		*msg;
	int			msg_idx;
	int			msg_status;
	int			is_last;
};

static void i2c_lpc2k_reset(struct lpc2k_i2c *i2c)
{
	/* Will force clear all statuses */
	writel(LPC24XX_CLEAR_ALL, i2c->base + LPC24XX_I2CONCLR);
	writel(0, i2c->base + LPC24XX_I2ADDR);
	writel(LPC24XX_I2EN, i2c->base + LPC24XX_I2CONSET);
}

static int i2c_lpc2k_clear_arb(struct lpc2k_i2c *i2c)
{
	unsigned long timeout = jiffies + msecs_to_jiffies(1000);

	/*
	 * 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;
		}

		cpu_relax();
	}

	return 0;
}

static void i2c_lpc2k_pump_msg(struct lpc2k_i2c *i2c)
{
	unsigned char data;
	u32 status;

	/*
	 * 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);

		writel(data, i2c->base + LPC24XX_I2DAT);
		writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR);
		break;

	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);
		} else if (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 */

Annotation

Implementation Notes