xref: /openbsd/sys/dev/cardbus/com_cardbus.c (revision c7101648)
1 /* $OpenBSD: com_cardbus.c,v 1.45 2024/05/24 06:26:47 jsg 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 <dev/ic/comreg.h>
60 #include <dev/ic/comvar.h>
61 #include <dev/ic/ns16550reg.h>
62 
63 #define	com_lcr		com_cfcr
64 
65 struct com_cardbus_softc {
66 	struct com_softc	cc_com;
67 	void			*cc_ih;
68 	cardbus_devfunc_t	cc_ct;
69 	bus_addr_t		cc_addr;
70 	pcireg_t		cc_base;
71 	bus_size_t		cc_size;
72 	pcireg_t		cc_csr;
73 	int			cc_cben;
74 	pcitag_t		cc_tag;
75 	pcireg_t		cc_reg;
76 	int			cc_type;
77 	u_char			cc_bug;
78 	pci_chipset_tag_t	cc_pc;
79 };
80 
81 #define DEVNAME(CSC) ((CSC)->cc_com.sc_dev.dv_xname)
82 
83 int	com_cardbus_match(struct device *, void *, void *);
84 void	com_cardbus_attach(struct device *, struct device *, void *);
85 int	com_cardbus_detach(struct device *, int);
86 
87 void	com_cardbus_setup(struct com_cardbus_softc *);
88 int	com_cardbus_enable(struct com_softc *);
89 void	com_cardbus_disable(struct com_softc *);
90 struct csdev *com_cardbus_find_csdev(struct cardbus_attach_args *);
91 int	com_cardbus_gofigure(struct cardbus_attach_args *,
92     struct com_cardbus_softc *);
93 
94 const struct cfattach com_cardbus_ca = {
95 	sizeof(struct com_cardbus_softc), com_cardbus_match,
96 	com_cardbus_attach, com_cardbus_detach, com_activate
97 };
98 
99 #define BUG_BROADCOM	0x01
100 
101 /* XXX Keep this list synchronized with the corresponding one in pucdata.c */
102 static struct csdev {
103 	u_short		vendor;
104 	u_short		product;
105 	pcireg_t	reg;
106 	u_char		type;
107 	u_char		bug;
108 } csdevs[] = {
109 	{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_GLOBALMODEM56,
110 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
111 	{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_MODEM56,
112 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
113 	{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_SERIAL,
114 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO, BUG_BROADCOM },
115 	{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_SERIAL_2,
116 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO, BUG_BROADCOM },
117 	{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_SERIAL_GC,
118 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
119 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MODEM56,
120 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
121 	{ PCI_VENDOR_OXFORD2, PCI_PRODUCT_OXFORD2_OXCB950,
122 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
123 	{ PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_CBEM56G,
124 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
125 	{ PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_MODEM56,
126 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
127 	{ PCI_VENDOR_WCH, PCI_PRODUCT_WCH_CH352,
128 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO },
129 	{ PCI_VENDOR_NETMOS, PCI_PRODUCT_NETMOS_NM9820,
130 	  CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }
131 };
132 
133 static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]);
134 
135 struct csdev*
com_cardbus_find_csdev(struct cardbus_attach_args * ca)136 com_cardbus_find_csdev(struct cardbus_attach_args *ca)
137 {
138 	struct csdev *cp;
139 
140 	for (cp = csdevs; cp < csdevs + ncsdevs; cp++)
141 		if (cp->vendor == PCI_VENDOR(ca->ca_id) &&
142 		    cp->product == PCI_PRODUCT(ca->ca_id))
143 			return (cp);
144 	return (NULL);
145 }
146 
147 int
com_cardbus_match(struct device * parent,void * match,void * aux)148 com_cardbus_match(struct device *parent, void *match, void *aux)
149 {
150 	struct cardbus_attach_args *ca = aux;
151 
152 	/* known devices are ok */
153 	if (com_cardbus_find_csdev(ca) != NULL)
154 	    return (10);
155 
156 	/* as are serial devices with a known UART */
157 	if (ca->ca_cis.funcid == PCMCIA_FUNCTION_SERIAL &&
158 	    ca->ca_cis.funce.serial.uart_present != 0 &&
159 	    (ca->ca_cis.funce.serial.uart_type == 0 ||	/* 8250 */
160 	    ca->ca_cis.funce.serial.uart_type == 1 ||	/* 16450 */
161 	    ca->ca_cis.funce.serial.uart_type == 2))	/* 16550 */
162 		return (1);
163 
164 	return (0);
165 }
166 
167 int
com_cardbus_gofigure(struct cardbus_attach_args * ca,struct com_cardbus_softc * csc)168 com_cardbus_gofigure(struct cardbus_attach_args *ca,
169     struct com_cardbus_softc *csc)
170 {
171 	int i, index = -1;
172 	pcireg_t cis_ptr;
173 	struct csdev *cp;
174 
175 	/* If this device is listed above, use the known values, */
176 	cp = com_cardbus_find_csdev(ca);
177 	if (cp != NULL) {
178 		csc->cc_reg = cp->reg;
179 		csc->cc_type = cp->type;
180 		csc->cc_bug = cp->bug;
181 		return (0);
182 	}
183 
184 	cis_ptr = pci_conf_read(ca->ca_pc, csc->cc_tag, CARDBUS_CIS_REG);
185 
186 	/* otherwise try to deduce which BAR and type to use from CIS.  If
187 	   there is only one BAR, it must be the one we should use, if
188 	   there are more, we're out of luck.  */
189 	for (i = 0; i < 7; i++) {
190 		/* ignore zero sized BARs */
191 		if (ca->ca_cis.bar[i].size == 0)
192 			continue;
193 		/* ignore the CIS BAR */
194 		if (CARDBUS_CIS_ASI_BAR(cis_ptr) ==
195 		    CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
196 			continue;
197 		if (index != -1)
198 			goto multi_bar;
199 		index = i;
200 	}
201 	if (index == -1) {
202 		printf(": couldn't find any base address tuple\n");
203 		return (1);
204 	}
205 	csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
206 	if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
207 		csc->cc_type = PCI_MAPREG_TYPE_MEM;
208 	else
209 		csc->cc_type = PCI_MAPREG_TYPE_IO;
210 	return (0);
211 
212   multi_bar:
213 	printf(": there are more than one possible base\n");
214 
215 	printf("%s: address for this device, "
216 	    "please report the following information\n",
217 	    DEVNAME(csc));
218 	printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc),
219 	    PCI_VENDOR(ca->ca_id), PCI_PRODUCT(ca->ca_id));
220 	for (i = 0; i < 7; i++) {
221 		/* ignore zero sized BARs */
222 		if (ca->ca_cis.bar[i].size == 0)
223 			continue;
224 		/* ignore the CIS BAR */
225 		if (CARDBUS_CIS_ASI_BAR(cis_ptr) ==
226 		    CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
227 			continue;
228 		printf("%s: base address %x type %s size %x\n",
229 		    DEVNAME(csc), CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
230 		    (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
231 		    ca->ca_cis.bar[i].size);
232 	}
233 	return (1);
234 }
235 
236 void
com_cardbus_attach(struct device * parent,struct device * self,void * aux)237 com_cardbus_attach(struct device *parent, struct device *self, void *aux)
238 {
239 	struct com_softc *sc = (struct com_softc*)self;
240 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self;
241 	struct cardbus_attach_args *ca = aux;
242 	cardbus_devfunc_t ct;
243 
244 	csc->cc_ct = ct = ca->ca_ct;
245 	csc->cc_tag = pci_make_tag(ca->ca_pc, ct->ct_bus, ct->ct_dev, ct->ct_func);
246 	csc->cc_pc = ca->ca_pc;
247 
248 	if (com_cardbus_gofigure(ca, csc) != 0)
249 		return;
250 
251 	if (Cardbus_mapreg_map(ca->ca_ct, csc->cc_reg, csc->cc_type, 0,
252 	    &sc->sc_iot, &sc->sc_ioh, &csc->cc_addr, &csc->cc_size) != 0) {
253 		printf(": can't map memory\n");
254 		return;
255 	}
256 
257 	csc->cc_base = csc->cc_addr;
258 	csc->cc_csr = PCI_COMMAND_MASTER_ENABLE;
259 	if (csc->cc_type == PCI_MAPREG_TYPE_IO) {
260 		csc->cc_base |= PCI_MAPREG_TYPE_IO;
261 		csc->cc_csr |= PCI_COMMAND_IO_ENABLE;
262 		csc->cc_cben = CARDBUS_IO_ENABLE;
263 	} else {
264 		csc->cc_csr |= PCI_COMMAND_MEM_ENABLE;
265 		csc->cc_cben = CARDBUS_MEM_ENABLE;
266 	}
267 
268 	sc->sc_iobase = csc->cc_addr;
269 	sc->sc_frequency = COM_FREQ;
270 
271 	sc->enable = com_cardbus_enable;
272 	sc->disable = com_cardbus_disable;
273 	sc->enabled = 0;
274 
275 	if (com_cardbus_enable(sc))
276 		return;
277 	sc->enabled = 1;
278 
279 	sc->sc_hwflags = 0;
280 	sc->sc_swflags = 0;
281 
282 	if (csc->cc_bug & BUG_BROADCOM)
283 		sc->sc_fifolen = 15;
284 
285 	com_attach_subr(sc);
286 }
287 
288 void
com_cardbus_setup(struct com_cardbus_softc * csc)289 com_cardbus_setup(struct com_cardbus_softc *csc)
290 {
291 	cardbus_devfunc_t ct = csc->cc_ct;
292 	cardbus_chipset_tag_t cc = ct->ct_cc;
293 	pci_chipset_tag_t pc = csc->cc_pc;
294 	cardbus_function_tag_t cf = ct->ct_cf;
295 	pcireg_t reg;
296 
297 	pci_conf_write(pc, csc->cc_tag, csc->cc_reg, csc->cc_base);
298 
299 	/* enable accesses on cardbus bridge */
300 	cf->cardbus_ctrl(cc, csc->cc_cben);
301 	cf->cardbus_ctrl(cc, CARDBUS_BM_ENABLE);
302 
303 	/* and the card itself */
304 	reg = pci_conf_read(pc, csc->cc_tag, PCI_COMMAND_STATUS_REG);
305 	reg &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
306 	reg |= csc->cc_csr;
307 	pci_conf_write(pc, csc->cc_tag, PCI_COMMAND_STATUS_REG, reg);
308 
309 	/*
310 	 * Make sure the latency timer is set to some reasonable
311 	 * value.
312 	 */
313 	reg = pci_conf_read(pc, csc->cc_tag, PCI_BHLC_REG);
314 	if (PCI_LATTIMER(reg) < 0x20) {
315 			reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
316 			reg |= (0x20 << PCI_LATTIMER_SHIFT);
317 			pci_conf_write(pc, csc->cc_tag, PCI_BHLC_REG, reg);
318 	}
319 }
320 
321 int
com_cardbus_enable(struct com_softc * sc)322 com_cardbus_enable(struct com_softc *sc)
323 {
324 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
325 	struct cardbus_softc *psc =
326 	    (struct cardbus_softc *)sc->sc_dev.dv_parent;
327 	cardbus_chipset_tag_t cc = psc->sc_cc;
328 	cardbus_function_tag_t cf = psc->sc_cf;
329 
330 	Cardbus_function_enable(csc->cc_ct);
331 
332 	com_cardbus_setup(csc);
333 
334 	/* establish the interrupt. */
335 	csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
336 	    IPL_TTY, comintr, sc, DEVNAME(csc));
337 	if (csc->cc_ih == NULL) {
338 		printf(": couldn't establish interrupt\n");
339 		return (1);
340 	}
341 
342 	printf(": irq %d", psc->sc_intrline);
343 
344 	return (0);
345 }
346 
347 void
com_cardbus_disable(struct com_softc * sc)348 com_cardbus_disable(struct com_softc *sc)
349 {
350 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
351 	struct cardbus_softc *psc =
352 	    (struct cardbus_softc *)sc->sc_dev.dv_parent;
353 	cardbus_chipset_tag_t cc = psc->sc_cc;
354 	cardbus_function_tag_t cf = psc->sc_cf;
355 
356 	cardbus_intr_disestablish(cc, cf, csc->cc_ih);
357 	Cardbus_function_disable(csc->cc_ct);
358 }
359 
360 int
com_cardbus_detach(struct device * self,int flags)361 com_cardbus_detach(struct device *self, int flags)
362 {
363 	struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self;
364 	struct com_softc *sc = (struct com_softc *) self;
365 	struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent;
366 	int error;
367 
368 	if ((error = com_detach(self, flags)) != 0)
369 		return (error);
370 
371 	cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
372 
373 	Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh,
374 	    csc->cc_size);
375 
376 	return (0);
377 }
378