irq-ingenic.c (3999B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 4 * Ingenic XBurst platform IRQ support 5 */ 6 7#include <linux/errno.h> 8#include <linux/init.h> 9#include <linux/types.h> 10#include <linux/interrupt.h> 11#include <linux/ioport.h> 12#include <linux/irqchip.h> 13#include <linux/of_address.h> 14#include <linux/of_irq.h> 15#include <linux/timex.h> 16#include <linux/slab.h> 17#include <linux/delay.h> 18 19#include <asm/io.h> 20 21struct ingenic_intc_data { 22 void __iomem *base; 23 struct irq_domain *domain; 24 unsigned num_chips; 25}; 26 27#define JZ_REG_INTC_STATUS 0x00 28#define JZ_REG_INTC_MASK 0x04 29#define JZ_REG_INTC_SET_MASK 0x08 30#define JZ_REG_INTC_CLEAR_MASK 0x0c 31#define JZ_REG_INTC_PENDING 0x10 32#define CHIP_SIZE 0x20 33 34static irqreturn_t intc_cascade(int irq, void *data) 35{ 36 struct ingenic_intc_data *intc = irq_get_handler_data(irq); 37 struct irq_domain *domain = intc->domain; 38 struct irq_chip_generic *gc; 39 uint32_t pending; 40 unsigned i; 41 42 for (i = 0; i < intc->num_chips; i++) { 43 gc = irq_get_domain_generic_chip(domain, i * 32); 44 45 pending = irq_reg_readl(gc, JZ_REG_INTC_PENDING); 46 if (!pending) 47 continue; 48 49 while (pending) { 50 int bit = __fls(pending); 51 52 generic_handle_domain_irq(domain, bit + (i * 32)); 53 pending &= ~BIT(bit); 54 } 55 } 56 57 return IRQ_HANDLED; 58} 59 60static int __init ingenic_intc_of_init(struct device_node *node, 61 unsigned num_chips) 62{ 63 struct ingenic_intc_data *intc; 64 struct irq_chip_generic *gc; 65 struct irq_chip_type *ct; 66 struct irq_domain *domain; 67 int parent_irq, err = 0; 68 unsigned i; 69 70 intc = kzalloc(sizeof(*intc), GFP_KERNEL); 71 if (!intc) { 72 err = -ENOMEM; 73 goto out_err; 74 } 75 76 parent_irq = irq_of_parse_and_map(node, 0); 77 if (!parent_irq) { 78 err = -EINVAL; 79 goto out_free; 80 } 81 82 err = irq_set_handler_data(parent_irq, intc); 83 if (err) 84 goto out_unmap_irq; 85 86 intc->num_chips = num_chips; 87 intc->base = of_iomap(node, 0); 88 if (!intc->base) { 89 err = -ENODEV; 90 goto out_unmap_irq; 91 } 92 93 domain = irq_domain_add_linear(node, num_chips * 32, 94 &irq_generic_chip_ops, NULL); 95 if (!domain) { 96 err = -ENOMEM; 97 goto out_unmap_base; 98 } 99 100 intc->domain = domain; 101 102 err = irq_alloc_domain_generic_chips(domain, 32, 1, "INTC", 103 handle_level_irq, 0, 104 IRQ_NOPROBE | IRQ_LEVEL, 0); 105 if (err) 106 goto out_domain_remove; 107 108 for (i = 0; i < num_chips; i++) { 109 gc = irq_get_domain_generic_chip(domain, i * 32); 110 111 gc->wake_enabled = IRQ_MSK(32); 112 gc->reg_base = intc->base + (i * CHIP_SIZE); 113 114 ct = gc->chip_types; 115 ct->regs.enable = JZ_REG_INTC_CLEAR_MASK; 116 ct->regs.disable = JZ_REG_INTC_SET_MASK; 117 ct->chip.irq_unmask = irq_gc_unmask_enable_reg; 118 ct->chip.irq_mask = irq_gc_mask_disable_reg; 119 ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; 120 ct->chip.irq_set_wake = irq_gc_set_wake; 121 ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND; 122 123 /* Mask all irqs */ 124 irq_reg_writel(gc, IRQ_MSK(32), JZ_REG_INTC_SET_MASK); 125 } 126 127 if (request_irq(parent_irq, intc_cascade, IRQF_NO_SUSPEND, 128 "SoC intc cascade interrupt", NULL)) 129 pr_err("Failed to register SoC intc cascade interrupt\n"); 130 return 0; 131 132out_domain_remove: 133 irq_domain_remove(domain); 134out_unmap_base: 135 iounmap(intc->base); 136out_unmap_irq: 137 irq_dispose_mapping(parent_irq); 138out_free: 139 kfree(intc); 140out_err: 141 return err; 142} 143 144static int __init intc_1chip_of_init(struct device_node *node, 145 struct device_node *parent) 146{ 147 return ingenic_intc_of_init(node, 1); 148} 149IRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init); 150IRQCHIP_DECLARE(jz4725b_intc, "ingenic,jz4725b-intc", intc_1chip_of_init); 151 152static int __init intc_2chip_of_init(struct device_node *node, 153 struct device_node *parent) 154{ 155 return ingenic_intc_of_init(node, 2); 156} 157IRQCHIP_DECLARE(jz4760_intc, "ingenic,jz4760-intc", intc_2chip_of_init); 158IRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", intc_2chip_of_init); 159IRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", intc_2chip_of_init); 160IRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", intc_2chip_of_init);