Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip/aspeed-intc: Add AST27XX INTC support

Support Aspeed Interrupt Controller on Aspeed Silicon SoCs.

ASPEED interrupt controller(INTC) maps the internal interrupt
sources to a parent interrupt controller, which can be GIC or INTC.

Signed-off-by: Kevin Chen <kevin_chen@aspeedtech.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20241016022410.1154574-3-kevin_chen@aspeedtech.com

authored by

Kevin Chen and committed by
Thomas Gleixner
010863f4 37a99ff5

+140
+1
drivers/irqchip/Makefile
··· 85 85 obj-$(CONFIG_LS_EXTIRQ) += irq-ls-extirq.o 86 86 obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o 87 87 obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o 88 + obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-intc.o 88 89 obj-$(CONFIG_STM32MP_EXTI) += irq-stm32mp-exti.o 89 90 obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o 90 91 obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o
+139
drivers/irqchip/irq-aspeed-intc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Aspeed Interrupt Controller. 4 + * 5 + * Copyright (C) 2023 ASPEED Technology Inc. 6 + */ 7 + 8 + #include <linux/bitops.h> 9 + #include <linux/irq.h> 10 + #include <linux/irqchip.h> 11 + #include <linux/irqchip/chained_irq.h> 12 + #include <linux/irqdomain.h> 13 + #include <linux/of_address.h> 14 + #include <linux/of_irq.h> 15 + #include <linux/io.h> 16 + #include <linux/spinlock.h> 17 + 18 + #define INTC_INT_ENABLE_REG 0x00 19 + #define INTC_INT_STATUS_REG 0x04 20 + #define INTC_IRQS_PER_WORD 32 21 + 22 + struct aspeed_intc_ic { 23 + void __iomem *base; 24 + raw_spinlock_t gic_lock; 25 + raw_spinlock_t intc_lock; 26 + struct irq_domain *irq_domain; 27 + }; 28 + 29 + static void aspeed_intc_ic_irq_handler(struct irq_desc *desc) 30 + { 31 + struct aspeed_intc_ic *intc_ic = irq_desc_get_handler_data(desc); 32 + struct irq_chip *chip = irq_desc_get_chip(desc); 33 + 34 + chained_irq_enter(chip, desc); 35 + 36 + scoped_guard(raw_spinlock, &intc_ic->gic_lock) { 37 + unsigned long bit, status; 38 + 39 + status = readl(intc_ic->base + INTC_INT_STATUS_REG); 40 + for_each_set_bit(bit, &status, INTC_IRQS_PER_WORD) { 41 + generic_handle_domain_irq(intc_ic->irq_domain, bit); 42 + writel(BIT(bit), intc_ic->base + INTC_INT_STATUS_REG); 43 + } 44 + } 45 + 46 + chained_irq_exit(chip, desc); 47 + } 48 + 49 + static void aspeed_intc_irq_mask(struct irq_data *data) 50 + { 51 + struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); 52 + unsigned int mask = readl(intc_ic->base + INTC_INT_ENABLE_REG) & ~BIT(data->hwirq); 53 + 54 + guard(raw_spinlock)(&intc_ic->intc_lock); 55 + writel(mask, intc_ic->base + INTC_INT_ENABLE_REG); 56 + } 57 + 58 + static void aspeed_intc_irq_unmask(struct irq_data *data) 59 + { 60 + struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); 61 + unsigned int unmask = readl(intc_ic->base + INTC_INT_ENABLE_REG) | BIT(data->hwirq); 62 + 63 + guard(raw_spinlock)(&intc_ic->intc_lock); 64 + writel(unmask, intc_ic->base + INTC_INT_ENABLE_REG); 65 + } 66 + 67 + static struct irq_chip aspeed_intc_chip = { 68 + .name = "ASPEED INTC", 69 + .irq_mask = aspeed_intc_irq_mask, 70 + .irq_unmask = aspeed_intc_irq_unmask, 71 + }; 72 + 73 + static int aspeed_intc_ic_map_irq_domain(struct irq_domain *domain, unsigned int irq, 74 + irq_hw_number_t hwirq) 75 + { 76 + irq_set_chip_and_handler(irq, &aspeed_intc_chip, handle_level_irq); 77 + irq_set_chip_data(irq, domain->host_data); 78 + 79 + return 0; 80 + } 81 + 82 + static const struct irq_domain_ops aspeed_intc_ic_irq_domain_ops = { 83 + .map = aspeed_intc_ic_map_irq_domain, 84 + }; 85 + 86 + static int __init aspeed_intc_ic_of_init(struct device_node *node, 87 + struct device_node *parent) 88 + { 89 + struct aspeed_intc_ic *intc_ic; 90 + int irq, i, ret = 0; 91 + 92 + intc_ic = kzalloc(sizeof(*intc_ic), GFP_KERNEL); 93 + if (!intc_ic) 94 + return -ENOMEM; 95 + 96 + intc_ic->base = of_iomap(node, 0); 97 + if (!intc_ic->base) { 98 + pr_err("Failed to iomap intc_ic base\n"); 99 + ret = -ENOMEM; 100 + goto err_free_ic; 101 + } 102 + writel(0xffffffff, intc_ic->base + INTC_INT_STATUS_REG); 103 + writel(0x0, intc_ic->base + INTC_INT_ENABLE_REG); 104 + 105 + intc_ic->irq_domain = irq_domain_add_linear(node, INTC_IRQS_PER_WORD, 106 + &aspeed_intc_ic_irq_domain_ops, intc_ic); 107 + if (!intc_ic->irq_domain) { 108 + ret = -ENOMEM; 109 + goto err_iounmap; 110 + } 111 + 112 + raw_spin_lock_init(&intc_ic->gic_lock); 113 + raw_spin_lock_init(&intc_ic->intc_lock); 114 + 115 + /* Check all the irq numbers valid. If not, unmaps all the base and frees the data. */ 116 + for (i = 0; i < of_irq_count(node); i++) { 117 + irq = irq_of_parse_and_map(node, i); 118 + if (!irq) { 119 + pr_err("Failed to get irq number\n"); 120 + ret = -EINVAL; 121 + goto err_iounmap; 122 + } 123 + } 124 + 125 + for (i = 0; i < of_irq_count(node); i++) { 126 + irq = irq_of_parse_and_map(node, i); 127 + irq_set_chained_handler_and_data(irq, aspeed_intc_ic_irq_handler, intc_ic); 128 + } 129 + 130 + return 0; 131 + 132 + err_iounmap: 133 + iounmap(intc_ic->base); 134 + err_free_ic: 135 + kfree(intc_ic); 136 + return ret; 137 + } 138 + 139 + IRQCHIP_DECLARE(ast2700_intc_ic, "aspeed,ast2700-intc-ic", aspeed_intc_ic_of_init);