OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [rc203soc/] [sw/] [uClinux/] [arch/] [armnommu/] [kernel/] [ecard.c] - Diff between revs 1765 and 1782

Only display areas with differences | Details | Blame | View Log

Rev 1765 Rev 1782
/*
/*
 * linux/arch/arm/kernel/ecard.c
 * linux/arch/arm/kernel/ecard.c
 *
 *
 *  Find all installed expansion cards, and handle interrupts from them.
 *  Find all installed expansion cards, and handle interrupts from them.
 *
 *
 * Copyright 1995-1998 Russell King
 * Copyright 1995-1998 Russell King
 *
 *
 * Created from information from Acorns RiscOS3 PRMs
 * Created from information from Acorns RiscOS3 PRMs
 *
 *
 * 08-Dec-1996  RMK     Added code for the 9'th expansion card - the ether
 * 08-Dec-1996  RMK     Added code for the 9'th expansion card - the ether
 *                      podule slot.
 *                      podule slot.
 * 06-May-1997  RMK     Added blacklist for cards whose loader doesn't work.
 * 06-May-1997  RMK     Added blacklist for cards whose loader doesn't work.
 * 12-Sep-1997  RMK     Created new handling of interrupt enables/disables
 * 12-Sep-1997  RMK     Created new handling of interrupt enables/disables
 *                      - cards can now register their own routine to control
 *                      - cards can now register their own routine to control
 *                      interrupts (recommended).
 *                      interrupts (recommended).
 * 29-Sep-1997  RMK     Expansion card interrupt hardware not being re-enabled
 * 29-Sep-1997  RMK     Expansion card interrupt hardware not being re-enabled
 *                      on reset from Linux. (Caused cards not to respond
 *                      on reset from Linux. (Caused cards not to respond
 *                      under RiscOS without hard reset).
 *                      under RiscOS without hard reset).
 * 15-Feb-1998  RMK     Added DMA support
 * 15-Feb-1998  RMK     Added DMA support
 * 12-Sep-1998  RMK     Added EASI support
 * 12-Sep-1998  RMK     Added EASI support
 * 10-Jan-1999  RMK     Run loaders in a simulated RISC OS environment.
 * 10-Jan-1999  RMK     Run loaders in a simulated RISC OS environment.
 */
 */
 
 
#define ECARD_C
#define ECARD_C
#define __KERNEL_SYSCALLS__
#define __KERNEL_SYSCALLS__
 
 
#include <linux/config.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/malloc.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/unistd.h>
 
 
#include <asm/dma.h>
#include <asm/dma.h>
#include <asm/ecard.h>
#include <asm/ecard.h>
#include <asm/hardware.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/irq.h>
#include <asm/arch/irq.h>
#include <asm/arch/irq.h>
#include <asm/pgtable.h>
#include <asm/pgtable.h>
 
 
#ifdef CONFIG_ARCH_ARC
#ifdef CONFIG_ARCH_ARC
#include <asm/arch/oldlatches.h>
#include <asm/arch/oldlatches.h>
#else
#else
#define oldlatch_init()
#define oldlatch_init()
#endif
#endif
 
 
enum req {
enum req {
        req_readbytes,
        req_readbytes,
        req_reset
        req_reset
};
};
 
 
struct ecard_request {
struct ecard_request {
        enum req        req;
        enum req        req;
        ecard_t         *ec;
        ecard_t         *ec;
        unsigned int    address;
        unsigned int    address;
        unsigned int    length;
        unsigned int    length;
        unsigned int    use_loader;
        unsigned int    use_loader;
        void            *buffer;
        void            *buffer;
};
};
 
 
struct expcard_blacklist {
struct expcard_blacklist {
        unsigned short   manufacturer;
        unsigned short   manufacturer;
        unsigned short   product;
        unsigned short   product;
        const char      *type;
        const char      *type;
};
};
 
 
static pid_t ecard_pid;
static pid_t ecard_pid;
static struct wait_queue *ecard_wait;
static struct wait_queue *ecard_wait;
static struct wait_queue *ecard_done;
static struct wait_queue *ecard_done;
static struct ecard_request *ecard_req;
static struct ecard_request *ecard_req;
static struct task_struct *ecard_tsk;
static struct task_struct *ecard_tsk;
static ecard_t *cards;
static ecard_t *cards;
static ecard_t *slot_to_expcard[MAX_ECARDS];
static ecard_t *slot_to_expcard[MAX_ECARDS];
#ifdef HAS_EXPMASK
#ifdef HAS_EXPMASK
static unsigned int have_expmask;
static unsigned int have_expmask;
#endif
#endif
 
 
/* List of descriptions of cards which don't have an extended
/* List of descriptions of cards which don't have an extended
 * identification, or chunk directories containing a description.
 * identification, or chunk directories containing a description.
 */
 */
static const struct expcard_blacklist blacklist[] = {
static const struct expcard_blacklist blacklist[] = {
        { MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" }
        { MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" }
};
};
 
 
asmlinkage extern int ecard_loader_reset(volatile unsigned char *pa,
asmlinkage extern int ecard_loader_reset(volatile unsigned char *pa,
                                         loader_t loader);
                                         loader_t loader);
asmlinkage extern int ecard_loader_read(int off, volatile unsigned char *pa,
asmlinkage extern int ecard_loader_read(int off, volatile unsigned char *pa,
                                        loader_t loader);
                                        loader_t loader);
extern int setup_arm_irq(int, struct irqaction *);
extern int setup_arm_irq(int, struct irqaction *);
extern void do_fast_IRQ(int irq);
extern void do_fast_IRQ(int irq);
 
 
 
 
static void
static void
ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs);
ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs);
 
 
static struct irqaction irqexpansioncard = {
static struct irqaction irqexpansioncard = {
        ecard_irq_noexpmask, 0, 0, "expansion cards", NULL, NULL
        ecard_irq_noexpmask, 0, 0, "expansion cards", NULL, NULL
};
};
 
 
static inline unsigned short
static inline unsigned short
ecard_getu16(unsigned char *v)
ecard_getu16(unsigned char *v)
{
{
        return v[0] | v[1] << 8;
        return v[0] | v[1] << 8;
}
}
 
 
static inline signed long
static inline signed long
ecard_gets24(unsigned char *v)
ecard_gets24(unsigned char *v)
{
{
        return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
        return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
}
}
 
 
static inline ecard_t *
static inline ecard_t *
slot_to_ecard(unsigned int slot)
slot_to_ecard(unsigned int slot)
{
{
        return slot < MAX_ECARDS ? slot_to_expcard[slot] : NULL;
        return slot < MAX_ECARDS ? slot_to_expcard[slot] : NULL;
}
}
 
 
/* ===================== Expansion card daemon ======================== */
/* ===================== Expansion card daemon ======================== */
/*
/*
 * Since the loader programs on the expansion cards need to be run
 * Since the loader programs on the expansion cards need to be run
 * in a specific environment, create a separate task with this
 * in a specific environment, create a separate task with this
 * environment up, and pass requests to this task as and when we
 * environment up, and pass requests to this task as and when we
 * need to.
 * need to.
 *
 *
 * This should allow 99% of loaders to be called from Linux.
 * This should allow 99% of loaders to be called from Linux.
 *
 *
 * From a security standpoint, we trust the card vendors.  This
 * From a security standpoint, we trust the card vendors.  This
 * may be a misplaced trust.
 * may be a misplaced trust.
 */
 */
#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
#define POD_INT_ADDR(x) ((volatile unsigned char *)\
#define POD_INT_ADDR(x) ((volatile unsigned char *)\
                         ((BUS_ADDR((x)) - IO_BASE) + IO_START))
                         ((BUS_ADDR((x)) - IO_BASE) + IO_START))
 
 
static void
static void
ecard_task_reset(struct ecard_request *req)
ecard_task_reset(struct ecard_request *req)
{
{
        if (req->ec == NULL) {
        if (req->ec == NULL) {
                ecard_t *ec;
                ecard_t *ec;
 
 
                for (ec = cards; ec; ec = ec->next) {
                for (ec = cards; ec; ec = ec->next) {
                        printk(KERN_DEBUG "Resetting card %d\n",
                        printk(KERN_DEBUG "Resetting card %d\n",
                               ec->slot_no);
                               ec->slot_no);
 
 
                        if (ec->loader)
                        if (ec->loader)
                                ecard_loader_reset(POD_INT_ADDR(ec->podaddr),
                                ecard_loader_reset(POD_INT_ADDR(ec->podaddr),
                                                   ec->loader);
                                                   ec->loader);
                }
                }
                printk(KERN_DEBUG "All cards reset\n");
                printk(KERN_DEBUG "All cards reset\n");
        } else if (req->ec->loader)
        } else if (req->ec->loader)
                ecard_loader_reset(POD_INT_ADDR(req->ec->podaddr),
                ecard_loader_reset(POD_INT_ADDR(req->ec->podaddr),
                                   req->ec->loader);
                                   req->ec->loader);
}
}
 
 
static void
static void
ecard_task_readbytes(struct ecard_request *req)
ecard_task_readbytes(struct ecard_request *req)
{
{
        unsigned char *buf = (unsigned char *)req->buffer;
        unsigned char *buf = (unsigned char *)req->buffer;
        volatile unsigned char *base_addr =
        volatile unsigned char *base_addr =
                (volatile unsigned char *)POD_INT_ADDR(req->ec->podaddr);
                (volatile unsigned char *)POD_INT_ADDR(req->ec->podaddr);
        unsigned int len = req->length;
        unsigned int len = req->length;
 
 
        if (req->ec->slot_no == 8) {
        if (req->ec->slot_no == 8) {
                /*
                /*
                 * The card maintains an index which
                 * The card maintains an index which
                 * increments the address into a 4096-byte
                 * increments the address into a 4096-byte
                 * page on each access.  We need to keep
                 * page on each access.  We need to keep
                 * track of the counter.
                 * track of the counter.
                 */
                 */
                static unsigned int index;
                static unsigned int index;
                unsigned int offset, page;
                unsigned int offset, page;
                unsigned char byte = 0; /* keep gcc quiet */
                unsigned char byte = 0; /* keep gcc quiet */
 
 
                offset = req->address & 4095;
                offset = req->address & 4095;
                page   = req->address >> 12;
                page   = req->address >> 12;
 
 
                if (page > 256)
                if (page > 256)
                        return;
                        return;
 
 
                page *= 4;
                page *= 4;
 
 
                if (offset == 0 || index > offset) {
                if (offset == 0 || index > offset) {
                        /*
                        /*
                         * We need to reset the index counter.
                         * We need to reset the index counter.
                         */
                         */
                        *base_addr = 0;
                        *base_addr = 0;
                        index = 0;
                        index = 0;
                }
                }
 
 
                while (index <= offset) {
                while (index <= offset) {
                        byte = base_addr[page];
                        byte = base_addr[page];
                        index += 1;
                        index += 1;
                }
                }
 
 
                while (len--) {
                while (len--) {
                        *buf++ = byte;
                        *buf++ = byte;
                        if (len) {
                        if (len) {
                                byte = base_addr[page];
                                byte = base_addr[page];
                                index += 1;
                                index += 1;
                        }
                        }
                }
                }
        } else {
        } else {
                unsigned int off = req->address;
                unsigned int off = req->address;
 
 
                if (!req->use_loader || !req->ec->loader) {
                if (!req->use_loader || !req->ec->loader) {
                        off *= 4;
                        off *= 4;
                        while (len--) {
                        while (len--) {
                                *buf++ = base_addr[off];
                                *buf++ = base_addr[off];
                                off += 4;
                                off += 4;
                        }
                        }
                } else {
                } else {
                        while(len--) {
                        while(len--) {
                                /*
                                /*
                                 * The following is required by some
                                 * The following is required by some
                                 * expansion card loader programs.
                                 * expansion card loader programs.
                                 */
                                 */
                                *(unsigned long *)0x108 = 0;
                                *(unsigned long *)0x108 = 0;
                                *buf++ = ecard_loader_read(off++, base_addr,
                                *buf++ = ecard_loader_read(off++, base_addr,
                                                           req->ec->loader);
                                                           req->ec->loader);
                        }
                        }
                }
                }
        }
        }
 
 
}
}
 
 
#if !defined(CONFIG_CPU_ARM2) && !defined(CONFIG_CPU_ARM3)
#if !defined(CONFIG_CPU_ARM2) && !defined(CONFIG_CPU_ARM3)
/*
/*
 * Set up the expansion card daemon's environment.
 * Set up the expansion card daemon's environment.
 */
 */
static void
static void
ecard_init_task(void)
ecard_init_task(void)
{
{
        /* We want to set up the page tables for the following mapping:
        /* We want to set up the page tables for the following mapping:
         *  Virtual     Physical
         *  Virtual     Physical
         *  0x03000000  0x03000000
         *  0x03000000  0x03000000
         *  0x03010000  unmapped
         *  0x03010000  unmapped
         *  0x03210000  0x03210000
         *  0x03210000  0x03210000
         *  0x03400000  unmapped
         *  0x03400000  unmapped
         *  0x08000000  0x08000000
         *  0x08000000  0x08000000
         *  0x10000000  unmapped
         *  0x10000000  unmapped
         *
         *
         * FIXME: we don't follow this 100% yet.
         * FIXME: we don't follow this 100% yet.
         */
         */
        pgd_t *src_pgd, *dst_pgd;
        pgd_t *src_pgd, *dst_pgd;
        unsigned int dst_addr = IO_START;
        unsigned int dst_addr = IO_START;
 
 
        src_pgd = pgd_offset(current->mm, IO_BASE);
        src_pgd = pgd_offset(current->mm, IO_BASE);
        dst_pgd = pgd_offset(current->mm, dst_addr);
        dst_pgd = pgd_offset(current->mm, dst_addr);
 
 
        while (dst_addr < IO_START + IO_SIZE) {
        while (dst_addr < IO_START + IO_SIZE) {
                *dst_pgd++ = *src_pgd++;
                *dst_pgd++ = *src_pgd++;
                dst_addr += PGDIR_SIZE;
                dst_addr += PGDIR_SIZE;
        }
        }
 
 
        flush_tlb_range(current->mm, IO_START, IO_START + IO_SIZE);
        flush_tlb_range(current->mm, IO_START, IO_START + IO_SIZE);
 
 
        dst_addr = EASI_START;
        dst_addr = EASI_START;
        src_pgd = pgd_offset(current->mm, EASI_BASE);
        src_pgd = pgd_offset(current->mm, EASI_BASE);
        dst_pgd = pgd_offset(current->mm, dst_addr);
        dst_pgd = pgd_offset(current->mm, dst_addr);
 
 
        while (dst_addr < EASI_START + EASI_SIZE) {
        while (dst_addr < EASI_START + EASI_SIZE) {
                *dst_pgd++ = *src_pgd++;
                *dst_pgd++ = *src_pgd++;
                dst_addr += PGDIR_SIZE;
                dst_addr += PGDIR_SIZE;
        }
        }
 
 
        flush_tlb_range(current->mm, EASI_START, EASI_START + EASI_SIZE);
        flush_tlb_range(current->mm, EASI_START, EASI_START + EASI_SIZE);
}
}
 
 
static int
static int
ecard_task(void * unused)
ecard_task(void * unused)
{
{
        ecard_tsk = current;
        ecard_tsk = current;
 
 
        current->session = 1;
        current->session = 1;
        current->pgrp = 1;
        current->pgrp = 1;
 
 
        /*
        /*
         * We don't want /any/ signals, not even SIGKILL
         * We don't want /any/ signals, not even SIGKILL
         */
         */
        current->blocked = -1;
        current->blocked = -1;
        current->signal = 0;
        current->signal = 0;
 
 
        strcpy(current->comm, "kecardd");
        strcpy(current->comm, "kecardd");
 
 
        /*
        /*
         * Set up the environment
         * Set up the environment
         */
         */
        ecard_init_task();
        ecard_init_task();
 
 
        while (1) {
        while (1) {
                struct ecard_request *req;
                struct ecard_request *req;
 
 
                do {
                do {
                        req = xchg(&ecard_req, NULL);
                        req = xchg(&ecard_req, NULL);
 
 
                        if (req == NULL) {
                        if (req == NULL) {
                                current->signal = 0;
                                current->signal = 0;
                                interruptible_sleep_on(&ecard_wait);
                                interruptible_sleep_on(&ecard_wait);
                        }
                        }
                } while (req == NULL);
                } while (req == NULL);
 
 
                switch (req->req) {
                switch (req->req) {
                case req_readbytes:
                case req_readbytes:
                        ecard_task_readbytes(req);
                        ecard_task_readbytes(req);
                        break;
                        break;
 
 
                case req_reset:
                case req_reset:
                        ecard_task_reset(req);
                        ecard_task_reset(req);
                        break;
                        break;
                }
                }
                wake_up(&ecard_done);
                wake_up(&ecard_done);
        }
        }
}
}
 
 
/*
/*
 * Wake the expansion card daemon to action our request.
 * Wake the expansion card daemon to action our request.
 *
 *
 * FIXME: The test here is not sufficient to detect if the
 * FIXME: The test here is not sufficient to detect if the
 * kcardd is running.
 * kcardd is running.
 */
 */
static inline void
static inline void
ecard_call(struct ecard_request *req)
ecard_call(struct ecard_request *req)
{
{
        /*
        /*
         * If we're called from task 0, or from an
         * If we're called from task 0, or from an
         * interrupt (will be keyboard interrupt),
         * interrupt (will be keyboard interrupt),
         * we forcefully set up the memory map, and
         * we forcefully set up the memory map, and
         * call the loader.  We can't schedule, or
         * call the loader.  We can't schedule, or
         * sleep for this call.
         * sleep for this call.
         */
         */
        if ((current == task[0] || intr_count == 1) &&
        if ((current == task[0] || intr_count == 1) &&
            req->req == req_reset && req->ec == NULL) {
            req->req == req_reset && req->ec == NULL) {
                ecard_init_task();
                ecard_init_task();
                ecard_task_reset(req);
                ecard_task_reset(req);
        } else {
        } else {
                if (ecard_pid <= 0)
                if (ecard_pid <= 0)
                        ecard_pid = kernel_thread(ecard_task, NULL, 0);
                        ecard_pid = kernel_thread(ecard_task, NULL, 0);
 
 
                ecard_req = req;
                ecard_req = req;
 
 
                wake_up(&ecard_wait);
                wake_up(&ecard_wait);
 
 
                sleep_on(&ecard_done);
                sleep_on(&ecard_done);
        }
        }
}
}
#else
#else
/*
/*
 * On 26-bit processors, we don't need the kcardd thread to access the
 * On 26-bit processors, we don't need the kcardd thread to access the
 * expansion card loaders.  We do it directly.
 * expansion card loaders.  We do it directly.
 */
 */
static inline void
static inline void
ecard_call(struct ecard_request *req)
ecard_call(struct ecard_request *req)
{
{
        if (req->req == req_reset)
        if (req->req == req_reset)
                ecard_task_reset(req);
                ecard_task_reset(req);
        else
        else
                ecard_task_readbytes(req);
                ecard_task_readbytes(req);
}
}
#endif
#endif
 
 
/* ======================= Mid-level card control ===================== */
/* ======================= Mid-level card control ===================== */
/*
/*
 * This is called to reset the loaders for each expansion card on reboot.
 * 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
 * This is required to make sure that the card is in the correct state
 * that RiscOS expects it to be.
 * that RiscOS expects it to be.
 */
 */
