Re: [PATCH v6 1/2] gpio: mlxbf3: Add gpio driver support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Mar 15, 2023 at 11:50 PM Asmaa Mnebhi <asmaa@xxxxxxxxxx> wrote:
>
> Add support for the BlueField-3 SoC GPIO driver.
> This driver configures and handles GPIO interrupts. It also enables a user
> to manipulate certain GPIO pins via libgpiod tools or other kernel drivers.
> The usables pins are defined via the "gpio-reserved-ranges" property.

Reviewed-by: Andy Shevchenko <andy.shevchenko@xxxxxxxxx>

> Signed-off-by: Asmaa Mnebhi <asmaa@xxxxxxxxxx>
> ---
>  drivers/gpio/Kconfig       |  13 ++
>  drivers/gpio/Makefile      |   1 +
>  drivers/gpio/gpio-mlxbf3.c | 248 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 262 insertions(+)
>  create mode 100644 drivers/gpio/gpio-mlxbf3.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index ec7cfd4f52b1..beec1f6c34ff 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -1534,6 +1534,19 @@ 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) || COMPILE_TEST
> +       select GPIO_GENERIC
> +       select GPIOLIB_IRQCHIP
> +       help
> +         Say Y if you want GPIO support on Mellanox BlueField 3 SoC.
> +         This GPIO controller supports interrupt handling and enables the
> +         manipulation of certain GPIO pins.
> +         This controller should be used in parallel with pinctrl-mlxbf3 to
> +         control the desired GPIOs.
> +         This driver can also be built as a module called mlxbf3-gpio.
> +
>  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..e30cee108986
> --- /dev/null
> +++ b/drivers/gpio/gpio-mlxbf3.c
> @@ -0,0 +1,248 @@
> +// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause
> +/* Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES */
> +
> +#include <linux/bitfield.h>
> +#include <linux/bitops.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.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          0x00
> +#define MLXBF_GPIO_FW_DATA_OUT_SET        0x04
> +
> +#define MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR 0x00
> +#define MLXBF_GPIO_FW_DATA_OUT_CLEAR      0x04
> +
> +#define MLXBF_GPIO_CAUSE_RISE_EN          0x00
> +#define MLXBF_GPIO_CAUSE_FALL_EN          0x04
> +#define MLXBF_GPIO_READ_DATA_IN           0x08
> +
> +#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;
> +
> +       /* YU GPIO block address */
> +       void __iomem *gpio_set_io;
> +       void __iomem *gpio_clr_io;
> +       void __iomem *gpio_io;
> +
> +       /* YU GPIO cause block address */
> +       void __iomem *gpio_cause_io;
> +};
> +
> +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);
> +       irq_hw_number_t offset = irqd_to_hwirq(irqd);
> +       unsigned long flags;
> +       u32 val;
> +
> +       gpiochip_enable_irq(gc, offset);
> +
> +       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);
> +       irq_hw_number_t 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);
> +
> +       gpiochip_disable_irq(gc, offset);
> +}
> +
> +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_domain_irq(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);
> +       irq_hw_number_t offset = irqd_to_hwirq(irqd);
> +       unsigned long flags;
> +       u32 val;
> +
> +       raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
> +
> +       switch (type & IRQ_TYPE_SENSE_MASK) {
> +       case IRQ_TYPE_EDGE_BOTH:
> +               val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
> +               val |= BIT(offset);
> +               writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
> +               val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
> +               val |= BIT(offset);
> +               writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
> +               break;
> +       case IRQ_TYPE_EDGE_RISING:
> +               val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
> +               val |= BIT(offset);
> +               writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
> +               break;
> +       case IRQ_TYPE_EDGE_FALLING:
> +               val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
> +               val |= BIT(offset);
> +               writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
> +               break;
> +       default:
> +               raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
> +               return -EINVAL;
> +       }
> +
> +       raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
> +
> +       irq_set_handler_locked(irqd, handle_edge_irq);
> +
> +       return 0;
> +}
> +
> +/* This function needs to be defined for handle_edge_irq() */
> +static void mlxbf3_gpio_irq_ack(struct irq_data *data)
> +{
> +}
> +
> +static const struct irq_chip gpio_mlxbf3_irqchip = {
> +       .name = "MLNXBF33",
> +       .irq_ack = mlxbf3_gpio_irq_ack,
> +       .irq_set_type = mlxbf3_gpio_irq_set_type,
> +       .irq_enable = mlxbf3_gpio_irq_enable,
> +       .irq_disable = mlxbf3_gpio_irq_disable,
> +       .flags = IRQCHIP_IMMUTABLE,
> +       GPIOCHIP_IRQ_RESOURCE_HELPERS,
> +};
> +
> +static int mlxbf3_gpio_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct mlxbf3_gpio_context *gs;
> +       struct gpio_irq_chip *girq;
> +       struct gpio_chip *gc;
> +       int ret, irq;
> +
> +       gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL);
> +       if (!gs)
> +               return -ENOMEM;
> +
> +       gs->gpio_io = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(gs->gpio_io))
> +               return PTR_ERR(gs->gpio_io);
> +
> +       gs->gpio_cause_io = devm_platform_ioremap_resource(pdev, 1);
> +       if (IS_ERR(gs->gpio_cause_io))
> +               return PTR_ERR(gs->gpio_cause_io);
> +
> +       gs->gpio_set_io = devm_platform_ioremap_resource(pdev, 2);
> +       if (IS_ERR(gs->gpio_set_io))
> +               return PTR_ERR(gs->gpio_set_io);
> +
> +       gs->gpio_clr_io = devm_platform_ioremap_resource(pdev, 3);
> +       if (IS_ERR(gs->gpio_clr_io))
> +               return PTR_ERR(gs->gpio_clr_io);
> +       gc = &gs->gc;
> +
> +       ret = bgpio_init(gc, dev, 4,
> +                       gs->gpio_io + MLXBF_GPIO_READ_DATA_IN,
> +                       gs->gpio_set_io + MLXBF_GPIO_FW_DATA_OUT_SET,
> +                       gs->gpio_clr_io + MLXBF_GPIO_FW_DATA_OUT_CLEAR,
> +                       gs->gpio_set_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_SET,
> +                       gs->gpio_clr_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR, 0);
> +
> +       gc->request = gpiochip_generic_request;
> +       gc->free = gpiochip_generic_free;
> +       gc->owner = THIS_MODULE;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq >= 0) {
> +               girq = &gs->gc.irq;
> +               gpio_irq_chip_set_chip(girq, &gpio_mlxbf3_irqchip);
> +               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;
> +               girq->handler = handle_bad_irq;
> +
> +               /*
> +                * 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, dev_name(dev), gs);
> +               if (ret)
> +                       return dev_err_probe(dev, ret, "failed to request IRQ");
> +       }
> +
> +       platform_set_drvdata(pdev, gs);
> +
> +       ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
> +       if (ret)
> +               dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n");
> +
> +       return 0;
> +}
> +
> +static const struct acpi_device_id 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@xxxxxxxxxx>");
> +MODULE_LICENSE("Dual BSD/GPL");
> --
> 2.30.1
>


-- 
With Best Regards,
Andy Shevchenko




[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux