diff mbox

regmap: Allow regmap_bulk_write() to work for "no-bus" regmaps

Message ID 20131217023047.GC31766@codeaurora.org (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Stephen Boyd Dec. 17, 2013, 2:30 a.m. UTC
regmap_bulk_write() should decay to performing individual writes
if we're using a "no-bus" regmap. Unfortunately, it returns an
error because there is no map->bus pointer. Fix it.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---

On 12/16, Mark Brown wrote:
> On Fri, Dec 13, 2013 at 01:37:07PM -0800, Stephen Boyd wrote:
> 
> > I came up with this (possibly ugly) patch. It works for my
> > specific case but I'm not sure if unpacking the val bits into an
> > unsigned int and passing that to _regmap_write() is sane. What do
> > you think?
> 
> It's not lovely but it's about as good as it gets.  I'd probaly just
> drop the raw single write case so it's simpler - just either raw write
> the lot or write a register at a time with unpacking (and so refactor
> the the loop that does the in place formatting into the raw case only
> and not bother for the single write).

Ok how about this?

 drivers/base/regmap/regmap.c | 43 ++++++++++++++++++-------------------------
 1 file changed, 18 insertions(+), 25 deletions(-)

Comments

Mark Brown Dec. 18, 2013, 6:45 p.m. UTC | #1
On Mon, Dec 16, 2013 at 06:30:47PM -0800, Stephen Boyd wrote:

> -	/* No formatting is require if val_byte is 1 */
> -	if (val_bytes == 1) {
> -		wval = (void *)val;
> +			ival = *(unsigned int *)(val + (i * val_bytes));
> +			ret = _regmap_write(map, reg + (i * map->reg_stride),
> +					ival);
> +			if (ret != 0)
> +				goto out;

This doesn't quite work - val is an array of objects of the size of the
size of a register not of unsigned integers so you're parsing extra data
out there.  That possibly wasn't the best choice of API but we have
quite a few users now so ick.
Stephen Boyd Dec. 23, 2013, 8:05 p.m. UTC | #2
On 12/18/13 10:45, Mark Brown wrote:
> On Mon, Dec 16, 2013 at 06:30:47PM -0800, Stephen Boyd wrote:
>
>> -	/* No formatting is require if val_byte is 1 */
>> -	if (val_bytes == 1) {
>> -		wval = (void *)val;
>> +			ival = *(unsigned int *)(val + (i * val_bytes));
>> +			ret = _regmap_write(map, reg + (i * map->reg_stride),
>> +					ival);
>> +			if (ret != 0)
>> +				goto out;
> This doesn't quite work - val is an array of objects of the size of the
> size of a register not of unsigned integers so you're parsing extra data
> out there.  That possibly wasn't the best choice of API but we have
> quite a few users now so ick.

Are you concerned that we'll read past the end of the val buffer? Do we
need to cast the pointer to be the appropriate size according to
val_bytes? Something like this?

                for (i = 0; i < val_count; i++) {
                        unsigned int ival;

                        switch (val_bytes) {
                        case 1: 
                                ival = *(u8 *)(val + (i * val_bytes));
                                break;
                        case 2: 
                                ival = *(u16 *)(val + (i * val_bytes));
                                break;
                        case 4: 
                                ival = *(u32 *)(val + (i * val_bytes));
                                break;
#ifdef CONFIG_64BIT
                        case 8: 
                                ival = *(u64 *)(val + (i * val_bytes));
                                break;
#endif
                        default:
                                ret = -EINVAL;
                                goto out;
                        }

                        ret = _regmap_write(map, reg + (i * map->reg_stride),
                                        ival);
                        if (ret != 0)
                                goto out;
                }
Mark Brown Dec. 24, 2013, 12:53 p.m. UTC | #3
On Mon, Dec 23, 2013 at 12:05:53PM -0800, Stephen Boyd wrote:
> On 12/18/13 10:45, Mark Brown wrote:

> > This doesn't quite work - val is an array of objects of the size of the
> > size of a register not of unsigned integers so you're parsing extra data
> > out there.  That possibly wasn't the best choice of API but we have
> > quite a few users now so ick.

> Are you concerned that we'll read past the end of the val buffer? Do we
> need to cast the pointer to be the appropriate size according to
> val_bytes? Something like this?

That's one issue, the other is that if we try to read (say) and 8 bit
value as an unsigned int we'll not just read the value we're looking
for.

>                 for (i = 0; i < val_count; i++) {
>                         unsigned int ival;
> 
>                         switch (val_bytes) {
>                         case 1: 
>                                 ival = *(u8 *)(val + (i * val_bytes));
>                                 break;

I think we do sadly.  Or refactor the API to work in unsigned ints
which would've been more sensible in the first place but that'd make it
asymmetrical with the read API as it stands...
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stephen Boyd Dec. 26, 2013, 7:34 p.m. UTC | #4
On 12/24, Mark Brown wrote:
> On Mon, Dec 23, 2013 at 12:05:53PM -0800, Stephen Boyd wrote:
> > On 12/18/13 10:45, Mark Brown wrote:
> 
> > > This doesn't quite work - val is an array of objects of the size of the
> > > size of a register not of unsigned integers so you're parsing extra data
> > > out there.  That possibly wasn't the best choice of API but we have
> > > quite a few users now so ick.
> 
> > Are you concerned that we'll read past the end of the val buffer? Do we
> > need to cast the pointer to be the appropriate size according to
> > val_bytes? Something like this?
> 
> That's one issue, the other is that if we try to read (say) and 8 bit
> value as an unsigned int we'll not just read the value we're looking
> for.

Ah right. My no-bus implementation was clearing out the upper 24
bits of the word so I could just send the u8 value. With this
approach that isn't necessary.

> 
> >                 for (i = 0; i < val_count; i++) {
> >                         unsigned int ival;
> > 
> >                         switch (val_bytes) {
> >                         case 1: 
> >                                 ival = *(u8 *)(val + (i * val_bytes));
> >                                 break;
> 
> I think we do sadly.  Or refactor the API to work in unsigned ints
> which would've been more sensible in the first place but that'd make it
> asymmetrical with the read API as it stands...

Ok it sounds like you're willing to go with this updated loop.
I'll resend a proper patch.
diff mbox

Patch

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 1ccd61b..12b80f6 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1514,21 +1514,30 @@  int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 {
 	int ret = 0, i;
 	size_t val_bytes = map->format.val_bytes;
-	void *wval;
 
-	if (!map->bus)
-		return -EINVAL;
-	if (!map->format.parse_inplace)
+	if (map->bus && !map->format.parse_inplace)
 		return -EINVAL;
 	if (reg % map->reg_stride)
 		return -EINVAL;
 
 	map->lock(map->lock_arg);
+	/*
+	 * Some devices don't support bulk write, for
+	 * them we have a series of single write operations.
+	 */
+	if (!map->bus || map->use_single_rw) {
+		for (i = 0; i < val_count; i++) {
+			unsigned int ival;
 
-	/* No formatting is require if val_byte is 1 */
-	if (val_bytes == 1) {
-		wval = (void *)val;
+			ival = *(unsigned int *)(val + (i * val_bytes));
+			ret = _regmap_write(map, reg + (i * map->reg_stride),
+					ival);
+			if (ret != 0)
+				goto out;
+		}
 	} else {
+		void *wval;
+
 		wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
 		if (!wval) {
 			ret = -ENOMEM;
@@ -1537,27 +1546,11 @@  int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 		}
 		for (i = 0; i < val_count * val_bytes; i += val_bytes)
 			map->format.parse_inplace(wval + i);
-	}
-	/*
-	 * Some devices does not support bulk write, for
-	 * them we have a series of single write operations.
-	 */
-	if (map->use_single_rw) {
-		for (i = 0; i < val_count; i++) {
-			ret = _regmap_raw_write(map,
-						reg + (i * map->reg_stride),
-						val + (i * val_bytes),
-						val_bytes);
-			if (ret != 0)
-				return ret;
-		}
-	} else {
+
 		ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
-	}
 
-	if (val_bytes != 1)
 		kfree(wval);
-
+	}
 out:
 	map->unlock(map->lock_arg);
 	return ret;