2017-08-18 00:53:45

by David Daney

[permalink] [raw]
Subject: [PATCH v8 0/7] genirq/gpio: Add driver for ThunderX and OCTEON-TX SoCs

The ThunderX/OCTEON-TX GPIO hardware looks like a PCIe device, with
the interrupt signal from each GPIO line being routed to a dedicated
MSI-X. This interrupt routing requires that we add some custom
processing to the beginning of the MSI-X irqdomain hierarchy.

Changes from v7:

- Refactoring of irqdomain.c changes as suggested by Marc Zyngier.

- Renaming of new handler functions in chip.c and making building of
these conditional on a Kconfig variable, suggested by Thomas Gleixner.

Changes from v6:

- Trivial rebase to v4.13.0-rc4: Omit device tree bindings patch,
which was already merged, and fix MAINTAINERS patch collision.

Changes from v5:

- Added .irq_{request,release}_resources() functions to irq_chip.

- Quit calling irq_set_irq_type() in the irq_map() function.

- Added more error condition checking to irq_domain_{push,pop}_irq()

Changes from v4:

- Rebased to post-v4.10 to support .set_config() function.

- Added .get_direction() support.

- Added PIN_CONFIG_INPUT_DEBOUNCE support.

- Removed some improper use of ENOSYS.

Changes from v3:

- Add some "depends on" to the driver Kconfig to avoid build errors
in some architectures when doing COMPILE_TEST builds.

Changes from v2:

- in 4/6: Added Rob Harring's Acked-by

- Added three patches to genirq/irqdomain to support interrupt code
in the driver.

- Rewrite irq code in driver to use irqdomain hierarchy.

- Other naming and style changes as recommended by Linus Walleij.

Changes from v1:

- in 1/3: Addressed Rob Harring's comments.

- in 2/3: Trivial cleanups found in internal review + add some
comments.

David Daney (7):
genirq: Export more irq_chip_*_parent() functions.
genirq: Add handle_fasteoi_{level,edge}_irq flow handlers.
irqdomain: Factor out code to add and remove items to and from the
revmap
irqdomain: Check for NULL function pointer in
irq_domain_free_irqs_hierarchy()
irqdomain: Add irq_domain_{push,pop}_irq() functions.
gpio: Add gpio driver support for ThunderX and OCTEON-TX
MAINTAINERS: Add entry for THUNDERX GPIO Driver.

MAINTAINERS | 5 +
drivers/gpio/Kconfig | 9 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-thunderx.c | 639 +++++++++++++++++++++++++++++++++++++++++++
include/linux/irq.h | 2 +
include/linux/irqdomain.h | 3 +
kernel/irq/Kconfig | 4 +
kernel/irq/chip.c | 109 ++++++++
kernel/irq/irqdomain.c | 230 ++++++++++++++--
9 files changed, 972 insertions(+), 30 deletions(-)
create mode 100644 drivers/gpio/gpio-thunderx.c

--
1.8.3.1


2017-08-18 00:53:49

by David Daney

[permalink] [raw]
Subject: [PATCH v8 1/7] genirq: Export more irq_chip_*_parent() functions.

Many of the family of functions including irq_chip_mask_parent(),
irq_chip_unmask_parent() are exported, but not all.

Add EXPORT_SYMBOL_GPL to irq_chip_enable_parent,
irq_chip_disable_parent and irq_chip_set_affinity_parent, so they
likewise are usable from modules.

Signed-off-by: David Daney <[email protected]>
---
kernel/irq/chip.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index a3cc37c..6514f07 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -1105,6 +1105,7 @@ void irq_chip_enable_parent(struct irq_data *data)
else
data->chip->irq_unmask(data);
}
+EXPORT_SYMBOL_GPL(irq_chip_enable_parent);

