regmap: drop cache if the bus transfer error
[cascardo/linux.git] / drivers / base / regmap / regmap.c
index df2d2ef..25d26bb 100644 (file)
@@ -1474,6 +1474,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
                ret = map->bus->write(map->bus_context, buf, len);
 
                kfree(buf);
+       } else if (ret != 0 && !map->cache_bypass && map->format.parse_val) {
+               regcache_drop_region(map, reg, reg + 1);
        }
 
        trace_regmap_hw_write_done(map, reg, val_len / map->format.val_bytes);
@@ -1777,8 +1779,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 +1789,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 +1826,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;