| /* |
| * 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 <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 <net/ip.h> |
| #include <arch/io.h> |
| |
| struct dev pcidevtab; |
| |
| static char *devname(void) |
| { |
| return pcidevtab.name; |
| } |
| |
| enum { |
| Qtopdir = 0, |
| |
| Qpcidir, |
| Qpcictl, |
| Qpciraw, |
| |
| PCI_CONFIG_SZ = 256, |
| }; |
| |
| #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}, |
| }; |
| |
| 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.name, 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.name, 0664, dp); |
| return 1; |
| } |
| return -1; |
| } |
| |
| static int |
| pcigen(struct chan *c, char *_1, struct dirtab *_2, int _3, int s, |
| struct dir *dp) |
| { |
| int tbdf; |
| struct pci_device *p; |
| 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, "#%s", |
| pcidevtab.name); |
| devdir(c, q, get_cur_genbuf(), 0, eve.name, 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, "#%s", |
| pcidevtab.name); |
| devdir(c, q, get_cur_genbuf(), 0, eve.name, 0555, dp); |
| return 1; |
| } |
| STAILQ_FOREACH(p, &pci_devices, all_dev) { |
| if (s < 2) |
| break; |
| s -= 2; |
| } |
| if (p == NULL) |
| return -1; |
| return pcidirgen(c, s + Qpcictl, pci_to_tbdf(p), 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(devname(), spec); |
| } |
| |
| struct walkqid *pciwalk(struct chan *c, struct chan *nc, char **name, |
| unsigned int nname) |
| { |
| return devwalk(c, nc, name, nname, (struct dirtab *)0, 0, pcigen); |
| } |
| |
| static size_t pcistat(struct chan *c, uint8_t *dp, size_t 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 *_) |
| { |
| } |
| |
| static size_t pciread(struct chan *c, void *va, size_t n, off64_t offset) |
| { |
| char buf[PCI_CONFIG_SZ], *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(EINVAL, ERROR_FIXME); |
| 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 < COUNT_OF(p->bar); i++) { |
| if (p->bar[i].mmio_sz == 0) |
| continue; |
| w = seprintf(w, ebuf, " %d:%.8lux %d", i, |
| p->bar[i].pio_base, 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(EINVAL, ERROR_FIXME); |
| 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(EINVAL, ERROR_FIXME); |
| } |
| return n; |
| } |
| |
| static size_t pciwrite(struct chan *c, void *va, size_t n, off64_t offset) |
| { |
| uint8_t *a; |
| int i, r, tbdf; |
| uint32_t x; |
| struct pci_device *p; |
| |
| if (n > PCI_CONFIG_SZ) |
| n = PCI_CONFIG_SZ; |
| a = va; |
| |
| 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(EINVAL, ERROR_FIXME); |
| if (offset > PCI_CONFIG_SZ) |
| return 0; |
| if (n + offset > PCI_CONFIG_SZ) |
| n = PCI_CONFIG_SZ - 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(EINVAL, ERROR_FIXME); |
| } |
| return n; |
| } |
| |
| struct dev pcidevtab __devtab = { |
| .name = "pci", |
| |
| .reset = devreset, |
| .init = devinit, |
| .shutdown = devshutdown, |
| .attach = pciattach, |
| .walk = pciwalk, |
| .stat = pcistat, |
| .open = pciopen, |
| .create = devcreate, |
| .close = pciclose, |
| .read = pciread, |
| .bread = devbread, |
| .write = pciwrite, |
| .bwrite = devbwrite, |
| .remove = devremove, |
| .wstat = devwstat, |
| }; |