xref: /openbsd-src/sys/dev/pci/agp_sis.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: agp_sis.c,v 1.10 2008/11/09 22:54:01 oga Exp $	*/
2 /*	$NetBSD: agp_sis.c,v 1.2 2001/09/15 00:25:00 thorpej Exp $	*/
3 
4 /*-
5  * Copyright (c) 2000 Doug Rabson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  *	$FreeBSD: src/sys/pci/agp_sis.c,v 1.3 2001/07/05 21:28:47 jhb Exp $
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/malloc.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/proc.h>
38 #include <sys/conf.h>
39 #include <sys/device.h>
40 #include <sys/agpio.h>
41 
42 #include <dev/pci/pcivar.h>
43 #include <dev/pci/pcireg.h>
44 #include <dev/pci/pcidevs.h>
45 #include <dev/pci/vga_pcivar.h>
46 #include <dev/pci/agpvar.h>
47 #include <dev/pci/agpreg.h>
48 
49 #include <machine/bus.h>
50 
51 struct agp_sis_softc {
52 	struct device		 dev;
53 	struct agp_softc	*agpdev;
54 	struct agp_gatt		*gatt;
55 	pci_chipset_tag_t	 ssc_pc;
56 	pcitag_t		 ssc_tag;
57 	bus_size_t		 initial_aperture;
58 };
59 
60 void	agp_sis_attach(struct device *, struct device *, void *);
61 int	agp_sis_probe(struct device *, void *, void *);
62 bus_size_t agp_sis_get_aperture(void *);
63 int	agp_sis_set_aperture(void *, bus_size_t);
64 int	agp_sis_bind_page(void *, off_t, bus_addr_t);
65 int	agp_sis_unbind_page(void *, off_t);
66 void	agp_sis_flush_tlb(void *);
67 
68 struct cfattach sisagp_ca = {
69         sizeof(struct agp_sis_softc), agp_sis_probe, agp_sis_attach
70 };
71 
72 struct cfdriver sisagp_cd = {
73 	NULL, "sisagp", DV_DULL
74 };
75 
76 const struct agp_methods agp_sis_methods = {
77 	agp_sis_get_aperture,
78 	agp_sis_bind_page,
79 	agp_sis_unbind_page,
80 	agp_sis_flush_tlb,
81 };
82 
83 int
84 agp_sis_probe(struct device *parent, void *match, void *aux)
85 {
86 	struct agp_attach_args	*aa = aux;
87 	struct pci_attach_args	*pa = aa->aa_pa;
88 
89 	/* Must be a pchb, don't attach to iommu-style agp devs */
90 	if (agpbus_probe(aa) == 1 && PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SIS &&
91 	   PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_SIS_755 &&
92 	   PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_SIS_760)
93 		return (1);
94 	return (0);
95 }
96 
97 
98 void
99 agp_sis_attach(struct device *parent, struct device *self, void *aux)
100 {
101 	struct agp_sis_softc	*ssc = (struct agp_sis_softc *)self;
102 	struct agp_attach_args	*aa = aux;
103 	struct pci_attach_args	*pa = aa->aa_pa;
104 	struct agp_gatt		*gatt;
105 	pcireg_t		 reg;
106 
107 	ssc->ssc_pc = pa->pa_pc;
108 	ssc->ssc_tag = pa->pa_tag;
109 	ssc->initial_aperture = agp_sis_get_aperture(ssc);
110 
111 	for (;;) {
112 		bus_size_t size = agp_sis_get_aperture(ssc);
113 		gatt = agp_alloc_gatt(pa->pa_dmat, size);
114 		if (gatt != NULL)
115 			break;
116 
117 		/*
118 		 * Probably failed to alloc congigious memory. Try reducing the
119 		 * aperture so that the gatt size reduces.
120 		 */
121 		if (agp_sis_set_aperture(ssc, size / 2)) {
122 			printf("can't set aperture size\n");
123 			return;
124 		}
125 	}
126 	ssc->gatt = gatt;
127 
128 	/* Install the gatt. */
129 	pci_conf_write(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_ATTBASE,
130 	    gatt->ag_physical);
131 
132 	/* Enable the aperture and auto-tlb-inval */
133 	reg = pci_conf_read(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_WINCTRL);
134 	reg |= (0x05 << 24) | 3;
135 	pci_conf_write(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_WINCTRL, reg);
136 
137 	ssc->agpdev = (struct agp_softc *)agp_attach_bus(pa, &agp_sis_methods,
138 	    AGP_APBASE, PCI_MAPREG_TYPE_MEM, &ssc->dev);
139 	return;
140 }
141 
142 #if 0
143 int
144 agp_sis_detach(struct agp_softc *sc)
145 {
146 	struct agp_sis_softc *ssc = sc->sc_chipc;
147 	pcireg_t reg;
148 	int error;
149 
150 	error = agp_generic_detach(sc);
151 	if (error)
152 		return (error);
153 
154 	reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, AGP_SIS_WINCTRL);
155 	reg &= ~3;
156 	reg &= 0x00ffffff;
157 	pci_conf_write(sc->sc_pc, sc->sc_pcitag, AGP_SIS_WINCTRL, reg);
158 
159 	/* Put the aperture back the way it started. */
160 	AGP_SET_APERTURE(sc, ssc->initial_aperture);
161 
162 	agp_free_gatt(sc, ssc->gatt);
163 	return (0);
164 }
165 #endif
166 
167 bus_size_t
168 agp_sis_get_aperture(void *sc)
169 {
170 	struct agp_sis_softc	*ssc = sc;
171 	int			 gws;
172 
173 	/*
174 	 * The aperture size is equal to 4M<<gws.
175 	 */
176 	gws = (pci_conf_read(ssc->ssc_pc, ssc->ssc_tag,
177 	    AGP_SIS_WINCTRL)&0x70) >> 4;
178 	return ((4 * 1024 * 1024) << gws);
179 }
180 
181 int
182 agp_sis_set_aperture(void *sc, bus_size_t aperture)
183 {
184 	struct agp_sis_softc	*ssc = sc;
185 	int gws;
186 	pcireg_t reg;
187 
188 	/*
189 	 * Check for a power of two and make sure its within the
190 	 * programmable range.
191 	 */
192 	if (aperture & (aperture - 1)
193 	    || aperture < 4*1024*1024
194 	    || aperture > 256*1024*1024)
195 		return (EINVAL);
196 
197 	gws = ffs(aperture / 4*1024*1024) - 1;
198 
199 	reg = pci_conf_read(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_WINCTRL);
200 	reg &= ~0x00000070;
201 	reg |= gws << 4;
202 	pci_conf_write(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_WINCTRL, reg);
203 
204 	return (0);
205 }
206 
207 int
208 agp_sis_bind_page(void *sc, off_t offset, bus_addr_t physical)
209 {
210 	struct agp_sis_softc	*ssc = sc;
211 
212 	if (offset < 0 || offset >= (ssc->gatt->ag_entries << AGP_PAGE_SHIFT))
213 		return (EINVAL);
214 
215 	ssc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical;
216 	return (0);
217 }
218 
219 int
220 agp_sis_unbind_page(void *sc, off_t offset)
221 {
222 	struct agp_sis_softc	*ssc = sc;
223 
224 	if (offset < 0 || offset >= (ssc->gatt->ag_entries << AGP_PAGE_SHIFT))
225 		return (EINVAL);
226 
227 	ssc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0;
228 	return (0);
229 }
230 
231 void
232 agp_sis_flush_tlb(void *sc)
233 {
234 	struct agp_sis_softc	*ssc = sc;
235 	pcireg_t		 reg;
236 
237 	reg = pci_conf_read(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_TLBFLUSH);
238 	reg &= 0xffffff00;
239 	reg |= 0x02;
240 	pci_conf_write(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_TLBFLUSH, reg);
241 }
242