1901484c1SDavid du Colombier /*
2d5789509SDavid du Colombier * myricom 10g-pcie-8a 10 Gb ethernet driver
3901484c1SDavid du Colombier * © 2007 erik quanstrom, coraid
441ac1ab6SDavid du Colombier *
541ac1ab6SDavid du Colombier * the card is big endian.
691b330d9SDavid du Colombier * we use uvlong rather than uintptr to hold addresses so that
741ac1ab6SDavid du Colombier * we don't get "warning: stupid shift" on 32-bit architectures.
8*217e9e83SDavid du Colombier *
9*217e9e83SDavid du Colombier * appears to have massively-bloated buffers.
10901484c1SDavid du Colombier */
11901484c1SDavid du Colombier #include "u.h"
12901484c1SDavid du Colombier #include "../port/lib.h"
13901484c1SDavid du Colombier #include "mem.h"
14901484c1SDavid du Colombier #include "dat.h"
15901484c1SDavid du Colombier #include "fns.h"
16901484c1SDavid du Colombier #include "io.h"
17901484c1SDavid du Colombier #include "../port/error.h"
18901484c1SDavid du Colombier #include "../port/netif.h"
19901484c1SDavid du Colombier
2041ac1ab6SDavid du Colombier #include "../pc/etherif.h"
21901484c1SDavid du Colombier
2241ac1ab6SDavid du Colombier #ifndef KiB
2341ac1ab6SDavid du Colombier #define KiB 1024u /* Kibi 0x0000000000000400 */
2441ac1ab6SDavid du Colombier #define MiB 1048576u /* Mebi 0x0000000000100000 */
2541ac1ab6SDavid du Colombier #endif /* KiB */
26901484c1SDavid du Colombier
27901484c1SDavid du Colombier #define dprint(...) if(debug) print(__VA_ARGS__)
28901484c1SDavid du Colombier #define pcicapdbg(...)
2941ac1ab6SDavid du Colombier #define malign(n) mallocalign((n), 4*KiB, 0, 0)
30901484c1SDavid du Colombier
31901484c1SDavid du Colombier #include "etherm10g2k.i"
32901484c1SDavid du Colombier #include "etherm10g4k.i"
33901484c1SDavid du Colombier
34901484c1SDavid du Colombier static int debug = 0;
35901484c1SDavid du Colombier static char Etimeout[] = "timeout";
36901484c1SDavid du Colombier
37901484c1SDavid du Colombier enum {
38901484c1SDavid du Colombier Epromsz = 256,
39*217e9e83SDavid du Colombier Maxslots= 1024, /* rcv descriptors; wasteful: only 9 needed */
40901484c1SDavid du Colombier Align = 4096,
41*217e9e83SDavid du Colombier Maxmtu = 9000, /* jumbos; bad idea */
42901484c1SDavid du Colombier Noconf = 0xffffffff,
43901484c1SDavid du Colombier
4441ac1ab6SDavid du Colombier Fwoffset= 1*MiB,
45901484c1SDavid du Colombier Cmdoff = 0xf80000, /* command port offset */
46901484c1SDavid du Colombier Fwsubmt = 0xfc0000, /* firmware submission command port offset */
47901484c1SDavid du Colombier Rdmaoff = 0xfc01c0, /* rdma command port offset */
48901484c1SDavid du Colombier };
49901484c1SDavid du Colombier
50901484c1SDavid du Colombier enum {
51901484c1SDavid du Colombier CZero,
52901484c1SDavid du Colombier Creset,
53901484c1SDavid du Colombier Cversion,
54901484c1SDavid du Colombier
55901484c1SDavid du Colombier CSintrqdma, /* issue these before Cetherup */
56901484c1SDavid du Colombier CSbigsz, /* in bytes bigsize = 2^n */
57901484c1SDavid du Colombier CSsmallsz,
58901484c1SDavid du Colombier
59901484c1SDavid du Colombier CGsendoff,
60901484c1SDavid du Colombier CGsmallrxoff,
61901484c1SDavid du Colombier CGbigrxoff,
62901484c1SDavid du Colombier CGirqackoff,
63901484c1SDavid du Colombier CGirqdeassoff,
64901484c1SDavid du Colombier CGsendrgsz,
65901484c1SDavid du Colombier CGrxrgsz,
66901484c1SDavid du Colombier
67901484c1SDavid du Colombier CSintrqsz, /* 2^n */
68901484c1SDavid du Colombier Cetherup, /* above parameters + mtu/mac addr must be set first. */
69901484c1SDavid du Colombier Cetherdn,
70901484c1SDavid du Colombier
71901484c1SDavid du Colombier CSmtu, /* below may be issued live */
72901484c1SDavid du Colombier CGcoaloff, /* in µs */
73901484c1SDavid du Colombier CSstatsrate, /* in µs */
74901484c1SDavid du Colombier CSstatsdma,
75901484c1SDavid du Colombier
76901484c1SDavid du Colombier Cpromisc,
77901484c1SDavid du Colombier Cnopromisc,
78901484c1SDavid du Colombier CSmac,
79901484c1SDavid du Colombier
80901484c1SDavid du Colombier Cenablefc,
81901484c1SDavid du Colombier Cdisablefc,
82901484c1SDavid du Colombier
83901484c1SDavid du Colombier Cdmatest, /* address in d[0-1], d[2]=length */
84901484c1SDavid du Colombier
85901484c1SDavid du Colombier Cenableallmc,
86901484c1SDavid du Colombier Cdisableallmc,
87901484c1SDavid du Colombier
88901484c1SDavid du Colombier CSjoinmc,
89901484c1SDavid du Colombier CSleavemc,
90901484c1SDavid du Colombier Cleaveallmc,
91901484c1SDavid du Colombier
92901484c1SDavid du Colombier CSstatsdma2, /* adds (unused) multicast stats */
93901484c1SDavid du Colombier };
94901484c1SDavid du Colombier
95901484c1SDavid du Colombier typedef union {
96901484c1SDavid du Colombier uint i[2];
97901484c1SDavid du Colombier uchar c[8];
98901484c1SDavid du Colombier } Cmd;
99901484c1SDavid du Colombier
10041ac1ab6SDavid du Colombier typedef ulong Slot;
101901484c1SDavid du Colombier typedef struct {
10291b330d9SDavid du Colombier ushort cksum;
10391b330d9SDavid du Colombier ushort len;
10441ac1ab6SDavid du Colombier } Slotparts;
105901484c1SDavid du Colombier
106901484c1SDavid du Colombier enum {
107901484c1SDavid du Colombier SFsmall = 1,
108901484c1SDavid du Colombier SFfirst = 2,
109901484c1SDavid du Colombier SFalign = 4,
110901484c1SDavid du Colombier SFnotso = 16,
111901484c1SDavid du Colombier };
112901484c1SDavid du Colombier
113901484c1SDavid du Colombier typedef struct {
11491b330d9SDavid du Colombier ulong high;
11591b330d9SDavid du Colombier ulong low;
11691b330d9SDavid du Colombier ushort hdroff;
11791b330d9SDavid du Colombier ushort len;
118901484c1SDavid du Colombier uchar pad;
119901484c1SDavid du Colombier uchar nrdma;
120901484c1SDavid du Colombier uchar chkoff;
121901484c1SDavid du Colombier uchar flags;
122901484c1SDavid du Colombier } Send;
123901484c1SDavid du Colombier
124901484c1SDavid du Colombier typedef struct {
125901484c1SDavid du Colombier QLock;
126901484c1SDavid du Colombier Send *lanai; /* tx ring (cksum+len in lanai memory) */
127901484c1SDavid du Colombier Send *host; /* tx ring (data in our memory) */
128901484c1SDavid du Colombier Block **bring;
129901484c1SDavid du Colombier // uchar *wcfifo; /* what the heck is a w/c fifo? */
130901484c1SDavid du Colombier int size; /* of buffers in the z8's memory */
13191b330d9SDavid du Colombier ulong segsz;
132901484c1SDavid du Colombier uint n; /* rxslots */
133901484c1SDavid du Colombier uint m; /* mask; rxslots must be a power of two */
134901484c1SDavid du Colombier uint i; /* number of segments (not frames) queued */
135901484c1SDavid du Colombier uint cnt; /* number of segments sent by the card */
136901484c1SDavid du Colombier
137901484c1SDavid du Colombier ulong npkt;
138901484c1SDavid du Colombier vlong nbytes;
139901484c1SDavid du Colombier } Tx;
140901484c1SDavid du Colombier
141901484c1SDavid du Colombier typedef struct {
142901484c1SDavid du Colombier Lock;
143901484c1SDavid du Colombier Block *head;
144901484c1SDavid du Colombier uint size; /* buffer size of each block */
145901484c1SDavid du Colombier uint n; /* n free buffers */
146901484c1SDavid du Colombier uint cnt;
147901484c1SDavid du Colombier } Bpool;
148901484c1SDavid du Colombier
14941ac1ab6SDavid du Colombier static Bpool smpool = { .size = 128, };
15041ac1ab6SDavid du Colombier static Bpool bgpool = { .size = Maxmtu, };
151901484c1SDavid du Colombier
152901484c1SDavid du Colombier typedef struct {
153901484c1SDavid du Colombier Bpool *pool; /* free buffers */
15491b330d9SDavid du Colombier ulong *lanai; /* rx ring; we have no permanent host shadow */
155901484c1SDavid du Colombier Block **host; /* called "info" in myricom driver */
156901484c1SDavid du Colombier // uchar *wcfifo; /* cmd submission fifo */
157901484c1SDavid du Colombier uint m;
158901484c1SDavid du Colombier uint n; /* rxslots */
159901484c1SDavid du Colombier uint i;
160901484c1SDavid du Colombier uint cnt; /* number of buffers allocated (lifetime) */
161901484c1SDavid du Colombier uint allocfail;
162901484c1SDavid du Colombier } Rx;
163901484c1SDavid du Colombier
164901484c1SDavid du Colombier /* dma mapped. unix network byte order. */
165901484c1SDavid du Colombier typedef struct {
166901484c1SDavid du Colombier uchar txcnt[4];
167901484c1SDavid du Colombier uchar linkstat[4];
168901484c1SDavid du Colombier uchar dlink[4];
169901484c1SDavid du Colombier uchar derror[4];
170901484c1SDavid du Colombier uchar drunt[4];
171901484c1SDavid du Colombier uchar doverrun[4];
172901484c1SDavid du Colombier uchar dnosm[4];
173901484c1SDavid du Colombier uchar dnobg[4];
174901484c1SDavid du Colombier uchar nrdma[4];
175901484c1SDavid du Colombier uchar txstopped;
176901484c1SDavid du Colombier uchar down;
177901484c1SDavid du Colombier uchar updated;
178901484c1SDavid du Colombier uchar valid;
179901484c1SDavid du Colombier } Stats;
180901484c1SDavid du Colombier
181901484c1SDavid du Colombier enum {
182901484c1SDavid du Colombier Detached,
183901484c1SDavid du Colombier Attached,
184901484c1SDavid du Colombier Runed,
185901484c1SDavid du Colombier };
186901484c1SDavid du Colombier
187901484c1SDavid du Colombier typedef struct {
188901484c1SDavid du Colombier Slot *entry;
18991b330d9SDavid du Colombier uvlong busaddr;
190901484c1SDavid du Colombier uint m;
191901484c1SDavid du Colombier uint n;
192901484c1SDavid du Colombier uint i;
193901484c1SDavid du Colombier } Done;
194901484c1SDavid du Colombier
195901484c1SDavid du Colombier typedef struct Ctlr Ctlr;
196901484c1SDavid du Colombier typedef struct Ctlr {
197901484c1SDavid du Colombier QLock;
198901484c1SDavid du Colombier int state;
199901484c1SDavid du Colombier int kprocs;
20091b330d9SDavid du Colombier uvlong port;
201901484c1SDavid du Colombier Pcidev* pcidev;
202901484c1SDavid du Colombier Ctlr* next;
203901484c1SDavid du Colombier int active;
204901484c1SDavid du Colombier int id; /* do we need this? */
205901484c1SDavid du Colombier
206901484c1SDavid du Colombier uchar ra[Eaddrlen];
207901484c1SDavid du Colombier
208901484c1SDavid du Colombier int ramsz;
209901484c1SDavid du Colombier uchar *ram;
210901484c1SDavid du Colombier
21191b330d9SDavid du Colombier ulong *irqack;
21291b330d9SDavid du Colombier ulong *irqdeass;
21391b330d9SDavid du Colombier ulong *coal;
214901484c1SDavid du Colombier
215901484c1SDavid du Colombier char eprom[Epromsz];
216901484c1SDavid du Colombier ulong serial; /* unit serial number */
217901484c1SDavid du Colombier
218901484c1SDavid du Colombier QLock cmdl;
219901484c1SDavid du Colombier Cmd *cmd; /* address of command return */
22091b330d9SDavid du Colombier uvlong cprt; /* bus address of command */
221901484c1SDavid du Colombier
22291b330d9SDavid du Colombier uvlong boot; /* boot address */
223901484c1SDavid du Colombier
224901484c1SDavid du Colombier Done done;
225901484c1SDavid du Colombier Tx tx;
226901484c1SDavid du Colombier Rx sm;
227901484c1SDavid du Colombier Rx bg;
228901484c1SDavid du Colombier Stats *stats;
22991b330d9SDavid du Colombier uvlong statsprt;
230901484c1SDavid du Colombier
231901484c1SDavid du Colombier Rendez rxrendez;
232901484c1SDavid du Colombier Rendez txrendez;
233901484c1SDavid du Colombier
234901484c1SDavid du Colombier int msi;
23591b330d9SDavid du Colombier ulong linkstat;
23691b330d9SDavid du Colombier ulong nrdma;
237901484c1SDavid du Colombier } Ctlr;
238901484c1SDavid du Colombier
239901484c1SDavid du Colombier static Ctlr *ctlrs;
240901484c1SDavid du Colombier
241901484c1SDavid du Colombier enum {
242901484c1SDavid du Colombier PciCapPMG = 0x01, /* power management */
243901484c1SDavid du Colombier PciCapAGP = 0x02,
244901484c1SDavid du Colombier PciCapVPD = 0x03, /* vital product data */
245901484c1SDavid du Colombier PciCapSID = 0x04, /* slot id */
246901484c1SDavid du Colombier PciCapMSI = 0x05,
247901484c1SDavid du Colombier PciCapCHS = 0x06, /* compact pci hot swap */
248901484c1SDavid du Colombier PciCapPCIX = 0x07,
249901484c1SDavid du Colombier PciCapHTC = 0x08, /* hypertransport irq conf */
250901484c1SDavid du Colombier PciCapVND = 0x09, /* vendor specific information */
251901484c1SDavid du Colombier PciCapHSW = 0x0C, /* hot swap */
252901484c1SDavid du Colombier PciCapPCIe = 0x10,
253901484c1SDavid du Colombier PciCapMSIX = 0x11,
254901484c1SDavid du Colombier };
255901484c1SDavid du Colombier
256901484c1SDavid du Colombier enum {
257901484c1SDavid du Colombier PcieAERC = 1,
258901484c1SDavid du Colombier PcieVC,
259901484c1SDavid du Colombier PcieSNC,
260901484c1SDavid du Colombier PciePBC,
261901484c1SDavid du Colombier };
262901484c1SDavid du Colombier
263901484c1SDavid du Colombier enum {
264901484c1SDavid du Colombier AercCCR = 0x18, /* control register */
265901484c1SDavid du Colombier };
266901484c1SDavid du Colombier
267901484c1SDavid du Colombier enum {
268901484c1SDavid du Colombier PcieCTL = 8,
269901484c1SDavid du Colombier PcieLCR = 12,
270901484c1SDavid du Colombier PcieMRD = 0x7000, /* maximum read size */
271901484c1SDavid du Colombier };
272901484c1SDavid du Colombier
27341ac1ab6SDavid du Colombier static int
pcicap(Pcidev * p,int cap)274901484c1SDavid du Colombier pcicap(Pcidev *p, int cap)
275901484c1SDavid du Colombier {
276901484c1SDavid du Colombier int i, c, off;
277901484c1SDavid du Colombier
278901484c1SDavid du Colombier pcicapdbg("pcicap: %x:%d\n", p->vid, p->did);
279901484c1SDavid du Colombier off = 0x34; /* 0x14 for cardbus */
280901484c1SDavid du Colombier for(i = 48; i--; ){
281901484c1SDavid du Colombier pcicapdbg("\t" "loop %x\n", off);
282901484c1SDavid du Colombier off = pcicfgr8(p, off);
283901484c1SDavid du Colombier pcicapdbg("\t" "pcicfgr8 %x\n", off);
284901484c1SDavid du Colombier if(off < 0x40)
285901484c1SDavid du Colombier break;
286901484c1SDavid du Colombier off &= ~3;
287901484c1SDavid du Colombier c = pcicfgr8(p, off);
288901484c1SDavid du Colombier pcicapdbg("\t" "pcicfgr8 %x\n", c);
289901484c1SDavid du Colombier if(c == 0xff)
290901484c1SDavid du Colombier break;
291901484c1SDavid du Colombier if(c == cap)
292901484c1SDavid du Colombier return off;
293901484c1SDavid du Colombier off++;
294901484c1SDavid du Colombier }
295901484c1SDavid du Colombier return 0;
296901484c1SDavid du Colombier }
297901484c1SDavid du Colombier
298901484c1SDavid du Colombier /*
299901484c1SDavid du Colombier * this function doesn't work because pcicgr32 doesn't have access
300901484c1SDavid du Colombier * to the pcie extended configuration space.
301901484c1SDavid du Colombier */
30241ac1ab6SDavid du Colombier static int
pciecap(Pcidev * p,int cap)303901484c1SDavid du Colombier pciecap(Pcidev *p, int cap)
304901484c1SDavid du Colombier {
305901484c1SDavid du Colombier uint off, i;
306901484c1SDavid du Colombier
307901484c1SDavid du Colombier off = 0x100;
308901484c1SDavid du Colombier while(((i = pcicfgr32(p, off)) & 0xffff) != cap){
309901484c1SDavid du Colombier off = i >> 20;
31080088de7SDavid du Colombier print("m10g: pciecap offset = %ud", off);
31141ac1ab6SDavid du Colombier if(off < 0x100 || off >= 4*KiB - 1)
312901484c1SDavid du Colombier return 0;
313901484c1SDavid du Colombier }
31480088de7SDavid du Colombier print("m10g: pciecap found = %ud", off);
315901484c1SDavid du Colombier return off;
316901484c1SDavid du Colombier }
317901484c1SDavid du Colombier
318901484c1SDavid du Colombier static int
setpcie(Pcidev * p)319901484c1SDavid du Colombier setpcie(Pcidev *p)
320901484c1SDavid du Colombier {
321901484c1SDavid du Colombier int off;
322901484c1SDavid du Colombier
323901484c1SDavid du Colombier /* set 4k writes */
324901484c1SDavid du Colombier off = pcicap(p, PciCapPCIe);
325901484c1SDavid du Colombier if(off < 64)
326901484c1SDavid du Colombier return -1;
327901484c1SDavid du Colombier off += PcieCTL;
328901484c1SDavid du Colombier pcicfgw16(p, off, (pcicfgr16(p, off) & ~PcieMRD) | 5<<12);
329901484c1SDavid du Colombier return 0;
330901484c1SDavid du Colombier }
331901484c1SDavid du Colombier
332901484c1SDavid du Colombier static int
whichfw(Pcidev * p)333901484c1SDavid du Colombier whichfw(Pcidev *p)
334901484c1SDavid du Colombier {
335901484c1SDavid du Colombier char *s;
336901484c1SDavid du Colombier int i, off, lanes, ecrc;
33791b330d9SDavid du Colombier ulong cap;
338901484c1SDavid du Colombier
339901484c1SDavid du Colombier /* check the number of configured lanes. */
340901484c1SDavid du Colombier off = pcicap(p, PciCapPCIe);
341901484c1SDavid du Colombier if(off < 64)
342901484c1SDavid du Colombier return -1;
343901484c1SDavid du Colombier off += PcieLCR;
344901484c1SDavid du Colombier cap = pcicfgr16(p, off);
345901484c1SDavid du Colombier lanes = (cap>>4) & 0x3f;
346901484c1SDavid du Colombier
347901484c1SDavid du Colombier /* check AERC register. we need it on. */
348901484c1SDavid du Colombier off = pciecap(p, PcieAERC);
34980088de7SDavid du Colombier print("; offset %d returned\n", off);
350901484c1SDavid du Colombier cap = 0;
351901484c1SDavid du Colombier if(off != 0){
352901484c1SDavid du Colombier off += AercCCR;
353901484c1SDavid du Colombier cap = pcicfgr32(p, off);
35480088de7SDavid du Colombier print("m10g: %lud cap\n", cap);
355901484c1SDavid du Colombier }
356901484c1SDavid du Colombier ecrc = (cap>>4) & 0xf;
357901484c1SDavid du Colombier /* if we don't like the aerc, kick it here. */
358901484c1SDavid du Colombier
35980088de7SDavid du Colombier print("m10g: %d lanes; ecrc=%d; ", lanes, ecrc);
360901484c1SDavid du Colombier if(s = getconf("myriforce")){
361901484c1SDavid du Colombier i = atoi(s);
36241ac1ab6SDavid du Colombier if(i != 4*KiB || i != 2*KiB)
36341ac1ab6SDavid du Colombier i = 2*KiB;
364901484c1SDavid du Colombier print("fw = %d [forced]\n", i);
365901484c1SDavid du Colombier return i;
366901484c1SDavid du Colombier }
367d5789509SDavid du Colombier if(lanes <= 4)
368901484c1SDavid du Colombier print("fw = 4096 [lanes]\n");
369d5789509SDavid du Colombier else if(ecrc & 10)
370901484c1SDavid du Colombier print("fw = 4096 [ecrc set]\n");
371d5789509SDavid du Colombier else
372901484c1SDavid du Colombier print("fw = 4096 [default]\n");
37341ac1ab6SDavid du Colombier return 4*KiB;
374901484c1SDavid du Colombier }
375901484c1SDavid du Colombier
376901484c1SDavid du Colombier static int
parseeprom(Ctlr * c)377901484c1SDavid du Colombier parseeprom(Ctlr *c)
378901484c1SDavid du Colombier {
379901484c1SDavid du Colombier int i, j, k, l, bits;
380901484c1SDavid du Colombier char *s;
381901484c1SDavid du Colombier
382901484c1SDavid du Colombier dprint("m10g eprom:\n");
383901484c1SDavid du Colombier s = c->eprom;
384901484c1SDavid du Colombier bits = 3;
385901484c1SDavid du Colombier for(i = 0; s[i] && i < Epromsz; i++){
386901484c1SDavid du Colombier l = strlen(s+i);
387901484c1SDavid du Colombier dprint("\t%s\n", s+i);
388901484c1SDavid du Colombier if(strncmp(s+i, "MAC=", 4) == 0 && l == 4+12+5){
389901484c1SDavid du Colombier bits ^= 1;
390901484c1SDavid du Colombier j = i + 4;
391901484c1SDavid du Colombier for(k = 0; k < 6; k++)
392901484c1SDavid du Colombier c->ra[k] = strtoul(s+j+3*k, 0, 16);
393901484c1SDavid du Colombier }else if(strncmp(s+i, "SN=", 3) == 0){
394901484c1SDavid du Colombier bits ^= 2;
395901484c1SDavid du Colombier c->serial = atoi(s+i+3);
396901484c1SDavid du Colombier }
397901484c1SDavid du Colombier i += l;
398901484c1SDavid du Colombier }
399901484c1SDavid du Colombier if(bits)
400901484c1SDavid du Colombier return -1;
401901484c1SDavid du Colombier return 0;
402901484c1SDavid du Colombier }
403901484c1SDavid du Colombier
40491b330d9SDavid du Colombier static ushort
pbit16(ushort i)40591b330d9SDavid du Colombier pbit16(ushort i)
406901484c1SDavid du Colombier {
40791b330d9SDavid du Colombier ushort j;
408901484c1SDavid du Colombier uchar *p;
409901484c1SDavid du Colombier
410901484c1SDavid du Colombier p = (uchar*)&j;
411901484c1SDavid du Colombier p[1] = i;
412901484c1SDavid du Colombier p[0] = i>>8;
413901484c1SDavid du Colombier return j;
414901484c1SDavid du Colombier }
415901484c1SDavid du Colombier
41691b330d9SDavid du Colombier static ushort
gbit16(uchar i[2])417901484c1SDavid du Colombier gbit16(uchar i[2])
418901484c1SDavid du Colombier {
41991b330d9SDavid du Colombier ushort j;
420901484c1SDavid du Colombier
421901484c1SDavid du Colombier j = i[1];
422901484c1SDavid du Colombier j |= i[0]<<8;
423901484c1SDavid du Colombier return j;
424901484c1SDavid du Colombier }
425901484c1SDavid du Colombier
42691b330d9SDavid du Colombier static ulong
pbit32(ulong i)42791b330d9SDavid du Colombier pbit32(ulong i)
428901484c1SDavid du Colombier {
42991b330d9SDavid du Colombier ulong j;
430901484c1SDavid du Colombier uchar *p;
431901484c1SDavid du Colombier
432901484c1SDavid du Colombier p = (uchar*)&j;
433901484c1SDavid du Colombier p[3] = i;
434901484c1SDavid du Colombier p[2] = i>>8;
435901484c1SDavid du Colombier p[1] = i>>16;
436901484c1SDavid du Colombier p[0] = i>>24;
437901484c1SDavid du Colombier return j;
438901484c1SDavid du Colombier }
439901484c1SDavid du Colombier
44091b330d9SDavid du Colombier static ulong
gbit32(uchar i[4])441901484c1SDavid du Colombier gbit32(uchar i[4])
442901484c1SDavid du Colombier {
44391b330d9SDavid du Colombier ulong j;
444901484c1SDavid du Colombier
445901484c1SDavid du Colombier j = i[3];
446901484c1SDavid du Colombier j |= i[2]<<8;
447901484c1SDavid du Colombier j |= i[1]<<16;
448901484c1SDavid du Colombier j |= i[0]<<24;
449901484c1SDavid du Colombier return j;
450901484c1SDavid du Colombier }
451901484c1SDavid du Colombier
452901484c1SDavid du Colombier static void
prepcmd(ulong * cmd,int i)45391b330d9SDavid du Colombier prepcmd(ulong *cmd, int i)
454901484c1SDavid du Colombier {
455901484c1SDavid du Colombier while(i-- > 0)
456901484c1SDavid du Colombier cmd[i] = pbit32(cmd[i]);
457901484c1SDavid du Colombier }
458901484c1SDavid du Colombier
459901484c1SDavid du Colombier /*
460901484c1SDavid du Colombier * the command looks like this (int 32bit integers)
461901484c1SDavid du Colombier * cmd type
462901484c1SDavid du Colombier * addr (low)
463901484c1SDavid du Colombier * addr (high)
464901484c1SDavid du Colombier * pad (used for dma testing)
465901484c1SDavid du Colombier * response (high)
466901484c1SDavid du Colombier * response (low)
467901484c1SDavid du Colombier * 40 byte = 5 int pad.
468901484c1SDavid du Colombier */
469901484c1SDavid du Colombier
47091b330d9SDavid du Colombier ulong
cmd(Ctlr * c,int type,uvlong data)47191b330d9SDavid du Colombier cmd(Ctlr *c, int type, uvlong data)
472901484c1SDavid du Colombier {
47391b330d9SDavid du Colombier ulong buf[16], i;
474901484c1SDavid du Colombier Cmd *cmd;
475901484c1SDavid du Colombier
476901484c1SDavid du Colombier qlock(&c->cmdl);
477901484c1SDavid du Colombier cmd = c->cmd;
478901484c1SDavid du Colombier cmd->i[1] = Noconf;
479901484c1SDavid du Colombier memset(buf, 0, sizeof buf);
480901484c1SDavid du Colombier buf[0] = type;
481901484c1SDavid du Colombier buf[1] = data;
48241ac1ab6SDavid du Colombier buf[2] = data >> 32;
48341ac1ab6SDavid du Colombier buf[4] = c->cprt >> 32;
484901484c1SDavid du Colombier buf[5] = c->cprt;
485901484c1SDavid du Colombier prepcmd(buf, 6);
486901484c1SDavid du Colombier coherence();
487901484c1SDavid du Colombier memmove(c->ram + Cmdoff, buf, sizeof buf);
488901484c1SDavid du Colombier
4896083aa43SDavid du Colombier if(waserror()){
4906083aa43SDavid du Colombier qunlock(&c->cmdl);
491901484c1SDavid du Colombier nexterror();
4926083aa43SDavid du Colombier }
493901484c1SDavid du Colombier for(i = 0; i < 15; i++){
494901484c1SDavid du Colombier if(cmd->i[1] != Noconf){
495901484c1SDavid du Colombier poperror();
496901484c1SDavid du Colombier i = gbit32(cmd->c);
497901484c1SDavid du Colombier qunlock(&c->cmdl);
498901484c1SDavid du Colombier if(cmd->i[1] != 0)
49991b330d9SDavid du Colombier dprint("[%lux]", i);
5006083aa43SDavid du Colombier return i; /* normal return */
501901484c1SDavid du Colombier }
502901484c1SDavid du Colombier tsleep(&up->sleep, return0, 0, 1);
503901484c1SDavid du Colombier }
504901484c1SDavid du Colombier iprint("m10g: cmd timeout [%ux %ux] cmd=%d\n",
505901484c1SDavid du Colombier cmd->i[0], cmd->i[1], type);
506901484c1SDavid du Colombier error(Etimeout);
507901484c1SDavid du Colombier return ~0; /* silence! */
508901484c1SDavid du Colombier }
509901484c1SDavid du Colombier
51091b330d9SDavid du Colombier ulong
maccmd(Ctlr * c,int type,uchar * m)511901484c1SDavid du Colombier maccmd(Ctlr *c, int type, uchar *m)
512901484c1SDavid du Colombier {
51391b330d9SDavid du Colombier ulong buf[16], i;
514901484c1SDavid du Colombier Cmd *cmd;
515901484c1SDavid du Colombier
516901484c1SDavid du Colombier qlock(&c->cmdl);
517901484c1SDavid du Colombier cmd = c->cmd;
518901484c1SDavid du Colombier cmd->i[1] = Noconf;
519901484c1SDavid du Colombier memset(buf, 0, sizeof buf);
520901484c1SDavid du Colombier buf[0] = type;
521901484c1SDavid du Colombier buf[1] = m[0]<<24 | m[1]<<16 | m[2]<<8 | m[3];
522901484c1SDavid du Colombier buf[2] = m[4]<< 8 | m[5];
52341ac1ab6SDavid du Colombier buf[4] = c->cprt >> 32;
524901484c1SDavid du Colombier buf[5] = c->cprt;
525901484c1SDavid du Colombier prepcmd(buf, 6);
526901484c1SDavid du Colombier coherence();
527901484c1SDavid du Colombier memmove(c->ram + Cmdoff, buf, sizeof buf);
528901484c1SDavid du Colombier
5296083aa43SDavid du Colombier if(waserror()){
5306083aa43SDavid du Colombier qunlock(&c->cmdl);
531901484c1SDavid du Colombier nexterror();
5326083aa43SDavid du Colombier }
533901484c1SDavid du Colombier for(i = 0; i < 15; i++){
534901484c1SDavid du Colombier if(cmd->i[1] != Noconf){
535901484c1SDavid du Colombier poperror();
536901484c1SDavid du Colombier i = gbit32(cmd->c);
537901484c1SDavid du Colombier qunlock(&c->cmdl);
538901484c1SDavid du Colombier if(cmd->i[1] != 0)
53991b330d9SDavid du Colombier dprint("[%lux]", i);
5406083aa43SDavid du Colombier return i; /* normal return */
541901484c1SDavid du Colombier }
542901484c1SDavid du Colombier tsleep(&up->sleep, return0, 0, 1);
543901484c1SDavid du Colombier }
544901484c1SDavid du Colombier iprint("m10g: maccmd timeout [%ux %ux] cmd=%d\n",
545901484c1SDavid du Colombier cmd->i[0], cmd->i[1], type);
546901484c1SDavid du Colombier error(Etimeout);
547901484c1SDavid du Colombier return ~0; /* silence! */
548901484c1SDavid du Colombier }
549901484c1SDavid du Colombier
550901484c1SDavid du Colombier /* remove this garbage after testing */
551901484c1SDavid du Colombier enum {
552901484c1SDavid du Colombier DMAread = 0x10000,
553901484c1SDavid du Colombier DMAwrite= 0x1,
554901484c1SDavid du Colombier };
555901484c1SDavid du Colombier
55691b330d9SDavid du Colombier ulong
dmatestcmd(Ctlr * c,int type,uvlong addr,int len)55791b330d9SDavid du Colombier dmatestcmd(Ctlr *c, int type, uvlong addr, int len)
558901484c1SDavid du Colombier {
55991b330d9SDavid du Colombier ulong buf[16], i;
560901484c1SDavid du Colombier
561901484c1SDavid du Colombier memset(buf, 0, sizeof buf);
562901484c1SDavid du Colombier memset(c->cmd, Noconf, sizeof *c->cmd);
563901484c1SDavid du Colombier buf[0] = Cdmatest;
564901484c1SDavid du Colombier buf[1] = addr;
56541ac1ab6SDavid du Colombier buf[2] = addr >> 32;
566901484c1SDavid du Colombier buf[3] = len * type;
56741ac1ab6SDavid du Colombier buf[4] = c->cprt >> 32;
568901484c1SDavid du Colombier buf[5] = c->cprt;
569901484c1SDavid du Colombier prepcmd(buf, 6);
570901484c1SDavid du Colombier coherence();
571901484c1SDavid du Colombier memmove(c->ram + Cmdoff, buf, sizeof buf);
572901484c1SDavid du Colombier
573901484c1SDavid du Colombier for(i = 0; i < 15; i++){
574901484c1SDavid du Colombier if(c->cmd->i[1] != Noconf){
575901484c1SDavid du Colombier i = gbit32(c->cmd->c);
576901484c1SDavid du Colombier if(i == 0)
577901484c1SDavid du Colombier error(Eio);
5786083aa43SDavid du Colombier return i; /* normal return */
579901484c1SDavid du Colombier }
580901484c1SDavid du Colombier tsleep(&up->sleep, return0, 0, 5);
581901484c1SDavid du Colombier }
582901484c1SDavid du Colombier error(Etimeout);
583901484c1SDavid du Colombier return ~0; /* silence! */
584901484c1SDavid du Colombier }
585901484c1SDavid du Colombier
58691b330d9SDavid du Colombier ulong
rdmacmd(Ctlr * c,int on)587901484c1SDavid du Colombier rdmacmd(Ctlr *c, int on)
588901484c1SDavid du Colombier {
58991b330d9SDavid du Colombier ulong buf[16], i;
590901484c1SDavid du Colombier
591901484c1SDavid du Colombier memset(buf, 0, sizeof buf);
592901484c1SDavid du Colombier c->cmd->i[0] = 0;
593901484c1SDavid du Colombier coherence();
59441ac1ab6SDavid du Colombier buf[0] = c->cprt >> 32;
595901484c1SDavid du Colombier buf[1] = c->cprt;
596901484c1SDavid du Colombier buf[2] = Noconf;
59741ac1ab6SDavid du Colombier buf[3] = c->cprt >> 32;
598901484c1SDavid du Colombier buf[4] = c->cprt;
599901484c1SDavid du Colombier buf[5] = on;
600901484c1SDavid du Colombier prepcmd(buf, 6);
601901484c1SDavid du Colombier memmove(c->ram + Rdmaoff, buf, sizeof buf);
602901484c1SDavid du Colombier
603901484c1SDavid du Colombier for(i = 0; i < 20; i++){
6046083aa43SDavid du Colombier if(c->cmd->i[0] == Noconf)
6056083aa43SDavid du Colombier return gbit32(c->cmd->c); /* normal return */
606901484c1SDavid du Colombier tsleep(&up->sleep, return0, 0, 1);
607901484c1SDavid du Colombier }
608901484c1SDavid du Colombier iprint("m10g: rdmacmd timeout\n");
6096083aa43SDavid du Colombier error(Etimeout);
610901484c1SDavid du Colombier return ~0; /* silence! */
611901484c1SDavid du Colombier }
612901484c1SDavid du Colombier
613901484c1SDavid du Colombier static int
loadfw(Ctlr * c,int * align)614901484c1SDavid du Colombier loadfw(Ctlr *c, int *align)
615901484c1SDavid du Colombier {
61691b330d9SDavid du Colombier ulong *f, *s, sz;
617901484c1SDavid du Colombier int i;
618901484c1SDavid du Colombier
61941ac1ab6SDavid du Colombier if((*align = whichfw(c->pcidev)) == 4*KiB){
62091b330d9SDavid du Colombier f = (ulong*)fw4k;
621901484c1SDavid du Colombier sz = sizeof fw4k;
622901484c1SDavid du Colombier }else{
62391b330d9SDavid du Colombier f = (ulong*)fw2k;
624901484c1SDavid du Colombier sz = sizeof fw2k;
625901484c1SDavid du Colombier }
626901484c1SDavid du Colombier
62791b330d9SDavid du Colombier s = (ulong*)(c->ram + Fwoffset);
628901484c1SDavid du Colombier for(i = 0; i < sz / 4; i++)
629901484c1SDavid du Colombier s[i] = f[i];
630901484c1SDavid du Colombier return sz & ~3;
631901484c1SDavid du Colombier }
632901484c1SDavid du Colombier
633901484c1SDavid du Colombier static int
bootfw(Ctlr * c)634901484c1SDavid du Colombier bootfw(Ctlr *c)
635901484c1SDavid du Colombier {
636901484c1SDavid du Colombier int i, sz, align;
63791b330d9SDavid du Colombier ulong buf[16];
638901484c1SDavid du Colombier Cmd* cmd;
639901484c1SDavid du Colombier
640901484c1SDavid du Colombier if((sz = loadfw(c, &align)) == 0)
641901484c1SDavid du Colombier return 0;
642901484c1SDavid du Colombier dprint("bootfw %d bytes ... ", sz);
643901484c1SDavid du Colombier cmd = c->cmd;
644901484c1SDavid du Colombier
645901484c1SDavid du Colombier memset(buf, 0, sizeof buf);
646901484c1SDavid du Colombier c->cmd->i[0] = 0;
647901484c1SDavid du Colombier coherence();
64841ac1ab6SDavid du Colombier buf[0] = c->cprt >> 32; /* upper dma target address */
649901484c1SDavid du Colombier buf[1] = c->cprt; /* lower */
650901484c1SDavid du Colombier buf[2] = Noconf; /* writeback */
651901484c1SDavid du Colombier buf[3] = Fwoffset + 8,
652901484c1SDavid du Colombier buf[4] = sz - 8;
653901484c1SDavid du Colombier buf[5] = 8;
654901484c1SDavid du Colombier buf[6] = 0;
655901484c1SDavid du Colombier prepcmd(buf, 7);
656901484c1SDavid du Colombier coherence();
657901484c1SDavid du Colombier memmove(c->ram + Fwsubmt, buf, sizeof buf);
658901484c1SDavid du Colombier
659901484c1SDavid du Colombier for(i = 0; i < 20; i++){
660901484c1SDavid du Colombier if(cmd->i[0] == Noconf)
661901484c1SDavid du Colombier break;
662901484c1SDavid du Colombier delay(1);
663901484c1SDavid du Colombier }
66491b330d9SDavid du Colombier dprint("[%lux %lux]", gbit32(cmd->c), gbit32(cmd->c+4));
665901484c1SDavid du Colombier if(i == 20){
666901484c1SDavid du Colombier print("m10g: cannot load fw\n");
667901484c1SDavid du Colombier return -1;
668901484c1SDavid du Colombier }
669901484c1SDavid du Colombier dprint("\n");
670901484c1SDavid du Colombier c->tx.segsz = align;
671901484c1SDavid du Colombier return 0;
672901484c1SDavid du Colombier }
673901484c1SDavid du Colombier
67441ac1ab6SDavid du Colombier static int
kickthebaby(Pcidev * p,Ctlr * c)675901484c1SDavid du Colombier kickthebaby(Pcidev *p, Ctlr *c)
676901484c1SDavid du Colombier {
677901484c1SDavid du Colombier /* don't kick the baby! */
67891b330d9SDavid du Colombier ulong code;
679901484c1SDavid du Colombier
680901484c1SDavid du Colombier pcicfgw8(p, 0x10 + c->boot, 0x3);
681901484c1SDavid du Colombier pcicfgw32(p, 0x18 + c->boot, 0xfffffff0);
682901484c1SDavid du Colombier code = pcicfgr32(p, 0x14 + c->boot);
683901484c1SDavid du Colombier
68491b330d9SDavid du Colombier dprint("reboot status = %lux\n", code);
685901484c1SDavid du Colombier if(code != 0xfffffff0)
686901484c1SDavid du Colombier return -1;
687901484c1SDavid du Colombier return 0;
688901484c1SDavid du Colombier }
689901484c1SDavid du Colombier
690901484c1SDavid du Colombier typedef struct {
691901484c1SDavid du Colombier uchar len[4];
692901484c1SDavid du Colombier uchar type[4];
693901484c1SDavid du Colombier char version[128];
694901484c1SDavid du Colombier uchar globals[4];
695901484c1SDavid du Colombier uchar ramsz[4];
696901484c1SDavid du Colombier uchar specs[4];
697901484c1SDavid du Colombier uchar specssz[4];
698901484c1SDavid du Colombier } Fwhdr;
699901484c1SDavid du Colombier
700901484c1SDavid du Colombier enum {
701901484c1SDavid du Colombier Tmx = 0x4d582020,
702901484c1SDavid du Colombier Tpcie = 0x70636965,
703901484c1SDavid du Colombier Teth = 0x45544820,
704901484c1SDavid du Colombier Tmcp0 = 0x4d435030,
705901484c1SDavid du Colombier };
706901484c1SDavid du Colombier
70741ac1ab6SDavid du Colombier static char *
fwtype(ulong type)70891b330d9SDavid du Colombier fwtype(ulong type)
709901484c1SDavid du Colombier {
710901484c1SDavid du Colombier switch(type){
711901484c1SDavid du Colombier case Tmx:
712901484c1SDavid du Colombier return "mx";
713901484c1SDavid du Colombier case Tpcie:
714901484c1SDavid du Colombier return "PCIe";
715901484c1SDavid du Colombier case Teth:
716901484c1SDavid du Colombier return "eth";
717901484c1SDavid du Colombier case Tmcp0:
718901484c1SDavid du Colombier return "mcp0";
719901484c1SDavid du Colombier }
720901484c1SDavid du Colombier return "*GOK*";
721901484c1SDavid du Colombier }
722901484c1SDavid du Colombier
72341ac1ab6SDavid du Colombier static int
chkfw(Ctlr * c)724901484c1SDavid du Colombier chkfw(Ctlr *c)
725901484c1SDavid du Colombier {
72664a26757SDavid du Colombier ulong off, type;
727901484c1SDavid du Colombier Fwhdr *h;
728901484c1SDavid du Colombier
729901484c1SDavid du Colombier off = gbit32(c->ram+0x3c);
73064a26757SDavid du Colombier dprint("firmware %lux\n", off);
731901484c1SDavid du Colombier if((off&3) || off + sizeof *h > c->ramsz){
73264a26757SDavid du Colombier print("!m10g: bad firmware %lux\n", off);
733901484c1SDavid du Colombier return -1;
734901484c1SDavid du Colombier }
735901484c1SDavid du Colombier h = (Fwhdr*)(c->ram + off);
736901484c1SDavid du Colombier type = gbit32(h->type);
737901484c1SDavid du Colombier dprint("\t" "type %s\n", fwtype(type));
738901484c1SDavid du Colombier dprint("\t" "vers %s\n", h->version);
73991b330d9SDavid du Colombier dprint("\t" "ramsz %lux\n", gbit32(h->ramsz));
740901484c1SDavid du Colombier if(type != Teth){
741901484c1SDavid du Colombier print("!m10g: bad card type %s\n", fwtype(type));
742901484c1SDavid du Colombier return -1;
743901484c1SDavid du Colombier }
744901484c1SDavid du Colombier
745901484c1SDavid du Colombier return bootfw(c) || rdmacmd(c, 0);
746901484c1SDavid du Colombier }
747901484c1SDavid du Colombier
748901484c1SDavid du Colombier static int
reset(Ether * e,Ctlr * c)749901484c1SDavid du Colombier reset(Ether *e, Ctlr *c)
750901484c1SDavid du Colombier {
75191b330d9SDavid du Colombier ulong i, sz;
752901484c1SDavid du Colombier
753901484c1SDavid du Colombier if(waserror()){
754901484c1SDavid du Colombier print("m10g: reset error\n");
755901484c1SDavid du Colombier nexterror();
756901484c1SDavid du Colombier return -1;
757901484c1SDavid du Colombier }
758901484c1SDavid du Colombier
759901484c1SDavid du Colombier chkfw(c);
760901484c1SDavid du Colombier cmd(c, Creset, 0);
761901484c1SDavid du Colombier
762901484c1SDavid du Colombier cmd(c, CSintrqsz, c->done.n * sizeof *c->done.entry);
763901484c1SDavid du Colombier cmd(c, CSintrqdma, c->done.busaddr);
76491b330d9SDavid du Colombier c->irqack = (ulong*)(c->ram + cmd(c, CGirqackoff, 0));
765901484c1SDavid du Colombier /* required only if we're not doing msi? */
76691b330d9SDavid du Colombier c->irqdeass = (ulong*)(c->ram + cmd(c, CGirqdeassoff, 0));
767901484c1SDavid du Colombier /* this is the driver default, why fiddle with this? */
76891b330d9SDavid du Colombier c->coal = (ulong*)(c->ram + cmd(c, CGcoaloff, 0));
769901484c1SDavid du Colombier *c->coal = pbit32(25);
770901484c1SDavid du Colombier
771901484c1SDavid du Colombier dprint("dma stats:\n");
772901484c1SDavid du Colombier rdmacmd(c, 1);
773901484c1SDavid du Colombier sz = c->tx.segsz;
774901484c1SDavid du Colombier i = dmatestcmd(c, DMAread, c->done.busaddr, sz);
77580088de7SDavid du Colombier print("m10g: read %lud MB/s;", ((i>>16)*sz*2) / (i&0xffff));
776901484c1SDavid du Colombier i = dmatestcmd(c, DMAwrite, c->done.busaddr, sz);
77780088de7SDavid du Colombier print(" write %lud MB/s;", ((i>>16)*sz*2) / (i&0xffff));
778901484c1SDavid du Colombier i = dmatestcmd(c, DMAwrite|DMAread, c->done.busaddr, sz);
77980088de7SDavid du Colombier print(" r/w %lud MB/s\n", ((i>>16)*sz*2*2) / (i&0xffff));
780901484c1SDavid du Colombier memset(c->done.entry, 0, c->done.n * sizeof *c->done.entry);
781901484c1SDavid du Colombier
782901484c1SDavid du Colombier maccmd(c, CSmac, c->ra);
783901484c1SDavid du Colombier // cmd(c, Cnopromisc, 0);
784901484c1SDavid du Colombier cmd(c, Cenablefc, 0);
785901484c1SDavid du Colombier e->maxmtu = Maxmtu;
786901484c1SDavid du Colombier cmd(c, CSmtu, e->maxmtu);
787901484c1SDavid du Colombier dprint("CSmtu %d...\n", e->maxmtu);
788901484c1SDavid du Colombier
789901484c1SDavid du Colombier poperror();
790901484c1SDavid du Colombier return 0;
791901484c1SDavid du Colombier }
792901484c1SDavid du Colombier
793901484c1SDavid du Colombier static void
ctlrfree(Ctlr * c)794901484c1SDavid du Colombier ctlrfree(Ctlr *c)
795901484c1SDavid du Colombier {
796901484c1SDavid du Colombier /* free up all the Block*s, too */
797901484c1SDavid du Colombier free(c->tx.host);
798901484c1SDavid du Colombier free(c->sm.host);
799901484c1SDavid du Colombier free(c->bg.host);
800901484c1SDavid du Colombier free(c->cmd);
801901484c1SDavid du Colombier free(c->done.entry);
802901484c1SDavid du Colombier free(c->stats);
803901484c1SDavid du Colombier free(c);
804901484c1SDavid du Colombier }
805901484c1SDavid du Colombier
806901484c1SDavid du Colombier static int
setmem(Pcidev * p,Ctlr * c)807901484c1SDavid du Colombier setmem(Pcidev *p, Ctlr *c)
808901484c1SDavid du Colombier {
80991b330d9SDavid du Colombier ulong i;
81091b330d9SDavid du Colombier uvlong raddr;
811901484c1SDavid du Colombier Done *d;
812901484c1SDavid du Colombier void *mem;
813901484c1SDavid du Colombier
814901484c1SDavid du Colombier c->tx.segsz = 2048;
81541ac1ab6SDavid du Colombier c->ramsz = 2*MiB - (2*48*KiB + 32*KiB) - 0x100;
816901484c1SDavid du Colombier if(c->ramsz > p->mem[0].size)
817901484c1SDavid du Colombier return -1;
818901484c1SDavid du Colombier
819901484c1SDavid du Colombier raddr = p->mem[0].bar & ~0x0F;
820901484c1SDavid du Colombier mem = vmap(raddr, p->mem[0].size);
821901484c1SDavid du Colombier if(mem == nil){
822901484c1SDavid du Colombier print("m10g: can't map %8.8lux\n", p->mem[0].bar);
823901484c1SDavid du Colombier return -1;
824901484c1SDavid du Colombier }
82541ac1ab6SDavid du Colombier dprint("%llux <- vmap(mem[0].size = %ux)\n", raddr, p->mem[0].size);
826901484c1SDavid du Colombier c->port = raddr;
827901484c1SDavid du Colombier c->ram = mem;
828901484c1SDavid du Colombier c->cmd = malign(sizeof *c->cmd);
829901484c1SDavid du Colombier c->cprt = PCIWADDR(c->cmd);
830901484c1SDavid du Colombier
831901484c1SDavid du Colombier d = &c->done;
832901484c1SDavid du Colombier d->n = Maxslots;
833901484c1SDavid du Colombier d->m = d->n - 1;
834901484c1SDavid du Colombier i = d->n * sizeof *d->entry;
835901484c1SDavid du Colombier d->entry = malign(i);
836901484c1SDavid du Colombier memset(d->entry, 0, i);
837901484c1SDavid du Colombier d->busaddr = PCIWADDR(d->entry);
838901484c1SDavid du Colombier
839901484c1SDavid du Colombier c->stats = malign(sizeof *c->stats);
840901484c1SDavid du Colombier memset(c->stats, 0, sizeof *c->stats);
841901484c1SDavid du Colombier c->statsprt = PCIWADDR(c->stats);
842901484c1SDavid du Colombier
843901484c1SDavid du Colombier memmove(c->eprom, c->ram + c->ramsz - Epromsz, Epromsz-2);
844901484c1SDavid du Colombier return setpcie(p) || parseeprom(c);
845901484c1SDavid du Colombier }
846901484c1SDavid du Colombier
847901484c1SDavid du Colombier static Rx*
whichrx(Ctlr * c,int sz)848901484c1SDavid du Colombier whichrx(Ctlr *c, int sz)
849901484c1SDavid du Colombier {
850901484c1SDavid du Colombier if(sz <= smpool.size)
851901484c1SDavid du Colombier return &c->sm;
852901484c1SDavid du Colombier return &c->bg;
853901484c1SDavid du Colombier }
854901484c1SDavid du Colombier
855901484c1SDavid du Colombier static Block*
balloc(Rx * rx)856901484c1SDavid du Colombier balloc(Rx* rx)
857901484c1SDavid du Colombier {
8587fd2696aSDavid du Colombier Block *bp;
859901484c1SDavid du Colombier
860901484c1SDavid du Colombier ilock(rx->pool);
8617fd2696aSDavid du Colombier if((bp = rx->pool->head) != nil){
8627fd2696aSDavid du Colombier rx->pool->head = bp->next;
8637fd2696aSDavid du Colombier bp->next = nil;
8647fd2696aSDavid du Colombier _xinc(&bp->ref); /* prevent bp from being freed */
865901484c1SDavid du Colombier rx->pool->n--;
866901484c1SDavid du Colombier }
867901484c1SDavid du Colombier iunlock(rx->pool);
8687fd2696aSDavid du Colombier return bp;
869901484c1SDavid du Colombier }
870901484c1SDavid du Colombier
871901484c1SDavid du Colombier static void
rbfree(Block * b,Bpool * p)872bfb6eab9SDavid du Colombier rbfree(Block *b, Bpool *p)
873901484c1SDavid du Colombier {
874901484c1SDavid du Colombier b->rp = b->wp = (uchar*)PGROUND((uintptr)b->base);
875bfb6eab9SDavid du Colombier b->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
876bfb6eab9SDavid du Colombier
877901484c1SDavid du Colombier ilock(p);
878901484c1SDavid du Colombier b->next = p->head;
879901484c1SDavid du Colombier p->head = b;
880901484c1SDavid du Colombier p->n++;
881901484c1SDavid du Colombier p->cnt++;
882901484c1SDavid du Colombier iunlock(p);
883901484c1SDavid du Colombier }
884901484c1SDavid du Colombier
885901484c1SDavid du Colombier static void
smbfree(Block * b)886bfb6eab9SDavid du Colombier smbfree(Block *b)
887bfb6eab9SDavid du Colombier {
888bfb6eab9SDavid du Colombier rbfree(b, &smpool);
889bfb6eab9SDavid du Colombier }
890bfb6eab9SDavid du Colombier
891bfb6eab9SDavid du Colombier static void
bgbfree(Block * b)892901484c1SDavid du Colombier bgbfree(Block *b)
893901484c1SDavid du Colombier {
894bfb6eab9SDavid du Colombier rbfree(b, &bgpool);
895901484c1SDavid du Colombier }
896901484c1SDavid du Colombier
897901484c1SDavid du Colombier static void
replenish(Rx * rx)898901484c1SDavid du Colombier replenish(Rx *rx)
899901484c1SDavid du Colombier {
90091b330d9SDavid du Colombier ulong buf[16], i, idx, e;
901901484c1SDavid du Colombier Bpool *p;
902901484c1SDavid du Colombier Block *b;
903901484c1SDavid du Colombier
904901484c1SDavid du Colombier p = rx->pool;
905901484c1SDavid du Colombier if(p->n < 8)
906901484c1SDavid du Colombier return;
907901484c1SDavid du Colombier memset(buf, 0, sizeof buf);
908901484c1SDavid du Colombier e = (rx->i - rx->cnt) & ~7;
909901484c1SDavid du Colombier e += rx->n;
910901484c1SDavid du Colombier while(p->n >= 8 && e){
911901484c1SDavid du Colombier idx = rx->cnt & rx->m;
912901484c1SDavid du Colombier for(i = 0; i < 8; i++){
913901484c1SDavid du Colombier b = balloc(rx);
91491b330d9SDavid du Colombier buf[i*2] = pbit32((uvlong)PCIWADDR(b->wp) >> 32);
915901484c1SDavid du Colombier buf[i*2+1] = pbit32(PCIWADDR(b->wp));
916901484c1SDavid du Colombier rx->host[idx+i] = b;
917901484c1SDavid du Colombier assert(b);
918901484c1SDavid du Colombier }
919901484c1SDavid du Colombier memmove(rx->lanai + 2*idx, buf, sizeof buf);
920901484c1SDavid du Colombier coherence();
921901484c1SDavid du Colombier rx->cnt += 8;
922901484c1SDavid du Colombier e -= 8;
923901484c1SDavid du Colombier }
924901484c1SDavid du Colombier if(e && p->n > 7+1)
92580088de7SDavid du Colombier print("m10g: should panic? pool->n = %d\n", p->n);
926901484c1SDavid du Colombier }
927901484c1SDavid du Colombier
928901484c1SDavid du Colombier /*
929901484c1SDavid du Colombier * future:
930901484c1SDavid du Colombier * if (c->mtrr >= 0) {
931901484c1SDavid du Colombier * c->tx.wcfifo = c->ram+0x200000;
932901484c1SDavid du Colombier * c->sm.wcfifo = c->ram+0x300000;
933901484c1SDavid du Colombier * c->bg.wcfifo = c->ram+0x340000;
934901484c1SDavid du Colombier * }
935901484c1SDavid du Colombier */
936901484c1SDavid du Colombier
937901484c1SDavid du Colombier static int
nextpow(int j)938901484c1SDavid du Colombier nextpow(int j)
939901484c1SDavid du Colombier {
940901484c1SDavid du Colombier int i;
941901484c1SDavid du Colombier
942901484c1SDavid du Colombier for(i = 0; j > (1 << i); i++)
943901484c1SDavid du Colombier ;
944901484c1SDavid du Colombier return 1 << i;
945901484c1SDavid du Colombier }
946901484c1SDavid du Colombier
947901484c1SDavid du Colombier static void*
emalign(int sz)948901484c1SDavid du Colombier emalign(int sz)
949901484c1SDavid du Colombier {
950901484c1SDavid du Colombier void *v;
951901484c1SDavid du Colombier
952901484c1SDavid du Colombier v = malign(sz);
953901484c1SDavid du Colombier if(v == nil)
954901484c1SDavid du Colombier error(Enomem);
955901484c1SDavid du Colombier memset(v, 0, sz);
956901484c1SDavid du Colombier return v;
957901484c1SDavid du Colombier }
958901484c1SDavid du Colombier
959901484c1SDavid du Colombier static void
open0(Ether * e,Ctlr * c)960901484c1SDavid du Colombier open0(Ether *e, Ctlr *c)
961901484c1SDavid du Colombier {
962901484c1SDavid du Colombier Block *b;
963901484c1SDavid du Colombier int i, sz, entries;
964901484c1SDavid du Colombier
965901484c1SDavid du Colombier entries = cmd(c, CGsendrgsz, 0) / sizeof *c->tx.lanai;
966901484c1SDavid du Colombier c->tx.lanai = (Send*)(c->ram + cmd(c, CGsendoff, 0));
967901484c1SDavid du Colombier c->tx.host = emalign(entries * sizeof *c->tx.host);
968901484c1SDavid du Colombier c->tx.bring = emalign(entries * sizeof *c->tx.bring);
969901484c1SDavid du Colombier c->tx.n = entries;
970901484c1SDavid du Colombier c->tx.m = entries-1;
971901484c1SDavid du Colombier
972901484c1SDavid du Colombier entries = cmd(c, CGrxrgsz, 0)/8;
973901484c1SDavid du Colombier c->sm.pool = &smpool;
974901484c1SDavid du Colombier cmd(c, CSsmallsz, c->sm.pool->size);
97591b330d9SDavid du Colombier c->sm.lanai = (ulong*)(c->ram + cmd(c, CGsmallrxoff, 0));
976901484c1SDavid du Colombier c->sm.n = entries;
977901484c1SDavid du Colombier c->sm.m = entries-1;
978901484c1SDavid du Colombier c->sm.host = emalign(entries * sizeof *c->sm.host);
979901484c1SDavid du Colombier
980901484c1SDavid du Colombier c->bg.pool = &bgpool;
981901484c1SDavid du Colombier c->bg.pool->size = nextpow(2 + e->maxmtu); /* 2-byte alignment pad */
982901484c1SDavid du Colombier cmd(c, CSbigsz, c->bg.pool->size);
98391b330d9SDavid du Colombier c->bg.lanai = (ulong*)(c->ram + cmd(c, CGbigrxoff, 0));
984901484c1SDavid du Colombier c->bg.n = entries;
985901484c1SDavid du Colombier c->bg.m = entries-1;
986901484c1SDavid du Colombier c->bg.host = emalign(entries * sizeof *c->bg.host);
987901484c1SDavid du Colombier
988901484c1SDavid du Colombier sz = c->sm.pool->size + BY2PG;
989901484c1SDavid du Colombier for(i = 0; i < c->sm.n; i++){
990901484c1SDavid du Colombier if((b = allocb(sz)) == 0)
991901484c1SDavid du Colombier break;
992901484c1SDavid du Colombier b->free = smbfree;
993901484c1SDavid du Colombier freeb(b);
994901484c1SDavid du Colombier }
995901484c1SDavid du Colombier sz = c->bg.pool->size + BY2PG;
996901484c1SDavid du Colombier for(i = 0; i < c->bg.n; i++){
997901484c1SDavid du Colombier if((b = allocb(sz)) == 0)
998901484c1SDavid du Colombier break;
999901484c1SDavid du Colombier b->free = bgbfree;
1000901484c1SDavid du Colombier freeb(b);
1001901484c1SDavid du Colombier }
1002901484c1SDavid du Colombier
1003901484c1SDavid du Colombier cmd(c, CSstatsdma, c->statsprt);
1004901484c1SDavid du Colombier c->linkstat = ~0;
1005901484c1SDavid du Colombier c->nrdma = 15;
1006901484c1SDavid du Colombier
1007901484c1SDavid du Colombier cmd(c, Cetherup, 0);
1008901484c1SDavid du Colombier }
1009901484c1SDavid du Colombier
1010901484c1SDavid du Colombier static Block*
nextblock(Ctlr * c)1011901484c1SDavid du Colombier nextblock(Ctlr *c)
1012901484c1SDavid du Colombier {
1013901484c1SDavid du Colombier uint i;
101491b330d9SDavid du Colombier ushort l, k;
1015901484c1SDavid du Colombier Block *b;
1016901484c1SDavid du Colombier Done *d;
1017901484c1SDavid du Colombier Rx *rx;
1018901484c1SDavid du Colombier Slot *s;
101941ac1ab6SDavid du Colombier Slotparts *sp;
1020901484c1SDavid du Colombier
1021901484c1SDavid du Colombier d = &c->done;
1022901484c1SDavid du Colombier s = d->entry;
1023901484c1SDavid du Colombier i = d->i & d->m;
102441ac1ab6SDavid du Colombier sp = (Slotparts *)(s + i);
102541ac1ab6SDavid du Colombier l = sp->len;
1026901484c1SDavid du Colombier if(l == 0)
1027901484c1SDavid du Colombier return 0;
102841ac1ab6SDavid du Colombier k = sp->cksum;
102941ac1ab6SDavid du Colombier s[i] = 0;
1030901484c1SDavid du Colombier d->i++;
1031901484c1SDavid du Colombier l = gbit16((uchar*)&l);
1032901484c1SDavid du Colombier //dprint("nextb: i=%d l=%d\n", d->i, l);
1033901484c1SDavid du Colombier rx = whichrx(c, l);
1034901484c1SDavid du Colombier if(rx->i >= rx->cnt){
1035901484c1SDavid du Colombier iprint("m10g: overrun\n");
1036901484c1SDavid du Colombier return 0;
1037901484c1SDavid du Colombier }
1038901484c1SDavid du Colombier i = rx->i & rx->m;
1039901484c1SDavid du Colombier b = rx->host[i];
1040901484c1SDavid du Colombier rx->host[i] = 0;
1041901484c1SDavid du Colombier if(b == 0){
1042901484c1SDavid du Colombier iprint("m10g: error rx to no block. memory is hosed.\n");
1043901484c1SDavid du Colombier return 0;
1044901484c1SDavid du Colombier }
1045901484c1SDavid du Colombier rx->i++;
1046901484c1SDavid du Colombier
1047901484c1SDavid du Colombier b->flag |= Bipck|Btcpck|Budpck;
1048901484c1SDavid du Colombier b->checksum = k;
1049901484c1SDavid du Colombier b->rp += 2;
1050901484c1SDavid du Colombier b->wp += 2+l;
1051901484c1SDavid du Colombier b->lim = b->wp; /* lie like a dog. */
1052901484c1SDavid du Colombier return b;
1053901484c1SDavid du Colombier }
1054901484c1SDavid du Colombier
1055901484c1SDavid du Colombier static int
rxcansleep(void * v)1056901484c1SDavid du Colombier rxcansleep(void *v)
1057901484c1SDavid du Colombier {
1058901484c1SDavid du Colombier Ctlr *c;
1059901484c1SDavid du Colombier Slot *s;
106041ac1ab6SDavid du Colombier Slotparts *sp;
1061901484c1SDavid du Colombier Done *d;
1062901484c1SDavid du Colombier
1063901484c1SDavid du Colombier c = v;
1064901484c1SDavid du Colombier d = &c->done;
1065901484c1SDavid du Colombier s = c->done.entry;
106641ac1ab6SDavid du Colombier sp = (Slotparts *)(s + (d->i & d->m));
106741ac1ab6SDavid du Colombier if(sp->len != 0)
1068901484c1SDavid du Colombier return -1;
1069901484c1SDavid du Colombier c->irqack[0] = pbit32(3);
1070901484c1SDavid du Colombier return 0;
1071901484c1SDavid du Colombier }
1072901484c1SDavid du Colombier
1073901484c1SDavid du Colombier static void
m10rx(void * v)1074901484c1SDavid du Colombier m10rx(void *v)
1075901484c1SDavid du Colombier {
1076901484c1SDavid du Colombier Ether *e;
1077901484c1SDavid du Colombier Ctlr *c;
1078901484c1SDavid du Colombier Block *b;
1079901484c1SDavid du Colombier
1080901484c1SDavid du Colombier e = v;
1081901484c1SDavid du Colombier c = e->ctlr;
1082901484c1SDavid du Colombier for(;;){
1083901484c1SDavid du Colombier replenish(&c->sm);
1084901484c1SDavid du Colombier replenish(&c->bg);
1085901484c1SDavid du Colombier sleep(&c->rxrendez, rxcansleep, c);
1086901484c1SDavid du Colombier while(b = nextblock(c))
1087901484c1SDavid du Colombier etheriq(e, b, 1);
1088901484c1SDavid du Colombier }
1089901484c1SDavid du Colombier }
1090901484c1SDavid du Colombier
109141ac1ab6SDavid du Colombier static void
txcleanup(Tx * tx,ulong n)109291b330d9SDavid du Colombier txcleanup(Tx *tx, ulong n)
1093901484c1SDavid du Colombier {
1094901484c1SDavid du Colombier Block *b;
1095901484c1SDavid du Colombier uint j, l, m;
1096901484c1SDavid du Colombier
1097901484c1SDavid du Colombier if(tx->npkt == n)
1098901484c1SDavid du Colombier return;
1099901484c1SDavid du Colombier l = 0;
1100901484c1SDavid du Colombier m = tx->m;
110141ac1ab6SDavid du Colombier /*
110241ac1ab6SDavid du Colombier * if tx->cnt == tx->i, yet tx->npkt == n-1, we just
110341ac1ab6SDavid du Colombier * caught ourselves and myricom card updating.
110441ac1ab6SDavid du Colombier */
1105901484c1SDavid du Colombier for(;; tx->cnt++){
1106901484c1SDavid du Colombier j = tx->cnt & tx->m;
1107901484c1SDavid du Colombier if(b = tx->bring[j]){
1108901484c1SDavid du Colombier tx->bring[j] = 0;
1109901484c1SDavid du Colombier tx->nbytes += BLEN(b);
1110901484c1SDavid du Colombier freeb(b);
1111901484c1SDavid du Colombier if(++tx->npkt == n)
1112901484c1SDavid du Colombier return;
1113901484c1SDavid du Colombier }
1114901484c1SDavid du Colombier if(tx->cnt == tx->i)
1115901484c1SDavid du Colombier return;
1116901484c1SDavid du Colombier if(l++ == m){
111780088de7SDavid du Colombier iprint("m10g: tx ovrun: %lud %lud\n", n, tx->npkt);
1118901484c1SDavid du Colombier return;
1119901484c1SDavid du Colombier }
1120901484c1SDavid du Colombier }
1121901484c1SDavid du Colombier }
1122901484c1SDavid du Colombier
1123901484c1SDavid du Colombier static int
txcansleep(void * v)1124901484c1SDavid du Colombier txcansleep(void *v)
1125901484c1SDavid du Colombier {
1126901484c1SDavid du Colombier Ctlr *c;
1127901484c1SDavid du Colombier
1128901484c1SDavid du Colombier c = v;
1129901484c1SDavid du Colombier if(c->tx.cnt != c->tx.i && c->tx.npkt != gbit32(c->stats->txcnt))
1130901484c1SDavid du Colombier return -1;
1131901484c1SDavid du Colombier return 0;
1132901484c1SDavid du Colombier }
1133901484c1SDavid du Colombier
113441ac1ab6SDavid du Colombier static void
txproc(void * v)1135901484c1SDavid du Colombier txproc(void *v)
1136901484c1SDavid du Colombier {
1137901484c1SDavid du Colombier Ether *e;
1138901484c1SDavid du Colombier Ctlr *c;
1139901484c1SDavid du Colombier Tx *tx;
1140901484c1SDavid du Colombier
1141901484c1SDavid du Colombier e = v;
1142901484c1SDavid du Colombier c = e->ctlr;
1143901484c1SDavid du Colombier tx = &c->tx;
1144901484c1SDavid du Colombier for(;;){
1145901484c1SDavid du Colombier sleep(&c->txrendez, txcansleep, c);
1146901484c1SDavid du Colombier txcleanup(tx, gbit32(c->stats->txcnt));
1147901484c1SDavid du Colombier }
1148901484c1SDavid du Colombier }
1149901484c1SDavid du Colombier
115041ac1ab6SDavid du Colombier static void
submittx(Tx * tx,int n)1151901484c1SDavid du Colombier submittx(Tx *tx, int n)
1152901484c1SDavid du Colombier {
1153901484c1SDavid du Colombier Send *l, *h;
1154901484c1SDavid du Colombier int i0, i, m;
1155901484c1SDavid du Colombier
1156901484c1SDavid du Colombier m = tx->m;
1157901484c1SDavid du Colombier i0 = tx->i & m;
1158901484c1SDavid du Colombier l = tx->lanai;
1159901484c1SDavid du Colombier h = tx->host;
1160901484c1SDavid du Colombier for(i = n-1; i >= 0; i--)
1161901484c1SDavid du Colombier memmove(l+(i + i0 & m), h+(i + i0 & m), sizeof *h);
1162901484c1SDavid du Colombier tx->i += n;
1163901484c1SDavid du Colombier // coherence();
1164901484c1SDavid du Colombier }
1165901484c1SDavid du Colombier
116641ac1ab6SDavid du Colombier static int
nsegments(Block * b,int segsz)1167901484c1SDavid du Colombier nsegments(Block *b, int segsz)
1168901484c1SDavid du Colombier {
1169901484c1SDavid du Colombier uintptr bus, end, slen, len;
1170901484c1SDavid du Colombier int i;
1171901484c1SDavid du Colombier
1172901484c1SDavid du Colombier bus = PCIWADDR(b->rp);
1173901484c1SDavid du Colombier i = 0;
1174901484c1SDavid du Colombier for(len = BLEN(b); len; len -= slen){
1175901484c1SDavid du Colombier end = bus + segsz & ~(segsz-1);
1176901484c1SDavid du Colombier slen = end - bus;
1177901484c1SDavid du Colombier if(slen > len)
1178901484c1SDavid du Colombier slen = len;
1179901484c1SDavid du Colombier bus += slen;
1180901484c1SDavid du Colombier i++;
1181901484c1SDavid du Colombier }
1182901484c1SDavid du Colombier return i;
1183901484c1SDavid du Colombier }
1184901484c1SDavid du Colombier
1185901484c1SDavid du Colombier static void
m10gtransmit(Ether * e)1186901484c1SDavid du Colombier m10gtransmit(Ether *e)
1187901484c1SDavid du Colombier {
118891b330d9SDavid du Colombier ushort slen;
118991b330d9SDavid du Colombier ulong i, cnt, rdma, nseg, count, end, bus, len, segsz;
1190901484c1SDavid du Colombier uchar flags;
1191901484c1SDavid du Colombier Block *b;
1192901484c1SDavid du Colombier Ctlr *c;
1193901484c1SDavid du Colombier Send *s, *s0, *s0m8;
1194901484c1SDavid du Colombier Tx *tx;
1195901484c1SDavid du Colombier
1196901484c1SDavid du Colombier c = e->ctlr;
1197901484c1SDavid du Colombier tx = &c->tx;
1198901484c1SDavid du Colombier segsz = tx->segsz;
1199901484c1SDavid du Colombier
1200901484c1SDavid du Colombier qlock(tx);
1201901484c1SDavid du Colombier count = 0;
1202901484c1SDavid du Colombier s = tx->host + (tx->i & tx->m);
1203901484c1SDavid du Colombier cnt = tx->cnt;
1204901484c1SDavid du Colombier s0 = tx->host + (cnt & tx->m);
120541ac1ab6SDavid du Colombier s0m8 = tx->host + ((cnt - 8) & tx->m);
1206901484c1SDavid du Colombier i = tx->i;
1207901484c1SDavid du Colombier for(; s >= s0 || s < s0m8; i += nseg){
1208901484c1SDavid du Colombier if((b = qget(e->oq)) == nil)
1209901484c1SDavid du Colombier break;
1210901484c1SDavid du Colombier flags = SFfirst|SFnotso;
1211901484c1SDavid du Colombier if((len = BLEN(b)) < 1520)
1212901484c1SDavid du Colombier flags |= SFsmall;
1213901484c1SDavid du Colombier rdma = nseg = nsegments(b, segsz);
1214901484c1SDavid du Colombier bus = PCIWADDR(b->rp);
1215901484c1SDavid du Colombier for(; len; len -= slen){
121664a26757SDavid du Colombier end = (bus + segsz) & ~(segsz-1);
1217901484c1SDavid du Colombier slen = end - bus;
1218901484c1SDavid du Colombier if(slen > len)
1219901484c1SDavid du Colombier slen = len;
1220901484c1SDavid du Colombier s->low = pbit32(bus);
1221901484c1SDavid du Colombier s->len = pbit16(slen);
1222901484c1SDavid du Colombier s->nrdma = rdma;
1223901484c1SDavid du Colombier s->flags = flags;
1224901484c1SDavid du Colombier
1225901484c1SDavid du Colombier bus += slen;
1226901484c1SDavid du Colombier if(++s == tx->host + tx->n)
1227901484c1SDavid du Colombier s = tx->host;
1228901484c1SDavid du Colombier count++;
1229901484c1SDavid du Colombier flags &= ~SFfirst;
1230901484c1SDavid du Colombier rdma = 1;
1231901484c1SDavid du Colombier }
123264a26757SDavid du Colombier tx->bring[(i + nseg - 1) & tx->m] = b;
1233901484c1SDavid du Colombier if(1 || count > 0){
1234901484c1SDavid du Colombier submittx(tx, count);
1235901484c1SDavid du Colombier count = 0;
1236901484c1SDavid du Colombier cnt = tx->cnt;
1237901484c1SDavid du Colombier s0 = tx->host + (cnt & tx->m);
123841ac1ab6SDavid du Colombier s0m8 = tx->host + ((cnt - 8) & tx->m);
1239901484c1SDavid du Colombier }
1240901484c1SDavid du Colombier }
1241901484c1SDavid du Colombier qunlock(tx);
1242901484c1SDavid du Colombier }
1243901484c1SDavid du Colombier
1244901484c1SDavid du Colombier static void
checkstats(Ether * e,Ctlr * c,Stats * s)1245901484c1SDavid du Colombier checkstats(Ether *e, Ctlr *c, Stats *s)
1246901484c1SDavid du Colombier {
124791b330d9SDavid du Colombier ulong i;
1248901484c1SDavid du Colombier
1249901484c1SDavid du Colombier if(s->updated == 0)
1250901484c1SDavid du Colombier return;
1251901484c1SDavid du Colombier
1252901484c1SDavid du Colombier i = gbit32(s->linkstat);
1253901484c1SDavid du Colombier if(c->linkstat != i){
1254901484c1SDavid du Colombier e->link = i;
1255901484c1SDavid du Colombier if(c->linkstat = i)
1256901484c1SDavid du Colombier dprint("m10g: link up\n");
1257901484c1SDavid du Colombier else
1258901484c1SDavid du Colombier dprint("m10g: link down\n");
1259901484c1SDavid du Colombier }
1260901484c1SDavid du Colombier i = gbit32(s->nrdma);
1261901484c1SDavid du Colombier if(i != c->nrdma){
126291b330d9SDavid du Colombier dprint("m10g: rdma timeout %ld\n", i);
1263901484c1SDavid du Colombier c->nrdma = i;
1264901484c1SDavid du Colombier }
1265901484c1SDavid du Colombier }
1266901484c1SDavid du Colombier
1267901484c1SDavid du Colombier static void
waitintx(Ctlr * c)1268901484c1SDavid du Colombier waitintx(Ctlr *c)
1269901484c1SDavid du Colombier {
1270901484c1SDavid du Colombier int i;
1271901484c1SDavid du Colombier
1272901484c1SDavid du Colombier for(i = 0; i < 1024*1024; i++){
1273901484c1SDavid du Colombier if(c->stats->valid == 0)
1274901484c1SDavid du Colombier break;
1275901484c1SDavid du Colombier coherence();
1276901484c1SDavid du Colombier }
1277901484c1SDavid du Colombier }
1278901484c1SDavid du Colombier
1279901484c1SDavid du Colombier static void
m10ginterrupt(Ureg *,void * v)1280901484c1SDavid du Colombier m10ginterrupt(Ureg *, void *v)
1281901484c1SDavid du Colombier {
1282901484c1SDavid du Colombier Ether *e;
1283901484c1SDavid du Colombier Ctlr *c;
1284901484c1SDavid du Colombier
1285901484c1SDavid du Colombier e = v;
1286901484c1SDavid du Colombier c = e->ctlr;
1287901484c1SDavid du Colombier
1288901484c1SDavid du Colombier if(c->state != Runed || c->stats->valid == 0) /* not ready for us? */
1289901484c1SDavid du Colombier return;
1290901484c1SDavid du Colombier
1291901484c1SDavid du Colombier if(c->stats->valid & 1)
1292901484c1SDavid du Colombier wakeup(&c->rxrendez);
1293901484c1SDavid du Colombier if(gbit32(c->stats->txcnt) != c->tx.npkt)
1294901484c1SDavid du Colombier wakeup(&c->txrendez);
1295901484c1SDavid du Colombier if(c->msi == 0)
1296901484c1SDavid du Colombier *c->irqdeass = 0;
1297901484c1SDavid du Colombier else
1298901484c1SDavid du Colombier c->stats->valid = 0;
1299901484c1SDavid du Colombier waitintx(c);
1300901484c1SDavid du Colombier checkstats(e, c, c->stats);
1301901484c1SDavid du Colombier c->irqack[1] = pbit32(3);
1302901484c1SDavid du Colombier }
1303901484c1SDavid du Colombier
1304901484c1SDavid du Colombier static void
m10gattach(Ether * e)1305901484c1SDavid du Colombier m10gattach(Ether *e)
1306901484c1SDavid du Colombier {
1307901484c1SDavid du Colombier Ctlr *c;
1308901484c1SDavid du Colombier char name[12];
1309901484c1SDavid du Colombier
1310901484c1SDavid du Colombier dprint("m10gattach\n");
1311901484c1SDavid du Colombier
1312901484c1SDavid du Colombier qlock(e->ctlr);
1313901484c1SDavid du Colombier c = e->ctlr;
1314901484c1SDavid du Colombier if(c->state != Detached){
1315901484c1SDavid du Colombier qunlock(c);
1316901484c1SDavid du Colombier return;
1317901484c1SDavid du Colombier }
1318901484c1SDavid du Colombier if(waserror()){
1319901484c1SDavid du Colombier c->state = Detached;
1320901484c1SDavid du Colombier qunlock(c);
1321901484c1SDavid du Colombier nexterror();
1322901484c1SDavid du Colombier }
1323901484c1SDavid du Colombier reset(e, c);
1324901484c1SDavid du Colombier c->state = Attached;
1325901484c1SDavid du Colombier open0(e, c);
1326901484c1SDavid du Colombier if(c->kprocs == 0){
1327901484c1SDavid du Colombier c->kprocs++;
1328901484c1SDavid du Colombier snprint(name, sizeof name, "#l%drxproc", e->ctlrno);
1329901484c1SDavid du Colombier kproc(name, m10rx, e);
1330901484c1SDavid du Colombier snprint(name, sizeof name, "#l%dtxproc", e->ctlrno);
1331901484c1SDavid du Colombier kproc(name, txproc, e);
1332901484c1SDavid du Colombier }
1333901484c1SDavid du Colombier c->state = Runed;
1334901484c1SDavid du Colombier qunlock(c);
1335901484c1SDavid du Colombier poperror();
1336901484c1SDavid du Colombier }
1337901484c1SDavid du Colombier
1338901484c1SDavid du Colombier static int
m10gdetach(Ctlr * c)1339901484c1SDavid du Colombier m10gdetach(Ctlr *c)
1340901484c1SDavid du Colombier {
1341901484c1SDavid du Colombier dprint("m10gdetach\n");
1342901484c1SDavid du Colombier // reset(e->ctlr);
1343901484c1SDavid du Colombier vunmap(c->ram, c->pcidev->mem[0].size);
13446083aa43SDavid du Colombier ctlrfree(c); /* this is a bad idea: don't free c */
1345901484c1SDavid du Colombier return -1;
1346901484c1SDavid du Colombier }
1347901484c1SDavid du Colombier
1348901484c1SDavid du Colombier static int
lstcount(Block * b)1349901484c1SDavid du Colombier lstcount(Block *b)
1350901484c1SDavid du Colombier {
1351901484c1SDavid du Colombier int i;
1352901484c1SDavid du Colombier
1353901484c1SDavid du Colombier i = 0;
1354901484c1SDavid du Colombier for(; b; b = b->next)
1355901484c1SDavid du Colombier i++;
1356901484c1SDavid du Colombier return i;
1357901484c1SDavid du Colombier }
1358901484c1SDavid du Colombier
1359901484c1SDavid du Colombier static long
m10gifstat(Ether * e,void * v,long n,ulong off)1360901484c1SDavid du Colombier m10gifstat(Ether *e, void *v, long n, ulong off)
1361901484c1SDavid du Colombier {
1362901484c1SDavid du Colombier char *p;
1363901484c1SDavid du Colombier Ctlr *c;
1364901484c1SDavid du Colombier Stats s;
1365901484c1SDavid du Colombier
1366901484c1SDavid du Colombier c = e->ctlr;
136746136019SDavid du Colombier p = malloc(READSTR+1);
1368aa72973aSDavid du Colombier if(p == nil)
1369aa72973aSDavid du Colombier error(Enomem);
1370901484c1SDavid du Colombier /* no point in locking this because this is done via dma. */
1371901484c1SDavid du Colombier memmove(&s, c->stats, sizeof s);
1372901484c1SDavid du Colombier
13736083aa43SDavid du Colombier snprint(p, READSTR,
137491b330d9SDavid du Colombier "txcnt = %lud\n" "linkstat = %lud\n" "dlink = %lud\n"
137591b330d9SDavid du Colombier "derror = %lud\n" "drunt = %lud\n" "doverrun = %lud\n"
137691b330d9SDavid du Colombier "dnosm = %lud\n" "dnobg = %lud\n" "nrdma = %lud\n"
1377901484c1SDavid du Colombier "txstopped = %ud\n" "down = %ud\n" "updated = %ud\n"
1378901484c1SDavid du Colombier "valid = %ud\n\n"
137991b330d9SDavid du Colombier "tx pkt = %lud\n" "tx bytes = %lld\n"
1380901484c1SDavid du Colombier "tx cnt = %ud\n" "tx n = %ud\n" "tx i = %ud\n"
138141ac1ab6SDavid du Colombier "sm cnt = %ud\n" "sm i = %ud\n" "sm n = %ud\n"
138241ac1ab6SDavid du Colombier "sm lst = %ud\n"
138341ac1ab6SDavid du Colombier "bg cnt = %ud\n" "bg i = %ud\n" "bg n = %ud\n"
138441ac1ab6SDavid du Colombier "bg lst = %ud\n"
138591b330d9SDavid du Colombier "segsz = %lud\n" "coal = %lud\n",
1386901484c1SDavid du Colombier gbit32(s.txcnt), gbit32(s.linkstat), gbit32(s.dlink),
1387901484c1SDavid du Colombier gbit32(s.derror), gbit32(s.drunt), gbit32(s.doverrun),
1388901484c1SDavid du Colombier gbit32(s.dnosm), gbit32(s.dnobg), gbit32(s.nrdma),
1389901484c1SDavid du Colombier s.txstopped, s.down, s.updated, s.valid,
1390901484c1SDavid du Colombier c->tx.npkt, c->tx.nbytes,
1391901484c1SDavid du Colombier c->tx.cnt, c->tx.n, c->tx.i,
1392901484c1SDavid du Colombier c->sm.cnt, c->sm.i, c->sm.pool->n, lstcount(c->sm.pool->head),
1393901484c1SDavid du Colombier c->bg.cnt, c->bg.i, c->bg.pool->n, lstcount(c->bg.pool->head),
1394901484c1SDavid du Colombier c->tx.segsz, gbit32((uchar*)c->coal));
1395901484c1SDavid du Colombier
1396901484c1SDavid du Colombier n = readstr(off, v, n, p);
1397901484c1SDavid du Colombier free(p);
1398901484c1SDavid du Colombier return n;
1399901484c1SDavid du Colombier }
1400901484c1SDavid du Colombier
140141ac1ab6SDavid du Colombier //static void
1402901484c1SDavid du Colombier //summary(Ether *e)
1403901484c1SDavid du Colombier //{
1404901484c1SDavid du Colombier // char *buf;
1405901484c1SDavid du Colombier // int n, i, j;
1406901484c1SDavid du Colombier //
1407901484c1SDavid du Colombier // if(e == 0)
1408901484c1SDavid du Colombier // return;
1409901484c1SDavid du Colombier // buf = malloc(n=250);
1410901484c1SDavid du Colombier // if(buf == 0)
1411901484c1SDavid du Colombier // return;
1412901484c1SDavid du Colombier //
1413901484c1SDavid du Colombier // snprint(buf, n, "oq\n");
1414901484c1SDavid du Colombier // qsummary(e->oq, buf+3, n-3-1);
1415901484c1SDavid du Colombier // iprint("%s", buf);
1416901484c1SDavid du Colombier //
1417901484c1SDavid du Colombier // if(e->f) for(i = 0; e->f[i]; i++){
1418901484c1SDavid du Colombier // j = snprint(buf, n, "f%d %d\n", i, e->f[i]->type);
1419901484c1SDavid du Colombier // qsummary(e->f[i]->in, buf+j, n-j-1);
1420901484c1SDavid du Colombier // print("%s", buf);
1421901484c1SDavid du Colombier // }
1422901484c1SDavid du Colombier //
1423901484c1SDavid du Colombier // free(buf);
1424901484c1SDavid du Colombier //}
1425901484c1SDavid du Colombier
142641ac1ab6SDavid du Colombier static void
rxring(Ctlr * c)1427901484c1SDavid du Colombier rxring(Ctlr *c)
1428901484c1SDavid du Colombier {
1429901484c1SDavid du Colombier Done *d;
1430901484c1SDavid du Colombier Slot *s;
143141ac1ab6SDavid du Colombier Slotparts *sp;
1432901484c1SDavid du Colombier int i;
1433901484c1SDavid du Colombier
1434901484c1SDavid du Colombier d = &c->done;
1435901484c1SDavid du Colombier s = d->entry;
143641ac1ab6SDavid du Colombier for(i = 0; i < d->n; i++) {
143741ac1ab6SDavid du Colombier sp = (Slotparts *)(s + i);
143841ac1ab6SDavid du Colombier if(sp->len)
143941ac1ab6SDavid du Colombier iprint("s[%d] = %d\n", i, sp->len);
144041ac1ab6SDavid du Colombier }
1441901484c1SDavid du Colombier }
1442901484c1SDavid du Colombier
1443901484c1SDavid du Colombier enum {
1444901484c1SDavid du Colombier CMdebug,
1445901484c1SDavid du Colombier CMcoal,
1446901484c1SDavid du Colombier CMwakeup,
1447901484c1SDavid du Colombier CMtxwakeup,
1448901484c1SDavid du Colombier CMqsummary,
1449901484c1SDavid du Colombier CMrxring,
1450901484c1SDavid du Colombier };
1451901484c1SDavid du Colombier
1452901484c1SDavid du Colombier static Cmdtab ctab[] = {
1453901484c1SDavid du Colombier CMdebug, "debug", 2,
1454901484c1SDavid du Colombier CMcoal, "coal", 2,
1455901484c1SDavid du Colombier CMwakeup, "wakeup", 1,
1456901484c1SDavid du Colombier CMtxwakeup, "txwakeup", 1,
1457901484c1SDavid du Colombier // CMqsummary, "q", 1,
1458901484c1SDavid du Colombier CMrxring, "rxring", 1,
1459901484c1SDavid du Colombier };
1460901484c1SDavid du Colombier
1461901484c1SDavid du Colombier static long
m10gctl(Ether * e,void * v,long n)1462901484c1SDavid du Colombier m10gctl(Ether *e, void *v, long n)
1463901484c1SDavid du Colombier {
1464901484c1SDavid du Colombier int i;
1465901484c1SDavid du Colombier Cmdbuf *c;
1466901484c1SDavid du Colombier Cmdtab *t;
1467901484c1SDavid du Colombier
1468901484c1SDavid du Colombier dprint("m10gctl\n");
1469901484c1SDavid du Colombier if(e->ctlr == nil)
1470901484c1SDavid du Colombier error(Enonexist);
1471901484c1SDavid du Colombier
1472901484c1SDavid du Colombier c = parsecmd(v, n);
1473901484c1SDavid du Colombier if(waserror()){
1474901484c1SDavid du Colombier free(c);
1475901484c1SDavid du Colombier nexterror();
1476901484c1SDavid du Colombier }
1477901484c1SDavid du Colombier t = lookupcmd(c, ctab, nelem(ctab));
1478901484c1SDavid du Colombier switch(t->index){
1479901484c1SDavid du Colombier case CMdebug:
148041ac1ab6SDavid du Colombier debug = (strcmp(c->f[1], "on") == 0);
1481901484c1SDavid du Colombier break;
1482901484c1SDavid du Colombier case CMcoal:
1483901484c1SDavid du Colombier i = atoi(c->f[1]);
1484901484c1SDavid du Colombier if(i < 0 || i > 1000)
1485901484c1SDavid du Colombier error(Ebadarg);
1486901484c1SDavid du Colombier *((Ctlr*)e->ctlr)->coal = pbit32(i);
1487901484c1SDavid du Colombier break;
1488901484c1SDavid du Colombier case CMwakeup:
1489901484c1SDavid du Colombier wakeup(&((Ctlr*)e->ctlr)->rxrendez); /* you're kidding, right? */
1490901484c1SDavid du Colombier break;
1491901484c1SDavid du Colombier case CMtxwakeup:
1492901484c1SDavid du Colombier wakeup(&((Ctlr*)e->ctlr)->txrendez); /* you're kidding, right? */
1493901484c1SDavid du Colombier break;
1494901484c1SDavid du Colombier // case CMqsummary:
1495901484c1SDavid du Colombier // summary(e);
1496901484c1SDavid du Colombier // break;
1497901484c1SDavid du Colombier case CMrxring:
1498901484c1SDavid du Colombier rxring(e->ctlr);
1499901484c1SDavid du Colombier break;
1500901484c1SDavid du Colombier default:
1501901484c1SDavid du Colombier error(Ebadarg);
1502901484c1SDavid du Colombier }
1503901484c1SDavid du Colombier free(c);
1504901484c1SDavid du Colombier poperror();
1505901484c1SDavid du Colombier return n;
1506901484c1SDavid du Colombier }
1507901484c1SDavid du Colombier
1508901484c1SDavid du Colombier static void
m10gshutdown(Ether * e)1509901484c1SDavid du Colombier m10gshutdown(Ether *e)
1510901484c1SDavid du Colombier {
1511901484c1SDavid du Colombier dprint("m10gshutdown\n");
1512901484c1SDavid du Colombier m10gdetach(e->ctlr);
1513901484c1SDavid du Colombier }
1514901484c1SDavid du Colombier
1515901484c1SDavid du Colombier static void
m10gpromiscuous(void * v,int on)1516901484c1SDavid du Colombier m10gpromiscuous(void *v, int on)
1517901484c1SDavid du Colombier {
1518901484c1SDavid du Colombier Ether *e;
1519901484c1SDavid du Colombier int i;
1520901484c1SDavid du Colombier
1521901484c1SDavid du Colombier dprint("m10gpromiscuous\n");
1522901484c1SDavid du Colombier e = v;
1523901484c1SDavid du Colombier if(on)
1524901484c1SDavid du Colombier i = Cpromisc;
1525901484c1SDavid du Colombier else
1526901484c1SDavid du Colombier i = Cnopromisc;
1527901484c1SDavid du Colombier cmd(e->ctlr, i, 0);
1528901484c1SDavid du Colombier }
1529901484c1SDavid du Colombier
1530901484c1SDavid du Colombier static int mcctab[] = { CSleavemc, CSjoinmc };
1531901484c1SDavid du Colombier static char *mcntab[] = { "leave", "join" };
1532901484c1SDavid du Colombier
1533901484c1SDavid du Colombier static void
m10gmulticast(void * v,uchar * ea,int on)1534901484c1SDavid du Colombier m10gmulticast(void *v, uchar *ea, int on)
1535901484c1SDavid du Colombier {
1536901484c1SDavid du Colombier Ether *e;
1537901484c1SDavid du Colombier int i;
1538901484c1SDavid du Colombier
1539901484c1SDavid du Colombier dprint("m10gmulticast\n");
1540901484c1SDavid du Colombier e = v;
1541901484c1SDavid du Colombier if((i = maccmd(e->ctlr, mcctab[on], ea)) != 0)
1542901484c1SDavid du Colombier print("m10g: can't %s %E: %d\n", mcntab[on], ea, i);
1543901484c1SDavid du Colombier }
1544901484c1SDavid du Colombier
1545901484c1SDavid du Colombier static void
m10gpci(void)1546901484c1SDavid du Colombier m10gpci(void)
1547901484c1SDavid du Colombier {
1548901484c1SDavid du Colombier Pcidev *p;
1549901484c1SDavid du Colombier Ctlr *t, *c;
1550901484c1SDavid du Colombier
1551901484c1SDavid du Colombier t = 0;
1552d5789509SDavid du Colombier for(p = 0; p = pcimatch(p, Vmyricom, 0); ){
1553d5789509SDavid du Colombier switch(p->did){
1554d5789509SDavid du Colombier case 0x8: /* 8a */
1555d5789509SDavid du Colombier break;
1556d5789509SDavid du Colombier case 0x9: /* 8a with msi-x fw */
1557d5789509SDavid du Colombier case 0xa: /* 8b */
1558d5789509SDavid du Colombier case 0xb: /* 8b2 */
1559d5789509SDavid du Colombier case 0xc: /* 2-8b2 */
1560d5789509SDavid du Colombier /* untested */
1561d5789509SDavid du Colombier break;
1562d5789509SDavid du Colombier default:
1563d5789509SDavid du Colombier print("etherm10g: unknown myricom did %#ux\n", p->did);
1564d5789509SDavid du Colombier continue;
1565d5789509SDavid du Colombier }
1566901484c1SDavid du Colombier c = malloc(sizeof *c);
1567901484c1SDavid du Colombier if(c == nil)
15686083aa43SDavid du Colombier break;
1569901484c1SDavid du Colombier c->pcidev = p;
1570901484c1SDavid du Colombier c->id = p->did<<16 | p->vid;
1571901484c1SDavid du Colombier c->boot = pcicap(p, PciCapVND);
1572901484c1SDavid du Colombier // kickthebaby(p, c);
1573901484c1SDavid du Colombier pcisetbme(p);
1574901484c1SDavid du Colombier if(setmem(p, c) == -1){
157580088de7SDavid du Colombier print("m10g: setmem failed\n");
1576901484c1SDavid du Colombier free(c);
1577901484c1SDavid du Colombier /* cleanup */
1578901484c1SDavid du Colombier continue;
1579901484c1SDavid du Colombier }
1580901484c1SDavid du Colombier if(t)
1581901484c1SDavid du Colombier t->next = c;
1582901484c1SDavid du Colombier else
1583901484c1SDavid du Colombier ctlrs = c;
1584901484c1SDavid du Colombier t = c;
1585901484c1SDavid du Colombier }
1586901484c1SDavid du Colombier }
1587901484c1SDavid du Colombier
1588901484c1SDavid du Colombier static int
m10gpnp(Ether * e)1589901484c1SDavid du Colombier m10gpnp(Ether *e)
1590901484c1SDavid du Colombier {
1591901484c1SDavid du Colombier Ctlr *c;
1592901484c1SDavid du Colombier
1593901484c1SDavid du Colombier if(ctlrs == nil)
1594901484c1SDavid du Colombier m10gpci();
1595901484c1SDavid du Colombier
1596901484c1SDavid du Colombier for(c = ctlrs; c != nil; c = c->next)
1597901484c1SDavid du Colombier if(c->active)
1598901484c1SDavid du Colombier continue;
1599901484c1SDavid du Colombier else if(e->port == 0 || e->port == c->port)
1600901484c1SDavid du Colombier break;
1601901484c1SDavid du Colombier if(c == nil)
1602901484c1SDavid du Colombier return -1;
1603901484c1SDavid du Colombier c->active = 1;
1604901484c1SDavid du Colombier
1605901484c1SDavid du Colombier e->ctlr = c;
1606901484c1SDavid du Colombier e->port = c->port;
1607901484c1SDavid du Colombier e->irq = c->pcidev->intl;
1608901484c1SDavid du Colombier e->tbdf = c->pcidev->tbdf;
1609901484c1SDavid du Colombier e->mbps = 10000;
1610901484c1SDavid du Colombier memmove(e->ea, c->ra, Eaddrlen);
1611901484c1SDavid du Colombier
1612901484c1SDavid du Colombier e->attach = m10gattach;
1613901484c1SDavid du Colombier e->detach = m10gshutdown;
1614901484c1SDavid du Colombier e->transmit = m10gtransmit;
1615901484c1SDavid du Colombier e->interrupt = m10ginterrupt;
1616901484c1SDavid du Colombier e->ifstat = m10gifstat;
1617901484c1SDavid du Colombier e->ctl = m10gctl;
1618901484c1SDavid du Colombier e->shutdown = m10gshutdown;
1619901484c1SDavid du Colombier
1620901484c1SDavid du Colombier e->arg = e;
1621901484c1SDavid du Colombier e->promiscuous = m10gpromiscuous;
1622901484c1SDavid du Colombier e->multicast = m10gmulticast;
1623901484c1SDavid du Colombier return 0;
1624901484c1SDavid du Colombier }
1625901484c1SDavid du Colombier
1626901484c1SDavid du Colombier void
etherm10glink(void)1627901484c1SDavid du Colombier etherm10glink(void)
1628901484c1SDavid du Colombier {
1629901484c1SDavid du Colombier addethercard("m10g", m10gpnp);
1630901484c1SDavid du Colombier }
1631