1/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. 2 * 3 * This program is free software; you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License version 2 and 5 * only version 2 as published by the Free Software Foundation. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 15 * 02110-1301, USA. 16 * 17 */ 18 19#include <linux/io.h> 20#include <linux/irq.h> 21#include <linux/interrupt.h> 22#include <asm/irq.h> 23 24static unsigned int int_enable; 25static unsigned int wake_enable; 26 27static struct sirc_regs_t sirc_regs = { 28 .int_enable = SPSS_SIRC_INT_ENABLE, 29 .int_enable_clear = SPSS_SIRC_INT_ENABLE_CLEAR, 30 .int_enable_set = SPSS_SIRC_INT_ENABLE_SET, 31 .int_type = SPSS_SIRC_INT_TYPE, 32 .int_polarity = SPSS_SIRC_INT_POLARITY, 33 .int_clear = SPSS_SIRC_INT_CLEAR, 34}; 35 36static struct sirc_cascade_regs sirc_reg_table[] = { 37 { 38 .int_status = SPSS_SIRC_IRQ_STATUS, 39 .cascade_irq = INT_SIRC_0, 40 } 41}; 42 43static unsigned int save_type; 44static unsigned int save_polarity; 45 46/* Mask off the given interrupt. Keep the int_enable mask in sync with 47 the enable reg, so it can be restored after power collapse. */ 48static void sirc_irq_mask(unsigned int irq) 49{ 50 unsigned int mask; 51 52 53 mask = 1 << (irq - FIRST_SIRC_IRQ); 54 writel(mask, sirc_regs.int_enable_clear); 55 int_enable &= ~mask; 56 return; 57} 58 59/* Unmask the given interrupt. Keep the int_enable mask in sync with 60 the enable reg, so it can be restored after power collapse. */ 61static void sirc_irq_unmask(unsigned int irq) 62{ 63 unsigned int mask; 64 65 mask = 1 << (irq - FIRST_SIRC_IRQ); 66 writel(mask, sirc_regs.int_enable_set); 67 int_enable |= mask; 68 return; 69} 70 71static void sirc_irq_ack(unsigned int irq) 72{ 73 unsigned int mask; 74 75 mask = 1 << (irq - FIRST_SIRC_IRQ); 76 writel(mask, sirc_regs.int_clear); 77 return; 78} 79 80static int sirc_irq_set_wake(unsigned int irq, unsigned int on) 81{ 82 unsigned int mask; 83 84 /* Used to set the interrupt enable mask during power collapse. */ 85 mask = 1 << (irq - FIRST_SIRC_IRQ); 86 if (on) 87 wake_enable |= mask; 88 else 89 wake_enable &= ~mask; 90 91 return 0; 92} 93 94static int sirc_irq_set_type(unsigned int irq, unsigned int flow_type) 95{ 96 unsigned int mask; 97 unsigned int val; 98 99 mask = 1 << (irq - FIRST_SIRC_IRQ); 100 val = readl(sirc_regs.int_polarity); 101 102 if (flow_type & (IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING)) 103 val |= mask; 104 else 105 val &= ~mask; 106 107 writel(val, sirc_regs.int_polarity); 108 109 val = readl(sirc_regs.int_type); 110 if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { 111 val |= mask; 112 irq_desc[irq].handle_irq = handle_edge_irq; 113 } else { 114 val &= ~mask; 115 irq_desc[irq].handle_irq = handle_level_irq; 116 } 117 118 writel(val, sirc_regs.int_type); 119 120 return 0; 121} 122 123/* Finds the pending interrupt on the passed cascade irq and redrives it */ 124static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc) 125{ 126 unsigned int reg = 0; 127 unsigned int sirq; 128 unsigned int status; 129 130 while ((reg < ARRAY_SIZE(sirc_reg_table)) && 131 (sirc_reg_table[reg].cascade_irq != irq)) 132 reg++; 133 134 status = readl(sirc_reg_table[reg].int_status); 135 status &= SIRC_MASK; 136 if (status == 0) 137 return; 138 139 for (sirq = 0; 140 (sirq < NR_SIRC_IRQS) && ((status & (1U << sirq)) == 0); 141 sirq++) 142 ; 143 generic_handle_irq(sirq+FIRST_SIRC_IRQ); 144 145 desc->chip->ack(irq); 146} 147 148static struct irq_chip sirc_irq_chip = { 149 .name = "sirc", 150 .ack = sirc_irq_ack, 151 .mask = sirc_irq_mask, 152 .unmask = sirc_irq_unmask, 153 .set_wake = sirc_irq_set_wake, 154 .set_type = sirc_irq_set_type, 155}; 156 157void __init msm_init_sirc(void) 158{ 159 int i; 160 161 int_enable = 0; 162 wake_enable = 0; 163 164 for (i = FIRST_SIRC_IRQ; i < LAST_SIRC_IRQ; i++) { 165 set_irq_chip(i, &sirc_irq_chip); 166 set_irq_handler(i, handle_edge_irq); 167 set_irq_flags(i, IRQF_VALID); 168 } 169 170 for (i = 0; i < ARRAY_SIZE(sirc_reg_table); i++) { 171 set_irq_chained_handler(sirc_reg_table[i].cascade_irq, 172 sirc_irq_handler); 173 set_irq_wake(sirc_reg_table[i].cascade_irq, 1); 174 } 175 return; 176} 177