1 | /* $NetBSD: com_cardbus.c,v 1.30 2011/08/01 11:20:27 drochner 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.30 2011/08/01 11:20:27 drochner 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* |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | |