1 | /* $NetBSD: vga_pci.c,v 1.55 2016/07/07 06:55:41 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1995, 1996 Carnegie-Mellon University. |
5 | * All rights reserved. |
6 | * |
7 | * Author: Chris G. Demetriou |
8 | * |
9 | * Permission to use, copy, modify and distribute this software and |
10 | * its documentation is hereby granted, provided that both the copyright |
11 | * notice and this permission notice appear in all copies of the |
12 | * software, derivative works or modified versions, and any portions |
13 | * thereof, and that both notices appear in supporting documentation. |
14 | * |
15 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" |
16 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND |
17 | * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. |
18 | * |
19 | * Carnegie Mellon requests users of this software to return to |
20 | * |
21 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU |
22 | * School of Computer Science |
23 | * Carnegie Mellon University |
24 | * Pittsburgh PA 15213-3890 |
25 | * |
26 | * any improvements or extensions that they make and grant Carnegie the |
27 | * rights to redistribute these changes. |
28 | */ |
29 | |
30 | #include <sys/cdefs.h> |
31 | __KERNEL_RCSID(0, "$NetBSD: vga_pci.c,v 1.55 2016/07/07 06:55:41 msaitoh Exp $" ); |
32 | |
33 | #include <sys/param.h> |
34 | #include <sys/systm.h> |
35 | #include <sys/kernel.h> |
36 | #include <sys/device.h> |
37 | #include <sys/malloc.h> |
38 | |
39 | #include <dev/pci/pcireg.h> |
40 | #include <dev/pci/pcivar.h> |
41 | #include <dev/pci/pcidevs.h> |
42 | #include <dev/pci/pciio.h> |
43 | |
44 | #include <dev/ic/mc6845reg.h> |
45 | #include <dev/ic/pcdisplayvar.h> |
46 | #include <dev/ic/vgareg.h> |
47 | #include <dev/ic/vgavar.h> |
48 | #include <dev/pci/vga_pcivar.h> |
49 | |
50 | #include <dev/isa/isareg.h> /* For legacy VGA address ranges */ |
51 | |
52 | #include <dev/wscons/wsconsio.h> |
53 | #include <dev/wscons/wsdisplayvar.h> |
54 | #include <dev/pci/wsdisplay_pci.h> |
55 | |
56 | #include "opt_vga.h" |
57 | |
58 | #ifdef VGA_POST |
59 | # if defined(__i386__) || defined(__amd64__) |
60 | # include "acpica.h" |
61 | # endif |
62 | #include <x86/vga_post.h> |
63 | #endif |
64 | |
65 | #define NBARS 6 /* number of PCI BARs */ |
66 | |
67 | struct vga_bar { |
68 | bus_addr_t vb_base; |
69 | bus_size_t vb_size; |
70 | pcireg_t vb_type; |
71 | int vb_flags; |
72 | }; |
73 | |
74 | struct vga_pci_softc { |
75 | struct vga_softc sc_vga; |
76 | |
77 | pci_chipset_tag_t sc_pc; |
78 | pcitag_t sc_pcitag; |
79 | |
80 | struct vga_bar sc_bars[NBARS]; |
81 | struct vga_bar sc_rom; |
82 | |
83 | #ifdef VGA_POST |
84 | struct vga_post *sc_posth; |
85 | #endif |
86 | |
87 | struct pci_attach_args sc_paa; |
88 | }; |
89 | |
90 | static int vga_pci_match(device_t, cfdata_t, void *); |
91 | static void vga_pci_attach(device_t, device_t, void *); |
92 | static int vga_pci_rescan(device_t, const char *, const int *); |
93 | static int vga_pci_lookup_quirks(struct pci_attach_args *); |
94 | static bool vga_pci_resume(device_t dv, const pmf_qual_t *); |
95 | |
96 | CFATTACH_DECL2_NEW(vga_pci, sizeof(struct vga_pci_softc), |
97 | vga_pci_match, vga_pci_attach, NULL, NULL, vga_pci_rescan, NULL); |
98 | |
99 | static int vga_pci_ioctl(void *, u_long, void *, int, struct lwp *); |
100 | static paddr_t vga_pci_mmap(void *, off_t, int); |
101 | |
102 | static const struct vga_funcs vga_pci_funcs = { |
103 | vga_pci_ioctl, |
104 | vga_pci_mmap, |
105 | }; |
106 | |
107 | static const struct { |
108 | int id; |
109 | int quirks; |
110 | } vga_pci_quirks[] = { |
111 | {PCI_ID_CODE(PCI_VENDOR_SILMOTION, PCI_PRODUCT_SILMOTION_SM712), |
112 | VGA_QUIRK_NOFASTSCROLL}, |
113 | {PCI_ID_CODE(PCI_VENDOR_CYRIX, PCI_PRODUCT_CYRIX_CX5530_VIDEO), |
114 | VGA_QUIRK_NOFASTSCROLL}, |
115 | }; |
116 | |
117 | static const struct { |
118 | int vid; |
119 | int quirks; |
120 | } vga_pci_vquirks[] = { |
121 | {PCI_VENDOR_ATI, VGA_QUIRK_ONEFONT}, |
122 | }; |
123 | |
124 | static int |
125 | vga_pci_lookup_quirks(struct pci_attach_args *pa) |
126 | { |
127 | int i; |
128 | |
129 | for (i = 0; i < sizeof(vga_pci_quirks) / sizeof (vga_pci_quirks[0]); |
130 | i++) { |
131 | if (vga_pci_quirks[i].id == pa->pa_id) |
132 | return (vga_pci_quirks[i].quirks); |
133 | } |
134 | for (i = 0; i < sizeof(vga_pci_vquirks) / sizeof (vga_pci_vquirks[0]); |
135 | i++) { |
136 | if (vga_pci_vquirks[i].vid == PCI_VENDOR(pa->pa_id)) |
137 | return (vga_pci_vquirks[i].quirks); |
138 | } |
139 | return (0); |
140 | } |
141 | |
142 | static int |
143 | vga_pci_match(device_t parent, cfdata_t match, void *aux) |
144 | { |
145 | struct pci_attach_args *pa = aux; |
146 | int potential; |
147 | |
148 | potential = 0; |
149 | |
150 | /* |
151 | * If it's prehistoric/vga or display/vga, we might match. |
152 | * For the console device, this is just a sanity check. |
153 | */ |
154 | if (PCI_CLASS(pa->pa_class) == PCI_CLASS_PREHISTORIC && |
155 | PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_PREHISTORIC_VGA) |
156 | potential = 1; |
157 | if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && |
158 | PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA) |
159 | potential = 1; |
160 | |
161 | if (!potential) |
162 | return (0); |
163 | |
164 | /* check whether it is disabled by firmware */ |
165 | if ((pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG) |
166 | & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)) |
167 | != (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)) |
168 | return (0); |
169 | |
170 | /* If it's the console, we have a winner! */ |
171 | if (vga_is_console(pa->pa_iot, WSDISPLAY_TYPE_PCIVGA)) |
172 | return (1); |
173 | |
174 | /* |
175 | * If we might match, make sure that the card actually looks OK. |
176 | */ |
177 | if (!vga_common_probe(pa->pa_iot, pa->pa_memt)) |
178 | return (0); |
179 | |
180 | return (1); |
181 | } |
182 | |
183 | static void |
184 | vga_pci_attach(device_t parent, device_t self, void *aux) |
185 | { |
186 | struct vga_pci_softc *psc = device_private(self); |
187 | struct vga_softc *sc = &psc->sc_vga; |
188 | struct pci_attach_args *pa = aux; |
189 | int bar, reg; |
190 | |
191 | sc->sc_dev = self; |
192 | psc->sc_pc = pa->pa_pc; |
193 | psc->sc_pcitag = pa->pa_tag; |
194 | psc->sc_paa = *pa; |
195 | |
196 | pci_aprint_devinfo(pa, NULL); |
197 | |
198 | /* |
199 | * Gather info about all the BARs. These are used to allow |
200 | * the X server to map the VGA device. |
201 | */ |
202 | for (bar = 0; bar < NBARS; bar++) { |
203 | reg = PCI_MAPREG_START + (bar * 4); |
204 | if (!pci_mapreg_probe(psc->sc_pc, psc->sc_pcitag, reg, |
205 | &psc->sc_bars[bar].vb_type)) { |
206 | /* there is no valid mapping register */ |
207 | continue; |
208 | } |
209 | if (PCI_MAPREG_TYPE(psc->sc_bars[bar].vb_type) == |
210 | PCI_MAPREG_TYPE_IO) { |
211 | /* Don't bother fetching I/O BARs. */ |
212 | continue; |
213 | } |
214 | #ifndef __LP64__ |
215 | if (PCI_MAPREG_MEM_TYPE(psc->sc_bars[bar].vb_type) == |
216 | PCI_MAPREG_MEM_TYPE_64BIT) { |
217 | /* XXX */ |
218 | aprint_error_dev(self, |
219 | "WARNING: ignoring 64-bit BAR @ 0x%02x\n" , reg); |
220 | bar++; |
221 | continue; |
222 | } |
223 | #endif |
224 | if (pci_mapreg_info(psc->sc_pc, psc->sc_pcitag, reg, |
225 | psc->sc_bars[bar].vb_type, |
226 | &psc->sc_bars[bar].vb_base, |
227 | &psc->sc_bars[bar].vb_size, |
228 | &psc->sc_bars[bar].vb_flags)) |
229 | aprint_error_dev(self, |
230 | "WARNING: strange BAR @ 0x%02x\n" , reg); |
231 | } |
232 | |
233 | /* XXX Expansion ROM? */ |
234 | |
235 | vga_common_attach(sc, pa->pa_iot, pa->pa_memt, WSDISPLAY_TYPE_PCIVGA, |
236 | vga_pci_lookup_quirks(pa), &vga_pci_funcs); |
237 | |
238 | #ifdef VGA_POST |
239 | psc->sc_posth = vga_post_init(pa->pa_bus, pa->pa_device, |
240 | pa->pa_function); |
241 | if (psc->sc_posth == NULL) |
242 | aprint_error_dev(self, |
243 | "WARNING: could not prepare POST handler\n" ); |
244 | #endif |
245 | |
246 | /* |
247 | * XXX Do not use the generic PCI framework for now as |
248 | * XXX it would power down the device when the console |
249 | * XXX is still using it. |
250 | */ |
251 | if (!pmf_device_register(self, NULL, vga_pci_resume)) |
252 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
253 | config_found_ia(self, "drm" , aux, vga_drm_print); |
254 | } |
255 | |
256 | static int |
257 | vga_pci_rescan(device_t self, const char *ifattr, const int *locators) |
258 | { |
259 | struct vga_pci_softc *psc = device_private(self); |
260 | |
261 | config_found_ia(self, "drm" , &psc->sc_paa, vga_drm_print); |
262 | |
263 | return 0; |
264 | } |
265 | |
266 | static bool |
267 | vga_pci_resume(device_t dv, const pmf_qual_t *qual) |
268 | { |
269 | #if defined(VGA_POST) && NACPICA > 0 |
270 | extern int acpi_md_vbios_reset; |
271 | #endif |
272 | struct vga_pci_softc *sc = device_private(dv); |
273 | |
274 | vga_resume(&sc->sc_vga); |
275 | |
276 | #if defined(VGA_POST) && NACPICA > 0 |
277 | if (sc->sc_posth != NULL && acpi_md_vbios_reset == 2) |
278 | vga_post_call(sc->sc_posth); |
279 | #endif |
280 | |
281 | return true; |
282 | } |
283 | |
284 | int |
285 | vga_pci_cnattach(bus_space_tag_t iot, bus_space_tag_t memt, |
286 | pci_chipset_tag_t pc, int bus, int device, |
287 | int function) |
288 | { |
289 | |
290 | return (vga_cnattach(iot, memt, WSDISPLAY_TYPE_PCIVGA, 0)); |
291 | } |
292 | |
293 | int |
294 | vga_drm_print(void *aux, const char *pnp) |
295 | { |
296 | if (pnp) |
297 | aprint_normal("drm at %s" , pnp); |
298 | return (UNCONF); |
299 | } |
300 | |
301 | |
302 | static int |
303 | vga_pci_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) |
304 | { |
305 | struct vga_config *vc = v; |
306 | struct vga_pci_softc *psc = (void *) vc->softc; |
307 | |
308 | switch (cmd) { |
309 | /* PCI config read/write passthrough. */ |
310 | case PCI_IOC_CFGREAD: |
311 | case PCI_IOC_CFGWRITE: |
312 | return pci_devioctl(psc->sc_pc, psc->sc_pcitag, |
313 | cmd, data, flag, l); |
314 | |
315 | case WSDISPLAYIO_GET_BUSID: |
316 | return wsdisplayio_busid_pci(vc->softc->sc_dev, |
317 | psc->sc_pc, psc->sc_pcitag, data); |
318 | |
319 | default: |
320 | return EPASSTHROUGH; |
321 | } |
322 | } |
323 | |
324 | static paddr_t |
325 | vga_pci_mmap(void *v, off_t offset, int prot) |
326 | { |
327 | struct vga_config *vc = v; |
328 | struct vga_pci_softc *psc = (void *) vc->softc; |
329 | struct vga_bar *vb; |
330 | int bar; |
331 | |
332 | for (bar = 0; bar < NBARS; bar++) { |
333 | vb = &psc->sc_bars[bar]; |
334 | if (vb->vb_size == 0) |
335 | continue; |
336 | if (offset >= vb->vb_base && |
337 | offset < (vb->vb_base + vb->vb_size)) { |
338 | /* XXX This the right thing to do with flags? */ |
339 | return (bus_space_mmap(vc->hdl.vh_memt, vb->vb_base, |
340 | (offset - vb->vb_base), prot, vb->vb_flags)); |
341 | } |
342 | } |
343 | |
344 | /* XXX Expansion ROM? */ |
345 | |
346 | /* |
347 | * Allow mmap access to the legacy ISA hole. This is where |
348 | * the legacy video BIOS will be located, and also where |
349 | * the legacy VGA display buffer is located. |
350 | * |
351 | * XXX Security implications, here? |
352 | */ |
353 | if (offset >= IOM_BEGIN && offset < IOM_END) |
354 | return (bus_space_mmap(vc->hdl.vh_memt, IOM_BEGIN, |
355 | (offset - IOM_BEGIN), prot, 0)); |
356 | |
357 | #ifdef PCI_MAGIC_IO_RANGE |
358 | /* allow to map our IO space on non-x86 machines */ |
359 | if ((offset >= PCI_MAGIC_IO_RANGE) && |
360 | (offset < PCI_MAGIC_IO_RANGE + 0x10000)) { |
361 | return bus_space_mmap(vc->hdl.vh_iot, |
362 | offset - PCI_MAGIC_IO_RANGE, |
363 | 0, prot, BUS_SPACE_MAP_LINEAR); |
364 | } |
365 | #endif |
366 | |
367 | /* Range not found. */ |
368 | return (-1); |
369 | } |
370 | |