1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * nxge_intr.c
29 *
30 * This file manages the interrupts for a hybrid I/O (hio) device.
31 * In the future, it may manage interrupts for all Neptune-based
32 * devices.
33 *
34 */
35
36 #include <sys/nxge/nxge_impl.h>
37 #include <sys/nxge/nxge_hio.h>
38
39 /*
40 * External prototypes
41 */
42
43 /* The following function may be found in nxge_[t|r]xdma.c */
44 extern uint_t nxge_tx_intr(char *, char *);
45 extern uint_t nxge_rx_intr(char *, char *);
46
47 /*
48 * Local prototypes
49 */
50 static int nxge_intr_vec_find(nxge_t *, vpc_type_t, int);
51
52 /*
53 * nxge_intr_add
54 *
55 * Add <channel>'s interrupt.
56 *
57 * Arguments:
58 * nxge
59 * type Tx or Rx
60 * channel The channel whose interrupt we want to add.
61 *
62 * Notes:
63 * Add here means: add a handler, enable, & arm the interrupt.
64 *
65 * Context:
66 * Service domain
67 *
68 */
69 nxge_status_t
nxge_intr_add(nxge_t * nxge,vpc_type_t type,int channel)70 nxge_intr_add(
71 nxge_t *nxge,
72 vpc_type_t type,
73 int channel)
74 {
75 nxge_intr_t *interrupts; /* The global interrupt data. */
76 nxge_ldg_t *group; /* The logical device group data. */
77 nxge_ldv_t *ldvp;
78 ddi_intr_handler_t *inthandler;
79 int vector;
80 int status1, status2;
81
82 char c = (type == VP_BOUND_TX ? 'T' : 'R');
83
84 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_add"));
85
86 if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) {
87 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
88 "nxge_intr_add(%cDC %d): vector not found", c, channel));
89 return (NXGE_ERROR);
90 }
91
92 ldvp = &nxge->ldgvp->ldvp[vector];
93 group = ldvp->ldgp;
94
95 if (group->nldvs == 1) {
96 inthandler = group->ldvp->ldv_intr_handler;
97 } else if (group->nldvs > 1) {
98 inthandler = group->sys_intr_handler;
99 } else {
100 inthandler = NULL;
101 }
102
103 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
104
105 status1 = DDI_SUCCESS;
106
107 if ((status2 = ddi_intr_add_handler(interrupts->htable[vector],
108 inthandler, group->ldvp, nxge))
109 != DDI_SUCCESS) {
110 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): "
111 "ddi_intr_add_handler(%d) returned %s",
112 c, channel, vector, nxge_ddi_perror(status2)));
113 status1 += status2;
114 }
115
116 interrupts->intr_added++;
117
118 /* Enable the interrupt. */
119 if ((status2 = ddi_intr_enable(interrupts->htable[vector]))
120 != DDI_SUCCESS) {
121 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): "
122 "ddi_intr_enable(%d) returned %s",
123 c, channel, vector, nxge_ddi_perror(status2)));
124 status1 += status2;
125 }
126
127 if (status1 == DDI_SUCCESS) {
128 interrupts->intr_enabled = B_TRUE;
129
130 /* Finally, arm the interrupt. */
131 if (group->nldvs == 1) {
132 npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge);
133 (void) npi_intr_ldg_mgmt_set(handle, group->ldg,
134 B_TRUE, group->ldg_timer);
135 }
136 }
137
138 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_add"));
139
140 return (NXGE_OK);
141 }
142
143 /*
144 * nxge_intr_remove
145 *
146 * Remove <channel>'s interrupt.
147 *
148 * Arguments:
149 * nxge
150 * type Tx or Rx
151 * channel The channel whose interrupt we want to remove.
152 *
153 * Notes:
154 * Remove here means: disarm, disable, & remove the handler.
155 *
156 * Context:
157 * Service domain
158 *
159 */
160 nxge_status_t
nxge_intr_remove(nxge_t * nxge,vpc_type_t type,int channel)161 nxge_intr_remove(
162 nxge_t *nxge,
163 vpc_type_t type,
164 int channel)
165 {
166 nxge_intr_t *interrupts; /* The global interrupt data. */
167 nxge_ldg_t *group; /* The logical device group data. */
168 nxge_ldv_t *ldvp;
169
170 int vector;
171 int status1, status2;
172
173 char c = (type == VP_BOUND_TX ? 'T' : 'R');
174
175 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_remove"));
176
177 if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) {
178 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
179 "nxge_intr_remove(%cDC %d): vector not found", c, channel));
180 return (NXGE_ERROR);
181 }
182
183 ldvp = &nxge->ldgvp->ldvp[vector];
184 group = ldvp->ldgp;
185
186 /* Disarm the interrupt. */
187 if (group->nldvs == 1) {
188 npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge);
189 group->arm = B_FALSE;
190 (void) npi_intr_ldg_mgmt_set(handle, group->ldg,
191 B_TRUE, group->ldg_timer);
192 group->arm = B_TRUE; /* HIOXXX There IS a better way */
193 }
194
195 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
196
197 status1 = DDI_SUCCESS;
198
199 /* Disable the interrupt. */
200 if ((status2 = ddi_intr_disable(interrupts->htable[vector]))
201 != DDI_SUCCESS) {
202 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)"
203 ": ddi_intr_disable(%d) returned %s",
204 c, channel, vector, nxge_ddi_perror(status2)));
205 status1 += status2;
206 }
207
208 /* Remove the interrupt handler. */
209 if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector]))
210 != DDI_SUCCESS) {
211 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)"
212 ": ddi_intr_remove_handler(%d) returned %s",
213 c, channel, vector, nxge_ddi_perror(status2)));
214 status1 += status2;
215 }
216
217 if (status1 == DDI_SUCCESS) {
218 interrupts->intr_added--;
219 if (interrupts->intr_added == 0)
220 interrupts->intr_enabled = B_FALSE;
221 }
222
223 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_remove"));
224
225 return (NXGE_OK);
226 }
227
228 /*
229 * nxge_intr_vec_find
230 *
231 * Find the interrupt vector associated with <channel>.
232 *
233 * Arguments:
234 * nxge
235 * type Tx or Rx
236 * channel The channel whose vector we want to find.
237 *
238 * Notes:
239 *
240 * Context:
241 * Service domain
242 *
243 */
244 static
245 int
nxge_intr_vec_find(nxge_t * nxge,vpc_type_t type,int channel)246 nxge_intr_vec_find(
247 nxge_t *nxge,
248 vpc_type_t type,
249 int channel)
250 {
251 nxge_hw_pt_cfg_t *hardware;
252 nxge_ldgv_t *ldgvp;
253 nxge_ldv_t *ldvp;
254
255 int first, limit, vector;
256
257 NXGE_DEBUG_MSG((nxge, HIO_CTL,
258 "==> nxge_intr_vec_find(%cDC %d)",
259 type == VP_BOUND_TX ? 'T' : 'R', channel));
260
261 if (nxge->ldgvp == 0) {
262 NXGE_DEBUG_MSG((nxge, HIO_CTL,
263 "nxge_hio_intr_vec_find(%cDC %d): ldgvp == 0",
264 type == VP_BOUND_TX ? 'T' : 'R', channel));
265 return (-1);
266 }
267
268 hardware = &nxge->pt_config.hw_config;
269
270 first = hardware->ldg_chn_start;
271 if (type == VP_BOUND_TX) {
272 first += 8; /* HIOXXX N2/NIU hack */
273 limit = first + hardware->tdc.count;
274 } else {
275 limit = first + hardware->max_rdcs;
276 }
277
278 ldgvp = nxge->ldgvp;
279 for (vector = first; vector < limit; vector++) {
280 ldvp = &ldgvp->ldvp[vector];
281 if (ldvp->channel == channel)
282 break;
283 }
284
285 if (vector == limit) {
286 return (-1);
287 }
288
289 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_vec_find"));
290
291 return (vector);
292 }
293
294 /*
295 * ---------------------------------------------------------------------
296 * HIO-specific interrupt functions.
297 * ---------------------------------------------------------------------
298 */
299
300 /*
301 * nxge_hio_intr_add
302 *
303 * Add <channel>'s interrupt.
304 *
305 * Arguments:
306 * nxge
307 * type Tx or Rx
308 * channel The channel whose interrupt we want to remove.
309 *
310 * Notes:
311 *
312 * Context:
313 * Guest domain
314 *
315 */
316 nxge_status_t
nxge_hio_intr_add(nxge_t * nxge,vpc_type_t type,int channel)317 nxge_hio_intr_add(
318 nxge_t *nxge,
319 vpc_type_t type,
320 int channel)
321 {
322 nxge_hio_dc_t *dc; /* The relevant DMA channel data structure. */
323 nxge_intr_t *interrupts; /* The global interrupt data. */
324 nxge_ldg_t *group; /* The logical device group data. */
325 ddi_intr_handler_t *inthandler;
326
327 int vector; /* A shorthand variable */
328 int ddi_status; /* The response to ddi_intr_add_handler */
329
330 char c = (type == VP_BOUND_TX ? 'T' : 'R');
331
332 NXGE_DEBUG_MSG((nxge, HIO_CTL,
333 "==> nxge_hio_intr_add(%cDC %d)", c, channel));
334
335 if (nxge->ldgvp == 0) {
336 NXGE_DEBUG_MSG((nxge, HIO_CTL,
337 "nxge_hio_intr_add(%cDC %d): ldgvp == 0", c, channel));
338 return (NXGE_ERROR);
339 }
340
341 if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) {
342 NXGE_DEBUG_MSG((nxge, HIO_CTL,
343 "nxge_hio_intr_add: find(%s, %d) failed", c, channel));
344 return (NXGE_ERROR);
345 }
346
347 /* 'nxge_intr_type' is a bad name for this data structure. */
348 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
349
350 /* Set <vector> here to make the following code easier to read. */
351 vector = dc->ldg.vector;
352
353 group = &nxge->ldgvp->ldgp[vector];
354
355 if (group->nldvs == 1) {
356 inthandler = group->ldvp->ldv_intr_handler;
357 } else if (group->nldvs > 1) {
358 inthandler = group->sys_intr_handler;
359 } else {
360 inthandler = NULL;
361 }
362
363 if ((ddi_status = ddi_intr_add_handler(interrupts->htable[vector],
364 inthandler, group->ldvp, nxge))
365 != DDI_SUCCESS) {
366 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
367 "nxge_hio_intr_add(%cDC %d): "
368 "ddi_intr_add_handler(%d) returned %s",
369 c, channel, vector, nxge_ddi_perror(ddi_status)));
370 return (NXGE_ERROR);
371 }
372
373 interrupts->intr_added++;
374
375 /* Enable the interrupt. */
376 if ((ddi_status = ddi_intr_enable(interrupts->htable[vector]))
377 != DDI_SUCCESS) {
378 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
379 "nxge_hio_intr_add(%cDC %d): "
380 "ddi_intr_enable(%d) returned %s",
381 c, channel, vector, nxge_ddi_perror(ddi_status)));
382 return (NXGE_ERROR);
383 }
384
385 interrupts->intr_enabled = B_TRUE;
386
387 /*
388 * Note: RDC interrupts will be armed in nxge_m_start(). This
389 * prevents us from getting an interrupt before we are ready
390 * to process packets.
391 */
392 if (type == VP_BOUND_TX) {
393 nxge_hio_ldgimgn(nxge, group);
394 }
395
396 dc->interrupting = B_TRUE;
397
398 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_add"));
399
400 return (NXGE_OK);
401 }
402
403 /*
404 * nxge_hio_intr_remove
405 *
406 * Remove <channel>'s interrupt.
407 *
408 * Arguments:
409 * nxge
410 * type Tx or Rx
411 * channel The channel whose interrupt we want to remove.
412 *
413 * Notes:
414 *
415 * Context:
416 * Guest domain
417 *
418 */
419 nxge_status_t
nxge_hio_intr_remove(nxge_t * nxge,vpc_type_t type,int channel)420 nxge_hio_intr_remove(
421 nxge_t *nxge,
422 vpc_type_t type,
423 int channel)
424 {
425 nxge_hio_dc_t *dc; /* The relevant DMA channel data structure. */
426 nxge_intr_t *interrupts; /* The global interrupt data. */
427 nxge_ldg_t *group; /* The logical device group data. */
428
429 int vector; /* A shorthand variable */
430 int status1, status2;
431
432 char c = (type == VP_BOUND_TX ? 'T' : 'R');
433
434 NXGE_DEBUG_MSG((nxge, HIO_CTL,
435 "==> nxge_hio_intr_remove(%cDC %d)", c, channel));
436
437 if (nxge->ldgvp == 0) {
438 NXGE_DEBUG_MSG((nxge, HIO_CTL,
439 "nxge_hio_intr_remove(%cDC %d): ldgvp == 0", c, channel));
440 return (NXGE_ERROR);
441 }
442
443 if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) {
444 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
445 "nxge_hio_intr_remove(%cDC %d): DC FIND failed",
446 c, channel));
447 return (NXGE_ERROR);
448 }
449
450 if (dc->interrupting == B_FALSE) {
451 NXGE_DEBUG_MSG((nxge, HIO_CTL,
452 "nxge_hio_intr_remove(%cDC %d): interrupting == FALSE",
453 c, channel));
454 return (NXGE_OK);
455 }
456
457 /* 'nxge_intr_type' is a bad name for this data structure. */
458 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
459
460 /* Set <vector> here to make the following code easier to read. */
461 vector = dc->ldg.vector;
462
463 group = &nxge->ldgvp->ldgp[vector];
464
465 /* Disarm the interrupt. */
466 group->arm = B_FALSE;
467 nxge_hio_ldgimgn(nxge, group);
468 group->arm = B_TRUE; /* HIOXXX There IS a better way */
469
470 status1 = DDI_SUCCESS;
471
472 /* Disable the interrupt. */
473 if ((status2 = ddi_intr_disable(interrupts->htable[vector]))
474 != DDI_SUCCESS) {
475 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
476 "nxge_hio_intr_remove(%cDC %d): "
477 "ddi_intr_disable(%d) returned %s",
478 c, channel, vector, nxge_ddi_perror(status2)));
479 status1 += status2;
480 }
481
482 /* Remove the interrupt handler. */
483 if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector]))
484 != DDI_SUCCESS) {
485 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
486 "nxge_hio_intr_remove(%cDC %d): "
487 "ddi_intr_remove_handle(%d) returned %s",
488 c, channel, vector, nxge_ddi_perror(status2)));
489 status1 += status2;
490 }
491
492 if (status1 == DDI_SUCCESS) {
493 dc->interrupting = B_FALSE;
494
495 interrupts->intr_added--;
496 if (interrupts->intr_added == 0)
497 interrupts->intr_enabled = B_FALSE;
498 }
499
500 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_remove"));
501
502 return (NXGE_OK);
503 }
504
505 /*
506 * nxge_hio_intr_init
507 *
508 * Initialize interrupts in a guest domain.
509 *
510 * Arguments:
511 * nxge
512 *
513 * Notes:
514 *
515 * Context:
516 * Guest domain
517 *
518 */
519 nxge_status_t
nxge_hio_intr_init(nxge_t * nxge)520 nxge_hio_intr_init(
521 nxge_t *nxge)
522 {
523 int *prop_val;
524 uint_t prop_len;
525
526 nxge_intr_t *interrupts;
527
528 int intr_type, behavior;
529 int nintrs, navail, nactual;
530 int inum = 0;
531 int ddi_status = DDI_SUCCESS;
532
533 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
534 int i;
535
536 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_init"));
537
538 /* Look up the "interrupts" property. */
539 if ((ddi_prop_lookup_int_array(DDI_DEV_T_ANY, nxge->dip, 0,
540 "interrupts", &prop_val, &prop_len)) != DDI_PROP_SUCCESS) {
541 NXGE_ERROR_MSG((nxge, HIO_CTL,
542 "==> nxge_hio_intr_init(obp): no 'interrupts' property"));
543 return (NXGE_ERROR);
544 }
545
546 /*
547 * For each device assigned, the content of each interrupts
548 * property is its logical device group.
549 *
550 * Assignment of interrupts property is in the the following
551 * order:
552 *
553 * two receive channels
554 * two transmit channels
555 */
556 for (i = 0; i < prop_len; i++) {
557 hardware->ldg[i] = prop_val[i];
558 NXGE_DEBUG_MSG((nxge, HIO_CTL,
559 "==> nxge_hio_intr_init(obp): F%d: interrupt #%d, ldg %d",
560 nxge->function_num, i, hardware->ldg[i]));
561 }
562 ddi_prop_free(prop_val);
563
564 hardware->max_grpids = prop_len;
565 hardware->max_ldgs = prop_len;
566 hardware->ldg_chn_start = 0;
567
568 /* ----------------------------------------------------- */
569 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
570
571 interrupts->intr_registered = B_FALSE;
572 interrupts->intr_enabled = B_FALSE;
573 interrupts->start_inum = 0;
574
575 ddi_status = ddi_intr_get_supported_types(
576 nxge->dip, &interrupts->intr_types);
577 if (ddi_status != DDI_SUCCESS) {
578 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
579 "ddi_intr_get_supported_types() returned 0x%x, "
580 "types = 0x%x", ddi_status, interrupts->intr_types));
581 return (NXGE_ERROR);
582 }
583
584 NXGE_ERROR_MSG((nxge, HIO_CTL, "ddi_intr_get_supported_types() "
585 "returned 0x%x, types = 0x%x", ddi_status, interrupts->intr_types));
586
587 /* HIOXXX hack */
588 interrupts->intr_type = DDI_INTR_TYPE_FIXED;
589 /* HIOXXX hack */
590
591 intr_type = interrupts->intr_type;
592
593 ddi_status = ddi_intr_get_navail(nxge->dip, intr_type, &navail);
594 if (ddi_status != DDI_SUCCESS) {
595 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
596 "ddi_intr_get_navail() returned %s, navail: %d",
597 ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
598 "DDI_INTR_NOTFOUND", navail));
599 return (NXGE_ERROR);
600 }
601
602 NXGE_DEBUG_MSG((nxge, HIO_CTL,
603 "nxge_hio_intr_init: number of available interrupts: %d", navail));
604
605 ddi_status = ddi_intr_get_nintrs(nxge->dip, intr_type, &nintrs);
606 if (ddi_status != DDI_SUCCESS) {
607 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
608 "ddi_intr_get_nintrs() returned %s, nintrs: %d",
609 ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
610 "DDI_INTR_NOTFOUND", nintrs));
611 return (NXGE_ERROR);
612 }
613
614 NXGE_DEBUG_MSG((nxge, HIO_CTL,
615 "nxge_hio_intr_init: number of interrupts: %d", nintrs));
616
617 interrupts->intr_size = navail * sizeof (ddi_intr_handle_t);
618 interrupts->htable = kmem_alloc(interrupts->intr_size, KM_SLEEP);
619
620 /*
621 * When <behavior> is set to DDI_INTR_ALLOC_STRICT,
622 * ddi_intr_alloc() succeeds if and only if <navail>
623 * interrupts are are allocated. Otherwise, it fails.
624 */
625 behavior = ((intr_type == DDI_INTR_TYPE_FIXED) ?
626 DDI_INTR_ALLOC_STRICT : DDI_INTR_ALLOC_NORMAL);
627
628 ddi_status = ddi_intr_alloc(nxge->dip, interrupts->htable, intr_type,
629 inum, navail, &nactual, behavior);
630 if (ddi_status != DDI_SUCCESS) {
631 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
632 "ddi_intr_alloc() returned 0x%x%, "
633 "number allocated: %d", ddi_status, nactual));
634 return (NXGE_ERROR);
635 }
636
637 NXGE_DEBUG_MSG((nxge, HIO_CTL,
638 "nxge_hio_intr_init: number of interrupts allocated: %d", nactual));
639
640 /* <ninterrupts> is a dead variable: we may as well use it. */
641 hardware->ninterrupts = nactual;
642
643 /* FOI: Get the interrupt priority. */
644 if ((ddi_status = ddi_intr_get_pri(interrupts->htable[0],
645 (uint_t *)&interrupts->pri)) != DDI_SUCCESS) {
646 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
647 " ddi_intr_get_pri() failed: %d", ddi_status));
648 }
649
650 NXGE_DEBUG_MSG((nxge, HIO_CTL,
651 "nxge_hio_intr_init: interrupt priority: %d", interrupts->pri));
652
653 /* FOI: Get our interrupt capability flags. */
654 if ((ddi_status = ddi_intr_get_cap(interrupts->htable[0],
655 &interrupts->intr_cap)) != DDI_SUCCESS) {
656 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
657 "ddi_intr_get_cap() failed: %d", ddi_status));
658 }
659
660 NXGE_DEBUG_MSG((nxge, HIO_CTL,
661 "nxge_hio_intr_init: interrupt capabilities: %d",
662 interrupts->intr_cap));
663
664 interrupts->intr_registered = B_TRUE;
665
666 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_init"));
667
668 return (NXGE_OK);
669 }
670
671 /*
672 * nxge_hio_intr_uninit
673 *
674 * Uninitialize interrupts in a guest domain.
675 *
676 * Arguments:
677 * nxge
678 *
679 * Notes:
680 *
681 * Context:
682 * Guest domain
683 */
684 void
nxge_hio_intr_uninit(nxge_t * nxge)685 nxge_hio_intr_uninit(
686 nxge_t *nxge)
687 {
688 nxge_hw_pt_cfg_t *hardware;
689 nxge_intr_t *interrupts;
690 nxge_ldgv_t *control;
691 int i;
692
693 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_uninit"));
694
695 /* ----------------------------------------------------- */
696 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
697
698 /*
699 * If necessary, disable any currently active interrupts.
700 */
701 if (interrupts->intr_enabled) {
702 nxge_grp_set_t *set;
703 nxge_grp_t *group;
704 int channel;
705
706 set = &nxge->tx_set;
707 group = set->group[0]; /* Assumption: only one group! */
708 for (channel = 0; channel < NXGE_MAX_TDCS; channel++) {
709 if ((1 << channel) & group->map) {
710 (void) nxge_hio_intr_remove(
711 nxge, VP_BOUND_TX, channel);
712 }
713 }
714
715 set = &nxge->rx_set;
716 group = set->group[0]; /* Assumption: only one group! */
717 for (channel = 0; channel < NXGE_MAX_RDCS; channel++) {
718 if ((1 << channel) & group->map) {
719 (void) nxge_hio_intr_remove(
720 nxge, VP_BOUND_RX, channel);
721 }
722 }
723 }
724
725 /*
726 * Free all of our allocated interrupts.
727 */
728 hardware = &nxge->pt_config.hw_config;
729 for (i = 0; i < hardware->ninterrupts; i++) {
730 if (interrupts->htable[i])
731 (void) ddi_intr_free(interrupts->htable[i]);
732 interrupts->htable[i] = 0;
733 }
734
735 interrupts->intr_registered = B_FALSE;
736 KMEM_FREE(interrupts->htable, interrupts->intr_size);
737 interrupts->htable = NULL;
738
739 if (nxge->ldgvp == NULL)
740 goto nxge_hio_intr_uninit_exit;
741
742 control = nxge->ldgvp;
743 if (control->ldgp) {
744 KMEM_FREE(control->ldgp,
745 sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS);
746 control->ldgp = 0;
747 }
748
749 if (control->ldvp) {
750 KMEM_FREE(control->ldvp,
751 sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS);
752 control->ldvp = 0;
753 }
754
755 KMEM_FREE(control, sizeof (nxge_ldgv_t));
756 nxge->ldgvp = NULL;
757
758 nxge_hio_intr_uninit_exit:
759 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_uninit"));
760 }
761
762 /*
763 * nxge_hio_tdsv_add
764 *
765 * Add a transmit device interrupt.
766 *
767 * Arguments:
768 * nxge
769 * dc The TDC whose interrupt we're adding
770 *
771 * Notes:
772 *
773 * Context:
774 * Guest domain
775 */
776 static
777 hv_rv_t
nxge_hio_tdsv_add(nxge_t * nxge,nxge_hio_dc_t * dc)778 nxge_hio_tdsv_add(
779 nxge_t *nxge,
780 nxge_hio_dc_t *dc)
781 {
782 nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
783 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
784 nxhv_dc_fp_t *tx = &nhd->hio.tx;
785 hv_rv_t hv_rv;
786
787 if (tx->getinfo == 0) {
788 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
789 "nx_hio_tdsv_add: tx->getinfo absent"));
790 return (EINVAL);
791 }
792
793 /*
794 * Get the dma channel information.
795 */
796 hv_rv = (*tx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
797 &dc->ldg.ldsv);
798 if (hv_rv != 0) {
799 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
800 "nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv));
801 return (EIO);
802 }
803
804 NXGE_DEBUG_MSG((nxge, HIO_CTL,
805 "nx_hio_tdsv_add: VRgroup = %d, LDSV = %d",
806 (int)dc->ldg.index, (int)dc->ldg.ldsv));
807
808 if (hardware->tdc.count == 0) {
809 hardware->tdc.start = dc->channel;
810 }
811
812 hardware->tdc.count++;
813 hardware->tdc.owned++;
814
815 /*
816 * In version 1.0 of the hybrid I/O driver, there
817 * are eight interrupt vectors per VR.
818 *
819 * Vectors 0 - 3 are reserved for RDCs.
820 * Vectors 4 - 7 are reserved for TDCs.
821 */
822 dc->ldg.vector = (dc->ldg.ldsv % 2) + HIO_INTR_BLOCK_SIZE;
823 // Version 1.0 hack only!
824
825 return (0);
826 }
827
828 /*
829 * nxge_hio_rdsv_add
830 *
831 * Add a transmit device interrupt.
832 *
833 * Arguments:
834 * nxge
835 * dc The RDC whose interrupt we're adding
836 *
837 * Notes:
838 *
839 * Context:
840 * Guest domain
841 */
842 static
843 hv_rv_t
nxge_hio_rdsv_add(nxge_t * nxge,nxge_hio_dc_t * dc)844 nxge_hio_rdsv_add(
845 nxge_t *nxge,
846 nxge_hio_dc_t *dc)
847 {
848 nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
849 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
850 nxhv_dc_fp_t *rx = &nhd->hio.rx;
851 hv_rv_t hv_rv;
852
853 if (rx->getinfo == 0) {
854 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
855 "nx_hio_tdsv_add: rx->getinfo absent"));
856 return (EINVAL);
857 }
858
859 /*
860 * Get DMA channel information.
861 */
862 hv_rv = (*rx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
863 &dc->ldg.ldsv);
864 if (hv_rv != 0) {
865 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
866 "nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv));
867 return (EIO);
868 }
869
870 NXGE_DEBUG_MSG((nxge, HIO_CTL,
871 "nx_hio_rdsv_add: VRgroup = %d, LDSV = %d",
872 (int)dc->ldg.index, (int)dc->ldg.ldsv));
873
874 if (hardware->max_rdcs == 0) {
875 hardware->start_rdc = dc->channel;
876 hardware->def_rdc = dc->channel;
877 }
878
879 hardware->max_rdcs++;
880
881 /*
882 * In version 1.0 of the hybrid I/O driver, there
883 * are eight interrupt vectors per VR.
884 *
885 * Vectors 0 - 3 are reserved for RDCs.
886 */
887 dc->ldg.vector = (dc->ldg.ldsv % 2);
888 // Version 1.0 hack only!
889
890 return (0);
891 }
892
893 /*
894 * nxge_hio_ldsv_add
895 *
896 * Add a transmit or receive interrupt.
897 *
898 * Arguments:
899 * nxge
900 * dc The DMA channel whose interrupt we're adding
901 *
902 * Notes:
903 * Guest domains can only add interrupts for DMA channels.
904 * They cannot access the MAC, MIF, or SYSERR interrupts.
905 *
906 * Context:
907 * Guest domain
908 */
909 int
nxge_hio_ldsv_add(nxge_t * nxge,nxge_hio_dc_t * dc)910 nxge_hio_ldsv_add(nxge_t *nxge, nxge_hio_dc_t *dc)
911 {
912 nxge_ldgv_t *control;
913 nxge_ldg_t *group;
914 nxge_ldv_t *device;
915
916 if (dc->type == VP_BOUND_TX) {
917 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(TDC %d)",
918 dc->channel));
919 if (nxge_hio_tdsv_add(nxge, dc) != 0)
920 return (EIO);
921 } else {
922 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(RDC %d)",
923 dc->channel));
924 if (nxge_hio_rdsv_add(nxge, dc) != 0)
925 return (EIO);
926 }
927
928 dc->ldg.map |= (1 << dc->ldg.ldsv);
929
930 control = nxge->ldgvp;
931 if (control == NULL) {
932 control = KMEM_ZALLOC(sizeof (nxge_ldgv_t), KM_SLEEP);
933 nxge->ldgvp = control;
934 control->maxldgs = 1;
935 control->maxldvs = 1;
936 control->ldgp = KMEM_ZALLOC(
937 sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS, KM_SLEEP);
938 control->ldvp = KMEM_ZALLOC(
939 sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS, KM_SLEEP);
940 } else {
941 control->maxldgs++;
942 control->maxldvs++;
943 }
944
945 /*
946 * Initialize the logical device group data structure first.
947 */
948 group = &control->ldgp[dc->ldg.vector];
949
950 (void) memset(group, 0, sizeof (*group));
951
952 /*
953 * <hw_config.ldg> is a copy of the "interrupts" property.
954 */
955 group->ldg = nxge->pt_config.hw_config.ldg[dc->ldg.vector];
956 group->vldg_index = (uint8_t)dc->ldg.index;
957 /*
958 * Since <vldg_index> is a dead variable, I'm reusing
959 * it in Hybrid I/O to calculate the offset into the
960 * virtual PIO_LDSV space.
961 */
962
963 group->arm = B_TRUE;
964 group->ldg_timer = NXGE_TIMER_LDG;
965 group->func = nxge->function_num;
966 group->vector = dc->ldg.vector;
967 /*
968 * <intdata> appears to be a dead variable.
969 * Though it is not used anywhere in the driver,
970 * we'll set it anyway.
971 */
972 group->intdata = SID_DATA(group->func, group->vector);
973
974 group->sys_intr_handler = nxge_intr; /* HIOXXX Does this work? */
975 group->nxgep = nxge;
976
977 /*
978 * Initialize the logical device state vector next.
979 */
980 device = &control->ldvp[dc->ldg.ldsv];
981
982 device->ldg_assigned = group->ldg;
983 device->ldv = dc->ldg.ldsv;
984
985 if (dc->type == VP_BOUND_TX) {
986 device->is_txdma = B_TRUE;
987 device->is_rxdma = B_FALSE;
988 device->ldv_intr_handler = nxge_tx_intr;
989 } else {
990 device->is_rxdma = B_TRUE;
991 device->is_txdma = B_FALSE;
992 device->ldv_intr_handler = nxge_rx_intr;
993 }
994 device->is_mif = B_FALSE;
995 device->is_mac = B_FALSE;
996 device->is_syserr = B_FALSE;
997 device->use_timer = B_FALSE; /* Set to B_TRUE for syserr only. */
998
999 device->channel = dc->channel;
1000 device->vdma_index = dc->page;
1001 device->func = nxge->function_num;
1002 device->ldgp = group;
1003 device->ldv_flags = 0;
1004 device->ldv_ldf_masks = 0;
1005
1006 device->nxgep = nxge;
1007
1008 /*
1009 * This code seems to imply a strict 1-to-1 correspondence.
1010 */
1011 group->nldvs++;
1012 group->ldvp = device;
1013
1014 control->nldvs++;
1015 control->ldg_intrs++;
1016
1017 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_ldsv_add"));
1018
1019 return (0);
1020 }
1021
1022 /*
1023 * nxge_hio_ldsv_im
1024 *
1025 * Manage a VLDG's interrupts.
1026 *
1027 * Arguments:
1028 * nxge
1029 * group The VLDG to manage
1030 *
1031 * Notes:
1032 * There are 8 sets of 4 64-bit registers per VR, 1 per LDG.
1033 * That sums to 256 bytes of virtual PIO_LDSV space.
1034 *
1035 * VLDG0 starts at offset 0,
1036 * VLDG1 starts at offset 32, etc.
1037 *
1038 * Each set consists of 4 registers:
1039 * Logical Device State Vector 0. LDSV0
1040 * Logical Device State Vector 1. LDSV1
1041 * Logical Device State Vector 2. LDSV2
1042 * Logical Device Group Interrupt Management. LDGIMGN
1043 *
1044 * The first three (LDSVx) are read-only. The 4th register is the
1045 * LDGIMGN, the LDG Interrupt Management register, which is used to
1046 * arm the LDG, or set its timer.
1047 *
1048 * The offset to write to is calculated as follows:
1049 *
1050 * 0x2000 + (VLDG << 4) + offset, where:
1051 * VDLG is the virtual group, i.e., index of the LDG.
1052 * offset is the offset (alignment 8) of the register
1053 * to read or write.
1054 *
1055 * So, for example, if we wanted to arm the first TDC of VRx, we would
1056 * calculate the address as:
1057 *
1058 * 0x2000 + (0 << 4) + 0x18 = 0x18
1059 *
1060 * Context:
1061 * Guest domain
1062 *
1063 */
1064 void
nxge_hio_ldsv_im(nxge_t * nxge,nxge_ldg_t * group,pio_ld_op_t op,uint64_t * value)1065 nxge_hio_ldsv_im(
1066 /* Read any register in the PIO_LDSV space. */
1067 nxge_t *nxge,
1068 nxge_ldg_t *group,
1069 pio_ld_op_t op,
1070 uint64_t *value)
1071 {
1072 uint64_t offset = VLDG_OFFSET;
1073
1074 offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1075 offset += (op * sizeof (uint64_t)); /* 0, 8, 16, 24 */
1076
1077 NXGE_REG_RD64(nxge->npi_handle, offset, value);
1078 }
1079
1080 void
nxge_hio_ldgimgn(nxge_t * nxge,nxge_ldg_t * group)1081 nxge_hio_ldgimgn(
1082 /* Write the PIO_LDGIMGN register. */
1083 nxge_t *nxge,
1084 nxge_ldg_t *group)
1085 {
1086 uint64_t offset = VLDG_OFFSET;
1087 ldgimgm_t mgm;
1088
1089 offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1090 offset += (PIO_LDGIMGN * sizeof (uint64_t)); /* 24 */
1091
1092 mgm.value = 0;
1093 if (group->arm) {
1094 mgm.bits.ldw.arm = 1;
1095 mgm.bits.ldw.timer = group->ldg_timer;
1096 } else {
1097 mgm.bits.ldw.arm = 0;
1098 mgm.bits.ldw.timer = 0;
1099 }
1100 NXGE_REG_WR64(nxge->npi_handle, offset, mgm.value);
1101 }
1102