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); 140