/**
* irq_chip_disable_parent - Disable the parent interrupt (defaults to mask if
@@ -1119,6 +1120,7 @@ void irq_chip_disable_parent(struct irq_data *data)
else
data->chip->irq_mask(data);
}
+EXPORT_SYMBOL_GPL(irq_chip_disable_parent);

/**
* irq_chip_ack_parent - Acknowledge the parent interrupt
@@ -1181,6 +1183,7 @@ int irq_chip_set_affinity_parent(struct irq_data *data,

return -ENOSYS;
}
+EXPORT_SYMBOL_GPL(irq_chip_set_affinity_parent);

/**
* irq_chip_set_type_parent - Set IRQ type on the parent interrupt
--
1.8.3.1

2017-08-18 00:53:55

by David Daney

[permalink] [raw]
Subject: [PATCH v8 4/7] irqdomain: Check for NULL function pointer in irq_domain_free_irqs_hierarchy()

A follow-on patch will call irq_domain_free_irqs_hierarchy() when the
free() function pointer may be NULL.

Add a NULL pointer check to handle this new use case.

Signed-off-by: David Daney <[email protected]>
---
kernel/irq/irqdomain.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 2093b88..24fda75 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1362,7 +1362,8 @@ static void irq_domain_free_irqs_hierarchy(struct irq_domain *domain,
unsigned int irq_base,
unsigned int nr_irqs)
{
- domain->ops->free(domain, irq_base, nr_irqs);
+ if (domain->ops->free)
+ domain->ops->free(domain, irq_base, nr_irqs);
}

int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain,
--
1.8.3.1

2017-08-18 00:54:02

by David Daney

[permalink] [raw]
Subject: [PATCH v8 6/7] gpio: Add gpio driver support for ThunderX and OCTEON-TX

Cavium ThunderX and OCTEON-TX are arm64 based SoCs. Add driver for
the on-chip GPIO pins.

Signed-off-by: David Daney <[email protected]>
---
drivers/gpio/Kconfig | 9 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-thunderx.c | 639 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 649 insertions(+)
create mode 100644 drivers/gpio/gpio-thunderx.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 461d6fc..ddf6643 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -450,6 +450,15 @@ config GPIO_TS4800
help
This driver support TS-4800 FPGA GPIO controllers.

+config GPIO_THUNDERX
+ tristate "Cavium ThunderX/OCTEON-TX GPIO"
+ depends on ARCH_THUNDER || (64BIT && COMPILE_TEST)
+ depends on PCI_MSI && IRQ_DOMAIN_HIERARCHY
+ select IRQ_FASTEOI_HIERARCHY_HANDLERS
+ help
+ Say yes here to support the on-chip GPIO lines on the ThunderX
+ and OCTEON-TX families of SoCs.
+
config GPIO_TZ1090
bool "Toumaz Xenif TZ1090 GPIO support"
depends on SOC_TZ1090
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index a9fda6c..9ae168a 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -113,6 +113,7 @@ obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o
+obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o
diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c
new file mode 100644
index 0000000..57efb25
--- /dev/null
+++ b/drivers/gpio/gpio-thunderx.c
@@ -0,0 +1,639 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2016, 2017 Cavium Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+
+#define GPIO_RX_DAT 0x0
+#define GPIO_TX_SET 0x8
+#define GPIO_TX_CLR 0x10
+#define GPIO_CONST 0x90
+#define GPIO_CONST_GPIOS_MASK 0xff
+#define GPIO_BIT_CFG 0x400
+#define GPIO_BIT_CFG_TX_OE BIT(0)
+#define GPIO_BIT_CFG_PIN_XOR BIT(1)
+#define GPIO_BIT_CFG_INT_EN BIT(2)
+#define GPIO_BIT_CFG_INT_TYPE BIT(3)
+#define GPIO_BIT_CFG_FIL_MASK GENMASK(11, 4)
+#define GPIO_BIT_CFG_FIL_CNT_SHIFT 4
+#define GPIO_BIT_CFG_FIL_SEL_SHIFT 8
+#define GPIO_BIT_CFG_TX_OD BIT(12)
+#define GPIO_BIT_CFG_PIN_SEL_MASK GENMASK(25, 16)
+#define GPIO_INTR 0x800
+#define GPIO_INTR_INTR BIT(0)
+#define GPIO_INTR_INTR_W1S BIT(1)
+#define GPIO_INTR_ENA_W1C BIT(2)
+#define GPIO_INTR_ENA_W1S BIT(3)
+#define GPIO_2ND_BANK 0x1400
+
+#define GLITCH_FILTER_400NS ((4u << GPIO_BIT_CFG_FIL_SEL_SHIFT) | \
+ (9u << GPIO_BIT_CFG_FIL_CNT_SHIFT))
+
+struct thunderx_gpio;
+
+struct thunderx_line {
+ struct thunderx_gpio *txgpio;
+ unsigned int line;
+ unsigned int fil_bits;
+};
+
+struct thunderx_gpio {
+ struct gpio_chip chip;
+ u8 __iomem *register_base;
+ struct irq_domain *irqd;
+ struct msix_entry *msix_entries; /* per line MSI-X */
+ struct thunderx_line *line_entries; /* per line irq info */
+ raw_spinlock_t lock;
+ unsigned long invert_mask[2];
+ unsigned long od_mask[2];
+ int base_msi;
+};
+
+static unsigned int bit_cfg_reg(unsigned int line)
+{
+ return 8 * line + GPIO_BIT_CFG;
+}
+
+static unsigned int intr_reg(unsigned int line)
+{
+ return 8 * line + GPIO_INTR;
+}
+
+static bool thunderx_gpio_is_gpio_nowarn(struct thunderx_gpio *txgpio,
+ unsigned int line)
+{
+ u64 bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
+
+ return (bit_cfg & GPIO_BIT_CFG_PIN_SEL_MASK) == 0;
+}
+
+/*
+ * Check (and WARN) that the pin is available for GPIO. We will not
+ * allow modification of the state of non-GPIO pins from this driver.
+ */
+static bool thunderx_gpio_is_gpio(struct thunderx_gpio *txgpio,
+ unsigned int line)
+{
+ bool rv = thunderx_gpio_is_gpio_nowarn(txgpio, line);
+
+ WARN_RATELIMIT(!rv, "Pin %d not available for GPIO\n", line);
+
+ return rv;
+}
+
+static int thunderx_gpio_request(struct gpio_chip *chip, unsigned int line)
+{
+ struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+
+ return thunderx_gpio_is_gpio(txgpio, line) ? 0 : -EIO;
+}
+
+static int thunderx_gpio_dir_in(struct gpio_chip *chip, unsigned int line)
+{
+ struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+
+ if (!thunderx_gpio_is_gpio(txgpio, line))
+ return -EIO;
+
+ raw_spin_lock(&txgpio->lock);
+ clear_bit(line, txgpio->invert_mask);
+ clear_bit(line, txgpio->od_mask);
+ writeq(txgpio->line_entries[line].fil_bits,
+ txgpio->register_base + bit_cfg_reg(line));
+ raw_spin_unlock(&txgpio->lock);
+ return 0;
+}
+
+static void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line,
+ int value)
+{
+ struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+ int bank = line / 64;
+ int bank_bit = line % 64;
+
+ void __iomem *reg = txgpio->register_base +
+ (bank * GPIO_2ND_BANK) + (value ? GPIO_TX_SET : GPIO_TX_CLR);
+
+ writeq(BIT_ULL(bank_bit), reg);
+}
+
+static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line,
+ int value)
+{
+ struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+ u64 bit_cfg = txgpio->line_entries[line].fil_bits | GPIO_BIT_CFG_TX_OE;
+
+ if (!thunderx_gpio_is_gpio(txgpio, line))
+ return -EIO;
+
+ raw_spin_lock(&txgpio->lock);
+
+ thunderx_gpio_set(chip, line, value);
+
+ if (test_bit(line, txgpio->invert_mask))
+ bit_cfg |= GPIO_BIT_CFG_PIN_XOR;
+
+ if (test_bit(line, txgpio->od_mask))
+ bit_cfg |= GPIO_BIT_CFG_TX_OD;
+
+ writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(line));
+
+ raw_spin_unlock(&txgpio->lock);
+ return 0;
+}
+
+static int thunderx_gpio_get_direction(struct gpio_chip *chip, unsigned int line)
+{
+ struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+ u64 bit_cfg;
+
+ if (!thunderx_gpio_is_gpio_nowarn(txgpio, line))
+ /*
+ * Say it is input for now to avoid WARNing on
+ * gpiochip_add_data(). We will WARN if someone
+ * requests it or tries to use it.
+ */
+ return 1;
+
+ bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
+
+ return !(bit_cfg & GPIO_BIT_CFG_TX_OE);
+}
+
+static int thunderx_gpio_set_config(struct gpio_chip *chip,
+ unsigned int line,
+ unsigned long cfg)
+{
+ bool orig_invert, orig_od, orig_dat, new_invert, new_od;
+ u32 arg, sel;
+ u64 bit_cfg;
+ int bank = line / 64;
+ int bank_bit = line % 64;
+ int ret = -ENOTSUPP;
+ struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+ void __iomem *reg = txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET;
+
+ if (!thunderx_gpio_is_gpio(txgpio, line))
+ return -EIO;
+
+ raw_spin_lock(&txgpio->lock);
+ orig_invert = test_bit(line, txgpio->invert_mask);
+ new_invert = orig_invert;
+ orig_od = test_bit(line, txgpio->od_mask);
+ new_od = orig_od;
+ orig_dat = ((readq(reg) >> bank_bit) & 1) ^ orig_invert;
+ bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
+ switch (pinconf_to_config_param(cfg)) {
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ /*
+ * Weird, setting open-drain mode causes signal
+ * inversion. Note this so we can compensate in the
+ * dir_out function.
+ */
+ set_bit(line, txgpio->invert_mask);
+ new_invert = true;
+ set_bit(line, txgpio->od_mask);
+ new_od = true;
+ ret = 0;
+ break;
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ clear_bit(line, txgpio->invert_mask);
+ new_invert = false;
+ clear_bit(line, txgpio->od_mask);
+ new_od = false;
+ ret = 0;
+ break;
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ arg = pinconf_to_config_argument(cfg);
+ if (arg > 1228) { /* 15 * 2^15 * 2.5nS maximum */
+ ret = -EINVAL;
+ break;
+ }
+ arg *= 400; /* scale to 2.5nS clocks. */
+ sel = 0;
+ while (arg > 15) {
+ sel++;
+ arg++; /* always round up */
+ arg >>= 1;
+ }
+ txgpio->line_entries[line].fil_bits =
+ (sel << GPIO_BIT_CFG_FIL_SEL_SHIFT) |
+ (arg << GPIO_BIT_CFG_FIL_CNT_SHIFT);
+ bit_cfg &= ~GPIO_BIT_CFG_FIL_MASK;
+ bit_cfg |= txgpio->line_entries[line].fil_bits;
+ writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(line));
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+ raw_spin_unlock(&txgpio->lock);
+
+ /*
+ * If currently output and OPEN_DRAIN changed, install the new
+ * settings
+ */
+ if ((new_invert != orig_invert || new_od != orig_od) &&
+ (bit_cfg & GPIO_BIT_CFG_TX_OE))
+ ret = thunderx_gpio_dir_out(chip, line, orig_dat ^ new_invert);
+
+ return ret;
+}
+
+static int thunderx_gpio_get(struct gpio_chip *chip, unsigned int line)
+{
+ struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+ int bank = line / 64;
+ int bank_bit = line % 64;
+ u64 read_bits = readq(txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_RX_DAT);
+ u64 masked_bits = read_bits & BIT_ULL(bank_bit);
+
+ if (test_bit(line, txgpio->invert_mask))
+ return masked_bits == 0;
+ else
+ return masked_bits != 0;
+}
+
+static void thunderx_gpio_set_multiple(struct gpio_chip *chip,
+ unsigned long *mask,
+ unsigned long *bits)
+{
+ int bank;
+ u64 set_bits, clear_bits;
+ struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+
+ for (bank = 0; bank <= chip->ngpio / 64; bank++) {
+ set_bits = bits[bank] & mask[bank];
+ clear_bits = ~bits[bank] & mask[bank];
+ writeq(set_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET);
+ writeq(clear_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_CLR);
+ }
+}
+
+static void thunderx_gpio_irq_ack(struct irq_data *data)
+{
+ struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+
+ writeq(GPIO_INTR_INTR,
+ txline->txgpio->register_base + intr_reg(txline->line));
+}
+
+static void thunderx_gpio_irq_mask(struct irq_data *data)
+{
+ struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+
+ writeq(GPIO_INTR_ENA_W1C,
+ txline->txgpio->register_base + intr_reg(txline->line));
+}
+
+static void thunderx_gpio_irq_mask_ack(struct irq_data *data)
+{
+ struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+
+ writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR,
+ txline->txgpio->register_base + intr_reg(txline->line));
+}
+
+static void thunderx_gpio_irq_unmask(struct irq_data *data)
+{
+ struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+
+ writeq(GPIO_INTR_ENA_W1S,
+ txline->txgpio->register_base + intr_reg(txline->line));
+}
+
+static int thunderx_gpio_irq_set_type(struct irq_data *data,
+ unsigned int flow_type)
+{
+ struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+ struct thunderx_gpio *txgpio = txline->txgpio;
+ u64 bit_cfg;
+
+ irqd_set_trigger_type(data, flow_type);
+
+ bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN;
+
+ if (flow_type & IRQ_TYPE_EDGE_BOTH) {
+ irq_set_handler_locked(data, handle_fasteoi_ack_irq);
+ bit_cfg |= GPIO_BIT_CFG_INT_TYPE;
+ } else {
+ irq_set_handler_locked(data, handle_fasteoi_mask_irq);
+ }
+
+ raw_spin_lock(&txgpio->lock);
+ if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)) {
+ bit_cfg |= GPIO_BIT_CFG_PIN_XOR;
+ set_bit(txline->line, txgpio->invert_mask);
+ } else {
+ clear_bit(txline->line, txgpio->invert_mask);
+ }
+ clear_bit(txline->line, txgpio->od_mask);
+ writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(txline->line));
+ raw_spin_unlock(&txgpio->lock);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static void thunderx_gpio_irq_enable(struct irq_data *data)
+{
+ irq_chip_enable_parent(data);
+ thunderx_gpio_irq_unmask(data);
+}
+
+static void thunderx_gpio_irq_disable(struct irq_data *data)
+{
+ thunderx_gpio_irq_mask(data);
+ irq_chip_disable_parent(data);
+}
+
+static int thunderx_gpio_irq_request_resources(struct irq_data *data)
+{
+ struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+ struct thunderx_gpio *txgpio = txline->txgpio;
+ struct irq_data *parent_data = data->parent_data;
+ int r;
+
+ r = gpiochip_lock_as_irq(&txgpio->chip, txline->line);
+ if (r)
+ return r;
+
+ if (parent_data && parent_data->chip->irq_request_resources) {
+ r = parent_data->chip->irq_request_resources(parent_data);
+ if (r)
+ goto error;
+ }
+
+ return 0;
+error:
+ gpiochip_unlock_as_irq(&txgpio->chip, txline->line);
+ return r;
+}
+
+static void thunderx_gpio_irq_release_resources(struct irq_data *data)
+{
+ struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+ struct thunderx_gpio *txgpio = txline->txgpio;
+ struct irq_data *parent_data = data->parent_data;
+
+ if (parent_data && parent_data->chip->irq_release_resources)
+ parent_data->chip->irq_release_resources(parent_data);
+
+ gpiochip_unlock_as_irq(&txgpio->chip, txline->line);
+}
+
+/*
+ * Interrupts are chained from underlying MSI-X vectors. We have
+ * these irq_chip functions to be able to handle level triggering
+ * semantics and other acknowledgment tasks associated with the GPIO
+ * mechanism.
+ */
+static struct irq_chip thunderx_gpio_irq_chip = {
+ .name = "GPIO",
+ .irq_enable = thunderx_gpio_irq_enable,
+ .irq_disable = thunderx_gpio_irq_disable,
+ .irq_ack = thunderx_gpio_irq_ack,
+ .irq_mask = thunderx_gpio_irq_mask,
+ .irq_mask_ack = thunderx_gpio_irq_mask_ack,
+ .irq_unmask = thunderx_gpio_irq_unmask,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .irq_request_resources = thunderx_gpio_irq_request_resources,
+ .irq_release_resources = thunderx_gpio_irq_release_resources,
+ .irq_set_type = thunderx_gpio_irq_set_type,
+
+ .flags = IRQCHIP_SET_TYPE_MASKED
+};
+
+static int thunderx_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct thunderx_gpio *txgpio = d->host_data;
+
+ if (hwirq >= txgpio->chip.ngpio)
+ return -EINVAL;
+ if (!thunderx_gpio_is_gpio_nowarn(txgpio, hwirq))
+ return -EPERM;
+ return 0;
+}
+
+static int thunderx_gpio_irq_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ irq_hw_number_t *hwirq,
+ unsigned int *type)
+{
+ struct thunderx_gpio *txgpio = d->host_data;
+
+ if (WARN_ON(fwspec->param_count < 2))
+ return -EINVAL;
+ if (fwspec->param[0] >= txgpio->chip.ngpio)
+ return -EINVAL;
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+}
+
+static int thunderx_gpio_irq_alloc(struct irq_domain *d, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ struct thunderx_line *txline = arg;
+
+ return irq_domain_set_hwirq_and_chip(d, virq, txline->line,
+ &thunderx_gpio_irq_chip, txline);
+}
+
+static const struct irq_domain_ops thunderx_gpio_irqd_ops = {
+ .map = thunderx_gpio_irq_map,
+ .alloc = thunderx_gpio_irq_alloc,
+ .translate = thunderx_gpio_irq_translate
+};
+
+static int thunderx_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+
+ return irq_find_mapping(txgpio->irqd, offset);
+}
+
+static int thunderx_gpio_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ void __iomem * const *tbl;
+ struct device *dev = &pdev->dev;
+ struct thunderx_gpio *txgpio;
+ struct gpio_chip *chip;
+ int ngpio, i;
+ int err = 0;
+
+ txgpio = devm_kzalloc(dev, sizeof(*txgpio), GFP_KERNEL);
+ if (!txgpio)
+ return -ENOMEM;
+
+ raw_spin_lock_init(&txgpio->lock);
+ chip = &txgpio->chip;
+
+ pci_set_drvdata(pdev, txgpio);
+
+ err = pcim_enable_device(pdev);
+ if (err) {
+ dev_err(dev, "Failed to enable PCI device: err %d\n", err);
+ goto out;
+ }
+
+ err = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
+ if (err) {
+ dev_err(dev, "Failed to iomap PCI device: err %d\n", err);
+ goto out;
+ }
+
+ tbl = pcim_iomap_table(pdev);
+ txgpio->register_base = tbl[0];
+ if (!txgpio->register_base) {
+ dev_err(dev, "Cannot map PCI resource\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (pdev->subsystem_device == 0xa10a) {
+ /* CN88XX has no GPIO_CONST register*/
+ ngpio = 50;
+ txgpio->base_msi = 48;
+ } else {
+ u64 c = readq(txgpio->register_base + GPIO_CONST);
+
+ ngpio = c & GPIO_CONST_GPIOS_MASK;
+ txgpio->base_msi = (c >> 8) & 0xff;
+ }
+
+ txgpio->msix_entries = devm_kzalloc(dev,
+ sizeof(struct msix_entry) * ngpio,
+ GFP_KERNEL);
+ if (!txgpio->msix_entries) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ txgpio->line_entries = devm_kzalloc(dev,
+ sizeof(struct thunderx_line) * ngpio,
+ GFP_KERNEL);
+ if (!txgpio->line_entries) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < ngpio; i++) {
+ u64 bit_cfg = readq(txgpio->register_base + bit_cfg_reg(i));
+
+ txgpio->msix_entries[i].entry = txgpio->base_msi + (2 * i);
+ txgpio->line_entries[i].line = i;
+ txgpio->line_entries[i].txgpio = txgpio;
+ /*
+ * If something has already programmed the pin, use
+ * the existing glitch filter settings, otherwise go
+ * to 400nS.
+ */
+ txgpio->line_entries[i].fil_bits = bit_cfg ?
+ (bit_cfg & GPIO_BIT_CFG_FIL_MASK) : GLITCH_FILTER_400NS;
+
+ if ((bit_cfg & GPIO_BIT_CFG_TX_OE) && (bit_cfg & GPIO_BIT_CFG_TX_OD))
+ set_bit(i, txgpio->od_mask);
+ if (bit_cfg & GPIO_BIT_CFG_PIN_XOR)
+ set_bit(i, txgpio->invert_mask);
+ }
+
+
+ /* Enable all MSI-X for interrupts on all possible lines. */
+ err = pci_enable_msix_range(pdev, txgpio->msix_entries, ngpio, ngpio);
+ if (err < 0)
+ goto out;
+
+ /*
+ * Push GPIO specific irqdomain on hierarchy created as a side
+ * effect of the pci_enable_msix()
+ */
+ txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain,
+ 0, 0, of_node_to_fwnode(dev->of_node),
+ &thunderx_gpio_irqd_ops, txgpio);
+ if (!txgpio->irqd)
+ goto out;
+
+ /* Push on irq_data and the domain for each line. */
+ for (i = 0; i < ngpio; i++) {
+ err = irq_domain_push_irq(txgpio->irqd,
+ txgpio->msix_entries[i].vector,
+ &txgpio->line_entries[i]);
+ if (err < 0)
+ dev_err(dev, "irq_domain_push_irq: %d\n", err);
+ }
+
+ chip->label = KBUILD_MODNAME;
+ chip->parent = dev;
+ chip->owner = THIS_MODULE;
+ chip->request = thunderx_gpio_request;
+ chip->base = -1; /* System allocated */
+ chip->can_sleep = false;
+ chip->ngpio = ngpio;
+ chip->get_direction = thunderx_gpio_get_direction;
+ chip->direction_input = thunderx_gpio_dir_in;
+ chip->get = thunderx_gpio_get;
+ chip->direction_output = thunderx_gpio_dir_out;
+ chip->set = thunderx_gpio_set;
+ chip->set_multiple = thunderx_gpio_set_multiple;
+ chip->set_config = thunderx_gpio_set_config;
+ chip->to_irq = thunderx_gpio_to_irq;
+ err = devm_gpiochip_add_data(dev, chip, txgpio);
+ if (err)
+ goto out;
+
+ dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n",
+ ngpio, chip->base);
+ return 0;
+out:
+ pci_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static void thunderx_gpio_remove(struct pci_dev *pdev)
+{
+ int i;
+ struct thunderx_gpio *txgpio = pci_get_drvdata(pdev);
+
+ for (i = 0; i < txgpio->chip.ngpio; i++)
+ irq_domain_pop_irq(txgpio->irqd,
+ txgpio->msix_entries[i].vector);
+
+ irq_domain_remove(txgpio->irqd);
+
+ pci_set_drvdata(pdev, NULL);
+}
+
+static const struct pci_device_id thunderx_gpio_id_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA00A) },
+ { 0, } /* end of table */
+};
+
+MODULE_DEVICE_TABLE(pci, thunderx_gpio_id_table);
+
+static struct pci_driver thunderx_gpio_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = thunderx_gpio_id_table,
+ .probe = thunderx_gpio_probe,
+ .remove = thunderx_gpio_remove,
+};
+
+module_pci_driver(thunderx_gpio_driver);
+
+MODULE_DESCRIPTION("Cavium Inc. ThunderX/OCTEON-TX GPIO Driver");
+MODULE_LICENSE("GPL");
--
1.8.3.1

