diff mbox series

[v3,09/10] gpio: bd73800: Support ROHM BD73800 PMIC GPIOs

Message ID 5eb294e68b8d5d2c1d3a12b14a55dd2a9c075a09.1788346553.git.mazziesaccount@gmail.com
State New
Headers show
Series Support ROHM BD73800 | expand

Commit Message

Matti Vaittinen Sept. 2, 2026, 11:07 a.m. UTC
From: Matti Vaittinen <mazziesaccount@gmail.com>

The ROHM BD73800 PMIC has 4 pins (named GPIO1, CLKOUT, FAULT_B and
EXTEN_OUT) which might have been set to operate as a GPI or GPO when OTP
(One Time Programmable memory) is written at device manufacturing.
Support the GPI/GPO use-case via GPIO framework.

The default OTP for these pins is to not use any of them as GPI or GPO.
(The GPIO1 defaults as an ADC input regardless the naming). Hence the
driver assumes none of these pins is a GPI/GPO unless explicitly pointed
as GPI or GPO via device tree.

Furthermore, pin's direction can't be changed after OTP configuration is
done. Also the default drive type for a GPO (CMOS / Open Drain) is set
by the OTP configuration. The BD73800 has a set of undocumented test
registers which should allow changing the drive type. Access to the test
register area or the test registers aren't documented and so this driver
does not support configuring the drive type even though it might be
doable.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>

---
Revision history:
 v2 => v3:
  - Use gpio-regmap as was suggested by Linus W.
 v1 => v2:
  - Return -ENODEV when no GPIO pins are found
  - Drop useless comment
  - Use 'data' instead of 'g' as a name of the private data struct. Do so
    consistently (change also the 'bdgpio' to 'data').
  - Use device_property_... instead of the fwnode_property_...
  - fix fault_b pin property name (re-use existing name)
---
 drivers/gpio/Kconfig        |  12 +++
 drivers/gpio/Makefile       |   1 +
 drivers/gpio/gpio-bd73800.c | 209 ++++++++++++++++++++++++++++++++++++
 3 files changed, 222 insertions(+)
 create mode 100644 drivers/gpio/gpio-bd73800.c

Comments

Linus Walleij Sept. 2, 2026, 12:39 p.m. UTC | #1
Hi Matti,

thanks for your patch!

On Wed, Sep 2, 2026 at 1:07 PM Matti Vaittinen
<matti.vaittinen@linux.dev> wrote:

> From: Matti Vaittinen <mazziesaccount@gmail.com>
>
> The ROHM BD73800 PMIC has 4 pins (named GPIO1, CLKOUT, FAULT_B and
> EXTEN_OUT) which might have been set to operate as a GPI or GPO when OTP
> (One Time Programmable memory) is written at device manufacturing.
> Support the GPI/GPO use-case via GPIO framework.
>
> The default OTP for these pins is to not use any of them as GPI or GPO.
> (The GPIO1 defaults as an ADC input regardless the naming). Hence the
> driver assumes none of these pins is a GPI/GPO unless explicitly pointed
> as GPI or GPO via device tree.
>
> Furthermore, pin's direction can't be changed after OTP configuration is
> done. Also the default drive type for a GPO (CMOS / Open Drain) is set
> by the OTP configuration. The BD73800 has a set of undocumented test
> registers which should allow changing the drive type. Access to the test
> register area or the test registers aren't documented and so this driver
> does not support configuring the drive type even though it might be
> doable.
>
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>

Pretty neat with gpio-regmap isn't it? :)
Reviewed-by: Linus Walleij <linusw@kernel.org>

Yours,
Linus Walleij
Bartosz Golaszewski Sept. 2, 2026, 12:58 p.m. UTC | #2
On Wed, 2 Sep 2026 13:07:32 +0200, Matti Vaittinen
<matti.vaittinen@linux.dev> said:
> From: Matti Vaittinen <mazziesaccount@gmail.com>
>
> The ROHM BD73800 PMIC has 4 pins (named GPIO1, CLKOUT, FAULT_B and
> EXTEN_OUT) which might have been set to operate as a GPI or GPO when OTP
> (One Time Programmable memory) is written at device manufacturing.
> Support the GPI/GPO use-case via GPIO framework.
>
> The default OTP for these pins is to not use any of them as GPI or GPO.
> (The GPIO1 defaults as an ADC input regardless the naming). Hence the
> driver assumes none of these pins is a GPI/GPO unless explicitly pointed
> as GPI or GPO via device tree.
>
> Furthermore, pin's direction can't be changed after OTP configuration is
> done. Also the default drive type for a GPO (CMOS / Open Drain) is set
> by the OTP configuration. The BD73800 has a set of undocumented test
> registers which should allow changing the drive type. Access to the test
> register area or the test registers aren't documented and so this driver
> does not support configuring the drive type even though it might be
> doable.
>
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
>

Looks good, just a couple nits.

