Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: mlxbf3: Add gpio driver support

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.

Signed-off-by: Asmaa Mnebhi <asmaa@nvidia.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

authored by

Asmaa Mnebhi and committed by
Bartosz Golaszewski
cd33f216 8efe1247

+262
+13
drivers/gpio/Kconfig
··· 1599 1599 help 1600 1600 Say Y here if you want GPIO support on Mellanox BlueField 2 SoC. 1601 1601 1602 + config GPIO_MLXBF3 1603 + tristate "Mellanox BlueField 3 SoC GPIO" 1604 + depends on (MELLANOX_PLATFORM && ARM64) || COMPILE_TEST 1605 + select GPIO_GENERIC 1606 + select GPIOLIB_IRQCHIP 1607 + help 1608 + Say Y if you want GPIO support on Mellanox BlueField 3 SoC. 1609 + This GPIO controller supports interrupt handling and enables the 1610 + manipulation of certain GPIO pins. 1611 + This controller should be used in parallel with pinctrl-mlxbf3 to 1612 + control the desired GPIOs. 1613 + This driver can also be built as a module called mlxbf3-gpio. 1614 + 1602 1615 config GPIO_ML_IOH 1603 1616 tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support" 1604 1617 depends on X86 || COMPILE_TEST
+1
drivers/gpio/Makefile
··· 104 104 obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o 105 105 obj-$(CONFIG_GPIO_MLXBF) += gpio-mlxbf.o 106 106 obj-$(CONFIG_GPIO_MLXBF2) += gpio-mlxbf2.o 107 + obj-$(CONFIG_GPIO_MLXBF3) += gpio-mlxbf3.o 107 108 obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o 108 109 obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o 109 110 obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o
+248
drivers/gpio/gpio-mlxbf3.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause 2 + /* Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES */ 3 + 4 + #include <linux/bitfield.h> 5 + #include <linux/bitops.h> 6 + #include <linux/device.h> 7 + #include <linux/err.h> 8 + #include <linux/gpio/driver.h> 9 + #include <linux/interrupt.h> 10 + #include <linux/io.h> 11 + #include <linux/module.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/spinlock.h> 14 + #include <linux/types.h> 15 + 16 + /* 17 + * There are 2 YU GPIO blocks: 18 + * gpio[0]: HOST_GPIO0->HOST_GPIO31 19 + * gpio[1]: HOST_GPIO32->HOST_GPIO55 20 + */ 21 + #define MLXBF3_GPIO_MAX_PINS_PER_BLOCK 32 22 + 23 + /* 24 + * fw_gpio[x] block registers and their offset 25 + */ 26 + #define MLXBF_GPIO_FW_OUTPUT_ENABLE_SET 0x00 27 + #define MLXBF_GPIO_FW_DATA_OUT_SET 0x04 28 + 29 + #define MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR 0x00 30 + #define MLXBF_GPIO_FW_DATA_OUT_CLEAR 0x04 31 + 32 + #define MLXBF_GPIO_CAUSE_RISE_EN 0x00 33 + #define MLXBF_GPIO_CAUSE_FALL_EN 0x04 34 + #define MLXBF_GPIO_READ_DATA_IN 0x08 35 + 36 + #define MLXBF_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x00 37 + #define MLXBF_GPIO_CAUSE_OR_EVTEN0 0x14 38 + #define MLXBF_GPIO_CAUSE_OR_CLRCAUSE 0x18 39 + 40 + struct mlxbf3_gpio_context { 41 + struct gpio_chip gc; 42 + 43 + /* YU GPIO block address */ 44 + void __iomem *gpio_set_io; 45 + void __iomem *gpio_clr_io; 46 + void __iomem *gpio_io; 47 + 48 + /* YU GPIO cause block address */ 49 + void __iomem *gpio_cause_io; 50 + }; 51 + 52 + static void mlxbf3_gpio_irq_enable(struct irq_data *irqd) 53 + { 54 + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); 55 + struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc); 56 + irq_hw_number_t offset = irqd_to_hwirq(irqd); 57 + unsigned long flags; 58 + u32 val; 59 + 60 + gpiochip_enable_irq(gc, offset); 61 + 62 + raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags); 63 + writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); 64 + 65 + val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); 66 + val |= BIT(offset); 67 + writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); 68 + raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); 69 + } 70 + 71 + static void mlxbf3_gpio_irq_disable(struct irq_data *irqd) 72 + { 73 + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); 74 + struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc); 75 + irq_hw_number_t offset = irqd_to_hwirq(irqd); 76 + unsigned long flags; 77 + u32 val; 78 + 79 + raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags); 80 + val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); 81 + val &= ~BIT(offset); 82 + writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); 83 + raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); 84 + 85 + gpiochip_disable_irq(gc, offset); 86 + } 87 + 88 + static irqreturn_t mlxbf3_gpio_irq_handler(int irq, void *ptr) 89 + { 90 + struct mlxbf3_gpio_context *gs = ptr; 91 + struct gpio_chip *gc = &gs->gc; 92 + unsigned long pending; 93 + u32 level; 94 + 95 + pending = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CAUSE_EVTEN0); 96 + writel(pending, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); 97 + 98 + for_each_set_bit(level, &pending, gc->ngpio) 99 + generic_handle_domain_irq(gc->irq.domain, level); 100 + 101 + return IRQ_RETVAL(pending); 102 + } 103 + 104 + static int 105 + mlxbf3_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) 106 + { 107 + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); 108 + struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc); 109 + irq_hw_number_t offset = irqd_to_hwirq(irqd); 110 + unsigned long flags; 111 + u32 val; 112 + 113 + raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags); 114 + 115 + switch (type & IRQ_TYPE_SENSE_MASK) { 116 + case IRQ_TYPE_EDGE_BOTH: 117 + val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN); 118 + val |= BIT(offset); 119 + writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN); 120 + val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN); 121 + val |= BIT(offset); 122 + writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN); 123 + break; 124 + case IRQ_TYPE_EDGE_RISING: 125 + val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN); 126 + val |= BIT(offset); 127 + writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN); 128 + break; 129 + case IRQ_TYPE_EDGE_FALLING: 130 + val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN); 131 + val |= BIT(offset); 132 + writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN); 133 + break; 134 + default: 135 + raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); 136 + return -EINVAL; 137 + } 138 + 139 + raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); 140 + 141 + irq_set_handler_locked(irqd, handle_edge_irq); 142 + 143 + return 0; 144 + } 145 + 146 + /* This function needs to be defined for handle_edge_irq() */ 147 + static void mlxbf3_gpio_irq_ack(struct irq_data *data) 148 + { 149 + } 150 + 151 + static const struct irq_chip gpio_mlxbf3_irqchip = { 152 + .name = "MLNXBF33", 153 + .irq_ack = mlxbf3_gpio_irq_ack, 154 + .irq_set_type = mlxbf3_gpio_irq_set_type, 155 + .irq_enable = mlxbf3_gpio_irq_enable, 156 + .irq_disable = mlxbf3_gpio_irq_disable, 157 + .flags = IRQCHIP_IMMUTABLE, 158 + GPIOCHIP_IRQ_RESOURCE_HELPERS, 159 + }; 160 + 161 + static int mlxbf3_gpio_probe(struct platform_device *pdev) 162 + { 163 + struct device *dev = &pdev->dev; 164 + struct mlxbf3_gpio_context *gs; 165 + struct gpio_irq_chip *girq; 166 + struct gpio_chip *gc; 167 + int ret, irq; 168 + 169 + gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL); 170 + if (!gs) 171 + return -ENOMEM; 172 + 173 + gs->gpio_io = devm_platform_ioremap_resource(pdev, 0); 174 + if (IS_ERR(gs->gpio_io)) 175 + return PTR_ERR(gs->gpio_io); 176 + 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 + gs->gpio_set_io = devm_platform_ioremap_resource(pdev, 2); 182 + if (IS_ERR(gs->gpio_set_io)) 183 + return PTR_ERR(gs->gpio_set_io); 184 + 185 + gs->gpio_clr_io = devm_platform_ioremap_resource(pdev, 3); 186 + if (IS_ERR(gs->gpio_clr_io)) 187 + return PTR_ERR(gs->gpio_clr_io); 188 + gc = &gs->gc; 189 + 190 + ret = bgpio_init(gc, dev, 4, 191 + gs->gpio_io + MLXBF_GPIO_READ_DATA_IN, 192 + gs->gpio_set_io + MLXBF_GPIO_FW_DATA_OUT_SET, 193 + gs->gpio_clr_io + MLXBF_GPIO_FW_DATA_OUT_CLEAR, 194 + gs->gpio_set_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_SET, 195 + gs->gpio_clr_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR, 0); 196 + 197 + gc->request = gpiochip_generic_request; 198 + gc->free = gpiochip_generic_free; 199 + gc->owner = THIS_MODULE; 200 + 201 + irq = platform_get_irq(pdev, 0); 202 + if (irq >= 0) { 203 + girq = &gs->gc.irq; 204 + gpio_irq_chip_set_chip(girq, &gpio_mlxbf3_irqchip); 205 + girq->default_type = IRQ_TYPE_NONE; 206 + /* This will let us handle the parent IRQ in the driver */ 207 + girq->num_parents = 0; 208 + girq->parents = NULL; 209 + girq->parent_handler = NULL; 210 + girq->handler = handle_bad_irq; 211 + 212 + /* 213 + * Directly request the irq here instead of passing 214 + * a flow-handler because the irq is shared. 215 + */ 216 + ret = devm_request_irq(dev, irq, mlxbf3_gpio_irq_handler, 217 + IRQF_SHARED, dev_name(dev), gs); 218 + if (ret) 219 + return dev_err_probe(dev, ret, "failed to request IRQ"); 220 + } 221 + 222 + platform_set_drvdata(pdev, gs); 223 + 224 + ret = devm_gpiochip_add_data(dev, &gs->gc, gs); 225 + if (ret) 226 + dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n"); 227 + 228 + return 0; 229 + } 230 + 231 + static const struct acpi_device_id mlxbf3_gpio_acpi_match[] = { 232 + { "MLNXBF33", 0 }, 233 + {} 234 + }; 235 + MODULE_DEVICE_TABLE(acpi, mlxbf3_gpio_acpi_match); 236 + 237 + static struct platform_driver mlxbf3_gpio_driver = { 238 + .driver = { 239 + .name = "mlxbf3_gpio", 240 + .acpi_match_table = mlxbf3_gpio_acpi_match, 241 + }, 242 + .probe = mlxbf3_gpio_probe, 243 + }; 244 + module_platform_driver(mlxbf3_gpio_driver); 245 + 246 + MODULE_DESCRIPTION("NVIDIA BlueField-3 GPIO Driver"); 247 + MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>"); 248 + MODULE_LICENSE("Dual BSD/GPL");