com_cardbus.c revision 1.2 1 /* $NetBSD: com_cardbus.c,v 1.2 2000/04/13 16:17:55 joda 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 /* This is a driver for CardBus based serial devices. It is less
36 generic than it could be, but it keeps the complexity down. So far
37 it assumes that anything that reports itself as a `serial' device
38 is infact a 16x50 or 8250, which is not necessarily true (in
39 practice this shouldn't be a problem). It also does not handle
40 devices in the `multiport serial' or `modem' sub-classes, I've
41 never seen any of thise, so I don't know what they might look like.
42
43 If the CardBus device only has one BAR (that is not also the CIS
44 BAR) listed in the CIS, it is assumed to be the one to use. For
45 devices with more than one BAR, the list of known devies has to be
46 updated below. */
47
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/tty.h>
51 #include <sys/device.h>
52
53 #include <dev/cardbus/cardbusvar.h>
54 #include <dev/cardbus/cardbusdevs.h>
55
56 #include <dev/ic/comreg.h>
57 #include <dev/ic/comvar.h>
58
59 struct com_cardbus_softc {
60 struct com_softc cc_com;
61 void *cc_ih;
62 cardbus_devfunc_t cc_ct;
63 bus_addr_t cc_addr;
64 cardbusreg_t cc_base;
65 bus_size_t cc_size;
66 cardbusreg_t cc_csr;
67 int cc_cben;
68 cardbustag_t cc_tag;
69 cardbusreg_t cc_reg;
70 int cc_type;
71 };
72
73 #define DEVNAME(CSC) ((CSC)->cc_com.sc_dev.dv_xname)
74
75 static int com_cardbus_match (struct device*, struct cfdata*, void*);
76 static void com_cardbus_attach (struct device*, struct device*, void*);
77 static int com_cardbus_detach (struct device*, int);
78
79 static void com_cardbus_setup(struct com_cardbus_softc*);
80 static int com_cardbus_enable (struct com_softc*);
81 static void com_cardbus_disable (struct com_softc*);
82
83 struct cfattach com_cardbus_ca = {
84 sizeof(struct com_cardbus_softc), com_cardbus_match,
85 com_cardbus_attach, com_cardbus_detach, com_activate
86 };
87
88 static struct csdev {
89 int vendor;
90 int product;
91 cardbusreg_t reg;
92 int type;
93 } csdevs[] = {
94 /* example entry */
95 { CARDBUS_VENDOR_XIRCOM, CARDBUS_PRODUCT_XIRCOM_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 if(CARDBUS_CLASS(ca->ca_class) == CARDBUS_CLASS_COMMUNICATIONS &&
119 CARDBUS_SUBCLASS(ca->ca_class) == CARDBUS_SUBCLASS_COMMUNICATIONS_SERIAL) {
120 if(find_csdev(ca) != NULL)
121 return 10;
122 return 1;
123 }
124 return 0;
125 }
126
127 static int
128 gofigure(struct cardbus_attach_args *ca, struct com_cardbus_softc *csc)
129 {
130 int i, index = -1;
131 cardbusreg_t cis_ptr;
132 struct csdev *cp;
133
134 /* If this device is listed above, use the known values, */
135 cp = find_csdev(ca);
136 if(cp != NULL) {
137 csc->cc_reg = cp->reg;
138 csc->cc_type = cp->type;
139 return 0;
140 }
141
142 cis_ptr = Cardbus_conf_read(csc->cc_ct, csc->cc_tag, CARDBUS_CIS_REG);
143
144 /* otherwise try to deduce which BAR and type to use from CIS. If
145 there is only one BAR, it must be the one we should use, if
146 there are more, we're out of luck. */
147 for(i = 0; i < 7; i++) {
148 /* ignore zero sized BARs */
149 if(ca->ca_cis.bar[i].size == 0)
150 continue;
151 /* ignore the CIS BAR */
152 if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
153 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
154 continue;
155 if(index != -1)
156 goto multi_bar;
157 index = i;
158 }
159 if(index == -1) {
160 printf(": couldn't find any base address tuple\n");
161 return 1;
162 }
163 csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
164 if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
165 csc->cc_type = CARDBUS_MAPREG_TYPE_MEM;
166 else
167 csc->cc_type = CARDBUS_MAPREG_TYPE_IO;
168 return 0;
169
170 multi_bar:
171 printf(": there are more than one possible base\n");
172
173 printf("%s: address for this device, "
174 "please report the following information\n",
175 DEVNAME(csc));
176 printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc),
177 CARDBUS_VENDOR(ca->ca_id), CARDBUS_PRODUCT(ca->ca_id));
178 for(i = 0; i < 7; i++) {
179 /* ignore zero sized BARs */
180 if(ca->ca_cis.bar[i].size == 0)
181 continue;
182 /* ignore the CIS BAR */
183 if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
184 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
185 continue;
186 printf("%s: base address %x type %s size %x\n",
187 DEVNAME(csc),
188 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
189 (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
190 ca->ca_cis.bar[i].size);
191 }
192 return 1;
193 }
194
195 static void
196 com_cardbus_attach (struct device *parent, struct device *self, void *aux)
197 {
198 struct com_softc *sc = (struct com_softc*)self;
199 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self;
200 struct cardbus_attach_args *ca = aux;
201
202 csc->cc_ct = ca->ca_ct;
203 csc->cc_tag = Cardbus_make_tag(csc->cc_ct);
204
205 if(gofigure(ca, csc) != 0)
206 return;
207
208 if(Cardbus_mapreg_map(ca->ca_ct,
209 csc->cc_reg,
210 csc->cc_type,
211 0,
212 &sc->sc_iot,
213 &sc->sc_ioh,
214 &csc->cc_addr,
215 &csc->cc_size) != 0) {
216 printf("failed to map memory");
217 return;
218 }
219
220 csc->cc_base = csc->cc_addr;
221 csc->cc_csr = CARDBUS_COMMAND_MASTER_ENABLE;
222 if(csc->cc_type == CARDBUS_MAPREG_TYPE_IO) {
223 csc->cc_base |= CARDBUS_MAPREG_TYPE_IO;
224 csc->cc_csr |= CARDBUS_COMMAND_IO_ENABLE;
225 csc->cc_cben = CARDBUS_IO_ENABLE;
226 } else {
227 csc->cc_csr |= CARDBUS_COMMAND_MEM_ENABLE;
228 csc->cc_cben = CARDBUS_MEM_ENABLE;
229 }
230 sc->sc_iobase = csc->cc_addr;
231
232 sc->sc_frequency = COM_FREQ;
233
234 sc->enable = com_cardbus_enable;
235 sc->disable = com_cardbus_disable;
236 sc->enabled = 0;
237
238 if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) {
239 printf(": %s %s\n", ca->ca_cis.cis1_info[0],
240 ca->ca_cis.cis1_info[1]);
241 printf("%s", DEVNAME(csc));
242 }
243
244 com_cardbus_setup(csc);
245
246 com_attach_subr(sc);
247
248 Cardbus_function_disable(csc->cc_ct);
249 }
250
251 static void
252 com_cardbus_setup(struct com_cardbus_softc *csc)
253 {
254 cardbus_devfunc_t ct = csc->cc_ct;
255 cardbus_chipset_tag_t cc = ct->ct_cc;
256 cardbus_function_tag_t cf = ct->ct_cf;
257 cardbusreg_t reg;
258
259 Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
260
261 /* enable accesses on cardbus bridge */
262 (*cf->cardbus_ctrl)(cc, csc->cc_cben);
263 (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
264
265 /* and the card itself */
266 reg = Cardbus_conf_read(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG);
267 reg &= ~(CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE);
268 reg |= csc->cc_csr;
269 Cardbus_conf_write(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG, reg);
270
271 /*
272 * Make sure the latency timer is set to some reasonable
273 * value.
274 */
275 reg = cardbus_conf_read(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG);
276 if (CARDBUS_LATTIMER(reg) < 0x20) {
277 reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT);
278 reg |= (0x20 << CARDBUS_LATTIMER_SHIFT);
279 cardbus_conf_write(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG, reg);
280 }
281 }
282
283 static int
284 com_cardbus_enable(struct com_softc *sc)
285 {
286 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
287 struct cardbus_softc *psc =
288 (struct cardbus_softc *)sc->sc_dev.dv_parent;
289 cardbus_chipset_tag_t cc = psc->sc_cc;
290 cardbus_function_tag_t cf = psc->sc_cf;
291
292 Cardbus_function_enable(csc->cc_ct);
293
294 com_cardbus_setup(csc);
295
296 /* establish the interrupt. */
297 csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
298 IPL_SERIAL, comintr, sc);
299 if (csc->cc_ih == NULL) {
300 printf("%s: couldn't establish interrupt\n",
301 DEVNAME(csc));
302 return 1;
303 }
304
305 printf("%s: interrupting at irq %d\n",
306 DEVNAME(csc), psc->sc_intrline);
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 struct cardbus_softc *psc =
316 (struct cardbus_softc *)sc->sc_dev.dv_parent;
317 cardbus_chipset_tag_t cc = psc->sc_cc;
318 cardbus_function_tag_t cf = psc->sc_cf;
319
320 cardbus_intr_disestablish(cc, cf, csc->cc_ih);
321 Cardbus_function_disable(csc->cc_ct);
322 }
323
324 static int
325 com_cardbus_detach(struct device *self, int flags)
326 {
327 struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self;
328 struct com_softc *sc = (struct com_softc *) self;
329 struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent;
330 int error;
331
332 if ((error = com_detach(self, flags)) != 0)
333 return error;
334
335 cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
336
337 Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh,
338 csc->cc_size);
339
340 return 0;
341 }
342