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