drivers/i2c/busses/i2c-hix5hd2.c

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

File Facts

System
Linux kernel
Corpus path
drivers/i2c/busses/i2c-hix5hd2.c
Extension
.c
Size
12639 bytes
Lines
524
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 hix5hd2_i2c_priv {
	struct i2c_adapter adap;
	struct i2c_msg *msg;
	struct completion msg_complete;
	unsigned int msg_idx;
	unsigned int msg_len;
	int stop;
	void __iomem *regs;
	struct clk *clk;
	struct device *dev;
	spinlock_t lock;	/* IRQ synchronization */
	int err;
	unsigned int freq;
	enum hix5hd2_i2c_state state;
};

static u32 hix5hd2_i2c_clr_pend_irq(struct hix5hd2_i2c_priv *priv)
{
	u32 val = readl_relaxed(priv->regs + HIX5I2C_SR);

	writel_relaxed(val, priv->regs + HIX5I2C_ICR);

	return val;
}

static void hix5hd2_i2c_clr_all_irq(struct hix5hd2_i2c_priv *priv)
{
	writel_relaxed(I2C_CLEAR_ALL, priv->regs + HIX5I2C_ICR);
}

static void hix5hd2_i2c_disable_irq(struct hix5hd2_i2c_priv *priv)
{
	writel_relaxed(0, priv->regs + HIX5I2C_CTRL);
}

static void hix5hd2_i2c_enable_irq(struct hix5hd2_i2c_priv *priv)
{
	writel_relaxed(I2C_ENABLE | I2C_UNMASK_TOTAL | I2C_UNMASK_ALL,
		       priv->regs + HIX5I2C_CTRL);
}

static void hix5hd2_i2c_drv_setrate(struct hix5hd2_i2c_priv *priv)
{
	u32 rate, val;
	u32 scl, sysclock;

	/* close all i2c interrupt */
	val = readl_relaxed(priv->regs + HIX5I2C_CTRL);
	writel_relaxed(val & (~I2C_UNMASK_TOTAL), priv->regs + HIX5I2C_CTRL);

	rate = priv->freq;
	sysclock = clk_get_rate(priv->clk);
	scl = (sysclock / (rate * 2)) / 2 - 1;
	writel_relaxed(scl, priv->regs + HIX5I2C_SCL_H);
	writel_relaxed(scl, priv->regs + HIX5I2C_SCL_L);

	/* restore original interrupt*/
	writel_relaxed(val, priv->regs + HIX5I2C_CTRL);

	dev_dbg(priv->dev, "%s: sysclock=%d, rate=%d, scl=%d\n",
		__func__, sysclock, rate, scl);
}

static void hix5hd2_i2c_init(struct hix5hd2_i2c_priv *priv)
{
	hix5hd2_i2c_disable_irq(priv);
	hix5hd2_i2c_drv_setrate(priv);
	hix5hd2_i2c_clr_all_irq(priv);
	hix5hd2_i2c_enable_irq(priv);
}

static void hix5hd2_i2c_reset(struct hix5hd2_i2c_priv *priv)
{
	clk_disable_unprepare(priv->clk);
	msleep(20);
	clk_prepare_enable(priv->clk);
	hix5hd2_i2c_init(priv);
}

static int hix5hd2_i2c_wait_bus_idle(struct hix5hd2_i2c_priv *priv)
{
	unsigned long stop_time;
	u32 int_status;

	/* wait for 100 milli seconds for the bus to be idle */
	stop_time = jiffies + msecs_to_jiffies(100);
	do {
		int_status = hix5hd2_i2c_clr_pend_irq(priv);
		if (!(int_status & I2C_BUSY))
			return 0;

Annotation

Implementation Notes