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