Merge tag 'regmap-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 13 Nov 2017 17:45:34 +0000 (09:45 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 13 Nov 2017 17:45:34 +0000 (09:45 -0800)
Pull regmap updates from Mark Brown:
 "After several quiet kernel releases we've got a couple of new features
  in regmap, support for using hwspinlocks as the lock for the internal
  data structures and a helper for polling on regmap_fields. The Kconfig
  dependencies on hwspinlocks were annoyingly difficult to squash
  between things behaving surprisingly and randconfig, I could've
  squashed those commits down but might've have caused hassle with other
  trees trying to use the new support.

   - support for using a hwspinlock to protect the regmap

   - an iopoll style helper for regmap_field"

* tag 'regmap-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: Fix unused warning
  regmap: Try to work around Kconfig exploding on HWSPINLOCK
  regmap: Clean up hwspinlock on regmap exit
  regmap: Also protect hwspinlock in error handling path
  regmap: Add a config option for hwspinlock
  regmap: Add hardware spinlock support
  regmap: avoid -Wint-in-bool-context warning
  regmap: add iopoll-like polling macro for regmap_field
  regmap: constify regmap_bus structures
  regmap: Avoid namespace collision within macro & tidy up

drivers/base/regmap/Kconfig
drivers/base/regmap/internal.h
drivers/base/regmap/regmap-spi.c
drivers/base/regmap/regmap-spmi.c
drivers/base/regmap/regmap.c
include/linux/regmap.h

index 0368fd7b3a414796f78db5e5d91788a3069358ca..3a1535d812d8c9d393338c30444cc64bb7d09651 100644 (file)
@@ -6,6 +6,7 @@
 config REGMAP
        default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
        select IRQ_DOMAIN if REGMAP_IRQ
+       select REGMAP_HWSPINLOCK if HWSPINLOCK=y
        bool
 
 config REGCACHE_COMPRESSED
@@ -37,3 +38,6 @@ config REGMAP_MMIO
 
 config REGMAP_IRQ
        bool
+
+config REGMAP_HWSPINLOCK
+       bool
index 2a4435d760289118f00882fb89693346972ae996..8641183cac2ff35ae883b8913b00831fa3a5af8a 100644 (file)
@@ -157,6 +157,8 @@ struct regmap {
 
        struct rb_root range_tree;
        void *selector_work_buf;        /* Scratch buffer used for selector */
+
+       struct hwspinlock *hwlock;
 };
 
 struct regcache_ops {
index edd9a839d004dcf98ab46b15c9002c958ad6fe56..c7150dd264d5806ca2fd0581b7c972c79949920e 100644 (file)
@@ -102,7 +102,7 @@ static int regmap_spi_read(void *context,
        return spi_write_then_read(spi, reg, reg_size, val, val_size);
 }
 
-static struct regmap_bus regmap_spi = {
+static const struct regmap_bus regmap_spi = {
        .write = regmap_spi_write,
        .gather_write = regmap_spi_gather_write,
        .async_write = regmap_spi_async_write,
index 4a36e415e938560ce2e3ba927b81888444767a7d..0bfb8ed244d50c054f3a1d88d78a32ea942930eb 100644 (file)
@@ -83,7 +83,7 @@ static int regmap_spmi_base_write(void *context, const void *data,
                                             count - 1);
 }
 
-static struct regmap_bus regmap_spmi_base = {
+static const struct regmap_bus regmap_spmi_base = {
        .read                           = regmap_spmi_base_read,
        .write                          = regmap_spmi_base_write,
        .gather_write                   = regmap_spmi_base_gather_write,
@@ -203,7 +203,7 @@ static int regmap_spmi_ext_write(void *context, const void *data,
                                            count - 2);
 }
 
-static struct regmap_bus regmap_spmi_ext = {
+static const struct regmap_bus regmap_spmi_ext = {
        .read                           = regmap_spmi_ext_read,
        .write                          = regmap_spmi_ext_write,
        .gather_write                   = regmap_spmi_ext_gather_write,
index b9a779a4a739cda497351be2b2dc51d69f35c022..8d516a9bfc01722567588523b92e7fdcc2b75339 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_map;
+#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:
@@ -1228,6 +1305,8 @@ void regmap_exit(struct regmap *map)
                kfree(async->work_buf);
                kfree(async);
        }
+       if (IS_ENABLED(REGMAP_HWSPINLOCK) && map->hwlock)
+               hwspin_lock_free(map->hwlock);
        kfree(map);
 }
 EXPORT_SYMBOL_GPL(regmap_exit);
index 978abfbac61783091046d63dbe23aedd2ad82b3c..15eddc1353bae3a272f6bbae881abe97981c0a6b 100644 (file)
@@ -120,21 +120,65 @@ struct reg_sequence {
  */
 #define regmap_read_poll_timeout(map, addr, val, cond, sleep_us, timeout_us) \
 ({ \
-       ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
+       u64 __timeout_us = (timeout_us); \
+       unsigned long __sleep_us = (sleep_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); \
+})
+
+/**
+ * regmap_field_read_poll_timeout - Poll until a condition is met or timeout
+ *
+ * @field: Regmap field to read from
+ * @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_field_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_field_read_poll_timeout(field, val, cond, sleep_us, timeout_us) \
+({ \
+       u64 __timeout_us = (timeout_us); \
+       unsigned long __sleep_us = (sleep_us); \
+       ktime_t timeout = ktime_add_us(ktime_get(), __timeout_us); \
        int pollret; \
-       might_sleep_if(sleep_us); \
+       might_sleep_if(__sleep_us); \
        for (;;) { \
-               pollret = regmap_read((map), (addr), &(val)); \
+               pollret = regmap_field_read((field), &(val)); \
                if (pollret) \
                        break; \
                if (cond) \
                        break; \
-               if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
-                       pollret = regmap_read((map), (addr), &(val)); \
+               if (__timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
+                       pollret = regmap_field_read((field), &(val)); \
                        break; \
                } \
-               if (sleep_us) \
-                       usleep_range((sleep_us >> 2) + 1, sleep_us); \
+               if (__sleep_us) \
+                       usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
        } \
        pollret ?: ((cond) ? 0 : -ETIMEDOUT); \
 })
@@ -273,6 +317,9 @@ typedef void (*regmap_unlock)(void *);
  *
  * @ranges: Array of configuration entries for virtual address ranges.
  * @num_ranges: Number of range configuration entries.
+ * @hwlock_id: Specify the hardware spinlock id.
+ * @hwlock_mode: The hardware spinlock mode, should be HWLOCK_IRQSTATE,
+ *              HWLOCK_IRQ or 0.
  */
 struct regmap_config {
        const char *name;
@@ -317,6 +364,9 @@ struct regmap_config {
 
        const struct regmap_range_cfg *ranges;
        unsigned int num_ranges;
+
+       unsigned int hwlock_id;
+       unsigned int hwlock_mode;
 };
 
 /**