xref: /plan9/sys/src/9/pc/devi82365.c (revision aa72973a2891ccbd3fb042462446761159389e19)
1219b2ee8SDavid du Colombier #include "u.h"
2219b2ee8SDavid du Colombier #include "../port/lib.h"
3219b2ee8SDavid du Colombier #include "mem.h"
4219b2ee8SDavid du Colombier #include "dat.h"
5219b2ee8SDavid du Colombier #include "fns.h"
6219b2ee8SDavid du Colombier #include "../port/error.h"
7219b2ee8SDavid du Colombier #include "io.h"
8219b2ee8SDavid du Colombier 
9219b2ee8SDavid du Colombier /*
109a747e4fSDavid du Colombier  *  Intel 82365SL PCIC controller and compatibles.
11219b2ee8SDavid du Colombier  */
12219b2ee8SDavid du Colombier enum
13219b2ee8SDavid du Colombier {
14219b2ee8SDavid du Colombier 	/*
15219b2ee8SDavid du Colombier 	 *  registers indices
16219b2ee8SDavid du Colombier 	 */
17219b2ee8SDavid du Colombier 	Rid=		0x0,		/* identification and revision */
18219b2ee8SDavid du Colombier 	Ris=		0x1,		/* interface status */
19219b2ee8SDavid du Colombier 	Rpc=	 	0x2,		/* power control */
20219b2ee8SDavid du Colombier 	 Foutena=	 (1<<7),	/*  output enable */
21219b2ee8SDavid du Colombier 	 Fautopower=	 (1<<5),	/*  automatic power switching */
22219b2ee8SDavid du Colombier 	 Fcardena=	 (1<<4),	/*  PC card enable */
23219b2ee8SDavid du Colombier 	Rigc= 		0x3,		/* interrupt and general control */
24219b2ee8SDavid du Colombier 	 Fiocard=	 (1<<5),	/*  I/O card (vs memory) */
25219b2ee8SDavid du Colombier 	 Fnotreset=	 (1<<6),	/*  reset if not set */
26219b2ee8SDavid du Colombier 	 FSMIena=	 (1<<4),	/*  enable change interrupt on SMI */
27219b2ee8SDavid du Colombier 	Rcsc= 		0x4,		/* card status change */
28219b2ee8SDavid du Colombier 	Rcscic= 	0x5,		/* card status change interrupt config */
29219b2ee8SDavid du Colombier 	 Fchangeena=	 (1<<3),	/*  card changed */
30219b2ee8SDavid du Colombier 	 Fbwarnena=	 (1<<1),	/*  card battery warning */
31219b2ee8SDavid du Colombier 	 Fbdeadena=	 (1<<0),	/*  card battery dead */
32219b2ee8SDavid du Colombier 	Rwe= 		0x6,		/* address window enable */
33219b2ee8SDavid du Colombier 	 Fmem16=	 (1<<5),	/*  use A23-A12 to decode address */
34219b2ee8SDavid du Colombier 	Rio= 		0x7,		/* I/O control */
35219b2ee8SDavid du Colombier 	 Fwidth16=	 (1<<0),	/*  16 bit data width */
36219b2ee8SDavid du Colombier 	 Fiocs16=	 (1<<1),	/*  IOCS16 determines data width */
377dd7cddfSDavid du Colombier 	 Fzerows=	 (1<<2),	/*  zero wait state */
38219b2ee8SDavid du Colombier 	 Ftiming=	 (1<<3),	/*  timing register to use */
39219b2ee8SDavid du Colombier 	Riobtm0lo=	0x8,		/* I/O address 0 start low byte */
40219b2ee8SDavid du Colombier 	Riobtm0hi=	0x9,		/* I/O address 0 start high byte */
41219b2ee8SDavid du Colombier 	Riotop0lo=	0xa,		/* I/O address 0 stop low byte */
42219b2ee8SDavid du Colombier 	Riotop0hi=	0xb,		/* I/O address 0 stop high byte */
43219b2ee8SDavid du Colombier 	Riobtm1lo=	0xc,		/* I/O address 1 start low byte */
44219b2ee8SDavid du Colombier 	Riobtm1hi=	0xd,		/* I/O address 1 start high byte */
45219b2ee8SDavid du Colombier 	Riotop1lo=	0xe,		/* I/O address 1 stop low byte */
46219b2ee8SDavid du Colombier 	Riotop1hi=	0xf,		/* I/O address 1 stop high byte */
47219b2ee8SDavid du Colombier 	Rmap=		0x10,		/* map 0 */
48219b2ee8SDavid du Colombier 
49219b2ee8SDavid du Colombier 	/*
50219b2ee8SDavid du Colombier 	 *  CL-PD67xx extension registers
51219b2ee8SDavid du Colombier 	 */
52219b2ee8SDavid du Colombier 	Rmisc1=		0x16,		/* misc control 1 */
53219b2ee8SDavid du Colombier 	 F5Vdetect=	 (1<<0),
54219b2ee8SDavid du Colombier 	 Fvcc3V=	 (1<<1),
55219b2ee8SDavid du Colombier 	 Fpmint=	 (1<<2),
56219b2ee8SDavid du Colombier 	 Fpsirq=	 (1<<3),
57219b2ee8SDavid du Colombier 	 Fspeaker=	 (1<<4),
58219b2ee8SDavid du Colombier 	 Finpack=	 (1<<7),
59219b2ee8SDavid du Colombier 	Rfifo=		0x17,		/* fifo control */
60219b2ee8SDavid du Colombier 	 Fflush=	 (1<<7),	/*  flush fifo */
61219b2ee8SDavid du Colombier 	Rmisc2=		0x1E,		/* misc control 2 */
62219b2ee8SDavid du Colombier 	 Flowpow=	 (1<<1),	/*  low power mode */
63219b2ee8SDavid du Colombier 	Rchipinfo=	0x1F,		/* chip information */
64219b2ee8SDavid du Colombier 	Ratactl=	0x26,		/* ATA control */
65219b2ee8SDavid du Colombier 
66219b2ee8SDavid du Colombier 	/*
67219b2ee8SDavid du Colombier 	 *  offsets into the system memory address maps
68219b2ee8SDavid du Colombier 	 */
69219b2ee8SDavid du Colombier 	Mbtmlo=		0x0,		/* System mem addr mapping start low byte */
70219b2ee8SDavid du Colombier 	Mbtmhi=		0x1,		/* System mem addr mapping start high byte */
71219b2ee8SDavid du Colombier 	 F16bit=	 (1<<7),	/*  16-bit wide data path */
72219b2ee8SDavid du Colombier 	Mtoplo=		0x2,		/* System mem addr mapping stop low byte */
73219b2ee8SDavid du Colombier 	Mtophi=		0x3,		/* System mem addr mapping stop high byte */
74219b2ee8SDavid du Colombier 	 Ftimer1=	 (1<<6),	/*  timer set 1 */
75219b2ee8SDavid du Colombier 	Mofflo=		0x4,		/* Card memory offset address low byte */
76219b2ee8SDavid du Colombier 	Moffhi=		0x5,		/* Card memory offset address high byte */
77219b2ee8SDavid du Colombier 	 Fregactive=	 (1<<6),	/*  attribute memory */
78219b2ee8SDavid du Colombier 
79219b2ee8SDavid du Colombier 	/*
80219b2ee8SDavid du Colombier 	 *  configuration registers - they start at an offset in attribute
81219b2ee8SDavid du Colombier 	 *  memory found in the CIS.
82219b2ee8SDavid du Colombier 	 */
83219b2ee8SDavid du Colombier 	Rconfig=	0,
84219b2ee8SDavid du Colombier 	 Creset=	 (1<<7),	/*  reset device */
85219b2ee8SDavid du Colombier 	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */
8615174232SDavid du Colombier 	 Cirq=		 (1<<2),	/*  IRQ enable */
8715174232SDavid du Colombier 	 Cdecode=	 (1<<1),	/*  address decode */
8815174232SDavid du Colombier 	 Cfunc=		 (1<<0),	/*  function enable */
8915174232SDavid du Colombier 	Riobase0=	5,
9015174232SDavid du Colombier 	Riobase1=	6,
9115174232SDavid du Colombier 	Riosize=	9,
92219b2ee8SDavid du Colombier };
93219b2ee8SDavid du Colombier 
94219b2ee8SDavid du Colombier #define MAP(x,o)	(Rmap + (x)*0x8 + o)
95219b2ee8SDavid du Colombier 
96219b2ee8SDavid du Colombier typedef struct I82365	I82365;
97219b2ee8SDavid du Colombier 
98219b2ee8SDavid du Colombier /* a controller */
99219b2ee8SDavid du Colombier enum
100219b2ee8SDavid du Colombier {
101219b2ee8SDavid du Colombier 	Ti82365,
102219b2ee8SDavid du Colombier 	Tpd6710,
103219b2ee8SDavid du Colombier 	Tpd6720,
1047dd7cddfSDavid du Colombier 	Tvg46x,
105219b2ee8SDavid du Colombier };
106219b2ee8SDavid du Colombier struct I82365
107219b2ee8SDavid du Colombier {
108219b2ee8SDavid du Colombier 	int	type;
109219b2ee8SDavid du Colombier 	int	dev;
110219b2ee8SDavid du Colombier 	int	nslot;
111219b2ee8SDavid du Colombier 	int	xreg;		/* index register address */
112219b2ee8SDavid du Colombier 	int	dreg;		/* data register address */
11359cc4ca5SDavid du Colombier 	int	irq;
114219b2ee8SDavid du Colombier };
115219b2ee8SDavid du Colombier static I82365 *controller[4];
116219b2ee8SDavid du Colombier static int ncontroller;
11780ee5cbfSDavid du Colombier static PCMslot	*slot;
11880ee5cbfSDavid du Colombier static PCMslot	*lastslot;
119219b2ee8SDavid du Colombier static nslot;
120219b2ee8SDavid du Colombier 
121219b2ee8SDavid du Colombier static void	i82365intr(Ureg*, void*);
122219b2ee8SDavid du Colombier static int	pcmio(int, ISAConf*);
1237dd7cddfSDavid du Colombier static long	pcmread(int, int, void*, long, vlong);
1247dd7cddfSDavid du Colombier static long	pcmwrite(int, int, void*, long, vlong);
1257dd7cddfSDavid du Colombier 
12680ee5cbfSDavid du Colombier static void i82365dump(PCMslot*);
127219b2ee8SDavid du Colombier 
128219b2ee8SDavid du Colombier /*
129219b2ee8SDavid du Colombier  *  reading and writing card registers
130219b2ee8SDavid du Colombier  */
131219b2ee8SDavid du Colombier static uchar
rdreg(PCMslot * pp,int index)13280ee5cbfSDavid du Colombier rdreg(PCMslot *pp, int index)
133219b2ee8SDavid du Colombier {
13480ee5cbfSDavid du Colombier 	outb(((I82365*)pp->cp)->xreg, pp->base + index);
13580ee5cbfSDavid du Colombier 	return inb(((I82365*)pp->cp)->dreg);
136219b2ee8SDavid du Colombier }
137219b2ee8SDavid du Colombier static void
wrreg(PCMslot * pp,int index,uchar val)13880ee5cbfSDavid du Colombier wrreg(PCMslot *pp, int index, uchar val)
139219b2ee8SDavid du Colombier {
14080ee5cbfSDavid du Colombier 	outb(((I82365*)pp->cp)->xreg, pp->base + index);
14180ee5cbfSDavid du Colombier 	outb(((I82365*)pp->cp)->dreg, val);
142219b2ee8SDavid du Colombier }
143219b2ee8SDavid du Colombier 
144219b2ee8SDavid du Colombier /*
145219b2ee8SDavid du Colombier  *  get info about card
146219b2ee8SDavid du Colombier  */
147219b2ee8SDavid du Colombier static void
slotinfo(PCMslot * pp)14880ee5cbfSDavid du Colombier slotinfo(PCMslot *pp)
149219b2ee8SDavid du Colombier {
150219b2ee8SDavid du Colombier 	uchar isr;
151219b2ee8SDavid du Colombier 
152219b2ee8SDavid du Colombier 	isr = rdreg(pp, Ris);
153219b2ee8SDavid du Colombier 	pp->occupied = (isr & (3<<2)) == (3<<2);
154219b2ee8SDavid du Colombier 	pp->powered = isr & (1<<6);
155219b2ee8SDavid du Colombier 	pp->battery = (isr & 3) == 3;
156219b2ee8SDavid du Colombier 	pp->wrprot = isr & (1<<4);
157219b2ee8SDavid du Colombier 	pp->busy = isr & (1<<5);
15880ee5cbfSDavid du Colombier 	pp->msec = TK2MS(MACHP(0)->ticks);
159219b2ee8SDavid du Colombier }
160219b2ee8SDavid du Colombier 
161219b2ee8SDavid du Colombier static int
vcode(int volt)162219b2ee8SDavid du Colombier vcode(int volt)
163219b2ee8SDavid du Colombier {
164219b2ee8SDavid du Colombier 	switch(volt){
165219b2ee8SDavid du Colombier 	case 5:
166219b2ee8SDavid du Colombier 		return 1;
167219b2ee8SDavid du Colombier 	case 12:
168219b2ee8SDavid du Colombier 		return 2;
169219b2ee8SDavid du Colombier 	default:
170219b2ee8SDavid du Colombier 		return 0;
171219b2ee8SDavid du Colombier 	}
172219b2ee8SDavid du Colombier }
173219b2ee8SDavid du Colombier 
174219b2ee8SDavid du Colombier /*
175219b2ee8SDavid du Colombier  *  enable the slot card
176219b2ee8SDavid du Colombier  */
177219b2ee8SDavid du Colombier static void
slotena(PCMslot * pp)17880ee5cbfSDavid du Colombier slotena(PCMslot *pp)
179219b2ee8SDavid du Colombier {
180219b2ee8SDavid du Colombier 	if(pp->enabled)
181219b2ee8SDavid du Colombier 		return;
182219b2ee8SDavid du Colombier 
183219b2ee8SDavid du Colombier 	/* power up and unreset, wait's are empirical (???) */
184219b2ee8SDavid du Colombier 	wrreg(pp, Rpc, Fautopower|Foutena|Fcardena);
185219b2ee8SDavid du Colombier 	delay(300);
186219b2ee8SDavid du Colombier 	wrreg(pp, Rigc, 0);
187219b2ee8SDavid du Colombier 	delay(100);
188219b2ee8SDavid du Colombier 	wrreg(pp, Rigc, Fnotreset);
18964c7f6b6SDavid du Colombier 	delay(500);
190219b2ee8SDavid du Colombier 
191219b2ee8SDavid du Colombier 	/* get configuration */
192219b2ee8SDavid du Colombier 	slotinfo(pp);
193219b2ee8SDavid du Colombier 	if(pp->occupied){
19480ee5cbfSDavid du Colombier 		pcmcisread(pp);
195219b2ee8SDavid du Colombier 		pp->enabled = 1;
196219b2ee8SDavid du Colombier 	} else
197219b2ee8SDavid du Colombier 		wrreg(pp, Rpc, Fautopower);
198219b2ee8SDavid du Colombier }
199219b2ee8SDavid du Colombier 
200219b2ee8SDavid du Colombier /*
201219b2ee8SDavid du Colombier  *  disable the slot card
202219b2ee8SDavid du Colombier  */
203219b2ee8SDavid du Colombier static void
slotdis(PCMslot * pp)20480ee5cbfSDavid du Colombier slotdis(PCMslot *pp)
205219b2ee8SDavid du Colombier {
206219b2ee8SDavid du Colombier 	wrreg(pp, Rpc, 0);	/* turn off card power */
207219b2ee8SDavid du Colombier 	wrreg(pp, Rwe, 0);	/* no windows */
208219b2ee8SDavid du Colombier 	pp->enabled = 0;
209219b2ee8SDavid du Colombier }
210219b2ee8SDavid du Colombier 
211219b2ee8SDavid du Colombier /*
212219b2ee8SDavid du Colombier  *  status change interrupt
213219b2ee8SDavid du Colombier  */
214219b2ee8SDavid du Colombier static void
i82365intr(Ureg *,void *)2157dd7cddfSDavid du Colombier i82365intr(Ureg *, void *)
216219b2ee8SDavid du Colombier {
217219b2ee8SDavid du Colombier 	uchar csc, was;
21880ee5cbfSDavid du Colombier 	PCMslot *pp;
219219b2ee8SDavid du Colombier 
220219b2ee8SDavid du Colombier 	if(slot == 0)
221219b2ee8SDavid du Colombier 		return;
222219b2ee8SDavid du Colombier 
223219b2ee8SDavid du Colombier 	for(pp = slot; pp < lastslot; pp++){
224219b2ee8SDavid du Colombier 		csc = rdreg(pp, Rcsc);
225219b2ee8SDavid du Colombier 		was = pp->occupied;
226219b2ee8SDavid du Colombier 		slotinfo(pp);
227219b2ee8SDavid du Colombier 		if(csc & (1<<3) && was != pp->occupied){
2287dd7cddfSDavid du Colombier 			if(!pp->occupied)
229219b2ee8SDavid du Colombier 				slotdis(pp);
230219b2ee8SDavid du Colombier 		}
231219b2ee8SDavid du Colombier 	}
232219b2ee8SDavid du Colombier }
233219b2ee8SDavid du Colombier 
234219b2ee8SDavid du Colombier enum
235219b2ee8SDavid du Colombier {
236219b2ee8SDavid du Colombier 	Mshift=	12,
237219b2ee8SDavid du Colombier 	Mgran=	(1<<Mshift),	/* granularity of maps */
238219b2ee8SDavid du Colombier 	Mmask=	~(Mgran-1),	/* mask for address bits important to the chip */
239219b2ee8SDavid du Colombier };
240219b2ee8SDavid du Colombier 
241219b2ee8SDavid du Colombier /*
242219b2ee8SDavid du Colombier  *  get a map for pc card region, return corrected len
243219b2ee8SDavid du Colombier  */
244219b2ee8SDavid du Colombier PCMmap*
pcmmap(int slotno,ulong offset,int len,int attr)245219b2ee8SDavid du Colombier pcmmap(int slotno, ulong offset, int len, int attr)
246219b2ee8SDavid du Colombier {
24780ee5cbfSDavid du Colombier 	PCMslot *pp;
248219b2ee8SDavid du Colombier 	uchar we, bit;
249219b2ee8SDavid du Colombier 	PCMmap *m, *nm;
250219b2ee8SDavid du Colombier 	int i;
251219b2ee8SDavid du Colombier 	ulong e;
252219b2ee8SDavid du Colombier 
253219b2ee8SDavid du Colombier 	pp = slot + slotno;
254219b2ee8SDavid du Colombier 	lock(&pp->mlock);
255219b2ee8SDavid du Colombier 
256219b2ee8SDavid du Colombier 	/* convert offset to granularity */
257219b2ee8SDavid du Colombier 	if(len <= 0)
258219b2ee8SDavid du Colombier 		len = 1;
259219b2ee8SDavid du Colombier 	e = ROUND(offset+len, Mgran);
260219b2ee8SDavid du Colombier 	offset &= Mmask;
261219b2ee8SDavid du Colombier 	len = e - offset;
262219b2ee8SDavid du Colombier 
263219b2ee8SDavid du Colombier 	/* look for a map that covers the right area */
264219b2ee8SDavid du Colombier 	we = rdreg(pp, Rwe);
265219b2ee8SDavid du Colombier 	bit = 1;
266219b2ee8SDavid du Colombier 	nm = 0;
26780ee5cbfSDavid du Colombier 	for(m = pp->mmap; m < &pp->mmap[nelem(pp->mmap)]; m++){
268219b2ee8SDavid du Colombier 		if((we & bit))
269219b2ee8SDavid du Colombier 		if(m->attr == attr)
270219b2ee8SDavid du Colombier 		if(offset >= m->ca && e <= m->cea){
271219b2ee8SDavid du Colombier 
272219b2ee8SDavid du Colombier 			m->ref++;
273219b2ee8SDavid du Colombier 			unlock(&pp->mlock);
274219b2ee8SDavid du Colombier 			return m;
275219b2ee8SDavid du Colombier 		}
276219b2ee8SDavid du Colombier 		bit <<= 1;
277219b2ee8SDavid du Colombier 		if(nm == 0 && m->ref == 0)
278219b2ee8SDavid du Colombier 			nm = m;
279219b2ee8SDavid du Colombier 	}
280219b2ee8SDavid du Colombier 	m = nm;
281219b2ee8SDavid du Colombier 	if(m == 0){
282219b2ee8SDavid du Colombier 		unlock(&pp->mlock);
283219b2ee8SDavid du Colombier 		return 0;
284219b2ee8SDavid du Colombier 	}
285219b2ee8SDavid du Colombier 
286219b2ee8SDavid du Colombier 	/* if isa space isn't big enough, free it and get more */
287219b2ee8SDavid du Colombier 	if(m->len < len){
288219b2ee8SDavid du Colombier 		if(m->isa){
2897dd7cddfSDavid du Colombier 			umbfree(m->isa, m->len);
290219b2ee8SDavid du Colombier 			m->len = 0;
291219b2ee8SDavid du Colombier 		}
2927dd7cddfSDavid du Colombier 		m->isa = PADDR(umbmalloc(0, len, Mgran));
293219b2ee8SDavid du Colombier 		if(m->isa == 0){
2949a747e4fSDavid du Colombier 			print("pcmmap: out of isa space\n");
295219b2ee8SDavid du Colombier 			unlock(&pp->mlock);
296219b2ee8SDavid du Colombier 			return 0;
297219b2ee8SDavid du Colombier 		}
298219b2ee8SDavid du Colombier 		m->len = len;
299219b2ee8SDavid du Colombier 	}
300219b2ee8SDavid du Colombier 
301219b2ee8SDavid du Colombier 	/* set up new map */
302219b2ee8SDavid du Colombier 	m->ca = offset;
303219b2ee8SDavid du Colombier 	m->cea = m->ca + m->len;
304219b2ee8SDavid du Colombier 	m->attr = attr;
305219b2ee8SDavid du Colombier 	i = m-pp->mmap;
306219b2ee8SDavid du Colombier 	bit = 1<<i;
307219b2ee8SDavid du Colombier 	wrreg(pp, Rwe, we & ~bit);		/* disable map before changing it */
308219b2ee8SDavid du Colombier 	wrreg(pp, MAP(i, Mbtmlo), m->isa>>Mshift);
309219b2ee8SDavid du Colombier 	wrreg(pp, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
310219b2ee8SDavid du Colombier 	wrreg(pp, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
311219b2ee8SDavid du Colombier 	wrreg(pp, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
312219b2ee8SDavid du Colombier 	offset -= m->isa;
313219b2ee8SDavid du Colombier 	offset &= (1<<25)-1;
314219b2ee8SDavid du Colombier 	offset >>= Mshift;
315219b2ee8SDavid du Colombier 	wrreg(pp, MAP(i, Mofflo), offset);
316219b2ee8SDavid du Colombier 	wrreg(pp, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
317219b2ee8SDavid du Colombier 	wrreg(pp, Rwe, we | bit);		/* enable map */
318219b2ee8SDavid du Colombier 	m->ref = 1;
319219b2ee8SDavid du Colombier 
320219b2ee8SDavid du Colombier 	unlock(&pp->mlock);
321219b2ee8SDavid du Colombier 	return m;
322219b2ee8SDavid du Colombier }
323219b2ee8SDavid du Colombier 
324219b2ee8SDavid du Colombier void
pcmunmap(int slotno,PCMmap * m)325219b2ee8SDavid du Colombier pcmunmap(int slotno, PCMmap* m)
326219b2ee8SDavid du Colombier {
32780ee5cbfSDavid du Colombier 	PCMslot *pp;
328219b2ee8SDavid du Colombier 
329219b2ee8SDavid du Colombier 	pp = slot + slotno;
330219b2ee8SDavid du Colombier 	lock(&pp->mlock);
331219b2ee8SDavid du Colombier 	m->ref--;
332219b2ee8SDavid du Colombier 	unlock(&pp->mlock);
333219b2ee8SDavid du Colombier }
334219b2ee8SDavid du Colombier 
335219b2ee8SDavid du Colombier static void
increfp(PCMslot * pp)33680ee5cbfSDavid du Colombier increfp(PCMslot *pp)
337219b2ee8SDavid du Colombier {
338219b2ee8SDavid du Colombier 	lock(pp);
339219b2ee8SDavid du Colombier 	if(pp->ref++ == 0)
340219b2ee8SDavid du Colombier 		slotena(pp);
341219b2ee8SDavid du Colombier 	unlock(pp);
342219b2ee8SDavid du Colombier }
343219b2ee8SDavid du Colombier 
344219b2ee8SDavid du Colombier static void
decrefp(PCMslot * pp)34580ee5cbfSDavid du Colombier decrefp(PCMslot *pp)
346219b2ee8SDavid du Colombier {
347219b2ee8SDavid du Colombier 	lock(pp);
348219b2ee8SDavid du Colombier 	if(pp->ref-- == 1)
349219b2ee8SDavid du Colombier 		slotdis(pp);
350219b2ee8SDavid du Colombier 	unlock(pp);
351219b2ee8SDavid du Colombier }
352219b2ee8SDavid du Colombier 
353219b2ee8SDavid du Colombier /*
354219b2ee8SDavid du Colombier  *  look for a card whose version contains 'idstr'
355219b2ee8SDavid du Colombier  */
3569a747e4fSDavid du Colombier static int
pcmcia_pcmspecial(char * idstr,ISAConf * isa)3579a747e4fSDavid du Colombier pcmcia_pcmspecial(char *idstr, ISAConf *isa)
358219b2ee8SDavid du Colombier {
35980ee5cbfSDavid du Colombier 	PCMslot *pp;
360219b2ee8SDavid du Colombier 	extern char *strstr(char*, char*);
36180ee5cbfSDavid du Colombier 	int enabled;
362219b2ee8SDavid du Colombier 
363219b2ee8SDavid du Colombier 	for(pp = slot; pp < lastslot; pp++){
364219b2ee8SDavid du Colombier 		if(pp->special)
365219b2ee8SDavid du Colombier 			continue;	/* already taken */
36680ee5cbfSDavid du Colombier 
36780ee5cbfSDavid du Colombier 		/*
36880ee5cbfSDavid du Colombier 		 *  make sure we don't power on cards when we already know what's
36980ee5cbfSDavid du Colombier 		 *  in them.  We'll reread every two minutes if necessary
37080ee5cbfSDavid du Colombier 		 */
37180ee5cbfSDavid du Colombier 		enabled = 0;
37280ee5cbfSDavid du Colombier 		if (pp->msec == ~0 || TK2MS(MACHP(0)->ticks) - pp->msec > 120000){
373219b2ee8SDavid du Colombier 			increfp(pp);
37480ee5cbfSDavid du Colombier 			enabled++;
37580ee5cbfSDavid du Colombier 		}
376219b2ee8SDavid du Colombier 
37759cc4ca5SDavid du Colombier 		if(pp->occupied) {
37880ee5cbfSDavid du Colombier 			if(strstr(pp->verstr, idstr)){
37980ee5cbfSDavid du Colombier 				if (!enabled){
38080ee5cbfSDavid du Colombier 					enabled = 1;
38180ee5cbfSDavid du Colombier 					increfp(pp);
38280ee5cbfSDavid du Colombier 				}
383219b2ee8SDavid du Colombier 				if(isa == 0 || pcmio(pp->slotno, isa) == 0){
384219b2ee8SDavid du Colombier 					pp->special = 1;
385219b2ee8SDavid du Colombier 					return pp->slotno;
386219b2ee8SDavid du Colombier 				}
38780ee5cbfSDavid du Colombier 			}
38859cc4ca5SDavid du Colombier 		} else
38959cc4ca5SDavid du Colombier 			pp->special = 1;
39080ee5cbfSDavid du Colombier 		if (enabled)
391219b2ee8SDavid du Colombier 			decrefp(pp);
392219b2ee8SDavid du Colombier 	}
393219b2ee8SDavid du Colombier 	return -1;
394219b2ee8SDavid du Colombier }
395219b2ee8SDavid du Colombier 
3969a747e4fSDavid du Colombier static void
pcmcia_pcmspecialclose(int slotno)3979a747e4fSDavid du Colombier pcmcia_pcmspecialclose(int slotno)
398219b2ee8SDavid du Colombier {
39980ee5cbfSDavid du Colombier 	PCMslot *pp;
400219b2ee8SDavid du Colombier 
401219b2ee8SDavid du Colombier 	if(slotno >= nslot)
402219b2ee8SDavid du Colombier 		panic("pcmspecialclose");
403219b2ee8SDavid du Colombier 	pp = slot + slotno;
404219b2ee8SDavid du Colombier 	pp->special = 0;
405219b2ee8SDavid du Colombier 	decrefp(pp);
406219b2ee8SDavid du Colombier }
407219b2ee8SDavid du Colombier 
408219b2ee8SDavid du Colombier enum
409219b2ee8SDavid du Colombier {
410219b2ee8SDavid du Colombier 	Qdir,
411219b2ee8SDavid du Colombier 	Qmem,
412219b2ee8SDavid du Colombier 	Qattr,
413219b2ee8SDavid du Colombier 	Qctl,
4147dd7cddfSDavid du Colombier 
4157dd7cddfSDavid du Colombier 	Nents = 3,
416219b2ee8SDavid du Colombier };
417219b2ee8SDavid du Colombier 
4189a747e4fSDavid du Colombier #define SLOTNO(c)	((ulong)((c->qid.path>>8)&0xff))
4199a747e4fSDavid du Colombier #define TYPE(c)	((ulong)(c->qid.path&0xff))
420219b2ee8SDavid du Colombier #define QID(s,t)	(((s)<<8)|(t))
421219b2ee8SDavid du Colombier 
422219b2ee8SDavid du Colombier static int
pcmgen(Chan * c,char *,Dirtab *,int,int i,Dir * dp)4239a747e4fSDavid du Colombier pcmgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp)
424219b2ee8SDavid du Colombier {
425219b2ee8SDavid du Colombier 	int slotno;
426219b2ee8SDavid du Colombier 	Qid qid;
427219b2ee8SDavid du Colombier 	long len;
42880ee5cbfSDavid du Colombier 	PCMslot *pp;
429219b2ee8SDavid du Colombier 
4307dd7cddfSDavid du Colombier 	if(i == DEVDOTDOT){
4319a747e4fSDavid du Colombier 		mkqid(&qid, Qdir, 0, QTDIR);
4329a747e4fSDavid du Colombier 		devdir(c, qid, "#y", 0, eve, 0555, dp);
4337dd7cddfSDavid du Colombier 		return 1;
4347dd7cddfSDavid du Colombier 	}
4357dd7cddfSDavid du Colombier 
4367dd7cddfSDavid du Colombier 	if(i >= Nents*nslot)
437219b2ee8SDavid du Colombier 		return -1;
4387dd7cddfSDavid du Colombier 	slotno = i/Nents;
439219b2ee8SDavid du Colombier 	pp = slot + slotno;
440219b2ee8SDavid du Colombier 	len = 0;
4417dd7cddfSDavid du Colombier 	switch(i%Nents){
442219b2ee8SDavid du Colombier 	case 0:
443219b2ee8SDavid du Colombier 		qid.path = QID(slotno, Qmem);
4449a747e4fSDavid du Colombier 		snprint(up->genbuf, sizeof up->genbuf, "pcm%dmem", slotno);
445219b2ee8SDavid du Colombier 		len = pp->memlen;
446219b2ee8SDavid du Colombier 		break;
447219b2ee8SDavid du Colombier 	case 1:
448219b2ee8SDavid du Colombier 		qid.path = QID(slotno, Qattr);
4499a747e4fSDavid du Colombier 		snprint(up->genbuf, sizeof up->genbuf, "pcm%dattr", slotno);
450219b2ee8SDavid du Colombier 		len = pp->memlen;
451219b2ee8SDavid du Colombier 		break;
452219b2ee8SDavid du Colombier 	case 2:
453219b2ee8SDavid du Colombier 		qid.path = QID(slotno, Qctl);
4549a747e4fSDavid du Colombier 		snprint(up->genbuf, sizeof up->genbuf, "pcm%dctl", slotno);
455219b2ee8SDavid du Colombier 		break;
456219b2ee8SDavid du Colombier 	}
457219b2ee8SDavid du Colombier 	qid.vers = 0;
4589a747e4fSDavid du Colombier 	qid.type = QTFILE;
4599a747e4fSDavid du Colombier 	devdir(c, qid, up->genbuf, len, eve, 0660, dp);
460219b2ee8SDavid du Colombier 	return 1;
461219b2ee8SDavid du Colombier }
462219b2ee8SDavid du Colombier 
463219b2ee8SDavid du Colombier static char *chipname[] =
464219b2ee8SDavid du Colombier {
465219b2ee8SDavid du Colombier [Ti82365]	"Intel 82365SL",
4669a747e4fSDavid du Colombier [Tpd6710]	"Cirrus Logic CL-PD6710",
4679a747e4fSDavid du Colombier [Tpd6720]	"Cirrus Logic CL-PD6720",
4687dd7cddfSDavid du Colombier [Tvg46x]	"Vadem VG-46x",
469219b2ee8SDavid du Colombier };
470219b2ee8SDavid du Colombier 
471219b2ee8SDavid du Colombier static I82365*
i82365probe(int x,int d,int dev)47259cc4ca5SDavid du Colombier i82365probe(int x, int d, int dev)
473219b2ee8SDavid du Colombier {
4747dd7cddfSDavid du Colombier 	uchar c, id;
475219b2ee8SDavid du Colombier 	I82365 *cp;
47659cc4ca5SDavid du Colombier 	ISAConf isa;
47759cc4ca5SDavid du Colombier 	int i, nslot;
478219b2ee8SDavid du Colombier 
479219b2ee8SDavid du Colombier 	outb(x, Rid + (dev<<7));
4807dd7cddfSDavid du Colombier 	id = inb(d);
4817dd7cddfSDavid du Colombier 	if((id & 0xf0) != 0x80)
48280ee5cbfSDavid du Colombier 		return 0;		/* not a memory & I/O card */
48380ee5cbfSDavid du Colombier 	if((id & 0x0f) == 0x00)
48480ee5cbfSDavid du Colombier 		return 0;		/* no revision number, not possible */
485219b2ee8SDavid du Colombier 
486219b2ee8SDavid du Colombier 	cp = xalloc(sizeof(I82365));
487219b2ee8SDavid du Colombier 	cp->xreg = x;
488219b2ee8SDavid du Colombier 	cp->dreg = d;
489219b2ee8SDavid du Colombier 	cp->dev = dev;
490219b2ee8SDavid du Colombier 	cp->type = Ti82365;
491219b2ee8SDavid du Colombier 	cp->nslot = 2;
492219b2ee8SDavid du Colombier 
4937dd7cddfSDavid du Colombier 	switch(id){
494219b2ee8SDavid du Colombier 	case 0x82:
495219b2ee8SDavid du Colombier 	case 0x83:
4967dd7cddfSDavid du Colombier 	case 0x84:
497219b2ee8SDavid du Colombier 		/* could be a cirrus */
498219b2ee8SDavid du Colombier 		outb(x, Rchipinfo + (dev<<7));
499219b2ee8SDavid du Colombier 		outb(d, 0);
500219b2ee8SDavid du Colombier 		c = inb(d);
5017dd7cddfSDavid du Colombier 		if((c & 0xc0) != 0xc0)
502219b2ee8SDavid du Colombier 			break;
5037dd7cddfSDavid du Colombier 		c = inb(d);
5047dd7cddfSDavid du Colombier 		if((c & 0xc0) != 0x00)
5057dd7cddfSDavid du Colombier 			break;
5067dd7cddfSDavid du Colombier 		if(c & 0x20){
507219b2ee8SDavid du Colombier 			cp->type = Tpd6720;
508219b2ee8SDavid du Colombier 		} else {
509219b2ee8SDavid du Colombier 			cp->type = Tpd6710;
510219b2ee8SDavid du Colombier 			cp->nslot = 1;
511219b2ee8SDavid du Colombier 		}
5129a747e4fSDavid du Colombier 
5139a747e4fSDavid du Colombier 		/* low power mode */
5149a747e4fSDavid du Colombier 		outb(x, Rmisc2 + (dev<<7));
5159a747e4fSDavid du Colombier 		c = inb(d);
5169a747e4fSDavid du Colombier 		outb(d, c & ~Flowpow);
517219b2ee8SDavid du Colombier 		break;
518219b2ee8SDavid du Colombier 	}
519219b2ee8SDavid du Colombier 
52080ee5cbfSDavid du Colombier 	/* if it's not a Cirrus, it could be a Vadem... */
5217dd7cddfSDavid du Colombier 	if(cp->type == Ti82365){
52280ee5cbfSDavid du Colombier 		/* unlock the Vadem extended regs */
5237dd7cddfSDavid du Colombier 		outb(x, 0x0E + (dev<<7));
5247dd7cddfSDavid du Colombier 		outb(x, 0x37 + (dev<<7));
52580ee5cbfSDavid du Colombier 
52680ee5cbfSDavid du Colombier 		/* make the id register show the Vadem id */
5277dd7cddfSDavid du Colombier 		outb(x, 0x3A + (dev<<7));
5287dd7cddfSDavid du Colombier 		c = inb(d);
5297dd7cddfSDavid du Colombier 		outb(d, c|0xC0);
5307dd7cddfSDavid du Colombier 		outb(x, Rid + (dev<<7));
5317dd7cddfSDavid du Colombier 		c = inb(d);
5327dd7cddfSDavid du Colombier 		if(c & 0x08)
5337dd7cddfSDavid du Colombier 			cp->type = Tvg46x;
53480ee5cbfSDavid du Colombier 
53580ee5cbfSDavid du Colombier 		/* go back to Intel compatible id */
5367dd7cddfSDavid du Colombier 		outb(x, 0x3A + (dev<<7));
5377dd7cddfSDavid du Colombier 		c = inb(d);
5387dd7cddfSDavid du Colombier 		outb(d, c & ~0xC0);
5397dd7cddfSDavid du Colombier 	}
540219b2ee8SDavid du Colombier 
54159cc4ca5SDavid du Colombier 	memset(&isa, 0, sizeof(ISAConf));
54259cc4ca5SDavid du Colombier 	if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq)
54359cc4ca5SDavid du Colombier 		cp->irq = isa.irq;
54459cc4ca5SDavid du Colombier 	else
54559cc4ca5SDavid du Colombier 		cp->irq = IrqPCMCIA;
54659cc4ca5SDavid du Colombier 
54759cc4ca5SDavid du Colombier 	for(i = 0; i < isa.nopt; i++){
54859cc4ca5SDavid du Colombier 		if(cistrncmp(isa.opt[i], "nslot=", 6))
54959cc4ca5SDavid du Colombier 			continue;
55059cc4ca5SDavid du Colombier 		nslot = strtol(&isa.opt[i][6], nil, 0);
55159cc4ca5SDavid du Colombier 		if(nslot > 0 && nslot <= 2)
55259cc4ca5SDavid du Colombier 			cp->nslot = nslot;
55359cc4ca5SDavid du Colombier 	}
55459cc4ca5SDavid du Colombier 
555219b2ee8SDavid du Colombier 	controller[ncontroller++] = cp;
556219b2ee8SDavid du Colombier 	return cp;
557219b2ee8SDavid du Colombier }
558219b2ee8SDavid du Colombier 
559219b2ee8SDavid du Colombier static void
i82365dump(PCMslot * pp)56080ee5cbfSDavid du Colombier i82365dump(PCMslot *pp)
561219b2ee8SDavid du Colombier {
562219b2ee8SDavid du Colombier 	int i;
563219b2ee8SDavid du Colombier 
564219b2ee8SDavid du Colombier 	for(i = 0; i < 0x40; i++){
5657dd7cddfSDavid du Colombier 		if((i&0x0F) == 0)
5667dd7cddfSDavid du Colombier 			print("\n%2.2uX:	", i);
5679a747e4fSDavid du Colombier 		print("%2.2uX ", rdreg(pp, i));
5687dd7cddfSDavid du Colombier 		if(((i+1) & 0x0F) == 0x08)
5697dd7cddfSDavid du Colombier 			print(" - ");
570219b2ee8SDavid du Colombier 	}
571219b2ee8SDavid du Colombier 	print("\n");
572219b2ee8SDavid du Colombier }
573219b2ee8SDavid du Colombier 
574219b2ee8SDavid du Colombier /*
575219b2ee8SDavid du Colombier  *  set up for slot cards
576219b2ee8SDavid du Colombier  */
5779a747e4fSDavid du Colombier void
devi82365link(void)5789a747e4fSDavid du Colombier devi82365link(void)
579219b2ee8SDavid du Colombier {
580219b2ee8SDavid du Colombier 	static int already;
58159cc4ca5SDavid du Colombier 	int i, j;
582219b2ee8SDavid du Colombier 	I82365 *cp;
58380ee5cbfSDavid du Colombier 	PCMslot *pp;
584b7b24591SDavid du Colombier 	char buf[32], *p;
585219b2ee8SDavid du Colombier 
586219b2ee8SDavid du Colombier 	if(already)
587219b2ee8SDavid du Colombier 		return;
588219b2ee8SDavid du Colombier 	already = 1;
589219b2ee8SDavid du Colombier 
590b7b24591SDavid du Colombier 	if((p=getconf("pcmcia0")) && strncmp(p, "disabled", 8)==0)
591b7b24591SDavid du Colombier 		return;
592b7b24591SDavid du Colombier 
5939a747e4fSDavid du Colombier 	if(_pcmspecial)
5949a747e4fSDavid du Colombier 		return;
5959a747e4fSDavid du Colombier 
5967dd7cddfSDavid du Colombier 	/* look for controllers if the ports aren't already taken */
59759cc4ca5SDavid du Colombier 	if(ioalloc(0x3E0, 2, 0, "i82365.0") >= 0){
59859cc4ca5SDavid du Colombier 		i82365probe(0x3E0, 0x3E1, 0);
59959cc4ca5SDavid du Colombier 		i82365probe(0x3E0, 0x3E1, 1);
6007dd7cddfSDavid du Colombier 		if(ncontroller == 0)
6017dd7cddfSDavid du Colombier 			iofree(0x3E0);
6027dd7cddfSDavid du Colombier 	}
60359cc4ca5SDavid du Colombier 	if(ioalloc(0x3E2, 2, 0, "i82365.1") >= 0){
6047dd7cddfSDavid du Colombier 		i = ncontroller;
60559cc4ca5SDavid du Colombier 		i82365probe(0x3E2, 0x3E3, 0);
60659cc4ca5SDavid du Colombier 		i82365probe(0x3E2, 0x3E3, 1);
6077dd7cddfSDavid du Colombier 		if(ncontroller == i)
6087dd7cddfSDavid du Colombier 			iofree(0x3E2);
6097dd7cddfSDavid du Colombier 	}
6109a747e4fSDavid du Colombier 
6119a747e4fSDavid du Colombier 	if(ncontroller == 0)
6129a747e4fSDavid du Colombier 		return;
6139a747e4fSDavid du Colombier 
6149a747e4fSDavid du Colombier 	_pcmspecial = pcmcia_pcmspecial;
6159a747e4fSDavid du Colombier 	_pcmspecialclose = pcmcia_pcmspecialclose;
6169a747e4fSDavid du Colombier 
617219b2ee8SDavid du Colombier 	for(i = 0; i < ncontroller; i++)
618219b2ee8SDavid du Colombier 		nslot += controller[i]->nslot;
61980ee5cbfSDavid du Colombier 	slot = xalloc(nslot * sizeof(PCMslot));
620219b2ee8SDavid du Colombier 
621219b2ee8SDavid du Colombier 	lastslot = slot;
622219b2ee8SDavid du Colombier 	for(i = 0; i < ncontroller; i++){
623219b2ee8SDavid du Colombier 		cp = controller[i];
6247dd7cddfSDavid du Colombier 		print("#y%d: %d slot %s: port 0x%uX irq %d\n",
62559cc4ca5SDavid du Colombier 			i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq);
626219b2ee8SDavid du Colombier 		for(j = 0; j < cp->nslot; j++){
627219b2ee8SDavid du Colombier 			pp = lastslot++;
628219b2ee8SDavid du Colombier 			pp->slotno = pp - slot;
629219b2ee8SDavid du Colombier 			pp->memlen = 64*MB;
630219b2ee8SDavid du Colombier 			pp->base = (cp->dev<<7) | (j<<6);
631219b2ee8SDavid du Colombier 			pp->cp = cp;
63280ee5cbfSDavid du Colombier 			pp->msec = ~0;
63380ee5cbfSDavid du Colombier 			pp->verstr[0] = 0;
634219b2ee8SDavid du Colombier 			slotdis(pp);
635219b2ee8SDavid du Colombier 
636219b2ee8SDavid du Colombier 			/* interrupt on status change */
63759cc4ca5SDavid du Colombier 			wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena);
6387dd7cddfSDavid du Colombier 			rdreg(pp, Rcsc);
639219b2ee8SDavid du Colombier 		}
640219b2ee8SDavid du Colombier 
641219b2ee8SDavid du Colombier 		/* for card management interrupts */
6429a747e4fSDavid du Colombier 		snprint(buf, sizeof buf, "i82365.%d", i);
64359cc4ca5SDavid du Colombier 		intrenable(cp->irq, i82365intr, 0, BUSUNKNOWN, buf);
64459cc4ca5SDavid du Colombier 	}
645219b2ee8SDavid du Colombier }
646219b2ee8SDavid du Colombier 
6477dd7cddfSDavid du Colombier static Chan*
i82365attach(char * spec)648219b2ee8SDavid du Colombier i82365attach(char *spec)
649219b2ee8SDavid du Colombier {
650219b2ee8SDavid du Colombier 	return devattach('y', spec);
651219b2ee8SDavid du Colombier }
652219b2ee8SDavid du Colombier 
6539a747e4fSDavid du Colombier static Walkqid*
i82365walk(Chan * c,Chan * nc,char ** name,int nname)6549a747e4fSDavid du Colombier i82365walk(Chan *c, Chan *nc, char **name, int nname)
655219b2ee8SDavid du Colombier {
6569a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, 0, 0, pcmgen);
657219b2ee8SDavid du Colombier }
658219b2ee8SDavid du Colombier 
6599a747e4fSDavid du Colombier static int
i82365stat(Chan * c,uchar * db,int n)6609a747e4fSDavid du Colombier i82365stat(Chan *c, uchar *db, int n)
661219b2ee8SDavid du Colombier {
6629a747e4fSDavid du Colombier 	return devstat(c, db, n, 0, 0, pcmgen);
663219b2ee8SDavid du Colombier }
664219b2ee8SDavid du Colombier 
6657dd7cddfSDavid du Colombier static Chan*
i82365open(Chan * c,int omode)666219b2ee8SDavid du Colombier i82365open(Chan *c, int omode)
667219b2ee8SDavid du Colombier {
6689a747e4fSDavid du Colombier 	if(c->qid.type & QTDIR){
669219b2ee8SDavid du Colombier 		if(omode != OREAD)
670219b2ee8SDavid du Colombier 			error(Eperm);
671219b2ee8SDavid du Colombier 	} else
672219b2ee8SDavid du Colombier 		increfp(slot + SLOTNO(c));
673219b2ee8SDavid du Colombier 	c->mode = openmode(omode);
674219b2ee8SDavid du Colombier 	c->flag |= COPEN;
675219b2ee8SDavid du Colombier 	c->offset = 0;
676219b2ee8SDavid du Colombier 	return c;
677219b2ee8SDavid du Colombier }
678219b2ee8SDavid du Colombier 
6797dd7cddfSDavid du Colombier static void
i82365close(Chan * c)680219b2ee8SDavid du Colombier i82365close(Chan *c)
681219b2ee8SDavid du Colombier {
682219b2ee8SDavid du Colombier 	if(c->flag & COPEN)
6839a747e4fSDavid du Colombier 		if((c->qid.type & QTDIR) == 0)
684219b2ee8SDavid du Colombier 			decrefp(slot+SLOTNO(c));
685219b2ee8SDavid du Colombier }
686219b2ee8SDavid du Colombier 
687219b2ee8SDavid du Colombier /* a memmove using only bytes */
688219b2ee8SDavid du Colombier static void
memmoveb(uchar * to,uchar * from,int n)689219b2ee8SDavid du Colombier memmoveb(uchar *to, uchar *from, int n)
690219b2ee8SDavid du Colombier {
691219b2ee8SDavid du Colombier 	while(n-- > 0)
692219b2ee8SDavid du Colombier 		*to++ = *from++;
693219b2ee8SDavid du Colombier }
694219b2ee8SDavid du Colombier 
695219b2ee8SDavid du Colombier /* a memmove using only shorts & bytes */
696219b2ee8SDavid du Colombier static void
memmoves(uchar * to,uchar * from,int n)697219b2ee8SDavid du Colombier memmoves(uchar *to, uchar *from, int n)
698219b2ee8SDavid du Colombier {
699219b2ee8SDavid du Colombier 	ushort *t, *f;
700219b2ee8SDavid du Colombier 
701219b2ee8SDavid du Colombier 	if((((ulong)to) & 1) || (((ulong)from) & 1) || (n & 1)){
702219b2ee8SDavid du Colombier 		while(n-- > 0)
703219b2ee8SDavid du Colombier 			*to++ = *from++;
704219b2ee8SDavid du Colombier 	} else {
705219b2ee8SDavid du Colombier 		n = n/2;
706219b2ee8SDavid du Colombier 		t = (ushort*)to;
707219b2ee8SDavid du Colombier 		f = (ushort*)from;
708219b2ee8SDavid du Colombier 		while(n-- > 0)
709219b2ee8SDavid du Colombier 			*t++ = *f++;
710219b2ee8SDavid du Colombier 	}
711219b2ee8SDavid du Colombier }
712219b2ee8SDavid du Colombier 
713219b2ee8SDavid du Colombier static long
pcmread(int slotno,int attr,void * a,long n,vlong off)7147dd7cddfSDavid du Colombier pcmread(int slotno, int attr, void *a, long n, vlong off)
715219b2ee8SDavid du Colombier {
716219b2ee8SDavid du Colombier 	int i, len;
717219b2ee8SDavid du Colombier 	PCMmap *m;
718219b2ee8SDavid du Colombier 	uchar *ac;
71980ee5cbfSDavid du Colombier 	PCMslot *pp;
7207dd7cddfSDavid du Colombier 	ulong offset = off;
721219b2ee8SDavid du Colombier 
722219b2ee8SDavid du Colombier 	pp = slot + slotno;
723219b2ee8SDavid du Colombier 	if(pp->memlen < offset)
724219b2ee8SDavid du Colombier 		return 0;
725219b2ee8SDavid du Colombier 	if(pp->memlen < offset + n)
726219b2ee8SDavid du Colombier 		n = pp->memlen - offset;
727219b2ee8SDavid du Colombier 
728219b2ee8SDavid du Colombier 	m = 0;
729219b2ee8SDavid du Colombier 	if(waserror()){
730219b2ee8SDavid du Colombier 		if(m)
731219b2ee8SDavid du Colombier 			pcmunmap(pp->slotno, m);
732219b2ee8SDavid du Colombier 		nexterror();
733219b2ee8SDavid du Colombier 	}
734219b2ee8SDavid du Colombier 
735219b2ee8SDavid du Colombier 	ac = a;
736219b2ee8SDavid du Colombier 	for(len = n; len > 0; len -= i){
737219b2ee8SDavid du Colombier 		m = pcmmap(pp->slotno, offset, 0, attr);
738219b2ee8SDavid du Colombier 		if(m == 0)
7398cd4f5a6SDavid du Colombier 			error("cannot map PCMCIA card");
740219b2ee8SDavid du Colombier 		if(offset + len > m->cea)
741219b2ee8SDavid du Colombier 			i = m->cea - offset;
742219b2ee8SDavid du Colombier 		else
743219b2ee8SDavid du Colombier 			i = len;
7447dd7cddfSDavid du Colombier 		memmoveb(ac, KADDR(m->isa + offset - m->ca), i);
745219b2ee8SDavid du Colombier 		pcmunmap(pp->slotno, m);
746219b2ee8SDavid du Colombier 		offset += i;
747219b2ee8SDavid du Colombier 		ac += i;
748219b2ee8SDavid du Colombier 	}
749219b2ee8SDavid du Colombier 
750219b2ee8SDavid du Colombier 	poperror();
751219b2ee8SDavid du Colombier 	return n;
752219b2ee8SDavid du Colombier }
753219b2ee8SDavid du Colombier 
7547dd7cddfSDavid du Colombier static long
i82365read(Chan * c,void * a,long n,vlong off)7557dd7cddfSDavid du Colombier i82365read(Chan *c, void *a, long n, vlong off)
756219b2ee8SDavid du Colombier {
75780ee5cbfSDavid du Colombier 	char *p, *buf, *e;
75880ee5cbfSDavid du Colombier 	PCMslot *pp;
7597dd7cddfSDavid du Colombier 	ulong offset = off;
760219b2ee8SDavid du Colombier 
7617dd7cddfSDavid du Colombier 	switch(TYPE(c)){
762219b2ee8SDavid du Colombier 	case Qdir:
763219b2ee8SDavid du Colombier 		return devdirread(c, a, n, 0, 0, pcmgen);
764219b2ee8SDavid du Colombier 	case Qmem:
765219b2ee8SDavid du Colombier 	case Qattr:
7667dd7cddfSDavid du Colombier 		return pcmread(SLOTNO(c), TYPE(c) == Qattr, a, n, off);
767219b2ee8SDavid du Colombier 	case Qctl:
76880ee5cbfSDavid du Colombier 		buf = p = malloc(READSTR);
769*aa72973aSDavid du Colombier 		if(p == nil)
770*aa72973aSDavid du Colombier 			error(Enomem);
77180ee5cbfSDavid du Colombier 		e = p + READSTR;
772219b2ee8SDavid du Colombier 		pp = slot + SLOTNO(c);
773219b2ee8SDavid du Colombier 
77480ee5cbfSDavid du Colombier 		buf[0] = 0;
77580ee5cbfSDavid du Colombier 		if(pp->occupied){
77680ee5cbfSDavid du Colombier 			p = seprint(p, e, "occupied\n");
77780ee5cbfSDavid du Colombier 			if(pp->verstr[0])
77880ee5cbfSDavid du Colombier 				p = seprint(p, e, "version %s\n", pp->verstr);
77980ee5cbfSDavid du Colombier 		}
7807dd7cddfSDavid du Colombier 		if(pp->enabled)
78180ee5cbfSDavid du Colombier 			p = seprint(p, e, "enabled\n");
7827dd7cddfSDavid du Colombier 		if(pp->powered)
78380ee5cbfSDavid du Colombier 			p = seprint(p, e, "powered\n");
7847dd7cddfSDavid du Colombier 		if(pp->configed)
78580ee5cbfSDavid du Colombier 			p = seprint(p, e, "configed\n");
7867dd7cddfSDavid du Colombier 		if(pp->wrprot)
78780ee5cbfSDavid du Colombier 			p = seprint(p, e, "write protected\n");
7887dd7cddfSDavid du Colombier 		if(pp->busy)
78980ee5cbfSDavid du Colombier 			p = seprint(p, e, "busy\n");
79080ee5cbfSDavid du Colombier 		seprint(p, e, "battery lvl %d\n", pp->battery);
7917dd7cddfSDavid du Colombier 
79280ee5cbfSDavid du Colombier 		n = readstr(offset, a, n, buf);
79380ee5cbfSDavid du Colombier 		free(buf);
7947dd7cddfSDavid du Colombier 
795219b2ee8SDavid du Colombier 		return n;
796219b2ee8SDavid du Colombier 	}
7977dd7cddfSDavid du Colombier 	error(Ebadarg);
7987dd7cddfSDavid du Colombier 	return -1;	/* not reached */
7997dd7cddfSDavid du Colombier }
800219b2ee8SDavid du Colombier 
801219b2ee8SDavid du Colombier static long
pcmwrite(int dev,int attr,void * a,long n,vlong off)8027dd7cddfSDavid du Colombier pcmwrite(int dev, int attr, void *a, long n, vlong off)
803219b2ee8SDavid du Colombier {
804219b2ee8SDavid du Colombier 	int i, len;
805219b2ee8SDavid du Colombier 	PCMmap *m;
806219b2ee8SDavid du Colombier 	uchar *ac;
80780ee5cbfSDavid du Colombier 	PCMslot *pp;
8087dd7cddfSDavid du Colombier 	ulong offset = off;
809219b2ee8SDavid du Colombier 
810219b2ee8SDavid du Colombier 	pp = slot + dev;
811219b2ee8SDavid du Colombier 	if(pp->memlen < offset)
812219b2ee8SDavid du Colombier 		return 0;
813219b2ee8SDavid du Colombier 	if(pp->memlen < offset + n)
814219b2ee8SDavid du Colombier 		n = pp->memlen - offset;
815219b2ee8SDavid du Colombier 
816219b2ee8SDavid du Colombier 	m = 0;
817219b2ee8SDavid du Colombier 	if(waserror()){
818219b2ee8SDavid du Colombier 		if(m)
819219b2ee8SDavid du Colombier 			pcmunmap(pp->slotno, m);
820219b2ee8SDavid du Colombier 		nexterror();
821219b2ee8SDavid du Colombier 	}
822219b2ee8SDavid du Colombier 
823219b2ee8SDavid du Colombier 	ac = a;
824219b2ee8SDavid du Colombier 	for(len = n; len > 0; len -= i){
825219b2ee8SDavid du Colombier 		m = pcmmap(pp->slotno, offset, 0, attr);
826219b2ee8SDavid du Colombier 		if(m == 0)
8278cd4f5a6SDavid du Colombier 			error("cannot map PCMCIA card");
828219b2ee8SDavid du Colombier 		if(offset + len > m->cea)
829219b2ee8SDavid du Colombier 			i = m->cea - offset;
830219b2ee8SDavid du Colombier 		else
831219b2ee8SDavid du Colombier 			i = len;
8327dd7cddfSDavid du Colombier 		memmoveb(KADDR(m->isa + offset - m->ca), ac, i);
833219b2ee8SDavid du Colombier 		pcmunmap(pp->slotno, m);
834219b2ee8SDavid du Colombier 		offset += i;
835219b2ee8SDavid du Colombier 		ac += i;
836219b2ee8SDavid du Colombier 	}
837219b2ee8SDavid du Colombier 
838219b2ee8SDavid du Colombier 	poperror();
839219b2ee8SDavid du Colombier 	return n;
840219b2ee8SDavid du Colombier }
841219b2ee8SDavid du Colombier 
8427dd7cddfSDavid du Colombier static long
i82365write(Chan * c,void * a,long n,vlong off)8437dd7cddfSDavid du Colombier i82365write(Chan *c, void *a, long n, vlong off)
844219b2ee8SDavid du Colombier {
84580ee5cbfSDavid du Colombier 	PCMslot *pp;
8467dd7cddfSDavid du Colombier 	char buf[32];
847219b2ee8SDavid du Colombier 
8487dd7cddfSDavid du Colombier 	switch(TYPE(c)){
8497dd7cddfSDavid du Colombier 	case Qctl:
8507dd7cddfSDavid du Colombier 		if(n >= sizeof(buf))
8517dd7cddfSDavid du Colombier 			n = sizeof(buf) - 1;
8527dd7cddfSDavid du Colombier 		strncpy(buf, a, n);
8537dd7cddfSDavid du Colombier 		buf[n] = 0;
8547dd7cddfSDavid du Colombier 		pp = slot + SLOTNO(c);
8557dd7cddfSDavid du Colombier 		if(!pp->occupied)
8567dd7cddfSDavid du Colombier 			error(Eio);
8577dd7cddfSDavid du Colombier 
8587dd7cddfSDavid du Colombier 		/* set vpp on card */
8597dd7cddfSDavid du Colombier 		if(strncmp(buf, "vpp", 3) == 0)
8607dd7cddfSDavid du Colombier 			wrreg(pp, Rpc, vcode(atoi(buf+3))|Fautopower|Foutena|Fcardena);
8617dd7cddfSDavid du Colombier 		return n;
862219b2ee8SDavid du Colombier 	case Qmem:
863219b2ee8SDavid du Colombier 	case Qattr:
864219b2ee8SDavid du Colombier 		pp = slot + SLOTNO(c);
865219b2ee8SDavid du Colombier 		if(pp->occupied == 0 || pp->enabled == 0)
866219b2ee8SDavid du Colombier 			error(Eio);
8677dd7cddfSDavid du Colombier 		n = pcmwrite(pp->slotno, TYPE(c) == Qattr, a, n, off);
868219b2ee8SDavid du Colombier 		if(n < 0)
869219b2ee8SDavid du Colombier 			error(Eio);
870219b2ee8SDavid du Colombier 		return n;
871219b2ee8SDavid du Colombier 	}
8727dd7cddfSDavid du Colombier 	error(Ebadarg);
8737dd7cddfSDavid du Colombier 	return -1;	/* not reached */
8747dd7cddfSDavid du Colombier }
8757dd7cddfSDavid du Colombier 
8767dd7cddfSDavid du Colombier Dev i82365devtab = {
8777dd7cddfSDavid du Colombier 	'y',
8787dd7cddfSDavid du Colombier 	"i82365",
8797dd7cddfSDavid du Colombier 
8809a747e4fSDavid du Colombier 	devreset,
8817dd7cddfSDavid du Colombier 	devinit,
8829a747e4fSDavid du Colombier 	devshutdown,
8837dd7cddfSDavid du Colombier 	i82365attach,
8847dd7cddfSDavid du Colombier 	i82365walk,
8857dd7cddfSDavid du Colombier 	i82365stat,
8867dd7cddfSDavid du Colombier 	i82365open,
8877dd7cddfSDavid du Colombier 	devcreate,
8887dd7cddfSDavid du Colombier 	i82365close,
8897dd7cddfSDavid du Colombier 	i82365read,
8907dd7cddfSDavid du Colombier 	devbread,
8917dd7cddfSDavid du Colombier 	i82365write,
8927dd7cddfSDavid du Colombier 	devbwrite,
8937dd7cddfSDavid du Colombier 	devremove,
8947dd7cddfSDavid du Colombier 	devwstat,
8957dd7cddfSDavid du Colombier };
896219b2ee8SDavid du Colombier 
897219b2ee8SDavid du Colombier /*
89880ee5cbfSDavid du Colombier  *  configure the PCMslot for IO.  We assume very heavily that we can read
8997dd7cddfSDavid du Colombier  *  configuration info from the CIS.  If not, we won't set up correctly.
900219b2ee8SDavid du Colombier  */
901219b2ee8SDavid du Colombier static int
pcmio(int slotno,ISAConf * isa)902219b2ee8SDavid du Colombier pcmio(int slotno, ISAConf *isa)
903219b2ee8SDavid du Colombier {
904219b2ee8SDavid du Colombier 	uchar we, x, *p;
90580ee5cbfSDavid du Colombier 	PCMslot *pp;
90680ee5cbfSDavid du Colombier 	PCMconftab *ct, *et, *t;
907219b2ee8SDavid du Colombier 	PCMmap *m;
9087dd7cddfSDavid du Colombier 	int i, index, irq;
9097dd7cddfSDavid du Colombier 	char *cp;
910219b2ee8SDavid du Colombier 
911219b2ee8SDavid du Colombier 	irq = isa->irq;
912219b2ee8SDavid du Colombier 	if(irq == 2)
913219b2ee8SDavid du Colombier 		irq = 9;
914219b2ee8SDavid du Colombier 
915219b2ee8SDavid du Colombier 	if(slotno > nslot)
916219b2ee8SDavid du Colombier 		return -1;
917219b2ee8SDavid du Colombier 	pp = slot + slotno;
918219b2ee8SDavid du Colombier 
919219b2ee8SDavid du Colombier 	if(!pp->occupied)
920219b2ee8SDavid du Colombier 		return -1;
921219b2ee8SDavid du Colombier 
9227dd7cddfSDavid du Colombier 	et = &pp->ctab[pp->nctab];
9237dd7cddfSDavid du Colombier 
9247dd7cddfSDavid du Colombier 	ct = 0;
9257dd7cddfSDavid du Colombier 	for(i = 0; i < isa->nopt; i++){
9267dd7cddfSDavid du Colombier 		if(strncmp(isa->opt[i], "index=", 6))
9277dd7cddfSDavid du Colombier 			continue;
9287dd7cddfSDavid du Colombier 		index = strtol(&isa->opt[i][6], &cp, 0);
9297dd7cddfSDavid du Colombier 		if(cp == &isa->opt[i][6] || index >= pp->nctab)
9307dd7cddfSDavid du Colombier 			return -1;
9317dd7cddfSDavid du Colombier 		ct = &pp->ctab[index];
9327dd7cddfSDavid du Colombier 	}
9337dd7cddfSDavid du Colombier 
93415174232SDavid du Colombier 	if(ct == 0){
9357dd7cddfSDavid du Colombier 		/* assume default is right */
9367dd7cddfSDavid du Colombier 		if(pp->def)
9377dd7cddfSDavid du Colombier 			ct = pp->def;
9387dd7cddfSDavid du Colombier 		else
9397dd7cddfSDavid du Colombier 			ct = pp->ctab;
9407dd7cddfSDavid du Colombier 
9417dd7cddfSDavid du Colombier 		/* try for best match */
9427dd7cddfSDavid du Colombier 		if(ct->nio == 0
9437dd7cddfSDavid du Colombier 		|| ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
9447dd7cddfSDavid du Colombier 			for(t = pp->ctab; t < et; t++)
9457dd7cddfSDavid du Colombier 				if(t->nio
9467dd7cddfSDavid du Colombier 				&& t->io[0].start == isa->port
9477dd7cddfSDavid du Colombier 				&& ((1<<irq) & t->irqs)){
9487dd7cddfSDavid du Colombier 					ct = t;
949219b2ee8SDavid du Colombier 					break;
950219b2ee8SDavid du Colombier 				}
9517dd7cddfSDavid du Colombier 		}
9527dd7cddfSDavid du Colombier 		if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
9537dd7cddfSDavid du Colombier 			for(t = pp->ctab; t < et; t++)
9547dd7cddfSDavid du Colombier 				if(t->nio && ((1<<irq) & t->irqs)){
9557dd7cddfSDavid du Colombier 					ct = t;
956219b2ee8SDavid du Colombier 					break;
9577dd7cddfSDavid du Colombier 				}
9587dd7cddfSDavid du Colombier 		}
9597dd7cddfSDavid du Colombier 		if(ct->nio == 0){
9607dd7cddfSDavid du Colombier 			for(t = pp->ctab; t < et; t++)
9617dd7cddfSDavid du Colombier 				if(t->nio){
9627dd7cddfSDavid du Colombier 					ct = t;
9637dd7cddfSDavid du Colombier 					break;
9647dd7cddfSDavid du Colombier 				}
9657dd7cddfSDavid du Colombier 		}
9667dd7cddfSDavid du Colombier 	}
967219b2ee8SDavid du Colombier 
9687dd7cddfSDavid du Colombier 	if(ct == et || ct->nio == 0)
9697dd7cddfSDavid du Colombier 		return -1;
9707dd7cddfSDavid du Colombier 	if(isa->port == 0 && ct->io[0].start == 0)
971219b2ee8SDavid du Colombier 		return -1;
972219b2ee8SDavid du Colombier 
973219b2ee8SDavid du Colombier 	/* route interrupts */
974219b2ee8SDavid du Colombier 	isa->irq = irq;
975219b2ee8SDavid du Colombier 	wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
976219b2ee8SDavid du Colombier 
977219b2ee8SDavid du Colombier 	/* set power and enable device */
978219b2ee8SDavid du Colombier 	x = vcode(ct->vpp1);
979219b2ee8SDavid du Colombier 	wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena);
980219b2ee8SDavid du Colombier 
981219b2ee8SDavid du Colombier 	/* 16-bit data path */
982219b2ee8SDavid du Colombier 	if(ct->bit16)
9837dd7cddfSDavid du Colombier 		x = Ftiming|Fiocs16|Fwidth16;
984219b2ee8SDavid du Colombier 	else
9857dd7cddfSDavid du Colombier 		x = Ftiming;
9867dd7cddfSDavid du Colombier 	if(ct->nio == 2 && ct->io[1].start)
9877dd7cddfSDavid du Colombier 		x |= x<<4;
9887dd7cddfSDavid du Colombier 	wrreg(pp, Rio, x);
989219b2ee8SDavid du Colombier 
9909a747e4fSDavid du Colombier 	/*
9919a747e4fSDavid du Colombier 	 * enable io port map 0
9929a747e4fSDavid du Colombier 	 * the 'top' register value includes the last valid address
9939a747e4fSDavid du Colombier 	 */
994219b2ee8SDavid du Colombier 	if(isa->port == 0)
9957dd7cddfSDavid du Colombier 		isa->port = ct->io[0].start;
996219b2ee8SDavid du Colombier 	we = rdreg(pp, Rwe);
997219b2ee8SDavid du Colombier 	wrreg(pp, Riobtm0lo, isa->port);
998219b2ee8SDavid du Colombier 	wrreg(pp, Riobtm0hi, isa->port>>8);
9997dd7cddfSDavid du Colombier 	i = isa->port+ct->io[0].len-1;
10007dd7cddfSDavid du Colombier 	wrreg(pp, Riotop0lo, i);
10017dd7cddfSDavid du Colombier 	wrreg(pp, Riotop0hi, i>>8);
10027dd7cddfSDavid du Colombier 	we |= 1<<6;
100315174232SDavid du Colombier 	if(ct->nio >= 2 && ct->io[1].start){
10047dd7cddfSDavid du Colombier 		wrreg(pp, Riobtm1lo, ct->io[1].start);
10057dd7cddfSDavid du Colombier 		wrreg(pp, Riobtm1hi, ct->io[1].start>>8);
10067dd7cddfSDavid du Colombier 		i = ct->io[1].start+ct->io[1].len-1;
10077dd7cddfSDavid du Colombier 		wrreg(pp, Riotop1lo, i);
10087dd7cddfSDavid du Colombier 		wrreg(pp, Riotop1hi, i>>8);
10097dd7cddfSDavid du Colombier 		we |= 1<<7;
10107dd7cddfSDavid du Colombier 	}
10117dd7cddfSDavid du Colombier 	wrreg(pp, Rwe, we);
1012219b2ee8SDavid du Colombier 
1013219b2ee8SDavid du Colombier 	/* only touch Rconfig if it is present */
101415174232SDavid du Colombier 	m = pcmmap(slotno, pp->cfg[0].caddr + Rconfig, 0x20, 1);
101515174232SDavid du Colombier 	p = KADDR(m->isa + pp->cfg[0].caddr - m->ca);
101615174232SDavid du Colombier 	if(pp->cfg[0].cpresent & (1<<Rconfig)){
1017219b2ee8SDavid du Colombier 		/*  Reset adapter */
1018219b2ee8SDavid du Colombier 
10199a747e4fSDavid du Colombier 		/*  set configuration and interrupt type.
10209a747e4fSDavid du Colombier 		 *  if level is possible on the card, use it.
10219a747e4fSDavid du Colombier 		 */
1022219b2ee8SDavid du Colombier 		x = ct->index;
10239a747e4fSDavid du Colombier 		if(ct->irqtype & 0x20)
1024219b2ee8SDavid du Colombier 			x |= Clevel;
1025219b2ee8SDavid du Colombier 
102615174232SDavid du Colombier 		/*  enable the device, enable address decode and
102715174232SDavid du Colombier 		 *  irq enable.
102815174232SDavid du Colombier 		 */
102915174232SDavid du Colombier 		x |= Cfunc|Cdecode|Cirq;
103015174232SDavid du Colombier 
103115174232SDavid du Colombier 		p[0] = x;
103215174232SDavid du Colombier 		//delay(5);
103315174232SDavid du Colombier 		microdelay(40);
1034219b2ee8SDavid du Colombier 	}
103515174232SDavid du Colombier 
103615174232SDavid du Colombier 	if(pp->cfg[0].cpresent & (1<<Riobase0)){
103715174232SDavid du Colombier 		/* set up the iobase 0 */
103815174232SDavid du Colombier 		p[Riobase0 << 1] = isa->port;
103915174232SDavid du Colombier 		p[Riobase1 << 1] = isa->port >> 8;
104015174232SDavid du Colombier 	}
104115174232SDavid du Colombier 
104215174232SDavid du Colombier 	if(pp->cfg[0].cpresent & (1<<Riosize))
104315174232SDavid du Colombier 		p[Riosize << 1] = ct->io[0].len;
104415174232SDavid du Colombier 	pcmunmap(slotno, m);
1045219b2ee8SDavid du Colombier 	return 0;
1046219b2ee8SDavid du Colombier }
1047