1/* $NetBSD: if_cs_pcmcia.c,v 1.18 2009/05/12 14:42:18 cegger Exp $ */
2
3/*-
4 * Copyright (c)2001 YAMAMOTO Takashi,
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: if_cs_pcmcia.c,v 1.18 2009/05/12 14:42:18 cegger Exp $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/device.h>
35#include <sys/socket.h>
36#include <sys/queue.h>
37
38#include <sys/rnd.h>
39
40#include <net/if.h>
41#include <net/if_ether.h>
42#include <net/if_media.h>
43
44#include <sys/bus.h>
45#include <sys/intr.h>
46
47#include <dev/pcmcia/pcmciareg.h>
48#include <dev/pcmcia/pcmciavar.h>
49#include <dev/pcmcia/pcmciadevs.h>
50
51#include <dev/ic/cs89x0reg.h>
52#include <dev/ic/cs89x0var.h>
53
54struct cs_pcmcia_softc;
55
56static int cs_pcmcia_match(device_t, cfdata_t, void *);
57static int cs_pcmcia_validate_config(struct pcmcia_config_entry *);
58static void cs_pcmcia_attach(device_t, device_t, void *);
59static int cs_pcmcia_detach(device_t, int);
60static int cs_pcmcia_enable(struct cs_softc *);
61static void cs_pcmcia_disable(struct cs_softc *);
62
63struct cs_pcmcia_softc {
64	struct cs_softc sc_cs; /* real "cs" softc */
65
66	struct pcmcia_function *sc_pf;
67
68	int sc_state;
69#define	CS_PCMCIA_ATTACHED	3
70};
71
72CFATTACH_DECL(cs_pcmcia, sizeof(struct cs_pcmcia_softc),
73    cs_pcmcia_match, cs_pcmcia_attach, cs_pcmcia_detach, cs_activate);
74
75static int
76cs_pcmcia_match(device_t parent, cfdata_t match,
77    void *aux)
78{
79	struct pcmcia_attach_args *pa = aux;
80
81	if (pa->manufacturer == PCMCIA_VENDOR_IBM &&
82	    pa->product == PCMCIA_PRODUCT_IBM_ETHERJET)
83		return (1);
84	return (0);
85}
86
87static int
88cs_pcmcia_validate_config(struct pcmcia_config_entry *cfe)
89{
90	if (cfe->iftype != PCMCIA_IFTYPE_IO ||
91	    cfe->num_memspace != 0 ||
92	    cfe->num_iospace != 1 ||
93	    cfe->iospace[0].length < CS8900_IOSIZE)
94		return (EINVAL);
95	return (0);
96}
97
98static void
99cs_pcmcia_attach(device_t parent, device_t self, void *aux)
100{
101	struct cs_pcmcia_softc *psc = (void *)self;
102	struct cs_softc *sc = (void *)&psc->sc_cs;
103	struct pcmcia_attach_args *pa = aux;
104	struct pcmcia_config_entry *cfe;
105	struct pcmcia_function *pf;
106	int error;
107
108	pf = psc->sc_pf = pa->pf;
109
110	error = pcmcia_function_configure(pa->pf, cs_pcmcia_validate_config);
111	if (error) {
112		aprint_error_dev(self, "configure failed, error=%d\n",
113		    error);
114		return;
115	}
116
117	cfe = pf->cfe;
118	sc->sc_iot = cfe->iospace[0].handle.iot;
119	sc->sc_ioh = cfe->iospace[0].handle.ioh;
120	sc->sc_irq = -1;
121#define CS_PCMCIA_HACK_FOR_CARDBUS
122#ifdef CS_PCMCIA_HACK_FOR_CARDBUS
123	/*
124	 * XXX is there a generic way to know if it's a cardbus or not?
125	 */
126	sc->sc_cfgflags |= CFGFLG_CARDBUS_HACK;
127#endif
128
129	error = cs_pcmcia_enable(sc);
130	if (error)
131		goto fail;
132
133	sc->sc_enable = cs_pcmcia_enable;
134	sc->sc_disable = cs_pcmcia_disable;
135
136	/* chip attach */
137	error = cs_attach(sc, 0, 0, 0, 0);
138	if (error)
139		goto fail2;
140
141	cs_pcmcia_disable(sc);
142	psc->sc_state = CS_PCMCIA_ATTACHED;
143	return;
144
145fail2:
146	cs_pcmcia_disable(sc);
147fail:
148	pcmcia_function_unconfigure(pf);
149}
150
151static int
152cs_pcmcia_detach(device_t self, int flags)
153{
154	struct cs_pcmcia_softc *psc = (void *)self;
155	struct cs_softc *sc = &psc->sc_cs;
156	int error;
157
158	if (psc->sc_state != CS_PCMCIA_ATTACHED)
159		return (0);
160
161	error = cs_detach(sc);
162	if (error)
163		return (error);
164
165	pcmcia_function_unconfigure(psc->sc_pf);
166
167	return (0);
168}
169
170static int
171cs_pcmcia_enable(struct cs_softc *sc)
172{
173	struct cs_pcmcia_softc *psc = (void *)sc;
174	int error;
175
176	sc->sc_ih = pcmcia_intr_establish(psc->sc_pf, IPL_NET, cs_intr, sc);
177	if (!sc->sc_ih)
178		return (EIO);
179
180	error = pcmcia_function_enable(psc->sc_pf);
181	if (error) {
182		pcmcia_intr_disestablish(psc->sc_pf, sc->sc_ih);
183		sc->sc_ih = 0;
184	}
185
186	return (error);
187}
188
189static void
190cs_pcmcia_disable(struct cs_softc *sc)
191{
192	struct cs_pcmcia_softc *psc = (void *)sc;
193
194	pcmcia_function_disable(psc->sc_pf);
195	pcmcia_intr_disestablish(psc->sc_pf, sc->sc_ih);
196	sc->sc_ih = 0;
197}
198