| /* |
| * linux/arch/sh/boards/ec3104/irq.c |
| * EC3104 companion chip support |
| * |
| * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> |
| * |
| */ |
| |
| #include <asm/io.h> |
| #include <asm/irq.h> |
| #include <asm/ec3104/ec3104.h> |
| |
| /* This is for debugging mostly; here's the table that I intend to keep |
| * in here: |
| * |
| * index function base addr power interrupt bit |
| * 0 power b0ec0000 --- 00000001 (unused) |
| * 1 irqs b0ec1000 --- 00000002 (unused) |
| * 2 ?? b0ec2000 b0ec0008 00000004 |
| * 3 PS2 (1) b0ec3000 b0ec000c 00000008 |
| * 4 PS2 (2) b0ec4000 b0ec0010 00000010 |
| * 5 ?? b0ec5000 b0ec0014 00000020 |
| * 6 I2C b0ec6000 b0ec0018 00000040 |
| * 7 serial (1) b0ec7000 b0ec001c 00000080 |
| * 8 serial (2) b0ec8000 b0ec0020 00000100 |
| * 9 serial (3) b0ec9000 b0ec0024 00000200 |
| * 10 serial (4) b0eca000 b0ec0028 00000400 |
| * 12 GPIO (1) b0ecc000 b0ec0030 |
| * 13 GPIO (2) b0ecc000 b0ec0030 |
| * 16 pcmcia (1) b0ed0000 b0ec0040 00010000 |
| * 17 pcmcia (2) b0ed1000 b0ec0044 00020000 |
| */ |
| |
| /* I used the register names from another interrupt controller I worked with, |
| * since it seems to be identical to the ec3104 except that all bits are |
| * inverted: |
| * |
| * IRR: Interrupt Request Register (pending and enabled interrupts) |
| * IMR: Interrupt Mask Register (which interrupts are enabled) |
| * IPR: Interrupt Pending Register (pending interrupts, even disabled ones) |
| * |
| * 0 bits mean pending or enabled, 1 bits mean not pending or disabled. all |
| * IRQs seem to be level-triggered. |
| */ |
| |
| #define EC3104_IRR (EC3104_BASE + 0x1000) |
| #define EC3104_IMR (EC3104_BASE + 0x1004) |
| #define EC3104_IPR (EC3104_BASE + 0x1008) |
| |
| #define ctrl_readl(addr) (*(volatile u32 *)(addr)) |
| #define ctrl_writel(data,addr) (*(volatile u32 *)(addr) = (data)) |
| #define ctrl_readb(addr) (*(volatile u8 *)(addr)) |
| |
| static char *ec3104_name(unsigned index) |
| { |
| switch(index) { |
| case 0: |
| return "power management"; |
| case 1: |
| return "interrupts"; |
| case 3: |
| return "PS2 (1)"; |
| case 4: |
| return "PS2 (2)"; |
| case 5: |
| return "I2C (1)"; |
| case 6: |
| return "I2C (2)"; |
| case 7: |
| return "serial (1)"; |
| case 8: |
| return "serial (2)"; |
| case 9: |
| return "serial (3)"; |
| case 10: |
| return "serial (4)"; |
| case 16: |
| return "pcmcia (1)"; |
| case 17: |
| return "pcmcia (2)"; |
| default: { |
| static char buf[32]; |
| |
| sprintf(buf, "unknown (%d)", index); |
| |
| return buf; |
| } |
| } |
| } |
| |
| int get_pending_interrupts(char *buf) |
| { |
| u32 ipr; |
| u32 bit; |
| char *p = buf; |
| |
| p += sprintf(p, "pending: ("); |
| |
| ipr = ctrl_inl(EC3104_IPR); |
| |
| for (bit = 1; bit < 32; bit++) |
| if (!(ipr & (1<<bit))) |
| p += sprintf(p, "%s ", ec3104_name(bit)); |
| |
| p += sprintf(p, ")\n"); |
| |
| return p - buf; |
| } |
| |
| static inline u32 ec3104_irq2mask(unsigned int irq) |
| { |
| return (1 << (irq - EC3104_IRQBASE)); |
| } |
| |
| static inline void mask_ec3104_irq(unsigned int irq) |
| { |
| u32 mask; |
| |
| mask = ctrl_readl(EC3104_IMR); |
| |
| mask |= ec3104_irq2mask(irq); |
| |
| ctrl_writel(mask, EC3104_IMR); |
| } |
| |
| static inline void unmask_ec3104_irq(unsigned int irq) |
| { |
| u32 mask; |
| |
| mask = ctrl_readl(EC3104_IMR); |
| |
| mask &= ~ec3104_irq2mask(irq); |
| |
| ctrl_writel(mask, EC3104_IMR); |
| } |
| |
| static void disable_ec3104_irq(unsigned int irq) |
| { |
| mask_ec3104_irq(irq); |
| } |
| |
| static void enable_ec3104_irq(unsigned int irq) |
| { |
| unmask_ec3104_irq(irq); |
| } |
| |
| static void mask_and_ack_ec3104_irq(unsigned int irq) |
| { |
| mask_ec3104_irq(irq); |
| } |
| |
| static void end_ec3104_irq(unsigned int irq) |
| { |
| if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) |
| unmask_ec3104_irq(irq); |
| } |
| |
| static unsigned int startup_ec3104_irq(unsigned int irq) |
| { |
| unmask_ec3104_irq(irq); |
| |
| return 0; |
| } |
| |
| static void shutdown_ec3104_irq(unsigned int irq) |
| { |
| mask_ec3104_irq(irq); |
| |
| } |
| |
| static struct hw_interrupt_type ec3104_int = { |
| .typename = "EC3104", |
| .enable = enable_ec3104_irq, |
| .disable = disable_ec3104_irq, |
| .ack = mask_and_ack_ec3104_irq, |
| .end = end_ec3104_irq, |
| .startup = startup_ec3104_irq, |
| .shutdown = shutdown_ec3104_irq, |
| }; |
| |
| /* Yuck. the _demux API is ugly */ |
| int ec3104_irq_demux(int irq) |
| { |
| if (irq == EC3104_IRQ) { |
| unsigned int mask; |
| |
| mask = ctrl_readl(EC3104_IRR); |
| |
| if (mask == 0xffffffff) |
| return EC3104_IRQ; |
| else |
| return EC3104_IRQBASE + ffz(mask); |
| } |
| |
| return irq; |
| } |