diff options
Diffstat (limited to 'drivers/gpio/gpio-thunderx.c')
-rw-r--r-- | drivers/gpio/gpio-thunderx.c | 221 |
1 files changed, 195 insertions, 26 deletions
diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index e573e469d429..686588e928ec 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -13,6 +13,7 @@ #include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_irq.h> #include <linux/pci.h> #include <linux/spinlock.h> #ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR @@ -231,8 +232,7 @@ static void cleanup_el3_irqs(struct task_struct *task) for (i = 0; i < MAX_GPIO; i++) { if (gpio_installed[i] && gpio_installed_tasks[i] && - ((gpio_installed_tasks[i] == task) || - (gpio_installed_tasks[i] == task->group_leader))) { + (gpio_installed_tasks[i] == task)) { pr_alert("Exiting, removing handler for GPIO %d\n", i); __remove_el3_inthandler(i); @@ -495,6 +495,149 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip, } } +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +static void thunderx_gpio_spi_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_INTR, + gpio->register_base + intr_reg(line)); +} + +static void thunderx_gpio_spi_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_ENA_W1C, gpio->register_base + intr_reg(line)); +} + +static void thunderx_gpio_spi_irq_mask_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR, + gpio->register_base + intr_reg(line)); +} + +static void thunderx_gpio_spi_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_ENA_W1S, gpio->register_base + intr_reg(line)); +} + +/* + * Do not set msix_entries for SPI IRQs. + */ +static int thunderx_gpio_spi_irq_request_resources(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + if (!thunderx_gpio_is_gpio(gpio, line)) + return -EIO; + + writeq(GPIO_INTR_ENA_W1C, gpio->register_base + intr_reg(line)); + + return 0; +} + +static void thunderx_gpio_spi_irq_release_resources(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_ENA_W1C, gpio->register_base + intr_reg(line)); + +} + +static int thunderx_gpio_spi_irq_set_type(struct irq_data *data, + unsigned int flow_type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + u64 bit_cfg; + unsigned long flags; + + irqd_set_trigger_type(data, flow_type); + + bit_cfg = GLITCH_FILTER_400NS | GPIO_BIT_CFG_INT_EN; + + raw_spin_lock_irqsave(&gpio->lock, flags); + if (flow_type & IRQ_TYPE_EDGE_BOTH) { + irq_set_handler_locked(data, handle_edge_irq); + bit_cfg |= GPIO_BIT_CFG_INT_TYPE; + } else { + irq_set_handler_locked(data, handle_level_irq); + } + + if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)) { + bit_cfg |= GPIO_BIT_CFG_PIN_XOR; + set_bit(line, gpio->invert_mask); + } else { + clear_bit(line, gpio->invert_mask); + } + clear_bit(line, gpio->od_mask); + writeq(bit_cfg, gpio->register_base + bit_cfg_reg(line)); + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + return IRQ_SET_MASK_OK; +} + +static void thunderx_gpio_spi_irq_handler(struct irq_desc *desc) +{ + unsigned int line; + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + + chained_irq_enter(irqchip, desc); + for (line = 0; line < chip->ngpio; line++) { + if (readq(gpio->register_base + intr_reg(line)) & + GPIO_INTR_INTR) { + generic_handle_irq(irq_find_mapping(chip->irq.domain, + line)); + writeq(GPIO_INTR_INTR, + gpio->register_base + intr_reg(line)); + } + } + chained_irq_exit(irqchip, desc); +} + +static struct irq_chip thunderx_gpio_spi_irq_chip = { + .name = "GPIO", + .irq_enable = thunderx_gpio_spi_irq_unmask, + .irq_disable = thunderx_gpio_spi_irq_mask, + .irq_ack = thunderx_gpio_spi_irq_ack, + .irq_mask = thunderx_gpio_spi_irq_mask, + .irq_mask_ack = thunderx_gpio_spi_irq_mask_ack, + .irq_unmask = thunderx_gpio_spi_irq_unmask, + .irq_set_type = thunderx_gpio_spi_irq_set_type, + .irq_request_resources = thunderx_gpio_spi_irq_request_resources, + .irq_release_resources = thunderx_gpio_spi_irq_release_resources, + .flags = IRQCHIP_SET_TYPE_MASKED +}; +#endif + static void thunderx_gpio_irq_ack(struct irq_data *data) { struct thunderx_line *txline = irq_data_get_irq_chip_data(data); @@ -539,6 +682,7 @@ static int thunderx_gpio_irq_set_type(struct irq_data *data, bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN; + raw_spin_lock_irqsave(&txgpio->lock, flags); if (flow_type & IRQ_TYPE_EDGE_BOTH) { irq_set_handler_locked(data, handle_fasteoi_ack_irq); bit_cfg |= GPIO_BIT_CFG_INT_TYPE; @@ -546,7 +690,6 @@ static int thunderx_gpio_irq_set_type(struct irq_data *data, irq_set_handler_locked(data, handle_fasteoi_mask_irq); } - raw_spin_lock_irqsave(&txgpio->lock, flags); 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); @@ -723,10 +866,15 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, goto out; } +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR + pdev->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); +#endif + txgpio->line_entries = devm_kcalloc(dev, ngpio, sizeof(struct thunderx_line), GFP_KERNEL); + if (!txgpio->line_entries) { err = -ENOMEM; goto out; @@ -752,31 +900,32 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, 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) { - err = -ENOMEM; - goto out; - } + if (pdev->irq == 0) { + /* + * 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) { + err = -ENOMEM; + 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); + /* 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; @@ -802,6 +951,20 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, ngpio, chip->base); #ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR + if (pdev->irq != 0) { + err = gpiochip_irqchip_add(chip, &thunderx_gpio_spi_irq_chip, 0, + handle_bad_irq, IRQ_TYPE_NONE); + if (err) { + dev_err(dev, "gpiochip_irqchip_add failed: %d\n", err); + goto irqchip_out; + } + + gpiochip_set_chained_irqchip(chip, + &thunderx_gpio_spi_irq_chip, + pdev->irq, + thunderx_gpio_spi_irq_handler); + } + /* Register task cleanup handler */ err = task_cleanup_handler_add(cleanup_el3_irqs); if (err != 0) { @@ -858,6 +1021,8 @@ cdev_alloc_err: alloc_chrdev_err: task_cleanup_handler_remove(cleanup_el3_irqs); cleanup_handler_err: +irqchip_out: + gpiochip_remove(chip); #endif out: pci_set_drvdata(pdev, NULL); @@ -869,11 +1034,15 @@ 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); + if (pdev->irq == 0) { + for (i = 0; i < txgpio->chip.ngpio; i++) + irq_domain_pop_irq(txgpio->irqd, + txgpio->msix_entries[i].vector); - irq_domain_remove(txgpio->irqd); + irq_domain_remove(txgpio->irqd); + } else { + gpiochip_remove(&txgpio->chip); + } pci_set_drvdata(pdev, NULL); |