void
void
ecard_reset(int slot)
ecard_reset(int slot)
{
{
        struct ecard_request req;
        struct ecard_request req;
 
 
        req.req = req_reset;
        req.req = req_reset;
 
 
        if (slot < 0)
        if (slot < 0)
                req.ec = NULL;
                req.ec = NULL;
        else
        else
                req.ec = slot_to_ecard(slot);
                req.ec = slot_to_ecard(slot);
 
 
        ecard_call(&req);
        ecard_call(&req);
 
 
#ifdef HAS_EXPMASK
#ifdef HAS_EXPMASK
        if (have_expmask && slot < 0) {
        if (have_expmask && slot < 0) {
                have_expmask |= ~0;
                have_expmask |= ~0;
                EXPMASK_ENABLE = have_expmask;
                EXPMASK_ENABLE = have_expmask;
        }
        }
#endif
#endif
}
}
 
 
static void
static void
ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
{
{
        struct ecard_request req;
        struct ecard_request req;
 
 
        req.req         = req_readbytes;
        req.req         = req_readbytes;
        req.ec          = ec;
        req.ec          = ec;
        req.address     = off;
        req.address     = off;
        req.length      = len;
        req.length      = len;
        req.use_loader  = useld;
        req.use_loader  = useld;
        req.buffer      = addr;
        req.buffer      = addr;
 
 
        ecard_call(&req);
        ecard_call(&req);
}
}
 
 
int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
{
{
        struct ex_chunk_dir excd;
        struct ex_chunk_dir excd;
        int index = 16;
        int index = 16;
        int useld = 0;
        int useld = 0;
 
 
        if (!ec->cid.cd)
        if (!ec->cid.cd)
                return 0;
                return 0;
 
 
        while(1) {
        while(1) {
                ecard_readbytes(&excd, ec, index, 8, useld);
                ecard_readbytes(&excd, ec, index, 8, useld);
                index += 8;
                index += 8;
                if (c_id(&excd) == 0) {
                if (c_id(&excd) == 0) {
                        if (!useld && ec->loader) {
                        if (!useld && ec->loader) {
                                useld = 1;
                                useld = 1;
                                index = 0;
                                index = 0;
                                continue;
                                continue;
                        }
                        }
                        return 0;
                        return 0;
                }
                }
                if (c_id(&excd) == 0xf0) { /* link */
                if (c_id(&excd) == 0xf0) { /* link */
                        index = c_start(&excd);
                        index = c_start(&excd);
                        continue;
                        continue;
                }
                }
                if (c_id(&excd) == 0x80) { /* loader */
                if (c_id(&excd) == 0x80) { /* loader */
                        if (!ec->loader) {
                        if (!ec->loader) {
                                ec->loader = (loader_t)kmalloc(c_len(&excd),
                                ec->loader = (loader_t)kmalloc(c_len(&excd),
                                                               GFP_KERNEL);
                                                               GFP_KERNEL);
                                ecard_readbytes(ec->loader, ec,
                                ecard_readbytes(ec->loader, ec,
                                                (int)c_start(&excd),
                                                (int)c_start(&excd),
                                                c_len(&excd), useld);
                                                c_len(&excd), useld);
                        }
                        }
                        continue;
                        continue;
                }
                }
                if (c_id(&excd) == id && num-- == 0)
                if (c_id(&excd) == id && num-- == 0)
                        break;
                        break;
        }
        }
 
 
        if (c_id(&excd) & 0x80) {
        if (c_id(&excd) & 0x80) {
                switch (c_id(&excd) & 0x70) {
                switch (c_id(&excd) & 0x70) {
                case 0x70:
                case 0x70:
                        ecard_readbytes((unsigned char *)excd.d.string, ec,
                        ecard_readbytes((unsigned char *)excd.d.string, ec,
                                        (int)c_start(&excd), c_len(&excd),
                                        (int)c_start(&excd), c_len(&excd),
                                        useld);
                                        useld);
                        break;
                        break;
                case 0x00:
                case 0x00:
                        break;
                        break;
                }
                }
        }
        }
        cd->start_offset = c_start(&excd);
        cd->start_offset = c_start(&excd);
        memcpy(cd->d.string, excd.d.string, 256);
        memcpy(cd->d.string, excd.d.string, 256);
        return 1;
        return 1;
}
}
 
 
/* ======================= Interrupt control ============================ */
/* ======================= Interrupt control ============================ */
 
 
static void ecard_def_irq_enable(ecard_t *ec, int irqnr)
static void ecard_def_irq_enable(ecard_t *ec, int irqnr)
{
{
#ifdef HAS_EXPMASK
#ifdef HAS_EXPMASK
        if (irqnr < 4 && have_expmask) {
        if (irqnr < 4 && have_expmask) {
                have_expmask |= 1 << irqnr;
                have_expmask |= 1 << irqnr;
                EXPMASK_ENABLE = have_expmask;
                EXPMASK_ENABLE = have_expmask;
        }
        }
#endif
#endif
}
}
 
 
static void ecard_def_irq_disable(ecard_t *ec, int irqnr)
static void ecard_def_irq_disable(ecard_t *ec, int irqnr)
{
{
#ifdef HAS_EXPMASK
#ifdef HAS_EXPMASK
        if (irqnr < 4 && have_expmask) {
        if (irqnr < 4 && have_expmask) {
                have_expmask &= ~(1 << irqnr);
                have_expmask &= ~(1 << irqnr);
                EXPMASK_ENABLE = have_expmask;
                EXPMASK_ENABLE = have_expmask;
        }
        }
#endif
#endif
}
}
 
 
static int ecard_def_irq_pending(ecard_t *ec)
static int ecard_def_irq_pending(ecard_t *ec)
{
{
        return !ec->irqmask || ec->irqaddr[0] & ec->irqmask;
        return !ec->irqmask || ec->irqaddr[0] & ec->irqmask;
}
}
 
 
static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr)
static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr)
{
{
        panic("ecard_def_fiq_enable called - impossible");
        panic("ecard_def_fiq_enable called - impossible");
}
}
 
 
static void ecard_def_fiq_disable(ecard_t *ec, int fiqnr)
static void ecard_def_fiq_disable(ecard_t *ec, int fiqnr)
{
{
        panic("ecard_def_fiq_disable called - impossible");
        panic("ecard_def_fiq_disable called - impossible");
}
}
 
 
static int ecard_def_fiq_pending(ecard_t *ec)
static int ecard_def_fiq_pending(ecard_t *ec)
{
{
        return !ec->fiqmask || ec->fiqaddr[0] & ec->fiqmask;
        return !ec->fiqmask || ec->fiqaddr[0] & ec->fiqmask;
}
}
 
 
static expansioncard_ops_t ecard_default_ops = {
static expansioncard_ops_t ecard_default_ops = {
        ecard_def_irq_enable,
        ecard_def_irq_enable,
        ecard_def_irq_disable,
        ecard_def_irq_disable,
        ecard_def_irq_pending,
        ecard_def_irq_pending,
        ecard_def_fiq_enable,
        ecard_def_fiq_enable,
        ecard_def_fiq_disable,
        ecard_def_fiq_disable,
        ecard_def_fiq_pending
        ecard_def_fiq_pending
};
};
 
 
/*
/*
 * Enable and disable interrupts from expansion cards.
 * Enable and disable interrupts from expansion cards.
 * (interrupts are disabled for these functions).
 * (interrupts are disabled for these functions).
 *
 *
 * They are not meant to be called directly, but via enable/disable_irq.
 * They are not meant to be called directly, but via enable/disable_irq.
 */
 */
