1 /* $OpenBSD: com_cardbus.c,v 1.44 2022/04/06 18:59:27 naddy 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 const 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 com_cardbus_find_csdev(struct cardbus_attach_args *ca)
139 {
140 struct csdev *cp;
141
142 for (cp = csdevs; cp < csdevs + ncsdevs; cp++)
143 if (cp->vendor == PCI_VENDOR(ca->ca_id) &&
144 cp->product == PCI_PRODUCT(ca->ca_id))
145 return (cp);
146 return (NULL);
147 }
148
149 int
150 com_cardbus_match(struct device *parent, void *match, void *aux)
151 {
152 struct cardbus_attach_args *ca = aux;
153
154 /* known devices are ok */
155 if (com_cardbus_find_csdev(ca) != NULL)
156 return (10);
157
158 /* as are serial devices with a known UART */
159 if (ca->ca_cis.funcid == PCMCIA_FUNCTION_SERIAL &&
160 ca->ca_cis.funce.serial.uart_present != 0 &&
161 (ca->ca_cis.funce.serial.uart_type == 0 || /* 8250 */
162 ca->ca_cis.funce.serial.uart_type == 1 || /* 16450 */
163 ca->ca_cis.funce.serial.uart_type == 2)) /* 16550 */
164 return (1);
165
166 return (0);
167 }
168
169 int
170 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 cp = com_cardbus_find_csdev(ca);
179 if (cp != NULL) {
180 csc->cc_reg = cp->reg;
181 csc->cc_type = cp->type;
182 csc->cc_bug = cp->bug;
183 return (0);
184 }
185
186 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 for (i = 0; i < 7; i++) {
192 /* ignore zero sized BARs */
193 if (ca->ca_cis.bar[i].size == 0)
194 continue;
195 /* ignore the CIS BAR */
196 if (CARDBUS_CIS_ASI_BAR(cis_ptr) ==
197 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
198 continue;
199 if (index != -1)
200 goto multi_bar;
201 index = i;
202 }
203 if (index == -1) {
204 printf(": couldn't find any base address tuple\n");
205 return (1);
206 }
207 csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
208 if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
209 csc->cc_type = PCI_MAPREG_TYPE_MEM;
210 else
211 csc->cc_type = PCI_MAPREG_TYPE_IO;
212 return (0);
213
214 multi_bar:
215 printf(": there are more than one possible base\n");
216
217 printf("%s: address for this device, "
218 "please report the following information\n",
219 DEVNAME(csc));
220 printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc),
221 PCI_VENDOR(ca->ca_id), PCI_PRODUCT(ca->ca_id));
222 for (i = 0; i < 7; i++) {
223 /* ignore zero sized BARs */
224 if (ca->ca_cis.bar[i].size == 0)
225 continue;
226 /* ignore the CIS BAR */
227 if (CARDBUS_CIS_ASI_BAR(cis_ptr) ==
228 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
229 continue;
230 printf("%s: base address %x type %s size %x\n",
231 DEVNAME(csc), CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
232 (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
233 ca->ca_cis.bar[i].size);
234 }
235 return (1);
236 }
237
238 void
239 com_cardbus_attach(struct device *parent, struct device *self, void *aux)
240 {
241 struct com_softc *sc = (struct com_softc*)self;
242 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self;
243 struct cardbus_attach_args *ca = aux;
244 cardbus_devfunc_t ct;
245
246 csc->cc_ct = ct = ca->ca_ct;
247 csc->cc_tag = pci_make_tag(ca->ca_pc, ct->ct_bus, ct->ct_dev, ct->ct_func);
248 csc->cc_pc = ca->ca_pc;
249
250 if (com_cardbus_gofigure(ca, csc) != 0)
251 return;
252
253 if (Cardbus_mapreg_map(ca->ca_ct, csc->cc_reg, csc->cc_type, 0,
254 &sc->sc_iot, &sc->sc_ioh, &csc->cc_addr, &csc->cc_size) != 0) {
255 printf(": can't map memory\n");
256 return;
257 }
258
259 csc->cc_base = csc->cc_addr;
260 csc->cc_csr = PCI_COMMAND_MASTER_ENABLE;
261 if (csc->cc_type == PCI_MAPREG_TYPE_IO) {
262 csc->cc_base |= PCI_MAPREG_TYPE_IO;
263 csc->cc_csr |= PCI_COMMAND_IO_ENABLE;
264 csc->cc_cben = CARDBUS_IO_ENABLE;
265 } else {
266 csc->cc_csr |= PCI_COMMAND_MEM_ENABLE;
267 csc->cc_cben = CARDBUS_MEM_ENABLE;
268 }
269
270 sc->sc_iobase = csc->cc_addr;
271 sc->sc_frequency = COM_FREQ;
272
273 sc->enable = com_cardbus_enable;
274 sc->disable = com_cardbus_disable;
275 sc->enabled = 0;
276
277 if (com_cardbus_enable(sc))
278 return;
279 sc->enabled = 1;
280
281 sc->sc_hwflags = 0;
282 sc->sc_swflags = 0;
283
284 if (csc->cc_bug & BUG_BROADCOM)
285 sc->sc_fifolen = 15;
286
287 com_attach_subr(sc);
288 }
289
290 void
291 com_cardbus_setup(struct com_cardbus_softc *csc)
292 {
293 cardbus_devfunc_t ct = csc->cc_ct;
294 cardbus_chipset_tag_t cc = ct->ct_cc;
295 pci_chipset_tag_t pc = csc->cc_pc;
296 cardbus_function_tag_t cf = ct->ct_cf;
297 pcireg_t reg;
298
299 pci_conf_write(pc, csc->cc_tag, csc->cc_reg, csc->cc_base);
300
301 /* enable accesses on cardbus bridge */
302 cf->cardbus_ctrl(cc, csc->cc_cben);
303 cf->cardbus_ctrl(cc, CARDBUS_BM_ENABLE);
304
305 /* and the card itself */
306 reg = pci_conf_read(pc, csc->cc_tag, PCI_COMMAND_STATUS_REG);
307 reg &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
308 reg |= csc->cc_csr;
309 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 reg = pci_conf_read(pc, csc->cc_tag, PCI_BHLC_REG);
316 if (PCI_LATTIMER(reg) < 0x20) {
317 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
318 reg |= (0x20 << PCI_LATTIMER_SHIFT);
319 pci_conf_write(pc, csc->cc_tag, PCI_BHLC_REG, reg);
320 }
321 }
322
323 int
324 com_cardbus_enable(struct com_softc *sc)
325 {
326 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
327 struct cardbus_softc *psc =
328 (struct cardbus_softc *)sc->sc_dev.dv_parent;
329 cardbus_chipset_tag_t cc = psc->sc_cc;
330 cardbus_function_tag_t cf = psc->sc_cf;
331
332 Cardbus_function_enable(csc->cc_ct);
333
334 com_cardbus_setup(csc);
335
336 /* establish the interrupt. */
337 csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
338 IPL_TTY, comintr, sc, DEVNAME(csc));
339 if (csc->cc_ih == NULL) {
340 printf(": couldn't establish interrupt\n");
341 return (1);
342 }
343
344 printf(": irq %d", psc->sc_intrline);
345
346 return (0);
347 }
348
349 void
350 com_cardbus_disable(struct com_softc *sc)
351 {
352 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
353 struct cardbus_softc *psc =
354 (struct cardbus_softc *)sc->sc_dev.dv_parent;
355 cardbus_chipset_tag_t cc = psc->sc_cc;
356 cardbus_function_tag_t cf = psc->sc_cf;
357
358 cardbus_intr_disestablish(cc, cf, csc->cc_ih);
359 Cardbus_function_disable(csc->cc_ct);
360 }
361
362 int
363 com_cardbus_detach(struct device *self, int flags)
364 {
365 struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self;
366 struct com_softc *sc = (struct com_softc *) self;
367 struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent;
368 int error;
369
370 if ((error = com_detach(self, flags)) != 0)
371 return (error);
372
373 cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
374
375 Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh,
376 csc->cc_size);
377
378 return (0);
379 }
Cache object: e85a7e29002ac6dec6596be877bc255c
|