From b18742d81bcf26a103f2aa0412d42fa9ef5c6f82 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Wed, 5 Sep 2001 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2001-09-05 --- bitsy/clock.c | 13 + pc/archmp.c | 2 + pc/clock.c | 12 + pc/dat.h | 18 + pc/devarch.c | 18 + pc/devi82365.c | 19 +- pc/devpccard.c | 1820 ++++++++++++++++++++++++++++++++++++++++++++++ pc/fns.h | 9 +- pc/i8259.c | 33 + pc/pci.c | 33 + pc/sd53c8xx.c | 3 + pc/sdata.c | 135 +++- pc/sdmylex.c | 3 + pc/trap.c | 24 + port/dev.c | 13 + port/devmnt.c | 1 - port/devsd.c | 686 ++++++++++++++--- port/lib.h | 1 + port/portclock.c | 13 - port/portfns.h | 2 + port/sd.h | 14 +- 21 files changed, 2735 insertions(+), 137 deletions(-) create mode 100644 pc/devpccard.c diff --git a/bitsy/clock.c b/bitsy/clock.c index e75a2cfbf0665e5be2de4d978625821fca3f45b1..667dff61938cb6ef67e0d5824059bcd1b0c27c50 100644 --- a/bitsy/clock.c +++ b/bitsy/clock.c @@ -135,3 +135,16 @@ void } } } + +ulong +TK2MS(ulong ticks) +{ + uvlong t, hz; + + t = ticks; + hz = HZ; + t *= 1000L; + t = t/hz; + ticks = t; + return ticks; +} diff --git a/pc/archmp.c b/pc/archmp.c index 145bfa0879e0cebc90f6aa2f04e612457783b7d5..e9c1fa7be17445add59c5f81eabffc341166ca28 100644 --- a/pc/archmp.c +++ b/pc/archmp.c @@ -98,6 +98,8 @@ PCArch archmp = { mpinit, /* intrinit */ mpintrenable, /* intrenable */ + 0, /* intrvecno */ + 0, /* intrdisable */ 0, /* clockenable */ }; diff --git a/pc/clock.c b/pc/clock.c index f20e88087ffce6b99738a71775da545bd07f4f72..f7266088aca71cc0b9a5a2fe9baf83924c893f90 100644 --- a/pc/clock.c +++ b/pc/clock.c @@ -41,3 +41,15 @@ microdelay(int microsecs) aamloop(microsecs); } +ulong +TK2MS(ulong ticks) +{ + uvlong t, hz; + + t = ticks; + hz = HZ; + t *= 1000L; + t = t/hz; + ticks = t; + return ticks; +} diff --git a/pc/dat.h b/pc/dat.h index cdffbc803fb74c54e79331b457c909e1a3dfe602..75de7f804723bd184975ecbf5ec881e1745f8a85 100644 --- a/pc/dat.h +++ b/pc/dat.h @@ -241,6 +241,8 @@ struct PCArch void (*intrinit)(void); int (*intrenable)(Vctl*); + int (*intrvecno)(int); + int (*intrdisable)(int); void (*clockenable)(void); uvlong (*fastclock)(uvlong*); @@ -278,3 +280,19 @@ Mach* machp[MAXMACH]; extern Mach *m; #define up (((Mach*)MACHADDR)->externup) + +/* + * hardware info about a device + */ +typedef struct { + ulong port; + int size; +} port_t; + +struct DevConf +{ + ulong interrupt; /* interrupt number */ + char *type; /* card type, malloced */ + int nports; /* Number of ports */ + port_t *ports; /* The ports themselves */ +}; diff --git a/pc/devarch.c b/pc/devarch.c index 7fbf69afaa4989701e45dd2116cad84ee341580d..f2b4029701aa40e113f7e8b0b2bb3f2945687bc9 100644 --- a/pc/devarch.c +++ b/pc/devarch.c @@ -52,6 +52,9 @@ static Dirtab archdir[Qmax] = { }; Lock archwlock; /* the lock is only for changing archdir */ int narchdir = Qbase; +int (*_pcmspecial)(char *, ISAConf *); +void (*_pcmspecialclose)(int); + /* * Add a file to the #P listing. Once added, you can't delete it. @@ -476,6 +479,8 @@ PCArch archgeneric = { i8259init, /* intrinit */ i8259enable, /* intrenable */ + i8259vecno, /* vector number */ + i8259disable, /* intrdisable */ i8253enable, /* clockenable */ @@ -723,3 +728,16 @@ fastticks(uvlong *hz) { return (*arch->fastclock)(hz); } + +int +pcmspecial(char *idstr, ISAConf *isa) +{ + return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1; +} + +void +pcmspecialclose(int a) +{ + if (_pcmspecialclose != nil) + _pcmspecialclose(a); +} diff --git a/pc/devi82365.c b/pc/devi82365.c index 612b05bc3548f3f7ea519957e1fa5b25806c1c6c..61486e692e3496ab4c86f8f39d6250bb7b88d153 100644 --- a/pc/devi82365.c +++ b/pc/devi82365.c @@ -348,8 +348,8 @@ decrefp(PCMslot *pp) /* * look for a card whose version contains 'idstr' */ -int -pcmspecial(char *idstr, ISAConf *isa) +static int +pcmcia_pcmspecial(char *idstr, ISAConf *isa) { PCMslot *pp; extern char *strstr(char*, char*); @@ -389,8 +389,8 @@ pcmspecial(char *idstr, ISAConf *isa) return -1; } -void -pcmspecialclose(int slotno) +static void +pcmcia_pcmspecialclose(int slotno) { PCMslot *pp; @@ -579,6 +579,17 @@ i82365reset(void) PCMslot *pp; char buf[32]; + if (!getconf("pcmcia0")) + return; + + if (_pcmspecial) { + print("#y: PCMCIA and CardBus at the same time?\n"); + return; + } + + _pcmspecial = pcmcia_pcmspecial; + _pcmspecialclose = pcmcia_pcmspecialclose; + if(already) return; already = 1; diff --git a/pc/devpccard.c b/pc/devpccard.c new file mode 100644 index 0000000000000000000000000000000000000000..3f66927f85854884e398fa61400e1fd289c7ea2a --- /dev/null +++ b/pc/devpccard.c @@ -0,0 +1,1820 @@ +/* + cardbus and pcmcia (grmph) support. +*/ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#define MAP(x,o) (Rmap + (x)*0x8 + o) + +enum { + TI_vid = 0x104c, + TI_1450_did = 0xAC1B, + + Ricoh_vid = 0x1180, + Ricoh_476_did = 0x0476, + + Nslots = 4, /* Maximum number of CardBus slots to use */ + + K = 1024, + M = K * K, + + LegacyAddr = 0x3e0, + NUMEVENTS = 10, +}; + +typedef struct { + ushort r_vid; + ushort r_did; + char *r_name; +} variant_t; + +static variant_t variant[] = { +{ Ricoh_vid, Ricoh_476_did, "Ricoh 476 PCI/Cardbus bridge", }, +{ TI_vid, TI_1450_did, "TI PCI-1450 Cardbus Controller", }, +}; + +/* Cardbus registers */ +enum { + SocketEvent = 0, + SocketMask = 1, + SocketState = 2, + SocketForce = 3, + SocketControl = 4, +}; + +enum { + SS_CCD = 3 << 1, + SS_POWER = 1 << 3, + SS_PC16 = 1 << 4, + SS_CBC = 1 << 5, + SS_NOTCARD = 1 << 7, + SS_BADVCC = 1 << 9, + SS_5V = 1 << 10, + SS_3V = 1 << 11, +}; + +enum { + SC_5V = 0x22, + SC_3V = 0x33, +}; + +enum { + SE_CCD = SS_CCD, + SE_POWER = SS_POWER, +}; + +enum { + PciPCR_IO = 1 << 0, + PciPCR_MEM = 1 << 1, + PciPCR_Master = 1 << 2, + + Nbars = 6, + Ncmd = 10, + CBIRQ = 9, + + PC16, + PC32, +}; + +enum { + Ti82365, + Tpd6710, + Tpd6720, + Tvg46x, +}; + +static char *chipname[] = { +[Ti82365] "Intel 82365SL", +[Tpd6710] "Cirrus Logic PD6710", +[Tpd6720] "Cirrus Logic PD6720", +[Tvg46x] "Vadem VG-46x", +}; + +/* + * Intel 82365SL PCIC controller for the PCMCIA or + * Cirrus Logic PD6710/PD6720 which is mostly register compatible + */ +enum +{ + /* + * registers indices + */ + Rid= 0x0, /* identification and revision */ + Ris= 0x1, /* interface status */ + Rpc= 0x2, /* power control */ + Foutena= (1<<7), /* output enable */ + Fautopower= (1<<5), /* automatic power switching */ + Fcardena= (1<<4), /* PC card enable */ + Rigc= 0x3, /* interrupt and general control */ + Fiocard= (1<<5), /* I/O card (vs memory) */ + Fnotreset= (1<<6), /* reset if not set */ + FSMIena= (1<<4), /* enable change interrupt on SMI */ + Rcsc= 0x4, /* card status change */ + Rcscic= 0x5, /* card status change interrupt config */ + Fchangeena= (1<<3), /* card changed */ + Fbwarnena= (1<<1), /* card battery warning */ + Fbdeadena= (1<<0), /* card battery dead */ + Rwe= 0x6, /* address window enable */ + Fmem16= (1<<5), /* use A23-A12 to decode address */ + Rio= 0x7, /* I/O control */ + Fwidth16= (1<<0), /* 16 bit data width */ + Fiocs16= (1<<1), /* IOCS16 determines data width */ + Fzerows= (1<<2), /* zero wait state */ + Ftiming= (1<<3), /* timing register to use */ + Riobtm0lo= 0x8, /* I/O address 0 start low byte */ + Riobtm0hi= 0x9, /* I/O address 0 start high byte */ + Riotop0lo= 0xa, /* I/O address 0 stop low byte */ + Riotop0hi= 0xb, /* I/O address 0 stop high byte */ + Riobtm1lo= 0xc, /* I/O address 1 start low byte */ + Riobtm1hi= 0xd, /* I/O address 1 start high byte */ + Riotop1lo= 0xe, /* I/O address 1 stop low byte */ + Riotop1hi= 0xf, /* I/O address 1 stop high byte */ + Rmap= 0x10, /* map 0 */ + + /* + * CL-PD67xx extension registers + */ + Rmisc1= 0x16, /* misc control 1 */ + F5Vdetect= (1<<0), + Fvcc3V= (1<<1), + Fpmint= (1<<2), + Fpsirq= (1<<3), + Fspeaker= (1<<4), + Finpack= (1<<7), + Rfifo= 0x17, /* fifo control */ + Fflush= (1<<7), /* flush fifo */ + Rmisc2= 0x1E, /* misc control 2 */ + Flowpow= (1<<1), /* low power mode */ + Rchipinfo= 0x1F, /* chip information */ + Ratactl= 0x26, /* ATA control */ + + /* + * offsets into the system memory address maps + */ + Mbtmlo= 0x0, /* System mem addr mapping start low byte */ + Mbtmhi= 0x1, /* System mem addr mapping start high byte */ + F16bit= (1<<7), /* 16-bit wide data path */ + Mtoplo= 0x2, /* System mem addr mapping stop low byte */ + Mtophi= 0x3, /* System mem addr mapping stop high byte */ + Ftimer1= (1<<6), /* timer set 1 */ + Mofflo= 0x4, /* Card memory offset address low byte */ + Moffhi= 0x5, /* Card memory offset address high byte */ + Fregactive= (1<<6), /* attribute memory */ + + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Creset= (1<<7), /* reset device */ + Clevel= (1<<6), /* level sensitive interrupt line */ +}; + +/* + * read and crack the card information structure enough to set + * important parameters like power + */ +/* cis memory walking */ +typedef struct Cisdat { + uchar *cisbase; + int cispos; + int cisskip; + int cislen; +} Cisdat; + +typedef struct { + char pi_verstr[512]; /* Version string */ + PCMmap pi_mmap[4]; /* maps, last is always for the kernel */ + ulong pi_conf_addr; /* Config address */ + uchar pi_conf_present; /* Config register present */ + int pi_nctab; /* In use configuration tables */ + PCMconftab pi_ctab[8]; /* Configuration tables */ + PCMconftab *pi_defctab; /* Default conftab */ + + int pi_port; /* Actual port usage */ + int pi_irq; /* Actual IRQ usage */ +} pcminfo_t; + +typedef struct { + QLock; + variant_t *cb_variant; /* Which CardBus chipset */ + Pcidev *cb_pci; /* The bridge itself */ + ulong *cb_regs; /* Cardbus registers */ + int cb_ltype; /* Legacy type */ + int cb_lindex; /* Legacy port index address */ + int cb_ldata; /* Legacy port data address */ + int cb_lbase; /* Base register for this socket */ + + int cb_state; /* Current state of card */ + int cb_type; /* Type of card */ + pcminfo_t cb_linfo; /* PCMCIA slot info */ + + int cb_refs; /* Number of refs to slot */ + QLock cb_refslock; /* inc/dev ref lock */ +} cb_t; + +static int managerstarted; + +enum { + Mshift= 12, + Mgran= (1<cb_state], messages[message]); + switch (cb->cb_state) { + case SlotEmpty: + + switch (message) { + case CardDetected: + cb->cb_state = SlotFull; + powerup(cb); + break; + case CardEjected: + break; + default: + print("#Y%d: Invalid message %s in SlotEmpty state\n", + (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotFull: + + switch (message) { + case CardPowered: + cb->cb_state = SlotPowered; + configure(cb); + break; + case CardEjected: + cb->cb_state = SlotEmpty; + powerdown(cb); + break; + default: + print("#Y%d: Invalid message %s in SlotFull state\n", + (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotPowered: + + switch (message) { + case CardConfigured: + cb->cb_state = SlotConfigured; + break; + case CardEjected: + cb->cb_state = SlotEmpty; + unconfigure(cb); + powerdown(cb); + break; + default: + print("#Y%d: Invalid message %s in SlotPowered state\n", + (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotConfigured: + + switch (message) { + case CardEjected: + cb->cb_state = SlotEmpty; + unconfigure(cb); + powerdown(cb); + break; + default: + print("#Y%d: Invalid message %s in SlotConfigured state\n", + (int)(cb - cbslots), messages[message]); + break; + } + break; + } +} + +static void +qengine(cb_t *cb, int message) +{ + qlock(cb); + engine(cb, message); + qunlock(cb); +} + +typedef struct { + cb_t *e_cb; + int e_message; +} events_t; + +static Lock levents; +static events_t events[NUMEVENTS]; +static Rendez revents; +static int nevents; + +static void +iengine(cb_t *cb, int message) +{ + if (nevents >= NUMEVENTS) { + print("#Y: Too many events queued, discarding request\n"); + return; + } + ilock(&levents); + events[nevents].e_cb = cb; + events[nevents].e_message = message; + nevents++; + iunlock(&levents); + wakeup(&revents); +} + +static int +eventoccured(void) +{ + return nevents > 0; +} + +static void +processevents(void *) +{ + while (1) { + int message; + cb_t *cb; + + sleep(&revents, (int (*)(void *))eventoccured, nil); + + cb = nil; + message = 0; + ilock(&levents); + if (nevents > 0) { + cb = events[0].e_cb; + message = events[0].e_message; + nevents--; + if (nevents > 0) + memmove(events, &events[1], nevents * sizeof(events_t)); + } + iunlock(&levents); + + if (cb) + qengine(cb, message); + } +} + +static void +interrupt(Ureg *, void *) +{ + int i; + + for (i = 0; i != nslots; i++) { + cb_t *cb = &cbslots[i]; + ulong event, state; + + event= cb->cb_regs[SocketEvent]; + state = cb->cb_regs[SocketState]; + rdreg(cb, Rcsc); /* Ack the interrupt */ + + // print("interrupt: slot %d, event %.8lX, state %.8lX, (%s)\n", + // (int)(cb - cbslots), event, state, states[cb->cb_state]); + + if (event & SE_CCD) { + cb->cb_regs[SocketEvent] |= SE_CCD; /* Ack interrupt */ + if (state & SE_CCD) + iengine(cb, CardEjected); + else + iengine(cb, CardDetected); + } + + if (event & SE_POWER) { + cb->cb_regs[SocketEvent] |= SE_POWER; /* Ack interrupt */ + iengine(cb, CardPowered); + } + } +} + +void +devpccardlink(void) +{ + static int initialized; + Pcidev *pci; + int i; + uchar intl; + + if (initialized) + return; + initialized = 1; + + if (!getconf("pccard0")) + return; + + _pcmspecial = pccard_pcmspecial; + _pcmspecialclose = pccard_pcmspecialclose; + + /* Allocate legacy space */ + if (ioalloc(LegacyAddr, 2, 0, "i82365.0") < 0) + print("#Y: WARNING: Cannot allocate legacy ports\n"); + + /* Find all CardBus controllers */ + pci = nil; + intl = (uchar)-1; + while ((pci = pcimatch(pci, 0, 0)) != nil) { + ulong baddr; + cb_t *cb; + int slot; + + for (i = 0; i != nelem(variant); i++) + if (pci->vid == variant[i].r_vid && pci->did == variant[i].r_did) + break; + if (i == nelem(variant)) + continue; + + /* initialize this slot */ + slot = nslots++; + cb = &cbslots[slot]; + + cb->cb_pci = pci; + cb->cb_variant = &variant[i]; + + if (intl != -1 && intl != pci->intl) + intrenable(pci->intl, interrupt, cb, pci->tbdf, "cardbus"); + intl = pci->intl; + baddr = pcicfgr32(cb->cb_pci, PciBAR0); + + cb->cb_regs = (ulong *)KADDR(upamalloc(baddr, 4096, 0)); + cb->cb_state = SlotEmpty; + + /* Don't really know what to do with this... */ + i82365probe(cb, LegacyAddr, LegacyAddr + 1); + + print("#Y%ld: %s, %.8ulX intl %d (%s%s)\n", cb - cbslots, + variant[i].r_name, baddr, pci->intl, + states[cb->cb_state], + (cb->cb_state == SlotEmpty)? "": + (cb->cb_type == PC16)? "PCMCIA": "CB"); + } + + if (nslots == 0) + return; + + for (i = 0; i != nslots; i++) { + cb_t *cb = &cbslots[i]; + + if ((cb->cb_regs[SocketState] & SE_CCD) == 0) + engine(cb, CardDetected); + } + + delay(500); /* Allow time for power up */ + + for (i = 0; i != nslots; i++) { + cb_t *cb = &cbslots[i]; + + if (cb->cb_regs[SocketState] & SE_POWER) + engine(cb, CardPowered); + + /* Enable interrupt on all events */ + cb->cb_regs[SocketMask] |= 0xF; + wrreg(cb, Rcscic, 0xC); + } +} + +static int +powerup(cb_t *cb) +{ + ulong state; + ushort bcr; + + if ((state = cb->cb_regs[SocketState]) & SS_PC16) { + + print("#Y%ld: Probed a PC16 card, powering up card\n", cb - cbslots); + cb->cb_type = PC16; + memset(&cb->cb_linfo, 0, sizeof(pcminfo_t)); + + /* power up and unreset, wait's are empirical (???) */ + wrreg(cb, Rpc, Fautopower|Foutena|Fcardena); + delay(300); + wrreg(cb, Rigc, 0); + delay(100); + wrreg(cb, Rigc, Fnotreset); + + return 1; + } + + if (cb->cb_regs[SocketState] & SS_CCD) + return 0; + + if ((state & SS_CBC) == 0 || (state & SS_NOTCARD)) { + print("#Y%ld: No cardbus card inserted\n", cb - cbslots); + return 0; + } + + if (state & SS_BADVCC) { + print("#Y%ld: Bad VCC request to card, powering down card!\n", + cb - cbslots); + cb->cb_regs[SocketControl] = 0; + return 0; + } + + if ((state & SS_3V) == 0 && (state & SS_5V) == 0) { + print("#Y%ld: Unsupported voltage, powering down card!\n", + cb - cbslots); + cb->cb_regs[SocketControl] = 0; + return 0; + } + + print("#Y%ld: card %spowered at %d volt\n", cb - cbslots, + (state & SS_POWER)? "": "not ", + (state & SS_3V)? 3: (state & SS_5V)? 5: -1); + + /* Power up the card + * and make sure the secondary bus is not in reset. + */ + cb->cb_regs[SocketControl] = (state & SS_5V)? SC_5V: SC_3V; + delay(50); + bcr = pcicfgr16(cb->cb_pci, PciBCR); + bcr &= ~0x40; + pcicfgw16(cb->cb_pci, PciBCR, bcr); + delay(100); + + cb->cb_type = PC32; + return 1; +} + +static void +powerdown(cb_t *cb) +{ + ushort bcr; + + if (cb->cb_type == PC16) { + + wrreg(cb, Rpc, 0); /* turn off card power */ + wrreg(cb, Rwe, 0); /* no windows */ + + cb->cb_type = -1; + return; + } + + bcr = pcicfgr16(cb->cb_pci, PciBCR); + bcr |= 0x40; + pcicfgw16(cb->cb_pci, PciBCR, bcr); + cb->cb_regs[SocketControl] = 0; + cb->cb_type = -1; +} + +static void +configure(cb_t *cb) +{ + int i; + Pcidev *pci; + + print("configuring slot %d (%s)\n", (int)(cb - cbslots), states[cb->cb_state]); + if (cb->cb_state == SlotConfigured) + return; + engine(cb, CardConfigured); + + delay(50); /* Emperically established */ + + if (cb->cb_type == PC16) { + i82365configure(cb); + return; + } + + /* Scan the CardBus for new PCI devices */ + pciscan(pcicfgr8(cb->cb_pci, PciSBN), &cb->cb_pci->bridge); + pci = cb->cb_pci->bridge; + while (pci) { + ulong size, bar; + int memindex, ioindex; + + /* Treat the found device as an ordinary PCI card. It seems that the + CIS is not always present in CardBus cards. XXX, need to support + multifunction cards */ + memindex = ioindex = 0; + for (i = 0; i != Nbars; i++) { + + if (pci->mem[i].size == 0) continue; + if (pci->mem[i].bar & 1) { + + // Allocate I/O space + if (ioindex > 1) { + print("#Y%ld: WARNING: Can only configure 2 I/O slots\n", cb - cbslots); + continue; + } + bar = ioreserve(-1, pci->mem[i].size, 0, "cardbus"); + pci->mem[i].bar = bar | 1; + pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), + pci->mem[i].bar); + pcicfgw16(cb->cb_pci, PciCBIBR0 + ioindex * 8, bar); + pcicfgw16(cb->cb_pci, PciCBILR0 + ioindex * 8, + bar + pci->mem[i].size - 1); + ioindex++; + continue; + } + + // Allocating memory space + if (memindex > 1) { + print("#Y%ld: WARNING: Can only configure 2 memory slots\n", cb - cbslots); + continue; + } + + bar = upamalloc(0, pci->mem[i].size, BY2PG); + pci->mem[i].bar = bar | (pci->mem[i].bar & 0x80); + pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), pci->mem[i].bar); + pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, bar); + pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, + bar + pci->mem[i].size - 1); + + if (pci->mem[i].bar & 0x80) + /* Enable prefetch */ + pcicfgw16(cb->cb_pci, PciBCR, + pcicfgr16(cb->cb_pci, PciBCR) | + (1 << (8 + memindex))); + + memindex++; + } + + if ((size = pcibarsize(pci, PciEBAR0)) > 0) { + + if (memindex > 1) + print("#Y%ld: WARNING: Too many memory spaces, not mapping ROM space\n", + cb - cbslots); + else { + pci->rom.bar = upamalloc(0, size, BY2PG); + pci->rom.size = size; + + pcicfgw32(pci, PciEBAR0, pci->rom.bar); + pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, + pci->rom.bar); + pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, + pci->rom.bar + pci->rom.size - 1); + } + } + + /* Set the basic PCI registers for the device */ + pcicfgw16(pci, PciPCR, + pcicfgr16(pci, PciPCR) | + PciPCR_IO|PciPCR_MEM|PciPCR_Master); + pcicfgw8(pci, PciCLS, 8); + pcicfgw8(pci, PciLTR, 64); + + if (pcicfgr8(pci, PciINTP)) { + pci->intl = pcicfgr8(cb->cb_pci, PciINTL); + pcicfgw8(pci, PciINTL, pci->intl); + + /* Route interrupts to INTA#/B# */ + pcicfgw16(cb->cb_pci, PciBCR, + pcicfgr16(cb->cb_pci, PciBCR) & ~(1 << 7)); + } + + pci = pci->list; + } +} + +static void +unconfigure(cb_t *cb) +{ + Pcidev *pci; + int i, ioindex, memindex; + + if (cb->cb_type == PC16) { + print("#Y%d: Don't know how to unconfigure a PC16 card\n", + (int)(cb - cbslots)); + + memset(&cb->cb_linfo, 0, sizeof(pcminfo_t)); + return; + } + + pci = cb->cb_pci->bridge; + if (pci == nil) + return; /* Not configured */ + cb->cb_pci->bridge = nil; + + memindex = ioindex = 0; + while (pci) { + Pcidev *_pci; + + for (i = 0; i != Nbars; i++) { + if (pci->mem[i].size == 0) continue; + if (pci->mem[i].bar & 1) { + iofree(pci->mem[i].bar & ~1); + pcicfgw16(cb->cb_pci, PciCBIBR0 + ioindex * 8, + (ushort)-1); + pcicfgw16(cb->cb_pci, PciCBILR0 + ioindex * 8, 0); + ioindex++; + continue; + } + + upafree(pci->mem[i].bar & ~0xF, pci->mem[i].size); + pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, + (ulong)-1); + pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 0); + pcicfgw16(cb->cb_pci, PciBCR, + pcicfgr16(cb->cb_pci, PciBCR) & + ~(1 << (8 + memindex))); + memindex++; + } + + if (pci->rom.bar && memindex < 2) { + upafree(pci->rom.bar & ~0xF, pci->rom.size); + pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, + (ulong)-1); + pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 0); + memindex++; + } + + _pci = pci->list; + free(_pci); + pci = _pci; + } +} + +static void +i82365configure(cb_t *cb) +{ + int this; + Cisdat cis; + PCMmap *m; + uchar type, link; + + /* + * Read all tuples in attribute space. + */ + m = isamap(cb, 0, 0, 1); + if(m == 0) + return; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = 2; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(;;){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + + switch(type){ + default: + break; + case 0x15: + tvers1(cb, &cis, type); + break; + case 0x1A: + tcfig(cb, &cis, type); + break; + case 0x1B: + tentry(cb, &cis, type); + break; + } + + if(link == 0xFF) + break; + cis.cispos = this + (2+link); + } + isaunmap(m); +} + +/* + * look for a card whose version contains 'idstr' + */ +static int +pccard_pcmspecial(char *idstr, ISAConf *isa) +{ + int i, irq; + PCMconftab *ct, *et; + pcminfo_t *pi; + cb_t *cb; + uchar x, we, *p; + + cb = nil; + for (i = 0; i != nslots; i++) { + cb = &cbslots[i]; + + qlock(cb); + if (cb->cb_state == SlotConfigured && + cb->cb_type == PC16 && + strstr(cb->cb_linfo.pi_verstr, idstr)) + break; + qunlock(cb); + } + + if (i == nslots) { + print("#Y: %s not found\n", idstr); + return -1; + } + + pi = &cb->cb_linfo; + + /* + * configure the PCMslot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ + irq = isa->irq; + if(irq == 2) + irq = 9; + + et = &pi->pi_ctab[pi->pi_nctab]; + ct = nil; + for(i = 0; i < isa->nopt; i++){ + int index; + char *cp; + + if(strncmp(isa->opt[i], "index=", 6)) + continue; + index = strtol(&isa->opt[i][6], &cp, 0); + if(cp == &isa->opt[i][6] || index >= pi->pi_nctab) { + qunlock(cb); + print("#Y%d: Cannot find index %d in conf table\n", + (int)(cb - cbslots), index); + return -1; + } + ct = &pi->pi_ctab[index]; + } + + if(ct == nil){ + PCMconftab *t; + + /* assume default is right */ + if(pi->pi_defctab) + ct = pi->pi_defctab; + else + ct = pi->pi_ctab; + + /* try for best match */ + if(ct->nio == 0 + || ct->io[0].start != isa->port || ((1<irqs) == 0){ + for(t = pi->pi_ctab; t < et; t++) + if(t->nio + && t->io[0].start == isa->port + && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0 || ((1<irqs) == 0){ + for(t = pi->pi_ctab; t < et; t++) + if(t->nio && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0){ + for(t = pi->pi_ctab; t < et; t++) + if(t->nio){ + ct = t; + break; + } + } + } + + if(ct == et || ct->nio == 0) { + qunlock(cb); + print("#Y%d: No configuration?\n", (int)(cb - cbslots)); + return -1; + } + if(isa->port == 0 && ct->io[0].start == 0) { + qunlock(cb); + print("#Y%d: No part or start address\n", (int)(cb - cbslots)); + return -1; + } + + /* route interrupts */ + isa->irq = irq; + wrreg(cb, Rigc, irq | Fnotreset | Fiocard); + + /* set power and enable device */ + x = vcode(ct->vpp1); + wrreg(cb, Rpc, x|Fautopower|Foutena|Fcardena); + + /* 16-bit data path */ + if(ct->bit16) + x = Ftiming|Fiocs16|Fwidth16; + else + x = Ftiming; + if(ct->nio == 2 && ct->io[1].start) + x |= x<<4; + wrreg(cb, Rio, x); + + /* + * enable io port map 0 + * the 'top' register value includes the last valid address + */ + if(isa->port == 0) + isa->port = ct->io[0].start; + we = rdreg(cb, Rwe); + wrreg(cb, Riobtm0lo, isa->port); + wrreg(cb, Riobtm0hi, isa->port>>8); + i = isa->port+ct->io[0].len-1; + wrreg(cb, Riotop0lo, i); + wrreg(cb, Riotop0hi, i>>8); + we |= 1<<6; + if(ct->nio == 2 && ct->io[1].start){ + wrreg(cb, Riobtm1lo, ct->io[1].start); + wrreg(cb, Riobtm1hi, ct->io[1].start>>8); + i = ct->io[1].start+ct->io[1].len-1; + wrreg(cb, Riotop1lo, i); + wrreg(cb, Riotop1hi, i>>8); + we |= 1<<7; + } + wrreg(cb, Rwe, we); + + /* only touch Rconfig if it is present */ + if(pi->pi_conf_present & (1<pi_conf_addr + Rconfig, 1, 1); + p = KADDR(m->isa + pi->pi_conf_addr + Rconfig - m->ca); + + /* set configuration and interrupt type */ + x = ct->index; + if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7)) + x |= Clevel; + *p = x; + delay(5); + + isaunmap(m); + } + + pi->pi_port = isa->port; + pi->pi_irq = isa->irq; + qunlock(cb); + + print("#Y%d: %s irq %ld, port %lX\n", (int)(cb - cbslots), pi->pi_verstr, isa->irq, isa->port); + return (int)(cb - cbslots); +} + +static void +pccard_pcmspecialclose(int slotno) +{ + cb_t *cb = &cbslots[slotno]; + + wrreg(cb, Rwe, 0); /* no windows */ +} + +static int +xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr) +{ + PCMmap *m; + Cisdat cis; + int i, l; + uchar *p; + uchar type, link, n, c; + int this, subtype; + cb_t *cb = &cbslots[slotno]; + + m = isamap(cb, 0, 0, attr); + if(m == 0) + return -1; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = attr ? 2 : 1; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(i = 0; i < 1000; i++){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + if(link == 0xFF) + break; + + n = link; + if (link > 1 && subtuple != -1) { + if (readc(&cis, &c) != 1) + break; + subtype = c; + n--; + } else + subtype = -1; + + if(type == tuple && subtype == subtuple) { + p = v; + for(l=0; lqid.path>>8)&0xff)) +#define TYPE(c) ((ulong)(c->qid.path&0xff)) +#define QID(s,t) (((s)<<8)|(t)) + +static int +pccardgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp) +{ + int slotno; + Qid qid; + long len; + int entry; + + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#Y", 0, eve, 0555, dp); + return 1; + } + + len = 0; + if(i >= Nents * nslots) return -1; + slotno = i / Nents; + entry = i % Nents; + if (entry == 0) { + qid.path = QID(slotno, Qctl); + snprint(up->genbuf, sizeof up->genbuf, "cb%dctl", slotno); + } + else { + /* Entries for memory regions. I'll implement them when + needed. (pb) */ + } + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, up->genbuf, len, eve, 0660, dp); + return 1; +} + +static Walkqid* +pccardwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, pccardgen); +} + +static int +pccardstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, pccardgen); +} + +static void +increfp(cb_t *cb) +{ + qlock(&cb->cb_refslock); + cb->cb_refs++; + qunlock(&cb->cb_refslock); +} + +static void +decrefp(cb_t *cb) +{ + qlock(&cb->cb_refslock); + cb->cb_refs--; + qunlock(&cb->cb_refslock); +} + +static Chan* +pccardopen(Chan *c, int omode) +{ + if (c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else + increfp(&cbslots[SLOTNO(c)]); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +pccardclose(Chan *c) +{ + if(c->flag & COPEN) + if((c->qid.type & QTDIR) == 0) + decrefp(&cbslots[SLOTNO(c)]); +} + +static long +pccardread(Chan *c, void *a, long n, vlong offset) +{ + cb_t *cb; + char *buf, *p, *e; + + switch(TYPE(c)){ + case Qdir: + return devdirread(c, a, n, 0, 0, pccardgen); + + case Qctl: + buf = p = malloc(READSTR); + buf[0] = 0; + e = p + READSTR; + + cb = &cbslots[SLOTNO(c)]; + qlock(cb); + p = seprint(p, e, "slot %ld: %s; ", cb - cbslots, states[cb->cb_state]); + + switch (cb->cb_type) { + case -1: + seprint(p, e, "\n"); + break; + + case PC32: + if (cb->cb_pci->bridge) { + Pcidev *pci = cb->cb_pci->bridge; + int i; + + while (pci) { + p = seprint(p, e, "%.4uX %.4uX; irq %d\n", + pci->vid, pci->did, pci->intl); + for (i = 0; i != Nbars; i++) + if (pci->mem[i].size) + p = seprint(p, e, + "\tmem[%d] %.8uX (%.8uX)\n", + i, pci->mem[i].bar, + pci->mem[i].size); + if (pci->rom.size) + p = seprint(p, e, "\tROM %.8uX (%.8uX)\n", i, + pci->rom.bar, pci->rom.size); + pci = pci->list; + } + } + break; + + case PC16: + if (cb->cb_state == SlotConfigured) { + pcminfo_t *pi = &cb->cb_linfo; + + p = seprint(p, e, "%s port %X; irq %d;\n", + pi->pi_verstr, pi->pi_port, + pi->pi_irq); + for (n = 0; n != pi->pi_nctab; n++) { + PCMconftab *ct; + int i; + + ct = &pi->pi_ctab[n]; + p = seprint(p, e, + "\tconfiguration[%d] irqs %.4X; vpp %d, %d; %s\n", + n, ct->irqs, ct->vpp1, ct->vpp2, + (ct == pi->pi_defctab)? "(default);": ""); + for (i = 0; i != ct->nio; i++) + if (ct->io[i].len > 0) + p = seprint(p, e, "\t\tio[%d] %.8lX %d\n", + i, ct->io[i].start, ct->io[i].len); + } + } + break; + } + qunlock(cb); + + n = readstr(offset, a, n, buf); + free(buf); + return n; + } + return 0; +} + +static long +pccardwrite(Chan *c, void *v, long n, vlong) +{ + Rune r; + ulong n0; + int i, nf; + char buf[255], *field[Ncmd], *device; + cb_t *cb; + + n0 = n; + switch(TYPE(c)){ + case Qctl: + cb = &cbslots[SLOTNO(c)]; + if(n > sizeof(buf)-1) n = sizeof(buf)-1; + memmove(buf, v, n); + buf[n] = '\0'; + + nf = getfields(buf, field, Ncmd, 1, " \t\n"); + for (i = 0; i != nf; i++) { + if (!strcmp(field[i], "down")) { + + if (i + 1 < nf && *field[i + 1] == '#') { + device = field[++i]; + device += chartorune(&r, device); + if ((n = devno(r, 1)) >= 0 && devtab[n]->config) + devtab[n]->config(0, device, nil); + } + qengine(cb, CardEjected); + } + else if (!strcmp(field[i], "power")) { + if ((cb->cb_regs[SocketState] & SS_CCD) == 0) + qengine(cb, CardDetected); + } + else + error(Ebadarg); + } + break; + } + return n0 - n; +} + +Dev pccarddevtab = { + 'Y', + "cardbus", + + devreset, + devinit, + pccardattach, + pccardwalk, + pccardstat, + pccardopen, + devcreate, + pccardclose, + pccardread, + devbread, + pccardwrite, + devbwrite, + devremove, + devwstat, +}; + +static PCMmap * +isamap(cb_t *cb, ulong offset, int len, int attr) +{ + uchar we, bit; + PCMmap *m, *nm; + pcminfo_t *pi; + int i; + ulong e; + + pi = &cb->cb_linfo; + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = ROUND(offset+len, Mgran); + offset &= Mmask; + len = e - offset; + + /* look for a map that covers the right area */ + we = rdreg(cb, Rwe); + bit = 1; + nm = 0; + for(m = pi->pi_mmap; m < &pi->pi_mmap[nelem(pi->pi_mmap)]; m++){ + if((we & bit)) + if(m->attr == attr) + if(offset >= m->ca && e <= m->cea){ + + m->ref++; + return m; + } + bit <<= 1; + if(nm == 0 && m->ref == 0) + nm = m; + } + m = nm; + if(m == 0) + return 0; + + /* if isa space isn't big enough, free it and get more */ + if(m->len < len){ + if(m->isa){ + umbfree(m->isa, m->len); + m->len = 0; + } + m->isa = PADDR(umbmalloc(0, len, Mgran)); + if(m->isa == 0){ + print("isamap: out of isa space\n"); + return 0; + } + m->len = len; + } + + /* set up new map */ + m->ca = offset; + m->cea = m->ca + m->len; + m->attr = attr; + i = m - pi->pi_mmap; + bit = 1<isa>>Mshift); + wrreg(cb, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit); + wrreg(cb, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift); + wrreg(cb, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8))); + offset -= m->isa; + offset &= (1<<25)-1; + offset >>= Mshift; + wrreg(cb, MAP(i, Mofflo), offset); + wrreg(cb, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0)); + wrreg(cb, Rwe, we | bit); /* enable map */ + m->ref = 1; + + return m; +} + +static void +isaunmap(PCMmap* m) +{ + m->ref--; +} + +/* + * reading and writing card registers + */ +static uchar +rdreg(cb_t *cb, int index) +{ + outb(cb->cb_lindex, cb->cb_lbase + index); + return inb(cb->cb_ldata); +} + +static void +wrreg(cb_t *cb, int index, uchar val) +{ + outb(cb->cb_lindex, cb->cb_lbase + index); + outb(cb->cb_ldata, val); +} + +static int +readc(Cisdat *cis, uchar *x) +{ + if(cis->cispos >= cis->cislen) + return 0; + *x = cis->cisbase[cis->cisskip*cis->cispos]; + cis->cispos++; + return 1; +} + +static ulong +getlong(Cisdat *cis, int size) +{ + uchar c; + int i; + ulong x; + + x = 0; + for(i = 0; i < size; i++){ + if(readc(cis, &c) != 1) + break; + x |= c<<(i*8); + } + return x; +} + +static void +tcfig(cb_t *cb, Cisdat *cis, int ) +{ + uchar size, rasize, rmsize; + uchar last; + pcminfo_t *pi; + + if(readc(cis, &size) != 1) + return; + rasize = (size&0x3) + 1; + rmsize = ((size>>2)&0xf) + 1; + if(readc(cis, &last) != 1) + return; + + pi = &cb->cb_linfo; + pi->pi_conf_addr = getlong(cis, rasize); + pi->pi_conf_present = getlong(cis, rmsize); +} + +static void +tvers1(cb_t *cb, Cisdat *cis, int ) +{ + uchar c, major, minor, last; + int i; + pcminfo_t *pi; + + pi = &cb->cb_linfo; + if(readc(cis, &major) != 1) + return; + if(readc(cis, &minor) != 1) + return; + last = 0; + for(i = 0; i < sizeof(pi->pi_verstr) - 1; i++){ + if(readc(cis, &c) != 1) + return; + if(c == 0) + c = ';'; + if(c == '\n') + c = ';'; + if(c == 0xff) + break; + if(c == ';' && last == ';') + continue; + pi->pi_verstr[i] = c; + last = c; + } + pi->pi_verstr[i] = 0; +} + +static ulong +microvolt(Cisdat *cis) +{ + uchar c; + ulong microvolts; + ulong exp; + + if(readc(cis, &c) != 1) + return 0; + exp = exponent[c&0x7]; + microvolts = vmant[(c>>3)&0xf]*exp; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + switch(c){ + case 0x7d: + break; /* high impedence when sleeping */ + case 0x7e: + case 0x7f: + microvolts = 0; /* no connection */ + break; + default: + exp /= 10; + microvolts += exp*(c&0x7f); + } + } + return microvolts; +} + +static ulong +nanoamps(Cisdat *cis) +{ + uchar c; + ulong nanoamps; + + if(readc(cis, &c) != 1) + return 0; + nanoamps = exponent[c&0x7]*vmant[(c>>3)&0xf]; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + if(c == 0x7d || c == 0x7e || c == 0x7f) + nanoamps = 0; + } + return nanoamps; +} + +/* + * only nominal voltage (feature 1) is important for config, + * other features must read card to stay in sync. + */ +static ulong +power(Cisdat *cis) +{ + uchar feature; + ulong mv; + + mv = 0; + if(readc(cis, &feature) != 1) + return 0; + if(feature & 1) + mv = microvolt(cis); + if(feature & 2) + microvolt(cis); + if(feature & 4) + microvolt(cis); + if(feature & 8) + nanoamps(cis); + if(feature & 0x10) + nanoamps(cis); + if(feature & 0x20) + nanoamps(cis); + if(feature & 0x40) + nanoamps(cis); + return mv/1000000; +} + +static ulong +ttiming(Cisdat *cis, int scale) +{ + uchar unscaled; + ulong nanosecs; + + if(readc(cis, &unscaled) != 1) + return 0; + nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10; + nanosecs = nanosecs * exponent[scale]; + return nanosecs; +} + +static void +timing(Cisdat *cis, PCMconftab *ct) +{ + uchar c, i; + + if(readc(cis, &c) != 1) + return; + i = c&0x3; + if(i != 3) + ct->maxwait = ttiming(cis, i); /* max wait */ + i = (c>>2)&0x7; + if(i != 7) + ct->readywait = ttiming(cis, i); /* max ready/busy wait */ + i = (c>>5)&0x7; + if(i != 7) + ct->otherwait = ttiming(cis, i); /* reserved wait */ +} + +static void +iospaces(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + int i, nio; + + ct->nio = 0; + if(readc(cis, &c) != 1) + return; + + ct->bit16 = ((c>>5)&3) >= 2; + if(!(c & 0x80)){ + ct->io[0].start = 0; + ct->io[0].len = 1<<(c&0x1f); + ct->nio = 1; + return; + } + + if(readc(cis, &c) != 1) + return; + + /* + * For each of the range descriptions read the + * start address and the length (value is length-1). + */ + nio = (c&0xf)+1; + for(i = 0; i < nio; i++){ + ct->io[i].start = getlong(cis, (c>>4)&0x3); + ct->io[i].len = getlong(cis, (c>>6)&0x3)+1; + } + ct->nio = nio; +} + +static void +irq(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + + if(readc(cis, &c) != 1) + return; + ct->irqtype = c & 0xe0; + if(c & 0x10) + ct->irqs = getlong(cis, 2); + else + ct->irqs = 1<<(c&0xf); + ct->irqs &= 0xDEB8; /* levels available to card */ +} + +static void +memspace(Cisdat *cis, int asize, int lsize, int host) +{ + ulong haddress, address, len; + + len = getlong(cis, lsize)*256; + address = getlong(cis, asize)*256; + USED(len, address); + if(host){ + haddress = getlong(cis, asize)*256; + USED(haddress); + } +} + +static void +tentry(cb_t *cb, Cisdat *cis, int ) +{ + uchar c, i, feature; + PCMconftab *ct; + pcminfo_t *pi; + + pi = &cb->cb_linfo; + if(pi->pi_nctab >= nelem(pi->pi_ctab)) + return; + if(readc(cis, &c) != 1) + return; + ct = &pi->pi_ctab[pi->pi_nctab++]; + + /* copy from last default config */ + if(pi->pi_defctab) + *ct = *pi->pi_defctab; + + ct->index = c & 0x3f; + + /* is this the new default? */ + if(c & 0x40) + pi->pi_defctab = ct; + + /* memory wait specified? */ + if(c & 0x80){ + if(readc(cis, &i) != 1) + return; + if(i&0x80) + ct->memwait = 1; + } + + if(readc(cis, &feature) != 1) + return; + switch(feature&0x3){ + case 1: + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 2: + power(cis); + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 3: + power(cis); + ct->vpp1 = power(cis); + ct->vpp2 = power(cis); + break; + default: + break; + } + if(feature&0x4) + timing(cis, ct); + if(feature&0x8) + iospaces(cis, ct); + if(feature&0x10) + irq(cis, ct); + switch((feature>>5)&0x3){ + case 1: + memspace(cis, 0, 2, 0); + break; + case 2: + memspace(cis, 2, 2, 0); + break; + case 3: + if(readc(cis, &c) != 1) + return; + for(i = 0; i <= (c&0x7); i++) + memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80); + break; + } +} + +static void +i82365probe(cb_t *cb, int lindex, int ldata) +{ + uchar c, id; + int dev = 0; /* According to the Ricoh spec 00->3F _and_ 80->BF seem + to be the same socket A (ditto for B). */ + + outb(lindex, Rid + (dev<<7)); + id = inb(ldata); + if((id & 0xf0) != 0x80) + return; /* not a memory & I/O card */ + if((id & 0x0f) == 0x00) + return; /* no revision number, not possible */ + + cb->cb_lindex = lindex; + cb->cb_ldata = ldata; + cb->cb_ltype = Ti82365; + cb->cb_lbase = (int)(cb - cbslots) * 0x40; + + switch(id){ + case 0x82: + case 0x83: + case 0x84: + /* could be a cirrus */ + outb(cb->cb_lindex, Rchipinfo + (dev<<7)); + outb(cb->cb_ldata, 0); + c = inb(cb->cb_ldata); + if((c & 0xc0) != 0xc0) + break; + c = inb(cb->cb_ldata); + if((c & 0xc0) != 0x00) + break; + if(c & 0x20){ + cb->cb_ltype = Tpd6720; + } else { + cb->cb_ltype = Tpd6710; + } + break; + } + + /* if it's not a Cirrus, it could be a Vadem... */ + if(cb->cb_ltype == Ti82365){ + /* unlock the Vadem extended regs */ + outb(cb->cb_lindex, 0x0E + (dev<<7)); + outb(cb->cb_lindex, 0x37 + (dev<<7)); + + /* make the id register show the Vadem id */ + outb(cb->cb_lindex, 0x3A + (dev<<7)); + c = inb(cb->cb_ldata); + outb(cb->cb_ldata, c|0xC0); + outb(cb->cb_lindex, Rid + (dev<<7)); + c = inb(cb->cb_ldata); + if(c & 0x08) + cb->cb_ltype = Tvg46x; + + /* go back to Intel compatible id */ + outb(cb->cb_lindex, 0x3A + (dev<<7)); + c = inb(cb->cb_ldata); + outb(cb->cb_ldata, c & ~0xC0); + } +} + +static int +vcode(int volt) +{ + switch(volt){ + case 5: + return 1; + case 12: + return 2; + default: + return 0; + } +} + diff --git a/pc/fns.h b/pc/fns.h index 504ac54695db99720545c8f8cde7ae57cd9a581c..e9030a9410f56a5c81b1c6a16b810f5fbf3e6620 100644 --- a/pc/fns.h +++ b/pc/fns.h @@ -37,8 +37,8 @@ ulong getcr2(void); ulong getcr3(void); ulong getcr4(void); char* getconf(char*); -int havecycintr(void); void halt(void); +int havecycintr(void); int i8042auxcmd(int); void i8042auxenable(void (*)(int, int)); void i8042reset(void); @@ -48,6 +48,8 @@ void i8253enable(void); uvlong i8253read(uvlong*); void i8259init(void); int i8259enable(Vctl*); +int i8259vecno(int); +int i8259disable(int); void idle(void); void idlehands(void); int inb(int); @@ -56,12 +58,14 @@ ushort ins(int); void inss(int, void*, int); ulong inl(int); void insl(int, void*, int); +void intrdisable(int, void (*)(Ureg *, void *), void*, int, char*); void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); void iofree(int); void ioinit(void); int iounused(int, int); int ioalloc(int, int, int, char*); int ioreserve(int, int, int, char*); +int iprint(char*, ...); int isaconfig(char*, int, ISAConf*); void kbdinit(void); #define kmapinval() @@ -93,6 +97,7 @@ int pcicfgr32(Pcidev*, int); void pcicfgw8(Pcidev*, int, int); void pcicfgw16(Pcidev*, int, int); void pcicfgw32(Pcidev*, int, int); +void pciclrbme(Pcidev*); void pcihinv(Pcidev*); Pcidev* pcimatch(Pcidev*, int, int); Pcidev* pcimatchtbdf(int); @@ -102,7 +107,9 @@ void pcmcisread(PCMslot*); int pcmcistuple(int, int, int, void*, int); PCMmap* pcmmap(int, ulong, int, int); int pcmspecial(char*, ISAConf*); +int (*_pcmspecial)(char *, ISAConf *); void pcmspecialclose(int); +void (*_pcmspecialclose)(int); void pcmunmap(int, PCMmap*); void printcpufreq(void); #define procrestore(p) diff --git a/pc/i8259.c b/pc/i8259.c index 4c3d905ae7044e19bddc20d6f0a705a5ac34df5f..562196b96fe5738eaa11a913ba7dbfdf716a879f 100644 --- a/pc/i8259.c +++ b/pc/i8259.c @@ -164,3 +164,36 @@ i8259enable(Vctl* v) return VectorPIC+irq; } + +int +i8259vecno(int irq) +{ + return VectorPIC+irq; +} + +int +i8259disable(int irq) +{ + int irqbit; + + /* + * Given an IRQ, disable the corresponding interrupt + * in the 8259. + */ + if(irq < 0 || irq > MaxIrqPIC){ + print("i8259disable: irq %d out of range\n", irq); + return -1; + } + irqbit = 1<>8) & 0xFF); + } + iunlock(&i8259lock); + return 0; +} diff --git a/pc/pci.c b/pc/pci.c index 6bcd245f277804787fc2f32d74114ec5e39a6fa4..10514849f5d29fb71740eda12c33b235c4db66b3 100644 --- a/pc/pci.c +++ b/pc/pci.c @@ -554,9 +554,32 @@ pcicfginit(void) list = &pciroot; for(bno = 0; bno <= pcimaxbno; bno++) { + int sbno = bno; bno = pcilscan(bno, list); + while(*list) list = &(*list)->link; + + if (sbno == 0) { + Pcidev *pci; + + /* + * If we have found a PCI-to-Cardbus bridge, make sure + * it has no valid mappings anymore. + */ + pci = pciroot; + while (pci) { + if (pci->ccrb == 6 && pci->ccru == 7) { + ushort bcr; + + /* reset the cardbus */ + bcr = pcicfgr16(pci, PciBCR); + pcicfgw16(pci, PciBCR, 0x40 | bcr); + pcicfgw16(pci, PciBCR, bcr); + } + pci = pci->link; + } + } } if(pciroot == nil) @@ -866,3 +889,13 @@ pcisetbme(Pcidev* p) pcr |= MASen; pcicfgw16(p, PciPCR, pcr); } + +void +pciclrbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr &= ~MASen; + pcicfgw16(p, PciPCR, pcr); +} diff --git a/pc/sd53c8xx.c b/pc/sd53c8xx.c index 99ce51ddac7ef3c11f2b7df51aef684e399a634a..658d41e92d2c417edfbe2d7b015e3124e5fb1936 100644 --- a/pc/sd53c8xx.c +++ b/pc/sd53c8xx.c @@ -2059,5 +2059,8 @@ SDifc sd53c8xxifc = { nil, /* wctl */ scsibio, /* bio */ + nil, /* probe */ + nil, /* clear */ + nil, /* stat */ }; diff --git a/pc/sdata.c b/pc/sdata.c index 8b69db0b4f906cec038ec0bb44224f2fe1b8e6e3..f3180a6a59a2a115e1cf1757862c83084aefe12f 100644 --- a/pc/sdata.c +++ b/pc/sdata.c @@ -199,11 +199,13 @@ typedef struct Ctlr { Pcidev* pcidev; void (*ienable)(Ctlr*); + void (*idisable)(Ctlr*); SDev* sdev; Drive* drive[2]; Prd* prdt; /* physical region descriptor table */ + void* prdtbase; QLock; /* current command */ Drive* curdrive; @@ -606,7 +608,7 @@ retry: atadmamode(drive); if(DEBUG & DbgCONFIG){ - print("dev %2.2uX port %uX config %4.4uX cap %4.4uX", + print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", dev, cmdport, iconfig, drive->info[Icapabilities]); print(" mwdma %4.4uX", drive->info[Imwdma]); @@ -642,9 +644,12 @@ ataprobe(int cmdport, int ctlport, int irq) Drive *drive; int dev, error, rhi, rlo; - if(ioalloc(cmdport, 8, 0, "atacmd") < 0) + if(ioalloc(cmdport, 8, 0, "atacmd") < 0) { + print("ataprobe: Cannot allocate %X\n", cmdport); return nil; + } if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){ + print("ataprobe: Cannot allocate %X\n", ctlport + As); iofree(cmdport); return nil; } @@ -744,11 +749,28 @@ tryedd1: free(drive); goto release; } + memset(ctlr, 0, sizeof(Ctlr)); if((sdev = malloc(sizeof(SDev))) == nil){ free(ctlr); free(drive); goto release; } + memset(sdev, 0, sizeof(SDev)); + if ((sdev->unit = (SDunit **)malloc(2 * sizeof(SDunit *))) == nil) { + free(sdev); + free(ctlr); + free(drive); + goto release; + } + memset(sdev->unit, 0, sizeof(SDunit *) * 2); + if ((sdev->unitflg = (int *)malloc(2 * sizeof(int))) == nil) { + free(sdev->unit); + free(sdev); + free(ctlr); + free(drive); + goto release; + } + memset(sdev->unitflg, 0, sizeof(int) * 2); drive->ctlr = ctlr; if(dev == Dev0){ ctlr->drive[0] = drive; @@ -788,6 +810,45 @@ tryedd1: return sdev; } +static void +ataclear(SDev *sdev) +{ + Ctlr* ctlr; + + ctlr = sdev->ctlr; + iofree(ctlr->cmdport); + iofree(ctlr->ctlport + As); + + if (ctlr->drive[0]) + free(ctlr->drive[0]); + if (ctlr->drive[1]) + free(ctlr->drive[1]); + if (sdev->name) + free(sdev->name); + free(sdev->unitflg); + free(sdev->unit); + free(ctlr); + free(sdev); +} + +static char * +atastat(SDev *sdev, char *p, char *e) +{ + Ctlr *ctlr = sdev->ctlr; + + return seprint(p, e, "%s ata port %X ctl %X irq %d\n", + sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq); +} + +static SDev* +ataprobew(DevConf *cf) +{ + if (cf->nports != 2) + error(Ebadarg); + + return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->interrupt); +} + static int atasetsense(Drive* drive, int status, int key, int asc, int ascq) { @@ -1676,8 +1737,10 @@ atapnp(void) continue; ctlr = sdev->ctlr; - if(ispc87415) + if(ispc87415) { ctlr->ienable = pc87415ienable; + print("pc87415disable: not yet implemented\n"); + } if(head != nil) tail->next = sdev; @@ -1698,6 +1761,42 @@ atapnp(void) } } +if(0){ + int port; + ISAConf isa; + + /* + * Hack for PCMCIA drives. + * This will be tidied once we figure out how the whole + * removeable device thing is going to work. + */ + memset(&isa, 0, sizeof(isa)); + isa.port = 0x180; /* change this for your machine */ + isa.irq = 11; /* change this for your machine */ + + port = isa.port+0x0C; + channel = pcmspecial("MK2001MPL", &isa); + if(channel == -1) + channel = pcmspecial("SunDisk", &isa); + if(channel == -1){ + isa.irq = 10; + channel = pcmspecial("CF", &isa); + } + if(channel == -1){ + isa.irq = 10; + channel = pcmspecial("OLYMPUS", &isa); + } + if(channel == -1){ + port = isa.port+0x204; + channel = pcmspecial("ATA/ATAPI", &isa); + } + if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){ + if(head != nil) + tail->next = sdev; + else + head = sdev; + } +} return head; } @@ -1756,9 +1855,12 @@ ataenable(SDev* sdev) ctlr = sdev->ctlr; if(ctlr->bmiba){ +#define ALIGN (4 * 1024) if(ctlr->pcidev != nil) pcisetbme(ctlr->pcidev); - ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024); + // ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024); + ctlr->prdtbase = xalloc(Nprd * sizeof(Prd) + ALIGN); + ctlr->prdt = (Prd *)(((ulong)ctlr->prdtbase + ALIGN) & ~(ALIGN - 1)); } snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); @@ -1769,6 +1871,26 @@ ataenable(SDev* sdev) return 1; } +static int +atadisable(SDev *sdev) +{ + Ctlr *ctlr; + char name[32]; + + ctlr = sdev->ctlr; + outb(ctlr->ctlport+Dc, Nien); /* disable interrupts */ + if (ctlr->idisable) + ctlr->idisable(ctlr); + snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); + intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); + if (ctlr->bmiba) { + if (ctlr->pcidev) + pciclrbme(ctlr->pcidev); + xfree(ctlr->prdtbase); + } + return 0; +} + static int atarctl(SDunit* unit, char* p, int l) { @@ -1875,7 +1997,7 @@ SDifc sdataifc = { atalegacy, /* legacy */ ataid, /* id */ ataenable, /* enable */ - nil, /* disable */ + atadisable, /* disable */ scsiverify, /* verify */ scsionline, /* online */ @@ -1884,4 +2006,7 @@ SDifc sdataifc = { atawctl, /* wctl */ scsibio, /* bio */ + ataprobew, /* probe */ + ataclear, /* clear */ + atastat, /* stat */ }; diff --git a/pc/sdmylex.c b/pc/sdmylex.c index 12a160cb69faaa765ae9503cef745ccfc2c6bd42..14099e6c7dde2ac25d91d4e910f055128f48c391 100644 --- a/pc/sdmylex.c +++ b/pc/sdmylex.c @@ -1238,4 +1238,7 @@ SDifc sdmylexifc = { nil, /* wctl */ scsibio, /* bio */ + nil, /* probe */ + nil, /* clear */ + nil, /* stat */ }; diff --git a/pc/trap.c b/pc/trap.c index a2fc341959a0a9dd9043a93bcab530c3517c59ab..1810073fbe79322e4a7ce818d81a055a531c197d 100644 --- a/pc/trap.c +++ b/pc/trap.c @@ -50,6 +50,30 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name) iunlock(&vctllock); } +void +intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name) +{ + Vctl **pv, *v; + int vno; + + vno = arch->intrvecno(irq); + ilock(&vctllock); + pv = &vctl[vno]; + while (*pv && + ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a || + strcmp((*pv)->name, name))) + pv = &((*pv)->next); + assert(*pv); + + v = *pv; + *pv = (*pv)->next; /* Link out the entry */ + + if (vctl[vno] == nil) + arch->intrdisable(irq); + iunlock(&vctllock); + xfree(v); +} + static long irqallocread(Chan*, void *vbuf, long n, vlong offset) { diff --git a/port/dev.c b/port/dev.c index ce649753bc6026f83e13a0bcf1679e2380a29323..ba198b74beac604ac39d4258be13a126f35d301c 100644 --- a/port/dev.c +++ b/port/dev.c @@ -388,3 +388,16 @@ devwstat(Chan*, uchar*, int) error(Eperm); return 0; } + +void +devpower(int) +{ + error(Eperm); +} + +int +devconfig(int, char *, DevConf *) +{ + error(Eperm); + return 0; +} diff --git a/port/devmnt.c b/port/devmnt.c index cd9a38a5743a3ea51ae8448a210dc1518d395a1f..6548b7a0f89d0169ffcda42f2f656ec21707f71c 100644 --- a/port/devmnt.c +++ b/port/devmnt.c @@ -670,7 +670,6 @@ mntread(Chan *c, void *buf, long n, vlong off) if(p != e) error(Esbadstat); } - return n; } diff --git a/port/devsd.c b/port/devsd.c index 329c82c8b439c0eaa3deba34ed17187dfe15ea7d..520aac89d51e3023ee72adcdbffeb339dc298ee7 100644 --- a/port/devsd.c +++ b/port/devsd.c @@ -15,11 +15,14 @@ extern Dev sddevtab; extern SDifc* sdifc[]; -static QLock sdqlock; -static SDev* sdlist; -static SDunit** sdunit; -static int* sdunitflg; -static int sdnunit; +typedef struct { + SDev *dt_dev; + int dt_nunits; /* num units in dev */ +} dev_t; + +static dev_t *devs; /* all devices */ +static QLock devslock; /* insertion and removal of devices */ +static int ndevs; /* total number of devices in the system */ enum { Rawcmd, @@ -30,6 +33,8 @@ enum { enum { Qtopdir = 1, /* top level directory */ Qtopbase, + Qtopctl = Qtopbase, + Qtopstat, Qunitdir, /* directory per unit */ Qunitbase, @@ -51,12 +56,22 @@ enum { NUnit = (1<>TypeSHIFT) & TypeMASK) #define PART(q) ((((ulong)(q).path)>>PartSHIFT) & PartMASK) #define UNIT(q) ((((ulong)(q).path)>>UnitSHIFT) & UnitMASK) -#define QID(u, p, t) (((u)<>DevSHIFT) & DevMASK) +#define QID(d,u, p, t) (((d)<idno == idno) + break; + + if (i == ndevs) + sdev = nil; + else { + sdev = devs[i].dt_dev; + incref(&sdev->r); + } + qunlock(&devslock); + return sdev; +} + static SDunit* sdgetunit(SDev* sdev, int subno) { - int index; SDunit *unit; char buf[32]; @@ -219,23 +254,27 @@ sdgetunit(SDev* sdev, int subno) * The device will be probed if it has not already been * successfully accessed. */ - qlock(&sdqlock); - index = sdev->index+subno; - unit = sdunit[index]; + qlock(&sdev->unitlock); + if (subno > sdev->nunit) { + qunlock(&sdev->unitlock); + return nil; + } + + unit = sdev->unit[subno]; if(unit == nil){ /* * Probe the unit only once. This decision * may be a little severe and reviewed later. */ - if(sdunitflg[index]){ - qunlock(&sdqlock); + if(sdev->unitflg[subno]){ + qunlock(&sdev->unitlock); return nil; } if((unit = malloc(sizeof(SDunit))) == nil){ - qunlock(&sdqlock); + qunlock(&sdev->unitlock); return nil; } - sdunitflg[index] = 1; + sdev->unitflg[subno] = 1; if(sdev->enabled == 0 && sdev->ifc->enable) sdev->ifc->enable(sdev); @@ -254,42 +293,21 @@ sdgetunit(SDev* sdev, int subno) * sdunit[] array. */ if(unit->dev->ifc->verify(unit) == 0){ - qunlock(&sdqlock); + qunlock(&sdev->unitlock); free(unit); return nil; } - sdunit[index] = unit; + sdev->unit[subno] = unit; } - qunlock(&sdqlock); - + qunlock(&sdev->unitlock); return unit; } -static SDunit* -sdindex2unit(int index) -{ - SDev *sdev; - - /* - * Associate a unit with a given index into the top-level - * device directory. - * The device will be probed if it has not already been - * successfully accessed. - */ - for(sdev = sdlist; sdev != nil; sdev = sdev->next){ - if(index >= sdev->index && index < sdev->index+sdev->nunit) - return sdgetunit(sdev, index-sdev->index); - } - - return nil; - -} - static void sdreset(void) { int i; - SDev *sdev, *tail; + SDev *sdev, *tail, *sdlist; /* * Probe all configured controllers and make a list @@ -297,7 +315,7 @@ sdreset(void) * of units attached and marking each device with an index * into the linear top-level directory array of units. */ - tail = nil; + tail = sdlist = nil; for(i = 0; sdifc[i] != nil; i++){ if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil) continue; @@ -305,14 +323,11 @@ sdreset(void) tail->next = sdev; else sdlist = sdev; - for(tail = sdev; tail->next != nil; tail = tail->next){ - sdev->index = sdnunit; - sdnunit += tail->nunit; - } - tail->index = sdnunit; - sdnunit += tail->nunit; + for(tail = sdev; tail->next != nil; tail = tail->next) + ndevs++; + ndevs++; } - + /* * Legacy and option code goes here. This will be hard... */ @@ -323,17 +338,8 @@ sdreset(void) * probed and structures allocated when attached. * Allocate controller names for the different types. */ - if(sdnunit == 0) - return; - if(sdnunit > NUnit) - sdnunit = NUnit; - if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil) - return; - if((sdunitflg = malloc(sdnunit*sizeof(int))) == nil){ - free(sdunit); - sdunit = nil; + if(ndevs == 0) return; - } for(i = 0; sdifc[i] != nil; i++){ /* * BUG: no check is made here or later when a @@ -342,6 +348,21 @@ sdreset(void) if(sdifc[i]->id) sdifc[i]->id(sdlist); } + + /* + * The IDs have been set, unlink the sdlist and copy the spec to + * the devtab. + */ + devs = (dev_t *)malloc(ndevs * sizeof(dev_t)); + memset(devs, 0, ndevs * sizeof(dev_t)); + i = 0; + while (sdlist != nil) { + devs[i].dt_dev = sdlist; + devs[i].dt_nunits = sdlist->nunit; + sdlist = sdlist->next; + devs[i].dt_dev->next = nil; + i++; + } } static int @@ -352,45 +373,69 @@ sd2gen(Chan* c, int i, Dir* dp) SDpart *pp; SDperm *perm; SDunit *unit; + SDev *sdev; + int rv; - unit = sdunit[UNIT(c->qid)]; + sdev = sdgetdev(DEV(c->qid)); + assert(sdev); + unit = sdev->unit[UNIT(c->qid)]; + + rv = -1; switch(i){ case Qctl: - mkqid(&q, QID(UNIT(c->qid), PART(c->qid), Qctl), unit->vers, QTFILE); + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl), + unit->vers, QTFILE); perm = &unit->ctlperm; if(emptystr(perm->user)){ kstrdup(&perm->user, eve); perm->perm = 0640; } devdir(c, q, "ctl", 0, perm->user, perm->perm, dp); - return 1; + rv = 1; + break; + case Qraw: - mkqid(&q, QID(UNIT(c->qid), PART(c->qid), Qraw), unit->vers, QTFILE); + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw), + unit->vers, QTFILE); perm = &unit->rawperm; if(emptystr(perm->user)){ kstrdup(&perm->user, eve); perm->perm = DMEXCL|0600; } devdir(c, q, "raw", 0, perm->user, perm->perm, dp); - return 1; + rv = 1; + break; + case Qpart: pp = &unit->part[PART(c->qid)]; l = (pp->end - pp->start) * (vlong)unit->secsize; - mkqid(&q, QID(UNIT(c->qid), PART(c->qid), Qpart), unit->vers+pp->vers, QTFILE); + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart), + unit->vers+pp->vers, QTFILE); if(emptystr(pp->user)) kstrdup(&pp->user, eve); devdir(c, q, pp->name, l, pp->user, pp->perm, dp); - return 1; - } - return -1; + rv = 1; + break; + } + + decref(&sdev->r); + return rv; } static int -sd1gen(Chan*, int i, Dir*) +sd1gen(Chan* c, int i, Dir* dp) { + Qid q; + switch(i){ - default: - return -1; + case Qtopctl: + mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE); + devdir(c, q, "sdctl", 0, eve, 0640, dp); + return 1; + case Qtopstat: + mkqid(&q, QID(0, 0, 0, Qtopstat), 0, QTFILE); + devdir(c, q, "sdstat", 0, eve, 0640, dp); + return 1; } return -1; } @@ -403,36 +448,70 @@ sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp) int i, r; SDpart *pp; SDunit *unit; + SDev *sdev; switch(TYPE(c->qid)){ - case Qtopdir: + case Qtopdir: { if(s == DEVDOTDOT){ - mkqid(&q, QID(s, 0, Qtopdir), 0, QTDIR); + mkqid(&q, QID(0, s, 0, Qtopdir), 0, QTDIR); sprint(up->genbuf, "#%C", sddevtab.dc); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } - if(s < sdnunit){ - if((unit = sdunit[s]) == nil){ - if((unit = sdindex2unit(s)) == nil) - return 0; - } - mkqid(&q, QID(s, 0, Qunitdir), 0, QTDIR); - if(emptystr(unit->user)) - kstrdup(&unit->user, eve); - devdir(c, q, unit->name, 0, unit->user, unit->perm, dp); - return 1; + + if (s == 0 || s == 1) + return sd1gen(c, s + Qtopbase, dp); + s -= 2; + + qlock(&devslock); + for (i = 0; i != ndevs; i++) { + if (s < devs[i].dt_nunits) + break; + s -= devs[i].dt_nunits; + } + + if (i == ndevs) { + /* Run of the end of the list */ + qunlock(&devslock); + return -1; } - s -= sdnunit; - return sd1gen(c, s+Qtopbase, dp); + + if ((sdev = devs[i].dt_dev) == nil) { + qunlock(&devslock); + return 0; + } + + incref(&sdev->r); + qunlock(&devslock); + + if((unit = sdev->unit[s]) == nil) + if((unit = sdgetunit(sdev, s)) == nil) { + decref(&sdev->r); + return 0; + } + + mkqid(&q, QID(sdev->idno, s, 0, Qunitdir), 0, QTDIR); + if(emptystr(unit->user)) + kstrdup(&unit->user, eve); + devdir(c, q, unit->name, 0, unit->user, unit->perm, dp); + decref(&sdev->r); + return 1; + } + case Qunitdir: if(s == DEVDOTDOT){ - mkqid(&q, QID(s, 0, Qtopdir), 0, QTDIR); + mkqid(&q, QID(0, s, 0, Qtopdir), 0, QTDIR); sprint(up->genbuf, "#%C", sddevtab.dc); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } - unit = sdunit[UNIT(c->qid)]; + + if ((sdev = sdgetdev(DEV(c->qid))) == nil) { + devdir(c, q, "unavailable", 0, eve, 0, dp); + return 1; + } + + unit = sdev->unit[UNIT(c->qid)]; qlock(&unit->ctl); /* @@ -450,32 +529,42 @@ sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp) if(i < Qpart){ r = sd2gen(c, i, dp); qunlock(&unit->ctl); + decref(&sdev->r); return r; } i -= Qpart; if(unit->part == nil || i >= unit->npart){ qunlock(&unit->ctl); + decref(&sdev->r); break; } pp = &unit->part[i]; if(!pp->valid){ qunlock(&unit->ctl); + decref(&sdev->r); return 0; } l = (pp->end - pp->start) * (vlong)unit->secsize; - mkqid(&q, QID(UNIT(c->qid), i, Qpart), unit->vers+pp->vers, QTFILE); + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart), + unit->vers+pp->vers, QTFILE); if(emptystr(pp->user)) kstrdup(&pp->user, eve); devdir(c, q, pp->name, l, pp->user, pp->perm, dp); qunlock(&unit->ctl); + decref(&sdev->r); return 1; case Qraw: case Qctl: case Qpart: - unit = sdunit[UNIT(c->qid)]; + if ((sdev = sdgetdev(DEV(c->qid))) == nil) { + devdir(c, q, "unavailable", 0, eve, 0, dp); + return 1; + } + unit = sdev->unit[UNIT(c->qid)]; qlock(&unit->ctl); r = sd2gen(c, TYPE(c->qid), dp); qunlock(&unit->ctl); + decref(&sdev->r); return r; default: break; @@ -490,11 +579,11 @@ sdattach(char* spec) Chan *c; char *p; SDev *sdev; - int idno, subno; + int idno, subno, i; - if(sdnunit == 0 || *spec == '\0'){ + if(ndevs == 0 || *spec == '\0'){ c = devattach(sddevtab.dc, spec); - mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR); + mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR); return c; } @@ -504,16 +593,23 @@ sdattach(char* spec) subno = strtol(&spec[3], &p, 0); if(p == &spec[3]) error(Ebadspec); - for(sdev = sdlist; sdev != nil; sdev = sdev->next){ - if(sdev->idno == idno && subno < sdev->nunit) + + qlock(&devslock); + for (sdev = nil, i = 0; i != ndevs; i++) + if ((sdev = devs[i].dt_dev) != nil && sdev->idno == idno) break; - } - if(sdev == nil || sdgetunit(sdev, subno) == nil) + + if (i == ndevs || subno >= sdev->nunit || sdgetunit(sdev, subno) == nil) { + qunlock(&devslock); error(Enonexist); + } + incref(&sdev->r); + qunlock(&devslock); c = devattach(sddevtab.dc, spec); - mkqid(&c->qid, QID(sdev->index+subno, 0, Qunitdir), 0, QTDIR); - c->dev = sdev->index+subno; + mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR); + c->dev = (sdev->idno << UnitLOG) + subno; + decref(&sdev->r); return c; } @@ -534,17 +630,23 @@ sdopen(Chan* c, int omode) { SDpart *pp; SDunit *unit; + SDev *sdev; + uchar tp; c = devopen(c, omode, 0, 0, sdgen); + if ((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart) + return c; + + sdev = sdgetdev(DEV(c->qid)); + if (sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + switch(TYPE(c->qid)){ - default: - break; case Qctl: - unit = sdunit[UNIT(c->qid)]; c->qid.vers = unit->vers; break; case Qraw: - unit = sdunit[UNIT(c->qid)]; c->qid.vers = unit->vers; if(!canlock(&unit->rawinuse)){ c->flag &= ~COPEN; @@ -553,7 +655,6 @@ sdopen(Chan* c, int omode) unit->state = Rawcmd; break; case Qpart: - unit = sdunit[UNIT(c->qid)]; qlock(&unit->ctl); if(waserror()){ qunlock(&unit->ctl); @@ -566,6 +667,7 @@ sdopen(Chan* c, int omode) poperror(); break; } + decref(&sdev->r); return c; } @@ -573,6 +675,7 @@ static void sdclose(Chan* c) { SDunit *unit; + SDev *sdev; if(c->qid.type & QTDIR) return; @@ -583,8 +686,12 @@ sdclose(Chan* c) default: break; case Qraw: - unit = sdunit[UNIT(c->qid)]; - unlock(&unit->rawinuse); + sdev = sdgetdev(DEV(c->qid)); + if (sdev) { + unit = sdev->unit[UNIT(c->qid)]; + unlock(&unit->rawinuse); + decref(&sdev->r); + } break; } } @@ -597,9 +704,15 @@ sdbio(Chan* c, int write, char* a, long len, vlong off) uchar *b; SDpart *pp; SDunit *unit; + SDev *sdev; ulong bno, max, nb, offset; - unit = sdunit[UNIT(c->qid)]; + sdev = sdgetdev(DEV(c->qid)); + if (sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + if (unit == nil) + error(Enonexist); nchange = 0; qlock(&unit->ctl); @@ -612,6 +725,7 @@ sdbio(Chan* c, int write, char* a, long len, vlong off) /* other errors; give up */ qunlock(&unit->ctl); + decref(&sdev->r); nexterror(); } pp = &unit->part[PART(c->qid)]; @@ -640,6 +754,7 @@ sdbio(Chan* c, int write, char* a, long len, vlong off) if(write) error(Eio); qunlock(&unit->ctl); + decref(&sdev->r); poperror(); return 0; } @@ -653,6 +768,8 @@ sdbio(Chan* c, int write, char* a, long len, vlong off) error(Enomem); if(waserror()){ sdfree(b); + if(!(unit->inquiry[1] & 0x80)) + decref(&sdev->r); /* gadverdamme! */ nexterror(); } @@ -696,6 +813,7 @@ sdbio(Chan* c, int write, char* a, long len, vlong off) poperror(); } + decref(&sdev->r); return len; } @@ -742,9 +860,10 @@ sdrio(SDreq* r, void* a, long n) static long sdread(Chan *c, void *a, long n, vlong off) { - char *p; + char *p, *e, *buf; SDpart *pp; SDunit *unit; + SDev *sdev; ulong offset; int i, l, status; @@ -752,11 +871,34 @@ sdread(Chan *c, void *a, long n, vlong off) switch(TYPE(c->qid)){ default: error(Eperm); + case Qtopstat: + p = buf = malloc(READSTR); + assert(p); + e = p + READSTR; + qlock(&devslock); + for (i = 0; i != ndevs; i++) { + SDev *sdev = devs[i].dt_dev; + + if (sdev->ifc->stat) + p = sdev->ifc->stat(sdev, p, e); + else + p = seprint(e, "%s; no statistics available\n", sdev->name); + } + qunlock(&devslock); + n = readstr(off, a, n, buf); + free(buf); + return n; + case Qtopdir: case Qunitdir: return devdirread(c, a, n, 0, 0, sdgen); + case Qctl: - unit = sdunit[UNIT(c->qid)]; + sdev = sdgetdev(DEV(c->qid)); + if (sdev == nil) + error(Enonexist); + + unit = sdev->unit[UNIT(c->qid)]; p = malloc(READSTR); l = snprint(p, READSTR, "inquiry %.48s\n", (char*)unit->inquiry+8); @@ -785,14 +927,21 @@ sdread(Chan *c, void *a, long n, vlong off) } } qunlock(&unit->ctl); + decref(&sdev->r); l = readstr(offset, a, n, p); free(p); return l; + case Qraw: - unit = sdunit[UNIT(c->qid)]; + sdev = sdgetdev(DEV(c->qid)); + if (sdev == nil) + error(Enonexist); + + unit = sdev->unit[UNIT(c->qid)]; qlock(&unit->raw); if(waserror()){ qunlock(&unit->raw); + decref(&sdev->r); nexterror(); } if(unit->state == Rawdata){ @@ -808,8 +957,10 @@ sdread(Chan *c, void *a, long n, vlong off) } else i = 0; qunlock(&unit->raw); + decref(&sdev->r); poperror(); return i; + case Qpart: return sdbio(c, 0, a, n, off); } @@ -817,24 +968,172 @@ sdread(Chan *c, void *a, long n, vlong off) return 0; } +typedef struct { + int o_on; + char *o_spec; + DevConf o_cf; +} confdata_t; + +static void +parse_switch(confdata_t *cd, char *option) +{ + if (!strcmp("on", option)) + cd->o_on = 1; + else if (!strcmp("off", option)) + cd->o_on = 0; + else + error(Ebadarg); +} + +static void +parse_spec(confdata_t *cd, char *option) +{ + if (strlen(option) > 1) + error(Ebadarg); + cd->o_spec = option; +} + +static port_t * +getnewport(DevConf *dc) +{ + port_t *p; + + p = (port_t *)malloc((dc->nports + 1) * sizeof(port_t)); + if (dc->nports > 0) { + memmove(p, dc->ports, dc->nports * sizeof(port_t)); + free(dc->ports); + } + dc->ports = p; + p = &dc->ports[dc->nports++]; + p->size = -1; + p->port = (ulong)-1; + return p; +} + +static void +parse_port(confdata_t *cd, char *option) +{ + char *e; + port_t *p; + + p = (cd->o_cf.nports == 0 || + cd->o_cf.ports[cd->o_cf.nports -1].port != (ulong)-1)? + getnewport(&cd->o_cf): &cd->o_cf.ports[cd->o_cf.nports - 1]; + p->port = strtol(option, &e, 0); + if (e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parse_size(confdata_t *cd, char *option) +{ + char *e; + port_t *p; + + p = (cd->o_cf.nports == 0 || cd->o_cf.ports[cd->o_cf.nports -1].size != -1)? + getnewport(&cd->o_cf): &cd->o_cf.ports[cd->o_cf.nports - 1]; + p->size = (int)strtol(option, &e, 0); + if (e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parse_irq(confdata_t *cd, char *option) +{ + char *e; + + cd->o_cf.interrupt = strtoul(option, &e, 0); + if (e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parse_type(confdata_t *cd, char *option) +{ + cd->o_cf.type = option; +} + +static struct { + char *option; + void (*parse)(confdata_t *, char *); +} options[] = { + { "switch", parse_switch, }, + { "spec", parse_spec, }, + { "port", parse_port, }, + { "size", parse_size, }, + { "irq", parse_irq, }, + { "type", parse_type, }, +}; + static long sdwrite(Chan *c, void *a, long n, vlong off) { Cmdbuf *cb; SDreq *req; SDunit *unit; + SDev *sdev; ulong end, start; switch(TYPE(c->qid)){ default: error(Eperm); + case Qtopctl: { + confdata_t cd; + char buf[256], *field[Ncmd]; + int nf, i, j; + + memset(&cd, 0, sizeof(confdata_t)); + if(n > sizeof(buf)-1) n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + cd.o_on = -1; + cd.o_spec = '\0'; + memset(&cd.o_cf, 0, sizeof(DevConf)); + + nf = getfields(buf, field, Ncmd, 1, " \t\n"); + for (i = 0; i < nf; i++) { + char *opt = field[i++]; + if (i >= nf) + error(Ebadarg); + for (j = 0; j != nelem(options); j++) + if (!strcmp(opt, options[j].option)) + break; + + if (j == nelem(options)) + error(Ebadarg); + options[j].parse(&cd, field[i]); + } + + if (cd.o_on < 0) + error(Ebadarg); + + if (cd.o_on) { + if (cd.o_spec == '\0' || cd.o_cf.nports == 0 || + cd.o_cf.interrupt == 0 || cd.o_cf.type == nil) + error(Ebadarg); + } + else { + if (cd.o_spec == '\0') + error(Ebadarg); + } + + if (sddevtab.config == nil) + error("No configuration function"); + sddevtab.config(cd.o_on, cd.o_spec, &cd.o_cf); + break; + } case Qctl: cb = parsecmd(a, n); - unit = sdunit[UNIT(c->qid)]; + sdev = sdgetdev(DEV(c->qid)); + if (sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; qlock(&unit->ctl); if(waserror()){ qunlock(&unit->ctl); + decref(&sdev->r); free(cb); nexterror(); } @@ -862,15 +1161,20 @@ sdwrite(Chan *c, void *a, long n, vlong off) else error(Ebadctl); qunlock(&unit->ctl); + decref(&sdev->r); poperror(); free(cb); break; case Qraw: - unit = sdunit[UNIT(c->qid)]; + sdev = sdgetdev(DEV(c->qid)); + if (sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; qlock(&unit->raw); if(waserror()){ qunlock(&unit->raw); + decref(&sdev->r); nexterror(); } switch(unit->state){ @@ -904,6 +1208,7 @@ sdwrite(Chan *c, void *a, long n, vlong off) n = sdrio(unit->req, a, n); } qunlock(&unit->raw); + decref(&sdev->r); poperror(); break; case Qpart: @@ -920,14 +1225,19 @@ sdwstat(Chan* c, uchar* dp, int n) SDpart *pp; SDperm *perm; SDunit *unit; + SDev *sdev; if(c->qid.type & QTDIR) - error(Eperm); + error(Eperm); - unit = sdunit[UNIT(c->qid)]; + sdev = sdgetdev(DEV(c->qid)); + if (sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; qlock(&unit->ctl); if(waserror()){ qunlock(&unit->ctl); + decref(&sdev->r); nexterror(); } @@ -957,10 +1267,162 @@ sdwstat(Chan* c, uchar* dp, int n) perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777); qunlock(&unit->ctl); + decref(&sdev->r); poperror(); return n; } +static char +getspec(char base) +{ + while (1) { + int i; + SDev *sdev; + + for (i = 0; i != ndevs; i++) + if ((sdev = devs[i].dt_dev) != nil && (char)sdev->idno == base) + break; + + if (i == ndevs) + return base; + base++; + } + return '\0'; +} + +static int +configure(char *spec, DevConf *cf) +{ + ISAConf isa; + dev_t *_devs; + SDev *tail, *sdev, *(*probe)(DevConf *); + char *p, name[32]; + int i, added_devs; + + if ((p = strchr(cf->type, '/')) != nil) + *p++ = '\0'; + + for(i = 0; sdifc[i] != nil; i++) + if(!strcmp(sdifc[i]->name, cf->type)) + break; + + if (sdifc[i] == nil) + error("type not found"); + + if ((probe = sdifc[i]->probe) == nil) + error("No probe function"); + + if (p) { + /* Try to find the card on the ISA bus. This code really belongs + in sdata and I'll move it later. Really! */ + memset(&isa, 0, sizeof(isa)); + isa.port = cf->ports[0].port; + isa.irq = cf->interrupt; + + if (pcmspecial(p, &isa) < 0) + error("Cannot find controller"); + } + + qlock(&devslock); + if (waserror()) { + qunlock(&devslock); + nexterror(); + } + + for (i = 0; i != ndevs; i++) + if ((sdev = devs[i].dt_dev) != nil && sdev->idno == *spec) + break; + if (i != ndevs) + error(Eexist); + + if ((sdev = (*probe)(cf)) == nil) + error("Cannot probe controller"); + poperror(); + + added_devs = 0; + tail = sdev; + while (tail) { + added_devs++; + tail = tail->next; + } + + _devs = (dev_t *)malloc((ndevs + added_devs) * sizeof(dev_t)); + memmove(_devs, devs, ndevs * sizeof(dev_t)); + free(devs); + devs = _devs; + + while (sdev) { + /* Assign `spec' to the device */ + *spec = getspec(*spec); + snprint(name, sizeof(name), "sd%c", *spec); + kstrdup(&sdev->name, name); + sdev->idno = *spec; + + devs[ndevs].dt_dev = sdev; + devs[ndevs].dt_nunits = sdev->nunit; + sdev = sdev->next; + devs[ndevs].dt_dev->next = nil; + ndevs++; + } + + qunlock(&devslock); + return 0; +} + +static int +unconfigure(char *spec) +{ + int i; + SDev *sdev; + + qlock(&devslock); + if (waserror()) { + qunlock(&devslock); + nexterror(); + } + + for (sdev = nil, i = 0; i != ndevs; i++) + if ((sdev = devs[i].dt_dev) != nil && sdev->idno == *spec) + break; + + if (i == ndevs) + error(Enonexist); + + if (sdev->r.ref) + error(Einuse); + + /* make sure no interrupts arrive anymore before removing resources */ + if (sdev->enabled && sdev->ifc->disable) + sdev->ifc->disable(sdev); + + /* we're alone and the device tab is locked; make the device unavailable */ + memmove(&devs[i], &devs[ndevs - 1], sizeof(dev_t)); + memset(&devs[ndevs - 1], 0, sizeof(dev_t)); + ndevs--; + + qunlock(&devslock); + poperror(); + + for (i = 0; i != sdev->nunit; i++) + if (sdev->unit[i]) { + SDunit *unit = sdev->unit[i]; + + free(unit->name); + free(unit->user); + free(unit); + } + + if (sdev->ifc->clear) + sdev->ifc->clear(sdev); + return 0; +} + +static int +sdconfig(int on, char *spec, DevConf *cf) +{ + return on? configure(spec, cf): unconfigure(spec); +} + Dev sddevtab = { 'S', "sd", @@ -979,4 +1441,6 @@ Dev sddevtab = { devbwrite, devremove, sdwstat, + devpower, + sdconfig, }; diff --git a/port/lib.h b/port/lib.h index b88814c5bf6b4b7a180225967cfb5047c7315e02..c562769ff5b9394ddcaf1313a4529fd1eb529e2e 100644 --- a/port/lib.h +++ b/port/lib.h @@ -122,6 +122,7 @@ typedef struct Waitmsg Waitmsg; #define QTDIR 0x80 /* type bit for directories */ #define QTAPPEND 0x40 /* type bit for append only files */ #define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ #define QTAUTH 0x08 /* type bit for authentication file */ #define QTFILE 0x00 /* plain file */ diff --git a/port/portclock.c b/port/portclock.c index c546fc0a83bbc27b920ea3f4da400394107e93e7..edc1917470ba0fa0aba72db21aae37dea74f34bb 100644 --- a/port/portclock.c +++ b/port/portclock.c @@ -86,16 +86,3 @@ portclock(Ureg *ur) segclock(ur->pc); } } - -ulong -TK2MS(ulong ticks) -{ - uvlong t, hz; - - t = ticks; - hz = HZ; - t *= 1000L; - t = t/hz; - ticks = t; - return ticks; -} diff --git a/port/portfns.h b/port/portfns.h index 36ab2804ccf0e922ec459b766353eee2fd568a0f..619f194c2b590974e0d5bce5d70f7f13f7511229 100644 --- a/port/portfns.h +++ b/port/portfns.h @@ -60,6 +60,7 @@ Chan* devattach(int, char*); Block* devbread(Chan*, long, ulong); long devbwrite(Chan*, Block*, ulong); Chan* devclone(Chan*); +int devconfig(int, char *, DevConf *); void devcreate(Chan*, char*, int, ulong); void devdir(Chan*, Qid, char*, vlong, char*, long, Dir*); long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*); @@ -68,6 +69,7 @@ void devinit(void); int devno(int, int); Chan* devopen(Chan*, int, Dirtab*, int, Devgen*); void devpermcheck(char*, ulong, int); +void devpower(int); void devremove(Chan*); void devreset(void); int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); diff --git a/port/sd.h b/port/sd.h index 55dcf8a43bb9cf22ae7e6d5b21ceed878a8461fc..f779c256a3ae7802d6b7ab2713d23b8017c76f4d 100644 --- a/port/sd.h +++ b/port/sd.h @@ -46,17 +46,24 @@ typedef struct SDunit { Log log; } SDunit; +/* + * Each controller is represented by a SDev, each controller is responsible + * for allocating its unit structures. + */ typedef struct SDev { + Ref r; /* Number of callers using device */ SDifc* ifc; /* pnp/legacy */ void *ctlr; int idno; char *name; - int index; /* into unit space */ - int nunit; SDev* next; QLock; /* enable/disable */ int enabled; + int nunit; /* Number of units */ + QLock unitlock; /* `Loading' of units */ + int *unitflg; /* Unit flags */ + SDunit **unit; /* Each controller has at least one unit */ } SDev; typedef struct SDifc { @@ -75,6 +82,9 @@ typedef struct SDifc { int (*wctl)(SDunit*, Cmdbuf*); long (*bio)(SDunit*, int, int, void*, long, long); + SDev* (*probe)(DevConf *); + void (*clear)(SDev *); + char* (*stat)(SDev*, char*, char*); } SDifc; typedef struct SDreq {