197 lines
5.2 KiB
C
197 lines
5.2 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|