[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAHp75VfnNOsfcyLM-UP61CMAF9sLOwMbRkAe5Ljhs2p8F=4Pgw@mail.gmail.com>
Date: Fri, 17 Mar 2023 14:54:15 +0200
From: Andy Shevchenko <andy.shevchenko@...il.com>
To: Asmaa Mnebhi <asmaa@...dia.com>
Cc: linus.walleij@...aro.org, bgolaszewski@...libre.com,
linux-gpio@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-acpi@...r.kernel.org
Subject: Re: [PATCH v6 1/2] gpio: mlxbf3: Add gpio driver support
On Wed, Mar 15, 2023 at 11:50 PM Asmaa Mnebhi <asmaa@...dia.com> 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@...il.com>
> Signed-off-by: Asmaa Mnebhi <asmaa@...dia.com>
> ---
> 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@...dia.com>");
> +MODULE_LICENSE("Dual BSD/GPL");
> --
> 2.30.1
>
--
With Best Regards,
Andy Shevchenko
Powered by blists - more mailing lists