1 /* 2 * arch/powerpc/platforms/embedded6xx/hlwd-pic.c 3 * 4 * Nintendo Wii "Hollywood" interrupt controller support. 5 * Copyright (C) 2009 The GameCube Linux Team 6 * Copyright (C) 2009 Albert Herranz 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * as published by the Free Software Foundation; either version 2 11 * of the License, or (at your option) any later version. 12 * 13 */ 14 #define DRV_MODULE_NAME "hlwd-pic" 15 #define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt 16 17 #include <linux/kernel.h> 18 #include <linux/init.h> 19 #include <linux/irq.h> 20 #include <linux/of.h> 21 #include <linux/of_address.h> 22 #include <linux/of_irq.h> 23 #include <asm/io.h> 24 25 #include "hlwd-pic.h" 26 27 #define HLWD_NR_IRQS 32 28 29 /* 30 * Each interrupt has a corresponding bit in both 31 * the Interrupt Cause (ICR) and Interrupt Mask (IMR) registers. 32 * 33 * Enabling/disabling an interrupt line involves asserting/clearing 34 * the corresponding bit in IMR. ACK'ing a request simply involves 35 * asserting the corresponding bit in ICR. 36 */ 37 #define HW_BROADWAY_ICR 0x00 38 #define HW_BROADWAY_IMR 0x04 39 40 41 /* 42 * IRQ chip hooks. 43 * 44 */ 45 46 static void hlwd_pic_mask_and_ack(struct irq_data *d) 47 { 48 int irq = irqd_to_hwirq(d); 49 void __iomem *io_base = irq_data_get_irq_chip_data(d); 50 u32 mask = 1 << irq; 51 52 clrbits32(io_base + HW_BROADWAY_IMR, mask); 53 out_be32(io_base + HW_BROADWAY_ICR, mask); 54 } 55 56 static void hlwd_pic_ack(struct irq_data *d) 57 { 58 int irq = irqd_to_hwirq(d); 59 void __iomem *io_base = irq_data_get_irq_chip_data(d); 60 61 out_be32(io_base + HW_BROADWAY_ICR, 1 << irq); 62 } 63 64 static void hlwd_pic_mask(struct irq_data *d) 65 { 66 int irq = irqd_to_hwirq(d); 67 void __iomem *io_base = irq_data_get_irq_chip_data(d); 68 69 clrbits32(io_base + HW_BROADWAY_IMR, 1 << irq); 70 } 71 72 static void hlwd_pic_unmask(struct irq_data *d) 73 { 74 int irq = irqd_to_hwirq(d); 75 void __iomem *io_base = irq_data_get_irq_chip_data(d); 76 77 setbits32(io_base + HW_BROADWAY_IMR, 1 << irq); 78 } 79 80 81 static struct irq_chip hlwd_pic = { 82 .name = "hlwd-pic", 83 .irq_ack = hlwd_pic_ack, 84 .irq_mask_ack = hlwd_pic_mask_and_ack, 85 .irq_mask = hlwd_pic_mask, 86 .irq_unmask = hlwd_pic_unmask, 87 }; 88 89 /* 90 * IRQ host hooks. 91 * 92 */ 93 94 static struct irq_domain *hlwd_irq_host; 95 96 static int hlwd_pic_map(struct irq_domain *h, unsigned int virq, 97 irq_hw_number_t hwirq) 98 { 99 irq_set_chip_data(virq, h->host_data); 100 irq_set_status_flags(virq, IRQ_LEVEL); 101 irq_set_chip_and_handler(virq, &hlwd_pic, handle_level_irq); 102 return 0; 103 } 104 105 static const struct irq_domain_ops hlwd_irq_domain_ops = { 106 .map = hlwd_pic_map, 107 }; 108 109 static unsigned int __hlwd_pic_get_irq(struct irq_domain *h) 110 { 111 void __iomem *io_base = h->host_data; 112 int irq; 113 u32 irq_status; 114 115 irq_status = in_be32(io_base + HW_BROADWAY_ICR) & 116 in_be32(io_base + HW_BROADWAY_IMR); 117 if (irq_status == 0) 118 return NO_IRQ; /* no more IRQs pending */ 119 120 irq = __ffs(irq_status); 121 return irq_linear_revmap(h, irq); 122 } 123 124 static void hlwd_pic_irq_cascade(unsigned int cascade_virq, 125 struct irq_desc *desc) 126 { 127 struct irq_chip *chip = irq_desc_get_chip(desc); 128 struct irq_domain *irq_domain = irq_get_handler_data(cascade_virq); 129 unsigned int virq; 130 131 raw_spin_lock(&desc->lock); 132 chip->irq_mask(&desc->irq_data); /* IRQ_LEVEL */ 133 raw_spin_unlock(&desc->lock); 134 135 virq = __hlwd_pic_get_irq(irq_domain); 136 if (virq != NO_IRQ) 137 generic_handle_irq(virq); 138 else 139 pr_err("spurious interrupt!\n"); 140 141 raw_spin_lock(&desc->lock); 142 chip->irq_ack(&desc->irq_data); /* IRQ_LEVEL */ 143 if (!irqd_irq_disabled(&desc->irq_data) && chip->irq_unmask) 144 chip->irq_unmask(&desc->irq_data); 145 raw_spin_unlock(&desc->lock); 146 } 147 148 /* 149 * Platform hooks. 150 * 151 */ 152 153 static void __hlwd_quiesce(void __iomem *io_base) 154 { 155 /* mask and ack all IRQs */ 156 out_be32(io_base + HW_BROADWAY_IMR, 0); 157 out_be32(io_base + HW_BROADWAY_ICR, 0xffffffff); 158 } 159 160 struct irq_domain *hlwd_pic_init(struct device_node *np) 161 { 162 struct irq_domain *irq_domain; 163 struct resource res; 164 void __iomem *io_base; 165 int retval; 166 167 retval = of_address_to_resource(np, 0, &res); 168 if (retval) { 169 pr_err("no io memory range found\n"); 170 return NULL; 171 } 172 io_base = ioremap(res.start, resource_size(&res)); 173 if (!io_base) { 174 pr_err("ioremap failed\n"); 175 return NULL; 176 } 177 178 pr_info("controller at 0x%08x mapped to 0x%p\n", res.start, io_base); 179 180 __hlwd_quiesce(io_base); 181 182 irq_domain = irq_domain_add_linear(np, HLWD_NR_IRQS, 183 &hlwd_irq_domain_ops, io_base); 184 if (!irq_domain) { 185 pr_err("failed to allocate irq_domain\n"); 186 iounmap(io_base); 187 return NULL; 188 } 189 190 return irq_domain; 191 } 192 193 unsigned int hlwd_pic_get_irq(void) 194 { 195 return __hlwd_pic_get_irq(hlwd_irq_host); 196 } 197 198 /* 199 * Probe function. 200 * 201 */ 202 203 void hlwd_pic_probe(void) 204 { 205 struct irq_domain *host; 206 struct device_node *np; 207 const u32 *interrupts; 208 int cascade_virq; 209 210 for_each_compatible_node(np, NULL, "nintendo,hollywood-pic") { 211 interrupts = of_get_property(np, "interrupts", NULL); 212 if (interrupts) { 213 host = hlwd_pic_init(np); 214 BUG_ON(!host); 215 cascade_virq = irq_of_parse_and_map(np, 0); 216 irq_set_handler_data(cascade_virq, host); 217 irq_set_chained_handler(cascade_virq, 218 hlwd_pic_irq_cascade); 219 hlwd_irq_host = host; 220 break; 221 } 222 } 223 } 224 225 /** 226 * hlwd_quiesce() - quiesce hollywood irq controller 227 * 228 * Mask and ack all interrupt sources. 229 * 230 */ 231 void hlwd_quiesce(void) 232 { 233 void __iomem *io_base = hlwd_irq_host->host_data; 234 235 __hlwd_quiesce(io_base); 236 } 237 238