akaros/kern/drivers/dev/pci.c
<<
>>
Prefs
   1/*
   2 * This file is part of the UCB release of Plan 9. It is subject to the license
   3 * terms in the LICENSE file found in the top-level directory of this
   4 * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
   5 * part of the UCB release of Plan 9, including this file, may be copied,
   6 * modified, propagated, or distributed except according to the terms contained
   7 * in the LICENSE file.
   8 */
   9
  10#include <slab.h>
  11#include <kmalloc.h>
  12#include <kref.h>
  13#include <string.h>
  14#include <stdio.h>
  15#include <assert.h>
  16#include <error.h>
  17#include <cpio.h>
  18#include <pmap.h>
  19#include <smp.h>
  20#include <net/ip.h>
  21#include <arch/io.h>
  22
  23struct dev pcidevtab;
  24
  25static char *devname(void)
  26{
  27        return pcidevtab.name;
  28}
  29
  30enum {
  31        Qtopdir = 0,
  32
  33        Qpcidir,
  34        Qpcictl,
  35        Qpciraw,
  36
  37        PCI_CONFIG_SZ = 256,
  38};
  39
  40#define TYPE(q)         ((uint32_t)(q).path & 0x0F)
  41#define QID(c, t)       (((c)<<4)|(t))
  42
  43static struct dirtab topdir[] = {
  44        {".", {Qtopdir, 0, QTDIR}, 0, 0555},
  45        {"pci", {Qpcidir, 0, QTDIR}, 0, 0555},
  46};
  47
  48static int pcidirgen(struct chan *c, int t, int tbdf, struct dir *dp)
  49{
  50        struct qid q;
  51
  52        q = (struct qid) {
  53                BUSBDF(tbdf) | t, 0, 0};
  54        switch (t) {
  55        case Qpcictl:
  56                snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%dctl",
  57                         BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
  58                devdir(c, q, get_cur_genbuf(), 0, eve.name, 0444, dp);
  59                return 1;
  60        case Qpciraw:
  61                snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%draw",
  62                         BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
  63                devdir(c, q, get_cur_genbuf(), 128, eve.name, 0664, dp);
  64                return 1;
  65        }
  66        return -1;
  67}
  68
  69static int
  70pcigen(struct chan *c, char *_1, struct dirtab *_2, int _3, int s,
  71       struct dir *dp)
  72{
  73        int tbdf;
  74        struct pci_device *p;
  75        struct qid q;
  76
  77        switch (TYPE(c->qid)) {
  78        case Qtopdir:
  79                if (s == DEVDOTDOT) {
  80                        q = (struct qid) { QID(0, Qtopdir), 0, QTDIR };
  81                        snprintf(get_cur_genbuf(), GENBUF_SZ, "#%s",
  82                                 pcidevtab.name);
  83                        devdir(c, q, get_cur_genbuf(), 0, eve.name, 0555, dp);
  84                        return 1;
  85                }
  86                return devgen(c, NULL, topdir, ARRAY_SIZE(topdir), s, dp);
  87        case Qpcidir:
  88                if (s == DEVDOTDOT) {
  89                        q = (struct qid) {
  90                                QID(0, Qtopdir), 0, QTDIR};
  91                        snprintf(get_cur_genbuf(), GENBUF_SZ, "#%s",
  92                                 pcidevtab.name);
  93                        devdir(c, q, get_cur_genbuf(), 0, eve.name, 0555, dp);
  94                        return 1;
  95                }
  96                STAILQ_FOREACH(p, &pci_devices, all_dev) {
  97                        if (s < 2)
  98                                break;
  99                        s -= 2;
 100                }
 101                if (p == NULL)
 102                        return -1;
 103                return pcidirgen(c, s + Qpcictl, pci_to_tbdf(p), dp);
 104        case Qpcictl:
 105        case Qpciraw:
 106                tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
 107                p = pci_match_tbdf(tbdf);
 108                if (p == NULL)
 109                        return -1;
 110                return pcidirgen(c, TYPE(c->qid), tbdf, dp);
 111        default:
 112                break;
 113        }
 114        return -1;
 115}
 116
 117static struct chan *pciattach(char *spec)
 118{
 119        return devattach(devname(), spec);
 120}
 121
 122struct walkqid *pciwalk(struct chan *c, struct chan *nc, char **name,
 123                        unsigned int nname)
 124{
 125        return devwalk(c, nc, name, nname, (struct dirtab *)0, 0, pcigen);
 126}
 127
 128static size_t pcistat(struct chan *c, uint8_t *dp, size_t n)
 129{
 130        return devstat(c, dp, n, (struct dirtab *)0, 0L, pcigen);
 131}
 132
 133static struct chan *pciopen(struct chan *c, int omode)
 134{
 135        c = devopen(c, omode, (struct dirtab *)0, 0, pcigen);
 136        switch (TYPE(c->qid)) {
 137        default:
 138                break;
 139        }
 140        return c;
 141}
 142
 143static void pciclose(struct chan *_)
 144{
 145}
 146
 147static size_t pciread(struct chan *c, void *va, size_t n, off64_t offset)
 148{
 149        char buf[PCI_CONFIG_SZ], *ebuf, *w, *a;
 150        int i, tbdf, r;
 151        uint32_t x;
 152        struct pci_device *p;
 153
 154        a = va;
 155        switch (TYPE(c->qid)) {
 156        case Qtopdir:
 157        case Qpcidir:
 158                return devdirread(c, a, n, (struct dirtab *)0, 0L, pcigen);
 159        case Qpcictl:
 160                tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
 161                p = pci_match_tbdf(tbdf);
 162                if (p == NULL)
 163                        error(EINVAL, ERROR_FIXME);
 164                ebuf = buf + sizeof(buf) - 1;   /* -1 for newline */
 165                w = seprintf(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
 166                             p->class, p->subclass, p->progif, p->ven_id,
 167                             p->dev_id, p->irqline);
 168                for (i = 0; i < COUNT_OF(p->bar); i++) {
 169                        if (p->bar[i].mmio_sz == 0)
 170                                continue;
 171                        w = seprintf(w, ebuf, " %d:%.8lux %4x/%8x %d", i,
 172                                     p->bar[i].pio_base, p->bar[i].mmio_base32,
 173                                     p->bar[i].mmio_base64, p->bar[i].mmio_sz);
 174                }
 175                *w++ = '\n';
 176                *w = '\0';
 177                return readstr(offset, a, n, buf);
 178        case Qpciraw:
 179                tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
 180                p = pci_match_tbdf(tbdf);
 181                if (p == NULL)
 182                        error(EINVAL, ERROR_FIXME);
 183                if (n + offset > 256)
 184                        n = 256 - offset;
 185                if (n < 0)
 186                        return 0;
 187                r = offset;
 188                if (!(r & 3) && n == 4) {
 189                        x = pcidev_read32(p, r);
 190                        PBIT32(a, x);
 191                        return 4;
 192                }
 193                if (!(r & 1) && n == 2) {
 194                        x = pcidev_read16(p, r);
 195                        PBIT16(a, x);
 196                        return 2;
 197                }
 198                for (i = 0; i < n; i++) {
 199                        x = pcidev_read8(p, r);
 200                        PBIT8(a, x);
 201                        a++;
 202                        r++;
 203                }
 204                return i;
 205        default:
 206                error(EINVAL, ERROR_FIXME);
 207        }
 208        return n;
 209}
 210
 211static size_t pciwrite(struct chan *c, void *va, size_t n, off64_t offset)
 212{
 213        uint8_t *a;
 214        int i, r, tbdf;
 215        uint32_t x;
 216        struct pci_device *p;
 217
 218        if (n > PCI_CONFIG_SZ)
 219                n = PCI_CONFIG_SZ;
 220        a = va;
 221
 222        switch (TYPE(c->qid)) {
 223        case Qpciraw:
 224                tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
 225                p = pci_match_tbdf(tbdf);
 226                if (p == NULL)
 227                        error(EINVAL, ERROR_FIXME);
 228                if (offset > PCI_CONFIG_SZ)
 229                        return 0;
 230                if (n + offset > PCI_CONFIG_SZ)
 231                        n = PCI_CONFIG_SZ - offset;
 232                r = offset;
 233                if (!(r & 3) && n == 4) {
 234                        x = GBIT32(a);
 235                        pcidev_write32(p, r, x);
 236                        return 4;
 237                }
 238                if (!(r & 1) && n == 2) {
 239                        x = GBIT16(a);
 240                        pcidev_write16(p, r, x);
 241                        return 2;
 242                }
 243                for (i = 0; i < n; i++) {
 244                        x = GBIT8(a);
 245                        pcidev_write8(p, r, x);
 246                        a++;
 247                        r++;
 248                }
 249                return i;
 250        default:
 251                error(EINVAL, ERROR_FIXME);
 252        }
 253        return n;
 254}
 255
 256struct dev pcidevtab __devtab = {
 257        .name = "pci",
 258
 259        .reset = devreset,
 260        .init = devinit,
 261        .shutdown = devshutdown,
 262        .attach = pciattach,
 263        .walk = pciwalk,
 264        .stat = pcistat,
 265        .open = pciopen,
 266        .create = devcreate,
 267        .close = pciclose,
 268        .read = pciread,
 269        .bread = devbread,
 270        .write = pciwrite,
 271        .bwrite = devbwrite,
 272        .remove = devremove,
 273        .wstat = devwstat,
 274};
 275