diff mbox series

[v3,1/3] gpio: regmap: Support few IC specific operations

Message ID 116225e2cee20a8ad793f775c5d98aa4d24318d4.1621863253.git.matti.vaittinen@fi.rohmeurope.com
State Superseded
Headers show
Series gpio: gpio-regmap: Support few custom operations | expand

Commit Message

Vaittinen, Matti May 25, 2021, 12:43 p.m. UTC
The set_config and init_valid_mask GPIO operations are usually very IC
specific. Allow IC drivers to provide these custom operations at
gpio-regmap registration.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>

---
Changelog v3:

IC drivers which need to implement some own GPIO operations are
likely to need data that is provided in gpio-regmap config. Divide
gpio-regmap in GPIO core specific (gpio_chip, ops and internal
book-keeping if any) and public configurations which can be given
(as read-only data) to IC drivers.

Re-use the gpio_regmap_config struct and pass it as such to IC driver
call-backs to avoid polluting interface and IC drivers with small

Changelog v2: (based on suggestions by Michael Walle)
  - drop gpio_regmap_set_drvdata()
  - drop checks and WARN() for pretty much impossible cases
---
 drivers/gpio/gpio-regmap.c  | 189 +++++++++++++++++++++---------------
 include/linux/gpio/regmap.h |  51 +++++++---
 2 files changed, 145 insertions(+), 95 deletions(-)

Comments

kernel test robot May 25, 2021, 2:45 p.m. UTC | #1
Hi Matti,

I love your patch! Yet something to improve:

[auto build test ERROR on c4681547bcce777daf576925a966ffa824edd09d]

url:    https://github.com/0day-ci/linux/commits/Matti-Vaittinen/gpio-gpio-regmap-Support-few-custom-operations/20210525-204554
base:   c4681547bcce777daf576925a966ffa824edd09d
config: powerpc-randconfig-s031-20210525 (attached as .config)
compiler: powerpc64-linux-gcc (GCC) 9.3.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # apt-get install sparse
        # sparse version: v0.6.3-341-g8af24329-dirty
        # https://github.com/0day-ci/linux/commit/fac1c3d2b667e599ee2e3bba357847f9a35f43c7
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Matti-Vaittinen/gpio-gpio-regmap-Support-few-custom-operations/20210525-204554
        git checkout fac1c3d2b667e599ee2e3bba357847f9a35f43c7
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' W=1 ARCH=powerpc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/gpio/gpio-sl28cpld.c: In function 'sl28cpld_gpio_probe':
>> drivers/gpio/gpio-sl28cpld.c:139:25: error: too few arguments to function 'devm_gpio_regmap_register'
     139 |  return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from drivers/gpio/gpio-sl28cpld.c:10:
   include/linux/gpio/regmap.h:105:21: note: declared here
     105 | struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
         |                     ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/gpio/gpio-sl28cpld.c:140:1: error: control reaches end of non-void function [-Werror=return-type]
     140 | }
         | ^
   cc1: some warnings being treated as errors


vim +/devm_gpio_regmap_register +139 drivers/gpio/gpio-sl28cpld.c

