📄 pcic.c
字号:
/* * 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. *//* * pcic98 : PC9801 original PCMCIA controller code for NS/A,Ne,NX/C,NR/L. * by Noriyuki Hosobuchi <yj8n-hsbc@asahi-net.or.jp> */#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/select.h>#include <machine/clock.h>#include <i386/isa/icu.h>#include <i386/isa/isa_device.h>#include <pccard/i82365.h>#ifdef PC98#include <pccard/pcic98reg.h>#endif#include <pccard/cardinfo.h>#include <pccard/driver.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 timeout_t pcic_reset;static void pcic_resume(struct slot *);static void pcic_disable __P((struct slot *));static void pcic_mapirq __P((struct slot *, int));static timeout_t pcictimeout;#ifdef LKMstatic int pcic_handle __P((struct lkm_table *lkmtp, int cmd));#endifstatic int pcic_memory(struct slot *, int);static int pcic_io(struct slot *, int);static u_int build_freelist(u_int);/* * Per-slot data table. */static struct pcic_slot { int slotnum; /* 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 *slt; /* Back ptr to slot */ u_char (*getb)(struct pcic_slot *, int); void (*putb)(struct pcic_slot *, int, u_char); u_char *regs; /* Pointer to regs in mem */} pcic_slots[PCIC_MAX_SLOTS];static int pcic_irq;static unsigned pcic_imask;static struct slot_ctrl cinfo;/* * Internal inline functions for accessing the PCIC. *//* * Read a register from the PCIC. */static __inline unsigned chargetb1(struct pcic_slot *sp, int reg){ outb(sp->index, sp->offset + reg); return inb(sp->data);}static __inline unsigned chargetb2(struct pcic_slot *sp, int reg){ return (sp->regs[reg]);}/* * Write a register on the PCIC */static __inline voidputb1(struct pcic_slot *sp, int reg, unsigned char val){ outb(sp->index, sp->offset + reg); outb(sp->data, val);}static __inline voidputb2(struct pcic_slot *sp, int reg, unsigned char val){ sp->regs[reg] = val;}/* * Clear bit(s) of a register. */static __inline voidclrb(struct pcic_slot *sp, int reg, unsigned char mask){ sp->putb(sp, reg, sp->getb(sp, reg) & ~mask);}/* * Set bit(s) of a register */static __inline voidsetb(struct pcic_slot *sp, int reg, unsigned char mask){ sp->putb(sp, reg, sp->getb(sp, reg) | mask);}/* * Write a 16 bit value to 2 adjacent PCIC registers */static __inline voidputw(struct pcic_slot *sp, int reg, unsigned short word){ sp->putb(sp, reg, word & 0xFF); sp->putb(sp, reg + 1, (word >> 8) & 0xff);}/* * Loadable kernel module interface. */#ifdef LKM/* * This defines the lkm_misc module use by modload * to define the module name. */MOD_MISC(pcic);/* * Module handler that processes loads and unloads. * Once the module is loaded, the probe routine * is called to install the slots (if any). */static intpcic_handle(struct lkm_table *lkmtp, int cmd){ 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(lkmtp, cmd); 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 "lkm_nullcmd". * 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). */intpcic_mod(struct lkm_table *lkmtp, int cmd, int ver){ MOD_DISPATCH(pcic, lkmtp, cmd, ver, pcic_handle, pcic_handle, lkm_nullcmd);}/* * pcic_unload - Called when unloading a LKM. * Disables interrupts and resets PCIC. */static intpcic_unload(struct lkm_table *lkmtp, int cmd){ int slot; struct pcic_slot *sp = pcic_slots; untimeout(pcictimeout, 0); if (pcic_irq) { for (slot = 0; slot < PCIC_MAX_SLOTS; slot++, sp++) { if (sp->slt) sp->putb(sp, PCIC_STAT_INT, 0); } unregister_intr(pcic_irq, pcicintr); } pccard_remove_controller(&cinfo); return(0);}#endif /* LKM */#if 0static voidpcic_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; }}#endifstatic voidnullfunc(int unit){ /* empty */}static u_intbuild_freelist(u_int pcic_mask){ int irq; u_int mask, freemask; /* No free IRQs (yet). */ freemask = 0; /* Walk through all of the IRQ's and find any that aren't allocated. */ for (irq = 1; irq < ICU_LEN; irq++) { /* * If the PCIC controller can't generate it, don't * bother checking to see if it it's free. */ mask = 1 << irq; if (!(mask & pcic_mask)) continue; /* See if the IRQ is free. */ if (register_intr(irq, 0, 0, nullfunc, NULL, irq) == 0) { /* Give it back, but add it to the mask */ INTRMASK(freemask, mask); unregister_intr(irq, nullfunc); } } #ifdef PCIC_DEBUG printf("Freelist of IRQ's <0x%x>\n", freemask);#endif return freemask; }/* * entry point from main code to map/unmap memory context. */static intpcic_memory(struct slot *slt, int win){ struct pcic_slot *sp = slt->cdata; struct mem_desc *mp = &slt->mem[win]; int reg = mp->window * PCIC_MEMSIZE + PCIC_MEMBASE;#ifdef PC98 if (sp->controller == PCIC_PC98) { if (mp->flags & MDF_ACTIVE) { /* slot = 0, window = 0, sys_addr = 0xda000, length = 8KB */ unsigned char x; if ((unsigned long)mp->start != 0xda000) { printf("sys_addr must be 0xda000. requested address = 0x%x\n", mp->start); return(EINVAL); } /* omajinai ??? */ outb(PCIC98_REG0, 0); x = inb(PCIC98_REG1); x &= 0xfc; x |= 0x02; outb(PCIC98_REG1, x); outw(PCIC98_REG_PAGOFS, 0); if (mp->flags & MDF_ATTR) { outb(PCIC98_REG6, inb(PCIC98_REG6) | PCIC98_ATTRMEM); }else{ outb(PCIC98_REG6, inb(PCIC98_REG6) & (~PCIC98_ATTRMEM)); } outb(PCIC98_REG_WINSEL, PCIC98_MAPWIN); #if 0 if (mp->flags & MDF_16BITS == 1) { /* 16bit */ outb(PCIC98_REG2, inb(PCIC98_REG2) & (~PCIC98_8BIT)); }else{ /* 8bit */ outb(PCIC98_REG2, inb(PCIC98_REG2) | PCIC98_8BIT); }#endif }else{ outb(PCIC98_REG_WINSEL, PCIC98_UNMAPWIN); } return 0; }#endif /* PC98 */ 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(sp, reg, sys_addr & 0xFFF); putw(sp, reg+2, (sys_addr + (mp->size >> 12) - 1) & 0xFFF); putw(sp, reg+4, ((mp->card >> 12) - sys_addr) & 0x3FFF);#if 0 printf("card offs = card_adr = 0x%x 0x%x, sys_addr = 0x%x\n", mp->card, ((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(sp, reg+1, PCIC_DATA16); if (mp->flags & MDF_ZEROWS) setb(sp, reg+1, PCIC_ZEROWS); if (mp->flags & MDF_WS0) setb(sp, reg+3, PCIC_MW0); if (mp->flags & MDF_WS1) setb(sp, reg+3, PCIC_MW1); if (mp->flags & MDF_ATTR) setb(sp, reg+5, PCIC_REG); if (mp->flags & MDF_WP) setb(sp, reg+5, PCIC_WP);#if 0 printf("Slot number %d, reg 0x%x, offs 0x%x\n", sp->slotnum, reg, sp->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", sp->getb(sp, reg), sp->getb(sp, reg+1), sp->getb(sp, reg+2), sp->getb(sp, reg+3), sp->getb(sp, reg+4), sp->getb(sp, reg+5), mp->flags);#endif /* * Enable the memory window. By experiment, we need a delay. */ setb(sp, PCIC_ADDRWINE, (1<<win) | PCIC_MEMCS16); DELAY(50); } else {#if 0 printf("Unmapping window %d\n", win);#endif clrb(sp, PCIC_ADDRWINE, 1<<win); putw(sp, reg, 0); putw(sp, reg+2, 0); putw(sp, reg+4, 0); } return(0);}/* * pcic_io - map or unmap I/O context */static intpcic_io(struct slot *slt, int win){ int mask, reg; struct pcic_slot *sp = slt->cdata; struct io_desc *ip = &slt->io[win];#ifdef PC98 if (sp->controller == PCIC_PC98) { unsigned char x;#if 0 if (win =! 0) { printf("pcic98:Illegal PCIC I/O window request(%d)!", win); return(EINVAL); }#endif if (ip->flags & IODF_ACTIVE) { unsigned short base; x = inb(PCIC98_REG2) & 0x0f; if (! (ip->flags & IODF_16BIT)) x |= PCIC98_8BIT; if (ip->size > 16) /* 128bytes mapping */ x |= PCIC98_MAP128; x |= PCIC98_IOMEMORY; outb(PCIC98_REG2, x); base = 0x80d0; outw(PCIC98_REG4, base); /* 98side IO base */ outw(PCIC98_REG5, ip->start); /* card side IO base */#ifdef PCIC_DEBUG printf("pcic98: IO mapped 0x%04x(98) -> 0x%04x(Card) and width %d bytes\n", base, ip->start, ip->size);#endif ip->start = base; }else{ outb(PCIC98_REG2, inb(PCIC98_REG2) & (~PCIC98_IOMEMORY)); } return 0; }#endif switch (win) { case 0: mask = PCIC_IO0_EN; reg = PCIC_IO0; break; case 1: mask = PCIC_IO1_EN; reg = PCIC_IO1; break; default: panic("Illegal PCIC I/O window request!"); } if (ip->flags & IODF_ACTIVE) { unsigned char x, ioctlv;#ifdef PCIC_DEBUGprintf("Map I/O 0x%x (size 0x%x) on Window %d\n", ip->start, ip->size, win);#endif /* PCIC_DEBUG */ putw(sp, reg, ip->start); putw(sp, reg+2, ip->start+ip->size-1); x = 0; 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; 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. */ ioctlv = sp->getb(sp, PCIC_IOCTL); DELAY(100); switch (win) { case 0: sp->putb(sp, PCIC_IOCTL, x | (ioctlv & 0xf0)); break; case 1: sp->putb(sp, PCIC_IOCTL, (x << 4) | (ioctlv & 0xf)); break; } DELAY(100); setb(sp, PCIC_ADDRWINE, mask); DELAY(100); } else { clrb(sp, PCIC_ADDRWINE, mask); DELAY(100); putw(sp, reg, 0); putw(sp, reg + 2, 0); } return(0);}/* * Look for an Intel PCIC (or compatible). * For each available slot, allocate a PC-CARD slot. *//* * VLSI 82C146 has incompatibilities about the I/O address * of slot 1. Assume it's the only PCIC whose vendor ID is 0x84, * contact Nate Williams <nate@FreeBSD.org> if incorrect. */intpcic_probe(void){ int slotnum, i, validslots = 0; u_int free_irqs; struct slot *slt; struct pcic_slot *sp; unsigned char c; static int maybe_vlsi = 0; /* Determine the list of free interrupts */ free_irqs = build_freelist(PCIC_INT_MASK_ALLOWED); /* * Initialise controller information structure.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -