1 | /* $NetBSD: pcmcia_cis_quirks.c,v 1.35 2013/09/14 13:13:33 joerg Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1998 Marc Horowitz. All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * 3. All advertising materials mentioning features or use of this software |
15 | * must display the following acknowledgement: |
16 | * This product includes software developed by Marc Horowitz. |
17 | * 4. The name of the author may not be used to endorse or promote products |
18 | * derived from this software without specific prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
22 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
23 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
24 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
29 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: pcmcia_cis_quirks.c,v 1.35 2013/09/14 13:13:33 joerg Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/systm.h> |
37 | #include <sys/device.h> |
38 | #include <sys/kernel.h> |
39 | #include <sys/mbuf.h> |
40 | |
41 | #include <dev/pcmcia/pcmciadevs.h> |
42 | #include <dev/pcmcia/pcmciareg.h> |
43 | #include <dev/pcmcia/pcmciachip.h> |
44 | #include <dev/pcmcia/pcmciavar.h> |
45 | |
46 | /* There are cards out there whose CIS flat-out lies. This file |
47 | contains struct pcmcia_function chains for those devices. */ |
48 | |
49 | /* these structures are just static templates which are then copied |
50 | into "live" allocated structures */ |
51 | |
52 | static const struct pcmcia_function pcmcia_3cxem556_func0 = { |
53 | .number = 0, /* function number */ |
54 | .function = PCMCIA_FUNCTION_NETWORK, |
55 | .last_config_index = 0x07, /* last cfe number */ |
56 | .ccr_base = 0x800, /* ccr_base */ |
57 | .ccr_mask = 0x63, /* ccr_mask */ |
58 | }; |
59 | |
60 | static const struct pcmcia_config_entry pcmcia_3cxem556_func0_cfe0 = { |
61 | .number = 0x07, /* cfe number */ |
62 | .flags = PCMCIA_CFE_IO8 | PCMCIA_CFE_IO16 | PCMCIA_CFE_IRQLEVEL, |
63 | .iftype = PCMCIA_IFTYPE_IO, |
64 | .num_iospace = 1, /* num_iospace */ |
65 | .iomask = 4, /* iomask */ |
66 | .iospace = { { .length = 0x0010, .start = 0 } }, /* iospace */ |
67 | .irqmask = 0xffff, /* irqmask */ |
68 | }; |
69 | |
70 | static const struct pcmcia_function pcmcia_3cxem556_func1 = { |
71 | .number = 1, /* function number */ |
72 | .function = PCMCIA_FUNCTION_SERIAL, |
73 | .last_config_index = 0x27, /* last cfe number */ |
74 | .ccr_base = 0x900, /* ccr_base */ |
75 | .ccr_mask = 0x63, /* ccr_mask */ |
76 | }; |
77 | |
78 | static const struct pcmcia_config_entry pcmcia_3cxem556_func1_cfe0 = { |
79 | .number = 0x27, /* cfe number */ |
80 | .flags = PCMCIA_CFE_IO8 | PCMCIA_CFE_IRQLEVEL, |
81 | .iftype = PCMCIA_IFTYPE_IO, |
82 | .num_iospace = 1, /* num_iospace */ |
83 | .iomask = 3, /* iomask */ |
84 | .iospace = { { .length = 0x0008, .start = 0 } }, /* iospace */ |
85 | .irqmask = 0xffff, /* irqmask */ |
86 | }; |
87 | |
88 | static const struct pcmcia_function pcmcia_3ccfem556bi_func0 = { |
89 | .number = 0, /* function number */ |
90 | .function = PCMCIA_FUNCTION_NETWORK, |
91 | .last_config_index = 0x07, /* last cfe number */ |
92 | .ccr_base = 0x1000, /* ccr_base */ |
93 | .ccr_mask = 0x267, /* ccr_mask */ |
94 | }; |
95 | |
96 | static const struct pcmcia_config_entry pcmcia_3ccfem556bi_func0_cfe0 = { |
97 | .number = 0x07, /* cfe number */ |
98 | .flags = PCMCIA_CFE_IO8 | PCMCIA_CFE_IO16 | PCMCIA_CFE_IRQLEVEL, |
99 | .iftype = PCMCIA_IFTYPE_IO, |
100 | .num_iospace = 1, /* num_iospace */ |
101 | .iomask = 5, /* iomask */ |
102 | .iospace = { { .length = 0x0020, .start = 0 } }, /* iospace */ |
103 | }; |
104 | |
105 | static const struct pcmcia_function pcmcia_3ccfem556bi_func1 = { |
106 | .number = 1, /* function number */ |
107 | .function = PCMCIA_FUNCTION_SERIAL, |
108 | .last_config_index = 0x27, /* last cfe number */ |
109 | .ccr_base = 0x1100, /* ccr_base */ |
110 | .ccr_mask = 0x277, /* ccr_mask */ |
111 | }; |
112 | |
113 | static const struct pcmcia_config_entry pcmcia_3ccfem556bi_func1_cfe0 = { |
114 | .number = 0x27, /* cfe number */ |
115 | .flags = PCMCIA_CFE_IO8 | PCMCIA_CFE_IRQLEVEL, |
116 | .iftype = PCMCIA_IFTYPE_IO, |
117 | .num_iospace = 1, /* num_iospace */ |
118 | .iomask = 3, /* iomask */ |
119 | .iospace = { { .length = 0x0008, .start = 0 } }, /* iospace */ |
120 | .irqmask = 0xffff, /* irqmask */ |
121 | }; |
122 | |
123 | static const struct pcmcia_function pcmcia_sveclancard_func0 = { |
124 | .number = 0, /* function number */ |
125 | .function = PCMCIA_FUNCTION_NETWORK, |
126 | .last_config_index = 0x1, /* last cfe number */ |
127 | .ccr_base = 0x100, /* ccr_base */ |
128 | .ccr_mask = 0x1, /* ccr_mask */ |
129 | }; |
130 | |
131 | static const struct pcmcia_config_entry pcmcia_sveclancard_func0_cfe0 = { |
132 | .number = 0x1, /* cfe number */ |
133 | .flags = PCMCIA_CFE_MWAIT_REQUIRED | PCMCIA_CFE_RDYBSY_ACTIVE | |
134 | PCMCIA_CFE_WP_ACTIVE | PCMCIA_CFE_BVD_ACTIVE | PCMCIA_CFE_IO16, |
135 | .iftype = PCMCIA_IFTYPE_IO, |
136 | .num_iospace = 1, /* num_iospace */ |
137 | .iomask = 5, /* iomask */ |
138 | .iospace = { { .length = 0x20, .start = 0x300 } }, /* iospace */ |
139 | .irqmask = 0xdeb8, /* irqmask */ |
140 | }; |
141 | |
142 | static const struct pcmcia_function pcmcia_ndc_nd5100_func0 = { |
143 | .number = 0, /* function number */ |
144 | .function = PCMCIA_FUNCTION_NETWORK, |
145 | .last_config_index = 0x23, /* last cfe number */ |
146 | .ccr_base = 0x3f8, /* ccr_base */ |
147 | .ccr_mask = 0x3, /* ccr_mask */ |
148 | }; |
149 | |
150 | static const struct pcmcia_config_entry pcmcia_ndc_nd5100_func0_cfe0 = { |
151 | .number = 0x20, /* cfe number */ |
152 | .flags = PCMCIA_CFE_MWAIT_REQUIRED | PCMCIA_CFE_IO16 | |
153 | PCMCIA_CFE_IRQLEVEL, |
154 | .iftype = PCMCIA_IFTYPE_IO, |
155 | .num_iospace = 1, /* num_iospace */ |
156 | .iomask = 5, /* iomask */ |
157 | .iospace = { { .length = 0x20, .start = 0x300 } }, /* iospace */ |
158 | .irqmask = 0xdeb8, /* irqmask */ |
159 | }; |
160 | |
161 | static const struct pcmcia_function pcmcia_emtac_a2424i_func0 = { |
162 | .number = 0, /* function number */ |
163 | .function = PCMCIA_FUNCTION_NETWORK, |
164 | .last_config_index = 0x21, /* last cfe number */ |
165 | .ccr_base = 0x3e0, /* ccr_base */ |
166 | .ccr_mask = 0x1, /* ccr_mask */ |
167 | }; |
168 | |
169 | static const struct pcmcia_config_entry pcmcia_emtac_a2424i_func0_cfe0 = { |
170 | .number = 0x21, /* cfe number */ |
171 | .flags = PCMCIA_CFE_IO16 | PCMCIA_CFE_IRQLEVEL | PCMCIA_CFE_IRQPULSE, |
172 | .iftype = PCMCIA_IFTYPE_IO, |
173 | .num_iospace = 1, /* num_iospace */ |
174 | .iomask = 6, /* iomask */ |
175 | .iospace = { { .length = 0x40, .start = 0x100 } }, /* iospace */ |
176 | .irqmask = 0xffff, /* irqmask */ |
177 | }; |
178 | |
179 | static const struct pcmcia_function pcmcia_fujitsu_j181_func0 = { |
180 | .number = 0, /* function number */ |
181 | .function = PCMCIA_FUNCTION_NETWORK, |
182 | .last_config_index = 0x21, /* last cfe number */ |
183 | .ccr_base = 0xfe0, /* ccr_base */ |
184 | .ccr_mask = 0xf, /* ccr_mask */ |
185 | }; |
186 | |
187 | static const struct pcmcia_config_entry pcmcia_fujitsu_j181_func0_cfe0 = { |
188 | .number = 0xc, /* cfe number */ |
189 | .flags = PCMCIA_CFE_MWAIT_REQUIRED | PCMCIA_CFE_WP_ACTIVE | |
190 | PCMCIA_CFE_IO8 | PCMCIA_CFE_IO16 | PCMCIA_CFE_IRQLEVEL | |
191 | PCMCIA_CFE_IRQPULSE, |
192 | .iftype = PCMCIA_IFTYPE_IO, |
193 | .num_iospace = 1, /* num_iospace */ |
194 | .iomask = 10, /* iomask */ |
195 | .iospace = { { .length = 0x20, .start = 0x140 } }, /* iospace */ |
196 | .irqmask = 0xffff, /* irqmask */ |
197 | }; |
198 | |
199 | static const struct pcmcia_function pcmcia_necinfrontia_ax420n_func0 = { |
200 | .number = 0, /* function number */ |
201 | .function = PCMCIA_FUNCTION_SERIAL, |
202 | .last_config_index = 0x38, /* last cfe number */ |
203 | .ccr_base = 0x200, /* ccr_base */ |
204 | .ccr_mask = 0x1f, /* ccr_mask */ |
205 | }; |
206 | |
207 | static const struct pcmcia_config_entry pcmcia_necinfrontia_ax420n_func0_cfe0 = { |
208 | .number = 0x25, /* cfe number */ |
209 | .flags = PCMCIA_CFE_RDYBSY_ACTIVE | PCMCIA_CFE_IO8 | |
210 | PCMCIA_CFE_IRQLEVEL | PCMCIA_CFE_POWERDOWN | |
211 | PCMCIA_CFE_AUDIO, |
212 | .iftype = PCMCIA_IFTYPE_IO, |
213 | .num_iospace = 1, /* num_iospace */ |
214 | .iomask = 10, /* iomask */ |
215 | .iospace = { { .length = 0x8, .start = 0x3f8 } }, /* iospace */ |
216 | .irqmask = 0x86bc, /* irqmask */ |
217 | }; |
218 | |
219 | static const struct pcmcia_cis_quirk pcmcia_cis_quirks[] = { |
220 | { PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CXEM556, |
221 | PCMCIA_CIS_INVALID, |
222 | &pcmcia_3cxem556_func0, &pcmcia_3cxem556_func0_cfe0 }, |
223 | { PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CXEM556, |
224 | PCMCIA_CIS_INVALID, |
225 | &pcmcia_3cxem556_func1, &pcmcia_3cxem556_func1_cfe0 }, |
226 | { PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CXEM556INT, |
227 | PCMCIA_CIS_INVALID, |
228 | &pcmcia_3cxem556_func0, &pcmcia_3cxem556_func0_cfe0 }, |
229 | { PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CXEM556INT, |
230 | PCMCIA_CIS_INVALID, |
231 | &pcmcia_3cxem556_func1, &pcmcia_3cxem556_func1_cfe0 }, |
232 | { PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CCFEM556BI, |
233 | PCMCIA_CIS_INVALID, |
234 | &pcmcia_3ccfem556bi_func0, &pcmcia_3ccfem556bi_func0_cfe0 }, |
235 | { PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CCFEM556BI, |
236 | PCMCIA_CIS_INVALID, |
237 | &pcmcia_3ccfem556bi_func1, &pcmcia_3ccfem556bi_func1_cfe0 }, |
238 | { PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID, |
239 | PCMCIA_CIS_SVEC_LANCARD, |
240 | &pcmcia_sveclancard_func0, &pcmcia_sveclancard_func0_cfe0 }, |
241 | { PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID, |
242 | PCMCIA_CIS_NDC_ND5100_E, |
243 | &pcmcia_ndc_nd5100_func0, &pcmcia_ndc_nd5100_func0_cfe0 }, |
244 | { PCMCIA_VENDOR_EMTAC, PCMCIA_PRODUCT_EMTAC_WLAN, |
245 | PCMCIA_CIS_INVALID, |
246 | &pcmcia_emtac_a2424i_func0, &pcmcia_emtac_a2424i_func0_cfe0 }, |
247 | { PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID, |
248 | PCMCIA_CIS_FUJITSU_FMV_J181, |
249 | &pcmcia_fujitsu_j181_func0, &pcmcia_fujitsu_j181_func0_cfe0 }, |
250 | { PCMCIA_VENDOR_NECINFRONTIA, PCMCIA_PRODUCT_NECINFRONTIA_AX420N, |
251 | PCMCIA_CIS_INVALID, |
252 | &pcmcia_necinfrontia_ax420n_func0, |
253 | &pcmcia_necinfrontia_ax420n_func0_cfe0 }, |
254 | }; |
255 | |
256 | static const int pcmcia_cis_nquirks = |
257 | sizeof(pcmcia_cis_quirks) / sizeof(pcmcia_cis_quirks[0]); |
258 | |
259 | void |
260 | pcmcia_check_cis_quirks(struct pcmcia_softc *sc) |
261 | { |
262 | int wiped = 0; |
263 | size_t i, j; |
264 | struct pcmcia_function *pf; |
265 | const struct pcmcia_function *pf_last; |
266 | struct pcmcia_config_entry *cfe; |
267 | struct pcmcia_card *card = &sc->card; |
268 | const struct pcmcia_cis_quirk *quirk; |
269 | |
270 | pf = NULL; |
271 | pf_last = NULL; |
272 | |
273 | for (i = 0; i < pcmcia_cis_nquirks; i++) { |
274 | quirk = &pcmcia_cis_quirks[i]; |
275 | |
276 | if (card->manufacturer == quirk->manufacturer && |
277 | card->manufacturer != PCMCIA_VENDOR_INVALID && |
278 | card->product == quirk->product && |
279 | card->product != PCMCIA_PRODUCT_INVALID) |
280 | goto match; |
281 | |
282 | for (j = 0; j < 2; j++) |
283 | if (card->cis1_info[j] == NULL || |
284 | quirk->cis1_info[j] == NULL || |
285 | strcmp(card->cis1_info[j], |
286 | quirk->cis1_info[j]) != 0) |
287 | goto nomatch; |
288 | |
289 | match: |
290 | if (!wiped) { |
291 | if (pcmcia_verbose) { |
292 | printf("%s: using CIS quirks for " , |
293 | device_xname(sc->dev)); |
294 | for (j = 0; j < 4; j++) { |
295 | if (card->cis1_info[j] == NULL) |
296 | break; |
297 | if (j) |
298 | printf(", " ); |
299 | printf("%s" , card->cis1_info[j]); |
300 | } |
301 | printf("\n" ); |
302 | } |
303 | pcmcia_free_pf(&card->pf_head); |
304 | wiped = 1; |
305 | } |
306 | |
307 | if (pf_last != quirk->pf) { |
308 | /* |
309 | * XXX: a driver which still calls pcmcia_card_attach |
310 | * very early attach stage should be fixed instead. |
311 | */ |
312 | pf = malloc(sizeof(*pf), M_DEVBUF, |
313 | cold ? M_NOWAIT : M_WAITOK); |
314 | if (pf == NULL) |
315 | panic("pcmcia_check_cis_quirks: malloc pf" ); |
316 | *pf = *quirk->pf; |
317 | SIMPLEQ_INIT(&pf->cfe_head); |
318 | SIMPLEQ_INSERT_TAIL(&card->pf_head, pf, pf_list); |
319 | pf_last = quirk->pf; |
320 | } |
321 | |
322 | /* |
323 | * XXX: see above. |
324 | */ |
325 | cfe = malloc(sizeof(*cfe), M_DEVBUF, |
326 | cold ? M_NOWAIT : M_WAITOK); |
327 | if (cfe == NULL) |
328 | panic("pcmcia_check_cis_quirks: malloc cfe" ); |
329 | *cfe = *quirk->cfe; |
330 | KASSERT(pf != NULL); |
331 | SIMPLEQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list); |
332 | |
333 | nomatch:; |
334 | } |
335 | } |
336 | |