1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * StarFive JH8100 External Interrupt Controller driver 4 * 5 * Copyright (C) 2023 StarFive Technology Co., Ltd. 6 * 7 * Author: Changhuang Liang <changhuang.liang@starfivetech.com> 8 */ 9 10 #define pr_fmt(fmt) "irq-starfive-jh8100: " fmt 11 12 #include <linux/bitops.h> 13 #include <linux/clk.h> 14 #include <linux/irq.h> 15 #include <linux/irqchip.h> 16 #include <linux/irqchip/chained_irq.h> 17 #include <linux/irqdomain.h> 18 #include <linux/of_address.h> 19 #include <linux/of_irq.h> 20 #include <linux/reset.h> 21 #include <linux/spinlock.h> 22 23 #define STARFIVE_INTC_SRC0_CLEAR 0x10 24 #define STARFIVE_INTC_SRC0_MASK 0x14 25 #define STARFIVE_INTC_SRC0_INT 0x1c 26 27 #define STARFIVE_INTC_SRC_IRQ_NUM 32 28 29 struct starfive_irq_chip { 30 void __iomem *base; 31 struct irq_domain *domain; 32 raw_spinlock_t lock; 33 }; 34 35 static void starfive_intc_bit_set(struct starfive_irq_chip *irqc, 36 u32 reg, u32 bit_mask) 37 { 38 u32 value; 39 40 value = ioread32(irqc->base + reg); 41 value |= bit_mask; 42 iowrite32(value, irqc->base + reg); 43 } 44 45 static void starfive_intc_bit_clear(struct starfive_irq_chip *irqc, 46 u32 reg, u32 bit_mask) 47 { 48 u32 value; 49 50 value = ioread32(irqc->base + reg); 51 value &= ~bit_mask; 52 iowrite32(value, irqc->base + reg); 53 } 54 55 static void starfive_intc_unmask(struct irq_data *d) 56 { 57 struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); 58 59 raw_spin_lock(&irqc->lock); 60 starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq)); 61 raw_spin_unlock(&irqc->lock); 62 } 63 64 static void starfive_intc_mask(struct irq_data *d) 65 { 66 struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); 67 68 raw_spin_lock(&irqc->lock); 69 starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq)); 70 raw_spin_unlock(&irqc->lock); 71 } 72 73 static struct irq_chip intc_dev = { 74 .name = "StarFive JH8100 INTC", 75 .irq_unmask = starfive_intc_unmask, 76 .irq_mask = starfive_intc_mask, 77 }; 78 79 static int starfive_intc_map(struct irq_domain *d, unsigned int irq, 80 irq_hw_number_t hwirq) 81 { 82 irq_domain_set_info(d, irq, hwirq, &intc_dev, d->host_data, 83 handle_level_irq, NULL, NULL); 84 85 return 0; 86 } 87 88 static const struct irq_domain_ops starfive_intc_domain_ops = { 89 .xlate = irq_domain_xlate_onecell, 90 .map = starfive_intc_map, 91 }; 92 93 static void starfive_intc_irq_handler(struct irq_desc *desc) 94 { 95 struct starfive_irq_chip *irqc = irq_data_get_irq_handler_data(&desc->irq_data); 96 struct irq_chip *chip = irq_desc_get_chip(desc); 97 unsigned long value; 98 int hwirq; 99 100 chained_irq_enter(chip, desc); 101 102 value = ioread32(irqc->base + STARFIVE_INTC_SRC0_INT); 103 while (value) { 104 hwirq = ffs(value) - 1; 105 106 generic_handle_domain_irq(irqc->domain, hwirq); 107 108 starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq)); 109 starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq)); 110 111 __clear_bit(hwirq, &value); 112 } 113 114 chained_irq_exit(chip, desc); 115 } 116 117 static int __init starfive_intc_init(struct device_node *intc, 118 struct device_node *parent) 119 { 120 struct starfive_irq_chip *irqc; 121 struct reset_control *rst; 122 struct clk *clk; 123 int parent_irq; 124 int ret; 125 126 irqc = kzalloc(sizeof(*irqc), GFP_KERNEL); 127 if (!irqc) 128 return -ENOMEM; 129 130 irqc->base = of_iomap(intc, 0); 131 if (!irqc->base) { 132 pr_err("Unable to map registers\n"); 133 ret = -ENXIO; 134 goto err_free; 135 } 136 137 rst = of_reset_control_get_exclusive(intc, NULL); 138 if (IS_ERR(rst)) { 139 pr_err("Unable to get reset control %pe\n", rst); 140 ret = PTR_ERR(rst); 141 goto err_unmap; 142 } 143 144 clk = of_clk_get(intc, 0); 145 if (IS_ERR(clk)) { 146 pr_err("Unable to get clock %pe\n", clk); 147 ret = PTR_ERR(clk); 148 goto err_reset_put; 149 } 150 151 ret = reset_control_deassert(rst); 152 if (ret) 153 goto err_clk_put; 154 155 ret = clk_prepare_enable(clk); 156 if (ret) 157 goto err_reset_assert; 158 159 raw_spin_lock_init(&irqc->lock); 160 161 irqc->domain = irq_domain_add_linear(intc, STARFIVE_INTC_SRC_IRQ_NUM, 162 &starfive_intc_domain_ops, irqc); 163 if (!irqc->domain) { 164 pr_err("Unable to create IRQ domain\n"); 165 ret = -EINVAL; 166 goto err_clk_disable; 167 } 168 169 parent_irq = of_irq_get(intc, 0); 170 if (parent_irq < 0) { 171 pr_err("Failed to get main IRQ: %d\n", parent_irq); 172 ret = parent_irq; 173 goto err_remove_domain; 174 } 175 176 irq_set_chained_handler_and_data(parent_irq, starfive_intc_irq_handler, 177 irqc); 178 179 pr_info("Interrupt controller register, nr_irqs %d\n", 180 STARFIVE_INTC_SRC_IRQ_NUM); 181 182 return 0; 183 184 err_remove_domain: 185 irq_domain_remove(irqc->domain); 186 err_clk_disable: 187 clk_disable_unprepare(clk); 188 err_reset_assert: 189 reset_control_assert(rst); 190 err_clk_put: 191 clk_put(clk); 192 err_reset_put: 193 reset_control_put(rst); 194 err_unmap: 195 iounmap(irqc->base); 196 err_free: 197 kfree(irqc); 198 return ret; 199 } 200 201 IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc) 202 IRQCHIP_MATCH("starfive,jh8100-intc", starfive_intc_init) 203 IRQCHIP_PLATFORM_DRIVER_END(starfive_intc) 204 205 MODULE_DESCRIPTION("StarFive JH8100 External Interrupt Controller"); 206 MODULE_LICENSE("GPL"); 207 MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>"); 208