2017-08-18 00:54:19

by David Daney

[permalink] [raw]
Subject: [PATCH v8 7/7] MAINTAINERS: Add entry for THUNDERX GPIO Driver.

Signed-off-by: David Daney <[email protected]>
---
MAINTAINERS | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 6f7721d..67d4000 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12993,6 +12993,11 @@ M: Yehezkel Bernat <[email protected]>
S: Maintained
F: drivers/thunderbolt/

+THUNDERX GPIO DRIVER
+M: David Daney <[email protected]>
+S: Maintained
+F: drivers/gpio/gpio-thunderx.c
+
TI AM437X VPFE DRIVER
M: "Lad, Prabhakar" <[email protected]>
L: [email protected]
--
1.8.3.1

2017-08-18 00:54:40

by David Daney

[permalink] [raw]
Subject: [PATCH v8 5/7] irqdomain: Add irq_domain_{push,pop}_irq() functions.

For an already existing irqdomain hierarchy, as might be obtained via
a call to pci_enable_msix_range(), a PCI driver wishing to add an
additional irqdomain to the hierarchy needs to be able to insert the
irqdomain to that already initialized hierarchy. Calling
irq_domain_create_hierarchy() allows the new irqdomain to be created,
but no existing code allows for initializing the associated irq_data.

Add a couple of helper functions (irq_domain_push_irq() and
irq_domain_pop_irq()) to initialize the irq_data for the new
irqdomain added to an existing hierarchy.

Signed-off-by: David Daney <[email protected]>
---
include/linux/irqdomain.h | 3 +
kernel/irq/irqdomain.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 172 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index cac77a5..2318f29 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -460,6 +460,9 @@ extern void irq_domain_free_irqs_common(struct irq_domain *domain,
extern void irq_domain_free_irqs_top(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs);

+extern int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg);
+extern int irq_domain_pop_irq(struct irq_domain *domain, int virq);
+
extern int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
unsigned int irq_base,
unsigned int nr_irqs, void *arg);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 24fda75..1ff9912 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1449,6 +1449,175 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
return ret;
}

+/* The irq_data was moved, fix the revmap to refer to the new location */
+static void irq_domain_fix_revmap(struct irq_data *d)
+{
+ void **slot;
+
+ if (d->hwirq < d->domain->revmap_size)
+ return; /* Not using radix tree. */
+
+ /* Fix up the revmap. */
+ mutex_lock(&revmap_trees_mutex);
+ slot = radix_tree_lookup_slot(&d->domain->revmap_tree, d->hwirq);
+ if (slot)
+ radix_tree_replace_slot(&d->domain->revmap_tree, slot, d);
+ mutex_unlock(&revmap_trees_mutex);
+}
+
+/**
+ * irq_domain_push_irq() - Push a domain in to the top of a hierarchy.
+ * @domain: Domain to push.
+ * @virq: Irq to push the domain in to.
+ * @arg: Passed to the irq_domain_ops alloc() function.
+ *
+ * For an already existing irqdomain hierarchy, as might be obtained
+ * via a call to pci_enable_msix(), add an additional domain to the
+ * head of the processing chain. Must be called before request_irq()
+ * has been called.
+ */
+int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg)
+{
+ struct irq_data *child_irq_data;
+ struct irq_data *root_irq_data = irq_get_irq_data(virq);
+ struct irq_desc *desc;
+ int rv = 0;
+
+ /*
+ * Check that no action has been set, which indicates the virq
+ * is in a state where this function doesn't have to deal with
+ * races between interrupt handling and maintaining the
+ * hierarchy. This will catch gross misuse. Attempting to
+ * make the check race free would require holding locks across
+ * calls to struct irq_domain_ops->alloc(), which could lead
+ * to deadlock, so we just do a simple check before starting.
+ */
+ desc = irq_to_desc(virq);
+ if (!desc)
+ return -EINVAL;
+ if (WARN_ON(desc->action))
+ return -EBUSY;
+
+ if (domain == NULL)
+ return -EINVAL;
+
+ if (WARN_ON(!irq_domain_is_hierarchy(domain)))
+ return -EINVAL;
+
+ if (domain->parent != root_irq_data->domain)
+ return -EINVAL;
+
+ if (!root_irq_data)
+ return -EINVAL;
+
+ child_irq_data = kzalloc_node(sizeof(*child_irq_data), GFP_KERNEL,
+ irq_data_get_node(root_irq_data));
+ if (!child_irq_data)
+ return -ENOMEM;
+
+ mutex_lock(&irq_domain_mutex);
+
+ /* Copy the original irq_data. */
+ *child_irq_data = *root_irq_data;
+
+ /*
+ * Overwrite the root_irq_data, which is embedded in struct
+ * irq_desc, with values for this domain.
+ */
+ root_irq_data->parent_data = child_irq_data;
+ root_irq_data->domain = domain;
+ root_irq_data->mask = 0;
+ root_irq_data->hwirq = 0;
+ root_irq_data->chip = NULL;
+ root_irq_data->chip_data = NULL;
+
+ /* May (probably does) set hwirq, chip, etc. */
+ rv = irq_domain_alloc_irqs_hierarchy(domain, virq, 1, arg);
+ if (rv) {
+ /* Restore the original irq_data. */
+ *root_irq_data = *child_irq_data;
+ goto error;
+ }
+
+ irq_domain_fix_revmap(child_irq_data);
+ irq_domain_set_mapping(domain, root_irq_data->hwirq, root_irq_data);
+
+error:
+ mutex_unlock(&irq_domain_mutex);
+
+ return rv;
+}
+EXPORT_SYMBOL_GPL(irq_domain_push_irq);
+
+/**
+ * irq_domain_pop_irq() - Remove a domain from the top of a hierarchy.
+ * @domain: Domain to remove.
+ * @virq: Irq to remove the domain from.
+ *
+ * Undo the effects of a call to irq_domain_push_irq(). Must be
+ * called either before request_irq() or after free_irq().
+ */
+int irq_domain_pop_irq(struct irq_domain *domain, int virq)
+{
+ struct irq_data *root_irq_data = irq_get_irq_data(virq);
+ struct irq_data *child_irq_data;
+ struct irq_data *tmp_irq_data;
+ struct irq_desc *desc;
+
+ /*
+ * Check that no action is set, which indicates the virq is in
+ * a state where this function doesn't have to deal with races
+ * between interrupt handling and maintaining the hierarchy.
+ * This will catch gross misuse. Attempting to make the check
+ * race free would require holding locks across calls to
+ * struct irq_domain_ops->free(), which could lead to
+ * deadlock, so we just do a simple check before starting.
+ */
+ desc = irq_to_desc(virq);
+ if (!desc)
+ return -EINVAL;
+ if (WARN_ON(desc->action))
+ return -EBUSY;
+
+ if (domain == NULL)
+ return -EINVAL;
+
+ if (!root_irq_data)
+ return -EINVAL;
+
+ tmp_irq_data = irq_domain_get_irq_data(domain, virq);
+
+ /* We can only "pop" if this domain is at the top of the list */
+ if (WARN_ON(root_irq_data != tmp_irq_data))
+ return -EINVAL;
+
+ if (WARN_ON(root_irq_data->domain != domain))
+ return -EINVAL;
+
+ child_irq_data = root_irq_data->parent_data;
+ if (WARN_ON(!child_irq_data))
+ return -EINVAL;
+
+ mutex_lock(&irq_domain_mutex);
+
+ root_irq_data->parent_data = NULL;
+
+ irq_domain_clear_mapping(domain, root_irq_data->hwirq);
+ irq_domain_free_irqs_hierarchy(domain, virq, 1);
+
+ /* Restore the original irq_data. */
+ *root_irq_data = *child_irq_data;
+
+ irq_domain_fix_revmap(root_irq_data);
+
+ mutex_unlock(&irq_domain_mutex);
+
+ kfree(child_irq_data);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(irq_domain_pop_irq);
+
/**
* irq_domain_free_irqs - Free IRQ number and associated data structures
* @virq: base IRQ number
--
1.8.3.1

2017-08-18 00:55:00

by David Daney

[permalink] [raw]
Subject: [PATCH v8 3/7] irqdomain: Factor out code to add and remove items to and from the revmap

The code to add and remove items to and from the revmap occurs several
times.

In preparation for the follow on patches that add more uses of this
code, factor this out in to separate static functions.

Signed-off-by: David Daney <[email protected]>
---
kernel/irq/irqdomain.c | 58 +++++++++++++++++++++++++-------------------------
1 file changed, 29 insertions(+), 29 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index f1f2514..2093b88 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -455,6 +455,31 @@ void irq_set_default_host(struct irq_domain *domain)
}
EXPORT_SYMBOL_GPL(irq_set_default_host);

+static void irq_domain_clear_mapping(struct irq_domain *domain,
+ irq_hw_number_t hwirq)
+{
+ if (hwirq < domain->revmap_size) {
+ domain->linear_revmap[hwirq] = 0;
+ } else {
+ mutex_lock(&revmap_trees_mutex);
+ radix_tree_delete(&domain->revmap_tree, hwirq);
+ mutex_unlock(&revmap_trees_mutex);
+ }
+}
+
+static void irq_domain_set_mapping(struct irq_domain *domain,
+ irq_hw_number_t hwirq,
+ struct irq_data *irq_data)
+{
+ if (hwirq < domain->revmap_size) {
+ domain->linear_revmap[hwirq] = irq_data->irq;
+ } else {
+ mutex_lock(&revmap_trees_mutex);
+ radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
+ mutex_unlock(&revmap_trees_mutex);
+ }
+}
+
void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
{
struct irq_data *irq_data = irq_get_irq_data(irq);
@@ -483,13 +508,7 @@ void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
domain->mapcount--;

/* Clear reverse map for this hwirq */
- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = 0;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_delete(&domain->revmap_tree, hwirq);
- mutex_unlock(&revmap_trees_mutex);
- }
+ irq_domain_clear_mapping(domain, hwirq);
}

int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
@@ -533,13 +552,7 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
}

domain->mapcount++;
- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = virq;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
- mutex_unlock(&revmap_trees_mutex);
- }
+ irq_domain_set_mapping(domain, hwirq, irq_data);
mutex_unlock(&irq_domain_mutex);

irq_clear_status_flags(virq, IRQ_NOREQUEST);
@@ -1138,16 +1151,9 @@ static void irq_domain_insert_irq(int virq)

for (data = irq_get_irq_data(virq); data; data = data->parent_data) {
struct irq_domain *domain = data->domain;
- irq_hw_number_t hwirq = data->hwirq;

domain->mapcount++;
- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = virq;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_insert(&domain->revmap_tree, hwirq, data);
- mutex_unlock(&revmap_trees_mutex);
- }
+ irq_domain_set_mapping(domain, data->hwirq, data);

/* If not already assigned, give the domain the chip's name */
if (!domain->name && data->chip)
@@ -1171,13 +1177,7 @@ static void irq_domain_remove_irq(int virq)
irq_hw_number_t hwirq = data->hwirq;

domain->mapcount--;
- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = 0;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_delete(&domain->revmap_tree, hwirq);
- mutex_unlock(&revmap_trees_mutex);
- }
+ irq_domain_clear_mapping(domain, hwirq);
}
}

--
1.8.3.1

2017-08-18 00:55:02

by David Daney

[permalink] [raw]
Subject: [PATCH v8 2/7] genirq: Add handle_fasteoi_{level,edge}_irq flow handlers.

Follow-on patch for gpio-thunderx uses a irqdomain hierarchy which
requires slightly different flow handlers, add them to chip.c which
contains most of the other flow handlers. Make these conditionally
compiled based on CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS.

Signed-off-by: David Daney <[email protected]>
---
include/linux/irq.h | 2 +
kernel/irq/Kconfig | 4 ++
kernel/irq/chip.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 112 insertions(+)

diff --git a/include/linux/irq.h b/include/linux/irq.h
index d2d54379..d4728bf 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -568,6 +568,8 @@ static inline int irq_set_parent(int irq, int parent_irq)
extern int irq_chip_pm_get(struct irq_data *data);
extern int irq_chip_pm_put(struct irq_data *data);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+extern void handle_fasteoi_ack_irq(struct irq_desc *desc);
+extern void handle_fasteoi_mask_irq(struct irq_desc *desc);
extern void irq_chip_enable_parent(struct irq_data *data);
extern void irq_chip_disable_parent(struct irq_data *data);
extern void irq_chip_ack_parent(struct irq_data *data);
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
index 27c4e77..aada687 100644
--- a/kernel/irq/Kconfig
+++ b/kernel/irq/Kconfig
@@ -68,6 +68,10 @@ config IRQ_DOMAIN_HIERARCHY
bool
select IRQ_DOMAIN

+# Support for hierarchical fasteoi+edge and fasteoi+level handlers
+config IRQ_FASTEOI_HIERARCHY_HANDLERS
+ bool
+
# Generic IRQ IPI support
config GENERIC_IRQ_IPI
bool
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 6514f07..2395898 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -1092,6 +1092,112 @@ void irq_cpu_offline(void)
}

