1*13d4bb4cSthorpej /* $NetBSD: vga_pci.c,v 1.59 2022/09/25 17:52:25 thorpej Exp $ */
2013507e3Sdrochner
3013507e3Sdrochner /*
4013507e3Sdrochner * Copyright (c) 1995, 1996 Carnegie-Mellon University.
5013507e3Sdrochner * All rights reserved.
6013507e3Sdrochner *
7013507e3Sdrochner * Author: Chris G. Demetriou
8013507e3Sdrochner *
9013507e3Sdrochner * Permission to use, copy, modify and distribute this software and
10013507e3Sdrochner * its documentation is hereby granted, provided that both the copyright
11013507e3Sdrochner * notice and this permission notice appear in all copies of the
12013507e3Sdrochner * software, derivative works or modified versions, and any portions
13013507e3Sdrochner * thereof, and that both notices appear in supporting documentation.
14013507e3Sdrochner *
15013507e3Sdrochner * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16013507e3Sdrochner * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17013507e3Sdrochner * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18013507e3Sdrochner *
19013507e3Sdrochner * Carnegie Mellon requests users of this software to return to
20013507e3Sdrochner *
21013507e3Sdrochner * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
22013507e3Sdrochner * School of Computer Science
23013507e3Sdrochner * Carnegie Mellon University
24013507e3Sdrochner * Pittsburgh PA 15213-3890
25013507e3Sdrochner *
26013507e3Sdrochner * any improvements or extensions that they make and grant Carnegie the
27013507e3Sdrochner * rights to redistribute these changes.
28013507e3Sdrochner */
29013507e3Sdrochner
309048aaaeSlukem #include <sys/cdefs.h>
31*13d4bb4cSthorpej __KERNEL_RCSID(0, "$NetBSD: vga_pci.c,v 1.59 2022/09/25 17:52:25 thorpej Exp $");
329048aaaeSlukem
33013507e3Sdrochner #include <sys/param.h>
34013507e3Sdrochner #include <sys/systm.h>
35013507e3Sdrochner #include <sys/kernel.h>
36013507e3Sdrochner #include <sys/device.h>
37013507e3Sdrochner
38013507e3Sdrochner #include <dev/pci/pcireg.h>
39013507e3Sdrochner #include <dev/pci/pcivar.h>
40013507e3Sdrochner #include <dev/pci/pcidevs.h>
415b09c93aSthorpej #include <dev/pci/pciio.h>
42013507e3Sdrochner
43b84dbbb3Sdrochner #include <dev/ic/mc6845reg.h>
44b84dbbb3Sdrochner #include <dev/ic/pcdisplayvar.h>
45013507e3Sdrochner #include <dev/ic/vgareg.h>
46013507e3Sdrochner #include <dev/ic/vgavar.h>
47013507e3Sdrochner #include <dev/pci/vga_pcivar.h>
48013507e3Sdrochner
49915af95aSthorpej #include <dev/isa/isareg.h> /* For legacy VGA address ranges */
50915af95aSthorpej
51013507e3Sdrochner #include <dev/wscons/wsconsio.h>
52013507e3Sdrochner #include <dev/wscons/wsdisplayvar.h>
53e28cc170Scegger #include <dev/pci/wsdisplay_pci.h>
54013507e3Sdrochner
559a98cd19Sjoerg #include "opt_vga.h"
569a98cd19Sjoerg
579a98cd19Sjoerg #ifdef VGA_POST
583ea923f7Sjoerg # if defined(__i386__) || defined(__amd64__)
59a3abcdbcSjmcneill # include "acpica.h"
603ea923f7Sjoerg # endif
619a98cd19Sjoerg #include <x86/vga_post.h>
629a98cd19Sjoerg #endif
639a98cd19Sjoerg
64915af95aSthorpej #define NBARS 6 /* number of PCI BARs */
65915af95aSthorpej
66915af95aSthorpej struct vga_bar {
67915af95aSthorpej bus_addr_t vb_base;
68915af95aSthorpej bus_size_t vb_size;
69915af95aSthorpej pcireg_t vb_type;
70915af95aSthorpej int vb_flags;
71915af95aSthorpej };
72915af95aSthorpej
73013507e3Sdrochner struct vga_pci_softc {
74b9fffe19Sthorpej struct vga_softc sc_vga;
75013507e3Sdrochner
765b09c93aSthorpej pci_chipset_tag_t sc_pc;
77b9fffe19Sthorpej pcitag_t sc_pcitag;
78915af95aSthorpej
79915af95aSthorpej struct vga_bar sc_bars[NBARS];
80915af95aSthorpej struct vga_bar sc_rom;
814c1d81b2Sjmcneill
829a98cd19Sjoerg #ifdef VGA_POST
839a98cd19Sjoerg struct vga_post *sc_posth;
849a98cd19Sjoerg #endif
854db90e1fSjmcneill
864db90e1fSjmcneill struct pci_attach_args sc_paa;
87013507e3Sdrochner };
88013507e3Sdrochner
89a591bc88Scegger static int vga_pci_match(device_t, cfdata_t, void *);
90a591bc88Scegger static void vga_pci_attach(device_t, device_t, void *);
91a591bc88Scegger static int vga_pci_rescan(device_t, const char *, const int *);
929b3975fdSdrochner static int vga_pci_lookup_quirks(struct pci_attach_args *);
93c1b390d4Sdyoung static bool vga_pci_resume(device_t dv, const pmf_qual_t *);
94013507e3Sdrochner
954db90e1fSjmcneill CFATTACH_DECL2_NEW(vga_pci, sizeof(struct vga_pci_softc),
964db90e1fSjmcneill vga_pci_match, vga_pci_attach, NULL, NULL, vga_pci_rescan, NULL);
97013507e3Sdrochner
9853524e44Schristos static int vga_pci_ioctl(void *, u_long, void *, int, struct lwp *);
99d36c43c5Sthorpej static paddr_t vga_pci_mmap(void *, off_t, int);
1005b09c93aSthorpej
101d36c43c5Sthorpej static const struct vga_funcs vga_pci_funcs = {
1025b09c93aSthorpej vga_pci_ioctl,
1035b09c93aSthorpej vga_pci_mmap,
1045b09c93aSthorpej };
1055b09c93aSthorpej
1069b3975fdSdrochner static const struct {
1079b3975fdSdrochner int id;
1089b3975fdSdrochner int quirks;
1099b3975fdSdrochner } vga_pci_quirks[] = {
1101014d5a4Ssalo {PCI_ID_CODE(PCI_VENDOR_SILMOTION, PCI_PRODUCT_SILMOTION_SM712),
11132f7789dSdrochner VGA_QUIRK_NOFASTSCROLL},
1123a1b5ff0Sshattered {PCI_ID_CODE(PCI_VENDOR_CYRIX, PCI_PRODUCT_CYRIX_CX5530_VIDEO),
1133a1b5ff0Sshattered VGA_QUIRK_NOFASTSCROLL},
1149b3975fdSdrochner };
1158ae04c4bSdrochner
1168ae04c4bSdrochner static const struct {
1178ae04c4bSdrochner int vid;
1188ae04c4bSdrochner int quirks;
1198ae04c4bSdrochner } vga_pci_vquirks[] = {
1208ae04c4bSdrochner {PCI_VENDOR_ATI, VGA_QUIRK_ONEFONT},
1218ae04c4bSdrochner };
1229b3975fdSdrochner
1239b3975fdSdrochner static int
vga_pci_lookup_quirks(struct pci_attach_args * pa)124d36c43c5Sthorpej vga_pci_lookup_quirks(struct pci_attach_args *pa)
1259b3975fdSdrochner {
1269b3975fdSdrochner int i;
1279b3975fdSdrochner
1289b3975fdSdrochner for (i = 0; i < sizeof(vga_pci_quirks) / sizeof (vga_pci_quirks[0]);
1299b3975fdSdrochner i++) {
1309b3975fdSdrochner if (vga_pci_quirks[i].id == pa->pa_id)
1319b3975fdSdrochner return (vga_pci_quirks[i].quirks);
1329b3975fdSdrochner }
1338ae04c4bSdrochner for (i = 0; i < sizeof(vga_pci_vquirks) / sizeof (vga_pci_vquirks[0]);
1348ae04c4bSdrochner i++) {
1358ae04c4bSdrochner if (vga_pci_vquirks[i].vid == PCI_VENDOR(pa->pa_id))
1368ae04c4bSdrochner return (vga_pci_vquirks[i].quirks);
1378ae04c4bSdrochner }
1389b3975fdSdrochner return (0);
1399b3975fdSdrochner }
1409b3975fdSdrochner
141d36c43c5Sthorpej static int
vga_pci_match(device_t parent,cfdata_t match,void * aux)142a591bc88Scegger vga_pci_match(device_t parent, cfdata_t match, void *aux)
143013507e3Sdrochner {
144013507e3Sdrochner struct pci_attach_args *pa = aux;
145013507e3Sdrochner int potential;
146013507e3Sdrochner
147013507e3Sdrochner potential = 0;
148013507e3Sdrochner
149013507e3Sdrochner /*
150013507e3Sdrochner * If it's prehistoric/vga or display/vga, we might match.
1518989ce5fSwiz * For the console device, this is just a sanity check.
152013507e3Sdrochner */
153013507e3Sdrochner if (PCI_CLASS(pa->pa_class) == PCI_CLASS_PREHISTORIC &&
154013507e3Sdrochner PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_PREHISTORIC_VGA)
155013507e3Sdrochner potential = 1;
156013507e3Sdrochner if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY &&
157013507e3Sdrochner PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA)
158013507e3Sdrochner potential = 1;
159013507e3Sdrochner
160013507e3Sdrochner if (!potential)
161013507e3Sdrochner return (0);
162013507e3Sdrochner
163013507e3Sdrochner /* check whether it is disabled by firmware */
164013507e3Sdrochner if ((pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG)
165013507e3Sdrochner & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
166013507e3Sdrochner != (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
167013507e3Sdrochner return (0);
168013507e3Sdrochner
169013507e3Sdrochner /* If it's the console, we have a winner! */
170013507e3Sdrochner if (vga_is_console(pa->pa_iot, WSDISPLAY_TYPE_PCIVGA))
171013507e3Sdrochner return (1);
172013507e3Sdrochner
173013507e3Sdrochner /*
174013507e3Sdrochner * If we might match, make sure that the card actually looks OK.
175013507e3Sdrochner */
176013507e3Sdrochner if (!vga_common_probe(pa->pa_iot, pa->pa_memt))
177013507e3Sdrochner return (0);
178013507e3Sdrochner
179013507e3Sdrochner return (1);
180013507e3Sdrochner }
181013507e3Sdrochner
182d36c43c5Sthorpej static void
vga_pci_attach(device_t parent,device_t self,void * aux)183a591bc88Scegger vga_pci_attach(device_t parent, device_t self, void *aux)
184013507e3Sdrochner {
185d293cfcaScube struct vga_pci_softc *psc = device_private(self);
186b9fffe19Sthorpej struct vga_softc *sc = &psc->sc_vga;
187013507e3Sdrochner struct pci_attach_args *pa = aux;
188915af95aSthorpej int bar, reg;
189013507e3Sdrochner
190d293cfcaScube sc->sc_dev = self;
1915b09c93aSthorpej psc->sc_pc = pa->pa_pc;
192b9fffe19Sthorpej psc->sc_pcitag = pa->pa_tag;
1934db90e1fSjmcneill psc->sc_paa = *pa;
194013507e3Sdrochner
195d8e1a7b6Sdrochner pci_aprint_devinfo(pa, NULL);
196013507e3Sdrochner
197915af95aSthorpej /*
198915af95aSthorpej * Gather info about all the BARs. These are used to allow
199915af95aSthorpej * the X server to map the VGA device.
200915af95aSthorpej */
201915af95aSthorpej for (bar = 0; bar < NBARS; bar++) {
202915af95aSthorpej reg = PCI_MAPREG_START + (bar * 4);
20334c0afbfSdrochner if (!pci_mapreg_probe(psc->sc_pc, psc->sc_pcitag, reg,
20434c0afbfSdrochner &psc->sc_bars[bar].vb_type)) {
20534c0afbfSdrochner /* there is no valid mapping register */
20634c0afbfSdrochner continue;
20734c0afbfSdrochner }
208915af95aSthorpej if (PCI_MAPREG_TYPE(psc->sc_bars[bar].vb_type) ==
209915af95aSthorpej PCI_MAPREG_TYPE_IO) {
210915af95aSthorpej /* Don't bother fetching I/O BARs. */
211915af95aSthorpej continue;
212915af95aSthorpej }
21324201ba6Smartin #ifndef __LP64__
214915af95aSthorpej if (PCI_MAPREG_MEM_TYPE(psc->sc_bars[bar].vb_type) ==
215915af95aSthorpej PCI_MAPREG_MEM_TYPE_64BIT) {
216915af95aSthorpej /* XXX */
217d293cfcaScube aprint_error_dev(self,
218d293cfcaScube "WARNING: ignoring 64-bit BAR @ 0x%02x\n", reg);
21934c0afbfSdrochner bar++;
220915af95aSthorpej continue;
221915af95aSthorpej }
22224201ba6Smartin #endif
22334c0afbfSdrochner if (pci_mapreg_info(psc->sc_pc, psc->sc_pcitag, reg,
224915af95aSthorpej psc->sc_bars[bar].vb_type,
225915af95aSthorpej &psc->sc_bars[bar].vb_base,
226915af95aSthorpej &psc->sc_bars[bar].vb_size,
22734c0afbfSdrochner &psc->sc_bars[bar].vb_flags))
228d293cfcaScube aprint_error_dev(self,
229d293cfcaScube "WARNING: strange BAR @ 0x%02x\n", reg);
230915af95aSthorpej }
231915af95aSthorpej
232e1d78452Sjdolecek /*
233e1d78452Sjdolecek * Disable INTx interrupts, there is no specific chipset driver for
234e1d78452Sjdolecek * this PCI device. Else unhandled display adapter interrupts
235e1d78452Sjdolecek * might freeze the CPU.
236e1d78452Sjdolecek */
237e1d78452Sjdolecek pcireg_t cmd = pci_conf_read(pa->pa_pc, pa->pa_tag,
238e1d78452Sjdolecek PCI_COMMAND_STATUS_REG);
239e1d78452Sjdolecek pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
240e1d78452Sjdolecek cmd | PCI_COMMAND_INTERRUPT_DISABLE);
241e1d78452Sjdolecek
242915af95aSthorpej /* XXX Expansion ROM? */
243915af95aSthorpej
244b9fffe19Sthorpej vga_common_attach(sc, pa->pa_iot, pa->pa_memt, WSDISPLAY_TYPE_PCIVGA,
2459b3975fdSdrochner vga_pci_lookup_quirks(pa), &vga_pci_funcs);
2462d9e04fcSdrochner
2479a98cd19Sjoerg #ifdef VGA_POST
2488bc54e5bSmsaitoh psc->sc_posth = vga_post_init(pa->pa_bus, pa->pa_device,
2498bc54e5bSmsaitoh pa->pa_function);
2509a98cd19Sjoerg if (psc->sc_posth == NULL)
2518bc54e5bSmsaitoh aprint_error_dev(self,
2528bc54e5bSmsaitoh "WARNING: could not prepare POST handler\n");
2539a98cd19Sjoerg #endif
2549a98cd19Sjoerg
2554c1d81b2Sjmcneill /*
2564c1d81b2Sjmcneill * XXX Do not use the generic PCI framework for now as
2574c1d81b2Sjmcneill * XXX it would power down the device when the console
2584c1d81b2Sjmcneill * XXX is still using it.
2594c1d81b2Sjmcneill */
2604c1d81b2Sjmcneill if (!pmf_device_register(self, NULL, vga_pci_resume))
2614c1d81b2Sjmcneill aprint_error_dev(self, "couldn't establish power handler\n");
2622685996bSthorpej config_found(self, aux, vga_drm_print,
263c7fb772bSthorpej CFARGS(.iattr = "drm"));
264013507e3Sdrochner }
265013507e3Sdrochner
2664db90e1fSjmcneill static int
vga_pci_rescan(device_t self,const char * ifattr,const int * locators)267a591bc88Scegger vga_pci_rescan(device_t self, const char *ifattr, const int *locators)
2684db90e1fSjmcneill {
2694db90e1fSjmcneill struct vga_pci_softc *psc = device_private(self);
2704db90e1fSjmcneill
2712685996bSthorpej config_found(self, &psc->sc_paa, vga_drm_print,
272c7fb772bSthorpej CFARGS(.iattr = "drm"));
2734db90e1fSjmcneill
2744db90e1fSjmcneill return 0;
2754db90e1fSjmcneill }
2764db90e1fSjmcneill
2774c1d81b2Sjmcneill static bool
vga_pci_resume(device_t dv,const pmf_qual_t * qual)278c1b390d4Sdyoung vga_pci_resume(device_t dv, const pmf_qual_t *qual)
2794c1d81b2Sjmcneill {
280a3abcdbcSjmcneill #if defined(VGA_POST) && NACPICA > 0
2810b2a6aefSjoerg extern int acpi_md_vbios_reset;
2820b2a6aefSjoerg #endif
2834c1d81b2Sjmcneill struct vga_pci_softc *sc = device_private(dv);
2844c1d81b2Sjmcneill
2854c1d81b2Sjmcneill vga_resume(&sc->sc_vga);
2864c1d81b2Sjmcneill
287a3abcdbcSjmcneill #if defined(VGA_POST) && NACPICA > 0
2880b2a6aefSjoerg if (sc->sc_posth != NULL && acpi_md_vbios_reset == 2)
2899a98cd19Sjoerg vga_post_call(sc->sc_posth);
2909a98cd19Sjoerg #endif
2919a98cd19Sjoerg
2924c1d81b2Sjmcneill return true;
2934c1d81b2Sjmcneill }
2944c1d81b2Sjmcneill
295013507e3Sdrochner int
vga_pci_cnattach(bus_space_tag_t iot,bus_space_tag_t memt,pci_chipset_tag_t pc,int bus,int device,int function)2969dbc5fd1Sjunyoung vga_pci_cnattach(bus_space_tag_t iot, bus_space_tag_t memt,
297168cd830Schristos pci_chipset_tag_t pc, int bus, int device,
298168cd830Schristos int function)
299013507e3Sdrochner {
30075cfc612Sjunyoung
301013507e3Sdrochner return (vga_cnattach(iot, memt, WSDISPLAY_TYPE_PCIVGA, 0));
302013507e3Sdrochner }
3035b09c93aSthorpej
3042d9e04fcSdrochner int
vga_drm_print(void * aux,const char * pnp)3052d9e04fcSdrochner vga_drm_print(void *aux, const char *pnp)
3062d9e04fcSdrochner {
3072d9e04fcSdrochner if (pnp)
3081a53395eSjmcneill aprint_normal("drm at %s", pnp);
3091a53395eSjmcneill return (UNCONF);
3102d9e04fcSdrochner }
3112d9e04fcSdrochner
3122d9e04fcSdrochner
313d36c43c5Sthorpej static int
vga_pci_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)31453524e44Schristos vga_pci_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
3155b09c93aSthorpej {
3165b09c93aSthorpej struct vga_config *vc = v;
3175b09c93aSthorpej struct vga_pci_softc *psc = (void *) vc->softc;
3185b09c93aSthorpej
3195b09c93aSthorpej switch (cmd) {
3205b09c93aSthorpej /* PCI config read/write passthrough. */
3215b09c93aSthorpej case PCI_IOC_CFGREAD:
3225b09c93aSthorpej case PCI_IOC_CFGWRITE:
323ca7fa246Scegger return pci_devioctl(psc->sc_pc, psc->sc_pcitag,
324ca7fa246Scegger cmd, data, flag, l);
3255b09c93aSthorpej
326e28cc170Scegger case WSDISPLAYIO_GET_BUSID:
327e28cc170Scegger return wsdisplayio_busid_pci(vc->softc->sc_dev,
328e28cc170Scegger psc->sc_pc, psc->sc_pcitag, data);
329e28cc170Scegger
3305b09c93aSthorpej default:
331ca7fa246Scegger return EPASSTHROUGH;
3325b09c93aSthorpej }
3335b09c93aSthorpej }
3345b09c93aSthorpej
335d36c43c5Sthorpej static paddr_t
vga_pci_mmap(void * v,off_t offset,int prot)3365b09c93aSthorpej vga_pci_mmap(void *v, off_t offset, int prot)
3375b09c93aSthorpej {
338915af95aSthorpej struct vga_config *vc = v;
339915af95aSthorpej struct vga_pci_softc *psc = (void *) vc->softc;
340915af95aSthorpej struct vga_bar *vb;
341915af95aSthorpej int bar;
3425b09c93aSthorpej
343915af95aSthorpej for (bar = 0; bar < NBARS; bar++) {
344915af95aSthorpej vb = &psc->sc_bars[bar];
345915af95aSthorpej if (vb->vb_size == 0)
346915af95aSthorpej continue;
347915af95aSthorpej if (offset >= vb->vb_base &&
348915af95aSthorpej offset < (vb->vb_base + vb->vb_size)) {
349915af95aSthorpej /* XXX This the right thing to do with flags? */
350915af95aSthorpej return (bus_space_mmap(vc->hdl.vh_memt, vb->vb_base,
351915af95aSthorpej (offset - vb->vb_base), prot, vb->vb_flags));
352915af95aSthorpej }
353915af95aSthorpej }
354915af95aSthorpej
355915af95aSthorpej /* XXX Expansion ROM? */
356915af95aSthorpej
357915af95aSthorpej /*
358915af95aSthorpej * Allow mmap access to the legacy ISA hole. This is where
359915af95aSthorpej * the legacy video BIOS will be located, and also where
360915af95aSthorpej * the legacy VGA display buffer is located.
361915af95aSthorpej *
362915af95aSthorpej * XXX Security implications, here?
363915af95aSthorpej */
364915af95aSthorpej if (offset >= IOM_BEGIN && offset < IOM_END)
365915af95aSthorpej return (bus_space_mmap(vc->hdl.vh_memt, IOM_BEGIN,
366915af95aSthorpej (offset - IOM_BEGIN), prot, 0));
367915af95aSthorpej
368486592abSmacallan #ifdef PCI_MAGIC_IO_RANGE
369486592abSmacallan /* allow to map our IO space on non-x86 machines */
370486592abSmacallan if ((offset >= PCI_MAGIC_IO_RANGE) &&
371486592abSmacallan (offset < PCI_MAGIC_IO_RANGE + 0x10000)) {
372486592abSmacallan return bus_space_mmap(vc->hdl.vh_iot,
373486592abSmacallan offset - PCI_MAGIC_IO_RANGE,
374486592abSmacallan 0, prot, BUS_SPACE_MAP_LINEAR);
375486592abSmacallan }
376486592abSmacallan #endif
377486592abSmacallan
378915af95aSthorpej /* Range not found. */
3795b09c93aSthorpej return (-1);
3805b09c93aSthorpej }
381