diff options
author | Scott Feldman <scofeldm@cisco.com> | 2008-09-15 12:17:11 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-09-18 11:34:53 -0400 |
commit | 01f2e4ead2c51226ed1283ef6a8388ca6f4cff8f (patch) | |
tree | b1cc2ef1a191a3bf00f371d5dbc2028e1fee01c5 /drivers/net/enic/vnic_dev.c | |
parent | 452c1ce218a68b5dbd626397ecfc65ca89dd3cbb (diff) |
enic: add Cisco 10G Ethernet NIC driver
Signed-off-by: Scott Feldman <scofeldm@cisco.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/net/enic/vnic_dev.c')
-rw-r--r-- | drivers/net/enic/vnic_dev.c | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/drivers/net/enic/vnic_dev.c b/drivers/net/enic/vnic_dev.c new file mode 100644 index 000000000000..4d104f5c30f9 --- /dev/null +++ b/drivers/net/enic/vnic_dev.c | |||
@@ -0,0 +1,674 @@ | |||
1 | /* | ||
2 | * Copyright 2008 Cisco Systems, Inc. All rights reserved. | ||
3 | * Copyright 2007 Nuova Systems, Inc. All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you may redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; version 2 of the License. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
10 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
12 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
13 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
15 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
16 | * SOFTWARE. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/pci.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/if_ether.h> | ||
26 | |||
27 | #include "vnic_resource.h" | ||
28 | #include "vnic_devcmd.h" | ||
29 | #include "vnic_dev.h" | ||
30 | #include "vnic_stats.h" | ||
31 | |||
32 | struct vnic_res { | ||
33 | void __iomem *vaddr; | ||
34 | unsigned int count; | ||
35 | }; | ||
36 | |||
37 | struct vnic_dev { | ||
38 | void *priv; | ||
39 | struct pci_dev *pdev; | ||
40 | struct vnic_res res[RES_TYPE_MAX]; | ||
41 | enum vnic_dev_intr_mode intr_mode; | ||
42 | struct vnic_devcmd __iomem *devcmd; | ||
43 | struct vnic_devcmd_notify *notify; | ||
44 | struct vnic_devcmd_notify notify_copy; | ||
45 | dma_addr_t notify_pa; | ||
46 | u32 *linkstatus; | ||
47 | dma_addr_t linkstatus_pa; | ||
48 | struct vnic_stats *stats; | ||
49 | dma_addr_t stats_pa; | ||
50 | struct vnic_devcmd_fw_info *fw_info; | ||
51 | dma_addr_t fw_info_pa; | ||
52 | }; | ||
53 | |||
54 | #define VNIC_MAX_RES_HDR_SIZE \ | ||
55 | (sizeof(struct vnic_resource_header) + \ | ||
56 | sizeof(struct vnic_resource) * RES_TYPE_MAX) | ||
57 | #define VNIC_RES_STRIDE 128 | ||
58 | |||
59 | void *vnic_dev_priv(struct vnic_dev *vdev) | ||
60 | { | ||
61 | return vdev->priv; | ||
62 | } | ||
63 | |||
64 | static int vnic_dev_discover_res(struct vnic_dev *vdev, | ||
65 | struct vnic_dev_bar *bar) | ||
66 | { | ||
67 | struct vnic_resource_header __iomem *rh; | ||
68 | struct vnic_resource __iomem *r; | ||
69 | u8 type; | ||
70 | |||
71 | if (bar->len < VNIC_MAX_RES_HDR_SIZE) { | ||
72 | printk(KERN_ERR "vNIC BAR0 res hdr length error\n"); | ||
73 | return -EINVAL; | ||
74 | } | ||
75 | |||
76 | rh = bar->vaddr; | ||
77 | if (!rh) { | ||
78 | printk(KERN_ERR "vNIC BAR0 res hdr not mem-mapped\n"); | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | if (ioread32(&rh->magic) != VNIC_RES_MAGIC || | ||
83 | ioread32(&rh->version) != VNIC_RES_VERSION) { | ||
84 | printk(KERN_ERR "vNIC BAR0 res magic/version error " | ||
85 | "exp (%lx/%lx) curr (%x/%x)\n", | ||
86 | VNIC_RES_MAGIC, VNIC_RES_VERSION, | ||
87 | ioread32(&rh->magic), ioread32(&rh->version)); | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | |||
91 | r = (struct vnic_resource __iomem *)(rh + 1); | ||
92 | |||
93 | while ((type = ioread8(&r->type)) != RES_TYPE_EOL) { | ||
94 | |||
95 | u8 bar_num = ioread8(&r->bar); | ||
96 | u32 bar_offset = ioread32(&r->bar_offset); | ||
97 | u32 count = ioread32(&r->count); | ||
98 | u32 len; | ||
99 | |||
100 | r++; | ||
101 | |||
102 | if (bar_num != 0) /* only mapping in BAR0 resources */ | ||
103 | continue; | ||
104 | |||
105 | switch (type) { | ||
106 | case RES_TYPE_WQ: | ||
107 | case RES_TYPE_RQ: | ||
108 | case RES_TYPE_CQ: | ||
109 | case RES_TYPE_INTR_CTRL: | ||
110 | /* each count is stride bytes long */ | ||
111 | len = count * VNIC_RES_STRIDE; | ||
112 | if (len + bar_offset > bar->len) { | ||
113 | printk(KERN_ERR "vNIC BAR0 resource %d " | ||
114 | "out-of-bounds, offset 0x%x + " | ||
115 | "size 0x%x > bar len 0x%lx\n", | ||
116 | type, bar_offset, | ||
117 | len, | ||
118 | bar->len); | ||
119 | return -EINVAL; | ||
120 | } | ||
121 | break; | ||
122 | case RES_TYPE_INTR_PBA_LEGACY: | ||
123 | case RES_TYPE_DEVCMD: | ||
124 | len = count; | ||
125 | break; | ||
126 | default: | ||
127 | continue; | ||
128 | } | ||
129 | |||
130 | vdev->res[type].count = count; | ||
131 | vdev->res[type].vaddr = (char __iomem *)bar->vaddr + bar_offset; | ||
132 | } | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | unsigned int vnic_dev_get_res_count(struct vnic_dev *vdev, | ||
138 | enum vnic_res_type type) | ||
139 | { | ||
140 | return vdev->res[type].count; | ||
141 | } | ||
142 | |||
143 | void __iomem *vnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type, | ||
144 | unsigned int index) | ||
145 | { | ||
146 | if (!vdev->res[type].vaddr) | ||
147 | return NULL; | ||
148 | |||
149 | switch (type) { | ||
150 | case RES_TYPE_WQ: | ||
151 | case RES_TYPE_RQ: | ||
152 | case RES_TYPE_CQ: | ||
153 | case RES_TYPE_INTR_CTRL: | ||
154 | return (char __iomem *)vdev->res[type].vaddr + | ||
155 | index * VNIC_RES_STRIDE; | ||
156 | default: | ||
157 | return (char __iomem *)vdev->res[type].vaddr; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | unsigned int vnic_dev_desc_ring_size(struct vnic_dev_ring *ring, | ||
162 | unsigned int desc_count, unsigned int desc_size) | ||
163 | { | ||
164 | /* The base address of the desc rings must be 512 byte aligned. | ||
165 | * Descriptor count is aligned to groups of 32 descriptors. A | ||
166 | * count of 0 means the maximum 4096 descriptors. Descriptor | ||
167 | * size is aligned to 16 bytes. | ||
168 | */ | ||
169 | |||
170 | unsigned int count_align = 32; | ||
171 | unsigned int desc_align = 16; | ||
172 | |||
173 | ring->base_align = 512; | ||
174 | |||
175 | if (desc_count == 0) | ||
176 | desc_count = 4096; | ||
177 | |||
178 | ring->desc_count = ALIGN(desc_count, count_align); | ||
179 | |||
180 | ring->desc_size = ALIGN(desc_size, desc_align); | ||
181 | |||
182 | ring->size = ring->desc_count * ring->desc_size; | ||
183 | ring->size_unaligned = ring->size + ring->base_align; | ||
184 | |||
185 | return ring->size_unaligned; | ||
186 | } | ||
187 | |||
188 | void vnic_dev_clear_desc_ring(struct vnic_dev_ring *ring) | ||
189 | { | ||
190 | memset(ring->descs, 0, ring->size); | ||
191 | } | ||
192 | |||
193 | int vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring, | ||
194 | unsigned int desc_count, unsigned int desc_size) | ||
195 | { | ||
196 | vnic_dev_desc_ring_size(ring, desc_count, desc_size); | ||
197 | |||
198 | ring->descs_unaligned = pci_alloc_consistent(vdev->pdev, | ||
199 | ring->size_unaligned, | ||
200 | &ring->base_addr_unaligned); | ||
201 | |||
202 | if (!ring->descs_unaligned) { | ||
203 | printk(KERN_ERR | ||
204 | "Failed to allocate ring (size=%d), aborting\n", | ||
205 | (int)ring->size); | ||
206 | return -ENOMEM; | ||
207 | } | ||
208 | |||
209 | ring->base_addr = ALIGN(ring->base_addr_unaligned, | ||
210 | ring->base_align); | ||
211 | ring->descs = (u8 *)ring->descs_unaligned + | ||
212 | (ring->base_addr - ring->base_addr_unaligned); | ||
213 | |||
214 | vnic_dev_clear_desc_ring(ring); | ||
215 | |||
216 | ring->desc_avail = ring->desc_count - 1; | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | void vnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring) | ||
222 | { | ||
223 | if (ring->descs) { | ||
224 | pci_free_consistent(vdev->pdev, | ||
225 | ring->size_unaligned, | ||
226 | ring->descs_unaligned, | ||
227 | ring->base_addr_unaligned); | ||
228 | ring->descs = NULL; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, | ||
233 | u64 *a0, u64 *a1, int wait) | ||
234 | { | ||
235 | struct vnic_devcmd __iomem *devcmd = vdev->devcmd; | ||
236 | int delay; | ||
237 | u32 status; | ||
238 | int dev_cmd_err[] = { | ||
239 | /* convert from fw's version of error.h to host's version */ | ||
240 | 0, /* ERR_SUCCESS */ | ||
241 | EINVAL, /* ERR_EINVAL */ | ||
242 | EFAULT, /* ERR_EFAULT */ | ||
243 | EPERM, /* ERR_EPERM */ | ||
244 | EBUSY, /* ERR_EBUSY */ | ||
245 | }; | ||
246 | int err; | ||
247 | |||
248 | status = ioread32(&devcmd->status); | ||
249 | if (status & STAT_BUSY) { | ||
250 | printk(KERN_ERR "Busy devcmd %d\n", _CMD_N(cmd)); | ||
251 | return -EBUSY; | ||
252 | } | ||
253 | |||
254 | if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) { | ||
255 | writeq(*a0, &devcmd->args[0]); | ||
256 | writeq(*a1, &devcmd->args[1]); | ||
257 | wmb(); | ||
258 | } | ||
259 | |||
260 | iowrite32(cmd, &devcmd->cmd); | ||
261 | |||
262 | if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT)) | ||
263 | return 0; | ||
264 | |||
265 | for (delay = 0; delay < wait; delay++) { | ||
266 | |||
267 | udelay(100); | ||
268 | |||
269 | status = ioread32(&devcmd->status); | ||
270 | if (!(status & STAT_BUSY)) { | ||
271 | |||
272 | if (status & STAT_ERROR) { | ||
273 | err = dev_cmd_err[(int)readq(&devcmd->args[0])]; | ||
274 | printk(KERN_ERR "Error %d devcmd %d\n", | ||
275 | err, _CMD_N(cmd)); | ||
276 | return -err; | ||
277 | } | ||
278 | |||
279 | if (_CMD_DIR(cmd) & _CMD_DIR_READ) { | ||
280 | rmb(); | ||
281 | *a0 = readq(&devcmd->args[0]); | ||
282 | *a1 = readq(&devcmd->args[1]); | ||
283 | } | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | printk(KERN_ERR "Timedout devcmd %d\n", _CMD_N(cmd)); | ||
290 | return -ETIMEDOUT; | ||
291 | } | ||
292 | |||
293 | int vnic_dev_fw_info(struct vnic_dev *vdev, | ||
294 | struct vnic_devcmd_fw_info **fw_info) | ||
295 | { | ||
296 | u64 a0, a1 = 0; | ||
297 | int wait = 1000; | ||
298 | int err = 0; | ||
299 | |||
300 | if (!vdev->fw_info) { | ||
301 | vdev->fw_info = pci_alloc_consistent(vdev->pdev, | ||
302 | sizeof(struct vnic_devcmd_fw_info), | ||
303 | &vdev->fw_info_pa); | ||
304 | if (!vdev->fw_info) | ||
305 | return -ENOMEM; | ||
306 | |||
307 | a0 = vdev->fw_info_pa; | ||
308 | |||
309 | /* only get fw_info once and cache it */ | ||
310 | err = vnic_dev_cmd(vdev, CMD_MCPU_FW_INFO, &a0, &a1, wait); | ||
311 | } | ||
312 | |||
313 | *fw_info = vdev->fw_info; | ||
314 | |||
315 | return err; | ||
316 | } | ||
317 | |||
318 | int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size, | ||
319 | void *value) | ||
320 | { | ||
321 | u64 a0, a1; | ||
322 | int wait = 1000; | ||
323 | int err; | ||
324 | |||
325 | a0 = offset; | ||
326 | a1 = size; | ||
327 | |||
328 | err = vnic_dev_cmd(vdev, CMD_DEV_SPEC, &a0, &a1, wait); | ||
329 | |||
330 | switch (size) { | ||
331 | case 1: *(u8 *)value = (u8)a0; break; | ||
332 | case 2: *(u16 *)value = (u16)a0; break; | ||
333 | case 4: *(u32 *)value = (u32)a0; break; | ||
334 | case 8: *(u64 *)value = a0; break; | ||
335 | default: BUG(); break; | ||
336 | } | ||
337 | |||
338 | return err; | ||
339 | } | ||
340 | |||
341 | int vnic_dev_stats_clear(struct vnic_dev *vdev) | ||
342 | { | ||
343 | u64 a0 = 0, a1 = 0; | ||
344 | int wait = 1000; | ||
345 | return vnic_dev_cmd(vdev, CMD_STATS_CLEAR, &a0, &a1, wait); | ||
346 | } | ||
347 | |||
348 | int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats) | ||
349 | { | ||
350 | u64 a0, a1; | ||
351 | int wait = 1000; | ||
352 | |||
353 | if (!vdev->stats) { | ||
354 | vdev->stats = pci_alloc_consistent(vdev->pdev, | ||
355 | sizeof(struct vnic_stats), &vdev->stats_pa); | ||
356 | if (!vdev->stats) | ||
357 | return -ENOMEM; | ||
358 | } | ||
359 | |||
360 | *stats = vdev->stats; | ||
361 | a0 = vdev->stats_pa; | ||
362 | a1 = sizeof(struct vnic_stats); | ||
363 | |||
364 | return vnic_dev_cmd(vdev, CMD_STATS_DUMP, &a0, &a1, wait); | ||
365 | } | ||
366 | |||
367 | int vnic_dev_close(struct vnic_dev *vdev) | ||
368 | { | ||
369 | u64 a0 = 0, a1 = 0; | ||
370 | int wait = 1000; | ||
371 | return vnic_dev_cmd(vdev, CMD_CLOSE, &a0, &a1, wait); | ||
372 | } | ||
373 | |||
374 | int vnic_dev_enable(struct vnic_dev *vdev) | ||
375 | { | ||
376 | u64 a0 = 0, a1 = 0; | ||
377 | int wait = 1000; | ||
378 | return vnic_dev_cmd(vdev, CMD_ENABLE, &a0, &a1, wait); | ||
379 | } | ||
380 | |||
381 | int vnic_dev_disable(struct vnic_dev *vdev) | ||
382 | { | ||
383 | u64 a0 = 0, a1 = 0; | ||
384 | int wait = 1000; | ||
385 | return vnic_dev_cmd(vdev, CMD_DISABLE, &a0, &a1, wait); | ||
386 | } | ||
387 | |||
388 | int vnic_dev_open(struct vnic_dev *vdev, int arg) | ||
389 | { | ||
390 | u64 a0 = (u32)arg, a1 = 0; | ||
391 | int wait = 1000; | ||
392 | return vnic_dev_cmd(vdev, CMD_OPEN, &a0, &a1, wait); | ||
393 | } | ||
394 | |||
395 | int vnic_dev_open_done(struct vnic_dev *vdev, int *done) | ||
396 | { | ||
397 | u64 a0 = 0, a1 = 0; | ||
398 | int wait = 1000; | ||
399 | int err; | ||
400 | |||
401 | *done = 0; | ||
402 | |||
403 | err = vnic_dev_cmd(vdev, CMD_OPEN_STATUS, &a0, &a1, wait); | ||
404 | if (err) | ||
405 | return err; | ||
406 | |||
407 | *done = (a0 == 0); | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) | ||
413 | { | ||
414 | u64 a0 = (u32)arg, a1 = 0; | ||
415 | int wait = 1000; | ||
416 | return vnic_dev_cmd(vdev, CMD_SOFT_RESET, &a0, &a1, wait); | ||
417 | } | ||
418 | |||
419 | int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) | ||
420 | { | ||
421 | u64 a0 = 0, a1 = 0; | ||
422 | int wait = 1000; | ||
423 | int err; | ||
424 | |||
425 | *done = 0; | ||
426 | |||
427 | err = vnic_dev_cmd(vdev, CMD_SOFT_RESET_STATUS, &a0, &a1, wait); | ||
428 | if (err) | ||
429 | return err; | ||
430 | |||
431 | *done = (a0 == 0); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | int vnic_dev_hang_notify(struct vnic_dev *vdev) | ||
437 | { | ||
438 | u64 a0, a1; | ||
439 | int wait = 1000; | ||
440 | return vnic_dev_cmd(vdev, CMD_HANG_NOTIFY, &a0, &a1, wait); | ||
441 | } | ||
442 | |||
443 | int vnic_dev_mac_addr(struct vnic_dev *vdev, u8 *mac_addr) | ||
444 | { | ||
445 | u64 a0, a1; | ||
446 | int wait = 1000; | ||
447 | int err, i; | ||
448 | |||
449 | for (i = 0; i < ETH_ALEN; i++) | ||
450 | mac_addr[i] = 0; | ||
451 | |||
452 | err = vnic_dev_cmd(vdev, CMD_MAC_ADDR, &a0, &a1, wait); | ||
453 | if (err) | ||
454 | return err; | ||
455 | |||
456 | for (i = 0; i < ETH_ALEN; i++) | ||
457 | mac_addr[i] = ((u8 *)&a0)[i]; | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, | ||
463 | int broadcast, int promisc, int allmulti) | ||
464 | { | ||
465 | u64 a0, a1 = 0; | ||
466 | int wait = 1000; | ||
467 | int err; | ||
468 | |||
469 | a0 = (directed ? CMD_PFILTER_DIRECTED : 0) | | ||
470 | (multicast ? CMD_PFILTER_MULTICAST : 0) | | ||
471 | (broadcast ? CMD_PFILTER_BROADCAST : 0) | | ||
472 | (promisc ? CMD_PFILTER_PROMISCUOUS : 0) | | ||
473 | (allmulti ? CMD_PFILTER_ALL_MULTICAST : 0); | ||
474 | |||
475 | err = vnic_dev_cmd(vdev, CMD_PACKET_FILTER, &a0, &a1, wait); | ||
476 | if (err) | ||
477 | printk(KERN_ERR "Can't set packet filter\n"); | ||
478 | } | ||
479 | |||
480 | void vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr) | ||
481 | { | ||
482 | u64 a0 = 0, a1 = 0; | ||
483 | int wait = 1000; | ||
484 | int err; | ||
485 | int i; | ||
486 | |||
487 | for (i = 0; i < ETH_ALEN; i++) | ||
488 | ((u8 *)&a0)[i] = addr[i]; | ||
489 | |||
490 | err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait); | ||
491 | if (err) | ||
492 | printk(KERN_ERR | ||
493 | "Can't add addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n", | ||
494 | addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], | ||
495 | err); | ||
496 | } | ||
497 | |||
498 | void vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr) | ||
499 | { | ||
500 | u64 a0 = 0, a1 = 0; | ||
501 | int wait = 1000; | ||
502 | int err; | ||
503 | int i; | ||
504 | |||
505 | for (i = 0; i < ETH_ALEN; i++) | ||
506 | ((u8 *)&a0)[i] = addr[i]; | ||
507 | |||
508 | err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a0, &a1, wait); | ||
509 | if (err) | ||
510 | printk(KERN_ERR | ||
511 | "Can't del addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n", | ||
512 | addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], | ||
513 | err); | ||
514 | } | ||
515 | |||
516 | int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr) | ||
517 | { | ||
518 | u64 a0, a1; | ||
519 | int wait = 1000; | ||
520 | |||
521 | if (!vdev->notify) { | ||
522 | vdev->notify = pci_alloc_consistent(vdev->pdev, | ||
523 | sizeof(struct vnic_devcmd_notify), | ||
524 | &vdev->notify_pa); | ||
525 | if (!vdev->notify) | ||
526 | return -ENOMEM; | ||
527 | } | ||
528 | |||
529 | a0 = vdev->notify_pa; | ||
530 | a1 = ((u64)intr << 32) & 0x0000ffff00000000ULL; | ||
531 | a1 += sizeof(struct vnic_devcmd_notify); | ||
532 | |||
533 | return vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait); | ||
534 | } | ||
535 | |||
536 | void vnic_dev_notify_unset(struct vnic_dev *vdev) | ||
537 | { | ||
538 | u64 a0, a1; | ||
539 | int wait = 1000; | ||
540 | |||
541 | a0 = 0; /* paddr = 0 to unset notify buffer */ | ||
542 | a1 = 0x0000ffff00000000ULL; /* intr num = -1 to unreg for intr */ | ||
543 | a1 += sizeof(struct vnic_devcmd_notify); | ||
544 | |||
545 | vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait); | ||
546 | } | ||
547 | |||
548 | static int vnic_dev_notify_ready(struct vnic_dev *vdev) | ||
549 | { | ||
550 | u32 *words; | ||
551 | unsigned int nwords = sizeof(struct vnic_devcmd_notify) / 4; | ||
552 | unsigned int i; | ||
553 | u32 csum; | ||
554 | |||
555 | if (!vdev->notify) | ||
556 | return 0; | ||
557 | |||
558 | do { | ||
559 | csum = 0; | ||
560 | memcpy(&vdev->notify_copy, vdev->notify, | ||
561 | sizeof(struct vnic_devcmd_notify)); | ||
562 | words = (u32 *)&vdev->notify_copy; | ||
563 | for (i = 1; i < nwords; i++) | ||
564 | csum += words[i]; | ||
565 | } while (csum != words[0]); | ||
566 | |||
567 | return 1; | ||
568 | } | ||
569 | |||
570 | int vnic_dev_init(struct vnic_dev *vdev, int arg) | ||
571 | { | ||
572 | u64 a0 = (u32)arg, a1 = 0; | ||
573 | int wait = 1000; | ||
574 | return vnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait); | ||
575 | } | ||
576 | |||
577 | int vnic_dev_link_status(struct vnic_dev *vdev) | ||
578 | { | ||
579 | if (vdev->linkstatus) | ||
580 | return *vdev->linkstatus; | ||
581 | |||
582 | if (!vnic_dev_notify_ready(vdev)) | ||
583 | return 0; | ||
584 | |||
585 | return vdev->notify_copy.link_state; | ||
586 | } | ||
587 | |||
588 | u32 vnic_dev_port_speed(struct vnic_dev *vdev) | ||
589 | { | ||
590 | if (!vnic_dev_notify_ready(vdev)) | ||
591 | return 0; | ||
592 | |||
593 | return vdev->notify_copy.port_speed; | ||
594 | } | ||
595 | |||
596 | u32 vnic_dev_msg_lvl(struct vnic_dev *vdev) | ||
597 | { | ||
598 | if (!vnic_dev_notify_ready(vdev)) | ||
599 | return 0; | ||
600 | |||
601 | return vdev->notify_copy.msglvl; | ||
602 | } | ||
603 | |||
604 | u32 vnic_dev_mtu(struct vnic_dev *vdev) | ||
605 | { | ||
606 | if (!vnic_dev_notify_ready(vdev)) | ||
607 | return 0; | ||
608 | |||
609 | return vdev->notify_copy.mtu; | ||
610 | } | ||
611 | |||
612 | void vnic_dev_set_intr_mode(struct vnic_dev *vdev, | ||
613 | enum vnic_dev_intr_mode intr_mode) | ||
614 | { | ||
615 | vdev->intr_mode = intr_mode; | ||
616 | } | ||
617 | |||
618 | enum vnic_dev_intr_mode vnic_dev_get_intr_mode( | ||
619 | struct vnic_dev *vdev) | ||
620 | { | ||
621 | return vdev->intr_mode; | ||
622 | } | ||
623 | |||
624 | void vnic_dev_unregister(struct vnic_dev *vdev) | ||
625 | { | ||
626 | if (vdev) { | ||
627 | if (vdev->notify) | ||
628 | pci_free_consistent(vdev->pdev, | ||
629 | sizeof(struct vnic_devcmd_notify), | ||
630 | vdev->notify, | ||
631 | vdev->notify_pa); | ||
632 | if (vdev->linkstatus) | ||
633 | pci_free_consistent(vdev->pdev, | ||
634 | sizeof(u32), | ||
635 | vdev->linkstatus, | ||
636 | vdev->linkstatus_pa); | ||
637 | if (vdev->stats) | ||
638 | pci_free_consistent(vdev->pdev, | ||
639 | sizeof(struct vnic_dev), | ||
640 | vdev->stats, vdev->stats_pa); | ||
641 | if (vdev->fw_info) | ||
642 | pci_free_consistent(vdev->pdev, | ||
643 | sizeof(struct vnic_devcmd_fw_info), | ||
644 | vdev->fw_info, vdev->fw_info_pa); | ||
645 | kfree(vdev); | ||
646 | } | ||
647 | } | ||
648 | |||
649 | struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev, | ||
650 | void *priv, struct pci_dev *pdev, struct vnic_dev_bar *bar) | ||
651 | { | ||
652 | if (!vdev) { | ||
653 | vdev = kzalloc(sizeof(struct vnic_dev), GFP_ATOMIC); | ||
654 | if (!vdev) | ||
655 | return NULL; | ||
656 | } | ||
657 | |||
658 | vdev->priv = priv; | ||
659 | vdev->pdev = pdev; | ||
660 | |||
661 | if (vnic_dev_discover_res(vdev, bar)) | ||
662 | goto err_out; | ||
663 | |||
664 | vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0); | ||
665 | if (!vdev->devcmd) | ||
666 | goto err_out; | ||
667 | |||
668 | return vdev; | ||
669 | |||
670 | err_out: | ||
671 | vnic_dev_unregister(vdev); | ||
672 | return NULL; | ||
673 | } | ||
674 | |||