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 |
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
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);
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.
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 --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");
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