* [PATCH v4 0/9] rtc: abx80x: add support for abx81x
@ 2026-08-10 19:38 Antoni Pokusinski
2026-08-10 19:38 ` [PATCH v4 1/9] dt-bindings: rtc: abx80x: document ABX81X RTCs Antoni Pokusinski
` (8 more replies)
0 siblings, 9 replies; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski
Hello,
This patch series adds support for the ABX81X RTCs, which are essentially
SPI versions of the ABX80X RTCs. The first patches (P2-P4) contain bug fixes
(added after sashiko's review in v1-v3), then (P5-P8) we have some cleanup and
reorganization changes (converting the driver to use the regmap API was
needed for this), followed by the actual ABX81X support.
Kind regards,
Antoni Pokusinski
---
Changes since v3:
* PATCH 2 ("rtc: abx80x: fix error check after i2c_smbus_read in read_alarm()")
New patch
* PATCH 3 ("rtc: abx80x: add mutex protection for register writes")
Replaced "guard()" with "scoped_guard()" in "abx80x_ioctl()"
* PATCH 4 ("rtc: abx80x: properly handle shared IRQs")
Disable all the unused interrupts in "probe()"
* PATCH 6 ("rtc: abx80x: use regmap instead of I2C specific API")
Changed the type of "flag" arguments in "regmap_read()" from "int" to "unsigned int"
---
Changes since v2:
* dropped patch "rtc: abx80x: move part autodetection before RV1805 configuration"
* PATCH 2 ("rtc: abx80x: add mutex protection for register writes")
New patch
* PATCH 5 ("rtc: abx80x: use regmap instead of I2C specific API")
- return PTR_ERR(regmap) on I2C regmap init error
* PATCH 8 ("rtc: abx80x: add support for ABX81X")
- return PTR_ERR(regmap) on SPI regmap init error
---
Changes since v1:
* PATCH 1 ("dt-bindings: rtc: abx80x: document ABX81X RTCs")
- added missing "spi-peripherals-props" reference
- added abx815 compatible strings to the trickle charger enum list
* PATCH 2 ("rtc: abx80x: move part autodetection before RV1805 configuration")
New patch
* PATCH 3 ("rtc: abx80x: properly handle shared IRQs")
New patch
* PATCH 5 ("rtc: abx80x: use regmap instead of I2C specific API")
- changed regmap_read() argument types from `int` to `unsigned int`
---
Antoni Pokusinski (9):
dt-bindings: rtc: abx80x: document ABX81X RTCs
rtc: abx80x: fix error check after i2c_smbus_read in read_alarm()
rtc: abx80x: add mutex protection for register writes
rtc: abx80x: properly handle shared IRQs
rtc: abx80x: add irq to struct abx80x_priv
rtc: abx80x: use regmap instead of I2C specific API
rtc: abx80x: replace read-modify-write pattern with regmap helpers
rtc: abx80x: create abx80x_i2c_probe()
rtc: abx80x: add support for ABX81X
.../bindings/rtc/abracon,abx80x.yaml | 18 +-
drivers/rtc/Kconfig | 25 +-
drivers/rtc/rtc-abx80x.c | 663 +++++++++++-------
3 files changed, 451 insertions(+), 255 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v4 1/9] dt-bindings: rtc: abx80x: document ABX81X RTCs
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
@ 2026-08-10 19:38 ` Antoni Pokusinski
2026-08-14 0:47 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 2/9] rtc: abx80x: fix error check after i2c_smbus_read in read_alarm() Antoni Pokusinski
` (7 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski,
Krzysztof Kozlowski
Abracon AB0815 and AB1815 are RTC modules with SPI interface.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
---
.../bindings/rtc/abracon,abx80x.yaml | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/rtc/abracon,abx80x.yaml b/Documentation/devicetree/bindings/rtc/abracon,abx80x.yaml
index 355b0598411a..d105570bcd5a 100644
--- a/Documentation/devicetree/bindings/rtc/abracon,abx80x.yaml
+++ b/Documentation/devicetree/bindings/rtc/abracon,abx80x.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/rtc/abracon,abx80x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Abracon ABX80X I2C ultra low power RTC/Alarm chip
+title: Abracon ABX80X (I2C) and ABX81X (SPI) ultra low power RTC/Alarm chips
maintainers:
- linux-rtc@vger.kernel.org
@@ -21,10 +21,12 @@ properties:
- abracon,ab0803
- abracon,ab0804
- abracon,ab0805
+ - abracon,ab0815
- abracon,ab1801
- abracon,ab1803
- abracon,ab1804
- abracon,ab1805
+ - abracon,ab1815
- microcrystal,rv1805
reg:
@@ -61,6 +63,7 @@ required:
- reg
allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
- $ref: rtc.yaml#
- if:
properties:
@@ -73,6 +76,8 @@ allOf:
- abracon,ab1804
- abracon,ab0805
- abracon,ab1805
+ - abracon,ab0815
+ - abracon,ab1815
then:
properties:
abracon,tc-diode: false
@@ -96,3 +101,14 @@ examples:
interrupts = <44 IRQ_TYPE_EDGE_FALLING>;
};
};
+
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@0 {
+ compatible = "abracon,ab1815";
+ reg = <0>;
+ };
+ };
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v4 2/9] rtc: abx80x: fix error check after i2c_smbus_read in read_alarm()
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
2026-08-10 19:38 ` [PATCH v4 1/9] dt-bindings: rtc: abx80x: document ABX81X RTCs Antoni Pokusinski
@ 2026-08-10 19:38 ` Antoni Pokusinski
2026-08-14 1:02 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 3/9] rtc: abx80x: add mutex protection for register writes Antoni Pokusinski
` (6 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski
i2c_smbus_read_i2c_block_data() returns the number of bytes read on
success and a negative error code on failure. The existing code treated
any non-zero return value as an error.
Fix the error handling by checking only for negative return values.
Fixes: 718a820a303c ("rtc: abx80x: add alarm support")
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
---
drivers/rtc/rtc-abx80x.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 5486d9d0b1e5..fdf9664ebb6f 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -294,7 +294,7 @@ static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ASC,
sizeof(buf), buf);
- if (err)
+ if (err < 0)
return err;
irq_mask = i2c_smbus_read_byte_data(client, ABX8XX_REG_IRQ);
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v4 3/9] rtc: abx80x: add mutex protection for register writes
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
2026-08-10 19:38 ` [PATCH v4 1/9] dt-bindings: rtc: abx80x: document ABX81X RTCs Antoni Pokusinski
2026-08-10 19:38 ` [PATCH v4 2/9] rtc: abx80x: fix error check after i2c_smbus_read in read_alarm() Antoni Pokusinski
@ 2026-08-10 19:38 ` Antoni Pokusinski
2026-08-14 1:20 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 4/9] rtc: abx80x: properly handle shared IRQs Antoni Pokusinski
` (5 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski
The ABX80X RTC driver performs multi-step register operations such as
NVMEM transfers or register writes preceded by a configuration key
write. Add a mutex to serialize all the register writes to protect
these sequences against race conditions.
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
---
drivers/rtc/rtc-abx80x.c | 45 +++++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 8 deletions(-)
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index fdf9664ebb6f..0c8d3416fd5a 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -15,6 +15,7 @@
#include <linux/i2c.h>
#include <linux/kstrtox.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/rtc.h>
#include <linux/watchdog.h>
@@ -127,6 +128,7 @@ struct abx80x_priv {
struct rtc_device *rtc;
struct i2c_client *client;
struct watchdog_device wdog;
+ struct mutex lock;
};
static int abx80x_write_config_key(struct i2c_client *client, u8 key)
@@ -219,6 +221,7 @@ static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct abx80x_priv *priv = i2c_get_clientdata(client);
unsigned char buf[8];
int err, flags;
@@ -234,6 +237,8 @@ static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
buf[ABX8XX_REG_YR] = bin2bcd(tm->tm_year - 100);
buf[ABX8XX_REG_WD] = tm->tm_wday;
+ guard(mutex)(&priv->lock);
+
err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_HTH,
sizeof(buf), buf);
if (err < 0) {
@@ -263,6 +268,8 @@ static irqreturn_t abx80x_handle_irq(int irq, void *dev_id)
struct rtc_device *rtc = priv->rtc;
int status;
+ guard(mutex)(&priv->lock);
+
status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
if (status < 0)
return IRQ_NONE;
@@ -317,6 +324,7 @@ static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct abx80x_priv *priv = i2c_get_clientdata(client);
u8 alarm[6];
int err;
@@ -330,6 +338,8 @@ static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t)
alarm[4] = bin2bcd(t->time.tm_mday);
alarm[5] = bin2bcd(t->time.tm_mon + 1);
+ guard(mutex)(&priv->lock);
+
err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_AHTH,
sizeof(alarm), alarm);
if (err < 0) {
@@ -352,6 +362,7 @@ static int abx80x_rtc_set_autocalibration(struct device *dev,
int autocalibration)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct abx80x_priv *priv = i2c_get_clientdata(client);
int retval, flags = 0;
if ((autocalibration != 0) && (autocalibration != 1024) &&
@@ -360,6 +371,8 @@ static int abx80x_rtc_set_autocalibration(struct device *dev,
return -EINVAL;
}
+ guard(mutex)(&priv->lock);
+
flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
if (flags < 0)
return flags;
@@ -443,6 +456,7 @@ static ssize_t oscillator_store(struct device *dev,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev->parent);
+ struct abx80x_priv *priv = i2c_get_clientdata(client);
int retval, flags, rc_mode = 0;
if (strncmp(buf, "rc", 2) == 0) {
@@ -454,6 +468,8 @@ static ssize_t oscillator_store(struct device *dev,
return -EINVAL;
}
+ guard(mutex)(&priv->lock);
+
flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
if (flags < 0)
return flags;
@@ -511,8 +527,11 @@ static const struct attribute_group rtc_calib_attr_group = {
static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct abx80x_priv *priv = i2c_get_clientdata(client);
int err;
+ guard(mutex)(&priv->lock);
+
if (enabled)
err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ,
(ABX8XX_IRQ_IM_1_4 |
@@ -526,6 +545,7 @@ 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);
+ struct abx80x_priv *priv = i2c_get_clientdata(client);
int status, tmp;
switch (cmd) {
@@ -539,16 +559,18 @@ static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
return put_user(tmp, (unsigned int __user *)arg);
case RTC_VL_CLR:
- status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
- if (status < 0)
- return status;
+ scoped_guard(mutex, &priv->lock) {
+ status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
+ if (status < 0)
+ return status;
- status &= ~ABX8XX_STATUS_BLF;
+ status &= ~ABX8XX_STATUS_BLF;
- tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS,
- status);
- if (tmp < 0)
- return tmp;
+ tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS,
+ status);
+ if (tmp < 0)
+ return tmp;
+ }
return 0;
@@ -617,6 +639,8 @@ static int __abx80x_wdog_set_timeout(struct watchdog_device *wdog,
struct abx80x_priv *priv = watchdog_get_drvdata(wdog);
u8 val = ABX8XX_WDT_WDS | timeout_bits(timeout);
+ guard(mutex)(&priv->lock);
+
/*
* Writing any timeout to the WDT register resets the watchdog timer.
* Writing 0 disables it.
@@ -701,6 +725,8 @@ static int abx80x_nvmem_xfer(struct abx80x_priv *priv, unsigned int offset,
len = min(lower + bytes, (size_t)ABX8XX_SRAM_WIN_SIZE) - lower;
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
+ guard(mutex)(&priv->lock);
+
ret = i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_EXTRAM,
extram);
if (ret)
@@ -908,6 +934,9 @@ static int abx80x_probe(struct i2c_client *client)
priv->rtc->ops = &abx80x_rtc_ops;
priv->client = client;
+ err = devm_mutex_init(&client->dev, &priv->lock);
+ if (err)
+ return err;
i2c_set_clientdata(client, priv);
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v4 4/9] rtc: abx80x: properly handle shared IRQs
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
` (2 preceding siblings ...)
2026-08-10 19:38 ` [PATCH v4 3/9] rtc: abx80x: add mutex protection for register writes Antoni Pokusinski
@ 2026-08-10 19:38 ` Antoni Pokusinski
2026-08-14 1:33 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 5/9] rtc: abx80x: add irq to struct abx80x_priv Antoni Pokusinski
` (4 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski
The abx80x RTC driver registers its interrupt handler with IRQF_SHARED,
but the handler always returned IRQ_HANDLED and cleared the status
register even when no interrupt source from the RTC was active.
This could cause the driver to claim interrupts belonging to other
devices sharing the same IRQ line and could also clear unrelated status
bits in the RTC status register.
Only handle the interrupt when the alarm or watchdog flags are set, and
clear only those interrupt status bits that were handled. Return
IRQ_NONE when the interrupt was not generated by the RTC.
Fixes: 718a820a303c ("rtc: abx80x: add alarm support")
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
---
drivers/rtc/rtc-abx80x.c | 54 ++++++++++++++++++++++++++++++++++++----
1 file changed, 49 insertions(+), 5 deletions(-)
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 0c8d3416fd5a..485d1ea08030 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -51,12 +51,18 @@
#define ABX8XX_CTRL2_RSVD BIT(5)
#define ABX8XX_REG_IRQ 0x12
+#define ABX8XX_IRQ_EX1E BIT(0)
+#define ABX8XX_IRQ_EX2E BIT(1)
#define ABX8XX_IRQ_AIE BIT(2)
+#define ABX8XX_IRQ_TIE BIT(3)
+#define ABX8XX_IRQ_BLIE BIT(4)
#define ABX8XX_IRQ_IM_1_4 (0x3 << 5)
#define ABX8XX_REG_CD_TIMER_CTL 0x18
#define ABX8XX_REG_OSC 0x1c
+#define ABX8XX_OSC_ACIE BIT(0)
+#define ABX8XX_OSC_OFIE BIT(1)
#define ABX8XX_OSC_FOS BIT(3)
#define ABX8XX_OSC_BOS BIT(4)
#define ABX8XX_OSC_ACAL_512 BIT(5)
@@ -266,27 +272,34 @@ 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 rtc_device *rtc = priv->rtc;
+ irqreturn_t handled = IRQ_NONE;
int status;
guard(mutex)(&priv->lock);
status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
if (status < 0)
- return IRQ_NONE;
+ return handled;
- if (status & ABX8XX_STATUS_AF)
+ if (status & ABX8XX_STATUS_AF) {
rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
+ handled = IRQ_HANDLED;
+ }
/*
* It is unclear if we'll get an interrupt before the external
* reset kicks in.
*/
- if (status & ABX8XX_STATUS_WDT)
+ if (status & ABX8XX_STATUS_WDT) {
dev_alert(&client->dev, "watchdog timeout interrupt.\n");
+ handled = IRQ_HANDLED;
+ }
- i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0);
+ if (handled == IRQ_HANDLED)
+ i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS,
+ status & ~(ABX8XX_STATUS_AF | ABX8XX_STATUS_WDT));
- return IRQ_HANDLED;
+ return handled;
}
static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
@@ -950,6 +963,37 @@ static int abx80x_probe(struct i2c_client *client)
if (err)
return err;
+ /* Disable unused interrupts */
+ data = i2c_smbus_read_byte_data(client, ABX8XX_REG_IRQ);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read irq register\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ,
+ data & ~(ABX8XX_IRQ_EX1E |
+ ABX8XX_IRQ_EX2E |
+ ABX8XX_IRQ_TIE |
+ ABX8XX_IRQ_BLIE));
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write irq register\n");
+ return -EIO;
+ }
+
+ data = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read Oscillator Control register\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC,
+ data & ~(ABX8XX_OSC_ACIE |
+ ABX8XX_OSC_OFIE));
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write Oscillator Control register\n");
+ return -EIO;
+ }
+
if (client->irq > 0) {
dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v4 5/9] rtc: abx80x: add irq to struct abx80x_priv
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
` (3 preceding siblings ...)
2026-08-10 19:38 ` [PATCH v4 4/9] rtc: abx80x: properly handle shared IRQs Antoni Pokusinski
@ 2026-08-10 19:38 ` Antoni Pokusinski
2026-08-14 1:44 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 6/9] rtc: abx80x: use regmap instead of I2C specific API Antoni Pokusinski
` (3 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski
This way the irq number is not tied to the i2c_client and can be
easily accessed also in the case of ABX81X SPI clocks.
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
---
drivers/rtc/rtc-abx80x.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 485d1ea08030..12a4dbfec639 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -135,6 +135,7 @@ struct abx80x_priv {
struct i2c_client *client;
struct watchdog_device wdog;
struct mutex lock;
+ int irq;
};
static int abx80x_write_config_key(struct i2c_client *client, u8 key)
@@ -305,11 +306,12 @@ static irqreturn_t abx80x_handle_irq(int irq, void *dev_id)
static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct abx80x_priv *priv = i2c_get_clientdata(client);
unsigned char buf[7];
int irq_mask, err;
- if (client->irq <= 0)
+ if (priv->irq <= 0)
return -EINVAL;
err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ASC,
@@ -341,7 +343,7 @@ static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t)
u8 alarm[6];
int err;
- if (client->irq <= 0)
+ if (priv->irq <= 0)
return -EINVAL;
alarm[0] = 0x0;
@@ -947,6 +949,7 @@ static int abx80x_probe(struct i2c_client *client)
priv->rtc->ops = &abx80x_rtc_ops;
priv->client = client;
+ priv->irq = client->irq;
err = devm_mutex_init(&client->dev, &priv->lock);
if (err)
return err;
@@ -994,19 +997,19 @@ static int abx80x_probe(struct i2c_client *client)
return -EIO;
}
- if (client->irq > 0) {
- dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
- err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ if (priv->irq > 0) {
+ dev_info(&client->dev, "IRQ %d supplied\n", priv->irq);
+ err = devm_request_threaded_irq(&client->dev, priv->irq, NULL,
abx80x_handle_irq,
IRQF_SHARED | IRQF_ONESHOT,
"abx8xx",
client);
if (err) {
dev_err(&client->dev, "unable to request IRQ, alarms disabled\n");
- client->irq = 0;
+ priv->irq = 0;
}
}
- if (client->irq <= 0)
+ if (priv->irq <= 0)
clear_bit(RTC_FEATURE_ALARM, priv->rtc->features);
err = rtc_add_group(priv->rtc, &rtc_calib_attr_group);
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v4 6/9] rtc: abx80x: use regmap instead of I2C specific API
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
` (4 preceding siblings ...)
2026-08-10 19:38 ` [PATCH v4 5/9] rtc: abx80x: add irq to struct abx80x_priv Antoni Pokusinski
@ 2026-08-10 19:38 ` Antoni Pokusinski
2026-08-14 1:57 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 7/9] rtc: abx80x: replace read-modify-write pattern with regmap helpers Antoni Pokusinski
` (2 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski
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.
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
---
drivers/rtc/Kconfig | 1 +
drivers/rtc/rtc-abx80x.c | 389 +++++++++++++++++++++------------------
2 files changed, 211 insertions(+), 179 deletions(-)
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 01def8231873..4d14bcba616e 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -215,6 +215,7 @@ config RTC_DRV_ABEOZ9
config RTC_DRV_ABX80X
tristate "Abracon ABx80x"
select WATCHDOG_CORE if WATCHDOG
+ select REGMAP_I2C
help
If you say yes here you get support for Abracon AB080X and AB180X
families of ultra-low-power battery- and capacitor-backed real-time
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 12a4dbfec639..5d41882f3c70 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
+#include <linux/regmap.h>
#include <linux/rtc.h>
#include <linux/watchdog.h>
@@ -132,53 +133,55 @@ static struct abx80x_cap abx80x_caps[] = {
struct abx80x_priv {
struct rtc_device *rtc;
- struct i2c_client *client;
+ struct regmap *regmap;
struct watchdog_device wdog;
struct mutex lock;
int irq;
};
-static int abx80x_write_config_key(struct i2c_client *client, u8 key)
+static int abx80x_write_config_key(struct device *dev, u8 key)
{
- if (i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY, key) < 0) {
- dev_err(&client->dev, "Unable to write configuration key\n");
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
+
+ if (regmap_write(priv->regmap, ABX8XX_REG_CFG_KEY, key) < 0) {
+ dev_err(dev, "Unable to write configuration key\n");
return -EIO;
}
return 0;
}
-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);
+ unsigned int flags = 0;
+ int err;
- 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);
+ if (err < 0) {
+ dev_err(dev, "Failed to read autocalibration attribute\n");
+ return err;
}
return (flags & ABX8XX_OSS_OMODE) ? 1 : 0;
}
-static int abx80x_enable_trickle_charger(struct i2c_client *client,
- u8 trickle_cfg)
+static int abx80x_enable_trickle_charger(struct device *dev, u8 trickle_cfg)
{
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
int err;
/*
* Write the configuration key register to enable access to the Trickle
* register
*/
- if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_MISC) < 0)
+ if (abx80x_write_config_key(dev, ABX8XX_CFG_KEY_MISC) < 0)
return -EIO;
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_TRICKLE,
- ABX8XX_TRICKLE_CHARGE_ENABLE |
- trickle_cfg);
+ err = regmap_write(priv->regmap, ABX8XX_REG_TRICKLE,
+ ABX8XX_TRICKLE_CHARGE_ENABLE | trickle_cfg);
if (err < 0) {
- dev_err(&client->dev, "Unable to write trickle register\n");
+ dev_err(dev, "Unable to write trickle register\n");
return -EIO;
}
@@ -187,19 +190,20 @@ static int abx80x_enable_trickle_charger(struct i2c_client *client,
static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
unsigned char buf[8];
- int err, flags, rc_mode = 0;
+ unsigned int flags;
+ int err, rc_mode = 0;
/* Read the Oscillator Failure only in XT mode */
- rc_mode = abx80x_is_rc_mode(client);
+ rc_mode = abx80x_is_rc_mode(dev);
if (rc_mode < 0)
return rc_mode;
if (!rc_mode) {
- flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS);
- if (flags < 0)
- return flags;
+ err = regmap_read(priv->regmap, ABX8XX_REG_OSS, &flags);
+ if (err < 0)
+ return err;
if (flags & ABX8XX_OSS_OF) {
dev_err(dev, "Oscillator failure, data is invalid.\n");
@@ -207,10 +211,9 @@ static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
}
}
- err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_HTH,
- sizeof(buf), buf);
+ err = regmap_bulk_read(priv->regmap, ABX8XX_REG_HTH, buf, sizeof(buf));
if (err < 0) {
- dev_err(&client->dev, "Unable to read date\n");
+ dev_err(dev, "Unable to read date\n");
return -EIO;
}
@@ -227,8 +230,7 @@ static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct abx80x_priv *priv = i2c_get_clientdata(client);
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
unsigned char buf[8];
int err, flags;
@@ -246,22 +248,22 @@ static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
guard(mutex)(&priv->lock);
- err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_HTH,
- sizeof(buf), buf);
+ err = regmap_bulk_write(priv->regmap, ABX8XX_REG_HTH, buf,
+ sizeof(buf));
if (err < 0) {
- dev_err(&client->dev, "Unable to write to date registers\n");
+ dev_err(dev, "Unable to write to date registers\n");
return -EIO;
}
/* Clear the OF bit of Oscillator Status Register */
- flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS);
- if (flags < 0)
- return flags;
+ err = regmap_read(priv->regmap, ABX8XX_REG_OSS, &flags);
+ if (err < 0)
+ return err;
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSS,
- flags & ~ABX8XX_OSS_OF);
+ err = regmap_write(priv->regmap, ABX8XX_REG_OSS,
+ flags & ~ABX8XX_OSS_OF);
if (err < 0) {
- dev_err(&client->dev, "Unable to write oscillator status register\n");
+ dev_err(dev, "Unable to write oscillator status register\n");
return err;
}
@@ -270,16 +272,17 @@ 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;
irqreturn_t handled = IRQ_NONE;
- int status;
+ unsigned int status;
+ int err;
guard(mutex)(&priv->lock);
- status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
- if (status < 0)
+ err = regmap_read(priv->regmap, ABX8XX_REG_STATUS, &status);
+ if (err < 0)
return handled;
if (status & ABX8XX_STATUS_AF) {
@@ -292,36 +295,35 @@ static irqreturn_t abx80x_handle_irq(int irq, void *dev_id)
* reset kicks in.
*/
if (status & ABX8XX_STATUS_WDT) {
- dev_alert(&client->dev, "watchdog timeout interrupt.\n");
+ dev_alert(dev, "watchdog timeout interrupt.\n");
handled = IRQ_HANDLED;
}
if (handled == IRQ_HANDLED)
- i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS,
- status & ~(ABX8XX_STATUS_AF | ABX8XX_STATUS_WDT));
+ regmap_write(priv->regmap, ABX8XX_REG_STATUS,
+ status & ~(ABX8XX_STATUS_AF | ABX8XX_STATUS_WDT));
return handled;
}
static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct abx80x_priv *priv = i2c_get_clientdata(client);
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
unsigned char buf[7];
- int irq_mask, err;
+ unsigned int irq_mask;
+ int err;
if (priv->irq <= 0)
return -EINVAL;
- err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ASC,
- sizeof(buf), buf);
- if (err < 0)
+ 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);
+ if (err < 0)
+ return err;
t->time.tm_sec = bcd2bin(buf[0] & 0x7F);
t->time.tm_min = bcd2bin(buf[1] & 0x7F);
@@ -338,8 +340,7 @@ static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct abx80x_priv *priv = i2c_get_clientdata(client);
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
u8 alarm[6];
int err;
@@ -355,17 +356,16 @@ static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t)
guard(mutex)(&priv->lock);
- err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_AHTH,
- sizeof(alarm), alarm);
+ err = regmap_bulk_write(priv->regmap, ABX8XX_REG_AHTH,
+ alarm, sizeof(alarm));
if (err < 0) {
- dev_err(&client->dev, "Unable to write alarm registers\n");
+ dev_err(dev, "Unable to write alarm registers\n");
return -EIO;
}
if (t->enabled) {
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ,
- (ABX8XX_IRQ_IM_1_4 |
- ABX8XX_IRQ_AIE));
+ err = regmap_write(priv->regmap, ABX8XX_REG_IRQ,
+ ABX8XX_IRQ_IM_1_4 | ABX8XX_IRQ_AIE);
if (err)
return err;
}
@@ -376,9 +376,9 @@ static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t)
static int abx80x_rtc_set_autocalibration(struct device *dev,
int autocalibration)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct abx80x_priv *priv = i2c_get_clientdata(client);
- int retval, flags = 0;
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
+ unsigned int flags = 0;
+ int retval;
if ((autocalibration != 0) && (autocalibration != 1024) &&
(autocalibration != 512)) {
@@ -388,9 +388,9 @@ static int abx80x_rtc_set_autocalibration(struct device *dev,
guard(mutex)(&priv->lock);
- flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
- if (flags < 0)
- return flags;
+ retval = regmap_read(priv->regmap, ABX8XX_REG_OSC, &flags);
+ if (retval < 0)
+ return retval;
if (autocalibration == 0) {
flags &= ~(ABX8XX_OSC_ACAL_512 | ABX8XX_OSC_ACAL_1024);
@@ -404,22 +404,23 @@ 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);
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);
+ unsigned int flags = 0;
+ int 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);
+ if (err < 0)
+ return err;
if (flags & ABX8XX_OSC_ACAL_512)
autocalibration = 512;
@@ -470,9 +471,9 @@ static ssize_t oscillator_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev->parent);
- struct abx80x_priv *priv = i2c_get_clientdata(client);
- int retval, flags, rc_mode = 0;
+ struct abx80x_priv *priv = dev_get_drvdata(dev->parent);
+ int retval, rc_mode = 0;
+ unsigned int flags;
if (strncmp(buf, "rc", 2) == 0) {
rc_mode = 1;
@@ -485,9 +486,9 @@ static ssize_t oscillator_store(struct device *dev,
guard(mutex)(&priv->lock);
- flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
- if (flags < 0)
- return flags;
+ retval = regmap_read(priv->regmap, ABX8XX_REG_OSC, &flags);
+ if (retval < 0)
+ return retval;
if (rc_mode == 0)
flags &= ~(ABX8XX_OSC_OSEL);
@@ -495,10 +496,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);
if (retval < 0) {
dev_err(dev, "Failed to write Oscillator Control register\n");
return retval;
@@ -511,9 +512,8 @@ static ssize_t oscillator_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc_mode = 0;
- struct i2c_client *client = to_i2c_client(dev->parent);
- rc_mode = abx80x_is_rc_mode(client);
+ rc_mode = abx80x_is_rc_mode(dev->parent);
if (rc_mode < 0) {
dev_err(dev, "Failed to read RTC oscillator selection\n");
@@ -541,33 +541,31 @@ static const struct attribute_group rtc_calib_attr_group = {
static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct abx80x_priv *priv = i2c_get_clientdata(client);
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
int err;
guard(mutex)(&priv->lock);
if (enabled)
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ,
- (ABX8XX_IRQ_IM_1_4 |
- ABX8XX_IRQ_AIE));
+ err = regmap_write(priv->regmap, ABX8XX_REG_IRQ,
+ ABX8XX_IRQ_IM_1_4 | ABX8XX_IRQ_AIE);
else
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ,
- ABX8XX_IRQ_IM_1_4);
+ err = regmap_write(priv->regmap, ABX8XX_REG_IRQ,
+ ABX8XX_IRQ_IM_1_4);
return err;
}
static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct abx80x_priv *priv = i2c_get_clientdata(client);
- int status, tmp;
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
+ unsigned int status;
+ int err, 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);
+ if (err < 0)
+ return err;
tmp = status & ABX8XX_STATUS_BLF ? RTC_VL_BACKUP_LOW : 0;
@@ -575,16 +573,15 @@ static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
case RTC_VL_CLR:
scoped_guard(mutex, &priv->lock) {
- status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
- if (status < 0)
- return status;
+ err = regmap_read(priv->regmap, ABX8XX_REG_STATUS, &status);
+ if (err < 0)
+ return err;
status &= ~ABX8XX_STATUS_BLF;
- tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS,
- status);
- if (tmp < 0)
- return tmp;
+ err = regmap_write(priv->regmap, ABX8XX_REG_STATUS, status);
+ if (err < 0)
+ return err;
}
return 0;
@@ -603,9 +600,9 @@ static const struct rtc_class_ops abx80x_rtc_ops = {
.ioctl = abx80x_ioctl,
};
-static int abx80x_dt_trickle_cfg(struct i2c_client *client)
+static int abx80x_dt_trickle_cfg(struct device *dev)
{
- struct device_node *np = client->dev.of_node;
+ struct device_node *np = dev->of_node;
const char *diode;
int trickle_cfg = 0;
int i, ret;
@@ -620,7 +617,7 @@ static int abx80x_dt_trickle_cfg(struct i2c_client *client)
} else if (!strcmp(diode, "schottky")) {
trickle_cfg |= ABX8XX_TRICKLE_SCHOTTKY_DIODE;
} else {
- dev_dbg(&client->dev, "Invalid tc-diode value: %s\n", diode);
+ dev_dbg(dev, "Invalid tc-diode value: %s\n", diode);
return -EINVAL;
}
@@ -633,7 +630,7 @@ static int abx80x_dt_trickle_cfg(struct i2c_client *client)
break;
if (i == sizeof(trickle_resistors)) {
- dev_dbg(&client->dev, "Invalid tc-resistor value: %u\n", tmp);
+ dev_dbg(dev, "Invalid tc-resistor value: %u\n", tmp);
return -EINVAL;
}
@@ -660,7 +657,7 @@ static int __abx80x_wdog_set_timeout(struct watchdog_device *wdog,
* Writing any timeout to the WDT register resets the watchdog timer.
* Writing 0 disables it.
*/
- return i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_WDT, val);
+ return regmap_write(priv->regmap, ABX8XX_REG_WDT, val);
}
static int abx80x_wdog_set_timeout(struct watchdog_device *wdog,
@@ -705,9 +702,11 @@ static const struct watchdog_ops abx80x_wdog_ops = {
.set_timeout = abx80x_wdog_set_timeout,
};
-static int abx80x_setup_watchdog(struct abx80x_priv *priv)
+static int abx80x_setup_watchdog(struct device *dev)
{
- priv->wdog.parent = &priv->client->dev;
+ struct abx80x_priv *priv = dev_get_drvdata(dev);
+
+ priv->wdog.parent = dev;
priv->wdog.ops = &abx80x_wdog_ops;
priv->wdog.info = &abx80x_wdog_info;
priv->wdog.min_timeout = 1;
@@ -716,10 +715,10 @@ static int abx80x_setup_watchdog(struct abx80x_priv *priv)
watchdog_set_drvdata(&priv->wdog, priv);
- return devm_watchdog_register_device(&priv->client->dev, &priv->wdog);
+ return devm_watchdog_register_device(dev, &priv->wdog);
}
#else
-static int abx80x_setup_watchdog(struct abx80x_priv *priv)
+static int abx80x_setup_watchdog(struct device *dev)
{
return 0;
}
@@ -731,33 +730,29 @@ static int abx80x_nvmem_xfer(struct abx80x_priv *priv, unsigned int offset,
int ret;
while (bytes) {
- u8 extram, reg, len, lower, upper;
+ u8 reg, len, lower, upper;
lower = FIELD_GET(NVMEM_ADDR_LOWER, offset);
upper = FIELD_GET(NVMEM_ADDR_UPPER, offset);
- extram = FIELD_PREP(ABX8XX_EXTRAM_XADS, upper);
reg = ABX8XX_SRAM_BASE + lower;
len = min(lower + bytes, (size_t)ABX8XX_SRAM_WIN_SIZE) - lower;
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
guard(mutex)(&priv->lock);
- ret = i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_EXTRAM,
- extram);
+ ret = regmap_update_bits(priv->regmap, ABX8XX_REG_EXTRAM,
+ ABX8XX_EXTRAM_XADS, upper);
if (ret)
return ret;
if (write) {
- ret = i2c_smbus_write_i2c_block_data(priv->client, reg,
- len, val);
+ ret = regmap_bulk_write(priv->regmap, reg, val, len);
if (ret)
return ret;
} else {
- ret = i2c_smbus_read_i2c_block_data(priv->client, reg,
- len, val);
- if (ret <= 0)
- return ret ? ret : -EIO;
- len = ret;
+ ret = regmap_bulk_read(priv->regmap, reg, val, len);
+ if (ret)
+ return ret;
}
offset += len;
@@ -793,6 +788,38 @@ static int abx80x_setup_nvmem(struct abx80x_priv *priv)
return devm_rtc_nvmem_register(priv->rtc, &config);
}
+static const struct regmap_range abx80x_no_read_ranges[] = {
+ regmap_reg_range(0x1e, 0x1e),
+ regmap_reg_range(0x22, 0x25),
+ regmap_reg_range(0x31, 0x3e),
+};
+
+static const struct regmap_range abx80x_no_write_ranges[] = {
+ regmap_reg_range(0x1e, 0x1e),
+ regmap_reg_range(0x22, 0x25),
+ regmap_reg_range(ABX8XX_REG_ID0, ABX8XX_REG_ID0 + 6),
+ regmap_reg_range(0x31, 0x3e),
+};
+
+static const struct regmap_access_table abx80x_read_table = {
+ .no_ranges = abx80x_no_read_ranges,
+ .n_no_ranges = ARRAY_SIZE(abx80x_no_read_ranges),
+};
+
+static const struct regmap_access_table abx80x_write_table = {
+ .no_ranges = abx80x_no_write_ranges,
+ .n_no_ranges = ARRAY_SIZE(abx80x_no_write_ranges),
+};
+
+static const struct regmap_config abx80x_regmap_config_i2c = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ABX8XX_SRAM_BASE + ABX8XX_SRAM_WIN_SIZE - 1,
+
+ .rd_table = &abx80x_read_table,
+ .wr_table = &abx80x_write_table,
+};
+
static const struct i2c_device_id abx80x_id[] = {
{ .name = "abx80x", .driver_data = ABX80X },
{ .name = "ab0801", .driver_data = AB0801 },
@@ -810,9 +837,11 @@ MODULE_DEVICE_TABLE(i2c, abx80x_id);
static int abx80x_probe(struct i2c_client *client)
{
+ struct regmap *regmap;
struct device_node *np = client->dev.of_node;
struct abx80x_priv *priv;
- int i, data, err, trickle_cfg = -EINVAL;
+ int i, err, trickle_cfg = -EINVAL;
+ unsigned int data;
char buf[7];
unsigned int part = (uintptr_t)i2c_get_match_data(client);
unsigned int partnumber;
@@ -824,8 +853,30 @@ static int abx80x_probe(struct i2c_client *client)
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
- err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ID0,
- sizeof(buf), buf);
+ regmap = devm_regmap_init_i2c(client, &abx80x_regmap_config_i2c);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Unable to allocate regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ priv->rtc = devm_rtc_allocate_device(&client->dev);
+ if (IS_ERR(priv->rtc))
+ return PTR_ERR(priv->rtc);
+
+ priv->rtc->ops = &abx80x_rtc_ops;
+ priv->irq = client->irq;
+ priv->regmap = regmap;
+ err = devm_mutex_init(&client->dev, &priv->lock);
+ if (err)
+ return err;
+
+ dev_set_drvdata(&client->dev, priv);
+
+ err = regmap_bulk_read(regmap, ABX8XX_REG_ID0, buf, sizeof(buf));
if (err < 0) {
dev_err(&client->dev, "Unable to read partnumber\n");
return -EIO;
@@ -840,16 +891,14 @@ static int abx80x_probe(struct i2c_client *client)
dev_info(&client->dev, "model %04x, revision %u.%u, lot %x, wafer %x, uid %x\n",
partnumber, majrev, minrev, lot, wafer, uid);
- data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL1);
- if (data < 0) {
+ err = regmap_read(regmap, ABX8XX_REG_CTRL1, &data);
+ if (err < 0) {
dev_err(&client->dev, "Unable to read control register\n");
return -EIO;
}
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL1,
- ((data & ~(ABX8XX_CTRL_12_24 |
- ABX8XX_CTRL_ARST)) |
- ABX8XX_CTRL_WRITE));
+ err = regmap_write(regmap, ABX8XX_REG_CTRL1,
+ (data & ~(ABX8XX_CTRL_12_24 | ABX8XX_CTRL_ARST)) | ABX8XX_CTRL_WRITE);
if (err < 0) {
dev_err(&client->dev, "Unable to write control register\n");
return -EIO;
@@ -863,15 +912,15 @@ static int abx80x_probe(struct i2c_client *client)
* register is set. RV-1805-C3 datasheet indicates that
* the bit should be cleared in section 11h - Control2.
*/
- data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL2);
- if (data < 0) {
+ err = regmap_read(regmap, ABX8XX_REG_CTRL2, &data);
+ if (err < 0) {
dev_err(&client->dev,
"Unable to read control2 register\n");
return -EIO;
}
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL2,
- data & ~ABX8XX_CTRL2_RSVD);
+ err = regmap_write(regmap, ABX8XX_REG_CTRL2,
+ data & ~ABX8XX_CTRL2_RSVD);
if (err < 0) {
dev_err(&client->dev,
"Unable to write control2 register\n");
@@ -883,8 +932,8 @@ static int abx80x_probe(struct i2c_client *client)
* 10pin package and the EXTI input is not present.
* Disable it to avoid leakage.
*/
- data = i2c_smbus_read_byte_data(client, ABX8XX_REG_OUT_CTRL);
- if (data < 0) {
+ err = regmap_read(regmap, ABX8XX_REG_OUT_CTRL, &data);
+ if (err < 0) {
dev_err(&client->dev,
"Unable to read output control register\n");
return -EIO;
@@ -894,11 +943,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);
if (err < 0) {
dev_err(&client->dev,
"Unable to write output control register\n");
@@ -926,38 +975,20 @@ static int abx80x_probe(struct i2c_client *client)
}
if (np && abx80x_caps[part].has_tc)
- trickle_cfg = abx80x_dt_trickle_cfg(client);
+ trickle_cfg = abx80x_dt_trickle_cfg(&client->dev);
if (trickle_cfg > 0) {
dev_info(&client->dev, "Enabling trickle charger: %02x\n",
trickle_cfg);
- abx80x_enable_trickle_charger(client, trickle_cfg);
+ abx80x_enable_trickle_charger(&client->dev, trickle_cfg);
}
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CD_TIMER_CTL,
- BIT(2));
- if (err)
- return err;
-
- priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
- if (priv == NULL)
- return -ENOMEM;
-
- priv->rtc = devm_rtc_allocate_device(&client->dev);
- if (IS_ERR(priv->rtc))
- return PTR_ERR(priv->rtc);
-
- priv->rtc->ops = &abx80x_rtc_ops;
- priv->client = client;
- priv->irq = client->irq;
- err = devm_mutex_init(&client->dev, &priv->lock);
+ err = regmap_write(regmap, ABX8XX_REG_CD_TIMER_CTL, BIT(2));
if (err)
return err;
- i2c_set_clientdata(client, priv);
-
if (abx80x_caps[part].has_wdog) {
- err = abx80x_setup_watchdog(priv);
+ err = abx80x_setup_watchdog(&client->dev);
if (err)
return err;
}
@@ -967,31 +998,31 @@ static int abx80x_probe(struct i2c_client *client)
return err;
/* Disable unused interrupts */
- data = i2c_smbus_read_byte_data(client, ABX8XX_REG_IRQ);
- if (data < 0) {
+ err = regmap_read(regmap, ABX8XX_REG_IRQ, &data);
+ if (err < 0) {
dev_err(&client->dev, "Unable to read irq register\n");
return -EIO;
}
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ,
- data & ~(ABX8XX_IRQ_EX1E |
- ABX8XX_IRQ_EX2E |
- ABX8XX_IRQ_TIE |
- ABX8XX_IRQ_BLIE));
+ err = regmap_write(regmap, ABX8XX_REG_IRQ,
+ data & ~(ABX8XX_IRQ_EX1E |
+ ABX8XX_IRQ_EX2E |
+ ABX8XX_IRQ_TIE |
+ ABX8XX_IRQ_BLIE));
if (err < 0) {
dev_err(&client->dev, "Unable to write irq register\n");
return -EIO;
}
- data = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
- if (data < 0) {
+ err = regmap_read(regmap, ABX8XX_REG_OSC, &data);
+ if (err < 0) {
dev_err(&client->dev, "Unable to read Oscillator Control register\n");
return -EIO;
}
- err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC,
- data & ~(ABX8XX_OSC_ACIE |
- ABX8XX_OSC_OFIE));
+ err = regmap_write(regmap, ABX8XX_REG_OSC,
+ data & ~(ABX8XX_OSC_ACIE |
+ ABX8XX_OSC_OFIE));
if (err < 0) {
dev_err(&client->dev, "Unable to write Oscillator Control register\n");
return -EIO;
@@ -1003,7 +1034,7 @@ static int abx80x_probe(struct i2c_client *client)
abx80x_handle_irq,
IRQF_SHARED | IRQF_ONESHOT,
"abx8xx",
- client);
+ &client->dev);
if (err) {
dev_err(&client->dev, "unable to request IRQ, alarms disabled\n");
priv->irq = 0;
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v4 7/9] rtc: abx80x: replace read-modify-write pattern with regmap helpers
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
` (5 preceding siblings ...)
2026-08-10 19:38 ` [PATCH v4 6/9] rtc: abx80x: use regmap instead of I2C specific API Antoni Pokusinski
@ 2026-08-10 19:38 ` Antoni Pokusinski
2026-08-14 2:07 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 8/9] rtc: abx80x: create abx80x_i2c_probe() Antoni Pokusinski
2026-08-10 19:38 ` [PATCH v4 9/9] rtc: abx80x: add support for ABX81X Antoni Pokusinski
8 siblings, 1 reply; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski
Before introducing the regmap usage in the driver, updating specific
bits of a register was performed using the read-modify-write pattern.
Now, the functions regmap_update_bits() and regmap_write_bits() can
be used to simplify the code.
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
---
drivers/rtc/rtc-abx80x.c | 135 ++++++++++-----------------------------
1 file changed, 35 insertions(+), 100 deletions(-)
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 5d41882f3c70..ab88af43b4fa 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -232,7 +232,7 @@ static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct abx80x_priv *priv = dev_get_drvdata(dev);
unsigned char buf[8];
- int err, flags;
+ int err;
if (tm->tm_year < 100)
return -EINVAL;
@@ -256,18 +256,11 @@ static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
}
/* Clear the OF bit of Oscillator Status Register */
- err = regmap_read(priv->regmap, ABX8XX_REG_OSS, &flags);
+ err = regmap_update_bits(priv->regmap, ABX8XX_REG_OSS, ABX8XX_OSS_OF, 0);
if (err < 0)
- return err;
-
- err = regmap_write(priv->regmap, ABX8XX_REG_OSS,
- flags & ~ABX8XX_OSS_OF);
- if (err < 0) {
dev_err(dev, "Unable to write oscillator status register\n");
- return err;
- }
- return 0;
+ return err;
}
static irqreturn_t abx80x_handle_irq(int irq, void *dev_id)
@@ -378,7 +371,6 @@ static int abx80x_rtc_set_autocalibration(struct device *dev,
{
struct abx80x_priv *priv = dev_get_drvdata(dev);
unsigned int flags = 0;
- int retval;
if ((autocalibration != 0) && (autocalibration != 1024) &&
(autocalibration != 512)) {
@@ -386,12 +378,6 @@ static int abx80x_rtc_set_autocalibration(struct device *dev,
return -EINVAL;
}
- guard(mutex)(&priv->lock);
-
- retval = regmap_read(priv->regmap, ABX8XX_REG_OSC, &flags);
- if (retval < 0)
- return retval;
-
if (autocalibration == 0) {
flags &= ~(ABX8XX_OSC_ACAL_512 | ABX8XX_OSC_ACAL_1024);
} else if (autocalibration == 1024) {
@@ -403,13 +389,15 @@ static int abx80x_rtc_set_autocalibration(struct device *dev,
flags |= (ABX8XX_OSC_ACAL_1024 | ABX8XX_OSC_ACAL_512);
}
+ guard(mutex)(&priv->lock);
+
/* Unlock write access to Oscillator Control Register */
if (abx80x_write_config_key(dev, ABX8XX_CFG_KEY_OSC) < 0)
return -EIO;
- retval = regmap_write(priv->regmap, ABX8XX_REG_OSC, flags);
-
- return retval;
+ return regmap_write_bits(priv->regmap, ABX8XX_REG_OSC,
+ ABX8XX_OSC_ACAL_1024 | ABX8XX_OSC_ACAL_512,
+ flags);
}
static int abx80x_rtc_get_autocalibration(struct device *dev)
@@ -473,7 +461,6 @@ static ssize_t oscillator_store(struct device *dev,
{
struct abx80x_priv *priv = dev_get_drvdata(dev->parent);
int retval, rc_mode = 0;
- unsigned int flags;
if (strncmp(buf, "rc", 2) == 0) {
rc_mode = 1;
@@ -486,24 +473,14 @@ static ssize_t oscillator_store(struct device *dev,
guard(mutex)(&priv->lock);
- retval = regmap_read(priv->regmap, ABX8XX_REG_OSC, &flags);
- if (retval < 0)
- return retval;
-
- if (rc_mode == 0)
- flags &= ~(ABX8XX_OSC_OSEL);
- else
- flags |= (ABX8XX_OSC_OSEL);
-
/* Unlock write access on Oscillator Control register */
if (abx80x_write_config_key(dev->parent, ABX8XX_CFG_KEY_OSC) < 0)
return -EIO;
- retval = regmap_write(priv->regmap, ABX8XX_REG_OSC, flags);
- if (retval < 0) {
+ retval = regmap_write_bits(priv->regmap, ABX8XX_REG_OSC, ABX8XX_OSC_OSEL,
+ rc_mode == 0 ? 0 : (ABX8XX_OSC_OSEL));
+ if (retval < 0)
dev_err(dev, "Failed to write Oscillator Control register\n");
- return retval;
- }
return retval ? retval : count;
}
@@ -573,18 +550,11 @@ static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
case RTC_VL_CLR:
scoped_guard(mutex, &priv->lock) {
- err = regmap_read(priv->regmap, ABX8XX_REG_STATUS, &status);
- if (err < 0)
- return err;
-
- status &= ~ABX8XX_STATUS_BLF;
-
- err = regmap_write(priv->regmap, ABX8XX_REG_STATUS, status);
- if (err < 0)
- return err;
+ err = regmap_update_bits(priv->regmap, ABX8XX_REG_STATUS,
+ ABX8XX_STATUS_BLF, 0);
}
- return 0;
+ return err;
default:
return -ENOIOCTLCMD;
@@ -841,7 +811,6 @@ static int abx80x_probe(struct i2c_client *client)
struct device_node *np = client->dev.of_node;
struct abx80x_priv *priv;
int i, err, trickle_cfg = -EINVAL;
- unsigned int data;
char buf[7];
unsigned int part = (uintptr_t)i2c_get_match_data(client);
unsigned int partnumber;
@@ -891,14 +860,9 @@ static int abx80x_probe(struct i2c_client *client)
dev_info(&client->dev, "model %04x, revision %u.%u, lot %x, wafer %x, uid %x\n",
partnumber, majrev, minrev, lot, wafer, uid);
- err = regmap_read(regmap, ABX8XX_REG_CTRL1, &data);
- if (err < 0) {
- dev_err(&client->dev, "Unable to read control register\n");
- return -EIO;
- }
-
- err = regmap_write(regmap, ABX8XX_REG_CTRL1,
- (data & ~(ABX8XX_CTRL_12_24 | ABX8XX_CTRL_ARST)) | ABX8XX_CTRL_WRITE);
+ err = regmap_update_bits(regmap, ABX8XX_REG_CTRL1,
+ ABX8XX_CTRL_12_24 | ABX8XX_CTRL_ARST | ABX8XX_CTRL_WRITE,
+ ABX8XX_CTRL_WRITE);
if (err < 0) {
dev_err(&client->dev, "Unable to write control register\n");
return -EIO;
@@ -912,30 +876,10 @@ static int abx80x_probe(struct i2c_client *client)
* register is set. RV-1805-C3 datasheet indicates that
* the bit should be cleared in section 11h - Control2.
*/
- err = regmap_read(regmap, ABX8XX_REG_CTRL2, &data);
- if (err < 0) {
- dev_err(&client->dev,
- "Unable to read control2 register\n");
- return -EIO;
- }
-
- err = regmap_write(regmap, ABX8XX_REG_CTRL2,
- data & ~ABX8XX_CTRL2_RSVD);
- if (err < 0) {
- dev_err(&client->dev,
- "Unable to write control2 register\n");
- return -EIO;
- }
-
- /*
- * Avoid extra power leakage. The RV1805 uses smaller
- * 10pin package and the EXTI input is not present.
- * Disable it to avoid leakage.
- */
- err = regmap_read(regmap, ABX8XX_REG_OUT_CTRL, &data);
+ err = regmap_update_bits(regmap, ABX8XX_REG_CTRL2,
+ ABX8XX_CTRL2_RSVD, 0);
if (err < 0) {
- dev_err(&client->dev,
- "Unable to read output control register\n");
+ dev_err(&client->dev, "Unable to write control2 register\n");
return -EIO;
}
@@ -946,8 +890,14 @@ static int abx80x_probe(struct i2c_client *client)
if (abx80x_write_config_key(&client->dev, ABX8XX_CFG_KEY_MISC) < 0)
return -EIO;
- err = regmap_write(regmap, ABX8XX_REG_OUT_CTRL,
- data | ABX8XX_OUT_CTRL_EXDS);
+ /*
+ * Avoid extra power leakage. The RV1805 uses smaller
+ * 10pin package and the EXTI input is not present.
+ * Disable it to avoid leakage.
+ */
+ err = regmap_write_bits(regmap, ABX8XX_REG_OUT_CTRL,
+ ABX8XX_OUT_CTRL_EXDS,
+ ABX8XX_OUT_CTRL_EXDS);
if (err < 0) {
dev_err(&client->dev,
"Unable to write output control register\n");
@@ -998,33 +948,18 @@ static int abx80x_probe(struct i2c_client *client)
return err;
/* Disable unused interrupts */
- err = regmap_read(regmap, ABX8XX_REG_IRQ, &data);
- if (err < 0) {
- dev_err(&client->dev, "Unable to read irq register\n");
- return -EIO;
- }
-
- err = regmap_write(regmap, ABX8XX_REG_IRQ,
- data & ~(ABX8XX_IRQ_EX1E |
- ABX8XX_IRQ_EX2E |
- ABX8XX_IRQ_TIE |
- ABX8XX_IRQ_BLIE));
- if (err < 0) {
- dev_err(&client->dev, "Unable to write irq register\n");
- return -EIO;
- }
-
- err = regmap_read(regmap, ABX8XX_REG_OSC, &data);
+ err = regmap_update_bits(regmap, ABX8XX_REG_IRQ,
+ ABX8XX_IRQ_EX1E | ABX8XX_IRQ_EX2E |
+ ABX8XX_IRQ_TIE | ABX8XX_IRQ_BLIE, 0);
if (err < 0) {
- dev_err(&client->dev, "Unable to read Oscillator Control register\n");
+ dev_err(&client->dev, "Unable to update irq register\n");
return -EIO;
}
- err = regmap_write(regmap, ABX8XX_REG_OSC,
- data & ~(ABX8XX_OSC_ACIE |
- ABX8XX_OSC_OFIE));
+ err = regmap_update_bits(regmap, ABX8XX_REG_OSC,
+ ABX8XX_OSC_ACIE | ABX8XX_OSC_OFIE, 0);
if (err < 0) {
- dev_err(&client->dev, "Unable to write Oscillator Control register\n");
+ dev_err(&client->dev, "Unable to update Oscillator Control register\n");
return -EIO;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v4 8/9] rtc: abx80x: create abx80x_i2c_probe()
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
` (6 preceding siblings ...)
2026-08-10 19:38 ` [PATCH v4 7/9] rtc: abx80x: replace read-modify-write pattern with regmap helpers Antoni Pokusinski
@ 2026-08-10 19:38 ` Antoni Pokusinski
2026-08-14 2:15 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 9/9] rtc: abx80x: add support for ABX81X Antoni Pokusinski
8 siblings, 1 reply; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski
Move the I2C-specific code from abx80x_probe() to the newly created
function. This is a preparation for introducing the support for
ABX81X RTCs which use SPI interface.
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
---
drivers/rtc/rtc-abx80x.c | 171 ++++++++++++++++++++++++---------------
1 file changed, 105 insertions(+), 66 deletions(-)
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index ab88af43b4fa..08b22c067699 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -781,73 +781,38 @@ static const struct regmap_access_table abx80x_write_table = {
.n_no_ranges = ARRAY_SIZE(abx80x_no_write_ranges),
};
-static const struct regmap_config abx80x_regmap_config_i2c = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = ABX8XX_SRAM_BASE + ABX8XX_SRAM_WIN_SIZE - 1,
-
- .rd_table = &abx80x_read_table,
- .wr_table = &abx80x_write_table,
-};
-
-static const struct i2c_device_id abx80x_id[] = {
- { .name = "abx80x", .driver_data = ABX80X },
- { .name = "ab0801", .driver_data = AB0801 },
- { .name = "ab0803", .driver_data = AB0803 },
- { .name = "ab0804", .driver_data = AB0804 },
- { .name = "ab0805", .driver_data = AB0805 },
- { .name = "ab1801", .driver_data = AB1801 },
- { .name = "ab1803", .driver_data = AB1803 },
- { .name = "ab1804", .driver_data = AB1804 },
- { .name = "ab1805", .driver_data = AB1805 },
- { .name = "rv1805", .driver_data = RV1805 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, abx80x_id);
-
-static int abx80x_probe(struct i2c_client *client)
+static int abx80x_probe(struct device *dev, struct regmap *regmap, int irq,
+ struct device_node *np, unsigned int part)
{
- struct regmap *regmap;
- struct device_node *np = client->dev.of_node;
struct abx80x_priv *priv;
int i, err, trickle_cfg = -EINVAL;
char buf[7];
- unsigned int part = (uintptr_t)i2c_get_match_data(client);
unsigned int partnumber;
unsigned int majrev, minrev;
unsigned int lot;
unsigned int wafer;
unsigned int uid;
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
- return -ENODEV;
-
- regmap = devm_regmap_init_i2c(client, &abx80x_regmap_config_i2c);
- if (IS_ERR(regmap)) {
- dev_err(&client->dev, "Unable to allocate regmap\n");
- return PTR_ERR(regmap);
- }
-
- priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
- priv->rtc = devm_rtc_allocate_device(&client->dev);
+ priv->rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(priv->rtc))
return PTR_ERR(priv->rtc);
priv->rtc->ops = &abx80x_rtc_ops;
- priv->irq = client->irq;
+ priv->irq = irq;
priv->regmap = regmap;
- err = devm_mutex_init(&client->dev, &priv->lock);
+ err = devm_mutex_init(dev, &priv->lock);
if (err)
return err;
- dev_set_drvdata(&client->dev, priv);
+ dev_set_drvdata(dev, priv);
err = regmap_bulk_read(regmap, ABX8XX_REG_ID0, buf, sizeof(buf));
if (err < 0) {
- dev_err(&client->dev, "Unable to read partnumber\n");
+ dev_err(dev, "Unable to read partnumber\n");
return -EIO;
}
@@ -857,14 +822,14 @@ static int abx80x_probe(struct i2c_client *client)
lot = ((buf[4] & 0x80) << 2) | ((buf[6] & 0x80) << 1) | buf[3];
uid = ((buf[4] & 0x7f) << 8) | buf[5];
wafer = (buf[6] & 0x7c) >> 2;
- dev_info(&client->dev, "model %04x, revision %u.%u, lot %x, wafer %x, uid %x\n",
+ dev_info(dev, "model %04x, revision %u.%u, lot %x, wafer %x, uid %x\n",
partnumber, majrev, minrev, lot, wafer, uid);
err = regmap_update_bits(regmap, ABX8XX_REG_CTRL1,
ABX8XX_CTRL_12_24 | ABX8XX_CTRL_ARST | ABX8XX_CTRL_WRITE,
ABX8XX_CTRL_WRITE);
if (err < 0) {
- dev_err(&client->dev, "Unable to write control register\n");
+ dev_err(dev, "Unable to write control register\n");
return -EIO;
}
@@ -879,7 +844,7 @@ static int abx80x_probe(struct i2c_client *client)
err = regmap_update_bits(regmap, ABX8XX_REG_CTRL2,
ABX8XX_CTRL2_RSVD, 0);
if (err < 0) {
- dev_err(&client->dev, "Unable to write control2 register\n");
+ dev_err(dev, "Unable to write control2 register\n");
return -EIO;
}
@@ -887,7 +852,7 @@ 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->dev, ABX8XX_CFG_KEY_MISC) < 0)
+ if (abx80x_write_config_key(dev, ABX8XX_CFG_KEY_MISC) < 0)
return -EIO;
/*
@@ -899,7 +864,7 @@ static int abx80x_probe(struct i2c_client *client)
ABX8XX_OUT_CTRL_EXDS,
ABX8XX_OUT_CTRL_EXDS);
if (err < 0) {
- dev_err(&client->dev,
+ dev_err(dev,
"Unable to write output control register\n");
return -EIO;
}
@@ -911,26 +876,24 @@ static int abx80x_probe(struct i2c_client *client)
if (partnumber == abx80x_caps[i].pn)
break;
if (abx80x_caps[i].pn == 0) {
- dev_err(&client->dev, "Unknown part: %04x\n",
- partnumber);
+ dev_err(dev, "Unknown part: %04x\n", partnumber);
return -EINVAL;
}
part = i;
}
if (partnumber != abx80x_caps[part].pn) {
- dev_err(&client->dev, "partnumber mismatch %04x != %04x\n",
+ dev_err(dev, "partnumber mismatch %04x != %04x\n",
partnumber, abx80x_caps[part].pn);
return -EINVAL;
}
if (np && abx80x_caps[part].has_tc)
- trickle_cfg = abx80x_dt_trickle_cfg(&client->dev);
+ trickle_cfg = abx80x_dt_trickle_cfg(dev);
if (trickle_cfg > 0) {
- dev_info(&client->dev, "Enabling trickle charger: %02x\n",
- trickle_cfg);
- abx80x_enable_trickle_charger(&client->dev, trickle_cfg);
+ dev_info(dev, "Enabling trickle charger: %02x\n", trickle_cfg);
+ abx80x_enable_trickle_charger(dev, trickle_cfg);
}
err = regmap_write(regmap, ABX8XX_REG_CD_TIMER_CTL, BIT(2));
@@ -938,7 +901,7 @@ static int abx80x_probe(struct i2c_client *client)
return err;
if (abx80x_caps[part].has_wdog) {
- err = abx80x_setup_watchdog(&client->dev);
+ err = abx80x_setup_watchdog(dev);
if (err)
return err;
}
@@ -952,26 +915,26 @@ static int abx80x_probe(struct i2c_client *client)
ABX8XX_IRQ_EX1E | ABX8XX_IRQ_EX2E |
ABX8XX_IRQ_TIE | ABX8XX_IRQ_BLIE, 0);
if (err < 0) {
- dev_err(&client->dev, "Unable to update irq register\n");
+ dev_err(dev, "Unable to update irq register\n");
return -EIO;
}
err = regmap_update_bits(regmap, ABX8XX_REG_OSC,
ABX8XX_OSC_ACIE | ABX8XX_OSC_OFIE, 0);
if (err < 0) {
- dev_err(&client->dev, "Unable to update Oscillator Control register\n");
+ dev_err(dev, "Unable to update Oscillator Control register\n");
return -EIO;
}
if (priv->irq > 0) {
- dev_info(&client->dev, "IRQ %d supplied\n", priv->irq);
- err = devm_request_threaded_irq(&client->dev, priv->irq, NULL,
+ dev_info(dev, "IRQ %d supplied\n", priv->irq);
+ err = devm_request_threaded_irq(dev, priv->irq, NULL,
abx80x_handle_irq,
IRQF_SHARED | IRQF_ONESHOT,
"abx8xx",
- &client->dev);
+ dev);
if (err) {
- dev_err(&client->dev, "unable to request IRQ, alarms disabled\n");
+ dev_err(dev, "unable to request IRQ, alarms disabled\n");
priv->irq = 0;
}
}
@@ -980,14 +943,39 @@ static int abx80x_probe(struct i2c_client *client)
err = rtc_add_group(priv->rtc, &rtc_calib_attr_group);
if (err) {
- dev_err(&client->dev, "Failed to create sysfs group: %d\n",
- err);
+ dev_err(dev, "Failed to create sysfs group: %d\n", err);
return err;
}
return devm_rtc_register_device(priv->rtc);
}
+#if IS_ENABLED(CONFIG_I2C)
+
+static const struct regmap_config abx80x_regmap_config_i2c = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ABX8XX_SRAM_BASE + ABX8XX_SRAM_WIN_SIZE - 1,
+
+ .rd_table = &abx80x_read_table,
+ .wr_table = &abx80x_write_table,
+};
+
+static const struct i2c_device_id abx80x_id[] = {
+ { .name = "abx80x", .driver_data = ABX80X },
+ { .name = "ab0801", .driver_data = AB0801 },
+ { .name = "ab0803", .driver_data = AB0803 },
+ { .name = "ab0804", .driver_data = AB0804 },
+ { .name = "ab0805", .driver_data = AB0805 },
+ { .name = "ab1801", .driver_data = AB1801 },
+ { .name = "ab1803", .driver_data = AB1803 },
+ { .name = "ab1804", .driver_data = AB1804 },
+ { .name = "ab1805", .driver_data = AB1805 },
+ { .name = "rv1805", .driver_data = RV1805 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, abx80x_id);
+
#ifdef CONFIG_OF
static const struct of_device_id abx80x_of_match[] = {
{
@@ -1035,16 +1023,67 @@ static const struct of_device_id abx80x_of_match[] = {
MODULE_DEVICE_TABLE(of, abx80x_of_match);
#endif
+static int abx80x_i2c_probe(struct i2c_client *client)
+{
+ unsigned int part = (uintptr_t)i2c_get_match_data(client);
+ struct regmap *regmap;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ regmap = devm_regmap_init_i2c(client, &abx80x_regmap_config_i2c);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Unable to allocate regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ return abx80x_probe(&client->dev, regmap, client->irq,
+ client->dev.of_node, part);
+}
+
static struct i2c_driver abx80x_driver = {
.driver = {
.name = "rtc-abx80x",
.of_match_table = of_match_ptr(abx80x_of_match),
},
- .probe = abx80x_probe,
+ .probe = abx80x_i2c_probe,
.id_table = abx80x_id,
};
-module_i2c_driver(abx80x_driver);
+static int abx80x_register_driver(void)
+{
+ return i2c_add_driver(&abx80x_driver);
+}
+
+static void abx80x_unregister_driver(void)
+{
+ i2c_del_driver(&abx80x_driver);
+}
+
+#else
+
+static int abx80x_register_driver(void)
+{
+ return 0;
+}
+
+static void abx80x_unregister_driver(void)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_I2C) */
+
+static int __init abx80x_init(void)
+{
+ return abx80x_register_driver();
+}
+module_init(abx80x_init);
+
+static void __exit abx80x_exit(void)
+{
+ abx80x_unregister_driver();
+}
+module_exit(abx80x_exit);
MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v4 9/9] rtc: abx80x: add support for ABX81X
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
` (7 preceding siblings ...)
2026-08-10 19:38 ` [PATCH v4 8/9] rtc: abx80x: create abx80x_i2c_probe() Antoni Pokusinski
@ 2026-08-10 19:38 ` Antoni Pokusinski
2026-08-14 2:31 ` sashiko-bot
8 siblings, 1 reply; 19+ messages in thread
From: Antoni Pokusinski @ 2026-08-10 19:38 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: linux-rtc, devicetree, linux-kernel, Antoni Pokusinski
Abracon ABX81X is a family of RTCs with SPI interface featuring
the same functionalities as the ABX80X I2C clocks.
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
---
drivers/rtc/Kconfig | 26 +++++-----
drivers/rtc/rtc-abx80x.c | 106 +++++++++++++++++++++++++++++++++++++--
2 files changed, 115 insertions(+), 17 deletions(-)
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 4d14bcba616e..d35221f81024 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -212,18 +212,6 @@ config RTC_DRV_ABEOZ9
This driver can also be built as a module. If so, the module
will be called rtc-ab-e0z9.
-config RTC_DRV_ABX80X
- tristate "Abracon ABx80x"
- select WATCHDOG_CORE if WATCHDOG
- select REGMAP_I2C
- help
- If you say yes here you get support for Abracon AB080X and AB180X
- families of ultra-low-power battery- and capacitor-backed real-time
- clock chips.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-abx80x.
-
config RTC_DRV_AC100
tristate "X-Powers AC100"
depends on MFD_AC100
@@ -955,6 +943,20 @@ config RTC_I2C_AND_SPI
comment "SPI and I2C RTC drivers"
+config RTC_DRV_ABX80X
+ tristate "Abracon ABx80x"
+ depends on RTC_I2C_AND_SPI
+ select WATCHDOG_CORE if WATCHDOG
+ select REGMAP_I2C if I2C
+ select REGMAP_SPI if SPI_MASTER
+ help
+ If you say yes here you get support for Abracon AB080X, AB180X,
+ AB081X and AB181X families of ultra-low-power battery- and
+ capacitor-backed real-time clock chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-abx80x.
+
config RTC_DRV_DS3232
tristate "Dallas/Maxim DS3232/DS3234"
depends on RTC_I2C_AND_SPI
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 08b22c067699..7145de876b50 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * A driver for the I2C members of the Abracon AB x8xx RTC family,
- * and compatible: AB 1805 and AB 0805
+ * A driver for the I2C and SPI members of the Abracon AB x8xx RTC family,
+ * and compatible: AB 1805, AB 0805, AB 1815 and AB 0815
*
* Copyright 2014-2015 Macq S.A.
*
@@ -19,6 +19,7 @@
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
+#include <linux/spi/spi.h>
#include <linux/watchdog.h>
#define ABX8XX_REG_HTH 0x00
@@ -109,8 +110,8 @@
static u8 trickle_resistors[] = {0, 3, 6, 11};
-enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
- AB1801, AB1803, AB1804, AB1805, RV1805, ABX80X};
+enum abx80x_chip {AB0801, AB0803, AB0804, AB0805, AB0815,
+ AB1801, AB1803, AB1804, AB1805, AB1815, RV1805, ABX80X};
struct abx80x_cap {
u16 pn;
@@ -123,10 +124,12 @@ static struct abx80x_cap abx80x_caps[] = {
[AB0803] = {.pn = 0x0803},
[AB0804] = {.pn = 0x0804, .has_tc = true, .has_wdog = true},
[AB0805] = {.pn = 0x0805, .has_tc = true, .has_wdog = true},
+ [AB0815] = {.pn = 0x0815, .has_tc = true, .has_wdog = true},
[AB1801] = {.pn = 0x1801},
[AB1803] = {.pn = 0x1803},
[AB1804] = {.pn = 0x1804, .has_tc = true, .has_wdog = true},
[AB1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true},
+ [AB1815] = {.pn = 0x1815, .has_tc = true, .has_wdog = true},
[RV1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true},
[ABX80X] = {.pn = 0}
};
@@ -1073,14 +1076,107 @@ static void abx80x_unregister_driver(void)
#endif /* IS_ENABLED(CONFIG_I2C) */
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static const struct regmap_config abx80x_regmap_config_spi = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ABX8XX_SRAM_BASE + ABX8XX_SRAM_WIN_SIZE - 1,
+
+ .rd_table = &abx80x_read_table,
+ .wr_table = &abx80x_write_table,
+
+ .write_flag_mask = BIT(7),
+};
+
+static const struct spi_device_id abx81x_id[] = {
+ { "ab0815", AB0815 },
+ { "ab1815", AB1815 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, abx81x_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id abx81x_of_match[] = {
+ {
+ .compatible = "abracon,ab0815",
+ .data = (void *)AB0815
+ },
+ {
+ .compatible = "abracon,ab1815",
+ .data = (void *)AB1815
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, abx81x_of_match);
+#endif
+
+static int abx81x_spi_probe(struct spi_device *spi)
+{
+ unsigned int part = (uintptr_t)spi_get_device_match_data(spi);
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &abx80x_regmap_config_spi);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "Unable to allocate regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ return abx80x_probe(&spi->dev, regmap, spi->irq,
+ spi->dev.of_node, part);
+}
+
+static struct spi_driver abx81x_driver = {
+ .driver = {
+ .name = "rtc-abx81x",
+ .of_match_table = of_match_ptr(abx81x_of_match),
+ },
+ .probe = abx81x_spi_probe,
+ .id_table = abx81x_id,
+};
+
+static int abx81x_register_driver(void)
+{
+ return spi_register_driver(&abx81x_driver);
+}
+
+static void abx81x_unregister_driver(void)
+{
+ spi_unregister_driver(&abx81x_driver);
+}
+
+#else
+
+static int abx81x_register_driver(void)
+{
+ return 0;
+}
+
+static void abx81x_unregister_driver(void)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_SPI_MASTER) */
+
static int __init abx80x_init(void)
{
- return abx80x_register_driver();
+ int ret;
+
+ ret = abx80x_register_driver();
+ if (ret)
+ return ret;
+
+ ret = abx81x_register_driver();
+ if (ret)
+ abx80x_unregister_driver();
+
+ return ret;
}
module_init(abx80x_init);
static void __exit abx80x_exit(void)
{
+ abx81x_unregister_driver();
abx80x_unregister_driver();
}
module_exit(abx80x_exit);
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v4 1/9] dt-bindings: rtc: abx80x: document ABX81X RTCs
2026-08-10 19:38 ` [PATCH v4 1/9] dt-bindings: rtc: abx80x: document ABX81X RTCs Antoni Pokusinski
@ 2026-08-14 0:47 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-14 0:47 UTC (permalink / raw)
To: Antoni Pokusinski
Cc: Alexandre Belloni, devicetree, linux-rtc, robh, conor+dt
> Abracon AB0815 and AB1815 are RTC modules with SPI interface.
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260810193826.210683-1-apokusinski01@gmail.com?part=1
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v4 2/9] rtc: abx80x: fix error check after i2c_smbus_read in read_alarm()
2026-08-10 19:38 ` [PATCH v4 2/9] rtc: abx80x: fix error check after i2c_smbus_read in read_alarm() Antoni Pokusinski
@ 2026-08-14 1:02 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-14 1:02 UTC (permalink / raw)
To: Antoni Pokusinski
Cc: linux-rtc, devicetree, robh, Alexandre Belloni, conor+dt
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] The relaxed error check allows short I2C reads to bypass validation, resulting in uninitialized stack memory being parsed and used.
Pre-existing issues:
- [High] The patch fails to properly fix the issue it claims to address because it returns a positive byte count on success, which the RTC subsystem treats as an error.
- [High] Concurrent sysfs accesses can cause data corruption in the hardware oscillator control register.
- [High] Concurrent NVMEM accesses cause data corruption due to unprotected page register updates.
--
--- Patch [2]: [PATCH v4 2/9] rtc: abx80x: fix error check after i2c_smbus_read in read_alarm() ---
commit 0f39bb1c012fd3e70a85b6d2103b11a6c2ce0801
Author: Antoni Pokusinski <apokusinski01@gmail.com>
rtc: abx80x: fix error check after i2c_smbus_read in read_alarm()
i2c_smbus_read_i2c_block_data() returns the number of bytes read on
success and a negative error code on failure. The existing code treated
any non-zero return value as an error.
Fix the error handling by checking only for negative return values.
> diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> index 5486d9d0b1e5d..fdf9664ebb6f0 100644
> --- a/drivers/rtc/rtc-abx80x.c
> +++ b/drivers/rtc/rtc-abx80x.c
> @@ -294,7 +294,7 @@ static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
>
> err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ASC,
> sizeof(buf), buf);
> - if (err)
> + if (err < 0)
[Severity: High]
Does this relaxed error check allow short I2C reads to bypass validation?
If i2c_smbus_read_i2c_block_data() returns a positive byte count less than
sizeof(buf), this check will pass and execution continues. The subsequent code
will then parse uninitialized stack memory from buf to populate the alarm
configuration, which could cause undefined behavior.
> return err;
>
> irq_mask = i2c_smbus_read_byte_data(client, ABX8XX_REG_IRQ);
[Severity: High]
This isn't a bug introduced by this patch, but since err contains the positive
number of bytes read on success, does returning it unmodified at the end of
abx80x_read_alarm() cause the RTC core to treat the success case as a failure?
drivers/rtc/rtc-abx80x.c:abx80x_read_alarm() {
...
t->enabled = !!(irq_mask & ABX8XX_IRQ_AIE);
t->pending = (buf[6] & ABX8XX_STATUS_AF) && t->enabled;
return err;
}
Since the caller will still see a positive return value, the alarm feature
remains broken. Should this be updated to return 0 on success?
[Severity: High]
This is a pre-existing issue, but does abx80x_rtc_read_time() also suffer from
the same missing length check pattern?
drivers/rtc/rtc-abx80x.c:abx80x_rtc_read_time() {
...
err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_HTH,
sizeof(buf), buf);
if (err < 0) {
dev_err(&client->dev, "Unable to read date\n");
return -EIO;
}
tm->tm_sec = bcd2bin(buf[ABX8XX_REG_SC] & 0x7F);
...
}
If a short read occurs, this allows uninitialized stack memory to be used when
setting the time.
[Severity: High]
This is a pre-existing issue, but can concurrent sysfs accesses cause data
corruption in the hardware oscillator control register?
The operations in abx80x_rtc_set_autocalibration() and oscillator_store() both
perform lockless read-modify-write on ABX8XX_REG_OSC:
drivers/rtc/rtc-abx80x.c:abx80x_rtc_set_autocalibration() {
...
flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
...
retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags);
...
}
drivers/rtc/rtc-abx80x.c:oscillator_store() {
...
flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
...
retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags);
...
}
Because these sysfs operations do not serialize through the RTC core's ops_lock,
is it possible for one thread's modification to be clobbered by another?
[Severity: High]
This is a pre-existing issue, but do concurrent NVMEM accesses cause data
corruption due to unprotected page register updates?
In abx80x_nvmem_xfer(), the driver writes to the page register before
performing an I2C block operation:
drivers/rtc/rtc-abx80x.c:abx80x_nvmem_xfer() {
...
ret = i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_EXTRAM,
extram);
if (ret)
return ret;
if (write) {
ret = i2c_smbus_write_i2c_block_data(priv->client, reg,
len, val);
...
}
Since the NVMEM core does not serialize accesses and the driver provides no
internal locking, could concurrent reads or writes from userspace interleave,
causing operations to target the incorrect memory page?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260810193826.210683-1-apokusinski01@gmail.com?part=2
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v4 3/9] rtc: abx80x: add mutex protection for register writes
2026-08-10 19:38 ` [PATCH v4 3/9] rtc: abx80x: add mutex protection for register writes Antoni Pokusinski
@ 2026-08-14 1:20 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-14 1:20 UTC (permalink / raw)
To: Antoni Pokusinski
Cc: robh, devicetree, linux-rtc, Alexandre Belloni, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Incomplete locking leaves configuration key sequences vulnerable to disruption by concurrent read operations.
--
--- Patch [3]: [PATCH v4 3/9] rtc: abx80x: add mutex protection for register writes ---
commit 45525a43d1a439e6b8a50fcba7c541c85b598f5c
Author: Antoni Pokusinski <apokusinski01@gmail.com>
rtc: abx80x: add mutex protection for register writes
The ABX80X RTC driver performs multi-step register operations such as
NVMEM transfers or register writes preceded by a configuration key write.
This patch adds a mutex to serialize register writes and protect these
sequences against race conditions.
> diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> index fdf9664ebb6f0..0c8d3416fd5a9 100644
> --- a/drivers/rtc/rtc-abx80x.c
> +++ b/drivers/rtc/rtc-abx80x.c
[ ... ]
> @@ -234,6 +237,8 @@ static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
> buf[ABX8XX_REG_YR] = bin2bcd(tm->tm_year - 100);
> buf[ABX8XX_REG_WD] = tm->tm_wday;
>
> + guard(mutex)(&priv->lock);
> +
[Severity: High]
This is a pre-existing issue, but since this patch introduces the mutex to
protect multi-step sequences, should the read paths also acquire the lock?
According to the ABX80X hardware specification, the configuration key is
automatically cleared by any subsequent register read or write.
If a sysfs attribute write like oscillator_store() acquires the lock and
writes the configuration key, but another process concurrently calls an
unprotected read path like abx80x_rtc_read_time() or abx80x_read_alarm():
abx80x_rtc_read_time()
err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_HTH, ...);
Could this interleaved I2C read silently clear the configuration key before
the protected write completes, causing the hardware configuration update to
fail?
> err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_HTH,
> sizeof(buf), buf);
> if (err < 0) {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260810193826.210683-1-apokusinski01@gmail.com?part=3
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v4 4/9] rtc: abx80x: properly handle shared IRQs
2026-08-10 19:38 ` [PATCH v4 4/9] rtc: abx80x: properly handle shared IRQs Antoni Pokusinski
@ 2026-08-14 1:33 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-14 1:33 UTC (permalink / raw)
To: Antoni Pokusinski
Cc: conor+dt, Alexandre Belloni, robh, linux-rtc, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Unsafe read-modify-write on the W0C status register clears unhandled and newly asserted interrupts.
- [High] The shared interrupt handler incorrectly claims interrupts without verifying if the alarm interrupt is enabled, risking interrupt storms or lost IRQs for other devices.
--
--- Patch [4]: [PATCH v4 4/9] rtc: abx80x: properly handle shared IRQs ---
commit 973a5c08e9e5b0f99ad16fb217339890d12578f8
Author: Antoni Pokusinski <apokusinski01@gmail.com>
rtc: abx80x: properly handle shared IRQs
The abx80x RTC driver registers its interrupt handler with IRQF_SHARED,
but the handler always returned IRQ_HANDLED and cleared the status
register even when no interrupt source from the RTC was active.
> diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> index 0c8d3416fd5a9..485d1ea08030d 100644
> --- a/drivers/rtc/rtc-abx80x.c
> +++ b/drivers/rtc/rtc-abx80x.c
[ ... ]
> @@ -266,27 +272,34 @@ 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 rtc_device *rtc = priv->rtc;
> + irqreturn_t handled = IRQ_NONE;
> int status;
>
> guard(mutex)(&priv->lock);
>
> status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
> if (status < 0)
> - return IRQ_NONE;
> + return handled;
>
> - if (status & ABX8XX_STATUS_AF)
> + if (status & ABX8XX_STATUS_AF) {
> rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
> + handled = IRQ_HANDLED;
> + }
[Severity: High]
This is a pre-existing issue, but since this patch aims to properly handle
shared IRQs, should this also check if the Alarm Interrupt Enable (AIE)
bit is set?
In many RTCs, the Alarm Flag (AF) is set upon a match regardless of whether
the interrupt is actually enabled. If AIE is disabled but AF is set, could
this incorrectly assume the RTC generated the interrupt, claim it from
other devices sharing the IRQ line, and emit a spurious RTC alarm event?
>
> /*
> * It is unclear if we'll get an interrupt before the external
> * reset kicks in.
> */
> - if (status & ABX8XX_STATUS_WDT)
> + if (status & ABX8XX_STATUS_WDT) {
> dev_alert(&client->dev, "watchdog timeout interrupt.\n");
> + handled = IRQ_HANDLED;
> + }
>
> - i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0);
> + if (handled == IRQ_HANDLED)
> + i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS,
> + status & ~(ABX8XX_STATUS_AF | ABX8XX_STATUS_WDT));
[Severity: High]
This isn't a bug introduced by this patch, as the original code wrote 0 to
clear all bits, but does this new read-modify-write operation still leave a
race condition for write-0-to-clear (W0C) registers?
If an unhandled interrupt asserts between reading ABX8XX_REG_STATUS and
writing it back, wouldn't writing back the read status (where the new
interrupt's bit was 0) write a 0 to that newly asserted bit, inadvertently
clearing it before it can be handled?
Additionally, does status & ~(ABX8XX_STATUS_AF | ABX8XX_STATUS_WDT)
unconditionally write 0 to the WDT bit, even if only the AF bit was
handled?
>
> - return IRQ_HANDLED;
> + return handled;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260810193826.210683-1-apokusinski01@gmail.com?part=4
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v4 5/9] rtc: abx80x: add irq to struct abx80x_priv
2026-08-10 19:38 ` [PATCH v4 5/9] rtc: abx80x: add irq to struct abx80x_priv Antoni Pokusinski
@ 2026-08-14 1:44 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-14 1:44 UTC (permalink / raw)
To: Antoni Pokusinski
Cc: Alexandre Belloni, devicetree, robh, conor+dt, linux-rtc
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Short reads from I2C block transfers leave stack buffers partially uninitialized, leading to a kernel information leak.
--
--- Patch [5]: [PATCH v4 5/9] rtc: abx80x: add irq to struct abx80x_priv ---
commit efc6d887b1cb64fa6fee7b2ea258d8477f65792a
Author: Antoni Pokusinski <apokusinski01@gmail.com>
rtc: abx80x: add irq to struct abx80x_priv
This way the irq number is not tied to the i2c_client and can be
easily accessed also in the case of ABX81X SPI clocks.
> diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> index 485d1ea08030d..12a4dbfec6396 100644
> --- a/drivers/rtc/rtc-abx80x.c
> +++ b/drivers/rtc/rtc-abx80x.c
[ ... ]
> @@ -305,11 +306,12 @@ static irqreturn_t abx80x_handle_irq(int irq, void *dev_id)
> static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
> {
> struct i2c_client *client = to_i2c_client(dev);
> + struct abx80x_priv *priv = i2c_get_clientdata(client);
> unsigned char buf[7];
>
> int irq_mask, err;
>
> - if (client->irq <= 0)
> + if (priv->irq <= 0)
> return -EINVAL;
>
> err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ASC,
[Severity: High]
Does the error check following this read adequately handle short reads?
Because i2c_smbus_read_i2c_block_data() returns the number of bytes read on
success, a short read will return a positive value less than sizeof(buf).
With the error check changed to if (err < 0) in this patch series, it fails
to catch short reads.
This bypasses the check and leaves the stack-allocated buf partially
uninitialized. When this uninitialized memory is parsed and eventually exposed
to userspace via RTC ioctls, it introduces a kernel information leak.
This is a pre-existing issue in other functions as well, but I wanted to
point it out since it affects this code path now:
drivers/rtc/rtc-abx80x.c:abx80x_rtc_read_time() {
...
err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_HTH,
sizeof(buf), buf);
if (err < 0) {
dev_err(&client->dev, "Unable to read date\n");
return -EIO;
}
...
}
and:
drivers/rtc/rtc-abx80x.c:abx80x_probe() {
...
err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ID0,
sizeof(buf), buf);
if (err < 0) {
dev_err(&client->dev, "Unable to read partnumber\n");
return -EIO;
}
...
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260810193826.210683-1-apokusinski01@gmail.com?part=5
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v4 6/9] rtc: abx80x: use regmap instead of I2C specific API
2026-08-10 19:38 ` [PATCH v4 6/9] rtc: abx80x: use regmap instead of I2C specific API Antoni Pokusinski
@ 2026-08-14 1:57 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-14 1:57 UTC (permalink / raw)
To: Antoni Pokusinski
Cc: devicetree, linux-rtc, Alexandre Belloni, conor+dt, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Pointer target signedness mismatch due to passing `int *` to `regmap_read()`.
--
--- Patch [6]: [PATCH v4 6/9] rtc: abx80x: use regmap instead of I2C specific API ---
commit 2c13ab934066f6ede54c1bee1c9c8966213b138d
Author: Antoni Pokusinski <apokusinski01@gmail.com>
Subject: rtc: abx80x: use regmap instead of I2C specific API
This commit converts the ABX80x RTC driver to use regmap instead of i2c_smbus
functions. This enables future support for ABX81X SPI-based variants.
> diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> index 12a4dbfec6396..5d41882f3c704 100644
> --- a/drivers/rtc/rtc-abx80x.c
> +++ b/drivers/rtc/rtc-abx80x.c
[ ... ]
> @@ -227,8 +230,7 @@ static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
>
> static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> - struct abx80x_priv *priv = i2c_get_clientdata(client);
> + struct abx80x_priv *priv = dev_get_drvdata(dev);
> unsigned char buf[8];
> int err, flags;
[ ... ]
> @@ -246,22 +248,22 @@ static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
>
> guard(mutex)(&priv->lock);
>
[ ... ]
>
> /* Clear the OF bit of Oscillator Status Register */
> - flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS);
> - if (flags < 0)
> - return flags;
> + err = regmap_read(priv->regmap, ABX8XX_REG_OSS, &flags);
[Severity: Low]
This isn't a bug, but could passing a pointer to the signed int flags into
regmap_read() trigger a pointer target signedness mismatch warning?
Since regmap_read() expects an unsigned int pointer for its value parameter,
would it be better to change the declaration of flags in abx80x_rtc_set_time()
to unsigned int to avoid cluttering the build output?
> + if (err < 0)
> + return err;
>
> - err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSS,
> - flags & ~ABX8XX_OSS_OF);
> + err = regmap_write(priv->regmap, ABX8XX_REG_OSS,
> + flags & ~ABX8XX_OSS_OF);
> if (err < 0) {
> - dev_err(&client->dev, "Unable to write oscillator status register\n");
> + dev_err(dev, "Unable to write oscillator status register\n");
> return err;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260810193826.210683-1-apokusinski01@gmail.com?part=6
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v4 7/9] rtc: abx80x: replace read-modify-write pattern with regmap helpers
2026-08-10 19:38 ` [PATCH v4 7/9] rtc: abx80x: replace read-modify-write pattern with regmap helpers Antoni Pokusinski
@ 2026-08-14 2:07 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-14 2:07 UTC (permalink / raw)
To: Antoni Pokusinski
Cc: linux-rtc, Alexandre Belloni, conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Using regmap_update_bits() or regmap_write_bits() breaks the hardware Configuration Key unlock sequence by injecting an I2C read between the unlock write and the protected register write.
--
--- Patch [7]: [PATCH v4 7/9] rtc: abx80x: replace read-modify-write pattern with regmap helpers ---
commit 9a313450ff6d4e2395638f901f9fccf926bd1dda
Author: Antoni Pokusinski <apokusinski01@gmail.com>
rtc: abx80x: replace read-modify-write pattern with regmap helpers
This commit replaces manual read-modify-write sequences with
regmap_update_bits() and regmap_write_bits() to simplify the code.
> diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> index 5d41882f3c704..ab88af43b4fa0 100644
> --- a/drivers/rtc/rtc-abx80x.c
> +++ b/drivers/rtc/rtc-abx80x.c
[ ... ]
> @@ -403,13 +389,15 @@ static int abx80x_rtc_set_autocalibration(struct device *dev,
> flags |= (ABX8XX_OSC_ACAL_1024 | ABX8XX_OSC_ACAL_512);
> }
>
> + guard(mutex)(&priv->lock);
> +
> /* Unlock write access to Oscillator Control Register */
> if (abx80x_write_config_key(dev, ABX8XX_CFG_KEY_OSC) < 0)
> return -EIO;
>
> - retval = regmap_write(priv->regmap, ABX8XX_REG_OSC, flags);
> -
> - return retval;
> + return regmap_write_bits(priv->regmap, ABX8XX_REG_OSC,
> + ABX8XX_OSC_ACAL_1024 | ABX8XX_OSC_ACAL_512,
> + flags);
> }
[Severity: High]
Does using regmap_write_bits() here break the hardware Configuration Key unlock
sequence?
Since regmap_write_bits() performs a read-modify-write cycle, it injects an
I2C read between the unlock write (abx80x_write_config_key) and the actual
register write.
If the hardware resets the key on any intervening I2C access, this might lock
the register again, causing the subsequent write to fail silently.
[ ... ]
> @@ -486,24 +473,14 @@ static ssize_t oscillator_store(struct device *dev,
[ ... ]
> /* Unlock write access on Oscillator Control register */
> if (abx80x_write_config_key(dev->parent, ABX8XX_CFG_KEY_OSC) < 0)
> return -EIO;
>
> - retval = regmap_write(priv->regmap, ABX8XX_REG_OSC, flags);
> - if (retval < 0) {
> + retval = regmap_write_bits(priv->regmap, ABX8XX_REG_OSC, ABX8XX_OSC_OSEL,
> + rc_mode == 0 ? 0 : (ABX8XX_OSC_OSEL));
> + if (retval < 0)
> dev_err(dev, "Failed to write Oscillator Control register\n");
[Severity: High]
Will this read-modify-write cycle also invalidate the unlock key before the
write occurs, similar to the issue in abx80x_rtc_set_autocalibration()?
[ ... ]
> @@ -946,8 +890,14 @@ static int abx80x_probe(struct i2c_client *client)
> if (abx80x_write_config_key(&client->dev, ABX8XX_CFG_KEY_MISC) < 0)
> return -EIO;
>
> - err = regmap_write(regmap, ABX8XX_REG_OUT_CTRL,
> - data | ABX8XX_OUT_CTRL_EXDS);
> + /*
> + * Avoid extra power leakage. The RV1805 uses smaller
> + * 10pin package and the EXTI input is not present.
> + * Disable it to avoid leakage.
> + */
> + err = regmap_write_bits(regmap, ABX8XX_REG_OUT_CTRL,
> + ABX8XX_OUT_CTRL_EXDS,
> + ABX8XX_OUT_CTRL_EXDS);
> if (err < 0) {
> dev_err(&client->dev,
> "Unable to write output control register\n");
[Severity: High]
Could the implicit I2C read inside regmap_write_bits() invalidate the MISC
unlock key here as well, causing the EXTI disable write to fail on RV1805
chips and leading to extra power leakage?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260810193826.210683-1-apokusinski01@gmail.com?part=7
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v4 8/9] rtc: abx80x: create abx80x_i2c_probe()
2026-08-10 19:38 ` [PATCH v4 8/9] rtc: abx80x: create abx80x_i2c_probe() Antoni Pokusinski
@ 2026-08-14 2:15 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-14 2:15 UTC (permalink / raw)
To: Antoni Pokusinski
Cc: devicetree, linux-rtc, Alexandre Belloni, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] RV1805-specific initialization workarounds are skipped if the device is probed using the generic 'ABX80X' compatible string.
--
--- Patch [8]: [PATCH v4 8/9] rtc: abx80x: create abx80x_i2c_probe() ---
commit 5c7e0bb55f377bff46772b45dbc25f48fd477301
Author: Antoni Pokusinski <apokusinski01@gmail.com>
rtc: abx80x: create abx80x_i2c_probe()
Move the I2C-specific code from abx80x_probe() to the newly created
function. This is a preparation for introducing the support for
ABX81X RTCs which use SPI interface.
> diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> index ab88af43b4fa0..08b22c0676995 100644
> --- a/drivers/rtc/rtc-abx80x.c
> +++ b/drivers/rtc/rtc-abx80x.c
[ ... ]
> @@ -911,26 +876,24 @@ static int abx80x_probe(struct i2c_client *client)
> if (partnumber == abx80x_caps[i].pn)
> break;
> if (abx80x_caps[i].pn == 0) {
> - dev_err(&client->dev, "Unknown part: %04x\n",
> - partnumber);
> + dev_err(dev, "Unknown part: %04x\n", partnumber);
> return -EINVAL;
> }
> part = i;
> }
[Severity: High]
This isn't a bug introduced by this patch, but does this ordering bypass the
RV1805 specific workarounds when part autodetection is used?
If an RV1805 device is probed using the generic "abracon,abx80x" compatible
string, the part variable is initially ABX80X. In abx80x_probe(), the code
checks if (part == RV1805) to apply quirks before executing the
if (part == ABX80X) autodetection block that actually identifies the part
as an RV1805.
Should the RV1805 quirks block be moved after part autodetection so they
are properly applied when an RV1805 is autodetected?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260810193826.210683-1-apokusinski01@gmail.com?part=8
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v4 9/9] rtc: abx80x: add support for ABX81X
2026-08-10 19:38 ` [PATCH v4 9/9] rtc: abx80x: add support for ABX81X Antoni Pokusinski
@ 2026-08-14 2:31 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-14 2:31 UTC (permalink / raw)
To: Antoni Pokusinski
Cc: Alexandre Belloni, linux-rtc, robh, conor+dt, devicetree
> Abracon ABX81X is a family of RTCs with SPI interface featuring
> the same functionalities as the ABX80X I2C clocks.
>
> Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260810193826.210683-1-apokusinski01@gmail.com?part=9
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-08-14 2:31 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-10 19:38 [PATCH v4 0/9] rtc: abx80x: add support for abx81x Antoni Pokusinski
2026-08-10 19:38 ` [PATCH v4 1/9] dt-bindings: rtc: abx80x: document ABX81X RTCs Antoni Pokusinski
2026-08-14 0:47 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 2/9] rtc: abx80x: fix error check after i2c_smbus_read in read_alarm() Antoni Pokusinski
2026-08-14 1:02 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 3/9] rtc: abx80x: add mutex protection for register writes Antoni Pokusinski
2026-08-14 1:20 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 4/9] rtc: abx80x: properly handle shared IRQs Antoni Pokusinski
2026-08-14 1:33 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 5/9] rtc: abx80x: add irq to struct abx80x_priv Antoni Pokusinski
2026-08-14 1:44 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 6/9] rtc: abx80x: use regmap instead of I2C specific API Antoni Pokusinski
2026-08-14 1:57 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 7/9] rtc: abx80x: replace read-modify-write pattern with regmap helpers Antoni Pokusinski
2026-08-14 2:07 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 8/9] rtc: abx80x: create abx80x_i2c_probe() Antoni Pokusinski
2026-08-14 2:15 ` sashiko-bot
2026-08-10 19:38 ` [PATCH v4 9/9] rtc: abx80x: add support for ABX81X Antoni Pokusinski
2026-08-14 2:31 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox