| /* |
| * arch/arm/mach-ks8695/irq.c |
| * |
| * Copyright (C) 2006 Ben Dooks <ben@simtec.co.uk> |
| * Copyright (C) 2006 Simtec Electronics |
| * |
| * 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/init.h> |
| #include <linux/module.h> |
| #include <linux/interrupt.h> |
| #include <linux/ioport.h> |
| #include <linux/sysdev.h> |
| #include <linux/io.h> |
| |
| #include <mach/hardware.h> |
| #include <asm/irq.h> |
| |
| #include <asm/mach/irq.h> |
| |
| #include <mach/regs-irq.h> |
| #include <mach/regs-gpio.h> |
| |
| static void ks8695_irq_mask(struct irq_data *d) |
| { |
| unsigned long inten; |
| |
| inten = __raw_readl(KS8695_IRQ_VA + KS8695_INTEN); |
| inten &= ~(1 << d->irq); |
| |
| __raw_writel(inten, KS8695_IRQ_VA + KS8695_INTEN); |
| } |
| |
| static void ks8695_irq_unmask(struct irq_data *d) |
| { |
| unsigned long inten; |
| |
| inten = __raw_readl(KS8695_IRQ_VA + KS8695_INTEN); |
| inten |= (1 << d->irq); |
| |
| __raw_writel(inten, KS8695_IRQ_VA + KS8695_INTEN); |
| } |
| |
| static void ks8695_irq_ack(struct irq_data *d) |
| { |
| __raw_writel((1 << d->irq), KS8695_IRQ_VA + KS8695_INTST); |
| } |
| |
| |
| static struct irq_chip ks8695_irq_level_chip; |
| static struct irq_chip ks8695_irq_edge_chip; |
| |
| |
| static int ks8695_irq_set_type(struct irq_data *d, unsigned int type) |
| { |
| unsigned long ctrl, mode; |
| unsigned short level_triggered = 0; |
| |
| ctrl = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC); |
| |
| switch (type) { |
| case IRQ_TYPE_LEVEL_HIGH: |
| mode = IOPC_TM_HIGH; |
| level_triggered = 1; |
| break; |
| case IRQ_TYPE_LEVEL_LOW: |
| mode = IOPC_TM_LOW; |
| level_triggered = 1; |
| break; |
| case IRQ_TYPE_EDGE_RISING: |
| mode = IOPC_TM_RISING; |
| break; |
| case IRQ_TYPE_EDGE_FALLING: |
| mode = IOPC_TM_FALLING; |
| break; |
| case IRQ_TYPE_EDGE_BOTH: |
| mode = IOPC_TM_EDGE; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| switch (d->irq) { |
| case KS8695_IRQ_EXTERN0: |
| ctrl &= ~IOPC_IOEINT0TM; |
| ctrl |= IOPC_IOEINT0_MODE(mode); |
| break; |
| case KS8695_IRQ_EXTERN1: |
| ctrl &= ~IOPC_IOEINT1TM; |
| ctrl |= IOPC_IOEINT1_MODE(mode); |
| break; |
| case KS8695_IRQ_EXTERN2: |
| ctrl &= ~IOPC_IOEINT2TM; |
| ctrl |= IOPC_IOEINT2_MODE(mode); |
| break; |
| case KS8695_IRQ_EXTERN3: |
| ctrl &= ~IOPC_IOEINT3TM; |
| ctrl |= IOPC_IOEINT3_MODE(mode); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| if (level_triggered) { |
| irq_set_chip(d->irq, &ks8695_irq_level_chip); |
| irq_set_handler(d->irq, handle_level_irq); |
| } |
| else { |
| irq_set_chip(d->irq, &ks8695_irq_edge_chip); |
| irq_set_handler(d->irq, handle_edge_irq); |
| } |
| |
| __raw_writel(ctrl, KS8695_GPIO_VA + KS8695_IOPC); |
| return 0; |
| } |
| |
| static struct irq_chip ks8695_irq_level_chip = { |
| .irq_ack = ks8695_irq_mask, |
| .irq_mask = ks8695_irq_mask, |
| .irq_unmask = ks8695_irq_unmask, |
| .irq_set_type = ks8695_irq_set_type, |
| }; |
| |
| static struct irq_chip ks8695_irq_edge_chip = { |
| .irq_ack = ks8695_irq_ack, |
| .irq_mask = ks8695_irq_mask, |
| .irq_unmask = ks8695_irq_unmask, |
| .irq_set_type = ks8695_irq_set_type, |
| }; |
| |
| void __init ks8695_init_irq(void) |
| { |
| unsigned int irq; |
| |
| /* Disable all interrupts initially */ |
| __raw_writel(0, KS8695_IRQ_VA + KS8695_INTMC); |
| __raw_writel(0, KS8695_IRQ_VA + KS8695_INTEN); |
| |
| for (irq = 0; irq < NR_IRQS; irq++) { |
| switch (irq) { |
| /* Level-triggered interrupts */ |
| case KS8695_IRQ_BUS_ERROR: |
| case KS8695_IRQ_UART_MODEM_STATUS: |
| case KS8695_IRQ_UART_LINE_STATUS: |
| case KS8695_IRQ_UART_RX: |
| case KS8695_IRQ_COMM_TX: |
| case KS8695_IRQ_COMM_RX: |
| irq_set_chip(irq, &ks8695_irq_level_chip); |
| irq_set_handler(irq, handle_level_irq); |
| break; |
| |
| /* Edge-triggered interrupts */ |
| default: |
| /* clear pending bit */ |
| ks8695_irq_ack(irq_get_irq_data(irq)); |
| irq_set_chip(irq, &ks8695_irq_edge_chip); |
| irq_set_handler(irq, handle_edge_irq); |
| } |
| |
| set_irq_flags(irq, IRQF_VALID); |
| } |
| } |