diff mbox series

[v3,1/2] Support NVIDIA BlueField-3 GPIO controller

Message ID c1bf45a868edcd3df5263fa76a32b28e6c9ca3d1.1676042188.git.asmaa@nvidia.com (mailing list archive)
State Handled Elsewhere, archived
Headers show
Series Add NVIDIA BlueField-3 GPIO driver and pin controller | expand

Commit Message

Asmaa Mnebhi Feb. 10, 2023, 3:39 p.m. UTC
This patch adds support for the BlueField-3 SoC GPIO driver
which allows:
- setting certain GPIOs as interrupts from other dependent drivers
- ability to manipulate certain GPIO pins via libgpiod tools

BlueField-3 has 56 GPIOs but the user is only allowed to change some
of them into GPIO mode. Use valid_mask to make it impossible to alter
the rest of the GPIOs.

Signed-off-by: Asmaa Mnebhi <asmaa@nvidia.com>
---
 drivers/gpio/Kconfig       |   7 +
 drivers/gpio/Makefile      |   1 +
 drivers/gpio/gpio-mlxbf3.c | 262 +++++++++++++++++++++++++++++++++++++
 3 files changed, 270 insertions(+)
 create mode 100644 drivers/gpio/gpio-mlxbf3.c

Comments

kernel test robot Feb. 11, 2023, 7:37 a.m. UTC | #1
Hi Asmaa,

I love your patch! Yet something to improve:

[auto build test ERROR on brgl/gpio/for-next]
[also build test ERROR on linusw-pinctrl/devel linusw-pinctrl/for-next linus/master v6.2-rc7 next-20230210]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Asmaa-Mnebhi/Support-NVIDIA-BlueField-3-GPIO-controller/20230210-234043
base:   https://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git gpio/for-next
patch link:    https://lore.kernel.org/r/c1bf45a868edcd3df5263fa76a32b28e6c9ca3d1.1676042188.git.asmaa%40nvidia.com
patch subject: [PATCH v3 1/2] Support NVIDIA BlueField-3 GPIO controller
config: loongarch-allmodconfig (https://download.01.org/0day-ci/archive/20230211/202302111542.y6a7wZeb-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 12.1.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/intel-lab-lkp/linux/commit/ea731fc5718b591e6c84ee33049e46b60882009d
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Asmaa-Mnebhi/Support-NVIDIA-BlueField-3-GPIO-controller/20230210-234043
        git checkout ea731fc5718b591e6c84ee33049e46b60882009d
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=loongarch olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=loongarch SHELL=/bin/bash drivers/gpio/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202302111542.y6a7wZeb-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/gpio/gpio-mlxbf3.c: In function 'mlxbf3_gpio_probe':
>> drivers/gpio/gpio-mlxbf3.c:225:23: error: implicit declaration of function 'devm_request_irq'; did you mean 'can_request_irq'? [-Werror=implicit-function-declaration]
     225 |                 ret = devm_request_irq(dev, irq, mlxbf3_gpio_irq_handler,
         |                       ^~~~~~~~~~~~~~~~
         |                       can_request_irq
>> drivers/gpio/gpio-mlxbf3.c:226:40: error: 'IRQF_SHARED' undeclared (first use in this function); did you mean 'VM_SHARED'?
     226 |                                        IRQF_SHARED, name, gs);
         |                                        ^~~~~~~~~~~
         |                                        VM_SHARED
   drivers/gpio/gpio-mlxbf3.c:226:40: note: each undeclared identifier is reported only once for each function it appears in
   cc1: some warnings being treated as errors


vim +225 drivers/gpio/gpio-mlxbf3.c

   148	
   149	static int
   150	mlxbf3_gpio_probe(struct platform_device *pdev)
   151	{
   152		struct device *dev = &pdev->dev;
   153		struct mlxbf3_gpio_context *gs;
   154		unsigned int npins, valid_mask;
   155		struct gpio_irq_chip *girq;
   156		struct gpio_chip *gc;
   157		struct resource *res;
   158		const char *name;
   159		int ret, irq;
   160	
   161		name = dev_name(dev);
   162	
   163		gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL);
   164		if (!gs)
   165			return -ENOMEM;
   166	
   167		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   168		if (!res)
   169			return -ENODEV;
   170	
   171		/* Resource shared with pinctrl driver */
   172		gs->gpio_io = devm_ioremap(dev, res->start, resource_size(res));
   173		if (!gs->gpio_io)
   174			return -ENOMEM;
   175	
   176		/* YU GPIO block address */
   177		gs->gpio_cause_io = devm_platform_ioremap_resource(pdev, 1);
   178		if (IS_ERR(gs->gpio_cause_io))
   179			return PTR_ERR(gs->gpio_cause_io);
   180	
   181		if (device_property_read_u32(dev, "npins", &npins))
   182			npins = MLXBF3_GPIO_MAX_PINS_PER_BLOCK;
   183	
   184		if (device_property_read_u32(dev, "valid_mask", &valid_mask))
   185			valid_mask = 0x0;
   186	
   187		gs->valid_mask = valid_mask;
   188	
   189		gc = &gs->gc;
   190	
   191		ret = bgpio_init(gc, dev, 4,
   192				gs->gpio_io + MLXBF_GPIO_READ_DATA_IN,
   193				gs->gpio_io + MLXBF_GPIO_FW_DATA_OUT_SET,
   194				gs->gpio_io + MLXBF_GPIO_FW_DATA_OUT_CLEAR,
   195				gs->gpio_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_SET,
   196				gs->gpio_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR, 0);
   197	
   198		gc->request = gpiochip_generic_request;
   199		gc->free = gpiochip_generic_free;
   200		gc->init_valid_mask = mlxbf3_gpio_init_valid_mask;
   201	
   202		gc->ngpio = npins;
   203		gc->owner = THIS_MODULE;
   204	
   205		irq = platform_get_irq(pdev, 0);
   206		if (irq >= 0) {
   207			gs->irq_chip.name = name;
   208			gs->irq_chip.irq_set_type = mlxbf3_gpio_irq_set_type;
   209			gs->irq_chip.irq_enable = mlxbf3_gpio_irq_enable;
   210			gs->irq_chip.irq_disable = mlxbf3_gpio_irq_disable;
   211	
   212			girq = &gs->gc.irq;
   213			girq->chip = &gs->irq_chip;
   214			girq->handler = handle_simple_irq;
   215			girq->default_type = IRQ_TYPE_NONE;
   216			/* This will let us handle the parent IRQ in the driver */
   217			girq->num_parents = 0;
   218			girq->parents = NULL;
   219			girq->parent_handler = NULL;
   220	
   221			/*
   222			 * Directly request the irq here instead of passing
   223			 * a flow-handler because the irq is shared.
   224			 */
 > 225			ret = devm_request_irq(dev, irq, mlxbf3_gpio_irq_handler,
 > 226					       IRQF_SHARED, name, gs);
   227			if (ret) {
   228				dev_err(dev, "failed to request IRQ");
   229				return ret;
   230			}
   231		}
   232	
   233		platform_set_drvdata(pdev, gs);
   234	
   235		ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
   236		if (ret) {
   237			dev_err(dev, "Failed adding memory mapped gpiochip\n");
   238			return ret;
   239		}
   240	
   241		return 0;
   242	}
   243
Andy Shevchenko Feb. 11, 2023, 11:51 a.m. UTC | #2
On Fri, Feb 10, 2023 at 10:39:40AM -0500, Asmaa Mnebhi wrote:
> This patch adds support for the BlueField-3 SoC GPIO driver

The Submitting Patches states that imperative speech should be used.

> which allows:
> - setting certain GPIOs as interrupts from other dependent drivers
> - ability to manipulate certain GPIO pins via libgpiod tools
> 
> BlueField-3 has 56 GPIOs but the user is only allowed to change some
> of them into GPIO mode. Use valid_mask to make it impossible to alter
> the rest of the GPIOs.

...

> +	help
> +	  Say Y here if you want GPIO support on Mellanox BlueField 3 SoC.

Have you run checkpatch? Nowadays 3+ lines of help is recommended.
I would suggest to add a standard phrase about module name in case
the module build is chosen.

...

> +// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause

> +

Redundant blank line

> +/*
> + * Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES
> + */

This can be on one line.

...

> +#include <linux/acpi.h>

No user of this header.

> +#include <linux/gpio/driver.h>
> +#include <linux/platform_device.h>

Approx dozen of header inclusions are missing.
(bits.h, types.h, spinlock.h, ...)

...

> +struct mlxbf3_gpio_context {
> +	struct gpio_chip gc;

> +	struct irq_chip irq_chip;

Have you run it on v6.1+ kernels? This should not be here, i.e. it must be
static and const.

> +	/* YU GPIO block address */
> +	void __iomem *gpio_io;
> +
> +	/* YU GPIO cause block address */
> +	void __iomem *gpio_cause_io;
> +
> +	/* Mask of valid gpios that can be accessed by software */
> +	unsigned int valid_mask;
> +};

...

> +		generic_handle_irq(irq_find_mapping(gc->irq.domain, level));

There is a helper that unites these two calls together.

...

> +	bool fall = false;
> +	bool rise = false;

Instead, just assign each of the in the corresponding switch-case.

> +	switch (type & IRQ_TYPE_SENSE_MASK) {
> +	case IRQ_TYPE_EDGE_BOTH:
> +		fall = true;
> +		rise = true;
> +		break;
> +	case IRQ_TYPE_EDGE_RISING:
> +		rise = true;
> +		break;
> +	case IRQ_TYPE_EDGE_FALLING:
> +		fall = true;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

...

> +	raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
> +	if (fall) {
> +		val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
> +		val |= BIT(offset);
> +		writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
> +	}
> +
> +	if (rise) {
> +		val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
> +		val |= BIT(offset);
> +		writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
> +	}
> +	raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);

Don't you need to choose and lock the IRQ handler here?

> +}

...

> +static int mlxbf3_gpio_init_valid_mask(struct gpio_chip *gc,
> +				       unsigned long *valid_mask,
> +				       unsigned int ngpios)
> +{
> +	struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
> +
> +	*valid_mask = gs->valid_mask;
> +
> +	return 0;
> +}

Why do you need this?


> +	struct resource *res;

Useless variable, see below.

...

> +	const char *name;


> +	name = dev_name(dev);

Useless, just call dev_name() where it's needed.

...

> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -ENODEV;
> +
> +	/* Resource shared with pinctrl driver */
> +	gs->gpio_io = devm_ioremap(dev, res->start, resource_size(res));
> +	if (!gs->gpio_io)
> +		return -ENOMEM;
> +
> +	/* YU GPIO block address */
> +	gs->gpio_cause_io = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(gs->gpio_cause_io))
> +		return PTR_ERR(gs->gpio_cause_io);

These can be folded in a single devm_platform_ioremap_resource() call.

...


> +	if (device_property_read_u32(dev, "npins", &npins))
> +		npins = MLXBF3_GPIO_MAX_PINS_PER_BLOCK;

You can get of conditional.

	npins = MLXBF3_GPIO_MAX_PINS_PER_BLOCK;
	device_property_read_u32(dev, "npins", &npins);

...

> +	if (device_property_read_u32(dev, "valid_mask", &valid_mask))
> +		valid_mask = 0x0;

Besides that you can move this directly to the respective call and drop
redundant private copy of valid mask, you mean that without that property
no valid GPIOs?

> +	gs->valid_mask = valid_mask;

...

> +		girq->handler = handle_simple_irq;

Should be handle_bad_irq() to avoid some subtle issues that hard to debug.

...

> +		ret = devm_request_irq(dev, irq, mlxbf3_gpio_irq_handler,
> +				       IRQF_SHARED, name, gs);
> +		if (ret) {

> +			dev_err(dev, "failed to request IRQ");
> +			return ret;

return dev_err_probe(...);

> +		}
> +	}

...

> +	ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
> +	if (ret) {
> +		dev_err(dev, "Failed adding memory mapped gpiochip\n");
> +		return ret;

Ditto.

> +	}

...

> +static const struct acpi_device_id __maybe_unused mlxbf3_gpio_acpi_match[] = {
> +	{ "MLNXBF33", 0 },

> +	{},

No comma for termination entry.

> +};

...

> +	.probe    = mlxbf3_gpio_probe,
> +};

> +

Redundant blank line.

> +module_platform_driver(mlxbf3_gpio_driver);
Asmaa Mnebhi Feb. 13, 2023, 11:14 p.m. UTC | #3
Thanks Andy for the feedback! Will address your comment in v4.

> +		generic_handle_irq(irq_find_mapping(gc->irq.domain, level));

There is a helper that unites these two calls together.

I didn't find any helper in v6.2. Could you please point me to it?

> +static int mlxbf3_gpio_init_valid_mask(struct gpio_chip *gc,
> +				       unsigned long *valid_mask,
> +				       unsigned int ngpios)
> +{
> +	struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
> +
> +	*valid_mask = gs->valid_mask;
> +
> +	return 0;
> +}

Why do you need this?

Since we only use ACPI tables and we want user space (libgpiod tool) or possibly (in the future)
other kernel drivers to have access to certain GPIO pins, we use this valid_mask to do so.
Linus previously explained that if we ask for a GPIO then it will be muxed in using .gpio_request_enable().
And so we would use .valid_mask to restrict the use of certain gpios.
Andy Shevchenko Feb. 13, 2023, 11:39 p.m. UTC | #4
Mon, Feb 13, 2023 at 11:14:06PM +0000, Asmaa Mnebhi kirjoitti:
> Thanks Andy for the feedback! Will address your comment in v4.
> 
> > +		generic_handle_irq(irq_find_mapping(gc->irq.domain, level));
> 
> There is a helper that unites these two calls together.
> 
> I didn't find any helper in v6.2. Could you please point me to it?

It's available even longer than just in v6.2:
generic_handle_domain_irq().

...

> > +static int mlxbf3_gpio_init_valid_mask(struct gpio_chip *gc,
> > +				       unsigned long *valid_mask,
> > +				       unsigned int ngpios)
> > +{
> > +	struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
> > +
> > +	*valid_mask = gs->valid_mask;
> > +
> > +	return 0;
> > +}
> 
> Why do you need this?
> 
> Since we only use ACPI tables and we want user space (libgpiod tool) or possibly (in the future)
> other kernel drivers to have access to certain GPIO pins, we use this valid_mask to do so.
> Linus previously explained that if we ask for a GPIO then it will be muxed in using .gpio_request_enable().
> And so we would use .valid_mask to restrict the use of certain gpios.

So, why you can't use gpio-reserved-ranges property?
diff mbox series

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ec7cfd4f52b1..3d56a83db284 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1534,6 +1534,13 @@  config GPIO_MLXBF2
 	help
 	  Say Y here if you want GPIO support on Mellanox BlueField 2 SoC.
 
+config GPIO_MLXBF3
+	tristate "Mellanox BlueField 3 SoC GPIO"
+	depends on (MELLANOX_PLATFORM && ARM64 && ACPI) || (64BIT && COMPILE_TEST)
+	select GPIO_GENERIC
+	help
+	  Say Y here if you want GPIO support on Mellanox BlueField 3 SoC.
+
 config GPIO_ML_IOH
 	tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
 	depends on X86 || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 010587025fc8..76545ca31457 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -101,6 +101,7 @@  obj-$(CONFIG_GPIO_MERRIFIELD)		+= gpio-merrifield.o
 obj-$(CONFIG_GPIO_ML_IOH)		+= gpio-ml-ioh.o
 obj-$(CONFIG_GPIO_MLXBF)		+= gpio-mlxbf.o
 obj-$(CONFIG_GPIO_MLXBF2)		+= gpio-mlxbf2.o
