com_cardbus.c revision 1.1 1 /* $NetBSD: com_cardbus.c,v 1.1 2000/04/13 11:17:53 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
249 static void
250 com_cardbus_setup(struct com_cardbus_softc *csc)
251 {
252 cardbus_devfunc_t ct = csc->cc_ct;
253 cardbus_chipset_tag_t cc = ct->ct_cc;
254 cardbus_function_tag_t cf = ct->ct_cf;
255 cardbusreg_t reg;
256
257 Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
258
259 /* enable accesses on cardbus bridge */
260 (*cf->cardbus_ctrl)(cc, csc->cc_cben);
261 (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
262
263 /* and the card itself */
264 reg = Cardbus_conf_read(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG);
265 reg &= ~(CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE);
266 reg |= csc->cc_csr;
267 Cardbus_conf_write(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG, reg);
268
269 /*
270 * Make sure the latency timer is set to some reasonable
271 * value.
272 */
273 reg = cardbus_conf_read(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG);
274 if (CARDBUS_LATTIMER(reg) < 0x20) {
275 reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT);
276 reg |= (0x20 << CARDBUS_LATTIMER_SHIFT);
277 cardbus_conf_write(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG, reg);
278 }
279 }
280
281 static int
282 com_cardbus_enable(struct com_softc *sc)
283 {
284 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
285 struct cardbus_softc *psc =
286 (struct cardbus_softc *)sc->sc_dev.dv_parent;
287 cardbus_chipset_tag_t cc = psc->sc_cc;
288 cardbus_function_tag_t cf = psc->sc_cf;
289
290 Cardbus_function_enable(csc->cc_ct);
291
292 com_cardbus_setup(csc);
293
294 /* establish the interrupt. */
295 csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
296 IPL_SERIAL, comintr, sc);
297 if (csc->cc_ih == NULL) {
298 printf("%s: couldn't establish interrupt\n",
299 DEVNAME(csc));
300 return 1;
301 }
302
303 printf("%s: interrupting at irq %d\n",
304 DEVNAME(csc), psc->sc_intrline);
305
306 return 0;
307 }
308
309 static void
310 com_cardbus_disable(struct com_softc *sc)
311 {
312 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
313 struct cardbus_softc *psc =
314 (struct cardbus_softc *)sc->sc_dev.dv_parent;
315 cardbus_chipset_tag_t cc = psc->sc_cc;
316 cardbus_function_tag_t cf = psc->sc_cf;
317
318 cardbus_intr_disestablish(cc, cf, csc->cc_ih);
319 Cardbus_function_disable(csc->cc_ct);
320 }
321
322 static int
323 com_cardbus_detach(struct device *self, int flags)
324 {
325 struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self;
326 struct com_softc *sc = (struct com_softc *) self;
327 struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent;
328 int error;
329
330 if ((error = com_detach(self, flags)) != 0)
331 return error;
332
333 cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
334
335 Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh,
336 csc->cc_size);
337
338 return 0;
339 }
340