xref: /netbsd/sys/dev/cardbus/com_cardbus.c (revision bf9ec67e)
1 /* $NetBSD: com_cardbus.c,v 1.6 2001/11/13 12:51:12 lukem 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.6 2001/11/13 12:51:12 lukem 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/cardbus/cardbusdevs.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 struct cfattach com_cardbus_ca = {
83 	sizeof(struct com_cardbus_softc), com_cardbus_match,
84 	com_cardbus_attach, com_cardbus_detach, com_activate
85 };
86 
87 static struct csdev {
88 	int		vendor;
89 	int		product;
90 	cardbusreg_t	reg;
91 	int		type;
92 } csdevs[] = {
93 	{ CARDBUS_VENDOR_XIRCOM, CARDBUS_PRODUCT_XIRCOM_MODEM56,
94 	  CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
95 	{ CARDBUS_VENDOR_INTEL, CARDBUS_PRODUCT_INTEL_MODEM56,
96 	  CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO }
97 };
98 
99 static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]);
100 
101 static struct csdev*
102 find_csdev(struct cardbus_attach_args *ca)
103 {
104 	struct csdev *cp;
105 
106 	for(cp = csdevs; cp < csdevs + ncsdevs; cp++)
107 		if(cp->vendor == CARDBUS_VENDOR(ca->ca_id) &&
108 		   cp->product == CARDBUS_PRODUCT(ca->ca_id))
109 			return cp;
110 	return NULL;
111 }
112 
113 static int
114 com_cardbus_match(struct device *parent, struct cfdata *match, void *aux)
115 {
116 	struct cardbus_attach_args *ca = aux;
117 
118 	/* known devices are ok */
119 	if(find_csdev(ca) != NULL)
120 	    return 10;
121 
122 	/* as are serial devices with a known UART */
123 	if(ca->ca_cis.funcid == PCMCIA_FUNCTION_SERIAL &&
124 	   ca->ca_cis.funce.serial.uart_present != 0 &&
125 	   (ca->ca_cis.funce.serial.uart_type == 0 ||	/* 8250 */
126 	    ca->ca_cis.funce.serial.uart_type == 1 ||	/* 16450 */
127 	    ca->ca_cis.funce.serial.uart_type == 2))	/* 16550 */
128 	    return 1;
129 
130 	return 0;
131 }
132 
133 static int
134 gofigure(struct cardbus_attach_args *ca, struct com_cardbus_softc *csc)
135 {
136 	int i, index = -1;
137 	cardbusreg_t cis_ptr;
138 	struct csdev *cp;
139 
140 	/* If this device is listed above, use the known values, */
141 	cp = find_csdev(ca);
142 	if(cp != NULL) {
143 		csc->cc_reg = cp->reg;
144 		csc->cc_type = cp->type;
145 		return 0;
146 	}
147 
148 	cis_ptr = Cardbus_conf_read(csc->cc_ct, csc->cc_tag, CARDBUS_CIS_REG);
149 
150 	/* otherwise try to deduce which BAR and type to use from CIS.  If
151 	   there is only one BAR, it must be the one we should use, if
152 	   there are more, we're out of luck.  */
153 	for(i = 0; i < 7; i++) {
154 		/* ignore zero sized BARs */
155 		if(ca->ca_cis.bar[i].size == 0)
156 			continue;
157 		/* ignore the CIS BAR */
158 		if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
159 		   CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
160 			continue;
161 		if(index != -1)
162 			goto multi_bar;
163 		index = i;
164 	}
165 	if(index == -1) {
166 		printf(": couldn't find any base address tuple\n");
167 		return 1;
168 	}
169 	csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
170 	if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
171 		csc->cc_type = CARDBUS_MAPREG_TYPE_MEM;
172 	else
173 		csc->cc_type = CARDBUS_MAPREG_TYPE_IO;
174 	return 0;
175 
176   multi_bar:
177 	printf(": there are more than one possible base\n");
178 
179 	printf("%s: address for this device, "
180 	       "please report the following information\n",
181 	       DEVNAME(csc));
182 	printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc),
183 	       CARDBUS_VENDOR(ca->ca_id), CARDBUS_PRODUCT(ca->ca_id));
184 	for(i = 0; i < 7; i++) {
185 		/* ignore zero sized BARs */
186 		if(ca->ca_cis.bar[i].size == 0)
187 			continue;
188 		/* ignore the CIS BAR */
189 		if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
190 		   CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
191 			continue;
192 		printf("%s: base address %x type %s size %x\n",
193 		       DEVNAME(csc),
194 		       CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
195 		       (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
196 		       ca->ca_cis.bar[i].size);
197 	}
198 	return 1;
199 }
200 
201 static void
202 com_cardbus_attach (struct device *parent, struct device *self, void *aux)
203 {
204 	struct com_softc *sc = (struct com_softc*)self;
205 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self;
206 	struct cardbus_attach_args *ca = aux;
207 
208 	csc->cc_ct = ca->ca_ct;
209 	csc->cc_tag = Cardbus_make_tag(csc->cc_ct);
210 
211 	if(gofigure(ca, csc) != 0)
212 		return;
213 
214 	if(Cardbus_mapreg_map(ca->ca_ct,
215 			      csc->cc_reg,
216 			      csc->cc_type,
217 			      0,
218 			      &sc->sc_iot,
219 			      &sc->sc_ioh,
220 			      &csc->cc_addr,
221 			      &csc->cc_size) != 0) {
222 		printf("failed to map memory");
223 		return;
224 	}
225 
226 	csc->cc_base = csc->cc_addr;
227 	csc->cc_csr = CARDBUS_COMMAND_MASTER_ENABLE;
228 	if(csc->cc_type == CARDBUS_MAPREG_TYPE_IO) {
229 		csc->cc_base |= CARDBUS_MAPREG_TYPE_IO;
230 		csc->cc_csr |= CARDBUS_COMMAND_IO_ENABLE;
231 		csc->cc_cben = CARDBUS_IO_ENABLE;
232 	} else {
233 		csc->cc_csr |= CARDBUS_COMMAND_MEM_ENABLE;
234 		csc->cc_cben = CARDBUS_MEM_ENABLE;
235 	}
236 	sc->sc_iobase = csc->cc_addr;
237 
238 	sc->sc_frequency = COM_FREQ;
239 
240 	sc->enable = com_cardbus_enable;
241 	sc->disable = com_cardbus_disable;
242 	sc->enabled = 0;
243 
244 	if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) {
245 		printf(": %s %s\n", ca->ca_cis.cis1_info[0],
246 		    ca->ca_cis.cis1_info[1]);
247 		printf("%s", DEVNAME(csc));
248 	}
249 
250 	com_cardbus_setup(csc);
251 
252 	com_attach_subr(sc);
253 
254 	Cardbus_function_disable(csc->cc_ct);
255 }
256 
257 static void
258 com_cardbus_setup(struct com_cardbus_softc *csc)
259 {
260         cardbus_devfunc_t ct = csc->cc_ct;
261         cardbus_chipset_tag_t cc = ct->ct_cc;
262         cardbus_function_tag_t cf = ct->ct_cf;
263 	cardbusreg_t reg;
264 
265 	Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
266 
267 	/* enable accesses on cardbus bridge */
268 	(*cf->cardbus_ctrl)(cc, csc->cc_cben);
269 	(*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
270 
271 	/* and the card itself */
272 	reg = Cardbus_conf_read(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG);
273 	reg &= ~(CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE);
274 	reg |= csc->cc_csr;
275 	Cardbus_conf_write(ct, csc->cc_tag, CARDBUS_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(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG);
282         if (CARDBUS_LATTIMER(reg) < 0x20) {
283                 reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT);
284                 reg |= (0x20 << CARDBUS_LATTIMER_SHIFT);
285                 cardbus_conf_write(cc, cf, csc->cc_tag, CARDBUS_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 	struct cardbus_softc *psc =
294 		(struct cardbus_softc *)sc->sc_dev.dv_parent;
295 	cardbus_chipset_tag_t cc = psc->sc_cc;
296 	cardbus_function_tag_t cf = psc->sc_cf;
297 
298 	Cardbus_function_enable(csc->cc_ct);
299 
300 	com_cardbus_setup(csc);
301 
302 	/* establish the interrupt. */
303 	csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
304 					    IPL_SERIAL, comintr, sc);
305 	if (csc->cc_ih == NULL) {
306 		printf("%s: couldn't establish interrupt\n",
307 		       DEVNAME(csc));
308 		return 1;
309 	}
310 
311 	printf("%s: interrupting at irq %d\n",
312 	       DEVNAME(csc), psc->sc_intrline);
313 
314 	return 0;
315 }
316 
317 static void
318 com_cardbus_disable(struct com_softc *sc)
319 {
320 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
321 	struct cardbus_softc *psc =
322 		(struct cardbus_softc *)sc->sc_dev.dv_parent;
323 	cardbus_chipset_tag_t cc = psc->sc_cc;
324 	cardbus_function_tag_t cf = psc->sc_cf;
325 
326 	cardbus_intr_disestablish(cc, cf, csc->cc_ih);
327 	Cardbus_function_disable(csc->cc_ct);
328 }
329 
330 static int
331 com_cardbus_detach(struct device *self, int flags)
332 {
333 	struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self;
334 	struct com_softc *sc = (struct com_softc *) self;
335 	struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent;
336 	int error;
337 
338 	if ((error = com_detach(self, flags)) != 0)
339 		return error;
340 
341 	cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
342 
343 	Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh,
344 			     csc->cc_size);
345 
346 	return 0;
347 }
348