#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+
+#ifdef CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS
+/**
+ * handle_fasteoi_ack_irq - irq handler for edge hierarchy
+ * stacked on transparent controllers
+ *
+ * @desc: the interrupt description structure for this irq
+ *
+ * Like handle_fasteoi_irq(), but for use with hierarchy where
+ * the irq_chip also needs to have its ->irq_ack() function
+ * called.
+ */
+void handle_fasteoi_ack_irq(struct irq_desc *desc)
+{
+ struct irq_chip *chip = desc->irq_data.chip;
+
+ raw_spin_lock(&desc->lock);
+
+ if (!irq_may_run(desc))
+ goto out;
+
+ desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
+
+ /*
+ * If its disabled or no action available
+ * then mask it and get out of here:
+ */
+ if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
+ desc->istate |= IRQS_PENDING;
+ mask_irq(desc);
+ goto out;
+ }
+
+ kstat_incr_irqs_this_cpu(desc);
+ if (desc->istate & IRQS_ONESHOT)
+ mask_irq(desc);
+
+ /* Start handling the irq */
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+
+ preflow_handler(desc);
+ handle_irq_event(desc);
+
+ cond_unmask_eoi_irq(desc, chip);
+
+ raw_spin_unlock(&desc->lock);
+ return;
+out:
+ if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
+ chip->irq_eoi(&desc->irq_data);
+ raw_spin_unlock(&desc->lock);
+}
+EXPORT_SYMBOL_GPL(handle_fasteoi_ack_irq);
+
+/**
+ * handle_fasteoi_mask_irq - irq handler for level hierarchy
+ * stacked on transparent controllers
+ *
+ * @desc: the interrupt description structure for this irq
+ *
+ * Like handle_fasteoi_irq(), but for use with hierarchy where
+ * the irq_chip also needs to have its ->irq_mask_ack() function
+ * called.
+ */
+void handle_fasteoi_mask_irq(struct irq_desc *desc)
+{
+ struct irq_chip *chip = desc->irq_data.chip;
+
+ raw_spin_lock(&desc->lock);
+ mask_ack_irq(desc);
+
+ if (!irq_may_run(desc))
+ goto out;
+
+ desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
+
+ /*
+ * If its disabled or no action available
+ * then mask it and get out of here:
+ */
+ if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
+ desc->istate |= IRQS_PENDING;
+ mask_irq(desc);
+ goto out;
+ }
+
+ kstat_incr_irqs_this_cpu(desc);
+ if (desc->istate & IRQS_ONESHOT)
+ mask_irq(desc);
+
+ preflow_handler(desc);
+ handle_irq_event(desc);
+
+ cond_unmask_eoi_irq(desc, chip);
+
+ raw_spin_unlock(&desc->lock);
+ return;
+out:
+ if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
+ chip->irq_eoi(&desc->irq_data);
+ raw_spin_unlock(&desc->lock);
+}
+EXPORT_SYMBOL_GPL(handle_fasteoi_mask_irq);
+
+#endif /* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */
+
/**
* irq_chip_enable_parent - Enable the parent interrupt (defaults to unmask if
* NULL)
--
1.8.3.1

2017-08-18 09:15:41

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v8 3/7] irqdomain: Factor out code to add and remove items to and from the revmap

On 18/08/17 01:53, David Daney wrote:
> The code to add and remove items to and from the revmap occurs several
> times.
>
> In preparation for the follow on patches that add more uses of this
> code, factor this out in to separate static functions.
>
> Signed-off-by: David Daney <[email protected]>

Reviewed-by: Marc Zyngier <[email protected]>

M.
--
Jazz is not dead. It just smells funny...

2017-08-18 09:16:15

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v8 4/7] irqdomain: Check for NULL function pointer in irq_domain_free_irqs_hierarchy()

On 18/08/17 01:53, David Daney wrote:
> A follow-on patch will call irq_domain_free_irqs_hierarchy() when the
> free() function pointer may be NULL.
>
> Add a NULL pointer check to handle this new use case.
>
> Signed-off-by: David Daney <[email protected]>

Reviewed-by: Marc Zyngier <[email protected]>

M.
--
Jazz is not dead. It just smells funny...

2017-08-18 09:17:34

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v8 5/7] irqdomain: Add irq_domain_{push,pop}_irq() functions.

On 18/08/17 01:53, David Daney wrote:
> For an already existing irqdomain hierarchy, as might be obtained via
> a call to pci_enable_msix_range(), a PCI driver wishing to add an
> additional irqdomain to the hierarchy needs to be able to insert the
> irqdomain to that already initialized hierarchy. Calling
> irq_domain_create_hierarchy() allows the new irqdomain to be created,
> but no existing code allows for initializing the associated irq_data.
>
> Add a couple of helper functions (irq_domain_push_irq() and
> irq_domain_pop_irq()) to initialize the irq_data for the new
> irqdomain added to an existing hierarchy.
>
> Signed-off-by: David Daney <[email protected]>
Reviewed-by: Marc Zyngier <[email protected]>

M.
--
Jazz is not dead. It just smells funny...

2017-08-18 09:25:04

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH v8 0/7] genirq/gpio: Add driver for ThunderX and OCTEON-TX SoCs

On Thu, 17 Aug 2017, David Daney wrote:

> The ThunderX/OCTEON-TX GPIO hardware looks like a PCIe device, with
> the interrupt signal from each GPIO line being routed to a dedicated
> MSI-X. This interrupt routing requires that we add some custom
> processing to the beginning of the MSI-X irqdomain hierarchy.

I merged the irq core patches into

git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/for-gpio

This branch can be pulled into the GPIO tree to avoid pull request
dependencies.

Thanks,

tglx

2017-08-18 09:28:10

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v8 2/7] genirq: Add handle_fasteoi_{level,edge}_irq flow handlers.

On 18/08/17 01:53, David Daney wrote:
> Follow-on patch for gpio-thunderx uses a irqdomain hierarchy which
> requires slightly different flow handlers, add them to chip.c which
> contains most of the other flow handlers. Make these conditionally
> compiled based on CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS.
>
> Signed-off-by: David Daney <[email protected]>

Nit: $SUBJECT doesn't reflect the new function names.

> ---
> include/linux/irq.h | 2 +
> kernel/irq/Kconfig | 4 ++
> kernel/irq/chip.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 112 insertions(+)
>
> diff --git a/include/linux/irq.h b/include/linux/irq.h
> index d2d54379..d4728bf 100644
> --- a/include/linux/irq.h
> +++ b/include/linux/irq.h
> @@ -568,6 +568,8 @@ static inline int irq_set_parent(int irq, int parent_irq)
> extern int irq_chip_pm_get(struct irq_data *data);
> extern int irq_chip_pm_put(struct irq_data *data);
> #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
> +extern void handle_fasteoi_ack_irq(struct irq_desc *desc);
> +extern void handle_fasteoi_mask_irq(struct irq_desc *desc);
> extern void irq_chip_enable_parent(struct irq_data *data);
> extern void irq_chip_disable_parent(struct irq_data *data);
> extern void irq_chip_ack_parent(struct irq_data *data);
> diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
> index 27c4e77..aada687 100644
> --- a/kernel/irq/Kconfig
> +++ b/kernel/irq/Kconfig
> @@ -68,6 +68,10 @@ config IRQ_DOMAIN_HIERARCHY
> bool
> select IRQ_DOMAIN
>
> +# Support for hierarchical fasteoi+edge and fasteoi+level handlers
> +config IRQ_FASTEOI_HIERARCHY_HANDLERS
> + bool
> +
> # Generic IRQ IPI support
> config GENERIC_IRQ_IPI
> bool
> diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
> index 6514f07..2395898 100644
> --- a/kernel/irq/chip.c
> +++ b/kernel/irq/chip.c
> @@ -1092,6 +1092,112 @@ void irq_cpu_offline(void)
> }
>
> #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
> +
> +#ifdef CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS
> +/**
> + * handle_fasteoi_ack_irq - irq handler for edge hierarchy
> + * stacked on transparent controllers
> + *
> + * @desc: the interrupt description structure for this irq
> + *
> + * Like handle_fasteoi_irq(), but for use with hierarchy where
> + * the irq_chip also needs to have its ->irq_ack() function
> + * called.
> + */
> +void handle_fasteoi_ack_irq(struct irq_desc *desc)
> +{
> + struct irq_chip *chip = desc->irq_data.chip;
> +
> + raw_spin_lock(&desc->lock);
> +
> + if (!irq_may_run(desc))
> + goto out;
> +
> + desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
> +
> + /*
> + * If its disabled or no action available
> + * then mask it and get out of here:
> + */
> + if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
> + desc->istate |= IRQS_PENDING;
> + mask_irq(desc);
> + goto out;
> + }
> +
> + kstat_incr_irqs_this_cpu(desc);
> + if (desc->istate & IRQS_ONESHOT)
> + mask_irq(desc);
> +
> + /* Start handling the irq */
> + desc->irq_data.chip->irq_ack(&desc->irq_data);
> +
> + preflow_handler(desc);
> + handle_irq_event(desc);
> +
> + cond_unmask_eoi_irq(desc, chip);
> +
> + raw_spin_unlock(&desc->lock);
> + return;
> +out:
> + if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
> + chip->irq_eoi(&desc->irq_data);
> + raw_spin_unlock(&desc->lock);
> +}
> +EXPORT_SYMBOL_GPL(handle_fasteoi_ack_irq);
> +
> +/**
> + * handle_fasteoi_mask_irq - irq handler for level hierarchy
> + * stacked on transparent controllers
> + *
> + * @desc: the interrupt description structure for this irq
> + *
> + * Like handle_fasteoi_irq(), but for use with hierarchy where
> + * the irq_chip also needs to have its ->irq_mask_ack() function
> + * called.
> + */
> +void handle_fasteoi_mask_irq(struct irq_desc *desc)
> +{
> + struct irq_chip *chip = desc->irq_data.chip;
> +
> + raw_spin_lock(&desc->lock);
> + mask_ack_irq(desc);
> +
> + if (!irq_may_run(desc))
> + goto out;
> +
> + desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
> +
> + /*
> + * If its disabled or no action available
> + * then mask it and get out of here:
> + */
> + if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
> + desc->istate |= IRQS_PENDING;
> + mask_irq(desc);
> + goto out;
> + }
> +
> + kstat_incr_irqs_this_cpu(desc);
> + if (desc->istate & IRQS_ONESHOT)
> + mask_irq(desc);
> +
> + preflow_handler(desc);
> + handle_irq_event(desc);
> +
> + cond_unmask_eoi_irq(desc, chip);
> +
> + raw_spin_unlock(&desc->lock);
> + return;
> +out:
> + if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
> + chip->irq_eoi(&desc->irq_data);
> + raw_spin_unlock(&desc->lock);
> +}
> +EXPORT_SYMBOL_GPL(handle_fasteoi_mask_irq);
> +
> +#endif /* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */
> +
> /**
> * irq_chip_enable_parent - Enable the parent interrupt (defaults to unmask if
> * NULL)
>

Acked-by: Marc Zyngier <[email protected]>

M.
--
Jazz is not dead. It just smells funny...

Subject: [tip:irq/core] genirq: Export more irq_chip_*_parent() functions

Commit-ID: 65efd9a49af8174b2283fd5b27e9edf30e4483d0
Gitweb: http://git.kernel.org/tip/65efd9a49af8174b2283fd5b27e9edf30e4483d0
Author: David Daney <[email protected]>
AuthorDate: Thu, 17 Aug 2017 17:53:30 -0700
Committer: Thomas Gleixner <[email protected]>
CommitDate: Fri, 18 Aug 2017 11:21:40 +0200

genirq: Export more irq_chip_*_parent() functions

Many of the family of functions including irq_chip_mask_parent(),
irq_chip_unmask_parent() are exported, but not all.

Add EXPORT_SYMBOL_GPL to irq_chip_enable_parent,
irq_chip_disable_parent and irq_chip_set_affinity_parent, so they
likewise are usable from modules.

Signed-off-by: David Daney <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Alexandre Courbot <[email protected]>
Cc: Marc Zyngier <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]

---
kernel/irq/chip.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index a3cc37c..6514f07 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -1105,6 +1105,7 @@ void irq_chip_enable_parent(struct irq_data *data)
else
data->chip->irq_unmask(data);
}
+EXPORT_SYMBOL_GPL(irq_chip_enable_parent);

/**
* irq_chip_disable_parent - Disable the parent interrupt (defaults to mask if
@@ -1119,6 +1120,7 @@ void irq_chip_disable_parent(struct irq_data *data)
else
data->chip->irq_mask(data);
}
+EXPORT_SYMBOL_GPL(irq_chip_disable_parent);

/**
* irq_chip_ack_parent - Acknowledge the parent interrupt
@@ -1181,6 +1183,7 @@ int irq_chip_set_affinity_parent(struct irq_data *data,

return -ENOSYS;
}
+EXPORT_SYMBOL_GPL(irq_chip_set_affinity_parent);

/**
* irq_chip_set_type_parent - Set IRQ type on the parent interrupt

Subject: [tip:irq/core] genirq: Add handle_fasteoi_{level,edge}_irq flow handlers

Commit-ID: 7703b08cc93b3586f9eb733f3a2b10bed634a5cf
Gitweb: http://git.kernel.org/tip/7703b08cc93b3586f9eb733f3a2b10bed634a5cf
Author: David Daney <[email protected]>
AuthorDate: Thu, 17 Aug 2017 17:53:31 -0700
Committer: Thomas Gleixner <[email protected]>
CommitDate: Fri, 18 Aug 2017 11:21:41 +0200

genirq: Add handle_fasteoi_{level,edge}_irq flow handlers

Follow-on patch for gpio-thunderx uses a irqdomain hierarchy which
requires slightly different flow handlers, add them to chip.c which
contains most of the other flow handlers. Make these conditionally
compiled based on CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS.

Signed-off-by: David Daney <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Alexandre Courbot <[email protected]>
Cc: Marc Zyngier <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]

---
include/linux/irq.h | 2 +
kernel/irq/Kconfig | 4 ++
kernel/irq/chip.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 112 insertions(+)

diff --git a/include/linux/irq.h b/include/linux/irq.h
index d2d54379..d4728bf 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -568,6 +568,8 @@ extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
extern int irq_chip_pm_get(struct irq_data *data);
extern int irq_chip_pm_put(struct irq_data *data);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+extern void handle_fasteoi_ack_irq(struct irq_desc *desc);
+extern void handle_fasteoi_mask_irq(struct irq_desc *desc);
extern void irq_chip_enable_parent(struct irq_data *data);
extern void irq_chip_disable_parent(struct irq_data *data);
extern void irq_chip_ack_parent(struct irq_data *data);
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
index 1d06af7..a117adf 100644
--- a/kernel/irq/Kconfig
+++ b/kernel/irq/Kconfig
@@ -73,6 +73,10 @@ config IRQ_DOMAIN_HIERARCHY
bool
select IRQ_DOMAIN

+# Support for hierarchical fasteoi+edge and fasteoi+level handlers
+config IRQ_FASTEOI_HIERARCHY_HANDLERS
+ bool
+
# Generic IRQ IPI support
config GENERIC_IRQ_IPI
bool
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 6514f07..2395898 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -1092,6 +1092,112 @@ void irq_cpu_offline(void)
}

#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+
+#ifdef CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS
+/**
+ * handle_fasteoi_ack_irq - irq handler for edge hierarchy
+ * stacked on transparent controllers
+ *
+ * @desc: the interrupt description structure for this irq
+ *
+ * Like handle_fasteoi_irq(), but for use with hierarchy where
+ * the irq_chip also needs to have its ->irq_ack() function
+ * called.
+ */
+void handle_fasteoi_ack_irq(struct irq_desc *desc)
+{
+ struct irq_chip *chip = desc->irq_data.chip;
+
+ raw_spin_lock(&desc->lock);
+
+ if (!irq_may_run(desc))
+ goto out;
+
+ desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
+
+ /*
+ * If its disabled or no action available
+ * then mask it and get out of here:
+ */
+ if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
+ desc->istate |= IRQS_PENDING;
+ mask_irq(desc);
+ goto out;
+ }
+
+ kstat_incr_irqs_this_cpu(desc);
+ if (desc->istate & IRQS_ONESHOT)
+ mask_irq(desc);
+
+ /* Start handling the irq */
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+
+ preflow_handler(desc);
+ handle_irq_event(desc);
+
+ cond_unmask_eoi_irq(desc, chip);
+
+ raw_spin_unlock(&desc->lock);
+ return;
+out:
+ if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
+ chip->irq_eoi(&desc->irq_data);
+ raw_spin_unlock(&desc->lock);
+}
+EXPORT_SYMBOL_GPL(handle_fasteoi_ack_irq);
+
+/**
+ * handle_fasteoi_mask_irq - irq handler for level hierarchy
+ * stacked on transparent controllers
+ *
+ * @desc: the interrupt description structure for this irq
+ *
+ * Like handle_fasteoi_irq(), but for use with hierarchy where
+ * the irq_chip also needs to have its ->irq_mask_ack() function
+ * called.
+ */
+void handle_fasteoi_mask_irq(struct irq_desc *desc)
+{
+ struct irq_chip *chip = desc->irq_data.chip;
+
+ raw_spin_lock(&desc->lock);
+ mask_ack_irq(desc);
+
+ if (!irq_may_run(desc))
+ goto out;
+
+ desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
+
+ /*
+ * If its disabled or no action available
+ * then mask it and get out of here:
+ */
+ if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
+ desc->istate |= IRQS_PENDING;
+ mask_irq(desc);
+ goto out;
+ }
+
+ kstat_incr_irqs_this_cpu(desc);
+ if (desc->istate & IRQS_ONESHOT)
+ mask_irq(desc);
+
+ preflow_handler(desc);
+ handle_irq_event(desc);
+
+ cond_unmask_eoi_irq(desc, chip);
+
+ raw_spin_unlock(&desc->lock);
+ return;
+out:
+ if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
+ chip->irq_eoi(&desc->irq_data);
+ raw_spin_unlock(&desc->lock);
+}
+EXPORT_SYMBOL_GPL(handle_fasteoi_mask_irq);
+
+#endif /* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */
+
/**
* irq_chip_enable_parent - Enable the parent interrupt (defaults to unmask if
* NULL)

Subject: [tip:irq/core] irqdomain: Factor out code to add and remove items to and from the revmap

Commit-ID: b526adfe1b0531fceba44b18c156e4edf9c6205c
Gitweb: http://git.kernel.org/tip/b526adfe1b0531fceba44b18c156e4edf9c6205c
Author: David Daney <[email protected]>
AuthorDate: Thu, 17 Aug 2017 17:53:32 -0700
Committer: Thomas Gleixner <[email protected]>
CommitDate: Fri, 18 Aug 2017 11:21:41 +0200

irqdomain: Factor out code to add and remove items to and from the revmap

The code to add and remove items to and from the revmap occurs several
times.

In preparation for the follow on patches that add more uses of this
code, factor this out in to separate static functions.

Signed-off-by: David Daney <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Reviewed-by: Marc Zyngier <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Alexandre Courbot <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]

---
kernel/irq/irqdomain.c | 58 +++++++++++++++++++++++++-------------------------
1 file changed, 29 insertions(+), 29 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index f1f2514..2093b88 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -455,6 +455,31 @@ void irq_set_default_host(struct irq_domain *domain)
}
EXPORT_SYMBOL_GPL(irq_set_default_host);

+static void irq_domain_clear_mapping(struct irq_domain *domain,
+ irq_hw_number_t hwirq)
+{
+ if (hwirq < domain->revmap_size) {
+ domain->linear_revmap[hwirq] = 0;
+ } else {
+ mutex_lock(&revmap_trees_mutex);
+ radix_tree_delete(&domain->revmap_tree, hwirq);
+ mutex_unlock(&revmap_trees_mutex);
+ }
+}
+
+static void irq_domain_set_mapping(struct irq_domain *domain,
+ irq_hw_number_t hwirq,
+ struct irq_data *irq_data)
+{
+ if (hwirq < domain->revmap_size) {
+ domain->linear_revmap[hwirq] = irq_data->irq;
+ } else {
+ mutex_lock(&revmap_trees_mutex);
+ radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
+ mutex_unlock(&revmap_trees_mutex);
+ }
+}
+
void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
{
struct irq_data *irq_data = irq_get_irq_data(irq);
@@ -483,13 +508,7 @@ void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
domain->mapcount--;

/* Clear reverse map for this hwirq */
- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = 0;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_delete(&domain->revmap_tree, hwirq);
- mutex_unlock(&revmap_trees_mutex);
- }
+ irq_domain_clear_mapping(domain, hwirq);
}