void ecard_enableirq(unsigned int irqnr)
void ecard_enableirq(unsigned int irqnr)
{
{
        ecard_t *ec = slot_to_ecard(irqnr);
        ecard_t *ec = slot_to_ecard(irqnr);
 
 
        if (ec) {
        if (ec) {
                if (!ec->ops)
                if (!ec->ops)
                        ec->ops = &ecard_default_ops;
                        ec->ops = &ecard_default_ops;
 
 
                if (ec->claimed && ec->ops->irqenable)
                if (ec->claimed && ec->ops->irqenable)
                        ec->ops->irqenable(ec, irqnr);
                        ec->ops->irqenable(ec, irqnr);
                else
                else
                        printk(KERN_ERR "ecard: rejecting request to "
                        printk(KERN_ERR "ecard: rejecting request to "
                                "enable IRQs for %d\n", irqnr);
                                "enable IRQs for %d\n", irqnr);
        }
        }
}
}
 
 
void ecard_disableirq(unsigned int irqnr)
void ecard_disableirq(unsigned int irqnr)
{
{
        ecard_t *ec = slot_to_ecard(irqnr);
        ecard_t *ec = slot_to_ecard(irqnr);
 
 
        if (ec) {
        if (ec) {
                if (!ec->ops)
                if (!ec->ops)
                        ec->ops = &ecard_default_ops;
                        ec->ops = &ecard_default_ops;
 
 
                if (ec->ops && ec->ops->irqdisable)
                if (ec->ops && ec->ops->irqdisable)
                        ec->ops->irqdisable(ec, irqnr);
                        ec->ops->irqdisable(ec, irqnr);
        }
        }
}
}
 
 
void ecard_enablefiq(unsigned int fiqnr)
void ecard_enablefiq(unsigned int fiqnr)
{
{
        ecard_t *ec = slot_to_ecard(fiqnr);
        ecard_t *ec = slot_to_ecard(fiqnr);
 
 
        if (ec) {
        if (ec) {
                if (!ec->ops)
                if (!ec->ops)
                        ec->ops = &ecard_default_ops;
                        ec->ops = &ecard_default_ops;
 
 
                if (ec->claimed && ec->ops->fiqenable)
                if (ec->claimed && ec->ops->fiqenable)
                        ec->ops->fiqenable(ec, fiqnr);
                        ec->ops->fiqenable(ec, fiqnr);
                else
                else
                        printk(KERN_ERR "ecard: rejecting request to "
                        printk(KERN_ERR "ecard: rejecting request to "
                                "enable FIQs for %d\n", fiqnr);
                                "enable FIQs for %d\n", fiqnr);
        }
        }
}
}
 
 
void ecard_disablefiq(unsigned int fiqnr)
void ecard_disablefiq(unsigned int fiqnr)
{
{
        ecard_t *ec = slot_to_ecard(fiqnr);
        ecard_t *ec = slot_to_ecard(fiqnr);
 
 
        if (ec) {
        if (ec) {
                if (!ec->ops)
                if (!ec->ops)
                        ec->ops = &ecard_default_ops;
                        ec->ops = &ecard_default_ops;
 
 
                if (ec->ops->fiqdisable)
                if (ec->ops->fiqdisable)
                        ec->ops->fiqdisable(ec, fiqnr);
                        ec->ops->fiqdisable(ec, fiqnr);
        }
        }
}
}
 
 
static void
static void
ecard_dump_irq_state(ecard_t *ec)
ecard_dump_irq_state(ecard_t *ec)
{
{
        printk("  %d: %sclaimed, ",
        printk("  %d: %sclaimed, ",
               ec->slot_no,
               ec->slot_no,
               ec->claimed ? "" : "not ");
               ec->claimed ? "" : "not ");
 
 
        if (ec->ops && ec->ops->irqpending &&
        if (ec->ops && ec->ops->irqpending &&
            ec->ops != &ecard_default_ops)
            ec->ops != &ecard_default_ops)
                printk("irq %spending\n",
                printk("irq %spending\n",
                       ec->ops->irqpending(ec) ? "" : "not ");
                       ec->ops->irqpending(ec) ? "" : "not ");
        else
        else
                printk("irqaddr %p, mask = %02X, status = %02X\n",
                printk("irqaddr %p, mask = %02X, status = %02X\n",
                       ec->irqaddr, ec->irqmask, *ec->irqaddr);
                       ec->irqaddr, ec->irqmask, *ec->irqaddr);
}
}
 
 
static void
static void
ecard_check_lockup(void)
ecard_check_lockup(void)
{
{
        static int last, lockup;
        static int last, lockup;
        ecard_t *ec;
        ecard_t *ec;
 
 
        /*
        /*
         * If the timer interrupt has not run since the last million
         * If the timer interrupt has not run since the last million
         * unrecognised expansion card interrupts, then there is
         * unrecognised expansion card interrupts, then there is
         * something seriously wrong.  Disable the expansion card
         * something seriously wrong.  Disable the expansion card
         * interrupts so at least we can continue.
         * interrupts so at least we can continue.
         *
         *
         * Maybe we ought to start a timer to re-enable them some time
         * Maybe we ought to start a timer to re-enable them some time
         * later?
         * later?
         */
         */
        if (last == jiffies) {
        if (last == jiffies) {
                lockup += 1;
                lockup += 1;
                if (lockup > 1000000) {
                if (lockup > 1000000) {
                        printk(KERN_ERR "\nInterrupt lockup detected - "
                        printk(KERN_ERR "\nInterrupt lockup detected - "
                               "disabling all expansion card interrupts\n");
                               "disabling all expansion card interrupts\n");
 
 
                        disable_irq(IRQ_EXPANSIONCARD);
                        disable_irq(IRQ_EXPANSIONCARD);
 
 
                        printk("Expansion card IRQ state:\n");
                        printk("Expansion card IRQ state:\n");
 
 
                        for (ec = cards; ec; ec = ec->next)
                        for (ec = cards; ec; ec = ec->next)
                                ecard_dump_irq_state(ec);
                                ecard_dump_irq_state(ec);
                }
                }
        } else
        } else
                lockup = 0;
                lockup = 0;
 
 
        /*
        /*
         * If we did not recognise the source of this interrupt,
         * If we did not recognise the source of this interrupt,
         * warn the user, but don't flood the user with these messages.
         * warn the user, but don't flood the user with these messages.
         */
         */
        if (!last || last + 500 < jiffies) {
        if (!last || last + 500 < jiffies) {
                last = jiffies;
                last = jiffies;
                printk(KERN_WARNING "Unrecognised interrupt from backplane\n");
                printk(KERN_WARNING "Unrecognised interrupt from backplane\n");
        }
        }
}
}
 
 
static void
static void
ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
{
{
        ecard_t *ec;
        ecard_t *ec;
        int called = 0;
        int called = 0;
 
 
        mask_irq(IRQ_EXPANSIONCARD);
        mask_irq(IRQ_EXPANSIONCARD);
        for (ec = cards; ec; ec = ec->next) {
        for (ec = cards; ec; ec = ec->next) {
                int pending;
                int pending;
 
 
                if (!ec->claimed || ec->irq == NO_IRQ || ec->slot_no == 8)
                if (!ec->claimed || ec->irq == NO_IRQ || ec->slot_no == 8)
                        continue;
                        continue;
 
 
                if (ec->ops && ec->ops->irqpending)
                if (ec->ops && ec->ops->irqpending)
                        pending = ec->ops->irqpending(ec);
                        pending = ec->ops->irqpending(ec);
                else
                else
                        pending = ecard_default_ops.irqpending(ec);
                        pending = ecard_default_ops.irqpending(ec);
 
 
                if (pending) {
                if (pending) {
                        do_fast_IRQ(ec->irq);
                        do_fast_IRQ(ec->irq);
                        called ++;
                        called ++;
                }
                }
        }
        }
        cli();
        cli();
        unmask_irq(IRQ_EXPANSIONCARD);
        unmask_irq(IRQ_EXPANSIONCARD);
 
 
        if (called == 0)
        if (called == 0)
                ecard_check_lockup();
                ecard_check_lockup();
}
}
 
 
#ifdef HAS_EXPMASK
#ifdef HAS_EXPMASK
static unsigned char priority_masks[] =
static unsigned char priority_masks[] =
{
{
        0xf0, 0xf1, 0xf3, 0xf7, 0xff, 0xff, 0xff, 0xff
        0xf0, 0xf1, 0xf3, 0xf7, 0xff, 0xff, 0xff, 0xff
};
};
 
 
static unsigned char first_set[] =
static unsigned char first_set[] =
{
{
        0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
        0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
        0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00
        0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00
};
};
 
 
static void
static void
ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs)
ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs)
{
{
        const unsigned int statusmask = 15;
        const unsigned int statusmask = 15;
        unsigned int status;
        unsigned int status;
 
 
        status = EXPMASK_STATUS & statusmask;
        status = EXPMASK_STATUS & statusmask;
        if (status) {
        if (status) {
                unsigned int slot;
                unsigned int slot;
                ecard_t *ec;
                ecard_t *ec;
again:
again:
                slot = first_set[status];
                slot = first_set[status];
                ec = slot_to_ecard(slot);
                ec = slot_to_ecard(slot);
                if (ec->claimed) {
                if (ec->claimed) {
                        unsigned int oldexpmask;
                        unsigned int oldexpmask;
                        /*
                        /*
                         * this ugly code is so that we can operate a
                         * this ugly code is so that we can operate a
                         * prioritorising system:
                         * prioritorising system:
                         *
                         *
                         * Card 0       highest priority
                         * Card 0       highest priority
                         * Card 1
                         * Card 1
                         * Card 2
                         * Card 2
                         * Card 3       lowest priority
                         * Card 3       lowest priority
                         *
                         *
                         * Serial cards should go in 0/1, ethernet/scsi in 2/3
                         * Serial cards should go in 0/1, ethernet/scsi in 2/3
                         * otherwise you will lose serial data at high speeds!
                         * otherwise you will lose serial data at high speeds!
                         */
                         */
                        oldexpmask = have_expmask;
                        oldexpmask = have_expmask;
                        EXPMASK_ENABLE = (have_expmask &= priority_masks[slot]);
                        EXPMASK_ENABLE = (have_expmask &= priority_masks[slot]);
                        sti();
                        sti();
                        do_fast_IRQ(ec->irq);
                        do_fast_IRQ(ec->irq);
                        cli();
                        cli();
                        EXPMASK_ENABLE = have_expmask = oldexpmask;
                        EXPMASK_ENABLE = have_expmask = oldexpmask;
                        status = EXPMASK_STATUS & statusmask;
                        status = EXPMASK_STATUS & statusmask;
                        if (status)
                        if (status)
                                goto again;
                                goto again;
                } else {
                } else {
                        printk(KERN_WARNING "card%d: interrupt from unclaimed "
                        printk(KERN_WARNING "card%d: interrupt from unclaimed "
                               "card???\n", slot);
                               "card???\n", slot);
                        EXPMASK_ENABLE = (have_expmask &= ~(1 << slot));
                        EXPMASK_ENABLE = (have_expmask &= ~(1 << slot));
                }
                }
        } else
        } else
                printk(KERN_WARNING "Wild interrupt from backplane (masks)\n");
                printk(KERN_WARNING "Wild interrupt from backplane (masks)\n");
}
}
 
 
static void
static void
ecard_probeirqhw(void)
ecard_probeirqhw(void)
{
{
        ecard_t *ec;
        ecard_t *ec;
        int found;
        int found;
 
 
        EXPMASK_ENABLE = 0x00;
        EXPMASK_ENABLE = 0x00;
        EXPMASK_STATUS = 0xff;
        EXPMASK_STATUS = 0xff;
        found = ((EXPMASK_STATUS & 15) == 0);
        found = ((EXPMASK_STATUS & 15) == 0);
        EXPMASK_ENABLE = 0xff;
        EXPMASK_ENABLE = 0xff;
 
 
        if (!found)
        if (!found)
                return;
                return;
 
 
        printk(KERN_DEBUG "Expansion card interrupt "
        printk(KERN_DEBUG "Expansion card interrupt "
               "management hardware found\n");
               "management hardware found\n");
 
 
        irqexpansioncard.handler = ecard_irq_expmask;
        irqexpansioncard.handler = ecard_irq_expmask;
 
 
        /* for each card present, set a bit to '1' */
        /* for each card present, set a bit to '1' */
        have_expmask = 0x80000000;
        have_expmask = 0x80000000;
 
 
        for (ec = cards; ec; ec = ec->next)
        for (ec = cards; ec; ec = ec->next)
                have_expmask |= 1 << ec->slot_no;
                have_expmask |= 1 << ec->slot_no;
 
 
        EXPMASK_ENABLE = have_expmask;
        EXPMASK_ENABLE = have_expmask;
}
}
#else
#else
#define ecard_probeirqhw()
#define ecard_probeirqhw()
#endif
#endif
 
 
unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
{
{
        switch (ec->slot_no) {
        switch (ec->slot_no) {
        case 0 ... 3:
        case 0 ... 3:
                switch (type) {
                switch (type) {
                case ECARD_MEMC:
                case ECARD_MEMC:
                        return IO_EC_MEMC_BASE + (ec->slot_no << 12);
                        return IO_EC_MEMC_BASE + (ec->slot_no << 12);
 
 
                case ECARD_IOC:
                case ECARD_IOC:
                        return IO_EC_IOC_BASE + (speed << 17) +
                        return IO_EC_IOC_BASE + (speed << 17) +
                                (ec->slot_no << 12);
                                (ec->slot_no << 12);
 
 
#ifdef IO_EC_EASI_BASE
#ifdef IO_EC_EASI_BASE
                case ECARD_EASI:
                case ECARD_EASI:
                        return IO_EC_EASI_BASE + (ec->slot_no << 22);
                        return IO_EC_EASI_BASE + (ec->slot_no << 22);
#endif
#endif
                }
                }
                break;
                break;
 
 
        case 4 ... 7:
        case 4 ... 7:
                switch (type) {
                switch (type) {
#ifdef IO_EC_IOC4_BASE
#ifdef IO_EC_IOC4_BASE
                case ECARD_IOC:
                case ECARD_IOC:
                        return IO_EC_IOC4_BASE + (speed << 17) +
                        return IO_EC_IOC4_BASE + (speed << 17) +
                                ((ec->slot_no - 4) << 12);
                                ((ec->slot_no - 4) << 12);
#endif
#endif
#ifdef IO_EC_EASI_BASE
#ifdef IO_EC_EASI_BASE
                case ECARD_EASI:
                case ECARD_EASI:
                        return IO_EC_EASI_BASE + (ec->slot_no << 22);
                        return IO_EC_EASI_BASE + (ec->slot_no << 22);
#endif
#endif
                default:
                default:
                        break;
                        break;
                }
                }
                break;
                break;
 
 
#ifdef IO_EC_MEMC8_BASE
#ifdef IO_EC_MEMC8_BASE
        case 8:
        case 8:
                return IO_EC_MEMC8_BASE;
                return IO_EC_MEMC8_BASE;
#endif
#endif
        }
        }
        return 0;
        return 0;
}
}
 
 
static int ecard_prints(char *buffer, ecard_t *ec)
static int ecard_prints(char *buffer, ecard_t *ec)
{
{
        char *start = buffer;
        char *start = buffer;
 
 
        buffer += sprintf(buffer, "  %d: %s ", ec->slot_no,
        buffer += sprintf(buffer, "  %d: %s ", ec->slot_no,
                          ec->type == ECARD_EASI ? "EASI" : "    ");
                          ec->type == ECARD_EASI ? "EASI" : "    ");
 
 
        if (ec->cid.id == 0) {
        if (ec->cid.id == 0) {
                struct in_chunk_dir incd;
                struct in_chunk_dir incd;
 
 
                buffer += sprintf(buffer, "[%04X:%04X] ",
                buffer += sprintf(buffer, "[%04X:%04X] ",
                        ec->cid.manufacturer, ec->cid.product);
                        ec->cid.manufacturer, ec->cid.product);
 
 
                if (!ec->card_desc && ec->cid.cd &&
                if (!ec->card_desc && ec->cid.cd &&
                    ecard_readchunk(&incd, ec, 0xf5, 0))
                    ecard_readchunk(&incd, ec, 0xf5, 0))
                        ec->card_desc = incd.d.string;
                        ec->card_desc = incd.d.string;
 
 
                if (!ec->card_desc)
                if (!ec->card_desc)
                        ec->card_desc = "*unknown*";
                        ec->card_desc = "*unknown*";
 
 
                buffer += sprintf(buffer, "%s\n", ec->card_desc);
                buffer += sprintf(buffer, "%s\n", ec->card_desc);
        } else
        } else
                buffer += sprintf(buffer, "Simple card %d\n", ec->cid.id);
                buffer += sprintf(buffer, "Simple card %d\n", ec->cid.id);
 
 
        return buffer - start;
        return buffer - start;
}
}
 
 
/*
/*
 * Probe for an expansion card.
 * Probe for an expansion card.
 *
 *
 * If bit 1 of the first byte of the card is set, then the
 * If bit 1 of the first byte of the card is set, then the
 * card does not exist.
 * card does not exist.
 */
 */
