Implement the pci device.
[akaros.git] / kern / drivers / dev / pci.c
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 <vfs.h>
11 #include <kfs.h>
12 #include <slab.h>
13 #include <kmalloc.h>
14 #include <kref.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <error.h>
19 #include <cpio.h>
20 #include <pmap.h>
21 #include <smp.h>
22 #include <ip.h>
23 #include <arch/io.h>
24
25 enum {
26         Qtopdir = 0,
27
28         Qpcidir,
29         Qpcictl,
30         Qpciraw,
31 };
32
33 #define TYPE(q)         ((uint32_t)(q).path & 0x0F)
34 #define QID(c, t)       (((c)<<4)|(t))
35
36 static struct dirtab topdir[] = {
37         {".", {Qtopdir, 0, QTDIR}, 0, 0555},
38         {"pci", {Qpcidir, 0, QTDIR}, 0, 0555},
39 };
40
41 extern struct dev pcidevtab;
42
43 static int pcidirgen(struct chan *c, int t, int tbdf, struct dir *dp)
44 {
45         struct qid q;
46
47         q = (struct qid) {
48         BUSBDF(tbdf) | t, 0, 0};
49         switch (t) {
50                 case Qpcictl:
51                         snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%dctl",
52                                          BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
53                         devdir(c, q, get_cur_genbuf(), 0, eve, 0444, dp);
54                         return 1;
55                 case Qpciraw:
56                         snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%draw",
57                                          BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
58                         devdir(c, q, get_cur_genbuf(), 128, eve, 0664, dp);
59                         return 1;
60         }
61         return -1;
62 }
63
64 static int
65 pcigen(struct chan *c, char *unused, struct dirtab *unuseddirtab, int unused_int, int s,
66            struct dir *dp)
67 {
68         int tbdf;
69         struct pci_device *p = NULL;
70         struct qid q;
71
72         switch (TYPE(c->qid)) {
73                 case Qtopdir:
74                         if (s == DEVDOTDOT) {
75                                 q = (struct qid) {
76                                 QID(0, Qtopdir), 0, QTDIR};
77                                 snprintf(get_cur_genbuf(), GENBUF_SZ, "#%C", pcidevtab.dc);
78                                 devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
79                                 return 1;
80                         }
81                         return devgen(c, NULL, topdir, ARRAY_SIZE(topdir), s, dp);
82                 case Qpcidir:
83                         if (s == DEVDOTDOT) {
84                                 q = (struct qid) {
85                                 QID(0, Qtopdir), 0, QTDIR};
86                                 snprintf(get_cur_genbuf(), GENBUF_SZ, "#%C", pcidevtab.dc);
87                                 devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
88                                 return 1;
89                         }
90                         p = pci_match_vd(NULL, 0, 0);
91                         while (s >= 2 && p != NULL) {
92                                 p = pci_match_vd(p, 0, 0);
93                                 s -= 2;
94                         }
95                         if (p == NULL)
96                                 return -1;
97                         return pcidirgen(c, s + Qpcictl, MKBUS(0,p->bus,p->dev,p->func), dp);
98                 case Qpcictl:
99                 case Qpciraw:
100                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
101                         p = pci_match_tbdf(tbdf);
102                         if (p == NULL)
103                                 return -1;
104                         return pcidirgen(c, TYPE(c->qid), tbdf, dp);
105                 default:
106                         break;
107         }
108         return -1;
109 }
110
111 static struct chan *pciattach(char *spec)
112 {
113         return devattach(pcidevtab.dc, spec);
114 }
115
116 struct walkqid *pciwalk(struct chan *c, struct chan *nc, char **name, int nname)
117 {
118         return devwalk(c, nc, name, nname, (struct dirtab *)0, 0, pcigen);
119 }
120
121 static int pcistat(struct chan *c, uint8_t * dp, int n)
122 {
123         return devstat(c, dp, n, (struct dirtab *)0, 0L, pcigen);
124 }
125
126 static struct chan *pciopen(struct chan *c, int omode)
127 {
128         c = devopen(c, omode, (struct dirtab *)0, 0, pcigen);
129         switch (TYPE(c->qid)) {
130                 default:
131                         break;
132         }
133         return c;
134 }
135
136 static void pciclose(struct chan *unused)
137 {
138 }
139
140 static long pciread(struct chan *c, void *va, long n, int64_t offset)
141 {
142         char buf[256], *ebuf, *w, *a;
143         int i, tbdf, r;
144         uint32_t x;
145         struct pci_device *p;
146
147         a = va;
148         switch (TYPE(c->qid)) {
149                 case Qtopdir:
150                 case Qpcidir:
151                         return devdirread(c, a, n, (struct dirtab *)0, 0L, pcigen);
152                 case Qpcictl:
153                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
154                         p = pci_match_tbdf(tbdf);
155                         if (p == NULL)
156                                 error(Egreg);
157                         ebuf = buf + sizeof buf - 1;    /* -1 for newline */
158                         w = seprintf(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
159                                                  p->class, p->subclass, p->progif, p->ven_id, p->dev_id, p->irqline);
160                         for (i = 0; i < p->nr_bars; i++) {
161                                 w = seprintf(w, ebuf, " %d:%.8lux %d", i, p->bar[i].raw_bar,
162                                              p->bar[i].mmio_sz);
163                         }
164                         *w++ = '\n';
165                         *w = '\0';
166                         return readstr(offset, a, n, buf);
167                 case Qpciraw:
168                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
169                         p = pci_match_tbdf(tbdf);
170                         if (p == NULL)
171                                 error(Egreg);
172                         if (n + offset > 256)
173                                 n = 256 - offset;
174                         if (n < 0)
175                                 return 0;
176                         r = offset;
177                         if (!(r & 3) && n == 4) {
178                                 x = pcidev_read32(p, r);
179                                 PBIT32(a, x);
180                                 return 4;
181                         }
182                         if (!(r & 1) && n == 2) {
183                                 x = pcidev_read16(p, r);
184                                 PBIT16(a, x);
185                                 return 2;
186                         }
187                         for (i = 0; i < n; i++) {
188                                 x = pcidev_read8(p, r);
189                                 PBIT8(a, x);
190                                 a++;
191                                 r++;
192                         }
193                         return i;
194                 default:
195                         error(Egreg);
196         }
197         return n;
198 }
199
200 static long pciwrite(struct chan *c, void *va, long n, int64_t offset)
201 {
202         char buf[256];
203         uint8_t *a;
204         int i, r, tbdf;
205         uint32_t x;
206         struct pci_device *p;
207
208         if (n >= sizeof(buf))
209                 n = sizeof(buf) - 1;
210         a = va;
211         strncpy(buf, (char *)a, n);
212         buf[n] = 0;
213
214         switch (TYPE(c->qid)) {
215                 case Qpciraw:
216                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
217                         p = pci_match_tbdf(tbdf);
218                         if (p == NULL)
219                                 error(Egreg);
220                         if (offset > 256)
221                                 return 0;
222                         if (n + offset > 256)
223                                 n = 256 - offset;
224                         r = offset;
225                         if (!(r & 3) && n == 4) {
226                                 x = GBIT32(a);
227                                 pcidev_write32(p, r, x);
228                                 return 4;
229                         }
230                         if (!(r & 1) && n == 2) {
231                                 x = GBIT16(a);
232                                 pcidev_write16(p, r, x);
233                                 return 2;
234                         }
235                         for (i = 0; i < n; i++) {
236                                 x = GBIT8(a);
237                                 pcidev_write8(p, r, x);
238                                 a++;
239                                 r++;
240                         }
241                         return i;
242                 default:
243                         error(Egreg);
244         }
245         return n;
246 }
247
248 struct dev pcidevtab __devtab = {
249         '$',
250         "pci",
251
252         devreset,
253         devinit,
254         devshutdown,
255         pciattach,
256         pciwalk,
257         pcistat,
258         pciopen,
259         devcreate,
260         pciclose,
261         pciread,
262         devbread,
263         pciwrite,
264         devbwrite,
265         devremove,
266         devwstat,
267 };