xref: /plan9-contrib/sys/src/9/bcm/vcore.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 
7 /*
8  * Mailbox interface with videocore gpu
9  */
10 
11 #define	MAILBOX		(VIRTIO+0xB880)
12 
13 typedef struct Prophdr Prophdr;
14 typedef struct Fbinfo Fbinfo;
15 typedef struct Vgpio Vgpio;
16 
17 enum {
18 	Read		= 0x00>>2,
19 	Write		= 0x00>>2,
20 	Peek		= 0x10>>2,
21 	Sender		= 0x14>>2,
22 	Status		= 0x18>>2,
23 		Full		= 1<<31,
24 		Empty		= 1<<30,
25 	Config		= 0x1C>>2,
26 	NRegs		= 0x20>>2,
27 
28 	ChanMask	= 0xF,
29 	ChanProps	= 8,
30 	ChanFb		= 1,
31 
32 	Req			= 0x0,
33 	RspOk		= 0x80000000,
34 	TagResp		= 1<<31,
35 
36 	TagGetfwrev	= 0x00000001,
37 	TagGetrev	= 0x00010002,
38 	TagGetmac	= 0x00010003,
39 	TagGetser	= 0x00010004,
40 	TagGetram	= 0x00010005,
41 	TagGetpower	= 0x00020001,
42 	TagSetpower	= 0x00028001,
43 		Powerwait	= 1<<1,
44 	TagGetclkspd= 0x00030002,
45 	TagGetclkmax= 0x00030004,
46 	TagSetclkspd= 0x00038002,
47 	TagGettemp	= 0x00030006,
48 	TagXhciReset= 0x00030058,
49 	TagFballoc	= 0x00040001,
50 	TagFbfree	= 0x00048001,
51 	TagFbblank	= 0x00040002,
52 	TagGetres	= 0x00040003,
53 	TagSetres	= 0x00048003,
54 	TagGetvres	= 0x00040004,
55 	TagSetvres	= 0x00048004,
56 	TagGetdepth	= 0x00040005,
57 	TagSetdepth	= 0x00048005,
58 	TagGetrgb	= 0x00040006,
59 	TagSetrgb	= 0x00048006,
60 	TagGetGpio	= 0x00040010,
61 
62 	Nvgpio		= 2,
63 };
64 
65 struct Fbinfo {
66 	u32int	xres;
67 	u32int	yres;
68 	u32int	xresvirtual;
69 	u32int	yresvirtual;
70 	u32int	pitch;			/* returned by gpu */
71 	u32int	bpp;
72 	u32int	xoffset;
73 	u32int	yoffset;
74 	u32int	base;			/* returned by gpu */
75 	u32int	screensize;		/* returned by gpu */
76 };
77 
78 
79 struct Prophdr {
80 	u32int	len;
81 	u32int	req;
82 	u32int	tag;
83 	u32int	tagbuflen;
84 	u32int	taglen;
85 	u32int	data[1];
86 };
87 
88 struct Vgpio {
89 	u32int	*counts;
90 	u16int	incs;
91 	u16int	decs;
92 	int	ison;
93 };
94 
95 static Vgpio vgpio;
96 
97 static void
vcwrite(uint chan,int val)98 vcwrite(uint chan, int val)
99 {
100 	u32int *r;
101 
102 	r = (u32int*)MAILBOX + NRegs;
103 	val &= ~ChanMask;
104 	while(r[Status]&Full)
105 		;
106 	coherence();
107 	r[Write] = val | chan;
108 }
109 
110 static int
vcread(uint chan)111 vcread(uint chan)
112 {
113 	u32int *r;
114 	int x;
115 
116 	r = (u32int*)MAILBOX;
117 	do{
118 		while(r[Status]&Empty)
119 			;
120 		coherence();
121 		x = r[Read];
122 	}while((x&ChanMask) != chan);
123 	return x & ~ChanMask;
124 }
125 
126 /*
127  * Property interface
128  */
129 
130 static int
vcreq(int tag,void * buf,int vallen,int rsplen)131 vcreq(int tag, void *buf, int vallen, int rsplen)
132 {
133 	uintptr r;
134 	int n;
135 	Prophdr *prop;
136 	uintptr aprop;
137 	static int busaddr = 1;
138 
139 	if(rsplen < vallen)
140 		rsplen = vallen;
141 	rsplen = (rsplen+3) & ~3;
142 	prop = (Prophdr*)(VCBUFFER);
143 	n = sizeof(Prophdr) + rsplen + 8;
144 	memset(prop, 0, n);
145 	prop->len = n;
146 	prop->req = Req;
147 	prop->tag = tag;
148 	prop->tagbuflen = rsplen;
149 	prop->taglen = vallen;
150 	if(vallen > 0)
151 		memmove(prop->data, buf, vallen);
152 	cachedwbinvse(prop, prop->len);
153 	for(;;){
154 		aprop = busaddr? dmaaddr(prop) : PTR2UINT(prop);
155 		vcwrite(ChanProps, aprop);
156 		r = vcread(ChanProps);
157 		if(r == aprop)
158 			break;
159 		if(!busaddr)
160 			return -1;
161 		busaddr = 0;
162 	}
163 	if(prop->req == RspOk &&
164 	   prop->tag == tag &&
165 	   (prop->taglen&TagResp)) {
166 		if((n = prop->taglen & ~TagResp) < rsplen)
167 			rsplen = n;
168 		memmove(buf, prop->data, rsplen);
169 	}else
170 		rsplen = -1;
171 
172 	return rsplen;
173 }
174 
175 /*
176  * Framebuffer
177  */
178 
179 static int
fbdefault(int * width,int * height,int * depth)180 fbdefault(int *width, int *height, int *depth)
181 {
182 	u32int buf[3];
183 	char *p;
184 
185 	if(vcreq(TagGetres, &buf[0], 0, 2*4) != 2*4 ||
186 	   vcreq(TagGetdepth, &buf[2], 0, 4) != 4)
187 		return -1;
188 	*width = buf[0];
189 	*height = buf[1];
190 	if((p = getconf("bcm2708_fb.fbdepth")) != nil)
191 		*depth = atoi(p);
192 	else
193 		*depth = buf[2];
194 	return 0;
195 }
196 
197 void*
fbinit(int set,int * width,int * height,int * depth)198 fbinit(int set, int *width, int *height, int *depth)
199 {
200 	Fbinfo *fi;
201 	uintptr va;
202 
203 	if(!set)
204 		fbdefault(width, height, depth);
205 	/* Screen width must be a multiple of 16 */
206 	*width &= ~0xF;
207 	fi = (Fbinfo*)(VCBUFFER);
208 	memset(fi, 0, sizeof(*fi));
209 	fi->xres = fi->xresvirtual = *width;
210 	fi->yres = fi->yresvirtual = *height;
211 	fi->bpp = *depth;
212 	cachedwbinvse(fi, sizeof(*fi));
213 	vcwrite(ChanFb, dmaaddr(fi));
214 	if(vcread(ChanFb) != 0)
215 		return 0;
216 	va = mmukmap(FRAMEBUFFER, fi->base & ~0xC0000000, fi->screensize);
217 	if(va)
218 		memset((char*)va, 0x7F, fi->screensize);
219 	return (void*)va;
220 }
221 
222 int
fbblank(int blank)223 fbblank(int blank)
224 {
225 	u32int buf[1];
226 
227 	buf[0] = blank;
228 	if(vcreq(TagFbblank, buf, sizeof buf, sizeof buf) != sizeof buf)
229 		return -1;
230 	return buf[0] & 1;
231 }
232 
233 /*
234  * Power management
235  */
236 void
setpower(int dev,int on)237 setpower(int dev, int on)
238 {
239 	u32int buf[2];
240 
241 	buf[0] = dev;
242 	buf[1] = Powerwait | (on? 1 : 0);
243 	vcreq(TagSetpower, buf, sizeof buf, sizeof buf);
244 }
245 
246 int
getpower(int dev)247 getpower(int dev)
248 {
249 	u32int buf[2];
250 
251 	buf[0] = dev;
252 	buf[1] = 0;
253 	if(vcreq(TagGetpower, buf, sizeof buf[0], sizeof buf) != sizeof buf)
254 		return -1;
255 	return buf[0] & 1;
256 }
257 
258 /*
259  * Get ethernet address (as hex string)
260  *	 [not reentrant]
261  */
262 char *
getethermac(void)263 getethermac(void)
264 {
265 	uchar ea[8];
266 	char *p;
267 	int i;
268 	static char buf[16];
269 
270 	memset(ea, 0, sizeof ea);
271 	vcreq(TagGetmac, ea, 0, sizeof ea);
272 	p = buf;
273 	for(i = 0; i < 6; i++)
274 		p += sprint(p, "%.2x", ea[i]);
275 	return buf;
276 }
277 
278 /*
279  * Get board revision
280  */
281 uint
getboardrev(void)282 getboardrev(void)
283 {
284 	u32int buf[1];
285 
286 	if(vcreq(TagGetrev, buf, 0, sizeof buf) != sizeof buf)
287 		return 0;
288 	return buf[0];
289 }
290 
291 /*
292  * Get firmware revision
293  */
294 uint
getfirmware(void)295 getfirmware(void)
296 {
297 	u32int buf[1];
298 
299 	if(vcreq(TagGetfwrev, buf, 0, sizeof buf) != sizeof buf)
300 		return 0;
301 	return buf[0];
302 }
303 
304 /*
305  * Get serial number
306  */
307 uvlong
getserial(void)308 getserial(void)
309 {
310 	uvlong buf;
311 
312 	if(vcreq(TagGetser, &buf, 0, sizeof buf) != sizeof buf)
313 		return 0;
314 	return buf;
315 }
316 
317 /*
318  * Get ARM ram
319  */
320 void
getramsize(Confmem * mem)321 getramsize(Confmem *mem)
322 {
323 	u32int buf[2];
324 
325 	if(vcreq(TagGetram, buf, 0, sizeof buf) != sizeof buf)
326 		return;
327 	mem->base = buf[0];
328 	mem->limit = buf[1];
329 }
330 
331 /*
332  * Get clock rate
333  */
334 ulong
getclkrate(int clkid)335 getclkrate(int clkid)
336 {
337 	u32int buf[2];
338 
339 	buf[0] = clkid;
340 	if(vcreq(TagGetclkspd, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf)
341 		return 0;
342 	return buf[1];
343 }
344 
345 /*
346  * Set clock rate to hz (or max speed if hz == 0)
347  */
348 void
setclkrate(int clkid,ulong hz)349 setclkrate(int clkid, ulong hz)
350 {
351 	u32int buf[2];
352 
353 	buf[0] = clkid;
354 	if(hz != 0)
355 		buf[1] = hz;
356 	else if(vcreq(TagGetclkmax, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf)
357 		return;
358 	vcreq(TagSetclkspd, buf, sizeof(buf), sizeof(buf));
359 }
360 
361 /*
362  * Get cpu temperature
363  */
364 uint
getcputemp(void)365 getcputemp(void)
366 {
367 	u32int buf[2];
368 
369 	buf[0] = 0;
370 	if(vcreq(TagGettemp, buf, sizeof(buf[0]), sizeof buf) != sizeof buf)
371 		return 0;
372 	return buf[1];
373 }
374 
375 /*
376  * Notify gpu that xhci firmware might need loading. This is for some
377  * pi4 board versions which are missing the eeprom chip for the vl805,
378  * requiring its firmware to come from the boot eeprom instead.
379  */
380 int
xhcireset(int devaddr)381 xhcireset(int devaddr)
382 {
383 	u32int buf[1];
384 
385 	buf[0] = devaddr;
386 	if(vcreq(TagXhciReset, buf, sizeof(buf), sizeof(buf[0])) == sizeof(buf[0]))
387 		return buf[0];
388 	return -1;
389 }
390 
391 /*
392  * Virtual GPIO - used for ACT LED on pi3
393  */
394 void
vgpinit(void)395 vgpinit(void)
396 {
397 	u32int buf[1];
398 	uintptr va;
399 
400 	buf[0] = 0;
401 	if(vcreq(TagGetGpio, buf, 0, sizeof(buf)) != sizeof buf || buf[0] == 0)
402 		return;
403 	va = mmukmap(VGPIO, buf[0] & ~0xC0000000, BY2PG);
404 	if(va == 0)
405 		return;
406 	vgpio.counts = (u32int*)va;
407 }
408 
409 void
vgpset(uint port,int on)410 vgpset(uint port, int on)
411 {
412 	if(vgpio.counts == nil || port >= Nvgpio || on == vgpio.ison)
413 		return;
414 	if(on)
415 		vgpio.incs++;
416 	else
417 		vgpio.decs++;
418 	vgpio.counts[port] = (vgpio.incs << 16) | vgpio.decs;
419 	vgpio.ison = on;
420 }
421