1a900e5d9SRob Herring /* 2a900e5d9SRob Herring * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. 3a900e5d9SRob Herring * http://www.samsung.com 4a900e5d9SRob Herring * 5a900e5d9SRob Herring * Combiner irqchip for EXYNOS 6a900e5d9SRob Herring * 7a900e5d9SRob Herring * This program is free software; you can redistribute it and/or modify 8a900e5d9SRob Herring * it under the terms of the GNU General Public License version 2 as 9a900e5d9SRob Herring * published by the Free Software Foundation. 10a900e5d9SRob Herring */ 11a900e5d9SRob Herring #include <linux/err.h> 12a900e5d9SRob Herring #include <linux/export.h> 13a900e5d9SRob Herring #include <linux/init.h> 14a900e5d9SRob Herring #include <linux/io.h> 15d34f03d4SArnd Bergmann #include <linux/slab.h> 16a900e5d9SRob Herring #include <linux/irqdomain.h> 17de88cbb7SCatalin Marinas #include <linux/irqchip/chained_irq.h> 18a900e5d9SRob Herring #include <linux/of_address.h> 19a900e5d9SRob Herring #include <linux/of_irq.h> 20a900e5d9SRob Herring 21a900e5d9SRob Herring #include "irqchip.h" 22a900e5d9SRob Herring 23a900e5d9SRob Herring #define COMBINER_ENABLE_SET 0x0 24a900e5d9SRob Herring #define COMBINER_ENABLE_CLEAR 0x4 25a900e5d9SRob Herring #define COMBINER_INT_STATUS 0xC 26a900e5d9SRob Herring 276761dcfeSArnd Bergmann #define IRQ_IN_COMBINER 8 286761dcfeSArnd Bergmann 29a900e5d9SRob Herring static DEFINE_SPINLOCK(irq_controller_lock); 30a900e5d9SRob Herring 31a900e5d9SRob Herring struct combiner_chip_data { 3220adee8fSArnd Bergmann unsigned int hwirq_offset; 33a900e5d9SRob Herring unsigned int irq_mask; 34a900e5d9SRob Herring void __iomem *base; 35df7ef462SChanho Park unsigned int parent_irq; 36a900e5d9SRob Herring }; 37a900e5d9SRob Herring 38a900e5d9SRob Herring static struct irq_domain *combiner_irq_domain; 39a900e5d9SRob Herring 40a900e5d9SRob Herring static inline void __iomem *combiner_base(struct irq_data *data) 41a900e5d9SRob Herring { 42a900e5d9SRob Herring struct combiner_chip_data *combiner_data = 43a900e5d9SRob Herring irq_data_get_irq_chip_data(data); 44a900e5d9SRob Herring 45a900e5d9SRob Herring return combiner_data->base; 46a900e5d9SRob Herring } 47a900e5d9SRob Herring 48a900e5d9SRob Herring static void combiner_mask_irq(struct irq_data *data) 49a900e5d9SRob Herring { 50a900e5d9SRob Herring u32 mask = 1 << (data->hwirq % 32); 51a900e5d9SRob Herring 52a900e5d9SRob Herring __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); 53a900e5d9SRob Herring } 54a900e5d9SRob Herring 55a900e5d9SRob Herring static void combiner_unmask_irq(struct irq_data *data) 56a900e5d9SRob Herring { 57a900e5d9SRob Herring u32 mask = 1 << (data->hwirq % 32); 58a900e5d9SRob Herring 59a900e5d9SRob Herring __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET); 60a900e5d9SRob Herring } 61a900e5d9SRob Herring 62a900e5d9SRob Herring static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) 63a900e5d9SRob Herring { 64a900e5d9SRob Herring struct combiner_chip_data *chip_data = irq_get_handler_data(irq); 65a900e5d9SRob Herring struct irq_chip *chip = irq_get_chip(irq); 66a900e5d9SRob Herring unsigned int cascade_irq, combiner_irq; 67a900e5d9SRob Herring unsigned long status; 68a900e5d9SRob Herring 69a900e5d9SRob Herring chained_irq_enter(chip, desc); 70a900e5d9SRob Herring 71a900e5d9SRob Herring spin_lock(&irq_controller_lock); 72a900e5d9SRob Herring status = __raw_readl(chip_data->base + COMBINER_INT_STATUS); 73a900e5d9SRob Herring spin_unlock(&irq_controller_lock); 74a900e5d9SRob Herring status &= chip_data->irq_mask; 75a900e5d9SRob Herring 76a900e5d9SRob Herring if (status == 0) 77a900e5d9SRob Herring goto out; 78a900e5d9SRob Herring 7920adee8fSArnd Bergmann combiner_irq = chip_data->hwirq_offset + __ffs(status); 8020adee8fSArnd Bergmann cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq); 81a900e5d9SRob Herring 8220adee8fSArnd Bergmann if (unlikely(!cascade_irq)) 83a8378485SPankaj Dubey handle_bad_irq(irq, desc); 84a900e5d9SRob Herring else 85a900e5d9SRob Herring generic_handle_irq(cascade_irq); 86a900e5d9SRob Herring 87a900e5d9SRob Herring out: 88a900e5d9SRob Herring chained_irq_exit(chip, desc); 89a900e5d9SRob Herring } 90a900e5d9SRob Herring 91df7ef462SChanho Park #ifdef CONFIG_SMP 92df7ef462SChanho Park static int combiner_set_affinity(struct irq_data *d, 93df7ef462SChanho Park const struct cpumask *mask_val, bool force) 94df7ef462SChanho Park { 95df7ef462SChanho Park struct combiner_chip_data *chip_data = irq_data_get_irq_chip_data(d); 96df7ef462SChanho Park struct irq_chip *chip = irq_get_chip(chip_data->parent_irq); 97df7ef462SChanho Park struct irq_data *data = irq_get_irq_data(chip_data->parent_irq); 98df7ef462SChanho Park 99df7ef462SChanho Park if (chip && chip->irq_set_affinity) 100df7ef462SChanho Park return chip->irq_set_affinity(data, mask_val, force); 101df7ef462SChanho Park else 102df7ef462SChanho Park return -EINVAL; 103df7ef462SChanho Park } 104df7ef462SChanho Park #endif 105df7ef462SChanho Park 106a900e5d9SRob Herring static struct irq_chip combiner_chip = { 107a900e5d9SRob Herring .name = "COMBINER", 108a900e5d9SRob Herring .irq_mask = combiner_mask_irq, 109a900e5d9SRob Herring .irq_unmask = combiner_unmask_irq, 110df7ef462SChanho Park #ifdef CONFIG_SMP 111df7ef462SChanho Park .irq_set_affinity = combiner_set_affinity, 112df7ef462SChanho Park #endif 113a900e5d9SRob Herring }; 114a900e5d9SRob Herring 115d34f03d4SArnd Bergmann static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data, 1164e164dc5SChanho Park unsigned int irq) 1174e164dc5SChanho Park { 118d34f03d4SArnd Bergmann if (irq_set_handler_data(irq, combiner_data) != 0) 119a900e5d9SRob Herring BUG(); 120a900e5d9SRob Herring irq_set_chained_handler(irq, combiner_handle_cascade_irq); 121a900e5d9SRob Herring } 122a900e5d9SRob Herring 123d34f03d4SArnd Bergmann static void __init combiner_init_one(struct combiner_chip_data *combiner_data, 124d34f03d4SArnd Bergmann unsigned int combiner_nr, 125df7ef462SChanho Park void __iomem *base, unsigned int irq) 126a900e5d9SRob Herring { 127d34f03d4SArnd Bergmann combiner_data->base = base; 12820adee8fSArnd Bergmann combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER; 129d34f03d4SArnd Bergmann combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3); 130d34f03d4SArnd Bergmann combiner_data->parent_irq = irq; 131a900e5d9SRob Herring 132a900e5d9SRob Herring /* Disable all interrupts */ 133d34f03d4SArnd Bergmann __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR); 134a900e5d9SRob Herring } 135a900e5d9SRob Herring 136a900e5d9SRob Herring static int combiner_irq_domain_xlate(struct irq_domain *d, 137a900e5d9SRob Herring struct device_node *controller, 138a900e5d9SRob Herring const u32 *intspec, unsigned int intsize, 139a900e5d9SRob Herring unsigned long *out_hwirq, 140a900e5d9SRob Herring unsigned int *out_type) 141a900e5d9SRob Herring { 142a900e5d9SRob Herring if (d->of_node != controller) 143a900e5d9SRob Herring return -EINVAL; 144a900e5d9SRob Herring 145a900e5d9SRob Herring if (intsize < 2) 146a900e5d9SRob Herring return -EINVAL; 147a900e5d9SRob Herring 1486761dcfeSArnd Bergmann *out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1]; 149a900e5d9SRob Herring *out_type = 0; 150a900e5d9SRob Herring 151a900e5d9SRob Herring return 0; 152a900e5d9SRob Herring } 153a900e5d9SRob Herring 154a900e5d9SRob Herring static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, 155a900e5d9SRob Herring irq_hw_number_t hw) 156a900e5d9SRob Herring { 157d34f03d4SArnd Bergmann struct combiner_chip_data *combiner_data = d->host_data; 158d34f03d4SArnd Bergmann 159a900e5d9SRob Herring irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); 160a900e5d9SRob Herring irq_set_chip_data(irq, &combiner_data[hw >> 3]); 161a900e5d9SRob Herring set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 162a900e5d9SRob Herring 163a900e5d9SRob Herring return 0; 164a900e5d9SRob Herring } 165a900e5d9SRob Herring 166a900e5d9SRob Herring static struct irq_domain_ops combiner_irq_domain_ops = { 167a900e5d9SRob Herring .xlate = combiner_irq_domain_xlate, 168a900e5d9SRob Herring .map = combiner_irq_domain_map, 169a900e5d9SRob Herring }; 170a900e5d9SRob Herring 171b8394deeSSachin Kamat static void __init combiner_init(void __iomem *combiner_base, 1726761dcfeSArnd Bergmann struct device_node *np, 1739403ac88SChander Kashyap unsigned int max_nr) 174a900e5d9SRob Herring { 175863a08dcSArnd Bergmann int i, irq; 1766761dcfeSArnd Bergmann unsigned int nr_irq; 177d34f03d4SArnd Bergmann struct combiner_chip_data *combiner_data; 178a900e5d9SRob Herring 1796761dcfeSArnd Bergmann nr_irq = max_nr * IRQ_IN_COMBINER; 1804e164dc5SChanho Park 181d34f03d4SArnd Bergmann combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL); 182d34f03d4SArnd Bergmann if (!combiner_data) { 183d34f03d4SArnd Bergmann pr_warning("%s: could not allocate combiner data\n", __func__); 184d34f03d4SArnd Bergmann return; 185a900e5d9SRob Herring } 1864e164dc5SChanho Park 1879403ac88SChander Kashyap combiner_irq_domain = irq_domain_add_linear(np, nr_irq, 188d34f03d4SArnd Bergmann &combiner_irq_domain_ops, combiner_data); 189a900e5d9SRob Herring if (WARN_ON(!combiner_irq_domain)) { 190a900e5d9SRob Herring pr_warning("%s: irq domain init failed\n", __func__); 191a900e5d9SRob Herring return; 192a900e5d9SRob Herring } 193a900e5d9SRob Herring 194a900e5d9SRob Herring for (i = 0; i < max_nr; i++) { 195a900e5d9SRob Herring irq = irq_of_parse_and_map(np, i); 19692c8e496SArnd Bergmann 197d34f03d4SArnd Bergmann combiner_init_one(&combiner_data[i], i, 198d34f03d4SArnd Bergmann combiner_base + (i >> 2) * 0x10, irq); 199d34f03d4SArnd Bergmann combiner_cascade_irq(&combiner_data[i], irq); 200a900e5d9SRob Herring } 201a900e5d9SRob Herring } 202a900e5d9SRob Herring 203a900e5d9SRob Herring static int __init combiner_of_init(struct device_node *np, 204a900e5d9SRob Herring struct device_node *parent) 205a900e5d9SRob Herring { 206a900e5d9SRob Herring void __iomem *combiner_base; 2076761dcfeSArnd Bergmann unsigned int max_nr = 20; 208a900e5d9SRob Herring 209a900e5d9SRob Herring combiner_base = of_iomap(np, 0); 210a900e5d9SRob Herring if (!combiner_base) { 211a900e5d9SRob Herring pr_err("%s: failed to map combiner registers\n", __func__); 212a900e5d9SRob Herring return -ENXIO; 213a900e5d9SRob Herring } 214a900e5d9SRob Herring 2156761dcfeSArnd Bergmann if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { 2166761dcfeSArnd Bergmann pr_info("%s: number of combiners not specified, " 2176761dcfeSArnd Bergmann "setting default as %d.\n", 2186761dcfeSArnd Bergmann __func__, max_nr); 2196761dcfeSArnd Bergmann } 2206761dcfeSArnd Bergmann 2219403ac88SChander Kashyap combiner_init(combiner_base, np, max_nr); 222a900e5d9SRob Herring 223a900e5d9SRob Herring return 0; 224a900e5d9SRob Herring } 225a900e5d9SRob Herring IRQCHIP_DECLARE(exynos4210_combiner, "samsung,exynos4210-combiner", 226a900e5d9SRob Herring combiner_of_init); 227