ref: e1cd22504e26d0a732a4219d38d6e5fe3e03aa21
dir: /sys/src/9/bcm/vcore.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" /* * Mailbox interface with videocore gpu */ #define MAILBOX (VIRTIO+0xB880) typedef struct Prophdr Prophdr; typedef struct Fbinfo Fbinfo; typedef struct Vgpio Vgpio; enum { Read = 0x00>>2, Write = 0x00>>2, Peek = 0x10>>2, Sender = 0x14>>2, Status = 0x18>>2, Full = 1<<31, Empty = 1<<30, Config = 0x1C>>2, NRegs = 0x20>>2, ChanMask = 0xF, ChanProps = 8, ChanFb = 1, Req = 0x0, RspOk = 0x80000000, TagResp = 1<<31, TagGetfwrev = 0x00000001, TagGetrev = 0x00010002, TagGetmac = 0x00010003, TagGetram = 0x00010005, TagGetpower = 0x00020001, TagSetpower = 0x00028001, Powerwait = 1<<1, TagGetclkstate = 0x00030001, TagGetclkspd = 0x00030002, TagGetclkmax = 0x00030004, TagSetclkstate = 0x00038001, TagSetclkspd = 0x00038002, TagGetEgpioState= 0x00030041, TagSetEgpioState= 0x00038041, TagSetSdhostClk = 0x00038042, TagGetEgpioConf = 0x00030043, TagSetEgpioConf = 0x00038043, TagGettemp = 0x00030006, TagXhciReset = 0x00030058, TagFballoc = 0x00040001, TagFbfree = 0x00048001, TagFbblank = 0x00040002, TagGetres = 0x00040003, TagSetres = 0x00048003, TagGetvres = 0x00040004, TagSetvres = 0x00048004, TagGetdepth = 0x00040005, TagSetdepth = 0x00048005, TagGetrgb = 0x00040006, TagSetrgb = 0x00048006, TagGetGpio = 0x00040010, Nvgpio = 2, }; struct Fbinfo { u32int xres; u32int yres; u32int xresvirtual; u32int yresvirtual; u32int pitch; /* returned by gpu */ u32int bpp; u32int xoffset; u32int yoffset; u32int base; /* returned by gpu */ u32int screensize; /* returned by gpu */ }; struct Prophdr { u32int len; u32int req; u32int tag; u32int tagbuflen; u32int taglen; u32int data[1]; }; struct Vgpio { u32int *counts; u16int incs; u16int decs; int ison; }; static Vgpio vgpio; static void vcwrite(uint chan, int val) { u32int *r; r = (u32int*)MAILBOX + NRegs; val &= ~ChanMask; while(r[Status]&Full) ; coherence(); r[Write] = val | chan; } static int vcread(uint chan) { u32int *r; int x; r = (u32int*)MAILBOX; do{ while(r[Status]&Empty) ; coherence(); x = r[Read]; }while((x&ChanMask) != chan); return x & ~ChanMask; } /* * Property interface */ static int vcreq(int tag, void *buf, int vallen, int rsplen) { uintptr r; int n; Prophdr *prop; uintptr aprop; static int busaddr = 1; if(rsplen < vallen) rsplen = vallen; rsplen = (rsplen+3) & ~3; prop = (Prophdr*)(VCBUFFER); n = sizeof(Prophdr) + rsplen + 8; memset(prop, 0, n); prop->len = n; prop->req = Req; prop->tag = tag; prop->tagbuflen = rsplen; prop->taglen = vallen; if(vallen > 0) memmove(prop->data, buf, vallen); cachedwbinvse(prop, n); for(;;){ aprop = busaddr? dmaaddr(prop) : (uintptr)prop; vcwrite(ChanProps, aprop); r = vcread(ChanProps); if(r == aprop) break; if(!busaddr) return -1; busaddr = 0; } cachedinvse(prop, n); if(prop->req == RspOk && prop->tag == tag && (prop->taglen&TagResp)) { if((n = prop->taglen & ~TagResp) < rsplen) rsplen = n; memmove(buf, prop->data, rsplen); }else rsplen = -1; return rsplen; } /* * Framebuffer */ static int fbdefault(int *width, int *height, int *depth) { u32int buf[3]; char *p; if(vcreq(TagGetres, &buf[0], 0, 2*4) != 2*4 || vcreq(TagGetdepth, &buf[2], 0, 4) != 4) return -1; *width = buf[0]; *height = buf[1]; if((p = getconf("bcm2708_fb.fbdepth")) != nil) *depth = atoi(p); else *depth = buf[2]; return 0; } void* fbinit(int set, int *width, int *height, int *depth) { Fbinfo *fi; uintptr va; if(!set) fbdefault(width, height, depth); /* Screen width must be a multiple of 16 */ *width &= ~0xF; fi = (Fbinfo*)(VCBUFFER); memset(fi, 0, sizeof(*fi)); fi->xres = fi->xresvirtual = *width; fi->yres = fi->yresvirtual = *height; fi->bpp = *depth; cachedwbinvse(fi, sizeof(*fi)); vcwrite(ChanFb, dmaaddr(fi)); if(vcread(ChanFb) != 0) return nil; cachedinvse(fi, sizeof(*fi)); va = mmukmap(FRAMEBUFFER, (fi->base&~0xC0000000)|PHYSDRAM, fi->screensize); if(va) memset((char*)va, 0x7F, fi->screensize); return (void*)va; } int fbblank(int blank) { u32int buf[1]; buf[0] = blank; if(vcreq(TagFbblank, buf, sizeof buf, sizeof buf) != sizeof buf) return -1; return buf[0] & 1; } /* * Power management */ void setpower(int dev, int on) { u32int buf[2]; buf[0] = dev; buf[1] = Powerwait | (on? 1 : 0); vcreq(TagSetpower, buf, sizeof buf, sizeof buf); } int getpower(int dev) { u32int buf[2]; buf[0] = dev; buf[1] = 0; if(vcreq(TagGetpower, buf, sizeof buf[0], sizeof buf) != sizeof buf) return -1; return buf[0] & 1; } /* * Get ethernet address (as hex string) * [not reentrant] */ char * getethermac(void) { uchar ea[8]; char *p; int i; static char buf[16]; memset(ea, 0, sizeof ea); vcreq(TagGetmac, ea, 0, sizeof ea); p = buf; for(i = 0; i < 6; i++) p += sprint(p, "%.2x", ea[i]); return buf; } /* * Get board revision */ uint getboardrev(void) { u32int buf[1]; if(vcreq(TagGetrev, buf, 0, sizeof buf) != sizeof buf) return 0; return buf[0]; } /* * Get firmware revision */ uint getfirmware(void) { u32int buf[1]; if(vcreq(TagGetfwrev, buf, 0, sizeof buf) != sizeof buf) return 0; return buf[0]; } /* * Get ARM ram */ void getramsize(Confmem *mem) { u32int buf[2]; if(vcreq(TagGetram, buf, 0, sizeof buf) != sizeof buf) return; mem->base = buf[0]; mem->limit = buf[1]; } /* * Get clock rate */ ulong getclkrate(int clkid) { u32int buf[2]; buf[0] = clkid; if(vcreq(TagGetclkspd, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf) return 0; return buf[1]; } /* * Set clock rate to hz (or max speed if hz == 0) */ void setclkrate(int clkid, ulong hz) { u32int buf[2]; buf[0] = clkid; if(hz != 0) buf[1] = hz; else if(vcreq(TagGetclkmax, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf) return; vcreq(TagSetclkspd, buf, sizeof(buf), sizeof(buf)); } /* * Get cpu temperature */ uint getcputemp(void) { u32int buf[2]; buf[0] = 0; if(vcreq(TagGettemp, buf, sizeof(buf[0]), sizeof buf) != sizeof buf) return 0; return buf[1]; } /* * Virtual GPIO - used for ACT LED on pi3 */ void vgpinit(void) { u32int buf[1]; uintptr va; buf[0] = 0; if(vcreq(TagGetGpio, buf, 0, sizeof(buf)) != sizeof buf || buf[0] == 0) return; va = mmukmap(VGPIO, buf[0] & ~0xC0000000, BY2PG); if(va == 0) return; vgpio.counts = (u32int*)va; } void vgpset(uint port, int on) { if(vgpio.counts == nil || port >= Nvgpio || on == vgpio.ison) return; if(on) vgpio.incs++; else vgpio.decs++; vgpio.counts[port] = (vgpio.incs << 16) | vgpio.decs; vgpio.ison = on; } /* * Raspberry Pi GPIO expander (Pi 3 and 4) */ void egpset(uint port, int on) { u32int buf[2]; if(port >= 8) return; buf[0] = 128 + port; buf[1] = on; vcreq(TagSetEgpioState, buf, sizeof(buf), sizeof(buf)); } /* * Notify gpu that xhci firmware might need loading. This is for some * pi4 board versions which are missing the eeprom chip for the vl805, * requiring its firmware to come from the boot eeprom instead. */ int xhcireset(int devaddr) { u32int buf[1]; buf[0] = devaddr; if(vcreq(TagXhciReset, buf, sizeof(buf), sizeof(buf[0])) == sizeof(buf[0])) return buf[0]; return -1; }