From: Nariman Poushin <nariman@opensource.wolfsonmicro.com>
To: broonie@kernel.org
Cc: gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org,
patches@opensource.wolfsonmicro.com
Subject: [RFC][PATCH] regmap: Add reg_sequence for use with multi_reg_write / register_patch
Date: Mon, 1 Jun 2015 10:20:12 +0100 [thread overview]
Message-ID: <20150601092012.GA31354@opensource.wolfsonmicro.com> (raw)
Support write sequences / patches with specified delays (in microseconds)
after some (or all) writes. Logically separate reg_default from the new
reg_sequence structure (which has an additional delay_us member) as the
reg_default tables can run in to the thousands (for modern devices) and the
additional memory usage due to an added unsigned int could be large
(especially on 64-bit systems).
We treat a delay in a sequence the same as we treat a page change as they
are logically similar (you can coalesce all writes within the same page or
if none of them require a delay after being written).
Signed-off-by: Nariman Poushin <nariman@opensource.wolfsonmicro.com>
---
I am not sure how best to update all the users of this patch (should
it be accepted), should I:
- Squash all the updates in to this patch (I suppose the benefit
there is that we don't break the kernel build from one patch
to the other)
- Provide separate patch(es) for the users
Thanks
Nariman
drivers/base/regmap/internal.h | 2 +-
drivers/base/regmap/regmap.c | 56 ++++++++++++++++++++++++++++++------------
include/linux/regmap.h | 21 +++++++++++++---
3 files changed, 59 insertions(+), 20 deletions(-)
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index b2b2849..873ddf9 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -136,7 +136,7 @@ struct regmap {
/* if set, the HW registers are known to match map->reg_defaults */
bool no_sync_defaults;
- struct reg_default *patch;
+ struct reg_sequence *patch;
int patch_regs;
/* if set, converts bulk rw to single rw */
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 6273ff0..75773fd 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
+#include <linux/delay.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
@@ -47,6 +48,17 @@ static int _regmap_bus_reg_write(void *context, unsigned int reg,
static int _regmap_bus_raw_write(void *context, unsigned int reg,
unsigned int val);
+void regmap_sequence_delay(unsigned int delay_us)
+{
+ /* For small delays it isn't worth setting up the hrtimers
+ * so fall back on udelay
+ */
+ if (delay_us < 10)
+ udelay(delay_us);
+ else
+ usleep_range(delay_us, delay_us * 2);
+}
+
bool regmap_reg_in_ranges(unsigned int reg,
const struct regmap_range *ranges,
unsigned int nranges)
@@ -1744,7 +1756,7 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write);
* relative. The page register has been written if that was neccessary.
*/
static int _regmap_raw_multi_reg_write(struct regmap *map,
- const struct reg_default *regs,
+ const struct reg_sequence *regs,
size_t num_regs)
{
int ret;
@@ -1801,17 +1813,18 @@ static unsigned int _regmap_register_page(struct regmap *map,
}
static int _regmap_range_multi_paged_reg_write(struct regmap *map,
- struct reg_default *regs,
+ struct reg_sequence *regs,
size_t num_regs)
{
int ret;
int i, n;
- struct reg_default *base;
+ struct reg_sequence *base;
unsigned int this_page = 0;
/*
* the set of registers are not neccessarily in order, but
* since the order of write must be preserved this algorithm
- * chops the set each time the page changes
+ * chops the set each time the page changes. This also applies
+ * if there is a delay required at any point in the sequence.
*/
base = regs;
for (i = 0, n = 0; i < num_regs; i++, n++) {
@@ -1819,17 +1832,21 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map,
struct regmap_range_node *range;
range = _regmap_range_lookup(map, reg);
- if (range) {
+ if (range || regs[i].delay_us) {
unsigned int win_page = _regmap_register_page(map, reg,
range);
if (i == 0)
this_page = win_page;
- if (win_page != this_page) {
+ if (win_page != this_page || regs[i].delay_us) {
this_page = win_page;
ret = _regmap_raw_multi_reg_write(map, base, n);
if (ret != 0)
return ret;
+
+ if (regs[i].delay_us)
+ regmap_sequence_delay(regs[i].delay_us);
+
base += n;
n = 0;
}
@@ -1844,7 +1861,7 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map,
}
static int _regmap_multi_reg_write(struct regmap *map,
- const struct reg_default *regs,
+ const struct reg_sequence *regs,
size_t num_regs)
{
int i;
@@ -1855,6 +1872,9 @@ static int _regmap_multi_reg_write(struct regmap *map,
ret = _regmap_write(map, regs[i].reg, regs[i].def);
if (ret != 0)
return ret;
+
+ if (regs[i].delay_us)
+ regmap_sequence_delay(regs[i].delay_us);
}
return 0;
}
@@ -1894,11 +1914,15 @@ static int _regmap_multi_reg_write(struct regmap *map,
for (i = 0; i < num_regs; i++) {
unsigned int reg = regs[i].reg;
struct regmap_range_node *range;
+
+ /* Coalesce all the writes between a page break or a delay
+ * in a sequence
+ */
range = _regmap_range_lookup(map, reg);
- if (range) {
- size_t len = sizeof(struct reg_default)*num_regs;
- struct reg_default *base = kmemdup(regs, len,
- GFP_KERNEL);
+ if (range || regs[i].delay_us) {
+ size_t len = sizeof(struct reg_sequence)*num_regs;
+ struct reg_sequence *base = kmemdup(regs, len,
+ GFP_KERNEL);
if (!base)
return -ENOMEM;
ret = _regmap_range_multi_paged_reg_write(map, base,
@@ -1930,7 +1954,7 @@ static int _regmap_multi_reg_write(struct regmap *map,
* A value of zero will be returned on success, a negative errno will be
* returned in error cases.
*/
-int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs,
+int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs,
int num_regs)
{
int ret;
@@ -1963,7 +1987,7 @@ EXPORT_SYMBOL_GPL(regmap_multi_reg_write);
* be returned in error cases.
*/
int regmap_multi_reg_write_bypassed(struct regmap *map,
- const struct reg_default *regs,
+ const struct reg_sequence *regs,
int num_regs)
{
int ret;
@@ -2553,10 +2577,10 @@ EXPORT_SYMBOL_GPL(regmap_async_complete);
* The caller must ensure that this function cannot be called
* concurrently with either itself or regcache_sync().
*/
-int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
+int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs,
int num_regs)
{
- struct reg_default *p;
+ struct reg_sequence *p;
int ret;
bool bypass;
@@ -2565,7 +2589,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
return 0;
p = krealloc(map->patch,
- sizeof(struct reg_default) * (map->patch_regs + num_regs),
+ sizeof(struct reg_sequence) * (map->patch_regs + num_regs),
GFP_KERNEL);
if (p) {
memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs));
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 116655d..c3bf366 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -50,6 +50,21 @@ struct reg_default {
unsigned int def;
};
+/**
+ * Register / Value pairs for sequences of writes, incorporating an optional
+ * delay in microseconds.
+ *
+ * @reg: Register address.
+ * @def: Register default value.
+ * @delay_us: Delay in microseconds
+ */
+
+struct reg_sequence {
+ unsigned reg;
+ unsigned def;
+ unsigned delay_us;
+};
+
#ifdef CONFIG_REGMAP
enum regmap_endian {
@@ -410,10 +425,10 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len);
int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
size_t val_count);
-int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs,
+int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs,
int num_regs);
int regmap_multi_reg_write_bypassed(struct regmap *map,
- const struct reg_default *regs,
+ const struct reg_sequence *regs,
int num_regs);
int regmap_raw_write_async(struct regmap *map, unsigned int reg,
const void *val, size_t val_len);
@@ -448,7 +463,7 @@ void regcache_mark_dirty(struct regmap *map);
bool regmap_check_range_table(struct regmap *map, unsigned int reg,
const struct regmap_access_table *table);
-int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
+int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs,
int num_regs);
int regmap_parse_val(struct regmap *map, const void *buf,
unsigned int *val);
--
2.1.4
next reply other threads:[~2015-06-01 9:20 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-06-01 9:20 Nariman Poushin [this message]
2015-06-02 18:15 ` [RFC][PATCH] regmap: Add reg_sequence for use with multi_reg_write / register_patch Mark Brown
2015-06-04 14:21 ` Nariman Poushin
2015-06-04 17:06 ` Mark Brown
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=20150601092012.GA31354@opensource.wolfsonmicro.com \
--to=nariman@opensource.wolfsonmicro.com \
--cc=broonie@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=patches@opensource.wolfsonmicro.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.