Re: [PATCH 3/6] rtc: abx80x: use regmap instead of I2C specific API

[email protected] Wed, 22 Jul 2026 20:16:45 +0000
Newsgroups org.kernel.vger.linux-rtc,org.kernel.vger.linux-devicetree
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Hardware unlock sequence is broken by read-modify-write inversion
- [Low] Incompatible pointer types passed to regmap_read()
--

--- Patch [3]: [PATCH 3/6] rtc: abx80x: use regmap instead of I2C specific API ---
commit 97cd1f33d168c96f848a8f59ba284bfe9c5f89fe
Author: Antoni Pokusinski <[email protected]>
Date:   Wed Jul 22 19:58:11 2026 +0000

    rtc: abx80x: use regmap instead of I2C specific API
    
    Rework the driver so that it uses regmap_* functions for communication
    with the device instead of the i2c_smbus_* API. This way the support for
    ABX81X RTCs which use SPI can be added later.

> diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> index 9b0e1d48d9ba3..e5ba821a67689 100644
> --- a/drivers/rtc/rtc-abx80x.c
> +++ b/drivers/rtc/rtc-abx80x.c
> @@ -125,52 +126,53 @@ static struct abx80x_cap abx80x_caps[] = {
[ ... ]
> -static int abx80x_is_rc_mode(struct i2c_client *client)
> +static int abx80x_is_rc_mode(struct device *dev)
>  {
> -	int flags = 0;
> +	struct abx80x_priv *priv = dev_get_drvdata(dev);
> +	int err, flags = 0;
>  
> -	flags =  i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS);
> -	if (flags < 0) {
> -		dev_err(&client->dev,
> -			"Failed to read autocalibration attribute\n");
> -		return flags;
> +	err = regmap_read(priv->regmap, ABX8XX_REG_OSS, &flags);

[Severity: Low]
Does this pass an incompatible pointer type to regmap_read()?

The API signature of regmap_read() expects an unsigned int * for its third
argument, but the address of a local int variable (&flags) is passed here.
While this may not cause memory corruption since int and unsigned int are
the same size, it violates the function signature and generates
-Wincompatible-pointer-types warnings, which can break builds compiling with
-Werror.

> +	if (err < 0) {
> +		dev_err(dev, "Failed to read autocalibration attribute\n");
> +		return err;
>  	}
[ ... ]
> @@ -259,13 +260,13 @@ static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  
>  static irqreturn_t abx80x_handle_irq(int irq, void *dev_id)
>  {
> -	struct i2c_client *client = dev_id;
> -	struct abx80x_priv *priv = i2c_get_clientdata(client);
> +	struct device *dev = dev_id;
> +	struct abx80x_priv *priv = dev_get_drvdata(dev);
>  	struct rtc_device *rtc = priv->rtc;
> -	int status;
> +	int err, status;
>  
> -	status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
> -	if (status < 0)
> +	err = regmap_read(priv->regmap, ABX8XX_REG_STATUS, &status);

[Severity: Low]
Does this pass an incompatible pointer type to regmap_read()?

Similar to abx80x_is_rc_mode() above, this passes an int pointer (&status)
instead of an unsigned int pointer.

> +	if (err < 0)
>  		return IRQ_NONE;
[ ... ]
> @@ -294,14 +294,13 @@ static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
>  	if (priv->irq <= 0)
>  		return -EINVAL;
>  
> -	err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ASC,
> -					    sizeof(buf), buf);
> +	err = regmap_bulk_read(priv->regmap, ABX8XX_REG_ASC, buf, sizeof(buf));
>  	if (err)
>  		return err;
>  
> -	irq_mask = i2c_smbus_read_byte_data(client, ABX8XX_REG_IRQ);
> -	if (irq_mask < 0)
> -		return irq_mask;
> +	err = regmap_read(priv->regmap, ABX8XX_REG_IRQ, &irq_mask);

[Severity: Low]
Does this pass an incompatible pointer type to regmap_read()?

This passes an int pointer (&irq_mask) instead of an unsigned int pointer.

> +	if (err < 0)
> +		return err;
[ ... ]
> @@ -379,22 +376,22 @@ static int abx80x_rtc_set_autocalibration(struct device *dev,
>  	}
>  
>  	/* Unlock write access to Oscillator Control Register */
> -	if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_OSC) < 0)
> +	if (abx80x_write_config_key(dev, ABX8XX_CFG_KEY_OSC) < 0)
>  		return -EIO;
>  
> -	retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags);
> +	retval = regmap_write(priv->regmap, ABX8XX_REG_OSC, flags);

