com_cardbus.c revision 1.28 1 /* $NetBSD: com_cardbus.c,v 1.28 2010/02/26 00:57:01 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.28 2010/02/26 00:57:01 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 pcitag_t cc_tag;
68 pcireg_t cc_reg;
69 int cc_type;
70 };
71
72 #define DEVICET(CSC) ((CSC)->cc_com.sc_dev)
73
74 static int com_cardbus_match (device_t, cfdata_t, void*);
75 static void com_cardbus_attach (device_t, device_t, void*);
76 static int com_cardbus_detach (device_t, int);
77
78 static void com_cardbus_setup(struct com_cardbus_softc*);
79 static int com_cardbus_enable (struct com_softc*);
80 static void com_cardbus_disable(struct com_softc*);
81
82 CFATTACH_DECL_NEW(com_cardbus, sizeof(struct com_cardbus_softc),
83 com_cardbus_match, com_cardbus_attach, com_cardbus_detach, NULL);
84
85 static struct csdev {
86 int vendor;
87 int product;
88 pcireg_t reg;
89 int type;
90 } csdevs[] = {
91 { PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_MODEM56,
92 PCI_BAR0, PCI_MAPREG_TYPE_IO },
93 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MODEM56,
94 PCI_BAR0, PCI_MAPREG_TYPE_IO },
95 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656_M,
96 PCI_BAR0, PCI_MAPREG_TYPE_IO },
97 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656B_M,
98 PCI_BAR0, PCI_MAPREG_TYPE_IO },
99 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656C_M,
100 PCI_BAR0, PCI_MAPREG_TYPE_IO },
101 };
102
103 static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]);
104
105 static struct csdev*
106 find_csdev(struct cardbus_attach_args *ca)
107 {
108 struct csdev *cp;
109
110 for(cp = csdevs; cp < csdevs + ncsdevs; cp++)
111 if(cp->vendor == PCI_VENDOR(ca->ca_id) &&
112 cp->product == PCI_PRODUCT(ca->ca_id))
113 return cp;
114 return NULL;
115 }
116
117 static int
118 com_cardbus_match(device_t parent, cfdata_t match, void *aux)
119 {
120 struct cardbus_attach_args *ca = aux;
121
122 /* known devices are ok */
123 if(find_csdev(ca) != NULL)
124 return 10;
125
126 /* as are serial devices with a known UART */
127 if(ca->ca_cis.funcid == PCMCIA_FUNCTION_SERIAL &&
128 ca->ca_cis.funce.serial.uart_present != 0 &&
129 (ca->ca_cis.funce.serial.uart_type == 0 || /* 8250 */
130 ca->ca_cis.funce.serial.uart_type == 1 || /* 16450 */
131 ca->ca_cis.funce.serial.uart_type == 2)) /* 16550 */
132 return 1;
133
134 return 0;
135 }
136
137 static int
138 gofigure(struct cardbus_attach_args *ca, struct com_cardbus_softc *csc)
139 {
140 int i, index = -1;
141 pcireg_t cis_ptr;
142 struct csdev *cp;
143
144 /* If this device is listed above, use the known values, */
145 cp = find_csdev(ca);
146 if(cp != NULL) {
147 csc->cc_reg = cp->reg;
148 csc->cc_type = cp->type;
149 return 0;
150 }
151
152 cis_ptr = Cardbus_conf_read(csc->cc_ct, csc->cc_tag, CARDBUS_CIS_REG);
153
154 /* otherwise try to deduce which BAR and type to use from CIS. If
155 there is only one BAR, it must be the one we should use, if
156 there are more, we're out of luck. */
157 for(i = 0; i < 7; i++) {
158 /* ignore zero sized BARs */
159 if(ca->ca_cis.bar[i].size == 0)
160 continue;
161 /* ignore the CIS BAR */
162 if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
163 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
164 continue;
165 if(index != -1)
166 goto multi_bar;
167 index = i;
168 }
169 if(index == -1) {
170 aprint_error(": couldn't find any base address tuple\n");
171 return 1;
172 }
173 csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
174 if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
175 csc->cc_type = PCI_MAPREG_TYPE_MEM;
176 else
177 csc->cc_type = PCI_MAPREG_TYPE_IO;
178 return 0;
179
180 multi_bar:
181 aprint_error(": there are more than one possible base\n");
182
183 aprint_error_dev(DEVICET(csc), "address for this device, "
184 "please report the following information\n");
185 aprint_error_dev(DEVICET(csc), "vendor 0x%x product 0x%x\n",
186 PCI_VENDOR(ca->ca_id), PCI_PRODUCT(ca->ca_id));
187 for(i = 0; i < 7; i++) {
188 /* ignore zero sized BARs */
189 if(ca->ca_cis.bar[i].size == 0)
190 continue;
191 /* ignore the CIS BAR */
192 if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
193 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
194 continue;
195 aprint_error_dev(DEVICET(csc),
196 "base address %x type %s size %x\n",
197 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
198 (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
199 ca->ca_cis.bar[i].size);
200 }
201 return 1;
202 }
203
204 static void
205 com_cardbus_attach (device_t parent, device_t self, void *aux)
206 {
207 struct com_softc *sc = device_private(self);
208 struct com_cardbus_softc *csc = device_private(self);
209 struct cardbus_attach_args *ca = aux;
210 bus_space_handle_t ioh;
211 bus_space_tag_t iot;
212
213 sc->sc_dev = self;
214 csc->cc_intrline = ca->ca_intrline;
215 csc->cc_ct = ca->ca_ct;
216 csc->cc_tag = ca->ca_tag;
217
218 if(gofigure(ca, csc) != 0)
219 return;
220
221 if(Cardbus_mapreg_map(ca->ca_ct,
222 csc->cc_reg,
223 csc->cc_type,
224 0,
225 &iot,
226 &ioh,
227 &csc->cc_addr,
228 &csc->cc_size) != 0) {
229 aprint_error("failed to map memory");
230 return;
231 }
232
233 COM_INIT_REGS(sc->sc_regs, iot, ioh, csc->cc_addr);
234
235 csc->cc_base = csc->cc_addr;
236 csc->cc_csr = PCI_COMMAND_MASTER_ENABLE;
237 if(csc->cc_type == PCI_MAPREG_TYPE_IO) {
238 csc->cc_base |= PCI_MAPREG_TYPE_IO;
239 csc->cc_csr |= PCI_COMMAND_IO_ENABLE;
240 } else {
241 csc->cc_csr |= PCI_COMMAND_MEM_ENABLE;
242 }
243
244 sc->sc_frequency = COM_FREQ;
245
246 sc->enable = com_cardbus_enable;
247 sc->disable = com_cardbus_disable;
248 sc->enabled = 0;
249
250 if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) {
251 aprint_normal(": %s %s\n", ca->ca_cis.cis1_info[0],
252 ca->ca_cis.cis1_info[1]);
253 aprint_normal("%s", device_xname(DEVICET(csc)));
254 }
255
256 com_cardbus_setup(csc);
257
258 com_attach_subr(sc);
259
260 Cardbus_function_disable(csc->cc_ct);
261 }
262
263 static void
264 com_cardbus_setup(struct com_cardbus_softc *csc)
265 {
266 cardbus_devfunc_t ct = csc->cc_ct;
267 pcireg_t reg;
268
269 Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
270
271 /* and the card itself */
272 reg = Cardbus_conf_read(ct, csc->cc_tag, PCI_COMMAND_STATUS_REG);
273 reg &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
274 reg |= csc->cc_csr;
275 Cardbus_conf_write(ct, csc->cc_tag, PCI_COMMAND_STATUS_REG, reg);
276
277 /*
278 * Make sure the latency timer is set to some reasonable
279 * value.
280 */
281 reg = Cardbus_conf_read(ct, csc->cc_tag, PCI_BHLC_REG);
282 if (PCI_LATTIMER(reg) < 0x20) {
283 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
284 reg |= (0x20 << PCI_LATTIMER_SHIFT);
285 Cardbus_conf_write(ct, csc->cc_tag, PCI_BHLC_REG, reg);
286 }
287 }
288
289 static int
290 com_cardbus_enable(struct com_softc *sc)
291 {
292 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
293 cardbus_devfunc_t ct = csc->cc_ct;
294 cardbus_chipset_tag_t cc = ct->ct_cc;
295 cardbus_function_tag_t cf = ct->ct_cf;
296
297 Cardbus_function_enable(ct);
298
299 com_cardbus_setup(csc);
300
301 /* establish the interrupt. */
302 csc->cc_ih = cardbus_intr_establish(cc, cf, csc->cc_intrline,
303 IPL_SERIAL, comintr, sc);
304 if (csc->cc_ih == NULL) {
305 aprint_error_dev(DEVICET(csc),
306 "couldn't establish interrupt\n");
307 return 1;
308 }
309
310 return 0;
311 }
312
313 static void
314 com_cardbus_disable(struct com_softc *sc)
315 {
316 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
317 cardbus_devfunc_t ct = csc->cc_ct;
318 cardbus_chipset_tag_t cc = ct->ct_cc;
319 cardbus_function_tag_t cf = ct->ct_cf;
320
321 cardbus_intr_disestablish(cc, cf, csc->cc_ih);
322 csc->cc_ih = NULL;
323
324 Cardbus_function_disable(ct);
325 }
326
327 static int
328 com_cardbus_detach(device_t self, int flags)
329 {
330 struct com_cardbus_softc *csc = device_private(self);
331 struct com_softc *sc = device_private(self);
332 cardbus_devfunc_t ct = csc->cc_ct;
333 int error;
334
335 if ((error = com_detach(self, flags)) != 0)
336 return error;
337
338 if (csc->cc_ih != NULL)
339 cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf, csc->cc_ih);
340
341 Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_regs.cr_iot,
342 sc->sc_regs.cr_ioh, csc->cc_size);
343
344 return 0;
345 }
346