diff options
Diffstat (limited to 'drivers/gpio/gpio-thunderx.c')
-rw-r--r-- | drivers/gpio/gpio-thunderx.c | 523 |
1 files changed, 489 insertions, 34 deletions
diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 715371b5102a..686588e928ec 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -13,9 +13,19 @@ #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 +#include <linux/arm-smccc.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/moduleparam.h> +#include <linux/uaccess.h> +#include <linux/mmu_context.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#endif #define GPIO_RX_DAT 0x0 #define GPIO_TX_SET 0x8 @@ -31,17 +41,63 @@ #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_BIT_CFG_PIN_SEL_MASK GENMASK(26, 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 MRVL_OCTEONTX2_96XX_PARTNUM 0xB2 + #define GLITCH_FILTER_400NS ((4u << GPIO_BIT_CFG_FIL_SEL_SHIFT) | \ (9u << GPIO_BIT_CFG_FIL_CNT_SHIFT)) +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +#define DEVICE_NAME "otx-gpio-ctr" +#define OTX_IOC_MAGIC 0xF2 +#define MAX_GPIO 80 + +static struct device *otx_device; +static struct class *otx_class; +static struct cdev *otx_cdev; +static dev_t otx_dev; +static DEFINE_SPINLOCK(el3_inthandler_lock); +static int gpio_in_use; +static int gpio_installed[MAX_GPIO]; +static struct thread_info *gpio_installed_threads[MAX_GPIO]; +static struct task_struct *gpio_installed_tasks[MAX_GPIO]; + +/* THUNDERX SMC definitons */ +/* X1 - gpio_num, X2 - sp, X3 - cpu, X4 - ttbr0 */ +#define THUNDERX_INSTALL_GPIO_INT 0xC2000801 +/* X1 - gpio_num */ +#define THUNDERX_REMOVE_GPIO_INT 0xC2000802 + +struct intr_hand { + u64 mask; + char name[50]; + u64 coffset; + u64 soffset; + irqreturn_t (*handler)(int, void *); +}; + +struct otx_gpio_usr_data { + u64 isr_base; + u64 sp; + u64 cpu; + u64 gpio_num; +}; + + +#define OTX_IOC_SET_GPIO_HANDLER \ + _IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data) + +#define OTX_IOC_CLR_GPIO_HANDLER \ + _IO(OTX_IOC_MAGIC, 2) +#endif + struct thunderx_gpio; struct thunderx_line { @@ -62,6 +118,159 @@ struct thunderx_gpio { int base_msi; }; +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +static inline int __install_el3_inthandler(unsigned long gpio_num, + unsigned long sp, + unsigned long cpu, + unsigned long ttbr0) +{ + struct arm_smccc_res res; + unsigned long flags; + int retval = -1; + + spin_lock_irqsave(&el3_inthandler_lock, flags); + if (!gpio_installed[gpio_num]) { + lock_context(current->group_leader->mm, gpio_num); + arm_smccc_smc(THUNDERX_INSTALL_GPIO_INT, gpio_num, + sp, cpu, ttbr0, 0, 0, 0, &res); + if (res.a0 == 0) { + gpio_installed[gpio_num] = 1; + gpio_installed_threads[gpio_num] + = current_thread_info(); + gpio_installed_tasks[gpio_num] + = current->group_leader; + retval = 0; + } else { + unlock_context_by_index(gpio_num); + } + } + spin_unlock_irqrestore(&el3_inthandler_lock, flags); + return retval; +} + +static inline int __remove_el3_inthandler(unsigned long gpio_num) +{ + struct arm_smccc_res res; + unsigned long flags; + unsigned int retval; + + spin_lock_irqsave(&el3_inthandler_lock, flags); + if (gpio_installed[gpio_num]) { + arm_smccc_smc(THUNDERX_REMOVE_GPIO_INT, gpio_num, + 0, 0, 0, 0, 0, 0, &res); + gpio_installed[gpio_num] = 0; + gpio_installed_threads[gpio_num] = NULL; + gpio_installed_tasks[gpio_num] = NULL; + unlock_context_by_index(gpio_num); + retval = 0; + } else { + retval = -1; + } + spin_unlock_irqrestore(&el3_inthandler_lock, flags); + return retval; +} + +static long otx_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct otx_gpio_usr_data gpio_usr; + u64 gpio_ttbr, gpio_isr_base, gpio_sp, gpio_cpu, gpio_num; + int ret; + //struct task_struct *task = current; + + if (!gpio_in_use) + return -EINVAL; + + if (_IOC_TYPE(cmd) != OTX_IOC_MAGIC) + return -ENOTTY; + + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_TYPE(cmd) & _IOC_WRITE) + err = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); + + if (err) + return -EFAULT; + + switch (cmd) { + case OTX_IOC_SET_GPIO_HANDLER: /*Install GPIO ISR handler*/ + ret = copy_from_user(&gpio_usr, (void *)arg, _IOC_SIZE(cmd)); + if (gpio_usr.gpio_num >= MAX_GPIO) + return -EINVAL; + if (ret) + return -EFAULT; + gpio_ttbr = 0; + //TODO: reserve a asid to avoid asid rollovers + asm volatile("mrs %0, ttbr0_el1\n\t" : "=r"(gpio_ttbr)); + gpio_isr_base = gpio_usr.isr_base; + gpio_sp = gpio_usr.sp; + gpio_cpu = gpio_usr.cpu; + gpio_num = gpio_usr.gpio_num; + ret = __install_el3_inthandler(gpio_num, gpio_sp, + gpio_cpu, gpio_isr_base); + if (ret != 0) + return -EEXIST; + break; + case OTX_IOC_CLR_GPIO_HANDLER: /*Clear GPIO ISR handler*/ + gpio_usr.gpio_num = arg; + if (gpio_usr.gpio_num >= MAX_GPIO) + return -EINVAL; + ret = __remove_el3_inthandler(gpio_usr.gpio_num); + if (ret != 0) + return -ENOENT; + break; + default: + return -ENOTTY; + } + return 0; +} + +static void cleanup_el3_irqs(struct task_struct *task) +{ + int i; + + for (i = 0; i < MAX_GPIO; i++) { + if (gpio_installed[i] && + gpio_installed_tasks[i] && + (gpio_installed_tasks[i] == task)) { + pr_alert("Exiting, removing handler for GPIO %d\n", + i); + __remove_el3_inthandler(i); + pr_alert("Exited, removed handler for GPIO %d\n", + i); + } else { + if (gpio_installed[i] && + (gpio_installed_threads[i] + == current_thread_info())) + pr_alert( + "Exiting, thread info matches, not removing handler for GPIO %d\n", + i); + } + } +} + +static int otx_dev_open(struct inode *inode, struct file *fp) +{ + gpio_in_use = 1; + return 0; +} + +static int otx_dev_release(struct inode *inode, struct file *fp) +{ + if (gpio_in_use == 0) + return -EINVAL; + gpio_in_use = 0; + return 0; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = otx_dev_open, + .release = otx_dev_release, + .unlocked_ioctl = otx_dev_ioctl +}; +#endif + static unsigned int bit_cfg_reg(unsigned int line) { return 8 * line + GPIO_BIT_CFG; @@ -104,16 +313,17 @@ static int thunderx_gpio_request(struct gpio_chip *chip, unsigned int line) static int thunderx_gpio_dir_in(struct gpio_chip *chip, unsigned int line) { struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + unsigned long flags; if (!thunderx_gpio_is_gpio(txgpio, line)) return -EIO; - raw_spin_lock(&txgpio->lock); + raw_spin_lock_irqsave(&txgpio->lock, flags); 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); + raw_spin_unlock_irqrestore(&txgpio->lock, flags); return 0; } @@ -135,11 +345,12 @@ static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line, { struct thunderx_gpio *txgpio = gpiochip_get_data(chip); u64 bit_cfg = txgpio->line_entries[line].fil_bits | GPIO_BIT_CFG_TX_OE; + unsigned long flags; if (!thunderx_gpio_is_gpio(txgpio, line)) return -EIO; - raw_spin_lock(&txgpio->lock); + raw_spin_lock_irqsave(&txgpio->lock, flags); thunderx_gpio_set(chip, line, value); @@ -151,7 +362,7 @@ static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line, writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(line)); - raw_spin_unlock(&txgpio->lock); + raw_spin_unlock_irqrestore(&txgpio->lock, flags); return 0; } @@ -185,11 +396,12 @@ static int thunderx_gpio_set_config(struct gpio_chip *chip, int ret = -ENOTSUPP; struct thunderx_gpio *txgpio = gpiochip_get_data(chip); void __iomem *reg = txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET; + unsigned long flags; if (!thunderx_gpio_is_gpio(txgpio, line)) return -EIO; - raw_spin_lock(&txgpio->lock); + raw_spin_lock_irqsave(&txgpio->lock, flags); orig_invert = test_bit(line, txgpio->invert_mask); new_invert = orig_invert; orig_od = test_bit(line, txgpio->od_mask); @@ -240,7 +452,7 @@ static int thunderx_gpio_set_config(struct gpio_chip *chip, default: break; } - raw_spin_unlock(&txgpio->lock); + raw_spin_unlock_irqrestore(&txgpio->lock, flags); /* * If currently output and OPEN_DRAIN changed, install the new @@ -283,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); @@ -321,11 +676,13 @@ static int thunderx_gpio_irq_set_type(struct irq_data *data, struct thunderx_line *txline = irq_data_get_irq_chip_data(data); struct thunderx_gpio *txgpio = txline->txgpio; u64 bit_cfg; + unsigned long flags; irqd_set_trigger_type(data, flow_type); 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; @@ -333,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(&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); @@ -342,7 +698,7 @@ static int thunderx_gpio_irq_set_type(struct irq_data *data, } clear_bit(txline->line, txgpio->od_mask); writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(txline->line)); - raw_spin_unlock(&txgpio->lock); + raw_spin_unlock_irqrestore(&txgpio->lock, flags); return IRQ_SET_MASK_OK; } @@ -493,7 +849,13 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, u64 c = readq(txgpio->register_base + GPIO_CONST); ngpio = c & GPIO_CONST_GPIOS_MASK; - txgpio->base_msi = (c >> 8) & 0xff; + + /* Workaround for all passes of T96xx */ + if (((pdev->subsystem_device >> 8) & 0xFF) == MRVL_OCTEONTX2_96XX_PARTNUM) { + txgpio->base_msi = 0x36; + } else { + txgpio->base_msi = (c >> 8) & 0xff; + } } txgpio->msix_entries = devm_kcalloc(dev, @@ -504,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; @@ -533,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; @@ -581,7 +949,81 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n", 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) { + dev_err(dev, "Failed to register cleanup handler: %d\n", err); + goto cleanup_handler_err; + } + + /* create a character device */ + err = alloc_chrdev_region(&otx_dev, 1, 1, DEVICE_NAME); + if (err != 0) { + dev_err(dev, "Failed to create device: %d\n", err); + goto alloc_chrdev_err; + } + + otx_cdev = cdev_alloc(); + if (!otx_cdev) { + err = -ENODEV; + goto cdev_alloc_err; + } + + cdev_init(otx_cdev, &fops); + err = cdev_add(otx_cdev, otx_dev, 1); + if (err < 0) { + err = -ENODEV; + goto cdev_add_err; + } + + /* create new class for sysfs*/ + otx_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(otx_class)) { + err = -ENODEV; + goto class_create_err; + } + + otx_device = device_create(otx_class, NULL, otx_dev, NULL, + DEVICE_NAME); + if (IS_ERR(otx_device)) { + err = -ENODEV; + goto device_create_err; + } +#endif + return 0; + +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +device_create_err: + class_destroy(otx_class); + +class_create_err: +cdev_add_err: + cdev_del(otx_cdev); +cdev_alloc_err: + unregister_chrdev_region(otx_dev, 1); +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); return err; @@ -592,13 +1034,26 @@ 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); + +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR + device_destroy(otx_class, otx_dev); + class_destroy(otx_class); + cdev_del(otx_cdev); + unregister_chrdev_region(otx_dev, 1); + + task_cleanup_handler_remove(cleanup_el3_irqs); +#endif } static const struct pci_device_id thunderx_gpio_id_table[] = { |