patch-2.1.80 linux/arch/arm/kernel/ecard.c

Next file: linux/arch/arm/kernel/entry-armo.S
Previous file: linux/arch/arm/kernel/dma.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.79/linux/arch/arm/kernel/ecard.c linux/arch/arm/kernel/ecard.c
@@ -0,0 +1,604 @@
+/*
+ * linux/arch/arm/kernel/ecard.c
+ *
+ *  Find all installed expansion cards, and handle interrupts from them.
+ *
+ * Copyright 1995,1996,1997 Russell King
+ *
+ * Created from information from Acorns RiscOS3 PRMs
+ *
+ * 08-Dec-1996	RMK	Added code for the 9'th expansion card - the ether podule slot.
+ * 06-May-1997  RMK	Added blacklist for cards whose loader doesn't work.
+ * 12-Sep-1997	RMK	Created new handling of interrupt enables/disables - cards can
+ *			now register their own routine to control interrupts (recommended).
+ * 29-Sep-1997	RMK	Expansion card interrupt hardware not being re-enabled on reset from
+ *			Linux. (Caused cards not to respond under RiscOS without hard reset).
+ */
+
+#define ECARD_C
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include <asm/irq-no.h>
+#include <asm/ecard.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/arch/irq.h>
+
+#ifdef CONFIG_ARCH_ARC
+#include <asm/arch/oldlatches.h>
+#else
+#define oldlatch_init()
+#endif
+
+#define BLACKLIST_NAME(m,p,s)	{ m, p, NULL, s }
+#define BLACKLIST_LOADER(m,p,l)	{ m, p, l, NULL }
+#define BLACKLIST_NOLOADER(m,p)	{ m, p, noloader, blacklisted_str }
+#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+
+extern unsigned long atomwide_serial_loader[], oak_scsi_loader[], noloader[];
+static const char blacklisted_str[] = "*loader blacklisted - not 32-bit compliant*";
+
+static const struct expcard_blacklist {
+	unsigned short	 manufacturer;
+	unsigned short	 product;
+	const loader_t 	 loader;
+	const char	*type;
+} blacklist[] = {
+/* Cards without names */
+    BLACKLIST_NAME(MANU_ACORN,		PROD_ACORN_ETHER1,	"Acorn Ether1"),
+
+/* Cards with corrected loader */
+  BLACKLIST_LOADER(MANU_ATOMWIDE,	PROD_ATOMWIDE_3PSERIAL,	atomwide_serial_loader),
+  BLACKLIST_LOADER(MANU_OAK,		PROD_OAK_SCSI,		oak_scsi_loader),
+
+/* Unsupported cards with no loader */
+BLACKLIST_NOLOADER(MANU_ALSYSTEMS,	PROD_ALSYS_SCSIATAPI),
+BLACKLIST_NOLOADER(MANU_MCS,		PROD_MCS_CONNECT32)
+};
+
+extern int setup_arm_irq(int, struct irqaction *);
+
+/*
+ * from linux/arch/arm/kernel/irq.c
+ */
+extern void do_ecard_IRQ(int irq, struct pt_regs *);
+
+static ecard_t expcard[MAX_ECARDS];
+static signed char irqno_to_expcard[16];
+static unsigned int ecard_numcards, ecard_numirqcards;
+static unsigned int have_expmask;
+static unsigned long kmem;
+
+static void ecard_def_irq_enable (ecard_t *ec, int irqnr)
+{
+#ifdef HAS_EXPMASK
+	if (irqnr < 4 && have_expmask) {
+		have_expmask |= 1 << irqnr;
+		EXPMASK_ENABLE = have_expmask;
+	}
+#endif
+}
+
+static void ecard_def_irq_disable (ecard_t *ec, int irqnr)
+{
+#ifdef HAS_EXPMASK
+	if (irqnr < 4 && have_expmask) {
+		have_expmask &= ~(1 << irqnr);
+		EXPMASK_ENABLE = have_expmask;
+	}
+#endif
+}
+
+static void ecard_def_fiq_enable (ecard_t *ec, int fiqnr)
+{
+	panic ("ecard_def_fiq_enable called - impossible");
+}
+
+static void ecard_def_fiq_disable (ecard_t *ec, int fiqnr)
+{
+	panic ("ecard_def_fiq_disable called - impossible");
+}
+
+static expansioncard_ops_t ecard_default_ops = {
+	ecard_def_irq_enable,
+	ecard_def_irq_disable,
+	ecard_def_fiq_enable,
+	ecard_def_fiq_disable
+};
+
+/*
+ * Enable and disable interrupts from expansion cards.
+ * (interrupts are disabled for these functions).
+ *
+ * They are not meant to be called directly, but via enable/disable_irq.
+ */
+void ecard_enableirq (unsigned int irqnr)
+{
+	if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) {
+		ecard_t *ec = expcard + irqno_to_expcard[irqnr];
+
+		if (!ec->ops)
+			ec->ops = &ecard_default_ops;
+
+		if (ec->claimed && ec->ops->irqenable)
+			ec->ops->irqenable (ec, irqnr);
+		else
+			printk (KERN_ERR "ecard: rejecting request to "
+				"enable IRQs for %d\n", irqnr);
+	}
+}
+
+void ecard_disableirq (unsigned int irqnr)
+{
+	if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) {
+		ecard_t *ec = expcard + irqno_to_expcard[irqnr];
+
+		if (!ec->ops)
+			ec->ops = &ecard_default_ops;
+
+		if (ec->ops && ec->ops->irqdisable)
+			ec->ops->irqdisable (ec, irqnr);
+	}
+}
+
+void ecard_enablefiq (unsigned int fiqnr)
+{
+	if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) {
+		ecard_t *ec = expcard + irqno_to_expcard[fiqnr];
+
+		if (!ec->ops)
+			ec->ops = &ecard_default_ops;
+
+		if (ec->claimed && ec->ops->fiqenable)
+			ec->ops->fiqenable (ec, fiqnr);
+		else
+			printk (KERN_ERR "ecard: rejecting request to "
+				"enable FIQs for %d\n", fiqnr);
+	}
+}
+
+void ecard_disablefiq (unsigned int fiqnr)
+{
+	if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) {
+		ecard_t *ec = expcard + irqno_to_expcard[fiqnr];
+
+		if (!ec->ops)
+			ec->ops = &ecard_default_ops;
+
+		if (ec->ops->fiqdisable)
+			ec->ops->fiqdisable (ec, fiqnr);
+	}
+}
+
+static void *ecard_malloc(int len)
+{
+	int r;
+
+	len = (len + 3) & ~3;
+
+	if (kmem) {
+		r = kmem;
+		kmem += len;
+		return (void *)r;
+	} else
+		return kmalloc(len, GFP_KERNEL);
+}
+
+static void ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
+{
+	const int num_cards = ecard_numirqcards;
+	int i, called = 0;
+
+	mask_irq (IRQ_EXPANSIONCARD);
+	for (i = 0; i < num_cards; i++) {
+		if (expcard[i].claimed && expcard[i].irq &&
+		    (!expcard[i].irqmask ||
+		     expcard[i].irqaddr[0] & expcard[i].irqmask)) {
+			do_ecard_IRQ(expcard[i].irq, regs);
+			called ++;
+		}
+	}
+	cli ();
+	unmask_irq (IRQ_EXPANSIONCARD);
+	if (called == 0)
+		printk (KERN_WARNING "Wild interrupt from backplane?\n");
+}
+
+#ifdef HAS_EXPMASK
+static unsigned char priority_masks[] =
+{
+	0xf0, 0xf1, 0xf3, 0xf7, 0xff, 0xff, 0xff, 0xff
+};
+
+static unsigned char first_set[] =
+{
+	0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+	0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00
+};
+
+static void ecard_irq_expmask (int intr_no, void *dev_id, struct pt_regs *regs)
+{
+	const unsigned int statusmask = 15;
+	unsigned int status;
+
+	status = EXPMASK_STATUS & statusmask;
+	if (status) {
+		unsigned int irqno;
+		ecard_t *ec;
+again:
+		irqno = first_set[status];
+		ec = expcard + irqno_to_expcard[irqno];
+		if (ec->claimed) {
+			unsigned int oldexpmask;
+			/*
+			 * this ugly code is so that we can operate a prioritorising system.
+			 * Card 0 	highest priority
+			 * Card 1
+			 * Card 2
+			 * Card 3	lowest priority
+			 * Serial cards should go in 0/1, ethernet/scsi in 2/3
+			 * otherwise you will lose serial data at high speeds!
+			 */
+			oldexpmask = have_expmask;
+			EXPMASK_ENABLE = (have_expmask &= priority_masks[irqno]);
+			sti ();
+			do_ecard_IRQ (ec->irq, regs);
+			cli ();
+			EXPMASK_ENABLE = have_expmask = oldexpmask;
+			status = EXPMASK_STATUS & statusmask;
+			if (status)
+				goto again;
+		} else {
+			printk (KERN_WARNING "card%d: interrupt from unclaimed card???\n", irqno);
+			EXPMASK_ENABLE = (have_expmask &= ~(1 << irqno));
+		}
+	} else
+		printk (KERN_WARNING "Wild interrupt from backplane (masks)\n");
+}
+
+static int ecard_checkirqhw (void)
+{
+	int found;
+
+	EXPMASK_ENABLE = 0x00;
+	EXPMASK_STATUS = 0xff;
+	found = ((EXPMASK_STATUS & 15) == 0);
+	EXPMASK_ENABLE = 0xff;
+
+	return found;
+}
+#endif
+
+static void ecard_readbytes (void *addr, ecard_t *ec, int off, int len, int useld)
+{
+	extern int ecard_loader_read(int off, volatile unsigned int pa, loader_t loader);
+	unsigned char *a = (unsigned char *)addr;
+
+	if (ec->slot_no == 8) {
+		static unsigned int lowaddress;
+		unsigned int laddr, haddr;
+		unsigned char byte = 0; /* keep gcc quiet */
+
+		laddr = off & 4095;	/* number of bytes to read from offset + base addr */
+		haddr = off >> 12;	/* offset into card from base addr */
+
+		if (haddr > 256)
+			return;
+
+		/*
+		 * If we require a low address or address 0, then reset, and start again...
+		 */
+		if (!off || lowaddress > laddr) {
+			outb (0, ec->podaddr);
+			lowaddress = 0;
+		}
+		while (lowaddress <= laddr) {
+			byte = inb (ec->podaddr + haddr);
+			lowaddress += 1;
+		}
+		while (len--) {
+			*a++ = byte;
+			if (len) {
+				byte = inb (ec->podaddr + haddr);
+				lowaddress += 1;
+			}
+		}
+	} else {
+		if (!useld || !ec->loader) {
+			while(len--)
+				*a++ = inb(ec->podaddr + (off++));
+		} else {
+			while(len--) {
+				*(unsigned long *)0x108 = 0; /* hack for some loaders!!! */
+				*a++ = ecard_loader_read(off++, BUS_ADDR(ec->podaddr), ec->loader);
+			}
+		}
+	}
+}
+
+/*
+ * This is called to reset the loaders for each expansion card on reboot.
+ *
+ * This is required to make sure that the card is in the correct state
+ * that RiscOS expects it to be.
+ */
+void ecard_reset (int card)
+{
+	extern int ecard_loader_reset (volatile unsigned int pa, loader_t loader);
+
+	if (card >= ecard_numcards)
+		return;
+    
+	if (card < 0) {
+		for (card = 0; card < ecard_numcards; card++)
+			if (expcard[card].loader)
+				ecard_loader_reset (BUS_ADDR(expcard[card].podaddr),
+							expcard[card].loader);
+	} else
+		if (expcard[card].loader)
+			ecard_loader_reset (BUS_ADDR(expcard[card].podaddr),
+						expcard[card].loader);
+
+#ifdef HAS_EXPMASK
+	if (have_expmask) {
+		have_expmask |= ~0;
+		EXPMASK_ENABLE = have_expmask;
+	}
+#endif
+}
+
+static unsigned int ecard_startcard;
+
+void ecard_startfind (void)
+{
+	ecard_startcard = 0;
+}
+
+ecard_t *ecard_find (int cld, const card_ids *cids)
+{
+	int card;
+	if (!cids) {
+		for (card = ecard_startcard; card < ecard_numcards; card++)
+			if (!expcard[card].claimed &&
+			    ((expcard[card].cld.ecld ^ cld) & 0x78) == 0)
+				break;
+	} else {
+		for (card = ecard_startcard; card < ecard_numcards; card++) {
+			unsigned int manufacturer, product;
+			int i;
+
+			if (expcard[card].claimed)
+				continue;
+
+			manufacturer = expcard[card].cld.manufacturer;
+			product = expcard[card].cld.product;
+
+			for (i = 0; cids[i].manufacturer != 65535; i++)
+				if (manufacturer == cids[i].manufacturer &&
+				    product == cids[i].product)
+					break;
+
+			if (cids[i].manufacturer != 65535)
+				break;
+		}
+	}
+	ecard_startcard = card + 1;
+	return card < ecard_numcards ? &expcard[card] : NULL;
+}
+
+int ecard_readchunk (struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
+{
+	struct ex_chunk_dir excd;
+	int index = 16;
+	int useld = 0;
+
+	while(1) {
+		ecard_readbytes(&excd, ec, index, 8, useld);
+		index += 8;
+		if (c_id(&excd) == 0) {
+			if (!useld && ec->loader) {
+				useld = 1;
+				index = 0;
+				continue;
+			}
+			return 0;
+		}
+		if (c_id(&excd) == 0xf0) { /* link */
+			index = c_start(&excd);
+			continue;
+		}
+		if (c_id(&excd) == 0x80) { /* loader */
+			if (!ec->loader) {
+				ec->loader = (loader_t)ecard_malloc(c_len(&excd));
+				ecard_readbytes(ec->loader, ec, (int)c_start(&excd), c_len(&excd), useld);
+			}
+			continue;
+		}
+		if (c_id(&excd) == id && num-- == 0)
+			break;
+	}
+
+	if (c_id(&excd) & 0x80) {
+		switch (c_id(&excd) & 0x70) {
+		case 0x70:
+			ecard_readbytes((unsigned char *)excd.d.string, ec,
+					(int)c_start(&excd), c_len(&excd), useld);
+			break;
+		case 0x00:
+			break;
+		}
+	}
+	cd->start_offset = c_start(&excd);
+	memcpy (cd->d.string, excd.d.string, 256);
+	return 1;
+}
+
+unsigned int ecard_address (ecard_t *ec, card_type_t memc, card_speed_t speed)
+{
+	switch (ec->slot_no) {
+	case 0:
+	case 1:
+	case 2:
+	case 3:
+		return (memc ? MEMCECIO_BASE : IOCECIO_BASE + (speed << 17)) + (ec->slot_no << 12);
+#ifdef IOCEC4IO_BASE
+	case 4:
+	case 5:
+	case 6:
+	case 7:
+		return (memc ? 0 : IOCEC4IO_BASE + (speed << 17)) + ((ec->slot_no - 4) << 12);
+#endif
+#ifdef MEMCEC8IO_BASE
+	case 8:
+		return MEMCEC8IO_BASE;
+#endif
+	}
+	return 0;
+}
+
+/*
+ * Probe for an expansion card.
+ *
+ * If bit 1 of the first byte of the card is set,
+ * then the card does not exist.
+ */
+static int ecard_probe (int card, int freeslot)
+{
+	ecard_t *ec = expcard + freeslot;
+	struct ex_ecld excld;
+	const char *card_desc = NULL;
+	int i;
+
+	irqno_to_expcard[card] = -1;
+
+	ec->slot_no = card;
+	if ((ec->podaddr = ecard_address (ec, 0, ECARD_SYNC)) == 0)
+		return 0;
+
+	excld.r_ecld = 2;
+	ecard_readbytes (&excld, ec, 0, 16, 0);
+	if (excld.r_ecld & 2)
+		return 0;
+
+	irqno_to_expcard[card] = freeslot;
+
+	ec->irq = -1;
+	ec->fiq = -1;
+	ec->cld.ecld = e_ecld(&excld);
+	ec->cld.manufacturer = e_manu(&excld);
+	ec->cld.product = e_prod(&excld);
+	ec->cld.country = e_country(&excld);
+	ec->cld.fiqmask = e_fiqmask(&excld);
+	ec->cld.irqmask = e_irqmask(&excld);
+	ec->cld.fiqaddr = e_fiqaddr(&excld);
+	ec->cld.irqaddr = e_irqaddr(&excld);
+	ec->fiqaddr =
+	ec->irqaddr = (unsigned char *)BUS_ADDR(ec->podaddr);
+	ec->fiqmask = 4;
+	ec->irqmask = 1;
+	ec->ops = &ecard_default_ops;
+
+	for (i = 0; i < sizeof (blacklist) / sizeof (*blacklist); i++)
+		if (blacklist[i].manufacturer == ec->cld.manufacturer &&
+		    blacklist[i].product == ec->cld.product) {
+			ec->loader = blacklist[i].loader;
+			card_desc = blacklist[i].type;
+			break;
+		}
+
+	if (card != 8) {
+		ec->irq = 32 + card;
+#if 0
+		ec->fiq = 96 + card;
+#endif
+	} else {
+		ec->irq = 11;
+		ec->fiq = -1;
+	}
+
+	if ((ec->cld.ecld & 0x78) == 0) {
+		struct in_chunk_dir incd;
+		printk ("\n  %d: [%04X:%04X] ", card, ec->cld.manufacturer, ec->cld.product);
+		if (e_is (&excld)) {
+			ec->fiqmask = e_fiqmask (&excld);
+			ec->irqmask = e_irqmask (&excld);
+			ec->fiqaddr += e_fiqaddr (&excld);
+			ec->irqaddr += e_irqaddr (&excld);
+		}
+		if (!card_desc && e_cd (&excld) && ecard_readchunk (&incd, ec, 0xf5, 0))
+			card_desc = incd.d.string;
+		if (card_desc)
+			printk ("%s", card_desc);
+		else
+			printk ("*Unknown*");
+	} else
+		printk("\n  %d: Simple card %d\n", card, (ec->cld.ecld >> 3) & 15);
+	return 1;
+}
+
+static struct irqaction irqexpansioncard = { ecard_irq_noexpmask, SA_INTERRUPT, 0, "expansion cards", NULL, NULL };
+
+/*
+ * Initialise the expansion card system.
+ * Locate all hardware - interrupt management and
+ * actual cards.
+ */
+unsigned long ecard_init(unsigned long start_mem)
+{
+	int i, nc = 0;
+
+	kmem = (start_mem | 3) & ~3;
+	memset (expcard, 0, sizeof (expcard));
+
+#ifdef HAS_EXPMASK
+	if (ecard_checkirqhw()) {
+		printk (KERN_DEBUG "Expansion card interrupt management hardware found\n");
+		irqexpansioncard.handler = ecard_irq_expmask;
+		have_expmask = -1;
+	}
+#endif
+	printk("Installed expansion cards:");
+
+	/*
+	 * First of all, probe all cards on the expansion card interrupt line
+	 */
+	for (i = 0; i < 4; i++)
+		if (ecard_probe (i, nc))
+			nc += 1;
+		else
+			have_expmask &= ~(1<<i);
+
+	ecard_numirqcards = nc;
+
+	/*
+	 * Now probe other cards with different interrupt lines
+	 */
+#ifdef MEMCEC8IO_BASE
+	if (ecard_probe (8, nc))
+		nc += 1;
+#endif
+	printk("\n");
+	ecard_numcards = nc;
+
+	if (nc && setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
+		printk ("Could not allocate interrupt for expansion cards\n");
+		return kmem;
+	}
+	
+#ifdef HAS_EXPMASK
+	if (nc && have_expmask)
+		EXPMASK_ENABLE = have_expmask;
+#endif
+	oldlatch_init ();
+	start_mem = kmem;
+	kmem = 0;
+	return start_mem;	
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov