agp_amd.c revision 76827
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 76827 2001-05-19 01:28:09Z alfred $
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>
3961452Sdfr
4061452Sdfr#include <pci/pcivar.h>
4161452Sdfr#include <pci/pcireg.h>
4261452Sdfr#include <pci/agppriv.h>
4361452Sdfr#include <pci/agpreg.h>
4461452Sdfr
4561452Sdfr#include <vm/vm.h>
4661452Sdfr#include <vm/vm_object.h>
4761452Sdfr#include <vm/pmap.h>
4861452Sdfr#include <machine/bus.h>
4961452Sdfr#include <machine/resource.h>
5061452Sdfr#include <sys/rman.h>
5161452Sdfr
5261501SdfrMALLOC_DECLARE(M_AGP);
5361501Sdfr
5461452Sdfr#define READ2(off)	bus_space_read_2(sc->bst, sc->bsh, off)
5561452Sdfr#define READ4(off)	bus_space_read_4(sc->bst, sc->bsh, off)
5661452Sdfr#define WRITE2(off,v)	bus_space_write_2(sc->bst, sc->bsh, off, v)
5761452Sdfr#define WRITE4(off,v)	bus_space_write_4(sc->bst, sc->bsh, off, v)
5861452Sdfr
5961501Sdfrstruct agp_amd_gatt {
6061501Sdfr	u_int32_t	ag_entries;
6161501Sdfr	u_int32_t      *ag_vdir;	/* virtual address of page dir */
6261501Sdfr	vm_offset_t	ag_pdir;	/* physical address of page dir */
6361501Sdfr	u_int32_t      *ag_virtual;	/* virtual address of gatt */
6461501Sdfr};
6561501Sdfr
6661452Sdfrstruct agp_amd_softc {
6761452Sdfr	struct agp_softc agp;
6861452Sdfr	struct resource *regs;	/* memory mapped control registers */
6961452Sdfr	bus_space_tag_t bst;	/* bus_space tag */
7061452Sdfr	bus_space_handle_t bsh;	/* bus_space handle */
7161452Sdfr	u_int32_t	initial_aperture; /* aperture size at startup */
7261501Sdfr	struct agp_amd_gatt *gatt;
7361452Sdfr};
7461452Sdfr
7561501Sdfrstatic struct agp_amd_gatt *
7661501Sdfragp_amd_alloc_gatt(device_t dev)
7761501Sdfr{
7861501Sdfr	u_int32_t apsize = AGP_GET_APERTURE(dev);
7961501Sdfr	u_int32_t entries = apsize >> AGP_PAGE_SHIFT;
8061501Sdfr	struct agp_amd_gatt *gatt;
8161501Sdfr	int i, npages;
8261501Sdfr
8361501Sdfr	if (bootverbose)
8461501Sdfr		device_printf(dev,
8561501Sdfr			      "allocating GATT for aperture of size %dM\n",
8661501Sdfr			      apsize / (1024*1024));
8761501Sdfr
8861501Sdfr	gatt = malloc(sizeof(struct agp_amd_gatt), M_AGP, M_NOWAIT);
8961501Sdfr	if (!gatt)
9061501Sdfr		return 0;
9161501Sdfr
9261501Sdfr	/*
9361501Sdfr	 * The AMD751 uses a page directory to map a non-contiguous
9461501Sdfr	 * gatt so we don't need to use contigmalloc.
9561501Sdfr	 */
9661501Sdfr	gatt->ag_entries = entries;
9761501Sdfr	gatt->ag_virtual = malloc(entries * sizeof(u_int32_t),
9861501Sdfr				  M_AGP, M_NOWAIT);
9961501Sdfr	if (!gatt->ag_virtual) {
10061501Sdfr		if (bootverbose)
10161501Sdfr			device_printf(dev, "allocation failed\n");
10261501Sdfr		free(gatt, M_AGP);
10361501Sdfr		return 0;
10461501Sdfr	}
10561501Sdfr	bzero(gatt->ag_virtual, entries * sizeof(u_int32_t));
10661501Sdfr
10761501Sdfr	/*
10861501Sdfr	 * Allocate the page directory.
10961501Sdfr	 */
11061501Sdfr	gatt->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT);
11161501Sdfr	if (!gatt->ag_vdir) {
11261501Sdfr		if (bootverbose)
11361501Sdfr			device_printf(dev,
11461501Sdfr				      "failed to allocate page directory\n");
11561501Sdfr		free(gatt->ag_virtual, M_AGP);
11661501Sdfr		free(gatt, M_AGP);
11761501Sdfr		return 0;
11861501Sdfr	}
11961501Sdfr	bzero(gatt->ag_vdir, AGP_PAGE_SIZE);
12061501Sdfr	gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir);
12161501Sdfr	gatt->ag_pdir = vtophys(gatt->ag_virtual);
12261501Sdfr
12361501Sdfr	/*
12461501Sdfr	 * Map the pages of the GATT into the page directory.
12561501Sdfr	 */
12661501Sdfr	npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1)
12761501Sdfr		  >> AGP_PAGE_SHIFT);
12861501Sdfr	for (i = 0; i < npages; i++) {
12961501Sdfr		vm_offset_t va;
13061501Sdfr		vm_offset_t pa;
13161501Sdfr
13261501Sdfr		va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE;
13361501Sdfr		pa = vtophys(va);
13461501Sdfr		gatt->ag_vdir[i] = pa | 1;
13561501Sdfr	}
13661501Sdfr
13761501Sdfr	/*
13861501Sdfr	 * Make sure the chipset can see everything.
13961501Sdfr	 */
14061501Sdfr	agp_flush_cache();
14161501Sdfr
14261501Sdfr	return gatt;
14361501Sdfr}
14461501Sdfr
14561501Sdfrstatic void
14661501Sdfragp_amd_free_gatt(struct agp_amd_gatt *gatt)
14761501Sdfr{
14861501Sdfr	free(gatt->ag_virtual, M_AGP);
14961501Sdfr	free(gatt->ag_vdir, M_AGP);
15061501Sdfr	free(gatt, M_AGP);
15161501Sdfr}
15261501Sdfr
15361452Sdfrstatic const char*
15461452Sdfragp_amd_match(device_t dev)
15561452Sdfr{
15661452Sdfr	if (pci_get_class(dev) != PCIC_BRIDGE
15761452Sdfr	    || pci_get_subclass(dev) != PCIS_BRIDGE_HOST)
15861452Sdfr		return NULL;
15961452Sdfr
16061452Sdfr	if (agp_find_caps(dev) == 0)
16161452Sdfr		return NULL;
16261452Sdfr
16361452Sdfr	switch (pci_get_devid(dev)) {
16461452Sdfr	case 0x70061022:
16561452Sdfr		return ("AMD 751 host to AGP bridge");
16661452Sdfr	};
16761452Sdfr
16861452Sdfr	return NULL;
16961452Sdfr}
17061452Sdfr
17161452Sdfrstatic int
17261452Sdfragp_amd_probe(device_t dev)
17361452Sdfr{
17461452Sdfr	const char *desc;
17561452Sdfr
17661452Sdfr	desc = agp_amd_match(dev);
17761452Sdfr	if (desc) {
17861452Sdfr		device_verbose(dev);
17961452Sdfr		device_set_desc(dev, desc);
18061452Sdfr		return 0;
18161452Sdfr	}
18261452Sdfr
18361452Sdfr	return ENXIO;
18461452Sdfr}
18561452Sdfr
18661452Sdfrstatic int
18761452Sdfragp_amd_attach(device_t dev)
18861452Sdfr{
18961452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
19061501Sdfr	struct agp_amd_gatt *gatt;
19161452Sdfr	int error, rid;
19261452Sdfr
19361452Sdfr	error = agp_generic_attach(dev);
19461452Sdfr	if (error)
19561452Sdfr		return error;
19661452Sdfr
19761452Sdfr	rid = AGP_AMD751_REGISTERS;
19861452Sdfr	sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
19961452Sdfr				      0, ~0, 1, RF_ACTIVE);
20061452Sdfr	if (!sc->regs) {
20161452Sdfr		agp_generic_detach(dev);
20261452Sdfr		return ENOMEM;
20361452Sdfr	}
20461452Sdfr
20561452Sdfr	sc->bst = rman_get_bustag(sc->regs);
20661452Sdfr	sc->bsh = rman_get_bushandle(sc->regs);
20761452Sdfr
20861452Sdfr	sc->initial_aperture = AGP_GET_APERTURE(dev);
20961452Sdfr
21061452Sdfr	for (;;) {
21161501Sdfr		gatt = agp_amd_alloc_gatt(dev);
21261452Sdfr		if (gatt)
21361452Sdfr			break;
21461452Sdfr
21561452Sdfr		/*
21661452Sdfr		 * Probably contigmalloc failure. Try reducing the
21761452Sdfr		 * aperture so that the gatt size reduces.
21861452Sdfr		 */
21961452Sdfr		if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2))
22061452Sdfr			return ENOMEM;
22161452Sdfr	}
22261452Sdfr	sc->gatt = gatt;
22361452Sdfr
22461452Sdfr	/* Install the gatt. */
22561501Sdfr	WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir);
22661452Sdfr
22761452Sdfr	/* Enable synchronisation between host and agp. */
22861501Sdfr	pci_write_config(dev,
22961501Sdfr			 AGP_AMD751_MODECTRL,
23061501Sdfr			 AGP_AMD751_MODECTRL_SYNEN, 1);
23161452Sdfr
23261501Sdfr	/* Set indexing mode for two-level and enable page dir cache */
23361501Sdfr	pci_write_config(dev,
23461501Sdfr			 AGP_AMD751_MODECTRL2,
23561501Sdfr			 AGP_AMD751_MODECTRL2_GPDCE, 1);
23661501Sdfr
23761452Sdfr	/* Enable the TLB and flush */
23861452Sdfr	WRITE2(AGP_AMD751_STATUS,
23961452Sdfr	       READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE);
24061452Sdfr	AGP_FLUSH_TLB(dev);
24161452Sdfr
24261501Sdfr	return 0;
24361452Sdfr}
24461452Sdfr
24561452Sdfrstatic int
24661452Sdfragp_amd_detach(device_t dev)
24761452Sdfr{
24861452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
24961503Sdfr	int error;
25061452Sdfr
25161503Sdfr	error = agp_generic_detach(dev);
25261503Sdfr	if (error)
25361503Sdfr		return error;
25461503Sdfr
25561452Sdfr	/* Disable the TLB.. */
25661452Sdfr	WRITE2(AGP_AMD751_STATUS,
25761452Sdfr	       READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE);
25861452Sdfr
25961452Sdfr	/* Disable host-agp sync */
26061452Sdfr	pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1);
26161452Sdfr
26261452Sdfr	/* Clear the GATT base */
26361452Sdfr	WRITE4(AGP_AMD751_ATTBASE, 0);
26461452Sdfr
26561452Sdfr	/* Put the aperture back the way it started. */
26661452Sdfr	AGP_SET_APERTURE(dev, sc->initial_aperture);
26761452Sdfr
26861501Sdfr	agp_amd_free_gatt(sc->gatt);
26961503Sdfr
27061503Sdfr	bus_release_resource(dev, SYS_RES_MEMORY,
27161503Sdfr			     AGP_AMD751_REGISTERS, sc->regs);
27261503Sdfr
27361452Sdfr	return 0;
27461452Sdfr}
27561452Sdfr
27661452Sdfrstatic u_int32_t
27761452Sdfragp_amd_get_aperture(device_t dev)
27861452Sdfr{
27961452Sdfr	int vas;
28061452Sdfr
28161452Sdfr	/*
28261452Sdfr	 * The aperture size is equal to 32M<<vas.
28361452Sdfr	 */
28461452Sdfr	vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1;
28561452Sdfr	return (32*1024*1024) << vas;
28661452Sdfr}
28761452Sdfr
28861452Sdfrstatic int
28961452Sdfragp_amd_set_aperture(device_t dev, u_int32_t aperture)
29061452Sdfr{
29161452Sdfr	int vas;
29261452Sdfr
29361452Sdfr	/*
29461452Sdfr	 * Check for a power of two and make sure its within the
29561452Sdfr	 * programmable range.
29661452Sdfr	 */
29761452Sdfr	if (aperture & (aperture - 1)
29861452Sdfr	    || aperture < 32*1024*1024
29961452Sdfr	    || aperture > 2U*1024*1024*1024)
30061452Sdfr		return EINVAL;
30161452Sdfr
30261452Sdfr	vas = ffs(aperture / 32*1024*1024) - 1;
30361452Sdfr
30461452Sdfr	pci_write_config(dev, AGP_AMD751_APCTRL,
30561452Sdfr			 ((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06)
30661452Sdfr			  | vas << 1), 1);
30761452Sdfr
30861452Sdfr	return 0;
30961452Sdfr}
31061452Sdfr
31161452Sdfrstatic int
31261452Sdfragp_amd_bind_page(device_t dev, int offset, vm_offset_t physical)
31361452Sdfr{
31461452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
31561452Sdfr
31661452Sdfr	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
31761452Sdfr		return EINVAL;
31861452Sdfr
31961452Sdfr	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1;
32061452Sdfr	return 0;
32161452Sdfr}
32261452Sdfr
32361452Sdfrstatic int
32461452Sdfragp_amd_unbind_page(device_t dev, int offset)
32561452Sdfr{
32661452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
32761452Sdfr
32861452Sdfr	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
32961452Sdfr		return EINVAL;
33061452Sdfr
33161452Sdfr	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0;
33261452Sdfr	return 0;
33361452Sdfr}
33461452Sdfr
33561452Sdfrstatic void
33661452Sdfragp_amd_flush_tlb(device_t dev)
33761452Sdfr{
33861452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
33961452Sdfr
34061452Sdfr	/* Set the cache invalidate bit and wait for the chipset to clear */
34161452Sdfr	WRITE4(AGP_AMD751_TLBCTRL, 1);
34261452Sdfr	do {
34361452Sdfr		DELAY(1);
34461452Sdfr	} while (READ4(AGP_AMD751_TLBCTRL));
34561452Sdfr}
34661452Sdfr
34761452Sdfrstatic device_method_t agp_amd_methods[] = {
34861452Sdfr	/* Device interface */
34961452Sdfr	DEVMETHOD(device_probe,		agp_amd_probe),
35061452Sdfr	DEVMETHOD(device_attach,	agp_amd_attach),
35161452Sdfr	DEVMETHOD(device_detach,	agp_amd_detach),
35261452Sdfr	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
35361452Sdfr	DEVMETHOD(device_suspend,	bus_generic_suspend),
35461452Sdfr	DEVMETHOD(device_resume,	bus_generic_resume),
35561452Sdfr
35661452Sdfr	/* AGP interface */
35761452Sdfr	DEVMETHOD(agp_get_aperture,	agp_amd_get_aperture),
35861452Sdfr	DEVMETHOD(agp_set_aperture,	agp_amd_set_aperture),
35961452Sdfr	DEVMETHOD(agp_bind_page,	agp_amd_bind_page),
36061452Sdfr	DEVMETHOD(agp_unbind_page,	agp_amd_unbind_page),
36161452Sdfr	DEVMETHOD(agp_flush_tlb,	agp_amd_flush_tlb),
36261452Sdfr	DEVMETHOD(agp_enable,		agp_generic_enable),
36361452Sdfr	DEVMETHOD(agp_alloc_memory,	agp_generic_alloc_memory),
36461452Sdfr	DEVMETHOD(agp_free_memory,	agp_generic_free_memory),
36561452Sdfr	DEVMETHOD(agp_bind_memory,	agp_generic_bind_memory),
36661452Sdfr	DEVMETHOD(agp_unbind_memory,	agp_generic_unbind_memory),
36761452Sdfr
36861452Sdfr	{ 0, 0 }
36961452Sdfr};
37061452Sdfr
37161452Sdfrstatic driver_t agp_amd_driver = {
37261452Sdfr	"agp",
37361452Sdfr	agp_amd_methods,
37461452Sdfr	sizeof(struct agp_amd_softc),
37561452Sdfr};
37661452Sdfr
37761452Sdfrstatic devclass_t agp_devclass;
37861452Sdfr
37961452SdfrDRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0);
380