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#include <linux/bitops.h> 21 22#include <asm/io.h> 23#include <asm/delay.h> 24#include <asm/irq.h> 25#include <asm/irc-regs.h> 26#include <asm/mb93493-irqs.h> 27#include <asm/mb93493-regs.h> 28 29#define IRQ_ROUTE_ONE(X) (X##_ROUTE << (X - IRQ_BASE_MB93493)) 30 31#define IRQ_ROUTING \ 32 (IRQ_ROUTE_ONE(IRQ_MB93493_VDC) | \ 33 IRQ_ROUTE_ONE(IRQ_MB93493_VCC) | \ 34 IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_OUT) | \ 35 IRQ_ROUTE_ONE(IRQ_MB93493_I2C_0) | \ 36 IRQ_ROUTE_ONE(IRQ_MB93493_I2C_1) | \ 37 IRQ_ROUTE_ONE(IRQ_MB93493_USB) | \ 38 IRQ_ROUTE_ONE(IRQ_MB93493_LOCAL_BUS) | \ 39 IRQ_ROUTE_ONE(IRQ_MB93493_PCMCIA) | \ 40 IRQ_ROUTE_ONE(IRQ_MB93493_GPIO) | \ 41 IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_IN)) 42 43/* 44 * daughter board PIC operations 45 * - there is no way to ACK interrupts in the MB93493 chip 46 */ 47static void frv_mb93493_mask(struct irq_data *d) 48{ 49 uint32_t iqsr; 50 volatile void *piqsr; 51 52 if (IRQ_ROUTING & (1 << (d->irq - IRQ_BASE_MB93493))) 53 piqsr = __addr_MB93493_IQSR(1); 54 else 55 piqsr = __addr_MB93493_IQSR(0); 56 57 iqsr = readl(piqsr); 58 iqsr &= ~(1 << (d->irq - IRQ_BASE_MB93493 + 16)); 59 writel(iqsr, piqsr); 60} 61 62static void frv_mb93493_ack(struct irq_data *d) 63{ 64} 65 66static void frv_mb93493_unmask(struct irq_data *d) 67{ 68 uint32_t iqsr; 69 volatile void *piqsr; 70 71 if (IRQ_ROUTING & (1 << (d->irq - IRQ_BASE_MB93493))) 72 piqsr = __addr_MB93493_IQSR(1); 73 else 74 piqsr = __addr_MB93493_IQSR(0); 75 76 iqsr = readl(piqsr); 77 iqsr |= 1 << (d->irq - IRQ_BASE_MB93493 + 16); 78 writel(iqsr, piqsr); 79} 80 81static struct irq_chip frv_mb93493_pic = { 82 .name = "mb93093", 83 .irq_ack = frv_mb93493_ack, 84 .irq_mask = frv_mb93493_mask, 85 .irq_mask_ack = frv_mb93493_mask, 86 .irq_unmask = frv_mb93493_unmask, 87}; 88 89/* 90 * MB93493 PIC interrupt handler 91 */ 92static irqreturn_t mb93493_interrupt(int irq, void *_piqsr) 93{ 94 volatile void *piqsr = _piqsr; 95 uint32_t iqsr; 96 97 iqsr = readl(piqsr); 98 iqsr = iqsr & (iqsr >> 16) & 0xffff; 99 100 /* poll all the triggered IRQs */ 101 while (iqsr) { 102 int irq; 103 104 asm("scan %1,gr0,%0" : "=r"(irq) : "r"(iqsr)); 105 irq = 31 - irq; 106 iqsr &= ~(1 << irq); 107 108 generic_handle_irq(IRQ_BASE_MB93493 + irq); 109 } 110 111 return IRQ_HANDLED; 112} 113 114/* 115 * define an interrupt action for each MB93493 PIC output 116 * - use dev_id to indicate the MB93493 PIC input to output mappings 117 */ 118static struct irqaction mb93493_irq[2] = { 119 [0] = { 120 .handler = mb93493_interrupt, 121 .flags = IRQF_SHARED, 122 .name = "mb93493.0", 123 .dev_id = (void *) __addr_MB93493_IQSR(0), 124 }, 125 [1] = { 126 .handler = mb93493_interrupt, 127 .flags = IRQF_SHARED, 128 .name = "mb93493.1", 129 .dev_id = (void *) __addr_MB93493_IQSR(1), 130 } 131}; 132 133/* 134 * initialise the motherboard MB93493's PIC 135 */ 136void __init mb93493_init(void) 137{ 138 int irq; 139 140 for (irq = IRQ_BASE_MB93493 + 0; irq <= IRQ_BASE_MB93493 + 10; irq++) 141 irq_set_chip_and_handler(irq, &frv_mb93493_pic, 142 handle_edge_irq); 143 144 /* the MB93493 drives external IRQ inputs on the CPU PIC */ 145 setup_irq(IRQ_CPU_MB93493_0, &mb93493_irq[0]); 146 setup_irq(IRQ_CPU_MB93493_1, &mb93493_irq[1]); 147} 148