Hi Andy, Thanks for your help! Please see my comments/questions below. -----Original Message----- From: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx> Sent: Monday, August 16, 2021 8:00 AM To: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx>; David Thompson <davthompson@xxxxxxxxxx>; linux-kernel@xxxxxxxxxxxxxxx; linux-gpio@xxxxxxxxxxxxxxx; netdev@xxxxxxxxxxxxxxx; linux-acpi@xxxxxxxxxxxxxxx Cc: Linus Walleij <linus.walleij@xxxxxxxxxx>; Bartosz Golaszewski <bgolaszewski@xxxxxxxxxxxx>; David S. Miller <davem@xxxxxxxxxxxxx>; Jakub Kicinski <kuba@xxxxxxxxxx>; Rafael J. Wysocki <rjw@xxxxxxxxxxxxx>; Asmaa Mnebhi <asmaa@xxxxxxxxxx>; Liming Sun <limings@xxxxxxxxxx> Subject: [PATCH v1 5/6] TODO: gpio: mlxbf2: Introduce IRQ support Importance: High TBD Signed-off-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx> --- drivers/gpio/gpio-mlxbf2.c | 106 +++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 3ed95e958c17..bd4c29120b62 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -43,9 +43,13 @@ #define YU_GPIO_MODE0 0x0c #define YU_GPIO_DATASET 0x14 #define YU_GPIO_DATACLEAR 0x18 +#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; @@ -218,6 +222,108 @@ static int mlxbf2_gpio_direction_output(struct gpio_chip *chip, return ret; } +static void mlxbf2_gpio_irq_enable(struct mlxbf2_gpio_context *gs, int +offset) { + 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); + + /* The INT_N interrupt level is active low. + * So enable cause fall bit to detect when GPIO + * state goes low. + */ + val = readl(gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); + + /* Enable PHY interrupt by setting the priority level */ + 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 mlxbf2_gpio_context *gs, int +offset) { + 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 void mlxbf2_gpio_irq_ack(struct mlxbf2_gpio_context *gs, int +offset) { + 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); + spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); } + +static irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr) { So how do you suggest registering this handler? 1) should I still use BF_RSH0_DEVICE_YU_INT shared interrupt signal? 2) or does Linux kernel know (based on parsing GpioInt) how trigger the handler based on the GPIO datain changing (active low/high)? In this case, the kernel will call this handler whenever the GPIO pin (9 or 12) value changes. I need to check whether GPIO is active low/high but lets assume for now it is open drain active low. We will use acpi_dev_gpio_irq_get to translate GpioInt to a Linux IRQ number: irq = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(dev), " phy-gpios ", 0); ret = devm_request_irq(dev, irq, mlxbf2_gpio_irq_handler, IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), gs); And I will need to add GpioInt to the GPI0 ACPI table as follows: // GPIO Controller Device(GPI0) { Name(_HID, "MLNXBF22") Name(_UID, Zero) Name(_CCA, 1) Name(_CRS, ResourceTemplate() { // for gpio[0] yu block Memory32Fixed(ReadWrite, 0x0280c000, 0x00000100) GpioInt (Level, ActiveLow, Exclusive, PullDefault, , " \\_SB.GPI0") {9} }) Name(_DSD, Package() { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package() { Package () { "phy-gpios", Package() {^GPI0, 0, 0, 0 }}, Package () { "rst-pin", 32 }, // GPIO pin triggering soft reset on BlueSphere and PRIS } }) } + 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); + for_each_set_bit(level, &pending, gc->ngpio) { + int nested_irq = irq_find_mapping(gc->irq.domain, level); + + handle_nested_irq(nested_irq); Now how can the mlxbf_gige_main.c driver also retrieve this nested_irq to register its interrupt handler as well? This irq.domain is only visible to the gpio-mlxbf2.c driver isn't it? phydev->irq (below) should be populated with nested_irq at init time because it is used to register the phy interrupt in this generic function: void phy_request_interrupt(struct phy_device *phydev) { int err; err = request_threaded_irq(phydev->irq, NULL, phy_interrupt, IRQF_ONESHOT | IRQF_SHARED, phydev_name(phydev), phydev); if (err) { phydev_warn(phydev, "Error %d requesting IRQ %d, falling back to polling\n", err, phydev->irq); phydev->irq = PHY_POLL; } else { if (phy_enable_interrupts(phydev)) { phydev_warn(phydev, "Can't enable interrupt, falling back to polling\n"); phy_free_interrupt(phydev); phydev->irq = PHY_POLL; } } } EXPORT_SYMBOL(phy_request_interrupt); + } + + return IRQ_RETVAL(pending); +} + +static void mlxbf2_gpio_irq_mask(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) % MLXBF2_GPIO_MAX_PINS_PER_BLOCK; Why is the modulo needed? Isn't the hwirq returned a number between 0 and MLXBF2_GPIO_MAX_PINS_PER_BLOCK-1 ? + + mlxbf2_gpio_irq_disable(gs, offset); +} + +static void mlxbf2_gpio_irq_unmask(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) % MLXBF2_GPIO_MAX_PINS_PER_BLOCK; + + mlxbf2_gpio_irq_enable(gs, offset); +} + +static void mlxbf2_gpio_irq_bus_lock(struct irq_data *irqd) { + mutex_lock(yu_arm_gpio_lock_param.lock); +} + +static void mlxbf2_gpio_irq_bus_sync_unlock(struct irq_data *irqd) { + mutex_unlock(yu_arm_gpio_lock_param.lock); +} + +static struct irq_chip mlxbf2_gpio_irq_chip = { + .name = "mlxbf2_gpio", + .irq_mask = mlxbf2_gpio_irq_mask, + .irq_unmask = mlxbf2_gpio_irq_unmask, + .irq_bus_lock = mlxbf2_gpio_irq_bus_lock, + .irq_bus_sync_unlock = mlxbf2_gpio_irq_bus_sync_unlock, +}; + We also need to make sure that the gpio driver is loaded before the mlxbf-gige driver. Otherwise, the mlxbf-gige 1G interface fails to come up. I have implemented this dependency on the gpio driver before, something like this at the end of the mlxbf-gige driver: MODULE_SOFTDEP("pre: gpio_mlxbf2"); /* BlueField-2 GPIO driver initialization routine. */ static int mlxbf2_gpio_probe(struct platform_device *pdev) -- 2.30.2