aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds2016-07-26 19:24:33 -0700
committerLinus Torvalds2016-07-26 19:24:33 -0700
commitae9799975ccf5f1b2c30227d6d02aa4129750a64 (patch)
treeddce82a458a041554a95477b67e728f2a8916efa
parent1cd04d293c818687795b83cd8f2626bd4662feeb (diff)
parentefeb1a3ab91dc6bead3b5a522c0ceca1d711feb1 (diff)
Merge tag 'regmap-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap
Pull regmap updates from Mark Brown: "Several small updates and API enhancements: - provide transparent unrolling of bulk writes into individual writes so they can be used with devices without raw formatting. - fix compatibility between I2C controllers supporting block commands and devices with more than 8 bit wide registers. - add some helpers for iopoll-like functionality and workarounds for weird interrupt controllers" * tag 'regmap-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: regmap: add iopoll-like polling macro regmap: Support bulk writes for devices without raw formatting regmap-i2c: Use i2c block command only if register value width is 8 bit regmap: irq: Add support to call client specific pre/post interrupt service regmap: Add file patterns for regmap device tree bindings
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/base/regmap/regmap-i2c.c2
-rw-r--r--drivers/base/regmap/regmap-irq.c15
-rw-r--r--drivers/base/regmap/regmap.c31
-rw-r--r--include/linux/regmap.h49
5 files changed, 90 insertions, 8 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index f4698b1cf847..b407c5f5caa4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9691,6 +9691,7 @@ M: Mark Brown <broonie@kernel.org>
L: linux-kernel@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git
S: Supported
+F: Documentation/devicetree/bindings/regmap/
F: drivers/base/regmap/
F: include/linux/regmap.h
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 1a8ec3b2b601..4735318f4268 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -259,7 +259,7 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
{
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
return &regmap_i2c;
- else if (config->reg_bits == 8 &&
+ else if (config->val_bits == 8 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
return &regmap_i2c_smbus_i2c_block;
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 26f799e71c82..ec262476d043 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -268,13 +268,16 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
bool handled = false;
u32 reg;
+ if (chip->handle_pre_irq)
+ chip->handle_pre_irq(chip->irq_drv_data);
+
if (chip->runtime_pm) {
ret = pm_runtime_get_sync(map->dev);
if (ret < 0) {
dev_err(map->dev, "IRQ thread failed to resume: %d\n",
ret);
pm_runtime_put(map->dev);
- return IRQ_NONE;
+ goto exit;
}
}
@@ -296,7 +299,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n",
ret);
- return IRQ_NONE;
+ goto exit;
}
for (i = 0; i < data->chip->num_regs; i++) {
@@ -312,7 +315,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
break;
default:
BUG();
- return IRQ_NONE;
+ goto exit;
}
}
@@ -329,7 +332,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
ret);
if (chip->runtime_pm)
pm_runtime_put(map->dev);
- return IRQ_NONE;
+ goto exit;
}
}
}
@@ -365,6 +368,10 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
if (chip->runtime_pm)
pm_runtime_put(map->dev);
+exit:
+ if (chip->handle_post_irq)
+ chip->handle_post_irq(chip->irq_drv_data);
+
if (handled)
return IRQ_HANDLED;
else
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index df2d2ef5d6b3..51fa7d66a393 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1777,8 +1777,6 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
size_t val_bytes = map->format.val_bytes;
size_t total_size = val_bytes * val_count;
- if (map->bus && !map->format.parse_inplace)
- return -EINVAL;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
@@ -1789,7 +1787,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
*
* The first if block is used for memory mapped io. It does not allow
* val_bytes of 3 for example.
- * The second one is used for busses which do not have this limitation
+ * The second one is for busses that do not provide raw I/O.
+ * The third one is used for busses which do not have these limitations
* and can write arbitrary value lengths.
*/
if (!map->bus) {
@@ -1825,6 +1824,32 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
}
out:
map->unlock(map->lock_arg);
+ } else if (map->bus && !map->format.parse_inplace) {
+ const u8 *u8 = val;
+ const u16 *u16 = val;
+ const u32 *u32 = val;
+ unsigned int ival;
+
+ for (i = 0; i < val_count; i++) {
+ switch (map->format.val_bytes) {
+ case 4:
+ ival = u32[i];
+ break;
+ case 2:
+ ival = u16[i];
+ break;
+ case 1:
+ ival = u8[i];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_write(map, reg + (i * map->reg_stride),
+ ival);
+ if (ret)
+ return ret;
+ }
} else if (map->use_single_write ||
(map->max_raw_write && map->max_raw_write < total_size)) {
int chunk_stride = map->reg_stride;
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 3dc08ce15426..2c12cc5af744 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -95,6 +95,45 @@ struct reg_sequence {
#define regmap_fields_force_update_bits(field, id, mask, val) \
regmap_fields_update_bits_base(field, id, mask, val, NULL, false, true)
+/**
+ * regmap_read_poll_timeout - Poll until a condition is met or a timeout occurs
+ * @map: Regmap to read from
+ * @addr: Address to poll
+ * @val: Unsigned integer variable to read the value into
+ * @cond: Break condition (usually involving @val)
+ * @sleep_us: Maximum time to sleep between reads in us (0
+ * tight-loops). Should be less than ~20ms since usleep_range
+ * is used (see Documentation/timers/timers-howto.txt).
+ * @timeout_us: Timeout in us, 0 means never timeout
+ *
+ * Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_read
+ * error return value in case of a error read. In the two former cases,
+ * the last read value at @addr is stored in @val. Must not be called
+ * from atomic context if sleep_us or timeout_us are used.
+ *
+ * This is modelled after the readx_poll_timeout macros in linux/iopoll.h.
+ */
+#define regmap_read_poll_timeout(map, addr, val, cond, sleep_us, timeout_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); \
+})
+
#ifdef CONFIG_REGMAP
enum regmap_endian {
@@ -851,6 +890,12 @@ struct regmap_irq {
* @num_type_reg: Number of type registers.
* @type_reg_stride: Stride to use for chips where type registers are not
* contiguous.
+ * @handle_pre_irq: Driver specific callback to handle interrupt from device
+ * before regmap_irq_handler process the interrupts.
+ * @handle_post_irq: Driver specific callback to handle interrupt from device
+ * after handling the interrupts in regmap_irq_handler().
+ * @irq_drv_data: Driver specific IRQ data which is passed as parameter when
+ * driver specific pre/post interrupt handler is called.
*/
struct regmap_irq_chip {
const char *name;
@@ -877,6 +922,10 @@ struct regmap_irq_chip {
int num_type_reg;
unsigned int type_reg_stride;
+
+ int (*handle_pre_irq)(void *irq_drv_data);
+ int (*handle_post_irq)(void *irq_drv_data);
+ void *irq_drv_data;
};
struct regmap_irq_chip_data;