xref: /openbsd/sys/dev/pci/agp_apple.c (revision d415bd75)
1 /*	$OpenBSD: agp_apple.c,v 1.8 2016/05/07 22:46:54 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2012 Martin Pieuchot <mpi@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/malloc.h>
22 #include <sys/device.h>
23 #include <sys/rwlock.h>
24 
25 #include <dev/pci/pcivar.h>
26 #include <dev/pci/pcireg.h>
27 #include <dev/pci/pcidevs.h>
28 #include <dev/pci/agpvar.h>
29 #include <dev/pci/agpreg.h>
30 
31 #include <machine/bus.h>
32 
33 #define M (1024 * 1024)
34 
35 struct agp_apple_softc {
36 	struct device		 dev;
37 	struct agp_softc	*agpdev;
38 	struct agp_gatt		*gatt;
39 	pci_chipset_tag_t	 asc_pc;
40 	pcitag_t		 asc_tag;
41 	bus_addr_t		 asc_apaddr;
42 	bus_size_t		 asc_apsize;
43 	int			 asc_flags;
44 #define	AGP_APPLE_ISU3 (1 << 0)
45 };
46 
47 int	agp_apple_match(struct device *, void *, void *);
48 void	agp_apple_attach(struct device *, struct device *, void *);
49 bus_size_t agp_apple_get_aperture(void *);
50 int	agp_apple_set_aperture(void *sc, bus_size_t);
51 void	agp_apple_bind_page(void *, bus_addr_t, paddr_t, int);
52 void	agp_apple_unbind_page(void *, bus_addr_t);
53 void	agp_apple_flush_tlb(void *);
54 int	agp_apple_enable(void *, uint32_t);
55 
56 const struct cfattach appleagp_ca = {
57 	sizeof(struct agp_apple_softc), agp_apple_match, agp_apple_attach,
58 };
59 
60 struct cfdriver appleagp_cd = {
61 	NULL, "appleagp", DV_DULL
62 };
63 
64 const struct agp_methods agp_apple_methods = {
65 	agp_apple_bind_page,
66 	agp_apple_unbind_page,
67 	agp_apple_flush_tlb,
68 	agp_apple_enable,
69 };
70 
71 int
72 agp_apple_match(struct device *parent, void *match, void *aux)
73 {
74 	struct agp_attach_args *aa = aux;
75 	struct pci_attach_args *pa = aa->aa_pa;
76 
77 	if (agpbus_probe(aa) != 1 || PCI_VENDOR(pa->pa_id) != PCI_VENDOR_APPLE)
78 		return (0);
79 
80 	switch (PCI_PRODUCT(pa->pa_id)) {
81 		case PCI_PRODUCT_APPLE_UNINORTH_AGP:
82 		case PCI_PRODUCT_APPLE_PANGEA_AGP:
83 		case PCI_PRODUCT_APPLE_UNINORTH2_AGP:
84 		case PCI_PRODUCT_APPLE_UNINORTH_AGP3:
85 		case PCI_PRODUCT_APPLE_INTREPID2_AGP:
86 			/* XXX until KMS works with these bridges */
87 			return (0);
88 		case PCI_PRODUCT_APPLE_U3_AGP:
89 		case PCI_PRODUCT_APPLE_U3L_AGP:
90 		case PCI_PRODUCT_APPLE_K2_AGP:
91 			return (1);
92 	}
93 
94 	return (0);
95 }
96 
97 void
98 agp_apple_attach(struct device *parent, struct device *self, void *aux)
99 {
100 	struct agp_apple_softc *asc = (struct agp_apple_softc *)self;
101 	struct agp_attach_args *aa = aux;
102 	struct pci_attach_args *pa = aa->aa_pa;
103 	struct agp_gatt *gatt;
104 
105 	asc->asc_tag = pa->pa_tag;
106 	asc->asc_pc = pa->pa_pc;
107 
108 	switch (PCI_PRODUCT(pa->pa_id)) {
109 		case PCI_PRODUCT_APPLE_U3_AGP:
110 		case PCI_PRODUCT_APPLE_U3L_AGP:
111 		case PCI_PRODUCT_APPLE_K2_AGP:
112 			asc->asc_flags |= AGP_APPLE_ISU3;
113 			break;
114 		default:
115 			break;
116 	}
117 
118 	/*
119 	 * XXX It looks like UniNorth GART only accepts an aperture
120 	 * base address of 0x00 certainly because it does not perform
121 	 * any physical-to-physical address translation.
122 	 */
123 	asc->asc_apaddr = 0x00;
124 	pci_conf_write(asc->asc_pc, asc->asc_tag, AGP_APPLE_APBASE,
125 	    asc->asc_apaddr);
126 
127 	/*
128 	 * There's no way to read the aperture size but all UniNorth
129 	 * chips seem to support an aperture of 256M (and 512M for U3).
130 	 */
131 	asc->asc_apsize = 256 * M;
132 	for (;;) {
133 		gatt = agp_alloc_gatt(pa->pa_dmat, asc->asc_apsize);
134 		if (gatt != NULL)
135 			break;
136 
137 		asc->asc_apsize /= 2;
138 	}
139 	asc->gatt = gatt;
140 
141 	if (agp_apple_set_aperture(asc, asc->asc_apsize)) {
142 		printf("failed to set aperture\n");
143 		return;
144 	}
145 
146 	agp_apple_flush_tlb(asc);
147 
148 	asc->agpdev = (struct agp_softc *)agp_attach_bus(pa, &agp_apple_methods,
149 	    asc->asc_apaddr, asc->asc_apsize, &asc->dev);
150 }
151 
152 bus_size_t
153 agp_apple_get_aperture(void *dev)
154 {
155 	struct agp_apple_softc *asc = dev;
156 
157 	return (asc->asc_apsize);
158 }
159 
160 int
161 agp_apple_set_aperture(void *dev, bus_size_t aperture)
162 {
163 	struct agp_apple_softc *asc = dev;
164 
165 	if (aperture % (4 * M) || aperture < (4 * M) || aperture > (256 * M))
166 		return (EINVAL);
167 
168 	pci_conf_write(asc->asc_pc, asc->asc_tag, AGP_APPLE_ATTBASE,
169 	    (asc->gatt->ag_physical & 0xfffff000) | (aperture >> 22));
170 
171 	return (0);
172 }
173 
174 #define flushd(p) __asm volatile("dcbst 0,%0; sync" ::"r"(p) : "memory")
175 
176 void
177 agp_apple_bind_page(void *v, bus_addr_t off, paddr_t pa, int flags)
178 {
179 	struct agp_apple_softc *asc = v;
180 	uint32_t entry;
181 
182 	if (off >= (asc->gatt->ag_entries << AGP_PAGE_SHIFT))
183 		return;
184 
185 	if (asc->asc_flags & AGP_APPLE_ISU3)
186 		entry = (pa >> PAGE_SHIFT | 0x80000000);
187 	else
188 		entry = htole32(pa | 0x01);
189 
190 	flushdcache((void *)pa, PAGE_SIZE);
191 
192 	asc->gatt->ag_virtual[off >> AGP_PAGE_SHIFT] = entry;
193 	flushd(&asc->gatt->ag_virtual[off >> AGP_PAGE_SHIFT]);
194 }
195 
196 void
197 agp_apple_unbind_page(void *v, bus_size_t off)
198 {
199 	struct agp_apple_softc *asc = v;
200 
201 	if (off >= (asc->gatt->ag_entries << AGP_PAGE_SHIFT))
202 		return;
203 
204 	asc->gatt->ag_virtual[off >> AGP_PAGE_SHIFT] = 0;
205 
206 	flushd(&asc->gatt->ag_virtual[off >> AGP_PAGE_SHIFT]);
207 }
208 
209 #undef flushd
210 
211 void
212 agp_apple_flush_tlb(void *v)
213 {
214 	struct agp_apple_softc *asc = v;
215 
216 	if (asc->asc_flags & AGP_APPLE_ISU3) {
217 		pci_conf_write(asc->asc_pc, asc->asc_tag, AGP_APPLE_GARTCTRL,
218 		    AGP_APPLE_GART_ENABLE | AGP_APPLE_GART_PERFRD |
219 		    AGP_APPLE_GART_INVALIDATE);
220 		pci_conf_write(asc->asc_pc, asc->asc_tag, AGP_APPLE_GARTCTRL,
221 		    AGP_APPLE_GART_ENABLE | AGP_APPLE_GART_PERFRD);
222 	} else {
223 		pci_conf_write(asc->asc_pc, asc->asc_tag, AGP_APPLE_GARTCTRL,
224 		    AGP_APPLE_GART_ENABLE | AGP_APPLE_GART_INVALIDATE);
225 		pci_conf_write(asc->asc_pc, asc->asc_tag, AGP_APPLE_GARTCTRL,
226 		    AGP_APPLE_GART_ENABLE);
227 	}
228 }
229 
230 int
231 agp_apple_enable(void *v, uint32_t mode)
232 {
233 	struct agp_apple_softc *asc = v;
234 
235 	if ((asc->asc_flags & AGP_APPLE_ISU3) == 0) {
236 		/*
237 		 * GART invalidate/SBA reset?  Linux and Darwin do something
238 		 * similar and it prevents GPU lockups with KMS.
239 		 */
240 		pci_conf_write(asc->asc_pc, asc->asc_tag, AGP_APPLE_GARTCTRL,
241 		    AGP_APPLE_GART_ENABLE | AGP_APPLE_GART_2XRESET);
242 		pci_conf_write(asc->asc_pc, asc->asc_tag,
243 		    AGP_APPLE_GARTCTRL, AGP_APPLE_GART_ENABLE);
244 	}
245 
246 	return (agp_generic_enable(asc->agpdev, mode));
247 }
248