Line data Source code
1 : /* $OpenBSD: puc.c,v 1.26 2018/05/02 19:11:01 phessler Exp $ */
2 : /* $NetBSD: puc.c,v 1.3 1999/02/06 06:29:54 cgd Exp $ */
3 :
4 : /*
5 : * Copyright (c) 1996, 1998, 1999
6 : * Christopher G. Demetriou. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : * 1. Redistributions of source code must retain the above copyright
12 : * notice, this list of conditions and the following disclaimer.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : * 3. All advertising materials mentioning features or use of this software
17 : * must display the following acknowledgement:
18 : * This product includes software developed by Christopher G. Demetriou
19 : * for the NetBSD Project.
20 : * 4. The name of the author may not be used to endorse or promote products
21 : * derived from this software without specific prior written permission
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 : */
34 :
35 : /*
36 : * PCI "universal" communication card device driver, glues com, lpt,
37 : * and similar ports to PCI via bridge chip often much larger than
38 : * the devices being glued.
39 : *
40 : * Author: Christopher G. Demetriou, May 14, 1998 (derived from NetBSD
41 : * sys/dev/pci/pciide.c, revision 1.6).
42 : *
43 : * These devices could be (and some times are) described as
44 : * communications/{serial,parallel}, etc. devices with known
45 : * programming interfaces, but those programming interfaces (in
46 : * particular the BAR assignments for devices, etc.) in fact are not
47 : * particularly well defined.
48 : *
49 : * After I/we have seen more of these devices, it may be possible
50 : * to generalize some of these bits. In particular, devices which
51 : * describe themselves as communications/serial/16[45]50, and
52 : * communications/parallel/??? might be attached via direct
53 : * 'com' and 'lpt' attachments to pci.
54 : */
55 :
56 : #include <sys/param.h>
57 : #include <sys/systm.h>
58 : #include <sys/device.h>
59 : #include <sys/tty.h>
60 :
61 : #include <dev/pci/pcireg.h>
62 : #include <dev/pci/pcivar.h>
63 : #include <dev/pci/pucvar.h>
64 :
65 : #include <dev/ic/comreg.h>
66 : #include <dev/ic/comvar.h>
67 :
68 : struct puc_pci_softc {
69 : struct puc_softc sc_psc;
70 :
71 : pci_chipset_tag_t pc;
72 : pci_intr_handle_t ih;
73 : };
74 :
75 : int puc_pci_match(struct device *, void *, void *);
76 : void puc_pci_attach(struct device *, struct device *, void *);
77 : int puc_pci_detach(struct device *, int);
78 : const char *puc_pci_intr_string(struct puc_attach_args *);
79 : void *puc_pci_intr_establish(struct puc_attach_args *, int,
80 : int (*)(void *), void *, char *);
81 :
82 : struct cfattach puc_pci_ca = {
83 : sizeof(struct puc_pci_softc), puc_pci_match,
84 : puc_pci_attach, puc_pci_detach
85 : };
86 :
87 : struct cfdriver puc_cd = {
88 : NULL, "puc", DV_DULL
89 : };
90 :
91 : const char *puc_port_type_name(int);
92 :
93 : int
94 0 : puc_pci_match(struct device *parent, void *match, void *aux)
95 : {
96 0 : struct pci_attach_args *pa = aux;
97 : const struct puc_device_description *desc;
98 : pcireg_t bhlc, subsys;
99 :
100 0 : bhlc = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
101 0 : if (PCI_HDRTYPE_TYPE(bhlc) != 0)
102 0 : return (0);
103 :
104 0 : subsys = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
105 :
106 0 : desc = puc_find_description(PCI_VENDOR(pa->pa_id),
107 0 : PCI_PRODUCT(pa->pa_id), PCI_VENDOR(subsys), PCI_PRODUCT(subsys));
108 0 : if (desc != NULL)
109 0 : return (1);
110 :
111 0 : return (0);
112 0 : }
113 :
114 : const char *
115 0 : puc_pci_intr_string(struct puc_attach_args *paa)
116 : {
117 0 : struct puc_pci_softc *sc = paa->puc;
118 :
119 0 : return (pci_intr_string(sc->pc, sc->ih));
120 : }
121 :
122 : void *
123 0 : puc_pci_intr_establish(struct puc_attach_args *paa, int type,
124 : int (*func)(void *), void *arg, char *name)
125 : {
126 0 : struct puc_pci_softc *sc = paa->puc;
127 0 : struct puc_softc *psc = &sc->sc_psc;
128 :
129 0 : psc->sc_ports[paa->port].intrhand =
130 0 : pci_intr_establish(sc->pc, sc->ih, type, func, arg, name);
131 :
132 0 : return (psc->sc_ports[paa->port].intrhand);
133 : }
134 :
135 : void
136 0 : puc_pci_attach(struct device *parent, struct device *self, void *aux)
137 : {
138 0 : struct puc_pci_softc *psc = (struct puc_pci_softc *)self;
139 0 : struct puc_softc *sc = &psc->sc_psc;
140 0 : struct pci_attach_args *pa = aux;
141 0 : struct puc_attach_args paa;
142 : pcireg_t subsys;
143 : int i;
144 :
145 0 : subsys = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
146 0 : sc->sc_desc = puc_find_description(PCI_VENDOR(pa->pa_id),
147 0 : PCI_PRODUCT(pa->pa_id), PCI_VENDOR(subsys), PCI_PRODUCT(subsys));
148 :
149 0 : puc_print_ports(sc->sc_desc);
150 :
151 0 : for (i = 0; i < PUC_NBARS; i++) {
152 0 : pcireg_t type;
153 : int bar;
154 :
155 0 : sc->sc_bar_mappings[i].mapped = 0;
156 0 : bar = PCI_MAPREG_START + 4 * i;
157 0 : if (!pci_mapreg_probe(pa->pa_pc, pa->pa_tag, bar, &type))
158 0 : continue;
159 :
160 0 : sc->sc_bar_mappings[i].mapped = (pci_mapreg_map(pa, bar, type,
161 0 : 0, &sc->sc_bar_mappings[i].t, &sc->sc_bar_mappings[i].h,
162 0 : &sc->sc_bar_mappings[i].a, &sc->sc_bar_mappings[i].s, 0)
163 0 : == 0);
164 0 : if (sc->sc_bar_mappings[i].mapped)
165 0 : continue;
166 :
167 : /*
168 : * If a port on this card is used as serial console,
169 : * mapping the associated BAR will fail because the
170 : * bus space is already mapped. In that case, we try
171 : * to re-use the already existing mapping.
172 : * Unfortunately this means that if a BAR is used to
173 : * support multiple ports, only the first port will
174 : * work.
175 : */
176 0 : if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar, type,
177 0 : &sc->sc_bar_mappings[i].a, NULL, NULL) == 0 &&
178 0 : pa->pa_iot == comconsiot &&
179 0 : sc->sc_bar_mappings[i].a == comconsaddr) {
180 0 : sc->sc_bar_mappings[i].t = comconsiot;
181 0 : sc->sc_bar_mappings[i].h = comconsioh;
182 0 : sc->sc_bar_mappings[i].s = COM_NPORTS;
183 0 : sc->sc_bar_mappings[i].mapped = 1;
184 0 : continue;
185 : }
186 :
187 0 : printf("%s: couldn't map BAR at offset 0x%lx\n",
188 0 : sc->sc_dev.dv_xname, (long)bar);
189 0 : }
190 :
191 : /* Map interrupt. */
192 0 : psc->pc = pa->pa_pc;
193 0 : if (pci_intr_map(pa, &psc->ih)) {
194 0 : printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname);
195 0 : return;
196 : }
197 :
198 0 : paa.puc = sc;
199 0 : paa.intr_string = &puc_pci_intr_string;
200 0 : paa.intr_establish = &puc_pci_intr_establish;
201 :
202 0 : puc_common_attach(sc, &paa);
203 0 : }
204 :
205 : void
206 0 : puc_common_attach(struct puc_softc *sc, struct puc_attach_args *paa)
207 : {
208 0 : const struct puc_device_description *desc = sc->sc_desc;
209 : int i, bar;
210 :
211 : /* Configure each port. */
212 0 : for (i = 0; i < PUC_MAX_PORTS; i++) {
213 0 : if (desc->ports[i].type == 0) /* neither com or lpt */
214 : continue;
215 : /* make sure the base address register is mapped */
216 0 : bar = PUC_PORT_BAR_INDEX(desc->ports[i].bar);
217 0 : if (!sc->sc_bar_mappings[bar].mapped) {
218 0 : printf("%s: %s port uses unmapped BAR (0x%x)\n",
219 0 : sc->sc_dev.dv_xname,
220 0 : puc_port_type_name(desc->ports[i].type),
221 0 : desc->ports[i].bar);
222 0 : continue;
223 : }
224 :
225 : /* set up to configure the child device */
226 0 : paa->port = i;
227 0 : paa->a = sc->sc_bar_mappings[bar].a;
228 0 : paa->t = sc->sc_bar_mappings[bar].t;
229 :
230 0 : paa->type = desc->ports[i].type;
231 :
232 0 : if (desc->ports[i].offset >= sc->sc_bar_mappings[bar].s ||
233 0 : bus_space_subregion(sc->sc_bar_mappings[bar].t,
234 0 : sc->sc_bar_mappings[bar].h, desc->ports[i].offset,
235 0 : sc->sc_bar_mappings[bar].s - desc->ports[i].offset,
236 0 : &paa->h)) {
237 0 : printf("%s: couldn't get subregion for port %d\n",
238 0 : sc->sc_dev.dv_xname, i);
239 0 : continue;
240 : }
241 :
242 : #if 0
243 : if (autoconf_verbose)
244 : printf("%s: port %d: %s @ (index %d) 0x%x "
245 : "(0x%lx, 0x%lx)\n", sc->sc_dev.dv_xname, paa->port,
246 : puc_port_type_name(paa->type), bar, (int)paa->a,
247 : (long)paa->t, (long)paa->h);
248 : #endif
249 :
250 : /* and configure it */
251 0 : sc->sc_ports[i].dev = config_found_sm(&sc->sc_dev, paa,
252 : puc_print, puc_submatch);
253 0 : }
254 0 : }
255 :
256 : int
257 0 : puc_pci_detach(struct device *self, int flags)
258 : {
259 0 : struct puc_pci_softc *sc = (struct puc_pci_softc *)self;
260 0 : struct puc_softc *psc = &sc->sc_psc;
261 : int i, rv;
262 :
263 0 : for (i = PUC_MAX_PORTS; i--; ) {
264 0 : if (psc->sc_ports[i].intrhand)
265 0 : pci_intr_disestablish(sc->pc,
266 : psc->sc_ports[i].intrhand);
267 0 : if (psc->sc_ports[i].dev)
268 0 : if ((rv = config_detach(psc->sc_ports[i].dev, flags)))
269 0 : return (rv);
270 : }
271 :
272 0 : for (i = PUC_NBARS; i--; )
273 0 : if (psc->sc_bar_mappings[i].mapped)
274 0 : bus_space_unmap(psc->sc_bar_mappings[i].t,
275 0 : psc->sc_bar_mappings[i].h,
276 0 : psc->sc_bar_mappings[i].s);
277 :
278 0 : return (0);
279 0 : }
280 :
281 : int
282 0 : puc_print(void *aux, const char *pnp)
283 : {
284 0 : struct puc_attach_args *paa = aux;
285 :
286 0 : if (pnp)
287 0 : printf("%s at %s", puc_port_type_name(paa->type), pnp);
288 0 : printf(" port %d", paa->port);
289 0 : return (UNCONF);
290 : }
291 :
292 : int
293 0 : puc_submatch(struct device *parent, void *vcf, void *aux)
294 : {
295 0 : struct cfdata *cf = (struct cfdata *)vcf;
296 0 : struct puc_attach_args *aa = aux;
297 :
298 0 : if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != aa->port)
299 0 : return 0;
300 0 : return ((*cf->cf_attach->ca_match)(parent, cf, aux));
301 0 : }
302 :
303 : const struct puc_device_description *
304 0 : puc_find_description(u_int16_t vend, u_int16_t prod,
305 : u_int16_t svend, u_int16_t sprod)
306 : {
307 : int i;
308 :
309 0 : for (i = 0; i < puc_ndevs; i++)
310 0 : if ((vend & puc_devs[i].rmask[0]) == puc_devs[i].rval[0] &&
311 0 : (prod & puc_devs[i].rmask[1]) == puc_devs[i].rval[1] &&
312 0 : (svend & puc_devs[i].rmask[2]) == puc_devs[i].rval[2] &&
313 0 : (sprod & puc_devs[i].rmask[3]) == puc_devs[i].rval[3])
314 0 : return (&puc_devs[i]);
315 :
316 0 : return (NULL);
317 0 : }
318 :
319 : const char *
320 0 : puc_port_type_name(int type)
321 : {
322 :
323 0 : if (PUC_IS_COM(type))
324 0 : return "com";
325 0 : if (PUC_IS_LPT(type))
326 0 : return "lpt";
327 0 : return (NULL);
328 0 : }
329 :
330 : void
331 0 : puc_print_ports(const struct puc_device_description *desc)
332 : {
333 : int i, ncom, nlpt;
334 :
335 0 : printf(": ports: ");
336 0 : for (i = ncom = nlpt = 0; i < PUC_MAX_PORTS; i++) {
337 0 : if (PUC_IS_COM(desc->ports[i].type))
338 0 : ncom++;
339 0 : else if (PUC_IS_LPT(desc->ports[i].type))
340 0 : nlpt++;
341 : }
342 0 : if (ncom)
343 0 : printf("%d com", ncom);
344 0 : if (nlpt) {
345 0 : if (ncom)
346 0 : printf(", ");
347 0 : printf("%d lpt", nlpt);
348 0 : }
349 0 : printf("\n");
350 0 : }
|