int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
@@ -533,13 +552,7 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
}

domain->mapcount++;
- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = virq;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
- mutex_unlock(&revmap_trees_mutex);
- }
+ irq_domain_set_mapping(domain, hwirq, irq_data);
mutex_unlock(&irq_domain_mutex);

irq_clear_status_flags(virq, IRQ_NOREQUEST);
@@ -1138,16 +1151,9 @@ static void irq_domain_insert_irq(int virq)

for (data = irq_get_irq_data(virq); data; data = data->parent_data) {
struct irq_domain *domain = data->domain;
- irq_hw_number_t hwirq = data->hwirq;

domain->mapcount++;
- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = virq;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_insert(&domain->revmap_tree, hwirq, data);
- mutex_unlock(&revmap_trees_mutex);
- }
+ irq_domain_set_mapping(domain, data->hwirq, data);

/* If not already assigned, give the domain the chip's name */
if (!domain->name && data->chip)
@@ -1171,13 +1177,7 @@ static void irq_domain_remove_irq(int virq)
irq_hw_number_t hwirq = data->hwirq;

domain->mapcount--;
- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = 0;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_delete(&domain->revmap_tree, hwirq);
- mutex_unlock(&revmap_trees_mutex);
- }
+ irq_domain_clear_mapping(domain, hwirq);
}
}


Subject: [tip:irq/core] irqdomain: Check for NULL function pointer in irq_domain_free_irqs_hierarchy()

Commit-ID: 0d12ec075a18f53e6f58ec95a4f534da2641bf9b
Gitweb: http://git.kernel.org/tip/0d12ec075a18f53e6f58ec95a4f534da2641bf9b
Author: David Daney <[email protected]>
AuthorDate: Thu, 17 Aug 2017 17:53:33 -0700
Committer: Thomas Gleixner <[email protected]>
CommitDate: Fri, 18 Aug 2017 11:21:42 +0200

irqdomain: Check for NULL function pointer in irq_domain_free_irqs_hierarchy()

A follow-on patch will call irq_domain_free_irqs_hierarchy() when the
free() function pointer may be NULL.

Add a NULL pointer check to handle this new use case.

Signed-off-by: David Daney <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Reviewed-by: Marc Zyngier <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Alexandre Courbot <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]

---
kernel/irq/irqdomain.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 2093b88..24fda75 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1362,7 +1362,8 @@ static void irq_domain_free_irqs_hierarchy(struct irq_domain *domain,
unsigned int irq_base,
unsigned int nr_irqs)
{
- domain->ops->free(domain, irq_base, nr_irqs);
+ if (domain->ops->free)
+ domain->ops->free(domain, irq_base, nr_irqs);
}

int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain,

2017-08-18 09:32:14

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH v8 2/7] genirq: Add handle_fasteoi_{level,edge}_irq flow handlers.

On Fri, 18 Aug 2017, Marc Zyngier wrote:

> On 18/08/17 01:53, David Daney wrote:
> > Follow-on patch for gpio-thunderx uses a irqdomain hierarchy which
> > requires slightly different flow handlers, add them to chip.c which
> > contains most of the other flow handlers. Make these conditionally
> > compiled based on CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS.
> >
> > Signed-off-by: David Daney <[email protected]>
>
> Nit: $SUBJECT doesn't reflect the new function names.

Bah, too late ....

Subject: [tip:irq/core] irqdomain: Add irq_domain_{push,pop}_irq() functions

Commit-ID: 495c38d3001fd226cf91df1d031320f349bcaf35
Gitweb: http://git.kernel.org/tip/495c38d3001fd226cf91df1d031320f349bcaf35
Author: David Daney <[email protected]>
AuthorDate: Thu, 17 Aug 2017 17:53:34 -0700
Committer: Thomas Gleixner <[email protected]>
CommitDate: Fri, 18 Aug 2017 11:21:42 +0200

irqdomain: Add irq_domain_{push,pop}_irq() functions

For an already existing irqdomain hierarchy, as might be obtained via
a call to pci_enable_msix_range(), a PCI driver wishing to add an
additional irqdomain to the hierarchy needs to be able to insert the
irqdomain to that already initialized hierarchy. Calling
irq_domain_create_hierarchy() allows the new irqdomain to be created,
but no existing code allows for initializing the associated irq_data.

Add a couple of helper functions (irq_domain_push_irq() and
irq_domain_pop_irq()) to initialize the irq_data for the new
irqdomain added to an existing hierarchy.

Signed-off-by: David Daney <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Reviewed-by: Marc Zyngier <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Alexandre Courbot <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]

---
include/linux/irqdomain.h | 3 +
kernel/irq/irqdomain.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 172 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index cac77a5..2318f29 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -460,6 +460,9 @@ extern void irq_domain_free_irqs_common(struct irq_domain *domain,
extern void irq_domain_free_irqs_top(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs);

+extern int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg);
+extern int irq_domain_pop_irq(struct irq_domain *domain, int virq);
+
extern int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
unsigned int irq_base,
unsigned int nr_irqs, void *arg);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 24fda75..1ff9912 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1449,6 +1449,175 @@ out_free_desc:
return ret;
}

+/* The irq_data was moved, fix the revmap to refer to the new location */
+static void irq_domain_fix_revmap(struct irq_data *d)
+{
+ void **slot;
+
+ if (d->hwirq < d->domain->revmap_size)
+ return; /* Not using radix tree. */
+
+ /* Fix up the revmap. */
+ mutex_lock(&revmap_trees_mutex);
+ slot = radix_tree_lookup_slot(&d->domain->revmap_tree, d->hwirq);
+ if (slot)
+ radix_tree_replace_slot(&d->domain->revmap_tree, slot, d);
+ mutex_unlock(&revmap_trees_mutex);
+}
+
+/**
+ * irq_domain_push_irq() - Push a domain in to the top of a hierarchy.
+ * @domain: Domain to push.
+ * @virq: Irq to push the domain in to.
+ * @arg: Passed to the irq_domain_ops alloc() function.
+ *
+ * For an already existing irqdomain hierarchy, as might be obtained
+ * via a call to pci_enable_msix(), add an additional domain to the
+ * head of the processing chain. Must be called before request_irq()
+ * has been called.
+ */
+int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg)
+{
+ struct irq_data *child_irq_data;
+ struct irq_data *root_irq_data = irq_get_irq_data(virq);
+ struct irq_desc *desc;
+ int rv = 0;
+
+ /*
+ * Check that no action has been set, which indicates the virq
+ * is in a state where this function doesn't have to deal with
+ * races between interrupt handling and maintaining the
+ * hierarchy. This will catch gross misuse. Attempting to
+ * make the check race free would require holding locks across
+ * calls to struct irq_domain_ops->alloc(), which could lead
+ * to deadlock, so we just do a simple check before starting.
+ */
+ desc = irq_to_desc(virq);
+ if (!desc)
+ return -EINVAL;
+ if (WARN_ON(desc->action))
+ return -EBUSY;
+
+ if (domain == NULL)
+ return -EINVAL;
+
+ if (WARN_ON(!irq_domain_is_hierarchy(domain)))
+ return -EINVAL;
+
+ if (domain->parent != root_irq_data->domain)
+ return -EINVAL;
+
+ if (!root_irq_data)
+ return -EINVAL;
+
+ child_irq_data = kzalloc_node(sizeof(*child_irq_data), GFP_KERNEL,
+ irq_data_get_node(root_irq_data));
+ if (!child_irq_data)
+ return -ENOMEM;
+
+ mutex_lock(&irq_domain_mutex);
+
+ /* Copy the original irq_data. */
+ *child_irq_data = *root_irq_data;
+
+ /*
+ * Overwrite the root_irq_data, which is embedded in struct
+ * irq_desc, with values for this domain.
+ */
+ root_irq_data->parent_data = child_irq_data;
+ root_irq_data->domain = domain;
+ root_irq_data->mask = 0;
+ root_irq_data->hwirq = 0;
+ root_irq_data->chip = NULL;
+ root_irq_data->chip_data = NULL;
+
+ /* May (probably does) set hwirq, chip, etc. */
+ rv = irq_domain_alloc_irqs_hierarchy(domain, virq, 1, arg);
+ if (rv) {
+ /* Restore the original irq_data. */
+ *root_irq_data = *child_irq_data;
+ goto error;
+ }
+
+ irq_domain_fix_revmap(child_irq_data);
+ irq_domain_set_mapping(domain, root_irq_data->hwirq, root_irq_data);
+
+error:
+ mutex_unlock(&irq_domain_mutex);
+
+ return rv;
+}
+EXPORT_SYMBOL_GPL(irq_domain_push_irq);
+
+/**
+ * irq_domain_pop_irq() - Remove a domain from the top of a hierarchy.
+ * @domain: Domain to remove.
+ * @virq: Irq to remove the domain from.
+ *
+ * Undo the effects of a call to irq_domain_push_irq(). Must be
+ * called either before request_irq() or after free_irq().
+ */
+int irq_domain_pop_irq(struct irq_domain *domain, int virq)
+{
+ struct irq_data *root_irq_data = irq_get_irq_data(virq);
+ struct irq_data *child_irq_data;
+ struct irq_data *tmp_irq_data;
+ struct irq_desc *desc;
+
+ /*
+ * Check that no action is set, which indicates the virq is in
+ * a state where this function doesn't have to deal with races
+ * between interrupt handling and maintaining the hierarchy.
+ * This will catch gross misuse. Attempting to make the check
+ * race free would require holding locks across calls to
+ * struct irq_domain_ops->free(), which could lead to
+ * deadlock, so we just do a simple check before starting.
+ */
+ desc = irq_to_desc(virq);
+ if (!desc)
+ return -EINVAL;
+ if (WARN_ON(desc->action))
+ return -EBUSY;
+
+ if (domain == NULL)
+ return -EINVAL;
+
+ if (!root_irq_data)
+ return -EINVAL;
+
+ tmp_irq_data = irq_domain_get_irq_data(domain, virq);
+
+ /* We can only "pop" if this domain is at the top of the list */
+ if (WARN_ON(root_irq_data != tmp_irq_data))
+ return -EINVAL;
+
+ if (WARN_ON(root_irq_data->domain != domain))
+ return -EINVAL;
+
+ child_irq_data = root_irq_data->parent_data;
+ if (WARN_ON(!child_irq_data))
+ return -EINVAL;
+
+ mutex_lock(&irq_domain_mutex);
+
+ root_irq_data->parent_data = NULL;
+
+ irq_domain_clear_mapping(domain, root_irq_data->hwirq);
+ irq_domain_free_irqs_hierarchy(domain, virq, 1);
+
+ /* Restore the original irq_data. */
+ *root_irq_data = *child_irq_data;
+
+ irq_domain_fix_revmap(root_irq_data);
+
+ mutex_unlock(&irq_domain_mutex);
+
+ kfree(child_irq_data);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(irq_domain_pop_irq);
+
/**
* irq_domain_free_irqs - Free IRQ number and associated data structures
* @virq: base IRQ number

2017-08-18 18:07:33

by David Daney

[permalink] [raw]
Subject: Re: [PATCH v8 0/7] genirq/gpio: Add driver for ThunderX and OCTEON-TX SoCs

On 08/18/2017 02:24 AM, Thomas Gleixner wrote:
> On Thu, 17 Aug 2017, David Daney wrote:
>
>> The ThunderX/OCTEON-TX GPIO hardware looks like a PCIe device, with
>> the interrupt signal from each GPIO line being routed to a dedicated
>> MSI-X. This interrupt routing requires that we add some custom
>> processing to the beginning of the MSI-X irqdomain hierarchy.
>
> I merged the irq core patches into
>
> git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/for-gpio
>
> This branch can be pulled into the GPIO tree to avoid pull request
> dependencies.
>
> Thanks,
>
> tglx
>

Thanks everybody for helping me work through the challenges of preparing
this patch set. Touching multiple subsystems under the stewardship of
multiple maintainers can be difficult, but I think this worked out well.

David Daney

2017-08-20 09:37:32

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH v8 0/7] genirq/gpio: Add driver for ThunderX and OCTEON-TX SoCs

On Fri, 18 Aug 2017, David Daney wrote:
> On 08/18/2017 02:24 AM, Thomas Gleixner wrote:
> > On Thu, 17 Aug 2017, David Daney wrote:
> >
> > > The ThunderX/OCTEON-TX GPIO hardware looks like a PCIe device, with
> > > the interrupt signal from each GPIO line being routed to a dedicated
> > > MSI-X. This interrupt routing requires that we add some custom
> > > processing to the beginning of the MSI-X irqdomain hierarchy.
> >
> > I merged the irq core patches into
> >
> > git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/for-gpio
> >
> > This branch can be pulled into the GPIO tree to avoid pull request
> > dependencies.
>
> Thanks everybody for helping me work through the challenges of preparing this
> patch set. Touching multiple subsystems under the stewardship of multiple
> maintainers can be difficult, but I think this worked out well.

Thanks for your patience and the excellent work!

tglx

2017-08-22 12:29:26

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v8 6/7] gpio: Add gpio driver support for ThunderX and OCTEON-TX

On Fri, Aug 18, 2017 at 2:53 AM, David Daney <[email protected]> wrote:

> Cavium ThunderX and OCTEON-TX are arm64 based SoCs. Add driver for
> the on-chip GPIO pins.
>
> Signed-off-by: David Daney <[email protected]>

Patch applied on top of Thomas changes that I pulled into the
GPIO tree yesterday.

Yours,
Linus Walleij

2017-08-22 12:30:25

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v8 7/7] MAINTAINERS: Add entry for THUNDERX GPIO Driver.

On Fri, Aug 18, 2017 at 2:53 AM, David Daney <[email protected]> wrote:

> Signed-off-by: David Daney <[email protected]>

Patch applied.

Yours,
Linus Walleij

2017-08-22 12:32:46

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v8 0/7] genirq/gpio: Add driver for ThunderX and OCTEON-TX SoCs

On Fri, Aug 18, 2017 at 8:07 PM, David Daney <[email protected]> wrote:

> Thanks everybody for helping me work through the challenges of preparing
> this patch set. Touching multiple subsystems under the stewardship of
> multiple maintainers can be difficult, but I think this worked out well.

I side with Thomas: thanks for working hard and doing the right thing
here where many would have given up.

It's exactly this deep integration of new drivers that make the kernel
provide such great infrastructure and clean drivers.

Now I feel I need to understand this think good enough to spot
the second driver that needs to do this already during early
review ...

Yours,
Linus Walleij