Home | History | Annotate | Line # | Download | only in pci
      1 /*	$NetBSD: agp_nvidia.c,v 1.1 2026/06/21 18:38:35 andvar Exp $ */
      2 
      3 /*-
      4  * SPDX-License-Identifier: BSD-2-Clause
      5  *
      6  * Copyright (c) 2003 Matthew N. Dodd <winter (at) jurai.net>
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28  * SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __KERNEL_RCSID(0, "$NetBSD: agp_nvidia.c,v 1.1 2026/06/21 18:38:35 andvar Exp $");
     33 
     34 #include <sys/param.h>
     35 #include <sys/agpio.h>
     36 #include <sys/bus.h>
     37 #include <sys/conf.h>
     38 #include <sys/device.h>
     39 #include <sys/kernel.h>
     40 #include <sys/malloc.h>
     41 #include <sys/proc.h>
     42 #include <sys/systm.h>
     43 
     44 #include <dev/pci/agpreg.h>
     45 #include <dev/pci/agpvar.h>
     46 #include <dev/pci/pcidevs.h>
     47 #include <dev/pci/pcireg.h>
     48 #include <dev/pci/pcivar.h>
     49 
     50 #include <machine/cpufunc.h>
     51 
     52 #define AGP_NFORCE_MEMCTLTAG(pc, dev)	pci_make_tag(pc, 0, 0, dev)
     53 #define AGP_NFORCE_PPBTAG(pc)	pci_make_tag(pc, 0, 30, 0)
     54 
     55 #define	SYSCFG		0xC0010010
     56 #define	IORR_BASE0	0xC0010016
     57 #define	IORR_MASK0	0xC0010017
     58 #define	AMD_K7_NUM_IORR	2
     59 
     60 struct agp_nvidia_softc {
     61 	struct agp_softc	agp;
     62 	uint32_t		initial_aperture; /* aperture size at startup */
     63 	struct agp_gatt *	gatt;
     64 
     65 	device_t		dev;		/* AGP Controller */
     66 	pcitag_t		mc1_tag;  /* memory controller func 1 */
     67 	pcitag_t		mc2_tag;  /* memory controller func 2 */
     68 	pcitag_t		bdev_tag;		/* Bridge */
     69 
     70 	uint32_t		wbc_mask;
     71 	int				num_dirs;
     72 	int				num_active_entries;
     73 	off_t			pg_offset;
     74 };
     75 
     76 
     77 static int agp_nvidia_init(struct agp_softc *);
     78 static uint32_t agp_nvidia_get_aperture(struct agp_softc *);
     79 static int agp_nvidia_set_aperture(struct agp_softc *, uint32_t);
     80 static int agp_nvidia_bind_page(struct agp_softc *, off_t, bus_addr_t);
     81 static int agp_nvidia_unbind_page(struct agp_softc *, off_t);
     82 static void agp_nvidia_flush_tlb(struct agp_softc *);
     83 #if 0
     84 static int agp_nvidia_detach(struct agp_softc *);
     85 #endif
     86 static int nvidia_init_iorr(uint32_t, uint32_t);
     87 
     88 
     89 static struct agp_methods agp_nvidia_methods = {
     90 	agp_nvidia_get_aperture,
     91 	agp_nvidia_set_aperture,
     92 	agp_nvidia_bind_page,
     93 	agp_nvidia_unbind_page,
     94 	agp_nvidia_flush_tlb,
     95 	agp_generic_enable,
     96 	agp_generic_alloc_memory,
     97 	agp_generic_free_memory,
     98 	agp_generic_bind_memory,
     99 	agp_generic_unbind_memory,
    100 };
    101 
    102 int
    103 agp_nvidia_attach(device_t parent, device_t self, void *aux)
    104 {
    105 	struct agp_softc *sc = device_private(self);
    106 	struct pci_attach_args *pa = aux;
    107 	struct agp_nvidia_softc *nsc;
    108 	struct agp_gatt *gatt;
    109 
    110 	nsc = malloc(sizeof *nsc, M_AGP, M_WAITOK);
    111 
    112 	switch (PCI_PRODUCT(pa->pa_id)) {
    113 	case PCI_PRODUCT_NVIDIA_NFORCE_PCHB:
    114 		nsc->wbc_mask = AGP_NVIDIA_NFORCE_WBC_MASK;
    115 		break;
    116 	case PCI_PRODUCT_NVIDIA_NFORCE2_PCHB:
    117 		nsc->wbc_mask = AGP_NVIDIA_NFORCE2_WBC_MASK;
    118 		break;
    119 	default:
    120 		/* Should never happen */
    121 		aprint_error_dev(self, "Bad chip id\n");
    122 		return ENODEV;
    123 	}
    124 
    125 	sc->as_chipc = nsc;
    126 	sc->as_methods = &agp_nvidia_methods;
    127 	nsc->mc1_tag = AGP_NFORCE_MEMCTLTAG(pa->pa_pc, 1);
    128 	nsc->mc2_tag = AGP_NFORCE_MEMCTLTAG(pa->pa_pc, 2);
    129 	nsc->bdev_tag = AGP_NFORCE_PPBTAG(pa->pa_pc);
    130 
    131 	pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP, &sc->as_capoff,
    132 	    NULL);
    133 
    134 	if (agp_map_aperture(pa, sc, AGP_APBASE) != 0) {
    135 		aprint_error(": can't map aperture\n");
    136 		free(nsc, M_AGP);
    137 		sc->as_chipc = NULL;
    138 		return ENXIO;
    139 	}
    140 
    141 	nsc->initial_aperture = AGP_GET_APERTURE(sc);
    142 
    143 	for (;;) {
    144 		gatt = agp_alloc_gatt(sc);
    145 		if (gatt)
    146 			break;
    147 
    148 		/*
    149 		 * Probably contigmalloc failure. Try reducing the
    150 		 * aperture so that the gatt size reduces.
    151 		 */
    152 		if (AGP_SET_APERTURE(sc, AGP_GET_APERTURE(sc) / 2)) {
    153 			agp_generic_detach(sc);
    154 			aprint_error(": failed to set aperture\n");
    155 			return ENOMEM;
    156 		}
    157 	}
    158 	nsc->gatt = gatt;
    159 
    160 	return agp_nvidia_init(sc);
    161 }
    162 
    163 static int
    164 agp_nvidia_init(struct agp_softc *sc)
    165 {
    166 	struct agp_nvidia_softc *nsc = sc->as_chipc;
    167 	struct agp_gatt *gatt = nsc->gatt;
    168 	uint32_t apbase;
    169 	uint32_t aplimit;
    170 	uint32_t temp;
    171 	int size, i, error;
    172 
    173 	apbase = sc->as_apaddr;
    174 	aplimit = apbase + AGP_GET_APERTURE(sc) - 1;
    175 	pci_conf_write(sc->as_pc, nsc->mc2_tag, AGP_NVIDIA_2_APBASE, apbase);
    176 	pci_conf_write(sc->as_pc, nsc->mc2_tag, AGP_NVIDIA_2_APLIMIT, aplimit);
    177 	pci_conf_write(sc->as_pc, nsc->bdev_tag, AGP_NVIDIA_3_APBASE, apbase);
    178 	pci_conf_write(sc->as_pc, nsc->bdev_tag, AGP_NVIDIA_3_APLIMIT, aplimit);
    179 
    180 	error = nvidia_init_iorr(apbase, AGP_GET_APERTURE(sc));
    181 	error = 0;
    182 	if (error) {
    183 		aprint_error_dev(sc->as_dev, "Failed to setup IORRs\n");
    184 		agp_generic_detach(sc);
    185 		return ENOMEM;
    186 	}
    187 
    188 	/* directory size is 64k */
    189 	size = AGP_GET_APERTURE(sc) / 1024 / 1024;
    190 	nsc->num_dirs = size / 64;
    191 	nsc->num_active_entries = (size == 32) ? 16384 : ((size * 1024) / 4);
    192 	nsc->pg_offset = 0;
    193 	if (nsc->num_dirs == 0) {
    194 		nsc->num_dirs = 1;
    195 		nsc->num_active_entries /= (64 / size);
    196 		nsc->pg_offset = rounddown2(apbase & (64 * 1024 * 1024 - 1),
    197 		    AGP_GET_APERTURE(sc)) / PAGE_SIZE;
    198 	}
    199 
    200 	/* (G)ATT Base Address */
    201 	for (i = 0; i < 8; i++) {
    202 		pci_conf_write(sc->as_pc, nsc->mc2_tag, AGP_NVIDIA_2_ATTBASE(i),
    203 		    (gatt->ag_physical + (i % nsc->num_dirs) * 64 * 1024) | 1);
    204 	}
    205 
    206 	/* GTLB Control */
    207 	temp = pci_conf_read(sc->as_pc, nsc->mc2_tag, AGP_NVIDIA_2_GARTCTRL);
    208 	pci_conf_write(sc->as_pc, nsc->mc2_tag, AGP_NVIDIA_2_GARTCTRL, temp | 0x11);
    209 
    210 	/* GART Control */
    211 	temp = pci_conf_read(sc->as_pc, sc->as_tag, AGP_NVIDIA_0_APSIZE);
    212 	pci_conf_write(sc->as_pc, sc->as_tag, AGP_NVIDIA_0_APSIZE, temp | 0x100);
    213 
    214 	return 0;
    215 }
    216 
    217 static uint32_t
    218 agp_nvidia_get_aperture(struct agp_softc *sc)
    219 {
    220 	uint8_t apsize;
    221 
    222 	apsize = pci_conf_read(sc->as_pc, sc->as_tag, AGP_NVIDIA_0_APSIZE)
    223 	    & AGP_NVIDIA_0_APSIZE_MASK;
    224 	switch (apsize) {
    225 	case 0: return (512 * 1024 * 1024);
    226 	case 8: return (256 * 1024 * 1024);
    227 	case 12: return (128 * 1024 * 1024);
    228 	case 14: return (64 * 1024 * 1024);
    229 	case 15: return (32 * 1024 * 1024);
    230 	default:
    231 		aprint_error_dev(sc->as_dev, "Invalid aperture setting 0x%x\n",
    232 		    apsize);
    233 		return 0;
    234 	}
    235 }
    236 
    237 static int
    238 agp_nvidia_set_aperture(struct agp_softc *sc, uint32_t aperture)
    239 {
    240 	uint8_t apsize;
    241 	pcireg_t reg;
    242 
    243 	switch (aperture) {
    244 	case (512 * 1024 * 1024): apsize = 0; break;
    245 	case (256 * 1024 * 1024): apsize = 8; break;
    246 	case (128 * 1024 * 1024): apsize = 12; break;
    247 	case (64 * 1024 * 1024): apsize = 14; break;
    248 	case (32 * 1024 * 1024): apsize = 15; break;
    249 	default:
    250 		aprint_error_dev(sc->as_dev, "Invalid aperture size (%uMB)\n",
    251 		    aperture / 1024 / 1024);
    252 		return EINVAL;
    253 	}
    254 
    255 	reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_NVIDIA_0_APSIZE);
    256 	reg = (reg & ~AGP_NVIDIA_0_APSIZE_MASK) | apsize;
    257 	pci_conf_write(sc->as_pc, sc->as_tag, AGP_NVIDIA_0_APSIZE, reg);
    258 	return 0;
    259 }
    260 
    261 static int
    262 agp_nvidia_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical)
    263 {
    264 	struct agp_nvidia_softc *nsc = sc->as_chipc;
    265 	uint32_t index;
    266 
    267 	if (offset >= (nsc->gatt->ag_entries << AGP_PAGE_SHIFT))
    268 		return EINVAL;
    269 
    270 	index = (nsc->pg_offset + offset) >> AGP_PAGE_SHIFT;
    271 	nsc->gatt->ag_virtual[index] = physical | 1;
    272 
    273 	return 0;
    274 }
    275 
    276 static int
    277 agp_nvidia_unbind_page(struct agp_softc *sc, off_t offset)
    278 {
    279 	struct agp_nvidia_softc *nsc = sc->as_chipc;
    280 	uint32_t index;
    281 
    282 	if (offset >= (nsc->gatt->ag_entries << AGP_PAGE_SHIFT))
    283 		return EINVAL;
    284 
    285 	index = (nsc->pg_offset + offset) >> AGP_PAGE_SHIFT;
    286 	nsc->gatt->ag_virtual[index] = 0;
    287 
    288 	return 0;
    289 }
    290 
    291 static void
    292 agp_nvidia_flush_tlb(struct agp_softc *sc)
    293 {
    294 	struct agp_nvidia_softc *nsc = sc->as_chipc;
    295 	uint32_t wbc_reg;
    296 	volatile uint32_t *ag_virtual;
    297 	int i, pages;
    298 
    299 	if (nsc->wbc_mask) {
    300 		wbc_reg = pci_conf_read(sc->as_pc, nsc->mc1_tag, AGP_NVIDIA_1_WBC);
    301 		wbc_reg |= nsc->wbc_mask;
    302 		pci_conf_write(sc->as_pc, nsc->mc1_tag, AGP_NVIDIA_1_WBC, wbc_reg);
    303 
    304 		/* Wait no more than 3 seconds. */
    305 		for (i = 0; i < 3000; i++) {
    306 			wbc_reg = pci_conf_read(sc->as_pc, nsc->mc1_tag, AGP_NVIDIA_1_WBC);
    307 
    308 			if ((nsc->wbc_mask & wbc_reg) == 0)
    309 				break;
    310 
    311 			DELAY(1000);
    312 			preempt_point();
    313 		}
    314 		if (i == 3000)
    315 			aprint_debug_dev(sc->as_dev, "TLB flush took more than 3 seconds.\n");
    316 	}
    317 
    318 	ag_virtual = (volatile uint32_t *)nsc->gatt->ag_virtual;
    319 
    320 	/* Flush TLB entries. */
    321 	pages = nsc->gatt->ag_entries * sizeof(uint32_t) / PAGE_SIZE;
    322 	for (i = 0; i < pages; i++)
    323 		(void)ag_virtual[i * PAGE_SIZE / sizeof(uint32_t)];
    324 	for (i = 0; i < pages; i++)
    325 		(void)ag_virtual[i * PAGE_SIZE / sizeof(uint32_t)];
    326 }
    327 
    328 #if 0
    329 static int
    330 agp_nvidia_detach(struct agp_softc *sc)
    331 {
    332 	int error;
    333 	uint32_t temp;
    334 	struct agp_nvidia_softc *nsc = sc->as_chipc;
    335 
    336 	error = agp_generic_detach(sc);
    337 	if (error)
    338 		return error;
    339 
    340 	/* GART Control */
    341 	temp = pci_conf_read(sc->as_pc, sc->as_tag, AGP_NVIDIA_0_APSIZE);
    342 	pci_conf_write(sc->as_pc, sc->as_tag, AGP_NVIDIA_0_APSIZE, temp & ~(0x100));
    343 
    344 	/* GTLB Control */
    345 	temp = pci_conf_read(sc->as_pc, nsc->mc2_tag, AGP_NVIDIA_2_GARTCTRL);
    346 	pci_conf_write(sc->as_pc, nsc->mc2_tag, AGP_NVIDIA_2_GARTCTRL, temp & ~(0x11));
    347 
    348 	/* Put the aperture back the way it started. */
    349 	AGP_SET_APERTURE(sc, nsc->initial_aperture);
    350 
    351 	/* restore iorr for previous aperture size */
    352 	nvidia_init_iorr(AGP_GET_APERTURE(sc), nsc->initial_aperture);
    353 
    354 	agp_free_gatt(sc, nsc->gatt);
    355 
    356 	return 0;
    357 }
    358 #endif
    359 
    360 static int
    361 nvidia_init_iorr(uint32_t addr, uint32_t size)
    362 {
    363 	uint64_t base, mask, sys;
    364 	uint32_t iorr_addr, free_iorr_addr;
    365 
    366 	/* Find the iorr that is already used for the addr */
    367 	/* If not found, determine the uppermost available iorr */
    368 	free_iorr_addr = AMD_K7_NUM_IORR;
    369 	for (iorr_addr = 0; iorr_addr < AMD_K7_NUM_IORR; iorr_addr++) {
    370 		base = rdmsr(IORR_BASE0 + 2 * iorr_addr);
    371 		mask = rdmsr(IORR_MASK0 + 2 * iorr_addr);
    372 
    373 		if ((base & 0xfffff000ULL) == (addr & 0xfffff000))
    374 			break;
    375 
    376 		if ((mask & 0x00000800ULL) == 0)
    377 			free_iorr_addr = iorr_addr;
    378 	}
    379 
    380 	if (iorr_addr >= AMD_K7_NUM_IORR) {
    381 		iorr_addr = free_iorr_addr;
    382 		if (iorr_addr >= AMD_K7_NUM_IORR)
    383 			return EINVAL;
    384 	}
    385 
    386 	base = (addr & ~0xfff) | 0x18;
    387 	mask = (0xfULL << 32) | rounddown2(0xfffff000, size) | 0x800;
    388 	wrmsr(IORR_BASE0 + 2 * iorr_addr, base);
    389 	wrmsr(IORR_MASK0 + 2 * iorr_addr, mask);
    390 
    391 	sys = rdmsr(SYSCFG);
    392 	sys |= 0x00100000ULL;
    393 	wrmsr(SYSCFG, sys);
    394 
    395 	return 0;
    396 }