agp_amd.c revision 87479
161452Sdfr/*-
261452Sdfr * Copyright (c) 2000 Doug Rabson
361452Sdfr * All rights reserved.
461452Sdfr *
561452Sdfr * Redistribution and use in source and binary forms, with or without
661452Sdfr * modification, are permitted provided that the following conditions
761452Sdfr * are met:
861452Sdfr * 1. Redistributions of source code must retain the above copyright
961452Sdfr *    notice, this list of conditions and the following disclaimer.
1061452Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1161452Sdfr *    notice, this list of conditions and the following disclaimer in the
1261452Sdfr *    documentation and/or other materials provided with the distribution.
1361452Sdfr *
1461452Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1561452Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1661452Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1761452Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1861452Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1961452Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2061452Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2161452Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2261452Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2361452Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2461452Sdfr * SUCH DAMAGE.
2561452Sdfr *
2661452Sdfr *	$FreeBSD: head/sys/dev/agp/agp_amd.c 87479 2001-12-07 05:41:26Z cokane $
2761452Sdfr */
2861452Sdfr
2961452Sdfr#include "opt_bus.h"
3061452Sdfr#include "opt_pci.h"
3161452Sdfr
3261452Sdfr#include <sys/param.h>
3361452Sdfr#include <sys/systm.h>
3461452Sdfr#include <sys/malloc.h>
3561452Sdfr#include <sys/kernel.h>
3661452Sdfr#include <sys/bus.h>
3761452Sdfr#include <sys/lock.h>
3876827Salfred#include <sys/mutex.h>
3979339Sjhb#include <sys/proc.h>
4061452Sdfr
4161452Sdfr#include <pci/pcivar.h>
4261452Sdfr#include <pci/pcireg.h>
4361452Sdfr#include <pci/agppriv.h>
4461452Sdfr#include <pci/agpreg.h>
4561452Sdfr
4661452Sdfr#include <vm/vm.h>
4761452Sdfr#include <vm/vm_object.h>
4861452Sdfr#include <vm/pmap.h>
4961452Sdfr#include <machine/bus.h>
5061452Sdfr#include <machine/resource.h>
5161452Sdfr#include <sys/rman.h>
5261452Sdfr
5361501SdfrMALLOC_DECLARE(M_AGP);
5461501Sdfr
5561452Sdfr#define READ2(off)	bus_space_read_2(sc->bst, sc->bsh, off)
5661452Sdfr#define READ4(off)	bus_space_read_4(sc->bst, sc->bsh, off)
5761452Sdfr#define WRITE2(off,v)	bus_space_write_2(sc->bst, sc->bsh, off, v)
5861452Sdfr#define WRITE4(off,v)	bus_space_write_4(sc->bst, sc->bsh, off, v)
5961452Sdfr
6061501Sdfrstruct agp_amd_gatt {
6161501Sdfr	u_int32_t	ag_entries;
6287479Scokane	u_int32_t      *ag_virtual;	/* virtual address of gatt */
6387479Scokane	vm_offset_t     ag_physical;
6461501Sdfr	u_int32_t      *ag_vdir;	/* virtual address of page dir */
6561501Sdfr	vm_offset_t	ag_pdir;	/* physical address of page dir */
6661501Sdfr};
6761501Sdfr
6861452Sdfrstruct agp_amd_softc {
6961452Sdfr	struct agp_softc agp;
7061452Sdfr	struct resource *regs;	/* memory mapped control registers */
7161452Sdfr	bus_space_tag_t bst;	/* bus_space tag */
7261452Sdfr	bus_space_handle_t bsh;	/* bus_space handle */
7361452Sdfr	u_int32_t	initial_aperture; /* aperture size at startup */
7461501Sdfr	struct agp_amd_gatt *gatt;
7561452Sdfr};
7661452Sdfr
7761501Sdfrstatic struct agp_amd_gatt *
7861501Sdfragp_amd_alloc_gatt(device_t dev)
7961501Sdfr{
8061501Sdfr	u_int32_t apsize = AGP_GET_APERTURE(dev);
8161501Sdfr	u_int32_t entries = apsize >> AGP_PAGE_SHIFT;
8261501Sdfr	struct agp_amd_gatt *gatt;
8387479Scokane	int i, npages, pdir_offset;
8461501Sdfr
8561501Sdfr	if (bootverbose)
8661501Sdfr		device_printf(dev,
8761501Sdfr			      "allocating GATT for aperture of size %dM\n",
8861501Sdfr			      apsize / (1024*1024));
8961501Sdfr
9061501Sdfr	gatt = malloc(sizeof(struct agp_amd_gatt), M_AGP, M_NOWAIT);
9161501Sdfr	if (!gatt)
9261501Sdfr		return 0;
9361501Sdfr
9461501Sdfr	/*
9561501Sdfr	 * The AMD751 uses a page directory to map a non-contiguous
9661501Sdfr	 * gatt so we don't need to use contigmalloc.
9787479Scokane	 * Malloc individual gatt pages and map them into the page
9887479Scokane	 * directory.
9961501Sdfr	 */
10061501Sdfr	gatt->ag_entries = entries;
10161501Sdfr	gatt->ag_virtual = malloc(entries * sizeof(u_int32_t),
10261501Sdfr				  M_AGP, M_NOWAIT);
10361501Sdfr	if (!gatt->ag_virtual) {
10461501Sdfr		if (bootverbose)
10561501Sdfr			device_printf(dev, "allocation failed\n");
10661501Sdfr		free(gatt, M_AGP);
10761501Sdfr		return 0;
10861501Sdfr	}
10961501Sdfr	bzero(gatt->ag_virtual, entries * sizeof(u_int32_t));
11061501Sdfr
11161501Sdfr	/*
11261501Sdfr	 * Allocate the page directory.
11361501Sdfr	 */
11461501Sdfr	gatt->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT);
11587479Scokane	bzero(gatt->ag_vdir, AGP_PAGE_SIZE);
11687479Scokane
11761501Sdfr	if (!gatt->ag_vdir) {
11861501Sdfr		if (bootverbose)
11961501Sdfr			device_printf(dev,
12061501Sdfr				      "failed to allocate page directory\n");
12161501Sdfr		free(gatt->ag_virtual, M_AGP);
12261501Sdfr		free(gatt, M_AGP);
12361501Sdfr		return 0;
12461501Sdfr	}
12561501Sdfr	gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir);
12687479Scokane	if(bootverbose)
12787479Scokane		device_printf(dev, "gatt -> ag_pdir %8x\n",
12887479Scokane				(vm_offset_t)gatt->ag_pdir);
12987479Scokane	/*
13087479Scokane	 * Allocate the gatt pages
13187479Scokane	 */
13287479Scokane	gatt->ag_entries = entries;
13387479Scokane	if(bootverbose)
13487479Scokane		device_printf(dev, "allocating GATT for %d AGP page entries\n",
13587479Scokane			gatt->ag_entries);
13687479Scokane	gatt->ag_virtual = malloc(entries * sizeof(u_int32_t), M_AGP,
13787479Scokane			M_NOWAIT);
13887479Scokane	if(!gatt->ag_virtual) {
13987479Scokane		if(bootverbose)
14087479Scokane			device_printf(dev, "allocation failed\n");
14187479Scokane		free(gatt, M_AGP);
14287479Scokane		return 0;
14387479Scokane	}
14487479Scokane	gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual);
14561501Sdfr
14661501Sdfr	/*
14761501Sdfr	 * Map the pages of the GATT into the page directory.
14887479Scokane	 *
14987479Scokane	 * The GATT page addresses are mapped into the directory offset by
15087479Scokane	 * an amount dependent on the base address of the aperture. This
15187479Scokane	 * is and offset into the page directory, not an offset added to
15287479Scokane	 * the addresses of the gatt pages.
15361501Sdfr	 */
15487479Scokane
15587479Scokane	pdir_offset = pci_read_config(dev, AGP_AMD751_APBASE, 4) >> 22;
15687479Scokane
15761501Sdfr	npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1)
15861501Sdfr		  >> AGP_PAGE_SHIFT);
15987479Scokane
16061501Sdfr	for (i = 0; i < npages; i++) {
16161501Sdfr		vm_offset_t va;
16261501Sdfr		vm_offset_t pa;
16361501Sdfr
16461501Sdfr		va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE;
16561501Sdfr		pa = vtophys(va);
16687479Scokane		gatt->ag_vdir[i + pdir_offset] = pa | 1;
16761501Sdfr	}
16861501Sdfr
16961501Sdfr	/*
17061501Sdfr	 * Make sure the chipset can see everything.
17161501Sdfr	 */
17261501Sdfr	agp_flush_cache();
17361501Sdfr
17461501Sdfr	return gatt;
17561501Sdfr}
17661501Sdfr
17761501Sdfrstatic void
17861501Sdfragp_amd_free_gatt(struct agp_amd_gatt *gatt)
17961501Sdfr{
18061501Sdfr	free(gatt->ag_virtual, M_AGP);
18161501Sdfr	free(gatt->ag_vdir, M_AGP);
18261501Sdfr	free(gatt, M_AGP);
18361501Sdfr}
18461501Sdfr
18561452Sdfrstatic const char*
18661452Sdfragp_amd_match(device_t dev)
18761452Sdfr{
18861452Sdfr	if (pci_get_class(dev) != PCIC_BRIDGE
18961452Sdfr	    || pci_get_subclass(dev) != PCIS_BRIDGE_HOST)
19061452Sdfr		return NULL;
19161452Sdfr
19261452Sdfr	if (agp_find_caps(dev) == 0)
19361452Sdfr		return NULL;
19461452Sdfr
19561452Sdfr	switch (pci_get_devid(dev)) {
19687479Scokane
19783699Scokane	case 0x700e1022:
19883699Scokane		return ("AMD 761 host to AGP bridge");
19987479Scokane
20061452Sdfr	case 0x70061022:
20161452Sdfr		return ("AMD 751 host to AGP bridge");
20287479Scokane
20387479Scokane	case 0x700c1022:
20487479Scokane		return ("AMD 762 host to AGP bridge");
20587479Scokane
20661452Sdfr	};
20761452Sdfr
20861452Sdfr	return NULL;
20961452Sdfr}
21061452Sdfr
21161452Sdfrstatic int
21261452Sdfragp_amd_probe(device_t dev)
21361452Sdfr{
21461452Sdfr	const char *desc;
21561452Sdfr
21661452Sdfr	desc = agp_amd_match(dev);
21761452Sdfr	if (desc) {
21861452Sdfr		device_verbose(dev);
21961452Sdfr		device_set_desc(dev, desc);
22061452Sdfr		return 0;
22161452Sdfr	}
22261452Sdfr
22361452Sdfr	return ENXIO;
22461452Sdfr}
22561452Sdfr
22661452Sdfrstatic int
22761452Sdfragp_amd_attach(device_t dev)
22861452Sdfr{
22961452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
23061501Sdfr	struct agp_amd_gatt *gatt;
23161452Sdfr	int error, rid;
23261452Sdfr
23361452Sdfr	error = agp_generic_attach(dev);
23461452Sdfr	if (error)
23561452Sdfr		return error;
23661452Sdfr
23761452Sdfr	rid = AGP_AMD751_REGISTERS;
23861452Sdfr	sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
23961452Sdfr				      0, ~0, 1, RF_ACTIVE);
24061452Sdfr	if (!sc->regs) {
24161452Sdfr		agp_generic_detach(dev);
24261452Sdfr		return ENOMEM;
24361452Sdfr	}
24461452Sdfr
24561452Sdfr	sc->bst = rman_get_bustag(sc->regs);
24661452Sdfr	sc->bsh = rman_get_bushandle(sc->regs);
24761452Sdfr
24861452Sdfr	sc->initial_aperture = AGP_GET_APERTURE(dev);
24961452Sdfr
25061452Sdfr	for (;;) {
25161501Sdfr		gatt = agp_amd_alloc_gatt(dev);
25261452Sdfr		if (gatt)
25361452Sdfr			break;
25461452Sdfr
25561452Sdfr		/*
25661452Sdfr		 * Probably contigmalloc failure. Try reducing the
25761452Sdfr		 * aperture so that the gatt size reduces.
25861452Sdfr		 */
25961452Sdfr		if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2))
26061452Sdfr			return ENOMEM;
26161452Sdfr	}
26261452Sdfr	sc->gatt = gatt;
26361452Sdfr
26461452Sdfr	/* Install the gatt. */
26561501Sdfr	WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir);
26661452Sdfr
26761452Sdfr	/* Enable synchronisation between host and agp. */
26861501Sdfr	pci_write_config(dev,
26961501Sdfr			 AGP_AMD751_MODECTRL,
27061501Sdfr			 AGP_AMD751_MODECTRL_SYNEN, 1);
27161452Sdfr
27261501Sdfr	/* Set indexing mode for two-level and enable page dir cache */
27361501Sdfr	pci_write_config(dev,
27461501Sdfr			 AGP_AMD751_MODECTRL2,
27561501Sdfr			 AGP_AMD751_MODECTRL2_GPDCE, 1);
27661501Sdfr
27761452Sdfr	/* Enable the TLB and flush */
27861452Sdfr	WRITE2(AGP_AMD751_STATUS,
27961452Sdfr	       READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE);
28061452Sdfr	AGP_FLUSH_TLB(dev);
28161452Sdfr
28261501Sdfr	return 0;
28361452Sdfr}
28461452Sdfr
28561452Sdfrstatic int
28661452Sdfragp_amd_detach(device_t dev)
28761452Sdfr{
28861452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
28961503Sdfr	int error;
29061452Sdfr
29161503Sdfr	error = agp_generic_detach(dev);
29261503Sdfr	if (error)
29361503Sdfr		return error;
29461503Sdfr
29561452Sdfr	/* Disable the TLB.. */
29661452Sdfr	WRITE2(AGP_AMD751_STATUS,
29761452Sdfr	       READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE);
29861452Sdfr
29961452Sdfr	/* Disable host-agp sync */
30061452Sdfr	pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1);
30161452Sdfr
30261452Sdfr	/* Clear the GATT base */
30361452Sdfr	WRITE4(AGP_AMD751_ATTBASE, 0);
30461452Sdfr
30561452Sdfr	/* Put the aperture back the way it started. */
30661452Sdfr	AGP_SET_APERTURE(dev, sc->initial_aperture);
30761452Sdfr
30861501Sdfr	agp_amd_free_gatt(sc->gatt);
30961503Sdfr
31061503Sdfr	bus_release_resource(dev, SYS_RES_MEMORY,
31161503Sdfr			     AGP_AMD751_REGISTERS, sc->regs);
31261503Sdfr
31361452Sdfr	return 0;
31461452Sdfr}
31561452Sdfr
31661452Sdfrstatic u_int32_t
31761452Sdfragp_amd_get_aperture(device_t dev)
31861452Sdfr{
31961452Sdfr	int vas;
32061452Sdfr
32161452Sdfr	/*
32261452Sdfr	 * The aperture size is equal to 32M<<vas.
32361452Sdfr	 */
32461452Sdfr	vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1;
32561452Sdfr	return (32*1024*1024) << vas;
32661452Sdfr}
32761452Sdfr
32861452Sdfrstatic int
32961452Sdfragp_amd_set_aperture(device_t dev, u_int32_t aperture)
33061452Sdfr{
33161452Sdfr	int vas;
33261452Sdfr
33361452Sdfr	/*
33461452Sdfr	 * Check for a power of two and make sure its within the
33561452Sdfr	 * programmable range.
33661452Sdfr	 */
33761452Sdfr	if (aperture & (aperture - 1)
33861452Sdfr	    || aperture < 32*1024*1024
33961452Sdfr	    || aperture > 2U*1024*1024*1024)
34061452Sdfr		return EINVAL;
34161452Sdfr
34261452Sdfr	vas = ffs(aperture / 32*1024*1024) - 1;
34361452Sdfr
34487479Scokane	/*
34587479Scokane	 * While the size register is bits 1-3 of APCTRL, bit 0 must be
34687479Scokane	 * set for the size value to be 'valid'
34787479Scokane	 */
34861452Sdfr	pci_write_config(dev, AGP_AMD751_APCTRL,
34987479Scokane			 (((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06)
35087479Scokane			  | ((vas << 1) | 1))), 1);
35161452Sdfr
35261452Sdfr	return 0;
35361452Sdfr}
35461452Sdfr
35561452Sdfrstatic int
35661452Sdfragp_amd_bind_page(device_t dev, int offset, vm_offset_t physical)
35761452Sdfr{
35861452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
35961452Sdfr
36061452Sdfr	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
36161452Sdfr		return EINVAL;
36261452Sdfr
36361452Sdfr	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1;
36487479Scokane
36587479Scokane	/* invalidate the cache */
36687479Scokane	AGP_FLUSH_TLB(dev);
36761452Sdfr	return 0;
36861452Sdfr}
36961452Sdfr
37061452Sdfrstatic int
37161452Sdfragp_amd_unbind_page(device_t dev, int offset)
37261452Sdfr{
37361452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
37461452Sdfr
37561452Sdfr	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
37661452Sdfr		return EINVAL;
37761452Sdfr
37861452Sdfr	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0;
37961452Sdfr	return 0;
38061452Sdfr}
38161452Sdfr
38261452Sdfrstatic void
38361452Sdfragp_amd_flush_tlb(device_t dev)
38461452Sdfr{
38561452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
38661452Sdfr
38761452Sdfr	/* Set the cache invalidate bit and wait for the chipset to clear */
38861452Sdfr	WRITE4(AGP_AMD751_TLBCTRL, 1);
38961452Sdfr	do {
39061452Sdfr		DELAY(1);
39161452Sdfr	} while (READ4(AGP_AMD751_TLBCTRL));
39261452Sdfr}
39361452Sdfr
39461452Sdfrstatic device_method_t agp_amd_methods[] = {
39561452Sdfr	/* Device interface */
39661452Sdfr	DEVMETHOD(device_probe,		agp_amd_probe),
39761452Sdfr	DEVMETHOD(device_attach,	agp_amd_attach),
39861452Sdfr	DEVMETHOD(device_detach,	agp_amd_detach),
39961452Sdfr	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
40061452Sdfr	DEVMETHOD(device_suspend,	bus_generic_suspend),
40161452Sdfr	DEVMETHOD(device_resume,	bus_generic_resume),
40261452Sdfr
40361452Sdfr	/* AGP interface */
40461452Sdfr	DEVMETHOD(agp_get_aperture,	agp_amd_get_aperture),
40561452Sdfr	DEVMETHOD(agp_set_aperture,	agp_amd_set_aperture),
40661452Sdfr	DEVMETHOD(agp_bind_page,	agp_amd_bind_page),
40761452Sdfr	DEVMETHOD(agp_unbind_page,	agp_amd_unbind_page),
40861452Sdfr	DEVMETHOD(agp_flush_tlb,	agp_amd_flush_tlb),
40961452Sdfr	DEVMETHOD(agp_enable,		agp_generic_enable),
41061452Sdfr	DEVMETHOD(agp_alloc_memory,	agp_generic_alloc_memory),
41161452Sdfr	DEVMETHOD(agp_free_memory,	agp_generic_free_memory),
41261452Sdfr	DEVMETHOD(agp_bind_memory,	agp_generic_bind_memory),
41361452Sdfr	DEVMETHOD(agp_unbind_memory,	agp_generic_unbind_memory),
41461452Sdfr
41561452Sdfr	{ 0, 0 }
41661452Sdfr};
41761452Sdfr
41861452Sdfrstatic driver_t agp_amd_driver = {
41961452Sdfr	"agp",
42061452Sdfr	agp_amd_methods,
42161452Sdfr	sizeof(struct agp_amd_softc),
42261452Sdfr};
42361452Sdfr
42461452Sdfrstatic devclass_t agp_devclass;
42561452Sdfr
42661452SdfrDRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0);
427