Re: [PATCH v8 2/2] i2c: ls2x: Add clocks property parsing and adjust bus speed

Xi Ruoyao <[email protected]> Wed, 29 Jul 2026 16:09:31 +0800
Newsgroups dev.linux.lists.loongarch,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-i2c,org.kernel.vger.stable
Message-ID <[email protected]>
On Tue, 2026-07-21 at 20:26 +0800, Hongliang Wang wrote:
> The i2c-ls2x driver supports dts and acpi parameter passing.
>=20
> In dts, uses clock framework, by parsing clocks property to
> get i2c bus reference clock, and define the div of reference
> clock by device data.
>=20
> In acpi, by passing clocks property to describe i2c bus reference
> clock and clock-div property to describe the div of reference clock.
>=20
> Based on i2c bus reference clock(clock_a), i2c bus speed(clock_s)
> and div, calculate the prcescale of i2c divider register. The
> calculation formula is
>=20
> prcescale =3D (clock_a*10)/(div*clock_s)-1
>=20
> Reviewed-by: Huacai Chen <[email protected]>
> Cc: [email protected]
> Signed-off-by: Hongliang Wang <[email protected]>

This resolves the jittery touchpad input on the EAECIS NL60R laptop.

Tested-by: Xi Ruoyao <[email protected]>

> ---
> =C2=A0drivers/i2c/busses/i2c-ls2x.c | 40 ++++++++++++++++++++++++++++++++=
---
> =C2=A01 file changed, 37 insertions(+), 3 deletions(-)
>=20
> diff --git a/drivers/i2c/busses/i2c-ls2x.c b/drivers/i2c/busses/i2c-ls2x.=
c
> index b475dd27b7af..25f8301ae14f 100644
> --- a/drivers/i2c/busses/i2c-ls2x.c
> +++ b/drivers/i2c/busses/i2c-ls2x.c
> @@ -12,6 +12,7 @@
> =C2=A0
> =C2=A0#include <linux/bitfield.h>
> =C2=A0#include <linux/bits.h>
> +#include <linux/clk.h>
> =C2=A0#include <linux/completion.h>
> =C2=A0#include <linux/device.h>
> =C2=A0#include <linux/iopoll.h>
> @@ -63,11 +64,19 @@
> =C2=A0/* The default bus frequency, which is an empirical value */
> =C2=A0#define LS2X_I2C_FREQ_STD	(33 * HZ_PER_KHZ)
> =C2=A0
> +/* The div of i2c reference clock on LS2K0500/2K1000/2K2000 */
> +#define LS2X_I2C_2K_CLOCK_DIV	40
> +
> +/* The div of i2c reference clock on LS7A1000/7A2000 */
> +#define LS2X_I2C_7A_CLOCK_DIV	50
> +
> =C2=A0struct ls2x_i2c_priv {
> =C2=A0	struct i2c_adapter	adapter;
> =C2=A0	void __iomem		*base;
> =C2=A0	struct i2c_timings	i2c_t;
> =C2=A0	struct completion	cmd_complete;
> +	unsigned int		div;
> +	unsigned int		pclk;
> =C2=A0};
> =C2=A0
> =C2=A0/*
> @@ -107,12 +116,13 @@ static void ls2x_i2c_adjust_bus_speed(struct ls2x_i=
2c_priv *priv)
> =C2=A0	else
> =C2=A0		t->bus_freq_hz =3D LS2X_I2C_FREQ_STD;
> =C2=A0
> +	val =3D (priv->pclk * 10) / (priv->div * t->bus_freq_hz) - 1;
> +
> =C2=A0	/*
> =C2=A0	 * According to the chip manual, we can only access the registers =
as bytes,
> =C2=A0	 * otherwise the high bits will be truncated.
> =C2=A0	 * So set the I2C frequency with a sequential writeb() instead of =
writew().
> =C2=A0	 */
> -	val =3D LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1;
> =C2=A0	writeb(FIELD_GET(GENMASK(7, 0), val), priv->base + I2C_LS2X_PRER_L=
O);
> =C2=A0	writeb(FIELD_GET(GENMASK(15, 8), val), priv->base + I2C_LS2X_PRER_=
HI);
> =C2=A0}
> @@ -287,6 +297,7 @@ static const struct i2c_algorithm ls2x_i2c_algo =3D {
> =C2=A0static int ls2x_i2c_probe(struct platform_device *pdev)
> =C2=A0{
> =C2=A0	int ret, irq;
> +	struct clk *clk;
> =C2=A0	struct i2c_adapter *adap;
> =C2=A0	struct ls2x_i2c_priv *priv;
> =C2=A0	struct device *dev =3D &pdev->dev;
> @@ -304,6 +315,29 @@ static int ls2x_i2c_probe(struct platform_device *pd=
ev)
> =C2=A0	if (irq < 0)
> =C2=A0		return irq;
> =C2=A0
> +	if (dev_of_node(dev)) {
> +		clk =3D devm_clk_get_optional_enabled(dev, NULL);
> +		if (IS_ERR(clk))
> +			return PTR_ERR(clk);
> +		if (clk)
> +			priv->pclk =3D clk_get_rate(clk);
> +		else
> +			priv->pclk =3D LS2X_I2C_PCLK_FREQ;
> +
> +		priv->div =3D (unsigned long)device_get_match_data(dev);
> +		if (!priv->div)
> +			priv->div =3D LS2X_I2C_2K_CLOCK_DIV;
> +	} else {
> +		/* clocks and clock-div are only ACPI properties. */
> +		ret =3D device_property_read_u32(dev, "clocks", &priv->pclk);
> +		if (ret)
> +			priv->pclk =3D LS2X_I2C_PCLK_FREQ;
> +
> +		ret =3D device_property_read_u32(dev, "clock-div", &priv->div);
> +		if (ret || !priv->div)
> +			priv->div =3D LS2X_I2C_7A_CLOCK_DIV;
> +	}
> +
> =C2=A0	/* Add the i2c adapter */
> =C2=A0	adap =3D &priv->adapter;
> =C2=A0	adap->retries =3D 5;
> @@ -349,8 +383,8 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ls2x_i2c_pm_ops,
> =C2=A0				 ls2x_i2c_suspend, ls2x_i2c_resume, NULL);
> =C2=A0
> =C2=A0static const struct of_device_id ls2x_i2c_id_table[] =3D {
> -	{ .compatible =3D "loongson,ls2k-i2c" },
> -	{ .compatible =3D "loongson,ls7a-i2c" },
> +	{ .compatible =3D "loongson,ls2k-i2c", .data =3D (void *)LS2X_I2C_2K_CL=
OCK_DIV, },
> +	{ .compatible =3D "loongson,ls7a-i2c", .data =3D (void *)LS2X_I2C_7A_CL=
OCK_DIV, },
> =C2=A0	{ /* sentinel */ }
> =C2=A0};
> =C2=A0MODULE_DEVICE_TABLE(of, ls2x_i2c_id_table);

--=20
Xi Ruoyao <[email protected]>