1 | /* $NetBSD: if_sm_pcmcia.c,v 1.56 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1997, 1998, 2000, 2004 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, |
9 | * NASA Ames Research Center, and by Charles M. Hannum. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: if_sm_pcmcia.c,v 1.56 2016/07/07 06:55:42 msaitoh Exp $" ); |
35 | |
36 | #include <sys/param.h> |
37 | #include <sys/systm.h> |
38 | #include <sys/mbuf.h> |
39 | #include <sys/socket.h> |
40 | #include <sys/ioctl.h> |
41 | #include <sys/errno.h> |
42 | #include <sys/syslog.h> |
43 | #include <sys/select.h> |
44 | #include <sys/device.h> |
45 | |
46 | #include <net/if.h> |
47 | #include <net/if_dl.h> |
48 | #include <net/if_ether.h> |
49 | #include <net/if_media.h> |
50 | |
51 | #include <sys/intr.h> |
52 | #include <sys/bus.h> |
53 | |
54 | #include <dev/mii/mii.h> |
55 | #include <dev/mii/miivar.h> |
56 | |
57 | #include <dev/ic/smc91cxxreg.h> |
58 | #include <dev/ic/smc91cxxvar.h> |
59 | |
60 | #include <dev/pcmcia/pcmciareg.h> |
61 | #include <dev/pcmcia/pcmciavar.h> |
62 | #include <dev/pcmcia/pcmciadevs.h> |
63 | |
64 | int sm_pcmcia_match(device_t, cfdata_t, void *); |
65 | int sm_pcmcia_validate_config(struct pcmcia_config_entry *); |
66 | void sm_pcmcia_attach(device_t, device_t, void *); |
67 | int sm_pcmcia_detach(device_t, int); |
68 | |
69 | struct sm_pcmcia_softc { |
70 | struct smc91cxx_softc sc_smc; /* real "smc" softc */ |
71 | |
72 | struct pcmcia_function *sc_pf; /* our PCMCIA function */ |
73 | void *sc_ih; /* interrupt cookie */ |
74 | |
75 | int sc_state; |
76 | #define SM_PCMCIA_ATTACHED 3 |
77 | }; |
78 | |
79 | CFATTACH_DECL_NEW(sm_pcmcia, sizeof(struct sm_pcmcia_softc), |
80 | sm_pcmcia_match, sm_pcmcia_attach, sm_pcmcia_detach, smc91cxx_activate); |
81 | |
82 | int sm_pcmcia_enable(struct smc91cxx_softc *); |
83 | void sm_pcmcia_disable(struct smc91cxx_softc *); |
84 | |
85 | int sm_pcmcia_ascii_enaddr(const char *, u_int8_t *); |
86 | |
87 | const struct pcmcia_product sm_pcmcia_products[] = { |
88 | { PCMCIA_VENDOR_MEGAHERTZ2, PCMCIA_PRODUCT_MEGAHERTZ2_EM1144, |
89 | PCMCIA_CIS_INVALID }, |
90 | |
91 | { PCMCIA_VENDOR_MEGAHERTZ2, PCMCIA_PRODUCT_MEGAHERTZ2_XJACK, |
92 | PCMCIA_CIS_INVALID }, |
93 | |
94 | { PCMCIA_VENDOR_NEWMEDIA, PCMCIA_PRODUCT_NEWMEDIA_BASICS, |
95 | PCMCIA_CIS_INVALID }, |
96 | |
97 | #if 0 |
98 | { PCMCIA_VENDOR_SMC, PCMCIA_PRODUCT_SMC_8020BT, |
99 | PCMCIA_CIS_INVALID }, |
100 | #endif |
101 | { PCMCIA_VENDOR_PSION, PCMCIA_PRODUCT_PSION_GOLDCARD, |
102 | PCMCIA_CIS_INVALID }, |
103 | }; |
104 | const size_t sm_pcmcia_nproducts = |
105 | sizeof(sm_pcmcia_products) / sizeof(sm_pcmcia_products[0]); |
106 | |
107 | int |
108 | sm_pcmcia_match(device_t parent, cfdata_t match, |
109 | void *aux) |
110 | { |
111 | struct pcmcia_attach_args *pa = aux; |
112 | |
113 | /* This is to differentiate the serial function of some cards. */ |
114 | if (pa->pf->function != PCMCIA_FUNCTION_NETWORK) |
115 | return (0); |
116 | |
117 | if (pcmcia_product_lookup(pa, sm_pcmcia_products, sm_pcmcia_nproducts, |
118 | sizeof(sm_pcmcia_products[0]), NULL)) |
119 | return (1); |
120 | return (0); |
121 | } |
122 | |
123 | int |
124 | sm_pcmcia_validate_config(struct pcmcia_config_entry *cfe) |
125 | { |
126 | if (cfe->iftype != PCMCIA_IFTYPE_IO || |
127 | cfe->num_memspace != 0 || |
128 | cfe->num_iospace != 1 || |
129 | cfe->iospace[0].length < SMC_IOSIZE) |
130 | return (EINVAL); |
131 | return (0); |
132 | } |
133 | |
134 | void |
135 | sm_pcmcia_attach(device_t parent, device_t self, void *aux) |
136 | { |
137 | struct sm_pcmcia_softc *psc = device_private(self); |
138 | struct smc91cxx_softc *sc = &psc->sc_smc; |
139 | struct pcmcia_attach_args *pa = aux; |
140 | struct pcmcia_config_entry *cfe; |
141 | u_int8_t enaddr[ETHER_ADDR_LEN]; |
142 | int error; |
143 | |
144 | sc->sc_dev = self; |
145 | psc->sc_pf = pa->pf; |
146 | |
147 | error = pcmcia_function_configure(pa->pf, sm_pcmcia_validate_config); |
148 | if (error) { |
149 | aprint_error_dev(self, "configure failed, error=%d\n" , error); |
150 | return; |
151 | } |
152 | |
153 | cfe = pa->pf->cfe; |
154 | sc->sc_bst = cfe->iospace[0].handle.iot; |
155 | sc->sc_bsh = cfe->iospace[0].handle.ioh; |
156 | |
157 | error = sm_pcmcia_enable(sc); |
158 | if (error) |
159 | goto fail; |
160 | |
161 | sc->sc_enable = sm_pcmcia_enable; |
162 | sc->sc_disable = sm_pcmcia_disable; |
163 | |
164 | /* |
165 | * First try to get the Ethernet address from FUNCE/LANNID tuple. |
166 | * If that fails, try one of the CIS info strings. |
167 | */ |
168 | if (pa->pf->pf_funce_lan_nidlen == ETHER_ADDR_LEN) { |
169 | memcpy(enaddr, pa->pf->pf_funce_lan_nid, ETHER_ADDR_LEN); |
170 | } else { |
171 | if (!sm_pcmcia_ascii_enaddr(pa->pf->sc->card.cis1_info[3], enaddr) && |
172 | !sm_pcmcia_ascii_enaddr(pa->pf->sc->card.cis1_info[2], enaddr)) |
173 | aprint_error_dev(self, |
174 | "unable to get Ethernet address\n" ); |
175 | } |
176 | |
177 | /* Perform generic intialization. */ |
178 | smc91cxx_attach(sc, enaddr); |
179 | |
180 | psc->sc_state = SM_PCMCIA_ATTACHED; |
181 | sm_pcmcia_disable(sc); |
182 | return; |
183 | |
184 | fail: |
185 | pcmcia_function_unconfigure(pa->pf); |
186 | } |
187 | |
188 | int |
189 | sm_pcmcia_detach(device_t self, int flags) |
190 | { |
191 | struct sm_pcmcia_softc *psc = device_private(self); |
192 | int error; |
193 | |
194 | if (psc->sc_state != SM_PCMCIA_ATTACHED) |
195 | return (0); |
196 | |
197 | error = smc91cxx_detach((device_t)&psc->sc_smc, flags); |
198 | if (error) |
199 | return (error); |
200 | |
201 | pcmcia_function_unconfigure(psc->sc_pf); |
202 | |
203 | return (0); |
204 | } |
205 | |
206 | int |
207 | sm_pcmcia_ascii_enaddr(const char *cisstr, u_int8_t *myla) |
208 | { |
209 | u_int8_t digit; |
210 | int i; |
211 | |
212 | /* No CIS string. */ |
213 | if (cisstr == 0) |
214 | return (0); |
215 | |
216 | memset(myla, 0, ETHER_ADDR_LEN); |
217 | |
218 | for (i = 0, digit = 0; i < (ETHER_ADDR_LEN * 2); i++) { |
219 | if (cisstr[i] >= '0' && cisstr[i] <= '9') |
220 | digit |= cisstr[i] - '0'; |
221 | else if (cisstr[i] >= 'a' && cisstr[i] <= 'f') |
222 | digit |= (cisstr[i] - 'a') + 10; |
223 | else if (cisstr[i] >= 'A' && cisstr[i] <= 'F') |
224 | digit |= (cisstr[i] - 'A') + 10; |
225 | else { |
226 | /* Bogus digit!! */ |
227 | return (0); |
228 | } |
229 | |
230 | /* Compensate for ordering of digits. */ |
231 | if (i & 1) { |
232 | myla[i >> 1] = digit; |
233 | digit = 0; |
234 | } else |
235 | digit <<= 4; |
236 | } |
237 | |
238 | return (1); |
239 | } |
240 | |
241 | int |
242 | sm_pcmcia_enable(struct smc91cxx_softc *sc) |
243 | { |
244 | struct sm_pcmcia_softc *psc = (struct sm_pcmcia_softc *)sc; |
245 | int error; |
246 | |
247 | /* Establish the interrupt handler. */ |
248 | psc->sc_ih = pcmcia_intr_establish(psc->sc_pf, IPL_NET, smc91cxx_intr, |
249 | sc); |
250 | if (!psc->sc_ih) |
251 | return (EIO); |
252 | |
253 | error = pcmcia_function_enable(psc->sc_pf); |
254 | if (error) { |
255 | pcmcia_intr_disestablish(psc->sc_pf, psc->sc_ih); |
256 | psc->sc_ih = 0; |
257 | } |
258 | |
259 | return (error); |
260 | } |
261 | |
262 | void |
263 | sm_pcmcia_disable(struct smc91cxx_softc *sc) |
264 | { |
265 | struct sm_pcmcia_softc *psc = (struct sm_pcmcia_softc *)sc; |
266 | |
267 | pcmcia_function_disable(psc->sc_pf); |
268 | pcmcia_intr_disestablish(psc->sc_pf, psc->sc_ih); |
269 | psc->sc_ih = 0; |
270 | } |
271 | |