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