diff options
author | phk <phk@FreeBSD.org> | 1995-08-24 08:56:20 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 1995-08-24 08:56:20 +0000 |
commit | cb797b9f3a51481777017af541d45fbb632918bd (patch) | |
tree | 79ea479f1d023d0a1dee6bad52acfca88dedd705 | |
parent | ae1e6673d90921816b6cdd6c732b2c8d0f5afcfa (diff) | |
download | FreeBSD-src-cb797b9f3a51481777017af541d45fbb632918bd.zip FreeBSD-src-cb797b9f3a51481777017af541d45fbb632918bd.tar.gz |
Andrew McRae's pcmcia/pccard code, the kernel part.
This is still very green, but I have managed to get my modem working.
Lots of work still to do, but now at least we can commit it. /phk
Reviewed by: phk
Submitted by: Andrew McRae <andrew@mega.com.au>
-rw-r--r-- | sys/dev/sio/sio.c | 120 | ||||
-rw-r--r-- | sys/i386/isa/sio.c | 120 | ||||
-rw-r--r-- | sys/isa/sio.c | 120 | ||||
-rw-r--r-- | sys/pccard/card.h | 134 | ||||
-rw-r--r-- | sys/pccard/cardinfo.h | 134 | ||||
-rw-r--r-- | sys/pccard/cis.h | 250 | ||||
-rw-r--r-- | sys/pccard/i82365.h | 207 | ||||
-rw-r--r-- | sys/pccard/pccard.c | 942 | ||||
-rw-r--r-- | sys/pccard/pcic.c | 761 | ||||
-rw-r--r-- | sys/pccard/skel.c | 189 | ||||
-rw-r--r-- | sys/pccard/slot.h | 128 |
11 files changed, 3102 insertions, 3 deletions
diff --git a/sys/dev/sio/sio.c b/sys/dev/sio/sio.c index 205fa5b..b6b296a 100644 --- a/sys/dev/sio/sio.c +++ b/sys/dev/sio/sio.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.109 1995/07/31 21:10:36 bde Exp $ + * $Id: sio.c,v 1.110 1995/08/13 07:49:35 bde Exp $ */ #include "sio.h" @@ -41,6 +41,9 @@ * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. + * + * Changes for PC-Card integration: + * - Added PC-Card driver table and handlers */ #include <sys/param.h> #include <sys/systm.h> @@ -93,6 +96,12 @@ #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ +#include "crd.h" +#if NCRD > 0 +#include <pccard/card.h> +#include <pccard/slot.h> +#endif /* NCRD > 0 */ + /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches @@ -351,6 +360,103 @@ static struct kern_devconf kdc_sio[NSIO] = { { "RS-232 serial port", DC_CLS_SERIAL /* class */ } }; +#if NCRD > 0 +/* + * PC-Card (PCMCIA) specific code. + */ +static int card_intr(struct pccard_dev *); /* Interrupt handler */ +void siounload(struct pccard_dev *); /* Disable driver */ +void siosuspend(struct pccard_dev *); /* Suspend driver */ +static int sioinit(struct pccard_dev *, int); /* init device */ + +static struct pccard_drv sio_info = + { + "sio", + card_intr, + siounload, + siosuspend, + sioinit, + 0, /* Attributes - presently unused */ + &tty_imask /* Interrupt mask for device */ + /* This should also include net_imask?? */ + }; +/* + * Called when a power down is wanted. Shuts down the + * device and configures the device as unavailable (but + * still loaded...). A resume is done by calling + * sioinit with first=0. This is called when the user suspends + * the system, or the APM code suspends the system. + */ +void +siosuspend(struct pccard_dev *dp) +{ + printf("sio%d: suspending\n", dp->isahd.id_unit); +} +/* + * Initialize the device - called from Slot manager. + * if first is set, then initially check for + * the device's existence before initialising it. + * Once initialised, the device table may be set up. + */ +int +sioinit(struct pccard_dev *dp, int first) +{ +/* + * validate unit number. + */ + if (first) + { + if (dp->isahd.id_unit >= NSIO) + return(ENODEV); +/* + * Make sure it isn't already probed. + */ + if (com_addr(dp->isahd.id_unit)) + return(EBUSY); +/* + * Probe the device. If a value is returned, the + * device was found at the location. + */ + if (sioprobe(&dp->isahd)==0) + return(ENXIO); + if (sioattach(&dp->isahd)==0) + return(ENXIO); + } +/* + * XXX TODO: + * If it was already inited before, the device structure + * should be already initialised. Here we should + * reset (and possibly restart) the hardware, but + * I am not sure of the best way to do this... + */ + return(0); +} +/* + * siounload - unload the driver and clear the table. + * XXX TODO: + * This is called usually when the card is ejected, but + * can be caused by the modunload of a controller driver. + * The idea is reset the driver's view of the device + * and ensure that any driver entry points such as + * read and write do not hang. + */ +void +siounload(struct pccard_dev *dp) +{ + printf("sio%d: unload\n", dp->isahd.id_unit); +} + +/* + * card_intr - Shared interrupt called from + * front end of PC-Card handler. + */ +static int +card_intr(struct pccard_dev *dp) +{ + siointr1(com_addr(dp->isahd.id_unit)); + return(1); +} +#endif /* NCRD > 0 */ static void sioregisterdev(id) @@ -359,6 +465,11 @@ sioregisterdev(id) int unit; unit = id->id_unit; +/* + * If already registered, don't try to re-register. + */ + if (kdc_sio[unit].kdc_isa) + return; if (unit != 0) kdc_sio[unit] = kdc_sio[0]; kdc_sio[unit].kdc_unit = unit; @@ -393,6 +504,13 @@ sioprobe(dev) / sizeof likely_com_ports[0]]; ++com_ptr) outb(*com_ptr + com_mcr, 0); +#if NCRD > 0 +/* + * If PC-Card probe required, then register driver with + * slot manager. + */ + pccard_add_driver(&sio_info); +#endif /* NCRD > 0 */ already_init = TRUE; } diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c index 205fa5b..b6b296a 100644 --- a/sys/i386/isa/sio.c +++ b/sys/i386/isa/sio.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.109 1995/07/31 21:10:36 bde Exp $ + * $Id: sio.c,v 1.110 1995/08/13 07:49:35 bde Exp $ */ #include "sio.h" @@ -41,6 +41,9 @@ * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. + * + * Changes for PC-Card integration: + * - Added PC-Card driver table and handlers */ #include <sys/param.h> #include <sys/systm.h> @@ -93,6 +96,12 @@ #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ +#include "crd.h" +#if NCRD > 0 +#include <pccard/card.h> +#include <pccard/slot.h> +#endif /* NCRD > 0 */ + /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches @@ -351,6 +360,103 @@ static struct kern_devconf kdc_sio[NSIO] = { { "RS-232 serial port", DC_CLS_SERIAL /* class */ } }; +#if NCRD > 0 +/* + * PC-Card (PCMCIA) specific code. + */ +static int card_intr(struct pccard_dev *); /* Interrupt handler */ +void siounload(struct pccard_dev *); /* Disable driver */ +void siosuspend(struct pccard_dev *); /* Suspend driver */ +static int sioinit(struct pccard_dev *, int); /* init device */ + +static struct pccard_drv sio_info = + { + "sio", + card_intr, + siounload, + siosuspend, + sioinit, + 0, /* Attributes - presently unused */ + &tty_imask /* Interrupt mask for device */ + /* This should also include net_imask?? */ + }; +/* + * Called when a power down is wanted. Shuts down the + * device and configures the device as unavailable (but + * still loaded...). A resume is done by calling + * sioinit with first=0. This is called when the user suspends + * the system, or the APM code suspends the system. + */ +void +siosuspend(struct pccard_dev *dp) +{ + printf("sio%d: suspending\n", dp->isahd.id_unit); +} +/* + * Initialize the device - called from Slot manager. + * if first is set, then initially check for + * the device's existence before initialising it. + * Once initialised, the device table may be set up. + */ +int +sioinit(struct pccard_dev *dp, int first) +{ +/* + * validate unit number. + */ + if (first) + { + if (dp->isahd.id_unit >= NSIO) + return(ENODEV); +/* + * Make sure it isn't already probed. + */ + if (com_addr(dp->isahd.id_unit)) + return(EBUSY); +/* + * Probe the device. If a value is returned, the + * device was found at the location. + */ + if (sioprobe(&dp->isahd)==0) + return(ENXIO); + if (sioattach(&dp->isahd)==0) + return(ENXIO); + } +/* + * XXX TODO: + * If it was already inited before, the device structure + * should be already initialised. Here we should + * reset (and possibly restart) the hardware, but + * I am not sure of the best way to do this... + */ + return(0); +} +/* + * siounload - unload the driver and clear the table. + * XXX TODO: + * This is called usually when the card is ejected, but + * can be caused by the modunload of a controller driver. + * The idea is reset the driver's view of the device + * and ensure that any driver entry points such as + * read and write do not hang. + */ +void +siounload(struct pccard_dev *dp) +{ + printf("sio%d: unload\n", dp->isahd.id_unit); +} + +/* + * card_intr - Shared interrupt called from + * front end of PC-Card handler. + */ +static int +card_intr(struct pccard_dev *dp) +{ + siointr1(com_addr(dp->isahd.id_unit)); + return(1); +} +#endif /* NCRD > 0 */ static void sioregisterdev(id) @@ -359,6 +465,11 @@ sioregisterdev(id) int unit; unit = id->id_unit; +/* + * If already registered, don't try to re-register. + */ + if (kdc_sio[unit].kdc_isa) + return; if (unit != 0) kdc_sio[unit] = kdc_sio[0]; kdc_sio[unit].kdc_unit = unit; @@ -393,6 +504,13 @@ sioprobe(dev) / sizeof likely_com_ports[0]]; ++com_ptr) outb(*com_ptr + com_mcr, 0); +#if NCRD > 0 +/* + * If PC-Card probe required, then register driver with + * slot manager. + */ + pccard_add_driver(&sio_info); +#endif /* NCRD > 0 */ already_init = TRUE; } diff --git a/sys/isa/sio.c b/sys/isa/sio.c index 205fa5b..b6b296a 100644 --- a/sys/isa/sio.c +++ b/sys/isa/sio.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.109 1995/07/31 21:10:36 bde Exp $ + * $Id: sio.c,v 1.110 1995/08/13 07:49:35 bde Exp $ */ #include "sio.h" @@ -41,6 +41,9 @@ * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. + * + * Changes for PC-Card integration: + * - Added PC-Card driver table and handlers */ #include <sys/param.h> #include <sys/systm.h> @@ -93,6 +96,12 @@ #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ +#include "crd.h" +#if NCRD > 0 +#include <pccard/card.h> +#include <pccard/slot.h> +#endif /* NCRD > 0 */ + /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches @@ -351,6 +360,103 @@ static struct kern_devconf kdc_sio[NSIO] = { { "RS-232 serial port", DC_CLS_SERIAL /* class */ } }; +#if NCRD > 0 +/* + * PC-Card (PCMCIA) specific code. + */ +static int card_intr(struct pccard_dev *); /* Interrupt handler */ +void siounload(struct pccard_dev *); /* Disable driver */ +void siosuspend(struct pccard_dev *); /* Suspend driver */ +static int sioinit(struct pccard_dev *, int); /* init device */ + +static struct pccard_drv sio_info = + { + "sio", + card_intr, + siounload, + siosuspend, + sioinit, + 0, /* Attributes - presently unused */ + &tty_imask /* Interrupt mask for device */ + /* This should also include net_imask?? */ + }; +/* + * Called when a power down is wanted. Shuts down the + * device and configures the device as unavailable (but + * still loaded...). A resume is done by calling + * sioinit with first=0. This is called when the user suspends + * the system, or the APM code suspends the system. + */ +void +siosuspend(struct pccard_dev *dp) +{ + printf("sio%d: suspending\n", dp->isahd.id_unit); +} +/* + * Initialize the device - called from Slot manager. + * if first is set, then initially check for + * the device's existence before initialising it. + * Once initialised, the device table may be set up. + */ +int +sioinit(struct pccard_dev *dp, int first) +{ +/* + * validate unit number. + */ + if (first) + { + if (dp->isahd.id_unit >= NSIO) + return(ENODEV); +/* + * Make sure it isn't already probed. + */ + if (com_addr(dp->isahd.id_unit)) + return(EBUSY); +/* + * Probe the device. If a value is returned, the + * device was found at the location. + */ + if (sioprobe(&dp->isahd)==0) + return(ENXIO); + if (sioattach(&dp->isahd)==0) + return(ENXIO); + } +/* + * XXX TODO: + * If it was already inited before, the device structure + * should be already initialised. Here we should + * reset (and possibly restart) the hardware, but + * I am not sure of the best way to do this... + */ + return(0); +} +/* + * siounload - unload the driver and clear the table. + * XXX TODO: + * This is called usually when the card is ejected, but + * can be caused by the modunload of a controller driver. + * The idea is reset the driver's view of the device + * and ensure that any driver entry points such as + * read and write do not hang. + */ +void +siounload(struct pccard_dev *dp) +{ + printf("sio%d: unload\n", dp->isahd.id_unit); +} + +/* + * card_intr - Shared interrupt called from + * front end of PC-Card handler. + */ +static int +card_intr(struct pccard_dev *dp) +{ + siointr1(com_addr(dp->isahd.id_unit)); + return(1); +} +#endif /* NCRD > 0 */ static void sioregisterdev(id) @@ -359,6 +465,11 @@ sioregisterdev(id) int unit; unit = id->id_unit; +/* + * If already registered, don't try to re-register. + */ + if (kdc_sio[unit].kdc_isa) + return; if (unit != 0) kdc_sio[unit] = kdc_sio[0]; kdc_sio[unit].kdc_unit = unit; @@ -393,6 +504,13 @@ sioprobe(dev) / sizeof likely_com_ports[0]]; ++com_ptr) outb(*com_ptr + com_mcr, 0); +#if NCRD > 0 +/* + * If PC-Card probe required, then register driver with + * slot manager. + */ + pccard_add_driver(&sio_info); +#endif /* NCRD > 0 */ already_init = TRUE; } diff --git a/sys/pccard/card.h b/sys/pccard/card.h new file mode 100644 index 0000000..30ee50e --- /dev/null +++ b/sys/pccard/card.h @@ -0,0 +1,134 @@ +/* + * Include file for PCMCIA user process interface + * + *------------------------------------------------------------------------- + * + * Copyright (c) 1995 Andrew McRae. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define PIOCGSTATE _IOR('P', 1, struct slotstate) /* Get slot state */ +#define PIOCGMEM _IOWR('P', 2, struct mem_desc) /* Get memory map */ +#define PIOCSMEM _IOW('P', 3, struct mem_desc) /* Set memory map */ +#define PIOCGIO _IOWR('P', 4, struct io_desc) /* Get I/O map */ +#define PIOCSIO _IOW('P', 5, struct io_desc) /* Set I/O map */ +#define PIOCSDRV _IOW('P', 6, struct drv_desc) /* Set driver */ +#define PIOCRWFLAG _IOW('P', 7, int) /* Set flags for drv use */ +#define PIOCRWMEM _IOWR('P', 8, unsigned long) /* Set mem for drv use */ +#define PIOCSPOW _IOW('P', 9, struct power) /* Set power structure */ +/* + * Debug codes. + */ +#define PIOCGREG _IOWR('P',100, struct pcic_reg) /* get reg */ +#define PIOCSREG _IOW('P', 101, struct pcic_reg) /* Set reg */ + + +/* + * Slot states for PIOCGSTATE + */ +enum cardstate { noslot, empty, filled }; + +/* + * Descriptor structure for memory map. + */ +struct mem_desc + { + int window; /* Memory map window number (0-4) */ + int flags; /* Flags - see below */ + caddr_t start; /* System memory start */ + int size; /* Size of memory area */ + unsigned long card; /* Card memory address */ + }; + +#define MDF_16BITS 0x01 /* Memory is 16 bits wide */ +#define MDF_ZEROWS 0x02 /* Set no wait states for memory */ +#define MDF_WS0 0x04 /* Wait state flags */ +#define MDF_WS1 0x08 +#define MDF_ATTR 0x10 /* Memory is attribute memory */ +#define MDF_WP 0x20 /* Write protect memory */ +#define MDF_ACTIVE 0x40 /* Context active (read-only) */ + +/* + * Descriptor structure for I/O map + */ +struct io_desc + { + int window; /* I/O map number (0-1) */ + int flags; /* Flags - see below */ + int start; /* I/O port start */ + int size; /* Number of port addresses */ + }; + +#define IODF_WS 0x01 /* Set wait states for 16 bit I/O access */ +#define IODF_16BIT 0x02 /* I/O access are 16 bit */ +#define IODF_CS16 0x04 /* Allow card selection of 16 bit access */ +#define IODF_ZEROWS 0x08 /* No wait states for 8 bit I/O */ +#define IODF_ACTIVE 0x10 /* Context active (read-only) */ + +/* + * Device descriptor for allocation of driver. + */ +struct drv_desc + { + char name[16]; /* Driver name */ + int unit; /* Driver unit number */ + unsigned long mem; /* Memory address of driver */ + int memsize; /* Memory size (if used) */ + int iobase; /* base of I/O ports */ + int irqmask; /* Interrupt number(s) to allocate */ + int flags; /* Device flags */ + }; + +struct pcic_reg + { + unsigned char reg; + unsigned char value; + }; +/* + * Slot information. Used to read current status of slot. + */ +struct slotstate + { + enum cardstate state; /* Current state of slot */ + int maxmem; /* Max allowed memory windows */ + int maxio; /* Max allowed I/O windows */ + int irqs; /* Bitmap of IRQs allowed */ + int flags; /* Capability flags */ + }; + +/* + * The power values are in volts * 10, e.g. 5V is 50, 3.3V is 33. + */ +struct power + { + int vcc; + int vpp; + }; + +/* + * Other system limits + */ +#define MAXSLOT 16 +#define NUM_MEM_WINDOWS 10 +#define NUM_IO_WINDOWS 6 +#define CARD_DEVICE "/dev/card%d" /* String for sprintf */ diff --git a/sys/pccard/cardinfo.h b/sys/pccard/cardinfo.h new file mode 100644 index 0000000..30ee50e --- /dev/null +++ b/sys/pccard/cardinfo.h @@ -0,0 +1,134 @@ +/* + * Include file for PCMCIA user process interface + * + *------------------------------------------------------------------------- + * + * Copyright (c) 1995 Andrew McRae. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define PIOCGSTATE _IOR('P', 1, struct slotstate) /* Get slot state */ +#define PIOCGMEM _IOWR('P', 2, struct mem_desc) /* Get memory map */ +#define PIOCSMEM _IOW('P', 3, struct mem_desc) /* Set memory map */ +#define PIOCGIO _IOWR('P', 4, struct io_desc) /* Get I/O map */ +#define PIOCSIO _IOW('P', 5, struct io_desc) /* Set I/O map */ +#define PIOCSDRV _IOW('P', 6, struct drv_desc) /* Set driver */ +#define PIOCRWFLAG _IOW('P', 7, int) /* Set flags for drv use */ +#define PIOCRWMEM _IOWR('P', 8, unsigned long) /* Set mem for drv use */ +#define PIOCSPOW _IOW('P', 9, struct power) /* Set power structure */ +/* + * Debug codes. + */ +#define PIOCGREG _IOWR('P',100, struct pcic_reg) /* get reg */ +#define PIOCSREG _IOW('P', 101, struct pcic_reg) /* Set reg */ + + +/* + * Slot states for PIOCGSTATE + */ +enum cardstate { noslot, empty, filled }; + +/* + * Descriptor structure for memory map. + */ +struct mem_desc + { + int window; /* Memory map window number (0-4) */ + int flags; /* Flags - see below */ + caddr_t start; /* System memory start */ + int size; /* Size of memory area */ + unsigned long card; /* Card memory address */ + }; + +#define MDF_16BITS 0x01 /* Memory is 16 bits wide */ +#define MDF_ZEROWS 0x02 /* Set no wait states for memory */ +#define MDF_WS0 0x04 /* Wait state flags */ +#define MDF_WS1 0x08 +#define MDF_ATTR 0x10 /* Memory is attribute memory */ +#define MDF_WP 0x20 /* Write protect memory */ +#define MDF_ACTIVE 0x40 /* Context active (read-only) */ + +/* + * Descriptor structure for I/O map + */ +struct io_desc + { + int window; /* I/O map number (0-1) */ + int flags; /* Flags - see below */ + int start; /* I/O port start */ + int size; /* Number of port addresses */ + }; + +#define IODF_WS 0x01 /* Set wait states for 16 bit I/O access */ +#define IODF_16BIT 0x02 /* I/O access are 16 bit */ +#define IODF_CS16 0x04 /* Allow card selection of 16 bit access */ +#define IODF_ZEROWS 0x08 /* No wait states for 8 bit I/O */ +#define IODF_ACTIVE 0x10 /* Context active (read-only) */ + +/* + * Device descriptor for allocation of driver. + */ +struct drv_desc + { + char name[16]; /* Driver name */ + int unit; /* Driver unit number */ + unsigned long mem; /* Memory address of driver */ + int memsize; /* Memory size (if used) */ + int iobase; /* base of I/O ports */ + int irqmask; /* Interrupt number(s) to allocate */ + int flags; /* Device flags */ + }; + +struct pcic_reg + { + unsigned char reg; + unsigned char value; + }; +/* + * Slot information. Used to read current status of slot. + */ +struct slotstate + { + enum cardstate state; /* Current state of slot */ + int maxmem; /* Max allowed memory windows */ + int maxio; /* Max allowed I/O windows */ + int irqs; /* Bitmap of IRQs allowed */ + int flags; /* Capability flags */ + }; + +/* + * The power values are in volts * 10, e.g. 5V is 50, 3.3V is 33. + */ +struct power + { + int vcc; + int vpp; + }; + +/* + * Other system limits + */ +#define MAXSLOT 16 +#define NUM_MEM_WINDOWS 10 +#define NUM_IO_WINDOWS 6 +#define CARD_DEVICE "/dev/card%d" /* String for sprintf */ diff --git a/sys/pccard/cis.h b/sys/pccard/cis.h new file mode 100644 index 0000000..677b197 --- /dev/null +++ b/sys/pccard/cis.h @@ -0,0 +1,250 @@ + +/* + * PCMCIA card structures and defines. + * These defines relate to the user level + * structures and card information, not + * driver/process communication. + *------------------------------------------------------------------------- + * + * Copyright (c) 1995 Andrew McRae. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Card Information Structure tuples definitions + * The structure of a tuple is basically: + * + * Tuple_code + * Tuple_data_length + * Tuple_data ... + * + * Tuples are contiguous in attribute memory, and + * are terminated with a 0xFF for the tuple code or + * the tuple length. + */ +#define CIS_NULL 0 /* Empty tuple */ +#define CIS_MEM_COMMON 0x01 /* Device descriptor, common memory */ +#define CIS_CHECKSUM 0x10 /* Checksum */ +#define CIS_LONGLINK_A 0x11 /* Link to Attribute memory */ +#define CIS_LONGLINK_C 0x12 /* Link to Common memory */ +#define CIS_LINKTARGET 0x13 /* Linked tuple must start with this. */ +#define CIS_NOLINK 0x14 /* Assume no common memory link tuple. */ +#define CIS_INFO_V1 0x15 /* Card info data, version 1 */ +#define CIS_ALTSTR 0x16 /* Alternate language string tuple. */ +#define CIS_MEM_ATTR 0x17 /* Device descriptor, Attribute memory */ +#define CIS_JEDEC_C 0x18 /* JEDEC descr for common memory */ +#define CIS_JEDEC_A 0x19 /* JEDEC descr for Attribute memory */ +#define CIS_CONF_MAP 0x1A /* Card Configuration map */ +#define CIS_CONFIG 0x1B /* Card Configuration entry */ +#define CIS_DEVICE_OC 0x1C /* Other conditions info - common memory */ +#define CIS_DEVICE_OA 0x1D /* Other conditions info - attribute memory */ +#define CIS_DEVICEGEO 0x1E /* Geometry info for common memory */ +#define CIS_DEVICEGEO_A 0x1F /* Geometry info for attribute memory */ +#define CIS_MANUF_ID 0x20 /* Card manufacturer's ID */ +#define CIS_FUNC_ID 0x21 /* Function of card */ +#define CIS_FUNC_EXT 0x22 /* Functional extension */ +/* + * Data recording format tuples. + */ +#define CIS_SW_INTERLV 0x23 /* Software interleave */ +#define CIS_VERS_2 0x40 /* Card info data, version 2 */ +#define CIS_FORMAT 0x41 /* Memory card format */ +#define CIS_GEOMETRY 0x42 /* Disk sector layout */ +#define CIS_BYTEORDER 0x43 /* Byte order of memory data */ +#define CIS_DATE 0x44 /* Format data/time */ +#define CIS_BATTERY 0x45 /* Battery replacement date */ +#define CIS_ORG 0x46 /* Organisation of data on card */ +#define CIS_END 0xFF /* Termination code */ + +/* + * Internal tuple definitions. + * + * Device descriptor for memory (CIS_MEM_ATTR, CIS_MEM_COMMON) + * + * Byte 1: + * 0xF0 - Device type + * 0x08 - Write protect switch + * 0x07 - Speed index (7 = extended speed) + * Byte 2: Extended speed (bit 7 = another follows) + * Byte 3: (ignored if 0xFF) + * 0xF8 - Addressable units (0's numbered) + * 0x07 - Unit size + * The three byte sequence is repeated until byte 1 == 0xFF + */ + +/* + * CIS_INFO_V1 - Version one card information. + * + * Byte 1: Major version number (should be 4) + * Byte 2: Minor version number (should be 1) + * Byte 3-x: Null terminated Manufacturer name + * Byte x-x: Null terminated product name + * Byte x-x: Null terminated additional info 1 + * Byte x-x: Null terminated additional info 2 + * Byte x: final byte must be 0xFF + */ +#define CIS_MAJOR_VERSION 4 +#define CIS_MINOR_VERSION 1 + +/* + * CIS_CONF_MAP - Provides an address map for the card + * configuration register(s), and a max value + * identifying the last configuration tuple. + * + * Byte 1: + * 0x3C - Register mask size (0's numbered) + * 0x03 - Register address size (0's numbered) + * Byte 2: + * 0x3F - ID of last configuration. + * Byte 3-n: Card register address (size is determined by + * the value in byte 1). + * Byte x-x: Card register masks (size determined by the + * value in byte 1) + */ + +/* + * CIS_CONFIG - Card configuration entry. Multiple tuples may + * exist of this type, each one describing a different + * memory/I-O map that can be used to address this card. + * The first one usually has extra config data about the + * card features. The final configuration tuple number + * is stored in the CIS_CONF_MAP tuple so that the complete + * list can be scanned. + * + * Byte 1: + * 0x3F - Configuration ID number. + * 0x40 - Indicates this is the default configuration + * 0x80 - Interface byte exists + * Byte 2: (exists only if bit 0x80 set in byte 1) + * 0x0F - Interface type value + * 0x10 - Battery voltage detect + * 0x20 - Write protect active + * 0x40 - RdyBsy active bit + * 0x80 - Wait signal required + * Byte 3: (features byte) + * 0x03 - Power sub-tuple(s) exists + * 0x04 - Timing sub-tuple exists + * 0x08 - I/O space sub-tuple exists + * 0x10 - IRQ sub-tuple exists + * 0x60 - Memory space sub-tuple(s) exists + * 0x80 - Miscellaneous sub-tuple exists + */ +#define CIS_FEAT_POWER(x) ((x) & 0x3) +#define CIS_FEAT_TIMING 0x4 +#define CIS_FEAT_I_O 0x8 +#define CIS_FEAT_IRQ 0x10 +#define CIS_FEAT_MEMORY(x) (((x) >> 5) & 0x3) +#define CIS_FEAT_MISC 0x80 +/* + * Depending on whether the "features" byte has the corresponding + * bit set, a number of sub-tuples follow. Some features have + * more than one sub-tuple, depending on the count within the + * features byte (e.g power feature bits allows up to 3 sub-tuples). + * + * Power structure sub-tuple: + * Byte 1: parameter exists - Each bit (starting from 0x01) indicates + * that a parameter block exists - up to 8 parameter blocks + * are therefore allowed). + * Byte 2: + * 0x7F - Parameter data + * 0x80 - More bytes follow (0 = last byte) + * + * Timing sub-tuple + * Byte 1: + * 0x03 - Wait scale + * 0x1C - Ready scale + * 0xE0 - Reserved scale + * Byte 2: extended wait scale if wait scale != 3 + * Byte 3: extended ready scale if ready scale != 7 + * Byte 4: extended reserved scale if reserved scale != 7 + */ +#define CIS_WAIT_SCALE(x) ((x) & 0x3) +#define CIS_READY_SCALE(x) (((x)>>2) & 0x7) +#define CIS_RESERVED_SCALE(x) (((x)>>5) & 0x7) +/* + * I/O mapping sub-tuple: + * Byte 1: + * 0x1F - I/O address lines + * 0x20 - 8 bit I/O + * 0x40 - 16 bit I/O + * 0x80 - I/O range?? + * Byte 2: + * 0x0F - 0's numbered count of I/O block subtuples following. + * 0x30 - Size of I/O address value within subtuple. Values + * can be 1 (8 bits), 2 (16 bits) or 3 (32 bits). + * 0xC0 - Size of I/O port block size value within subtuple. + * I/O block sub-tuples, count from previous block: + * Byte 1-n: I/O start address + * Byte x-x: Size of I/O port block. + */ +#define CIS_IO_ADDR(x) ((x) & 0x1F) +#define CIS_IO_8BIT 0x20 +#define CIS_IO_16BIT 0x40 +#define CIS_IO_RANGE 0x80 +#define CIS_IO_BLKS(x) ((x) & 0xF) +#define CIS_IO_ADSZ(x) (((x)>>4) & 3) +#define CIS_IO_BLKSZ(x) (((x)>>6) & 3) +/* + * IRQ sub-tuple. + * Byte 1: + * 0x0F - Irq number or mask bits + * 0x10 - IRQ mask values exist + * 0x20 - Level triggered interrupts + * 0x40 - Pulse triggered requests + * 0x80 - Interrupt sharing. + * Byte 2-3: Interrupt req mask (if 0x10 of byte 1 set). + */ +#define CIS_IRQ_IRQN(x) ((x) & 0xF) +#define CIS_IRQ_MASK 0x10 +#define CIS_IRQ_LEVEL 0x20 +#define CIS_IRQ_PULSE 0x40 +#define CIS_IRQ_SHARING 0x80 +/* + * Memory block subtuple. Depending on the features bits, the + * following subtuples are used: + * mem features == 1 + * Byte 1-2: upper 16 bits of 24 bit memory length. + * mem features == 2 + * Byte 1-2: upper 16 bits of 24 bit memory length. + * Byte 3-4: upper 16 bits of 24 bit memory address. + * mem_features == 3 + * Byte 1: + * 0x07 - 0's numbered count of memory sub-tuples + * 0x18 - Memory length size (1's numbered) + * 0x60 - Memory address size (1's numbered) + * 0x80 - Host address value exists + * Memory sub-tuples follow: + * Byte 1-n: Memory length value (<< 8) + * Byte n-n: Memory card address value (<< 8) + * Byte n-n: Memory host address value (<< 8) + */ +#define CIS_FEAT_MEM_NONE 0 /* No memory config */ +#define CIS_FEAT_MEM_LEN 1 /* Just length */ +#define CIS_FEAT_MEM_ADDR 2 /* Card address & length */ +#define CIS_FEAT_MEM_WIN 3 /* Multiple windows */ + +#define CIS_MEM_WINS(x) (((x) & 0x7)+1) +#define CIS_MEM_LENSZ(x) (((x) >> 3) & 0x3) +#define CIS_MEM_ADDRSZ(x) (((x) >> 5) & 0x3) +#define CIS_MEM_HOST 0x80 diff --git a/sys/pccard/i82365.h b/sys/pccard/i82365.h new file mode 100644 index 0000000..4c4d233 --- /dev/null +++ b/sys/pccard/i82365.h @@ -0,0 +1,207 @@ +/* + * i82365.h - Definitions for Intel 82365 PCIC + * PCMCIA Card Interface Controller + * + * originally by Barry Jaspan; hacked over by Keith Moore + * hacked to unrecognisability by Andrew McRae (andrew@mega.com.au) + * + * Updated 3/3/95 to include Cirrus Logic stuff. + *------------------------------------------------------------------------- + * + * Copyright (c) 1995 Andrew McRae. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define PCIC_I82365 0 /* Intel chip */ +#define PCIC_IBM 1 /* IBM clone */ +#define PCIC_VLSI 2 /* VLSI chip */ +#define PCIC_PD672X 3 /* Cirrus logic 627x */ +#define PCIC_PD6710 4 +#define PCIC_CL6729 5 +#define PCIC_VG468 6 +/* + * Address of the controllers. Each controller can manage + * two PCMCIA slots. Up to 8 slots are supported in total. + * The PCIC controller is accessed via an index port and a + * data port. The index port has the 8 bit address of the + * register accessed via the data port. How I long for + * real memory mapped I/O! + * The top two bits of the index address are used to + * identify the port number, and the lower 6 bits + * select one of the 64 possible data registers. + */ +#define PCIC_INDEX_0 0x3E0 /* index reg, chips 0 and 1 */ +#define PCIC_DATA_0 0x3E1 /* data register, chips 0 and 1 */ +#define PCIC_INDEX_1 0x3E2 /* index reg, chips 2 and 3 */ +#define PCIC_DATA_1 0x3E3 /* data register, chips 2 and 3 */ +/* + * Register index addresses. + */ +#define PCIC_ID_REV 0x00 /* Identification and Revision */ +#define PCIC_STATUS 0x01 /* Interface Status */ +#define PCIC_POWER 0x02 /* Power and RESETDRV control */ +#define PCIC_INT_GEN 0x03 /* Interrupt and General Control */ +#define PCIC_STAT_CHG 0x04 /* Card Status Change */ +#define PCIC_STAT_INT 0x05 /* Card Status Change Interrupt Config */ +#define PCIC_ADDRWINE 0x06 /* Address Window Enable */ +#define PCIC_IOCTL 0x07 /* I/O Control */ +#define PCIC_IO0 0x08 /* I/O Address 0 */ +#define PCIC_IO1 0x0c /* I/O Address 1 */ +#define PCIC_MEMBASE 0x10 /* Base of memory window registers */ +#define PCIC_CDGC 0x16 /* Card Detect and General Control */ +#define PCIC_GLO_CTRL 0x1e /* Global Control Register */ + +#define PCIC_TIME_SETUP0 0x3a +#define PCIC_TIME_CMD0 0x3b +#define PCIC_TIME_RECOV0 0x3c +#define PCIC_TIME_SETUP1 0x3d +#define PCIC_TIME_CMD1 0x3e +#define PCIC_TIME_RECOV1 0x3f + +#define PCIC_SLOT_SIZE 0x40 /* Size of register set for one slot */ + +/* Now register bits, ordered by reg # */ + +/* For Identification and Revision (PCIC_ID_REV) */ +#define PCIC_INTEL0 0x82 /* Intel 82365SL Rev. 0; Both Memory and I/O */ +#define PCIC_INTEL1 0x83 /* Intel 82365SL Rev. 1; Both Memory and I/O */ +#define PCIC_IBM1 0x88 /* IBM PCIC clone; Both Memory and I/O */ +#define PCIC_IBM2 0x89 /* IBM PCIC clone; Both Memory and I/O */ + +/* For Interface Status register (PCIC_STATUS) */ +#define PCIC_VPPV 0x80 /* Vpp_valid */ +#define PCIC_POW 0x40 /* PC Card power active */ +#define PCIC_READY 0x20 /* Ready/~Busy */ +#define PCIC_MWP 0x10 /* Memory Write Protect */ +#define PCIC_CD 0x0C /* Both card detect bits */ +#define PCIC_BVD 0x03 /* Both Battery Voltage Detect bits */ + +/* For the Power and RESETDRV register (PCIC_POWER) */ +#define PCIC_OUTENA 0x80 /* Output Enable */ +#define PCIC_DISRST 0x40 /* Disable RESETDRV */ +#define PCIC_APSENA 0x20 /* Auto Pwer Switch Enable */ +#define PCIC_VCC 0x18 /* Vcc control bits */ +#define PCIC_VCC_5V 0x10 /* 5 volts */ +#define PCIC_VCC_3V 0x18 /* 3 volts */ +#define PCIC_VPP 0x0C /* Vpp control bits */ +#define PCIC_VPP_5V 0x01 /* 5 volts */ +#define PCIC_VPP_12V 0x02 /* 12 volts */ + +/* For the Interrupt and General Control register (PCIC_INT_GEN) */ +#define PCIC_CARDTYPE 0x20 /* Card Type 0 = memory, 1 = I/O */ +#define PCIC_IOCARD 0x20 +#define PCIC_MEMCARD 0x00 +#define PCIC_CARDRESET 0x40 /* Card reset 0 = Reset, 1 = Normal */ +#define PCIC_INTR_ENA 0x10 /* Interrupt enable */ + +/* For the Card Status Change register (PCIC_STAT_CHG) */ +#define PCIC_CDTCH 0x08 /* Card Detect Change */ +#define PCIC_RDYCH 0x04 /* Ready Change */ +#define PCIC_BATWRN 0x02 /* Battery Warning */ +#define PCIC_BATDED 0x01 /* Battery Dead */ + +/* + * For the Address Window Enable Register (PCIC_ADDRWINE) + * The lower 6 bits contain enable bits for the memory + * windows (LSB = memory window 0). + */ +#define PCIC_MEMCS16 0x20 /* ~MEMCS16 Decode A23-A12 */ +#define PCIC_IO0_EN 0x40 /* I/O Window 0 Enable */ +#define PCIC_IO1_EN 0x80 /* I/O Window 1 Enable */ + +/* + * For the I/O Control Register (PCIC_IOCTL) + * The lower nybble is the flags for I/O window 0 + * The upper nybble is the flags for I/O window 1 + */ +#define PCIC_IO_16BIT 0x01 /* I/O to this segment is 16 bit */ +#define PCIC_IO_CS16 0x02 /* I/O cs16 source is the card */ +#define PCIC_IO_0WS 0x04 /* zero wait states added on 8 bit cycles */ +#define PCIC_IO_WS 0x08 /* Wait states added for 16 bit cycles */ + +/* + * The memory window registers contain the start and end + * physical host address that the PCIC maps to the card, + * and an offset calculated from the card memory address. + * All values are shifted down 12 bits, so allocation is + * done in 4Kb blocks. Only 12 bits of each value is + * stored, limiting the range to the ISA address size of + * 24 bits. The upper 4 bits of the most significant byte + * within the values are used for various flags. + * + * The layout is: + * + * base+0 : lower 8 bits of system memory start address + * base+1 : upper 4 bits of system memory start address + flags + * base+2 : lower 8 bits of system memory end address + * base+3 : upper 4 bits of system memory end address + flags + * base+4 : lower 8 bits of offset to card address + * base+5 : upper 4 bits of offset to card address + flags + * + * The following two bytes are reserved for other use. + */ +#define PCIC_MEMSIZE 8 +/* + * Flags for system memory start address upper byte + */ +#define PCIC_ZEROWS 0x40 /* Zero wait states */ +#define PCIC_DATA16 0x80 /* Data width is 16 bits */ + +/* + * Flags for system memory end address upper byte + */ +#define PCIC_MW0 0x40 /* Wait state bit 0 */ +#define PCIC_MW1 0x80 /* Wait state bit 1 */ + +/* + * Flags for card offset upper byte + */ +#define PCIC_REG 0x40 /* Attribute/Common select (why called Reg?) */ +#define PCIC_WP 0x80 /* Write-protect this window */ + +/* For Card Detect and General Control register (PCIC_CDGC) */ +#define PCIC_16_DL_INH 0x01 /* 16-bit memory delay inhibit */ +#define PCIC_CNFG_RST_EN 0x02 /* configuration reset enable */ +#define PCIC_GPI_EN 0x04 /* GPI Enable */ +#define PCIC_GPI_TRANS 0x08 /* GPI Transition Control */ +#define PCIC_CDRES_EN 0x10 /* card detect resume enable */ +#define PCIC_SW_CD_INT 0x20 /* s/w card detect interrupt */ + +/* For Global Control register (PCIC_GLO_CTRL) */ +#define PCIC_PWR_DOWN 0x01 /* power down */ +#define PCIC_LVL_MODE 0x02 /* level mode interrupt enable */ +#define PCIC_WB_CSCINT 0x04 /* explicit write-back csc intr */ +#define PCIC_IRQ0_LEVEL 0x08 /* irq 14 pulse mode enable */ +#define PCIC_IRQ1_LEVEL 0x10 + +/* + * Mask of allowable interrupts. + * Ints are 3,4,5,7,9,10,11,12,14,15 + */ +#define PCIC_INT_MASK_ALLOWED 0xDEB8 + +#define PCIC_IO_WIN 2 +#define PCIC_MEM_WIN 5 + +#define PCIC_MAX_SLOTS 8 diff --git a/sys/pccard/pccard.c b/sys/pccard/pccard.c new file mode 100644 index 0000000..34d6339 --- /dev/null +++ b/sys/pccard/pccard.c @@ -0,0 +1,942 @@ +/* + * pccard.c - Interface code for PC-CARD controllers. + * + * June 1995, Andrew McRae (andrew@mega.com.au) + *------------------------------------------------------------------------- + * + * Copyright (c) 1995 Andrew McRae. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "crd.h" +#if NCRD > 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/errno.h> +#include <sys/file.h> +#include <sys/proc.h> +#include <sys/ioctl.h> +#include <sys/syslog.h> +#include <sys/devconf.h> +#include <sys/malloc.h> + +#include <i386/isa/isa.h> +#include <i386/isa/isa_device.h> +#include <i386/isa/icu.h> + +#include "apm.h" +#if NAPM > 0 +#include <machine/apm_bios.h> +#endif /* NAPM > 0 */ + +#include <pccard/card.h> +#include <pccard/slot.h> + +#define PCCARD_MEMSIZE (4*1024) + +#define MIN(a,b) ((a)<(b)?(a):(b)) + +/* + * cdevsw entry points + */ +int crdopen __P((dev_t dev, int oflags, int devtype, + struct proc *p)); +int crdclose __P((dev_t dev, int fflag, int devtype, + struct proc *p)); +int crdread __P((dev_t dev, struct uio *uio, int ioflag)); +int crdwrite __P((dev_t dev, struct uio *uio, int ioflag)); +int crdioctl __P((dev_t dev, int cmd, caddr_t data, + int fflag, struct proc *p)); +int crdselect __P((dev_t dev, int rw, struct proc *p)); + +static int allocate_driver(struct slot *, struct drv_desc *); +static void inserted(void *); +static void disable_slot(struct slot *); +static int invalid_io_memory(unsigned long, int); +static struct pccard_drv *find_driver(char *); +static void remove_device(struct pccard_dev *); +static void slot_irq_handler(int); +#if NAPM > 0 +/* + * For the APM stuff, the apmhook structure is kept + * separate from the slot structure so that the slot + * drivers do not need to know about the hooks (or the + * data structures). + */ +static int slot_suspend(struct slot *sp); +static int slot_resume(struct slot *sp); +static struct apmhook s_hook[MAXSLOT]; /* APM suspend */ +static struct apmhook r_hook[MAXSLOT]; /* APM resume */ +#endif /* NAPM > 0 */ + +void pcic_probe(); + +static struct slot *pccard_slots[MAXSLOT]; /* slot entries */ +static struct slot *slot_list; +static struct slot_cont *cont_list; +static struct pccard_drv *drivers; /* Card drivers */ +/* + * The driver interface for read/write uses a block + * of memory in the ISA I/O memory space allocated via + * an ioctl setting. + */ +static unsigned long pccard_mem; /* Physical memory */ +static unsigned char *pccard_kmem; /* Kernel virtual address */ +/* + * pccard_configure - called by autoconf code. + * Probes for various PC-CARD controllers, and + * initialises data structures to point to the + * various slots. + * + * Each controller indicates the number of slots + * that it sees, and these are mapped to a master + * slot number accessed via the character device entries. + */ +void +pccard_configure() +{ +struct slot_cont *cp; +struct slot *sp; + +#include "pcic.h" +#if NPCIC > 0 + pcic_probe(); +#endif +} +/* + * pccard_add_driver - Add a new driver to the list of + * drivers available for allocation. + */ +void +pccard_add_driver(struct pccard_drv *dp) +{ +/* + * If already loaded, then reject the driver. + */ + if (find_driver(dp->name)) + { + printf("Driver %s already loaded\n", dp->name); + return; + } + dp->next = drivers; + drivers = dp; +} +/* + * pccard_remove_driver - called to unlink driver + * from devices. Usually called when drivers are + * are unloaded from kernel. + */ +void +pccard_remove_driver(struct pccard_drv *dp) +{ +struct slot *sp; +struct pccard_dev *devp, *next; +struct pccard_drv *drvp; + + for (sp = slot_list; sp; sp = sp->next) + for (devp = sp->devices; devp; devp = next) + { + next = devp->next; + if (devp->drv == dp) + remove_device(devp); + } +/* + * Once all the devices belonging to this driver have been + * freed, then remove the driver from the list + * of registered drivers. + */ + if (drivers == dp) + drivers = dp->next; + else + for (drvp = drivers; drvp->next; drvp = drvp->next) + if (drvp->next == dp) + { + drvp->next = dp->next; + break; + } +} +/* + * pccard_remove_controller - Called when the slot + * driver is unloaded. The plan is to unload + * drivers from the slots, and then remove the + * slots from the slot list, and then finally + * remove the controller structure. Messy... + */ +void +pccard_remove_controller(struct slot_cont *cp) +{ +struct slot *sp, *next, *last = 0; +struct slot_cont *cl; +struct pccard_dev *dp; + + for (sp = slot_list; sp; sp = next) + { + next = sp->next; +/* + * If this slot belongs to this controller, + * remove this slot. + */ + if (sp->cinfo == cp) + { + pccard_slots[sp->slot] = 0; + if (sp->insert_timeout) + untimeout(inserted, (void *)sp); +/* + * Unload the drivers attached to this slot. + */ + while (dp = sp->devices) + remove_device(dp); +/* + * Disable the slot and unlink the slot from the slot list. + */ + disable_slot(sp); + if (last) + last->next = next; + else + slot_list = next; +#if NAPM > 0 + apm_hook_disestablish(APM_HOOK_SUSPEND, + &s_hook[sp->slot]); + apm_hook_disestablish(APM_HOOK_RESUME, + &r_hook[sp->slot]); +#endif + if (cp->extra && sp->cdata) + FREE(sp->cdata, M_DEVBUF); + FREE(sp, M_DEVBUF); +/* + * xx Can't use sp after we have freed it. + */ + } + else + last = sp; + } +/* + * Unlink controller structure from controller list. + */ + if (cont_list == cp) + cont_list = cp->next; + else + for (cl = cont_list; cl->next; cl = cl->next) + if (cl->next == cp) + { + cl->next = cp->next; + break; + } +} + +/* + * disable_slot - Disables the slot by removing + * the power and unmapping the I/O + */ +static void +disable_slot(struct slot *sp) +{ +int i; +struct pccard_dev *devp; +/* + * Unload all the drivers on this slot. Note we can't + * call remove_device from here, because this may be called + * from the event routine, which is called from the slot + * controller's ISR, and this could remove the device + * structure out in the middle of some driver activity. + * + * Note that a race condition is possible here; if a + * driver is accessing the device and it is removed, then + * all bets are off... + */ + for (devp = sp->devices; devp; devp = devp->next) + { + devp->drv->unload(devp); + devp->running = 0; + } +/* + * Power off the slot. + */ + sp->cinfo->disable(sp); +/* + * De-activate all contexts. + */ + for (i = 0; i < sp->cinfo->maxmem; i++) + if (sp->mem[i].flags & MDF_ACTIVE) + { + sp->mem[i].flags = 0; + (void)sp->cinfo->mapmem(sp, i); + } + for (i = 0; i < sp->cinfo->maxio; i++) + if (sp->io[i].flags & IODF_ACTIVE) + { + sp->io[i].flags = 0; + (void)sp->cinfo->mapio(sp, i); + } +} + +/* + * APM hooks for suspending and resuming. + */ +#if NAPM > 0 +static int +slot_suspend(struct slot *sp) +{ +struct pccard_dev *dp; + + for (dp = sp->devices; dp; dp = dp->next) + (void)dp->drv->suspend(dp); + sp->cinfo->disable(sp); + return(0); +} +static int +slot_resume(struct slot *sp) +{ +struct pccard_dev *dp; + + sp->cinfo->power(sp); + if (sp->irq) + sp->cinfo->mapirq(sp, sp->irq); + for (dp = sp->devices; dp; dp = dp->next) + (void)dp->drv->init(dp, 0); + return(0); +} +#endif /* NAPM > 0 */ +/* + * pccard_alloc_slot - Called from controller probe + * routine, this function allocates a new PC-CARD slot + * and initialises the data structures using the data provided. + * It returns the allocated structure to the probe routine + * to allow the controller specific data to be initialised. + */ +struct slot * +pccard_alloc_slot(struct slot_cont *cp) +{ +struct slot *sp; +int slotno; + + for (slotno = 0; slotno < MAXSLOT; slotno++) + if (pccard_slots[slotno] == 0) + break; + if (slotno >= MAXSLOT) + return(0); + MALLOC(sp, struct slot *, sizeof(*sp), M_DEVBUF, M_WAITOK); + bzero(sp, sizeof(*sp)); + if (cp->extra) + { + MALLOC(sp->cdata, void *, cp->extra, M_DEVBUF, M_WAITOK); + bzero(sp->cdata, cp->extra); + } + sp->cinfo = cp; + sp->slot = slotno; + pccard_slots[slotno] = sp; + sp->next = slot_list; + slot_list = sp; +/* + * If this controller hasn't been seen before, then + * link it into the list of controllers. + */ + if (cp->slots++ == 0) + { + cp->next = cont_list; + cont_list = cp; + if (cp->maxmem > NUM_MEM_WINDOWS) + cp->maxmem = NUM_MEM_WINDOWS; + if (cp->maxio > NUM_IO_WINDOWS) + cp->maxio = NUM_IO_WINDOWS; + printf("PC-Card %s (%d mem & %d I/O windows)\n", + cp->name, cp->maxmem, cp->maxio); + } +#if NAPM > 0 + { + struct apmhook *ap; + + ap = &s_hook[sp->slot]; + ap->ah_fun = slot_suspend; + ap->ah_arg = (void *) sp; + ap->ah_name = cp->name; + ap->ah_order = APM_MID_ORDER; + apm_hook_establish(APM_HOOK_SUSPEND, ap); + ap = &r_hook[sp->slot]; + ap->ah_fun = slot_resume; + ap->ah_arg = (void *) sp; + ap->ah_name = cp->name; + ap->ah_order = APM_MID_ORDER; + apm_hook_establish(APM_HOOK_RESUME, ap); + } +#endif /* NAPM > 0 */ + return(sp); +} +/* + * pccard_alloc_intr - allocate an interrupt from the + * free interrupts and return its number. The interrupts + * allowed are passed as a mask. + */ +int +pccard_alloc_intr(int imask, inthand2_t *hand, int unit, int *maskp) +{ +int rv, irq; +unsigned int mask; + + for (irq = 1; irq < 16; irq++) + { + mask = 1ul << irq; + if ((mask & imask) && + register_intr(irq, 0, 0, hand, maskp, unit)==0) + { + if (maskp) + INTRMASK (*maskp, mask); + + update_intr_masks(); + + INTREN (mask); + return(irq); + } + } + return(-1); +} +/* + * allocate_driver - Create a new device entry for this + * slot, and attach a driver to it. + */ +static int +allocate_driver(struct slot *sp, struct drv_desc *drvp) +{ +struct pccard_dev *devp; +struct pccard_drv *dp; +int err, irq = 0, s; + + dp = find_driver(drvp->name); + if (dp == 0) + return(ENXIO); +/* + * If an instance of this driver is already installed, + * but not running, then remove it. If it is running, + * then reject the request. + */ + for (devp = sp->devices; devp; devp = devp->next) + if (devp->drv == dp && devp->isahd.id_unit == drvp->unit) + { + if (devp->running) + return(EBUSY); + remove_device(devp); + break; + } +/* + * If an interrupt mask has been given, then check it + * against the slot interrupt (if one has been allocated). + */ + if (drvp->irqmask && dp->imask) + { + if ((sp->cinfo->irqs & drvp->irqmask)==0) + return(EINVAL); + if (sp->irq) + { + if (((1 << sp->irq)&drvp->irqmask)==0) + return(EINVAL); + sp->irqref++; + irq = sp->irq; + } +/* + * Attempt to allocate an interrupt. XXX We lose at the moment + * if the second device relies on a different interrupt mask. + */ + else + { + irq = pccard_alloc_intr(drvp->irqmask, + slot_irq_handler, (int)sp, dp->imask); + if (irq < 0) + return(EINVAL); + sp->irq = irq; + sp->irqref = 1; + sp->cinfo->mapirq(sp, sp->irq); + } + } + MALLOC(devp, struct pccard_dev *, sizeof(*devp), M_DEVBUF, M_WAITOK); + bzero(devp, sizeof(*devp)); +/* + * Create an entry for the device under this slot. + */ + devp->drv = dp; + devp->sp = sp; + devp->isahd.id_unit = drvp->unit; + devp->isahd.id_msize = drvp->memsize; + devp->isahd.id_iobase = drvp->iobase; + if (irq) + devp->isahd.id_irq = 1 << irq; + devp->isahd.id_flags = drvp->flags; +/* + * Convert the memory to kernel space. + */ + if (drvp->mem) + devp->isahd.id_maddr = (caddr_t)(drvp->mem + atdevbase - 0xA0000); + else + devp->isahd.id_maddr = 0; + devp->next = sp->devices; + sp->devices = devp; + s = splhigh(); + err = dp->init(devp, 1); + splx(s); +/* + * If the init functions returns no error, then the + * device has been successfully installed. If so, then + * attach it to the slot, otherwise free it and return + * the error. + */ + if (err) + remove_device(devp); + else + devp->running = 1; + return(err); +} +static void +remove_device(struct pccard_dev *dp) +{ +struct slot *sp = dp->sp; +struct pccard_dev *list; +int s; + +/* + * If an interrupt is enabled on this slot, + * then unregister it if no-one else is using it. + */ + s = splhigh(); + if (dp->running) + dp->drv->unload(dp); + if (dp->isahd.id_irq && --sp->irqref == 0) + { + sp->cinfo->mapirq(sp, 0); + unregister_intr(sp->irq, slot_irq_handler); + sp->irq = 0; + } + splx(s); +/* + * Remove from device list on this slot. + */ + if (sp->devices == dp) + sp->devices = dp->next; + else + for (list = sp->devices; list->next; list = list->next) + if (list->next == dp) + { + list->next = dp->next; + break; + } +/* + * Finally, free the memory space. + */ + FREE(dp, M_DEVBUF); +} +/* + * card insert routine - Called from a timeout to debounce + * insertion events. + */ +static void +inserted(void *arg) +{ +struct slot *sp = arg; + + sp->insert_timeout = 0; + sp->state = filled; +/* + * Enable 5V to the card so that the CIS can be read. + */ + sp->pwr.vcc = 50; + sp->pwr.vpp = 0; + sp->cinfo->power(sp); + printf("Card inserted, slot %d\n", sp->slot); +/* + * Now reset the card. + */ + sp->cinfo->reset(sp); +} +/* + * Card event callback. Called at splhigh to prevent + * device interrupts from interceding. + */ +void +pccard_event(struct slot *sp, enum card_event event) +{ +int s; + + if (sp->insert_timeout) + { + sp->insert_timeout = 0; + untimeout(inserted, (void *)sp); + } + switch(event) + { +/* + * The slot and devices are disabled, but the + * data structures are not unlinked. + */ + case card_removed: + if (sp->state == filled) + { + s = splhigh(); + disable_slot(sp); + sp->state = empty; + splx(s); + printf("Card removed, slot %d\n", sp->slot); + } + break; + case card_inserted: + sp->insert_timeout = 1; + timeout(inserted, (void *)sp, hz/4); + break; + } +} +/* + * slot_irq_handler - Interrupt handler for shared irq devices. + */ +static void +slot_irq_handler(int sp) +{ +struct pccard_dev *dp; + +/* + * For each device that has the shared interrupt, + * call the interrupt handler. If the interrupt was + * caught, the handler returns true. + */ + for (dp = ((struct slot *)sp)->devices; dp; dp = dp->next) + if (dp->isahd.id_irq && dp->running && dp->drv->handler(dp)) + return; + printf("Slot %d, unfielded interrupt (%d)\n", + ((struct slot *)sp)->slot, ((struct slot *)sp)->irq); +} +/* + * Device driver interface. + */ +int +crdopen(dev_t dev, int oflags, int devtype, struct proc *p) +{ +struct slot *sp; + + if (minor(dev) >= MAXSLOT) + return(ENXIO); + sp = pccard_slots[minor(dev)]; + if (sp==0) + return(ENXIO); + if (sp->rwmem == 0) + sp->rwmem = MDF_ATTR; + return(0); +} +/* + * Close doesn't de-allocate any resources, since + * slots may be assigned to drivers already. + */ +int +crdclose(dev_t dev, int fflag, int devtype, struct proc *p) +{ + return(0); +} +/* + * read interface. Map memory at lseek offset, + * then transfer to user space. + */ +int +crdread(dev_t dev, struct uio *uio, int ioflag) +{ +struct slot *sp = pccard_slots[minor(dev)]; +unsigned char *p; +int error = 0, win, count; +struct mem_desc *mp, oldmap; +unsigned int offs; + + if (sp == 0 || sp->state != filled) + return(ENXIO); + if (pccard_mem == 0) + return(ENOMEM); + for (win = 0; win < sp->cinfo->maxmem; win++) + if ((sp->mem[win].flags & MDF_ACTIVE)==0) + break; + if (win >= sp->cinfo->maxmem) + return(EBUSY); + mp = &sp->mem[win]; + oldmap = *mp; + mp->flags = sp->rwmem|MDF_ACTIVE; +#if 0 + printf("Rd at offs %d, size %d\n", (int)uio->uio_offset, + uio->uio_resid); +#endif + while (uio->uio_resid && error == 0) + { + mp->card = uio->uio_offset; + mp->size = PCCARD_MEMSIZE; + mp->start = (caddr_t)pccard_mem; + if (error = sp->cinfo->mapmem(sp, win)) + break; + offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1); + p = pccard_kmem + offs; + count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid); + error = uiomove(p, count, uio); + } +/* + * Restore original map. + */ + *mp = oldmap; + sp->cinfo->mapmem(sp, win); + + return(error); +} +/* + * crdwrite - Write data to card memory. + * Handles wrap around so that only one memory + * window is used. + */ +int +crdwrite(dev_t dev, struct uio *uio, int ioflag) +{ +struct slot *sp = pccard_slots[minor(dev)]; +unsigned char *p, c; +int error = 0, win, count; +struct mem_desc *mp, oldmap; +unsigned int offs; + + if (sp == 0 || sp->state != filled) + return(ENXIO); + if (pccard_mem == 0) + return(ENOMEM); + for (win = 0; win < sp->cinfo->maxmem; win++) + if ((sp->mem[win].flags & MDF_ACTIVE)==0) + break; + if (win >= sp->cinfo->maxmem) + return(EBUSY); + mp = &sp->mem[win]; + oldmap = *mp; + mp->flags = sp->rwmem|MDF_ACTIVE; +#if 0 + printf("Wr at offs %d, size %d\n", (int)uio->uio_offset, + uio->uio_resid); +#endif + while (uio->uio_resid && error == 0) + { + mp->card = uio->uio_offset; + mp->size = PCCARD_MEMSIZE; + mp->start = (caddr_t)pccard_mem; + if (error = sp->cinfo->mapmem(sp, win)) + break; + offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1); + p = pccard_kmem + offs; + count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid); +#if 0 + printf("Writing %d bytes to address 0x%x\n", count, p); +#endif + error = uiomove(p, count, uio); + } +/* + * Restore original map. + */ + *mp = oldmap; + sp->cinfo->mapmem(sp, win); + + return(error); +} +/* + * ioctl calls - allows setting/getting of memory and I/O + * descriptors, and assignment of drivers. + */ +int +crdioctl(dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p) +{ +int s; +struct slot *sp = pccard_slots[minor(dev)]; +struct mem_desc *mp; +struct io_desc *ip; + + if (sp == 0 && cmd != PIOCRWMEM) + return(ENXIO); + switch(cmd) + { + default: + if (sp->cinfo->ioctl) + return(sp->cinfo->ioctl(sp, cmd, data)); + return(EINVAL); + case PIOCGSTATE: + s = splhigh(); + ((struct slotstate *)data)->state = sp->state; + sp->laststate = sp->state; + splx(s); + ((struct slotstate *)data)->maxmem = sp->cinfo->maxmem; + ((struct slotstate *)data)->maxio = sp->cinfo->maxio; + ((struct slotstate *)data)->irqs = sp->cinfo->irqs; + break; +/* + * Get memory context. + */ + case PIOCGMEM: + s = ((struct mem_desc *)data)->window; + if (s < 0 || s >= sp->cinfo->maxmem) + return(EINVAL); + mp = &sp->mem[s]; + ((struct mem_desc *)data)->flags = mp->flags; + ((struct mem_desc *)data)->start = mp->start; + ((struct mem_desc *)data)->size = mp->size; + ((struct mem_desc *)data)->card = mp->card; + break; +/* + * Set memory context. If context already active, then unmap it. + * It is hard to see how the parameters can be checked. + * At the very least, we only allow root to set the context. + */ + case PIOCSMEM: + if (suser(p->p_ucred, &p->p_acflag)) + return(EPERM); + if (sp->state != filled) + return(ENXIO); + s = ((struct mem_desc *)data)->window; + if (s < 0 || s >= sp->cinfo->maxmem) + return(EINVAL); + sp->mem[s] = *((struct mem_desc *)data); + return(sp->cinfo->mapmem(sp, s)); +/* + * Get I/O port context. + */ + case PIOCGIO: + s = ((struct io_desc *)data)->window; + if (s < 0 || s >= sp->cinfo->maxio) + return(EINVAL); + ip = &sp->io[s]; + ((struct io_desc *)data)->flags = ip->flags; + ((struct io_desc *)data)->start = ip->start; + ((struct io_desc *)data)->size = ip->size; + break; +/* + * Set I/O port context. + */ + case PIOCSIO: + if (suser(p->p_ucred, &p->p_acflag)) + return(EPERM); + if (sp->state != filled) + return(ENXIO); + s = ((struct io_desc *)data)->window; + if (s < 0 || s >= sp->cinfo->maxio) + return(EINVAL); + sp->io[s] = *((struct io_desc *)data); + return(sp->cinfo->mapio(sp, s)); + break; +/* + * Set memory window flags for read/write interface. + */ + case PIOCRWFLAG: + sp->rwmem = *(int *)data; + break; +/* + * Set the memory window to be used for the read/write + * interface. + */ + case PIOCRWMEM: + if (*(unsigned long *)data == 0) + { + if (pccard_mem) + *(unsigned long *)data = pccard_mem; + break; + } + if (suser(p->p_ucred, &p->p_acflag)) + return(EPERM); +/* + * Validate the memory by checking it against the + * I/O memory range. It must also start on an aligned block size. + */ + if (invalid_io_memory(*(unsigned long *)data, PCCARD_MEMSIZE)) + return(EINVAL); + if (*(unsigned long *)data & (PCCARD_MEMSIZE-1)) + return(EINVAL); +/* + * Map it to kernel VM. + */ + pccard_mem = *(unsigned long *)data; + pccard_kmem = (unsigned char *)(pccard_mem + + atdevbase - 0xA0000); + break; +/* + * Set power values + */ + case PIOCSPOW: + sp->pwr = *(struct power *)data; + return(sp->cinfo->power(sp)); +/* + * Allocate a driver to this slot. + */ + case PIOCSDRV: + if (suser(p->p_ucred, &p->p_acflag)) + return(EPERM); + return(allocate_driver(sp, (struct drv_desc *)data)); + } + return(0); +} +/* + * select - Selects on exceptions will return true + * when a change in card status occurs. + */ +int +crdselect(dev_t dev, int rw, struct proc *p) +{ +int s; +struct slot *sp = pccard_slots[minor(dev)]; + + switch (rw) { + case FREAD: + return 1; + case FWRITE: + return 1; +/* + * select for exception - card event. + */ + case 0: + s = splhigh(); + if (sp == 0 || sp->laststate != sp->state) + { + splx(s); + return(1); + } + selrecord(p, &sp->selp); + splx(s); + } + return(0); +} +/* + * invalid_io_memory - verify that the ISA I/O memory block + * is a valid and unallocated address. + * A simple check of the range is done, and then a + * search of the current devices is done to check for + * overlapping regions. + */ +static int +invalid_io_memory(unsigned long adr, int size) +{ + if (adr < 0xC0000 || (adr+size) > 0x100000) + return(1); + return(0); +} +static struct pccard_drv * +find_driver(char *name) +{ +struct pccard_drv *dp; + + for (dp = drivers; dp; dp = dp->next) + if (strcmp(dp->name, name)==0) + return(dp); + return(0); +} + +#endif /* NCRD */ diff --git a/sys/pccard/pcic.c b/sys/pccard/pcic.c new file mode 100644 index 0000000..dbee979 --- /dev/null +++ b/sys/pccard/pcic.c @@ -0,0 +1,761 @@ +/* + * Intel PCIC or compatible Controller driver + * May be built using LKM to make a loadable module. + *------------------------------------------------------------------------- + * + * Copyright (c) 1995 Andrew McRae. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef LKM +#define NPCIC 1 +#else +#include "pcic.h" +#endif + +#if NPCIC > 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/errno.h> +#include <sys/file.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/sysent.h> +#include <sys/exec.h> +#include <sys/lkm.h> + +#include <machine/clock.h> + +#include <i386/isa/isa.h> +#include <i386/isa/isa_device.h> +#include <i386/isa/icu.h> + +#include <pccard/i82365.h> +#include <pccard/card.h> +#include <pccard/slot.h> + +/* + * Prototypes for interrupt handler. + */ +static void pcicintr __P((int unit)); +static int pcic_ioctl __P((struct slot *, int, caddr_t)); +static int pcic_power __P((struct slot *)); +static void pcic_reset __P((struct slot *)); +static void pcic_disable __P((struct slot *)); +static void pcic_mapirq __P((struct slot *, int)); + +/* + * Per-slot data table. + */ +static struct pcic_slot + { + int slot; /* My slot number */ + int index; /* Index register */ + int data; /* Data register */ + int offset; /* Offset value for index */ + char controller; /* Device type */ + char revision; /* Device Revision */ + struct slot *sp; /* Back ptr to slot */ + } pcic_slots[PCIC_MAX_SLOTS]; +static int pcic_irq; +static unsigned long pcic_imask; +static struct slot_cont cinfo; + +static int pcic_memory(struct slot *, int); +static int pcic_io(struct slot *, int); +int pcic_probe(); + +/* + * Internal inline functions for accessing the PCIC. + */ +/* + * Read a register from the PCIC. + */ +static inline unsigned char +getb (struct pcic_slot *sp, int reg) +{ + outb (sp->index, sp->offset + reg); + return inb (sp->data); +} + +/* + * Write a register on the PCIC + */ +static inline void +putb (struct pcic_slot *sp, int reg, unsigned char val) +{ + outb (sp->index, sp->offset + reg); + outb (sp->data, val); +} +/* + * Clear bit(s) of a register. + */ +static inline void +clrb(struct pcic_slot *sp, int reg, unsigned char mask) +{ + putb (sp, reg, getb (sp, reg) & ~mask); +} +/* + * Set bit(s) of a register + */ +static inline void +setb(struct pcic_slot *sp, int reg, unsigned char mask) +{ + putb (sp, reg, getb (sp, reg) | mask); +} + +/* + * Write a 16 bit value to 2 adjacent PCIC registers + */ +static inline void +putw (struct pcic_slot *sp, int reg, unsigned short word) +{ + putb (sp, reg, word & 0xFF); + putb (sp, reg + 1, (word >> 8) & 0xff); +} + + +/* + * Get a 16 bit value + */ +static unsigned short +getw (struct pcic_slot *sp, int reg) +{ + return (getb(sp, reg) | (getb(sp, reg+1) << 8)); +} +/* + * Loadable kernel module interface. + */ +#ifdef LKM +/* + * This defines the lkm_misc module use by modload + * to define the module name. + */ + MOD_MISC( "pcic") + + +static int pcic_unload(); +/* + * Module handler that processes loads and unloads. + * Once the module is loaded, the probe routine + * is called to install the slots (if any). + */ + +static int +pcic_handle( lkmtp, cmd) +struct lkm_table *lkmtp; +int cmd; +{ + int i; + struct lkm_misc *args = lkmtp->private.lkm_misc; + int err = 0; /* default = success*/ + + switch( cmd) { + case LKM_E_LOAD: + + /* + * Don't load twice! (lkmexists() is exported by kern_lkm.c) + */ + if( lkmexists( lkmtp)) + return( EEXIST); +/* + * Call the probe routine to find the slots. If + * no slots exist, then don't bother loading the module. + */ + if (pcic_probe() == 0) + return(ENODEV); + break; /* Success*/ +/* + * Attempt to unload the slot driver. + */ + case LKM_E_UNLOAD: + printf("Unloading PCIC driver\n"); + err = pcic_unload(); + break; /* Success*/ + + default: /* we only understand load/unload*/ + err = EINVAL; + break; + } + + return( err); +} + +/* + * External entry point; should generally match name of .o file. The + * arguments are always the same for all loaded modules. The "load", + * "unload", and "stat" functions in "DISPATCH" will be called under + * their respective circumstances unless their value is "nosys". If + * called, they are called with the same arguments (cmd is included to + * allow the use of a single function, ver is included for version + * matching between modules and the kernel loader for the modules). + * + * Since we expect to link in the kernel and add external symbols to + * the kernel symbol name space in a future version, generally all + * functions used in the implementation of a particular module should + * be static unless they are expected to be seen in other modules or + * to resolve unresolved symbols alread existing in the kernel (the + * second case is not likely to ever occur). + * + * The entry point should return 0 unless it is refusing load (in which + * case it should return an errno from errno.h). + */ +int +lkm_pcic(lkmtp, cmd, ver) +struct lkm_table *lkmtp; +int cmd; +int ver; +{ + DISPATCH(lkmtp,cmd,ver,pcic_handle,pcic_handle,nosys) +} +/* + * pcic_unload - Called when unloading a LKM. + * Disables interrupts and resets PCIC. + */ +static int +pcic_unload() +{ +int slot; +struct pcic_slot *cp = pcic_slots; + + if (pcic_irq) + { + for (slot = 0; slot < PCIC_MAX_SLOTS; slot++, cp++) + if (cp->sp) + putb(cp, PCIC_STAT_INT, 0); + unregister_intr(pcic_irq, pcicintr); + } + pccard_remove_controller(&cinfo); + return(0); +} +#endif LKM + +static void +pcic_print_regs (struct pcic_slot *sp) +{ + int i, j; + + for (i = 0; i < PCIC_SLOT_SIZE; i += 16) { + for (j = 0; j < 16; ++j) + printf ("%02x ", getb (sp, i + j)); + printf ("\n"); + } +} +#if 0 +static void +pcic_dump_attributes (unsigned char *scratch, int maxlen) +{ + int i,j,k; + + i = 0; + while (scratch[i] != 0xff && i < maxlen) { + unsigned char link = scratch[i+2]; + +/* + * Dump attribute memory + */ + if (scratch[i]) + { + printf ("[%02x] ", i); + for (j = 0; j < 2 * link + 4 && j < 128; j += 2) + printf ("%02x ", scratch[j + i]); + printf ("\n"); + } + i += 4 + 2 * link; + } +} +#endif + +/* + * entry point from main code to map/unmap memory context. + */ +static int +pcic_memory(struct slot *sp, int win) +{ +struct pcic_slot *cp = sp->cdata; +struct mem_desc *mp = &sp->mem[win]; +int reg = mp->window * PCIC_MEMSIZE + PCIC_MEMBASE; + + if (mp->flags & MDF_ACTIVE) + { + unsigned long sys_addr = (unsigned long)mp->start >> 12; +/* + * Write the addresses, card offsets and length. + * The values are all stored as the upper 12 bits of the + * 24 bit address i.e everything is allocated as 4 Kb chunks. + */ + putw (cp, reg, sys_addr & 0xFFF); + putw (cp, reg+2, (sys_addr + (mp->size >> 12) - 1) & 0xFFF); + putw (cp, reg+4, ((mp->card >> 12) - sys_addr) & 0x3FFF); +#if 0 + printf("card offs = 0x%x, sys_addr = 0x%x\n", ((mp->card >> 12) - sys_addr) & 0x3FFF, sys_addr); +#endif +/* + * Each 16 bit register has some flags in the upper bits. + */ + if (mp->flags & MDF_16BITS) + setb(cp, reg+1, PCIC_DATA16); + if (mp->flags & MDF_ZEROWS) + setb(cp, reg+1, PCIC_ZEROWS); + if (mp->flags & MDF_WS0) + setb(cp, reg+3, PCIC_MW0); + if (mp->flags & MDF_WS1) + setb(cp, reg+3, PCIC_MW1); + if (mp->flags & MDF_ATTR) + setb(cp, reg+5, PCIC_REG); + if (mp->flags & MDF_WP) + setb(cp, reg+5, PCIC_WP); +#if 0 + printf("Slot number %d, reg 0x%x, offs 0x%x\n", + cp->slot, reg, cp->offset); + printf("Map window to sys addr 0x%x for %d bytes, card 0x%x\n", + mp->start, mp->size, mp->card); + printf("regs are: 0x%02x%02x 0x%02x%02x 0x%02x%02x flags 0x%x\n", + getb(cp, reg), getb(cp, reg+1), + getb(cp, reg+2), getb(cp, reg+3), + getb(cp, reg+4), getb(cp, reg+5), + mp->flags); +#endif +/* + * Enable the memory window. By experiment, we need a delay. + */ + setb (cp, PCIC_ADDRWINE, (1<<win) | PCIC_MEMCS16); + DELAY(50); + } + else + { +#if 0 + printf("Unmapping window %d\n", win); +#endif + clrb (cp, PCIC_ADDRWINE, 1<<win); + putw (cp, reg, 0); + putw (cp, reg+2, 0); + putw (cp, reg+4, 0); + } + return(0); +} +/* + * pcic_io - map or unmap I/O context + */ +static int +pcic_io(struct slot *sp, int win) +{ +int mask, reg; +struct pcic_slot *cp = sp->cdata; +struct io_desc *ip = &sp->io[win]; + + if (win) + { + mask = PCIC_IO0_EN; + reg = PCIC_IO0; + } + else + { + mask = PCIC_IO1_EN; + reg = PCIC_IO1; + } + if (ip->flags & IODF_ACTIVE) + { + unsigned char x = 0; + + putw (cp, reg, ip->start); + putw (cp, reg+2, ip->start+ip->size-1); + if (ip->flags & IODF_ZEROWS) + x = PCIC_IO_0WS; + if (ip->flags & IODF_WS) + x |= PCIC_IO_WS; + if (ip->flags & IODF_CS16) + x |= PCIC_IO_CS16; + else if (ip->flags & IODF_16BIT) + x |= PCIC_IO_16BIT; +/* + * Extract the current flags and merge with new flags. + * Flags for window 0 in lower nybble, and in upper nybble + * for window 1. + */ + if (win) + putb(cp, PCIC_IOCTL, (x << 4) | + (getb(cp, PCIC_IOCTL) & 0xF)); + else + putb(cp, PCIC_IOCTL, x | (getb(cp, PCIC_IOCTL) & 0xF0)); + setb (cp, PCIC_ADDRWINE, mask); + DELAY(100); + } + else + { + clrb (cp, PCIC_ADDRWINE, mask); + putw (cp, reg, 0); + putw (cp, reg + 2, 0); + } + return(0); +} +/* + * Look for an Intel PCIC (or compatible). + * For each available slot, allocate a PC-CARD slot. + */ + +int +pcic_probe () +{ +int slot, i, validslots = 0; +struct slot *sp; +struct pcic_slot *cp; +unsigned char c; + +/* + * Initialise controller information structure. + */ + cinfo.mapmem = pcic_memory; + cinfo.mapio = pcic_io; + cinfo.ioctl = pcic_ioctl; + cinfo.power = pcic_power; + cinfo.mapirq = pcic_mapirq; + cinfo.reset = pcic_reset; + cinfo.disable = pcic_disable; + cinfo.maxmem = PCIC_MEM_WIN; + cinfo.maxio = PCIC_IO_WIN; + cinfo.irqs = PCIC_INT_MASK_ALLOWED; + +#ifdef LKM + bzero(pcic_slots, sizeof(pcic_slots)); +#endif + cp = pcic_slots; + for (slot = 0; slot < PCIC_MAX_SLOTS; slot++, cp++) { +/* + * Initialise the PCIC slot table. + */ + if (slot < 4) + { + cp->index = PCIC_INDEX_0; + cp->data = PCIC_DATA_0; + cp->offset = slot * PCIC_SLOT_SIZE; + } + else + { + cp->index = PCIC_INDEX_1; + cp->data = PCIC_DATA_1; + cp->offset = (slot - 4) * PCIC_SLOT_SIZE; + } + /* + * see if there's a PCMCIA controller here + * Intel PCMCIA controllers use 0x82 and 0x83 + * IBM clone chips use 0x88 and 0x89, apparently + */ + c = getb (cp, PCIC_ID_REV); + cp->revision = -1; + switch(c) + { +/* + * 82365 or clones. + */ + case 0x82: + case 0x83: + cp->controller = PCIC_I82365; + cp->revision = c & 1; +/* + * Now check for VADEM chips. + */ + outb(cp->index, 0x0E); + outb(cp->index, 0x37); + setb(cp, 0x3A, 0x40); + c = getb (cp, PCIC_ID_REV); + if (c & 0x08) + { + cp->controller = PCIC_VG468; + cp->revision = c & 7; + clrb(cp, 0x3A, 0x40); + } + break; +/* + * VLSI chips. + */ + case 0x84: + cp->controller = PCIC_VLSI; + break; + case 0x88: + case 0x89: + cp->controller = PCIC_IBM; + cp->revision = c & 1; + break; + default: + continue; + } +/* + * Check for Cirrus logic chips. + */ + putb(cp, 0x1F, 0); + c = getb(cp, 0x1F); + if ((c & 0xC0) == 0xC0) + { + c = getb(cp, 0x1F); + if ((c & 0xC0) == 0) + { + if (c & 0x20) + cp->controller = PCIC_PD672X; + else + cp->controller = PCIC_PD6710; + cp->revision = 8 - ((c & 0x1F) >> 2); + } + } + switch(cp->controller) + { + case PCIC_I82365: + cinfo.name = "Intel 82365"; + break; + case PCIC_IBM: + cinfo.name = "IBM PCIC"; + break; + case PCIC_PD672X: + cinfo.name = "Cirrus Logic PD672X"; + break; + case PCIC_PD6710: + cinfo.name = "Cirrus Logic PD6710"; + break; + case PCIC_VG468: + cinfo.name = "Vadem 468"; + break; + default: + cinfo.name = "Unknown!"; + break; + } +/* + * clear out the registers. + */ + for (i = 2; i < 0x40; i++) + putb(cp, i, 0); +/* + * OK it seems we have a PCIC or lookalike. + * Allocate a slot and initialise the data structures. + */ + validslots++; + cp->slot = slot; + sp = pccard_alloc_slot(&cinfo); + if (sp == 0) + continue; + sp->cdata = cp; + cp->sp = sp; +/* + * If we haven't allocated an interrupt for the controller, + * then attempt to get one. + */ + if (pcic_irq == 0) + { + pcic_irq = pccard_alloc_intr(PCIC_INT_MASK_ALLOWED, + pcicintr, 0, &pcic_imask); +#if 0 + for (try = 0; try < 16; try++) + if (((1 << try) & PCIC_INT_MASK_ALLOWED) && + !pccard_alloc_intr(try, pcicintr, 0, &tty_imask)) + { + pcic_irq = try; + break; + } +#endif + if (pcic_irq < 0) + printf("pcic: failed to allocate IRQ\n"); + } +/* + * Check for a card in this slot. + */ + setb (cp, PCIC_POWER, PCIC_APSENA | PCIC_DISRST); + if ((getb (cp, PCIC_STATUS) & PCIC_CD) != PCIC_CD) + sp->laststate = sp->state = empty; + else + { + sp->laststate = sp->state = filled; + pccard_event(cp->sp, card_inserted); + } +/* + * Assign IRQ for slot changes + */ + if (pcic_irq > 0) + putb(cp, PCIC_STAT_INT, (pcic_irq << 4) | 0xF); + } + return(validslots); +} +/* + * ioctl calls - Controller specific ioctls + */ +static int +pcic_ioctl(struct slot *sp, int cmd, caddr_t data) +{ +int s; + + switch(cmd) + { + default: + return(EINVAL); +/* + * Get/set PCIC registers + */ + case PIOCGREG: + ((struct pcic_reg *)data)->value = + getb(sp->cdata, ((struct pcic_reg *)data)->reg); + break; + case PIOCSREG: + putb(sp->cdata, ((struct pcic_reg *)data)->reg, + ((struct pcic_reg *)data)->value); + break; + } + return(0); +} +/* + * pcic_power - Enable the power of the slot according to + * the parameters in the power structure(s). + */ +static int +pcic_power(struct slot *slotp) +{ +unsigned char reg = PCIC_DISRST|PCIC_APSENA; +struct pcic_slot *sp = slotp->cdata; + + switch(sp->controller) + { + case PCIC_PD672X: + case PCIC_PD6710: + switch(slotp->pwr.vpp) + { + default: + return(EINVAL); + case 0: + break; + case 50: + case 33: + reg |= PCIC_VPP_5V; + break; + case 120: + reg |= PCIC_VPP_12V; + break; + } + switch(slotp->pwr.vcc) + { + default: + return(EINVAL); + case 0: + break; + case 33: + reg |= PCIC_VCC_5V; + setb(sp, 0x16, 0x02); + break; + case 50: + reg |= PCIC_VCC_5V; + clrb(sp, 0x16, 0x02); + break; + } + } + putb (sp, PCIC_POWER, reg); + DELAY(300*1000); + if (slotp->pwr.vcc) + { + reg |= PCIC_OUTENA; + putb (sp, PCIC_POWER, reg); + DELAY (100*000); + } + return(0); +} + +/* + * tell the PCIC which irq we want to use. only the following are legal: + * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 + */ +static void +pcic_mapirq (struct slot *slotp, int irq) +{ +struct pcic_slot *sp = slotp->cdata; + + if (irq == 0) + clrb(sp, PCIC_INT_GEN, 0xF); + else + putb (sp, PCIC_INT_GEN, (getb (sp, PCIC_INT_GEN) & 0xF0) | irq); +} +/* + * pcic_reset - Reset the card and enable initial power. + * Allow + */ +static void +pcic_reset(struct slot *slotp) +{ +struct pcic_slot *sp = slotp->cdata; + + clrb (sp, PCIC_INT_GEN, PCIC_CARDRESET); + DELAY (200*1000); + setb (sp, PCIC_INT_GEN, PCIC_CARDRESET|PCIC_IOCARD); + DELAY (200*1000); + if (sp->controller == PCIC_PD672X || + sp->controller == PCIC_PD6710) + { + putb(sp, PCIC_TIME_SETUP0, 0x1); + putb(sp, PCIC_TIME_CMD0, 0x6); + putb(sp, PCIC_TIME_RECOV0, 0x0); + putb(sp, PCIC_TIME_SETUP1, 1); + putb(sp, PCIC_TIME_CMD1, 0x5F); + putb(sp, PCIC_TIME_RECOV1, 0); + } +} +/* + * pcic_disable - Disable the slot. + */ +static void +pcic_disable(struct slot *slotp) +{ +struct pcic_slot *sp = slotp->cdata; + + putb(sp, PCIC_INT_GEN, 0); + putb(sp, PCIC_POWER, 0); +} + +/* + * PCIC Interrupt handler. + * Check each slot in turn, and read the card status change + * register. If this is non-zero, then a change has occurred + * on this card, so send an event to the main code. + */ +static void +pcicintr(int unit) +{ +int slot, s; +unsigned char chg; +struct pcic_slot *cp = pcic_slots; + + s = splhigh(); + for (slot = 0; slot < PCIC_MAX_SLOTS; slot++, cp++) + if (cp->sp) + if (chg = getb(cp, PCIC_STAT_CHG)) + if (chg & PCIC_CDTCH) + { + if ((getb(cp, PCIC_STATUS) & PCIC_CD) == + PCIC_CD) + pccard_event(cp->sp, + card_inserted); + else + pccard_event(cp->sp, + card_removed); + } + splx(s); +} +#endif diff --git a/sys/pccard/skel.c b/sys/pccard/skel.c new file mode 100644 index 0000000..dd5b503 --- /dev/null +++ b/sys/pccard/skel.c @@ -0,0 +1,189 @@ +/* + * Loadable kernel module skeleton driver + * 11 July 1995 Andrew McRae + * + *------------------------------------------------------------------------- + * + * Copyright (c) 1995 Andrew McRae. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/mount.h> +#include <sys/sysent.h> +#include <sys/exec.h> +#include <sys/lkm.h> +#include <sys/errno.h> + +#include <pccard/card.h> +#include <pccard/slot.h> + +/* + * This defines the lkm_misc module use by modload + * to define the module name. + */ + MOD_MISC( "skel") + + +static int skelintr(struct pccard_dev *); /* Interrupt handler */ +static void skelunload(struct pccard_dev *); /* Disable driver */ +static void skelsuspend(struct pccard_dev *); /* Suspend driver */ +static int skelinit(struct pccard_dev *, int); /* init device */ + +static struct pccard_drv skel_info = + { + "skel", + skelintr, + skelunload, + skelsuspend, + skelinit, + }; +static int opened; /* Rather minimal device state... */ + +/* + * Module handler that processes loads and unloads. + * Once the module is loaded, the add driver routine is called + * to register the driver. + * If an unload is requested the remove driver routine is + * called to deregister the driver before unloading. + */ +static int +skel_handle( lkmtp, cmd) +struct lkm_table *lkmtp; +int cmd; +{ + int i; + struct lkm_misc *args = lkmtp->private.lkm_misc; + int err = 0; /* default = success*/ + + switch( cmd) { + case LKM_E_LOAD: + + /* + * Don't load twice! (lkmexists() is exported by kern_lkm.c) + */ + if( lkmexists( lkmtp)) + return( EEXIST); +/* + * Now register the driver + */ + pccard_add_driver(&skel_info); + break; /* Success*/ +/* + * Attempt to deregister the driver. + */ + case LKM_E_UNLOAD: + pccard_remove_driver(&skel_info); + break; /* Success*/ + + default: /* we only understand load/unload*/ + err = EINVAL; + break; + } + + return( err); +} + + +/* + * External entry point; should generally match name of .o file. The + * arguments are always the same for all loaded modules. The "load", + * "unload", and "stat" functions in "DISPATCH" will be called under + * their respective circumstances unless their value is "nosys". If + * called, they are called with the same arguments (cmd is included to + * allow the use of a single function, ver is included for version + * matching between modules and the kernel loader for the modules). + * + * Since we expect to link in the kernel and add external symbols to + * the kernel symbol name space in a future version, generally all + * functions used in the implementation of a particular module should + * be static unless they are expected to be seen in other modules or + * to resolve unresolved symbols alread existing in the kernel (the + * second case is not likely to ever occur). + * + * The entry point should return 0 unless it is refusing load (in which + * case it should return an errno from errno.h). + */ +int +lkm_skel(lkmtp, cmd, ver) +struct lkm_table *lkmtp; +int cmd; +int ver; +{ + DISPATCH(lkmtp,cmd,ver,skel_handle,skel_handle,nosys) +} +/* + * Skeleton driver entry points for PCCARD configuration. + */ +/* + * The device entry is being removed. Shut it down, + * and turn off interrupts etc. Not called unless + * the device was successfully installed. + */ +static void +skelunload(struct pccard_dev *dp) +{ + printf("skel%d: unload\n", dp->unit); + opened &= ~(1 << dp->unit); +} +/* + * Called when a power down is wanted. Shuts down the + * device and configures the device as unavailable (but + * still loaded...). A resume is done by calling + * skelinit with first=0. + */ +static void +skelsuspend(struct pccard_dev *dp) +{ + printf("skel%d: suspending\n", dp->unit); +} +/* + * Initialize the device. + * if first is set, then initially check for + * the device's existence before initialising it. + * Once initialised, the device table may be set up. + */ +static int +skelinit(struct pccard_dev *dp, int first) +{ + if (first && ((1 << dp->unit)&opened)) + return(EBUSY); + if (first) + opened |= 1 << dp->unit; + printf("skel%d: init, first = %d\n", dp->unit, first); + printf("iomem = 0x%x, iobase = 0x%x\n", dp->memory, dp->ioaddr); + return(0); +} +/* + * Interrupt handler. + * Returns true if the interrupt is for us. + */ +static int +skelintr(struct pccard_dev *dp) +{ + return(0); +} diff --git a/sys/pccard/slot.h b/sys/pccard/slot.h new file mode 100644 index 0000000..fa29991 --- /dev/null +++ b/sys/pccard/slot.h @@ -0,0 +1,128 @@ +/* + * Slot structures for PC-CARD interface. + * Each slot has a controller specific structure + * attached to it. A slot number allows + * mapping from the character device to the + * slot structure. This is separate to the + * controller slot number to allow multiple controllers + * to be accessed. + *------------------------------------------------------------------------- + * + * Copyright (c) 1995 Andrew McRae. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Controller data - Specific to each slot controller. + */ +struct slot_cont + { + int (*mapmem)(); /* Map memory */ + int (*mapio)(); /* Map io */ + void (*reset)(); /* init */ + void (*disable)(); /* Disable slot */ + int (*power)(); /* Set power values */ + int (*ioctl)(); /* ioctl to lower level */ + void (*mapirq)(); /* Map interrupt number */ + int extra; /* Controller specific size */ + int maxmem; /* Number of allowed memory windows */ + int maxio; /* Number of allowed I/O windows */ + int irqs; /* IRQ's that are allowed */ + char *name; /* controller name */ +/* + * The rest is maintained by the mainline PC-CARD code. + */ + + struct slot_cont *next; /* Allows linked list of controllers */ + int slots; /* Slots available */ + }; +/* + * Driver structure - each driver registers itself + * with the mainline PC-CARD code. These drivers are + * then available for linking to the devices. + */ +struct pccard_dev; + +struct pccard_drv + { + char *name; /* Driver name */ + int (*handler)(struct pccard_dev *); /* Interrupt handler */ + void (*unload)(struct pccard_dev *); /* Disable driver */ + void (*suspend)(struct pccard_dev *); /* Suspend driver */ + int (*init)(struct pccard_dev *, int); /* init device */ + int attr; /* driver attributes */ + unsigned int *imask; /* Interrupt mask ptr */ + + struct pccard_drv *next; + }; +/* + * Device structure for cards. Each card may have one + * or more drivers attached to it; each driver is assumed + * to require at most one interrupt handler, one I/O block + * and one memory block. This structure is used to link the different + * devices together. + */ +struct pccard_dev + { + struct pccard_dev *next; /* List of drivers */ + struct isa_device isahd; /* Device details */ + struct pccard_drv *drv; + void *arg; /* Device argument */ + struct slot *sp; /* Back pointer to slot */ + int running; /* Current state of driver */ + }; + +/* + * Per-slot structure. + */ +struct slot + { + struct slot *next; /* Master list */ + int slot; /* Slot number */ + int flags; /* Slot flags (see below) */ + int rwmem; /* Read/write flags */ + int ex_sel; /* PID for select */ + int irq; /* IRQ allocated (0 = none) */ + int irqref; /* Reference count of driver IRQs */ + struct pccard_dev *devices; /* List of drivers attached */ +/* + * flags. + */ + unsigned int insert_timeout:1; /* Insert timeout active */ + + enum cardstate state, laststate; /* Current/last card states */ + struct selinfo selp; /* Info for select */ + struct mem_desc mem[NUM_MEM_WINDOWS]; /* Memory windows */ + struct io_desc io[NUM_IO_WINDOWS]; /* I/O windows */ + struct power pwr; /* Power values */ + struct slot_cont *cinfo; /* Per-controller data */ + void *cdata; /* Controller specific data */ + }; + +enum card_event { card_removed, card_inserted }; + +struct slot *pccard_alloc_slot(struct slot_cont *); +void pccard_event(struct slot *, enum card_event); +void pccard_remove_controller(struct slot_cont *); +int pccard_alloc_intr(); +void pccard_add_driver(struct pccard_drv *); |