agp_amd.c revision 61452
1/*-
2 * Copyright (c) 2000 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$FreeBSD: head/sys/dev/agp/agp_amd.c 61452 2000-06-09 16:04:30Z dfr $
27 */
28
29#include "opt_bus.h"
30#include "opt_pci.h"
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/malloc.h>
35#include <sys/kernel.h>
36#include <sys/bus.h>
37#include <sys/lock.h>
38
39#include <pci/pcivar.h>
40#include <pci/pcireg.h>
41#include <pci/agppriv.h>
42#include <pci/agpreg.h>
43
44#include <vm/vm.h>
45#include <vm/vm_object.h>
46#include <vm/pmap.h>
47#include <machine/clock.h>
48#include <machine/bus.h>
49#include <machine/resource.h>
50#include <sys/rman.h>
51
52#define READ2(off)	bus_space_read_2(sc->bst, sc->bsh, off)
53#define READ4(off)	bus_space_read_4(sc->bst, sc->bsh, off)
54#define WRITE2(off,v)	bus_space_write_2(sc->bst, sc->bsh, off, v)
55#define WRITE4(off,v)	bus_space_write_4(sc->bst, sc->bsh, off, v)
56
57struct agp_amd_softc {
58	struct agp_softc agp;
59	struct resource *regs;	/* memory mapped control registers */
60	bus_space_tag_t bst;	/* bus_space tag */
61	bus_space_handle_t bsh;	/* bus_space handle */
62	u_int32_t	initial_aperture; /* aperture size at startup */
63	struct agp_gatt *gatt;
64};
65
66static const char*
67agp_amd_match(device_t dev)
68{
69	if (pci_get_class(dev) != PCIC_BRIDGE
70	    || pci_get_subclass(dev) != PCIS_BRIDGE_HOST)
71		return NULL;
72
73	if (agp_find_caps(dev) == 0)
74		return NULL;
75
76	switch (pci_get_devid(dev)) {
77	case 0x70061022:
78		return ("AMD 751 host to AGP bridge");
79	};
80
81	return NULL;
82}
83
84static int
85agp_amd_probe(device_t dev)
86{
87	const char *desc;
88
89	desc = agp_amd_match(dev);
90	if (desc) {
91		device_verbose(dev);
92		device_set_desc(dev, desc);
93		return 0;
94	}
95
96	return ENXIO;
97}
98
99static int
100agp_amd_attach(device_t dev)
101{
102	struct agp_amd_softc *sc = device_get_softc(dev);
103	struct agp_gatt *gatt;
104	int error, rid;
105
106	error = agp_generic_attach(dev);
107	if (error)
108		return error;
109
110	rid = AGP_AMD751_REGISTERS;
111	sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
112				      0, ~0, 1, RF_ACTIVE);
113	if (!sc->regs) {
114		agp_generic_detach(dev);
115		return ENOMEM;
116	}
117
118	sc->bst = rman_get_bustag(sc->regs);
119	sc->bsh = rman_get_bushandle(sc->regs);
120
121	sc->initial_aperture = AGP_GET_APERTURE(dev);
122
123	for (;;) {
124		gatt = agp_alloc_gatt(dev);
125		if (gatt)
126			break;
127
128		/*
129		 * Probably contigmalloc failure. Try reducing the
130		 * aperture so that the gatt size reduces.
131		 */
132		if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2))
133			return ENOMEM;
134	}
135	sc->gatt = gatt;
136
137	/* Install the gatt. */
138	WRITE4(AGP_AMD751_ATTBASE, gatt->ag_physical);
139
140	/* Enable synchronisation between host and agp. */
141	pci_write_config(dev, AGP_AMD751_MODECTRL, 0x80, 1);
142
143	/* Enable the TLB and flush */
144	WRITE2(AGP_AMD751_STATUS,
145	       READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE);
146	AGP_FLUSH_TLB(dev);
147
148	return agp_generic_attach(dev);
149}
150
151static int
152agp_amd_detach(device_t dev)
153{
154	struct agp_amd_softc *sc = device_get_softc(dev);
155
156	/* Disable the TLB.. */
157	WRITE2(AGP_AMD751_STATUS,
158	       READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE);
159
160	/* Disable host-agp sync */
161	pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1);
162
163	/* Clear the GATT base */
164	WRITE4(AGP_AMD751_ATTBASE, 0);
165
166	/* Put the aperture back the way it started. */
167	AGP_SET_APERTURE(dev, sc->initial_aperture);
168
169	agp_free_gatt(sc->gatt);
170	return 0;
171}
172
173static u_int32_t
174agp_amd_get_aperture(device_t dev)
175{
176	int vas;
177
178	/*
179	 * The aperture size is equal to 32M<<vas.
180	 */
181	vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1;
182	return (32*1024*1024) << vas;
183}
184
185static int
186agp_amd_set_aperture(device_t dev, u_int32_t aperture)
187{
188	int vas;
189
190	/*
191	 * Check for a power of two and make sure its within the
192	 * programmable range.
193	 */
194	if (aperture & (aperture - 1)
195	    || aperture < 32*1024*1024
196	    || aperture > 2U*1024*1024*1024)
197		return EINVAL;
198
199	vas = ffs(aperture / 32*1024*1024) - 1;
200
201	pci_write_config(dev, AGP_AMD751_APCTRL,
202			 ((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06)
203			  | vas << 1), 1);
204
205	return 0;
206}
207
208static int
209agp_amd_bind_page(device_t dev, int offset, vm_offset_t physical)
210{
211	struct agp_amd_softc *sc = device_get_softc(dev);
212
213	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
214		return EINVAL;
215
216	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1;
217	return 0;
218}
219
220static int
221agp_amd_unbind_page(device_t dev, int offset)
222{
223	struct agp_amd_softc *sc = device_get_softc(dev);
224
225	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
226		return EINVAL;
227
228	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0;
229	return 0;
230}
231
232static void
233agp_amd_flush_tlb(device_t dev)
234{
235	struct agp_amd_softc *sc = device_get_softc(dev);
236
237	/* Set the cache invalidate bit and wait for the chipset to clear */
238	WRITE4(AGP_AMD751_TLBCTRL, 1);
239	do {
240		DELAY(1);
241	} while (READ4(AGP_AMD751_TLBCTRL));
242}
243
244static device_method_t agp_amd_methods[] = {
245	/* Device interface */
246	DEVMETHOD(device_probe,		agp_amd_probe),
247	DEVMETHOD(device_attach,	agp_amd_attach),
248	DEVMETHOD(device_detach,	agp_amd_detach),
249	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
250	DEVMETHOD(device_suspend,	bus_generic_suspend),
251	DEVMETHOD(device_resume,	bus_generic_resume),
252
253	/* AGP interface */
254	DEVMETHOD(agp_get_aperture,	agp_amd_get_aperture),
255	DEVMETHOD(agp_set_aperture,	agp_amd_set_aperture),
256	DEVMETHOD(agp_bind_page,	agp_amd_bind_page),
257	DEVMETHOD(agp_unbind_page,	agp_amd_unbind_page),
258	DEVMETHOD(agp_flush_tlb,	agp_amd_flush_tlb),
259	DEVMETHOD(agp_enable,		agp_generic_enable),
260	DEVMETHOD(agp_alloc_memory,	agp_generic_alloc_memory),
261	DEVMETHOD(agp_free_memory,	agp_generic_free_memory),
262	DEVMETHOD(agp_bind_memory,	agp_generic_bind_memory),
263	DEVMETHOD(agp_unbind_memory,	agp_generic_unbind_memory),
264
265	{ 0, 0 }
266};
267
268static driver_t agp_amd_driver = {
269	"agp",
270	agp_amd_methods,
271	sizeof(struct agp_amd_softc),
272};
273
274static devclass_t agp_devclass;
275
276DRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0);
277