| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | *  linux/drivers/irqchip/irq-zevio.c | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au> | 
|  | 5 | * | 
|  | 6 | * This program is free software; you can redistribute it and/or modify | 
|  | 7 | * it under the terms of the GNU General Public License version 2, as | 
|  | 8 | * published by the Free Software Foundation. | 
|  | 9 | * | 
|  | 10 | */ | 
|  | 11 |  | 
|  | 12 | #include <linux/io.h> | 
|  | 13 | #include <linux/irq.h> | 
|  | 14 | #include <linux/irqchip.h> | 
|  | 15 | #include <linux/of.h> | 
|  | 16 | #include <linux/of_address.h> | 
|  | 17 | #include <linux/of_irq.h> | 
|  | 18 |  | 
|  | 19 | #include <asm/mach/irq.h> | 
|  | 20 | #include <asm/exception.h> | 
|  | 21 |  | 
|  | 22 | #define IO_STATUS	0x000 | 
|  | 23 | #define IO_RAW_STATUS	0x004 | 
|  | 24 | #define IO_ENABLE	0x008 | 
|  | 25 | #define IO_DISABLE	0x00C | 
|  | 26 | #define IO_CURRENT	0x020 | 
|  | 27 | #define IO_RESET	0x028 | 
|  | 28 | #define IO_MAX_PRIOTY	0x02C | 
|  | 29 |  | 
|  | 30 | #define IO_IRQ_BASE	0x000 | 
|  | 31 | #define IO_FIQ_BASE	0x100 | 
|  | 32 |  | 
|  | 33 | #define IO_INVERT_SEL	0x200 | 
|  | 34 | #define IO_STICKY_SEL	0x204 | 
|  | 35 | #define IO_PRIORITY_SEL	0x300 | 
|  | 36 |  | 
|  | 37 | #define MAX_INTRS	32 | 
|  | 38 | #define FIQ_START	MAX_INTRS | 
|  | 39 |  | 
|  | 40 | static struct irq_domain *zevio_irq_domain; | 
|  | 41 | static void __iomem *zevio_irq_io; | 
|  | 42 |  | 
|  | 43 | static void zevio_irq_ack(struct irq_data *irqd) | 
|  | 44 | { | 
|  | 45 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(irqd); | 
|  | 46 | struct irq_chip_regs *regs = &irq_data_get_chip_type(irqd)->regs; | 
|  | 47 |  | 
|  | 48 | readl(gc->reg_base + regs->ack); | 
|  | 49 | } | 
|  | 50 |  | 
|  | 51 | static void __exception_irq_entry zevio_handle_irq(struct pt_regs *regs) | 
|  | 52 | { | 
|  | 53 | int irqnr; | 
|  | 54 |  | 
|  | 55 | while (readl(zevio_irq_io + IO_STATUS)) { | 
|  | 56 | irqnr = readl(zevio_irq_io + IO_CURRENT); | 
|  | 57 | handle_domain_irq(zevio_irq_domain, irqnr, regs); | 
|  | 58 | }; | 
|  | 59 | } | 
|  | 60 |  | 
|  | 61 | static void __init zevio_init_irq_base(void __iomem *base) | 
|  | 62 | { | 
|  | 63 | /* Disable all interrupts */ | 
|  | 64 | writel(~0, base + IO_DISABLE); | 
|  | 65 |  | 
|  | 66 | /* Accept interrupts of all priorities */ | 
|  | 67 | writel(0xF, base + IO_MAX_PRIOTY); | 
|  | 68 |  | 
|  | 69 | /* Reset existing interrupts */ | 
|  | 70 | readl(base + IO_RESET); | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | static int __init zevio_of_init(struct device_node *node, | 
|  | 74 | struct device_node *parent) | 
|  | 75 | { | 
|  | 76 | unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; | 
|  | 77 | struct irq_chip_generic *gc; | 
|  | 78 | int ret; | 
|  | 79 |  | 
|  | 80 | if (WARN_ON(zevio_irq_io || zevio_irq_domain)) | 
|  | 81 | return -EBUSY; | 
|  | 82 |  | 
|  | 83 | zevio_irq_io = of_iomap(node, 0); | 
|  | 84 | BUG_ON(!zevio_irq_io); | 
|  | 85 |  | 
|  | 86 | /* Do not invert interrupt status bits */ | 
|  | 87 | writel(~0, zevio_irq_io + IO_INVERT_SEL); | 
|  | 88 |  | 
|  | 89 | /* Disable sticky interrupts */ | 
|  | 90 | writel(0, zevio_irq_io + IO_STICKY_SEL); | 
|  | 91 |  | 
|  | 92 | /* We don't use IRQ priorities. Set each IRQ to highest priority. */ | 
|  | 93 | memset_io(zevio_irq_io + IO_PRIORITY_SEL, 0, MAX_INTRS * sizeof(u32)); | 
|  | 94 |  | 
|  | 95 | /* Init IRQ and FIQ */ | 
|  | 96 | zevio_init_irq_base(zevio_irq_io + IO_IRQ_BASE); | 
|  | 97 | zevio_init_irq_base(zevio_irq_io + IO_FIQ_BASE); | 
|  | 98 |  | 
|  | 99 | zevio_irq_domain = irq_domain_add_linear(node, MAX_INTRS, | 
|  | 100 | &irq_generic_chip_ops, NULL); | 
|  | 101 | BUG_ON(!zevio_irq_domain); | 
|  | 102 |  | 
|  | 103 | ret = irq_alloc_domain_generic_chips(zevio_irq_domain, MAX_INTRS, 1, | 
|  | 104 | "zevio_intc", handle_level_irq, | 
|  | 105 | clr, 0, IRQ_GC_INIT_MASK_CACHE); | 
|  | 106 | BUG_ON(ret); | 
|  | 107 |  | 
|  | 108 | gc = irq_get_domain_generic_chip(zevio_irq_domain, 0); | 
|  | 109 | gc->reg_base				= zevio_irq_io; | 
|  | 110 | gc->chip_types[0].chip.irq_ack		= zevio_irq_ack; | 
|  | 111 | gc->chip_types[0].chip.irq_mask		= irq_gc_mask_disable_reg; | 
|  | 112 | gc->chip_types[0].chip.irq_unmask	= irq_gc_unmask_enable_reg; | 
|  | 113 | gc->chip_types[0].regs.mask		= IO_IRQ_BASE + IO_ENABLE; | 
|  | 114 | gc->chip_types[0].regs.enable		= IO_IRQ_BASE + IO_ENABLE; | 
|  | 115 | gc->chip_types[0].regs.disable		= IO_IRQ_BASE + IO_DISABLE; | 
|  | 116 | gc->chip_types[0].regs.ack		= IO_IRQ_BASE + IO_RESET; | 
|  | 117 |  | 
|  | 118 | set_handle_irq(zevio_handle_irq); | 
|  | 119 |  | 
|  | 120 | pr_info("TI-NSPIRE classic IRQ controller\n"); | 
|  | 121 | return 0; | 
|  | 122 | } | 
|  | 123 |  | 
|  | 124 | IRQCHIP_DECLARE(zevio_irq, "lsi,zevio-intc", zevio_of_init); |