/* Copyright (C) Charles Forsyth * See /doc/license/NOTICE.Plan9-9k.txt for details about the licensing. */ /* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio * See /doc/license/gpl-2.0.txt for details about the licensing. */ /* Portions of this file are Copyright (C) 9front's team. * See /doc/license/9front-mit for details about the licensing. * See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors. */ /* * access to PCI configuration space (devpnp.c without PNP) * * TODO * - extend PCI raw access to configuration space (writes, byte/short access?) * - implement PCI access to memory/io space/BIOS ROM * - use c->aux instead of performing lookup on each read/write? */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #define DPRINT if(0) print #define XPRINT if(1) print enum { Qtopdir = 0, Qpcidir, Qpcictl, Qpciraw, }; #define TYPE(q) ((uint32_t)(q).path & 0x0F) #define QID(c, t) (((c)<<4)|(t)) static Dirtab topdir[] = { ".", { Qtopdir, 0, QTDIR }, 0, 0555, "pci", { Qpcidir, 0, QTDIR }, 0, 0555, }; extern Dev pcidevtab; static int pcigen2(Chan *c, int t, int tbdf, Dir *dp) { Qid q; q = (Qid){BUSBDF(tbdf)|t, 0, 0}; switch(t) { case Qpcictl: jehanne_snprint(up->genbuf, sizeof(up->genbuf), "%d.%d.%dctl", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); devdir(c, q, up->genbuf, 0, eve, 0444, dp); return 1; case Qpciraw: jehanne_snprint(up->genbuf, sizeof(up->genbuf), "%d.%d.%draw", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); devdir(c, q, up->genbuf, 128, eve, 0444, dp); return 1; } return -1; } static int pcigen(Chan *c, char * _1, Dirtab* _2, int _3, int s, Dir *dp) { Qid q; Pcidev *p; int tbdf; switch(TYPE(c->qid)){ case Qtopdir: if(s == DEVDOTDOT){ q = (Qid){QID(0, Qtopdir), 0, QTDIR}; jehanne_snprint(up->genbuf, sizeof(up->genbuf), "#%C", pcidevtab.dc); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } return devgen(c, nil, topdir, nelem(topdir), s, dp); case Qpcidir: if(s == DEVDOTDOT){ q = (Qid){QID(0, Qtopdir), 0, QTDIR}; jehanne_snprint(up->genbuf, sizeof(up->genbuf), "#%C", pcidevtab.dc); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } p = pcimatch(nil, 0, 0); while(s >= 2 && p != nil) { p = pcimatch(p, 0, 0); s -= 2; } if(p == nil) return -1; return pcigen2(c, s+Qpcictl, p->tbdf, dp); case Qpcictl: case Qpciraw: tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((uint32_t)c->qid.path); p = pcimatchtbdf(tbdf); if(p == nil) return -1; return pcigen2(c, TYPE(c->qid), tbdf, dp); default: break; } return -1; } static Chan* pciattach(Chan *c, Chan *ac, char *spec, int flags) { return devattach(pcidevtab.dc, spec); } Walkqid* pciwalk(Chan* c, Chan *nc, char** name, int nname) { return devwalk(c, nc, name, nname, nil, 0, pcigen); } static long pcistat(Chan* c, uint8_t* dp, long n) { return devstat(c, dp, n, nil, 0, pcigen); } static Chan* pciopen(Chan *c, unsigned long omode) { return devopen(c, omode, nil, 0, pcigen); } static void pciclose(Chan* _1) { } static long pciread(Chan *c, void *va, long n, int64_t offset) { uint32_t x; Pcidev *p; char buf[256], *ebuf, *w; char *a = va; int i, tbdf, r; switch(TYPE(c->qid)){ case Qtopdir: case Qpcidir: return devdirread(c, a, n, nil, 0, pcigen); case Qpcictl: tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((uint32_t)c->qid.path); p = pcimatchtbdf(tbdf); if(p == nil) error(Egreg); ebuf = buf+sizeof buf-1; /* -1 for newline */ w = jehanne_seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d", p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl); for(i=0; imem); i++){ if(p->mem[i].size == 0) continue; w = jehanne_seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size); } *w++ = '\n'; *w = '\0'; return readstr(offset, a, n, buf); case Qpciraw: tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((uint32_t)c->qid.path); p = pcimatchtbdf(tbdf); if(p == nil) error(Egreg); if(offset > 256) return 0; if(n+offset > 256) n = 256-offset; r = offset; if(!(r & 3) && n == 4){ x = pcicfgr32(p, r); PBIT32(a, x); return 4; } if(!(r & 1) && n == 2){ x = pcicfgr16(p, r); PBIT16(a, x); return 2; } for(i = 0; i < n; i++){ x = pcicfgr8(p, r); PBIT8(a, x); a++; r++; } return i; default: error(Egreg); } return n; } static long pciwrite(Chan *c, void *va, long n, int64_t offset) { char buf[256]; Pcidev *p; uint32_t x; uint8_t *a; int i, r, tbdf; if(n >= sizeof(buf)) n = sizeof(buf)-1; jehanne_strncpy(buf, va, n); buf[n] = 0; switch(TYPE(c->qid)){ case Qpciraw: tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((uint32_t)c->qid.path); p = pcimatchtbdf(tbdf); if(p == nil) error(Egreg); if(offset > 256) return 0; if(n+offset > 256) n = 256-offset; a = va; r = offset; if(!(r & 3) && n == 4){ x = GBIT32(a); pcicfgw32(p, r, x); return 4; } if(!(r & 1) && n == 2){ x = GBIT16(a); pcicfgw16(p, r, x); return 2; } for(i = 0; i < n; i++){ x = GBIT8(a); pcicfgw8(p, r, x); a++; r++; } return i; default: error(Egreg); } return n; } Dev pcidevtab = { '$', "pci", devreset, devinit, devshutdown, pciattach, pciwalk, pcistat, pciopen, devcreate, pciclose, pciread, devbread, pciwrite, devbwrite, devremove, devwstat, };