1028ee972SAlbert Herranz /* 2028ee972SAlbert Herranz * arch/powerpc/platforms/embedded6xx/flipper-pic.c 3028ee972SAlbert Herranz * 4028ee972SAlbert Herranz * Nintendo GameCube/Wii "Flipper" interrupt controller support. 5028ee972SAlbert Herranz * Copyright (C) 2004-2009 The GameCube Linux Team 6028ee972SAlbert Herranz * Copyright (C) 2007,2008,2009 Albert Herranz 7028ee972SAlbert Herranz * 8028ee972SAlbert Herranz * This program is free software; you can redistribute it and/or 9028ee972SAlbert Herranz * modify it under the terms of the GNU General Public License 10028ee972SAlbert Herranz * as published by the Free Software Foundation; either version 2 11028ee972SAlbert Herranz * of the License, or (at your option) any later version. 12028ee972SAlbert Herranz * 13028ee972SAlbert Herranz */ 14028ee972SAlbert Herranz #define DRV_MODULE_NAME "flipper-pic" 15028ee972SAlbert Herranz #define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt 16028ee972SAlbert Herranz 17028ee972SAlbert Herranz #include <linux/kernel.h> 18028ee972SAlbert Herranz #include <linux/init.h> 19028ee972SAlbert Herranz #include <linux/irq.h> 20028ee972SAlbert Herranz #include <linux/of.h> 21028ee972SAlbert Herranz #include <asm/io.h> 22028ee972SAlbert Herranz 23028ee972SAlbert Herranz #include "flipper-pic.h" 24028ee972SAlbert Herranz 25028ee972SAlbert Herranz #define FLIPPER_NR_IRQS 32 26028ee972SAlbert Herranz 27028ee972SAlbert Herranz /* 28028ee972SAlbert Herranz * Each interrupt has a corresponding bit in both 29028ee972SAlbert Herranz * the Interrupt Cause (ICR) and Interrupt Mask (IMR) registers. 30028ee972SAlbert Herranz * 31028ee972SAlbert Herranz * Enabling/disabling an interrupt line involves setting/clearing 32028ee972SAlbert Herranz * the corresponding bit in IMR. 33028ee972SAlbert Herranz * Except for the RSW interrupt, all interrupts get deasserted automatically 34028ee972SAlbert Herranz * when the source deasserts the interrupt. 35028ee972SAlbert Herranz */ 36028ee972SAlbert Herranz #define FLIPPER_ICR 0x00 37028ee972SAlbert Herranz #define FLIPPER_ICR_RSS (1<<16) /* reset switch state */ 38028ee972SAlbert Herranz 39028ee972SAlbert Herranz #define FLIPPER_IMR 0x04 40028ee972SAlbert Herranz 41028ee972SAlbert Herranz #define FLIPPER_RESET 0x24 42028ee972SAlbert Herranz 43028ee972SAlbert Herranz 44028ee972SAlbert Herranz /* 45028ee972SAlbert Herranz * IRQ chip hooks. 46028ee972SAlbert Herranz * 47028ee972SAlbert Herranz */ 48028ee972SAlbert Herranz 490bf8878eSLennert Buytenhek static void flipper_pic_mask_and_ack(struct irq_data *d) 50028ee972SAlbert Herranz { 51476eb491SGrant Likely int irq = irqd_to_hwirq(d); 520bf8878eSLennert Buytenhek void __iomem *io_base = irq_data_get_irq_chip_data(d); 53028ee972SAlbert Herranz u32 mask = 1 << irq; 54028ee972SAlbert Herranz 55028ee972SAlbert Herranz clrbits32(io_base + FLIPPER_IMR, mask); 56028ee972SAlbert Herranz /* this is at least needed for RSW */ 57028ee972SAlbert Herranz out_be32(io_base + FLIPPER_ICR, mask); 58028ee972SAlbert Herranz } 59028ee972SAlbert Herranz 600bf8878eSLennert Buytenhek static void flipper_pic_ack(struct irq_data *d) 61028ee972SAlbert Herranz { 62476eb491SGrant Likely int irq = irqd_to_hwirq(d); 630bf8878eSLennert Buytenhek void __iomem *io_base = irq_data_get_irq_chip_data(d); 64028ee972SAlbert Herranz 65028ee972SAlbert Herranz /* this is at least needed for RSW */ 66028ee972SAlbert Herranz out_be32(io_base + FLIPPER_ICR, 1 << irq); 67028ee972SAlbert Herranz } 68028ee972SAlbert Herranz 690bf8878eSLennert Buytenhek static void flipper_pic_mask(struct irq_data *d) 70028ee972SAlbert Herranz { 71476eb491SGrant Likely int irq = irqd_to_hwirq(d); 720bf8878eSLennert Buytenhek void __iomem *io_base = irq_data_get_irq_chip_data(d); 73028ee972SAlbert Herranz 74028ee972SAlbert Herranz clrbits32(io_base + FLIPPER_IMR, 1 << irq); 75028ee972SAlbert Herranz } 76028ee972SAlbert Herranz 770bf8878eSLennert Buytenhek static void flipper_pic_unmask(struct irq_data *d) 78028ee972SAlbert Herranz { 79476eb491SGrant Likely int irq = irqd_to_hwirq(d); 800bf8878eSLennert Buytenhek void __iomem *io_base = irq_data_get_irq_chip_data(d); 81028ee972SAlbert Herranz 82028ee972SAlbert Herranz setbits32(io_base + FLIPPER_IMR, 1 << irq); 83028ee972SAlbert Herranz } 84028ee972SAlbert Herranz 85028ee972SAlbert Herranz 86028ee972SAlbert Herranz static struct irq_chip flipper_pic = { 87028ee972SAlbert Herranz .name = "flipper-pic", 880bf8878eSLennert Buytenhek .irq_ack = flipper_pic_ack, 890bf8878eSLennert Buytenhek .irq_mask_ack = flipper_pic_mask_and_ack, 900bf8878eSLennert Buytenhek .irq_mask = flipper_pic_mask, 910bf8878eSLennert Buytenhek .irq_unmask = flipper_pic_unmask, 92028ee972SAlbert Herranz }; 93028ee972SAlbert Herranz 94028ee972SAlbert Herranz /* 95028ee972SAlbert Herranz * IRQ host hooks. 96028ee972SAlbert Herranz * 97028ee972SAlbert Herranz */ 98028ee972SAlbert Herranz 99bae1d8f1SGrant Likely static struct irq_domain *flipper_irq_host; 100028ee972SAlbert Herranz 101bae1d8f1SGrant Likely static int flipper_pic_map(struct irq_domain *h, unsigned int virq, 102028ee972SAlbert Herranz irq_hw_number_t hwirq) 103028ee972SAlbert Herranz { 104ec775d0eSThomas Gleixner irq_set_chip_data(virq, h->host_data); 10598488db9SThomas Gleixner irq_set_status_flags(virq, IRQ_LEVEL); 106ec775d0eSThomas Gleixner irq_set_chip_and_handler(virq, &flipper_pic, handle_level_irq); 107028ee972SAlbert Herranz return 0; 108028ee972SAlbert Herranz } 109028ee972SAlbert Herranz 110bae1d8f1SGrant Likely static int flipper_pic_match(struct irq_domain *h, struct device_node *np) 111028ee972SAlbert Herranz { 112028ee972SAlbert Herranz return 1; 113028ee972SAlbert Herranz } 114028ee972SAlbert Herranz 115028ee972SAlbert Herranz 1169f70b8ebSGrant Likely static const struct irq_domain_ops flipper_irq_domain_ops = { 117028ee972SAlbert Herranz .map = flipper_pic_map, 118028ee972SAlbert Herranz .match = flipper_pic_match, 119028ee972SAlbert Herranz }; 120028ee972SAlbert Herranz 121028ee972SAlbert Herranz /* 122028ee972SAlbert Herranz * Platform hooks. 123028ee972SAlbert Herranz * 124028ee972SAlbert Herranz */ 125028ee972SAlbert Herranz 126028ee972SAlbert Herranz static void __flipper_quiesce(void __iomem *io_base) 127028ee972SAlbert Herranz { 128028ee972SAlbert Herranz /* mask and ack all IRQs */ 129028ee972SAlbert Herranz out_be32(io_base + FLIPPER_IMR, 0x00000000); 130028ee972SAlbert Herranz out_be32(io_base + FLIPPER_ICR, 0xffffffff); 131028ee972SAlbert Herranz } 132028ee972SAlbert Herranz 133bae1d8f1SGrant Likely struct irq_domain * __init flipper_pic_init(struct device_node *np) 134028ee972SAlbert Herranz { 135028ee972SAlbert Herranz struct device_node *pi; 136bae1d8f1SGrant Likely struct irq_domain *irq_domain = NULL; 137028ee972SAlbert Herranz struct resource res; 138028ee972SAlbert Herranz void __iomem *io_base; 139028ee972SAlbert Herranz int retval; 140028ee972SAlbert Herranz 141028ee972SAlbert Herranz pi = of_get_parent(np); 142028ee972SAlbert Herranz if (!pi) { 143028ee972SAlbert Herranz pr_err("no parent found\n"); 144028ee972SAlbert Herranz goto out; 145028ee972SAlbert Herranz } 146028ee972SAlbert Herranz if (!of_device_is_compatible(pi, "nintendo,flipper-pi")) { 147028ee972SAlbert Herranz pr_err("unexpected parent compatible\n"); 148028ee972SAlbert Herranz goto out; 149028ee972SAlbert Herranz } 150028ee972SAlbert Herranz 151028ee972SAlbert Herranz retval = of_address_to_resource(pi, 0, &res); 152028ee972SAlbert Herranz if (retval) { 153028ee972SAlbert Herranz pr_err("no io memory range found\n"); 154028ee972SAlbert Herranz goto out; 155028ee972SAlbert Herranz } 156028ee972SAlbert Herranz io_base = ioremap(res.start, resource_size(&res)); 157028ee972SAlbert Herranz 158028ee972SAlbert Herranz pr_info("controller at 0x%08x mapped to 0x%p\n", res.start, io_base); 159028ee972SAlbert Herranz 160028ee972SAlbert Herranz __flipper_quiesce(io_base); 161028ee972SAlbert Herranz 162a8db8cf0SGrant Likely irq_domain = irq_domain_add_linear(np, FLIPPER_NR_IRQS, 163a8db8cf0SGrant Likely &flipper_irq_domain_ops, io_base); 164bae1d8f1SGrant Likely if (!irq_domain) { 165bae1d8f1SGrant Likely pr_err("failed to allocate irq_domain\n"); 166028ee972SAlbert Herranz return NULL; 167028ee972SAlbert Herranz } 168028ee972SAlbert Herranz 169028ee972SAlbert Herranz out: 170bae1d8f1SGrant Likely return irq_domain; 171028ee972SAlbert Herranz } 172028ee972SAlbert Herranz 173028ee972SAlbert Herranz unsigned int flipper_pic_get_irq(void) 174028ee972SAlbert Herranz { 175*6d166fecSPaul Gortmaker void __iomem *io_base = flipper_irq_host->host_data; 176028ee972SAlbert Herranz int irq; 177028ee972SAlbert Herranz u32 irq_status; 178028ee972SAlbert Herranz 179028ee972SAlbert Herranz irq_status = in_be32(io_base + FLIPPER_ICR) & 180028ee972SAlbert Herranz in_be32(io_base + FLIPPER_IMR); 181028ee972SAlbert Herranz if (irq_status == 0) 182028ee972SAlbert Herranz return NO_IRQ; /* no more IRQs pending */ 183028ee972SAlbert Herranz 184028ee972SAlbert Herranz irq = __ffs(irq_status); 185*6d166fecSPaul Gortmaker return irq_linear_revmap(flipper_irq_host, irq); 186028ee972SAlbert Herranz } 187028ee972SAlbert Herranz 188028ee972SAlbert Herranz /* 189028ee972SAlbert Herranz * Probe function. 190028ee972SAlbert Herranz * 191028ee972SAlbert Herranz */ 192028ee972SAlbert Herranz 193028ee972SAlbert Herranz void __init flipper_pic_probe(void) 194028ee972SAlbert Herranz { 195028ee972SAlbert Herranz struct device_node *np; 196028ee972SAlbert Herranz 197028ee972SAlbert Herranz np = of_find_compatible_node(NULL, NULL, "nintendo,flipper-pic"); 198028ee972SAlbert Herranz BUG_ON(!np); 199028ee972SAlbert Herranz 200*6d166fecSPaul Gortmaker flipper_irq_host = flipper_pic_init(np); 201028ee972SAlbert Herranz BUG_ON(!flipper_irq_host); 202028ee972SAlbert Herranz 203028ee972SAlbert Herranz irq_set_default_host(flipper_irq_host); 204028ee972SAlbert Herranz 205028ee972SAlbert Herranz of_node_put(np); 206028ee972SAlbert Herranz } 207028ee972SAlbert Herranz 208028ee972SAlbert Herranz /* 209028ee972SAlbert Herranz * Misc functions related to the flipper chipset. 210028ee972SAlbert Herranz * 211028ee972SAlbert Herranz */ 212028ee972SAlbert Herranz 213028ee972SAlbert Herranz /** 214028ee972SAlbert Herranz * flipper_quiesce() - quiesce flipper irq controller 215028ee972SAlbert Herranz * 216028ee972SAlbert Herranz * Mask and ack all interrupt sources. 217028ee972SAlbert Herranz * 218028ee972SAlbert Herranz */ 219028ee972SAlbert Herranz void flipper_quiesce(void) 220028ee972SAlbert Herranz { 221028ee972SAlbert Herranz void __iomem *io_base = flipper_irq_host->host_data; 222028ee972SAlbert Herranz 223028ee972SAlbert Herranz __flipper_quiesce(io_base); 224028ee972SAlbert Herranz } 225028ee972SAlbert Herranz 226028ee972SAlbert Herranz /* 227028ee972SAlbert Herranz * Resets the platform. 228028ee972SAlbert Herranz */ 229028ee972SAlbert Herranz void flipper_platform_reset(void) 230028ee972SAlbert Herranz { 231028ee972SAlbert Herranz void __iomem *io_base; 232028ee972SAlbert Herranz 233028ee972SAlbert Herranz if (flipper_irq_host && flipper_irq_host->host_data) { 234028ee972SAlbert Herranz io_base = flipper_irq_host->host_data; 235028ee972SAlbert Herranz out_8(io_base + FLIPPER_RESET, 0x00); 236028ee972SAlbert Herranz } 237028ee972SAlbert Herranz } 238028ee972SAlbert Herranz 239028ee972SAlbert Herranz /* 240028ee972SAlbert Herranz * Returns non-zero if the reset button is pressed. 241028ee972SAlbert Herranz */ 242028ee972SAlbert Herranz int flipper_is_reset_button_pressed(void) 243028ee972SAlbert Herranz { 244028ee972SAlbert Herranz void __iomem *io_base; 245028ee972SAlbert Herranz u32 icr; 246028ee972SAlbert Herranz 247028ee972SAlbert Herranz if (flipper_irq_host && flipper_irq_host->host_data) { 248028ee972SAlbert Herranz io_base = flipper_irq_host->host_data; 249028ee972SAlbert Herranz icr = in_be32(io_base + FLIPPER_ICR); 250028ee972SAlbert Herranz return !(icr & FLIPPER_ICR_RSS); 251028ee972SAlbert Herranz } 252028ee972SAlbert Herranz return 0; 253028ee972SAlbert Herranz } 254028ee972SAlbert Herranz 255