b7536d8749e549 Michael Walle 2020-09-14   88  
b7536d8749e549 Michael Walle 2020-09-14   89  static int sl28cpld_gpio_probe(struct platform_device *pdev)
b7536d8749e549 Michael Walle 2020-09-14   90  {
b7536d8749e549 Michael Walle 2020-09-14   91  	struct gpio_regmap_config config = {0};
b7536d8749e549 Michael Walle 2020-09-14   92  	enum sl28cpld_gpio_type type;
b7536d8749e549 Michael Walle 2020-09-14   93  	struct regmap *regmap;
b7536d8749e549 Michael Walle 2020-09-14   94  	u32 base;
b7536d8749e549 Michael Walle 2020-09-14   95  	int ret;
b7536d8749e549 Michael Walle 2020-09-14   96  
b7536d8749e549 Michael Walle 2020-09-14   97  	if (!pdev->dev.parent)
b7536d8749e549 Michael Walle 2020-09-14   98  		return -ENODEV;
b7536d8749e549 Michael Walle 2020-09-14   99  
b7536d8749e549 Michael Walle 2020-09-14  100  	type = (uintptr_t)device_get_match_data(&pdev->dev);
b7536d8749e549 Michael Walle 2020-09-14  101  	if (!type)
b7536d8749e549 Michael Walle 2020-09-14  102  		return -ENODEV;
b7536d8749e549 Michael Walle 2020-09-14  103  
b7536d8749e549 Michael Walle 2020-09-14  104  	ret = device_property_read_u32(&pdev->dev, "reg", &base);
b7536d8749e549 Michael Walle 2020-09-14  105  	if (ret)
b7536d8749e549 Michael Walle 2020-09-14  106  		return -EINVAL;
b7536d8749e549 Michael Walle 2020-09-14  107  
b7536d8749e549 Michael Walle 2020-09-14  108  	regmap = dev_get_regmap(pdev->dev.parent, NULL);
b7536d8749e549 Michael Walle 2020-09-14  109  	if (!regmap)
b7536d8749e549 Michael Walle 2020-09-14  110  		return -ENODEV;
b7536d8749e549 Michael Walle 2020-09-14  111  
b7536d8749e549 Michael Walle 2020-09-14  112  	config.regmap = regmap;
b7536d8749e549 Michael Walle 2020-09-14  113  	config.parent = &pdev->dev;
b7536d8749e549 Michael Walle 2020-09-14  114  	config.ngpio = 8;
b7536d8749e549 Michael Walle 2020-09-14  115  
b7536d8749e549 Michael Walle 2020-09-14  116  	switch (type) {
b7536d8749e549 Michael Walle 2020-09-14  117  	case SL28CPLD_GPIO:
b7536d8749e549 Michael Walle 2020-09-14  118  		config.reg_dat_base = base + GPIO_REG_IN;
b7536d8749e549 Michael Walle 2020-09-14  119  		config.reg_set_base = base + GPIO_REG_OUT;
b7536d8749e549 Michael Walle 2020-09-14  120  		/* reg_dir_out_base might be zero */
b7536d8749e549 Michael Walle 2020-09-14  121  		config.reg_dir_out_base = GPIO_REGMAP_ADDR(base + GPIO_REG_DIR);
b7536d8749e549 Michael Walle 2020-09-14  122  
b7536d8749e549 Michael Walle 2020-09-14  123  		/* This type supports interrupts */
b7536d8749e549 Michael Walle 2020-09-14  124  		ret = sl28cpld_gpio_irq_init(pdev, base, &config);
b7536d8749e549 Michael Walle 2020-09-14  125  		if (ret)
b7536d8749e549 Michael Walle 2020-09-14  126  			return ret;
b7536d8749e549 Michael Walle 2020-09-14  127  		break;
b7536d8749e549 Michael Walle 2020-09-14  128  	case SL28CPLD_GPO:
b7536d8749e549 Michael Walle 2020-09-14  129  		config.reg_set_base = base + GPO_REG_OUT;
b7536d8749e549 Michael Walle 2020-09-14  130  		break;
b7536d8749e549 Michael Walle 2020-09-14  131  	case SL28CPLD_GPI:
b7536d8749e549 Michael Walle 2020-09-14  132  		config.reg_dat_base = base + GPI_REG_IN;
b7536d8749e549 Michael Walle 2020-09-14  133  		break;
b7536d8749e549 Michael Walle 2020-09-14  134  	default:
b7536d8749e549 Michael Walle 2020-09-14  135  		dev_err(&pdev->dev, "unknown type %d\n", type);
b7536d8749e549 Michael Walle 2020-09-14  136  		return -ENODEV;
b7536d8749e549 Michael Walle 2020-09-14  137  	}
b7536d8749e549 Michael Walle 2020-09-14  138  
b7536d8749e549 Michael Walle 2020-09-14 @139  	return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
b7536d8749e549 Michael Walle 2020-09-14  140  }
b7536d8749e549 Michael Walle 2020-09-14  141  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot May 25, 2021, 3:31 p.m. UTC | #2
Hi Matti,

I love your patch! Yet something to improve:

[auto build test ERROR on c4681547bcce777daf576925a966ffa824edd09d]

url:    https://github.com/0day-ci/linux/commits/Matti-Vaittinen/gpio-gpio-regmap-Support-few-custom-operations/20210525-204554
base:   c4681547bcce777daf576925a966ffa824edd09d
config: arm64-randconfig-r014-20210525 (attached as .config)
compiler: aarch64-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/fac1c3d2b667e599ee2e3bba357847f9a35f43c7
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Matti-Vaittinen/gpio-gpio-regmap-Support-few-custom-operations/20210525-204554
        git checkout fac1c3d2b667e599ee2e3bba357847f9a35f43c7
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arm64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/pinctrl/bcm/pinctrl-bcm63xx.c: In function 'bcm63xx_gpio_probe':
>> drivers/pinctrl/bcm/pinctrl-bcm63xx.c:59:5: error: 'struct gpio_regmap_config' has no member named 'reg_mask_xlate'
      59 |  grc.reg_mask_xlate = bcm63xx_reg_mask_xlate;
         |     ^
>> drivers/pinctrl/bcm/pinctrl-bcm63xx.c:61:25: error: too few arguments to function 'devm_gpio_regmap_register'
      61 |  return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &grc));
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from drivers/pinctrl/bcm/pinctrl-bcm63xx.c:9:
   include/linux/gpio/regmap.h:105:21: note: declared here
     105 | struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
         |                     ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/pinctrl/bcm/pinctrl-bcm63xx.c:62:1: error: control reaches end of non-void function [-Werror=return-type]
      62 | }
         | ^
   cc1: some warnings being treated as errors


vim +59 drivers/pinctrl/bcm/pinctrl-bcm63xx.c

132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  44  
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  45  static int bcm63xx_gpio_probe(struct device *dev, struct device_node *node,
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  46  			      const struct bcm63xx_pinctrl_soc *soc,
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  47  			      struct bcm63xx_pinctrl *pc)
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  48  {
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  49  	struct gpio_regmap_config grc = {0};
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  50  
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  51  	grc.parent = dev;
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  52  	grc.fwnode = &node->fwnode;
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  53  	grc.ngpio = soc->ngpios;
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  54  	grc.ngpio_per_reg = BCM63XX_BANK_GPIOS;
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  55  	grc.regmap = pc->regs;
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  56  	grc.reg_dat_base = BCM63XX_DATA_REG;
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  57  	grc.reg_dir_out_base = BCM63XX_DIROUT_REG;
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  58  	grc.reg_set_base = BCM63XX_DATA_REG;
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24 @59  	grc.reg_mask_xlate = bcm63xx_reg_mask_xlate;
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  60  
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24 @61  	return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &grc));
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  62  }
132f95016db0a0 Álvaro Fernández Rojas 2021-03-24  63  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index 134cedf151a7..4f0903d1acd5 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -12,23 +12,13 @@ 
 #include <linux/regmap.h>
 
 struct gpio_regmap {
-	struct device *parent;
-	struct regmap *regmap;
+	struct gpio_regmap_config config;
+	struct gpio_regmap_ops *ops;
 	struct gpio_chip gpio_chip;
 
-	int reg_stride;
-	int ngpio_per_reg;
-	unsigned int reg_dat_base;
-	unsigned int reg_set_base;
-	unsigned int reg_clr_base;
-	unsigned int reg_dir_in_base;
-	unsigned int reg_dir_out_base;
-
-	int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
-			      unsigned int offset, unsigned int *reg,
-			      unsigned int *mask);
-
-	void *driver_data;
+	int (*reg_mask_xlate)(const struct gpio_regmap_config *config,
+			      unsigned int base, unsigned int offset,
+			      unsigned int *reg, unsigned int *mask);
 };
 
 static unsigned int gpio_regmap_addr(unsigned int addr)
@@ -39,14 +29,35 @@  static unsigned int gpio_regmap_addr(unsigned int addr)
 	return addr;
 }
 
-static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
+static int regmap_gpio_init_valid_mask(struct gpio_chip *gc,
+					unsigned long *valid_mask,
+					unsigned int ngpios)
+{
+	struct gpio_regmap *gpio;
+
+	gpio = gpiochip_get_data(gc);
+
+	return gpio->ops->init_valid_mask(&gpio->config, valid_mask, ngpios);
+}
+
+static int gpio_regmap_set_config(struct gpio_chip *gc, unsigned int offset,
+				  unsigned long config)
+{
+	struct gpio_regmap *gpio;
+
+	gpio = gpiochip_get_data(gc);
+
+	return gpio->ops->set_config(&gpio->config, offset, config);
+}
+
+static int gpio_regmap_simple_xlate(const struct gpio_regmap_config *config,
 				    unsigned int base, unsigned int offset,
 				    unsigned int *reg, unsigned int *mask)
 {
-	unsigned int line = offset % gpio->ngpio_per_reg;
-	unsigned int stride = offset / gpio->ngpio_per_reg;
+	unsigned int line = offset % config->ngpio_per_reg;
+	unsigned int stride = offset / config->ngpio_per_reg;
 
-	*reg = base + stride * gpio->reg_stride;
+	*reg = base + stride * config->reg_stride;
 	*mask = BIT(line);
 
 	return 0;
@@ -55,20 +66,21 @@  static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
 static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
 {
 	struct gpio_regmap *gpio = gpiochip_get_data(chip);
+	struct gpio_regmap_config *config = &gpio->config;
 	unsigned int base, val, reg, mask;
 	int ret;
 
 	/* we might not have an output register if we are input only */
-	if (gpio->reg_dat_base)
-		base = gpio_regmap_addr(gpio->reg_dat_base);
+	if (config->reg_dat_base)
+		base = gpio_regmap_addr(config->reg_dat_base);
 	else
-		base = gpio_regmap_addr(gpio->reg_set_base);
+		base = gpio_regmap_addr(config->reg_set_base);
 
-	ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+	ret = gpio->reg_mask_xlate(config, base, offset, &reg, &mask);
 	if (ret)
 		return ret;
 
-	ret = regmap_read(gpio->regmap, reg, &val);
+	ret = regmap_read(config->regmap, reg, &val);
 	if (ret)
 		return ret;
 
@@ -79,53 +91,56 @@  static void gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
 			    int val)
 {
 	struct gpio_regmap *gpio = gpiochip_get_data(chip);
-	unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
+	struct gpio_regmap_config *config = &gpio->config;
+	unsigned int base = gpio_regmap_addr(config->reg_set_base);
 	unsigned int reg, mask;
 
-	gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+	gpio->reg_mask_xlate(config, base, offset, &reg, &mask);
 	if (val)
-		regmap_update_bits(gpio->regmap, reg, mask, mask);
+		regmap_update_bits(config->regmap, reg, mask, mask);
 	else
-		regmap_update_bits(gpio->regmap, reg, mask, 0);
+		regmap_update_bits(config->regmap, reg, mask, 0);
 }
 
 static void gpio_regmap_set_with_clear(struct gpio_chip *chip,
 				       unsigned int offset, int val)
 {
 	struct gpio_regmap *gpio = gpiochip_get_data(chip);
+	struct gpio_regmap_config *config = &gpio->config;
 	unsigned int base, reg, mask;
 
 	if (val)
-		base = gpio_regmap_addr(gpio->reg_set_base);
+		base = gpio_regmap_addr(config->reg_set_base);
 	else
-		base = gpio_regmap_addr(gpio->reg_clr_base);
+		base = gpio_regmap_addr(config->reg_clr_base);
 
-	gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
-	regmap_write(gpio->regmap, reg, mask);
+	gpio->reg_mask_xlate(config, base, offset, &reg, &mask);
+	regmap_write(config->regmap, reg, mask);
 }
 
 static int gpio_regmap_get_direction(struct gpio_chip *chip,
 				     unsigned int offset)
 {
 	struct gpio_regmap *gpio = gpiochip_get_data(chip);
+	struct gpio_regmap_config *config = &gpio->config;
 	unsigned int base, val, reg, mask;
 	int invert, ret;
 
-	if (gpio->reg_dir_out_base) {
-		base = gpio_regmap_addr(gpio->reg_dir_out_base);
+	if (config->reg_dir_out_base) {
+		base = gpio_regmap_addr(config->reg_dir_out_base);
 		invert = 0;
-	} else if (gpio->reg_dir_in_base) {
-		base = gpio_regmap_addr(gpio->reg_dir_in_base);
+	} else if (config->reg_dir_in_base) {
+		base = gpio_regmap_addr(config->reg_dir_in_base);
 		invert = 1;
 	} else {
 		return -EOPNOTSUPP;
 	}
 
-	ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+	ret = gpio->reg_mask_xlate(config, base, offset, &reg, &mask);
 	if (ret)
 		return ret;
 
-	ret = regmap_read(gpio->regmap, reg, &val);
+	ret = regmap_read(config->regmap, reg, &val);
 	if (ret)
 		return ret;
 
@@ -139,20 +154,21 @@  static int gpio_regmap_set_direction(struct gpio_chip *chip,
 				     unsigned int offset, bool output)
 {
 	struct gpio_regmap *gpio = gpiochip_get_data(chip);
+	struct gpio_regmap_config *config = &gpio->config;
 	unsigned int base, val, reg, mask;
 	int invert, ret;
 
-	if (gpio->reg_dir_out_base) {
-		base = gpio_regmap_addr(gpio->reg_dir_out_base);
+	if (config->reg_dir_out_base) {
+		base = gpio_regmap_addr(config->reg_dir_out_base);
 		invert = 0;
-	} else if (gpio->reg_dir_in_base) {
-		base = gpio_regmap_addr(gpio->reg_dir_in_base);
+	} else if (config->reg_dir_in_base) {
+		base = gpio_regmap_addr(config->reg_dir_in_base);
 		invert = 1;
 	} else {
 		return -EOPNOTSUPP;
 	}
 
-	ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+	ret = gpio->reg_mask_xlate(config, base, offset, &reg, &mask);
 	if (ret)
 		return ret;
 
@@ -161,7 +177,7 @@  static int gpio_regmap_set_direction(struct gpio_chip *chip,
 	else
 		val = output ? mask : 0;
 
-	return regmap_update_bits(gpio->regmap, reg, mask, val);
+	return regmap_update_bits(config->regmap, reg, mask, val);
 }
 
 static int gpio_regmap_direction_input(struct gpio_chip *chip,
@@ -178,25 +194,18 @@  static int gpio_regmap_direction_output(struct gpio_chip *chip,
 	return gpio_regmap_set_direction(chip, offset, true);
 }
 
-void gpio_regmap_set_drvdata(struct gpio_regmap *gpio, void *data)
-{
-	gpio->driver_data = data;
-}
-EXPORT_SYMBOL_GPL(gpio_regmap_set_drvdata);
-
-void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio)
-{
-	return gpio->driver_data;
-}
-EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata);
-
 /**
  * gpio_regmap_register() - Register a generic regmap GPIO controller
- * @config: configuration for gpio_regmap
+ * @config:	configuration for gpio_regmap
+ * @ops:	Provide pointer IC specific functions to handle needs where
+ *		the standard gpio_regmap does not provide generic functions
+ *		or provided functions do not fit the IC. Can be set NULL if
+ *		no IC specific operations are required.
  *
  * Return: A pointer to the registered gpio_regmap or ERR_PTR error value.
  */
-struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config)
+struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config,
+					 const struct gpio_regmap_ops *ops)
 {
 	struct gpio_regmap *gpio;
 	struct gpio_chip *chip;
@@ -225,26 +234,40 @@  struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
 	if (!gpio)
 		return ERR_PTR(-ENOMEM);
 
-	gpio->parent = config->parent;
-	gpio->regmap = config->regmap;
-	gpio->ngpio_per_reg = config->ngpio_per_reg;
-	gpio->reg_stride = config->reg_stride;
-	gpio->reg_mask_xlate = config->reg_mask_xlate;
-	gpio->reg_dat_base = config->reg_dat_base;
-	gpio->reg_set_base = config->reg_set_base;
-	gpio->reg_clr_base = config->reg_clr_base;
-	gpio->reg_dir_in_base = config->reg_dir_in_base;
-	gpio->reg_dir_out_base = config->reg_dir_out_base;
+	gpio->config = *config;
+	if (ops) {
+		/*
+		 * We could avoid ops struct allocation if just the
+		 * xlate is given as it is used directly from gpio_regmap.
+		 * I don't think that optimization is worth the hassle as
+		 * there may not be many cases with custom xlate and no other
+		 * ops. We can change this if I am wrong.
+		 */
+		gpio->ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+		if (!gpio->ops) {
+			ret = -ENOMEM;
+			goto err_free_gpio;
+		}
+		*gpio->ops = *ops;
+	}
 
 	/* if not set, assume there is only one register */
-	if (!gpio->ngpio_per_reg)
-		gpio->ngpio_per_reg = config->ngpio;
+	if (!gpio->config.ngpio_per_reg)
+		gpio->config.ngpio_per_reg = config->ngpio;
 
 	/* if not set, assume they are consecutive */
-	if (!gpio->reg_stride)
-		gpio->reg_stride = 1;
+	if (!gpio->config.reg_stride)
+		gpio->config.reg_stride = 1;
 
-	if (!gpio->reg_mask_xlate)
+	/*
+	 * Dublicate the reg_mask_xlate to gpio_regmap so we don't need to
+	 * always check if ops is populated and reg_mask_xlate is given
+	 * - or allocate whole ops struct just for unconditional
+	 * reg_mask_xlate if no ops are required.
+	 */
+	if (ops && ops->reg_mask_xlate)
+		gpio->reg_mask_xlate = ops->reg_mask_xlate;
+	else
 		gpio->reg_mask_xlate = gpio_regmap_simple_xlate;
 
 	chip = &gpio->gpio_chip;
@@ -253,7 +276,12 @@  struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
 	chip->ngpio = config->ngpio;
 	chip->names = config->names;
 	chip->label = config->label ?: dev_name(config->parent);
-
+	if (gpio->ops) {
+		if (gpio->ops->set_config)
+			chip->set_config = gpio_regmap_set_config;
+		if (gpio->ops->init_valid_mask)
+			chip->init_valid_mask = regmap_gpio_init_valid_mask;
+	}
 #if defined(CONFIG_OF_GPIO)
 	/* gpiolib will use of_node of the parent if chip->of_node is NULL */
 	chip->of_node = to_of_node(config->fwnode);
@@ -269,12 +297,12 @@  struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
 	chip->can_sleep = true;
 
 	chip->get = gpio_regmap_get;
-	if (gpio->reg_set_base && gpio->reg_clr_base)
+	if (gpio->config.reg_set_base && gpio->config.reg_clr_base)
 		chip->set = gpio_regmap_set_with_clear;
-	else if (gpio->reg_set_base)
+	else if (gpio->config.reg_set_base)
 		chip->set = gpio_regmap_set;
 
-	if (gpio->reg_dir_in_base || gpio->reg_dir_out_base) {
+	if (gpio->config.reg_dir_in_base || gpio->config.reg_dir_out_base) {
 		chip->get_direction = gpio_regmap_get_direction;
 		chip->direction_input = gpio_regmap_direction_input;
 		chip->direction_output = gpio_regmap_direction_output;
@@ -295,6 +323,7 @@  struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
 err_remove_gpiochip:
 	gpiochip_remove(chip);
 err_free_gpio:
+	kfree(gpio->ops);
 	kfree(gpio);
 	return ERR_PTR(ret);
 }
@@ -307,6 +336,7 @@  EXPORT_SYMBOL_GPL(gpio_regmap_register);
 void gpio_regmap_unregister(struct gpio_regmap *gpio)
 {
 	gpiochip_remove(&gpio->gpio_chip);
+	kfree(gpio->ops);
 	kfree(gpio);
 }
 EXPORT_SYMBOL_GPL(gpio_regmap_unregister);
@@ -328,7 +358,8 @@  static void devm_gpio_regmap_unregister(struct device *dev, void *res)
  * Return: A pointer to the registered gpio_regmap or ERR_PTR error value.
  */
 struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
-					      const struct gpio_regmap_config *config)
+					      const struct gpio_regmap_config *config,
+					      const struct gpio_regmap_ops *ops)
 {
 	struct gpio_regmap **ptr, *gpio;
 
@@ -337,7 +368,7 @@  struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
 	if (!ptr)
 		return ERR_PTR(-ENOMEM);
 
-	gpio = gpio_regmap_register(config);
+	gpio = gpio_regmap_register(config, ops);
 	if (!IS_ERR(gpio)) {
 		*ptr = gpio;
 		devres_add(dev, ptr);
diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h
index 334dd928042b..ba1a4b14969b 100644
--- a/include/linux/gpio/regmap.h
+++ b/include/linux/gpio/regmap.h
@@ -6,12 +6,39 @@ 
 struct device;
 struct fwnode_handle;
 struct gpio_regmap;
+struct gpio_regmap_config;
 struct irq_domain;
 struct regmap;
 
 #define GPIO_REGMAP_ADDR_ZERO ((unsigned int)(-1))
 #define GPIO_REGMAP_ADDR(addr) ((addr) ? : GPIO_REGMAP_ADDR_ZERO)
 
+/**
+ * struct gpio_regmap_ops - Custom operations for regmap_gpio.
+ * @reg_mask_xlate:     Translates base address and GPIO
+ *			offset to a register/bitmask pair. If not given the
+ *			default gpio_regmap_simple_xlate() is used.
+ * @set_config:		Hook for all kinds of settings. Uses the same packed
+ *			config format as generic pinconf.
+ * @init_valid_mask:	Routine to initialize @valid_mask, to be used if not
+ *			all GPIOs are valid.
+ *
+ * Provide ustom operations for the regmap_gpio controller where the
+ * standard regmap_gpio operations are insufficient.
+ * The ->reg_mask_xlate translates a given base address and GPIO offset to
+ * register and mask pair. The base address is one of the given register
+ * base addresses in the gpio_regmap_config structure.
+ */
+struct gpio_regmap_ops {
+	int (*reg_mask_xlate)(const struct gpio_regmap_config *config,
+			      unsigned int base, unsigned int offset,
+			      unsigned int *reg, unsigned int *mask);
+	int (*set_config)(const struct gpio_regmap_config *regmap_config,
+			  unsigned int offset, unsigned long config);
+	int (*init_valid_mask)(const struct gpio_regmap_config *config,
+			       unsigned long *valid_mask, unsigned int ngpios);
+};
+
 /**
  * struct gpio_regmap_config - Description of a generic regmap gpio_chip.
  * @parent:		The parent device
@@ -33,14 +60,9 @@  struct regmap;
  * @ngpio_per_reg:	Number of GPIOs per register
  * @irq_domain:		(Optional) IRQ domain if the controller is
  *			interrupt-capable
- * @reg_mask_xlate:     (Optional) Translates base address and GPIO
- *			offset to a register/bitmask pair. If not
- *			given the default gpio_regmap_simple_xlate()
- *			is used.
- *
- * The ->reg_mask_xlate translates a given base address and GPIO offset to
- * register and mask pair. The base address is one of the given register
- * base addresses in this structure.
+ * @drvdata:		(Optional) Pointer to IC specific data which is
+ *			not used by gpio-remap but is provided "as is" to
+ *			the driver callback(s).
  *
  * Although all register base addresses are marked as optional, there are
  * several rules:
@@ -74,17 +96,14 @@  struct gpio_regmap_config {
 	int reg_stride;
 	int ngpio_per_reg;
 	struct irq_domain *irq_domain;
-
-	int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
-			      unsigned int offset, unsigned int *reg,
-			      unsigned int *mask);
+	void *drvdata;
 };
 
-struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config);
+struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config,
+					 const struct gpio_regmap_ops *ops);
 void gpio_regmap_unregister(struct gpio_regmap *gpio);
 struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
-					      const struct gpio_regmap_config *config);
-void gpio_regmap_set_drvdata(struct gpio_regmap *gpio, void *data);
-void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio);
+					      const struct gpio_regmap_config *config,
+					      const struct gpio_regmap_ops *ops);
 
 #endif /* _LINUX_GPIO_REGMAP_H */