1 /*	$NetBSD: pcmcia_cis_quirks.c,v 1.6 2000/04/12 21:07:55 scw Exp $ */
2 /*	$FreeBSD: src/sys/dev/pccard/pccard_cis_quirks.c,v 1.15 2005/03/26 21:30:49 sam Exp $ */
3 
4 #define	PCCARDDEBUG
5 
6 /*-
7  * Copyright (c) 1998 Marc Horowitz.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by Marc Horowitz.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/bus.h>
37 #include <sys/malloc.h>
38 #include <sys/systm.h>
39 
40 #include <bus/pccard/pccard_cis.h>
41 #include <bus/pccard/pccardvar.h>
42 #include <bus/pccard/pccarddevs.h>
43 
44 /* There are cards out there whose CIS flat-out lies.  This file
45    contains struct pccard_function chains for those devices. */
46 
47 /* these structures are just static templates which are then copied
48    into "live" allocated structures */
49 
50 struct pccard_function pccard_3cxem556_func0 = {
51 	0,			/* function number */
52 	PCCARD_FUNCTION_NETWORK,
53 	0x07,			/* last cfe number */
54 	0x800,			/* ccr_base */
55 	0x63,			/* ccr_mask */
56 };
57 
58 struct pccard_config_entry pccard_3cxem556_func0_cfe0 = {
59 	0x07,			/* cfe number */
60 	PCCARD_CFE_IO8 | PCCARD_CFE_IO16 | PCCARD_CFE_IRQLEVEL,
61 	PCCARD_IFTYPE_IO,
62 	1,			/* num_iospace */
63 	4,			/* iomask */
64 	{ { 0x0010, 0 } },	/* iospace */
65 	0xffff,			/* irqmask */
66 	0,			/* num_memspace */
67 	{ },			/* memspace */
68 	0,			/* maxtwins */
69 };
70 
71 static struct pccard_function pccard_3cxem556_func1 = {
72 	1,			/* function number */
73 	PCCARD_FUNCTION_SERIAL,
74 	0x27,			/* last cfe number */
75 	0x900,			/* ccr_base */
76 	0x63,			/* ccr_mask */
77 };
78 
79 static struct pccard_config_entry pccard_3cxem556_func1_cfe0 = {
80 	0x27,			/* cfe number */
81 	PCCARD_CFE_IO8 | PCCARD_CFE_IRQLEVEL,
82 	PCCARD_IFTYPE_IO,
83 	1,			/* num_iospace */
84 	3,			/* iomask */
85 	{ { 0x0008, 0 } },	/* iospace */
86 	0xffff,			/* irqmask */
87 	0,			/* num_memspace */
88 	{ },			/* memspace */
89 	0,			/* maxtwins */
90 };
91 
92 static struct pccard_function pccard_3ccfem556bi_func0 = {
93 	0,			/* function number */
94 	PCCARD_FUNCTION_NETWORK,
95 	0x07,			/* last cfe number */
96 	0x1000,			/* ccr_base */
97 	0x267,			/* ccr_mask */
98 };
99 
100 static struct pccard_config_entry pccard_3ccfem556bi_func0_cfe0 = {
101 	0x07,			/* cfe number */
102 	PCCARD_CFE_IO8 | PCCARD_CFE_IO16 | PCCARD_CFE_IRQLEVEL,
103 	PCCARD_IFTYPE_IO,
104 	1,			/* num_iospace */
105 	5,			/* iomask */
106 	{ { 0x0020, 0 } },	/* iospace */
107 	0xffff,			/* irqmask */
108 	0,			/* num_memspace */
109 	{ },			/* memspace */
110 	0,			/* maxtwins */
111 };
112 
113 static struct pccard_function pccard_3ccfem556bi_func1 = {
114 	1,			/* function number */
115 	PCCARD_FUNCTION_SERIAL,
116 	0x27,			/* last cfe number */
117 	0x1100,			/* ccr_base */
118 	0x277,			/* ccr_mask */
119 };
120 
121 static struct pccard_config_entry pccard_3ccfem556bi_func1_cfe0 = {
122 	0x27,			/* cfe number */
123 	PCCARD_CFE_IO8 | PCCARD_CFE_IRQLEVEL,
124 	PCCARD_IFTYPE_IO,
125 	1,			/* num_iospace */
126 	3,			/* iomask */
127 	{ { 0x0008, 0 } },	/* iospace */
128 	0xffff,			/* irqmask */
129 	0,			/* num_memspace */
130 	{ },			/* memspace */
131 	0,			/* maxtwins */
132 };
133 
134 static struct pccard_function pccard_sveclancard_func0 = {
135 	0,			/* function number */
136 	PCCARD_FUNCTION_NETWORK,
137 	0x1,			/* last cfe number */
138 	0x100,			/* ccr_base */
139 	0x1,			/* ccr_mask */
140 };
141 
142 static struct pccard_config_entry pccard_sveclancard_func0_cfe0 = {
143 	0x1,			/* cfe number */
144 	PCCARD_CFE_MWAIT_REQUIRED | PCCARD_CFE_RDYBSY_ACTIVE |
145 	PCCARD_CFE_WP_ACTIVE | PCCARD_CFE_BVD_ACTIVE | PCCARD_CFE_IO16,
146 	PCCARD_IFTYPE_IO,
147 	1,			/* num_iospace */
148 	5,			/* iomask */
149 	{ { 0x20, 0x300 } },	/* iospace */
150 	0xdeb8,			/* irqmask */
151 	0,			/* num_memspace */
152 	{ },			/* memspace */
153 	0,			/* maxtwins */
154 };
155 
156 static struct pccard_function pccard_ndc_nd5100_func0 = {
157 	0,			/* function number */
158 	PCCARD_FUNCTION_NETWORK,
159 	0x23,			/* last cfe number */
160 	0x3f8,			/* ccr_base */
161 	0x3,			/* ccr_mask */
162 };
163 
164 static struct pccard_config_entry pccard_ndc_nd5100_func0_cfe0 = {
165 	0x20,			/* cfe number */
166 	PCCARD_CFE_MWAIT_REQUIRED | PCCARD_CFE_IO16 | PCCARD_CFE_IRQLEVEL,
167 	PCCARD_IFTYPE_IO,
168 	1,			/* num_iospace */
169 	5,			/* iomask */
170 	{ { 0x20, 0x300 } },	/* iospace */
171 	0xdeb8,			/* irqmask */
172 	0,			/* num_memspace */
173 	{ },			/* memspace */
174 	0,			/* maxtwins */
175 };
176 
177 static struct pccard_cis_quirk pccard_cis_quirks[] = {
178 	{ PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CXEM556, PCMCIA_CIS_INVALID,
179 	  &pccard_3cxem556_func0, &pccard_3cxem556_func0_cfe0 },
180 	{ PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CXEM556, PCMCIA_CIS_INVALID,
181 	  &pccard_3cxem556_func1, &pccard_3cxem556_func1_cfe0 },
182 	{ PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CXEM556INT, PCMCIA_CIS_INVALID,
183 	  &pccard_3cxem556_func0, &pccard_3cxem556_func0_cfe0 },
184 	{ PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CXEM556INT, PCMCIA_CIS_INVALID,
185 	  &pccard_3cxem556_func1, &pccard_3cxem556_func1_cfe0 },
186 	{ PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CCFEM556BI,
187 	  PCMCIA_CIS_INVALID,
188 	  &pccard_3ccfem556bi_func0, &pccard_3ccfem556bi_func0_cfe0 },
189 	{ PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CCFEM556BI,
190 	  PCMCIA_CIS_INVALID,
191 	  &pccard_3ccfem556bi_func1, &pccard_3ccfem556bi_func1_cfe0 },
192 	{ PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID, PCMCIA_CIS_SVEC_LANCARD,
193 	  &pccard_sveclancard_func0, &pccard_sveclancard_func0_cfe0 },
194 	{ PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID, PCMCIA_CIS_NDC_ND5100_E,
195 	  &pccard_ndc_nd5100_func0, &pccard_ndc_nd5100_func0_cfe0 },
196 };
197 
198 static int n_pccard_cis_quirks = NELEM(pccard_cis_quirks);
199 
200 static int
201 pccard_cis_quirk_match(struct pccard_softc *sc, struct pccard_cis_quirk *q)
202 {
203 	if ((sc->card.manufacturer == q->manufacturer) &&
204 		(sc->card.product == q->product) &&
205 		(((sc->card.manufacturer != PCMCIA_VENDOR_INVALID) &&
206 		  (sc->card.product != PCMCIA_PRODUCT_INVALID)) ||
207 		 ((sc->card.manufacturer == PCMCIA_VENDOR_INVALID) &&
208 		  (sc->card.product == PCMCIA_PRODUCT_INVALID) &&
209 		  sc->card.cis1_info[0] &&
210 		  (strcmp(sc->card.cis1_info[0], q->cis1_info[0]) == 0) &&
211 		  sc->card.cis1_info[1] &&
212 		  (strcmp(sc->card.cis1_info[1], q->cis1_info[1]) == 0))))
213 		return (1);
214 	return (0);
215 }
216 
217 void
218 pccard_check_cis_quirks(device_t dev)
219 {
220 	struct pccard_softc *sc = PCCARD_SOFTC(dev);
221 	int wiped = 0;
222 	int i, j;
223 	struct pccard_function *pf, *pf_next, *pf_last;
224 	struct pccard_config_entry *cfe, *cfe_next;
225 	struct pccard_cis_quirk *q;
226 
227 	pf = NULL;
228 	pf_last = NULL;
229 
230 	for (i=0; i<n_pccard_cis_quirks; i++) {
231 		q = &pccard_cis_quirks[i];
232 		if (!pccard_cis_quirk_match(sc, q))
233 			continue;
234 		if (!wiped) {
235 			if (bootverbose) {
236 				device_printf(dev, "using CIS quirks for ");
237 				for (j = 0; j < 4; j++) {
238 					if (sc->card.cis1_info[j] == NULL)
239 						break;
240 					if (j)
241 						kprintf(", ");
242 					kprintf("%s", sc->card.cis1_info[j]);
243 				}
244 				kprintf("\n");
245 			}
246 
247 			for (pf = STAILQ_FIRST(&sc->card.pf_head); pf != NULL;
248 			     pf = pf_next) {
249 				for (cfe = STAILQ_FIRST(&pf->cfe_head); cfe != NULL;
250 				     cfe = cfe_next) {
251 					cfe_next = STAILQ_NEXT(cfe, cfe_list);
252 					kfree(cfe, M_DEVBUF);
253 				}
254 				pf_next = STAILQ_NEXT(pf, pf_list);
255 				kfree(pf, M_DEVBUF);
256 			}
257 
258 			STAILQ_INIT(&sc->card.pf_head);
259 			wiped = 1;
260 		}
261 
262 		if (pf_last == q->pf) {
263 			cfe = kmalloc(sizeof(*cfe), M_DEVBUF, M_NOWAIT);
264 			if (cfe == NULL) {
265 				device_printf(dev, "no memory for quirk (1)\n");
266 				continue;
267 			}
268 			*cfe = *q->cfe;
269 			STAILQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list);
270 		} else {
271 			pf = kmalloc(sizeof(*pf), M_DEVBUF, M_NOWAIT);
272 			if (pf == NULL) {
273 				device_printf(dev,
274 					"no memory for pccard function\n");
275 				continue;
276 			}
277 			*pf = *q->pf;
278 			STAILQ_INIT(&pf->cfe_head);
279 			cfe = kmalloc(sizeof(*cfe), M_DEVBUF, M_NOWAIT);
280 			if (cfe == NULL) {
281 				kfree(pf, M_DEVBUF);
282 				device_printf(dev, "no memory for quirk (2)\n");
283 				continue;
284 			}
285 			*cfe = *q->cfe;
286 			STAILQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list);
287 			STAILQ_INSERT_TAIL(&sc->card.pf_head, pf, pf_list);
288 			pf_last = q->pf;
289 		}
290 	}
291 }
292