Merge series "Use raw spinlocks in the ls-extirq driver" from Vladimir Oltean <vladim...
authorMark Brown <broonie@kernel.org>
Thu, 26 Aug 2021 12:40:35 +0000 (13:40 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 26 Aug 2021 12:40:35 +0000 (13:40 +0100)
The ls-extirq irqchip driver accesses regmap inside its implementation
of the struct irq_chip :: irq_set_type method, and currently regmap
only knows to lock using normal spinlocks. But the method above wants
raw spinlock context, so this isn't going to work and triggers a
"[ BUG: Invalid wait context ]" splat.

The best we can do given the arrangement of the code is to patch regmap
and the syscon driver: regmap to support raw spinlocks, and syscon to
request them on behalf of its ls-extirq consumer.

Link: https://lore.kernel.org/lkml/20210825135438.ubcuxm5vctt6ne2q@skbuf/T/#u
Vladimir Oltean (2):
  regmap: teach regmap to use raw spinlocks if requested in the config
  mfd: syscon: request a regmap with raw spinlocks for some devices

 drivers/base/regmap/internal.h |  4 ++++
 drivers/base/regmap/regmap.c   | 35 +++++++++++++++++++++++++++++-----
 drivers/mfd/syscon.c           | 16 ++++++++++++++++
 include/linux/regmap.h         |  2 ++
 4 files changed, 52 insertions(+), 5 deletions(-)

--
2.25.1

base-commit: 6efb943b8616ec53a5e444193dccf1af9ad627b5

1  2 
drivers/base/regmap/regmap.c
include/linux/regmap.h

@@@ -243,16 -243,6 +243,16 @@@ static void regmap_format_7_9_write(str
        *out = cpu_to_be16((reg << 9) | val);
  }
  
 +static void regmap_format_7_17_write(struct regmap *map,
 +                                  unsigned int reg, unsigned int val)
 +{
 +      u8 *out = map->work_buf;
 +
 +      out[2] = val;
 +      out[1] = val >> 8;
 +      out[0] = (val >> 16) | (reg << 1);
 +}
 +
  static void regmap_format_10_14_write(struct regmap *map,
                                    unsigned int reg, unsigned int val)
  {
@@@ -533,6 -523,23 +533,23 @@@ __releases(&map->spinlock
        spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags);
  }
  
+ static void regmap_lock_raw_spinlock(void *__map)
+ __acquires(&map->raw_spinlock)
+ {
+       struct regmap *map = __map;
+       unsigned long flags;
+       raw_spin_lock_irqsave(&map->raw_spinlock, flags);
+       map->raw_spinlock_flags = flags;
+ }
+ static void regmap_unlock_raw_spinlock(void *__map)
+ __releases(&map->raw_spinlock)
+ {
+       struct regmap *map = __map;
+       raw_spin_unlock_irqrestore(&map->raw_spinlock, map->raw_spinlock_flags);
+ }
  static void dev_get_regmap_release(struct device *dev, void *res)
  {
        /*
@@@ -770,11 -777,19 +787,19 @@@ struct regmap *__regmap_init(struct dev
        } else {
                if ((bus && bus->fast_io) ||
                    config->fast_io) {
-                       spin_lock_init(&map->spinlock);
-                       map->lock = regmap_lock_spinlock;
-                       map->unlock = regmap_unlock_spinlock;
-                       lockdep_set_class_and_name(&map->spinlock,
-                                                  lock_key, lock_name);
+                       if (config->use_raw_spinlock) {
+                               raw_spin_lock_init(&map->raw_spinlock);
+                               map->lock = regmap_lock_raw_spinlock;
+                               map->unlock = regmap_unlock_raw_spinlock;
+                               lockdep_set_class_and_name(&map->raw_spinlock,
+                                                          lock_key, lock_name);
+                       } else {
+                               spin_lock_init(&map->spinlock);
+                               map->lock = regmap_lock_spinlock;
+                               map->unlock = regmap_unlock_spinlock;
+                               lockdep_set_class_and_name(&map->spinlock,
+                                                          lock_key, lock_name);
+                       }
                } else {
                        mutex_init(&map->mutex);
                        map->lock = regmap_lock_mutex;
                case 9:
                        map->format.format_write = regmap_format_7_9_write;
                        break;
 +              case 17:
 +                      map->format.format_write = regmap_format_7_17_write;
 +                      break;
                default:
                        goto err_hwlock;
                }
@@@ -1126,10 -1138,10 +1151,10 @@@ skip_format_initialization
                /* Make sure, that this register range has no selector
                   or data window within its boundary */
                for (j = 0; j < config->num_ranges; j++) {
 -                      unsigned sel_reg = config->ranges[j].selector_reg;
 -                      unsigned win_min = config->ranges[j].window_start;
 -                      unsigned win_max = win_min +
 -                                         config->ranges[j].window_len - 1;
 +                      unsigned int sel_reg = config->ranges[j].selector_reg;
 +                      unsigned int win_min = config->ranges[j].window_start;
 +                      unsigned int win_max = win_min +
 +                                             config->ranges[j].window_len - 1;
  
                        /* Allow data window inside its own virtual range */
                        if (j == i)
@@@ -1298,7 -1310,7 +1323,7 @@@ EXPORT_SYMBOL_GPL(devm_regmap_field_all
   */
  int regmap_field_bulk_alloc(struct regmap *regmap,
                            struct regmap_field **rm_field,
 -                          struct reg_field *reg_field,
 +                          const struct reg_field *reg_field,
                            int num_fields)
  {
        struct regmap_field *rf;
@@@ -1334,7 -1346,7 +1359,7 @@@ EXPORT_SYMBOL_GPL(regmap_field_bulk_all
  int devm_regmap_field_bulk_alloc(struct device *dev,
                                 struct regmap *regmap,
                                 struct regmap_field **rm_field,
 -                               struct reg_field *reg_field,
 +                               const struct reg_field *reg_field,
                                 int num_fields)
  {
        struct regmap_field *rf;
@@@ -1509,8 -1521,6 +1534,8 @@@ void regmap_exit(struct regmap *map
                mutex_destroy(&map->mutex);
        kfree_const(map->name);
        kfree(map->patch);
 +      if (map->bus && map->bus->free_on_exit)
 +              kfree(map->bus);
        kfree(map);
  }
  EXPORT_SYMBOL_GPL(regmap_exit);
@@@ -1667,7 -1677,7 +1692,7 @@@ static int _regmap_raw_write_impl(struc
                        if (ret) {
                                dev_err(map->dev,
                                        "Error in caching of register: %x ret: %d\n",
 -                                      reg + i, ret);
 +                                      reg + regmap_get_offset(map, i), ret);
                                return ret;
                        }
                }
diff --combined include/linux/regmap.h
@@@ -27,7 -27,6 +27,7 @@@ struct device_node
  struct i2c_client;
  struct i3c_device;
  struct irq_domain;
 +struct mdio_device;
  struct slim_device;
  struct spi_device;
  struct spmi_device;
@@@ -344,6 -343,7 +344,7 @@@ typedef void (*regmap_unlock)(void *)
   * @ranges: Array of configuration entries for virtual address ranges.
   * @num_ranges: Number of range configuration entries.
   * @use_hwlock: Indicate if a hardware spinlock should be used.
+  * @use_raw_spinlock: Indicate if a raw spinlock should be used.
   * @hwlock_id: Specify the hardware spinlock id.
   * @hwlock_mode: The hardware spinlock mode, should be HWLOCK_IRQSTATE,
   *             HWLOCK_IRQ or 0.
@@@ -403,6 -403,7 +404,7 @@@ struct regmap_config 
        unsigned int num_ranges;
  
        bool use_hwlock;
+       bool use_raw_spinlock;
        unsigned int hwlock_id;
        unsigned int hwlock_mode;
  
@@@ -503,7 -504,6 +505,7 @@@ typedef void (*regmap_hw_free_context)(
   *     DEFAULT, BIG is assumed.
   * @max_raw_read: Max raw read size that can be used on the bus.
   * @max_raw_write: Max raw write size that can be used on the bus.
 + * @free_on_exit: kfree this on exit of regmap
   */
  struct regmap_bus {
        bool fast_io;
        enum regmap_endian val_format_endian_default;
        size_t max_raw_read;
        size_t max_raw_write;
 +      bool free_on_exit;
  };
  
  /*
@@@ -541,10 -540,6 +543,10 @@@ struct regmap *__regmap_init_i2c(struc
                                 const struct regmap_config *config,
                                 struct lock_class_key *lock_key,
                                 const char *lock_name);
 +struct regmap *__regmap_init_mdio(struct mdio_device *mdio_dev,
 +                               const struct regmap_config *config,
 +                               struct lock_class_key *lock_key,
 +                               const char *lock_name);
  struct regmap *__regmap_init_sccb(struct i2c_client *i2c,
                                  const struct regmap_config *config,
                                  struct lock_class_key *lock_key,
@@@ -601,10 -596,6 +603,10 @@@ struct regmap *__devm_regmap_init_i2c(s
                                      const struct regmap_config *config,
                                      struct lock_class_key *lock_key,
                                      const char *lock_name);
 +struct regmap *__devm_regmap_init_mdio(struct mdio_device *mdio_dev,
 +                                    const struct regmap_config *config,
 +                                    struct lock_class_key *lock_key,
 +                                    const char *lock_name);
  struct regmap *__devm_regmap_init_sccb(struct i2c_client *i2c,
                                       const struct regmap_config *config,
                                       struct lock_class_key *lock_key,
@@@ -709,19 -700,6 +711,19 @@@ int regmap_attach_dev(struct device *de
                                i2c, config)
  
  /**
 + * regmap_init_mdio() - Initialise register map
 + *
 + * @mdio_dev: Device that will be interacted with
 + * @config: Configuration for register map
 + *
 + * The return value will be an ERR_PTR() on error or a valid pointer to
 + * a struct regmap.
 + */
 +#define regmap_init_mdio(mdio_dev, config)                            \
 +      __regmap_lockdep_wrapper(__regmap_init_mdio, #config,           \
 +                              mdio_dev, config)
 +
 +/**
   * regmap_init_sccb() - Initialise register map
   *
   * @i2c: Device that will be interacted with
@@@ -913,20 -891,6 +915,20 @@@ bool regmap_ac97_default_volatile(struc
                                i2c, config)
  
  /**
 + * devm_regmap_init_mdio() - Initialise managed register map
 + *
 + * @mdio_dev: Device that will be interacted with
 + * @config: Configuration for register map
 + *
 + * The return value will be an ERR_PTR() on error or a valid pointer
 + * to a struct regmap.  The regmap will be automatically freed by the
 + * device management code.
 + */
 +#define devm_regmap_init_mdio(mdio_dev, config)                               \
 +      __regmap_lockdep_wrapper(__devm_regmap_init_mdio, #config,      \
 +                              mdio_dev, config)
 +
 +/**
   * devm_regmap_init_sccb() - Initialise managed register map
   *
   * @i2c: Device that will be interacted with
@@@ -1269,13 -1233,12 +1271,13 @@@ void devm_regmap_field_free(struct devi
  
  int regmap_field_bulk_alloc(struct regmap *regmap,
                             struct regmap_field **rm_field,
 -                           struct reg_field *reg_field,
 +                           const struct reg_field *reg_field,
                             int num_fields);
  void regmap_field_bulk_free(struct regmap_field *field);
  int devm_regmap_field_bulk_alloc(struct device *dev, struct regmap *regmap,
                                 struct regmap_field **field,
 -                               struct reg_field *reg_field, int num_fields);
 +                               const struct reg_field *reg_field,
 +                               int num_fields);
  void devm_regmap_field_bulk_free(struct device *dev,
                                 struct regmap_field *field);
  
@@@ -1450,7 -1413,6 +1452,7 @@@ struct regmap_irq_sub_irq_map 
   * @not_fixed_stride: Used when chip peripherals are not laid out with fixed
   *                  stride. Must be used with sub_reg_offsets containing the
   *                  offsets to each peripheral.
 + * @status_invert: Inverted status register: cleared bits are active interrupts.
   * @runtime_pm:  Hold a runtime PM lock on the device when accessing it.
   *
   * @num_regs:    Number of registers in each control bank.
@@@ -1503,7 -1465,6 +1505,7 @@@ struct regmap_irq_chip 
        bool type_in_mask:1;
        bool clear_on_unmask:1;
        bool not_fixed_stride:1;
 +      bool status_invert:1;
  
        int num_regs;