[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[patch] Add NForce AGP support



Hi all,

Here's a patch to add AGP support for the NForce and NForce2 chipsets,
adapted from FreeBSD.  Since I'm not sure about all the FreeBSD
constructs being converted properly, especially the handling of the
memory controllers, I would like to ask you for your comments on this.
Please be gentle. ;)

Also, I don't have NForce hardware lying around, so if you have this
kind of hardware, please try this diff out.  You should see a line in
dmesg telling you a "NForce PCI Host" or "NForce2 PCI" is detected.

Then you need to start X.org using the nv(4) driver, to see if the
hardware acceleration works.  If not, or if you experience horrible
crashes (quite likely :), please mail me your dmesg and Xorg.log files.

Cheers,
Dimitry

diff -urNd sys.orig/arch/i386/conf/files.i386 sys/arch/i386/conf/files.i386
--- sys.orig/arch/i386/conf/files.i386	Sat Mar  4 17:27:03 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	Tue Mar  7 21:29:10 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/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	Tue Mar  7 21:29:36 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	Tue Mar  7 21:29:36 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 *);