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

Re: [patch] Add NForce AGP support



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