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
27116192Sobrien#include <sys/cdefs.h>
28116192Sobrien__FBSDID("$FreeBSD$");
29116192Sobrien
3061452Sdfr#include "opt_bus.h"
3161452Sdfr
3261452Sdfr#include <sys/param.h>
3361452Sdfr#include <sys/systm.h>
3461452Sdfr#include <sys/malloc.h>
3561452Sdfr#include <sys/kernel.h>
36129878Sphk#include <sys/module.h>
3761452Sdfr#include <sys/bus.h>
3861452Sdfr#include <sys/lock.h>
3976827Salfred#include <sys/mutex.h>
4079339Sjhb#include <sys/proc.h>
4161452Sdfr
42173573Sjhb#include <dev/agp/agppriv.h>
43173573Sjhb#include <dev/agp/agpreg.h>
44119288Simp#include <dev/pci/pcivar.h>
45119288Simp#include <dev/pci/pcireg.h>
4661452Sdfr
4761452Sdfr#include <vm/vm.h>
4861452Sdfr#include <vm/vm_object.h>
4961452Sdfr#include <vm/pmap.h>
5061452Sdfr#include <machine/bus.h>
5161452Sdfr#include <machine/resource.h>
5261452Sdfr#include <sys/rman.h>
5361452Sdfr
5461501SdfrMALLOC_DECLARE(M_AGP);
5561501Sdfr
5661452Sdfr#define READ2(off)	bus_space_read_2(sc->bst, sc->bsh, off)
5761452Sdfr#define READ4(off)	bus_space_read_4(sc->bst, sc->bsh, off)
5861452Sdfr#define WRITE2(off,v)	bus_space_write_2(sc->bst, sc->bsh, off, v)
5961452Sdfr#define WRITE4(off,v)	bus_space_write_4(sc->bst, sc->bsh, off, v)
6061452Sdfr
6161501Sdfrstruct agp_amd_gatt {
6261501Sdfr	u_int32_t	ag_entries;
6387479Scokane	u_int32_t      *ag_virtual;	/* virtual address of gatt */
6487479Scokane	vm_offset_t     ag_physical;
6561501Sdfr	u_int32_t      *ag_vdir;	/* virtual address of page dir */
6661501Sdfr	vm_offset_t	ag_pdir;	/* physical address of page dir */
6761501Sdfr};
6861501Sdfr
6961452Sdfrstruct agp_amd_softc {
70133851Sobrien	struct agp_softc	agp;
71133851Sobrien	struct resource	       *regs;	/* memory mapped control registers */
72133851Sobrien	bus_space_tag_t		bst;	/* bus_space tag */
73133851Sobrien	bus_space_handle_t	bsh;	/* bus_space handle */
74133851Sobrien	u_int32_t		initial_aperture; /* aperture size at startup */
75133851Sobrien	struct agp_amd_gatt    *gatt;
7661452Sdfr};
7761452Sdfr
7861501Sdfrstatic struct agp_amd_gatt *
7961501Sdfragp_amd_alloc_gatt(device_t dev)
8061501Sdfr{
8161501Sdfr	u_int32_t apsize = AGP_GET_APERTURE(dev);
8261501Sdfr	u_int32_t entries = apsize >> AGP_PAGE_SHIFT;
8361501Sdfr	struct agp_amd_gatt *gatt;
8487479Scokane	int i, npages, pdir_offset;
8561501Sdfr
8661501Sdfr	if (bootverbose)
8761501Sdfr		device_printf(dev,
8861501Sdfr			      "allocating GATT for aperture of size %dM\n",
8961501Sdfr			      apsize / (1024*1024));
9061501Sdfr
9161501Sdfr	gatt = malloc(sizeof(struct agp_amd_gatt), M_AGP, M_NOWAIT);
9261501Sdfr	if (!gatt)
9361501Sdfr		return 0;
9461501Sdfr
9561501Sdfr	/*
9661501Sdfr	 * The AMD751 uses a page directory to map a non-contiguous
9761501Sdfr	 * gatt so we don't need to use contigmalloc.
9887479Scokane	 * Malloc individual gatt pages and map them into the page
9987479Scokane	 * directory.
10061501Sdfr	 */
10161501Sdfr	gatt->ag_entries = entries;
10261501Sdfr	gatt->ag_virtual = malloc(entries * sizeof(u_int32_t),
10361501Sdfr				  M_AGP, M_NOWAIT);
10461501Sdfr	if (!gatt->ag_virtual) {
10561501Sdfr		if (bootverbose)
10661501Sdfr			device_printf(dev, "allocation failed\n");
10761501Sdfr		free(gatt, M_AGP);
10861501Sdfr		return 0;
10961501Sdfr	}
11061501Sdfr	bzero(gatt->ag_virtual, entries * sizeof(u_int32_t));
11161501Sdfr
11261501Sdfr	/*
11361501Sdfr	 * Allocate the page directory.
11461501Sdfr	 */
11561501Sdfr	gatt->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT);
11661501Sdfr	if (!gatt->ag_vdir) {
11761501Sdfr		if (bootverbose)
11861501Sdfr			device_printf(dev,
11961501Sdfr				      "failed to allocate page directory\n");
12061501Sdfr		free(gatt->ag_virtual, M_AGP);
12161501Sdfr		free(gatt, M_AGP);
12261501Sdfr		return 0;
12361501Sdfr	}
12494790Scokane	bzero(gatt->ag_vdir, AGP_PAGE_SIZE);
12594790Scokane
12661501Sdfr	gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir);
12787479Scokane	if(bootverbose)
128105145Smarcel		device_printf(dev, "gatt -> ag_pdir %#lx\n",
129105145Smarcel		    (u_long)gatt->ag_pdir);
13087479Scokane	/*
13187479Scokane	 * Allocate the gatt pages
13287479Scokane	 */
13387479Scokane	gatt->ag_entries = entries;
13487479Scokane	if(bootverbose)
13587479Scokane		device_printf(dev, "allocating GATT for %d AGP page entries\n",
13687479Scokane			gatt->ag_entries);
13794790Scokane
13887479Scokane	gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual);
13961501Sdfr
14061501Sdfr	/*
14161501Sdfr	 * Map the pages of the GATT into the page directory.
14287479Scokane	 *
14387479Scokane	 * The GATT page addresses are mapped into the directory offset by
14487479Scokane	 * an amount dependent on the base address of the aperture. This
14587479Scokane	 * is and offset into the page directory, not an offset added to
14687479Scokane	 * the addresses of the gatt pages.
14761501Sdfr	 */
14887479Scokane
14987479Scokane	pdir_offset = pci_read_config(dev, AGP_AMD751_APBASE, 4) >> 22;
15087479Scokane
15161501Sdfr	npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1)
15261501Sdfr		  >> AGP_PAGE_SHIFT);
15387479Scokane
15461501Sdfr	for (i = 0; i < npages; i++) {
15561501Sdfr		vm_offset_t va;
15661501Sdfr		vm_offset_t pa;
15761501Sdfr
15861501Sdfr		va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE;
15961501Sdfr		pa = vtophys(va);
16087479Scokane		gatt->ag_vdir[i + pdir_offset] = pa | 1;
16161501Sdfr	}
16261501Sdfr
16361501Sdfr	/*
16461501Sdfr	 * Make sure the chipset can see everything.
16561501Sdfr	 */
16661501Sdfr	agp_flush_cache();
16761501Sdfr
16861501Sdfr	return gatt;
16961501Sdfr}
17061501Sdfr
17161501Sdfrstatic void
17261501Sdfragp_amd_free_gatt(struct agp_amd_gatt *gatt)
17361501Sdfr{
17461501Sdfr	free(gatt->ag_virtual, M_AGP);
17561501Sdfr	free(gatt->ag_vdir, M_AGP);
17661501Sdfr	free(gatt, M_AGP);
17761501Sdfr}
17861501Sdfr
17961452Sdfrstatic const char*
18061452Sdfragp_amd_match(device_t dev)
18161452Sdfr{
18261452Sdfr	if (pci_get_class(dev) != PCIC_BRIDGE
18361452Sdfr	    || pci_get_subclass(dev) != PCIS_BRIDGE_HOST)
18461452Sdfr		return NULL;
18561452Sdfr
18661452Sdfr	if (agp_find_caps(dev) == 0)
18761452Sdfr		return NULL;
18861452Sdfr
18961452Sdfr	switch (pci_get_devid(dev)) {
190133851Sobrien	case 0x70061022:
191133851Sobrien		return ("AMD 751 host to AGP bridge");
19283699Scokane	case 0x700e1022:
19383699Scokane		return ("AMD 761 host to AGP bridge");
19487479Scokane	case 0x700c1022:
19587479Scokane		return ("AMD 762 host to AGP bridge");
19661452Sdfr	};
19761452Sdfr
19861452Sdfr	return NULL;
19961452Sdfr}
20061452Sdfr
20161452Sdfrstatic int
20261452Sdfragp_amd_probe(device_t dev)
20361452Sdfr{
20461452Sdfr	const char *desc;
20561452Sdfr
206127815Snjl	if (resource_disabled("agp", device_get_unit(dev)))
207127815Snjl		return (ENXIO);
20861452Sdfr	desc = agp_amd_match(dev);
20961452Sdfr	if (desc) {
21061452Sdfr		device_set_desc(dev, desc);
211142398Simp		return BUS_PROBE_DEFAULT;
21261452Sdfr	}
21361452Sdfr
21461452Sdfr	return ENXIO;
21561452Sdfr}
21661452Sdfr
21761452Sdfrstatic int
21861452Sdfragp_amd_attach(device_t dev)
21961452Sdfr{
22061452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
22161501Sdfr	struct agp_amd_gatt *gatt;
22261452Sdfr	int error, rid;
22361452Sdfr
22461452Sdfr	error = agp_generic_attach(dev);
22561452Sdfr	if (error)
22661452Sdfr		return error;
22761452Sdfr
22861452Sdfr	rid = AGP_AMD751_REGISTERS;
229127135Snjl	sc->regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
230127135Snjl					  RF_ACTIVE);
23161452Sdfr	if (!sc->regs) {
23261452Sdfr		agp_generic_detach(dev);
23361452Sdfr		return ENOMEM;
23461452Sdfr	}
23561452Sdfr
23661452Sdfr	sc->bst = rman_get_bustag(sc->regs);
23761452Sdfr	sc->bsh = rman_get_bushandle(sc->regs);
23861452Sdfr
23961452Sdfr	sc->initial_aperture = AGP_GET_APERTURE(dev);
24061452Sdfr
24161452Sdfr	for (;;) {
24261501Sdfr		gatt = agp_amd_alloc_gatt(dev);
24361452Sdfr		if (gatt)
24461452Sdfr			break;
24561452Sdfr
24661452Sdfr		/*
24761452Sdfr		 * Probably contigmalloc failure. Try reducing the
24861452Sdfr		 * aperture so that the gatt size reduces.
24961452Sdfr		 */
25061452Sdfr		if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2))
25161452Sdfr			return ENOMEM;
25261452Sdfr	}
25361452Sdfr	sc->gatt = gatt;
25461452Sdfr
25561452Sdfr	/* Install the gatt. */
25661501Sdfr	WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir);
25761452Sdfr
25861452Sdfr	/* Enable synchronisation between host and agp. */
25961501Sdfr	pci_write_config(dev,
26061501Sdfr			 AGP_AMD751_MODECTRL,
26161501Sdfr			 AGP_AMD751_MODECTRL_SYNEN, 1);
26261452Sdfr
26361501Sdfr	/* Set indexing mode for two-level and enable page dir cache */
26461501Sdfr	pci_write_config(dev,
26561501Sdfr			 AGP_AMD751_MODECTRL2,
26661501Sdfr			 AGP_AMD751_MODECTRL2_GPDCE, 1);
26761501Sdfr
26861452Sdfr	/* Enable the TLB and flush */
26961452Sdfr	WRITE2(AGP_AMD751_STATUS,
27061452Sdfr	       READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE);
27161452Sdfr	AGP_FLUSH_TLB(dev);
27261452Sdfr
27361501Sdfr	return 0;
27461452Sdfr}
27561452Sdfr
27661452Sdfrstatic int
27761452Sdfragp_amd_detach(device_t dev)
27861452Sdfr{
27961452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
28061452Sdfr
281173203Sjhb	agp_free_cdev(dev);
28261503Sdfr
28361452Sdfr	/* Disable the TLB.. */
28461452Sdfr	WRITE2(AGP_AMD751_STATUS,
28561452Sdfr	       READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE);
28661452Sdfr
28761452Sdfr	/* Disable host-agp sync */
28861452Sdfr	pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1);
28961452Sdfr
29061452Sdfr	/* Clear the GATT base */
29161452Sdfr	WRITE4(AGP_AMD751_ATTBASE, 0);
29261452Sdfr
29361452Sdfr	/* Put the aperture back the way it started. */
29461452Sdfr	AGP_SET_APERTURE(dev, sc->initial_aperture);
29561452Sdfr
29661501Sdfr	agp_amd_free_gatt(sc->gatt);
297173203Sjhb	agp_free_res(dev);
29861503Sdfr
29961503Sdfr	bus_release_resource(dev, SYS_RES_MEMORY,
30061503Sdfr			     AGP_AMD751_REGISTERS, sc->regs);
30161503Sdfr
30261452Sdfr	return 0;
30361452Sdfr}
30461452Sdfr
30561452Sdfrstatic u_int32_t
30661452Sdfragp_amd_get_aperture(device_t dev)
30761452Sdfr{
30861452Sdfr	int vas;
30961452Sdfr
31061452Sdfr	/*
31161452Sdfr	 * The aperture size is equal to 32M<<vas.
31261452Sdfr	 */
31361452Sdfr	vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1;
31461452Sdfr	return (32*1024*1024) << vas;
31561452Sdfr}
31661452Sdfr
31761452Sdfrstatic int
31861452Sdfragp_amd_set_aperture(device_t dev, u_int32_t aperture)
31961452Sdfr{
32061452Sdfr	int vas;
32161452Sdfr
32261452Sdfr	/*
32361452Sdfr	 * Check for a power of two and make sure its within the
32461452Sdfr	 * programmable range.
32561452Sdfr	 */
32661452Sdfr	if (aperture & (aperture - 1)
32761452Sdfr	    || aperture < 32*1024*1024
32861452Sdfr	    || aperture > 2U*1024*1024*1024)
32961452Sdfr		return EINVAL;
33061452Sdfr
33161452Sdfr	vas = ffs(aperture / 32*1024*1024) - 1;
33261452Sdfr
33387479Scokane	/*
33487479Scokane	 * While the size register is bits 1-3 of APCTRL, bit 0 must be
33587479Scokane	 * set for the size value to be 'valid'
33687479Scokane	 */
33761452Sdfr	pci_write_config(dev, AGP_AMD751_APCTRL,
33887479Scokane			 (((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06)
33987479Scokane			  | ((vas << 1) | 1))), 1);
34061452Sdfr
34161452Sdfr	return 0;
34261452Sdfr}
34361452Sdfr
34461452Sdfrstatic int
345194017Savgagp_amd_bind_page(device_t dev, vm_offset_t offset, vm_offset_t physical)
34661452Sdfr{
34761452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
34861452Sdfr
349194017Savg	if (offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
35061452Sdfr		return EINVAL;
35161452Sdfr
35261452Sdfr	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1;
35387479Scokane
35487479Scokane	/* invalidate the cache */
35587479Scokane	AGP_FLUSH_TLB(dev);
35661452Sdfr	return 0;
35761452Sdfr}
35861452Sdfr
35961452Sdfrstatic int
360194017Savgagp_amd_unbind_page(device_t dev, vm_offset_t offset)
36161452Sdfr{
36261452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
36361452Sdfr
364194017Savg	if (offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
36561452Sdfr		return EINVAL;
36661452Sdfr
36761452Sdfr	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0;
36861452Sdfr	return 0;
36961452Sdfr}
37061452Sdfr
37161452Sdfrstatic void
37261452Sdfragp_amd_flush_tlb(device_t dev)
37361452Sdfr{
37461452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
37561452Sdfr
37661452Sdfr	/* Set the cache invalidate bit and wait for the chipset to clear */
37761452Sdfr	WRITE4(AGP_AMD751_TLBCTRL, 1);
37861452Sdfr	do {
37961452Sdfr		DELAY(1);
38061452Sdfr	} while (READ4(AGP_AMD751_TLBCTRL));
38161452Sdfr}
38261452Sdfr
38361452Sdfrstatic device_method_t agp_amd_methods[] = {
38461452Sdfr	/* Device interface */
38561452Sdfr	DEVMETHOD(device_probe,		agp_amd_probe),
38661452Sdfr	DEVMETHOD(device_attach,	agp_amd_attach),
38761452Sdfr	DEVMETHOD(device_detach,	agp_amd_detach),
38861452Sdfr	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
38961452Sdfr	DEVMETHOD(device_suspend,	bus_generic_suspend),
39061452Sdfr	DEVMETHOD(device_resume,	bus_generic_resume),
39161452Sdfr
39261452Sdfr	/* AGP interface */
39361452Sdfr	DEVMETHOD(agp_get_aperture,	agp_amd_get_aperture),
39461452Sdfr	DEVMETHOD(agp_set_aperture,	agp_amd_set_aperture),
39561452Sdfr	DEVMETHOD(agp_bind_page,	agp_amd_bind_page),
39661452Sdfr	DEVMETHOD(agp_unbind_page,	agp_amd_unbind_page),
39761452Sdfr	DEVMETHOD(agp_flush_tlb,	agp_amd_flush_tlb),
39861452Sdfr	DEVMETHOD(agp_enable,		agp_generic_enable),
39961452Sdfr	DEVMETHOD(agp_alloc_memory,	agp_generic_alloc_memory),
40061452Sdfr	DEVMETHOD(agp_free_memory,	agp_generic_free_memory),
40161452Sdfr	DEVMETHOD(agp_bind_memory,	agp_generic_bind_memory),
40261452Sdfr	DEVMETHOD(agp_unbind_memory,	agp_generic_unbind_memory),
40361452Sdfr
40461452Sdfr	{ 0, 0 }
40561452Sdfr};
40661452Sdfr
40761452Sdfrstatic driver_t agp_amd_driver = {
40861452Sdfr	"agp",
40961452Sdfr	agp_amd_methods,
41061452Sdfr	sizeof(struct agp_amd_softc),
41161452Sdfr};
41261452Sdfr
41361452Sdfrstatic devclass_t agp_devclass;
41461452Sdfr
415153572SjhbDRIVER_MODULE(agp_amd, hostb, agp_amd_driver, agp_devclass, 0, 0);
416113506SmdoddMODULE_DEPEND(agp_amd, agp, 1, 1, 1);
417113506SmdoddMODULE_DEPEND(agp_amd, pci, 1, 1, 1);
418