[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [patch] Add NForce AGP support
- To: tech_(_at_)_openbsd_(_dot_)_org
- Subject: Re: [patch] Add NForce AGP support
- From: Dimitry Andric <dimitry_(_at_)_andric_(_dot_)_com>
- Date: Wed, 08 Mar 2006 13:12:43 +0100
- Cc: Marco Peereboom <marco_(_at_)_peereboom_(_dot_)_us>
Dimitry Andric wrote:
> Here's a patch to add AGP support for the NForce and NForce2 chipsets
I've modified the diff slightly, to add NForce/NForce2 as PCI hosts.
This should hopefully allow the built-in video adapter to be detected.
Also, if you want to test this, please make sure there's no "real" AGP
card in the box, because it will disable the built-in adapter.
Cheers,
Dimitry
diff -urNd sys.orig/arch/i386/conf/files.i386 sys/arch/i386/conf/files.i386
--- sys.orig/arch/i386/conf/files.i386 Wed Mar 8 11:27:59 2006
+++ sys/arch/i386/conf/files.i386 Tue Mar 7 15:14:16 2006
@@ -102,6 +102,7 @@
file dev/pci/agp_amd.c pciagp
file dev/pci/agp_i810.c pciagp
file dev/pci/agp_intel.c pciagp
+file dev/pci/agp_nvidia.c pciagp
file dev/pci/agp_sis.c pciagp
file dev/pci/agp_via.c pciagp
file arch/i386/pci/pciide_machdep.c pciide
diff -urNd sys.orig/arch/i386/pci/agp_machdep.c sys/arch/i386/pci/agp_machdep.c
--- sys.orig/arch/i386/pci/agp_machdep.c Wed Mar 8 11:27:59 2006
+++ sys/arch/i386/pci/agp_machdep.c Tue Mar 7 15:21:23 2006
@@ -50,6 +50,8 @@
{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82915G_IV, agp_i810_attach },
{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82915GM_IGD, agp_i810_attach },
{ PCI_VENDOR_INTEL, -1, agp_intel_attach },
+ { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE_PCHB, agp_nvidia_attach },
+ { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_PCHB, agp_nvidia_attach },
{ PCI_VENDOR_SIS, -1, agp_sis_attach },
{ PCI_VENDOR_VIATECH, -1, agp_via_attach },
{ 0, 0, NULL }
diff -urNd sys.orig/arch/i386/pci/pchb.c sys/arch/i386/pci/pchb.c
--- sys.orig/arch/i386/pci/pchb.c Wed Mar 8 11:27:59 2006
+++ sys/arch/i386/pci/pchb.c Wed Mar 8 11:21:17 2006
@@ -199,6 +199,7 @@
switch (PCI_VENDOR(pa->pa_id)) {
#ifdef PCIAGP
case PCI_VENDOR_ALI:
+ case PCI_VENDOR_NVIDIA:
case PCI_VENDOR_SIS:
case PCI_VENDOR_VIATECH:
pciagp_set_pchb(pa);
diff -urNd sys.orig/dev/pci/agp_nvidia.c sys/dev/pci/agp_nvidia.c
--- sys.orig/dev/pci/agp_nvidia.c Thu Jan 1 01:00:00 1970
+++ sys/dev/pci/agp_nvidia.c Tue Mar 7 21:07:48 2006
@@ -0,0 +1,394 @@
+/* $OpenBSD$ */
+
+/*-
+ * Copyright (c) 2003 Matthew N. Dodd <winter_(_at_)_jurai_(_dot_)_net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/pci/agp_nvidia.c,v 1.11 2005/12/20 21:12:26 jhb Exp $
+ */
+
+/*
+ * Written using information gleaned from the
+ * NVIDIA nForce/nForce2 AGPGART Linux Kernel Patch.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/lock.h>
+#include <sys/agpio.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/vga_pcivar.h>
+#include <dev/pci/agpvar.h>
+#include <dev/pci/agpreg.h>
+#include <dev/pci/pcidevs.h>
+
+#include <machine/bus.h>
+#include <machine/cpufunc.h>
+
+struct agp_nvidia_softc {
+ u_int32_t initial_aperture; /* aperture size at startup */
+ struct agp_gatt * gatt;
+
+ pcitag_t tag; /* AGP Controller */
+ pcitag_t mc1_tag; /* Memory Controller */
+ pcitag_t mc2_tag; /* Memory Controller */
+ pcitag_t btag; /* Bridge */
+
+ u_int32_t wbc_mask;
+ int num_dirs;
+ int num_active_entries;
+ off_t pg_offset;
+};
+
+static u_int32_t agp_nvidia_get_aperture(struct vga_pci_softc *);
+static int agp_nvidia_set_aperture(struct vga_pci_softc *, u_int32_t);
+static int agp_nvidia_bind_page(struct vga_pci_softc *, off_t, bus_addr_t);
+static int agp_nvidia_unbind_page(struct vga_pci_softc *, off_t);
+static void agp_nvidia_flush_tlb(struct vga_pci_softc *);
+static int nvidia_init_iorr(u_int32_t, u_int32_t);
+
+struct agp_methods agp_nvidia_methods = {
+ agp_nvidia_get_aperture,
+ agp_nvidia_set_aperture,
+ agp_nvidia_bind_page,
+ agp_nvidia_unbind_page,
+ agp_nvidia_flush_tlb,
+ agp_generic_enable,
+ agp_generic_alloc_memory,
+ agp_generic_free_memory,
+ agp_generic_bind_memory,
+ agp_generic_unbind_memory,
+};
+
+int
+agp_nvidia_attach(struct vga_pci_softc *sc, struct pci_attach_args *pa,
+ struct pci_attach_args *pchb_pa)
+{
+ struct agp_nvidia_softc *nsc;
+ struct agp_gatt *gatt;
+ u_int32_t apbase;
+ u_int32_t aplimit;
+ pcireg_t temp;
+ int size;
+ int i;
+ int error;
+
+ nsc = malloc(sizeof *nsc, M_DEVBUF, M_NOWAIT);
+ if (nsc == NULL) {
+ printf(": can't allocate chipset-specific softc\n");
+ return (ENOMEM);
+ }
+ memset(nsc, 0, sizeof *nsc);
+ sc->sc_chipc = nsc;
+ sc->sc_methods = &agp_nvidia_methods;
+
+ if (agp_map_aperture(sc) != 0) {
+ printf(": failed to map aperture\n");
+ free(nsc, M_DEVBUF);
+ sc->sc_chipc = NULL;
+ return (ENXIO);
+ }
+
+ switch (PCI_PRODUCT(pa->pa_id)) {
+ case PCI_PRODUCT_NVIDIA_NFORCE_PCHB:
+ nsc->wbc_mask = 0x00010000;
+ break;
+ case PCI_PRODUCT_NVIDIA_NFORCE2_PCHB:
+ nsc->wbc_mask = 0x80000000;
+ break;
+ default:
+ printf(": unknown product id %#x\n", PCI_PRODUCT(pa->pa_id));
+ free(nsc, M_DEVBUF);
+ sc->sc_chipc = NULL;
+ return (ENODEV);
+ }
+
+ /* AGP Controller */
+ nsc->tag = pa->pa_tag;
+
+ /* Memory Controller 1 */
+ nsc->mc1_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 1);
+ /* XXX Validate existence */
+
+ /* Memory Controller 2 */
+ nsc->mc2_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 2);
+ /* XXX Validate existence */
+
+ /* AGP Host to PCI Bridge */
+ nsc->btag = pci_make_tag(pa->pa_pc, pa->pa_bus, 30, 0);
+ /* XXX Validate existence */
+
+ nsc->initial_aperture = AGP_GET_APERTURE(sc);
+
+ for (;;) {
+ gatt = agp_alloc_gatt(sc);
+ if (gatt != NULL)
+ break;
+ /*
+ * Probably contigmalloc failure. Try reducing the
+ * aperture so that the gatt size reduces.
+ */
+ if (AGP_SET_APERTURE(sc, AGP_GET_APERTURE(sc) / 2)) {
+ free(nsc, M_DEVBUF);
+ sc->sc_chipc = NULL;
+ return (ENOMEM);
+ }
+ }
+ nsc->gatt = gatt;
+
+ apbase = sc->sc_apaddr;
+ aplimit = apbase + AGP_GET_APERTURE(sc) - 1;
+ pci_conf_write(sc->sc_pc, nsc->mc2_tag, AGP_NVIDIA_2_APBASE, apbase);
+ pci_conf_write(sc->sc_pc, nsc->mc2_tag, AGP_NVIDIA_2_APLIMIT, aplimit);
+ pci_conf_write(sc->sc_pc, nsc->btag, AGP_NVIDIA_3_APBASE, apbase);
+ pci_conf_write(sc->sc_pc, nsc->btag, AGP_NVIDIA_3_APLIMIT, aplimit);
+
+ error = nvidia_init_iorr(apbase, AGP_GET_APERTURE(sc));
+ if (error) {
+ printf("Failed to setup IORRs\n");
+ agp_free_gatt(sc, gatt);
+ free(nsc, M_DEVBUF);
+ sc->sc_chipc = NULL;
+ return (ENXIO);
+ }
+
+ /* directory size is 64k */
+ size = AGP_GET_APERTURE(sc) / 1024 / 1024;
+ nsc->num_dirs = size / 64;
+ nsc->num_active_entries = (size == 32) ? 16384 : ((size * 1024) / 4);
+ nsc->pg_offset = 0;
+ if (nsc->num_dirs == 0) {
+ nsc->num_dirs = 1;
+ nsc->num_active_entries /= (64 / size);
+ nsc->pg_offset = (apbase & (64 * 1024 * 1024 - 1) &
+ ~(AGP_GET_APERTURE(sc) - 1)) / PAGE_SIZE;
+ }
+
+ /* (G)ATT Base Address */
+ for (i = 0; i < 8; i++) {
+ pci_conf_write(sc->sc_pc, nsc->mc2_tag, AGP_NVIDIA_2_ATTBASE(i),
+ (nsc->gatt->ag_physical +
+ (i % nsc->num_dirs) * 64 * 1024) | 1);
+ }
+
+ /* GTLB Control */
+ temp = pci_conf_read(sc->sc_pc, nsc->mc2_tag, AGP_NVIDIA_2_GARTCTRL);
+ pci_conf_write(sc->sc_pc, nsc->mc2_tag, AGP_NVIDIA_2_GARTCTRL, temp | 0x11);
+
+ /* GART Control */
+ temp = pci_conf_read(sc->sc_pc, sc->sc_pcitag, AGP_NVIDIA_0_APSIZE);
+ pci_conf_write(sc->sc_pc, sc->sc_pcitag, AGP_NVIDIA_0_APSIZE, temp | 0x100);
+
+ return (0);
+}
+
+#if 0
+static int
+agp_nvidia_detach(struct vga_pci_softc *sc)
+{
+ struct agp_nvidia_softc *nsc = sc->sc_chipc;
+ int error;
+ pcireg_t temp;
+
+ error = agp_generic_detach(sc);
+ if (error)
+ return (error);
+
+ /* GART Control */
+ temp = pci_conf_read(sc->sc_pc, sc->sc_pcitag, AGP_NVIDIA_0_APSIZE);
+ pci_conf_write(sc->sc_pc, sc->sc_pcitag, AGP_NVIDIA_0_APSIZE, temp & ~0x100);
+
+ /* GTLB Control */
+ temp = pci_conf_read(sc->sc_pc, nsc->mc2_tag, AGP_NVIDIA_2_GARTCTRL);
+ pci_conf_write(sc->sc_pc, nsc->mc2_tag, AGP_NVIDIA_2_GARTCTRL, temp & ~0x11);
+
+ /* Put the aperture back the way it started. */
+ AGP_SET_APERTURE(sc, nsc->initial_aperture);
+
+ /* restore iorr for previous aperture size */
+ nvidia_init_iorr(sc->sc_apaddr, nsc->initial_aperture);
+
+ agp_free_gatt(sc, nsc->gatt);
+
+ return (0);
+}
+#endif
+
+static u_int32_t
+agp_nvidia_get_aperture(struct vga_pci_softc *sc)
+{
+ pcireg_t reg;
+
+ reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, AGP_NVIDIA_0_APSIZE);
+ reg &= 0xf;
+ switch (reg) {
+ case 0: return (512 * 1024 * 1024); break;
+ case 8: return (256 * 1024 * 1024); break;
+ case 12: return (128 * 1024 * 1024); break;
+ case 14: return (64 * 1024 * 1024); break;
+ case 15: return (32 * 1024 * 1024); break;
+ default:
+ printf("Invalid aperture setting 0x%x", reg);
+ return 0;
+ }
+}
+
+static int
+agp_nvidia_set_aperture(struct vga_pci_softc *sc, u_int32_t aperture)
+{
+ u_int8_t val;
+ u_int8_t key;
+
+ switch (aperture) {
+ case (512 * 1024 * 1024): key = 0; break;
+ case (256 * 1024 * 1024): key = 8; break;
+ case (128 * 1024 * 1024): key = 12; break;
+ case (64 * 1024 * 1024): key = 14; break;
+ case (32 * 1024 * 1024): key = 15; break;
+ default:
+ printf("Invalid aperture size (%dMb)\n", aperture / 1024 / 1024);
+ return (EINVAL);
+ }
+ val = pci_conf_read(sc->sc_pc, sc->sc_pcitag, AGP_NVIDIA_0_APSIZE);
+ val &= ~0xf;
+ val |= key;
+ pci_conf_write(sc->sc_pc, sc->sc_pcitag, AGP_NVIDIA_0_APSIZE, val);
+
+ return (0);
+}
+
+static int
+agp_nvidia_bind_page(struct vga_pci_softc *sc, off_t offset, bus_addr_t physical)
+{
+ struct agp_nvidia_softc *nsc = sc->sc_chipc;
+ u_int32_t index;
+
+ if (offset < 0 || offset >= (nsc->gatt->ag_entries << AGP_PAGE_SHIFT))
+ return (EINVAL);
+
+ index = (nsc->pg_offset + offset) >> AGP_PAGE_SHIFT;
+ nsc->gatt->ag_virtual[index] = physical | 1;
+
+ return (0);
+}
+
+static int
+agp_nvidia_unbind_page(struct vga_pci_softc *sc, off_t offset)
+{
+ struct agp_nvidia_softc *nsc = sc->sc_chipc;
+ u_int32_t index;
+
+ if (offset < 0 || offset >= (nsc->gatt->ag_entries << AGP_PAGE_SHIFT))
+ return (EINVAL);
+
+ index = (nsc->pg_offset + offset) >> AGP_PAGE_SHIFT;
+ nsc->gatt->ag_virtual[index] = 0;
+
+ return (0);
+}
+
+static void
+agp_nvidia_flush_tlb(struct vga_pci_softc *sc)
+{
+ struct agp_nvidia_softc *nsc = sc->sc_chipc;
+ u_int32_t wbc_reg, temp;
+ volatile u_int32_t *ag_virtual;
+ int i;
+
+ if (nsc->wbc_mask) {
+ wbc_reg = pci_conf_read(sc->sc_pc, nsc->mc1_tag, AGP_NVIDIA_1_WBC);
+ wbc_reg |= nsc->wbc_mask;
+ pci_conf_write(sc->sc_pc, nsc->mc1_tag, AGP_NVIDIA_1_WBC, wbc_reg);
+
+ /* Wait no more than 3 seconds. */
+ for (i = 0; i < 3000; i++) {
+ wbc_reg = pci_conf_read(sc->sc_pc, nsc->mc1_tag, AGP_NVIDIA_1_WBC);
+ if ((nsc->wbc_mask & wbc_reg) == 0)
+ break;
+ else
+ DELAY(1000);
+ }
+ if (i == 3000)
+ printf("TLB flush took more than 3 seconds.\n");
+ }
+
+ ag_virtual = (volatile u_int32_t *)nsc->gatt->ag_virtual;
+
+ /* Flush TLB entries. */
+ for(i = 0; i < 32 + 1; i++)
+ temp = ag_virtual[i * PAGE_SIZE / sizeof(u_int32_t)];
+ for(i = 0; i < 32 + 1; i++)
+ temp = ag_virtual[i * PAGE_SIZE / sizeof(u_int32_t)];
+}
+
+#define SYSCFG 0xC0010010
+#define IORR_BASE0 0xC0010016
+#define IORR_MASK0 0xC0010017
+#define AMD_K7_NUM_IORR 2
+
+static int
+nvidia_init_iorr(u_int32_t addr, u_int32_t size)
+{
+ quad_t base, mask, sys;
+ u_int32_t iorr_addr, free_iorr_addr;
+
+ /* Find the iorr that is already used for the addr */
+ /* If not found, determine the uppermost available iorr */
+ free_iorr_addr = AMD_K7_NUM_IORR;
+ for(iorr_addr = 0; iorr_addr < AMD_K7_NUM_IORR; iorr_addr++) {
+ base = rdmsr(IORR_BASE0 + 2 * iorr_addr);
+ mask = rdmsr(IORR_MASK0 + 2 * iorr_addr);
+
+ if ((base & 0xfffff000ULL) == (addr & 0xfffff000))
+ break;
+
+ if ((mask & 0x00000800ULL) == 0)
+ free_iorr_addr = iorr_addr;
+ }
+
+ if (iorr_addr >= AMD_K7_NUM_IORR) {
+ iorr_addr = free_iorr_addr;
+ if (iorr_addr >= AMD_K7_NUM_IORR)
+ return (EINVAL);
+ }
+
+ base = (addr & ~0xfff) | 0x18;
+ mask = (0xfULL << 32) | ((~(size - 1)) & 0xfffff000) | 0x800;
+ wrmsr(IORR_BASE0 + 2 * iorr_addr, base);
+ wrmsr(IORR_MASK0 + 2 * iorr_addr, mask);
+
+ sys = rdmsr(SYSCFG);
+ sys |= 0x00100000ULL;
+ wrmsr(SYSCFG, sys);
+
+ return (0);
+}
+
diff -urNd sys.orig/dev/pci/agpreg.h sys/dev/pci/agpreg.h
--- sys.orig/dev/pci/agpreg.h Wed Mar 8 11:27:59 2006
+++ sys/dev/pci/agpreg.h Tue Mar 7 15:54:59 2006
@@ -202,5 +219,18 @@
#define AGP_I915_MSAC_GMASIZE 0x02
#define AGP_I915_MSAC_GMASIZE_128 0x02
#define AGP_I915_MSAC_GMASIZE_256 0x00
+
+/*
+ * NVIDIA nForce/nForce2 registers
+ */
+#define AGP_NVIDIA_0_APBASE 0x10
+#define AGP_NVIDIA_0_APSIZE 0x80
+#define AGP_NVIDIA_1_WBC 0xf0
+#define AGP_NVIDIA_2_GARTCTRL 0xd0
+#define AGP_NVIDIA_2_APBASE 0xd8
+#define AGP_NVIDIA_2_APLIMIT 0xdc
+#define AGP_NVIDIA_2_ATTBASE(i) (0xe0 + (i) * 4)
+#define AGP_NVIDIA_3_APBASE 0x50
+#define AGP_NVIDIA_3_APLIMIT 0x54
#endif /* !_PCI_AGPREG_H_ */
diff -urNd sys.orig/dev/pci/agpvar.h sys/dev/pci/agpvar.h
--- sys.orig/dev/pci/agpvar.h Wed Mar 8 11:27:59 2006
+++ sys/dev/pci/agpvar.h Tue Mar 7 15:56:01 2006
@@ -56,10 +56,7 @@
/* #define AGP_DEBUG */
#ifdef AGP_DEBUG
-#define AGP_DPF(x...) do { \
- printf("agp: "); \
- printf(##x); \
-} while (0)
+#define AGP_DPF(fmt, arg...) printf("agp: " fmt ,##arg)
#else
#define AGP_DPF(x...) do {} while (0)
#endif
@@ -133,6 +130,8 @@
int agp_i810_attach(struct vga_pci_softc *, struct pci_attach_args *,
struct pci_attach_args *);
int agp_intel_attach(struct vga_pci_softc *, struct pci_attach_args *,
+ struct pci_attach_args *);
+int agp_nvidia_attach(struct vga_pci_softc *, struct pci_attach_args *,
struct pci_attach_args *);
int agp_via_attach(struct vga_pci_softc *, struct pci_attach_args *,
struct pci_attach_args *);
Visit your host, monkey.org