1b1479ebbSBoris BREZILLON /* 2b1479ebbSBoris BREZILLON * Atmel AT91 common AIC (Advanced Interrupt Controller) code shared by 3b1479ebbSBoris BREZILLON * irq-atmel-aic and irq-atmel-aic5 drivers 4b1479ebbSBoris BREZILLON * 5b1479ebbSBoris BREZILLON * Copyright (C) 2004 SAN People 6b1479ebbSBoris BREZILLON * Copyright (C) 2004 ATMEL 7b1479ebbSBoris BREZILLON * Copyright (C) Rick Bronson 8b1479ebbSBoris BREZILLON * Copyright (C) 2014 Free Electrons 9b1479ebbSBoris BREZILLON * 10b1479ebbSBoris BREZILLON * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 11b1479ebbSBoris BREZILLON * 12b1479ebbSBoris BREZILLON * This file is licensed under the terms of the GNU General Public 13b1479ebbSBoris BREZILLON * License version 2. This program is licensed "as is" without any 14b1479ebbSBoris BREZILLON * warranty of any kind, whether express or implied. 15b1479ebbSBoris BREZILLON */ 16b1479ebbSBoris BREZILLON 17b1479ebbSBoris BREZILLON #include <linux/errno.h> 18b1479ebbSBoris BREZILLON #include <linux/io.h> 19b1479ebbSBoris BREZILLON #include <linux/irq.h> 20b1479ebbSBoris BREZILLON #include <linux/irqdomain.h> 21b1479ebbSBoris BREZILLON #include <linux/of.h> 22b1479ebbSBoris BREZILLON #include <linux/of_address.h> 23b1479ebbSBoris BREZILLON #include <linux/slab.h> 24b1479ebbSBoris BREZILLON 25b1479ebbSBoris BREZILLON #include "irq-atmel-aic-common.h" 26b1479ebbSBoris BREZILLON 27b1479ebbSBoris BREZILLON #define AT91_AIC_PRIOR GENMASK(2, 0) 28b1479ebbSBoris BREZILLON #define AT91_AIC_IRQ_MIN_PRIORITY 0 29b1479ebbSBoris BREZILLON #define AT91_AIC_IRQ_MAX_PRIORITY 7 30b1479ebbSBoris BREZILLON 31b1479ebbSBoris BREZILLON #define AT91_AIC_SRCTYPE GENMASK(7, 6) 32b1479ebbSBoris BREZILLON #define AT91_AIC_SRCTYPE_LOW (0 << 5) 33b1479ebbSBoris BREZILLON #define AT91_AIC_SRCTYPE_FALLING (1 << 5) 34b1479ebbSBoris BREZILLON #define AT91_AIC_SRCTYPE_HIGH (2 << 5) 35b1479ebbSBoris BREZILLON #define AT91_AIC_SRCTYPE_RISING (3 << 5) 36b1479ebbSBoris BREZILLON 37b1479ebbSBoris BREZILLON struct aic_chip_data { 38b1479ebbSBoris BREZILLON u32 ext_irqs; 39b1479ebbSBoris BREZILLON }; 40b1479ebbSBoris BREZILLON 41b1479ebbSBoris BREZILLON static void aic_common_shutdown(struct irq_data *d) 42b1479ebbSBoris BREZILLON { 43b1479ebbSBoris BREZILLON struct irq_chip_type *ct = irq_data_get_chip_type(d); 44b1479ebbSBoris BREZILLON 45b1479ebbSBoris BREZILLON ct->chip.irq_mask(d); 46b1479ebbSBoris BREZILLON } 47b1479ebbSBoris BREZILLON 48b1479ebbSBoris BREZILLON int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val) 49b1479ebbSBoris BREZILLON { 50b1479ebbSBoris BREZILLON struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 51b1479ebbSBoris BREZILLON struct aic_chip_data *aic = gc->private; 52b1479ebbSBoris BREZILLON unsigned aic_type; 53b1479ebbSBoris BREZILLON 54b1479ebbSBoris BREZILLON switch (type) { 55b1479ebbSBoris BREZILLON case IRQ_TYPE_LEVEL_HIGH: 56b1479ebbSBoris BREZILLON aic_type = AT91_AIC_SRCTYPE_HIGH; 57b1479ebbSBoris BREZILLON break; 58b1479ebbSBoris BREZILLON case IRQ_TYPE_EDGE_RISING: 59b1479ebbSBoris BREZILLON aic_type = AT91_AIC_SRCTYPE_RISING; 60b1479ebbSBoris BREZILLON break; 61b1479ebbSBoris BREZILLON case IRQ_TYPE_LEVEL_LOW: 62b1479ebbSBoris BREZILLON if (!(d->mask & aic->ext_irqs)) 63b1479ebbSBoris BREZILLON return -EINVAL; 64b1479ebbSBoris BREZILLON 65b1479ebbSBoris BREZILLON aic_type = AT91_AIC_SRCTYPE_LOW; 66b1479ebbSBoris BREZILLON break; 67b1479ebbSBoris BREZILLON case IRQ_TYPE_EDGE_FALLING: 68b1479ebbSBoris BREZILLON if (!(d->mask & aic->ext_irqs)) 69b1479ebbSBoris BREZILLON return -EINVAL; 70b1479ebbSBoris BREZILLON 71b1479ebbSBoris BREZILLON aic_type = AT91_AIC_SRCTYPE_FALLING; 72b1479ebbSBoris BREZILLON break; 73b1479ebbSBoris BREZILLON default: 74b1479ebbSBoris BREZILLON return -EINVAL; 75b1479ebbSBoris BREZILLON } 76b1479ebbSBoris BREZILLON 77b1479ebbSBoris BREZILLON *val &= AT91_AIC_SRCTYPE; 78b1479ebbSBoris BREZILLON *val |= aic_type; 79b1479ebbSBoris BREZILLON 80b1479ebbSBoris BREZILLON return 0; 81b1479ebbSBoris BREZILLON } 82b1479ebbSBoris BREZILLON 83b1479ebbSBoris BREZILLON int aic_common_set_priority(int priority, unsigned *val) 84b1479ebbSBoris BREZILLON { 85b1479ebbSBoris BREZILLON if (priority < AT91_AIC_IRQ_MIN_PRIORITY || 86b1479ebbSBoris BREZILLON priority > AT91_AIC_IRQ_MAX_PRIORITY) 87b1479ebbSBoris BREZILLON return -EINVAL; 88b1479ebbSBoris BREZILLON 89b1479ebbSBoris BREZILLON *val &= AT91_AIC_PRIOR; 90b1479ebbSBoris BREZILLON *val |= priority; 91b1479ebbSBoris BREZILLON 92b1479ebbSBoris BREZILLON return 0; 93b1479ebbSBoris BREZILLON } 94b1479ebbSBoris BREZILLON 95b1479ebbSBoris BREZILLON int aic_common_irq_domain_xlate(struct irq_domain *d, 96b1479ebbSBoris BREZILLON struct device_node *ctrlr, 97b1479ebbSBoris BREZILLON const u32 *intspec, 98b1479ebbSBoris BREZILLON unsigned int intsize, 99b1479ebbSBoris BREZILLON irq_hw_number_t *out_hwirq, 100b1479ebbSBoris BREZILLON unsigned int *out_type) 101b1479ebbSBoris BREZILLON { 102b1479ebbSBoris BREZILLON if (WARN_ON(intsize < 3)) 103b1479ebbSBoris BREZILLON return -EINVAL; 104b1479ebbSBoris BREZILLON 105b1479ebbSBoris BREZILLON if (WARN_ON((intspec[2] < AT91_AIC_IRQ_MIN_PRIORITY) || 106b1479ebbSBoris BREZILLON (intspec[2] > AT91_AIC_IRQ_MAX_PRIORITY))) 107b1479ebbSBoris BREZILLON return -EINVAL; 108b1479ebbSBoris BREZILLON 109b1479ebbSBoris BREZILLON *out_hwirq = intspec[0]; 110b1479ebbSBoris BREZILLON *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; 111b1479ebbSBoris BREZILLON 112b1479ebbSBoris BREZILLON return 0; 113b1479ebbSBoris BREZILLON } 114b1479ebbSBoris BREZILLON 115b1479ebbSBoris BREZILLON static void __init aic_common_ext_irq_of_init(struct irq_domain *domain) 116b1479ebbSBoris BREZILLON { 117b1479ebbSBoris BREZILLON struct device_node *node = domain->of_node; 118b1479ebbSBoris BREZILLON struct irq_chip_generic *gc; 119b1479ebbSBoris BREZILLON struct aic_chip_data *aic; 120b1479ebbSBoris BREZILLON struct property *prop; 121b1479ebbSBoris BREZILLON const __be32 *p; 122b1479ebbSBoris BREZILLON u32 hwirq; 123b1479ebbSBoris BREZILLON 124b1479ebbSBoris BREZILLON gc = irq_get_domain_generic_chip(domain, 0); 125b1479ebbSBoris BREZILLON 126b1479ebbSBoris BREZILLON aic = gc->private; 127b1479ebbSBoris BREZILLON aic->ext_irqs |= 1; 128b1479ebbSBoris BREZILLON 129b1479ebbSBoris BREZILLON of_property_for_each_u32(node, "atmel,external-irqs", prop, p, hwirq) { 130b1479ebbSBoris BREZILLON gc = irq_get_domain_generic_chip(domain, hwirq); 131b1479ebbSBoris BREZILLON if (!gc) { 132b1479ebbSBoris BREZILLON pr_warn("AIC: external irq %d >= %d skip it\n", 133b1479ebbSBoris BREZILLON hwirq, domain->revmap_size); 134b1479ebbSBoris BREZILLON continue; 135b1479ebbSBoris BREZILLON } 136b1479ebbSBoris BREZILLON 137b1479ebbSBoris BREZILLON aic = gc->private; 138b1479ebbSBoris BREZILLON aic->ext_irqs |= (1 << (hwirq % 32)); 139b1479ebbSBoris BREZILLON } 140b1479ebbSBoris BREZILLON } 141b1479ebbSBoris BREZILLON 1423d61467fSBoris BREZILLON #define AT91_RTC_IDR 0x24 1433d61467fSBoris BREZILLON #define AT91_RTC_IMR 0x28 1443d61467fSBoris BREZILLON #define AT91_RTC_IRQ_MASK 0x1f 1453d61467fSBoris BREZILLON 1463d61467fSBoris BREZILLON void __init aic_common_rtc_irq_fixup(struct device_node *root) 1473d61467fSBoris BREZILLON { 1483d61467fSBoris BREZILLON struct device_node *np; 1493d61467fSBoris BREZILLON void __iomem *regs; 1503d61467fSBoris BREZILLON 1513d61467fSBoris BREZILLON np = of_find_compatible_node(root, NULL, "atmel,at91rm9200-rtc"); 1523d61467fSBoris BREZILLON if (!np) 1533d61467fSBoris BREZILLON np = of_find_compatible_node(root, NULL, 1543d61467fSBoris BREZILLON "atmel,at91sam9x5-rtc"); 1553d61467fSBoris BREZILLON 1563d61467fSBoris BREZILLON if (!np) 1573d61467fSBoris BREZILLON return; 1583d61467fSBoris BREZILLON 1593d61467fSBoris BREZILLON regs = of_iomap(np, 0); 1603d61467fSBoris BREZILLON of_node_put(np); 1613d61467fSBoris BREZILLON 1623d61467fSBoris BREZILLON if (!regs) 1633d61467fSBoris BREZILLON return; 1643d61467fSBoris BREZILLON 1653d61467fSBoris BREZILLON writel(AT91_RTC_IRQ_MASK, regs + AT91_RTC_IDR); 1663d61467fSBoris BREZILLON 1673d61467fSBoris BREZILLON iounmap(regs); 1683d61467fSBoris BREZILLON } 1693d61467fSBoris BREZILLON 170b2f579b5SBoris BREZILLON void __init aic_common_irq_fixup(const struct of_device_id *matches) 171b2f579b5SBoris BREZILLON { 172b2f579b5SBoris BREZILLON struct device_node *root = of_find_node_by_path("/"); 173b2f579b5SBoris BREZILLON const struct of_device_id *match; 174b2f579b5SBoris BREZILLON 175b2f579b5SBoris BREZILLON if (!root) 176b2f579b5SBoris BREZILLON return; 177b2f579b5SBoris BREZILLON 178b2f579b5SBoris BREZILLON match = of_match_node(matches, root); 179b2f579b5SBoris BREZILLON of_node_put(root); 180b2f579b5SBoris BREZILLON 181b2f579b5SBoris BREZILLON if (match) { 182b2f579b5SBoris BREZILLON void (*fixup)(struct device_node *) = match->data; 183b2f579b5SBoris BREZILLON fixup(root); 184b2f579b5SBoris BREZILLON } 185b2f579b5SBoris BREZILLON 186b2f579b5SBoris BREZILLON of_node_put(root); 187b2f579b5SBoris BREZILLON } 188b2f579b5SBoris BREZILLON 189b1479ebbSBoris BREZILLON struct irq_domain *__init aic_common_of_init(struct device_node *node, 190b1479ebbSBoris BREZILLON const struct irq_domain_ops *ops, 191b1479ebbSBoris BREZILLON const char *name, int nirqs) 192b1479ebbSBoris BREZILLON { 193b1479ebbSBoris BREZILLON struct irq_chip_generic *gc; 194b1479ebbSBoris BREZILLON struct irq_domain *domain; 195b1479ebbSBoris BREZILLON struct aic_chip_data *aic; 196b1479ebbSBoris BREZILLON void __iomem *reg_base; 197b1479ebbSBoris BREZILLON int nchips; 198b1479ebbSBoris BREZILLON int ret; 199b1479ebbSBoris BREZILLON int i; 200b1479ebbSBoris BREZILLON 201b1479ebbSBoris BREZILLON nchips = DIV_ROUND_UP(nirqs, 32); 202b1479ebbSBoris BREZILLON 203b1479ebbSBoris BREZILLON reg_base = of_iomap(node, 0); 204b1479ebbSBoris BREZILLON if (!reg_base) 205b1479ebbSBoris BREZILLON return ERR_PTR(-ENOMEM); 206b1479ebbSBoris BREZILLON 207b1479ebbSBoris BREZILLON aic = kcalloc(nchips, sizeof(*aic), GFP_KERNEL); 208b1479ebbSBoris BREZILLON if (!aic) { 209b1479ebbSBoris BREZILLON ret = -ENOMEM; 210b1479ebbSBoris BREZILLON goto err_iounmap; 211b1479ebbSBoris BREZILLON } 212b1479ebbSBoris BREZILLON 213b1479ebbSBoris BREZILLON domain = irq_domain_add_linear(node, nchips * 32, ops, aic); 214b1479ebbSBoris BREZILLON if (!domain) { 215b1479ebbSBoris BREZILLON ret = -ENOMEM; 216b1479ebbSBoris BREZILLON goto err_free_aic; 217b1479ebbSBoris BREZILLON } 218b1479ebbSBoris BREZILLON 219b1479ebbSBoris BREZILLON ret = irq_alloc_domain_generic_chips(domain, 32, 1, name, 220*45977fe3SBoris Brezillon handle_fasteoi_irq, 221*45977fe3SBoris Brezillon IRQ_NOREQUEST | IRQ_NOPROBE | 222*45977fe3SBoris Brezillon IRQ_NOAUTOEN, 0, 0); 223b1479ebbSBoris BREZILLON if (ret) 224b1479ebbSBoris BREZILLON goto err_domain_remove; 225b1479ebbSBoris BREZILLON 226b1479ebbSBoris BREZILLON for (i = 0; i < nchips; i++) { 227b1479ebbSBoris BREZILLON gc = irq_get_domain_generic_chip(domain, i * 32); 228b1479ebbSBoris BREZILLON 229b1479ebbSBoris BREZILLON gc->reg_base = reg_base; 230b1479ebbSBoris BREZILLON 231b1479ebbSBoris BREZILLON gc->unused = 0; 232b1479ebbSBoris BREZILLON gc->wake_enabled = ~0; 233b1479ebbSBoris BREZILLON gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK; 234b1479ebbSBoris BREZILLON gc->chip_types[0].chip.irq_eoi = irq_gc_eoi; 235b1479ebbSBoris BREZILLON gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; 236b1479ebbSBoris BREZILLON gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown; 237b1479ebbSBoris BREZILLON gc->private = &aic[i]; 238b1479ebbSBoris BREZILLON } 239b1479ebbSBoris BREZILLON 240b1479ebbSBoris BREZILLON aic_common_ext_irq_of_init(domain); 241b1479ebbSBoris BREZILLON 242b1479ebbSBoris BREZILLON return domain; 243b1479ebbSBoris BREZILLON 244b1479ebbSBoris BREZILLON err_domain_remove: 245b1479ebbSBoris BREZILLON irq_domain_remove(domain); 246b1479ebbSBoris BREZILLON 247b1479ebbSBoris BREZILLON err_free_aic: 248b1479ebbSBoris BREZILLON kfree(aic); 249b1479ebbSBoris BREZILLON 250b1479ebbSBoris BREZILLON err_iounmap: 251b1479ebbSBoris BREZILLON iounmap(reg_base); 252b1479ebbSBoris BREZILLON 253b1479ebbSBoris BREZILLON return ERR_PTR(ret); 254b1479ebbSBoris BREZILLON } 255