+obj-$(CONFIG_GPIO_MLXBF3)		+= gpio-mlxbf3.o
 obj-$(CONFIG_GPIO_MM_LANTIQ)		+= gpio-mm-lantiq.o
 obj-$(CONFIG_GPIO_MOCKUP)		+= gpio-mockup.o
 obj-$(CONFIG_GPIO_MOXTET)		+= gpio-moxtet.o
diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c
new file mode 100644
index 000000000000..ddd27c316035
--- /dev/null
+++ b/drivers/gpio/gpio-mlxbf3.c
@@ -0,0 +1,262 @@ 
+// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause
+
+/*
+ * Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES
+ */
+
+#include <linux/acpi.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+
+/*
+ * There are 2 YU GPIO blocks:
+ * gpio[0]: HOST_GPIO0->HOST_GPIO31
+ * gpio[1]: HOST_GPIO32->HOST_GPIO55
+ */
+#define MLXBF3_GPIO_MAX_PINS_PER_BLOCK 32
+
+/*
+ * fw_gpio[x] block registers and their offset
+ */
+#define MLXBF_GPIO_FW_OUTPUT_ENABLE_SET	  0x04
+#define MLXBF_GPIO_FW_DATA_OUT_SET        0x08
+#define MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR 0x18
+#define MLXBF_GPIO_FW_DATA_OUT_CLEAR      0x1c
+#define MLXBF_GPIO_CAUSE_RISE_EN          0x28
+#define MLXBF_GPIO_CAUSE_FALL_EN          0x2c
+#define MLXBF_GPIO_READ_DATA_IN           0x30
+
+#define MLXBF_GPIO_CAUSE_OR_CAUSE_EVTEN0  0x00
+#define MLXBF_GPIO_CAUSE_OR_EVTEN0        0x14
+#define MLXBF_GPIO_CAUSE_OR_CLRCAUSE      0x18
+
+struct mlxbf3_gpio_context {
+	struct gpio_chip gc;
+	struct irq_chip irq_chip;
+
+	/* YU GPIO block address */
+	void __iomem *gpio_io;
+
+	/* YU GPIO cause block address */
+	void __iomem *gpio_cause_io;
+
+	/* Mask of valid gpios that can be accessed by software */
+	unsigned int valid_mask;
+};
+
+static void mlxbf3_gpio_irq_enable(struct irq_data *irqd)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
+	int offset = irqd_to_hwirq(irqd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
+	writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE);
+
+	val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
+	val |= BIT(offset);
+	writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
+	raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
+}
+
+static void mlxbf3_gpio_irq_disable(struct irq_data *irqd)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
+	int offset = irqd_to_hwirq(irqd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
+	val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
+	val &= ~BIT(offset);
+	writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
+	raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
+}
+
+static irqreturn_t mlxbf3_gpio_irq_handler(int irq, void *ptr)
+{
+	struct mlxbf3_gpio_context *gs = ptr;
+	struct gpio_chip *gc = &gs->gc;
+	unsigned long pending;
+	u32 level;
+
+	pending = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CAUSE_EVTEN0);
+	writel(pending, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE);
+
+	for_each_set_bit(level, &pending, gc->ngpio)
+		generic_handle_irq(irq_find_mapping(gc->irq.domain, level));
+
+	return IRQ_RETVAL(pending);
+}
+
+static int
+mlxbf3_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
+	int offset = irqd_to_hwirq(irqd);
+	unsigned long flags;
+	bool fall = false;
+	bool rise = false;
+	u32 val;
+
+	switch (type & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_EDGE_BOTH:
+		fall = true;
+		rise = true;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		rise = true;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		fall = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
+	if (fall) {
+		val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
+		val |= BIT(offset);
+		writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
+	}
+
+	if (rise) {
+		val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
+		val |= BIT(offset);
+		writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
+	}
+	raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
+
+	return 0;
+}
+
+static int mlxbf3_gpio_init_valid_mask(struct gpio_chip *gc,
+				       unsigned long *valid_mask,
+				       unsigned int ngpios)
+{
+	struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
+
+	*valid_mask = gs->valid_mask;
+
+	return 0;
+}
+
+static int
+mlxbf3_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mlxbf3_gpio_context *gs;
+	unsigned int npins, valid_mask;
+	struct gpio_irq_chip *girq;
+	struct gpio_chip *gc;
+	struct resource *res;
+	const char *name;
+	int ret, irq;
+
+	name = dev_name(dev);
+
+	gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL);
+	if (!gs)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	/* Resource shared with pinctrl driver */
+	gs->gpio_io = devm_ioremap(dev, res->start, resource_size(res));
+	if (!gs->gpio_io)
+		return -ENOMEM;
+
+	/* YU GPIO block address */
+	gs->gpio_cause_io = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(gs->gpio_cause_io))
+		return PTR_ERR(gs->gpio_cause_io);
+
+	if (device_property_read_u32(dev, "npins", &npins))
+		npins = MLXBF3_GPIO_MAX_PINS_PER_BLOCK;
+
+	if (device_property_read_u32(dev, "valid_mask", &valid_mask))
+		valid_mask = 0x0;
+
+	gs->valid_mask = valid_mask;
+
+	gc = &gs->gc;
+
+	ret = bgpio_init(gc, dev, 4,
+			gs->gpio_io + MLXBF_GPIO_READ_DATA_IN,
+			gs->gpio_io + MLXBF_GPIO_FW_DATA_OUT_SET,
+			gs->gpio_io + MLXBF_GPIO_FW_DATA_OUT_CLEAR,
+			gs->gpio_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_SET,
+			gs->gpio_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR, 0);
+
+	gc->request = gpiochip_generic_request;
+	gc->free = gpiochip_generic_free;
+	gc->init_valid_mask = mlxbf3_gpio_init_valid_mask;
+
+	gc->ngpio = npins;
+	gc->owner = THIS_MODULE;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq >= 0) {
+		gs->irq_chip.name = name;
+		gs->irq_chip.irq_set_type = mlxbf3_gpio_irq_set_type;
+		gs->irq_chip.irq_enable = mlxbf3_gpio_irq_enable;
+		gs->irq_chip.irq_disable = mlxbf3_gpio_irq_disable;
+
+		girq = &gs->gc.irq;
+		girq->chip = &gs->irq_chip;
+		girq->handler = handle_simple_irq;
+		girq->default_type = IRQ_TYPE_NONE;
+		/* This will let us handle the parent IRQ in the driver */
+		girq->num_parents = 0;
+		girq->parents = NULL;
+		girq->parent_handler = NULL;
+
+		/*
+		 * Directly request the irq here instead of passing
+		 * a flow-handler because the irq is shared.
+		 */
+		ret = devm_request_irq(dev, irq, mlxbf3_gpio_irq_handler,
+				       IRQF_SHARED, name, gs);
+		if (ret) {
+			dev_err(dev, "failed to request IRQ");
+			return ret;
+		}
+	}
+
+	platform_set_drvdata(pdev, gs);
+
+	ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
+	if (ret) {
+		dev_err(dev, "Failed adding memory mapped gpiochip\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct acpi_device_id __maybe_unused mlxbf3_gpio_acpi_match[] = {
+	{ "MLNXBF33", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, mlxbf3_gpio_acpi_match);
+
+static struct platform_driver mlxbf3_gpio_driver = {
+	.driver = {
+		.name = "mlxbf3_gpio",
+		.acpi_match_table = mlxbf3_gpio_acpi_match,
+	},
+	.probe    = mlxbf3_gpio_probe,
+};
+
+module_platform_driver(mlxbf3_gpio_driver);
+
+MODULE_DESCRIPTION("NVIDIA BlueField-3 GPIO Driver");
+MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>");
+MODULE_LICENSE("Dual BSD/GPL");