#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.h>
+#include <gpiolib.h>
#define GPIO_EDU_СFG 0x0
#define GPIO_EDU_СFG_OUTPUT BIT(0)
#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)
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;
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)
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");
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[] = {