1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. 4 * http://www.samsung.com 5 * 6 * Combiner irqchip for EXYNOS 7 */ 8 #include <linux/err.h> 9 #include <linux/export.h> 10 #include <linux/init.h> 11 #include <linux/io.h> 12 #include <linux/slab.h> 13 #include <linux/syscore_ops.h> 14 #include <linux/irqdomain.h> 15 #include <linux/irqchip.h> 16 #include <linux/irqchip/chained_irq.h> 17 #include <linux/interrupt.h> 18 #include <linux/of_address.h> 19 #include <linux/of_irq.h> 20 21 #define COMBINER_ENABLE_SET 0x0 22 #define COMBINER_ENABLE_CLEAR 0x4 23 #define COMBINER_INT_STATUS 0xC 24 25 #define IRQ_IN_COMBINER 8 26 27 struct combiner_chip_data { 28 unsigned int hwirq_offset; 29 unsigned int irq_mask; 30 void __iomem *base; 31 unsigned int parent_irq; 32 #ifdef CONFIG_PM 33 u32 pm_save; 34 #endif 35 }; 36 37 static struct combiner_chip_data *combiner_data; 38 static struct irq_domain *combiner_irq_domain; 39 static unsigned int max_nr = 20; 40 41 static inline void __iomem *combiner_base(struct irq_data *data) 42 { 43 struct combiner_chip_data *combiner_data = 44 irq_data_get_irq_chip_data(data); 45 46 return combiner_data->base; 47 } 48 49 static void combiner_mask_irq(struct irq_data *data) 50 { 51 u32 mask = 1 << (data->hwirq % 32); 52 53 writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); 54 } 55 56 static void combiner_unmask_irq(struct irq_data *data) 57 { 58 u32 mask = 1 << (data->hwirq % 32); 59 60 writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_SET); 61 } 62 63 static void combiner_handle_cascade_irq(struct irq_desc *desc) 64 { 65 struct combiner_chip_data *chip_data = irq_desc_get_handler_data(desc); 66 struct irq_chip *chip = irq_desc_get_chip(desc); 67 unsigned int combiner_irq; 68 unsigned long status; 69 int ret; 70 71 chained_irq_enter(chip, desc); 72 73 status = readl_relaxed(chip_data->base + COMBINER_INT_STATUS); 74 status &= chip_data->irq_mask; 75 76 if (status == 0) 77 goto out; 78 79 combiner_irq = chip_data->hwirq_offset + __ffs(status); 80 ret = generic_handle_domain_irq(combiner_irq_domain, combiner_irq); 81 if (unlikely(ret)) 82 handle_bad_irq(desc); 83 84 out: 85 chained_irq_exit(chip, desc); 86 } 87 88 #ifdef CONFIG_SMP 89 static int combiner_set_affinity(struct irq_data *d, 90 const struct cpumask *mask_val, bool force) 91 { 92 struct combiner_chip_data *chip_data = irq_data_get_irq_chip_data(d); 93 struct irq_chip *chip = irq_get_chip(chip_data->parent_irq); 94 struct irq_data *data = irq_get_irq_data(chip_data->parent_irq); 95 96 if (chip && chip->irq_set_affinity) 97 return chip->irq_set_affinity(data, mask_val, force); 98 else 99 return -EINVAL; 100 } 101 #endif 102 103 static struct irq_chip combiner_chip = { 104 .name = "COMBINER", 105 .irq_mask = combiner_mask_irq, 106 .irq_unmask = combiner_unmask_irq, 107 #ifdef CONFIG_SMP 108 .irq_set_affinity = combiner_set_affinity, 109 #endif 110 }; 111 112 static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data, 113 unsigned int irq) 114 { 115 irq_set_chained_handler_and_data(irq, combiner_handle_cascade_irq, 116 combiner_data); 117 } 118 119 static void __init combiner_init_one(struct combiner_chip_data *combiner_data, 120 unsigned int combiner_nr, 121 void __iomem *base, unsigned int irq) 122 { 123 combiner_data->base = base; 124 combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER; 125 combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3); 126 combiner_data->parent_irq = irq; 127 128 /* Disable all interrupts */ 129 writel_relaxed(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR); 130 } 131 132 static int combiner_irq_domain_xlate(struct irq_domain *d, 133 struct device_node *controller, 134 const u32 *intspec, unsigned int intsize, 135 unsigned long *out_hwirq, 136 unsigned int *out_type) 137 { 138 if (irq_domain_get_of_node(d) != controller) 139 return -EINVAL; 140 141 if (intsize < 2) 142 return -EINVAL; 143 144 *out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1]; 145 *out_type = 0; 146 147 return 0; 148 } 149 150 static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, 151 irq_hw_number_t hw) 152 { 153 struct combiner_chip_data *combiner_data = d->host_data; 154 155 irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); 156 irq_set_chip_data(irq, &combiner_data[hw >> 3]); 157 irq_set_probe(irq); 158 159 return 0; 160 } 161 162 static const struct irq_domain_ops combiner_irq_domain_ops = { 163 .xlate = combiner_irq_domain_xlate, 164 .map = combiner_irq_domain_map, 165 }; 166 167 static void __init combiner_init(void __iomem *combiner_base, 168 struct device_node *np) 169 { 170 int i, irq; 171 unsigned int nr_irq; 172 173 nr_irq = max_nr * IRQ_IN_COMBINER; 174 175 combiner_data = kzalloc_objs(*combiner_data, max_nr); 176 if (!combiner_data) 177 return; 178 179 combiner_irq_domain = irq_domain_create_linear(of_fwnode_handle(np), nr_irq, 180 &combiner_irq_domain_ops, combiner_data); 181 if (WARN_ON(!combiner_irq_domain)) { 182 pr_warn("%s: irq domain init failed\n", __func__); 183 return; 184 } 185 186 for (i = 0; i < max_nr; i++) { 187 irq = irq_of_parse_and_map(np, i); 188 189 combiner_init_one(&combiner_data[i], i, 190 combiner_base + (i >> 2) * 0x10, irq); 191 combiner_cascade_irq(&combiner_data[i], irq); 192 } 193 } 194 195 #ifdef CONFIG_PM 196 197 /** 198 * combiner_suspend - save interrupt combiner state before suspend 199 * @data: syscore context 200 * 201 * Save the interrupt enable set register for all combiner groups since 202 * the state is lost when the system enters into a sleep state. 203 * 204 */ 205 static int combiner_suspend(void *data) 206 { 207 int i; 208 209 for (i = 0; i < max_nr; i++) 210 combiner_data[i].pm_save = 211 readl_relaxed(combiner_data[i].base + COMBINER_ENABLE_SET); 212 213 return 0; 214 } 215 216 /** 217 * combiner_resume - restore interrupt combiner state after resume 218 * @data: syscore context 219 * 220 * Restore the interrupt enable set register for all combiner groups since 221 * the state is lost when the system enters into a sleep state on suspend. 222 * 223 */ 224 static void combiner_resume(void *data) 225 { 226 int i; 227 228 for (i = 0; i < max_nr; i++) { 229 writel_relaxed(combiner_data[i].irq_mask, 230 combiner_data[i].base + COMBINER_ENABLE_CLEAR); 231 writel_relaxed(combiner_data[i].pm_save, 232 combiner_data[i].base + COMBINER_ENABLE_SET); 233 } 234 } 235 236 #else 237 #define combiner_suspend NULL 238 #define combiner_resume NULL 239 #endif 240 241 static const struct syscore_ops combiner_syscore_ops = { 242 .suspend = combiner_suspend, 243 .resume = combiner_resume, 244 }; 245 246 static struct syscore combiner_syscore = { 247 .ops = &combiner_syscore_ops, 248 }; 249 250 static int __init combiner_of_init(struct device_node *np, 251 struct device_node *parent) 252 { 253 void __iomem *combiner_base; 254 255 combiner_base = of_iomap(np, 0); 256 if (!combiner_base) { 257 pr_err("%s: failed to map combiner registers\n", __func__); 258 return -ENXIO; 259 } 260 261 if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { 262 pr_info("%s: number of combiners not specified, " 263 "setting default as %d.\n", 264 __func__, max_nr); 265 } 266 267 combiner_init(combiner_base, np); 268 269 register_syscore(&combiner_syscore); 270 271 return 0; 272 } 273 IRQCHIP_DECLARE(exynos4210_combiner, "samsung,exynos4210-combiner", 274 combiner_of_init); 275