| /* |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| #include <linux/types.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/list.h> |
| #include <linux/device.h> |
| #include <linux/slab.h> |
| #include <linux/string.h> |
| #include <linux/sysdev.h> |
| |
| #include <mach/hardware.h> |
| #include <mach/msi.h> |
| #include <asm/io.h> |
| #include <asm/irq.h> |
| #include <asm/setup.h> |
| #include <asm/mach-types.h> |
| |
| #include <asm/mach/arch.h> |
| #include <asm/mach/irq.h> |
| #include <asm/mach/map.h> |
| |
| #include "ctrlEnv/mvCtrlEnvLib.h" |
| #include "boardEnv/mvBoardEnvLib.h" |
| #include "gpp/mvGpp.h" |
| #include "mvOs.h" |
| |
| |
| unsigned int irq_int_type[NR_IRQS]; |
| |
| static void mv_mask_irq(unsigned int irq) |
| { |
| if(irq < 32) { |
| MV_REG_BIT_RESET(MV_IRQ_MASK_LOW_REG, (1 << irq) ); |
| } else if(irq < 64) { /* (irq > 32 && irq < 64) */ |
| MV_REG_BIT_RESET(MV_IRQ_MASK_HIGH_REG, (1 << (irq - 32)) ); |
| } else if(irq < 96) { /* (irq > 64 && irq < 96) */ |
| MV_REG_BIT_RESET(MV_IRQ_MASK_ERROR_REG, (1 << (irq - 64)) ); |
| } else if(irq < IRQ_MSI_START) { |
| MV_U32 bitmask = 1 << (irq & 0x1F); |
| MV_U32 reg = (irq - IRQ_GPP_START) >> 5; |
| MV_REG_BIT_RESET(MV_GPP_IRQ_MASK_REG(reg), bitmask); |
| } else if(irq < NR_IRQS) { |
| /* Doorbell interrupts are handled in PCIe MSI related code. */ |
| printk("mv_unmask_irq: ERR, irq-%u out of scope\n",irq); |
| } else |
| printk("mv_mask_irq: ERR, irq-%u out of scope\n",irq); |
| } |
| |
| |
| static void mv_unmask_irq(unsigned int irq) |
| { |
| if(irq < 32) { |
| MV_REG_BIT_SET(MV_IRQ_MASK_LOW_REG, (1 << irq) ); |
| } else if(irq < 64) { /* (irq > 32 && irq < 64) */ |
| MV_REG_BIT_SET(MV_IRQ_MASK_HIGH_REG, (1 << (irq - 32)) ); |
| } else if(irq < 96) { /* (irq > 64 && irq < 96) */ |
| MV_REG_BIT_SET(MV_IRQ_MASK_ERROR_REG, (1 << (irq - 64)) ); |
| } else if(irq < IRQ_MSI_START) { |
| MV_U32 bitmask = 1 << (irq & 0x1F); |
| MV_U32 reg = (irq - IRQ_GPP_START) >> 5; |
| MV_REG_BIT_SET(MV_GPP_IRQ_MASK_REG(reg), bitmask); |
| if (irq_int_type[irq] == GPP_IRQ_TYPE_CHANGE_LEVEL) { |
| (MV_REG_READ(MV_GPP_IRQ_POLARITY(reg)) & bitmask)? |
| MV_REG_BIT_RESET(MV_GPP_IRQ_POLARITY(reg), bitmask): |
| MV_REG_BIT_SET(MV_GPP_IRQ_POLARITY(reg), bitmask); |
| } |
| } else if(irq < NR_IRQS) { |
| /* Doorbell interrupts are handled in PCIe MSI related code. */ |
| printk("mv_unmask_irq: ERR, irq-%u out of scope\n",irq); |
| } else |
| printk("mv_unmask_irq: ERR, irq-%u out of scope\n",irq); |
| } |
| |
| struct irq_chip mv_chip = { |
| .ack = mv_mask_irq, |
| .mask = mv_mask_irq, |
| .unmask = mv_unmask_irq, |
| }; |
| |
| void __init mv_init_irq(void) |
| { |
| u32 i; |
| MV_U32 gppMask; |
| MV_U32 maxGppGroup; |
| |
| if (MV_6601_DEV_ID == mvCtrlModelGet()) |
| maxGppGroup = 2; |
| else |
| maxGppGroup = 3; |
| |
| /* Disable all interrupts initially. */ |
| MV_REG_WRITE(MV_IRQ_MASK_LOW_REG, 0x0); |
| MV_REG_WRITE(MV_FIQ_MASK_LOW_REG, 0x0); |
| MV_REG_WRITE(MV_IRQ_MASK_HIGH_REG, 0x0); |
| MV_REG_WRITE(MV_FIQ_MASK_HIGH_REG, 0x0); |
| MV_REG_WRITE(MV_IRQ_MASK_ERROR_REG, 0x0); |
| MV_REG_WRITE(MV_FIQ_MASK_ERROR_REG, 0x0); |
| |
| for(i = 0; i < maxGppGroup; i++) { |
| MV_REG_WRITE(MV_GPP_IRQ_MASK_REG(i), 0x0); |
| MV_REG_WRITE(MV_GPP_IRQ_EDGE_REG(i), 0x0); |
| } |
| |
| /* Set gpp interrupts as needed */ |
| for(i = 0; i < maxGppGroup; i++) { |
| gppMask = mvBoardGpioIntMaskGet(i); |
| mvGppTypeSet(i, gppMask , (MV_GPP_IN & gppMask)); |
| mvGppPolaritySet(i, gppMask , (MV_GPP_IN_INVERT & gppMask)); |
| } |
| |
| /* enable GPP in the main mask high */ |
| if (MV_6601_DEV_ID == mvCtrlModelGet()) { |
| MV_REG_BIT_SET(MV_IRQ_MASK_HIGH_REG, |
| MV_BIT_MASK(GPP_LOW_0_7_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_LOW_8_15_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_LOW_16_23_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_LOW_24_31_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_MID_0_7_IRQ_NUM - 32)); |
| } else { |
| MV_REG_BIT_SET(MV_IRQ_MASK_HIGH_REG, |
| MV_BIT_MASK(GPP_LOW_0_7_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_LOW_8_15_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_LOW_16_23_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_LOW_24_31_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_MID_0_7_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_MID_8_15_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_MID_16_23_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_MID_24_31_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_HIGH_0_7_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_HIGH_8_15_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_HIGH_16_23_IRQ_NUM - 32) | |
| MV_BIT_MASK(GPP_HIGH_24_IRQ_NUM - 32)); |
| } |
| |
| /* clear all int */ |
| MV_REG_WRITE(MV_IRQ_CAUSE_LOW_REG, 0x0); |
| MV_REG_WRITE(MV_IRQ_CAUSE_HIGH_REG, 0x0); |
| MV_REG_WRITE(MV_IRQ_CAUSE_ERROR_REG, 0x0); |
| |
| for(i = 0; i < maxGppGroup; i++) |
| MV_REG_WRITE(MV_GPP_IRQ_CAUSE_REG(i), 0x0); |
| |
| /* Do the core module ones */ |
| for (i = 0; i < NR_IRQS; i++) { |
| set_irq_chip(i, &mv_chip); |
| set_irq_handler(i, handle_level_irq); |
| set_irq_flags(i, IRQF_VALID | IRQF_PROBE); |
| } |
| |
| /* init GPP IRQs in default level mode*/ |
| for (i = 0; i < NR_IRQS; i++) |
| irq_int_type[i] = GPP_IRQ_TYPE_LEVEL; |
| |
| /* TBD. Add support for error interrupts */ |
| |
| kw2_msi_init(); |
| } |
| |