[parisc-linux-cvs] 2.4.9-pa79, greatly improved EISA support patch
Helge Deller
deller@gmx.de
Thu, 8 Nov 2001 00:54:45 +0100
--------------Boundary-00=_9RFGM72Y64QGTZP80OU4
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 8bit
On Thursday 08 November 2001 00:51, Helge Deller wrote:
> CVSROOT: /var/cvs
> Module name: linux
> Changes by: deller 01/11/07 16:51:31
>
> Modified files:
> . : Makefile
> drivers/gsc : eisa.c
> arch/parisc/kernel: irq.c
> arch/parisc/lib: io.c
>
> Log message:
> - 2.4.9-pa79
> - greatly improved EISA support, tested with an old ISA NE2000 NIC on irq 10 :-)
>
> This patch was contributed by Daniel Engstrom <5116@telia.com>,
> Thanks a lot Daniel!
--------------Boundary-00=_9RFGM72Y64QGTZP80OU4
Content-Type: text/plain;
charset="iso-8859-1";
name="diff"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="diff"
Index: Makefile
===================================================================
RCS file: /var/cvs/linux/Makefile,v
retrieving revision 1.190
diff -u -p -r1.190 Makefile
--- Makefile 2001/11/05 07:42:14 1.190
+++ Makefile 2001/11/07 23:43:58
@@ -1,7 +1,7 @@
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 9
-EXTRAVERSION = -pa78
+EXTRAVERSION = -pa79
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
Index: drivers/gsc/eisa.c
===================================================================
RCS file: /var/cvs/linux/drivers/gsc/eisa.c,v
retrieving revision 1.10
diff -u -p -r1.10 eisa.c
--- drivers/gsc/eisa.c 2001/11/05 07:42:15 1.10
+++ drivers/gsc/eisa.c 2001/11/07 23:44:09
@@ -7,12 +7,24 @@
* 2 of the License, or (at your option) any later version.
*
* Copyright (c) 2001 Matthew Wilcox for Hewlett Packard
+ * Copyright (c) 2001 Daniel Engstrom <5116@telia.com>
*
* There are two distinct EISA adapters. Mongoose is found in machines
* before the 712; then the Wax ASIC is used. To complicate matters, the
* Wax ASIC also includes a PS/2 and RS-232 controller, but those are
* dealt with elsewhere; this file is concerned only with the EISA portions
* of Wax.
+ *
+ *
+ * HINT:
+ * -----
+ * To allow an ISA card to work properly in the EISA slot you need to
+ * set an edge trigger level. This may be done on the palo command line
+ * by adding the kernel parameter "eisa_irq_edge=n,n2,[...]]", with
+ * n and n2 as the irq levels you want to use.
+ *
+ * Example: "eisa-irq_edge=10,11" allows ISA cards to operate at
+ * irq levels 10 and 11.
*/
#include <linux/init.h>
@@ -27,11 +39,20 @@
#include <asm/gsc.h>
#include <asm/hardware.h>
#include <asm/processor.h>
+#include <asm/delay.h>
+#if 0
+#define EISA_DBG(msg, arg... ) printk(KERN_DEBUG "eisa: " msg , ## arg )
+#else
+#define EISA_DBG(msg, arg... )
+#endif
+
+static spinlock_t eisa_irq_lock = SPIN_LOCK_UNLOCKED;
+
/* We can only have one EISA adapter in the system because neither
* implementation can be flexed.
*/
-struct eisa_ba {
+static struct eisa_ba {
struct pci_hba_data hba;
} eisa_dev;
@@ -57,14 +78,14 @@ unsigned char eisa_in8(unsigned short po
unsigned short eisa_in16(unsigned short port)
{
if (EISA_bus)
- return le16_to_cpu(gsc_readw(eisa_permute(port)));
+ return gsc_readw(eisa_permute(port));
return 0xffff;
}
unsigned int eisa_in32(unsigned short port)
{
if (EISA_bus)
- return le32_to_cpu(gsc_readl(eisa_permute(port)));
+ return gsc_readl(eisa_permute(port));
return 0xffffffff;
}
@@ -76,54 +97,211 @@ void eisa_out8(unsigned char data, unsig
void eisa_out16(unsigned short data, unsigned short port)
{
- if (EISA_bus)
- gsc_writew(cpu_to_le16(data), eisa_permute(port));
+ if (EISA_bus)
+ gsc_writew(data, eisa_permute(port));
}
void eisa_out32(unsigned int data, unsigned short port)
{
if (EISA_bus)
- gsc_writel(cpu_to_le32(data), eisa_permute(port));
+ gsc_writel(data, eisa_permute(port));
}
/* Interrupt handling */
+/* cached interrupt mask registers */
+static int master_mask;
+static int slave_mask;
+
+/* the trig level can be set with the
+ * eisa_irq_edge=n,n,n commanlnine parameter
+ * We should really read this from the EEPROM
+ * in the furure.
+ */
+/* irq 13,8,2,1,0 must be edge */
+static unsigned int eisa_irq_level=0xdef8; /* these are to be level-trigged */
+
+
+/* called by free irq */
static void eisa_disable_irq(void *irq_dev, int irq)
{
- return;
+ unsigned long flags;
+
+ EISA_DBG("disable irq %d\n", irq);
+ /* just mask for now */
+ spin_lock_irqsave(&eisa_irq_lock, flags);
+ if (irq & 8) {
+ slave_mask |= (1 << (irq&7));
+ eisa_out8(slave_mask, 0xa1);
+ } else {
+ master_mask |= (1 << (irq&7));
+ eisa_out8(master_mask, 0x21);
+ }
+ spin_unlock_irqrestore(&eisa_irq_lock, flags);
+ EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21));
+ EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1));
}
+/* called by request irq */
static void eisa_enable_irq(void *irq_dev, int irq)
{
- return;
+ unsigned long flags;
+ EISA_DBG("enable irq %d\n", irq);
+
+ spin_lock_irqsave(&eisa_irq_lock, flags);
+ if (irq & 8) {
+ slave_mask &= ~(1 << (irq&7));
+ eisa_out8(slave_mask, 0xa1);
+ } else {
+ master_mask &= ~(1 << (irq&7));
+ eisa_out8(master_mask, 0x21);
+ }
+ spin_unlock_irqrestore(&eisa_irq_lock, flags);
+ EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21));
+ EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1));
}
static void eisa_mask_irq(void *irq_dev, int irq)
{
- return;
+ unsigned long flags;
+ EISA_DBG("mask irq %d\n", irq);
+
+ /* mask irq */
+ spin_lock_irqsave(&eisa_irq_lock, flags);
+ if (irq & 8) {
+ slave_mask |= (1 << (irq&7));
+ eisa_out8(slave_mask, 0xa1);
+ } else {
+ master_mask |= (1 << (irq&7));
+ eisa_out8(master_mask, 0x21);
+ }
+ spin_unlock_irqrestore(&eisa_irq_lock, flags);
}
static void eisa_unmask_irq(void *irq_dev, int irq)
{
- return;
+ unsigned long flags;
+ EISA_DBG("unmask irq %d\n", irq);
+
+ /* unmask */
+ spin_lock_irqsave(&eisa_irq_lock, flags);
+ if (irq & 8) {
+ slave_mask &= ~(1 << (irq&7));
+ eisa_out8(slave_mask, 0xa1);
+ } else {
+ master_mask &= ~(1 << (irq&7));
+ eisa_out8(master_mask, 0x21);
+ }
+ spin_unlock_irqrestore(&eisa_irq_lock, flags);
}
-struct irqaction action[IRQ_PER_REGION];
+static struct irqaction action[IRQ_PER_REGION];
-#define EISA_IRQ_REGION 0 /* Compatibility */
-
+/* EISA needs to be fixed at IRQ region #0 (EISA_IRQ_REGION) */
static struct irq_region eisa_irq_region = {
- { eisa_disable_irq, eisa_enable_irq, eisa_mask_irq, eisa_unmask_irq },
- { NULL, "EISA", EISA_IRQ_REGION },
- action,
+ ops: { eisa_disable_irq, eisa_enable_irq, eisa_mask_irq, eisa_unmask_irq },
+ data: { name: "EISA", irqbase: 0 },
+ action: action,
};
static void eisa_irq(int _, void *intr_dev, struct pt_regs *regs)
{
extern void do_irq(struct irqaction *a, int i, struct pt_regs *p);
- int irq = gsc_readb(0xfc01f000) & 0xf; /* EISA supports 16 irqs */
+ int irq = gsc_readb(0xfc01f000); /* EISA supports 16 irqs */
+ unsigned long flags;
+
+ spin_lock_irqsave(&eisa_irq_lock, flags);
+ /* read IRR command */
+ eisa_out8(0x0a, 0x20);
+ eisa_out8(0x0a, 0xa0);
+
+ EISA_DBG("irq IAR %02x 8259-1 irr %02x 8259-2 irr %02x\n",
+ irq, eisa_in8(0x20), eisa_in8(0xa0));
+
+ /* read ISR command */
+ eisa_out8(0x0a, 0x20);
+ eisa_out8(0x0a, 0xa0);
+ EISA_DBG("irq 8259-1 isr %02x imr %02x 8259-2 isr %02x imr %02x\n",
+ eisa_in8(0x20), eisa_in8(0x21), eisa_in8(0xa0), eisa_in8(0xa1));
+
+ irq &= 0xf;
+
+ /* mask irq and write eoi */
+ if (irq & 8) {
+ slave_mask |= (1 << (irq&7));
+ eisa_out8(slave_mask, 0xa1);
+ eisa_out8(0x60 | (irq&7),0xa0);/* 'Specific EOI' to slave */
+ eisa_out8(0x62,0x20); /* 'Specific EOI' to master-IRQ2 */
+
+ } else {
+ master_mask |= (1 << (irq&7));
+ eisa_out8(master_mask, 0x21);
+ eisa_out8(0x60|irq,0x20); /* 'Specific EOI' to master */
+ }
+ spin_unlock_irqrestore(&eisa_irq_lock, flags);
+
do_irq(&eisa_irq_region.action[irq], EISA_IRQ_REGION + irq, regs);
+
+ spin_lock_irqsave(&eisa_irq_lock, flags);
+ /* unmask */
+ if (irq & 8) {
+ slave_mask &= ~(1 << (irq&7));
+ eisa_out8(slave_mask, 0xa1);
+ } else {
+ master_mask &= ~(1 << (irq&7));
+ eisa_out8(master_mask, 0x21);
+ }
+ spin_unlock_irqrestore(&eisa_irq_lock, flags);
+}
+
+static void dummy_irq2_handler(int _, void *dev, struct pt_regs *regs)
+{
+ printk(KERN_ALERT "eisa: uhh, irq2?\n");
+}
+
+static void init_eisa_pic(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&eisa_irq_lock, flags);
+
+ eisa_out8(0xff, 0x21); /* mask during init */
+ eisa_out8(0xff, 0xa1); /* mask during init */
+
+ /* master pic */
+ eisa_out8(0x11,0x20); /* ICW1 */
+ eisa_out8(0x00,0x21); /* ICW2 */
+ eisa_out8(0x04,0x21); /* ICW3 */
+ eisa_out8(0x01,0x21); /* ICW4 */
+ eisa_out8(0x40,0x20); /* OCW2 */
+
+ /* slave pic */
+ eisa_out8(0x11,0xa0); /* ICW1 */
+ eisa_out8(0x08,0xa1); /* ICW2 */
+ eisa_out8(0x02,0xa1); /* ICW3 */
+ eisa_out8(0x01,0xa1); /* ICW4 */
+ eisa_out8(0x40,0xa0); /* OCW2 */
+
+ udelay(100);
+
+ slave_mask = 0xff;
+ master_mask = 0xfb;
+ eisa_out8(slave_mask, 0xa1); /* OCW1 */
+ eisa_out8(master_mask, 0x21); /* OCW1 */
+
+ /* setup trig level */
+ EISA_DBG("EISA edge/level %04x\n", eisa_irq_level);
+
+ eisa_out8(eisa_irq_level&0xff, 0x4d0); /* Set all irq's to edge */
+ eisa_out8((eisa_irq_level >> 8) & 0xff, 0x4d1);
+
+ EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21));
+ EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1));
+ EISA_DBG("pic0 edge/level %02x\n", eisa_in8(0x4d0));
+ EISA_DBG("pic1 edge/level %02x\n", eisa_in8(0x4d1));
+
+ spin_unlock_irqrestore(&eisa_irq_lock, flags);
}
/* Device initialisation */
@@ -134,9 +312,10 @@ static int __devinit eisa_probe(struct p
{
int result;
char *name = is_mongoose(dev) ? "Mongoose" : "Wax";
-
- printk("%s EISA Adapter found at 0x%08lx\n", name, dev->hpa);
+ printk(KERN_INFO "%s EISA Adapter found at 0x%08lx\n",
+ name, dev->hpa);
+
eisa_dev.hba.lmmio_space.name = "EISA";
eisa_dev.hba.lmmio_space.start = (unsigned long) 0xfffffffffc000000;
eisa_dev.hba.lmmio_space.end = (unsigned long) 0xffffffffffbfffff;
@@ -157,20 +336,23 @@ static int __devinit eisa_probe(struct p
}
pcibios_register_hba(&eisa_dev.hba);
- if (!dev->irq) {
- printk(KERN_ERR "EISA: failed to claim IRQ\n");
- return -ENODEV;
- }
-
- result = request_irq(dev->irq, eisa_irq, 0, "EISA", NULL);
+ init_eisa_pic();
+
+ result = request_irq(dev->irq, eisa_irq, SA_SHIRQ, "EISA", NULL);
if (result) {
printk(KERN_ERR "EISA: request_irq failed!\n");
return result;
}
+
+ /* Reserve IRQ2 */
+ action[2].handler = dummy_irq2_handler;
+ action[2].name = "cascade";
+
+ eisa_irq_region.data.dev = dev;
irq_region[0] = &eisa_irq_region;
-
+
EISA_bus = 1;
-
+
return 0;
}
@@ -192,3 +374,36 @@ void __init eisa_init(void)
{
register_parisc_driver(&eisa_driver);
}
+
+
+static int __init eisa_irq_setup(char *str)
+{
+ char *cur = str;
+ int val;
+
+ EISA_DBG("IRQ setup\n");
+ while (cur != NULL) {
+ char *pe;
+
+ val = (int) simple_strtoul(cur, &pe, 0);
+ if (val > 15 || val < 0) {
+ printk(KERN_ERR "eisa: EISA irq value are 0-15\n");
+ continue;
+ }
+ if (val == 2) {
+ val = 9;
+ }
+ eisa_irq_level &= ~(1<<val); /* clear the corresponding bit */
+ EISA_DBG("setting IRQ %d to edge-triggered mode\n", val);
+
+ if ((cur = strchr(cur, ','))) {
+ cur++;
+ } else {
+ break;
+ }
+ }
+ return 1;
+}
+
+__setup("eisa_irq_edge=", eisa_irq_setup);
+
Index: arch/parisc/kernel/irq.c
===================================================================
RCS file: /var/cvs/linux/arch/parisc/kernel/irq.c,v
retrieving revision 1.49
diff -u -p -r1.49 irq.c
--- arch/parisc/kernel/irq.c 2001/11/05 07:42:15 1.49
+++ arch/parisc/kernel/irq.c 2001/11/07 23:44:35
@@ -116,6 +116,7 @@ void mask_irq(int irq)
struct irq_region *region;
DBG_IRQ(irq, ("mask_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)));
+ irq = irq_cannonicalize(irq);
region = irq_region[IRQ_REGION(irq)];
if (region->ops.mask_irq)
region->ops.mask_irq(region->data.dev, IRQ_OFFSET(irq));
@@ -126,6 +127,7 @@ void unmask_irq(int irq)
struct irq_region *region;
DBG_IRQ(irq, ("unmask_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)));
+ irq = irq_cannonicalize(irq);
region = irq_region[IRQ_REGION(irq)];
if (region->ops.unmask_irq)
region->ops.unmask_irq(region->data.dev, IRQ_OFFSET(irq));
@@ -136,6 +138,7 @@ void disable_irq(int irq)
struct irq_region *region;
DBG_IRQ(irq, ("disable_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)));
+ irq = irq_cannonicalize(irq);
region = irq_region[IRQ_REGION(irq)];
if (region->ops.disable_irq)
region->ops.disable_irq(region->data.dev, IRQ_OFFSET(irq));
@@ -148,6 +151,7 @@ void enable_irq(int irq)
struct irq_region *region;
DBG_IRQ(irq, ("enable_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)));
+ irq = irq_cannonicalize(irq);
region = irq_region[IRQ_REGION(irq)];
if (region->ops.enable_irq)
@@ -530,6 +534,7 @@ int request_irq(unsigned int irq,
printk(KERN_INFO "request_irq(%d, %p, 0x%lx, %s, %p)\n",irq, handler, irqflags, devname, dev_id);
#endif
+ irq = irq_cannonicalize(irq);
/* request_irq()/free_irq() may not be called from interrupt context. */
if (in_interrupt())
BUG();
@@ -589,6 +594,8 @@ void free_irq(unsigned int irq, void *de
struct irqaction *action, **p;
/* See comments in request_irq() about interrupt context */
+ irq = irq_cannonicalize(irq);
+
if (in_interrupt()) BUG();
spin_lock(&irq_lock);
@@ -667,7 +674,7 @@ unsigned long probe_irq_on(void)
*/
for (i = EISA_MAX_IRQS-1; i > 0; i--) {
struct irqaction *action;
-
+
spin_lock_irq(&irq_lock);
action = region->action + i;
if (!action->handler) {
Index: arch/parisc/lib/io.c
===================================================================
RCS file: /var/cvs/linux/arch/parisc/lib/io.c,v
retrieving revision 1.5
diff -u -p -r1.5 io.c
--- arch/parisc/lib/io.c 2001/08/24 14:45:05 1.5
+++ arch/parisc/lib/io.c 2001/11/07 23:44:49
@@ -160,32 +160,67 @@ void insb (unsigned long port, void *dst
*/
void insw (unsigned long port, void *dst, unsigned long count)
{
- if (((unsigned long)dst) & 0x3) {
- if (((unsigned long)dst) & 0x1) {
- BUG();
+ unsigned int l = 0, l2;
+
+ if (!count)
+ return;
+
+ switch (((unsigned long) dst) & 0x3)
+ {
+ case 0x00: /* Buffer 32-bit aligned */
+ while (count>=2) {
+
+ count -= 2;
+ l = inw(port) << 16;
+ l |= inw(port);
+ *(unsigned int *) dst = l;
+ ((unsigned int *) dst)++;
}
- if (!count)
- return;
- count--;
- *(unsigned short* ) dst = inw(port);
+ if (count) {
+ *(unsigned short *) dst = inw(port);
+ }
+ break;
+
+ case 0x02: /* Buffer 16-bit aligned */
+ *(unsigned short *) dst = inw(port);
((unsigned short *) dst)++;
- }
-
- while (count >= 2) {
- unsigned int w;
- count -= 2;
- w = inw(port) << 16;
- w |= inw(port);
- *(unsigned int *) dst = w;
- ((unsigned int *) dst)++;
- }
-
- if (count) {
- *(unsigned short*) dst = inw(port);
+ count--;
+ while (count>=2) {
+
+ count -= 2;
+ l = inw(port) << 16;
+ l |= inw(port);
+ *(unsigned int *) dst = l;
+ ((unsigned int *) dst)++;
+ }
+ if (count) {
+ *(unsigned short *) dst = inw(port);
+ }
+ break;
+
+ case 0x01: /* Buffer 8-bit aligned */
+ case 0x03:
+ /* I don't bother with 32bit transfers
+ * in this case, 16bit will have to do -- DE */
+ --count;
+
+ l = inw(port);
+ *(unsigned char *) dst = l >> 8;
+ ((unsigned char *) dst)++;
+ while (count--)
+ {
+ l2 = inw(port);
+ *(unsigned short *) dst = (l & 0xff) << 8 | (l2 >> 8);
+ ((unsigned short *) dst)++;
+ l = l2;
+ }
+ *(unsigned char *) dst = l & 0xff;
+ break;
}
}
+
/*
* Read COUNT 32-bit words from port PORT into memory starting at
* SRC. Now works with any alignment in SRC. Performance is important,
@@ -286,27 +321,63 @@ void outsb(unsigned long port, const voi
*/
void outsw (unsigned long port, const void *src, unsigned long count)
{
- if (((unsigned long)src) & 0x3) {
- if (((unsigned long)src) & 0x1) {
- /* unaligned input buffer */
- BUG();
+ unsigned int l = 0, l2;
+
+ if (!count)
+ return;
+
+ switch (((unsigned long) src) & 0x3)
+ {
+ case 0x00: /* Buffer 32-bit aligned */
+ while (count>=2) {
+ count -= 2;
+ l = *(unsigned int *) src;
+ ((unsigned int *) src)++;
+ outw(l >> 16, port);
+ outw(l & 0xffff, port);
}
+ if (count) {
+ outw(*(unsigned short*)src, port);
+ }
+ break;
+
+ case 0x02: /* Buffer 16-bit aligned */
+
outw(*(unsigned short*)src, port);
((unsigned short *) src)++;
- --count;
- }
-
- while (count >= 2) {
- unsigned int w;
- count -= 2;
- w = *(unsigned int *) src;
- ((unsigned int *) src)++;
- outw(w >> 16, port);
- outw(w & 0xff, port);
- }
-
- if (count) {
- outw(*(unsigned short *) src, port);
+ count--;
+
+ while (count>=2) {
+ count -= 2;
+ l = *(unsigned int *) src;
+ ((unsigned int *) src)++;
+ outw(l >> 16, port);
+ outw(l & 0xffff, port);
+ }
+ if (count) {
+ outw(*(unsigned short*)src, port);
+ }
+ break;
+
+ case 0x01: /* Buffer 8-bit aligned */
+ /* I don't bother with 32bit transfers
+ * in this case, 16bit will have to do -- DE */
+
+ l = *(unsigned char *) src << 8;
+ ((unsigned char *) src)++;
+ count--;
+ while (count)
+ {
+ count--;
+ l2 = *(unsigned short *) src;
+ ((unsigned short *) src)++;
+ outw(l | l2 >> 8, port);
+ l = l2 << 8;
+ }
+ l2 = *(unsigned char *) src;
+ outw (l | l2>>8, port);
+ break;
+
}
}
--------------Boundary-00=_9RFGM72Y64QGTZP80OU4--