drivers/i2c/busses/i2c-altera.c

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

File Facts

System
Linux kernel
Corpus path
drivers/i2c/busses/i2c-altera.c
Extension
.c
Size
14162 bytes
Lines
497
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 altr_i2c_dev {
	void __iomem *base;
	struct i2c_msg *msg;
	size_t msg_len;
	int msg_err;
	struct completion msg_complete;
	struct device *dev;
	struct i2c_adapter adapter;
	struct clk *i2c_clk;
	u32 bus_clk_rate;
	u8 *buf;
	u32 fifo_size;
	u32 isr_mask;
	u32 isr_status;
	struct mutex isr_mutex;
};

static void
altr_i2c_int_enable(struct altr_i2c_dev *idev, u32 mask, bool enable)
{
	u32 int_en;

	int_en = readl(idev->base + ALTR_I2C_ISER);
	if (enable)
		idev->isr_mask = int_en | mask;
	else
		idev->isr_mask = int_en & ~mask;

	writel(idev->isr_mask, idev->base + ALTR_I2C_ISER);
}

static void altr_i2c_int_clear(struct altr_i2c_dev *idev, u32 mask)
{
	u32 int_en = readl(idev->base + ALTR_I2C_ISR);

	writel(int_en | mask, idev->base + ALTR_I2C_ISR);
}

static void altr_i2c_core_disable(struct altr_i2c_dev *idev)
{
	u32 tmp = readl(idev->base + ALTR_I2C_CTRL);

	writel(tmp & ~ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL);
}

static void altr_i2c_core_enable(struct altr_i2c_dev *idev)
{
	u32 tmp = readl(idev->base + ALTR_I2C_CTRL);

	writel(tmp | ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL);
}

static void altr_i2c_reset(struct altr_i2c_dev *idev)
{
	altr_i2c_core_disable(idev);
	altr_i2c_core_enable(idev);
}

static inline void altr_i2c_stop(struct altr_i2c_dev *idev)
{
	writel(ALTR_I2C_TFR_CMD_STO, idev->base + ALTR_I2C_TFR_CMD);
}

static void altr_i2c_init(struct altr_i2c_dev *idev)
{
	u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate;
	u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000;
	u32 tmp = (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_RXT_SHFT) |
		  (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_TCT_SHFT);
	u32 t_high, t_low;

	if (idev->bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ) {
		tmp &= ~ALTR_I2C_CTRL_BSPEED;
		/* Standard mode SCL 50/50 */
		t_high = divisor * 1 / 2;
		t_low = divisor * 1 / 2;
	} else {
		tmp |= ALTR_I2C_CTRL_BSPEED;
		/* Fast mode SCL 33/66 */
		t_high = divisor * 1 / 3;
		t_low = divisor * 2 / 3;
	}
	writel(tmp, idev->base + ALTR_I2C_CTRL);

	dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n",
		idev->bus_clk_rate, clk_mhz, divisor);

	/* Reset controller */
	altr_i2c_reset(idev);

Annotation

Implementation Notes