com_cardbus.c revision 1.26 1 /* $NetBSD: com_cardbus.c,v 1.26 2010/02/25 22:31:51 dyoung Exp $ */
2
3 /*
4 * Copyright (c) 2000 Johan Danielsson
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 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
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 *
18 * 3. Neither the name of author nor the names of any contributors may
19 * be used to endorse or promote products derived from this
20 * software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
26 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 /* A driver for CardBus based serial devices.
36
37 If the CardBus device only has one BAR (that is not also the CIS
38 BAR) listed in the CIS, it is assumed to be the one to use. For
39 devices with more than one BAR, the list of known devices has to be
40 updated below. */
41
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: com_cardbus.c,v 1.26 2010/02/25 22:31:51 dyoung Exp $");
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/tty.h>
48 #include <sys/device.h>
49
50 #include <dev/cardbus/cardbusvar.h>
51 #include <dev/pci/pcidevs.h>
52
53 #include <dev/pcmcia/pcmciareg.h>
54
55 #include <dev/ic/comreg.h>
56 #include <dev/ic/comvar.h>
57
58 struct com_cardbus_softc {
59 struct com_softc cc_com;
60 cardbus_intr_line_t cc_intrline;
61 void *cc_ih;
62 cardbus_devfunc_t cc_ct;
63 bus_addr_t cc_addr;
64 pcireg_t cc_base;
65 bus_size_t cc_size;
66 pcireg_t cc_csr;
67 int cc_cben;
68 pcitag_t cc_tag;
69 pcireg_t cc_reg;
70 int cc_type;
71 };
72
73 #define DEVICET(CSC) ((CSC)->cc_com.sc_dev)
74
75 static int com_cardbus_match (device_t, cfdata_t, void*);
76 static void com_cardbus_attach (device_t, device_t, void*);
77 static int com_cardbus_detach (device_t, int);
78
79 static void com_cardbus_setup(struct com_cardbus_softc*);
80 static int com_cardbus_enable (struct com_softc*);
81 static void com_cardbus_disable(struct com_softc*);
82
83 CFATTACH_DECL_NEW(com_cardbus, sizeof(struct com_cardbus_softc),
84 com_cardbus_match, com_cardbus_attach, com_cardbus_detach, NULL);
85
86 static struct csdev {
87 int vendor;
88 int product;
89 pcireg_t reg;
90 int type;
91 } csdevs[] = {
92 { PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_MODEM56,
93 CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
94 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MODEM56,
95 CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
96 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656_M,
97 CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
98 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656B_M,
99 CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
100 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656C_M,
101 CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
102 };
103
104 static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]);
105
106 static struct csdev*
107 find_csdev(struct cardbus_attach_args *ca)
108 {
109 struct csdev *cp;
110
111 for(cp = csdevs; cp < csdevs + ncsdevs; cp++)
112 if(cp->vendor == CARDBUS_VENDOR(ca->ca_id) &&
113 cp->product == CARDBUS_PRODUCT(ca->ca_id))
114 return cp;
115 return NULL;
116 }
117
118 static int
119 com_cardbus_match(device_t parent, cfdata_t match, void *aux)
120 {
121 struct cardbus_attach_args *ca = aux;
122
123 /* known devices are ok */
124 if(find_csdev(ca) != NULL)
125 return 10;
126
127 /* as are serial devices with a known UART */
128 if(ca->ca_cis.funcid == PCMCIA_FUNCTION_SERIAL &&
129 ca->ca_cis.funce.serial.uart_present != 0 &&
130 (ca->ca_cis.funce.serial.uart_type == 0 || /* 8250 */
131 ca->ca_cis.funce.serial.uart_type == 1 || /* 16450 */
132 ca->ca_cis.funce.serial.uart_type == 2)) /* 16550 */
133 return 1;
134
135 return 0;
136 }
137
138 static int
139 gofigure(struct cardbus_attach_args *ca, struct com_cardbus_softc *csc)
140 {
141 int i, index = -1;
142 pcireg_t cis_ptr;
143 struct csdev *cp;
144
145 /* If this device is listed above, use the known values, */
146 cp = find_csdev(ca);
147 if(cp != NULL) {
148 csc->cc_reg = cp->reg;
149 csc->cc_type = cp->type;
150 return 0;
151 }
152
153 cis_ptr = Cardbus_conf_read(csc->cc_ct, csc->cc_tag, CARDBUS_CIS_REG);
154
155 /* otherwise try to deduce which BAR and type to use from CIS. If
156 there is only one BAR, it must be the one we should use, if
157 there are more, we're out of luck. */
158 for(i = 0; i < 7; i++) {
159 /* ignore zero sized BARs */
160 if(ca->ca_cis.bar[i].size == 0)
161 continue;
162 /* ignore the CIS BAR */
163 if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
164 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
165 continue;
166 if(index != -1)
167 goto multi_bar;
168 index = i;
169 }
170 if(index == -1) {
171 aprint_error(": couldn't find any base address tuple\n");
172 return 1;
173 }
174 csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
175 if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
176 csc->cc_type = CARDBUS_MAPREG_TYPE_MEM;
177 else
178 csc->cc_type = CARDBUS_MAPREG_TYPE_IO;
179 return 0;
180
181 multi_bar:
182 aprint_error(": there are more than one possible base\n");
183
184 aprint_error_dev(DEVICET(csc), "address for this device, "
185 "please report the following information\n");
186 aprint_error_dev(DEVICET(csc), "vendor 0x%x product 0x%x\n",
187 CARDBUS_VENDOR(ca->ca_id), CARDBUS_PRODUCT(ca->ca_id));
188 for(i = 0; i < 7; i++) {
189 /* ignore zero sized BARs */
190 if(ca->ca_cis.bar[i].size == 0)
191 continue;
192 /* ignore the CIS BAR */
193 if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
194 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
195 continue;
196 aprint_error_dev(DEVICET(csc),
197 "base address %x type %s size %x\n",
198 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
199 (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
200 ca->ca_cis.bar[i].size);
201 }
202 return 1;
203 }
204
205 static void
206 com_cardbus_attach (device_t parent, device_t self, void *aux)
207 {
208 struct com_softc *sc = device_private(self);
209 struct com_cardbus_softc *csc = device_private(self);
210 struct cardbus_attach_args *ca = aux;
211 bus_space_handle_t ioh;
212 bus_space_tag_t iot;
213
214 sc->sc_dev = self;
215 csc->cc_intrline = ca->ca_intrline;
216 csc->cc_ct = ca->ca_ct;
217 csc->cc_tag = ca->ca_tag;
218
219 if(gofigure(ca, csc) != 0)
220 return;
221
222 if(Cardbus_mapreg_map(ca->ca_ct,
223 csc->cc_reg,
224 csc->cc_type,
225 0,
226 &iot,
227 &ioh,
228 &csc->cc_addr,
229 &csc->cc_size) != 0) {
230 aprint_error("failed to map memory");
231 return;
232 }
233
234 COM_INIT_REGS(sc->sc_regs, iot, ioh, csc->cc_addr);
235
236 csc->cc_base = csc->cc_addr;
237 csc->cc_csr = CARDBUS_COMMAND_MASTER_ENABLE;
238 if(csc->cc_type == CARDBUS_MAPREG_TYPE_IO) {
239 csc->cc_base |= CARDBUS_MAPREG_TYPE_IO;
240 csc->cc_csr |= CARDBUS_COMMAND_IO_ENABLE;
241 csc->cc_cben = CARDBUS_IO_ENABLE;
242 } else {
243 csc->cc_csr |= CARDBUS_COMMAND_MEM_ENABLE;
244 csc->cc_cben = CARDBUS_MEM_ENABLE;
245 }
246
247 sc->sc_frequency = COM_FREQ;
248
249 sc->enable = com_cardbus_enable;
250 sc->disable = com_cardbus_disable;
251 sc->enabled = 0;
252
253 if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) {
254 aprint_normal(": %s %s\n", ca->ca_cis.cis1_info[0],
255 ca->ca_cis.cis1_info[1]);
256 aprint_normal("%s", device_xname(DEVICET(csc)));
257 }
258
259 com_cardbus_setup(csc);
260
261 com_attach_subr(sc);
262
263 Cardbus_function_disable(csc->cc_ct);
264 }
265
266 static void
267 com_cardbus_setup(struct com_cardbus_softc *csc)
268 {
269 cardbus_devfunc_t ct = csc->cc_ct;
270 cardbus_chipset_tag_t cc = ct->ct_cc;
271 cardbus_function_tag_t cf = ct->ct_cf;
272 pcireg_t reg;
273
274 Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
275
276 /* enable accesses on cardbus bridge */
277 (*cf->cardbus_ctrl)(cc, csc->cc_cben);
278 (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
279
280 /* and the card itself */
281 reg = Cardbus_conf_read(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG);
282 reg &= ~(CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE);
283 reg |= csc->cc_csr;
284 Cardbus_conf_write(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG, reg);
285
286 /*
287 * Make sure the latency timer is set to some reasonable
288 * value.
289 */
290 reg = cardbus_conf_read(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG);
291 if (CARDBUS_LATTIMER(reg) < 0x20) {
292 reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT);
293 reg |= (0x20 << CARDBUS_LATTIMER_SHIFT);
294 cardbus_conf_write(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG, reg);
295 }
296 }
297
298 static int
299 com_cardbus_enable(struct com_softc *sc)
300 {
301 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
302 cardbus_devfunc_t ct = csc->cc_ct;
303 cardbus_chipset_tag_t cc = ct->ct_cc;
304 cardbus_function_tag_t cf = ct->ct_cf;
305
306 Cardbus_function_enable(ct);
307
308 com_cardbus_setup(csc);
309
310 /* establish the interrupt. */
311 csc->cc_ih = cardbus_intr_establish(cc, cf, csc->cc_intrline,
312 IPL_SERIAL, comintr, sc);
313 if (csc->cc_ih == NULL) {
314 aprint_error_dev(DEVICET(csc),
315 "couldn't establish interrupt\n");
316 return 1;
317 }
318
319 return 0;
320 }
321
322 static void
323 com_cardbus_disable(struct com_softc *sc)
324 {
325 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
326 cardbus_devfunc_t ct = csc->cc_ct;
327 cardbus_chipset_tag_t cc = ct->ct_cc;
328 cardbus_function_tag_t cf = ct->ct_cf;
329
330 cardbus_intr_disestablish(cc, cf, csc->cc_ih);
331 csc->cc_ih = NULL;
332
333 Cardbus_function_disable(ct);
334 }
335
336 static int
337 com_cardbus_detach(device_t self, int flags)
338 {
339 struct com_cardbus_softc *csc = device_private(self);
340 struct com_softc *sc = device_private(self);
341 cardbus_devfunc_t ct = csc->cc_ct;
342 int error;
343
344 if ((error = com_detach(self, flags)) != 0)
345 return error;
346
347 if (csc->cc_ih != NULL)
348 cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf, csc->cc_ih);
349
350 Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_regs.cr_iot,
351 sc->sc_regs.cr_ioh, csc->cc_size);
352
353 return 0;
354 }
355