From a316f214bb0ee283b8751401a20b651dd1383081 Mon Sep 17 00:00:00 2001 From: Nikita Shubin Date: Sun, 14 Apr 2024 15:38:50 +0300 Subject: [PATCH] [stage 2] gpio: pcie-edu: Add vector irq support Add per pin/value interrupts support, each interrupt is connected to pin low/high event and data handling moved completely to driver as we can't rely on QEMU registers anymore. Signed-off-by: Nikita Shubin --- drivers/gpio/gpio-pcie-edu.c | 112 ++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-pcie-edu.c b/drivers/gpio/gpio-pcie-edu.c index d0618e2a05a3b..0a99e2782d0f7 100644 --- a/drivers/gpio/gpio-pcie-edu.c +++ b/drivers/gpio/gpio-pcie-edu.c @@ -16,6 +16,7 @@ #include #include #include +#include #define GPIO_EDU_СFG 0x0 #define GPIO_EDU_СFG_OUTPUT BIT(0) @@ -33,16 +34,24 @@ #define GPIO_EDU_PIN_STRIDE 0x10 #define GPIO_EDU_PIN_CNT 8 -#define GPIO_EDU_MSIX_VECTORS_CNT 1 +#define GPIO_EDU_MSIX_VECTORS_CNT (GPIO_EDU_PIN_CNT * 2) + +struct gpio_edu_vector { + struct gpio_edu *parent; + int idx; + int val; +}; struct gpio_edu { struct device *dev; struct regmap *map; struct irq_chip *irq_chip; struct irq_domain *irq_domain; - unsigned long ien; - unsigned long fallen; - unsigned long riseen; + unsigned long ien; + unsigned long fallen; + unsigned long riseen; + unsigned long data; + struct gpio_edu_vector vec[GPIO_EDU_MSIX_VECTORS_CNT]; }; static int gpio_edu_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) @@ -89,13 +98,56 @@ static irqreturn_t gpio_edu_isr(int virq, void *data) return IRQ_HANDLED; } -static int gpio_edu_setup_irqs(struct pci_dev *pdev, struct gpio_edu *edu) +static irqreturn_t gpio_edu_vec_isr(int virq, void *data) +{ + struct gpio_edu_vector *vec = data; + struct gpio_edu *edu = vec->parent; + bool raise_irq; + + if (vec->val) { + set_bit(vec->idx, &edu->data); + raise_irq = test_bit(vec->idx, &edu->riseen); + } else { + clear_bit(vec->idx, &edu->data); + raise_irq = test_bit(vec->idx, &edu->fallen); + } + + if (raise_irq) + generic_handle_domain_irq(edu->irq_domain, vec->idx); + + return IRQ_HANDLED; +} + +static int gpio_edu_setup_irqs(struct pci_dev *pdev, struct gpio_edu *edu, + int num_vec) { - int rc, irq_base; + struct gpio_edu_vector *vec; + int rc, irq_base, i; + + switch (num_vec) { + case 1: + rc = devm_request_irq(&pdev->dev, pci_irq_vector(pdev, 0), + gpio_edu_isr, 0, + "gpio_edu_isr", edu); + break; + case GPIO_EDU_MSIX_VECTORS_CNT: + for (i = 0; i < num_vec; i++) { + vec = &edu->vec[i]; + rc = devm_request_irq(&pdev->dev, pci_irq_vector(pdev, i), + gpio_edu_vec_isr, 0, + "gpio_edu_vec_isr", vec); + if (rc) + break; + + vec->parent = edu; + vec->idx = i / 2; + vec->val = i % 2; + } + break; + default: + return -EINVAL; + } - rc = devm_request_irq(&pdev->dev, pci_irq_vector(pdev, 0), - gpio_edu_isr, 0, - "gpio_edu_vec_isr", edu); if (rc) return rc; @@ -167,15 +219,24 @@ static int gpio_edu_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigne return 0; } +static int gpio_edu_get(struct gpio_chip *chip, unsigned int nr) +{ + struct device *dev = chip->parent; + struct gpio_edu *gpio_edu = dev_get_platdata(dev); + + return test_bit(nr, &gpio_edu->data); +} + static int gpio_edu_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct gpio_regmap_config gpio_config = {}; const char *const name = pci_name(pdev); struct device *const dev = &pdev->dev; + struct gpio_device *gpio_dev; struct gpio_edu *gpioedu; unsigned int cfg; void __iomem *regs; - int err; + int err, num_vec; err = pcim_enable_device(pdev); if (err) @@ -197,16 +258,16 @@ static int gpio_edu_probe(struct pci_dev *pdev, const struct pci_device_id *id) return dev_err_probe(dev, PTR_ERR(gpioedu->map), "Unable to initialize register map\n"); - err = pci_alloc_irq_vectors(pdev, - GPIO_EDU_MSIX_VECTORS_CNT, + num_vec = pci_alloc_irq_vectors(pdev, + 1, GPIO_EDU_MSIX_VECTORS_CNT, PCI_IRQ_MSIX); - if (err < 0) - return dev_err_probe(dev, err, + if (num_vec < 0) + return dev_err_probe(dev, num_vec, "Unable to allocate irqs\n"); gpioedu->irq_chip = &edu_irq_chip; - err = gpio_edu_setup_irqs(pdev, gpioedu); + err = gpio_edu_setup_irqs(pdev, gpioedu, num_vec); if (err) return dev_err_probe(dev, err, "Failed to setup irqs"); @@ -236,7 +297,26 @@ static int gpio_edu_probe(struct pci_dev *pdev, const struct pci_device_id *id) gpio_config.drvdata = gpioedu->map; gpio_config.irq_domain = gpioedu->irq_domain; - return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); + err = PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); + if (err) + return err; + + if (num_vec == 1) + return 0; + + gpio_dev = gpio_device_find_by_fwnode(dev_fwnode(dev)); + if (!gpio_dev) { + return dev_err_probe(dev, -EINVAL, + "Failed finding self"); + } + + gpio_dev->chip->get = gpio_edu_get; + + gpio_device_put(gpio_dev); + + dev->platform_data = gpioedu; + + return 0; } static const struct pci_device_id gpio_edu_pci_dev_id[] = { -- 2.30.2