From d189171aa3b9e01b2e2d022f375f999a4a68d525 Mon Sep 17 00:00:00 2001 From: Asmaa Mnebhi Date: Thu, 30 Mar 2023 12:38:19 -0400 Subject: [PATCH 77/77] UBUNTU: SAUCE: gpio: mlxbf3: Add gpio driver support X-NVConfidentiality: public BugLink: https://bugs.launchpad.net/bugs/2012743 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 gpio-mlxbf3.c driver has already been approved for upstreaming but is not yet available for cherry-picking. Signed-off-by: Asmaa Mnebhi Acked-by: Tim Gardner Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/gpio/Kconfig | 10 +- drivers/gpio/gpio-mlxbf3.c | 285 +++++++++++++++++---------------------------- 2 files changed, 116 insertions(+), 179 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index bf1b2b787..a31b7ffea 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1461,10 +1461,16 @@ config GPIO_MLXBF2 config GPIO_MLXBF3 tristate "Mellanox BlueField 3 SoC GPIO" - depends on (MELLANOX_PLATFORM && ARM64 && ACPI) || (64BIT && COMPILE_TEST) + depends on (MELLANOX_PLATFORM && ARM64) || COMPILE_TEST select GPIO_GENERIC + select GPIOLIB_IRQCHIP help - Say Y here if you want GPIO support on Mellanox BlueField 3 SoC. + 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" diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 45f0946ac..51dce3ae1 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -1,27 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause +/* Copyright (C) 2021-2023 NVIDIA CORPORATION & AFFILIATES */ -/* - * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES - */ - -#include #include #include #include +#include #include #include #include -#include -#include -#include #include #include -#include -#include #include #include +#include -#define DRV_VERSION "1.0" +#define DRV_VERSION "2.0" /* * There are 2 YU GPIO blocks: @@ -33,103 +26,51 @@ /* * fw_gpio[x] block registers and their offset */ -#define YU_GPIO_FW_CONTROL_SET 0x00 -#define YU_GPIO_FW_OUTPUT_ENABLE_SET 0x04 -#define YU_GPIO_FW_DATA_OUT_SET 0x08 -#define YU_GPIO_FW_CONTROL_CLEAR 0x14 -#define YU_GPIO_FW_OUTPUT_ENABLE_CLEAR 0x18 -#define YU_GPIO_FW_DATA_OUT_CLEAR 0x1c -#define YU_GPIO_CAUSE_RISE_EN 0x28 -#define YU_GPIO_CAUSE_FALL_EN 0x2c -#define YU_GPIO_READ_DATA_IN 0x30 -#define YU_GPIO_READ_OUTPUT_ENABLE 0x34 -#define YU_GPIO_READ_DATA_OUT 0x38 -#define YU_GPIO_READ_FW_CONTROL 0x44 - -#define YU_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x00 -#define YU_GPIO_CAUSE_OR_EVTEN0 0x14 -#define YU_GPIO_CAUSE_OR_CLRCAUSE 0x18 - -/* BlueField-3 gpio block context structure. */ +#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; - struct irq_chip irq_chip; - /* YU GPIO blocks address */ + /* 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; - uint32_t ctrl_gpio_mask; + /* Mask of valid gpios that can be accessed by software */ + unsigned int valid_mask; }; -static void mlxbf3_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) -{ - struct mlxbf3_gpio_context *gs = gpiochip_get_data(chip); - - /* Software can only control GPIO pins defined by ctrl_gpio_mask */ - if (!(BIT(offset) & gs->ctrl_gpio_mask)) - return; - - if (val) { - writel(BIT(offset), gs->gpio_io + YU_GPIO_FW_DATA_OUT_SET); - } else { - writel(BIT(offset), gs->gpio_io + YU_GPIO_FW_DATA_OUT_CLEAR); - } - - wmb(); - - /* This needs to be done last to avoid glitches */ - writel(BIT(offset), gs->gpio_io + YU_GPIO_FW_CONTROL_SET); -} - -static int mlxbf3_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) -{ - struct mlxbf3_gpio_context *gs = gpiochip_get_data(chip); - unsigned long flags; - - spin_lock_irqsave(&gs->gc.bgpio_lock, flags); - - writel(BIT(offset), gs->gpio_io + YU_GPIO_FW_OUTPUT_ENABLE_CLEAR); - writel(BIT(offset), gs->gpio_io + YU_GPIO_FW_CONTROL_CLEAR); - - spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); - - return 0; -} - -static int mlxbf3_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, - int value) -{ - struct mlxbf3_gpio_context *gs = gpiochip_get_data(chip); - unsigned long flags; - - spin_lock_irqsave(&gs->gc.bgpio_lock, flags); - - writel(BIT(offset), gs->gpio_io + YU_GPIO_FW_OUTPUT_ENABLE_SET); - - spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); - - return 0; -} - 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); + irq_hw_number_t offset = irqd_to_hwirq(irqd); unsigned long flags; u32 val; + gpiochip_enable_irq(gc, offset); + spin_lock_irqsave(&gs->gc.bgpio_lock, flags); - writel(BIT(offset), gs->gpio_cause_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); - val = readl(gs->gpio_cause_io + YU_GPIO_CAUSE_OR_EVTEN0); + val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); val |= BIT(offset); - writel(val, gs->gpio_cause_io + YU_GPIO_CAUSE_OR_EVTEN0); + writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); } @@ -137,15 +78,17 @@ 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); + irq_hw_number_t offset = irqd_to_hwirq(irqd); unsigned long flags; u32 val; spin_lock_irqsave(&gs->gc.bgpio_lock, flags); - val = readl(gs->gpio_cause_io + YU_GPIO_CAUSE_OR_EVTEN0); + val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); val &= ~BIT(offset); - writel(val, gs->gpio_cause_io + YU_GPIO_CAUSE_OR_EVTEN0); + writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); + + gpiochip_disable_irq(gc, offset); } static irqreturn_t mlxbf3_gpio_irq_handler(int irq, void *ptr) @@ -155,8 +98,8 @@ static irqreturn_t mlxbf3_gpio_irq_handler(int irq, void *ptr) unsigned long pending; u32 level; - pending = readl(gs->gpio_cause_io + YU_GPIO_CAUSE_OR_CAUSE_EVTEN0); - writel(pending, gs->gpio_cause_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + 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) { int gpio_irq = irq_find_mapping(gc->irq.domain, level); @@ -171,155 +114,145 @@ 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); + irq_hw_number_t offset = irqd_to_hwirq(irqd); unsigned long flags; - bool fall = false; - bool rise = false; u32 val; + spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_BOTH: - fall = true; - rise = true; + 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: - rise = true; + 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: - fall = true; + 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: + spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); return -EINVAL; } - spin_lock_irqsave(&gs->gc.bgpio_lock, flags); - if (fall) { - val = readl(gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); - val |= BIT(offset); - writel(val, gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); - } - - if (rise) { - val = readl(gs->gpio_io + YU_GPIO_CAUSE_RISE_EN); - val |= BIT(offset); - writel(val, gs->gpio_io + YU_GPIO_CAUSE_RISE_EN); - } spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); + irq_set_handler_locked(irqd, handle_edge_irq); + return 0; } -/* BlueField-3 GPIO driver initialization routine. */ -static int -mlxbf3_gpio_probe(struct platform_device *pdev) +/* This function needs to be defined for handle_edge_irq() */ +static void mlxbf3_gpio_irq_ack(struct irq_data *data) +{ +} + +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 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, +}; + +static int mlxbf3_gpio_probe(struct platform_device *pdev) { - struct mlxbf3_gpio_context *gs; struct device *dev = &pdev->dev; + struct mlxbf3_gpio_context *gs; struct gpio_irq_chip *girq; struct gpio_chip *gc; - unsigned int npins; - const char *name; int ret, irq; - name = dev_name(dev); - gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL); if (!gs) return -ENOMEM; - /* YU GPIO block address */ gs->gpio_io = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(gs->gpio_io)) return PTR_ERR(gs->gpio_io); - /* 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; + gs->gpio_set_io = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(gs->gpio_set_io)) + return PTR_ERR(gs->gpio_set_io); - if (device_property_read_u32(dev, "ctrl_gpio_mask", &gs->ctrl_gpio_mask)) - gs->ctrl_gpio_mask = 0x0; + gs->gpio_clr_io = devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(gs->gpio_clr_io)) + return PTR_ERR(gs->gpio_clr_io); + + gs->valid_mask = 0x0; + device_property_read_u32(dev, "valid_mask", &gs->valid_mask); gc = &gs->gc; - /* To set the direction to input, just give control to HW by setting - * YU_GPIO_FW_CONTROL_CLEAR. - * If the GPIO is controlled by HW, read its value via read_data_in register. - * - * When the direction = output, the GPIO is controlled by SW and - * datain=dataout. If software modifies the value of the GPIO pin, - * the value can be read from read_data_in without changing the direction. - */ ret = bgpio_init(gc, dev, 4, - gs->gpio_io + YU_GPIO_READ_DATA_IN, - NULL, - NULL, - NULL, - NULL, - 0); - - gc->set = mlxbf3_gpio_set; - gc->direction_input = mlxbf3_gpio_direction_input; - gc->direction_output = mlxbf3_gpio_direction_output; - - gc->ngpio = npins; + 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; + gc->init_valid_mask = mlxbf3_gpio_init_valid_mask; 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->chip = &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, name, gs); - if (ret) { - dev_err(dev, "failed to request IRQ"); - return ret; - } + 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(dev, "Failed adding memory mapped gpiochip\n"); - return ret; - } - - return 0; -} - -static int mlxbf3_gpio_remove(struct platform_device *pdev) -{ - struct mlxbf3_gpio_context *gs = platform_get_drvdata(pdev); - - /* Set the GPIO control back to HW */ - writel(gs->ctrl_gpio_mask, gs->gpio_io + YU_GPIO_FW_CONTROL_CLEAR); + if (ret) + dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n"); return 0; } -static const struct acpi_device_id __maybe_unused mlxbf3_gpio_acpi_match[] = { +static const struct acpi_device_id mlxbf3_gpio_acpi_match[] = { { "MLNXBF33", 0 }, - {}, + {} }; MODULE_DEVICE_TABLE(acpi, mlxbf3_gpio_acpi_match); @@ -329,12 +262,10 @@ static struct platform_driver mlxbf3_gpio_driver = { .acpi_match_table = mlxbf3_gpio_acpi_match, }, .probe = mlxbf3_gpio_probe, - .remove = mlxbf3_gpio_remove, }; - module_platform_driver(mlxbf3_gpio_driver); -MODULE_DESCRIPTION("Mellanox BlueField-3 GPIO Driver"); +MODULE_DESCRIPTION("NVIDIA BlueField-3 GPIO Driver"); MODULE_AUTHOR("Asmaa Mnebhi "); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(DRV_VERSION); -- 2.14.1