1/* irq-mb93493.c: MB93493 companion chip interrupt handler 2 * 3 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 12#include <linux/ptrace.h> 13#include <linux/errno.h> 14#include <linux/signal.h> 15#include <linux/sched.h> 16#include <linux/ioport.h> 17#include <linux/interrupt.h> 18#include <linux/init.h> 19#include <linux/irq.h> 20 21#include <asm/io.h> 22#include <asm/system.h> 23#include <asm/bitops.h> 24#include <asm/delay.h> 25#include <asm/irq.h> 26#include <asm/irc-regs.h> 27#include <asm/mb93493-irqs.h> 28#include <asm/mb93493-regs.h> 29 30#define IRQ_ROUTE_ONE(X) (X##_ROUTE << (X - IRQ_BASE_MB93493)) 31 32#define IRQ_ROUTING \ 33 (IRQ_ROUTE_ONE(IRQ_MB93493_VDC) | \ 34 IRQ_ROUTE_ONE(IRQ_MB93493_VCC) | \ 35 IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_OUT) | \ 36 IRQ_ROUTE_ONE(IRQ_MB93493_I2C_0) | \ 37 IRQ_ROUTE_ONE(IRQ_MB93493_I2C_1) | \ 38 IRQ_ROUTE_ONE(IRQ_MB93493_USB) | \ 39 IRQ_ROUTE_ONE(IRQ_MB93493_LOCAL_BUS) | \ 40 IRQ_ROUTE_ONE(IRQ_MB93493_PCMCIA) | \ 41 IRQ_ROUTE_ONE(IRQ_MB93493_GPIO) | \ 42 IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_IN)) 43 44/* 45 * daughter board PIC operations 46 * - there is no way to ACK interrupts in the MB93493 chip 47 */ 48static void frv_mb93493_mask(unsigned int irq) 49{ 50 uint32_t iqsr; 51 volatile void *piqsr; 52 53 if (IRQ_ROUTING & (1 << (irq - IRQ_BASE_MB93493))) 54 piqsr = __addr_MB93493_IQSR(1); 55 else 56 piqsr = __addr_MB93493_IQSR(0); 57 58 iqsr = readl(piqsr); 59 iqsr &= ~(1 << (irq - IRQ_BASE_MB93493 + 16)); 60 writel(iqsr, piqsr); 61} 62 63static void frv_mb93493_ack(unsigned int irq) 64{ 65} 66 67static void frv_mb93493_unmask(unsigned int irq) 68{ 69 uint32_t iqsr; 70 volatile void *piqsr; 71 72 if (IRQ_ROUTING & (1 << (irq - IRQ_BASE_MB93493))) 73 piqsr = __addr_MB93493_IQSR(1); 74 else 75 piqsr = __addr_MB93493_IQSR(0); 76 77 iqsr = readl(piqsr); 78 iqsr |= 1 << (irq - IRQ_BASE_MB93493 + 16); 79 writel(iqsr, piqsr); 80} 81 82static struct irq_chip frv_mb93493_pic = { 83 .name = "mb93093", 84 .ack = frv_mb93493_ack, 85 .mask = frv_mb93493_mask, 86 .mask_ack = frv_mb93493_mask, 87 .unmask = frv_mb93493_unmask, 88}; 89 90/* 91 * MB93493 PIC interrupt handler 92 */ 93static irqreturn_t mb93493_interrupt(int irq, void *_piqsr) 94{ 95 volatile void *piqsr = _piqsr; 96 uint32_t iqsr; 97 98 iqsr = readl(piqsr); 99 iqsr = iqsr & (iqsr >> 16) & 0xffff; 100 101 /* poll all the triggered IRQs */ 102 while (iqsr) { 103 int irq; 104 105 asm("scan %1,gr0,%0" : "=r"(irq) : "r"(iqsr)); 106 irq = 31 - irq; 107 iqsr &= ~(1 << irq); 108 109 generic_handle_irq(IRQ_BASE_MB93493 + irq); 110 } 111 112 return IRQ_HANDLED; 113} 114 115/* 116 * define an interrupt action for each MB93493 PIC output 117 * - use dev_id to indicate the MB93493 PIC input to output mappings 118 */ 119static struct irqaction mb93493_irq[2] = { 120 [0] = { 121 .handler = mb93493_interrupt, 122 .flags = IRQF_DISABLED | IRQF_SHARED, 123 .mask = CPU_MASK_NONE, 124 .name = "mb93493.0", 125 .dev_id = (void *) __addr_MB93493_IQSR(0), 126 }, 127 [1] = { 128 .handler = mb93493_interrupt, 129 .flags = IRQF_DISABLED | IRQF_SHARED, 130 .mask = CPU_MASK_NONE, 131 .name = "mb93493.1", 132 .dev_id = (void *) __addr_MB93493_IQSR(1), 133 } 134}; 135 136/* 137 * initialise the motherboard MB93493's PIC 138 */ 139void __init mb93493_init(void) 140{ 141 int irq; 142 143 for (irq = IRQ_BASE_MB93493 + 0; irq <= IRQ_BASE_MB93493 + 10; irq++) 144 set_irq_chip_and_handler(irq, &frv_mb93493_pic, handle_edge_irq); 145 146 /* the MB93493 drives external IRQ inputs on the CPU PIC */ 147 setup_irq(IRQ_CPU_MB93493_0, &mb93493_irq[0]); 148 setup_irq(IRQ_CPU_MB93493_1, &mb93493_irq[1]); 149} 150