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

irqchip/aspeed-scu-ic: Add support for AST2700 SCU interrupt controllers

AST2700 continues the multi-instance SCU interrupt controller model
introduced in the AST2600, with four independent interrupt domains (scu-ic0
to 3).

Unlike earlier generations which combine interrupt enable and status bits
into a single register, AST2700 separates these into distinct IER and ISR
registers. Support for this layout is implemented by using register offsets
and separate chained IRQ handlers.

The variant table is extended to cover AST2700 IC instances, enabling
shared initialization logic while preserving support for previous SoCs.

[ tglx: Simplified the logic and cleaned up coding style ]

Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250908011812.1033858-5-ryan_chen@aspeedtech.com

authored by

Ryan Chen and committed by
Thomas Gleixner
b2a0c13f ed724044

+102 -17
+102 -17
drivers/irqchip/irq-aspeed-scu-ic.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 /* 3 - * Aspeed AST24XX, AST25XX, and AST26XX SCU Interrupt Controller 3 + * Aspeed AST24XX, AST25XX, AST26XX, and AST27XX SCU Interrupt Controller 4 4 * Copyright 2019 IBM Corporation 5 5 * 6 6 * Eddie James <eajames@linux.ibm.com> ··· 17 17 18 18 #define ASPEED_SCU_IC_STATUS GENMASK(28, 16) 19 19 #define ASPEED_SCU_IC_STATUS_SHIFT 16 20 + #define AST2700_SCU_IC_STATUS GENMASK(15, 0) 20 21 21 22 struct aspeed_scu_ic_variant { 22 23 const char *compatible; 23 24 unsigned long irq_enable; 24 25 unsigned long irq_shift; 25 26 unsigned int num_irqs; 27 + unsigned long ier; 28 + unsigned long isr; 26 29 }; 27 30 28 - #define SCU_VARIANT(_compat, _shift, _enable, _num) { \ 31 + #define SCU_VARIANT(_compat, _shift, _enable, _num, _ier, _isr) { \ 29 32 .compatible = _compat, \ 30 33 .irq_shift = _shift, \ 31 34 .irq_enable = _enable, \ 32 35 .num_irqs = _num, \ 36 + .ier = _ier, \ 37 + .isr = _isr, \ 33 38 } 34 39 35 40 static const struct aspeed_scu_ic_variant scu_ic_variants[] __initconst = { 36 - SCU_VARIANT("aspeed,ast2400-scu-ic", 0, GENMASK(15, 0), 7), 37 - SCU_VARIANT("aspeed,ast2500-scu-ic", 0, GENMASK(15, 0), 7), 38 - SCU_VARIANT("aspeed,ast2600-scu-ic0", 0, GENMASK(5, 0), 6), 39 - SCU_VARIANT("aspeed,ast2600-scu-ic1", 4, GENMASK(5, 4), 2), 41 + SCU_VARIANT("aspeed,ast2400-scu-ic", 0, GENMASK(15, 0), 7, 0x00, 0x00), 42 + SCU_VARIANT("aspeed,ast2500-scu-ic", 0, GENMASK(15, 0), 7, 0x00, 0x00), 43 + SCU_VARIANT("aspeed,ast2600-scu-ic0", 0, GENMASK(5, 0), 6, 0x00, 0x00), 44 + SCU_VARIANT("aspeed,ast2600-scu-ic1", 4, GENMASK(5, 4), 2, 0x00, 0x00), 45 + SCU_VARIANT("aspeed,ast2700-scu-ic0", 0, GENMASK(3, 0), 4, 0x00, 0x04), 46 + SCU_VARIANT("aspeed,ast2700-scu-ic1", 0, GENMASK(3, 0), 4, 0x00, 0x04), 47 + SCU_VARIANT("aspeed,ast2700-scu-ic2", 0, GENMASK(3, 0), 4, 0x04, 0x00), 48 + SCU_VARIANT("aspeed,ast2700-scu-ic3", 0, GENMASK(1, 0), 2, 0x04, 0x00), 40 49 }; 41 50 42 51 struct aspeed_scu_ic { ··· 54 45 unsigned int num_irqs; 55 46 void __iomem *base; 56 47 struct irq_domain *irq_domain; 48 + unsigned long ier; 49 + unsigned long isr; 57 50 }; 58 51 59 - static void aspeed_scu_ic_irq_handler(struct irq_desc *desc) 52 + static inline bool scu_has_split_isr(struct aspeed_scu_ic *scu) 53 + { 54 + return scu->ier != scu->isr; 55 + } 56 + 57 + static void aspeed_scu_ic_irq_handler_combined(struct irq_desc *desc) 60 58 { 61 59 struct aspeed_scu_ic *scu_ic = irq_desc_get_handler_data(desc); 62 60 struct irq_chip *chip = irq_desc_get_chip(desc); ··· 99 83 chained_irq_exit(chip, desc); 100 84 } 101 85 102 - static void aspeed_scu_ic_irq_mask(struct irq_data *data) 86 + static void aspeed_scu_ic_irq_handler_split(struct irq_desc *desc) 87 + { 88 + struct aspeed_scu_ic *scu_ic = irq_desc_get_handler_data(desc); 89 + struct irq_chip *chip = irq_desc_get_chip(desc); 90 + unsigned long bit, enabled, max, status; 91 + unsigned int sts, mask; 92 + 93 + chained_irq_enter(chip, desc); 94 + 95 + mask = scu_ic->irq_enable; 96 + sts = readl(scu_ic->base + scu_ic->isr); 97 + enabled = sts & scu_ic->irq_enable; 98 + sts = readl(scu_ic->base + scu_ic->isr); 99 + status = sts & enabled; 100 + 101 + bit = scu_ic->irq_shift; 102 + max = scu_ic->num_irqs + bit; 103 + 104 + for_each_set_bit_from(bit, &status, max) { 105 + generic_handle_domain_irq(scu_ic->irq_domain, bit - scu_ic->irq_shift); 106 + /* Clear interrupt */ 107 + writel(BIT(bit), scu_ic->base + scu_ic->isr); 108 + } 109 + 110 + chained_irq_exit(chip, desc); 111 + } 112 + 113 + static void aspeed_scu_ic_irq_mask_combined(struct irq_data *data) 103 114 { 104 115 struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(data); 105 116 unsigned int bit = BIT(data->hwirq + scu_ic->irq_shift); ··· 140 97 writel(readl(scu_ic->base) & ~mask, scu_ic->base); 141 98 } 142 99 143 - static void aspeed_scu_ic_irq_unmask(struct irq_data *data) 100 + static void aspeed_scu_ic_irq_unmask_combined(struct irq_data *data) 144 101 { 145 102 struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(data); 146 103 unsigned int bit = BIT(data->hwirq + scu_ic->irq_shift); ··· 154 111 writel((readl(scu_ic->base) & ~mask) | bit, scu_ic->base); 155 112 } 156 113 114 + static void aspeed_scu_ic_irq_mask_split(struct irq_data *data) 115 + { 116 + struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(data); 117 + unsigned int mask = BIT(data->hwirq + scu_ic->irq_shift); 118 + 119 + writel(readl(scu_ic->base) & ~mask, scu_ic->base + scu_ic->ier); 120 + } 121 + 122 + static void aspeed_scu_ic_irq_unmask_split(struct irq_data *data) 123 + { 124 + struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(data); 125 + unsigned int bit = BIT(data->hwirq + scu_ic->irq_shift); 126 + 127 + writel(readl(scu_ic->base) | bit, scu_ic->base + scu_ic->ier); 128 + } 129 + 157 130 static int aspeed_scu_ic_irq_set_affinity(struct irq_data *data, 158 131 const struct cpumask *dest, 159 132 bool force) ··· 177 118 return -EINVAL; 178 119 } 179 120 180 - static struct irq_chip aspeed_scu_ic_chip = { 121 + static struct irq_chip aspeed_scu_ic_chip_combined = { 181 122 .name = "aspeed-scu-ic", 182 - .irq_mask = aspeed_scu_ic_irq_mask, 183 - .irq_unmask = aspeed_scu_ic_irq_unmask, 184 - .irq_set_affinity = aspeed_scu_ic_irq_set_affinity, 123 + .irq_mask = aspeed_scu_ic_irq_mask_combined, 124 + .irq_unmask = aspeed_scu_ic_irq_unmask_combined, 125 + .irq_set_affinity = aspeed_scu_ic_irq_set_affinity, 126 + }; 127 + 128 + static struct irq_chip aspeed_scu_ic_chip_split = { 129 + .name = "ast2700-scu-ic", 130 + .irq_mask = aspeed_scu_ic_irq_mask_split, 131 + .irq_unmask = aspeed_scu_ic_irq_unmask_split, 132 + .irq_set_affinity = aspeed_scu_ic_irq_set_affinity, 185 133 }; 186 134 187 135 static int aspeed_scu_ic_map(struct irq_domain *domain, unsigned int irq, 188 136 irq_hw_number_t hwirq) 189 137 { 190 - irq_set_chip_and_handler(irq, &aspeed_scu_ic_chip, handle_level_irq); 138 + struct aspeed_scu_ic *scu_ic = domain->host_data; 139 + 140 + if (scu_has_split_isr(scu_ic)) 141 + irq_set_chip_and_handler(irq, &aspeed_scu_ic_chip_split, handle_level_irq); 142 + else 143 + irq_set_chip_and_handler(irq, &aspeed_scu_ic_chip_combined, handle_level_irq); 191 144 irq_set_chip_data(irq, domain->host_data); 192 145 193 146 return 0; ··· 219 148 rc = PTR_ERR(scu_ic->base); 220 149 goto err; 221 150 } 222 - writel(ASPEED_SCU_IC_STATUS, scu_ic->base); 223 - writel(0, scu_ic->base); 151 + 152 + if (scu_has_split_isr(scu_ic)) { 153 + writel(AST2700_SCU_IC_STATUS, scu_ic->base + scu_ic->isr); 154 + writel(0, scu_ic->base + scu_ic->ier); 155 + } else { 156 + writel(ASPEED_SCU_IC_STATUS, scu_ic->base); 157 + writel(0, scu_ic->base); 158 + } 224 159 225 160 irq = irq_of_parse_and_map(node, 0); 226 161 if (!irq) { ··· 241 164 goto err; 242 165 } 243 166 244 - irq_set_chained_handler_and_data(irq, aspeed_scu_ic_irq_handler, 167 + irq_set_chained_handler_and_data(irq, scu_has_split_isr(scu_ic) ? 168 + aspeed_scu_ic_irq_handler_split : 169 + aspeed_scu_ic_irq_handler_combined, 245 170 scu_ic); 246 171 247 172 return 0; ··· 278 199 scu_ic->irq_enable = variant->irq_enable; 279 200 scu_ic->irq_shift = variant->irq_shift; 280 201 scu_ic->num_irqs = variant->num_irqs; 202 + scu_ic->ier = variant->ier; 203 + scu_ic->isr = variant->isr; 281 204 282 205 return aspeed_scu_ic_of_init_common(scu_ic, node); 283 206 } ··· 288 207 IRQCHIP_DECLARE(ast2500_scu_ic, "aspeed,ast2500-scu-ic", aspeed_scu_ic_of_init); 289 208 IRQCHIP_DECLARE(ast2600_scu_ic0, "aspeed,ast2600-scu-ic0", aspeed_scu_ic_of_init); 290 209 IRQCHIP_DECLARE(ast2600_scu_ic1, "aspeed,ast2600-scu-ic1", aspeed_scu_ic_of_init); 210 + IRQCHIP_DECLARE(ast2700_scu_ic0, "aspeed,ast2700-scu-ic0", aspeed_scu_ic_of_init); 211 + IRQCHIP_DECLARE(ast2700_scu_ic1, "aspeed,ast2700-scu-ic1", aspeed_scu_ic_of_init); 212 + IRQCHIP_DECLARE(ast2700_scu_ic2, "aspeed,ast2700-scu-ic2", aspeed_scu_ic_of_init); 213 + IRQCHIP_DECLARE(ast2700_scu_ic3, "aspeed,ast2700-scu-ic3", aspeed_scu_ic_of_init);