#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#define POWERREGS (VIRTIO+0x100000)
#define RPIVIDASB (VIRTIO+0xC11000)
#define COREREGS (VIRTIO+0xC04000)
enum {
Qdir = 0,
Qdone,
};
enum {
IRQv3d = 106,
};
enum {
ASB_V3D_S_CTRL = 0x08>>2,
ASB_V3D_M_CTRL = 0x0C>>2,
ASB_REQ_STOP = 1<<0,
ASB_ACK = 1<<1,
PM_GRAFX = 0x10C>>2,
PM_V3DRSTN = 1<<6,
Password = 0x5A<<24,
};
enum {
V3D_CTL_INT_STS = 0x50>>2,
V3D_CTL_INT_CLR = 0x58>>2,
V3D_HUB_INT_STS = 0x50>>2,
V3D_HUB_INT_CLR = 0x58>>2,
V3D_HUB_INT_MSK_STS = 0x0005c>>2,
V3D_HUB_INT_MSK_SET = 0x00060>>2,
V3D_HUB_INT_MSK_CLR = 0x00064>>2,
V3D_HUB_INT_MMU_WRV = 1<<5,
V3D_HUB_INT_MMU_PTI = 1<<4,
V3D_HUB_INT_MMU_CAP = 1<<3,
V3D_HUB_INT_TFUC = 1<<1,
V3D_HUB_IRQS = V3D_HUB_INT_MMU_WRV | V3D_HUB_INT_MMU_PTI | V3D_HUB_INT_MMU_CAP | V3D_HUB_INT_TFUC,
V3D_CTL_INT_MSK_STS = 0x0005c>>2,
V3D_CTL_INT_MSK_SET = 0x00060>>2,
V3D_CTL_INT_MSK_CLR = 0x00064>>2,
V3D_INT_CSDDONE = 1<<7,
V3D_INT_GMPV = 1<<5,
V3D_INT_SPILLUSE = 1<<3,
V3D_INT_OUTOMEM = 1<<2,
V3D_INT_FLDONE = 1<<1,
V3D_INT_FRDONE = 1<<0,
V3D_CORE_IRQS = V3D_INT_OUTOMEM | V3D_INT_FLDONE | V3D_INT_FRDONE | V3D_INT_CSDDONE | V3D_INT_GMPV,
};
Dirtab v3ddir[] = {
".", {Qdir, 0, QTDIR}, 0, 0555,
"done", {Qdone, 0}, 0, 0664,
};
static Rendez rend;
static u32int status;
void
powerv3d(void)
{
u32int val;
u32int *asb = (u32int *)RPIVIDASB;
u32int *pwr = (u32int *)POWERREGS;
pwr[PM_GRAFX] = Password | pwr[PM_GRAFX] | PM_V3DRSTN;
val = asb[ASB_V3D_M_CTRL] & ~ASB_REQ_STOP;
asb[ASB_V3D_M_CTRL] = Password | val;
while(asb[ASB_V3D_M_CTRL] & ASB_ACK)
;
val = asb[ASB_V3D_S_CTRL] & ~ASB_REQ_STOP;
asb[ASB_V3D_S_CTRL] = Password | val;
while(asb[ASB_V3D_S_CTRL] & ASB_ACK)
;
}
static void
interrupt(Ureg *, void*)
{
u32int *core = (u32int *)COREREGS;
u32int intsts;
intsts = core[V3D_CTL_INT_STS];
core[V3D_CTL_INT_CLR] = intsts;
if(status){
status = intsts;
wakeup(&rend);
}
intsts = core[V3D_HUB_INT_STS];
core[V3D_HUB_INT_CLR] = intsts;
}
void
v3dinit(void)
{
u32int *core = (u32int *)COREREGS;
Physseg seg;
memset(&seg, 0, sizeof(seg));
seg.attr = SG_PHYSICAL | SG_DEVICE | SG_NOEXEC;
seg.name = "v3d";
seg.pa = 0xfec00000;
seg.size = BY2PG;
addphysseg(&seg);
powerv3d();
core[V3D_CTL_INT_CLR] = V3D_CORE_IRQS;
core[V3D_HUB_INT_CLR] = V3D_HUB_IRQS;
core[V3D_CTL_INT_MSK_SET] = ~V3D_CORE_IRQS;
core[V3D_CTL_INT_MSK_CLR] = V3D_CORE_IRQS;
core[V3D_HUB_INT_MSK_SET] = ~V3D_HUB_IRQS;
core[V3D_HUB_INT_MSK_CLR] = V3D_HUB_IRQS;
intrenable(IRQv3d, interrupt, nil, BUSUNKNOWN, "v3d");
}
static int
done(void*)
{
return status;
}
static long
v3dread(Chan *c, void *buf, long n, vlong off)
{
if(c->qid.type & QTDIR)
return devdirread(c, buf, n, v3ddir, nelem(v3ddir), devgen);
switch((ulong)c->qid.path){
case Qdone:
sleep(&rend, done, nil);
return readnum(off, buf, n, status, NUMSIZE);
}
error(Ebadarg);
return 0;
}
static long
v3dwrite(Chan*, void*, long, vlong)
{
error(Eperm);
return 0;
}
static Chan*
v3dattach(char *spec)
{
return devattach(L'β', spec);
}
static Walkqid*
v3dwalk(Chan* c, Chan *nc, char** name, int nname)
{
return devwalk(c, nc, name, nname, v3ddir, nelem(v3ddir), devgen);
}
static int
v3dstat(Chan* c, uchar* dp, int n)
{
return devstat(c, dp, n, v3ddir, nelem(v3ddir), devgen);
}
static Chan*
v3dopen(Chan* c, int omode)
{
omode = openmode(omode);
switch((ulong)c->qid.path){
case Qdone:
if(omode != OREAD)
error(Eperm);
break;
}
return devopen(c, omode, v3ddir, nelem(v3ddir), devgen);
}
static void
v3dclose(Chan*)
{
}
Dev v3ddevtab = {
L'β',
"v3d",
devreset,
v3dinit,
devshutdown,
v3dattach,
v3dwalk,
v3dstat,
v3dopen,
devcreate,
v3dclose,
v3dread,
devbread,
v3dwrite,
devbwrite,
devremove,
devwstat,
};
|