From: "Peng Fan (OSS)" <peng.fan@oss.nxp.com>
To: Mark Brown <broonie@kernel.org>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
"Rafael J. Wysocki" <rafael@kernel.org>,
Danilo Krummrich <dakr@kernel.org>,
Johan Hovold <johan@kernel.org>,
Lucas Tanure <tanureal@opensource.cirrus.com>
Cc: linux-kernel@vger.kernel.org, driver-core@lists.linux.dev,
Peng Fan <peng.fan@nxp.com>
Subject: [PATCH 7/9] regmap: drop regmap_bus::free_on_exit
Date: Tue, 10 Nov 2026 16:25:31 +0800 [thread overview]
Message-ID: <20261110-regmap-improve-v1-7-3a76b6c10ec6@nxp.com> (raw)
In-Reply-To: <20261110-regmap-improve-v1-0-3a76b6c10ec6@nxp.com>
From: Peng Fan <peng.fan@nxp.com>
The SPI and I2C bus helpers kmemdup the static regmap_bus to override
max_raw_read and max_raw_write per device and mark the copy with
free_on_exit so that the regmap core frees it. This is the only user of
free_on_exit, and the per-device limits can instead be applied to the
map while it is being initialized.
Add an optional regmap_bus::init_limits() callback which is invoked by
__regmap_init() after the bus raw I/O limits have been copied and before
the cache is initialized. The latter matters for maps with raw register
defaults: regcache_hw_init() reads them from the device while the map is
still being initialized and uses max_raw_read to chunk the transfer.
Use the callback for the I2C and SPI buses, which allows using the static
bus structures directly. This removes the per-device bus allocations and
lets us drop free_on_exit along with the associated error and exit paths
in the regmap core.
No functional changes.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/base/regmap/regmap-i2c.c | 71 +++++++++++++++++++++-------------------
drivers/base/regmap/regmap-spi.c | 64 ++++++++++++++----------------------
drivers/base/regmap/regmap.c | 7 ++--
include/linux/regmap.h | 7 ++--
4 files changed, 70 insertions(+), 79 deletions(-)
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index eb3db02ad8fb..247dd20eb02a 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -12,6 +12,36 @@
#include "internal.h"
+static void regmap_i2c_init_limits(struct regmap *map,
+ const struct regmap_config *config)
+{
+ struct device *dev = map->bus_context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ const struct i2c_adapter_quirks *quirks = i2c->adapter->quirks;
+ size_t max_read = map->max_raw_read;
+ size_t max_write = map->max_raw_write;
+
+ if (!quirks)
+ return;
+
+ if (quirks->max_read_len &&
+ (max_read == 0 || max_read > quirks->max_read_len))
+ max_read = quirks->max_read_len;
+
+ if (quirks->max_write_len &&
+ (max_write == 0 || max_write > quirks->max_write_len)) {
+ size_t reg_reserve_size;
+
+ reg_reserve_size = (config->reg_bits + config->pad_bits) /
+ BITS_PER_BYTE;
+ if (quirks->max_write_len > reg_reserve_size)
+ max_write = quirks->max_write_len - reg_reserve_size;
+ }
+
+ map->max_raw_read = max_read;
+ map->max_raw_write = max_write;
+}
+
static int regmap_smbus_byte_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
@@ -46,6 +76,7 @@ static int regmap_smbus_byte_reg_write(void *context, unsigned int reg,
static const struct regmap_bus regmap_smbus_byte = {
.reg_write = regmap_smbus_byte_reg_write,
.reg_read = regmap_smbus_byte_reg_read,
+ .init_limits = regmap_i2c_init_limits,
};
static int regmap_smbus_word_reg_read(void *context, unsigned int reg,
@@ -82,6 +113,7 @@ static int regmap_smbus_word_reg_write(void *context, unsigned int reg,
static const struct regmap_bus regmap_smbus_word = {
.reg_write = regmap_smbus_word_reg_write,
.reg_read = regmap_smbus_word_reg_read,
+ .init_limits = regmap_i2c_init_limits,
};
static int regmap_smbus_word_read_swapped(void *context, unsigned int reg,
@@ -118,6 +150,7 @@ static int regmap_smbus_word_write_swapped(void *context, unsigned int reg,
static const struct regmap_bus regmap_smbus_word_swapped = {
.reg_write = regmap_smbus_word_write_swapped,
.reg_read = regmap_smbus_word_read_swapped,
+ .init_limits = regmap_i2c_init_limits,
};
static int regmap_i2c_write(void *context, const void *data, size_t count)
@@ -203,6 +236,7 @@ static const struct regmap_bus regmap_i2c = {
.read = regmap_i2c_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
+ .init_limits = regmap_i2c_init_limits,
};
static int regmap_i2c_smbus_i2c_write(void *context, const void *data,
@@ -244,6 +278,7 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block = {
.read = regmap_i2c_smbus_i2c_read,
.max_raw_read = I2C_SMBUS_BLOCK_MAX - 1,
.max_raw_write = I2C_SMBUS_BLOCK_MAX - 1,
+ .init_limits = regmap_i2c_init_limits,
};
static int regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data,
@@ -301,6 +336,7 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
.read = regmap_i2c_smbus_i2c_read_reg16,
.max_raw_read = I2C_SMBUS_BLOCK_MAX - 2,
.max_raw_write = I2C_SMBUS_BLOCK_MAX - 2,
+ .init_limits = regmap_i2c_init_limits,
};
/*
@@ -345,15 +381,13 @@ static const struct regmap_bus regmap_smbus_byte_word_reg16 = {
.read = regmap_i2c_smbus_i2c_read_reg16,
.max_raw_read = I2C_SMBUS_BLOCK_MAX - 2,
.max_raw_write = 1,
+ .init_limits = regmap_i2c_init_limits,
};
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
const struct regmap_config *config)
{
- const struct i2c_adapter_quirks *quirks;
const struct regmap_bus *bus = NULL;
- struct regmap_bus *ret_bus;
- u16 max_read, max_write;
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
bus = ®map_i2c;
@@ -391,37 +425,6 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
if (!bus)
return ERR_PTR(-ENOTSUPP);
- max_read = bus->max_raw_read;
- max_write = bus->max_raw_write;
-
- quirks = i2c->adapter->quirks;
- if (quirks) {
- if (quirks->max_read_len &&
- (max_read == 0 || max_read > quirks->max_read_len))
- max_read = quirks->max_read_len;
-
- if (quirks->max_write_len &&
- (max_write == 0 || max_write > quirks->max_write_len)) {
- size_t reg_reserve_size;
-
- reg_reserve_size = (config->reg_bits + config->pad_bits) /
- BITS_PER_BYTE;
- if (quirks->max_write_len > reg_reserve_size)
- max_write = quirks->max_write_len - reg_reserve_size;
- }
-
- if (max_read != bus->max_raw_read ||
- max_write != bus->max_raw_write) {
- ret_bus = kmemdup(bus, sizeof(*bus), GFP_KERNEL);
- if (!ret_bus)
- return ERR_PTR(-ENOMEM);
- ret_bus->free_on_exit = true;
- ret_bus->max_raw_read = max_read;
- ret_bus->max_raw_write = max_write;
- bus = ret_bus;
- }
- }
-
return bus;
}
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
index b9fec387997e..8eaebef07833 100644
--- a/drivers/base/regmap/regmap-spi.c
+++ b/drivers/base/regmap/regmap-spi.c
@@ -98,6 +98,26 @@ static int regmap_spi_read(void *context,
return spi_write_then_read(spi, reg, reg_size, val, val_size);
}
+static void regmap_spi_init_limits(struct regmap *map,
+ const struct regmap_config *config)
+{
+ struct device *dev = map->bus_context;
+ struct spi_device *spi = to_spi_device(dev);
+ size_t max_size = spi_max_transfer_size(spi);
+ size_t max_msg_size, reg_reserve_size;
+
+ if (max_size == SIZE_MAX)
+ return;
+
+ max_msg_size = spi_max_message_size(spi);
+ reg_reserve_size = (config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
+ if (max_size + reg_reserve_size > max_msg_size)
+ max_size -= reg_reserve_size;
+
+ map->max_raw_read = max_size;
+ map->max_raw_write = max_size;
+}
+
static const struct regmap_bus regmap_spi = {
.write = regmap_spi_write,
.gather_write = regmap_spi_gather_write,
@@ -107,46 +127,16 @@ static const struct regmap_bus regmap_spi = {
.read_flag_mask = 0x80,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
+ .init_limits = regmap_spi_init_limits,
};
-static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi,
- const struct regmap_config *config)
-{
- size_t max_size = spi_max_transfer_size(spi);
- size_t max_msg_size, reg_reserve_size;
- struct regmap_bus *bus;
-
- if (max_size != SIZE_MAX) {
- bus = kmemdup(®map_spi, sizeof(*bus), GFP_KERNEL);
- if (!bus)
- return ERR_PTR(-ENOMEM);
-
- max_msg_size = spi_max_message_size(spi);
- reg_reserve_size = (config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
- if (max_size + reg_reserve_size > max_msg_size)
- max_size -= reg_reserve_size;
-
- bus->free_on_exit = true;
- bus->max_raw_read = max_size;
- bus->max_raw_write = max_size;
-
- return bus;
- }
-
- return ®map_spi;
-}
-
struct regmap *__regmap_init_spi(struct spi_device *spi,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
- const struct regmap_bus *bus = regmap_get_spi_bus(spi, config);
-
- if (IS_ERR(bus))
- return ERR_CAST(bus);
-
- return __regmap_init(&spi->dev, bus, &spi->dev, config, lock_key, lock_name);
+ return __regmap_init(&spi->dev, ®map_spi, &spi->dev, config,
+ lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_spi);
@@ -155,12 +145,8 @@ struct regmap *__devm_regmap_init_spi(struct spi_device *spi,
struct lock_class_key *lock_key,
const char *lock_name)
{
- const struct regmap_bus *bus = regmap_get_spi_bus(spi, config);
-
- if (IS_ERR(bus))
- return ERR_CAST(bus);
-
- return __devm_regmap_init(&spi->dev, bus, &spi->dev, config, lock_key, lock_name);
+ return __devm_regmap_init(&spi->dev, ®map_spi, &spi->dev, config,
+ lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spi);
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 0a7f73fc1101..c5acfe687d9c 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1179,6 +1179,9 @@ struct regmap *__regmap_init(struct device *dev,
if (ret)
goto err_range;
+ if (bus && bus->init_limits)
+ bus->init_limits(map, config);
+
ret = regcache_init(map, config);
if (ret != 0)
goto err_range;
@@ -1206,8 +1209,6 @@ struct regmap *__regmap_init(struct device *dev,
err_map:
kfree(map);
err:
- if (bus && bus->free_on_exit)
- kfree(bus);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(__regmap_init);
@@ -1513,8 +1514,6 @@ void regmap_exit(struct regmap *map)
mutex_destroy(&map->mutex);
kfree_const(map->name);
kfree(map->patch);
- if (map->bus && map->bus->free_on_exit)
- kfree(map->bus);
kfree(map);
}
EXPORT_SYMBOL_GPL(regmap_exit);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index a72f17ab6264..161457726163 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -579,7 +579,6 @@ typedef void (*regmap_hw_free_context)(void *context);
* to perform locking. This field is ignored if custom lock/unlock
* functions are used (see fields lock/unlock of
* struct regmap_config).
- * @free_on_exit: kfree this on exit of regmap
* @write: Write operation.
* @gather_write: Write operation with split register/value, return -ENOTSUPP
* if not implemented on a given device.
@@ -610,10 +609,12 @@ typedef void (*regmap_hw_free_context)(void *context);
* DEFAULT, BIG is assumed.
* @max_raw_read: Max raw read size that can be used on the bus.
* @max_raw_write: Max raw write size that can be used on the bus.
+ * @init_limits: Optional callback to adjust the maximum raw read and write
+ * sizes for a device. Called during regmap initialization before the
+ * cache is initialized.
*/
struct regmap_bus {
bool fast_io;
- bool free_on_exit;
regmap_hw_write write;
regmap_hw_gather_write gather_write;
regmap_hw_async_write async_write;
@@ -630,6 +631,8 @@ struct regmap_bus {
enum regmap_endian val_format_endian_default;
size_t max_raw_read;
size_t max_raw_write;
+ void (*init_limits)(struct regmap *map,
+ const struct regmap_config *config);
};
/*
--
2.51.0
next prev parent reply other threads:[~2026-11-10 8:29 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 8:25 [PATCH 0/9] regmap: clean up and improve __regmap_init() Peng Fan (OSS)
2026-11-10 8:25 ` [PATCH 1/9] regmap-i2c: keep bus raw write limit when applying read quirk Peng Fan (OSS)
2026-11-10 8:25 ` [PATCH 2/9] regmap-i2c: avoid underflow when applying write length quirk Peng Fan (OSS)
2026-11-10 8:25 ` [PATCH 3/9] regmap: deduplicate fast_io checks Peng Fan (OSS)
2026-11-10 8:25 ` [PATCH 4/9] regmap: factor out regmap_set_lock() Peng Fan (OSS)
2026-11-10 8:25 ` [PATCH 5/9] regmap: factor out regmap_init_ranges() Peng Fan (OSS)
2026-11-10 8:25 ` [PATCH 6/9] regmap: factor out format initialisation into helpers Peng Fan (OSS)
2026-11-10 8:25 ` Peng Fan (OSS) [this message]
2026-11-10 8:25 ` [PATCH 8/9] regmap: use cleanup API in __regmap_init() Peng Fan (OSS)
2026-11-10 8:25 ` [PATCH 9/9] regmap: kunit: add test for regmap_bus::init_limits Peng Fan (OSS)
2026-11-10 11:47 ` [PATCH 0/9] regmap: clean up and improve __regmap_init() Peng Fan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261110-regmap-improve-v1-7-3a76b6c10ec6@nxp.com \
--to=peng.fan@oss.nxp.com \
--cc=broonie@kernel.org \
--cc=dakr@kernel.org \
--cc=driver-core@lists.linux.dev \
--cc=gregkh@linuxfoundation.org \
--cc=johan@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=peng.fan@nxp.com \
--cc=rafael@kernel.org \
--cc=tanureal@opensource.cirrus.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®