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