xref: /netbsd/sys/dev/cardbus/com_cardbus.c (revision f9581bce)
1 /* $NetBSD: com_cardbus.c,v 1.31 2018/12/08 17:46:13 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.31 2018/12/08 17:46:13 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/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 	pcireg_t		cc_base;
64 	bus_size_t		cc_size;
65 	pcireg_t		cc_csr;
66 	pcitag_t		cc_tag;
67 	pcireg_t		cc_reg;
68 	int			cc_type;
69 };
70 
71 #define DEVICET(CSC) ((CSC)->cc_com.sc_dev)
72 
73 static int com_cardbus_match (device_t, cfdata_t, void*);
74 static void com_cardbus_attach (device_t, device_t, void*);
75 static int com_cardbus_detach (device_t, int);
76 
77 static void com_cardbus_setup(struct com_cardbus_softc*);
78 static int com_cardbus_enable (struct com_softc*);
79 static void com_cardbus_disable(struct com_softc*);
80 
81 CFATTACH_DECL_NEW(com_cardbus, sizeof(struct com_cardbus_softc),
82     com_cardbus_match, com_cardbus_attach, com_cardbus_detach, NULL);
83 
84 static struct csdev {
85 	int		vendor;
86 	int		product;
87 	pcireg_t	reg;
88 	int		type;
89 } csdevs[] = {
90 	{ PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_MODEM56,
91 	  PCI_BAR0, PCI_MAPREG_TYPE_IO },
92 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MODEM56,
93 	  PCI_BAR0, PCI_MAPREG_TYPE_IO },
94 	{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656_M,
95 	  PCI_BAR0, PCI_MAPREG_TYPE_IO },
96 	{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656B_M,
97 	  PCI_BAR0, PCI_MAPREG_TYPE_IO },
98 	{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C656C_M,
99 	  PCI_BAR0, PCI_MAPREG_TYPE_IO },
100 };
101 
102 static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]);
103 
104 static struct csdev*
find_csdev(struct cardbus_attach_args * ca)105 find_csdev(struct cardbus_attach_args *ca)
106 {
107 	struct csdev *cp;
108 
109 	for(cp = csdevs; cp < csdevs + ncsdevs; cp++)
110 		if(cp->vendor == PCI_VENDOR(ca->ca_id) &&
111 		   cp->product == PCI_PRODUCT(ca->ca_id))
112 			return cp;
113 	return NULL;
114 }
115 
116 static int
com_cardbus_match(device_t parent,cfdata_t match,void * aux)117 com_cardbus_match(device_t parent, cfdata_t match, void *aux)
118 {
119 	struct cardbus_attach_args *ca = aux;
120 
121 	/* known devices are ok */
122 	if(find_csdev(ca) != NULL)
123 	    return 10;
124 
125 	/* as are serial devices with a known UART */
126 	if(ca->ca_cis.funcid == PCMCIA_FUNCTION_SERIAL &&
127 	   ca->ca_cis.funce.serial.uart_present != 0 &&
128 	   (ca->ca_cis.funce.serial.uart_type == 0 ||	/* 8250 */
129 	    ca->ca_cis.funce.serial.uart_type == 1 ||	/* 16450 */
130 	    ca->ca_cis.funce.serial.uart_type == 2))	/* 16550 */
131 	    return 1;
132 
133 	return 0;
134 }
135 
136 static int
gofigure(struct cardbus_attach_args * ca,struct com_cardbus_softc * csc)137 gofigure(struct cardbus_attach_args *ca, struct com_cardbus_softc *csc)
138 {
139 	int i, index = -1;
140 	pcireg_t cis_ptr;
141 	struct csdev *cp;
142 
143 	/* If this device is listed above, use the known values, */
144 	cp = find_csdev(ca);
145 	if(cp != NULL) {
146 		csc->cc_reg = cp->reg;
147 		csc->cc_type = cp->type;
148 		return 0;
149 	}
150 
151 	cis_ptr = Cardbus_conf_read(csc->cc_ct, csc->cc_tag, CARDBUS_CIS_REG);
152 
153 	/* otherwise try to deduce which BAR and type to use from CIS.  If
154 	   there is only one BAR, it must be the one we should use, if
155 	   there are more, we're out of luck.  */
156 	for(i = 0; i < 7; i++) {
157 		/* ignore zero sized BARs */
158 		if(ca->ca_cis.bar[i].size == 0)
159 			continue;
160 		/* ignore the CIS BAR */
161 		if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
162 		   CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
163 			continue;
164 		if(index != -1)
165 			goto multi_bar;
166 		index = i;
167 	}
168 	if(index == -1) {
169 		aprint_error(": couldn't find any base address tuple\n");
170 		return 1;
171 	}
172 	csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
173 	if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
174 		csc->cc_type = PCI_MAPREG_TYPE_MEM;
175 	else
176 		csc->cc_type = PCI_MAPREG_TYPE_IO;
177 	return 0;
178 
179   multi_bar:
180 	aprint_error(": there are more than one possible base\n");
181 
182 	aprint_error_dev(DEVICET(csc), "address for this device, "
183 	       "please report the following information\n");
184 	aprint_error_dev(DEVICET(csc), "vendor 0x%x product 0x%x\n",
185 	       PCI_VENDOR(ca->ca_id), PCI_PRODUCT(ca->ca_id));
186 	for(i = 0; i < 7; i++) {
187 		/* ignore zero sized BARs */
188 		if(ca->ca_cis.bar[i].size == 0)
189 			continue;
190 		/* ignore the CIS BAR */
191 		if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
192 		   CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
193 			continue;
194 		aprint_error_dev(DEVICET(csc),
195 		       "base address %x type %s size %x\n",
196 		       CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
197 		       (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
198 		       ca->ca_cis.bar[i].size);
199 	}
200 	return 1;
201 }
202 
203 static void
com_cardbus_attach(device_t parent,device_t self,void * aux)204 com_cardbus_attach (device_t parent, device_t self, void *aux)
205 {
206 	struct com_softc *sc = device_private(self);
207 	struct com_cardbus_softc *csc = device_private(self);
208 	struct cardbus_attach_args *ca = aux;
209 	bus_space_handle_t	ioh;
210 	bus_space_tag_t		iot;
211 
212 	sc->sc_dev = self;
213 	csc->cc_ct = ca->ca_ct;
214 	csc->cc_tag = ca->ca_tag;
215 
216 	if(gofigure(ca, csc) != 0)
217 		return;
218 
219 	if(Cardbus_mapreg_map(ca->ca_ct,
220 			      csc->cc_reg,
221 			      csc->cc_type,
222 			      0,
223 			      &iot,
224 			      &ioh,
225 			      &csc->cc_addr,
226 			      &csc->cc_size) != 0) {
227 		aprint_error("failed to map memory");
228 		return;
229 	}
230 
231 	com_init_regs(&sc->sc_regs, iot, ioh, csc->cc_addr);
232 
233 	csc->cc_base = csc->cc_addr;
234 	csc->cc_csr = PCI_COMMAND_MASTER_ENABLE;
235 	if(csc->cc_type == PCI_MAPREG_TYPE_IO) {
236 		csc->cc_base |= PCI_MAPREG_TYPE_IO;
237 		csc->cc_csr |= PCI_COMMAND_IO_ENABLE;
238 	} else {
239 		csc->cc_csr |= PCI_COMMAND_MEM_ENABLE;
240 	}
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 		aprint_normal(": %s %s\n", ca->ca_cis.cis1_info[0],
250 		    ca->ca_cis.cis1_info[1]);
251 		aprint_normal("%s", device_xname(DEVICET(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
com_cardbus_setup(struct com_cardbus_softc * csc)262 com_cardbus_setup(struct com_cardbus_softc *csc)
263 {
264         cardbus_devfunc_t ct = csc->cc_ct;
265 	pcireg_t reg;
266 
267 	Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
268 
269 	/* and the card itself */
270 	reg = Cardbus_conf_read(ct, csc->cc_tag, PCI_COMMAND_STATUS_REG);
271 	reg &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
272 	reg |= csc->cc_csr;
273 	Cardbus_conf_write(ct, csc->cc_tag, PCI_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(ct, csc->cc_tag, PCI_BHLC_REG);
280         if (PCI_LATTIMER(reg) < 0x20) {
281                 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
282                 reg |= (0x20 << PCI_LATTIMER_SHIFT);
283                 Cardbus_conf_write(ct, csc->cc_tag, PCI_BHLC_REG, reg);
284         }
285 }
286 
287 static int
com_cardbus_enable(struct com_softc * sc)288 com_cardbus_enable(struct com_softc *sc)
289 {
290 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
291 	cardbus_devfunc_t ct = csc->cc_ct;
292 
293 	Cardbus_function_enable(ct);
294 
295 	com_cardbus_setup(csc);
296 
297 	/* establish the interrupt. */
298 	csc->cc_ih = Cardbus_intr_establish(ct, IPL_SERIAL, comintr, sc);
299 	if (csc->cc_ih == NULL) {
300 		aprint_error_dev(DEVICET(csc),
301 		    "couldn't establish interrupt\n");
302 		return 1;
303 	}
304 
305 	return 0;
306 }
307 
308 static void
com_cardbus_disable(struct com_softc * sc)309 com_cardbus_disable(struct com_softc *sc)
310 {
311 	struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
312 	cardbus_devfunc_t ct = csc->cc_ct;
313 
314 	Cardbus_intr_disestablish(ct, csc->cc_ih);
315 	csc->cc_ih = NULL;
316 
317 	Cardbus_function_disable(ct);
318 }
319 
320 static int
com_cardbus_detach(device_t self,int flags)321 com_cardbus_detach(device_t self, int flags)
322 {
323 	struct com_cardbus_softc *csc = device_private(self);
324 	struct com_softc *sc = device_private(self);
325 	cardbus_devfunc_t ct = csc->cc_ct;
326 	int error;
327 
328 	if ((error = com_detach(self, flags)) != 0)
329 		return error;
330 
331 	if (csc->cc_ih != NULL)
332 		Cardbus_intr_disestablish(ct, csc->cc_ih);
333 
334 	Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_regs.cr_iot,
335 	    sc->sc_regs.cr_ioh, csc->cc_size);
336 
337 	return 0;
338 }
339