regmap: allow upshifting register addresses before performing operations
authorMaxime Chevallier <maxime.chevallier@bootlin.com>
Fri, 7 Apr 2023 15:26:04 +0000 (17:26 +0200)
committerMark Brown <broonie@kernel.org>
Fri, 7 Apr 2023 16:28:19 +0000 (17:28 +0100)
Similar to the existing reg_downshift mechanism, that is used to
translate register addresses on busses that have a smaller address
stride, it's also possible to want to upshift register addresses.

Such a case was encountered when network PHYs and PCS that usually sit
on a MDIO bus (16-bits register with a stride of 1) are integrated
directly as memory-mapped devices. Here, the same register layout
defined in 802.3 is used, but the register now have a larger stride.

Introduce a mechanism to also allow upshifting register addresses.
Re-purpose reg_downshift into a more generic, signed reg_shift, whose
sign indicates the direction of the shift. To avoid confusion, also
introduce macros to explicitly indicate if we want to downshift or
upshift.

For bisectability, change any use of reg_downshift to use reg_shift.

Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Tested-by: Colin Foster <colin.foster@in-advantage.com>
Link: https://lore.kernel.org/r/20230407152604.105467-1-maxime.chevallier@bootlin.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/base/regmap/internal.h
drivers/base/regmap/regmap.c
drivers/mfd/ocelot-spi.c
include/linux/regmap.h

index 6361df6f553a4aed400f4248937854edc438812c..9bd0dfd1e2596fc3cff0dd1ace31c8e293fb89f7 100644 (file)
@@ -31,8 +31,8 @@ struct regmap_format {
        size_t buf_size;
        size_t reg_bytes;
        size_t pad_bytes;
-       size_t reg_downshift;
        size_t val_bytes;
+       s8 reg_shift;
        void (*format_write)(struct regmap *map,
                             unsigned int reg, unsigned int val);
        void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
index 473b65b102db8c5cf3c0f82dfd8b821078829b83..db7851f0e3b8cd7542b93a09d9a9224557868fd7 100644 (file)
@@ -814,7 +814,7 @@ struct regmap *__regmap_init(struct device *dev,
 
        map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
        map->format.pad_bytes = config->pad_bits / 8;
-       map->format.reg_downshift = config->reg_downshift;
+       map->format.reg_shift = config->reg_shift;
        map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
        map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
                        config->val_bits + config->pad_bits, 8);
@@ -1679,7 +1679,13 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
 static unsigned int regmap_reg_addr(struct regmap *map, unsigned int reg)
 {
        reg += map->reg_base;
-       return reg >> map->format.reg_downshift;
+
+       if (map->format.reg_shift > 0)
+               reg >>= map->format.reg_shift;
+       else if (map->format.reg_shift < 0)
+               reg <<= -(map->format.reg_shift);
+
+       return reg;
 }
 
 static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
index 2ecd271de2fb960a8604a160813a8bacb16b9071..2d1349a10ca934a6fd7736a4ea372b72bc1db84b 100644 (file)
@@ -125,7 +125,7 @@ static int ocelot_spi_initialize(struct device *dev)
 static const struct regmap_config ocelot_spi_regmap_config = {
        .reg_bits = 24,
        .reg_stride = 4,
-       .reg_downshift = 2,
+       .reg_shift = REGMAP_DOWNSHIFT(2),
        .val_bits = 32,
 
        .write_flag_mask = 0x80,
index 8743b177399ea02b1b45ffc7876418fa9ed94c56..c2b9cc5db8241f614c5c2cf3c5baefea786dbdc8 100644 (file)
@@ -46,6 +46,14 @@ struct sdw_slave;
 #define REGMAP_MDIO_C45_DEVAD_MASK     GENMASK(20, 16)
 #define REGMAP_MDIO_C45_REGNUM_MASK    GENMASK(15, 0)
 
+/*
+ * regmap.reg_shift indicates by how much we must shift registers prior to
+ * performing any operation. It's a signed value, positive numbers means
+ * downshifting the register's address, while negative numbers means upshifting.
+ */
+#define REGMAP_UPSHIFT(s)      (-(s))
+#define REGMAP_DOWNSHIFT(s)    (s)
+
 /* An enum of all the supported cache types */
 enum regcache_type {
        REGCACHE_NONE,
@@ -246,8 +254,9 @@ typedef void (*regmap_unlock)(void *);
  * @reg_stride: The register address stride. Valid register addresses are a
  *              multiple of this value. If set to 0, a value of 1 will be
  *              used.
- * @reg_downshift: The number of bits to downshift the register before
- *                performing any operations.
+ * @reg_shift: The number of bits to shift the register before performing any
+ *            operations. Any positive number will be downshifted, and negative
+ *            values will be upshifted
  * @reg_base: Value to be added to every register address before performing any
  *           operation.
  * @pad_bits: Number of bits of padding between register and value.
@@ -381,7 +390,7 @@ struct regmap_config {
 
        int reg_bits;
        int reg_stride;
-       int reg_downshift;
+       int reg_shift;
        unsigned int reg_base;
        int pad_bits;
        int val_bits;