xref: /netbsd/sys/dev/cardbus/com_cardbus.c (revision 6550d01e)
1 /* $NetBSD: com_cardbus.c,v 1.29 2010/03/18 20:54:56 dyoung 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.29 2010/03/18 20:54:56 dyoung 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 	cardbus_intr_line_t	cc_intrline;
61 	void			*cc_ih;
62 	cardbus_devfunc_t	cc_ct;
63 	bus_addr_t		cc_addr;
64 	pcireg_t		cc_base;
65 	bus_size_t		cc_size;
66 	pcireg_t		cc_csr;
67 	pcitag_t		cc_tag;
68 	pcireg_t		cc_reg;
69 	int			cc_type;
70 };
71 
72 #define DEVICET(CSC) ((CSC)->cc_com.sc_dev)
73 
74 static int com_cardbus_match (device_t, cfdata_t, void*);
75 static void com_cardbus_attach (device_t, device_t, void*);
76 static int com_cardbus_detach (device_t, 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_NEW(com_cardbus, sizeof(struct com_cardbus_softc),
83     com_cardbus_match, com_cardbus_attach, com_cardbus_detach, NULL);
84 
85 static struct csdev {
86 	int		vendor;
87 	int		product;
88 	pcireg_t	reg;
89 	int		type;
90 } csdevs[] = {
91 	{ PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_MODEM56,
92 	  PCI_BAR0, PCI_MAPREG_TYPE_IO },
93 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MODEM56,
94 	  PCI_BAR0, PCI_MAPREG_TYPE_IO },
95 	{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656_M,
96 	  PCI_BAR0, PCI_MAPREG_TYPE_IO },
97 	{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656B_M,
98 	  PCI_BAR0, PCI_MAPREG_TYPE_IO },
99 	{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656C_M,
100 	  PCI_BAR0, PCI_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 == PCI_VENDOR(ca->ca_id) &&
112 		   cp->product == PCI_PRODUCT(ca->ca_id))
113 			return cp;
114 	return NULL;
115 }
116 
117 static int
118 com_cardbus_match(device_t parent, cfdata_t 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 	pcireg_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 		aprint_error(": 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 = PCI_MAPREG_TYPE_MEM;
176 	else
177 		csc->cc_type = PCI_MAPREG_TYPE_IO;
178 	return 0;
179 
180   multi_bar:
181 	aprint_error(": there are more than one possible base\n");
182 
183 	aprint_error_dev(DEVICET(csc), "address for this device, "
184 	       "please report the following information\n");
185 	aprint_error_dev(DEVICET(csc), "vendor 0x%x product 0x%x\n",
186 	       PCI_VENDOR(ca->ca_id), PCI_PRODUCT(ca->ca_id));
187 	for(i = 0; i < 7; i++) {
188 		/* ignore zero sized BARs */
189 		if(ca->ca_cis.bar[i].size == 0)
190 			continue;
191 		/* ignore the CIS BAR */
192 		if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
193 		   CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
194 			continue;
195 		aprint_error_dev(DEVICET(csc),
196 		       "base address %x type %s size %x\n",
197 		       CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
198 		       (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
199 		       ca->ca_cis.bar[i].size);
200 	}
201 	return 1;
202 }
203 
204 static void
205 com_cardbus_attach (device_t parent, device_t self, void *aux)
206 {
207 	struct com_softc *sc = device_private(self);
208 	struct com_cardbus_softc *csc = device_private(self);
209 	struct cardbus_attach_args *ca = aux;
210 	bus_space_handle_t	ioh;
211 	bus_space_tag_t		iot;
212 
213 	sc->sc_dev = self;
214 	csc->cc_intrline = ca->ca_intrline;
215 	csc->cc_ct = ca->ca_ct;
216 	csc->cc_tag = ca->ca_tag;
217 
218 	if(gofigure(ca, csc) != 0)
219 		return;
220 
221 	if(Cardbus_mapreg_map(ca->ca_ct,
222 			      csc->cc_reg,
223 			      csc->cc_type,
224 			      0,
225 			      &iot,
226 			      &ioh,
227 			      &csc->cc_addr,
228 			      &csc->cc_size) != 0) {
229 		aprint_error("failed to map memory");
230 		return;
231 	}
232 
233 	COM_INIT_REGS(sc->sc_regs, iot, ioh, csc->cc_addr);
234 
235 	csc->cc_base = csc->cc_addr;
236 	csc->cc_csr = PCI_COMMAND_MASTER_ENABLE;
237 	if(csc->cc_type == PCI_MAPREG_TYPE_IO) {
238 		csc->cc_base |= PCI_MAPREG_TYPE_IO;
239 		csc->cc_csr |= PCI_COMMAND_IO_ENABLE;
240 	} else {
241 		csc->cc_csr |= PCI_COMMAND_MEM_ENABLE;
242 	}
243 
244 	sc->sc_frequency = COM_FREQ;
245 
246 	sc->enable = com_cardbus_enable;
247 	sc->disable = com_cardbus_disable;
248 	sc->enabled = 0;
249 
250 	if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) {
251 		aprint_normal(": %s %s\n", ca->ca_cis.cis1_info[0],
252 		    ca->ca_cis.cis1_info[1]);
253 		aprint_normal("%s", device_xname(DEVICET(csc)));
254 	}
255 
256 	com_cardbus_setup(csc);
257 
258 	com_attach_subr(sc);
259 
260 	Cardbus_function_disable(csc->cc_ct);
261 }
262 
263 static void
264 com_cardbus_setup(struct com_cardbus_softc *csc)
265 {
266         cardbus_devfunc_t ct = csc->cc_ct;
267 	pcireg_t reg;
268 
269 	Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
270 
271 	/* and the card itself */
272 	reg = Cardbus_conf_read(ct, csc->cc_tag, PCI_COMMAND_STATUS_REG);
273 	reg &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
274 	reg |= csc->cc_csr;
275 	Cardbus_conf_write(ct, csc->cc_tag, PCI_COMMAND_STATUS_REG, reg);
276 
277         /*
278          * Make sure the latency timer is set to some reasonable
279          * value.
280          */
281         reg = Cardbus_conf_read(ct, csc->cc_tag, PCI_BHLC_REG);
282         if (PCI_LATTIMER(reg) < 0x20) {
283                 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
284                 reg |= (0x20 << PCI_LATTIMER_SHIFT);
285                 Cardbus_conf_write(ct, csc->cc_tag, PCI_BHLC_REG, reg);
286         }
287 }
288 
289 static int
290 com_cardbus_enable(struct com_softc *sc)
291 {
292 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
293 	cardbus_devfunc_t ct = csc->cc_ct;
294 
295 	Cardbus_function_enable(ct);
296 
297 	com_cardbus_setup(csc);
298 
299 	/* establish the interrupt. */
300 	csc->cc_ih = Cardbus_intr_establish(ct, csc->cc_intrline,
301 					    IPL_SERIAL, comintr, sc);
302 	if (csc->cc_ih == NULL) {
303 		aprint_error_dev(DEVICET(csc),
304 		    "couldn't establish interrupt\n");
305 		return 1;
306 	}
307 
308 	return 0;
309 }
310 
311 static void
312 com_cardbus_disable(struct com_softc *sc)
313 {
314 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
315 	cardbus_devfunc_t ct = csc->cc_ct;
316 
317 	Cardbus_intr_disestablish(ct, csc->cc_ih);
318 	csc->cc_ih = NULL;
319 
320 	Cardbus_function_disable(ct);
321 }
322 
323 static int
324 com_cardbus_detach(device_t self, int flags)
325 {
326 	struct com_cardbus_softc *csc = device_private(self);
327 	struct com_softc *sc = device_private(self);
328 	cardbus_devfunc_t ct = csc->cc_ct;
329 	int error;
330 
331 	if ((error = com_detach(self, flags)) != 0)
332 		return error;
333 
334 	if (csc->cc_ih != NULL)
335 		Cardbus_intr_disestablish(ct, csc->cc_ih);
336 
337 	Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_regs.cr_iot,
338 	    sc->sc_regs.cr_ioh, csc->cc_size);
339 
340 	return 0;
341 }
342