Merge branch 'rework/fast-next-seq' into for-linus
[linux-2.6-microblaze.git] / drivers / gpio / gpio-mlxbf2.c
index 40a052b..3d89912 100644 (file)
@@ -1,9 +1,14 @@
 // SPDX-License-Identifier: GPL-2.0
 
+/*
+ * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES
+ */
+
 #include <linux/bitfield.h>
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #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
 
 struct mlxbf2_gpio_context_save_regs {
        u32 gpio_mode0;
@@ -55,6 +65,7 @@ struct mlxbf2_gpio_context_save_regs {
 /* BlueField-2 gpio block context structure. */
 struct mlxbf2_gpio_context {
        struct gpio_chip gc;
+       struct irq_chip irq_chip;
 
        /* YU GPIO blocks address */
        void __iomem *gpio_io;
@@ -218,15 +229,114 @@ 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);
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
+       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);
+       spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
+}
+
+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);
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
+       val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
+       val &= ~BIT(offset);
+       writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
+       spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
+}
+
+static irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr)
+{
+       struct mlxbf2_gpio_context *gs = ptr;
+       struct gpio_chip *gc = &gs->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) {
+               int gpio_irq = irq_find_mapping(gc->irq.domain, level);
+               generic_handle_irq(gpio_irq);
+       }
+
+       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);
+       unsigned long flags;
+       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;
+       }
+
+       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);
+
+       return 0;
+}
+
 /* BlueField-2 GPIO driver initialization routine. */
 static int
 mlxbf2_gpio_probe(struct platform_device *pdev)
 {
        struct mlxbf2_gpio_context *gs;
        struct device *dev = &pdev->dev;
+       struct gpio_irq_chip *girq;
        struct gpio_chip *gc;
        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)
@@ -266,6 +376,34 @@ mlxbf2_gpio_probe(struct platform_device *pdev)
        gc->ngpio = npins;
        gc->owner = THIS_MODULE;
 
+       irq = platform_get_irq(pdev, 0);
+       if (irq >= 0) {
+               gs->irq_chip.name = name;
+               gs->irq_chip.irq_set_type = mlxbf2_gpio_irq_set_type;
+               gs->irq_chip.irq_enable = mlxbf2_gpio_irq_enable;
+               gs->irq_chip.irq_disable = mlxbf2_gpio_irq_disable;
+
+               girq = &gs->gc.irq;
+               girq->chip = &gs->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) {
+                       dev_err(dev, "failed to request IRQ");
+                       return ret;
+               }
+       }
+
        platform_set_drvdata(pdev, gs);
 
        ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
@@ -320,5 +458,5 @@ static struct platform_driver mlxbf2_gpio_driver = {
 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");