xref: /netbsd-src/sys/dev/cardbus/com_cardbus.c (revision e5548b402ae4c44fb816de42c7bba9581ce23ef5)
1 /* $NetBSD: com_cardbus.c,v 1.15 2005/12/11 12:21:15 christos 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.15 2005/12/11 12:21:15 christos 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 = (struct com_softc*)self;
209 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self;
210 	struct cardbus_attach_args *ca = aux;
211 
212 	csc->cc_ct = ca->ca_ct;
213 	csc->cc_tag = Cardbus_make_tag(csc->cc_ct);
214 
215 	if(gofigure(ca, csc) != 0)
216 		return;
217 
218 	if(Cardbus_mapreg_map(ca->ca_ct,
219 			      csc->cc_reg,
220 			      csc->cc_type,
221 			      0,
222 			      &sc->sc_iot,
223 			      &sc->sc_ioh,
224 			      &csc->cc_addr,
225 			      &csc->cc_size) != 0) {
226 		printf("failed to map memory");
227 		return;
228 	}
229 
230 	csc->cc_base = csc->cc_addr;
231 	csc->cc_csr = CARDBUS_COMMAND_MASTER_ENABLE;
232 	if(csc->cc_type == CARDBUS_MAPREG_TYPE_IO) {
233 		csc->cc_base |= CARDBUS_MAPREG_TYPE_IO;
234 		csc->cc_csr |= CARDBUS_COMMAND_IO_ENABLE;
235 		csc->cc_cben = CARDBUS_IO_ENABLE;
236 	} else {
237 		csc->cc_csr |= CARDBUS_COMMAND_MEM_ENABLE;
238 		csc->cc_cben = CARDBUS_MEM_ENABLE;
239 	}
240 	sc->sc_iobase = csc->cc_addr;
241 
242 	sc->sc_frequency = COM_FREQ;
243 
244 	sc->enable = com_cardbus_enable;
245 	sc->disable = com_cardbus_disable;
246 	sc->enabled = 0;
247 
248 	if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) {
249 		printf(": %s %s\n", ca->ca_cis.cis1_info[0],
250 		    ca->ca_cis.cis1_info[1]);
251 		printf("%s", DEVNAME(csc));
252 	}
253 
254 	com_cardbus_setup(csc);
255 
256 	com_attach_subr(sc);
257 
258 	Cardbus_function_disable(csc->cc_ct);
259 }
260 
261 static void
262 com_cardbus_setup(struct com_cardbus_softc *csc)
263 {
264         cardbus_devfunc_t ct = csc->cc_ct;
265         cardbus_chipset_tag_t cc = ct->ct_cc;
266         cardbus_function_tag_t cf = ct->ct_cf;
267 	cardbusreg_t reg;
268 
269 	Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
270 
271 	/* enable accesses on cardbus bridge */
272 	(*cf->cardbus_ctrl)(cc, csc->cc_cben);
273 	(*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
274 
275 	/* and the card itself */
276 	reg = Cardbus_conf_read(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG);
277 	reg &= ~(CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE);
278 	reg |= csc->cc_csr;
279 	Cardbus_conf_write(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG, reg);
280 
281         /*
282          * Make sure the latency timer is set to some reasonable
283          * value.
284          */
285         reg = cardbus_conf_read(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG);
286         if (CARDBUS_LATTIMER(reg) < 0x20) {
287                 reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT);
288                 reg |= (0x20 << CARDBUS_LATTIMER_SHIFT);
289                 cardbus_conf_write(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG, reg);
290         }
291 }
292 
293 static int
294 com_cardbus_enable(struct com_softc *sc)
295 {
296 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
297 	struct cardbus_softc *psc =
298 		(struct cardbus_softc *)sc->sc_dev.dv_parent;
299 	cardbus_chipset_tag_t cc = psc->sc_cc;
300 	cardbus_function_tag_t cf = psc->sc_cf;
301 
302 	Cardbus_function_enable(csc->cc_ct);
303 
304 	com_cardbus_setup(csc);
305 
306 	/* establish the interrupt. */
307 	csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
308 					    IPL_SERIAL, comintr, sc);
309 	if (csc->cc_ih == NULL) {
310 		printf("%s: couldn't establish interrupt\n",
311 		       DEVNAME(csc));
312 		return 1;
313 	}
314 
315 	printf("%s: interrupting at irq %d\n",
316 	       DEVNAME(csc), psc->sc_intrline);
317 
318 	return 0;
319 }
320 
321 static void
322 com_cardbus_disable(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_intr_disestablish(cc, cf, csc->cc_ih);
331 	csc->cc_ih = NULL;
332 
333 	Cardbus_function_disable(csc->cc_ct);
334 }
335 
336 static int
337 com_cardbus_detach(struct device *self, int flags)
338 {
339 	struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self;
340 	struct com_softc *sc = (struct com_softc *) self;
341 	struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent;
342 	int error;
343 
344 	if ((error = com_detach(self, flags)) != 0)
345 		return error;
346 
347 	if (csc->cc_ih != NULL)
348 		cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
349 
350 	Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh,
351 			     csc->cc_size);
352 
353 	return 0;
354 }
355