esp_pcmcia.c revision 1.12
1/*	$NetBSD: esp_pcmcia.c,v 1.12 2002/09/27 20:40:55 thorpej Exp $	*/
2
3/*-
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles M. Hannum.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *        This product includes software developed by the NetBSD
21 *        Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 *    contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#include <sys/cdefs.h>
40__KERNEL_RCSID(0, "$NetBSD: esp_pcmcia.c,v 1.12 2002/09/27 20:40:55 thorpej Exp $");
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/device.h>
45#include <sys/buf.h>
46
47#include <machine/bus.h>
48#include <machine/intr.h>
49
50#include <dev/scsipi/scsi_all.h>
51#include <dev/scsipi/scsipi_all.h>
52#include <dev/scsipi/scsiconf.h>
53
54#include <dev/pcmcia/pcmciareg.h>
55#include <dev/pcmcia/pcmciavar.h>
56#include <dev/pcmcia/pcmciadevs.h>
57
58#include <dev/ic/ncr53c9xreg.h>
59#include <dev/ic/ncr53c9xvar.h>
60
61struct esp_pcmcia_softc {
62	struct ncr53c9x_softc	sc_ncr53c9x;	/* glue to MI code */
63
64	int		sc_active;		/* Pseudo-DMA state vars */
65	int		sc_tc;
66	int		sc_datain;
67	size_t		sc_dmasize;
68	size_t		sc_dmatrans;
69	char		**sc_dmaaddr;
70	size_t		*sc_pdmalen;
71
72	/* PCMCIA-specific goo. */
73	struct pcmcia_io_handle sc_pcioh;	/* PCMCIA i/o space info */
74	int sc_io_window;			/* our i/o window */
75	struct pcmcia_function *sc_pf;		/* our PCMCIA function */
76	void *sc_ih;				/* interrupt handler */
77#ifdef ESP_PCMCIA_POLL
78	struct callout sc_poll_ch;
79#endif
80	int sc_flags;
81#define	ESP_PCMCIA_ATTACHED	1		/* attach completed */
82#define ESP_PCMCIA_ATTACHING	2		/* attach in progress */
83};
84
85int	esp_pcmcia_match __P((struct device *, struct cfdata *, void *));
86void	esp_pcmcia_attach __P((struct device *, struct device *, void *));
87void	esp_pcmcia_init __P((struct esp_pcmcia_softc *));
88int	esp_pcmcia_detach __P((struct device *, int));
89int	esp_pcmcia_enable __P((void *, int));
90
91const struct cfattach esp_pcmcia_ca = {
92	sizeof(struct esp_pcmcia_softc), esp_pcmcia_match, esp_pcmcia_attach,
93	esp_pcmcia_detach
94};
95
96/*
97 * Functions and the switch for the MI code.
98 */
99#ifdef ESP_PCMCIA_POLL
100void	esp_pcmcia_poll __P((void *));
101#endif
102u_char	esp_pcmcia_read_reg __P((struct ncr53c9x_softc *, int));
103void	esp_pcmcia_write_reg __P((struct ncr53c9x_softc *, int, u_char));
104int	esp_pcmcia_dma_isintr __P((struct ncr53c9x_softc *));
105void	esp_pcmcia_dma_reset __P((struct ncr53c9x_softc *));
106int	esp_pcmcia_dma_intr __P((struct ncr53c9x_softc *));
107int	esp_pcmcia_dma_setup __P((struct ncr53c9x_softc *, caddr_t *,
108	    size_t *, int, size_t *));
109void	esp_pcmcia_dma_go __P((struct ncr53c9x_softc *));
110void	esp_pcmcia_dma_stop __P((struct ncr53c9x_softc *));
111int	esp_pcmcia_dma_isactive __P((struct ncr53c9x_softc *));
112
113struct ncr53c9x_glue esp_pcmcia_glue = {
114	esp_pcmcia_read_reg,
115	esp_pcmcia_write_reg,
116	esp_pcmcia_dma_isintr,
117	esp_pcmcia_dma_reset,
118	esp_pcmcia_dma_intr,
119	esp_pcmcia_dma_setup,
120	esp_pcmcia_dma_go,
121	esp_pcmcia_dma_stop,
122	esp_pcmcia_dma_isactive,
123	NULL,			/* gl_clear_latched_intr */
124};
125
126const struct pcmcia_product esp_pcmcia_products[] = {
127	{ PCMCIA_STR_PANASONIC_KXLC002,		PCMCIA_VENDOR_PANASONIC,
128	  PCMCIA_PRODUCT_PANASONIC_KXLC002,	0 },
129
130	{ NULL }
131};
132
133int
134esp_pcmcia_match(parent, match, aux)
135	struct device *parent;
136	struct cfdata *match;
137	void *aux;
138{
139	struct pcmcia_attach_args *pa = aux;
140
141	if (pcmcia_product_lookup(pa, esp_pcmcia_products,
142	    sizeof esp_pcmcia_products[0], NULL) != NULL)
143		return (1);
144	return (0);
145}
146
147void
148esp_pcmcia_attach(parent, self, aux)
149	struct device *parent, *self;
150	void *aux;
151{
152	struct esp_pcmcia_softc *esc = (void *)self;
153	struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x;
154	struct pcmcia_attach_args *pa = aux;
155	struct pcmcia_config_entry *cfe;
156	struct pcmcia_function *pf = pa->pf;
157	const struct pcmcia_product *pp;
158
159	esc->sc_pf = pf;
160
161	SIMPLEQ_FOREACH(cfe, &pf->cfe_head, cfe_list) {
162		if (cfe->num_memspace != 0 ||
163		    cfe->num_iospace != 1)
164			continue;
165
166		if (pcmcia_io_alloc(pa->pf, cfe->iospace[0].start,
167		    cfe->iospace[0].length, 0, &esc->sc_pcioh) == 0)
168			break;
169	}
170
171	if (cfe == 0) {
172		printf(": can't alloc i/o space\n");
173		goto no_config_entry;
174	}
175
176	/* Enable the card. */
177	pcmcia_function_init(pf, cfe);
178	if (pcmcia_function_enable(pf)) {
179		printf(": function enable failed\n");
180		goto enable_failed;
181	}
182
183	/* Map in the I/O space */
184	if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0, esc->sc_pcioh.size,
185	    &esc->sc_pcioh, &esc->sc_io_window)) {
186		printf(": can't map i/o space\n");
187		goto iomap_failed;
188	}
189
190	pp = pcmcia_product_lookup(pa, esp_pcmcia_products,
191	    sizeof esp_pcmcia_products[0], NULL);
192	if (pp == NULL) {
193		printf("\n");
194		panic("esp_pcmcia_attach: impossible");
195	}
196
197	printf(": %s\n", pp->pp_name);
198
199	esp_pcmcia_init(esc);
200
201	/*
202	 *  Initialize nca board itself.
203	 */
204	esc->sc_flags |= ESP_PCMCIA_ATTACHING;
205	sc->sc_adapter.adapt_minphys = minphys;
206	sc->sc_adapter.adapt_request = ncr53c9x_scsipi_request;
207	ncr53c9x_attach(sc);
208	esc->sc_flags &= ~ESP_PCMCIA_ATTACHING;
209	esc->sc_flags |= ESP_PCMCIA_ATTACHED;
210	return;
211
212iomap_failed:
213	/* Disable the device. */
214	pcmcia_function_disable(esc->sc_pf);
215
216enable_failed:
217	/* Unmap our I/O space. */
218	pcmcia_io_free(esc->sc_pf, &esc->sc_pcioh);
219
220no_config_entry:
221	return;
222}
223
224void
225esp_pcmcia_init(esc)
226	struct esp_pcmcia_softc *esc;
227{
228	struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x;
229	bus_space_tag_t iot = esc->sc_pcioh.iot;
230	bus_space_handle_t ioh = esc->sc_pcioh.ioh;
231
232	/* id 7, clock 40M, parity ON, sync OFF, fast ON, slow ON */
233
234	sc->sc_glue = &esp_pcmcia_glue;
235
236#ifdef ESP_PCMCIA_POLL
237	callout_init(&esc->sc_poll_ch);
238#endif
239
240	sc->sc_rev = NCR_VARIANT_ESP406;
241	sc->sc_id = 7;
242	sc->sc_freq = 40;
243	/* try -PARENB -SLOW */
244	sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB | NCRCFG1_SLOW;
245	/* try +FE */
246	sc->sc_cfg2 = NCRCFG2_SCSI2;
247	/* try -IDM -FSCSI -FCLK */
248	sc->sc_cfg3 = NCRESPCFG3_CDB | NCRESPCFG3_FCLK | NCRESPCFG3_IDM |
249	    NCRESPCFG3_FSCSI;
250	sc->sc_cfg4 = NCRCFG4_ACTNEG;
251	/* try +INTP */
252	sc->sc_cfg5 = NCRCFG5_CRS1 | NCRCFG5_AADDR | NCRCFG5_PTRINC;
253	sc->sc_minsync = 0;
254	sc->sc_maxxfer = 64 * 1024;
255
256	bus_space_write_1(iot, ioh, NCR_CFG5, sc->sc_cfg5);
257
258	bus_space_write_1(iot, ioh, NCR_PIOI, 0);
259	bus_space_write_1(iot, ioh, NCR_PSTAT, 0);
260	bus_space_write_1(iot, ioh, 0x09, 0x24);
261
262	bus_space_write_1(iot, ioh, NCR_CFG4, sc->sc_cfg4);
263}
264
265int
266esp_pcmcia_detach(self, flags)
267	struct device *self;
268	int flags;
269{
270	struct esp_pcmcia_softc *esc = (void *)self;
271	int error;
272
273	if ((esc->sc_flags & ESP_PCMCIA_ATTACHED) == 0) {
274		/* Nothing to detach. */
275		return (0);
276	}
277
278	error = ncr53c9x_detach(&esc->sc_ncr53c9x, flags);
279	if (error)
280		return (error);
281
282	/* Unmap our i/o window and i/o space. */
283	pcmcia_io_unmap(esc->sc_pf, esc->sc_io_window);
284	pcmcia_io_free(esc->sc_pf, &esc->sc_pcioh);
285
286	return (0);
287}
288
289int
290esp_pcmcia_enable(arg, onoff)
291	void *arg;
292	int onoff;
293{
294	struct esp_pcmcia_softc *esc = arg;
295
296	if (onoff) {
297#ifdef ESP_PCMCIA_POLL
298		callout_reset(&esc->sc_poll_ch, 1, esp_pcmcia_poll, esc);
299#else
300		/* Establish the interrupt handler. */
301		esc->sc_ih = pcmcia_intr_establish(esc->sc_pf, IPL_BIO,
302		    ncr53c9x_intr, &esc->sc_ncr53c9x);
303		if (esc->sc_ih == NULL) {
304			printf("%s: couldn't establish interrupt handler\n",
305			    esc->sc_ncr53c9x.sc_dev.dv_xname);
306			return (EIO);
307		}
308#endif
309
310		/*
311		 * If attach is in progress, we know that card power is
312		 * enabled and chip will be initialized later.
313		 * Otherwise, enable and reset now.
314		 */
315		if ((esc->sc_flags & ESP_PCMCIA_ATTACHING) == 0) {
316			if (pcmcia_function_enable(esc->sc_pf)) {
317				printf("%s: couldn't enable PCMCIA function\n",
318				    esc->sc_ncr53c9x.sc_dev.dv_xname);
319				pcmcia_intr_disestablish(esc->sc_pf,
320				    esc->sc_ih);
321				return (EIO);
322			}
323
324			/* Initialize only chip.  */
325			ncr53c9x_init(&esc->sc_ncr53c9x, 0);
326		}
327	} else {
328		pcmcia_function_disable(esc->sc_pf);
329#ifdef ESP_PCMCIA_POLL
330		callout_stop(&esc->sc_poll_ch);
331#else
332		pcmcia_intr_disestablish(esc->sc_pf, esc->sc_ih);
333#endif
334	}
335
336	return (0);
337}
338
339#ifdef ESP_PCMCIA_POLL
340void
341esp_pcmcia_poll(arg)
342	void *arg;
343{
344	struct esp_pcmcia_softc *esc = arg;
345
346	(void) ncr53c9x_intr(&esc->sc_ncr53c9x);
347	callout_reset(&esc->sc_poll_ch, 1, esp_pcmcia_poll, esc);
348}
349#endif
350
351/*
352 * Glue functions.
353 */
354u_char
355esp_pcmcia_read_reg(sc, reg)
356	struct ncr53c9x_softc *sc;
357	int reg;
358{
359	struct esp_pcmcia_softc *esc = (struct esp_pcmcia_softc *)sc;
360	u_char v;
361
362	v = bus_space_read_1(esc->sc_pcioh.iot, esc->sc_pcioh.ioh, reg);
363	return v;
364}
365
366void
367esp_pcmcia_write_reg(sc, reg, val)
368	struct ncr53c9x_softc *sc;
369	int reg;
370	u_char val;
371{
372	struct esp_pcmcia_softc *esc = (struct esp_pcmcia_softc *)sc;
373	u_char v = val;
374
375	if (reg == NCR_CMD && v == (NCRCMD_TRANS|NCRCMD_DMA))
376		v = NCRCMD_TRANS;
377	bus_space_write_1(esc->sc_pcioh.iot, esc->sc_pcioh.ioh, reg, v);
378}
379
380int
381esp_pcmcia_dma_isintr(sc)
382	struct ncr53c9x_softc *sc;
383{
384
385	return NCR_READ_REG(sc, NCR_STAT) & NCRSTAT_INT;
386}
387
388void
389esp_pcmcia_dma_reset(sc)
390	struct ncr53c9x_softc *sc;
391{
392	struct esp_pcmcia_softc *esc = (struct esp_pcmcia_softc *)sc;
393
394	esc->sc_active = 0;
395	esc->sc_tc = 0;
396}
397
398int
399esp_pcmcia_dma_intr(sc)
400	struct ncr53c9x_softc *sc;
401{
402	struct esp_pcmcia_softc *esc = (struct esp_pcmcia_softc *)sc;
403	u_char	*p;
404	u_int	espphase, espstat, espintr;
405	int	cnt;
406
407	if (esc->sc_active == 0) {
408		printf("%s: dma_intr--inactive DMA\n", sc->sc_dev.dv_xname);
409		return -1;
410	}
411
412	if ((sc->sc_espintr & NCRINTR_BS) == 0) {
413		esc->sc_active = 0;
414		return 0;
415	}
416
417	cnt = *esc->sc_pdmalen;
418	if (*esc->sc_pdmalen == 0) {
419		printf("%s: data interrupt, but no count left\n",
420		    sc->sc_dev.dv_xname);
421	}
422
423	p = *esc->sc_dmaaddr;
424	espphase = sc->sc_phase;
425	espstat = (u_int) sc->sc_espstat;
426	espintr = (u_int) sc->sc_espintr;
427	do {
428		if (esc->sc_datain) {
429			*p++ = NCR_READ_REG(sc, NCR_FIFO);
430			cnt--;
431			if (espphase == DATA_IN_PHASE)
432				NCR_WRITE_REG(sc, NCR_CMD, NCRCMD_TRANS);
433			else
434				esc->sc_active = 0;
435	 	} else {
436			if (espphase == DATA_OUT_PHASE ||
437			    espphase == MESSAGE_OUT_PHASE) {
438				NCR_WRITE_REG(sc, NCR_FIFO, *p++);
439				cnt--;
440				NCR_WRITE_REG(sc, NCR_CMD, NCRCMD_TRANS);
441			} else
442				esc->sc_active = 0;
443		}
444
445		if (esc->sc_active) {
446			while (!(NCR_READ_REG(sc, NCR_STAT) & NCRSTAT_INT));
447			espstat = NCR_READ_REG(sc, NCR_STAT);
448			espintr = NCR_READ_REG(sc, NCR_INTR);
449			espphase = (espintr & NCRINTR_DIS)
450				    ? /* Disconnected */ BUSFREE_PHASE
451				    : espstat & PHASE_MASK;
452		}
453	} while (esc->sc_active && espintr);
454	sc->sc_phase = espphase;
455	sc->sc_espstat = (u_char) espstat;
456	sc->sc_espintr = (u_char) espintr;
457	*esc->sc_dmaaddr = p;
458	*esc->sc_pdmalen = cnt;
459
460	if (*esc->sc_pdmalen == 0)
461		esc->sc_tc = NCRSTAT_TC;
462	sc->sc_espstat |= esc->sc_tc;
463	return 0;
464}
465
466int
467esp_pcmcia_dma_setup(sc, addr, len, datain, dmasize)
468	struct ncr53c9x_softc *sc;
469	caddr_t *addr;
470	size_t *len;
471	int datain;
472	size_t *dmasize;
473{
474	struct esp_pcmcia_softc *esc = (struct esp_pcmcia_softc *)sc;
475
476	esc->sc_dmaaddr = addr;
477	esc->sc_pdmalen = len;
478	esc->sc_datain = datain;
479	esc->sc_dmasize = *dmasize;
480	esc->sc_tc = 0;
481
482	return 0;
483}
484
485void
486esp_pcmcia_dma_go(sc)
487	struct ncr53c9x_softc *sc;
488{
489	struct esp_pcmcia_softc *esc = (struct esp_pcmcia_softc *)sc;
490
491	esc->sc_active = 1;
492}
493
494void
495esp_pcmcia_dma_stop(sc)
496	struct ncr53c9x_softc *sc;
497{
498}
499
500int
501esp_pcmcia_dma_isactive(sc)
502	struct ncr53c9x_softc *sc;
503{
504	struct esp_pcmcia_softc *esc = (struct esp_pcmcia_softc *)sc;
505
506	return (esc->sc_active);
507}
508