diff options
Diffstat (limited to 'drivers/gpio/gpio-mlxbf2.c')
| -rw-r--r-- | drivers/gpio/gpio-mlxbf2.c | 245 |
1 files changed, 191 insertions, 54 deletions
diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 94d5efce1721..6668686a28ff 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -1,17 +1,25 @@ // SPDX-License-Identifier: GPL-2.0 -#include <linux/acpi.h> +/* + * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES + */ + #include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/gpio/driver.h> +#include <linux/gpio/generic.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/resource.h> +#include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/types.h> @@ -43,27 +51,29 @@ #define YU_GPIO_MODE0 0x0c #define YU_GPIO_DATASET 0x14 #define YU_GPIO_DATACLEAR 0x18 +#define YU_GPIO_CAUSE_RISE_EN 0x44 +#define YU_GPIO_CAUSE_FALL_EN 0x48 #define YU_GPIO_MODE1_CLEAR 0x50 #define YU_GPIO_MODE0_SET 0x54 #define YU_GPIO_MODE0_CLEAR 0x58 +#define YU_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x80 +#define YU_GPIO_CAUSE_OR_EVTEN0 0x94 +#define YU_GPIO_CAUSE_OR_CLRCAUSE 0x98 -#ifdef CONFIG_PM struct mlxbf2_gpio_context_save_regs { u32 gpio_mode0; u32 gpio_mode1; }; -#endif /* BlueField-2 gpio block context structure. */ struct mlxbf2_gpio_context { - struct gpio_chip gc; + struct gpio_generic_chip chip; /* YU GPIO blocks address */ void __iomem *gpio_io; + struct device *dev; -#ifdef CONFIG_PM struct mlxbf2_gpio_context_save_regs *csave_regs; -#endif }; /* BlueField-2 gpio shared structure. */ @@ -73,11 +83,8 @@ struct mlxbf2_gpio_param { struct mutex *lock; }; -static struct resource yu_arm_gpio_lock_res = { - .start = YU_ARM_GPIO_LOCK_ADDR, - .end = YU_ARM_GPIO_LOCK_ADDR + YU_ARM_GPIO_LOCK_SIZE - 1, - .name = "YU_ARM_GPIO_LOCK", -}; +static struct resource yu_arm_gpio_lock_res = + DEFINE_RES_MEM_NAMED(YU_ARM_GPIO_LOCK_ADDR, YU_ARM_GPIO_LOCK_SIZE, "YU_ARM_GPIO_LOCK"); static DEFINE_MUTEX(yu_arm_gpio_lock_mutex); @@ -127,7 +134,7 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs) u32 arm_gpio_lock_val; mutex_lock(yu_arm_gpio_lock_param.lock); - spin_lock(&gs->gc.bgpio_lock); + gpio_generic_chip_lock(&gs->chip); arm_gpio_lock_val = readl(yu_arm_gpio_lock_param.io); @@ -135,7 +142,7 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs) * When lock active bit[31] is set, ModeX is write enabled */ if (YU_LOCK_ACTIVE_BIT(arm_gpio_lock_val)) { - spin_unlock(&gs->gc.bgpio_lock); + gpio_generic_chip_unlock(&gs->chip); mutex_unlock(yu_arm_gpio_lock_param.lock); return -EINVAL; } @@ -149,9 +156,11 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs) * Release the YU arm_gpio_lock after changing the direction mode. */ static void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs) + __releases(&gs->chip.lock) + __releases(yu_arm_gpio_lock_param.lock) { writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io); - spin_unlock(&gs->gc.bgpio_lock); + gpio_generic_chip_unlock(&gs->chip); mutex_unlock(yu_arm_gpio_lock_param.lock); } @@ -223,70 +232,201 @@ static int mlxbf2_gpio_direction_output(struct gpio_chip *chip, return ret; } +static void mlxbf2_gpio_irq_enable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(irqd); + u32 val; + + gpiochip_enable_irq(gc, irqd_to_hwirq(irqd)); + guard(gpio_generic_lock_irqsave)(&gs->chip); + val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + + val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); +} + +static void mlxbf2_gpio_irq_disable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(irqd); + u32 val; + + scoped_guard(gpio_generic_lock_irqsave, &gs->chip) { + val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + val &= ~BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + } + + gpiochip_disable_irq(gc, irqd_to_hwirq(irqd)); +} + +static irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr) +{ + struct mlxbf2_gpio_context *gs = ptr; + struct gpio_chip *gc = &gs->chip.gc; + unsigned long pending; + u32 level; + + pending = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CAUSE_EVTEN0); + writel(pending, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + + for_each_set_bit(level, &pending, gc->ngpio) + generic_handle_domain_irq_safe(gc->irq.domain, level); + + return IRQ_RETVAL(pending); +} + +static int +mlxbf2_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(irqd); + 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; + } + + guard(gpio_generic_lock_irqsave)(&gs->chip); + + 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); + } + + return 0; +} + +static void mlxbf2_gpio_irq_print_chip(struct irq_data *irqd, + struct seq_file *p) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + + seq_puts(p, dev_name(gs->dev)); +} + +static const struct irq_chip mlxbf2_gpio_irq_chip = { + .irq_set_type = mlxbf2_gpio_irq_set_type, + .irq_enable = mlxbf2_gpio_irq_enable, + .irq_disable = mlxbf2_gpio_irq_disable, + .irq_print_chip = mlxbf2_gpio_irq_print_chip, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + /* BlueField-2 GPIO driver initialization routine. */ static int mlxbf2_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config; struct mlxbf2_gpio_context *gs; struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; struct gpio_chip *gc; - struct resource *res; unsigned int npins; - int ret; + 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 */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; + gs->dev = dev; - gs->gpio_io = devm_ioremap(dev, res->start, resource_size(res)); - if (!gs->gpio_io) - 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); ret = mlxbf2_gpio_get_lock_res(pdev); - if (ret) { - dev_err(dev, "Failed to get yu_arm_gpio_lock resource\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to get yu_arm_gpio_lock resource\n"); if (device_property_read_u32(dev, "npins", &npins)) npins = MLXBF2_GPIO_MAX_PINS_PER_BLOCK; - gc = &gs->gc; + gc = &gs->chip.gc; + + config = (struct gpio_generic_chip_config) { + .dev = dev, + .sz = 4, + .dat = gs->gpio_io + YU_GPIO_DATAIN, + .set = gs->gpio_io + YU_GPIO_DATASET, + .clr = gs->gpio_io + YU_GPIO_DATACLEAR, + }; - ret = bgpio_init(gc, dev, 4, - gs->gpio_io + YU_GPIO_DATAIN, - gs->gpio_io + YU_GPIO_DATASET, - gs->gpio_io + YU_GPIO_DATACLEAR, - NULL, - NULL, - 0); + ret = gpio_generic_chip_init(&gs->chip, &config); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize the generic GPIO chip\n"); gc->direction_input = mlxbf2_gpio_direction_input; gc->direction_output = mlxbf2_gpio_direction_output; gc->ngpio = npins; gc->owner = THIS_MODULE; + irq = platform_get_irq_optional(pdev, 0); + if (irq >= 0) { + girq = &gs->chip.gc.irq; + gpio_irq_chip_set_chip(girq, &mlxbf2_gpio_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, mlxbf2_gpio_irq_handler, + IRQF_SHARED, name, 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; - } + ret = devm_gpiochip_add_data(dev, &gs->chip.gc, gs); + if (ret) + return dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n"); return 0; } -#ifdef CONFIG_PM -static int mlxbf2_gpio_suspend(struct platform_device *pdev, - pm_message_t state) +static int mlxbf2_gpio_suspend(struct device *dev) { - struct mlxbf2_gpio_context *gs = platform_get_drvdata(pdev); + struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev); gs->csave_regs->gpio_mode0 = readl(gs->gpio_io + YU_GPIO_MODE0); @@ -296,9 +436,9 @@ static int mlxbf2_gpio_suspend(struct platform_device *pdev, return 0; } -static int mlxbf2_gpio_resume(struct platform_device *pdev) +static int mlxbf2_gpio_resume(struct device *dev) { - struct mlxbf2_gpio_context *gs = platform_get_drvdata(pdev); + struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev); writel(gs->csave_regs->gpio_mode0, gs->gpio_io + YU_GPIO_MODE0); @@ -307,9 +447,9 @@ static int mlxbf2_gpio_resume(struct platform_device *pdev) return 0; } -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume); -static const struct acpi_device_id mlxbf2_gpio_acpi_match[] = { +static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = { { "MLNXBF22", 0 }, {}, }; @@ -318,17 +458,14 @@ MODULE_DEVICE_TABLE(acpi, mlxbf2_gpio_acpi_match); static struct platform_driver mlxbf2_gpio_driver = { .driver = { .name = "mlxbf2_gpio", - .acpi_match_table = ACPI_PTR(mlxbf2_gpio_acpi_match), + .acpi_match_table = mlxbf2_gpio_acpi_match, + .pm = pm_sleep_ptr(&mlxbf2_pm_ops), }, .probe = mlxbf2_gpio_probe, -#ifdef CONFIG_PM - .suspend = mlxbf2_gpio_suspend, - .resume = mlxbf2_gpio_resume, -#endif }; module_platform_driver(mlxbf2_gpio_driver); MODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver"); -MODULE_AUTHOR("Mellanox Technologies"); +MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>"); MODULE_LICENSE("GPL v2"); |
