* [PATCH] rtc: abx80x: Disable alarm feature if no interrupt attached
From: Anthony Pighin (Nokia) @ 2025-11-25 18:00 UTC (permalink / raw)
To: linux-rtc@vger.kernel.org; +Cc: alexandre.belloni@bootlin.com
Commit 795cda8338ea ("rtc: interface: Fix long-standing race when setting
alarm") exposed an issue where the rtc-abx80x driver does not clear the
alarm feature bit, but instead relies on the set_alarm operation to return
invalid.
For example, when a RTC_UIE_ON ioctl is handled, it should abort at the
feature validation. Instead, it proceeds to the rtc_timer_enqueue(),
which used to return an error from the set_alarm call. However,
following the race condition handling, which likely should not be
discarding predecing errors, a success condition is returned to the
ioctl() caller. This results in (for example):
hwclock: select() to /dev/rtc0 to wait for clock tick timed out
Notwithstanding the validity of the race condition handling, if an interrupt
wasn't specified, or could not be attached, the driver should clear the
alarm feature bit.
Fixes: 718a820a303c ("rtc: abx80x: add alarm support")
Signed-off-by: Anthony Pighin <anthony.pighin@nokia.com>
---
drivers/rtc/rtc-abx80x.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 3fee27914ba8..5f3a3e60a19d 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -933,6 +933,8 @@ static int abx80x_probe(struct i2c_client *client)
client->irq = 0;
}
}
+ if (client->irq <= 0)
+ clear_bit(RTC_FEATURE_ALARM, priv->rtc->features);
err = rtc_add_group(priv->rtc, &rtc_calib_attr_group);
if (err) {
--
2.43.0
^ permalink raw reply related
* [PATCH] rtc: interface: Alarm race handling should not discard preceding error
From: Anthony Pighin (Nokia) @ 2025-11-25 17:35 UTC (permalink / raw)
To: linux-rtc@vger.kernel.org; +Cc: alexandre.belloni@bootlin.com
Commit 795cda8338ea ("rtc: interface: Fix long-standing race when setting
alarm") should not discard any errors from the preceding validations.
Prior to that commit, if the alarm feature was disabled, or the
set_alarm failed, a meaningful error code would be returned to the
caller for further action.
After, more often than not, the __rtc_read_time will cause a success
return code instead, misleading the caller.
An example of this is when timer_enqueue is called for a rtc-abx080x
device. Since that driver does not clear the alarm feature bit, but
instead relies on the set_alarm operation to return invalid, the discard
of the return code causes very different behaviour; i.e.
hwclock: select() to /dev/rtc0 to wait for clock tick timed out
Fixes: 795cda8338ea ("rtc: interface: Fix long-standing race when setting alarm")
Signed-off-by: Anthony Pighin <anthony.pighin@nokia.com>
---
drivers/rtc/interface.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index b8b298efd9a9..1906f4884a83 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -457,7 +457,7 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
* are in, we can return -ETIME to signal that the timer has already
* expired, which is true in both cases.
*/
- if ((scheduled - now) <= 1) {
+ if (!err && (scheduled - now) <= 1) {
err = __rtc_read_time(rtc, &tm);
if (err)
return err;
--
2.43.0
^ permalink raw reply related
* [PATCH v4 5/5] rtc: pcf85363: add watchdog support with configurable step size
From: Lakshay Piplani @ 2025-11-21 12:11 UTC (permalink / raw)
To: alexandre.belloni, linux-rtc, linux-kernel, robh, krzk+dt,
conor+dt, devicetree, wim, linux, linux-watchdog
Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
Lakshay Piplani
In-Reply-To: <20251121121137.3043764-1-lakshay.piplani@nxp.com>
Add watchdog timer support to PCF85263/PCF85363 using the linux watchdog
subsystem. The driver programs the hardware watchdog timeout based on
the requested period.
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
---
V3 -> V4:
- Use watchdog_init_timeout(&wd->wdd, 0, dev) to allow devicetree or module parameter overrides;
fallback to WD_DEFAULT_TIMEOUT if not provided.
- Centralized clock selection logic in pcf85363_wdt_select_clock() and applied dynamically
whenever timeout changes.
- Removed unused repeat variable and simplified timeout handling for clarity.
V2 -> V3:
- Split into separate patches as suggested:
- Battery switch-over detection.
- Timestamp recording for TS pin and battery switch-over events.
- Offset calibration.
- Watchdog timer (to be reviewed by watchdog maintainers).
- Dropped Alarm2 support
- Switched to rtc_add_group() for sysfs attributes
V1 -> V2:
- Watchdog related changes due to removal of vendor specific properties
from device tree
* remove vendor DT knobs (enable/timeout/stepsize/repeat)
* use watchdog_init_timeout (with 10s default)
* derive clock_sel from final timeout
* default, repeat=true (repeat mode)
- Fixed uninitalised warning on 'ret' (reported by kernel test robot)
- Use dev_dbg instead of dev_info for debug related print messages
- Minor cleanup and comments.
drivers/rtc/rtc-pcf85363.c | 156 ++++++++++++++++++++++++++++++++++++-
1 file changed, 154 insertions(+), 2 deletions(-)
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
index 665bbbb169b0..3eb87306c83c 100644
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -5,6 +5,10 @@
* Driver for NXP PCF85363 real-time clock.
*
* Copyright (C) 2017 Eric Nelson
+ *
+ * Copyright 2025 NXP
+ * Added support for timestamps, battery switch-over,
+ * watchdog, offset calibration.
*/
#include <linux/module.h>
#include <linux/i2c.h>
@@ -18,6 +22,7 @@
#include <linux/of.h>
#include <linux/rtc.h>
#include <linux/regmap.h>
+#include <linux/watchdog.h>
/*
* Date/Time registers
@@ -128,6 +133,17 @@
#define OFFSET_MAXIMUM 127
#define OFFSET_MASK 0xFF
+#define WD_TIMEOUT_SHIFT 2
+#define WD_CLKSEL_MASK GENMASK(1, 0)
+#define WD_CLKSEL_0_25HZ 0x00
+#define WD_CLKSEL_1HZ 0x01
+#define WD_CLKSEL_4HZ 0x02
+#define WD_CLKSEL_16HZ 0x03
+
+#define WD_DEFAULT_TIMEOUT 10
+#define WD_TIMEOUT_MIN 1
+#define WD_TIMEOUT_MAX 0x1F
+
struct pcf85363 {
struct rtc_device *rtc;
struct regmap *regmap;
@@ -139,6 +155,14 @@ struct pcf85x63_config {
unsigned int num_nvram;
};
+struct pcf85363_watchdog {
+ struct watchdog_device wdd;
+ struct regmap *regmap;
+ struct device *dev;
+ u8 timeout_val;
+ u8 clock_sel;
+};
+
static int pcf85363_load_capacitance(struct pcf85363 *pcf85363, struct device_node *node)
{
u32 load = 7000;
@@ -324,12 +348,13 @@ static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id)
return IRQ_NONE;
if (flags) {
- dev_dbg(&pcf85363->rtc->dev, "IRQ flags: 0x%02x%s%s%s%s%s\n",
+ dev_dbg(&pcf85363->rtc->dev, "IRQ flags: 0x%02x%s%s%s%s%s%s\n",
flags, (flags & FLAGS_A1F) ? " [A1F]" : "",
(flags & FLAGS_TSR1F) ? " [TSR1F]" : "",
(flags & FLAGS_TSR2F) ? " [TSR2F]" : "",
(flags & FLAGS_TSR3F) ? " [TSR3F]" : "",
- (flags & FLAGS_BSF) ? " [BSF]" : "");
+ (flags & FLAGS_BSF) ? " [BSF]" : "",
+ (flags & FLAGS_WDF) ? " [WDF]" : "");
}
if (flags & FLAGS_A1F) {
@@ -361,6 +386,11 @@ static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id)
handled = true;
}
+ if (flags & FLAGS_WDF) {
+ regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_WDF, 0);
+ handled = true;
+ }
+
return handled ? IRQ_HANDLED : IRQ_NONE;
}
@@ -504,6 +534,124 @@ static const struct pcf85x63_config pcf_85363_config = {
.num_nvram = 2
};
+static void pcf85363_wdt_select_clock(struct pcf85363_watchdog *wd)
+{
+ unsigned int t = wd->wdd.timeout;
+
+ if (t <= 2)
+ wd->clock_sel = WD_CLKSEL_16HZ;
+ else if (t <= 8)
+ wd->clock_sel = WD_CLKSEL_4HZ;
+ else if (t <= 16)
+ wd->clock_sel = WD_CLKSEL_1HZ;
+ else
+ wd->clock_sel = WD_CLKSEL_0_25HZ;
+}
+
+/*
+ * This function sets the watchdog control register based on the timeout,
+ * clock selection and repeat mode settings. It prepares the value to
+ * write into the watchdog control register (CTRL_WDOG).
+ */
+static int pcf85363_wdt_reload(struct pcf85363_watchdog *wd)
+{
+ u8 val;
+
+ val = ((wd->timeout_val & WD_TIMEOUT_MAX) << WD_TIMEOUT_SHIFT) |
+ (wd->clock_sel & WD_CLKSEL_MASK);
+
+ return regmap_write(wd->regmap, CTRL_WDOG, val);
+}
+
+static int pcf85363_wdt_start(struct watchdog_device *wdd)
+{
+ struct pcf85363_watchdog *wd = watchdog_get_drvdata(wdd);
+
+ return pcf85363_wdt_reload(wd);
+}
+
+static int pcf85363_wdt_stop(struct watchdog_device *wdd)
+{
+ struct pcf85363_watchdog *wd = watchdog_get_drvdata(wdd);
+
+ return regmap_write(wd->regmap, CTRL_WDOG, 0);
+}
+
+static int pcf85363_wdt_ping(struct watchdog_device *wdd)
+{
+ struct pcf85363_watchdog *wd = watchdog_get_drvdata(wdd);
+
+ regmap_update_bits(wd->regmap, CTRL_FLAGS, FLAGS_WDF, 0);
+
+ return pcf85363_wdt_reload(wd);
+}
+
+static int pcf85363_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct pcf85363_watchdog *wd = watchdog_get_drvdata(wdd);
+
+ wd->timeout_val = clamp(timeout, WD_TIMEOUT_MIN, WD_TIMEOUT_MAX);
+ wdd->timeout = wd->timeout_val;
+
+ pcf85363_wdt_select_clock(wd);
+
+ return pcf85363_wdt_reload(wd);
+}
+
+static const struct watchdog_info pcf85363_wdt_info = {
+ .identity = "PCF85363 Watchdog",
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
+};
+
+static const struct watchdog_ops pcf85363_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = pcf85363_wdt_start,
+ .stop = pcf85363_wdt_stop,
+ .ping = pcf85363_wdt_ping,
+ .set_timeout = pcf85363_wdt_set_timeout,
+};
+
+static int pcf85363_watchdog_init(struct device *dev, struct regmap *regmap)
+{
+ struct pcf85363_watchdog *wd;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_WATCHDOG))
+ return 0;
+
+ wd = devm_kzalloc(dev, sizeof(*wd), GFP_KERNEL);
+ if (!wd)
+ return -ENOMEM;
+
+ wd->regmap = regmap;
+ wd->dev = dev;
+
+ wd->wdd.info = &pcf85363_wdt_info;
+ wd->wdd.ops = &pcf85363_wdt_ops;
+ wd->wdd.min_timeout = WD_TIMEOUT_MIN;
+ wd->wdd.max_timeout = WD_TIMEOUT_MAX;
+ wd->wdd.parent = dev;
+ wd->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+
+ ret = watchdog_init_timeout(&wd->wdd, 0, dev);
+ if (ret)
+ wd->wdd.timeout = WD_DEFAULT_TIMEOUT;
+
+ wd->timeout_val = wd->wdd.timeout;
+ pcf85363_wdt_select_clock(wd);
+
+ ret = regmap_update_bits(regmap, CTRL_FLAGS, FLAGS_WDF, 0);
+ if (ret) {
+ dev_err(dev, "failed to clear WDF:%d\n", ret);
+ return ret;
+ }
+
+ watchdog_set_drvdata(&wd->wdd, wd);
+
+ return devm_watchdog_register_device(dev, &wd->wdd);
+}
+
/*
* Reads 6 bytes of timestamp data starting at the given base register,
* converts them from BCD to binary, and formats the result into a
@@ -685,6 +833,10 @@ static int pcf85363_probe(struct i2c_client *client)
PIN_IO_TSPM | PIN_IO_TSIM,
PIN_IO_TSPM | PIN_IO_TSIM);
+ ret = pcf85363_watchdog_init(dev, pcf85363->regmap);
+ if (ret)
+ dev_err_probe(dev, ret, "Watchdog init failed\n");
+
if (irq_a > 0 || wakeup_source)
device_init_wakeup(dev, true);
--
2.25.1
^ permalink raw reply related
* [PATCH v4 4/5] rtc: pcf85363: add oscillator offset calibration support
From: Lakshay Piplani @ 2025-11-21 12:11 UTC (permalink / raw)
To: alexandre.belloni, linux-rtc, linux-kernel, robh, krzk+dt,
conor+dt, devicetree, wim, linux, linux-watchdog
Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
Lakshay Piplani
In-Reply-To: <20251121121137.3043764-1-lakshay.piplani@nxp.com>
Expose the oscillator offset register of PCF85263/PCF85363 through the
rtc_class_ops read_offset and set_offset callbacks, allowing userspace
to apply frequency correction for drift compensation.
The correction mode defaults to normal mode (OFFM = 0), where each step
introduces an offset of approximately 2.170 ppm and corrections occur
every 4 hours.
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
---
V3 -> V4:
- No changes in v4.
V2 -> V3:
- Split into separate patches as suggested:
- Battery switch-over detection.
- Timestamp recording for TS pin and battery switch-over events.
- Offset calibration.
- Watchdog timer (to be reviewed by watchdog maintainers).
- Dropped Alarm2 support
- Switched to rtc_add_group() for sysfs attributes
V1 -> V2:
- Watchdog related changes due to removal of vendor specific properties
from device tree
* remove vendor DT knobs (enable/timeout/stepsize/repeat)
* use watchdog_init_timeout (with 10s default)
* derive clock_sel from final timeout
* default, repeat=true (repeat mode)
- Fixed uninitalised warning on 'ret' (reported by kernel test robot)
- Use dev_dbg instead of dev_info for debug related print messages
- Minor cleanup and comments.
drivers/rtc/rtc-pcf85363.c | 46 ++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
index e10e58f69012..665bbbb169b0 100644
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -123,6 +123,11 @@
#define TSR2_SHIFT 2
#define TSR3_SHIFT 6
+#define OFFSET_SIGN_BIT 7
+#define OFFSET_MINIMUM -128
+#define OFFSET_MAXIMUM 127
+#define OFFSET_MASK 0xFF
+
struct pcf85363 {
struct rtc_device *rtc;
struct regmap *regmap;
@@ -359,6 +364,45 @@ static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id)
return handled ? IRQ_HANDLED : IRQ_NONE;
}
+/*
+ * Read the current RTC offset from the CTRL_OFFSET
+ * register. This value is an 8-bit signed 2's complement
+ * value that corrects osciallator drift.
+ */
+static int pcf85363_read_offset(struct device *dev, long *offset)
+{
+ struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(pcf85363->regmap, CTRL_OFFSET, &val);
+
+ if (ret)
+ return ret;
+
+ *offset = sign_extend32(val & OFFSET_MASK, OFFSET_SIGN_BIT);
+
+ return 0;
+}
+
+/*
+ * Write an oscillator offset correction value to
+ * the CTRL_OFFSET register. The valid range is
+ * -128 to 127 (8-bit signed), typically used to fine
+ * tune accuracy.
+ */
+static int pcf85363_set_offset(struct device *dev, long offset)
+{
+ struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
+
+ if (offset < OFFSET_MINIMUM || offset > OFFSET_MAXIMUM) {
+ dev_warn(dev, "Offset out of range: %ld\n", offset);
+ return -ERANGE;
+ }
+
+ return regmap_write(pcf85363->regmap, CTRL_OFFSET, offset & OFFSET_MASK);
+}
+
static int pcf85363_rtc_ioctl(struct device *dev,
unsigned int cmd, unsigned long arg)
{
@@ -396,6 +440,8 @@ static const struct rtc_class_ops rtc_ops = {
.read_alarm = pcf85363_rtc_read_alarm,
.set_alarm = pcf85363_rtc_set_alarm,
.alarm_irq_enable = pcf85363_rtc_alarm_irq_enable,
+ .read_offset = pcf85363_read_offset,
+ .set_offset = pcf85363_set_offset,
};
static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val,
--
2.25.1
^ permalink raw reply related
* [PATCH v4 3/5] rtc: pcf85363: add timestamp support with configurable timestamp mode
From: Lakshay Piplani @ 2025-11-21 12:11 UTC (permalink / raw)
To: alexandre.belloni, linux-rtc, linux-kernel, robh, krzk+dt,
conor+dt, devicetree, wim, linux, linux-watchdog
Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
Lakshay Piplani
In-Reply-To: <20251121121137.3043764-1-lakshay.piplani@nxp.com>
Add support for the timestamp capture registers available on PCF85263 and
PCF85363. The registers latch the current time when selected events occur,
such as TS pin activation or battery switch-over.
The capture source can be configured via the nxp,timestamp-mode device
tree property, and latched values are exported through read-only sysfs
attributes.
Additionally:
- Use rtc_add_group() instead of sysfs_create_group() to register the
timestamp attributes under the RTC class device (/sys/class/rtc/rtcX).
- Perform minor cleanups in the probe function for better readability.
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
---
V3 -> V4:
- No changes in v4.
V2 -> V3:
- Split into separate patches as suggested:
- Battery switch-over detection.
- Timestamp recording for TS pin and battery switch-over events.
- Offset calibration.
- Watchdog timer (to be reviewed by watchdog maintainers).
- Dropped Alarm2 support
- Switched to rtc_add_group() for sysfs attributes
V1 -> V2:
- Watchdog related changes due to removal of vendor specific properties
from device tree
* remove vendor DT knobs (enable/timeout/stepsize/repeat)
* use watchdog_init_timeout (with 10s default)
* derive clock_sel from final timeout
* default, repeat=true (repeat mode)
- Fixed uninitalised warning on 'ret' (reported by kernel test robot)
- Use dev_dbg instead of dev_info for debug related print messages
- Minor cleanup and comments.
drivers/rtc/rtc-pcf85363.c | 209 +++++++++++++++++++++++++++++++------
1 file changed, 175 insertions(+), 34 deletions(-)
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
index c03d5a65c5f7..e10e58f69012 100644
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -16,6 +16,7 @@
#include <linux/bcd.h>
#include <linux/device.h>
#include <linux/of.h>
+#include <linux/rtc.h>
#include <linux/regmap.h>
/*
@@ -101,19 +102,31 @@
#define PIN_IO_INTA_OUT 2
#define PIN_IO_INTA_HIZ 3
+#define PIN_IO_TSPM GENMASK(3, 2)
+#define PIN_IO_TSIM BIT(4)
+
#define OSC_CAP_SEL GENMASK(1, 0)
#define OSC_CAP_6000 0x01
#define OSC_CAP_12500 0x02
#define STOP_EN_STOP BIT(0)
+#define RTCM_BIT BIT(4)
#define RESET_CPR 0xa4
#define NVRAM_SIZE 0x40
+#define TSR1_MASK 0x03
+#define TSR2_MASK 0x07
+#define TSR3_MASK 0x03
+#define TSR1_SHIFT 0
+#define TSR2_SHIFT 2
+#define TSR3_SHIFT 6
+
struct pcf85363 {
struct rtc_device *rtc;
struct regmap *regmap;
+ u8 ts_valid_flags;
};
struct pcf85x63_config {
@@ -306,8 +319,11 @@ static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id)
return IRQ_NONE;
if (flags) {
- dev_dbg(&pcf85363->rtc->dev, "IRQ flags: 0x%02x%s%s\n",
+ dev_dbg(&pcf85363->rtc->dev, "IRQ flags: 0x%02x%s%s%s%s%s\n",
flags, (flags & FLAGS_A1F) ? " [A1F]" : "",
+ (flags & FLAGS_TSR1F) ? " [TSR1F]" : "",
+ (flags & FLAGS_TSR2F) ? " [TSR2F]" : "",
+ (flags & FLAGS_TSR3F) ? " [TSR3F]" : "",
(flags & FLAGS_BSF) ? " [BSF]" : "");
}
@@ -317,6 +333,24 @@ static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id)
handled = true;
}
+ if (flags & FLAGS_TSR1F) {
+ regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_TSR1F, 0);
+ pcf85363->ts_valid_flags |= FLAGS_TSR1F;
+ handled = true;
+ }
+
+ if (flags & FLAGS_TSR2F) {
+ regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_TSR2F, 0);
+ pcf85363->ts_valid_flags |= FLAGS_TSR2F;
+ handled = true;
+ }
+
+ if (flags & FLAGS_TSR3F) {
+ regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_TSR3F, 0);
+ pcf85363->ts_valid_flags |= FLAGS_TSR3F;
+ handled = true;
+ }
+
if (flags & FLAGS_BSF) {
regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_BSF, 0);
handled = true;
@@ -424,11 +458,94 @@ static const struct pcf85x63_config pcf_85363_config = {
.num_nvram = 2
};
+/*
+ * Reads 6 bytes of timestamp data starting at the given base register,
+ * converts them from BCD to binary, and formats the result into a
+ * human-readable string in "YYYY-MM-DD HH:MM:SS" format.
+ */
+static int pcf85363_read_timestamp(struct pcf85363 *pcf85363, u8 base_reg, char *buf)
+{
+ struct rtc_time tm;
+ u8 regs[6];
+ int ret;
+
+ ret = regmap_bulk_read(pcf85363->regmap, base_reg, regs, sizeof(regs));
+
+ if (ret)
+ return ret;
+
+ tm.tm_sec = bcd2bin(regs[0]);
+ tm.tm_min = bcd2bin(regs[1]);
+ tm.tm_hour = bcd2bin(regs[2]);
+ tm.tm_mday = bcd2bin(regs[3]);
+ tm.tm_mon = bcd2bin(regs[4]) - 1;
+ tm.tm_year = bcd2bin(regs[5]) + 100;
+
+ return sysfs_emit(buf, "%04d-%02d-%02d %02d:%02d:%02d\n",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
+/*
+ * Checks whether a specific timestamp flag is set. If so, reads and
+ * returns the formatted timestamp. Otherwise, returns "00-00-00 00:00:00".
+ */
+
+static ssize_t pcf85363_timestamp_show(struct device *dev, char *buf,
+ u8 timestamp_flag, u8 base_reg)
+{
+ struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
+
+ if (!(pcf85363->ts_valid_flags & timestamp_flag))
+ return sysfs_emit(buf, "00-00-00 00:00:00\n");
+
+ return pcf85363_read_timestamp(pcf85363, base_reg, buf);
+}
+
+static ssize_t timestamp1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return pcf85363_timestamp_show(dev, buf, FLAGS_TSR1F, DT_TIMESTAMP1);
+}
+static DEVICE_ATTR_RO(timestamp1);
+
+static ssize_t timestamp2_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return pcf85363_timestamp_show(dev, buf, FLAGS_TSR2F, DT_TIMESTAMP2);
+}
+static DEVICE_ATTR_RO(timestamp2);
+
+static ssize_t timestamp3_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return pcf85363_timestamp_show(dev, buf, FLAGS_TSR3F, DT_TIMESTAMP3);
+}
+static DEVICE_ATTR_RO(timestamp3);
+
+static struct attribute *pcf85363_attrs[] = {
+ &dev_attr_timestamp1.attr,
+ &dev_attr_timestamp2.attr,
+ &dev_attr_timestamp3.attr,
+ NULL,
+};
+
+static const struct attribute_group pcf85363_attr_group = {
+ .attrs = pcf85363_attrs,
+};
+
static int pcf85363_probe(struct i2c_client *client)
{
- struct pcf85363 *pcf85363;
const struct pcf85x63_config *config = &pcf_85363_config;
const void *data = of_device_get_match_data(&client->dev);
+ struct device *dev = &client->dev;
+ struct pcf85363 *pcf85363;
+ int irq_a = client->irq;
+ bool wakeup_source;
+ int ret, i, err;
+ u32 tsr_mode[3];
+ u8 val;
+
static struct nvmem_config nvmem_cfg[] = {
{
.name = "pcf85x63-",
@@ -446,25 +563,43 @@ static int pcf85363_probe(struct i2c_client *client)
.reg_write = pcf85363_nvram_write,
},
};
- int ret, i, err;
- bool wakeup_source;
if (data)
config = data;
- pcf85363 = devm_kzalloc(&client->dev, sizeof(struct pcf85363),
- GFP_KERNEL);
+ pcf85363 = devm_kzalloc(&client->dev, sizeof(*pcf85363), GFP_KERNEL);
if (!pcf85363)
return -ENOMEM;
+ pcf85363->ts_valid_flags = 0;
+
pcf85363->regmap = devm_regmap_init_i2c(client, &config->regmap);
- if (IS_ERR(pcf85363->regmap)) {
- dev_err(&client->dev, "regmap allocation failed\n");
- return PTR_ERR(pcf85363->regmap);
- }
+ if (IS_ERR(pcf85363->regmap))
+ return dev_err_probe(dev, PTR_ERR(pcf85363->regmap), "regmap init failed\n");
i2c_set_clientdata(client, pcf85363);
+ ret = regmap_update_bits(pcf85363->regmap, CTRL_FUNCTION, RTCM_BIT, 0);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable RTC mode\n");
+
+ if (!device_property_read_u32_array(dev, "nxp,timestamp-mode", tsr_mode, 3)) {
+ tsr_mode[0] &= TSR1_MASK;
+ tsr_mode[1] &= TSR2_MASK;
+ tsr_mode[2] &= TSR3_MASK;
+
+ val = (tsr_mode[2] << TSR3_SHIFT) |
+ (tsr_mode[1] << TSR2_SHIFT) |
+ (tsr_mode[0] << TSR1_SHIFT);
+
+ ret = regmap_write(pcf85363->regmap, DT_TS_MODE, val);
+ if (ret)
+ dev_warn(dev, "Failed to write timestamp mode register\n");
+
+ dev_dbg(dev, "Timestamp mode set: TSR1=0x%x TSR2=0x%x TSR3=0x%x\n",
+ tsr_mode[0], tsr_mode[1], tsr_mode[2]);
+ }
+
pcf85363->rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(pcf85363->rtc))
return PTR_ERR(pcf85363->rtc);
@@ -478,38 +613,44 @@ static int pcf85363_probe(struct i2c_client *client)
pcf85363->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
pcf85363->rtc->range_max = RTC_TIMESTAMP_END_2099;
- wakeup_source = device_property_read_bool(&client->dev,
- "wakeup-source");
- if (client->irq > 0 || wakeup_source) {
- regmap_write(pcf85363->regmap, CTRL_FLAGS, 0);
- regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO,
- PIN_IO_INTAPM, PIN_IO_INTA_OUT);
- }
+ wakeup_source = device_property_read_bool(dev, "wakeup-source");
- if (client->irq > 0) {
- unsigned long irqflags = IRQF_TRIGGER_LOW;
+ ret = regmap_write(pcf85363->regmap, CTRL_FLAGS, 0x00);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to clear CTRL_FLAGS\n");
+
+ if (irq_a > 0) {
+ regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO, PIN_IO_INTAPM, PIN_IO_INTA_OUT);
+ ret = devm_request_threaded_irq(dev, irq_a, NULL,
+ pcf85363_rtc_handle_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "pcf85363-inta", client);
- if (dev_fwnode(&client->dev))
- irqflags = 0;
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, pcf85363_rtc_handle_irq,
- irqflags | IRQF_ONESHOT,
- "pcf85363", client);
if (ret) {
- dev_warn(&client->dev,
- "unable to request IRQ, alarms disabled\n");
- client->irq = 0;
+ dev_err_probe(dev, ret, "INTA IRQ request failed\n");
+ irq_a = 0;
+ } else {
+ regmap_write(pcf85363->regmap, CTRL_INTA_EN, INT_BSIE
+ | INT_TSRIE);
}
}
- if (client->irq > 0 || wakeup_source) {
- device_init_wakeup(&client->dev, true);
- set_bit(RTC_FEATURE_ALARM, pcf85363->rtc->features);
- } else {
- clear_bit(RTC_FEATURE_ALARM, pcf85363->rtc->features);
- }
+ regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO,
+ PIN_IO_TSPM | PIN_IO_TSIM,
+ PIN_IO_TSPM | PIN_IO_TSIM);
+
+ if (irq_a > 0 || wakeup_source)
+ device_init_wakeup(dev, true);
+
+ dev_set_drvdata(&pcf85363->rtc->dev, pcf85363);
+
+ ret = rtc_add_group(pcf85363->rtc, &pcf85363_attr_group);
+ if (ret)
+ return ret;
ret = devm_rtc_register_device(pcf85363->rtc);
+ if (ret)
+ return dev_err_probe(dev, ret, "RTC registration failed\n");
for (i = 0; i < config->num_nvram; i++) {
nvmem_cfg[i].priv = pcf85363;
--
2.25.1
^ permalink raw reply related
* [PATCH v4 2/5] rtc: pcf85363: support reporting battery switch-over via RTC_VL
From: Lakshay Piplani @ 2025-11-21 12:11 UTC (permalink / raw)
To: alexandre.belloni, linux-rtc, linux-kernel, robh, krzk+dt,
conor+dt, devicetree, wim, linux, linux-watchdog
Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
Lakshay Piplani
In-Reply-To: <20251121121137.3043764-1-lakshay.piplani@nxp.com>
Add battery switch-over reporting for PCF85263/PCF85363 using the standard
RTC_VL_* ioctl interface. When the backup supply takes over, the BSF flag
is exposed to userspace through RTC_VL_READ and can be cleared using
RTC_VL_CLR.
This allows applications to detect loss of main power without relying on
non-standard interfaces.
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
---
V3 -> V4:
- No changes in v4.
V2 -> V3:
- Split into separate patches as suggested:
- Battery switch-over detection.
- Timestamp recording for TS pin and battery switch-over events.
- Offset calibration.
- Watchdog timer (to be reviewed by watchdog maintainers).
- Dropped Alarm2 support
- Switched to rtc_add_group() for sysfs attributes
V1 -> V2:
- Watchdog related changes due to removal of vendor specific properties
from device tree
* remove vendor DT knobs (enable/timeout/stepsize/repeat)
* use watchdog_init_timeout (with 10s default)
* derive clock_sel from final timeout
* default, repeat=true (repeat mode)
- Fixed uninitalised warning on 'ret' (reported by kernel test robot)
- Use dev_dbg instead of dev_info for debug related print messages
- Minor cleanup and comments.
drivers/rtc/rtc-pcf85363.c | 49 ++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
index 540042b9eec8..c03d5a65c5f7 100644
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -14,6 +14,7 @@
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/bcd.h>
+#include <linux/device.h>
#include <linux/of.h>
#include <linux/regmap.h>
@@ -295,23 +296,67 @@ static int pcf85363_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id)
{
struct pcf85363 *pcf85363 = i2c_get_clientdata(dev_id);
+ bool handled = false;
unsigned int flags;
int err;
err = regmap_read(pcf85363->regmap, CTRL_FLAGS, &flags);
+
if (err)
return IRQ_NONE;
+ if (flags) {
+ dev_dbg(&pcf85363->rtc->dev, "IRQ flags: 0x%02x%s%s\n",
+ flags, (flags & FLAGS_A1F) ? " [A1F]" : "",
+ (flags & FLAGS_BSF) ? " [BSF]" : "");
+ }
+
if (flags & FLAGS_A1F) {
rtc_update_irq(pcf85363->rtc, 1, RTC_IRQF | RTC_AF);
regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_A1F, 0);
- return IRQ_HANDLED;
+ handled = true;
}
- return IRQ_NONE;
+ if (flags & FLAGS_BSF) {
+ regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_BSF, 0);
+ handled = true;
+ }
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int pcf85363_rtc_ioctl(struct device *dev,
+ unsigned int cmd, unsigned long arg)
+{
+ struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ switch (cmd) {
+ case RTC_VL_READ: {
+ u32 status = 0;
+
+ ret = regmap_read(pcf85363->regmap, CTRL_FLAGS, &val);
+
+ if (ret)
+ return ret;
+
+ if (val & FLAGS_BSF)
+ status |= RTC_VL_BACKUP_SWITCH;
+
+ return put_user(status, (u32 __user *)arg);
+ }
+
+ case RTC_VL_CLR:
+ return regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_BSF, 0);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
}
static const struct rtc_class_ops rtc_ops = {
+ .ioctl = pcf85363_rtc_ioctl,
.read_time = pcf85363_rtc_read_time,
.set_time = pcf85363_rtc_set_time,
.read_alarm = pcf85363_rtc_read_alarm,
--
2.25.1
^ permalink raw reply related
* [PATCH v4 1/5] dt-bindings: rtc: nxp,pcf85363: add timestamp mode config
From: Lakshay Piplani @ 2025-11-21 12:11 UTC (permalink / raw)
To: alexandre.belloni, linux-rtc, linux-kernel, robh, krzk+dt,
conor+dt, devicetree, wim, linux, linux-watchdog
Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
Lakshay Piplani
NXP PCF85263/PCF85363 provides three timestamp registers (TSR1-TSR3)
which latch the current time when a selected event occurs. Add a
vendor specific property, nxp,timestamp-mode, to select the event
source for each register.
Also introduce a new header 'pcf85363-tsr.h' to expose
macros for timestamp mode fields, improving readability
of device tree file.
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
V3 -> V4:
- Added Reviewed-by tag in commit message (previously only in changelog)
V2 -> V3:
- No changes in v3
- Added Reviewed-by: Rob Herring <robh@kernel.org>
V1 -> V2:
- Addressed review comments from Rob Herring:
* use $ref: /schemas/types.yaml#/definitions/uint32-array
* tuple form with exactly 3 items (TSR1/TSR2/TSR3), per items decimal enums
* define 'nxp,timestamp-mode' clearly
* drop watchdog related vendor properties
* remove watchdog related vendor properties from i2c example
.../devicetree/bindings/rtc/nxp,pcf85363.yaml | 23 ++++++++++++++-
include/dt-bindings/rtc/pcf85363-tsr.h | 28 +++++++++++++++++++
2 files changed, 50 insertions(+), 1 deletion(-)
create mode 100644 include/dt-bindings/rtc/pcf85363-tsr.h
diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf85363.yaml b/Documentation/devicetree/bindings/rtc/nxp,pcf85363.yaml
index 52aa3e2091e9..cf9c155162d6 100644
--- a/Documentation/devicetree/bindings/rtc/nxp,pcf85363.yaml
+++ b/Documentation/devicetree/bindings/rtc/nxp,pcf85363.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/rtc/nxp,pcf85363.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Philips PCF85263/PCF85363 Real Time Clock
+title: NXP PCF85263/PCF85363 Real Time Clock
maintainers:
- Alexandre Belloni <alexandre.belloni@bootlin.com>
@@ -39,6 +39,24 @@ properties:
start-year: true
wakeup-source: true
+ nxp,timestamp-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ items:
+ - enum: [0, 1, 2] # TSR1: NONE, FE, LE
+ description: TSR1 mode
+ - enum: [0, 1, 2, 3, 4, 5] # TSR2: NONE, FB, LB, LV, FE, LE
+ description: TSR2 mode
+ - enum: [0, 1, 2, 3] # TSR3: NONE, FB, LB, LV
+ description: TSR3 mode
+ description: |
+ Defines timestamp modes for TSR1, TSR2, and TSR3.
+ Use macros from <dt-bindings/rtc/pcf85363-tsr.h>.
+
+ Each value corresponds to a mode constant:
+ - TSR1: NONE, FE, LE
+ - TSR2: NONE, FB, LB, LV, FE, LE
+ - TSR3: NONE, FB, LB, LV
+
required:
- compatible
- reg
@@ -47,6 +65,7 @@ additionalProperties: false
examples:
- |
+ #include <dt-bindings/rtc/pcf85363-tsr.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
@@ -56,5 +75,7 @@ examples:
reg = <0x51>;
#clock-cells = <0>;
quartz-load-femtofarads = <12500>;
+ wakeup-source;
+ nxp,timestamp-mode = <PCF85363_TSR1_FE PCF85363_TSR2_LB PCF85363_TSR3_LV>;
};
};
diff --git a/include/dt-bindings/rtc/pcf85363-tsr.h b/include/dt-bindings/rtc/pcf85363-tsr.h
new file mode 100644
index 000000000000..1fb5b9b3601e
--- /dev/null
+++ b/include/dt-bindings/rtc/pcf85363-tsr.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * Copyright 2025 NXP
+ */
+
+#ifndef _DT_BINDINGS_RTC_PCF85363_TSR_H
+#define _DT_BINDINGS_RTC_PCF85363_TSR_H
+
+/* TSR1 modes */
+#define PCF85363_TSR1_NONE 0x00
+#define PCF85363_TSR1_FE 0x01
+#define PCF85363_TSR1_LE 0x02
+
+/* TSR2 modes */
+#define PCF85363_TSR2_NONE 0x00
+#define PCF85363_TSR2_FB 0x01
+#define PCF85363_TSR2_LB 0x02
+#define PCF85363_TSR2_LV 0x03
+#define PCF85363_TSR2_FE 0x04
+#define PCF85363_TSR2_LE 0x05
+
+/* TSR3 modes */
+#define PCF85363_TSR3_NONE 0x00
+#define PCF85363_TSR3_FB 0x01
+#define PCF85363_TSR3_LB 0x02
+#define PCF85363_TSR3_LV 0x03
+
+#endif /* _DT_BINDINGS_RTC_PCF85363_TSR_H */
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v5 04/16] dt-bindings: battery: Voltage drop properties
From: Rob Herring (Arm) @ 2025-11-20 16:11 UTC (permalink / raw)
To: Matti Vaittinen
Cc: Pavel Machek, Alexandre Belloni, Linus Walleij, devicetree,
Liam Girdwood, Sebastian Reichel, Mark Brown, linux-gpio,
Andreas Kemnade, Michael Turquette, linux-pm, Krzysztof Kozlowski,
linux-kernel, Stephen Boyd, Matti Vaittinen, linux-clk,
Conor Dooley, Lee Jones, linux-rtc, linux-leds, Matti Vaittinen,
Bartosz Golaszewski
In-Reply-To: <93768cba6688714756fca49cc57d46a111885863.1763625920.git.mazziesaccount@gmail.com>
On Thu, 20 Nov 2025 10:20:24 +0200, Matti Vaittinen wrote:
> From: Matti Vaittinen <mazziesaccount@gmail.com>
>
> ROHM has developed a so called "zero-correction" -algorithm to improve
> the fuel-gauging accuracy close to the point where battery is depleted.
> This relies on battery specific "VDR" (voltage drop rate) tables, which
> are measured from the battery, and which describe the voltage drop rate.
> More thorough explanation about the "zero correction" and "VDR"
> parameters is here:
> https://lore.kernel.org/all/676253b9-ff69-7891-1f26-a8b5bb5a421b@fi.rohmeurope.com/
>
> Document the VDR zero-correction specific battery properties used by the
> BD71815, BD71828, BD72720 and some other ROHM chargers. (Note, charger
> drivers aren't upstream yet).
>
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
>
> ---
>
> Revision history:
> v4 => v5:
> - Move volt-drop parameters from rohm,vdr-battry,yaml to the
> battery.yaml
> - drop rohm, -prefix from volt-drop-* properties
> - Drop the rohm,vdr-battry,yaml
> - Add comment clarifying what the rohm,volt-drop-* properties are for
> because this may no longer be obvious as they were moved to common
> battery.yaml
> - Drop Linus Walleij's rb-tag because the concept was changed
>
> v3 => v4:
> - No changes
>
> v2 => v3:
> - Constrain VDR threshold voltage to 48V
> - Use standard '-bp' -suffix for the rohm,volt-drop-soc
>
> RFCv1 => v2:
> - Add units to rohm,volt-drop-soc (tenths of %)
> - Give real temperatures matching the VDR tables, instead of vague
> 'high', 'normal', 'low', 'very low'. (Add table of temperatures and
> use number matching the right temperature index in the VDR table name).
> - Fix typoed 'algorithm' in commit message.
>
> The parameters are describing the battery voltage drop rates - so they
> are properties of the battery, not the charger. Thus they do not belong
> in the charger node.
>
> The right place for them is the battery node, which is described by the
> generic "battery.yaml". There were some discussion whether these
> properties should be in their own file, or if they should be added to
> battery.yaml. Discussion can be found from:
> https://lore.kernel.org/all/52b99bf7-bfea-4cee-aa57-4c13e87eaa0d@gmail.com/
> This patch implements the volt-drop properties as generic (not vemdor
> specific) properties in the battery.yaml. It's worth noting that these
> properties are:
>
> - Meaningful only for those charger drivers which have the VDR
> algorithm implemented. (And even though the algorithm is not charger
> specific, AFAICS, it is currently only used by some ROHM PMIC
> drivers).
> - Technique of measuring the VDR tables for a battery is not widely
> known. AFAICS, only folks at ROHM are measuring those for some
> customer products. We do have those tables available for some of the
> products, like Kobo e-readers though.
> ---
> .../bindings/power/supply/battery.yaml | 22 +++++++++++++++++++
> 1 file changed, 22 insertions(+)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH 3/3] mfd: sec: drop now unused struct sec_pmic_dev::irq_data
From: André Draszik @ 2025-11-20 14:38 UTC (permalink / raw)
To: Lee Jones
Cc: Krzysztof Kozlowski, Alexandre Belloni, Peter Griffin,
Tudor Ambarus, Will McVicker, Juan Yescas, Douglas Anderson,
kernel-team, Kaustabh Chakraborty, linux-kernel,
linux-samsung-soc, linux-rtc
In-Reply-To: <20251120103553.GZ1949330@google.com>
Hi Lee,
On Thu, 2025-11-20 at 10:35 +0000, Lee Jones wrote:
> On Fri, 14 Nov 2025, André Draszik wrote:
>
> > This was used only to allow the s5m RTC driver to deal with the alarm
> > IRQ. That driver now uses a different approach to acquire that IRQ, and
> > ::irq_data doesn't need to be kept around anymore.
> >
> > Signed-off-by: André Draszik <andre.draszik@linaro.org>
> > ---
> > drivers/mfd/sec-common.c | 5 +++--
> > drivers/mfd/sec-core.h | 2 +-
> > drivers/mfd/sec-irq.c | 10 ++--------
> > include/linux/mfd/samsung/core.h | 1 -
> > 4 files changed, 6 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c
> > index 77370db52a7ba81234136b29f85892f4b197f429..794c4e5398e7dd1a816aff9a6559a6c19fec75a5 100644
> > --- a/drivers/mfd/sec-common.c
> > +++ b/drivers/mfd/sec-common.c
> > @@ -163,6 +163,7 @@ sec_pmic_parse_dt_pdata(struct device *dev)
> > int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
> > struct regmap *regmap, struct i2c_client *client)
> > {
> > + struct regmap_irq_chip_data *irq_data;
> > struct sec_platform_data *pdata;
> > const struct mfd_cell *sec_devs;
> > struct sec_pmic_dev *sec_pmic;
> > @@ -187,7 +188,7 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
> >
> > sec_pmic->pdata = pdata;
> >
> > - ret = sec_irq_init(sec_pmic);
> > + ret = sec_irq_init(sec_pmic, &irq_data);
> > if (ret)
> > return ret;
> >
> > @@ -240,7 +241,7 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
> > sec_pmic->device_type);
> > }
> > ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs,
> > - NULL, 0, regmap_irq_get_domain(sec_pmic->irq_data));
> > + NULL, 0, regmap_irq_get_domain(irq_data));
> > if (ret)
> > return ret;
> >
> > diff --git a/drivers/mfd/sec-core.h b/drivers/mfd/sec-core.h
> > index 92c7558ab8b0de44a52e028eeb7998e38358cb4c..c639180ea686f4308af3f872cb1d2209d201b2e7 100644
> > --- a/drivers/mfd/sec-core.h
> > +++ b/drivers/mfd/sec-core.h
> > @@ -18,6 +18,6 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
> > struct regmap *regmap, struct i2c_client *client);
> > void sec_pmic_shutdown(struct device *dev);
> >
> > -int sec_irq_init(struct sec_pmic_dev *sec_pmic);
> > +int sec_irq_init(struct sec_pmic_dev *sec_pmic, struct regmap_irq_chip_data **irq_data);
> >
> > #endif /* __SEC_CORE_INT_H */
> > diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
> > index c5c80b1ba104e6c5a55b442d2f10a8554201a961..05d4cc350a351d994e00ba08f5ce966d0d5c6a0b 100644
> > --- a/drivers/mfd/sec-irq.c
> > +++ b/drivers/mfd/sec-irq.c
> > @@ -253,7 +253,7 @@ static const struct regmap_irq_chip s5m8767_irq_chip = {
> > .ack_base = S5M8767_REG_INT1,
> > };
> >
> > -int sec_irq_init(struct sec_pmic_dev *sec_pmic)
> > +int sec_irq_init(struct sec_pmic_dev *sec_pmic, struct regmap_irq_chip_data **irq_data)
>
> Instead of passing around pointers to pointers, why not return irq_data
> or NULL?
That was mainly to keep change smaller - I've updated the code as per
your suggestion in v2:
https://lore.kernel.org/r/20251120-s5m-alarm-v2-0-cc15f0e32161@linaro.org
Cheers,
Andre'
^ permalink raw reply
* [PATCH v2 3/3] mfd: sec: drop now unused struct sec_pmic_dev::irq_data
From: André Draszik @ 2025-11-20 14:38 UTC (permalink / raw)
To: Krzysztof Kozlowski, Lee Jones, Alexandre Belloni
Cc: Peter Griffin, Tudor Ambarus, Will McVicker, Juan Yescas,
Douglas Anderson, kernel-team, Kaustabh Chakraborty, linux-kernel,
linux-samsung-soc, linux-rtc, André Draszik
In-Reply-To: <20251120-s5m-alarm-v2-0-cc15f0e32161@linaro.org>
This was used only to allow the s5m RTC driver to deal with the alarm
IRQ. That driver now uses a different approach to acquire that IRQ, and
::irq_data doesn't need to be kept around anymore.
Signed-off-by: André Draszik <andre.draszik@linaro.org>
---
drivers/mfd/sec-common.c | 9 +++---
drivers/mfd/sec-core.h | 2 +-
drivers/mfd/sec-irq.c | 63 ++++++++++++++++++----------------------
include/linux/mfd/samsung/core.h | 1 -
4 files changed, 35 insertions(+), 40 deletions(-)
diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c
index 77370db52a7ba81234136b29f85892f4b197f429..0021f9ae8484fd0afc2e47c813a953c91fa38546 100644
--- a/drivers/mfd/sec-common.c
+++ b/drivers/mfd/sec-common.c
@@ -163,6 +163,7 @@ sec_pmic_parse_dt_pdata(struct device *dev)
int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
struct regmap *regmap, struct i2c_client *client)
{
+ struct regmap_irq_chip_data *irq_data;
struct sec_platform_data *pdata;
const struct mfd_cell *sec_devs;
struct sec_pmic_dev *sec_pmic;
@@ -187,9 +188,9 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
sec_pmic->pdata = pdata;
- ret = sec_irq_init(sec_pmic);
- if (ret)
- return ret;
+ irq_data = sec_irq_init(sec_pmic);
+ if (IS_ERR(irq_data))
+ return PTR_ERR(irq_data);
pm_runtime_set_active(sec_pmic->dev);
@@ -240,7 +241,7 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
sec_pmic->device_type);
}
ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs,
- NULL, 0, regmap_irq_get_domain(sec_pmic->irq_data));
+ NULL, 0, regmap_irq_get_domain(irq_data));
if (ret)
return ret;
diff --git a/drivers/mfd/sec-core.h b/drivers/mfd/sec-core.h
index 92c7558ab8b0de44a52e028eeb7998e38358cb4c..8d85c70c232612d1f7e5fb61b2acd25bf03a62e0 100644
--- a/drivers/mfd/sec-core.h
+++ b/drivers/mfd/sec-core.h
@@ -18,6 +18,6 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
struct regmap *regmap, struct i2c_client *client);
void sec_pmic_shutdown(struct device *dev);
-int sec_irq_init(struct sec_pmic_dev *sec_pmic);
+struct regmap_irq_chip_data *sec_irq_init(struct sec_pmic_dev *sec_pmic);
#endif /* __SEC_CORE_INT_H */
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
index d992e41e716dcdc060421e1db8475523842a12be..96f53c3617da4cb54f650f9b98c0b934b823ceda 100644
--- a/drivers/mfd/sec-irq.c
+++ b/drivers/mfd/sec-irq.c
@@ -268,26 +268,28 @@ static const struct regmap_irq_chip s5m8767_irq_chip = {
.ack_base = S5M8767_REG_INT1,
};
-static int s2mpg1x_add_chained_irq_chip(struct device *dev, struct regmap *regmap, int pirq,
- struct regmap_irq_chip_data *parent,
- const struct regmap_irq_chip *chip,
- struct regmap_irq_chip_data **data)
+static struct regmap_irq_chip_data *
+s2mpg1x_add_chained_irq_chip(struct device *dev, struct regmap *regmap, int pirq,
+ struct regmap_irq_chip_data *parent,
+ const struct regmap_irq_chip *chip)
{
+ struct regmap_irq_chip_data *data;
int irq, ret;
irq = regmap_irq_get_virq(parent, pirq);
if (irq < 0)
- return dev_err_probe(dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n", pirq,
- chip->name);
+ return dev_err_ptr_probe(dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n",
+ pirq, chip->name);
- ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT | IRQF_SHARED, 0, chip, data);
+ ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT | IRQF_SHARED, 0, chip,
+ &data);
if (ret)
- return dev_err_probe(dev, ret, "Failed to add %s IRQ chip\n", chip->name);
+ return dev_err_ptr_probe(dev, ret, "Failed to add %s IRQ chip\n", chip->name);
- return 0;
+ return data;
}
-static int sec_irq_init_s2mpg1x(struct sec_pmic_dev *sec_pmic)
+static struct regmap_irq_chip_data *sec_irq_init_s2mpg1x(struct sec_pmic_dev *sec_pmic)
{
const struct regmap_irq_chip *irq_chip, *chained_irq_chip;
struct regmap_irq_chip_data *irq_data;
@@ -302,27 +304,28 @@ static int sec_irq_init_s2mpg1x(struct sec_pmic_dev *sec_pmic)
chained_pirq = S2MPG10_COMMON_IRQ_PMIC;
break;
default:
- return dev_err_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n",
- sec_pmic->device_type);
+ return dev_err_ptr_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n",
+ sec_pmic->device_type);
};
regmap_common = dev_get_regmap(sec_pmic->dev, "common");
if (!regmap_common)
- return dev_err_probe(sec_pmic->dev, -EINVAL, "No 'common' regmap %d\n",
- sec_pmic->device_type);
+ return dev_err_ptr_probe(sec_pmic->dev, -EINVAL, "No 'common' regmap %d\n",
+ sec_pmic->device_type);
ret = devm_regmap_add_irq_chip(sec_pmic->dev, regmap_common, sec_pmic->irq, IRQF_ONESHOT, 0,
irq_chip, &irq_data);
if (ret)
- return dev_err_probe(sec_pmic->dev, ret, "Failed to add %s IRQ chip\n",
- irq_chip->name);
+ return dev_err_ptr_probe(sec_pmic->dev, ret, "Failed to add %s IRQ chip\n",
+ irq_chip->name);
return s2mpg1x_add_chained_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic, chained_pirq,
- irq_data, chained_irq_chip, &sec_pmic->irq_data);
+ irq_data, chained_irq_chip);
}
-int sec_irq_init(struct sec_pmic_dev *sec_pmic)
+struct regmap_irq_chip_data *sec_irq_init(struct sec_pmic_dev *sec_pmic)
{
+ struct regmap_irq_chip_data *sec_irq_chip_data;
const struct regmap_irq_chip *sec_irq_chip;
int ret;
@@ -331,7 +334,7 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
sec_irq_chip = &s5m8767_irq_chip;
break;
case S2DOS05:
- return 0;
+ return NULL;
case S2MPA01:
sec_irq_chip = &s2mps14_irq_chip;
break;
@@ -356,30 +359,22 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
sec_irq_chip = &s2mpu05_irq_chip;
break;
default:
- return dev_err_probe(sec_pmic->dev, -EINVAL,
- "Unsupported device type %d\n",
- sec_pmic->device_type);
+ return dev_err_ptr_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n",
+ sec_pmic->device_type);
}
if (!sec_pmic->irq) {
dev_warn(sec_pmic->dev,
"No interrupt specified, no interrupts\n");
- return 0;
+ return NULL;
}
ret = devm_regmap_add_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic,
sec_pmic->irq, IRQF_ONESHOT,
- 0, sec_irq_chip, &sec_pmic->irq_data);
+ 0, sec_irq_chip, &sec_irq_chip_data);
if (ret)
- return dev_err_probe(sec_pmic->dev, ret,
- "Failed to add %s IRQ chip\n",
- sec_irq_chip->name);
+ return dev_err_ptr_probe(sec_pmic->dev, ret, "Failed to add %s IRQ chip\n",
+ sec_irq_chip->name);
- /*
- * The rtc-s5m driver requests S2MPS14_IRQ_RTCA0 also for S2MPS11
- * so the interrupt number must be consistent.
- */
- BUILD_BUG_ON(((enum s2mps14_irq)S2MPS11_IRQ_RTCA0) != S2MPS14_IRQ_RTCA0);
-
- return 0;
+ return sec_irq_chip_data;
}
diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h
index d785e101fe795a5d8f9cccf4ccc4232437e89416..c7c3c8cd8d5f99ef0cc3188e1c3b49031f4750f2 100644
--- a/include/linux/mfd/samsung/core.h
+++ b/include/linux/mfd/samsung/core.h
@@ -69,7 +69,6 @@ struct sec_pmic_dev {
int device_type;
int irq;
- struct regmap_irq_chip_data *irq_data;
};
struct sec_platform_data {
--
2.52.0.rc1.455.g30608eb744-goog
^ permalink raw reply related
* [PATCH v2 1/3] mfd: sec: add rtc alarm IRQ as platform device resource
From: André Draszik @ 2025-11-20 14:38 UTC (permalink / raw)
To: Krzysztof Kozlowski, Lee Jones, Alexandre Belloni
Cc: Peter Griffin, Tudor Ambarus, Will McVicker, Juan Yescas,
Douglas Anderson, kernel-team, Kaustabh Chakraborty, linux-kernel,
linux-samsung-soc, linux-rtc, André Draszik
In-Reply-To: <20251120-s5m-alarm-v2-0-cc15f0e32161@linaro.org>
By adding the RTC alarm IRQ to the MFD cell as a resource, the child
driver (rtc) can simply query that IRQ, instead of having a lookup
table itself.
This change therefore allows the child driver to be simplified with
regards to determining the alarm IRQ.
Signed-off-by: André Draszik <andre.draszik@linaro.org>
---
drivers/mfd/sec-common.c | 38 +++++++++++++++++++++++++++++---------
1 file changed, 29 insertions(+), 9 deletions(-)
diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c
index 42d55e70e34c8d7cd68cddaecc88017e259365b4..77370db52a7ba81234136b29f85892f4b197f429 100644
--- a/drivers/mfd/sec-common.c
+++ b/drivers/mfd/sec-common.c
@@ -23,9 +23,13 @@
#include <linux/regmap.h>
#include "sec-core.h"
+static const struct resource s5m8767_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S5M8767_IRQ_RTCA1, "alarm"),
+};
+
static const struct mfd_cell s5m8767_devs[] = {
MFD_CELL_NAME("s5m8767-pmic"),
- MFD_CELL_NAME("s5m-rtc"),
+ MFD_CELL_RES("s5m-rtc", s5m8767_rtc_resources),
MFD_CELL_OF("s5m8767-clk", NULL, NULL, 0, 0, "samsung,s5m8767-clk"),
};
@@ -33,50 +37,66 @@ static const struct mfd_cell s2dos05_devs[] = {
MFD_CELL_NAME("s2dos05-regulator"),
};
+static const struct resource s2mpg10_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S2MPG10_IRQ_RTCA0, "alarm"),
+};
+
static const struct mfd_cell s2mpg10_devs[] = {
MFD_CELL_NAME("s2mpg10-meter"),
MFD_CELL_NAME("s2mpg10-regulator"),
- MFD_CELL_NAME("s2mpg10-rtc"),
+ MFD_CELL_RES("s2mpg10-rtc", s2mpg10_rtc_resources),
MFD_CELL_OF("s2mpg10-clk", NULL, NULL, 0, 0, "samsung,s2mpg10-clk"),
MFD_CELL_OF("s2mpg10-gpio", NULL, NULL, 0, 0, "samsung,s2mpg10-gpio"),
};
+static const struct resource s2mps11_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S2MPS11_IRQ_RTCA0, "alarm"),
+};
+
static const struct mfd_cell s2mps11_devs[] = {
MFD_CELL_NAME("s2mps11-regulator"),
- MFD_CELL_NAME("s2mps14-rtc"),
+ MFD_CELL_RES("s2mps14-rtc", s2mps11_rtc_resources),
MFD_CELL_OF("s2mps11-clk", NULL, NULL, 0, 0, "samsung,s2mps11-clk"),
};
+static const struct resource s2mps14_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S2MPS14_IRQ_RTCA0, "alarm"),
+};
+
static const struct mfd_cell s2mps13_devs[] = {
MFD_CELL_NAME("s2mps13-regulator"),
- MFD_CELL_NAME("s2mps13-rtc"),
+ MFD_CELL_RES("s2mps13-rtc", s2mps14_rtc_resources),
MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"),
};
static const struct mfd_cell s2mps14_devs[] = {
MFD_CELL_NAME("s2mps14-regulator"),
- MFD_CELL_NAME("s2mps14-rtc"),
+ MFD_CELL_RES("s2mps14-rtc", s2mps14_rtc_resources),
MFD_CELL_OF("s2mps14-clk", NULL, NULL, 0, 0, "samsung,s2mps14-clk"),
};
static const struct mfd_cell s2mps15_devs[] = {
MFD_CELL_NAME("s2mps15-regulator"),
- MFD_CELL_NAME("s2mps15-rtc"),
+ MFD_CELL_RES("s2mps15-rtc", s2mps14_rtc_resources),
MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"),
};
static const struct mfd_cell s2mpa01_devs[] = {
MFD_CELL_NAME("s2mpa01-pmic"),
- MFD_CELL_NAME("s2mps14-rtc"),
+ MFD_CELL_RES("s2mps14-rtc", s2mps14_rtc_resources),
};
static const struct mfd_cell s2mpu02_devs[] = {
MFD_CELL_NAME("s2mpu02-regulator"),
};
+static const struct resource s2mpu05_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S2MPU05_IRQ_RTCA0, "alarm"),
+};
+
static const struct mfd_cell s2mpu05_devs[] = {
MFD_CELL_NAME("s2mpu05-regulator"),
- MFD_CELL_NAME("s2mps15-rtc"),
+ MFD_CELL_RES("s2mps15-rtc", s2mpu05_rtc_resources),
};
static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic)
@@ -220,7 +240,7 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
sec_pmic->device_type);
}
ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs,
- NULL, 0, NULL);
+ NULL, 0, regmap_irq_get_domain(sec_pmic->irq_data));
if (ret)
return ret;
--
2.52.0.rc1.455.g30608eb744-goog
^ permalink raw reply related
* [PATCH v2 2/3] rtc: s5m: query platform device IRQ resource for alarm IRQ
From: André Draszik @ 2025-11-20 14:38 UTC (permalink / raw)
To: Krzysztof Kozlowski, Lee Jones, Alexandre Belloni
Cc: Peter Griffin, Tudor Ambarus, Will McVicker, Juan Yescas,
Douglas Anderson, kernel-team, Kaustabh Chakraborty, linux-kernel,
linux-samsung-soc, linux-rtc, André Draszik
In-Reply-To: <20251120-s5m-alarm-v2-0-cc15f0e32161@linaro.org>
The core driver now exposes the alarm IRQ as a resource, so we can drop
the lookup from here to simplify the code and make adding support for
additional variants easier in this driver.
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: André Draszik <andre.draszik@linaro.org>
---
drivers/rtc/rtc-s5m.c | 21 ++++++++-------------
1 file changed, 8 insertions(+), 13 deletions(-)
diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c
index a7220b4d0e8dd35786b060e2a4106e2a39fe743f..c6ed5a4ca8a0e4554b1c88c879b01fc384735007 100644
--- a/drivers/rtc/rtc-s5m.c
+++ b/drivers/rtc/rtc-s5m.c
@@ -15,7 +15,6 @@
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/mfd/samsung/core.h>
-#include <linux/mfd/samsung/irq.h>
#include <linux/mfd/samsung/rtc.h>
#include <linux/mfd/samsung/s2mps14.h>
@@ -683,22 +682,18 @@ static int s5m_rtc_probe(struct platform_device *pdev)
case S2MPS15X:
regmap_cfg = &s2mps14_rtc_regmap_config;
info->regs = &s2mps15_rtc_regs;
- alarm_irq = S2MPS14_IRQ_RTCA0;
break;
case S2MPS14X:
regmap_cfg = &s2mps14_rtc_regmap_config;
info->regs = &s2mps14_rtc_regs;
- alarm_irq = S2MPS14_IRQ_RTCA0;
break;
case S2MPS13X:
regmap_cfg = &s2mps14_rtc_regmap_config;
info->regs = &s2mps13_rtc_regs;
- alarm_irq = S2MPS14_IRQ_RTCA0;
break;
case S5M8767X:
regmap_cfg = &s5m_rtc_regmap_config;
info->regs = &s5m_rtc_regs;
- alarm_irq = S5M8767_IRQ_RTCA1;
break;
default:
return dev_err_probe(&pdev->dev, -ENODEV,
@@ -719,7 +714,6 @@ static int s5m_rtc_probe(struct platform_device *pdev)
"Failed to allocate regmap\n");
} else if (device_type == S2MPG10) {
info->regs = &s2mpg10_rtc_regs;
- alarm_irq = S2MPG10_IRQ_RTCA0;
} else {
return dev_err_probe(&pdev->dev, -ENODEV,
"Unsupported device type %d\n",
@@ -730,13 +724,14 @@ static int s5m_rtc_probe(struct platform_device *pdev)
info->s5m87xx = s5m87xx;
info->device_type = device_type;
- if (s5m87xx->irq_data) {
- info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq);
- if (info->irq <= 0)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "Failed to get virtual IRQ %d\n",
- alarm_irq);
- }
+ alarm_irq = platform_get_irq_byname_optional(pdev, "alarm");
+ if (alarm_irq > 0)
+ info->irq = alarm_irq;
+ else if (alarm_irq == -ENXIO)
+ info->irq = 0;
+ else
+ return dev_err_probe(&pdev->dev, alarm_irq ? : -EINVAL,
+ "IRQ 'alarm' not found\n");
platform_set_drvdata(pdev, info);
--
2.52.0.rc1.455.g30608eb744-goog
^ permalink raw reply related
* [PATCH v2 0/3] Samsung mfd/rtc driver alarm IRQ simplification
From: André Draszik @ 2025-11-20 14:38 UTC (permalink / raw)
To: Krzysztof Kozlowski, Lee Jones, Alexandre Belloni
Cc: Peter Griffin, Tudor Ambarus, Will McVicker, Juan Yescas,
Douglas Anderson, kernel-team, Kaustabh Chakraborty, linux-kernel,
linux-samsung-soc, linux-rtc, André Draszik
Hi,
With the attached patches the Samsung s5m RTC driver is simplified a
little bit with regards to alarm IRQ acquisition.
The end result is that instead of having a list of IRQ numbers for each
variant (and a BUILD_BUG_ON() to ensure consistency), the RTC driver
queries the 'alarm' platform resource from the parent (mfd cell).
Additionally, we can drop a now-useless field from runtime data,
reducing memory consumption slightly.
The attached patches must be applied in-order as patch 2 without 1 will
fail at runtime, and patch 3 without 2 will fail at build time. I would
expect them all to go via the MFD tree. Alternatively, they could be
applied individually to the respective kernel trees during multiple
kernel release cycles, but that seems a needless complication and
delay.
Signed-off-by: André Draszik <andre.draszik@linaro.org>
---
Changes in v2:
- rebase on top of https://lore.kernel.org/r/20251114-s2mpg10-chained-irq-v1-1-34ddfa49c4cd@linaro.org
- return struct regmap_irq_chip_data * in sec_irq_init() (Lee)
- collect tags
- Link to v1: https://lore.kernel.org/r/20251114-s5m-alarm-v1-0-c9b3bebae65f@linaro.org
---
André Draszik (3):
mfd: sec: add rtc alarm IRQ as platform device resource
rtc: s5m: query platform device IRQ resource for alarm IRQ
mfd: sec: drop now unused struct sec_pmic_dev::irq_data
drivers/mfd/sec-common.c | 45 ++++++++++++++++++++--------
drivers/mfd/sec-core.h | 2 +-
drivers/mfd/sec-irq.c | 63 ++++++++++++++++++----------------------
drivers/rtc/rtc-s5m.c | 21 +++++---------
include/linux/mfd/samsung/core.h | 1 -
5 files changed, 71 insertions(+), 61 deletions(-)
---
base-commit: 9ad5de6d54f306b2bbf7ceb27e67a60c58a71224
change-id: 20251114-s5m-alarm-3de705ea53ce
Best regards,
--
André Draszik <andre.draszik@linaro.org>
^ permalink raw reply
* Re: [PATCH v5 00/11] mfd: macsmc: add rtc, hwmon and hid subdevices
From: Lee Jones @ 2025-11-20 13:47 UTC (permalink / raw)
To: James Calligeros
Cc: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alexandre Belloni,
Jean Delvare, Guenter Roeck, Dmitry Torokhov, Jonathan Corbet,
asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
linux-hwmon, linux-input, linux-doc, Mark Kettenis, Hector Martin
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>
On Wed, 12 Nov 2025, James Calligeros wrote:
> Hi all,
>
> This series adds support for the remaining SMC subdevices. These are the
> RTC, hwmon, and HID devices. They are being submitted together as the RTC
> and hwmon drivers both require changes to the SMC DT schema.
>
> The RTC driver is responsible for getting and setting the system clock,
> and requires an NVMEM cell. This series replaces Sven's original RTC driver
> submission [1].
>
> The hwmon function is an interesting one. While each Apple Silicon device
> exposes pretty similar sets of sensors, these all seem to be paired to
> different SMC keys in the firmware interface. This is true even when the
> sensors are on the SoC. For example, an M1 MacBook Pro will use different
> keys to access the LITTLE core temperature sensors to an M1 Mac mini. This
> necessitates describing which keys correspond to which sensors for each
> device individually, and populating the hwmon structs at runtime. We do
> this with a node in the device tree. This series includes only the keys
> for sensors which we know to be common to all devices. The SMC is also
> responsible for monitoring and controlling fan speeds on systems with fans,
> which we expose via the hwmon driver.
>
> The SMC also handles the hardware power button and lid switch. Power
> button presses and lid opening/closing are emitted as HID events, so we
> add an input subdevice to handle them.
>
> Since there are no real dependencies between the components of this series,
> it should be fine for each subsystem to take the relevant patches through
> their trees. The mfd one-liners should be taken in order to avoid trivial
> conflicts. Per [2], the hwmon driver should be merged along with the preceding
> mfd patch adding the __SMC_KEY macro to avoid build errors.
Apart from my (perhaps naive) question on patch 5, the other MFD patches
look okay to me. Once my question has been answered, I can apply the
MFD, or at least 3 of them, orthogonally.
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH v5 05/11] mfd: macsmc: Add new __SMC_KEY macro
From: Lee Jones @ 2025-11-20 13:44 UTC (permalink / raw)
To: James Calligeros
Cc: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alexandre Belloni,
Jean Delvare, Guenter Roeck, Dmitry Torokhov, Jonathan Corbet,
asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
linux-hwmon, linux-input, linux-doc
In-Reply-To: <20251112-macsmc-subdevs-v5-5-728e4b91fe81@gmail.com>
On Wed, 12 Nov 2025, James Calligeros wrote:
> When using the _SMC_KEY macro in switch/case statements, GCC 15.2.1 errors
> out with 'case label does not reduce to an integer constant'. Introduce
> a new __SMC_KEY macro that can be used instead.
>
> Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
> ---
> include/linux/mfd/macsmc.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/include/linux/mfd/macsmc.h b/include/linux/mfd/macsmc.h
> index 6b13f01a8592..f6f80c33b5cf 100644
> --- a/include/linux/mfd/macsmc.h
> +++ b/include/linux/mfd/macsmc.h
> @@ -41,6 +41,7 @@ typedef u32 smc_key;
> */
> #define SMC_KEY(s) (smc_key)(_SMC_KEY(#s))
> #define _SMC_KEY(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3])
> +#define __SMC_KEY(a, b, c, d) (((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(c) << 8) | ((u32)(d)))
Are we expecting users/consumers to be able to tell the difference
between SMC_KEY and __SMC_KEY (assuming that _SMC_KEY is just an
internal)?
I have not tested this and it is just off the top of my head, but does
this work:
#define _SMC_KEY(s) __SMC_KEY((s)[0], (s)[1], (s)[2], (s)[3])
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH 3/3] mfd: sec: drop now unused struct sec_pmic_dev::irq_data
From: Lee Jones @ 2025-11-20 10:35 UTC (permalink / raw)
To: André Draszik
Cc: Krzysztof Kozlowski, Alexandre Belloni, Peter Griffin,
Tudor Ambarus, Will McVicker, Juan Yescas, Douglas Anderson,
kernel-team, Kaustabh Chakraborty, linux-kernel,
linux-samsung-soc, linux-rtc
In-Reply-To: <20251114-s5m-alarm-v1-3-c9b3bebae65f@linaro.org>
On Fri, 14 Nov 2025, André Draszik wrote:
> This was used only to allow the s5m RTC driver to deal with the alarm
> IRQ. That driver now uses a different approach to acquire that IRQ, and
> ::irq_data doesn't need to be kept around anymore.
>
> Signed-off-by: André Draszik <andre.draszik@linaro.org>
> ---
> drivers/mfd/sec-common.c | 5 +++--
> drivers/mfd/sec-core.h | 2 +-
> drivers/mfd/sec-irq.c | 10 ++--------
> include/linux/mfd/samsung/core.h | 1 -
> 4 files changed, 6 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c
> index 77370db52a7ba81234136b29f85892f4b197f429..794c4e5398e7dd1a816aff9a6559a6c19fec75a5 100644
> --- a/drivers/mfd/sec-common.c
> +++ b/drivers/mfd/sec-common.c
> @@ -163,6 +163,7 @@ sec_pmic_parse_dt_pdata(struct device *dev)
> int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
> struct regmap *regmap, struct i2c_client *client)
> {
> + struct regmap_irq_chip_data *irq_data;
> struct sec_platform_data *pdata;
> const struct mfd_cell *sec_devs;
> struct sec_pmic_dev *sec_pmic;
> @@ -187,7 +188,7 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
>
> sec_pmic->pdata = pdata;
>
> - ret = sec_irq_init(sec_pmic);
> + ret = sec_irq_init(sec_pmic, &irq_data);
> if (ret)
> return ret;
>
> @@ -240,7 +241,7 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
> sec_pmic->device_type);
> }
> ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs,
> - NULL, 0, regmap_irq_get_domain(sec_pmic->irq_data));
> + NULL, 0, regmap_irq_get_domain(irq_data));
> if (ret)
> return ret;
>
> diff --git a/drivers/mfd/sec-core.h b/drivers/mfd/sec-core.h
> index 92c7558ab8b0de44a52e028eeb7998e38358cb4c..c639180ea686f4308af3f872cb1d2209d201b2e7 100644
> --- a/drivers/mfd/sec-core.h
> +++ b/drivers/mfd/sec-core.h
> @@ -18,6 +18,6 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
> struct regmap *regmap, struct i2c_client *client);
> void sec_pmic_shutdown(struct device *dev);
>
> -int sec_irq_init(struct sec_pmic_dev *sec_pmic);
> +int sec_irq_init(struct sec_pmic_dev *sec_pmic, struct regmap_irq_chip_data **irq_data);
>
> #endif /* __SEC_CORE_INT_H */
> diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
> index c5c80b1ba104e6c5a55b442d2f10a8554201a961..05d4cc350a351d994e00ba08f5ce966d0d5c6a0b 100644
> --- a/drivers/mfd/sec-irq.c
> +++ b/drivers/mfd/sec-irq.c
> @@ -253,7 +253,7 @@ static const struct regmap_irq_chip s5m8767_irq_chip = {
> .ack_base = S5M8767_REG_INT1,
> };
>
> -int sec_irq_init(struct sec_pmic_dev *sec_pmic)
> +int sec_irq_init(struct sec_pmic_dev *sec_pmic, struct regmap_irq_chip_data **irq_data)
Instead of passing around pointers to pointers, why not return irq_data
or NULL?
> {
> const struct regmap_irq_chip *sec_irq_chip;
> int ret;
> @@ -302,17 +302,11 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
>
> ret = devm_regmap_add_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic,
> sec_pmic->irq, IRQF_ONESHOT,
> - 0, sec_irq_chip, &sec_pmic->irq_data);
> + 0, sec_irq_chip, irq_data);
> if (ret)
> return dev_err_probe(sec_pmic->dev, ret,
> "Failed to add %s IRQ chip\n",
> sec_irq_chip->name);
>
> - /*
> - * The rtc-s5m driver requests S2MPS14_IRQ_RTCA0 also for S2MPS11
> - * so the interrupt number must be consistent.
> - */
> - BUILD_BUG_ON(((enum s2mps14_irq)S2MPS11_IRQ_RTCA0) != S2MPS14_IRQ_RTCA0);
> -
> return 0;
> }
> diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h
> index d785e101fe795a5d8f9cccf4ccc4232437e89416..c7c3c8cd8d5f99ef0cc3188e1c3b49031f4750f2 100644
> --- a/include/linux/mfd/samsung/core.h
> +++ b/include/linux/mfd/samsung/core.h
> @@ -69,7 +69,6 @@ struct sec_pmic_dev {
>
> int device_type;
> int irq;
> - struct regmap_irq_chip_data *irq_data;
> };
>
> struct sec_platform_data {
>
> --
> 2.52.0.rc1.455.g30608eb744-goog
>
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH 0/3] Samsung mfd/rtc driver alarm IRQ simplification
From: André Draszik @ 2025-11-20 10:32 UTC (permalink / raw)
To: Lee Jones
Cc: Krzysztof Kozlowski, Alexandre Belloni, Peter Griffin,
Tudor Ambarus, Will McVicker, Juan Yescas, Douglas Anderson,
kernel-team, Kaustabh Chakraborty, linux-kernel,
linux-samsung-soc, linux-rtc
In-Reply-To: <20251120103104.GY1949330@google.com>
Hi Lee,
On Thu, 2025-11-20 at 10:31 +0000, Lee Jones wrote:
> On Fri, 14 Nov 2025, André Draszik wrote:
>
> > Hi,
> >
> > With the attached patches the Samsung s5m RTC driver is simplified a
> > little bit with regards to alarm IRQ acquisition.
> >
> > The end result is that instead of having a list of IRQ numbers for each
> > variant (and a BUILD_BUG_ON() to ensure consistency), the RTC driver
> > queries the 'alarm' platform resource from the parent (mfd cell).
> >
> > Additionally, we can drop a now-useless field from runtime data,
> > reducing memory consumption slightly.
> >
> > The attached patches must be applied in-order. I would expect them all
> > to go via the MFD tree. Alternatively, they could be applied one after
> > another during multiple kernel release cycles, but that seems a
> > needless complication.
> >
> > Signed-off-by: André Draszik <andre.draszik@linaro.org>
> > ---
> > André Draszik (3):
> > mfd: sec: add rtc alarm IRQ as platform device resource
> > rtc: s5m: query platform device IRQ resource for alarm IRQ
> > mfd: sec: drop now unused struct sec_pmic_dev::irq_data
> >
> > drivers/mfd/sec-common.c | 41 ++++++++++++++++++++++++++++++----------
> > drivers/mfd/sec-core.h | 2 +-
> > drivers/mfd/sec-irq.c | 10 ++--------
> > drivers/rtc/rtc-s5m.c | 21 ++++++++------------
> > include/linux/mfd/samsung/core.h | 1 -
> > 5 files changed, 42 insertions(+), 33 deletions(-)
>
> This needs to be rebased now, right?
Yes, I'll do that now. Thanks for merging the other patch.
Cheers,
Andre'
^ permalink raw reply
* Re: [PATCH 0/3] Samsung mfd/rtc driver alarm IRQ simplification
From: Lee Jones @ 2025-11-20 10:31 UTC (permalink / raw)
To: André Draszik
Cc: Krzysztof Kozlowski, Alexandre Belloni, Peter Griffin,
Tudor Ambarus, Will McVicker, Juan Yescas, Douglas Anderson,
kernel-team, Kaustabh Chakraborty, linux-kernel,
linux-samsung-soc, linux-rtc
In-Reply-To: <20251114-s5m-alarm-v1-0-c9b3bebae65f@linaro.org>
On Fri, 14 Nov 2025, André Draszik wrote:
> Hi,
>
> With the attached patches the Samsung s5m RTC driver is simplified a
> little bit with regards to alarm IRQ acquisition.
>
> The end result is that instead of having a list of IRQ numbers for each
> variant (and a BUILD_BUG_ON() to ensure consistency), the RTC driver
> queries the 'alarm' platform resource from the parent (mfd cell).
>
> Additionally, we can drop a now-useless field from runtime data,
> reducing memory consumption slightly.
>
> The attached patches must be applied in-order. I would expect them all
> to go via the MFD tree. Alternatively, they could be applied one after
> another during multiple kernel release cycles, but that seems a
> needless complication.
>
> Signed-off-by: André Draszik <andre.draszik@linaro.org>
> ---
> André Draszik (3):
> mfd: sec: add rtc alarm IRQ as platform device resource
> rtc: s5m: query platform device IRQ resource for alarm IRQ
> mfd: sec: drop now unused struct sec_pmic_dev::irq_data
>
> drivers/mfd/sec-common.c | 41 ++++++++++++++++++++++++++++++----------
> drivers/mfd/sec-core.h | 2 +-
> drivers/mfd/sec-irq.c | 10 ++--------
> drivers/rtc/rtc-s5m.c | 21 ++++++++------------
> include/linux/mfd/samsung/core.h | 1 -
> 5 files changed, 42 insertions(+), 33 deletions(-)
This needs to be rebased now, right?
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH v5 01/16] dt-bindings: regulator: ROHM BD72720
From: Rob Herring (Arm) @ 2025-11-20 9:57 UTC (permalink / raw)
To: Matti Vaittinen
Cc: Matti Vaittinen, Krzysztof Kozlowski, Linus Walleij,
Liam Girdwood, linux-gpio, Bartosz Golaszewski, Conor Dooley,
linux-leds, linux-pm, Alexandre Belloni, devicetree,
Sebastian Reichel, Michael Turquette, Stephen Boyd, Mark Brown,
Pavel Machek, linux-clk, Matti Vaittinen, Andreas Kemnade,
Lee Jones, linux-rtc, linux-kernel
In-Reply-To: <28726d1e0573a6efb6e70716a23ba27c4fc93c6d.1763625920.git.mazziesaccount@gmail.com>
On Thu, 20 Nov 2025 10:19:40 +0200, Matti Vaittinen wrote:
> From: Matti Vaittinen <mazziesaccount@gmail.com>
>
> The ROHM BD72720 is a new PMIC with 10 BUCk and 11 LDO regulators.
>
> The BD72720 is designed to support using the BUCK10 as a supply for
> the LDOs 1 to 4. When the BUCK10 is used for this, it can be set to a
> LDON_HEAD mode. In this mode, the BUCK10 voltage can't be controlled by
> software, but the voltage is adjusted by PMIC to match the LDO1 .. LDO4
> voltages with a given offset. Offset can be 50mV .. 300mV and is
> changeable at 50mV steps.
>
> Add 'ldon-head-microvolt' property to denote a board which is designed
> to utilize the LDON_HEAD mode.
>
> All other properties are already existing.
>
> Add dt-binding doc for ROHM BD72720 regulators to make it usable.
>
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
> Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
>
> ---
> Revision history:
> v4 =>
> - No changes
>
> v3 => v4:
> - Drop type from ldon-head
> - Fix the name patterns for regulator nodes and names
>
> v2 => v3:
> - drop unnecessary descriptions
> - use microvolts for the 'ldon-head' dt-property
>
> RFCv1 => v2:
> - No changes
> ---
> .../regulator/rohm,bd72720-regulator.yaml | 148 ++++++++++++++++++
> 1 file changed, 148 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/regulator/rohm,bd72720-regulator.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
doc reference errors (make refcheckdocs):
Warning: Documentation/devicetree/bindings/regulator/rohm,bd72720-regulator.yaml references a file that doesn't exist: Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml
Documentation/devicetree/bindings/regulator/rohm,bd72720-regulator.yaml: Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/28726d1e0573a6efb6e70716a23ba27c4fc93c6d.1763625920.git.mazziesaccount@gmail.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply
* [PATCH v5 16/16] MAINTAINERS: Add ROHM BD72720 PMIC
From: Matti Vaittinen @ 2025-11-20 8:24 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
Andreas Kemnade
In-Reply-To: <cover.1763625920.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1022 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Add the ROHM BD72720 PMIC driver files to be maintained by undersigned.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
RFCv1 =>:
- No changes
---
MAINTAINERS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index fe01aa31c58b..7e3c1eac7cda 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22353,6 +22353,7 @@ S: Supported
F: drivers/clk/clk-bd718x7.c
F: drivers/gpio/gpio-bd71815.c
F: drivers/gpio/gpio-bd71828.c
+F: drivers/gpio/gpio-bd72720.c
F: drivers/mfd/rohm-bd71828.c
F: drivers/mfd/rohm-bd718x7.c
F: drivers/mfd/rohm-bd9576.c
@@ -22369,6 +22370,7 @@ F: drivers/watchdog/bd96801_wdt.c
F: include/linux/mfd/rohm-bd71815.h
F: include/linux/mfd/rohm-bd71828.h
F: include/linux/mfd/rohm-bd718x7.h
+F: include/linux/mfd/rohm-bd72720.h
F: include/linux/mfd/rohm-bd957x.h
F: include/linux/mfd/rohm-bd96801.h
F: include/linux/mfd/rohm-bd96802.h
--
2.51.1
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v5 15/16] power: supply: bd71828-power: Support ROHM BD72720
From: Matti Vaittinen @ 2025-11-20 8:24 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
Andreas Kemnade
In-Reply-To: <cover.1763625920.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 11744 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
The ROHM BD72720 is a power management IC with a charger and coulomb
counter block which is closely related to the charger / coulomb counter
found from the BD71815, BD71828, BD71879 which are all supported by the
bd71828-power driver. Due to the similarities it makes sense to support
also the BD72720 with the same driver.
Add basic support for the charger logic on ROHM BD72720.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v2 => :
- No changes
RFCv1 => v2:
- Support using 9-bit register addresses (offset of 0x100) with the
BD72720
- Simplify probe and IC data as we don't need two regmaps
- Drop two BD72720 specific functions as we no longer need different
regmap for it.
Note: This patch depends on the series: "power: supply: add charger for
BD71828" by Andreas:
https://lore.kernel.org/all/20250918-bd71828-charger-v5-0-851164839c28@kemnade.info/
NOTE: Fuel-gauging is not supported. You can find an unmaintained
downstream reference-driver with a fuel-gauge example from:
https://github.com/RohmSemiconductor/Linux-Kernel-PMIC-Drivers/releases/tag/bd72720-reference-driver-v1
---
drivers/power/supply/bd71828-power.c | 134 +++++++++++++++++++++++----
1 file changed, 116 insertions(+), 18 deletions(-)
diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c
index ce73c0f48397..438e220a9cb7 100644
--- a/drivers/power/supply/bd71828-power.c
+++ b/drivers/power/supply/bd71828-power.c
@@ -5,6 +5,7 @@
#include <linux/kernel.h>
#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-bd72720.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
@@ -51,12 +52,14 @@ struct pwr_regs {
unsigned int chg_state;
unsigned int bat_temp;
unsigned int dcin_stat;
+ unsigned int dcin_online_mask;
unsigned int dcin_collapse_limit;
unsigned int chg_set1;
unsigned int chg_en;
unsigned int vbat_alm_limit_u;
unsigned int conf;
unsigned int vdcin;
+ unsigned int vdcin_himask;
};
static const struct pwr_regs pwr_regs_bd71828 = {
@@ -67,12 +70,14 @@ static const struct pwr_regs pwr_regs_bd71828 = {
.chg_state = BD71828_REG_CHG_STATE,
.bat_temp = BD71828_REG_BAT_TEMP,
.dcin_stat = BD71828_REG_DCIN_STAT,
+ .dcin_online_mask = BD7182x_MASK_DCIN_DET,
.dcin_collapse_limit = BD71828_REG_DCIN_CLPS,
.chg_set1 = BD71828_REG_CHG_SET1,
.chg_en = BD71828_REG_CHG_EN,
.vbat_alm_limit_u = BD71828_REG_ALM_VBAT_LIMIT_U,
.conf = BD71828_REG_CONF,
.vdcin = BD71828_REG_VDCIN_U,
+ .vdcin_himask = BD7182x_MASK_VDCIN_U,
};
static const struct pwr_regs pwr_regs_bd71815 = {
@@ -85,6 +90,7 @@ static const struct pwr_regs pwr_regs_bd71815 = {
.chg_state = BD71815_REG_CHG_STATE,
.bat_temp = BD71815_REG_BAT_TEMP,
.dcin_stat = BD71815_REG_DCIN_STAT,
+ .dcin_online_mask = BD7182x_MASK_DCIN_DET,
.dcin_collapse_limit = BD71815_REG_DCIN_CLPS,
.chg_set1 = BD71815_REG_CHG_SET1,
.chg_en = BD71815_REG_CHG_SET1,
@@ -92,6 +98,31 @@ static const struct pwr_regs pwr_regs_bd71815 = {
.conf = BD71815_REG_CONF,
.vdcin = BD71815_REG_VM_DCIN_U,
+ .vdcin_himask = BD7182x_MASK_VDCIN_U,
+};
+
+static struct pwr_regs pwr_regs_bd72720 = {
+ .vbat_avg = BD72720_REG_VM_SA_VBAT_U,
+ .ibat = BD72720_REG_CC_CURCD_U,
+ .ibat_avg = BD72720_REG_CC_SA_CURCD_U,
+ .btemp_vth = BD72720_REG_VM_BTMP_U,
+ /*
+ * Note, state 0x40 IMP_CHK. not documented
+ * on other variants but was still handled in
+ * existing code. No memory traces as to why.
+ */
+ .chg_state = BD72720_REG_CHG_STATE,
+ .bat_temp = BD72720_REG_CHG_BAT_TEMP_STAT,
+ .dcin_stat = BD72720_REG_INT_VBUS_SRC,
+ .dcin_online_mask = BD72720_MASK_DCIN_DET,
+ .dcin_collapse_limit = -1, /* Automatic. Setting not supported */
+ .chg_set1 = BD72720_REG_CHG_SET_1,
+ .chg_en = BD72720_REG_CHG_EN,
+ /* 15mV note in data-sheet */
+ .vbat_alm_limit_u = BD72720_REG_ALM_VBAT_TH_U,
+ .conf = BD72720_REG_CONF, /* o XSTB, only PON. Seprate slave addr */
+ .vdcin = BD72720_REG_VM_VBUS_U, /* 10 bits not 11 as with other ICs */
+ .vdcin_himask = BD72720_MASK_VDCIN_U,
};
struct bd71828_power {
@@ -298,7 +329,7 @@ static int get_chg_online(struct bd71828_power *pwr, int *chg_online)
dev_err(pwr->dev, "Failed to read DCIN status\n");
return ret;
}
- *chg_online = ((r & BD7182x_MASK_DCIN_DET) != 0);
+ *chg_online = ((r & pwr->regs->dcin_online_mask) != 0);
return 0;
}
@@ -329,8 +360,8 @@ static int bd71828_bat_inserted(struct bd71828_power *pwr)
ret = val & BD7182x_MASK_CONF_PON;
if (ret)
- regmap_update_bits(pwr->regmap, pwr->regs->conf,
- BD7182x_MASK_CONF_PON, 0);
+ if (regmap_update_bits(pwr->regmap, pwr->regs->conf, BD7182x_MASK_CONF_PON, 0))
+ dev_err(pwr->dev, "Failed to write CONF register\n");
return ret;
}
@@ -358,11 +389,13 @@ static int bd71828_init_hardware(struct bd71828_power *pwr)
int ret;
/* TODO: Collapse limit should come from device-tree ? */
- ret = regmap_write(pwr->regmap, pwr->regs->dcin_collapse_limit,
- BD7182x_DCIN_COLLAPSE_DEFAULT);
- if (ret) {
- dev_err(pwr->dev, "Failed to write DCIN collapse limit\n");
- return ret;
+ if (pwr->regs->dcin_collapse_limit != (unsigned int)-1) {
+ ret = regmap_write(pwr->regmap, pwr->regs->dcin_collapse_limit,
+ BD7182x_DCIN_COLLAPSE_DEFAULT);
+ if (ret) {
+ dev_err(pwr->dev, "Failed to write DCIN collapse limit\n");
+ return ret;
+ }
}
ret = pwr->bat_inserted(pwr);
@@ -419,7 +452,7 @@ static int bd71828_charger_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = bd7182x_read16_himask(pwr, pwr->regs->vdcin,
- BD7182x_MASK_VDCIN_U, &tmp);
+ pwr->regs->vdcin_himask, &tmp);
if (ret)
return ret;
@@ -630,6 +663,9 @@ BD_ISR_AC(dcin_ovp_det, "DCIN OVER VOLTAGE", true)
BD_ISR_DUMMY(dcin_mon_det, "DCIN voltage below threshold")
BD_ISR_DUMMY(dcin_mon_res, "DCIN voltage above threshold")
+BD_ISR_DUMMY(vbus_curr_limit, "VBUS current limited")
+BD_ISR_DUMMY(vsys_ov_res, "VSYS over-voltage cleared")
+BD_ISR_DUMMY(vsys_ov_det, "VSYS over-voltage")
BD_ISR_DUMMY(vsys_uv_res, "VSYS under-voltage cleared")
BD_ISR_DUMMY(vsys_uv_det, "VSYS under-voltage")
BD_ISR_DUMMY(vsys_low_res, "'VSYS low' cleared")
@@ -878,6 +914,51 @@ static int bd7182x_get_irqs(struct platform_device *pdev,
BDIRQ("bd71828-temp-125-over", bd71828_temp_vf125_det),
BDIRQ("bd71828-temp-125-under", bd71828_temp_vf125_res),
};
+ static const struct bd7182x_irq_res bd72720_irqs[] = {
+ BDIRQ("bd72720_int_vbus_rmv", BD_ISR_NAME(dcin_removed)),
+ BDIRQ("bd72720_int_vbus_det", bd7182x_dcin_detected),
+ BDIRQ("bd72720_int_vbus_mon_res", BD_ISR_NAME(dcin_mon_res)),
+ BDIRQ("bd72720_int_vbus_mon_det", BD_ISR_NAME(dcin_mon_det)),
+ BDIRQ("bd72720_int_vsys_mon_res", BD_ISR_NAME(vsys_mon_res)),
+ BDIRQ("bd72720_int_vsys_mon_det", BD_ISR_NAME(vsys_mon_det)),
+ BDIRQ("bd72720_int_vsys_uv_res", BD_ISR_NAME(vsys_uv_res)),
+ BDIRQ("bd72720_int_vsys_uv_det", BD_ISR_NAME(vsys_uv_det)),
+ BDIRQ("bd72720_int_vsys_lo_res", BD_ISR_NAME(vsys_low_res)),
+ BDIRQ("bd72720_int_vsys_lo_det", BD_ISR_NAME(vsys_low_det)),
+ BDIRQ("bd72720_int_vsys_ov_res", BD_ISR_NAME(vsys_ov_res)),
+ BDIRQ("bd72720_int_vsys_ov_det", BD_ISR_NAME(vsys_ov_det)),
+ BDIRQ("bd72720_int_bat_ilim", BD_ISR_NAME(vbus_curr_limit)),
+ BDIRQ("bd72720_int_chg_done", bd718x7_chg_done),
+ BDIRQ("bd72720_int_extemp_tout", BD_ISR_NAME(chg_wdg_temp)),
+ BDIRQ("bd72720_int_chg_wdt_exp", BD_ISR_NAME(chg_wdg)),
+ BDIRQ("bd72720_int_bat_mnt_out", BD_ISR_NAME(rechg_res)),
+ BDIRQ("bd72720_int_bat_mnt_in", BD_ISR_NAME(rechg_det)),
+ BDIRQ("bd72720_int_chg_trns", BD_ISR_NAME(chg_state_changed)),
+
+ BDIRQ("bd72720_int_vbat_mon_res", BD_ISR_NAME(bat_mon_res)),
+ BDIRQ("bd72720_int_vbat_mon_det", BD_ISR_NAME(bat_mon)),
+ BDIRQ("bd72720_int_vbat_sht_res", BD_ISR_NAME(bat_short_res)),
+ BDIRQ("bd72720_int_vbat_sht_det", BD_ISR_NAME(bat_short)),
+ BDIRQ("bd72720_int_vbat_lo_res", BD_ISR_NAME(bat_low_res)),
+ BDIRQ("bd72720_int_vbat_lo_det", BD_ISR_NAME(bat_low)),
+ BDIRQ("bd72720_int_vbat_ov_res", BD_ISR_NAME(bat_ov_res)),
+ BDIRQ("bd72720_int_vbat_ov_det", BD_ISR_NAME(bat_ov)),
+ BDIRQ("bd72720_int_bat_rmv", BD_ISR_NAME(bat_removed)),
+ BDIRQ("bd72720_int_bat_det", BD_ISR_NAME(bat_det)),
+ BDIRQ("bd72720_int_dbat_det", BD_ISR_NAME(bat_dead)),
+ BDIRQ("bd72720_int_bat_temp_trns", BD_ISR_NAME(temp_transit)),
+ BDIRQ("bd72720_int_lobtmp_res", BD_ISR_NAME(temp_bat_low_res)),
+ BDIRQ("bd72720_int_lobtmp_det", BD_ISR_NAME(temp_bat_low)),
+ BDIRQ("bd72720_int_ovbtmp_res", BD_ISR_NAME(temp_bat_hi_res)),
+ BDIRQ("bd72720_int_ovbtmp_det", BD_ISR_NAME(temp_bat_hi)),
+ BDIRQ("bd72720_int_ocur1_res", BD_ISR_NAME(bat_oc1_res)),
+ BDIRQ("bd72720_int_ocur1_det", BD_ISR_NAME(bat_oc1)),
+ BDIRQ("bd72720_int_ocur2_res", BD_ISR_NAME(bat_oc2_res)),
+ BDIRQ("bd72720_int_ocur2_det", BD_ISR_NAME(bat_oc2)),
+ BDIRQ("bd72720_int_ocur3_res", BD_ISR_NAME(bat_oc3_res)),
+ BDIRQ("bd72720_int_ocur3_det", BD_ISR_NAME(bat_oc3)),
+ BDIRQ("bd72720_int_cc_mon2_det", BD_ISR_NAME(bat_cc_mon)),
+ };
int num_irqs;
const struct bd7182x_irq_res *irqs;
@@ -890,6 +971,10 @@ static int bd7182x_get_irqs(struct platform_device *pdev,
irqs = &bd71815_irqs[0];
num_irqs = ARRAY_SIZE(bd71815_irqs);
break;
+ case ROHM_CHIP_TYPE_BD72720:
+ irqs = &bd72720_irqs[0];
+ num_irqs = ARRAY_SIZE(bd72720_irqs);
+ break;
default:
return -EINVAL;
}
@@ -958,21 +1043,27 @@ static int bd71828_power_probe(struct platform_device *pdev)
struct power_supply_config ac_cfg = {};
struct power_supply_config bat_cfg = {};
int ret;
- struct regmap *regmap;
-
- regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!regmap) {
- dev_err(&pdev->dev, "No parent regmap\n");
- return -EINVAL;
- }
pwr = devm_kzalloc(&pdev->dev, sizeof(*pwr), GFP_KERNEL);
if (!pwr)
return -ENOMEM;
- pwr->regmap = regmap;
- pwr->dev = &pdev->dev;
+ /*
+ * The BD72720 MFD device registers two regmaps. Power-supply driver
+ * uses the "wrap-map", which provides access to both of the I2C slave
+ * addresses used by the BD72720
+ */
pwr->chip_type = platform_get_device_id(pdev)->driver_data;
+ if (pwr->chip_type != ROHM_CHIP_TYPE_BD72720)
+ pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ else
+ pwr->regmap = dev_get_regmap(pdev->dev.parent, "wrap-map");
+ if (!pwr->regmap) {
+ dev_err(&pdev->dev, "No parent regmap\n");
+ return -EINVAL;
+ }
+
+ pwr->dev = &pdev->dev;
switch (pwr->chip_type) {
case ROHM_CHIP_TYPE_BD71828:
@@ -985,6 +1076,12 @@ static int bd71828_power_probe(struct platform_device *pdev)
pwr->get_temp = bd71815_get_temp;
pwr->regs = &pwr_regs_bd71815;
break;
+ case ROHM_CHIP_TYPE_BD72720:
+ pwr->bat_inserted = bd71828_bat_inserted;
+ pwr->regs = &pwr_regs_bd72720;
+ pwr->get_temp = bd71828_get_temp;
+ dev_dbg(pwr->dev, "Found ROHM BD72720\n");
+ break;
default:
dev_err(pwr->dev, "Unknown PMIC\n");
return -EINVAL;
@@ -1030,6 +1127,7 @@ static int bd71828_power_probe(struct platform_device *pdev)
static const struct platform_device_id bd71828_charger_id[] = {
{ "bd71815-power", ROHM_CHIP_TYPE_BD71815 },
{ "bd71828-power", ROHM_CHIP_TYPE_BD71828 },
+ { "bd72720-power", ROHM_CHIP_TYPE_BD72720 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd71828_charger_id);
--
2.51.1
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v5 14/16] power: supply: bd71828: Support wider register addresses
From: Matti Vaittinen @ 2025-11-20 8:24 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
Andreas Kemnade
In-Reply-To: <cover.1763625920.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2492 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
The BD71828 power-supply driver assumes register addresses to be 8-bit.
The new BD72720 will use stacked register maps to hide paging which is
done using secondary I2C slave address. This requires use of 9-bit
register addresses in the power-supply driver (added offset 0x100 to
the 8-bit hardware register addresses).
The cost is slightly used memory consumption as the members in the
struct pwr_regs will be changed from u8 to unsigned int, which means 3
byte increase / member / instance.
This is currently 14 members (expected to possibly be increased when
adding new variants / new functionality which may introduce new
registers, but not expected to grow much) and 2 instances (will be 3
instances when BD72720 gets added).
So, even if the number of registers grew to 50 it'd be 150 bytes /
instance. Assuming we eventually supported 5 variants, it'd be
5 * 150 bytes, which stays very reasonable considering systems we are
dealing with.
As a side note, we can reduce the "wasted space / member / instance" from
3 bytes to 1 byte, by using u16 instead of the unsigned int if needed. I
rather use unsigned int to be initially prepared for devices with 32 bit
registers if there is no need to count bytes.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v2 => :
- No changes
RFCv1 => v2:
- New patch
---
drivers/power/supply/bd71828-power.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c
index f667baedeb77..ce73c0f48397 100644
--- a/drivers/power/supply/bd71828-power.c
+++ b/drivers/power/supply/bd71828-power.c
@@ -44,19 +44,19 @@
#define VBAT_LOW_TH 0x00D4
struct pwr_regs {
- u8 vbat_avg;
- u8 ibat;
- u8 ibat_avg;
- u8 btemp_vth;
- u8 chg_state;
- u8 bat_temp;
- u8 dcin_stat;
- u8 dcin_collapse_limit;
- u8 chg_set1;
- u8 chg_en;
- u8 vbat_alm_limit_u;
- u8 conf;
- u8 vdcin;
+ unsigned int vbat_avg;
+ unsigned int ibat;
+ unsigned int ibat_avg;
+ unsigned int btemp_vth;
+ unsigned int chg_state;
+ unsigned int bat_temp;
+ unsigned int dcin_stat;
+ unsigned int dcin_collapse_limit;
+ unsigned int chg_set1;
+ unsigned int chg_en;
+ unsigned int vbat_alm_limit_u;
+ unsigned int conf;
+ unsigned int vdcin;
};
static const struct pwr_regs pwr_regs_bd71828 = {
--
2.51.1
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v5 13/16] rtc: bd70528: Support BD72720 rtc
From: Matti Vaittinen @ 2025-11-20 8:23 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
Andreas Kemnade
In-Reply-To: <cover.1763625920.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 3329 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
The BD72720 has similar RTC block as a few other ROHM PMICs.
Add support for BD72720 RTC.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
Revision history:
RFCv1 =>:
- No changes
---
drivers/rtc/Kconfig | 3 ++-
drivers/rtc/rtc-bd70528.c | 21 ++++++++++++++-------
2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2933c41c77c8..418f6c28847a 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -561,7 +561,8 @@ config RTC_DRV_BD70528
depends on MFD_ROHM_BD71828
help
If you say Y here you will get support for the RTC
- block on ROHM BD71815 and BD71828 Power Management IC.
+ block on ROHM BD71815, BD71828 and BD72720 Power
+ Management ICs.
This driver can also be built as a module. If so, the module
will be called rtc-bd70528.
diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
index 954ac4ef53e8..4c8599761b2e 100644
--- a/drivers/rtc/rtc-bd70528.c
+++ b/drivers/rtc/rtc-bd70528.c
@@ -7,6 +7,7 @@
#include <linux/bcd.h>
#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-bd72720.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -262,13 +263,13 @@ static int bd70528_probe(struct platform_device *pdev)
/*
* See also BD718XX_ALM_EN_OFFSET:
- * This works for BD71828 and BD71815 as they have same offset
- * between ALM0 start and ALM0_MASK. If new ICs are to be
- * added this requires proper check as ALM0_MASK is not located
- * at the end of ALM0 block - but after all ALM blocks so if
- * amount of ALMs differ the offset to enable/disable is likely
- * to be incorrect and enable/disable must be given as own
- * reg address here.
+ * This works for BD71828, BD71815, and BD72720 as they all
+ * have same offset between the ALM0 start and the ALM0_MASK.
+ * If new ICs are to be added this requires proper check as
+ * the ALM0_MASK is not located at the end of ALM0 block -
+ * but after all ALM blocks. If amount of ALMs differ, the
+ * offset to enable/disable is likely to be incorrect and
+ * enable/disable must be given as own reg address here.
*/
bd_rtc->bd718xx_alm_block_start = BD71815_REG_RTC_ALM_START;
hour_reg = BD71815_REG_HOUR;
@@ -278,6 +279,11 @@ static int bd70528_probe(struct platform_device *pdev)
bd_rtc->bd718xx_alm_block_start = BD71828_REG_RTC_ALM_START;
hour_reg = BD71828_REG_RTC_HOUR;
break;
+ case ROHM_CHIP_TYPE_BD72720:
+ bd_rtc->reg_time_start = BD72720_REG_RTC_START;
+ bd_rtc->bd718xx_alm_block_start = BD72720_REG_RTC_ALM_START;
+ hour_reg = BD72720_REG_RTC_HOUR;
+ break;
default:
dev_err(&pdev->dev, "Unknown chip\n");
return -ENOENT;
@@ -337,6 +343,7 @@ static int bd70528_probe(struct platform_device *pdev)
static const struct platform_device_id bd718x7_rtc_id[] = {
{ "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 },
{ "bd71815-rtc", ROHM_CHIP_TYPE_BD71815 },
+ { "bd72720-rtc", ROHM_CHIP_TYPE_BD72720 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id);
--
2.51.1
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v5 12/16] clk: clk-bd718x7: Support BD72720 clk gate
From: Matti Vaittinen @ 2025-11-20 8:23 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
Andreas Kemnade
In-Reply-To: <cover.1763625920.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2578 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
The BD72720 has similar simple clk gate as a few other ROHM PMICs.
Add support for BD72720 clk gate.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Acked-by: Stephen Boyd <sboyd@kernel.org>
---
Revision history:
RFCv1 =>:
- No changes
---
drivers/clk/Kconfig | 4 ++--
drivers/clk/clk-bd718x7.c | 10 ++++++++--
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3a1611008e48..619bd63a3c77 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -475,8 +475,8 @@ config COMMON_CLK_BD718XX
tristate "Clock driver for 32K clk gates on ROHM PMICs"
depends on MFD_ROHM_BD718XX || MFD_ROHM_BD71828
help
- This driver supports ROHM BD71837, BD71847, BD71850, BD71815
- and BD71828 PMICs clock gates.
+ This driver supports ROHM BD71837, BD71847, BD71850, BD71815,
+ BD71828, and BD72720 PMICs clock gates.
config COMMON_CLK_FIXED_MMIO
bool "Clock driver for Memory Mapped Fixed values"
diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c
index ac40b669d60b..1cae974e6d1d 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -19,7 +19,8 @@
#define BD71828_REG_OUT32K 0x4B
/* BD71837 and BD71847 */
#define BD718XX_REG_OUT32K 0x2E
-
+/* BD72720 */
+#define BD72720_REG_OUT32K 0x9a
/*
* BD71837, BD71847, and BD71828 all use bit [0] to clk output control
*/
@@ -118,6 +119,10 @@ static int bd71837_clk_probe(struct platform_device *pdev)
c->reg = BD71815_REG_OUT32K;
c->mask = CLK_OUT_EN_MASK;
break;
+ case ROHM_CHIP_TYPE_BD72720:
+ c->reg = BD72720_REG_OUT32K;
+ c->mask = CLK_OUT_EN_MASK;
+ break;
default:
dev_err(&pdev->dev, "Unknown clk chip\n");
return -EINVAL;
@@ -146,6 +151,7 @@ static const struct platform_device_id bd718x7_clk_id[] = {
{ "bd71847-clk", ROHM_CHIP_TYPE_BD71847 },
{ "bd71828-clk", ROHM_CHIP_TYPE_BD71828 },
{ "bd71815-clk", ROHM_CHIP_TYPE_BD71815 },
+ { "bd72720-clk", ROHM_CHIP_TYPE_BD72720 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
@@ -161,6 +167,6 @@ static struct platform_driver bd71837_clk = {
module_platform_driver(bd71837_clk);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
-MODULE_DESCRIPTION("BD718(15/18/28/37/47/50) and chip clk driver");
+MODULE_DESCRIPTION("BD718(15/18/28/37/47/50) and BD72720 chip clk driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bd718xx-clk");
--
2.51.1
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v5 11/16] gpio: Support ROHM BD72720 gpios
From: Matti Vaittinen @ 2025-11-20 8:23 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
Andreas Kemnade
In-Reply-To: <cover.1763625920.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 10901 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
The ROHM BD72720 has 6 pins which may be configured as GPIOs. The
GPIO1 ... GPIO5 and EPDEN pins. The configuration is done to OTP at the
manufacturing, and it can't be read at runtime. The device-tree is
required to tell the software which of the pins are used as GPIOs.
Keep the pin mapping static regardless the OTP. This way the user-space
can always access the BASE+N for GPIO(N+1) (N = 0 to 4), and BASE + 5
for the EPDEN pin. Do this by setting always the number of GPIOs to 6,
and by using the valid-mask to invalidate the pins which aren't configured
as GPIOs.
First two pins can be set to be either input or output by OTP. Direction
can't be changed by software. Rest of the pins can be set as outputs
only. All of the pins support generating interrupts.
Support the Input/Output state getting/setting and the output mode
configuration (open-drain/push-pull).
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
Revision history:
RFCv1 => :
- No changes
---
drivers/gpio/Kconfig | 9 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-bd72720.c | 281 ++++++++++++++++++++++++++++++++++++
3 files changed, 291 insertions(+)
create mode 100644 drivers/gpio/gpio-bd72720.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7ee3afbc2b05..0c612c5163c5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1319,6 +1319,15 @@ config GPIO_BD71828
This driver can also be built as a module. If so, the module
will be called gpio-bd71828.
+config GPIO_BD72720
+ tristate "ROHM BD72720 and BD73900 PMIC GPIO support"
+ depends on MFD_ROHM_BD71828
+ help
+ Support for GPIO on ROHM BD72720 and BD73900 PMICs. There are two
+ pins which can be configured to GPI or GPO, and three pins which can
+ be configured to GPO on the ROHM PMIC. The pin configuration is done
+ on OTP at manufacturing.
+
config GPIO_BD9571MWV
tristate "ROHM BD9571 GPIO support"
depends on MFD_BD9571MWV
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ec296fa14bfd..7a5d03db3021 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o
obj-$(CONFIG_GPIO_BD71815) += gpio-bd71815.o
obj-$(CONFIG_GPIO_BD71828) += gpio-bd71828.o
+obj-$(CONFIG_GPIO_BD72720) += gpio-bd72720.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BLZP1600) += gpio-blzp1600.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c
new file mode 100644
index 000000000000..6549dbf4c7ad
--- /dev/null
+++ b/drivers/gpio/gpio-bd72720.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support to GPIOs on ROHM BD72720 and BD79300
+ * Copyright 2025 ROHM Semiconductors.
+ * Author: Matti Vaittinen <mazziesaccount@gmail.com>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/rohm-bd72720.h>
+
+#define BD72720_GPIO_OPEN_DRAIN 0
+#define BD72720_GPIO_CMOS BIT(1)
+#define BD72720_INT_GPIO1_IN_SRC 4
+/*
+ * The BD72720 has several "one time programmable" (OTP) configurations which
+ * can be set at manufacturing phase. A set of these options allow using pins
+ * as GPIO. The OTP configuration can't be read at run-time, so drivers rely on
+ * device-tree to advertise the correct options.
+ *
+ * Both DVS[0,1] pins can be configured to be used for:
+ * - OTP0: regulator RUN state control
+ * - OTP1: GPI
+ * - OTP2: GPO
+ * - OTP3: Power sequencer output
+ * Data-sheet also states that these PINs can always be used for IRQ but the
+ * driver limits this by allowing them to be used for IRQs with OTP1 only.
+ *
+ * Pins GPIO_EXTEN0 (GPIO3), GPIO_EXTEN1 (GPIO4), GPIO_FAULT_B (GPIO5) have OTP
+ * options for a specific (non GPIO) purposes, but also an option to configure
+ * them to be used as a GPO.
+ *
+ * OTP settings can be separately configured for each pin.
+ *
+ * DT properties:
+ * "rohm,pin-dvs0" and "rohm,pin-dvs1" can be set to one of the values:
+ * "dvs-input", "gpi", "gpo".
+ *
+ * "rohm,pin-exten0", "rohm,pin-exten1" and "rohm,pin-fault_b" can be set to:
+ * "gpo"
+ */
+
+enum bd72720_gpio_state {
+ BD72720_PIN_UNKNOWN,
+ BD72720_PIN_GPI,
+ BD72720_PIN_GPO,
+};
+
+enum {
+ BD72720_GPIO1,
+ BD72720_GPIO2,
+ BD72720_GPIO3,
+ BD72720_GPIO4,
+ BD72720_GPIO5,
+ BD72720_GPIO_EPDEN,
+ BD72720_NUM_GPIOS
+};
+
+struct bd72720_gpio {
+ /* chip.parent points the MFD which provides DT node and regmap */
+ struct gpio_chip chip;
+ /* dev points to the platform device for devm and prints */
+ struct device *dev;
+ struct regmap *regmap;
+ int gpio_is_input;
+};
+
+static int bd72720gpi_get(struct bd72720_gpio *bdgpio, unsigned int reg_offset)
+{
+ int ret, val, shift;
+
+ ret = regmap_read(bdgpio->regmap, BD72720_REG_INT_ETC1_SRC, &val);
+ if (ret)
+ return ret;
+
+ shift = BD72720_INT_GPIO1_IN_SRC + reg_offset;
+
+ return (val >> shift) & 1;
+}
+
+static int bd72720gpo_get(struct bd72720_gpio *bdgpio,
+ unsigned int offset)
+{
+ const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+ BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+ BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+ int ret, val;
+
+ ret = regmap_read(bdgpio->regmap, regs[offset], &val);
+ if (ret)
+ return ret;
+
+ return val & BD72720_GPIO_HIGH;
+}
+
+static int bd72720gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+
+ if (BIT(offset) & bdgpio->gpio_is_input)
+ return bd72720gpi_get(bdgpio, offset);
+
+ return bd72720gpo_get(bdgpio, offset);
+}
+
+static int bd72720gpo_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+ const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+ BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+ BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+
+ if (BIT(offset) & bdgpio->gpio_is_input) {
+ dev_dbg(bdgpio->dev, "pin %d not output.\n", offset);
+ return -EINVAL;
+ }
+
+ if (value)
+ return regmap_set_bits(bdgpio->regmap, regs[offset],
+ BD72720_GPIO_HIGH);
+
+ return regmap_clear_bits(bdgpio->regmap, regs[offset],
+ BD72720_GPIO_HIGH);
+}
+
+static int bd72720_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+ const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+ BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+ BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+
+ /*
+ * We can only set the output mode, which makes sense only when output
+ * OTP configuration is used.
+ */
+ if (BIT(offset) & bdgpio->gpio_is_input)
+ return -ENOTSUPP;
+
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ return regmap_update_bits(bdgpio->regmap,
+ regs[offset],
+ BD72720_GPIO_DRIVE_MASK,
+ BD72720_GPIO_OPEN_DRAIN);
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ return regmap_update_bits(bdgpio->regmap,
+ regs[offset],
+ BD72720_GPIO_DRIVE_MASK,
+ BD72720_GPIO_CMOS);
+ default:
+ break;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int bd72720gpo_direction_get(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+
+ if (BIT(offset) & bdgpio->gpio_is_input)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int bd72720_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ static const char * const properties[] = {
+ "rohm,pin-dvs0", "rohm,pin-dvs1", "rohm,pin-exten0",
+ "rohm,pin-exten1", "rohm,pin-fault_b"
+ };
+ struct bd72720_gpio *g = gpiochip_get_data(gc);
+ const char *val;
+ int i, ret;
+
+ *valid_mask = BIT(BD72720_GPIO_EPDEN);
+
+ if (!gc->parent)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(properties); i++) {
+ ret = fwnode_property_read_string(dev_fwnode(gc->parent),
+ properties[i], &val);
+
+ if (ret) {
+ if (ret == -EINVAL)
+ continue;
+
+ dev_err(g->dev, "pin %d (%s), bad configuration\n", i,
+ properties[i]);
+
+ return ret;
+ }
+
+ if (strcmp(val, "gpi") == 0) {
+ if (i != BD72720_GPIO1 && i != BD72720_GPIO2) {
+ dev_warn(g->dev,
+ "pin %d (%s) does not support INPUT mode",
+ i, properties[i]);
+ continue;
+ }
+
+ *valid_mask |= BIT(i);
+ g->gpio_is_input |= BIT(i);
+ } else if (strcmp(val, "gpo") == 0) {
+ *valid_mask |= BIT(i);
+ }
+ }
+
+ return 0;
+}
+
+/* Template for GPIO chip */
+static const struct gpio_chip bd72720gpo_chip = {
+ .label = "bd72720",
+ .owner = THIS_MODULE,
+ .get = bd72720gpio_get,
+ .get_direction = bd72720gpo_direction_get,
+ .set = bd72720gpo_set,
+ .set_config = bd72720_gpio_set_config,
+ .init_valid_mask = bd72720_valid_mask,
+ .can_sleep = true,
+ .ngpio = BD72720_NUM_GPIOS,
+ .base = -1,
+};
+
+static int gpo_bd72720_probe(struct platform_device *pdev)
+{
+ struct bd72720_gpio *g;
+ struct device *parent, *dev;
+
+ /*
+ * Bind devm lifetime to this platform device => use dev for devm.
+ * also the prints should originate from this device.
+ */
+ dev = &pdev->dev;
+ /* The device-tree and regmap come from MFD => use parent for that */
+ parent = dev->parent;
+
+ g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
+ if (!g)
+ return -ENOMEM;
+
+ g->chip = bd72720gpo_chip;
+ g->dev = dev;
+ g->chip.parent = parent;
+ g->regmap = dev_get_regmap(parent, NULL);
+
+ return devm_gpiochip_add_data(dev, &g->chip, g);
+}
+
+static const struct platform_device_id bd72720_gpio_id[] = {
+ { "bd72720-gpio" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd72720_gpio_id);
+
+static struct platform_driver gpo_bd72720_driver = {
+ .driver = {
+ .name = "bd72720-gpio",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = gpo_bd72720_probe,
+ .id_table = bd72720_gpio_id,
+};
+module_platform_driver(gpo_bd72720_driver);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("GPIO interface for BD72720 and BD73900");
+MODULE_LICENSE("GPL");
--
2.51.1
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox