1/* 2 * TX4939 irq routines 3 * Based on linux/arch/mips/kernel/irq_txx9.c, 4 * and RBTX49xx patch from CELF patch archive. 5 * 6 * Copyright 2001, 2003-2005 MontaVista Software Inc. 7 * Author: MontaVista Software, Inc. 8 * ahennessy@mvista.com 9 * source@mvista.com 10 * Copyright (C) 2000-2001,2005-2007 Toshiba Corporation 11 * 12 * This file is subject to the terms and conditions of the GNU General Public 13 * License. See the file "COPYING" in the main directory of this archive 14 * for more details. 15 */ 16/* 17 * TX4939 defines 64 IRQs. 18 * Similer to irq_txx9.c but different register layouts. 19 */ 20#include <linux/init.h> 21#include <linux/interrupt.h> 22#include <linux/irq.h> 23#include <linux/types.h> 24#include <asm/irq_cpu.h> 25#include <asm/txx9irq.h> 26#include <asm/txx9/tx4939.h> 27 28/* IRCER : Int. Control Enable */ 29#define TXx9_IRCER_ICE 0x00000001 30 31/* IRCR : Int. Control */ 32#define TXx9_IRCR_LOW 0x00000000 33#define TXx9_IRCR_HIGH 0x00000001 34#define TXx9_IRCR_DOWN 0x00000002 35#define TXx9_IRCR_UP 0x00000003 36#define TXx9_IRCR_EDGE(cr) ((cr) & 0x00000002) 37 38/* IRSCR : Int. Status Control */ 39#define TXx9_IRSCR_EIClrE 0x00000100 40#define TXx9_IRSCR_EIClr_MASK 0x0000000f 41 42/* IRCSR : Int. Current Status */ 43#define TXx9_IRCSR_IF 0x00010000 44 45#define irc_dlevel 0 46#define irc_elevel 1 47 48static struct { 49 unsigned char level; 50 unsigned char mode; 51} tx4939irq[TX4939_NUM_IR] __read_mostly; 52 53static void tx4939_irq_unmask(struct irq_data *d) 54{ 55 unsigned int irq_nr = d->irq - TXX9_IRQ_BASE; 56 u32 __iomem *lvlp; 57 int ofs; 58 if (irq_nr < 32) { 59 irq_nr--; 60 lvlp = &tx4939_ircptr->lvl[(irq_nr % 16) / 2].r; 61 } else { 62 irq_nr -= 32; 63 lvlp = &tx4939_ircptr->lvl[8 + (irq_nr % 16) / 2].r; 64 } 65 ofs = (irq_nr & 16) + (irq_nr & 1) * 8; 66 __raw_writel((__raw_readl(lvlp) & ~(0xff << ofs)) 67 | (tx4939irq[irq_nr].level << ofs), 68 lvlp); 69} 70 71static inline void tx4939_irq_mask(struct irq_data *d) 72{ 73 unsigned int irq_nr = d->irq - TXX9_IRQ_BASE; 74 u32 __iomem *lvlp; 75 int ofs; 76 if (irq_nr < 32) { 77 irq_nr--; 78 lvlp = &tx4939_ircptr->lvl[(irq_nr % 16) / 2].r; 79 } else { 80 irq_nr -= 32; 81 lvlp = &tx4939_ircptr->lvl[8 + (irq_nr % 16) / 2].r; 82 } 83 ofs = (irq_nr & 16) + (irq_nr & 1) * 8; 84 __raw_writel((__raw_readl(lvlp) & ~(0xff << ofs)) 85 | (irc_dlevel << ofs), 86 lvlp); 87 mmiowb(); 88} 89 90static void tx4939_irq_mask_ack(struct irq_data *d) 91{ 92 unsigned int irq_nr = d->irq - TXX9_IRQ_BASE; 93 94 tx4939_irq_mask(d); 95 if (TXx9_IRCR_EDGE(tx4939irq[irq_nr].mode)) { 96 irq_nr--; 97 /* clear edge detection */ 98 __raw_writel((TXx9_IRSCR_EIClrE | (irq_nr & 0xf)) 99 << (irq_nr & 0x10), 100 &tx4939_ircptr->edc.r); 101 } 102} 103 104static int tx4939_irq_set_type(struct irq_data *d, unsigned int flow_type) 105{ 106 unsigned int irq_nr = d->irq - TXX9_IRQ_BASE; 107 u32 cr; 108 u32 __iomem *crp; 109 int ofs; 110 int mode; 111 112 if (flow_type & IRQF_TRIGGER_PROBE) 113 return 0; 114 switch (flow_type & IRQF_TRIGGER_MASK) { 115 case IRQF_TRIGGER_RISING: 116 mode = TXx9_IRCR_UP; 117 break; 118 case IRQF_TRIGGER_FALLING: 119 mode = TXx9_IRCR_DOWN; 120 break; 121 case IRQF_TRIGGER_HIGH: 122 mode = TXx9_IRCR_HIGH; 123 break; 124 case IRQF_TRIGGER_LOW: 125 mode = TXx9_IRCR_LOW; 126 break; 127 default: 128 return -EINVAL; 129 } 130 if (irq_nr < 32) { 131 irq_nr--; 132 crp = &tx4939_ircptr->dm[(irq_nr & 8) >> 3].r; 133 } else { 134 irq_nr -= 32; 135 crp = &tx4939_ircptr->dm2[((irq_nr & 8) >> 3)].r; 136 } 137 ofs = (((irq_nr & 16) >> 1) | (irq_nr & (8 - 1))) * 2; 138 cr = __raw_readl(crp); 139 cr &= ~(0x3 << ofs); 140 cr |= (mode & 0x3) << ofs; 141 __raw_writel(cr, crp); 142 tx4939irq[irq_nr].mode = mode; 143 return 0; 144} 145 146static struct irq_chip tx4939_irq_chip = { 147 .name = "TX4939", 148 .irq_ack = tx4939_irq_mask_ack, 149 .irq_mask = tx4939_irq_mask, 150 .irq_mask_ack = tx4939_irq_mask_ack, 151 .irq_unmask = tx4939_irq_unmask, 152 .irq_set_type = tx4939_irq_set_type, 153}; 154 155static int tx4939_irq_set_pri(int irc_irq, int new_pri) 156{ 157 int old_pri; 158 159 if ((unsigned int)irc_irq >= TX4939_NUM_IR) 160 return 0; 161 old_pri = tx4939irq[irc_irq].level; 162 tx4939irq[irc_irq].level = new_pri; 163 return old_pri; 164} 165 166void __init tx4939_irq_init(void) 167{ 168 int i; 169 170 mips_cpu_irq_init(); 171 /* disable interrupt control */ 172 __raw_writel(0, &tx4939_ircptr->den.r); 173 __raw_writel(0, &tx4939_ircptr->maskint.r); 174 __raw_writel(0, &tx4939_ircptr->maskext.r); 175 /* irq_base + 0 is not used */ 176 for (i = 1; i < TX4939_NUM_IR; i++) { 177 tx4939irq[i].level = 4; /* middle level */ 178 tx4939irq[i].mode = TXx9_IRCR_LOW; 179 irq_set_chip_and_handler(TXX9_IRQ_BASE + i, &tx4939_irq_chip, 180 handle_level_irq); 181 } 182 183 /* mask all IRC interrupts */ 184 __raw_writel(0, &tx4939_ircptr->msk.r); 185 for (i = 0; i < 16; i++) 186 __raw_writel(0, &tx4939_ircptr->lvl[i].r); 187 /* setup IRC interrupt mode (Low Active) */ 188 for (i = 0; i < 2; i++) 189 __raw_writel(0, &tx4939_ircptr->dm[i].r); 190 for (i = 0; i < 2; i++) 191 __raw_writel(0, &tx4939_ircptr->dm2[i].r); 192 /* enable interrupt control */ 193 __raw_writel(TXx9_IRCER_ICE, &tx4939_ircptr->den.r); 194 __raw_writel(irc_elevel, &tx4939_ircptr->msk.r); 195 196 irq_set_chained_handler(MIPS_CPU_IRQ_BASE + TX4939_IRC_INT, 197 handle_simple_irq); 198 199 /* raise priority for errors, timers, sio */ 200 tx4939_irq_set_pri(TX4939_IR_WTOERR, 7); 201 tx4939_irq_set_pri(TX4939_IR_PCIERR, 7); 202 tx4939_irq_set_pri(TX4939_IR_PCIPME, 7); 203 for (i = 0; i < TX4939_NUM_IR_TMR; i++) 204 tx4939_irq_set_pri(TX4939_IR_TMR(i), 6); 205 for (i = 0; i < TX4939_NUM_IR_SIO; i++) 206 tx4939_irq_set_pri(TX4939_IR_SIO(i), 5); 207} 208 209int tx4939_irq(void) 210{ 211 u32 csr = __raw_readl(&tx4939_ircptr->cs.r); 212 213 if (likely(!(csr & TXx9_IRCSR_IF))) 214 return TXX9_IRQ_BASE + (csr & (TX4939_NUM_IR - 1)); 215 return -1; 216} 217