1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 2 /* 3 * Copyright (c) 2020 MediaTek Inc. 4 * Author Mark-PK Tsai <mark-pk.tsai@mediatek.com> 5 */ 6 #include <linux/interrupt.h> 7 #include <linux/io.h> 8 #include <linux/irq.h> 9 #include <linux/irqchip.h> 10 #include <linux/irqdomain.h> 11 #include <linux/of.h> 12 #include <linux/of_address.h> 13 #include <linux/of_irq.h> 14 #include <linux/slab.h> 15 #include <linux/spinlock.h> 16 #include <linux/syscore_ops.h> 17 18 #define MST_INTC_MAX_IRQS 64 19 20 #define INTC_MASK 0x0 21 #define INTC_REV_POLARITY 0x10 22 #define INTC_EOI 0x20 23 24 #ifdef CONFIG_PM_SLEEP 25 static LIST_HEAD(mst_intc_list); 26 #endif 27 28 struct mst_intc_chip_data { 29 raw_spinlock_t lock; 30 unsigned int irq_start, nr_irqs; 31 void __iomem *base; 32 bool no_eoi; 33 #ifdef CONFIG_PM_SLEEP 34 struct list_head entry; 35 u16 saved_polarity_conf[DIV_ROUND_UP(MST_INTC_MAX_IRQS, 16)]; 36 #endif 37 }; 38 39 static void mst_set_irq(struct irq_data *d, u32 offset) 40 { 41 irq_hw_number_t hwirq = irqd_to_hwirq(d); 42 struct mst_intc_chip_data *cd = irq_data_get_irq_chip_data(d); 43 u16 val, mask; 44 unsigned long flags; 45 46 mask = 1 << (hwirq % 16); 47 offset += (hwirq / 16) * 4; 48 49 raw_spin_lock_irqsave(&cd->lock, flags); 50 val = readw_relaxed(cd->base + offset) | mask; 51 writew_relaxed(val, cd->base + offset); 52 raw_spin_unlock_irqrestore(&cd->lock, flags); 53 } 54 55 static void mst_clear_irq(struct irq_data *d, u32 offset) 56 { 57 irq_hw_number_t hwirq = irqd_to_hwirq(d); 58 struct mst_intc_chip_data *cd = irq_data_get_irq_chip_data(d); 59 u16 val, mask; 60 unsigned long flags; 61 62 mask = 1 << (hwirq % 16); 63 offset += (hwirq / 16) * 4; 64 65 raw_spin_lock_irqsave(&cd->lock, flags); 66 val = readw_relaxed(cd->base + offset) & ~mask; 67 writew_relaxed(val, cd->base + offset); 68 raw_spin_unlock_irqrestore(&cd->lock, flags); 69 } 70 71 static void mst_intc_mask_irq(struct irq_data *d) 72 { 73 mst_set_irq(d, INTC_MASK); 74 irq_chip_mask_parent(d); 75 } 76 77 static void mst_intc_unmask_irq(struct irq_data *d) 78 { 79 mst_clear_irq(d, INTC_MASK); 80 irq_chip_unmask_parent(d); 81 } 82 83 static void mst_intc_eoi_irq(struct irq_data *d) 84 { 85 struct mst_intc_chip_data *cd = irq_data_get_irq_chip_data(d); 86 87 if (!cd->no_eoi) 88 mst_set_irq(d, INTC_EOI); 89 90 irq_chip_eoi_parent(d); 91 } 92 93 static int mst_irq_chip_set_type(struct irq_data *data, unsigned int type) 94 { 95 switch (type) { 96 case IRQ_TYPE_LEVEL_LOW: 97 case IRQ_TYPE_EDGE_FALLING: 98 mst_set_irq(data, INTC_REV_POLARITY); 99 break; 100 case IRQ_TYPE_LEVEL_HIGH: 101 case IRQ_TYPE_EDGE_RISING: 102 mst_clear_irq(data, INTC_REV_POLARITY); 103 break; 104 default: 105 return -EINVAL; 106 } 107 108 return irq_chip_set_type_parent(data, IRQ_TYPE_LEVEL_HIGH); 109 } 110 111 static struct irq_chip mst_intc_chip = { 112 .name = "mst-intc", 113 .irq_mask = mst_intc_mask_irq, 114 .irq_unmask = mst_intc_unmask_irq, 115 .irq_eoi = mst_intc_eoi_irq, 116 .irq_get_irqchip_state = irq_chip_get_parent_state, 117 .irq_set_irqchip_state = irq_chip_set_parent_state, 118 .irq_set_affinity = irq_chip_set_affinity_parent, 119 .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent, 120 .irq_set_type = mst_irq_chip_set_type, 121 .irq_retrigger = irq_chip_retrigger_hierarchy, 122 .flags = IRQCHIP_SET_TYPE_MASKED | 123 IRQCHIP_SKIP_SET_WAKE | 124 IRQCHIP_MASK_ON_SUSPEND, 125 }; 126 127 #ifdef CONFIG_PM_SLEEP 128 static void mst_intc_polarity_save(struct mst_intc_chip_data *cd) 129 { 130 int i; 131 void __iomem *addr = cd->base + INTC_REV_POLARITY; 132 133 for (i = 0; i < DIV_ROUND_UP(cd->nr_irqs, 16); i++) 134 cd->saved_polarity_conf[i] = readw_relaxed(addr + i * 4); 135 } 136 137 static void mst_intc_polarity_restore(struct mst_intc_chip_data *cd) 138 { 139 int i; 140 void __iomem *addr = cd->base + INTC_REV_POLARITY; 141 142 for (i = 0; i < DIV_ROUND_UP(cd->nr_irqs, 16); i++) 143 writew_relaxed(cd->saved_polarity_conf[i], addr + i * 4); 144 } 145 146 static void mst_irq_resume(void *data) 147 { 148 struct mst_intc_chip_data *cd; 149 150 list_for_each_entry(cd, &mst_intc_list, entry) 151 mst_intc_polarity_restore(cd); 152 } 153 154 static int mst_irq_suspend(void *data) 155 { 156 struct mst_intc_chip_data *cd; 157 158 list_for_each_entry(cd, &mst_intc_list, entry) 159 mst_intc_polarity_save(cd); 160 return 0; 161 } 162 163 static const struct syscore_ops mst_irq_syscore_ops = { 164 .suspend = mst_irq_suspend, 165 .resume = mst_irq_resume, 166 }; 167 168 static struct syscore mst_irq_syscore = { 169 .ops = &mst_irq_syscore_ops, 170 }; 171 172 static int __init mst_irq_pm_init(void) 173 { 174 register_syscore(&mst_irq_syscore); 175 return 0; 176 } 177 late_initcall(mst_irq_pm_init); 178 #endif 179 180 static int mst_intc_domain_translate(struct irq_domain *d, 181 struct irq_fwspec *fwspec, 182 unsigned long *hwirq, 183 unsigned int *type) 184 { 185 struct mst_intc_chip_data *cd = d->host_data; 186 187 if (is_of_node(fwspec->fwnode)) { 188 if (fwspec->param_count != 3) 189 return -EINVAL; 190 191 /* No PPI should point to this domain */ 192 if (fwspec->param[0] != 0) 193 return -EINVAL; 194 195 if (fwspec->param[1] >= cd->nr_irqs) 196 return -EINVAL; 197 198 *hwirq = fwspec->param[1]; 199 *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; 200 return 0; 201 } 202 203 return -EINVAL; 204 } 205 206 static int mst_intc_domain_alloc(struct irq_domain *domain, unsigned int virq, 207 unsigned int nr_irqs, void *data) 208 { 209 int i; 210 irq_hw_number_t hwirq; 211 struct irq_fwspec parent_fwspec, *fwspec = data; 212 struct mst_intc_chip_data *cd = domain->host_data; 213 214 /* Not GIC compliant */ 215 if (fwspec->param_count != 3) 216 return -EINVAL; 217 218 /* No PPI should point to this domain */ 219 if (fwspec->param[0]) 220 return -EINVAL; 221 222 hwirq = fwspec->param[1]; 223 for (i = 0; i < nr_irqs; i++) 224 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, 225 &mst_intc_chip, 226 domain->host_data); 227 228 parent_fwspec = *fwspec; 229 parent_fwspec.fwnode = domain->parent->fwnode; 230 parent_fwspec.param[1] = cd->irq_start + hwirq; 231 232 /* 233 * mst-intc latch the interrupt request if it's edge triggered, 234 * so the output signal to parent GIC is always level sensitive. 235 * And if the irq signal is active low, configure it to active high 236 * to meet GIC SPI spec in mst_irq_chip_set_type via REV_POLARITY bit. 237 */ 238 parent_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; 239 240 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_fwspec); 241 } 242 243 static const struct irq_domain_ops mst_intc_domain_ops = { 244 .translate = mst_intc_domain_translate, 245 .alloc = mst_intc_domain_alloc, 246 .free = irq_domain_free_irqs_common, 247 }; 248 249 static int __init mst_intc_of_init(struct device_node *dn, 250 struct device_node *parent) 251 { 252 struct irq_domain *domain, *domain_parent; 253 struct mst_intc_chip_data *cd; 254 u32 irq_start, irq_end; 255 256 domain_parent = irq_find_host(parent); 257 if (!domain_parent) { 258 pr_err("mst-intc: interrupt-parent not found\n"); 259 return -EINVAL; 260 } 261 262 if (of_property_read_u32_index(dn, "mstar,irqs-map-range", 0, &irq_start) || 263 of_property_read_u32_index(dn, "mstar,irqs-map-range", 1, &irq_end)) 264 return -EINVAL; 265 266 cd = kzalloc(sizeof(*cd), GFP_KERNEL); 267 if (!cd) 268 return -ENOMEM; 269 270 cd->base = of_iomap(dn, 0); 271 if (!cd->base) { 272 kfree(cd); 273 return -ENOMEM; 274 } 275 276 cd->no_eoi = of_property_read_bool(dn, "mstar,intc-no-eoi"); 277 raw_spin_lock_init(&cd->lock); 278 cd->irq_start = irq_start; 279 cd->nr_irqs = irq_end - irq_start + 1; 280 domain = irq_domain_create_hierarchy(domain_parent, 0, cd->nr_irqs, of_fwnode_handle(dn), 281 &mst_intc_domain_ops, cd); 282 if (!domain) { 283 iounmap(cd->base); 284 kfree(cd); 285 return -ENOMEM; 286 } 287 288 #ifdef CONFIG_PM_SLEEP 289 INIT_LIST_HEAD(&cd->entry); 290 list_add_tail(&cd->entry, &mst_intc_list); 291 #endif 292 return 0; 293 } 294 295 IRQCHIP_DECLARE(mst_intc, "mstar,mst-intc", mst_intc_of_init); 296