blob: 6f41af5743601b8c01ae38911322601b9b1a63de [file] [log] [blame] [edit]
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <vfs.h>
#include <kfs.h>
#include <slab.h>
#include <kmalloc.h>
#include <kref.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <error.h>
#include <cpio.h>
#include <pmap.h>
#include <smp.h>
#include <ip.h>
#include <arch/io.h>
enum {
Qtopdir = 0,
Qpcidir,
Qpcictl,
Qpciraw,
};
#define TYPE(q) ((uint32_t)(q).path & 0x0F)
#define QID(c, t) (((c)<<4)|(t))
static struct dirtab topdir[] = {
{".", {Qtopdir, 0, QTDIR}, 0, 0555},
{"pci", {Qpcidir, 0, QTDIR}, 0, 0555},
};
extern struct dev pcidevtab;
static int pcidirgen(struct chan *c, int t, int tbdf, struct dir *dp)
{
struct qid q;
q = (struct qid) {
BUSBDF(tbdf) | t, 0, 0};
switch (t) {
case Qpcictl:
snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%dctl",
BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
devdir(c, q, get_cur_genbuf(), 0, eve, 0444, dp);
return 1;
case Qpciraw:
snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%draw",
BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
devdir(c, q, get_cur_genbuf(), 128, eve, 0664, dp);
return 1;
}
return -1;
}
static int
pcigen(struct chan *c, char *unused, struct dirtab *unuseddirtab, int unused_int, int s,
struct dir *dp)
{
int tbdf;
struct pci_device *p = NULL;
struct qid q;
switch (TYPE(c->qid)) {
case Qtopdir:
if (s == DEVDOTDOT) {
q = (struct qid) {
QID(0, Qtopdir), 0, QTDIR};
snprintf(get_cur_genbuf(), GENBUF_SZ, "#%C", pcidevtab.dc);
devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
return 1;
}
return devgen(c, NULL, topdir, ARRAY_SIZE(topdir), s, dp);
case Qpcidir:
if (s == DEVDOTDOT) {
q = (struct qid) {
QID(0, Qtopdir), 0, QTDIR};
snprintf(get_cur_genbuf(), GENBUF_SZ, "#%C", pcidevtab.dc);
devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
return 1;
}
p = pci_match_vd(NULL, 0, 0);
while (s >= 2 && p != NULL) {
p = pci_match_vd(p, 0, 0);
s -= 2;
}
if (p == NULL)
return -1;
return pcidirgen(c, s + Qpcictl, MKBUS(0,p->bus,p->dev,p->func), dp);
case Qpcictl:
case Qpciraw:
tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
p = pci_match_tbdf(tbdf);
if (p == NULL)
return -1;
return pcidirgen(c, TYPE(c->qid), tbdf, dp);
default:
break;
}
return -1;
}
static struct chan *pciattach(char *spec)
{
return devattach(pcidevtab.dc, spec);
}
struct walkqid *pciwalk(struct chan *c, struct chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, (struct dirtab *)0, 0, pcigen);
}
static int pcistat(struct chan *c, uint8_t * dp, int n)
{
return devstat(c, dp, n, (struct dirtab *)0, 0L, pcigen);
}
static struct chan *pciopen(struct chan *c, int omode)
{
c = devopen(c, omode, (struct dirtab *)0, 0, pcigen);
switch (TYPE(c->qid)) {
default:
break;
}
return c;
}
static void pciclose(struct chan *unused)
{
}
static long pciread(struct chan *c, void *va, long n, int64_t offset)
{
char buf[256], *ebuf, *w, *a;
int i, tbdf, r;
uint32_t x;
struct pci_device *p;
a = va;
switch (TYPE(c->qid)) {
case Qtopdir:
case Qpcidir:
return devdirread(c, a, n, (struct dirtab *)0, 0L, pcigen);
case Qpcictl:
tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
p = pci_match_tbdf(tbdf);
if (p == NULL)
error(Egreg);
ebuf = buf + sizeof buf - 1; /* -1 for newline */
w = seprintf(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
p->class, p->subclass, p->progif, p->ven_id, p->dev_id, p->irqline);
for (i = 0; i < p->nr_bars; i++) {
w = seprintf(w, ebuf, " %d:%.8lux %d", i, p->bar[i].raw_bar,
p->bar[i].mmio_sz);
}
*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 = pci_match_tbdf(tbdf);
if (p == NULL)
error(Egreg);
if (n + offset > 256)
n = 256 - offset;
if (n < 0)
return 0;
r = offset;
if (!(r & 3) && n == 4) {
x = pcidev_read32(p, r);
PBIT32(a, x);
return 4;
}
if (!(r & 1) && n == 2) {
x = pcidev_read16(p, r);
PBIT16(a, x);
return 2;
}
for (i = 0; i < n; i++) {
x = pcidev_read8(p, r);
PBIT8(a, x);
a++;
r++;
}
return i;
default:
error(Egreg);
}
return n;
}
static long pciwrite(struct chan *c, void *va, long n, int64_t offset)
{
char buf[256];
uint8_t *a;
int i, r, tbdf;
uint32_t x;
struct pci_device *p;
if (n >= sizeof(buf))
n = sizeof(buf) - 1;
a = va;
strncpy(buf, (char *)a, n);
buf[n] = 0;
switch (TYPE(c->qid)) {
case Qpciraw:
tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
p = pci_match_tbdf(tbdf);
if (p == NULL)
error(Egreg);
if (offset > 256)
return 0;
if (n + offset > 256)
n = 256 - offset;
r = offset;
if (!(r & 3) && n == 4) {
x = GBIT32(a);
pcidev_write32(p, r, x);
return 4;
}
if (!(r & 1) && n == 2) {
x = GBIT16(a);
pcidev_write16(p, r, x);
return 2;
}
for (i = 0; i < n; i++) {
x = GBIT8(a);
pcidev_write8(p, r, x);
a++;
r++;
}
return i;
default:
error(Egreg);
}
return n;
}
struct dev pcidevtab __devtab = {
'$',
"pci",
devreset,
devinit,
devshutdown,
pciattach,
pciwalk,
pcistat,
pciopen,
devcreate,
pciclose,
pciread,
devbread,
pciwrite,
devbwrite,
devremove,
devwstat,
};