drivers/i2c/busses/i2c-owl.c

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

File Facts

System
Linux kernel
Corpus path
drivers/i2c/busses/i2c-owl.c
Extension
.c
Size
13666 bytes
Lines
523
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 owl_i2c_dev {
	struct i2c_adapter	adap;
	struct i2c_msg		*msg;
	struct completion	msg_complete;
	struct clk		*clk;
	spinlock_t		lock;
	void __iomem		*base;
	unsigned long		clk_rate;
	u32			bus_freq;
	u32			msg_ptr;
	int			err;
};

static void owl_i2c_update_reg(void __iomem *reg, unsigned int val, bool state)
{
	unsigned int regval;

	regval = readl(reg);

	if (state)
		regval |= val;
	else
		regval &= ~val;

	writel(regval, reg);
}

static void owl_i2c_reset(struct owl_i2c_dev *i2c_dev)
{
	owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
			   OWL_I2C_CTL_EN, false);
	mdelay(1);
	owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
			   OWL_I2C_CTL_EN, true);

	/* Clear status registers */
	writel(0, i2c_dev->base + OWL_I2C_REG_STAT);
}

static int owl_i2c_reset_fifo(struct owl_i2c_dev *i2c_dev)
{
	unsigned int val, timeout = 0;

	/* Reset FIFO */
	owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
			   OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR,
			   true);

	/* Wait 50ms for FIFO reset complete */
	do {
		val = readl(i2c_dev->base + OWL_I2C_REG_FIFOCTL);
		if (!(val & (OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR)))
			break;
		usleep_range(500, 1000);
	} while (timeout++ < OWL_I2C_MAX_RETRIES);

	if (timeout > OWL_I2C_MAX_RETRIES) {
		dev_err(&i2c_dev->adap.dev, "FIFO reset timeout\n");
		return -ETIMEDOUT;
	}

	return 0;
}

static void owl_i2c_set_freq(struct owl_i2c_dev *i2c_dev)
{
	unsigned int val;

	val = DIV_ROUND_UP(i2c_dev->clk_rate, i2c_dev->bus_freq * 16);

	/* Set clock divider factor */
	writel(OWL_I2C_DIV_FACTOR(val), i2c_dev->base + OWL_I2C_REG_CLKDIV);
}

static void owl_i2c_xfer_data(struct owl_i2c_dev *i2c_dev)
{
	struct i2c_msg *msg = i2c_dev->msg;
	unsigned int stat, fifostat;

	i2c_dev->err = 0;

	/* Handle NACK from target */
	fifostat = readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT);
	if (fifostat & OWL_I2C_FIFOSTAT_RNB) {
		i2c_dev->err = -ENXIO;
		/* Clear NACK error bit by writing "1" */
		owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOSTAT,
				   OWL_I2C_FIFOSTAT_RNB, true);
		return;
	}

Annotation

Implementation Notes