Line data Source code
1 : /* $OpenBSD: com_cardbus.c,v 1.43 2015/11/14 14:47:56 miod Exp $ */
2 : /* $NetBSD: com_cardbus.c,v 1.4 2000/04/17 09:21:59 joda Exp $ */
3 :
4 : /*
5 : * Copyright (c) 2000 Johan Danielsson
6 : * All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
15 : * 2. Redistributions in binary form must reproduce the above copyright
16 : * notice, this list of conditions and the following disclaimer in the
17 : * documentation and/or other materials provided with the distribution.
18 : *
19 : * 3. Neither the name of author nor the names of any contributors may
20 : * be used to endorse or promote products derived from this
21 : * software without specific prior written permission.
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
27 : * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 : */
35 :
36 : /* This is a driver for CardBus based serial devices. It is less
37 : generic than it could be, but it keeps the complexity down. So far
38 : it assumes that anything that reports itself as a `serial' device
39 : is infact a 16x50 or 8250, which is not necessarily true (in
40 : practice this shouldn't be a problem). It also does not handle
41 : devices in the `multiport serial' or `modem' sub-classes, I've
42 : never seen any of these, so I don't know what they might look like.
43 :
44 : If the CardBus device only has one BAR (that is not also the CIS
45 : BAR) listed in the CIS, it is assumed to be the one to use. For
46 : devices with more than one BAR, the list of known devies has to be
47 : updated below. */
48 :
49 : #include <sys/param.h>
50 : #include <sys/systm.h>
51 : #include <sys/tty.h>
52 : #include <sys/device.h>
53 :
54 : #include <dev/cardbus/cardbusvar.h>
55 : #include <dev/pci/pcidevs.h>
56 :
57 : #include <dev/pcmcia/pcmciareg.h>
58 :
59 : #include "com.h"
60 :
61 : #include <dev/ic/comreg.h>
62 : #include <dev/ic/comvar.h>
63 : #include <dev/ic/ns16550reg.h>
64 :
65 : #define com_lcr com_cfcr
66 :
67 : struct com_cardbus_softc {
68 : struct com_softc cc_com;
69 : void *cc_ih;
70 : cardbus_devfunc_t cc_ct;
71 : bus_addr_t cc_addr;
72 : pcireg_t cc_base;
73 : bus_size_t cc_size;
74 : pcireg_t cc_csr;
75 : int cc_cben;
76 : pcitag_t cc_tag;
77 : pcireg_t cc_reg;
78 : int cc_type;
79 : u_char cc_bug;
80 : pci_chipset_tag_t cc_pc;
81 : };
82 :
83 : #define DEVNAME(CSC) ((CSC)->cc_com.sc_dev.dv_xname)
84 :
85 : int com_cardbus_match(struct device *, void *, void *);
86 : void com_cardbus_attach(struct device *, struct device *, void *);
87 : int com_cardbus_detach(struct device *, int);
88 :
89 : void com_cardbus_setup(struct com_cardbus_softc *);
90 : int com_cardbus_enable(struct com_softc *);
91 : void com_cardbus_disable(struct com_softc *);
92 : struct csdev *com_cardbus_find_csdev(struct cardbus_attach_args *);
93 : int com_cardbus_gofigure(struct cardbus_attach_args *,
94 : struct com_cardbus_softc *);
95 :
96 : struct cfattach com_cardbus_ca = {
97 : sizeof(struct com_cardbus_softc), com_cardbus_match,
98 : com_cardbus_attach, com_cardbus_detach, com_activate
99 : };
100 :
101 : #define BUG_BROADCOM 0x01
102 :
103 : /* XXX Keep this list synchronized with the corresponding one in pucdata.c */
104 : static struct csdev {
105 : u_short vendor;
106 : u_short product;
107 : pcireg_t reg;
108 : u_char type;
109 : u_char bug;
110 : } csdevs[] = {
111 : { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_GLOBALMODEM56,
112 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
113 : { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_MODEM56,
114 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
115 : { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_SERIAL,
116 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO, BUG_BROADCOM },
117 : { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_SERIAL_2,
118 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO, BUG_BROADCOM },
119 : { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_SERIAL_GC,
120 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
121 : { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MODEM56,
122 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
123 : { PCI_VENDOR_OXFORD2, PCI_PRODUCT_OXFORD2_OXCB950,
124 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
125 : { PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_CBEM56G,
126 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
127 : { PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_MODEM56,
128 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
129 : { PCI_VENDOR_WCH, PCI_PRODUCT_WCH_CH352,
130 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
131 : { PCI_VENDOR_NETMOS, PCI_PRODUCT_NETMOS_NM9820,
132 : CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }
133 : };
134 :
135 : static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]);
136 :
137 : struct csdev*
138 0 : com_cardbus_find_csdev(struct cardbus_attach_args *ca)
139 : {
140 : struct csdev *cp;
141 :
142 0 : for (cp = csdevs; cp < csdevs + ncsdevs; cp++)
143 0 : if (cp->vendor == PCI_VENDOR(ca->ca_id) &&
144 0 : cp->product == PCI_PRODUCT(ca->ca_id))
145 0 : return (cp);
146 0 : return (NULL);
147 0 : }
148 :
149 : int
150 0 : com_cardbus_match(struct device *parent, void *match, void *aux)
151 : {
152 0 : struct cardbus_attach_args *ca = aux;
153 :
154 : /* known devices are ok */
155 0 : if (com_cardbus_find_csdev(ca) != NULL)
156 0 : return (10);
157 :
158 : /* as are serial devices with a known UART */
159 0 : if (ca->ca_cis.funcid == PCMCIA_FUNCTION_SERIAL &&
160 0 : ca->ca_cis.funce.serial.uart_present != 0 &&
161 0 : (ca->ca_cis.funce.serial.uart_type == 0 || /* 8250 */
162 0 : ca->ca_cis.funce.serial.uart_type == 1 || /* 16450 */
163 0 : ca->ca_cis.funce.serial.uart_type == 2)) /* 16550 */
164 0 : return (1);
165 :
166 0 : return (0);
167 0 : }
168 :
169 : int
170 0 : com_cardbus_gofigure(struct cardbus_attach_args *ca,
171 : struct com_cardbus_softc *csc)
172 : {
173 : int i, index = -1;
174 : pcireg_t cis_ptr;
175 : struct csdev *cp;
176 :
177 : /* If this device is listed above, use the known values, */
178 0 : cp = com_cardbus_find_csdev(ca);
179 0 : if (cp != NULL) {
180 0 : csc->cc_reg = cp->reg;
181 0 : csc->cc_type = cp->type;
182 0 : csc->cc_bug = cp->bug;
183 0 : return (0);
184 : }
185 :
186 0 : cis_ptr = pci_conf_read(ca->ca_pc, csc->cc_tag, CARDBUS_CIS_REG);
187 :
188 : /* otherwise try to deduce which BAR and type to use from CIS. If
189 : there is only one BAR, it must be the one we should use, if
190 : there are more, we're out of luck. */
191 0 : for (i = 0; i < 7; i++) {
192 : /* ignore zero sized BARs */
193 0 : if (ca->ca_cis.bar[i].size == 0)
194 : continue;
195 : /* ignore the CIS BAR */
196 0 : if (CARDBUS_CIS_ASI_BAR(cis_ptr) ==
197 0 : CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
198 : continue;
199 0 : if (index != -1)
200 : goto multi_bar;
201 : index = i;
202 0 : }
203 0 : if (index == -1) {
204 0 : printf(": couldn't find any base address tuple\n");
205 0 : return (1);
206 : }
207 0 : csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
208 0 : if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
209 0 : csc->cc_type = PCI_MAPREG_TYPE_MEM;
210 : else
211 0 : csc->cc_type = PCI_MAPREG_TYPE_IO;
212 0 : return (0);
213 :
214 : multi_bar:
215 0 : printf(": there are more than one possible base\n");
216 :
217 0 : printf("%s: address for this device, "
218 : "please report the following information\n",
219 0 : DEVNAME(csc));
220 0 : printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc),
221 0 : PCI_VENDOR(ca->ca_id), PCI_PRODUCT(ca->ca_id));
222 0 : for (i = 0; i < 7; i++) {
223 : /* ignore zero sized BARs */
224 0 : if (ca->ca_cis.bar[i].size == 0)
225 : continue;
226 : /* ignore the CIS BAR */
227 0 : if (CARDBUS_CIS_ASI_BAR(cis_ptr) ==
228 0 : CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
229 : continue;
230 0 : printf("%s: base address %x type %s size %x\n",
231 : DEVNAME(csc), CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
232 0 : (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
233 : ca->ca_cis.bar[i].size);
234 0 : }
235 0 : return (1);
236 0 : }
237 :
238 : void
239 0 : com_cardbus_attach(struct device *parent, struct device *self, void *aux)
240 : {
241 0 : struct com_softc *sc = (struct com_softc*)self;
242 0 : struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self;
243 0 : struct cardbus_attach_args *ca = aux;
244 : cardbus_devfunc_t ct;
245 :
246 0 : csc->cc_ct = ct = ca->ca_ct;
247 0 : csc->cc_tag = pci_make_tag(ca->ca_pc, ct->ct_bus, ct->ct_dev, ct->ct_func);
248 0 : csc->cc_pc = ca->ca_pc;
249 :
250 0 : if (com_cardbus_gofigure(ca, csc) != 0)
251 0 : return;
252 :
253 0 : if (Cardbus_mapreg_map(ca->ca_ct, csc->cc_reg, csc->cc_type, 0,
254 0 : &sc->sc_iot, &sc->sc_ioh, &csc->cc_addr, &csc->cc_size) != 0) {
255 0 : printf(": can't map memory\n");
256 0 : return;
257 : }
258 :
259 0 : csc->cc_base = csc->cc_addr;
260 0 : csc->cc_csr = PCI_COMMAND_MASTER_ENABLE;
261 0 : if (csc->cc_type == PCI_MAPREG_TYPE_IO) {
262 0 : csc->cc_base |= PCI_MAPREG_TYPE_IO;
263 0 : csc->cc_csr |= PCI_COMMAND_IO_ENABLE;
264 0 : csc->cc_cben = CARDBUS_IO_ENABLE;
265 0 : } else {
266 0 : csc->cc_csr |= PCI_COMMAND_MEM_ENABLE;
267 0 : csc->cc_cben = CARDBUS_MEM_ENABLE;
268 : }
269 :
270 0 : sc->sc_iobase = csc->cc_addr;
271 0 : sc->sc_frequency = COM_FREQ;
272 :
273 0 : sc->enable = com_cardbus_enable;
274 0 : sc->disable = com_cardbus_disable;
275 0 : sc->enabled = 0;
276 :
277 0 : if (com_cardbus_enable(sc))
278 0 : return;
279 0 : sc->enabled = 1;
280 :
281 0 : sc->sc_hwflags = 0;
282 0 : sc->sc_swflags = 0;
283 :
284 0 : if (csc->cc_bug & BUG_BROADCOM)
285 0 : sc->sc_fifolen = 15;
286 :
287 0 : com_attach_subr(sc);
288 0 : }
289 :
290 : void
291 0 : com_cardbus_setup(struct com_cardbus_softc *csc)
292 : {
293 0 : cardbus_devfunc_t ct = csc->cc_ct;
294 0 : cardbus_chipset_tag_t cc = ct->ct_cc;
295 0 : pci_chipset_tag_t pc = csc->cc_pc;
296 0 : cardbus_function_tag_t cf = ct->ct_cf;
297 : pcireg_t reg;
298 :
299 0 : pci_conf_write(pc, csc->cc_tag, csc->cc_reg, csc->cc_base);
300 :
301 : /* enable accesses on cardbus bridge */
302 0 : cf->cardbus_ctrl(cc, csc->cc_cben);
303 0 : cf->cardbus_ctrl(cc, CARDBUS_BM_ENABLE);
304 :
305 : /* and the card itself */
306 0 : reg = pci_conf_read(pc, csc->cc_tag, PCI_COMMAND_STATUS_REG);
307 0 : reg &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
308 0 : reg |= csc->cc_csr;
309 0 : pci_conf_write(pc, csc->cc_tag, PCI_COMMAND_STATUS_REG, reg);
310 :
311 : /*
312 : * Make sure the latency timer is set to some reasonable
313 : * value.
314 : */
315 0 : reg = pci_conf_read(pc, csc->cc_tag, PCI_BHLC_REG);
316 0 : if (PCI_LATTIMER(reg) < 0x20) {
317 0 : reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
318 0 : reg |= (0x20 << PCI_LATTIMER_SHIFT);
319 0 : pci_conf_write(pc, csc->cc_tag, PCI_BHLC_REG, reg);
320 0 : }
321 0 : }
322 :
323 : int
324 0 : com_cardbus_enable(struct com_softc *sc)
325 : {
326 0 : struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
327 : struct cardbus_softc *psc =
328 0 : (struct cardbus_softc *)sc->sc_dev.dv_parent;
329 0 : cardbus_chipset_tag_t cc = psc->sc_cc;
330 0 : cardbus_function_tag_t cf = psc->sc_cf;
331 :
332 0 : Cardbus_function_enable(csc->cc_ct);
333 :
334 0 : com_cardbus_setup(csc);
335 :
336 : /* establish the interrupt. */
337 0 : csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
338 0 : IPL_TTY, comintr, sc, DEVNAME(csc));
339 0 : if (csc->cc_ih == NULL) {
340 0 : printf(": couldn't establish interrupt\n");
341 0 : return (1);
342 : }
343 :
344 0 : printf(": irq %d", psc->sc_intrline);
345 :
346 0 : return (0);
347 0 : }
348 :
349 : void
350 0 : com_cardbus_disable(struct com_softc *sc)
351 : {
352 0 : struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
353 : struct cardbus_softc *psc =
354 0 : (struct cardbus_softc *)sc->sc_dev.dv_parent;
355 0 : cardbus_chipset_tag_t cc = psc->sc_cc;
356 0 : cardbus_function_tag_t cf = psc->sc_cf;
357 :
358 0 : cardbus_intr_disestablish(cc, cf, csc->cc_ih);
359 0 : Cardbus_function_disable(csc->cc_ct);
360 0 : }
361 :
362 : int
363 0 : com_cardbus_detach(struct device *self, int flags)
364 : {
365 0 : struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self;
366 0 : struct com_softc *sc = (struct com_softc *) self;
367 0 : struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent;
368 : int error;
369 :
370 0 : if ((error = com_detach(self, flags)) != 0)
371 0 : return (error);
372 :
373 0 : cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
374 :
375 0 : Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh,
376 : csc->cc_size);
377 :
378 0 : return (0);
379 0 : }
|