com_cardbus.c revision 1.17.4.1 1 /* $NetBSD: com_cardbus.c,v 1.17.4.1 2006/06/15 16:32:28 gdamore 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.17.4.1 2006/06/15 16:32:28 gdamore 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 void *cc_ih;
61 cardbus_devfunc_t cc_ct;
62 bus_addr_t cc_addr;
63 cardbusreg_t cc_base;
64 bus_size_t cc_size;
65 cardbusreg_t cc_csr;
66 int cc_cben;
67 cardbustag_t cc_tag;
68 cardbusreg_t cc_reg;
69 int cc_type;
70 };
71
72 #define DEVNAME(CSC) ((CSC)->cc_com.sc_dev.dv_xname)
73
74 static int com_cardbus_match (struct device*, struct cfdata*, void*);
75 static void com_cardbus_attach (struct device*, struct device*, void*);
76 static int com_cardbus_detach (struct device*, 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(com_cardbus, sizeof(struct com_cardbus_softc),
83 com_cardbus_match, com_cardbus_attach, com_cardbus_detach, com_activate);
84
85 static struct csdev {
86 int vendor;
87 int product;
88 cardbusreg_t reg;
89 int type;
90 } csdevs[] = {
91 { PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_MODEM56,
92 CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
93 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MODEM56,
94 CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
95 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656_M,
96 CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
97 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656B_M,
98 CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
99 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656C_M,
100 CARDBUS_BASE0_REG, CARDBUS_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 == CARDBUS_VENDOR(ca->ca_id) &&
112 cp->product == CARDBUS_PRODUCT(ca->ca_id))
113 return cp;
114 return NULL;
115 }
116
117 static int
118 com_cardbus_match(struct device *parent, struct cfdata *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 cardbusreg_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 printf(": 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 = CARDBUS_MAPREG_TYPE_MEM;
176 else
177 csc->cc_type = CARDBUS_MAPREG_TYPE_IO;
178 return 0;
179
180 multi_bar:
181 printf(": there are more than one possible base\n");
182
183 printf("%s: address for this device, "
184 "please report the following information\n",
185 DEVNAME(csc));
186 printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc),
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 printf("%s: base address %x type %s size %x\n",
197 DEVNAME(csc),
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 (struct device *parent, struct device *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 csc->cc_ct = ca->ca_ct;
215 csc->cc_tag = Cardbus_make_tag(csc->cc_ct);
216
217 if(gofigure(ca, csc) != 0)
218 return;
219
220 if(Cardbus_mapreg_map(ca->ca_ct,
221 csc->cc_reg,
222 csc->cc_type,
223 0,
224 &iot,
225 &ioh,
226 &csc->cc_addr,
227 &csc->cc_size) != 0) {
228 printf("failed to map memory");
229 return;
230 }
231
232 COM_INIT_REGS(sc->sc_regs, iot, ioh, csc->cc_addr);
233
234 csc->cc_base = csc->cc_addr;
235 csc->cc_csr = CARDBUS_COMMAND_MASTER_ENABLE;
236 if(csc->cc_type == CARDBUS_MAPREG_TYPE_IO) {
237 csc->cc_base |= CARDBUS_MAPREG_TYPE_IO;
238 csc->cc_csr |= CARDBUS_COMMAND_IO_ENABLE;
239 csc->cc_cben = CARDBUS_IO_ENABLE;
240 } else {
241 csc->cc_csr |= CARDBUS_COMMAND_MEM_ENABLE;
242 csc->cc_cben = CARDBUS_MEM_ENABLE;
243 }
244 sc->sc_regs.iobase = csc->cc_addr;
245
246 sc->sc_frequency = COM_FREQ;
247
248 sc->enable = com_cardbus_enable;
249 sc->disable = com_cardbus_disable;
250 sc->enabled = 0;
251
252 if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) {
253 printf(": %s %s\n", ca->ca_cis.cis1_info[0],
254 ca->ca_cis.cis1_info[1]);
255 printf("%s", DEVNAME(csc));
256 }
257
258 com_cardbus_setup(csc);
259
260 com_attach_subr(sc);
261
262 Cardbus_function_disable(csc->cc_ct);
263 }
264
265 static void
266 com_cardbus_setup(struct com_cardbus_softc *csc)
267 {
268 cardbus_devfunc_t ct = csc->cc_ct;
269 cardbus_chipset_tag_t cc = ct->ct_cc;
270 cardbus_function_tag_t cf = ct->ct_cf;
271 cardbusreg_t reg;
272
273 Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
274
275 /* enable accesses on cardbus bridge */
276 (*cf->cardbus_ctrl)(cc, csc->cc_cben);
277 (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
278
279 /* and the card itself */
280 reg = Cardbus_conf_read(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG);
281 reg &= ~(CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE);
282 reg |= csc->cc_csr;
283 Cardbus_conf_write(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG, reg);
284
285 /*
286 * Make sure the latency timer is set to some reasonable
287 * value.
288 */
289 reg = cardbus_conf_read(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG);
290 if (CARDBUS_LATTIMER(reg) < 0x20) {
291 reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT);
292 reg |= (0x20 << CARDBUS_LATTIMER_SHIFT);
293 cardbus_conf_write(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG, reg);
294 }
295 }
296
297 static int
298 com_cardbus_enable(struct com_softc *sc)
299 {
300 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
301 struct cardbus_softc *psc =
302 (struct cardbus_softc *)device_parent(&sc->sc_dev);
303 cardbus_chipset_tag_t cc = psc->sc_cc;
304 cardbus_function_tag_t cf = psc->sc_cf;
305
306 Cardbus_function_enable(csc->cc_ct);
307
308 com_cardbus_setup(csc);
309
310 /* establish the interrupt. */
311 csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
312 IPL_SERIAL, comintr, sc);
313 if (csc->cc_ih == NULL) {
314 printf("%s: couldn't establish interrupt\n",
315 DEVNAME(csc));
316 return 1;
317 }
318
319 printf("%s: interrupting at irq %d\n",
320 DEVNAME(csc), psc->sc_intrline);
321
322 return 0;
323 }
324
325 static void
326 com_cardbus_disable(struct com_softc *sc)
327 {
328 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
329 struct cardbus_softc *psc =
330 (struct cardbus_softc *)device_parent(&sc->sc_dev);
331 cardbus_chipset_tag_t cc = psc->sc_cc;
332 cardbus_function_tag_t cf = psc->sc_cf;
333
334 cardbus_intr_disestablish(cc, cf, csc->cc_ih);
335 csc->cc_ih = NULL;
336
337 Cardbus_function_disable(csc->cc_ct);
338 }
339
340 static int
341 com_cardbus_detach(struct device *self, int flags)
342 {
343 struct com_cardbus_softc *csc = device_private(self);
344 struct com_softc *sc = device_private(self);
345 struct cardbus_softc *psc = device_private(device_parent(self));
346 int error;
347
348 if ((error = com_detach(self, flags)) != 0)
349 return error;
350
351 if (csc->cc_ih != NULL)
352 cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
353
354 Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_regs.iot,
355 sc->sc_regs.ioh, csc->cc_size);
356
357 return 0;
358 }
359