[Severity: High]
Does this hardware unlock sequence get broken by read-modify-write inversion?

According to the datasheet, any subsequent I2C access (read or write) resets
the configuration key to zero. If this sequence uses a read-modify-write
operation (such as regmap_write_bits()), a hardware read is performed after
the unlock key is written. This read resets the configuration key, causing the
hardware to ignore the subsequent write and breaking features like
autocalibration.

>  
>  	return retval;
>  }
>  
>  static int abx80x_rtc_get_autocalibration(struct device *dev)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> -	int flags = 0, autocalibration;
> +	struct abx80x_priv *priv = dev_get_drvdata(dev);
> +	int flags = 0, autocalibration, err;
>  
> -	flags =  i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
> -	if (flags < 0)
> -		return flags;
> +	err = regmap_read(priv->regmap, ABX8XX_REG_OSC, &flags);

[Severity: Low]
Does this pass an incompatible pointer type to regmap_read()?

This passes an int pointer (&flags) instead of an unsigned int pointer.

> +	if (err < 0)
> +		return err;
[ ... ]
> @@ -467,10 +464,10 @@ static ssize_t oscillator_store(struct device *dev,
>  		flags |= (ABX8XX_OSC_OSEL);
>  
>  	/* Unlock write access on Oscillator Control register */
> -	if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_OSC) < 0)
> +	if (abx80x_write_config_key(dev->parent, ABX8XX_CFG_KEY_OSC) < 0)
>  		return -EIO;
>  
> -	retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags);
> +	retval = regmap_write(priv->regmap, ABX8XX_REG_OSC, flags);

[Severity: High]
Does this hardware unlock sequence get broken by read-modify-write inversion?

As noted in abx80x_rtc_set_autocalibration() above, a hardware read performed
after the unlock key is written clears the key. This resets the key before the
protected write if a read-modify-write operation like regmap_write_bits() is
used.

>  	if (retval < 0) {
>  		dev_err(dev, "Failed to write Oscillator Control register\n");
[ ... ]
> @@ -513,45 +509,43 @@ static const struct attribute_group rtc_calib_attr_group = {
>  
>  static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled)
>  {
[ ... ]
>  static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> -	int status, tmp;
> +	struct abx80x_priv *priv = dev_get_drvdata(dev);
> +	int err, status, tmp;
>  
>  	switch (cmd) {
>  	case RTC_VL_READ:
> -		status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
> -		if (status < 0)
> -			return status;
> +		err = regmap_read(priv->regmap, ABX8XX_REG_STATUS, &status);

[Severity: Low]
Does this pass an incompatible pointer type to regmap_read()?

This passes an int pointer (&status) instead of an unsigned int pointer.

> +		if (err < 0)
> +			return err;
[ ... ]
> @@ -856,11 +898,11 @@ static int abx80x_probe(struct i2c_client *client)
>  		 * Write the configuration key register to enable access to
>  		 * the config2 register
>  		 */
> -		if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_MISC) < 0)
> +		if (abx80x_write_config_key(&client->dev, ABX8XX_CFG_KEY_MISC) < 0)
>  			return -EIO;
>  
> -		err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OUT_CTRL,
> -						data | ABX8XX_OUT_CTRL_EXDS);
> +		err = regmap_write(regmap, ABX8XX_REG_OUT_CTRL,
> +				   data | ABX8XX_OUT_CTRL_EXDS);

[Severity: High]
Does this hardware unlock sequence get broken by read-modify-write inversion?

For RV1805, the key is written, but if a read-modify-write operation like
regmap_write_bits() is used here, it performs a read before writing the EXTI
disable bit. This read clears the key, leaving the write ineffective and
defeating the extra power leakage workaround.

>  		if (err < 0) {
>  			dev_err(&client->dev,
>  				"Unable to write output control register\n");

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=3