irq-mtk-sysirq.c (5928B)
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 17struct 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 26static 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 60static 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 .flags = IRQCHIP_SKIP_SET_WAKE, 69}; 70 71static int mtk_sysirq_domain_translate(struct irq_domain *d, 72 struct irq_fwspec *fwspec, 73 unsigned long *hwirq, 74 unsigned int *type) 75{ 76 if (is_of_node(fwspec->fwnode)) { 77 if (fwspec->param_count != 3) 78 return -EINVAL; 79 80 /* No PPI should point to this domain */ 81 if (fwspec->param[0] != 0) 82 return -EINVAL; 83 84 *hwirq = fwspec->param[1]; 85 *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; 86 return 0; 87 } 88 89 return -EINVAL; 90} 91 92static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq, 93 unsigned int nr_irqs, void *arg) 94{ 95 int i; 96 irq_hw_number_t hwirq; 97 struct irq_fwspec *fwspec = arg; 98 struct irq_fwspec gic_fwspec = *fwspec; 99 100 if (fwspec->param_count != 3) 101 return -EINVAL; 102 103 /* sysirq doesn't support PPI */ 104 if (fwspec->param[0]) 105 return -EINVAL; 106 107 hwirq = fwspec->param[1]; 108 for (i = 0; i < nr_irqs; i++) 109 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, 110 &mtk_sysirq_chip, 111 domain->host_data); 112 113 gic_fwspec.fwnode = domain->parent->fwnode; 114 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_fwspec); 115} 116 117static const struct irq_domain_ops sysirq_domain_ops = { 118 .translate = mtk_sysirq_domain_translate, 119 .alloc = mtk_sysirq_domain_alloc, 120 .free = irq_domain_free_irqs_common, 121}; 122 123static int __init mtk_sysirq_of_init(struct device_node *node, 124 struct device_node *parent) 125{ 126 struct irq_domain *domain, *domain_parent; 127 struct mtk_sysirq_chip_data *chip_data; 128 int ret, size, intpol_num = 0, nr_intpol_bases = 0, i = 0; 129 130 domain_parent = irq_find_host(parent); 131 if (!domain_parent) { 132 pr_err("mtk_sysirq: interrupt-parent not found\n"); 133 return -EINVAL; 134 } 135 136 chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL); 137 if (!chip_data) 138 return -ENOMEM; 139 140 while (of_get_address(node, i++, NULL, NULL)) 141 nr_intpol_bases++; 142 143 if (nr_intpol_bases == 0) { 144 pr_err("mtk_sysirq: base address not specified\n"); 145 ret = -EINVAL; 146 goto out_free_chip; 147 } 148 149 chip_data->intpol_words = kcalloc(nr_intpol_bases, 150 sizeof(*chip_data->intpol_words), 151 GFP_KERNEL); 152 if (!chip_data->intpol_words) { 153 ret = -ENOMEM; 154 goto out_free_chip; 155 } 156 157 chip_data->intpol_bases = kcalloc(nr_intpol_bases, 158 sizeof(*chip_data->intpol_bases), 159 GFP_KERNEL); 160 if (!chip_data->intpol_bases) { 161 ret = -ENOMEM; 162 goto out_free_intpol_words; 163 } 164 165 for (i = 0; i < nr_intpol_bases; i++) { 166 struct resource res; 167 168 ret = of_address_to_resource(node, i, &res); 169 size = resource_size(&res); 170 intpol_num += size * 8; 171 chip_data->intpol_words[i] = size / 4; 172 chip_data->intpol_bases[i] = of_iomap(node, i); 173 if (ret || !chip_data->intpol_bases[i]) { 174 pr_err("%pOF: couldn't map region %d\n", node, i); 175 ret = -ENODEV; 176 goto out_free_intpol; 177 } 178 } 179 180 chip_data->intpol_idx = kcalloc(intpol_num, 181 sizeof(*chip_data->intpol_idx), 182 GFP_KERNEL); 183 if (!chip_data->intpol_idx) { 184 ret = -ENOMEM; 185 goto out_free_intpol; 186 } 187 188 chip_data->which_word = kcalloc(intpol_num, 189 sizeof(*chip_data->which_word), 190 GFP_KERNEL); 191 if (!chip_data->which_word) { 192 ret = -ENOMEM; 193 goto out_free_intpol_idx; 194 } 195 196 /* 197 * assign an index of the intpol_bases for each irq 198 * to set it fast later 199 */ 200 for (i = 0; i < intpol_num ; i++) { 201 u32 word = i / 32, j; 202 203 for (j = 0; word >= chip_data->intpol_words[j] ; j++) 204 word -= chip_data->intpol_words[j]; 205 206 chip_data->intpol_idx[i] = j; 207 chip_data->which_word[i] = word; 208 } 209 210 domain = irq_domain_add_hierarchy(domain_parent, 0, intpol_num, node, 211 &sysirq_domain_ops, chip_data); 212 if (!domain) { 213 ret = -ENOMEM; 214 goto out_free_which_word; 215 } 216 raw_spin_lock_init(&chip_data->lock); 217 218 return 0; 219 220out_free_which_word: 221 kfree(chip_data->which_word); 222out_free_intpol_idx: 223 kfree(chip_data->intpol_idx); 224out_free_intpol: 225 for (i = 0; i < nr_intpol_bases; i++) 226 if (chip_data->intpol_bases[i]) 227 iounmap(chip_data->intpol_bases[i]); 228 kfree(chip_data->intpol_bases); 229out_free_intpol_words: 230 kfree(chip_data->intpol_words); 231out_free_chip: 232 kfree(chip_data); 233 return ret; 234} 235IRQCHIP_DECLARE(mtk_sysirq, "mediatek,mt6577-sysirq", mtk_sysirq_of_init);