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

irqchip: Add Aspeed SCU interrupt controller

The Aspeed SOCs provide some interrupts through the System Control
Unit registers. Add an interrupt controller that provides these
interrupts to the system.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Link: https://lore.kernel.org/r/1579123790-6894-3-git-send-email-eajames@linux.ibm.com

authored by

Eddie James and committed by
Marc Zyngier
04f60590 5350a237

+241 -1
+1
MAINTAINERS
··· 2697 2697 L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) 2698 2698 S: Maintained 2699 2699 F: Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2xxx-scu-ic.txt 2700 + F: drivers/irqchip/irq-aspeed-scu-ic.c 2700 2701 F: include/dt-bindings/interrupt-controller/aspeed-scu-ic.h 2701 2702 2702 2703 ASPEED VIDEO ENGINE DRIVER
+1 -1
drivers/irqchip/Makefile
··· 87 87 obj-$(CONFIG_LS_EXTIRQ) += irq-ls-extirq.o 88 88 obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o 89 89 obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o 90 - obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o 90 + obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o 91 91 obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o 92 92 obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o 93 93 obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o
+239
drivers/irqchip/irq-aspeed-scu-ic.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Aspeed AST24XX, AST25XX, and AST26XX SCU Interrupt Controller 4 + * Copyright 2019 IBM Corporation 5 + * 6 + * Eddie James <eajames@linux.ibm.com> 7 + */ 8 + 9 + #include <linux/bitops.h> 10 + #include <linux/irq.h> 11 + #include <linux/irqchip.h> 12 + #include <linux/irqchip/chained_irq.h> 13 + #include <linux/irqdomain.h> 14 + #include <linux/mfd/syscon.h> 15 + #include <linux/of_irq.h> 16 + #include <linux/regmap.h> 17 + 18 + #define ASPEED_SCU_IC_REG 0x018 19 + #define ASPEED_SCU_IC_SHIFT 0 20 + #define ASPEED_SCU_IC_ENABLE GENMASK(6, ASPEED_SCU_IC_SHIFT) 21 + #define ASPEED_SCU_IC_NUM_IRQS 7 22 + #define ASPEED_SCU_IC_STATUS_SHIFT 16 23 + 24 + #define ASPEED_AST2600_SCU_IC0_REG 0x560 25 + #define ASPEED_AST2600_SCU_IC0_SHIFT 0 26 + #define ASPEED_AST2600_SCU_IC0_ENABLE \ 27 + GENMASK(5, ASPEED_AST2600_SCU_IC0_SHIFT) 28 + #define ASPEED_AST2600_SCU_IC0_NUM_IRQS 6 29 + 30 + #define ASPEED_AST2600_SCU_IC1_REG 0x570 31 + #define ASPEED_AST2600_SCU_IC1_SHIFT 4 32 + #define ASPEED_AST2600_SCU_IC1_ENABLE \ 33 + GENMASK(5, ASPEED_AST2600_SCU_IC1_SHIFT) 34 + #define ASPEED_AST2600_SCU_IC1_NUM_IRQS 2 35 + 36 + struct aspeed_scu_ic { 37 + unsigned long irq_enable; 38 + unsigned long irq_shift; 39 + unsigned int num_irqs; 40 + unsigned int reg; 41 + struct regmap *scu; 42 + struct irq_domain *irq_domain; 43 + }; 44 + 45 + static void aspeed_scu_ic_irq_handler(struct irq_desc *desc) 46 + { 47 + unsigned int irq; 48 + unsigned int sts; 49 + unsigned long bit; 50 + unsigned long enabled; 51 + unsigned long max; 52 + unsigned long status; 53 + struct aspeed_scu_ic *scu_ic = irq_desc_get_handler_data(desc); 54 + struct irq_chip *chip = irq_desc_get_chip(desc); 55 + unsigned int mask = scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT; 56 + 57 + chained_irq_enter(chip, desc); 58 + 59 + /* 60 + * The SCU IC has just one register to control its operation and read 61 + * status. The interrupt enable bits occupy the lower 16 bits of the 62 + * register, while the interrupt status bits occupy the upper 16 bits. 63 + * The status bit for a given interrupt is always 16 bits shifted from 64 + * the enable bit for the same interrupt. 65 + * Therefore, perform the IRQ operations in the enable bit space by 66 + * shifting the status down to get the mapping and then back up to 67 + * clear the bit. 68 + */ 69 + regmap_read(scu_ic->scu, scu_ic->reg, &sts); 70 + enabled = sts & scu_ic->irq_enable; 71 + status = (sts >> ASPEED_SCU_IC_STATUS_SHIFT) & enabled; 72 + 73 + bit = scu_ic->irq_shift; 74 + max = scu_ic->num_irqs + bit; 75 + 76 + for_each_set_bit_from(bit, &status, max) { 77 + irq = irq_find_mapping(scu_ic->irq_domain, 78 + bit - scu_ic->irq_shift); 79 + generic_handle_irq(irq); 80 + 81 + regmap_update_bits(scu_ic->scu, scu_ic->reg, mask, 82 + BIT(bit + ASPEED_SCU_IC_STATUS_SHIFT)); 83 + } 84 + 85 + chained_irq_exit(chip, desc); 86 + } 87 + 88 + static void aspeed_scu_ic_irq_mask(struct irq_data *data) 89 + { 90 + struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(data); 91 + unsigned int mask = BIT(data->hwirq + scu_ic->irq_shift) | 92 + (scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT); 93 + 94 + /* 95 + * Status bits are cleared by writing 1. In order to prevent the mask 96 + * operation from clearing the status bits, they should be under the 97 + * mask and written with 0. 98 + */ 99 + regmap_update_bits(scu_ic->scu, scu_ic->reg, mask, 0); 100 + } 101 + 102 + static void aspeed_scu_ic_irq_unmask(struct irq_data *data) 103 + { 104 + struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(data); 105 + unsigned int bit = BIT(data->hwirq + scu_ic->irq_shift); 106 + unsigned int mask = bit | 107 + (scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT); 108 + 109 + /* 110 + * Status bits are cleared by writing 1. In order to prevent the unmask 111 + * operation from clearing the status bits, they should be under the 112 + * mask and written with 0. 113 + */ 114 + regmap_update_bits(scu_ic->scu, scu_ic->reg, mask, bit); 115 + } 116 + 117 + static int aspeed_scu_ic_irq_set_affinity(struct irq_data *data, 118 + const struct cpumask *dest, 119 + bool force) 120 + { 121 + return -EINVAL; 122 + } 123 + 124 + static struct irq_chip aspeed_scu_ic_chip = { 125 + .name = "aspeed-scu-ic", 126 + .irq_mask = aspeed_scu_ic_irq_mask, 127 + .irq_unmask = aspeed_scu_ic_irq_unmask, 128 + .irq_set_affinity = aspeed_scu_ic_irq_set_affinity, 129 + }; 130 + 131 + static int aspeed_scu_ic_map(struct irq_domain *domain, unsigned int irq, 132 + irq_hw_number_t hwirq) 133 + { 134 + irq_set_chip_and_handler(irq, &aspeed_scu_ic_chip, handle_level_irq); 135 + irq_set_chip_data(irq, domain->host_data); 136 + 137 + return 0; 138 + } 139 + 140 + static const struct irq_domain_ops aspeed_scu_ic_domain_ops = { 141 + .map = aspeed_scu_ic_map, 142 + }; 143 + 144 + static int aspeed_scu_ic_of_init_common(struct aspeed_scu_ic *scu_ic, 145 + struct device_node *node) 146 + { 147 + int irq; 148 + int rc = 0; 149 + 150 + if (!node->parent) { 151 + rc = -ENODEV; 152 + goto err; 153 + } 154 + 155 + scu_ic->scu = syscon_node_to_regmap(node->parent); 156 + if (IS_ERR(scu_ic->scu)) { 157 + rc = PTR_ERR(scu_ic->scu); 158 + goto err; 159 + } 160 + 161 + irq = irq_of_parse_and_map(node, 0); 162 + if (irq < 0) { 163 + rc = irq; 164 + goto err; 165 + } 166 + 167 + scu_ic->irq_domain = irq_domain_add_linear(node, scu_ic->num_irqs, 168 + &aspeed_scu_ic_domain_ops, 169 + scu_ic); 170 + if (!scu_ic->irq_domain) { 171 + rc = -ENOMEM; 172 + goto err; 173 + } 174 + 175 + irq_set_chained_handler_and_data(irq, aspeed_scu_ic_irq_handler, 176 + scu_ic); 177 + 178 + return 0; 179 + 180 + err: 181 + kfree(scu_ic); 182 + 183 + return rc; 184 + } 185 + 186 + static int __init aspeed_scu_ic_of_init(struct device_node *node, 187 + struct device_node *parent) 188 + { 189 + struct aspeed_scu_ic *scu_ic = kzalloc(sizeof(*scu_ic), GFP_KERNEL); 190 + 191 + if (!scu_ic) 192 + return -ENOMEM; 193 + 194 + scu_ic->irq_enable = ASPEED_SCU_IC_ENABLE; 195 + scu_ic->irq_shift = ASPEED_SCU_IC_SHIFT; 196 + scu_ic->num_irqs = ASPEED_SCU_IC_NUM_IRQS; 197 + scu_ic->reg = ASPEED_SCU_IC_REG; 198 + 199 + return aspeed_scu_ic_of_init_common(scu_ic, node); 200 + } 201 + 202 + static int __init aspeed_ast2600_scu_ic0_of_init(struct device_node *node, 203 + struct device_node *parent) 204 + { 205 + struct aspeed_scu_ic *scu_ic = kzalloc(sizeof(*scu_ic), GFP_KERNEL); 206 + 207 + if (!scu_ic) 208 + return -ENOMEM; 209 + 210 + scu_ic->irq_enable = ASPEED_AST2600_SCU_IC0_ENABLE; 211 + scu_ic->irq_shift = ASPEED_AST2600_SCU_IC0_SHIFT; 212 + scu_ic->num_irqs = ASPEED_AST2600_SCU_IC0_NUM_IRQS; 213 + scu_ic->reg = ASPEED_AST2600_SCU_IC0_REG; 214 + 215 + return aspeed_scu_ic_of_init_common(scu_ic, node); 216 + } 217 + 218 + static int __init aspeed_ast2600_scu_ic1_of_init(struct device_node *node, 219 + struct device_node *parent) 220 + { 221 + struct aspeed_scu_ic *scu_ic = kzalloc(sizeof(*scu_ic), GFP_KERNEL); 222 + 223 + if (!scu_ic) 224 + return -ENOMEM; 225 + 226 + scu_ic->irq_enable = ASPEED_AST2600_SCU_IC1_ENABLE; 227 + scu_ic->irq_shift = ASPEED_AST2600_SCU_IC1_SHIFT; 228 + scu_ic->num_irqs = ASPEED_AST2600_SCU_IC1_NUM_IRQS; 229 + scu_ic->reg = ASPEED_AST2600_SCU_IC1_REG; 230 + 231 + return aspeed_scu_ic_of_init_common(scu_ic, node); 232 + } 233 + 234 + IRQCHIP_DECLARE(ast2400_scu_ic, "aspeed,ast2400-scu-ic", aspeed_scu_ic_of_init); 235 + IRQCHIP_DECLARE(ast2500_scu_ic, "aspeed,ast2500-scu-ic", aspeed_scu_ic_of_init); 236 + IRQCHIP_DECLARE(ast2600_scu_ic0, "aspeed,ast2600-scu-ic0", 237 + aspeed_ast2600_scu_ic0_of_init); 238 + IRQCHIP_DECLARE(ast2600_scu_ic1, "aspeed,ast2600-scu-ic1", 239 + aspeed_ast2600_scu_ic1_of_init);