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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Driver for handling Serengeti I/O SRAM
29 * for Solaris <-> SC comm.
30 */
31
32 #include <sys/types.h>
33 #include <sys/systm.h>
34 #include <sys/cpuvar.h>
35 #include <sys/dditypes.h>
36 #include <sys/sunndi.h>
37 #include <sys/param.h>
38 #include <sys/mutex.h>
39 #include <sys/sysmacros.h>
40 #include <sys/errno.h>
41 #include <sys/file.h>
42 #include <sys/kmem.h>
43 #include <sys/promif.h>
44 #include <sys/prom_plat.h>
45 #include <sys/sunddi.h>
46 #include <sys/ddi.h>
47
48 #include <sys/serengeti.h>
49 #include <sys/sgsbbc_priv.h>
50 #include <sys/sgsbbc_iosram_priv.h>
51 #include <sys/sgsbbc_mailbox_priv.h>
52
53 /*
54 * Local stuff
55 */
56 static int iosram_rw(int, uint32_t, caddr_t, uint32_t, int);
57 static int iosram_convert_key(char *);
58 static int iosram_switch_intr(void);
59 static int tunnel_init(sbbc_softstate_t *, tunnel_t *);
60 static void tunnel_fini(tunnel_t *);
61 static void tunnel_commit(sbbc_softstate_t *, tunnel_t *);
62 static void clear_break();
63
64 #define IOSRAM_GETB(tunnel, buf, sram, count) \
65 ddi_rep_get8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR)
66
67 #define IOSRAM_PUTB(tunnel, buf, sram, count) \
68 ddi_rep_put8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR)
69
70 #define IOSRAM_PUT(tunnel, sram, buf, size) \
71 /* CSTYLED */ \
72 ddi_put##size(tunnel->reg_handle, (uint##size##_t *)sram, \
73 /* CSTYLED */ \
74 *((uint##size##_t *)buf))
75
76 #define IOSRAM_GET(tunnel, sram, buf, size) \
77 /* CSTYLED */ \
78 *(uint##size##_t *)buf = ddi_get##size(tunnel->reg_handle, \
79 /* CSTYLED */ \
80 (uint##size##_t *)sram)
81
82 /*
83 * sgsbbc_iosram_is_chosen(struct sbbc_softstate *softsp)
84 *
85 * Looks up "chosen" node property to
86 * determine if it is the chosen IOSRAM.
87 */
88 int
sgsbbc_iosram_is_chosen(sbbc_softstate_t * softsp)89 sgsbbc_iosram_is_chosen(sbbc_softstate_t *softsp)
90 {
91 char pn[MAXNAMELEN];
92 char chosen_iosram[MAXNAMELEN];
93 int nodeid;
94 int chosen;
95 uint_t tunnel;
96 extern pnode_t chosen_nodeid;
97
98 ASSERT(chosen_nodeid);
99
100 nodeid = chosen_nodeid;
101 (void) prom_getprop(nodeid, "iosram", (caddr_t)&tunnel);
102
103 /*
104 * get the full OBP pathname of this node
105 */
106 if (prom_phandle_to_path((phandle_t)tunnel, chosen_iosram,
107 sizeof (chosen_iosram)) < 0) {
108 cmn_err(CE_NOTE, "prom_phandle_to_path(%x) failed\n", tunnel);
109 return (0);
110 }
111
112 SGSBBC_DBG_ALL("sgsbbc_iosram(%d): prom_phandle_to_path(%x) is '%s'\n",
113 softsp->sbbc_instance, nodeid, chosen_iosram);
114
115 (void) ddi_pathname(softsp->dip, pn);
116 SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ddi_pathname(%p) is '%s'\n",
117 softsp->sbbc_instance, (void *)softsp->dip, pn);
118
119 chosen = (strcmp(chosen_iosram, pn) == 0) ? 1 : 0;
120 SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance,
121 chosen? "MASTER" : "SLAVE");
122 SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance,
123 (chosen ? "MASTER" : "SLAVE"));
124
125 return (chosen);
126 }
127
128 void
iosram_init()129 iosram_init()
130 {
131 int i;
132
133 if ((master_iosram = kmem_zalloc(sizeof (struct chosen_iosram),
134 KM_NOSLEEP)) == NULL) {
135 prom_printf("Can't allocate space for Chosen IOSRAM\n");
136 panic("Can't allocate space for Chosen IOSRAM");
137 }
138
139 if ((master_iosram->tunnel = kmem_zalloc(sizeof (tunnel_t),
140 KM_NOSLEEP)) == NULL) {
141 prom_printf("Can't allocate space for tunnel\n");
142 panic("Can't allocate space for tunnel");
143 }
144
145 master_iosram->iosram_sbbc = NULL;
146
147 for (i = 0; i < SBBC_MAX_KEYS; i++) {
148 master_iosram->tunnel->tunnel_keys[i].key = 0;
149 master_iosram->tunnel->tunnel_keys[i].base = NULL;
150 master_iosram->tunnel->tunnel_keys[i].size = 0;
151 }
152
153 for (i = 0; i < SBBC_MAX_INTRS; i++)
154 master_iosram->intrs[i].sbbc_handler = NULL;
155
156 mutex_init(&master_iosram->iosram_lock, NULL, MUTEX_DEFAULT, NULL);
157 rw_init(&master_iosram->tunnel_lock, NULL, RW_DEFAULT, NULL);
158 }
159 void
iosram_fini()160 iosram_fini()
161 {
162 struct tunnel_key *tunnel;
163 int i;
164
165 rw_destroy(&master_iosram->tunnel_lock);
166 mutex_destroy(&master_iosram->iosram_lock);
167
168 /*
169 * destroy any tunnel maps
170 */
171 for (i = 0; i < SBBC_MAX_KEYS; i++) {
172 tunnel = &master_iosram->tunnel->tunnel_keys[i];
173 if (tunnel->base != NULL) {
174 ddi_regs_map_free(&tunnel->reg_handle);
175 tunnel->base = NULL;
176 }
177 }
178
179 kmem_free(master_iosram->tunnel, sizeof (tunnel_t));
180
181 kmem_free(master_iosram, sizeof (struct chosen_iosram));
182
183 master_iosram = NULL;
184 }
185
186 static void
check_iosram_ver(uint16_t version)187 check_iosram_ver(uint16_t version)
188 {
189 uint8_t max_ver = MAX_IOSRAM_TOC_VER;
190 uint8_t major_ver =
191 (version >> IOSRAM_TOC_VER_SHIFT) & IOSRAM_TOC_VER_MASK;
192
193 SGSBBC_DBG_ALL("IOSRAM TOC version: %d.%d\n", major_ver,
194 version & IOSRAM_TOC_VER_MASK);
195 SGSBBC_DBG_ALL("Max supported IOSRAM TOC version: %d\n", max_ver);
196 if (major_ver > max_ver) {
197 panic("Up-rev System Controller version.\n"
198 "You must restore an earlier revision of System "
199 "Controller firmware, or upgrade Solaris.\n"
200 "Please consult the System Controller release notice "
201 "for additional details.");
202 }
203 }
204
205 static void
tunnel_commit(sbbc_softstate_t * softsp,tunnel_t * new_tunnel)206 tunnel_commit(sbbc_softstate_t *softsp, tunnel_t *new_tunnel)
207 {
208 ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
209
210 master_iosram->iosram_sbbc = softsp;
211 master_iosram->tunnel = new_tunnel;
212 softsp->chosen = TRUE;
213
214 /*
215 * SBBC has pointer to interrupt handlers for simplicity
216 */
217 softsp->intr_hdlrs = master_iosram->intrs;
218 }
219
220 static int
tunnel_init(sbbc_softstate_t * softsp,tunnel_t * new_tunnel)221 tunnel_init(sbbc_softstate_t *softsp, tunnel_t *new_tunnel)
222 {
223 struct iosram_toc *toc = NULL;
224 int i, key;
225 struct tunnel_key *tunnel;
226 ddi_acc_handle_t toc_handle;
227 struct ddi_device_acc_attr attr;
228
229 ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
230
231 if ((softsp == (sbbc_softstate_t *)NULL) ||
232 (new_tunnel == (tunnel_t *)NULL)) {
233
234 return (DDI_FAILURE);
235 }
236
237 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
238 attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
239 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
240
241 SGSBBC_DBG_ALL("map in the IOSRAM TOC at offset %x\n",
242 softsp->sram_toc);
243
244 /*
245 * First map in the TOC, then set up the tunnel
246 */
247 if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
248 (caddr_t *)&toc,
249 SBBC_SRAM_OFFSET + softsp->sram_toc,
250 sizeof (struct iosram_toc),
251 &attr, &toc_handle) != DDI_SUCCESS) {
252 cmn_err(CE_WARN, "sbbc%d: unable to map SRAM "
253 "registers", ddi_get_instance(softsp->dip));
254 return (DDI_FAILURE);
255 }
256 SGSBBC_DBG_ALL("dip=%p mapped TOC %p\n", (void *)softsp->dip,
257 (void *)toc);
258
259 check_iosram_ver(toc->iosram_version);
260
261 for (i = 0; i < toc->iosram_tagno; i++) {
262 key = iosram_convert_key(toc->iosram_keys[i].key);
263 if ((key > 0) && (key < SBBC_MAX_KEYS)) {
264 tunnel = &new_tunnel->tunnel_keys[key];
265 tunnel->key = key;
266 tunnel->size = toc->iosram_keys[i].size;
267 /*
268 * map in the SRAM area using the offset
269 * from the base of SRAM + SRAM offset into
270 * the register property for the SBBC base
271 * address
272 */
273 if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
274 (caddr_t *)&tunnel->base,
275 SBBC_SRAM_OFFSET + toc->iosram_keys[i].offset,
276 toc->iosram_keys[i].size, &attr,
277 &tunnel->reg_handle) != DDI_SUCCESS) {
278 cmn_err(CE_WARN, "sbbc%d: unable to map SRAM "
279 "registers", ddi_get_instance(softsp->dip));
280 return (DDI_FAILURE);
281 }
282 SGSBBC_DBG_ALL("%d: key %s size %d offset %x addr %p\n",
283 i, toc->iosram_keys[i].key,
284 toc->iosram_keys[i].size,
285 toc->iosram_keys[i].offset,
286 (void *)tunnel->base);
287
288 }
289 }
290
291
292 if (toc != NULL) {
293 ddi_regs_map_free(&toc_handle);
294 }
295
296 /*
297 * Set up the 'interrupt reason' SRAM pointers
298 * for the SBBC interrupt handler
299 */
300 if (INVALID_KEY(new_tunnel, SBBC_SC_INTR_KEY)) {
301 /*
302 * Can't really do much if these are not here
303 */
304 prom_printf("No Interrupt Reason Fields set by SC\n");
305 cmn_err(CE_WARN, "No Interrupt Reason Fields set by SC");
306 return (DDI_FAILURE);
307 }
308
309 return (DDI_SUCCESS);
310 }
311
312 /*
313 * Unmap a tunnel
314 */
315 static void
tunnel_fini(tunnel_t * tunnel)316 tunnel_fini(tunnel_t *tunnel)
317 {
318 int i;
319 struct tunnel_key *tunnel_key;
320
321 /*
322 * Unmap the tunnel
323 */
324 for (i = 0; i < SBBC_MAX_KEYS; i++) {
325 tunnel_key = &tunnel->tunnel_keys[i];
326 if (tunnel_key->base != NULL) {
327 ddi_regs_map_free(&tunnel_key->reg_handle);
328 tunnel_key->base = NULL;
329 }
330 }
331 }
332
333 static void
clear_break()334 clear_break()
335 {
336 struct tunnel_key tunnel_key;
337 uint32_t *intr_in_reason;
338 ddi_acc_handle_t intr_in_handle;
339
340 ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
341
342 tunnel_key = master_iosram->tunnel->tunnel_keys[SBBC_SC_INTR_KEY];
343 intr_in_reason = (uint32_t *)tunnel_key.base;
344 intr_in_handle = tunnel_key.reg_handle;
345 ddi_put32(intr_in_handle, intr_in_reason,
346 ddi_get32(intr_in_handle, intr_in_reason) & ~SBBC_CONSOLE_BRK);
347 }
348
349 int
iosram_tunnel_init(sbbc_softstate_t * softsp)350 iosram_tunnel_init(sbbc_softstate_t *softsp)
351 {
352 int rc;
353
354 ASSERT(master_iosram);
355
356 mutex_enter(&master_iosram->iosram_lock);
357
358 if ((rc = tunnel_init(softsp, master_iosram->tunnel)) == DDI_SUCCESS) {
359 tunnel_commit(softsp, master_iosram->tunnel);
360 clear_break();
361 }
362
363
364 mutex_exit(&master_iosram->iosram_lock);
365
366 return (rc);
367 }
368
369 int
iosram_read(int key,uint32_t offset,caddr_t buf,uint32_t size)370 iosram_read(int key, uint32_t offset, caddr_t buf, uint32_t size)
371 {
372 return (iosram_rw(key, offset, buf, size, FREAD));
373 }
374
375 int
iosram_write(int key,uint32_t offset,caddr_t buf,uint32_t size)376 iosram_write(int key, uint32_t offset, caddr_t buf, uint32_t size)
377 {
378 return (iosram_rw(key, offset, buf, size, FWRITE));
379 }
380
381
382 static int
iosram_rw(int key,uint32_t offset,caddr_t buf,uint32_t size,int flag)383 iosram_rw(int key, uint32_t offset, caddr_t buf, uint32_t size, int flag)
384 {
385 struct tunnel_key *tunnel;
386 caddr_t sram_src;
387
388 /*
389 * Return right away if there is nothing to read/write.
390 */
391 if (size == 0)
392 return (0);
393
394 rw_enter(&master_iosram->tunnel_lock, RW_READER);
395
396 /*
397 * Key not matched ?
398 */
399 if (INVALID_KEY(master_iosram->tunnel, key)) {
400 rw_exit(&master_iosram->tunnel_lock);
401 return (ENXIO);
402 }
403
404 tunnel = &master_iosram->tunnel->tunnel_keys[key];
405 if ((offset + size) > tunnel->size) {
406 rw_exit(&master_iosram->tunnel_lock);
407 return (EFBIG);
408 }
409
410 sram_src = tunnel->base + offset;
411
412 /*
413 * Atomic reads/writes might be necessary for some clients.
414 * We assume that such clients could guarantee their buffers
415 * are aligned at the boundary of the request sizes. We also
416 * assume that the source/destination of such requests are
417 * aligned at the right boundaries in IOSRAM. If either
418 * condition fails, byte access is performed.
419 */
420 if (flag == FREAD) {
421 switch (size) {
422 case sizeof (uint16_t):
423 case sizeof (uint32_t):
424 case sizeof (uint64_t):
425 if (IS_P2ALIGNED(sram_src, size) &&
426 IS_P2ALIGNED(buf, size)) {
427
428 if (size == sizeof (uint16_t))
429 IOSRAM_GET(tunnel, sram_src, buf, 16);
430 else if (size == sizeof (uint32_t))
431 IOSRAM_GET(tunnel, sram_src, buf, 32);
432 else
433 IOSRAM_GET(tunnel, sram_src, buf, 64);
434 break;
435 }
436 /* FALLTHRU */
437 default:
438 IOSRAM_GETB(tunnel, (uint8_t *)buf,
439 (uint8_t *)sram_src, (size_t)size);
440 break;
441 }
442 } else {
443 switch (size) {
444 case sizeof (uint16_t):
445 case sizeof (uint32_t):
446 case sizeof (uint64_t):
447 if (IS_P2ALIGNED(sram_src, size) &&
448 IS_P2ALIGNED(buf, size)) {
449
450 if (size == sizeof (uint16_t))
451 IOSRAM_PUT(tunnel, sram_src, buf, 16);
452 else if (size == sizeof (uint32_t))
453 IOSRAM_PUT(tunnel, sram_src, buf, 32);
454 else
455 IOSRAM_PUT(tunnel, sram_src, buf, 64);
456 break;
457 }
458 /* FALLTHRU */
459 default:
460 IOSRAM_PUTB(tunnel, (uint8_t *)buf,
461 (uint8_t *)sram_src, (size_t)size);
462 break;
463 }
464 }
465
466 rw_exit(&master_iosram->tunnel_lock);
467 return (0);
468
469 }
470
471 int
iosram_size(int key)472 iosram_size(int key)
473 {
474 int size = -1;
475
476 rw_enter(&master_iosram->tunnel_lock, RW_READER);
477
478 /*
479 * Key not matched ?
480 */
481 if (!INVALID_KEY(master_iosram->tunnel, key))
482 size = master_iosram->tunnel->tunnel_keys[key].size;
483
484 rw_exit(&master_iosram->tunnel_lock);
485
486 return (size);
487 }
488
489 /*
490 * Generate an interrupt to the SC using the SBBC EPLD
491 *
492 * Note: intr_num can be multiple interrupts OR'ed together
493 */
494 int
iosram_send_intr(uint32_t intr_num)495 iosram_send_intr(uint32_t intr_num)
496 {
497
498 int rc = 0;
499 uint32_t intr_reason;
500 uint32_t intr_enabled;
501
502 /*
503 * Verify that we have already set up the master sbbc
504 */
505 if (master_iosram == NULL)
506 return (ENXIO);
507
508 /*
509 * Grab the lock to prevent tunnel switch in the middle
510 * of sending an interrupt.
511 */
512 mutex_enter(&master_iosram->iosram_lock);
513
514 if (master_iosram->iosram_sbbc == NULL) {
515 rc = ENXIO;
516 goto send_intr_exit;
517 }
518
519 if ((rc = sbbc_send_intr(master_iosram->iosram_sbbc, FALSE)) != 0) {
520 /*
521 * previous interrupts have not been cleared yet by the SC
522 */
523 goto send_intr_exit;
524 }
525
526 /*
527 * Set a bit in the interrupt reason field
528 * call back into the sbbc handler to hit the EPLD
529 *
530 * First check the interrupts enabled by the SC
531 */
532 if ((rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0,
533 (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
534
535 goto send_intr_exit;
536 }
537
538 if ((intr_enabled & intr_num) != intr_num) {
539 /*
540 * at least one of the interrupts is
541 * not enabled by the SC
542 */
543 rc = ENOTSUP;
544 goto send_intr_exit;
545 }
546
547 if ((rc = iosram_read(SBBC_INTR_SC_KEY, 0,
548 (caddr_t)&intr_reason, sizeof (intr_reason))) != 0) {
549
550 goto send_intr_exit;
551 }
552
553 if ((intr_reason & intr_num) == intr_num) {
554 /*
555 * All interrupts specified are already pending
556 */
557 rc = EBUSY;
558 goto send_intr_exit;
559 }
560
561 intr_reason |= intr_num;
562
563 if ((rc = iosram_write(SBBC_INTR_SC_KEY, 0,
564 (caddr_t)&intr_reason, sizeof (intr_reason))) != 0) {
565
566 goto send_intr_exit;
567 }
568
569 /*
570 * Hit the EPLD interrupt bit
571 */
572
573 rc = sbbc_send_intr(master_iosram->iosram_sbbc, TRUE);
574
575 send_intr_exit:
576
577 mutex_exit(&master_iosram->iosram_lock);
578
579 return (rc);
580 }
581
582 /*
583 * Register an interrupt handler
584 */
585 int
iosram_reg_intr(uint32_t intr_num,sbbc_intrfunc_t intr_handler,caddr_t arg,uint_t * state,kmutex_t * lock)586 iosram_reg_intr(uint32_t intr_num, sbbc_intrfunc_t intr_handler,
587 caddr_t arg, uint_t *state, kmutex_t *lock)
588 {
589 sbbc_softstate_t *softsp;
590 int rc = 0;
591 sbbc_intrs_t *intr;
592 int intr_no;
593 uint32_t intr_enabled;
594
595 /*
596 * Verify that we have already set up the master sbbc
597 */
598 if (master_iosram == NULL)
599 return (ENXIO);
600
601 /*
602 * determine which bit is this intr_num for ?
603 */
604 for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
605 if (intr_num == (1 << intr_no))
606 break;
607 }
608
609 /*
610 * Check the parameters
611 */
612 if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS) ||
613 (intr_handler == NULL) || (state == NULL) ||
614 (lock == NULL))
615 return (EINVAL);
616
617 mutex_enter(&master_iosram->iosram_lock);
618
619 if ((softsp = master_iosram->iosram_sbbc) == NULL) {
620 mutex_exit(&master_iosram->iosram_lock);
621 return (ENXIO);
622 }
623
624 mutex_enter(&softsp->sbbc_lock);
625
626 intr = &master_iosram->intrs[intr_no];
627
628 if (intr->sbbc_handler != (sbbc_intrfunc_t)NULL) {
629 rc = EBUSY;
630 goto reg_intr_exit;
631 }
632
633 intr->sbbc_handler = intr_handler;
634 intr->sbbc_arg = (void *)arg;
635 intr->sbbc_intr_state = state;
636 intr->sbbc_intr_lock = lock;
637 intr->sbbc_intr_next = (sbbc_intrs_t *)NULL;
638
639 /*
640 * we need to make sure that the mutex is for
641 * an ADAPTIVE lock, so call mutex_init() again with
642 * the sbbc iblock cookie
643 */
644 mutex_init(lock, NULL, MUTEX_DRIVER,
645 (void *)softsp->iblock);
646
647 if (ddi_add_softintr(softsp->dip, DDI_SOFTINT_HIGH,
648 &intr->sbbc_intr_id, NULL, NULL,
649 intr_handler, (caddr_t)arg) != DDI_SUCCESS) {
650
651 cmn_err(CE_WARN, "Can't add SBBC softint");
652 rc = EAGAIN;
653 goto reg_intr_exit;
654 }
655
656 /*
657 * Set the bit in the Interrupts Enabled Field for this
658 * interrupt
659 */
660 if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
661 (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
662
663 goto reg_intr_exit;
664 }
665
666 intr_enabled |= intr_num;
667
668 if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0,
669 (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
670
671 goto reg_intr_exit;
672 }
673
674 reg_intr_exit:
675
676 mutex_exit(&softsp->sbbc_lock);
677 mutex_exit(&master_iosram->iosram_lock);
678
679 return (rc);
680 }
681
682 /*
683 * Remove an interrupt handler
684 */
685 int
iosram_unreg_intr(uint32_t intr_num)686 iosram_unreg_intr(uint32_t intr_num)
687 {
688 sbbc_softstate_t *softsp;
689 int rc = 0;
690 sbbc_intrs_t *intr;
691 int intr_no;
692 uint32_t intr_enabled;
693
694 /*
695 * Verify that we have already set up the master sbbc
696 */
697 if (master_iosram == NULL)
698 return (ENXIO);
699
700 /*
701 * determine which bit is this intr_num for ?
702 */
703 for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
704 if (intr_num == (1 << intr_no))
705 break;
706 }
707
708 if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS))
709 return (EINVAL);
710
711 mutex_enter(&master_iosram->iosram_lock);
712
713 if ((softsp = master_iosram->iosram_sbbc) == NULL) {
714 mutex_exit(&master_iosram->iosram_lock);
715 return (ENXIO);
716 }
717
718 mutex_enter(&softsp->sbbc_lock);
719
720 intr = &master_iosram->intrs[intr_no];
721
722 /*
723 * No handler installed
724 */
725 if (intr->sbbc_handler == (sbbc_intrfunc_t)NULL) {
726 rc = EINVAL;
727 goto unreg_intr_exit;
728 }
729
730 /*
731 * Unset the bit in the Interrupts Enabled Field for this
732 * interrupt
733 */
734 if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
735 (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
736
737 goto unreg_intr_exit;
738 }
739
740 intr_enabled &= ~intr_num;
741
742 if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0,
743 (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
744
745 goto unreg_intr_exit;
746 }
747
748 /*
749 * If handler is running, wait until it's done.
750 * It won't get triggered again because we disabled it above.
751 * When we wait, drop sbbc_lock so other interrupt handlers
752 * can still run.
753 */
754 for (; ; ) {
755 mutex_enter(intr->sbbc_intr_lock);
756 if (*(intr->sbbc_intr_state) != SBBC_INTR_IDLE) {
757 mutex_exit(intr->sbbc_intr_lock);
758 mutex_exit(&softsp->sbbc_lock);
759 delay(drv_usectohz(10000));
760 mutex_enter(&softsp->sbbc_lock);
761 mutex_enter(intr->sbbc_intr_lock);
762 } else {
763 break;
764 }
765 mutex_exit(intr->sbbc_intr_lock);
766 }
767
768 if (intr->sbbc_intr_id)
769 ddi_remove_softintr(intr->sbbc_intr_id);
770
771 intr->sbbc_handler = (sbbc_intrfunc_t)NULL;
772 intr->sbbc_arg = (void *)NULL;
773 intr->sbbc_intr_id = 0;
774 intr->sbbc_intr_state = NULL;
775 intr->sbbc_intr_lock = (kmutex_t *)NULL;
776 intr->sbbc_intr_next = (sbbc_intrs_t *)NULL;
777
778 unreg_intr_exit:
779
780 mutex_exit(&softsp->sbbc_lock);
781 mutex_exit(&master_iosram->iosram_lock);
782
783 return (rc);
784 }
785
786 /*
787 * sgsbbc_iosram_switchfrom(softsp)
788 * Switch master tunnel away from the specified instance.
789 */
790 int
sgsbbc_iosram_switchfrom(struct sbbc_softstate * softsp)791 sgsbbc_iosram_switchfrom(struct sbbc_softstate *softsp)
792 {
793 struct sbbc_softstate *sp;
794 int rv = DDI_FAILURE;
795 int new_instance;
796
797 /*
798 * Find the candidate target of tunnel from the linked list.
799 */
800 mutex_enter(&chosen_lock);
801 ASSERT(sgsbbc_instances);
802
803 for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
804 if (softsp == sp)
805 continue;
806
807 if (sp->sbbc_state & SBBC_STATE_DETACH)
808 continue;
809 break;
810 }
811 if (sp == NULL) {
812 /* at least one IOSRAM should be attached */
813 rv = DDI_FAILURE;
814 } else {
815 /* Do the tunnel switch */
816 new_instance = ddi_get_instance(sp->dip);
817 rv = iosram_switch_tunnel(new_instance);
818 if (rv == DDI_SUCCESS) {
819 /* reset the chosen_iosram back ref */
820 sp->iosram = master_iosram;
821 }
822 }
823 mutex_exit(&chosen_lock);
824 return (rv);
825 }
826
827
828 /*
829 * Switch the tunnel to a different I/O board.
830 * At the moment, we will say that this is
831 * called with the instance of the SBBC to switch
832 * to. This will probably change, but as long as we
833 * can get a devinfo/softstate for the target SBBC it
834 * doesn't matter what the parameter is.
835 */
836 int
iosram_switch_tunnel(int instance)837 iosram_switch_tunnel(int instance)
838 {
839
840 sbbc_softstate_t *to_softsp, *from_softsp;
841 dev_info_t *pdip; /* parent dip */
842 tunnel_t *new_tunnel; /* new tunnel */
843 int portid;
844 uint_t node; /* node id to pass to OBP */
845 uint_t board; /* board number to pass to OBP */
846 int rc = DDI_SUCCESS;
847 static fn_t f = "iosram_switch_tunnel";
848
849 /* Check the firmware for tunnel switch support */
850 if (prom_test("SUNW,switch-tunnel") != 0) {
851 cmn_err(CE_WARN, "Firmware does not support tunnel switch");
852 return (DDI_FAILURE);
853 }
854
855 if ((master_iosram == NULL) || (master_mbox == NULL))
856 return (DDI_FAILURE);
857
858 if (!(to_softsp = sbbc_get_soft_state(instance)))
859 return (DDI_FAILURE);
860
861 /*
862 * create the new tunnel
863 */
864 if ((new_tunnel = kmem_zalloc(sizeof (tunnel_t), KM_NOSLEEP)) == NULL) {
865 cmn_err(CE_WARN, "Can't allocate space for new tunnel");
866 return (DDI_FAILURE);
867 }
868
869 pdip = ddi_get_parent(to_softsp->dip);
870 if ((portid = ddi_getprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
871 "portid", -1)) < 0) {
872
873 SGSBBC_DBG_ALL("%s: couldn't get portid\n", f);
874 return (DDI_FAILURE);
875 }
876
877 /*
878 * Compute node id and board number from port id
879 */
880 node = SG_PORTID_TO_NODEID(portid);
881 board = SG_IO_BD_PORTID_TO_BD_NUM(portid);
882
883 /*
884 * lock the chosen IOSRAM
885 */
886 mutex_enter(&master_iosram->iosram_lock);
887
888 if (master_iosram->iosram_sbbc == NULL) {
889 mutex_exit(&master_iosram->iosram_lock);
890 return (DDI_FAILURE);
891 }
892
893 /*
894 * If the target SBBC has not mapped in its
895 * register address space, do it now
896 */
897 mutex_enter(&to_softsp->sbbc_lock);
898 if (to_softsp->sbbc_regs == NULL) {
899 if (sbbc_map_regs(to_softsp) != DDI_SUCCESS) {
900 mutex_exit(&to_softsp->sbbc_lock);
901 mutex_exit(&master_iosram->iosram_lock);
902 return (DDI_FAILURE);
903 }
904 }
905
906 /*
907 * Get a pointer to the current sbbc
908 */
909 from_softsp = master_iosram->iosram_sbbc;
910
911 mutex_enter(&from_softsp->sbbc_lock);
912
913 /*
914 * Disable interrupts from the SC now
915 */
916 sbbc_disable_intr(from_softsp);
917
918 /*
919 * move SC interrupts to the new tunnel
920 */
921 if ((rc = sbbc_add_intr(to_softsp)) == DDI_FAILURE) {
922 cmn_err(CE_WARN, "Failed to add new interrupt handler");
923 } else if ((rc = tunnel_init(to_softsp, new_tunnel)) == DDI_FAILURE) {
924 cmn_err(CE_WARN, "Failed to initialize new tunnel");
925 ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock);
926 } else {
927 rw_enter(&master_iosram->tunnel_lock, RW_WRITER);
928
929 /*
930 * If OBP switch is unsuccessful, abort the switch.
931 */
932 if ((rc = prom_serengeti_tunnel_switch(node, board))
933 != DDI_SUCCESS) {
934
935 /*
936 * Restart other CPUs.
937 */
938 rw_exit(&master_iosram->tunnel_lock);
939
940 cmn_err(CE_WARN, "OBP failed to switch tunnel");
941
942 /*
943 * Remove interrupt
944 */
945 ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock);
946
947 /*
948 * Unmap new tunnel
949 */
950 tunnel_fini(new_tunnel);
951 } else {
952 tunnel_t *orig_tunnel;
953
954 orig_tunnel = master_iosram->tunnel;
955 tunnel_commit(to_softsp, new_tunnel);
956
957 rw_exit(&master_iosram->tunnel_lock);
958
959 /*
960 * Remove interrupt from original softsp
961 */
962 ddi_remove_intr(from_softsp->dip, 0,
963 from_softsp->iblock);
964 /*
965 * Unmap original tunnel
966 */
967 tunnel_fini(orig_tunnel);
968 kmem_free(orig_tunnel, sizeof (tunnel_t));
969
970 /*
971 * Move the softintrs to the new dip.
972 */
973 (void) iosram_switch_intr();
974 (void) sbbc_mbox_switch(to_softsp);
975
976 from_softsp->chosen = FALSE;
977
978 }
979 }
980
981 /*
982 * Enable interrupt.
983 */
984 sbbc_enable_intr(master_iosram->iosram_sbbc);
985
986 /*
987 * Unlock and get out
988 */
989 mutex_exit(&from_softsp->sbbc_lock);
990 mutex_exit(&to_softsp->sbbc_lock);
991 mutex_exit(&master_iosram->iosram_lock);
992
993 /*
994 * Call the interrupt handler directly in case
995 * we have missed an interrupt
996 */
997 (void) sbbc_intr_handler((caddr_t)master_iosram->iosram_sbbc);
998
999 if (rc != DDI_SUCCESS) {
1000 /*
1001 * Free up the new_tunnel
1002 */
1003 kmem_free(new_tunnel, sizeof (tunnel_t));
1004 cmn_err(CE_WARN, "Tunnel switch failed");
1005 }
1006
1007 return (rc);
1008
1009 }
1010
1011 /*
1012 * convert an alphanumeric OBP key to
1013 * our defined numeric keys
1014 */
1015 static int
iosram_convert_key(char * toc_key)1016 iosram_convert_key(char *toc_key)
1017 {
1018
1019 if (strcmp(toc_key, TOCKEY_DOMSTAT) == 0)
1020 return (SBBC_DOMAIN_KEY);
1021 if (strcmp(toc_key, TOCKEY_KEYSWPO) == 0)
1022 return (SBBC_KEYSWITCH_KEY);
1023 if (strcmp(toc_key, TOCKEY_TODDATA) == 0)
1024 return (SBBC_TOD_KEY);
1025 if (strcmp(toc_key, TOCKEY_SOLCONS) == 0)
1026 return (SBBC_CONSOLE_KEY);
1027 if (strcmp(toc_key, TOCKEY_SOLMBOX) == 0)
1028 return (SBBC_MAILBOX_KEY);
1029 if (strcmp(toc_key, TOCKEY_SOLSCIR) == 0)
1030 return (SBBC_INTR_SC_KEY);
1031 if (strcmp(toc_key, TOCKEY_SCSOLIR) == 0)
1032 return (SBBC_SC_INTR_KEY);
1033 if (strcmp(toc_key, TOCKEY_ENVINFO) == 0)
1034 return (SBBC_ENVCTRL_KEY);
1035 if (strcmp(toc_key, TOCKEY_SOLSCIE) == 0)
1036 return (SBBC_INTR_SC_ENABLED_KEY);
1037 if (strcmp(toc_key, TOCKEY_SCSOLIE) == 0)
1038 return (SBBC_SC_INTR_ENABLED_KEY);
1039 if (strcmp(toc_key, TOCKEY_SIGBLCK) == 0)
1040 return (SBBC_SIGBLCK_KEY);
1041
1042 /* Unknown key */
1043 return (-1);
1044 }
1045
1046 /*
1047 * Move the software interrupts from the old dip to the new dip
1048 * when doing tunnel switch.
1049 */
1050 static int
iosram_switch_intr()1051 iosram_switch_intr()
1052 {
1053 sbbc_intrs_t *intr;
1054 int intr_no;
1055 int rc = 0;
1056
1057 ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
1058
1059 for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
1060 intr = &master_iosram->intrs[intr_no];
1061
1062 if (intr->sbbc_intr_id) {
1063 ddi_remove_softintr(intr->sbbc_intr_id);
1064
1065 if (ddi_add_softintr(master_iosram->iosram_sbbc->dip,
1066 DDI_SOFTINT_HIGH,
1067 &intr->sbbc_intr_id, NULL, NULL,
1068 intr->sbbc_handler, intr->sbbc_arg)
1069 != DDI_SUCCESS) {
1070
1071 cmn_err(CE_WARN, "Can't add SBBC softint for "
1072 "interrupt %x", intr_no << 1);
1073 rc = EAGAIN;
1074 }
1075 }
1076 }
1077
1078 return (rc);
1079 }
1080