> ---
> Revision history:
>  v2 => v3:
>   - Use gpio-regmap as was suggested by Linus W.
>  v1 => v2:
>   - Return -ENODEV when no GPIO pins are found
>   - Drop useless comment
>   - Use 'data' instead of 'g' as a name of the private data struct. Do so
>     consistently (change also the 'bdgpio' to 'data').
>   - Use device_property_... instead of the fwnode_property_...
>   - fix fault_b pin property name (re-use existing name)
> ---
>  drivers/gpio/Kconfig        |  12 +++
>  drivers/gpio/Makefile       |   1 +
>  drivers/gpio/gpio-bd73800.c | 209 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 222 insertions(+)
>  create mode 100644 drivers/gpio/gpio-bd73800.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index a48586bb8edb..64127740a248 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -1385,6 +1385,18 @@ config GPIO_BD72720
>  	  be configured to GPO on the ROHM PMIC. The pin configuration is done
>  	  on OTP at manufacturing.
>
> +config GPIO_BD73800
> +	tristate "ROHM BD73800 GPIO support"
> +	depends on MFD_ROHM_BD71828
> +	select GPIO_REGMAP
> +	help
> +	  Support for GPIOs on ROHM BD73800 PMIC. There can be up to 4
> +	  GPI or GPO pins available on the PMIC in total. The purpose of
> +	  the pins is decided at the device manufacturing by OTP
> +	  configuration and can't be reconfigured later. Enable this
> +	  if your PMIC has pins set as GPIs or GPOs and if you wish to
> +	  control the pins via the GPIO framework.
> +
>  config GPIO_BD9571MWV
>  	tristate "ROHM BD9571 GPIO support"
>  	depends on MFD_BD9571MWV
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index dc9e6d643b5b..dc5e0583e328 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_BCM_XGS_IPROC)	+= gpio-xgs-iproc.o
>  obj-$(CONFIG_GPIO_BD71815)		+= gpio-bd71815.o
>  obj-$(CONFIG_GPIO_BD71828)		+= gpio-bd71828.o
>  obj-$(CONFIG_GPIO_BD72720)		+= gpio-bd72720.o
> +obj-$(CONFIG_GPIO_BD73800)		+= gpio-bd73800.o
>  obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd9571mwv.o
>  obj-$(CONFIG_GPIO_BLZP1600)		+= gpio-blzp1600.o
>  obj-$(CONFIG_GPIO_BRCMSTB)		+= gpio-brcmstb.o
> diff --git a/drivers/gpio/gpio-bd73800.c b/drivers/gpio/gpio-bd73800.c
> new file mode 100644
> index 000000000000..824901859a61
> --- /dev/null
> +++ b/drivers/gpio/gpio-bd73800.c
> @@ -0,0 +1,209 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support to GPIOs on ROHM BD73800
> + * Copyright 2024 ROHM Semiconductors.
> + * Author: Matti Vaittinen <mazziesaccount@gmail.com>
> + */
> +
> +#include <linux/bitmap.h>
> +#include <linux/bitops.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/gpio/regmap.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/rohm-bd73800.h>
> +
> +#define BD73800_GPIO_MAX_PINS 4
> +/*
> + * The BD73800 has several "one time programmable" (OTP) configurations which
> + * can be set at manufacturing phase. Some of these options allow using
> + * individual pins as GPI or GPO (not both at the same time). The OTP
> + * configuration can't be read at run-time, so drivers rely on device-tree to
> + * advertise the OTP programmed in manufacturing.
> + *
> + * The pins which can be used as GPIO are:
> + * GPIO1, CLKOUT (GPIO2), FAULT_B, EXTEN_OUT.
> + *
> + * The OTP options 2 and 3 state for all the pins:
> + *  - OTP2: GPI (also IRQ source)
> + *  - OTP3: GPO (NOTE: This is actually 2 different OTP options. Either a
> + *    register controllable output or a power-sequence controlled output.
> + *    The "gpo" referred here means only the register controllable output.)
> + *    The datasheet refers to this as: "<pin> output is controlled by
> + *    GPIO<N>_OUT or power on/off sequencer to control external VRs. ON/OFF
> + *    sequence timing is configurable."
> + *
> + * The data-sheet further says that the GPI/GPO is not a default OTP
> + * configuration for any of the pins. Hence the GPIO driver defaults to a pin
> + * not being a GPI or GPO, but requires the pin to be explicitly marked as a
> + * GPI or GPO in the device-tree.
> + *
> + * DT properties:
> + * "rohm,pin-gpio1", "rohm,pin-clkout", "rohm,pin-fault_b", "rohm,pin-exten"
> + * can be set to one of the values "gpi" or "gpo" to enable them to be used as
> + * GPIO.
> + *
> + * The amount of GPIO lines the chip exposes to the user-space (chip.ngpio) is
> + * always the same, regardless of the OTP variant in use. The lines which are
> + * not usable as GPIO on a given OTP variant are marked invalid via the GPIO
> + * valid_mask. This way the user-space always sees a constant amount of GPIO
> + * lines, where numbering (relative to the chip's base) stays also the same.
> + * Users can then use the valid_mask to find out which of the lines are usable.
> + */
> +
> +struct bd73800_gpio {
> +	/* dev points to the platform device for devm and prints */
> +	struct device *dev;
> +	struct regmap *regmap;
> +	/* Pins which have been OTP configured as GPI or GPO */
> +	DECLARE_BITMAP(valid_mask, BD73800_GPIO_MAX_PINS);
> +	/* Subset of valid_mask - pins which have been OTP configured as GPO */
> +	DECLARE_BITMAP(output_mask, BD73800_GPIO_MAX_PINS);
> +};
> +
> +static const char * const bd73800_gpio_properties[BD73800_GPIO_MAX_PINS] = {
> +	"rohm,pin-gpio1", "rohm,pin-clkout", "rohm,pin-fault_b", "rohm,pin-exten"

Can you put the properties on separate lines?

> +};
> +
> +static int bd73800_gpio_get_pins(struct bd73800_gpio *data)
> +{
> +	struct device *parent = data->dev->parent;
> +	const char *val;
> +	int i, ret;
> +
> +	for (i = 0; i < BD73800_GPIO_MAX_PINS; i++) {
> +		ret = device_property_read_string(parent,
> +						  bd73800_gpio_properties[i],
> +						  &val);
> +		if (ret) {
> +			if (ret == -EINVAL)
> +				continue;
> +
> +			return dev_err_probe(data->dev, ret,
> +					"pin %d (%s), bad configuration\n", i,
> +					bd73800_gpio_properties[i]);
> +		}
> +
> +		if (!strcmp(val, "gpi")) {
> +			__set_bit(i, data->valid_mask);
> +		} else if (!strcmp(val, "gpo")) {
> +			__set_bit(i, data->valid_mask);
> +			__set_bit(i, data->output_mask);
> +		} else {
> +			dev_warn(data->dev,
> +				"pin %d (%s), unknown value '%s' ignored\n", i,
> +				bd73800_gpio_properties[i], val);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int bd73800_gpio_init_valid_mask(struct gpio_chip *gc,
> +					unsigned long *valid_mask,
> +					unsigned int ngpios)
> +{
> +	struct gpio_regmap *gpio = gpiochip_get_data(gc);
> +	struct bd73800_gpio *data = gpio_regmap_get_drvdata(gpio);
> +
> +	bitmap_copy(valid_mask, data->valid_mask, ngpios);
> +
> +	return 0;
> +}
> +
> +/*
> + * The used register depends on OTP:
> + *  - If OTP has set pin as GPO, only the GPO_OUT register is valid.
> + *  - If OTP has set pin as GPI, only the INT_5_SRC register is valid.
> + */
> +static int bd73800_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
> +				       enum gpio_regmap_operation op,
> +				       unsigned int base, unsigned int offset,
> +				       unsigned int *reg, unsigned int *mask)
> +{
> +	struct bd73800_gpio *data = gpio_regmap_get_drvdata(gpio);
> +	bool is_output = test_bit(offset, data->output_mask);
> +
> +	if (is_output)
> +		*reg = BD73800_REG_GPO_OUT;
> +	else
> +		*reg = base;
> +
> +	*mask = BIT(offset);
> +
> +	return 0;
> +}
> +
> +static int gpo_bd73800_probe(struct platform_device *pdev)
> +{
> +	struct gpio_regmap_config config = { };
> +	struct bd73800_gpio *data;
> +	struct device *parent, *dev;
> +	struct gpio_regmap *gpio;
> +	int ret;
> +
> +	dev = &pdev->dev;
> +	/* The device-tree and regmap come from MFD => use parent for that */
> +	parent = dev->parent;
> +
> +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->dev = dev;
> +	data->regmap = dev_get_regmap(parent, NULL);
> +	if (!data->regmap)
> +		return dev_err_probe(dev, -ENODEV, "no parent regmap\n");
> +
> +	ret = bd73800_gpio_get_pins(data);
> +	if (ret)
> +		return ret;
> +
> +	if (bitmap_empty(data->valid_mask, BD73800_GPIO_MAX_PINS)) {
> +		/*
> +		 * The BD73800 may or may not have pins allocated for GPIO
> +		 * depending on the OTP used at manufacturing.
> +		 * If there are no pins, then we have nothing to do.
> +		 */
> +		dev_dbg(dev, "no GPIO pins\n");
> +		return -ENODEV;
> +	}
> +
> +	config.parent = parent;
> +	config.regmap = data->regmap;
> +	config.label = "bd73800";
> +	config.ngpio = BD73800_GPIO_MAX_PINS;
> +	config.reg_dat_base = BD73800_REG_INT_5_SRC;
> +	config.reg_set_base = BD73800_REG_GPO_OUT;
> +	config.reg_mask_xlate = bd73800_gpio_reg_mask_xlate;
> +	config.init_valid_mask = bd73800_gpio_init_valid_mask;
> +	/* All pins that are valid GPIO lines also have a fixed direction */
> +	config.fixed_direction_mask = data->valid_mask;
> +	config.fixed_direction_output = data->output_mask;
> +	config.drvdata = data;
> +
> +	gpio = devm_gpio_regmap_register(dev, &config);
> +
> +	return PTR_ERR_OR_ZERO(gpio);

Why not return PTR_ERR_OR_ZERO(devm_gpio_regmap_register())?

> +}
> +
> +static const struct platform_device_id bd73800_gpio_id[] = {
> +	{ "bd73800-gpio" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(platform, bd73800_gpio_id);
> +
> +static struct platform_driver gpo_bd73800_driver = {
> +	.driver = {
> +		.name = "bd73800-gpio",
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +	},
> +	.probe = gpo_bd73800_probe,
> +	.id_table = bd73800_gpio_id,
> +};
> +module_platform_driver(gpo_bd73800_driver);
> +
> +MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>");
> +MODULE_DESCRIPTION("GPIO interface for BD73800");
> +MODULE_LICENSE("GPL");
> --
> 2.55.0
>
>

With that:

Acked-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Matti Vaittinen Sept. 3, 2026, 5 a.m. UTC | #3
Hi dee Ho Bartosz,

Thanks for the reviews!

On 02/09/2026 15:58, Bartosz Golaszewski wrote:
> On Wed, 2 Sep 2026 13:07:32 +0200, Matti Vaittinen
> <matti.vaittinen@linux.dev> said:
>> From: Matti Vaittinen <mazziesaccount@gmail.com>
>>
>> The ROHM BD73800 PMIC has 4 pins (named GPIO1, CLKOUT, FAULT_B and
>> EXTEN_OUT) which might have been set to operate as a GPI or GPO when OTP
>> (One Time Programmable memory) is written at device manufacturing.
>> Support the GPI/GPO use-case via GPIO framework.
>>
>> The default OTP for these pins is to not use any of them as GPI or GPO.
>> (The GPIO1 defaults as an ADC input regardless the naming). Hence the
>> driver assumes none of these pins is a GPI/GPO unless explicitly pointed
>> as GPI or GPO via device tree.
>>
>> Furthermore, pin's direction can't be changed after OTP configuration is
>> done. Also the default drive type for a GPO (CMOS / Open Drain) is set
>> by the OTP configuration. The BD73800 has a set of undocumented test
>> registers which should allow changing the drive type. Access to the test
>> register area or the test registers aren't documented and so this driver
>> does not support configuring the drive type even though it might be
>> doable.
>>
>> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
>>

//snip

>> +
>> +static const char * const bd73800_gpio_properties[BD73800_GPIO_MAX_PINS] = {
>> +	"rohm,pin-gpio1", "rohm,pin-clkout", "rohm,pin-fault_b", "rohm,pin-exten"
> 
> Can you put the properties on separate lines?

Sure, no problem, thanks. I just wonder if I should re-spin the whole 
series for this. I suppose I'll wait until the next week, to see if I'll 
get any other comments.
>> +};
>> +

//snip

>> +
>> +static int gpo_bd73800_probe(struct platform_device *pdev)
>> +{
>> +	struct gpio_regmap_config config = { };
>> +	struct bd73800_gpio *data;
>> +	struct device *parent, *dev;
>> +	struct gpio_regmap *gpio;
>> +	int ret;
>> +
>> +	dev = &pdev->dev;
>> +	/* The device-tree and regmap come from MFD => use parent for that */
>> +	parent = dev->parent;
>> +
>> +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	data->dev = dev;
>> +	data->regmap = dev_get_regmap(parent, NULL);
>> +	if (!data->regmap)
>> +		return dev_err_probe(dev, -ENODEV, "no parent regmap\n");
>> +
>> +	ret = bd73800_gpio_get_pins(data);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (bitmap_empty(data->valid_mask, BD73800_GPIO_MAX_PINS)) {
>> +		/*
>> +		 * The BD73800 may or may not have pins allocated for GPIO
>> +		 * depending on the OTP used at manufacturing.
>> +		 * If there are no pins, then we have nothing to do.
>> +		 */
>> +		dev_dbg(dev, "no GPIO pins\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	config.parent = parent;
>> +	config.regmap = data->regmap;
>> +	config.label = "bd73800";
>> +	config.ngpio = BD73800_GPIO_MAX_PINS;
>> +	config.reg_dat_base = BD73800_REG_INT_5_SRC;
>> +	config.reg_set_base = BD73800_REG_GPO_OUT;
>> +	config.reg_mask_xlate = bd73800_gpio_reg_mask_xlate;
>> +	config.init_valid_mask = bd73800_gpio_init_valid_mask;
>> +	/* All pins that are valid GPIO lines also have a fixed direction */
>> +	config.fixed_direction_mask = data->valid_mask;
>> +	config.fixed_direction_output = data->output_mask;
>> +	config.drvdata = data;
>> +
>> +	gpio = devm_gpio_regmap_register(dev, &config);
>> +
>> +	return PTR_ERR_OR_ZERO(gpio);
> 
> Why not return PTR_ERR_OR_ZERO(devm_gpio_regmap_register())?

How strongly do you feel about it? It's not a big deal, but I always 
find it a bit harder to read when functions / macros are called inside a 
parameter list. Thus I'd rather keep it like this, just for the sake of 
my own eyes :)

>> +}
>> +

> 
> With that:
> 
> Acked-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

Yours,
	-- Matti
Matti Vaittinen Sept. 3, 2026, 5:02 a.m. UTC | #4
On 02/09/2026 15:39, Linus Walleij wrote:
> Hi Matti,
> 
> thanks for your patch!
> 
> On Wed, Sep 2, 2026 at 1:07 PM Matti Vaittinen
> <matti.vaittinen@linux.dev> wrote:
> 
>> From: Matti Vaittinen <mazziesaccount@gmail.com>
>>
>> The ROHM BD73800 PMIC has 4 pins (named GPIO1, CLKOUT, FAULT_B and
>> EXTEN_OUT) which might have been set to operate as a GPI or GPO when OTP
>> (One Time Programmable memory) is written at device manufacturing.
>> Support the GPI/GPO use-case via GPIO framework.
>>
>> The default OTP for these pins is to not use any of them as GPI or GPO.
>> (The GPIO1 defaults as an ADC input regardless the naming). Hence the
>> driver assumes none of these pins is a GPI/GPO unless explicitly pointed
>> as GPI or GPO via device tree.
>>
>> Furthermore, pin's direction can't be changed after OTP configuration is
>> done. Also the default drive type for a GPO (CMOS / Open Drain) is set
>> by the OTP configuration. The BD73800 has a set of undocumented test
>> registers which should allow changing the drive type. Access to the test
>> register area or the test registers aren't documented and so this driver
>> does not support configuring the drive type even though it might be
>> doable.
>>
>> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
> 
> Pretty neat with gpio-regmap isn't it? :)

Yep. I like the added flexibility now that it has got those callbacks a 
driver can register... ;)

> Reviewed-by: Linus Walleij <linusw@kernel.org>

Thanks!

Yours,
	-- Matti
Bartosz Golaszewski Sept. 3, 2026, 7:53 a.m. UTC | #5
On Thu, 3 Sep 2026 07:00:24 +0200, Matti Vaittinen
<mazziesaccount@gmail.com> said:
>>
>> Why not return PTR_ERR_OR_ZERO(devm_gpio_regmap_register())?
>
> How strongly do you feel about it? It's not a big deal, but I always
> find it a bit harder to read when functions / macros are called inside a
> parameter list. Thus I'd rather keep it like this, just for the sake of
> my own eyes :)
>

If you need to respin, please change it, if not, I've given my Ack and LGTM.
Same above.

Bart
diff mbox series

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index a48586bb8edb..64127740a248 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1385,6 +1385,18 @@  config GPIO_BD72720
 	  be configured to GPO on the ROHM PMIC. The pin configuration is done
 	  on OTP at manufacturing.
 
+config GPIO_BD73800
+	tristate "ROHM BD73800 GPIO support"
+	depends on MFD_ROHM_BD71828
+	select GPIO_REGMAP
+	help
+	  Support for GPIOs on ROHM BD73800 PMIC. There can be up to 4
+	  GPI or GPO pins available on the PMIC in total. The purpose of
+	  the pins is decided at the device manufacturing by OTP
+	  configuration and can't be reconfigured later. Enable this
+	  if your PMIC has pins set as GPIs or GPOs and if you wish to
+	  control the pins via the GPIO framework.
+
 config GPIO_BD9571MWV
 	tristate "ROHM BD9571 GPIO support"
 	depends on MFD_BD9571MWV
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index dc9e6d643b5b..dc5e0583e328 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -45,6 +45,7 @@  obj-$(CONFIG_GPIO_BCM_XGS_IPROC)	+= gpio-xgs-iproc.o
 obj-$(CONFIG_GPIO_BD71815)		+= gpio-bd71815.o
 obj-$(CONFIG_GPIO_BD71828)		+= gpio-bd71828.o
 obj-$(CONFIG_GPIO_BD72720)		+= gpio-bd72720.o
+obj-$(CONFIG_GPIO_BD73800)		+= gpio-bd73800.o
 obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd9571mwv.o
 obj-$(CONFIG_GPIO_BLZP1600)		+= gpio-blzp1600.o
 obj-$(CONFIG_GPIO_BRCMSTB)		+= gpio-brcmstb.o
diff --git a/drivers/gpio/gpio-bd73800.c b/drivers/gpio/gpio-bd73800.c
new file mode 100644
index 000000000000..824901859a61
--- /dev/null
+++ b/drivers/gpio/gpio-bd73800.c
@@ -0,0 +1,209 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support to GPIOs on ROHM BD73800
+ * Copyright 2024 ROHM Semiconductors.
+ * Author: Matti Vaittinen <mazziesaccount@gmail.com>
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/rohm-bd73800.h>
+
+#define BD73800_GPIO_MAX_PINS 4
+/*
+ * The BD73800 has several "one time programmable" (OTP) configurations which
+ * can be set at manufacturing phase. Some of these options allow using
+ * individual pins as GPI or GPO (not both at the same time). The OTP
+ * configuration can't be read at run-time, so drivers rely on device-tree to
+ * advertise the OTP programmed in manufacturing.
+ *
+ * The pins which can be used as GPIO are:
+ * GPIO1, CLKOUT (GPIO2), FAULT_B, EXTEN_OUT.
+ *
+ * The OTP options 2 and 3 state for all the pins:
+ *  - OTP2: GPI (also IRQ source)
+ *  - OTP3: GPO (NOTE: This is actually 2 different OTP options. Either a
+ *    register controllable output or a power-sequence controlled output.
+ *    The "gpo" referred here means only the register controllable output.)
+ *    The datasheet refers to this as: "<pin> output is controlled by
+ *    GPIO<N>_OUT or power on/off sequencer to control external VRs. ON/OFF
+ *    sequence timing is configurable."
+ *
+ * The data-sheet further says that the GPI/GPO is not a default OTP
+ * configuration for any of the pins. Hence the GPIO driver defaults to a pin
+ * not being a GPI or GPO, but requires the pin to be explicitly marked as a
+ * GPI or GPO in the device-tree.
+ *
+ * DT properties:
+ * "rohm,pin-gpio1", "rohm,pin-clkout", "rohm,pin-fault_b", "rohm,pin-exten"
+ * can be set to one of the values "gpi" or "gpo" to enable them to be used as
+ * GPIO.
+ *
+ * The amount of GPIO lines the chip exposes to the user-space (chip.ngpio) is
+ * always the same, regardless of the OTP variant in use. The lines which are
+ * not usable as GPIO on a given OTP variant are marked invalid via the GPIO
+ * valid_mask. This way the user-space always sees a constant amount of GPIO
+ * lines, where numbering (relative to the chip's base) stays also the same.
+ * Users can then use the valid_mask to find out which of the lines are usable.
+ */
+
+struct bd73800_gpio {
+	/* dev points to the platform device for devm and prints */
+	struct device *dev;
+	struct regmap *regmap;
+	/* Pins which have been OTP configured as GPI or GPO */
+	DECLARE_BITMAP(valid_mask, BD73800_GPIO_MAX_PINS);
+	/* Subset of valid_mask - pins which have been OTP configured as GPO */
+	DECLARE_BITMAP(output_mask, BD73800_GPIO_MAX_PINS);
+};
+
+static const char * const bd73800_gpio_properties[BD73800_GPIO_MAX_PINS] = {
+	"rohm,pin-gpio1", "rohm,pin-clkout", "rohm,pin-fault_b", "rohm,pin-exten"
+};
+
+static int bd73800_gpio_get_pins(struct bd73800_gpio *data)
+{
+	struct device *parent = data->dev->parent;
+	const char *val;
+	int i, ret;
+
+	for (i = 0; i < BD73800_GPIO_MAX_PINS; i++) {
+		ret = device_property_read_string(parent,
+						  bd73800_gpio_properties[i],
+						  &val);
+		if (ret) {
+			if (ret == -EINVAL)
+				continue;
+
+			return dev_err_probe(data->dev, ret,
+					"pin %d (%s), bad configuration\n", i,
+					bd73800_gpio_properties[i]);
+		}
+
+		if (!strcmp(val, "gpi")) {
+			__set_bit(i, data->valid_mask);
+		} else if (!strcmp(val, "gpo")) {
+			__set_bit(i, data->valid_mask);
+			__set_bit(i, data->output_mask);
+		} else {
+			dev_warn(data->dev,
+				"pin %d (%s), unknown value '%s' ignored\n", i,
+				bd73800_gpio_properties[i], val);
+		}
+	}
+
+	return 0;
+}
+
+static int bd73800_gpio_init_valid_mask(struct gpio_chip *gc,
+					unsigned long *valid_mask,
+					unsigned int ngpios)
+{
+	struct gpio_regmap *gpio = gpiochip_get_data(gc);
+	struct bd73800_gpio *data = gpio_regmap_get_drvdata(gpio);
+
+	bitmap_copy(valid_mask, data->valid_mask, ngpios);
+
+	return 0;
+}
+
+/*
+ * The used register depends on OTP:
+ *  - If OTP has set pin as GPO, only the GPO_OUT register is valid.
+ *  - If OTP has set pin as GPI, only the INT_5_SRC register is valid.
+ */
+static int bd73800_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
+				       enum gpio_regmap_operation op,
+				       unsigned int base, unsigned int offset,
+				       unsigned int *reg, unsigned int *mask)
+{
+	struct bd73800_gpio *data = gpio_regmap_get_drvdata(gpio);
+	bool is_output = test_bit(offset, data->output_mask);
+
+	if (is_output)
+		*reg = BD73800_REG_GPO_OUT;
+	else
+		*reg = base;
+
+	*mask = BIT(offset);
+
+	return 0;
+}
+
+static int gpo_bd73800_probe(struct platform_device *pdev)
+{
+	struct gpio_regmap_config config = { };
+	struct bd73800_gpio *data;
+	struct device *parent, *dev;
+	struct gpio_regmap *gpio;
+	int ret;
+
+	dev = &pdev->dev;
+	/* The device-tree and regmap come from MFD => use parent for that */
+	parent = dev->parent;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dev = dev;
+	data->regmap = dev_get_regmap(parent, NULL);
+	if (!data->regmap)
+		return dev_err_probe(dev, -ENODEV, "no parent regmap\n");
+
+	ret = bd73800_gpio_get_pins(data);
+	if (ret)
+		return ret;
+
+	if (bitmap_empty(data->valid_mask, BD73800_GPIO_MAX_PINS)) {
+		/*
+		 * The BD73800 may or may not have pins allocated for GPIO
+		 * depending on the OTP used at manufacturing.
+		 * If there are no pins, then we have nothing to do.
+		 */
+		dev_dbg(dev, "no GPIO pins\n");
+		return -ENODEV;
+	}
+
+	config.parent = parent;
+	config.regmap = data->regmap;
+	config.label = "bd73800";
+	config.ngpio = BD73800_GPIO_MAX_PINS;
+	config.reg_dat_base = BD73800_REG_INT_5_SRC;
+	config.reg_set_base = BD73800_REG_GPO_OUT;
+	config.reg_mask_xlate = bd73800_gpio_reg_mask_xlate;
+	config.init_valid_mask = bd73800_gpio_init_valid_mask;
+	/* All pins that are valid GPIO lines also have a fixed direction */
+	config.fixed_direction_mask = data->valid_mask;
+	config.fixed_direction_output = data->output_mask;
+	config.drvdata = data;
+
+	gpio = devm_gpio_regmap_register(dev, &config);
+
+	return PTR_ERR_OR_ZERO(gpio);
+}
+
+static const struct platform_device_id bd73800_gpio_id[] = {
+	{ "bd73800-gpio" },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, bd73800_gpio_id);
+
+static struct platform_driver gpo_bd73800_driver = {
+	.driver = {
+		.name = "bd73800-gpio",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.probe = gpo_bd73800_probe,
+	.id_table = bd73800_gpio_id,
+};
+module_platform_driver(gpo_bd73800_driver);
+
+MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>");
+MODULE_DESCRIPTION("GPIO interface for BD73800");
+MODULE_LICENSE("GPL");