ncr53c500_pccard.c revision 113315
1/*	$FreeBSD: head/sys/dev/ncv/ncr53c500_pccard.c 113315 2003-04-10 04:36:02Z imp $	*/
2/*	$NecBSD: ncr53c500_pisa.c,v 1.28 1998/11/26 01:59:11 honda Exp $	*/
3/*	$NetBSD$	*/
4
5/*
6 * [Ported for FreeBSD]
7 *  Copyright (c) 2000
8 *      Noriaki Mitsunaga, Mitsuru Iwasaki and Takanori Watanabe.
9 *      All rights reserved.
10 * [NetBSD for NEC PC-98 series]
11 *  Copyright (c) 1995, 1996, 1997, 1998
12 *	NetBSD/pc98 porting staff. All rights reserved.
13 *  Copyright (c) 1995, 1996, 1997, 1998
14 *	Naofumi HONDA. All rights reserved.
15 *
16 *  Redistribution and use in source and binary forms, with or without
17 *  modification, are permitted provided that the following conditions
18 *  are met:
19 *  1. Redistributions of source code must retain the above copyright
20 *     notice, this list of conditions and the following disclaimer.
21 *  2. Redistributions in binary form must reproduce the above copyright
22 *     notice, this list of conditions and the following disclaimer in the
23 *     documentation and/or other materials provided with the distribution.
24 *  3. The name of the author may not be used to endorse or promote products
25 *     derived from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
31 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/malloc.h>
43#include <sys/errno.h>
44
45#include <machine/bus.h>
46#include <machine/bus_pio.h>
47#include <machine/dvcfg.h>
48
49#include <sys/device_port.h>
50
51#include <dev/pccard/pccarddevs.h>
52#include <dev/pccard/pccardvar.h>
53
54#include <cam/scsi/scsi_low.h>
55#include <cam/scsi/scsi_low_pisa.h>
56
57#include <dev/ncv/ncr53c500reg.h>
58#include <dev/ncv/ncr53c500hw.h>
59#include <dev/ncv/ncr53c500var.h>
60
61#define KME_KXLC004_01 0x100
62#define OFFSET_KME_KXLC004_01 0x10
63
64#include	<sys/kernel.h>
65#include	<sys/module.h>
66#if !defined(__FreeBSD__) || __FreeBSD_version < 500014
67#include	<sys/select.h>
68#endif
69#include	<pccard/cardinfo.h>
70#include	<pccard/slot.h>
71
72static int ncvprobe(DEVPORT_PDEVICE devi);
73static int ncvattach(DEVPORT_PDEVICE devi);
74
75static void	ncv_card_unload(DEVPORT_PDEVICE);
76
77static const struct ncv_product {
78	struct pccard_product	prod;
79	int flags;
80} ncv_products[] = {
81	{ PCMCIA_CARD(EPSON, SC200, 0), 0},
82	{ PCMCIA_CARD(PANASONIC, KXLC002, 0), 0xb4d00000 },
83	{ PCMCIA_CARD(PANASONIC, KXLC004, 0), 0xb4d00100 },
84	{ PCMCIA_CARD(MACNICA, MPS100, 0), 0xb6250000 },
85	{ PCMCIA_CARD(MACNICA, MPS110, 0), 0 },
86	{ PCMCIA_CARD(NEC, PC9801N_J03R, 0), 0 },
87	{ PCMCIA_CARD(NEWMEDIA, BASICS_SCSI, 0), 0 },
88	{ PCMCIA_CARD(QLOGIC, PC05, 0), 0x84d00000 },
89#define FLAGS_REX5572 0x84d00000
90	{ PCMCIA_CARD(RATOC, REX5572, 0), FLAGS_REX5572 },
91	{ PCMCIA_CARD(RATOC, REX9530, 0), 0x84d00000 },
92	{ { NULL }, 0 }
93};
94
95/*
96 * Additional code for FreeBSD new-bus PCCard frontend
97 */
98
99static void
100ncv_pccard_intr(void * arg)
101{
102	ncvintr(arg);
103}
104
105static void
106ncv_release_resource(DEVPORT_PDEVICE dev)
107{
108	struct ncv_softc	*sc = device_get_softc(dev);
109
110	if (sc->ncv_intrhand) {
111		bus_teardown_intr(dev, sc->irq_res, sc->ncv_intrhand);
112	}
113
114	if (sc->port_res) {
115		bus_release_resource(dev, SYS_RES_IOPORT,
116				     sc->port_rid, sc->port_res);
117	}
118
119	if (sc->port_res_dmy) {
120		bus_release_resource(dev, SYS_RES_IOPORT,
121				     sc->port_rid_dmy, sc->port_res_dmy);
122	}
123
124	if (sc->irq_res) {
125		bus_release_resource(dev, SYS_RES_IRQ,
126				     sc->irq_rid, sc->irq_res);
127	}
128
129	if (sc->mem_res) {
130		bus_release_resource(dev, SYS_RES_MEMORY,
131				     sc->mem_rid, sc->mem_res);
132	}
133}
134
135static int
136ncv_alloc_resource(DEVPORT_PDEVICE dev)
137{
138	struct ncv_softc	*sc = device_get_softc(dev);
139	u_int32_t		flags = DEVPORT_PDEVFLAGS(dev);
140	u_long			ioaddr, iosize, maddr, msize;
141	int			error;
142	bus_addr_t		offset = 0;
143
144	if(flags & KME_KXLC004_01)
145		offset = OFFSET_KME_KXLC004_01;
146
147	error = bus_get_resource(dev, SYS_RES_IOPORT, 0, &ioaddr, &iosize);
148	if (error || (iosize < (offset + NCVIOSZ))) {
149		return(ENOMEM);
150	}
151
152	sc->port_rid = 0;
153	sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid,
154					  ioaddr+offset, ioaddr+iosize-offset,
155					  iosize-offset, RF_ACTIVE);
156	if (sc->port_res == NULL) {
157		ncv_release_resource(dev);
158		return(ENOMEM);
159	}
160
161	if (offset != 0) {
162		sc->port_rid_dmy = 0;
163		sc->port_res_dmy = bus_alloc_resource(dev, SYS_RES_IOPORT,
164						&sc->port_rid_dmy,
165						ioaddr, ioaddr+offset, offset,
166						RF_ACTIVE);
167		if (sc->port_res_dmy == NULL) {
168			printf("Warning: cannot allocate IOPORT partially.\n");
169		}
170	} else {
171		sc->port_rid_dmy = 0;
172		sc->port_res_dmy = NULL;
173	}
174
175	sc->irq_rid = 0;
176	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
177					 0, ~0, 1, RF_ACTIVE);
178	if (sc->irq_res == NULL) {
179		ncv_release_resource(dev);
180		return(ENOMEM);
181	}
182
183	error = bus_get_resource(dev, SYS_RES_MEMORY, 0, &maddr, &msize);
184	if (error) {
185		return(0);	/* XXX */
186	}
187
188	/* no need to allocate memory if not configured */
189	if (maddr == 0 || msize == 0) {
190		return(0);
191	}
192
193	sc->mem_rid = 0;
194	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem_rid,
195					 0, ~0, 1, RF_ACTIVE);
196	if (sc->mem_res == NULL) {
197		ncv_release_resource(dev);
198		return(ENOMEM);
199	}
200
201	return(0);
202}
203
204static int ncv_pccard_match(device_t dev)
205{
206	const struct ncv_product *pp;
207	char *vendorstr;
208	char *prodstr;
209
210	if ((pp = (const struct ncv_product *) pccard_product_lookup(dev,
211	    (const struct pccard_product *) ncv_products,
212	    sizeof(ncv_products[0]), NULL)) != NULL) {
213		if (pp->prod.pp_name != NULL)
214			device_set_desc(dev, pp->prod.pp_name);
215		device_set_flags(dev, pp->flags);
216		return(0);
217	}
218	if (pccard_get_vendor_str(dev, &vendorstr))
219		return(EIO);
220	if (pccard_get_product_str(dev, &prodstr))
221		return(EIO);
222	if (strcmp(vendorstr, "RATOC System Inc.") == 0 &&
223		strncmp(prodstr, "SOUND/SCSI2 CARD", 16) == 0) {
224		device_set_desc(dev, "RATOC REX-5572");
225		device_set_flags(dev, FLAGS_REX5572);
226		return (0);
227	}
228	return(EIO);
229}
230
231static int
232ncv_pccard_probe(DEVPORT_PDEVICE dev)
233{
234	struct ncv_softc	*sc = device_get_softc(dev);
235	int			error;
236
237	bzero(sc, sizeof(struct ncv_softc));
238
239	error = ncv_alloc_resource(dev);
240	if (error) {
241		return(error);
242	}
243
244	if (ncvprobe(dev) == 0) {
245		ncv_release_resource(dev);
246		return(ENXIO);
247	}
248
249	ncv_release_resource(dev);
250
251	return(0);
252}
253
254static int
255ncv_pccard_attach(DEVPORT_PDEVICE dev)
256{
257	struct ncv_softc	*sc = device_get_softc(dev);
258	int			error;
259
260	error = ncv_alloc_resource(dev);
261	if (error) {
262		return(error);
263	}
264
265	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CAM | INTR_ENTROPY,
266			       ncv_pccard_intr, (void *)sc, &sc->ncv_intrhand);
267	if (error) {
268		ncv_release_resource(dev);
269		return(error);
270	}
271
272	if (ncvattach(dev) == 0) {
273		ncv_release_resource(dev);
274		return(ENXIO);
275	}
276
277	return(0);
278}
279
280static	void
281ncv_pccard_detach(DEVPORT_PDEVICE dev)
282{
283	ncv_card_unload(dev);
284	ncv_release_resource(dev);
285}
286
287static device_method_t ncv_pccard_methods[] = {
288	/* Device interface */
289	DEVMETHOD(device_probe,		pccard_compat_probe),
290	DEVMETHOD(device_attach,	pccard_compat_attach),
291	DEVMETHOD(device_detach,	ncv_pccard_detach),
292
293	/* Card interface */
294	DEVMETHOD(card_compat_match,	ncv_pccard_match),
295	DEVMETHOD(card_compat_probe,	ncv_pccard_probe),
296	DEVMETHOD(card_compat_attach,	ncv_pccard_attach),
297
298	{ 0, 0 }
299};
300
301static driver_t ncv_pccard_driver = {
302	"ncv",
303	ncv_pccard_methods,
304	sizeof(struct ncv_softc),
305};
306
307static devclass_t ncv_devclass;
308
309MODULE_DEPEND(ncv, scsi_low, 1, 1, 1);
310DRIVER_MODULE(ncv, pccard, ncv_pccard_driver, ncv_devclass, 0, 0);
311
312static void
313ncv_card_unload(DEVPORT_PDEVICE devi)
314{
315	struct ncv_softc *sc = DEVPORT_PDEVGET_SOFTC(devi);
316	intrmask_t s;
317
318	printf("%s: unload\n", sc->sc_sclow.sl_xname);
319	s = splcam();
320	scsi_low_deactivate((struct scsi_low_softc *)sc);
321        scsi_low_dettach(&sc->sc_sclow);
322	splx(s);
323}
324
325static int
326ncvprobe(DEVPORT_PDEVICE devi)
327{
328	int rv;
329	struct ncv_softc *sc = device_get_softc(devi);
330	u_int32_t flags = DEVPORT_PDEVFLAGS(devi);
331
332	rv = ncvprobesubr(rman_get_bustag(sc->port_res),
333			  rman_get_bushandle(sc->port_res),
334			  flags, NCV_HOSTID);
335
336	return rv;
337}
338
339static int
340ncvattach(DEVPORT_PDEVICE devi)
341{
342	struct ncv_softc *sc;
343	struct scsi_low_softc *slp;
344	u_int32_t flags = DEVPORT_PDEVFLAGS(devi);
345	intrmask_t s;
346	char dvname[16]; /* SCSI_LOW_DVNAME_LEN */
347
348	strcpy(dvname, "ncv");
349
350	sc = DEVPORT_PDEVALLOC_SOFTC(devi);
351	if (sc == NULL) {
352		return(0);
353	}
354
355	slp = &sc->sc_sclow;
356	slp->sl_dev = devi;
357	sc->sc_iot = rman_get_bustag(sc->port_res);
358	sc->sc_ioh = rman_get_bushandle(sc->port_res);
359
360	slp->sl_hostid = NCV_HOSTID;
361	slp->sl_cfgflags = flags;
362
363	s = splcam();
364	ncvattachsubr(sc);
365	splx(s);
366
367	return(NCVIOSZ);
368}
369