drivers/i2c/busses/i2c-ls2x.c

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

File Facts

System
Linux kernel
Corpus path
drivers/i2c/busses/i2c-ls2x.c
Extension
.c
Size
9682 bytes
Lines
378
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 ls2x_i2c_priv {
	struct i2c_adapter	adapter;
	void __iomem		*base;
	struct i2c_timings	i2c_t;
	struct completion	cmd_complete;
};

/*
 * Interrupt service routine.
 * This gets called whenever an I2C interrupt occurs.
 */
static irqreturn_t ls2x_i2c_isr(int this_irq, void *dev_id)
{
	struct ls2x_i2c_priv *priv = dev_id;

	if (!(readb(priv->base + I2C_LS2X_SR) & LS2X_SR_IF))
		return IRQ_NONE;

	writeb(LS2X_CR_IACK, priv->base + I2C_LS2X_CR);
	complete(&priv->cmd_complete);
	return IRQ_HANDLED;
}

/*
 * The ls2x i2c controller supports standard mode and fast mode, so the
 * maximum bus frequency is '400kHz'.
 * The bus frequency is set to the empirical value of '33KHz' by default,
 * but it can also be taken from ACPI or FDT for compatibility with more
 * devices.
 */
static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv)
{
	u16 val;
	struct i2c_timings *t = &priv->i2c_t;
	struct device *dev = priv->adapter.dev.parent;
	u32 acpi_speed = i2c_acpi_find_bus_speed(dev);

	i2c_parse_fw_timings(dev, t, false);

	if (acpi_speed || t->bus_freq_hz)
		t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
	else
		t->bus_freq_hz = LS2X_I2C_FREQ_STD;

	/*
	 * According to the chip manual, we can only access the registers as bytes,
	 * otherwise the high bits will be truncated.
	 * So set the I2C frequency with a sequential writeb() instead of writew().
	 */
	val = LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1;
	writeb(FIELD_GET(GENMASK(7, 0), val), priv->base + I2C_LS2X_PRER_LO);
	writeb(FIELD_GET(GENMASK(15, 8), val), priv->base + I2C_LS2X_PRER_HI);
}

static void ls2x_i2c_init(struct ls2x_i2c_priv *priv)
{
	/* Set i2c frequency setting mode and disable interrupts. */
	writeb(readb(priv->base + I2C_LS2X_CTR) & ~CTR_FREQ_MASK,
	       priv->base + I2C_LS2X_CTR);

	ls2x_i2c_adjust_bus_speed(priv);

	/* Set i2c normal operating mode and enable interrupts. */
	writeb(readb(priv->base + I2C_LS2X_CTR) | CTR_READY_MASK,
	       priv->base + I2C_LS2X_CTR);
}

static int ls2x_i2c_xfer_byte(struct ls2x_i2c_priv *priv, u8 txdata, u8 *rxdatap)
{
	u8 rxdata;
	unsigned long time_left;

	writeb(txdata, priv->base + I2C_LS2X_CR);

	time_left = wait_for_completion_timeout(&priv->cmd_complete,
						priv->adapter.timeout);
	if (!time_left)
		return -ETIMEDOUT;

	rxdata = readb(priv->base + I2C_LS2X_SR);
	if (rxdatap)
		*rxdatap = rxdata;

	return 0;
}

static int ls2x_i2c_send_byte(struct ls2x_i2c_priv *priv, u8 txdata)
{
	int ret;
	u8 rxdata;

Annotation

Implementation Notes