/* $Id: setup_cqreek.c,v 1.6 2001/02/14 09:36:42 gniibe Exp $
*
* arch/sh/kernel/setup_cqreek.c
*
* Copyright (C) 2000 Niibe Yutaka
*
* CqREEK IDE/ISA Bridge Support.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/io_generic.h>
#include <asm/irq.h>
#include <asm/machvec.h>
#include <asm/machvec_init.h>
#include <asm/rtc.h>
#define BRIDGE_FEATURE 0x0002
#define BRIDGE_IDE_CTRL 0x0018
#define BRIDGE_IDE_INTR_LVL 0x001A
#define BRIDGE_IDE_INTR_MASK 0x001C
#define BRIDGE_IDE_INTR_STAT 0x001E
#define BRIDGE_ISA_CTRL 0x0028
#define BRIDGE_ISA_INTR_LVL 0x002A
#define BRIDGE_ISA_INTR_MASK 0x002C
#define BRIDGE_ISA_INTR_STAT 0x002E
#define IDE_OFFSET 0xA4000000UL
#define ISA_OFFSET 0xA4A00000UL
static unsigned long cqreek_port2addr(unsigned long port)
{
if (0x0000<=port && port<=0x0040)
return IDE_OFFSET + port;
if ((0x01f0<=port && port<=0x01f7) || port == 0x03f6)
return IDE_OFFSET + port;
return ISA_OFFSET + port;
}
struct cqreek_irq_data {
unsigned short mask_port; /* Port of Interrupt Mask Register */
unsigned short stat_port; /* Port of Interrupt Status Register */
unsigned short bit; /* Value of the bit */
};
static struct cqreek_irq_data cqreek_irq_data[NR_IRQS];
static void disable_cqreek_irq(unsigned int irq)
{
unsigned long flags;
unsigned short mask;
unsigned short mask_port = cqreek_irq_data[irq].mask_port;
unsigned short bit = cqreek_irq_data[irq].bit;
save_and_cli(flags);
/* Disable IRQ */
mask = inw(mask_port) & ~bit;
outw_p(mask, mask_port);
restore_flags(flags);
}
static void enable_cqreek_irq(unsigned int irq)
{
unsigned long flags;
unsigned short mask;
unsigned short mask_port = cqreek_irq_data[irq].mask_port;
unsigned short bit = cqreek_irq_data[irq].bit;
save_and_cli(flags);
/* Enable IRQ */
mask = inw(mask_port) | bit;
outw_p(mask, mask_port);
restore_flags(flags);
}
static void mask_and_ack_cqreek(unsigned int irq)
{
unsigned short stat_port = cqreek_irq_data[irq].stat_port;
unsigned short bit = cqreek_irq_data[irq].bit;
inw(stat_port);
disable_cqreek_irq(irq);
/* Clear IRQ (it might be edge IRQ) */
outw_p(bit, stat_port);
}
static void end_cqreek_irq(unsigned int irq)
{
enable_cqreek_irq(irq);
}
static unsigned int startup_cqreek_irq(unsigned int irq)
{
enable_cqreek_irq(irq);
return 0;
}
static void shutdown_cqreek_irq(unsigned int irq)
{
disable_cqreek_irq(irq);
}
static struct hw_interrupt_type cqreek_irq_type = {
"CqREEK-IRQ",
startup_cqreek_irq,
shutdown_cqreek_irq,
enable_cqreek_irq,
disable_cqreek_irq,
mask_and_ack_cqreek,
end_cqreek_irq
};
static int has_ide, has_isa;
/* XXX: This is just for test for my NE2000 ISA board
What we really need is virtualized IRQ and demultiplexer like HP600 port */
void __init init_cqreek_IRQ(void)
{
if (has_ide) {
cqreek_irq_data[14].mask_port = BRIDGE_IDE_INTR_MASK;
cqreek_irq_data[14].stat_port = BRIDGE_IDE_INTR_STAT;
cqreek_irq_data[14].bit = 1;
irq_desc[14].handler = &cqreek_irq_type;
irq_desc[14].status = IRQ_DISABLED;
irq_desc[14].action = 0;
irq_desc[14].depth = 1;
disable_cqreek_irq(14);
}
if (has_isa) {
cqreek_irq_data[10].mask_port = BRIDGE_ISA_INTR_MASK;
cqreek_irq_data[10].stat_port = BRIDGE_ISA_INTR_STAT;
cqreek_irq_data[10].bit = (1 << 10);
/* XXX: Err... we may need demultiplexer for ISA irq... */
irq_desc[10].handler = &cqreek_irq_type;
irq_desc[10].status = IRQ_DISABLED;
irq_desc[10].action = 0;
irq_desc[10].depth = 1;
disable_cqreek_irq(10);
}
}
/*
* Initialize the board
*/
void __init setup_cqreek(void)
{
extern void disable_hlt(void);
int i;
/* udelay is not available at setup time yet... */
#define DELAY() do {for (i=0; i<10000; i++) ctrl_inw(0xa0000000);} while(0)
/*
* XXX: I don't know the reason, but it becomes so fragile with
* "sleep", so we need to stop sleeping.
*/
disable_hlt();
if ((inw (BRIDGE_FEATURE) & 1)) { /* We have IDE interface */
outw_p(0, BRIDGE_IDE_INTR_LVL);
outw_p(0, BRIDGE_IDE_INTR_MASK);
outw_p(0, BRIDGE_IDE_CTRL);
DELAY();
outw_p(0x8000, BRIDGE_IDE_CTRL);
DELAY();
outw_p(0xffff, BRIDGE_IDE_INTR_STAT); /* Clear interrupt status */
outw_p(0x0f-14, BRIDGE_IDE_INTR_LVL); /* Use 14 IPR */
outw_p(1, BRIDGE_IDE_INTR_MASK); /* Enable interrupt */
has_ide=1;
}
if ((inw (BRIDGE_FEATURE) & 2)) { /* We have ISA interface */
outw_p(0, BRIDGE_ISA_INTR_LVL);
outw_p(0, BRIDGE_ISA_INTR_MASK);
outw_p(0, BRIDGE_ISA_CTRL);
DELAY();
outw_p(0x8000, BRIDGE_ISA_CTRL);
DELAY();
outw_p(0xffff, BRIDGE_ISA_INTR_STAT); /* Clear interrupt status */
outw_p(0x0f-10, BRIDGE_ISA_INTR_LVL); /* Use 10 IPR */
outw_p(0xfff8, BRIDGE_ISA_INTR_MASK); /* Enable interrupt */
has_isa=1;
}
printk(KERN_INFO "CqREEK Setup (IDE=%d, ISA=%d)...done\n", has_ide, has_isa);
}
/*
* The Machine Vector
*/
struct sh_machine_vector mv_cqreek __initmv = {
mv_name: "CqREEK",
#if defined(__SH4__)
mv_nr_irqs: 48,
#elif defined(CONFIG_CPU_SUBTYPE_SH7708)
mv_nr_irqs: 32,
#elif defined(CONFIG_CPU_SUBTYPE_SH7709)
mv_nr_irqs: 61,
#endif
mv_inb: generic_inb,
mv_inw: generic_inw,
mv_inl: generic_inl,
mv_outb: generic_outb,
mv_outw: generic_outw,
mv_outl: generic_outl,
mv_inb_p: generic_inb_p,
mv_inw_p: generic_inw_p,
mv_inl_p: generic_inl_p,
mv_outb_p: generic_outb_p,
mv_outw_p: generic_outw_p,
mv_outl_p: generic_outl_p,
mv_insb: generic_insb,
mv_insw: generic_insw,
mv_insl: generic_insl,
mv_outsb: generic_outsb,
mv_outsw: generic_outsw,
mv_outsl: generic_outsl,
mv_readb: generic_readb,
mv_readw: generic_readw,
mv_readl: generic_readl,
mv_writeb: generic_writeb,
mv_writew: generic_writew,
mv_writel: generic_writel,
mv_init_arch: setup_cqreek,
mv_init_irq: init_cqreek_IRQ,
mv_isa_port2addr: cqreek_port2addr,
mv_ioremap: generic_ioremap,
mv_iounmap: generic_iounmap,
mv_rtc_gettimeofday: sh_rtc_gettimeofday,
mv_rtc_settimeofday: sh_rtc_settimeofday,
};
ALIAS_MV(cqreek)