1 /* 2 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. 3 * http://www.samsung.com 4 * 5 * Combiner irqchip for EXYNOS 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 #include <linux/err.h> 12 #include <linux/export.h> 13 #include <linux/init.h> 14 #include <linux/io.h> 15 #include <linux/irqdomain.h> 16 #include <linux/of_address.h> 17 #include <linux/of_irq.h> 18 #include <asm/mach/irq.h> 19 20 #include <plat/cpu.h> 21 22 #include "irqchip.h" 23 24 #define COMBINER_ENABLE_SET 0x0 25 #define COMBINER_ENABLE_CLEAR 0x4 26 #define COMBINER_INT_STATUS 0xC 27 28 static DEFINE_SPINLOCK(irq_controller_lock); 29 30 struct combiner_chip_data { 31 unsigned int irq_offset; 32 unsigned int irq_mask; 33 void __iomem *base; 34 }; 35 36 static struct irq_domain *combiner_irq_domain; 37 static struct combiner_chip_data combiner_data[MAX_COMBINER_NR]; 38 39 static inline void __iomem *combiner_base(struct irq_data *data) 40 { 41 struct combiner_chip_data *combiner_data = 42 irq_data_get_irq_chip_data(data); 43 44 return combiner_data->base; 45 } 46 47 static void combiner_mask_irq(struct irq_data *data) 48 { 49 u32 mask = 1 << (data->hwirq % 32); 50 51 __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); 52 } 53 54 static void combiner_unmask_irq(struct irq_data *data) 55 { 56 u32 mask = 1 << (data->hwirq % 32); 57 58 __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET); 59 } 60 61 static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) 62 { 63 struct combiner_chip_data *chip_data = irq_get_handler_data(irq); 64 struct irq_chip *chip = irq_get_chip(irq); 65 unsigned int cascade_irq, combiner_irq; 66 unsigned long status; 67 68 chained_irq_enter(chip, desc); 69 70 spin_lock(&irq_controller_lock); 71 status = __raw_readl(chip_data->base + COMBINER_INT_STATUS); 72 spin_unlock(&irq_controller_lock); 73 status &= chip_data->irq_mask; 74 75 if (status == 0) 76 goto out; 77 78 combiner_irq = __ffs(status); 79 80 cascade_irq = combiner_irq + (chip_data->irq_offset & ~31); 81 if (unlikely(cascade_irq >= NR_IRQS)) 82 do_bad_IRQ(cascade_irq, desc); 83 else 84 generic_handle_irq(cascade_irq); 85 86 out: 87 chained_irq_exit(chip, desc); 88 } 89 90 static struct irq_chip combiner_chip = { 91 .name = "COMBINER", 92 .irq_mask = combiner_mask_irq, 93 .irq_unmask = combiner_unmask_irq, 94 }; 95 96 static void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq) 97 { 98 unsigned int max_nr; 99 100 if (soc_is_exynos5250()) 101 max_nr = EXYNOS5_MAX_COMBINER_NR; 102 else 103 max_nr = EXYNOS4_MAX_COMBINER_NR; 104 105 if (combiner_nr >= max_nr) 106 BUG(); 107 if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0) 108 BUG(); 109 irq_set_chained_handler(irq, combiner_handle_cascade_irq); 110 } 111 112 static void __init combiner_init_one(unsigned int combiner_nr, 113 void __iomem *base) 114 { 115 combiner_data[combiner_nr].base = base; 116 combiner_data[combiner_nr].irq_offset = irq_find_mapping( 117 combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER); 118 combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); 119 120 /* Disable all interrupts */ 121 __raw_writel(combiner_data[combiner_nr].irq_mask, 122 base + COMBINER_ENABLE_CLEAR); 123 } 124 125 #ifdef CONFIG_OF 126 static int combiner_irq_domain_xlate(struct irq_domain *d, 127 struct device_node *controller, 128 const u32 *intspec, unsigned int intsize, 129 unsigned long *out_hwirq, 130 unsigned int *out_type) 131 { 132 if (d->of_node != controller) 133 return -EINVAL; 134 135 if (intsize < 2) 136 return -EINVAL; 137 138 *out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1]; 139 *out_type = 0; 140 141 return 0; 142 } 143 #else 144 static int combiner_irq_domain_xlate(struct irq_domain *d, 145 struct device_node *controller, 146 const u32 *intspec, unsigned int intsize, 147 unsigned long *out_hwirq, 148 unsigned int *out_type) 149 { 150 return -EINVAL; 151 } 152 #endif 153 154 static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, 155 irq_hw_number_t hw) 156 { 157 irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); 158 irq_set_chip_data(irq, &combiner_data[hw >> 3]); 159 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 160 161 return 0; 162 } 163 164 static struct irq_domain_ops combiner_irq_domain_ops = { 165 .xlate = combiner_irq_domain_xlate, 166 .map = combiner_irq_domain_map, 167 }; 168 169 void __init combiner_init(void __iomem *combiner_base, 170 struct device_node *np) 171 { 172 int i, irq, irq_base; 173 unsigned int max_nr, nr_irq; 174 175 if (np) { 176 if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { 177 pr_warning("%s: number of combiners not specified, " 178 "setting default as %d.\n", 179 __func__, EXYNOS4_MAX_COMBINER_NR); 180 max_nr = EXYNOS4_MAX_COMBINER_NR; 181 } 182 } else { 183 max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR : 184 EXYNOS4_MAX_COMBINER_NR; 185 } 186 nr_irq = max_nr * MAX_IRQ_IN_COMBINER; 187 188 irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0); 189 if (IS_ERR_VALUE(irq_base)) { 190 irq_base = COMBINER_IRQ(0, 0); 191 pr_warning("%s: irq desc alloc failed. Continuing with %d as linux irq base\n", __func__, irq_base); 192 } 193 194 combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0, 195 &combiner_irq_domain_ops, &combiner_data); 196 if (WARN_ON(!combiner_irq_domain)) { 197 pr_warning("%s: irq domain init failed\n", __func__); 198 return; 199 } 200 201 for (i = 0; i < max_nr; i++) { 202 combiner_init_one(i, combiner_base + (i >> 2) * 0x10); 203 irq = IRQ_SPI(i); 204 #ifdef CONFIG_OF 205 if (np) 206 irq = irq_of_parse_and_map(np, i); 207 #endif 208 combiner_cascade_irq(i, irq); 209 } 210 } 211 212 #ifdef CONFIG_OF 213 static int __init combiner_of_init(struct device_node *np, 214 struct device_node *parent) 215 { 216 void __iomem *combiner_base; 217 218 combiner_base = of_iomap(np, 0); 219 if (!combiner_base) { 220 pr_err("%s: failed to map combiner registers\n", __func__); 221 return -ENXIO; 222 } 223 224 combiner_init(combiner_base, np); 225 226 return 0; 227 } 228 IRQCHIP_DECLARE(exynos4210_combiner, "samsung,exynos4210-combiner", 229 combiner_of_init); 230 #endif 231