plan9front/sys/src/9/bcm/vcore.c

433 lines
7.1 KiB
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;
}