static int
static int
ecard_probe(int slot, card_type_t type)
ecard_probe(int slot, card_type_t type)
{
{
        ecard_t **ecp;
        ecard_t **ecp;
        ecard_t *ec;
        ecard_t *ec;
        struct ex_ecid cid;
        struct ex_ecid cid;
        char buffer[200];
        char buffer[200];
        int i, rc = -ENOMEM;
        int i, rc = -ENOMEM;
 
 
        ec = kmalloc(sizeof(ecard_t), GFP_KERNEL);
        ec = kmalloc(sizeof(ecard_t), GFP_KERNEL);
 
 
        if (!ec)
        if (!ec)
                goto nodev;
                goto nodev;
 
 
        memset(ec, 0, sizeof(ecard_t));
        memset(ec, 0, sizeof(ecard_t));
 
 
        ec->slot_no     = slot;
        ec->slot_no     = slot;
        ec->type        = type;
        ec->type        = type;
        ec->irq         = NO_IRQ;
        ec->irq         = NO_IRQ;
        ec->fiq         = NO_IRQ;
        ec->fiq         = NO_IRQ;
        ec->dma         = NO_DMA;
        ec->dma         = NO_DMA;
        ec->card_desc   = NULL;
        ec->card_desc   = NULL;
        ec->ops         = &ecard_default_ops;
        ec->ops         = &ecard_default_ops;
 
 
        rc = -ENODEV;
        rc = -ENODEV;
        if ((ec->podaddr = ecard_address(ec, type, ECARD_SYNC)) == 0)
        if ((ec->podaddr = ecard_address(ec, type, ECARD_SYNC)) == 0)
                goto nodev;
                goto nodev;
 
 
        cid.r_zero = 1;
        cid.r_zero = 1;
        ecard_readbytes(&cid, ec, 0, 16, 0);
        ecard_readbytes(&cid, ec, 0, 16, 0);
        if (cid.r_zero)
        if (cid.r_zero)
                goto nodev;
                goto nodev;
 
 
        ec->cid.id      = cid.r_id;
        ec->cid.id      = cid.r_id;
        ec->cid.cd      = cid.r_cd;
        ec->cid.cd      = cid.r_cd;
        ec->cid.is      = cid.r_is;
        ec->cid.is      = cid.r_is;
        ec->cid.w       = cid.r_w;
        ec->cid.w       = cid.r_w;
        ec->cid.manufacturer = ecard_getu16(cid.r_manu);
        ec->cid.manufacturer = ecard_getu16(cid.r_manu);
        ec->cid.product = ecard_getu16(cid.r_prod);
        ec->cid.product = ecard_getu16(cid.r_prod);
        ec->cid.country = cid.r_country;
        ec->cid.country = cid.r_country;
        ec->cid.irqmask = cid.r_irqmask;
        ec->cid.irqmask = cid.r_irqmask;
        ec->cid.irqoff  = ecard_gets24(cid.r_irqoff);
        ec->cid.irqoff  = ecard_gets24(cid.r_irqoff);
        ec->cid.fiqmask = cid.r_fiqmask;
        ec->cid.fiqmask = cid.r_fiqmask;
        ec->cid.fiqoff  = ecard_gets24(cid.r_fiqoff);
        ec->cid.fiqoff  = ecard_gets24(cid.r_fiqoff);
        ec->fiqaddr     =
        ec->fiqaddr     =
        ec->irqaddr     = (unsigned char *)ioaddr(ec->podaddr);
        ec->irqaddr     = (unsigned char *)ioaddr(ec->podaddr);
 
 
        if (ec->cid.is) {
        if (ec->cid.is) {
                ec->irqmask = ec->cid.irqmask;
                ec->irqmask = ec->cid.irqmask;
                ec->irqaddr += ec->cid.irqoff;
                ec->irqaddr += ec->cid.irqoff;
                ec->fiqmask = ec->cid.fiqmask;
                ec->fiqmask = ec->cid.fiqmask;
                ec->fiqaddr += ec->cid.fiqoff;
                ec->fiqaddr += ec->cid.fiqoff;
        } else {
        } else {
                ec->irqmask = 1;
                ec->irqmask = 1;
                ec->fiqmask = 4;
                ec->fiqmask = 4;
        }
        }
 
 
        for (i = 0; i < sizeof(blacklist) / sizeof(*blacklist); i++)
        for (i = 0; i < sizeof(blacklist) / sizeof(*blacklist); i++)
                if (blacklist[i].manufacturer == ec->cid.manufacturer &&
                if (blacklist[i].manufacturer == ec->cid.manufacturer &&
                    blacklist[i].product == ec->cid.product) {
                    blacklist[i].product == ec->cid.product) {
                        ec->card_desc = blacklist[i].type;
                        ec->card_desc = blacklist[i].type;
                        break;
                        break;
                }
                }
 
 
        ec->irq = 32 + slot;
        ec->irq = 32 + slot;
#ifdef IO_EC_MEMC8_BASE
#ifdef IO_EC_MEMC8_BASE
        if (slot == 8)
        if (slot == 8)
                ec->irq = 11;
                ec->irq = 11;
#endif
#endif
#ifdef CONFIG_ARCH_RPC
#ifdef CONFIG_ARCH_RPC
        /* On RiscPC, only first two slots have DMA capability */
        /* On RiscPC, only first two slots have DMA capability */
        if (slot < 2)
        if (slot < 2)
                ec->dma = 2 + slot;
                ec->dma = 2 + slot;
#endif
#endif
#if 0   /* We don't support FIQs on expansion cards at the moment */
#if 0   /* We don't support FIQs on expansion cards at the moment */
        ec->fiq = 96 + slot;
        ec->fiq = 96 + slot;
#endif
#endif
 
 
        rc = 0;
        rc = 0;
 
 
        for (ecp = &cards; *ecp; ecp = &(*ecp)->next);
        for (ecp = &cards; *ecp; ecp = &(*ecp)->next);
 
 
        *ecp = ec;
        *ecp = ec;
 
 
nodev:
nodev:
        if (rc && ec)
        if (rc && ec)
                kfree(ec);
                kfree(ec);
        else {
        else {
                slot_to_expcard[slot] = ec;
                slot_to_expcard[slot] = ec;
 
 
                ecard_prints(buffer, ec);
                ecard_prints(buffer, ec);
                printk("%s", buffer);
                printk("%s", buffer);
        }
        }
        return rc;
        return rc;
}
}
 
 
static ecard_t *finding_pos;
static ecard_t *finding_pos;
 
 
void ecard_startfind(void)
void ecard_startfind(void)
{
{
        finding_pos = NULL;
        finding_pos = NULL;
}
}
 
 
ecard_t *ecard_find(int cid, const card_ids *cids)
ecard_t *ecard_find(int cid, const card_ids *cids)
{
{
        if (!finding_pos)
        if (!finding_pos)
                finding_pos = cards;
                finding_pos = cards;
        else
        else
                finding_pos = finding_pos->next;
                finding_pos = finding_pos->next;
 
 
        for (; finding_pos; finding_pos = finding_pos->next) {
        for (; finding_pos; finding_pos = finding_pos->next) {
                if (finding_pos->claimed)
                if (finding_pos->claimed)
                        continue;
                        continue;
 
 
                if (!cids) {
                if (!cids) {
                        if ((finding_pos->cid.id ^ cid) == 0)
                        if ((finding_pos->cid.id ^ cid) == 0)
                                break;
                                break;
                } else {
                } else {
                        unsigned int manufacturer, product;
                        unsigned int manufacturer, product;
                        int i;
                        int i;
 
 
                        manufacturer = finding_pos->cid.manufacturer;
                        manufacturer = finding_pos->cid.manufacturer;
                        product = finding_pos->cid.product;
                        product = finding_pos->cid.product;
 
 
                        for (i = 0; cids[i].manufacturer != 65535; i++)
                        for (i = 0; cids[i].manufacturer != 65535; i++)
                                if (manufacturer == cids[i].manufacturer &&
                                if (manufacturer == cids[i].manufacturer &&
                                    product == cids[i].product)
                                    product == cids[i].product)
                                        break;
                                        break;
 
 
                        if (cids[i].manufacturer != 65535)
                        if (cids[i].manufacturer != 65535)
                                break;
                                break;
                }
                }
        }
        }
 
 
        return finding_pos;
        return finding_pos;
}
}
 
 
static void ecard_free_all(void)
static void ecard_free_all(void)
{
{
        ecard_t *ec, *ecn;
        ecard_t *ec, *ecn;
 
 
        for (ec = cards; ec; ec = ecn) {
        for (ec = cards; ec; ec = ecn) {
                ecn = ec->next;
                ecn = ec->next;
 
 
                kfree(ec);
                kfree(ec);
        }
        }
 
 
        cards = NULL;
        cards = NULL;
 
 
        memset(slot_to_expcard, 0, sizeof(slot_to_expcard));
        memset(slot_to_expcard, 0, sizeof(slot_to_expcard));
}
}
 
 
/*
/*
 * Initialise the expansion card system.
 * Initialise the expansion card system.
 * Locate all hardware - interrupt management and
 * Locate all hardware - interrupt management and
 * actual cards.
 * actual cards.
 */
 */
void ecard_init(void)
void ecard_init(void)
{
{
        int slot;
        int slot;
 
 
        oldlatch_init();
        oldlatch_init();
 
 
        printk("Probing expansion cards: (does not imply support)\n");
        printk("Probing expansion cards: (does not imply support)\n");
 
 
        for (slot = 0; slot < 8; slot ++) {
        for (slot = 0; slot < 8; slot ++) {
                if (ecard_probe(slot, ECARD_EASI) == -ENODEV)
                if (ecard_probe(slot, ECARD_EASI) == -ENODEV)
                        ecard_probe(slot, ECARD_IOC);
                        ecard_probe(slot, ECARD_IOC);
        }
        }
 
 
#ifdef IO_EC_MEMC8_BASE
#ifdef IO_EC_MEMC8_BASE
        ecard_probe(8, ECARD_IOC);
        ecard_probe(8, ECARD_IOC);
#endif
#endif
 
 
        ecard_probeirqhw();
        ecard_probeirqhw();
 
 
        if (setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
        if (setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
                printk(KERN_ERR "Unable to claim IRQ%d for expansion cards\n",
                printk(KERN_ERR "Unable to claim IRQ%d for expansion cards\n",
                       IRQ_EXPANSIONCARD);
                       IRQ_EXPANSIONCARD);
                ecard_free_all();
                ecard_free_all();
        }
        }
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.