forsyth20121205-174063a80cc13 years agoHistory
1076 lines · 20.4 KB · C
RawDownload
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h" /* *  MPC821/3 PCMCIA driver (prototype) * * unlike the i82365 adapter, there isn't an offset register: * card addresses are simply the lower order 26 bits of the host address. * * to do: *	split allocation of memory/attrib (all 26 bits valid) and io space (typically 10 or 12 bits) *	correct config *	interrupts and i/o space access *	DMA? *	power control */ enum{	Maxctlr=	1,	Maxslot=	2,	Slotashift=	16, 	/* pipr */	Cbvs1=	1<<15,	Cbvs2=	1<<14,	Cbwp=	1<<13,	Cbcd2=	1<<12,	Cbcd1=	1<<11,	Cbbvd2=	1<<10,	Cbbvd1=	1<<9,	Cbrdy=	1<<8, 	/* pscr and per */	Cbvs1_c=	1<<15,	Cbvs2_c=	1<<14,	Cbwp_c=	1<<13,	Cbcd2_c=	1<<12,	Cbcd1_c=	1<<11,	Cbbvd2_c=	1<<10,	Cbbvd1_c=	1<<9,	Cbrdy_l=	1<<7,	Cbrdy_h=	1<<6,	Cbrdy_r=	1<<5,	Cbrdy_f=	1<<4, 	/* pgcr[n] */	Cbdreq_int=	0<<14,	Cbdreq_iois16=	2<<14,	Cbdreq_spkr=	3<<14,	Cboe=	1<<7,	Cbreset=	1<<6, 	/* porN */	Rport8=	0<<6,	Rport16=	1<<6,	Rmtype=	7<<3,	/* memory type field */	 Rmem=	0<<3,	/* common memory space */	 Rattrib=	2<<3,	/* attribute space */	 Rio=		3<<3,	 Rdma=	4<<3,	/* normal DMA */	 Rdmalx=	5<<3,	/* DMA, last transaction */	 RA22_23= 6<<3,	/* ``drive A22 and A23 signals on CE2 and CE1'' */	RslotB=	1<<2,	/* select slot B (always, on MPC823) */	Rwp=	1<<1,	/* write protect */	Rvalid=	1<<0,	/* region valid */ 	Nmap=		8,		/* max number of maps to use */ 	/*	 *  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 */	Rccsr=	2,	 Ciack	= (1<<0),	 Cipend	= (1<<1),	 Cpwrdown=	(1<<2),	 Caudioen=	(1<<3),	 Ciois8=	(1<<5),	 Cchgena=	(1<<6),	 Cchange=	(1<<7),	Rpin=	4,	/* pin replacement register */	Rscpr=	6,	/* socket and copy register */	Riob0=	10,	Riob1=	12,	Riob2=	14,	Riob3=	16,	Riolim=	18, 	Maxctab=	8,		/* maximum configuration table entries */	MaxCIS = 8192,		/* maximum CIS size in bytes */	Mgran = 8192,		/* maximum size of reads and writes */ 	Statusbounce=20,	/* msec debounce time */}; typedef struct Ctlr Ctlr; /* a controller (there's only one) */struct Ctlr{	int	dev;	int	nslot; 	/* memory maps */	Lock	mlock;		/* lock down the maps */	PCMmap	mmap[Nmap];	/* maps */ 	/* IO port allocation */	ulong	nextport;};static Ctlr controller[Maxctlr]; static PCMslot	*slot;static PCMslot	*lastslot;static int	nslot; static	Map	pcmmapv[Nmap+1];static	RMap	pcmmaps = {"PCMCIA mappings"}; static void	pcmciaintr(Ureg*, void*);static void	pcmciareset(void);static int	pcmio(int, ISAConf*);static long	pcmread(int, int, void*, long, ulong);static long	pcmwrite(int, int, void*, long, ulong);static void	slotdis(PCMslot*); static ulong pcmmalloc(ulong, long);static void	pcmfree(ulong, long);static void pcmciaproc(void*); static void pcmciadump(PCMslot*); /* *  get info about card */static voidslotinfo(PCMslot *pp){	ulong pipr; 	pipr = (m->iomem->pipr >> pp->slotshift) & 0xFF00;print("pipr=%8.8lux/%lux\n", m->iomem->pipr, pipr);	pp->v3_3 = (pipr&Cbvs1)!=0;	pp->voltage = (((pipr & Cbvs2)!=0)<<1) | ((pipr & Cbvs1)!=0);	pp->occupied = (pipr&(Cbcd1|Cbcd2))==0;	pp->powered = pcmpowered(pp->slotno);	pp->battery = (pipr & (Cbbvd1|Cbbvd2))>>9;	pp->wrprot = (pipr&Cbwp)!=0;	pp->busy = (pipr&Cbrdy)==0;} static voidpcmdelay(int ms){	if(up == nil)		delay(ms);	else		tsleep(&up->sleep, return0, nil, ms);} /* *  enable the slot card */static voidslotena(PCMslot *pp){	IMM *io; 	if(pp->enabled)		return;	m->iomem->pgcr[pp->slotno] &= ~Cboe;	pcmpower(pp->slotno, 1);	eieio();	pcmdelay(300);	io = m->iomem;	io->pgcr[pp->slotno] |= Cbreset;	/* active high */	eieio();	pcmdelay(100);	io->pgcr[pp->slotno] &= ~Cbreset;	eieio();	pcmdelay(500);	/* ludicrous delay */ 	/* get configuration */	slotinfo(pp);	if(pp->occupied){		if(pp->cisread == 0){			pcmcisread(pp);			pp->cisread = 1;		}		pp->enabled = 1;	} else{		print("empty slot\n");		slotdis(pp);	}} /* *  disable the slot card */static voidslotdis(PCMslot *pp){	int i;	Ctlr *ctlr;	PCMmap *pm; iprint("slotdis %d\n", pp->slotno);	pcmpower(pp->slotno, 0);	m->iomem->pgcr[pp->slotno] |= Cboe;	ctlr = pp->ctlr;	for(i = 0; i < nelem(ctlr->mmap); i++){		pm = &ctlr->mmap[i];		if(m->iomem->pcmr[i].option & Rvalid && pm->slotno == pp->slotno)			pcmunmap(pp->slotno, pm);	}	pp->enabled = 0;	pp->cisread = 0;} static voidpcmciardy(Ureg *ur, void *a){	PCMslot *pp;	ulong w; 	pp = a;	w = (m->iomem->pipr >> pp->slotshift) & 0xFF00;	if(pp->occupied && (w & Cbrdy) == 0){ /* interrupt */print("PCM.%dI#%lux|", pp->slotno, w);		if(pp->intr.f != nil)			pp->intr.f(ur, pp->intr.arg);	}} voidpcmintrenable(int slotno, void (*f)(Ureg*, void*), void *arg){	PCMslot *pp;	IMM *io;	char name[KNAMELEN]; 	if(slotno < 0 || slotno >= nslot)		panic("pcmintrenable");	snprint(name, sizeof(name), "pcmcia.irq%d", slotno);	io = ioplock();	pp = slot+slotno;	pp->intr.f = f;	pp->intr.arg = arg;	intrenable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name);	io->per |= Cbrdy_l;	/* assumes used for irq, not rdy; assumes level interrupt always right */	iopunlock();} voidpcmintrdisable(int slotno, void (*f)(Ureg*, void*), void *arg){	PCMslot *pp;	IMM *io;	char name[KNAMELEN]; 	if(slotno < 0 || slotno >= nslot)		panic("pcmintrdisable");	snprint(name, sizeof(name), "pcmcia.irq%d", slotno);	io = ioplock();	pp = slot+slotno;	if(pp->intr.f == f && pp->intr.arg == arg){		pp->intr.f = nil;		pp->intr.arg = nil;		intrdisable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name);		io->per &= ~Cbrdy_l;	}	iopunlock();} /* *  status change interrupt * * this wakes a monitoring process to read the CIS, * rather than holding other interrupts out here. */ static Rendez pcmstate; static intstatechanged(void *a){	PCMslot *pp;	int in; 	pp = a;	in = (m->iomem->pipr & (Cbcd1|Cbcd2))==0;	return in != pp->occupied;} static voidpcmciaintr(Ureg*, void*){	ulong events; 	if(slot == 0)		return;	events = m->iomem->pscr & (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c);	eieio();	m->iomem->pscr = events;	/* TO DO: other slot */iprint("PCM: #%lux|", events);iprint("pipr=#%lux|", m->iomem->pipr & 0xFF00);	wakeup(&pcmstate);} static voidpcmciaproc(void*){	ulong csc;	PCMslot *pp;	int was; 	for(;;){		sleep(&pcmstate, statechanged, slot+1);		tsleep(&up->sleep, return0, nil, Statusbounce);		/*		 * voltage change 1,2		 * write protect change		 * card detect 1,2		 * battery voltage 1 change (or SPKR-bar)		 * battery voltage 2 change (or STSCHG-bar)		 * card B rdy / IRQ-bar low		 * card B rdy / IRQ-bar high		 * card B rdy / IRQ-bar rising edge		 * card B rdy / IRQ-bar falling edge		 *		 * TO DO: currently only handle card-present changes		 */ 		for(pp = slot; pp < lastslot; pp++){			if(pp->memlen == 0)				continue;			csc = (m->iomem->pipr>>pp->slotshift) & (Cbcd1|Cbcd2);			was = pp->occupied;			slotinfo(pp);			if(csc == 0 && was != pp->occupied){				if(!pp->occupied){					slotdis(pp);					if(pp->special && pp->notify.f != nil)						pp->notify.f(pp->notify.arg, 1);				}			}		}	}} static uchar greycode[] = {	0, 1, 3, 2, 6, 7, 5, 4, 014, 015, 017, 016, 012, 013, 011, 010,	030, 031, 033, 032, 036, 037, 035, 034, 024, 025, 027}; /* *  get a map for pc card region, return corrected len */PCMmap*pcmmap(int slotno, ulong offset, int len, int attr){	Ctlr *ctlr;	PCMslot *pp;	PCMmap *pm, *nm;	IMM *io;	int i;	ulong e, bsize, code, opt; 	if(0)		print("pcmmap: %d #%lux %d #%x\n", slotno, offset, len, attr);	pp = slot + slotno;	if(!pp->occupied)		return nil;	if(attr == 1){	/* account for ../port/cis.c's conventions */		attr = Rattrib;		if(len <= 0)			len = MaxCIS*2;	/* TO DO */	}	ctlr = pp->ctlr; 	/* convert offset to granularity */	if(len <= 0)		len = 1;	e = offset+len;	for(i=0;; i++){		if(i >= nelem(greycode))			return nil;		bsize = 1<<i;		offset &= ~(bsize-1);		if(e <= offset+bsize)			break;	}	code = greycode[i];	if(0)		print("i=%d bsize=%lud code=0%luo\n", i, bsize, code);	e = offset+bsize;	len = bsize; 	lock(&ctlr->mlock); 	/* look for an existing map that covers the right area */	io = m->iomem;	nm = nil;	for(i=0; i<Nmap; i++){		pm = &ctlr->mmap[i];		if(io->pcmr[i].option & Rvalid &&		   pm->slotno == slotno &&		   pm->attr == attr &&		   offset >= pm->ca && e <= pm->cea){			pm->ref++;			unlock(&ctlr->mlock);			return pm;		}		if(nm == 0 && pm->ref == 0)			nm = pm;	}	pm = nm;	if(pm == nil){		unlock(&ctlr->mlock);		return nil;	} 	/* set up new map */	pm->isa = pcmmalloc(offset, len);	if(pm->isa == 0){		/* address not available: in use, or too much to map */		unlock(&ctlr->mlock);		return 0;	}	if(0)		print("mx=%d isa=#%lux\n", (int)(pm - ctlr->mmap), pm->isa); 	pm->len = len;	pm->ca = offset;	pm->cea = pm->ca + pm->len;	pm->attr = attr;	i = pm - ctlr->mmap;	io->pcmr[i].option &= ~Rvalid;	/* disable map before changing it */	io->pcmr[i].base = pm->isa;	opt = attr;	opt |= code<<27;	if((attr&Rmtype) == Rio){		opt |= 4<<12;	/* PSST */		opt |= 8<<7;	/* PSL */		opt |= 2<<16;	/* PSHT */	}else{		opt |= 6<<12;	/* PSST */		opt |= 24<<7;	/* PSL */		opt |= 8<<16;	/* PSHT */	}	if((attr & Rport16) == 0)		opt |= Rport8;	if(slotno == 1)		opt |= RslotB;	io->pcmr[i].option = opt | Rvalid;	pm->slotno = slotno;	pm->ref = 1; 	unlock(&ctlr->mlock);	return pm;} static voidpcmiomap(PCMslot *pp, PCMconftab *ct, int i){	int n, attr;	Ctlr *ctlr; 	if(0)		print("pcm iomap #%lux %lud\n", ct->io[i].start, ct->io[i].len);	if(ct->io[i].len <= 0)		return;	if(ct->io[i].start == 0){		n = 1<<ct->nlines;		ctlr = pp->ctlr;		lock(&ctlr->mlock);		if(ctlr->nextport == 0)			ctlr->nextport = 0xF000;		ctlr->nextport = (ctlr->nextport + n - 1) & ~(n-1);		ct->io[i].start = ctlr->nextport;		ct->io[i].len = n;		ctlr->nextport += n;		unlock(&ctlr->mlock);	}	attr = Rio;	if(ct->bit16)		attr |= Rport16;	ct->io[i].map = pcmmap(pp->slotno, ct->io[i].start, ct->io[i].len, attr);} 

Lines 1–500 of 1076.