plan9fox/sys/src/9/bcm/vcore.c
cinap_lenrek bd23963c8f bcm64: fix usb xhci controller on pi4 8GB variant (thanks richard miller)
On the 8GB variant of the raspberry pi 4,
the eeprom chip for the xhci controller is missing and
instead loaded from sdram (by the gpu firmware).

for this, the gpu firmware needs to be notified of
the xhci controllers pci bus address (after reset)
that was assigned by our pci enumeration code.
2020-07-02 21:04:01 +02:00

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;
}