1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2014 MediaTek Inc. 4 * Author: Joe.C <yingjoe.chen@mediatek.com> 5 */ 6 7 #include <linux/irq.h> 8 #include <linux/irqchip.h> 9 #include <linux/irqdomain.h> 10 #include <linux/of.h> 11 #include <linux/of_irq.h> 12 #include <linux/of_address.h> 13 #include <linux/io.h> 14 #include <linux/slab.h> 15 #include <linux/spinlock.h> 16 17 struct mtk_sysirq_chip_data { 18 raw_spinlock_t lock; 19 u32 nr_intpol_bases; 20 void __iomem **intpol_bases; 21 u32 *intpol_words; 22 u8 *intpol_idx; 23 u16 *which_word; 24 }; 25 26 static int mtk_sysirq_set_type(struct irq_data *data, unsigned int type) 27 { 28 irq_hw_number_t hwirq = data->hwirq; 29 struct mtk_sysirq_chip_data *chip_data = data->chip_data; 30 u8 intpol_idx = chip_data->intpol_idx[hwirq]; 31 void __iomem *base; 32 u32 offset, reg_index, value; 33 unsigned long flags; 34 int ret; 35 36 base = chip_data->intpol_bases[intpol_idx]; 37 reg_index = chip_data->which_word[hwirq]; 38 offset = hwirq & 0x1f; 39 40 raw_spin_lock_irqsave(&chip_data->lock, flags); 41 value = readl_relaxed(base + reg_index * 4); 42 if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING) { 43 if (type == IRQ_TYPE_LEVEL_LOW) 44 type = IRQ_TYPE_LEVEL_HIGH; 45 else 46 type = IRQ_TYPE_EDGE_RISING; 47 value |= (1 << offset); 48 } else { 49 value &= ~(1 << offset); 50 } 51 52 writel_relaxed(value, base + reg_index * 4); 53 54 data = data->parent_data; 55 ret = data->chip->irq_set_type(data, type); 56 raw_spin_unlock_irqrestore(&chip_data->lock, flags); 57 return ret; 58 } 59 60 static struct irq_chip mtk_sysirq_chip = { 61 .name = "MT_SYSIRQ", 62 .irq_mask = irq_chip_mask_parent, 63 .irq_unmask = irq_chip_unmask_parent, 64 .irq_eoi = irq_chip_eoi_parent, 65 .irq_set_type = mtk_sysirq_set_type, 66 .irq_retrigger = irq_chip_retrigger_hierarchy, 67 .irq_set_affinity = irq_chip_set_affinity_parent, 68 }; 69 70 static int mtk_sysirq_domain_translate(struct irq_domain *d, 71 struct irq_fwspec *fwspec, 72 unsigned long *hwirq, 73 unsigned int *type) 74 { 75 if (is_of_node(fwspec->fwnode)) { 76 if (fwspec->param_count != 3) 77 return -EINVAL; 78 79 /* No PPI should point to this domain */ 80 if (fwspec->param[0] != 0) 81 return -EINVAL; 82 83 *hwirq = fwspec->param[1]; 84 *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; 85 return 0; 86 } 87 88 return -EINVAL; 89 } 90 91 static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq, 92 unsigned int nr_irqs, void *arg) 93 { 94 int i; 95 irq_hw_number_t hwirq; 96 struct irq_fwspec *fwspec = arg; 97 struct irq_fwspec gic_fwspec = *fwspec; 98 99 if (fwspec->param_count != 3) 100 return -EINVAL; 101 102 /* sysirq doesn't support PPI */ 103 if (fwspec->param[0]) 104 return -EINVAL; 105 106 hwirq = fwspec->param[1]; 107 for (i = 0; i < nr_irqs; i++) 108 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, 109 &mtk_sysirq_chip, 110 domain->host_data); 111 112 gic_fwspec.fwnode = domain->parent->fwnode; 113 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_fwspec); 114 } 115 116 static const struct irq_domain_ops sysirq_domain_ops = { 117 .translate = mtk_sysirq_domain_translate, 118 .alloc = mtk_sysirq_domain_alloc, 119 .free = irq_domain_free_irqs_common, 120 }; 121 122 static int __init mtk_sysirq_of_init(struct device_node *node, 123 struct device_node *parent) 124 { 125 struct irq_domain *domain, *domain_parent; 126 struct mtk_sysirq_chip_data *chip_data; 127 int ret, size, intpol_num = 0, nr_intpol_bases = 0, i = 0; 128 129 domain_parent = irq_find_host(parent); 130 if (!domain_parent) { 131 pr_err("mtk_sysirq: interrupt-parent not found\n"); 132 return -EINVAL; 133 } 134 135 chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL); 136 if (!chip_data) 137 return -ENOMEM; 138 139 while (of_get_address(node, i++, NULL, NULL)) 140 nr_intpol_bases++; 141 142 if (nr_intpol_bases == 0) { 143 pr_err("mtk_sysirq: base address not specified\n"); 144 ret = -EINVAL; 145 goto out_free_chip; 146 } 147 148 chip_data->intpol_words = kcalloc(nr_intpol_bases, 149 sizeof(*chip_data->intpol_words), 150 GFP_KERNEL); 151 if (!chip_data->intpol_words) { 152 ret = -ENOMEM; 153 goto out_free_chip; 154 } 155 156 chip_data->intpol_bases = kcalloc(nr_intpol_bases, 157 sizeof(*chip_data->intpol_bases), 158 GFP_KERNEL); 159 if (!chip_data->intpol_bases) { 160 ret = -ENOMEM; 161 goto out_free_intpol_words; 162 } 163 164 for (i = 0; i < nr_intpol_bases; i++) { 165 struct resource res; 166 167 ret = of_address_to_resource(node, i, &res); 168 size = resource_size(&res); 169 intpol_num += size * 8; 170 chip_data->intpol_words[i] = size / 4; 171 chip_data->intpol_bases[i] = of_iomap(node, i); 172 if (ret || !chip_data->intpol_bases[i]) { 173 pr_err("%pOF: couldn't map region %d\n", node, i); 174 ret = -ENODEV; 175 goto out_free_intpol; 176 } 177 } 178 179 chip_data->intpol_idx = kcalloc(intpol_num, 180 sizeof(*chip_data->intpol_idx), 181 GFP_KERNEL); 182 if (!chip_data->intpol_idx) { 183 ret = -ENOMEM; 184 goto out_free_intpol; 185 } 186 187 chip_data->which_word = kcalloc(intpol_num, 188 sizeof(*chip_data->which_word), 189 GFP_KERNEL); 190 if (!chip_data->which_word) { 191 ret = -ENOMEM; 192 goto out_free_intpol_idx; 193 } 194 195 /* 196 * assign an index of the intpol_bases for each irq 197 * to set it fast later 198 */ 199 for (i = 0; i < intpol_num ; i++) { 200 u32 word = i / 32, j; 201 202 for (j = 0; word >= chip_data->intpol_words[j] ; j++) 203 word -= chip_data->intpol_words[j]; 204 205 chip_data->intpol_idx[i] = j; 206 chip_data->which_word[i] = word; 207 } 208 209 domain = irq_domain_add_hierarchy(domain_parent, 0, intpol_num, node, 210 &sysirq_domain_ops, chip_data); 211 if (!domain) { 212 ret = -ENOMEM; 213 goto out_free_which_word; 214 } 215 raw_spin_lock_init(&chip_data->lock); 216 217 return 0; 218 219 out_free_which_word: 220 kfree(chip_data->which_word); 221 out_free_intpol_idx: 222 kfree(chip_data->intpol_idx); 223 out_free_intpol: 224 for (i = 0; i < nr_intpol_bases; i++) 225 if (chip_data->intpol_bases[i]) 226 iounmap(chip_data->intpol_bases[i]); 227 kfree(chip_data->intpol_bases); 228 out_free_intpol_words: 229 kfree(chip_data->intpol_words); 230 out_free_chip: 231 kfree(chip_data); 232 return ret; 233 } 234 IRQCHIP_PLATFORM_DRIVER_BEGIN(mtk_sysirq) 235 IRQCHIP_MATCH("mediatek,mt6577-sysirq", mtk_sysirq_of_init) 236 IRQCHIP_PLATFORM_DRIVER_END(mtk_sysirq) 237