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: releng/10.2/sys/dev/agp/agp_amd.c 275406 2014-12-02 13:46:13Z tijl $");
29116192Sobrien
3061452Sdfr#include <sys/param.h>
3161452Sdfr#include <sys/systm.h>
3261452Sdfr#include <sys/malloc.h>
3361452Sdfr#include <sys/kernel.h>
34129878Sphk#include <sys/module.h>
3561452Sdfr#include <sys/bus.h>
3661452Sdfr#include <sys/lock.h>
3776827Salfred#include <sys/mutex.h>
3879339Sjhb#include <sys/proc.h>
3961452Sdfr
40173573Sjhb#include <dev/agp/agppriv.h>
41173573Sjhb#include <dev/agp/agpreg.h>
42119288Simp#include <dev/pci/pcivar.h>
43119288Simp#include <dev/pci/pcireg.h>
4461452Sdfr
4561452Sdfr#include <vm/vm.h>
46275406Stijl#include <vm/vm_extern.h>
47275406Stijl#include <vm/vm_kern.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
97275406Stijl	 * gatt so we don't need to use kmem_alloc_contig.
98275406Stijl	 * Allocate individual GATT pages and map them into the page
9987479Scokane	 * directory.
10061501Sdfr	 */
10161501Sdfr	gatt->ag_entries = entries;
102275406Stijl	gatt->ag_virtual = (void *)kmem_alloc_attr(kernel_arena,
103275406Stijl	    entries * sizeof(u_int32_t), M_NOWAIT | M_ZERO, 0, ~0,
104275406Stijl	    VM_MEMATTR_WRITE_COMBINING);
10561501Sdfr	if (!gatt->ag_virtual) {
10661501Sdfr		if (bootverbose)
10761501Sdfr			device_printf(dev, "allocation failed\n");
10861501Sdfr		free(gatt, M_AGP);
10961501Sdfr		return 0;
11061501Sdfr	}
11161501Sdfr
11261501Sdfr	/*
11361501Sdfr	 * Allocate the page directory.
11461501Sdfr	 */
115275406Stijl	gatt->ag_vdir = (void *)kmem_alloc_attr(kernel_arena, AGP_PAGE_SIZE,
116275406Stijl	    M_NOWAIT | M_ZERO, 0, ~0, VM_MEMATTR_WRITE_COMBINING);
11761501Sdfr	if (!gatt->ag_vdir) {
11861501Sdfr		if (bootverbose)
11961501Sdfr			device_printf(dev,
12061501Sdfr				      "failed to allocate page directory\n");
121275406Stijl		kmem_free(kernel_arena, (vm_offset_t)gatt->ag_virtual,
122275406Stijl		    entries * sizeof(u_int32_t));
12361501Sdfr		free(gatt, M_AGP);
12461501Sdfr		return 0;
12561501Sdfr	}
12694790Scokane
12761501Sdfr	gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir);
12887479Scokane	if(bootverbose)
129105145Smarcel		device_printf(dev, "gatt -> ag_pdir %#lx\n",
130105145Smarcel		    (u_long)gatt->ag_pdir);
13187479Scokane	/*
13287479Scokane	 * Allocate the gatt pages
13387479Scokane	 */
13487479Scokane	gatt->ag_entries = entries;
13587479Scokane	if(bootverbose)
13687479Scokane		device_printf(dev, "allocating GATT for %d AGP page entries\n",
13787479Scokane			gatt->ag_entries);
13894790Scokane
13987479Scokane	gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual);
14061501Sdfr
14161501Sdfr	/*
14261501Sdfr	 * Map the pages of the GATT into the page directory.
14387479Scokane	 *
14487479Scokane	 * The GATT page addresses are mapped into the directory offset by
14587479Scokane	 * an amount dependent on the base address of the aperture. This
14687479Scokane	 * is and offset into the page directory, not an offset added to
14787479Scokane	 * the addresses of the gatt pages.
14861501Sdfr	 */
14987479Scokane
15087479Scokane	pdir_offset = pci_read_config(dev, AGP_AMD751_APBASE, 4) >> 22;
15187479Scokane
15261501Sdfr	npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1)
15361501Sdfr		  >> AGP_PAGE_SHIFT);
15487479Scokane
15561501Sdfr	for (i = 0; i < npages; i++) {
15661501Sdfr		vm_offset_t va;
15761501Sdfr		vm_offset_t pa;
15861501Sdfr
15961501Sdfr		va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE;
16061501Sdfr		pa = vtophys(va);
16187479Scokane		gatt->ag_vdir[i + pdir_offset] = pa | 1;
16261501Sdfr	}
16361501Sdfr
16461501Sdfr	return gatt;
16561501Sdfr}
16661501Sdfr
16761501Sdfrstatic void
16861501Sdfragp_amd_free_gatt(struct agp_amd_gatt *gatt)
16961501Sdfr{
170275406Stijl	kmem_free(kernel_arena, (vm_offset_t)gatt->ag_vdir, AGP_PAGE_SIZE);
171275406Stijl	kmem_free(kernel_arena, (vm_offset_t)gatt->ag_virtual,
172275406Stijl	    gatt->ag_entries * sizeof(u_int32_t));
17361501Sdfr	free(gatt, M_AGP);
17461501Sdfr}
17561501Sdfr
17661452Sdfrstatic const char*
17761452Sdfragp_amd_match(device_t dev)
17861452Sdfr{
17961452Sdfr	if (pci_get_class(dev) != PCIC_BRIDGE
18061452Sdfr	    || pci_get_subclass(dev) != PCIS_BRIDGE_HOST)
18161452Sdfr		return NULL;
18261452Sdfr
18361452Sdfr	if (agp_find_caps(dev) == 0)
18461452Sdfr		return NULL;
18561452Sdfr
18661452Sdfr	switch (pci_get_devid(dev)) {
187133851Sobrien	case 0x70061022:
188133851Sobrien		return ("AMD 751 host to AGP bridge");
18983699Scokane	case 0x700e1022:
19083699Scokane		return ("AMD 761 host to AGP bridge");
19187479Scokane	case 0x700c1022:
19287479Scokane		return ("AMD 762 host to AGP bridge");
193244926Santoine	}
19461452Sdfr
19561452Sdfr	return NULL;
19661452Sdfr}
19761452Sdfr
19861452Sdfrstatic int
19961452Sdfragp_amd_probe(device_t dev)
20061452Sdfr{
20161452Sdfr	const char *desc;
20261452Sdfr
203241885Seadler	if (resource_disabled("agp", device_get_unit(dev)))
204241885Seadler		return (ENXIO);
20561452Sdfr	desc = agp_amd_match(dev);
20661452Sdfr	if (desc) {
20761452Sdfr		device_set_desc(dev, desc);
208142398Simp		return BUS_PROBE_DEFAULT;
20961452Sdfr	}
21061452Sdfr
21161452Sdfr	return ENXIO;
21261452Sdfr}
21361452Sdfr
21461452Sdfrstatic int
21561452Sdfragp_amd_attach(device_t dev)
21661452Sdfr{
21761452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
21861501Sdfr	struct agp_amd_gatt *gatt;
21961452Sdfr	int error, rid;
22061452Sdfr
22161452Sdfr	error = agp_generic_attach(dev);
22261452Sdfr	if (error)
22361452Sdfr		return error;
22461452Sdfr
22561452Sdfr	rid = AGP_AMD751_REGISTERS;
226127135Snjl	sc->regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
227127135Snjl					  RF_ACTIVE);
22861452Sdfr	if (!sc->regs) {
22961452Sdfr		agp_generic_detach(dev);
23061452Sdfr		return ENOMEM;
23161452Sdfr	}
23261452Sdfr
23361452Sdfr	sc->bst = rman_get_bustag(sc->regs);
23461452Sdfr	sc->bsh = rman_get_bushandle(sc->regs);
23561452Sdfr
23661452Sdfr	sc->initial_aperture = AGP_GET_APERTURE(dev);
23761452Sdfr
23861452Sdfr	for (;;) {
23961501Sdfr		gatt = agp_amd_alloc_gatt(dev);
24061452Sdfr		if (gatt)
24161452Sdfr			break;
24261452Sdfr
24361452Sdfr		/*
24461452Sdfr		 * Probably contigmalloc failure. Try reducing the
24561452Sdfr		 * aperture so that the gatt size reduces.
24661452Sdfr		 */
24761452Sdfr		if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2))
24861452Sdfr			return ENOMEM;
24961452Sdfr	}
25061452Sdfr	sc->gatt = gatt;
25161452Sdfr
25261452Sdfr	/* Install the gatt. */
25361501Sdfr	WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir);
25461452Sdfr
25561452Sdfr	/* Enable synchronisation between host and agp. */
25661501Sdfr	pci_write_config(dev,
25761501Sdfr			 AGP_AMD751_MODECTRL,
25861501Sdfr			 AGP_AMD751_MODECTRL_SYNEN, 1);
25961452Sdfr
26061501Sdfr	/* Set indexing mode for two-level and enable page dir cache */
26161501Sdfr	pci_write_config(dev,
26261501Sdfr			 AGP_AMD751_MODECTRL2,
26361501Sdfr			 AGP_AMD751_MODECTRL2_GPDCE, 1);
26461501Sdfr
26561452Sdfr	/* Enable the TLB and flush */
26661452Sdfr	WRITE2(AGP_AMD751_STATUS,
26761452Sdfr	       READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE);
26861452Sdfr	AGP_FLUSH_TLB(dev);
26961452Sdfr
27061501Sdfr	return 0;
27161452Sdfr}
27261452Sdfr
27361452Sdfrstatic int
27461452Sdfragp_amd_detach(device_t dev)
27561452Sdfr{
27661452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
27761452Sdfr
278173203Sjhb	agp_free_cdev(dev);
27961503Sdfr
28061452Sdfr	/* Disable the TLB.. */
28161452Sdfr	WRITE2(AGP_AMD751_STATUS,
28261452Sdfr	       READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE);
28361452Sdfr
28461452Sdfr	/* Disable host-agp sync */
28561452Sdfr	pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1);
28661452Sdfr
28761452Sdfr	/* Clear the GATT base */
28861452Sdfr	WRITE4(AGP_AMD751_ATTBASE, 0);
28961452Sdfr
29061452Sdfr	/* Put the aperture back the way it started. */
29161452Sdfr	AGP_SET_APERTURE(dev, sc->initial_aperture);
29261452Sdfr
29361501Sdfr	agp_amd_free_gatt(sc->gatt);
294173203Sjhb	agp_free_res(dev);
29561503Sdfr
29661503Sdfr	bus_release_resource(dev, SYS_RES_MEMORY,
29761503Sdfr			     AGP_AMD751_REGISTERS, sc->regs);
29861503Sdfr
29961452Sdfr	return 0;
30061452Sdfr}
30161452Sdfr
30261452Sdfrstatic u_int32_t
30361452Sdfragp_amd_get_aperture(device_t dev)
30461452Sdfr{
30561452Sdfr	int vas;
30661452Sdfr
30761452Sdfr	/*
30861452Sdfr	 * The aperture size is equal to 32M<<vas.
30961452Sdfr	 */
31061452Sdfr	vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1;
31161452Sdfr	return (32*1024*1024) << vas;
31261452Sdfr}
31361452Sdfr
31461452Sdfrstatic int
31561452Sdfragp_amd_set_aperture(device_t dev, u_int32_t aperture)
31661452Sdfr{
31761452Sdfr	int vas;
31861452Sdfr
31961452Sdfr	/*
32061452Sdfr	 * Check for a power of two and make sure its within the
32161452Sdfr	 * programmable range.
32261452Sdfr	 */
32361452Sdfr	if (aperture & (aperture - 1)
32461452Sdfr	    || aperture < 32*1024*1024
32561452Sdfr	    || aperture > 2U*1024*1024*1024)
32661452Sdfr		return EINVAL;
32761452Sdfr
32861452Sdfr	vas = ffs(aperture / 32*1024*1024) - 1;
32961452Sdfr
33087479Scokane	/*
33187479Scokane	 * While the size register is bits 1-3 of APCTRL, bit 0 must be
33287479Scokane	 * set for the size value to be 'valid'
33387479Scokane	 */
33461452Sdfr	pci_write_config(dev, AGP_AMD751_APCTRL,
33587479Scokane			 (((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06)
33687479Scokane			  | ((vas << 1) | 1))), 1);
33761452Sdfr
33861452Sdfr	return 0;
33961452Sdfr}
34061452Sdfr
34161452Sdfrstatic int
342194017Savgagp_amd_bind_page(device_t dev, vm_offset_t offset, vm_offset_t physical)
34361452Sdfr{
34461452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
34561452Sdfr
346194017Savg	if (offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
34761452Sdfr		return EINVAL;
34861452Sdfr
34961452Sdfr	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1;
35061452Sdfr	return 0;
35161452Sdfr}
35261452Sdfr
35361452Sdfrstatic int
354194017Savgagp_amd_unbind_page(device_t dev, vm_offset_t offset)
35561452Sdfr{
35661452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
35761452Sdfr
358194017Savg	if (offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
35961452Sdfr		return EINVAL;
36061452Sdfr
36161452Sdfr	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0;
36261452Sdfr	return 0;
36361452Sdfr}
36461452Sdfr
36561452Sdfrstatic void
36661452Sdfragp_amd_flush_tlb(device_t dev)
36761452Sdfr{
36861452Sdfr	struct agp_amd_softc *sc = device_get_softc(dev);
36961452Sdfr
37061452Sdfr	/* Set the cache invalidate bit and wait for the chipset to clear */
37161452Sdfr	WRITE4(AGP_AMD751_TLBCTRL, 1);
37261452Sdfr	do {
37361452Sdfr		DELAY(1);
37461452Sdfr	} while (READ4(AGP_AMD751_TLBCTRL));
37561452Sdfr}
37661452Sdfr
37761452Sdfrstatic device_method_t agp_amd_methods[] = {
37861452Sdfr	/* Device interface */
37961452Sdfr	DEVMETHOD(device_probe,		agp_amd_probe),
38061452Sdfr	DEVMETHOD(device_attach,	agp_amd_attach),
38161452Sdfr	DEVMETHOD(device_detach,	agp_amd_detach),
38261452Sdfr	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
38361452Sdfr	DEVMETHOD(device_suspend,	bus_generic_suspend),
38461452Sdfr	DEVMETHOD(device_resume,	bus_generic_resume),
38561452Sdfr
38661452Sdfr	/* AGP interface */
38761452Sdfr	DEVMETHOD(agp_get_aperture,	agp_amd_get_aperture),
38861452Sdfr	DEVMETHOD(agp_set_aperture,	agp_amd_set_aperture),
38961452Sdfr	DEVMETHOD(agp_bind_page,	agp_amd_bind_page),
39061452Sdfr	DEVMETHOD(agp_unbind_page,	agp_amd_unbind_page),
39161452Sdfr	DEVMETHOD(agp_flush_tlb,	agp_amd_flush_tlb),
39261452Sdfr	DEVMETHOD(agp_enable,		agp_generic_enable),
39361452Sdfr	DEVMETHOD(agp_alloc_memory,	agp_generic_alloc_memory),
39461452Sdfr	DEVMETHOD(agp_free_memory,	agp_generic_free_memory),
39561452Sdfr	DEVMETHOD(agp_bind_memory,	agp_generic_bind_memory),
39661452Sdfr	DEVMETHOD(agp_unbind_memory,	agp_generic_unbind_memory),
39761452Sdfr
39861452Sdfr	{ 0, 0 }
39961452Sdfr};
40061452Sdfr
40161452Sdfrstatic driver_t agp_amd_driver = {
40261452Sdfr	"agp",
40361452Sdfr	agp_amd_methods,
40461452Sdfr	sizeof(struct agp_amd_softc),
40561452Sdfr};
40661452Sdfr
40761452Sdfrstatic devclass_t agp_devclass;
40861452Sdfr
409153572SjhbDRIVER_MODULE(agp_amd, hostb, agp_amd_driver, agp_devclass, 0, 0);
410113506SmdoddMODULE_DEPEND(agp_amd, agp, 1, 1, 1);
411113506SmdoddMODULE_DEPEND(agp_amd, pci, 1, 1, 1);
412