regmap: Updates for v5.8
This has been a very active release for the regmap API for some reason, a lot of it due to new devices with odd requirements that can sensibly be handled here. - Add support for buses implementing a custom reg_update_bits() method in case the bus has a native operation for this. - Support 16 bit register addresses in SMBus. - Allow customization of the device attached to regmap-irq. - Helpers for bitfield operations and per-port field initializations. -----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAl7U4SkTHGJyb29uaWVA a2VybmVsLm9yZwAKCRAk1otyXVSH0EmYB/4jBeaFTowIhas+dXx/+P9Yooe0/3h9 wihte3ZcHIf0ox02nPEd3W/Uf1KpVd5gmHDHJOI0aPCQLk+s4+V9JaaPN4LwIJwL ttl3RP9Bco50BgjMYj3gNuoIya9tjI7bxvblii76IgFb6PjneQURQHc2Rqx72cj5 PVDLKRfkYLJWGdYzRoImKHLAeH6hQTm3juIajq7EaqdA5lXtad4SFp86k0iOhd4l L16Bcgvs0alt3Lq4Sm49gU2mAxfIh88CpoxGIOkTr+W0L9EP5HeB0hCr67BsRXcq DFe0XBgjFJj+hxD4ojBJuvaAm54jcSOiMSKGRhyPsZsSXmzYmylkGYvM =7YCr -----END PGP SIGNATURE----- Merge tag 'regmap-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap Pull regmap updates from Mark Brown: "This has been a very active release for the regmap API for some reason, a lot of it due to new devices with odd requirements that can sensibly be handled here. - Add support for buses implementing a custom reg_update_bits() method in case the bus has a native operation for this. - Support 16 bit register addresses in SMBus. - Allow customization of the device attached to regmap-irq. - Helpers for bitfield operations and per-port field initializations" * tag 'regmap-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: regmap: provide helpers for simple bit operations regmap: add helper for per-port regfield initialization regmap-i2c: add 16-bit width registers support regmap: Simplify implementation of the regmap_field_read_poll_timeout() macro regmap: Simplify implementation of the regmap_read_poll_timeout() macro regmap: add reg_sequence helpers regmap-irq: make it possible to add irq_chip do a specific device node regmap: Add bus reg_update_bits() support regmap: debugfs: check count when read regmap file
This commit is contained in:
commit
213fd09e1a
@ -227,6 +227,9 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
|
||||
if (*ppos < 0 || !count)
|
||||
return -EINVAL;
|
||||
|
||||
if (count > (PAGE_SIZE << (MAX_ORDER - 1)))
|
||||
count = PAGE_SIZE << (MAX_ORDER - 1);
|
||||
|
||||
buf = kmalloc(count, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
@ -371,6 +374,9 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
|
||||
if (*ppos < 0 || !count)
|
||||
return -EINVAL;
|
||||
|
||||
if (count > (PAGE_SIZE << (MAX_ORDER - 1)))
|
||||
count = PAGE_SIZE << (MAX_ORDER - 1);
|
||||
|
||||
buf = kmalloc(count, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
@ -246,6 +246,63 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block = {
|
||||
.max_raw_write = I2C_SMBUS_BLOCK_MAX,
|
||||
};
|
||||
|
||||
static int regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data,
|
||||
size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
|
||||
if (count < 2)
|
||||
return -EINVAL;
|
||||
|
||||
count--;
|
||||
return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
|
||||
(u8 *)data + 1);
|
||||
}
|
||||
|
||||
static int regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg,
|
||||
size_t reg_size, void *val,
|
||||
size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
int ret, count, len = val_size;
|
||||
|
||||
if (reg_size != 2)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(i2c, ((u16 *)reg)[0] & 0xff,
|
||||
((u16 *)reg)[0] >> 8);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
count = 0;
|
||||
do {
|
||||
/* Current Address Read */
|
||||
ret = i2c_smbus_read_byte(i2c);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
*((u8 *)val++) = ret;
|
||||
count++;
|
||||
len--;
|
||||
} while (len > 0);
|
||||
|
||||
if (count == val_size)
|
||||
return 0;
|
||||
else if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
|
||||
.write = regmap_i2c_smbus_i2c_write_reg16,
|
||||
.read = regmap_i2c_smbus_i2c_read_reg16,
|
||||
.max_raw_read = I2C_SMBUS_BLOCK_MAX,
|
||||
.max_raw_write = I2C_SMBUS_BLOCK_MAX,
|
||||
};
|
||||
|
||||
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
@ -255,6 +312,10 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
|
||||
i2c_check_functionality(i2c->adapter,
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return ®map_i2c_smbus_i2c_block;
|
||||
else if (config->val_bits == 8 && config->reg_bits == 16 &&
|
||||
i2c_check_functionality(i2c->adapter,
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return ®map_i2c_smbus_i2c_block_reg16;
|
||||
else if (config->val_bits == 16 && config->reg_bits == 8 &&
|
||||
i2c_check_functionality(i2c->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
|
@ -541,8 +541,9 @@ static const struct irq_domain_ops regmap_domain_ops = {
|
||||
};
|
||||
|
||||
/**
|
||||
* regmap_add_irq_chip() - Use standard regmap IRQ controller handling
|
||||
* regmap_add_irq_chip_np() - Use standard regmap IRQ controller handling
|
||||
*
|
||||
* @np: The device_node where the IRQ domain should be added to.
|
||||
* @map: The regmap for the device.
|
||||
* @irq: The IRQ the device uses to signal interrupts.
|
||||
* @irq_flags: The IRQF_ flags to use for the primary interrupt.
|
||||
@ -556,9 +557,10 @@ static const struct irq_domain_ops regmap_domain_ops = {
|
||||
* register cache. The chip driver is responsible for restoring the
|
||||
* register values used by the IRQ controller over suspend and resume.
|
||||
*/
|
||||
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
int irq_base, const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data)
|
||||
int regmap_add_irq_chip_np(struct device_node *np, struct regmap *map, int irq,
|
||||
int irq_flags, int irq_base,
|
||||
const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data)
|
||||
{
|
||||
struct regmap_irq_chip_data *d;
|
||||
int i;
|
||||
@ -769,12 +771,10 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
}
|
||||
|
||||
if (irq_base)
|
||||
d->domain = irq_domain_add_legacy(map->dev->of_node,
|
||||
chip->num_irqs, irq_base, 0,
|
||||
®map_domain_ops, d);
|
||||
d->domain = irq_domain_add_legacy(np, chip->num_irqs, irq_base,
|
||||
0, ®map_domain_ops, d);
|
||||
else
|
||||
d->domain = irq_domain_add_linear(map->dev->of_node,
|
||||
chip->num_irqs,
|
||||
d->domain = irq_domain_add_linear(np, chip->num_irqs,
|
||||
®map_domain_ops, d);
|
||||
if (!d->domain) {
|
||||
dev_err(map->dev, "Failed to create IRQ domain\n");
|
||||
@ -808,6 +808,30 @@ err_alloc:
|
||||
kfree(d);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_add_irq_chip_np);
|
||||
|
||||
/**
|
||||
* regmap_add_irq_chip() - Use standard regmap IRQ controller handling
|
||||
*
|
||||
* @map: The regmap for the device.
|
||||
* @irq: The IRQ the device uses to signal interrupts.
|
||||
* @irq_flags: The IRQF_ flags to use for the primary interrupt.
|
||||
* @irq_base: Allocate at specific IRQ number if irq_base > 0.
|
||||
* @chip: Configuration for the interrupt controller.
|
||||
* @data: Runtime data structure for the controller, allocated on success.
|
||||
*
|
||||
* Returns 0 on success or an errno on failure.
|
||||
*
|
||||
* This is the same as regmap_add_irq_chip_np, except that the device
|
||||
* node of the regmap is used.
|
||||
*/
|
||||
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
int irq_base, const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data)
|
||||
{
|
||||
return regmap_add_irq_chip_np(map->dev->of_node, map, irq, irq_flags,
|
||||
irq_base, chip, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_add_irq_chip);
|
||||
|
||||
/**
|
||||
@ -874,6 +898,51 @@ static int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data)
|
||||
return *r == data;
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_regmap_add_irq_chip_np() - Resource manager regmap_add_irq_chip_np()
|
||||
*
|
||||
* @dev: The device pointer on which irq_chip belongs to.
|
||||
* @np: The device_node where the IRQ domain should be added to.
|
||||
* @map: The regmap for the device.
|
||||
* @irq: The IRQ the device uses to signal interrupts
|
||||
* @irq_flags: The IRQF_ flags to use for the primary interrupt.
|
||||
* @irq_base: Allocate at specific IRQ number if irq_base > 0.
|
||||
* @chip: Configuration for the interrupt controller.
|
||||
* @data: Runtime data structure for the controller, allocated on success
|
||||
*
|
||||
* Returns 0 on success or an errno on failure.
|
||||
*
|
||||
* The ®map_irq_chip_data will be automatically released when the device is
|
||||
* unbound.
|
||||
*/
|
||||
int devm_regmap_add_irq_chip_np(struct device *dev, struct device_node *np,
|
||||
struct regmap *map, int irq, int irq_flags,
|
||||
int irq_base,
|
||||
const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data)
|
||||
{
|
||||
struct regmap_irq_chip_data **ptr, *d;
|
||||
int ret;
|
||||
|
||||
ptr = devres_alloc(devm_regmap_irq_chip_release, sizeof(*ptr),
|
||||
GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = regmap_add_irq_chip_np(np, map, irq, irq_flags, irq_base,
|
||||
chip, &d);
|
||||
if (ret < 0) {
|
||||
devres_free(ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*ptr = d;
|
||||
devres_add(dev, ptr);
|
||||
*data = d;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip_np);
|
||||
|
||||
/**
|
||||
* devm_regmap_add_irq_chip() - Resource manager regmap_add_irq_chip()
|
||||
*
|
||||
@ -895,25 +964,8 @@ int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq,
|
||||
const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data)
|
||||
{
|
||||
struct regmap_irq_chip_data **ptr, *d;
|
||||
int ret;
|
||||
|
||||
ptr = devres_alloc(devm_regmap_irq_chip_release, sizeof(*ptr),
|
||||
GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = regmap_add_irq_chip(map, irq, irq_flags, irq_base,
|
||||
chip, &d);
|
||||
if (ret < 0) {
|
||||
devres_free(ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*ptr = d;
|
||||
devres_add(dev, ptr);
|
||||
*data = d;
|
||||
return 0;
|
||||
return devm_regmap_add_irq_chip_np(dev, map->dev->of_node, map, irq,
|
||||
irq_flags, irq_base, chip, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip);
|
||||
|
||||
|
@ -827,6 +827,7 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
} else if (!bus->read || !bus->write) {
|
||||
map->reg_read = _regmap_bus_reg_read;
|
||||
map->reg_write = _regmap_bus_reg_write;
|
||||
map->reg_update_bits = bus->reg_update_bits;
|
||||
|
||||
map->defer_caching = false;
|
||||
goto skip_format_initialization;
|
||||
@ -2936,6 +2937,28 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_update_bits_base);
|
||||
|
||||
/**
|
||||
* regmap_test_bits() - Check if all specified bits are set in a register.
|
||||
*
|
||||
* @map: Register map to operate on
|
||||
* @reg: Register to read from
|
||||
* @bits: Bits to test
|
||||
*
|
||||
* Returns -1 if the underlying regmap_read() fails, 0 if at least one of the
|
||||
* tested bits is not set and 1 if all tested bits are set.
|
||||
*/
|
||||
int regmap_test_bits(struct regmap *map, unsigned int reg, unsigned int bits)
|
||||
{
|
||||
unsigned int val, ret;
|
||||
|
||||
ret = regmap_read(map, reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (val & bits) == bits;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_test_bits);
|
||||
|
||||
void regmap_async_complete_cb(struct regmap_async *async, int ret)
|
||||
{
|
||||
struct regmap *map = async->map;
|
||||
|
@ -17,10 +17,12 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
struct module;
|
||||
struct clk;
|
||||
struct device;
|
||||
struct device_node;
|
||||
struct i2c_client;
|
||||
struct i3c_device;
|
||||
struct irq_domain;
|
||||
@ -71,6 +73,13 @@ struct reg_sequence {
|
||||
unsigned int delay_us;
|
||||
};
|
||||
|
||||
#define REG_SEQ(_reg, _def, _delay_us) { \
|
||||
.reg = _reg, \
|
||||
.def = _def, \
|
||||
.delay_us = _delay_us, \
|
||||
}
|
||||
#define REG_SEQ0(_reg, _def) REG_SEQ(_reg, _def, 0)
|
||||
|
||||
#define regmap_update_bits(map, reg, mask, val) \
|
||||
regmap_update_bits_base(map, reg, mask, val, NULL, false, false)
|
||||
#define regmap_update_bits_async(map, reg, mask, val)\
|
||||
@ -122,26 +131,10 @@ struct reg_sequence {
|
||||
*/
|
||||
#define regmap_read_poll_timeout(map, addr, val, cond, sleep_us, timeout_us) \
|
||||
({ \
|
||||
u64 __timeout_us = (timeout_us); \
|
||||
unsigned long __sleep_us = (sleep_us); \
|
||||
ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
|
||||
int __ret; \
|
||||
might_sleep_if(__sleep_us); \
|
||||
for (;;) { \
|
||||
__ret = regmap_read((map), (addr), &(val)); \
|
||||
if (__ret) \
|
||||
break; \
|
||||
if (cond) \
|
||||
break; \
|
||||
if ((__timeout_us) && \
|
||||
ktime_compare(ktime_get(), __timeout) > 0) { \
|
||||
__ret = regmap_read((map), (addr), &(val)); \
|
||||
break; \
|
||||
} \
|
||||
if (__sleep_us) \
|
||||
usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
|
||||
} \
|
||||
__ret ?: ((cond) ? 0 : -ETIMEDOUT); \
|
||||
int __ret, __tmp; \
|
||||
__tmp = read_poll_timeout(regmap_read, __ret, __ret || (cond), \
|
||||
sleep_us, timeout_us, false, (map), (addr), &(val)); \
|
||||
__ret ?: __tmp; \
|
||||
})
|
||||
|
||||
/**
|
||||
@ -209,25 +202,10 @@ struct reg_sequence {
|
||||
*/
|
||||
#define regmap_field_read_poll_timeout(field, val, cond, sleep_us, timeout_us) \
|
||||
({ \
|
||||
u64 __timeout_us = (timeout_us); \
|
||||
unsigned long __sleep_us = (sleep_us); \
|
||||
ktime_t timeout = ktime_add_us(ktime_get(), __timeout_us); \
|
||||
int pollret; \
|
||||
might_sleep_if(__sleep_us); \
|
||||
for (;;) { \
|
||||
pollret = regmap_field_read((field), &(val)); \
|
||||
if (pollret) \
|
||||
break; \
|
||||
if (cond) \
|
||||
break; \
|
||||
if (__timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
|
||||
pollret = regmap_field_read((field), &(val)); \
|
||||
break; \
|
||||
} \
|
||||
if (__sleep_us) \
|
||||
usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
|
||||
} \
|
||||
pollret ?: ((cond) ? 0 : -ETIMEDOUT); \
|
||||
int __ret, __tmp; \
|
||||
__tmp = read_poll_timeout(regmap_field_read, __ret, __ret || (cond), \
|
||||
sleep_us, timeout_us, false, (field), &(val)); \
|
||||
__ret ?: __tmp; \
|
||||
})
|
||||
|
||||
#ifdef CONFIG_REGMAP
|
||||
@ -1111,6 +1089,21 @@ bool regmap_reg_in_ranges(unsigned int reg,
|
||||
const struct regmap_range *ranges,
|
||||
unsigned int nranges);
|
||||
|
||||
static inline int regmap_set_bits(struct regmap *map,
|
||||
unsigned int reg, unsigned int bits)
|
||||
{
|
||||
return regmap_update_bits_base(map, reg, bits, bits,
|
||||
NULL, false, false);
|
||||
}
|
||||
|
||||
static inline int regmap_clear_bits(struct regmap *map,
|
||||
unsigned int reg, unsigned int bits)
|
||||
{
|
||||
return regmap_update_bits_base(map, reg, bits, 0, NULL, false, false);
|
||||
}
|
||||
|
||||
int regmap_test_bits(struct regmap *map, unsigned int reg, unsigned int bits);
|
||||
|
||||
/**
|
||||
* struct reg_field - Description of an register field
|
||||
*
|
||||
@ -1134,6 +1127,14 @@ struct reg_field {
|
||||
.msb = _msb, \
|
||||
}
|
||||
|
||||
#define REG_FIELD_ID(_reg, _lsb, _msb, _size, _offset) { \
|
||||
.reg = _reg, \
|
||||
.lsb = _lsb, \
|
||||
.msb = _msb, \
|
||||
.id_size = _size, \
|
||||
.id_offset = _offset, \
|
||||
}
|
||||
|
||||
struct regmap_field *regmap_field_alloc(struct regmap *regmap,
|
||||
struct reg_field reg_field);
|
||||
void regmap_field_free(struct regmap_field *field);
|
||||
@ -1310,12 +1311,21 @@ struct regmap_irq_chip_data;
|
||||
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
int irq_base, const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data);
|
||||
int regmap_add_irq_chip_np(struct device_node *np, struct regmap *map, int irq,
|
||||
int irq_flags, int irq_base,
|
||||
const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data);
|
||||
void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data);
|
||||
|
||||
int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq,
|
||||
int irq_flags, int irq_base,
|
||||
const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data);
|
||||
int devm_regmap_add_irq_chip_np(struct device *dev, struct device_node *np,
|
||||
struct regmap *map, int irq, int irq_flags,
|
||||
int irq_base,
|
||||
const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data);
|
||||
void devm_regmap_del_irq_chip(struct device *dev, int irq,
|
||||
struct regmap_irq_chip_data *data);
|
||||
|
||||
@ -1410,6 +1420,27 @@ static inline int regmap_update_bits_base(struct regmap *map, unsigned int reg,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int regmap_set_bits(struct regmap *map,
|
||||
unsigned int reg, unsigned int bits)
|
||||
{
|
||||
WARN_ONCE(1, "regmap API is disabled");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int regmap_clear_bits(struct regmap *map,
|
||||
unsigned int reg, unsigned int bits)
|
||||
{
|
||||
WARN_ONCE(1, "regmap API is disabled");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int regmap_test_bits(struct regmap *map,
|
||||
unsigned int reg, unsigned int bits)
|
||||
{
|
||||
WARN_ONCE(1, "regmap API is disabled");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int regmap_field_update_bits_base(struct regmap_field *field,
|
||||
unsigned int mask, unsigned int val,
|
||||
bool *change, bool async, bool force)
|
||||
|
Loading…
Reference in New Issue
Block a user