regmap: Also protect hwspinlock in error handling path
[sfrench/cifs-2.6.git] / drivers / base / regmap / regmap.c
index b9a779a4a739cda497351be2b2dc51d69f35c022..5ff549fa880becc39a8aa53b324a70744711bfa2 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/sched.h>
 #include <linux/delay.h>
 #include <linux/log2.h>
+#include <linux/hwspinlock.h>
 
 #define CREATE_TRACE_POINTS
 #include "trace.h"
@@ -413,6 +414,51 @@ static unsigned int regmap_parse_64_native(const void *buf)
 }
 #endif
 
+#ifdef REGMAP_HWSPINLOCK
+static void regmap_lock_hwlock(void *__map)
+{
+       struct regmap *map = __map;
+
+       hwspin_lock_timeout(map->hwlock, UINT_MAX);
+}
+
+static void regmap_lock_hwlock_irq(void *__map)
+{
+       struct regmap *map = __map;
+
+       hwspin_lock_timeout_irq(map->hwlock, UINT_MAX);
+}
+
+static void regmap_lock_hwlock_irqsave(void *__map)
+{
+       struct regmap *map = __map;
+
+       hwspin_lock_timeout_irqsave(map->hwlock, UINT_MAX,
+                                   &map->spinlock_flags);
+}
+
+static void regmap_unlock_hwlock(void *__map)
+{
+       struct regmap *map = __map;
+
+       hwspin_unlock(map->hwlock);
+}
+
+static void regmap_unlock_hwlock_irq(void *__map)
+{
+       struct regmap *map = __map;
+
+       hwspin_unlock_irq(map->hwlock);
+}
+
+static void regmap_unlock_hwlock_irqrestore(void *__map)
+{
+       struct regmap *map = __map;
+
+       hwspin_unlock_irqrestore(map->hwlock, &map->spinlock_flags);
+}
+#endif
+
 static void regmap_lock_mutex(void *__map)
 {
        struct regmap *map = __map;
@@ -627,6 +673,34 @@ struct regmap *__regmap_init(struct device *dev,
                map->lock = config->lock;
                map->unlock = config->unlock;
                map->lock_arg = config->lock_arg;
+       } else if (config->hwlock_id) {
+#ifdef REGMAP_HWSPINLOCK
+               map->hwlock = hwspin_lock_request_specific(config->hwlock_id);
+               if (!map->hwlock) {
+                       ret = -ENXIO;
+                       goto err_map;
+               }
+
+               switch (config->hwlock_mode) {
+               case HWLOCK_IRQSTATE:
+                       map->lock = regmap_lock_hwlock_irqsave;
+                       map->unlock = regmap_unlock_hwlock_irqrestore;
+                       break;
+               case HWLOCK_IRQ:
+                       map->lock = regmap_lock_hwlock_irq;
+                       map->unlock = regmap_unlock_hwlock_irq;
+                       break;
+               default:
+                       map->lock = regmap_lock_hwlock;
+                       map->unlock = regmap_unlock_hwlock;
+                       break;
+               }
+
+               map->lock_arg = map;
+#else
+               ret = -EINVAL;
+               goto err;
+#endif
        } else {
                if ((bus && bus->fast_io) ||
                    config->fast_io) {
@@ -729,7 +803,7 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.format_write = regmap_format_2_6_write;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
 
@@ -739,7 +813,7 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.format_write = regmap_format_4_12_write;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
 
@@ -749,7 +823,7 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.format_write = regmap_format_7_9_write;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
 
@@ -759,7 +833,7 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.format_write = regmap_format_10_14_write;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
 
@@ -779,13 +853,13 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.format_reg = regmap_format_16_native;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
 
        case 24:
                if (reg_endian != REGMAP_ENDIAN_BIG)
-                       goto err_map;
+                       goto err_hwlock;
                map->format.format_reg = regmap_format_24;
                break;
 
@@ -801,7 +875,7 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.format_reg = regmap_format_32_native;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
 
@@ -818,13 +892,13 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.format_reg = regmap_format_64_native;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
 #endif
 
        default:
-               goto err_map;
+               goto err_hwlock;
        }
 
        if (val_endian == REGMAP_ENDIAN_NATIVE)
@@ -853,12 +927,12 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.parse_val = regmap_parse_16_native;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
        case 24:
                if (val_endian != REGMAP_ENDIAN_BIG)
-                       goto err_map;
+                       goto err_hwlock;
                map->format.format_val = regmap_format_24;
                map->format.parse_val = regmap_parse_24;
                break;
@@ -879,7 +953,7 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.parse_val = regmap_parse_32_native;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
 #ifdef CONFIG_64BIT
@@ -900,7 +974,7 @@ struct regmap *__regmap_init(struct device *dev,
                        map->format.parse_val = regmap_parse_64_native;
                        break;
                default:
-                       goto err_map;
+                       goto err_hwlock;
                }
                break;
 #endif
@@ -909,18 +983,18 @@ struct regmap *__regmap_init(struct device *dev,
        if (map->format.format_write) {
                if ((reg_endian != REGMAP_ENDIAN_BIG) ||
                    (val_endian != REGMAP_ENDIAN_BIG))
-                       goto err_map;
+                       goto err_hwlock;
                map->use_single_write = true;
        }
 
        if (!map->format.format_write &&
            !(map->format.format_reg && map->format.format_val))
-               goto err_map;
+               goto err_hwlock;
 
        map->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL);
        if (map->work_buf == NULL) {
                ret = -ENOMEM;
-               goto err_map;
+               goto err_hwlock;
        }
 
        if (map->format.format_write) {
@@ -1041,6 +1115,9 @@ err_regcache:
 err_range:
        regmap_range_exit(map);
        kfree(map->work_buf);
+err_hwlock:
+       if (IS_ENABLED(REGMAP_HWSPINLOCK) && map->hwlock)
+               hwspin_lock_free(map->hwlock);
 err_map:
        kfree(map);
 err: