103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2103831d35Sstevel 2203831d35Sstevel /* 23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel /* 2803831d35Sstevel * Driver for handling Serengeti I/O SRAM 2903831d35Sstevel * for Solaris <-> SC comm. 3003831d35Sstevel */ 3103831d35Sstevel 3203831d35Sstevel #include <sys/types.h> 3303831d35Sstevel #include <sys/systm.h> 3403831d35Sstevel #include <sys/cpuvar.h> 3503831d35Sstevel #include <sys/dditypes.h> 3603831d35Sstevel #include <sys/sunndi.h> 3703831d35Sstevel #include <sys/param.h> 3803831d35Sstevel #include <sys/mutex.h> 3903831d35Sstevel #include <sys/sysmacros.h> 4003831d35Sstevel #include <sys/errno.h> 4103831d35Sstevel #include <sys/file.h> 4203831d35Sstevel #include <sys/kmem.h> 4303831d35Sstevel #include <sys/promif.h> 4403831d35Sstevel #include <sys/prom_plat.h> 4503831d35Sstevel #include <sys/sunddi.h> 4603831d35Sstevel #include <sys/ddi.h> 4703831d35Sstevel 4803831d35Sstevel #include <sys/serengeti.h> 4903831d35Sstevel #include <sys/sgsbbc_priv.h> 5003831d35Sstevel #include <sys/sgsbbc_iosram_priv.h> 5103831d35Sstevel #include <sys/sgsbbc_mailbox_priv.h> 5203831d35Sstevel 5303831d35Sstevel /* 5403831d35Sstevel * Local stuff 5503831d35Sstevel */ 5603831d35Sstevel static int iosram_rw(int, uint32_t, caddr_t, uint32_t, int); 5703831d35Sstevel static int iosram_convert_key(char *); 5803831d35Sstevel static int iosram_switch_intr(void); 5903831d35Sstevel static int tunnel_init(sbbc_softstate_t *, tunnel_t *); 6003831d35Sstevel static void tunnel_fini(tunnel_t *); 6103831d35Sstevel static void tunnel_commit(sbbc_softstate_t *, tunnel_t *); 6203831d35Sstevel static void clear_break(); 6303831d35Sstevel 6403831d35Sstevel #define IOSRAM_GETB(tunnel, buf, sram, count) \ 6503831d35Sstevel ddi_rep_get8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR) 6603831d35Sstevel 6703831d35Sstevel #define IOSRAM_PUTB(tunnel, buf, sram, count) \ 6803831d35Sstevel ddi_rep_put8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR) 6903831d35Sstevel 7003831d35Sstevel #define IOSRAM_PUT(tunnel, sram, buf, size) \ 7103831d35Sstevel /* CSTYLED */ \ 7203831d35Sstevel ddi_put##size(tunnel->reg_handle, (uint##size##_t *)sram, \ 7303831d35Sstevel /* CSTYLED */ \ 7403831d35Sstevel *((uint##size##_t *)buf)) 7503831d35Sstevel 7603831d35Sstevel #define IOSRAM_GET(tunnel, sram, buf, size) \ 7703831d35Sstevel /* CSTYLED */ \ 7803831d35Sstevel *(uint##size##_t *)buf = ddi_get##size(tunnel->reg_handle, \ 7903831d35Sstevel /* CSTYLED */ \ 8003831d35Sstevel (uint##size##_t *)sram) 8103831d35Sstevel 8203831d35Sstevel /* 8303831d35Sstevel * sgsbbc_iosram_is_chosen(struct sbbc_softstate *softsp) 8403831d35Sstevel * 8503831d35Sstevel * Looks up "chosen" node property to 8603831d35Sstevel * determine if it is the chosen IOSRAM. 8703831d35Sstevel */ 8803831d35Sstevel int 8903831d35Sstevel sgsbbc_iosram_is_chosen(sbbc_softstate_t *softsp) 9003831d35Sstevel { 9103831d35Sstevel char pn[MAXNAMELEN]; 9203831d35Sstevel char chosen_iosram[MAXNAMELEN]; 9303831d35Sstevel int nodeid; 9403831d35Sstevel int chosen; 9503831d35Sstevel uint_t tunnel; 9603831d35Sstevel extern pnode_t chosen_nodeid; 9703831d35Sstevel 9803831d35Sstevel ASSERT(chosen_nodeid); 9903831d35Sstevel 10003831d35Sstevel nodeid = chosen_nodeid; 10103831d35Sstevel (void) prom_getprop(nodeid, "iosram", (caddr_t)&tunnel); 10203831d35Sstevel 10303831d35Sstevel /* 10403831d35Sstevel * get the full OBP pathname of this node 10503831d35Sstevel */ 10603831d35Sstevel if (prom_phandle_to_path((phandle_t)tunnel, chosen_iosram, 10703831d35Sstevel sizeof (chosen_iosram)) < 0) { 10803831d35Sstevel cmn_err(CE_NOTE, "prom_phandle_to_path(%x) failed\n", tunnel); 10903831d35Sstevel return (0); 11003831d35Sstevel } 11103831d35Sstevel 11203831d35Sstevel SGSBBC_DBG_ALL("sgsbbc_iosram(%d): prom_phandle_to_path(%x) is '%s'\n", 11303831d35Sstevel softsp->sbbc_instance, nodeid, chosen_iosram); 11403831d35Sstevel 11503831d35Sstevel (void) ddi_pathname(softsp->dip, pn); 11603831d35Sstevel SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ddi_pathname(%p) is '%s'\n", 117*07d06da5SSurya Prakki softsp->sbbc_instance, (void *)softsp->dip, pn); 11803831d35Sstevel 11903831d35Sstevel chosen = (strcmp(chosen_iosram, pn) == 0) ? 1 : 0; 12003831d35Sstevel SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance, 12103831d35Sstevel chosen? "MASTER" : "SLAVE"); 12203831d35Sstevel SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance, 12303831d35Sstevel (chosen ? "MASTER" : "SLAVE")); 12403831d35Sstevel 12503831d35Sstevel return (chosen); 12603831d35Sstevel } 12703831d35Sstevel 12803831d35Sstevel void 12903831d35Sstevel iosram_init() 13003831d35Sstevel { 13103831d35Sstevel int i; 13203831d35Sstevel 13303831d35Sstevel if ((master_iosram = kmem_zalloc(sizeof (struct chosen_iosram), 13403831d35Sstevel KM_NOSLEEP)) == NULL) { 13503831d35Sstevel prom_printf("Can't allocate space for Chosen IOSRAM\n"); 13603831d35Sstevel panic("Can't allocate space for Chosen IOSRAM"); 13703831d35Sstevel } 13803831d35Sstevel 13903831d35Sstevel if ((master_iosram->tunnel = kmem_zalloc(sizeof (tunnel_t), 14003831d35Sstevel KM_NOSLEEP)) == NULL) { 14103831d35Sstevel prom_printf("Can't allocate space for tunnel\n"); 14203831d35Sstevel panic("Can't allocate space for tunnel"); 14303831d35Sstevel } 14403831d35Sstevel 14503831d35Sstevel master_iosram->iosram_sbbc = NULL; 14603831d35Sstevel 14703831d35Sstevel for (i = 0; i < SBBC_MAX_KEYS; i++) { 14803831d35Sstevel master_iosram->tunnel->tunnel_keys[i].key = 0; 14903831d35Sstevel master_iosram->tunnel->tunnel_keys[i].base = NULL; 15003831d35Sstevel master_iosram->tunnel->tunnel_keys[i].size = 0; 15103831d35Sstevel } 15203831d35Sstevel 15303831d35Sstevel for (i = 0; i < SBBC_MAX_INTRS; i++) 15403831d35Sstevel master_iosram->intrs[i].sbbc_handler = NULL; 15503831d35Sstevel 15603831d35Sstevel mutex_init(&master_iosram->iosram_lock, NULL, MUTEX_DEFAULT, NULL); 15703831d35Sstevel rw_init(&master_iosram->tunnel_lock, NULL, RW_DEFAULT, NULL); 15803831d35Sstevel } 15903831d35Sstevel void 16003831d35Sstevel iosram_fini() 16103831d35Sstevel { 16203831d35Sstevel struct tunnel_key *tunnel; 16303831d35Sstevel int i; 16403831d35Sstevel 16503831d35Sstevel rw_destroy(&master_iosram->tunnel_lock); 16603831d35Sstevel mutex_destroy(&master_iosram->iosram_lock); 16703831d35Sstevel 16803831d35Sstevel /* 16903831d35Sstevel * destroy any tunnel maps 17003831d35Sstevel */ 17103831d35Sstevel for (i = 0; i < SBBC_MAX_KEYS; i++) { 17203831d35Sstevel tunnel = &master_iosram->tunnel->tunnel_keys[i]; 17303831d35Sstevel if (tunnel->base != NULL) { 17403831d35Sstevel ddi_regs_map_free(&tunnel->reg_handle); 17503831d35Sstevel tunnel->base = NULL; 17603831d35Sstevel } 17703831d35Sstevel } 17803831d35Sstevel 17903831d35Sstevel kmem_free(master_iosram->tunnel, sizeof (tunnel_t)); 18003831d35Sstevel 18103831d35Sstevel kmem_free(master_iosram, sizeof (struct chosen_iosram)); 18203831d35Sstevel 18303831d35Sstevel master_iosram = NULL; 18403831d35Sstevel } 18503831d35Sstevel 18603831d35Sstevel static void 18703831d35Sstevel check_iosram_ver(uint16_t version) 18803831d35Sstevel { 18903831d35Sstevel uint8_t max_ver = MAX_IOSRAM_TOC_VER; 19003831d35Sstevel uint8_t major_ver = 19103831d35Sstevel (version >> IOSRAM_TOC_VER_SHIFT) & IOSRAM_TOC_VER_MASK; 19203831d35Sstevel 19303831d35Sstevel SGSBBC_DBG_ALL("IOSRAM TOC version: %d.%d\n", major_ver, 19403831d35Sstevel version & IOSRAM_TOC_VER_MASK); 19503831d35Sstevel SGSBBC_DBG_ALL("Max supported IOSRAM TOC version: %d\n", max_ver); 19603831d35Sstevel if (major_ver > max_ver) { 19703831d35Sstevel panic("Up-rev System Controller version.\n" 19803831d35Sstevel "You must restore an earlier revision of System " 19903831d35Sstevel "Controller firmware, or upgrade Solaris.\n" 20003831d35Sstevel "Please consult the System Controller release notice " 20103831d35Sstevel "for additional details."); 20203831d35Sstevel } 20303831d35Sstevel } 20403831d35Sstevel 20503831d35Sstevel static void 20603831d35Sstevel tunnel_commit(sbbc_softstate_t *softsp, tunnel_t *new_tunnel) 20703831d35Sstevel { 20803831d35Sstevel ASSERT(MUTEX_HELD(&master_iosram->iosram_lock)); 20903831d35Sstevel 21003831d35Sstevel master_iosram->iosram_sbbc = softsp; 21103831d35Sstevel master_iosram->tunnel = new_tunnel; 21203831d35Sstevel softsp->chosen = TRUE; 21303831d35Sstevel 21403831d35Sstevel /* 21503831d35Sstevel * SBBC has pointer to interrupt handlers for simplicity 21603831d35Sstevel */ 21703831d35Sstevel softsp->intr_hdlrs = master_iosram->intrs; 21803831d35Sstevel } 21903831d35Sstevel 22003831d35Sstevel static int 22103831d35Sstevel tunnel_init(sbbc_softstate_t *softsp, tunnel_t *new_tunnel) 22203831d35Sstevel { 22303831d35Sstevel struct iosram_toc *toc = NULL; 22403831d35Sstevel int i, key; 22503831d35Sstevel struct tunnel_key *tunnel; 22603831d35Sstevel ddi_acc_handle_t toc_handle; 22703831d35Sstevel struct ddi_device_acc_attr attr; 22803831d35Sstevel 22903831d35Sstevel ASSERT(MUTEX_HELD(&master_iosram->iosram_lock)); 23003831d35Sstevel 23103831d35Sstevel if ((softsp == (sbbc_softstate_t *)NULL) || 23203831d35Sstevel (new_tunnel == (tunnel_t *)NULL)) { 23303831d35Sstevel 23403831d35Sstevel return (DDI_FAILURE); 23503831d35Sstevel } 23603831d35Sstevel 23703831d35Sstevel attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 23803831d35Sstevel attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 23903831d35Sstevel attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 24003831d35Sstevel 24103831d35Sstevel SGSBBC_DBG_ALL("map in the IOSRAM TOC at offset %x\n", 24203831d35Sstevel softsp->sram_toc); 24303831d35Sstevel 24403831d35Sstevel /* 24503831d35Sstevel * First map in the TOC, then set up the tunnel 24603831d35Sstevel */ 24703831d35Sstevel if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS, 24803831d35Sstevel (caddr_t *)&toc, 24903831d35Sstevel SBBC_SRAM_OFFSET + softsp->sram_toc, 25003831d35Sstevel sizeof (struct iosram_toc), 25103831d35Sstevel &attr, &toc_handle) != DDI_SUCCESS) { 25203831d35Sstevel cmn_err(CE_WARN, "sbbc%d: unable to map SRAM " 25303831d35Sstevel "registers", ddi_get_instance(softsp->dip)); 25403831d35Sstevel return (DDI_FAILURE); 25503831d35Sstevel } 256*07d06da5SSurya Prakki SGSBBC_DBG_ALL("dip=%p mapped TOC %p\n", (void *)softsp->dip, 257*07d06da5SSurya Prakki (void *)toc); 25803831d35Sstevel 25903831d35Sstevel check_iosram_ver(toc->iosram_version); 26003831d35Sstevel 26103831d35Sstevel for (i = 0; i < toc->iosram_tagno; i++) { 26203831d35Sstevel key = iosram_convert_key(toc->iosram_keys[i].key); 26303831d35Sstevel if ((key > 0) && (key < SBBC_MAX_KEYS)) { 26403831d35Sstevel tunnel = &new_tunnel->tunnel_keys[key]; 26503831d35Sstevel tunnel->key = key; 26603831d35Sstevel tunnel->size = toc->iosram_keys[i].size; 26703831d35Sstevel /* 26803831d35Sstevel * map in the SRAM area using the offset 26903831d35Sstevel * from the base of SRAM + SRAM offset into 27003831d35Sstevel * the register property for the SBBC base 27103831d35Sstevel * address 27203831d35Sstevel */ 27303831d35Sstevel if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS, 27403831d35Sstevel (caddr_t *)&tunnel->base, 27503831d35Sstevel SBBC_SRAM_OFFSET + toc->iosram_keys[i].offset, 27603831d35Sstevel toc->iosram_keys[i].size, &attr, 27703831d35Sstevel &tunnel->reg_handle) != DDI_SUCCESS) { 27803831d35Sstevel cmn_err(CE_WARN, "sbbc%d: unable to map SRAM " 27903831d35Sstevel "registers", ddi_get_instance(softsp->dip)); 28003831d35Sstevel return (DDI_FAILURE); 28103831d35Sstevel } 28203831d35Sstevel SGSBBC_DBG_ALL("%d: key %s size %d offset %x addr %p\n", 28303831d35Sstevel i, toc->iosram_keys[i].key, 28403831d35Sstevel toc->iosram_keys[i].size, 28503831d35Sstevel toc->iosram_keys[i].offset, 286*07d06da5SSurya Prakki (void *)tunnel->base); 28703831d35Sstevel 28803831d35Sstevel } 28903831d35Sstevel } 29003831d35Sstevel 29103831d35Sstevel 29203831d35Sstevel if (toc != NULL) { 29303831d35Sstevel ddi_regs_map_free(&toc_handle); 29403831d35Sstevel } 29503831d35Sstevel 29603831d35Sstevel /* 29703831d35Sstevel * Set up the 'interrupt reason' SRAM pointers 29803831d35Sstevel * for the SBBC interrupt handler 29903831d35Sstevel */ 30003831d35Sstevel if (INVALID_KEY(new_tunnel, SBBC_SC_INTR_KEY)) { 30103831d35Sstevel /* 30203831d35Sstevel * Can't really do much if these are not here 30303831d35Sstevel */ 30403831d35Sstevel prom_printf("No Interrupt Reason Fields set by SC\n"); 30503831d35Sstevel cmn_err(CE_WARN, "No Interrupt Reason Fields set by SC"); 30603831d35Sstevel return (DDI_FAILURE); 30703831d35Sstevel } 30803831d35Sstevel 30903831d35Sstevel return (DDI_SUCCESS); 31003831d35Sstevel } 31103831d35Sstevel 31203831d35Sstevel /* 31303831d35Sstevel * Unmap a tunnel 31403831d35Sstevel */ 31503831d35Sstevel static void 31603831d35Sstevel tunnel_fini(tunnel_t *tunnel) 31703831d35Sstevel { 31803831d35Sstevel int i; 31903831d35Sstevel struct tunnel_key *tunnel_key; 32003831d35Sstevel 32103831d35Sstevel /* 32203831d35Sstevel * Unmap the tunnel 32303831d35Sstevel */ 32403831d35Sstevel for (i = 0; i < SBBC_MAX_KEYS; i++) { 32503831d35Sstevel tunnel_key = &tunnel->tunnel_keys[i]; 32603831d35Sstevel if (tunnel_key->base != NULL) { 32703831d35Sstevel ddi_regs_map_free(&tunnel_key->reg_handle); 32803831d35Sstevel tunnel_key->base = NULL; 32903831d35Sstevel } 33003831d35Sstevel } 33103831d35Sstevel } 33203831d35Sstevel 33303831d35Sstevel static void 33403831d35Sstevel clear_break() 33503831d35Sstevel { 33603831d35Sstevel struct tunnel_key tunnel_key; 33703831d35Sstevel uint32_t *intr_in_reason; 33803831d35Sstevel ddi_acc_handle_t intr_in_handle; 33903831d35Sstevel 34003831d35Sstevel ASSERT(MUTEX_HELD(&master_iosram->iosram_lock)); 34103831d35Sstevel 34203831d35Sstevel tunnel_key = master_iosram->tunnel->tunnel_keys[SBBC_SC_INTR_KEY]; 34303831d35Sstevel intr_in_reason = (uint32_t *)tunnel_key.base; 34403831d35Sstevel intr_in_handle = tunnel_key.reg_handle; 34503831d35Sstevel ddi_put32(intr_in_handle, intr_in_reason, 34603831d35Sstevel ddi_get32(intr_in_handle, intr_in_reason) & ~SBBC_CONSOLE_BRK); 34703831d35Sstevel } 34803831d35Sstevel 34903831d35Sstevel int 35003831d35Sstevel iosram_tunnel_init(sbbc_softstate_t *softsp) 35103831d35Sstevel { 35203831d35Sstevel int rc; 35303831d35Sstevel 35403831d35Sstevel ASSERT(master_iosram); 35503831d35Sstevel 35603831d35Sstevel mutex_enter(&master_iosram->iosram_lock); 35703831d35Sstevel 35803831d35Sstevel if ((rc = tunnel_init(softsp, master_iosram->tunnel)) == DDI_SUCCESS) { 35903831d35Sstevel tunnel_commit(softsp, master_iosram->tunnel); 36003831d35Sstevel clear_break(); 36103831d35Sstevel } 36203831d35Sstevel 36303831d35Sstevel 36403831d35Sstevel mutex_exit(&master_iosram->iosram_lock); 36503831d35Sstevel 36603831d35Sstevel return (rc); 36703831d35Sstevel } 36803831d35Sstevel 36903831d35Sstevel int 37003831d35Sstevel iosram_read(int key, uint32_t offset, caddr_t buf, uint32_t size) 37103831d35Sstevel { 37203831d35Sstevel return (iosram_rw(key, offset, buf, size, FREAD)); 37303831d35Sstevel } 37403831d35Sstevel 37503831d35Sstevel int 37603831d35Sstevel iosram_write(int key, uint32_t offset, caddr_t buf, uint32_t size) 37703831d35Sstevel { 37803831d35Sstevel return (iosram_rw(key, offset, buf, size, FWRITE)); 37903831d35Sstevel } 38003831d35Sstevel 38103831d35Sstevel 38203831d35Sstevel static int 38303831d35Sstevel iosram_rw(int key, uint32_t offset, caddr_t buf, uint32_t size, int flag) 38403831d35Sstevel { 38503831d35Sstevel struct tunnel_key *tunnel; 38603831d35Sstevel caddr_t sram_src; 38703831d35Sstevel 38803831d35Sstevel /* 38903831d35Sstevel * Return right away if there is nothing to read/write. 39003831d35Sstevel */ 39103831d35Sstevel if (size == 0) 39203831d35Sstevel return (0); 39303831d35Sstevel 39403831d35Sstevel rw_enter(&master_iosram->tunnel_lock, RW_READER); 39503831d35Sstevel 39603831d35Sstevel /* 39703831d35Sstevel * Key not matched ? 39803831d35Sstevel */ 39903831d35Sstevel if (INVALID_KEY(master_iosram->tunnel, key)) { 40003831d35Sstevel rw_exit(&master_iosram->tunnel_lock); 40103831d35Sstevel return (ENXIO); 40203831d35Sstevel } 40303831d35Sstevel 40403831d35Sstevel tunnel = &master_iosram->tunnel->tunnel_keys[key]; 40503831d35Sstevel if ((offset + size) > tunnel->size) { 40603831d35Sstevel rw_exit(&master_iosram->tunnel_lock); 40703831d35Sstevel return (EFBIG); 40803831d35Sstevel } 40903831d35Sstevel 41003831d35Sstevel sram_src = tunnel->base + offset; 41103831d35Sstevel 41203831d35Sstevel /* 41303831d35Sstevel * Atomic reads/writes might be necessary for some clients. 41403831d35Sstevel * We assume that such clients could guarantee their buffers 41503831d35Sstevel * are aligned at the boundary of the request sizes. We also 41603831d35Sstevel * assume that the source/destination of such requests are 41703831d35Sstevel * aligned at the right boundaries in IOSRAM. If either 41803831d35Sstevel * condition fails, byte access is performed. 41903831d35Sstevel */ 42003831d35Sstevel if (flag == FREAD) { 42103831d35Sstevel switch (size) { 42203831d35Sstevel case sizeof (uint16_t): 42303831d35Sstevel case sizeof (uint32_t): 42403831d35Sstevel case sizeof (uint64_t): 42503831d35Sstevel if (IS_P2ALIGNED(sram_src, size) && 42603831d35Sstevel IS_P2ALIGNED(buf, size)) { 42703831d35Sstevel 42803831d35Sstevel if (size == sizeof (uint16_t)) 42903831d35Sstevel IOSRAM_GET(tunnel, sram_src, buf, 16); 43003831d35Sstevel else if (size == sizeof (uint32_t)) 43103831d35Sstevel IOSRAM_GET(tunnel, sram_src, buf, 32); 43203831d35Sstevel else 43303831d35Sstevel IOSRAM_GET(tunnel, sram_src, buf, 64); 43403831d35Sstevel break; 43503831d35Sstevel } 43603831d35Sstevel /* FALLTHRU */ 43703831d35Sstevel default: 43803831d35Sstevel IOSRAM_GETB(tunnel, (uint8_t *)buf, 43903831d35Sstevel (uint8_t *)sram_src, (size_t)size); 44003831d35Sstevel break; 44103831d35Sstevel } 44203831d35Sstevel } else { 44303831d35Sstevel switch (size) { 44403831d35Sstevel case sizeof (uint16_t): 44503831d35Sstevel case sizeof (uint32_t): 44603831d35Sstevel case sizeof (uint64_t): 44703831d35Sstevel if (IS_P2ALIGNED(sram_src, size) && 44803831d35Sstevel IS_P2ALIGNED(buf, size)) { 44903831d35Sstevel 45003831d35Sstevel if (size == sizeof (uint16_t)) 45103831d35Sstevel IOSRAM_PUT(tunnel, sram_src, buf, 16); 45203831d35Sstevel else if (size == sizeof (uint32_t)) 45303831d35Sstevel IOSRAM_PUT(tunnel, sram_src, buf, 32); 45403831d35Sstevel else 45503831d35Sstevel IOSRAM_PUT(tunnel, sram_src, buf, 64); 45603831d35Sstevel break; 45703831d35Sstevel } 45803831d35Sstevel /* FALLTHRU */ 45903831d35Sstevel default: 46003831d35Sstevel IOSRAM_PUTB(tunnel, (uint8_t *)buf, 46103831d35Sstevel (uint8_t *)sram_src, (size_t)size); 46203831d35Sstevel break; 46303831d35Sstevel } 46403831d35Sstevel } 46503831d35Sstevel 46603831d35Sstevel rw_exit(&master_iosram->tunnel_lock); 46703831d35Sstevel return (0); 46803831d35Sstevel 46903831d35Sstevel } 47003831d35Sstevel 47103831d35Sstevel int 47203831d35Sstevel iosram_size(int key) 47303831d35Sstevel { 47403831d35Sstevel int size = -1; 47503831d35Sstevel 47603831d35Sstevel rw_enter(&master_iosram->tunnel_lock, RW_READER); 47703831d35Sstevel 47803831d35Sstevel /* 47903831d35Sstevel * Key not matched ? 48003831d35Sstevel */ 48103831d35Sstevel if (!INVALID_KEY(master_iosram->tunnel, key)) 48203831d35Sstevel size = master_iosram->tunnel->tunnel_keys[key].size; 48303831d35Sstevel 48403831d35Sstevel rw_exit(&master_iosram->tunnel_lock); 48503831d35Sstevel 48603831d35Sstevel return (size); 48703831d35Sstevel } 48803831d35Sstevel 48903831d35Sstevel /* 49003831d35Sstevel * Generate an interrupt to the SC using the SBBC EPLD 49103831d35Sstevel * 49203831d35Sstevel * Note: intr_num can be multiple interrupts OR'ed together 49303831d35Sstevel */ 49403831d35Sstevel int 49503831d35Sstevel iosram_send_intr(uint32_t intr_num) 49603831d35Sstevel { 49703831d35Sstevel 49803831d35Sstevel int rc = 0; 49903831d35Sstevel uint32_t intr_reason; 50003831d35Sstevel uint32_t intr_enabled; 50103831d35Sstevel 50203831d35Sstevel /* 50303831d35Sstevel * Verify that we have already set up the master sbbc 50403831d35Sstevel */ 50503831d35Sstevel if (master_iosram == NULL) 50603831d35Sstevel return (ENXIO); 50703831d35Sstevel 50803831d35Sstevel /* 50903831d35Sstevel * Grab the lock to prevent tunnel switch in the middle 51003831d35Sstevel * of sending an interrupt. 51103831d35Sstevel */ 51203831d35Sstevel mutex_enter(&master_iosram->iosram_lock); 51303831d35Sstevel 51403831d35Sstevel if (master_iosram->iosram_sbbc == NULL) { 51503831d35Sstevel rc = ENXIO; 51603831d35Sstevel goto send_intr_exit; 51703831d35Sstevel } 51803831d35Sstevel 51903831d35Sstevel if ((rc = sbbc_send_intr(master_iosram->iosram_sbbc, FALSE)) != 0) { 52003831d35Sstevel /* 52103831d35Sstevel * previous interrupts have not been cleared yet by the SC 52203831d35Sstevel */ 52303831d35Sstevel goto send_intr_exit; 52403831d35Sstevel } 52503831d35Sstevel 52603831d35Sstevel /* 52703831d35Sstevel * Set a bit in the interrupt reason field 52803831d35Sstevel * call back into the sbbc handler to hit the EPLD 52903831d35Sstevel * 53003831d35Sstevel * First check the interrupts enabled by the SC 53103831d35Sstevel */ 53203831d35Sstevel if ((rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0, 53303831d35Sstevel (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) { 53403831d35Sstevel 53503831d35Sstevel goto send_intr_exit; 53603831d35Sstevel } 53703831d35Sstevel 53803831d35Sstevel if ((intr_enabled & intr_num) != intr_num) { 53903831d35Sstevel /* 54003831d35Sstevel * at least one of the interrupts is 54103831d35Sstevel * not enabled by the SC 54203831d35Sstevel */ 54303831d35Sstevel rc = ENOTSUP; 54403831d35Sstevel goto send_intr_exit; 54503831d35Sstevel } 54603831d35Sstevel 54703831d35Sstevel if ((rc = iosram_read(SBBC_INTR_SC_KEY, 0, 54803831d35Sstevel (caddr_t)&intr_reason, sizeof (intr_reason))) != 0) { 54903831d35Sstevel 55003831d35Sstevel goto send_intr_exit; 55103831d35Sstevel } 55203831d35Sstevel 55303831d35Sstevel if ((intr_reason & intr_num) == intr_num) { 55403831d35Sstevel /* 55503831d35Sstevel * All interrupts specified are already pending 55603831d35Sstevel */ 55703831d35Sstevel rc = EBUSY; 55803831d35Sstevel goto send_intr_exit; 55903831d35Sstevel } 56003831d35Sstevel 56103831d35Sstevel intr_reason |= intr_num; 56203831d35Sstevel 56303831d35Sstevel if ((rc = iosram_write(SBBC_INTR_SC_KEY, 0, 56403831d35Sstevel (caddr_t)&intr_reason, sizeof (intr_reason))) != 0) { 56503831d35Sstevel 56603831d35Sstevel goto send_intr_exit; 56703831d35Sstevel } 56803831d35Sstevel 56903831d35Sstevel /* 57003831d35Sstevel * Hit the EPLD interrupt bit 57103831d35Sstevel */ 57203831d35Sstevel 57303831d35Sstevel rc = sbbc_send_intr(master_iosram->iosram_sbbc, TRUE); 57403831d35Sstevel 57503831d35Sstevel send_intr_exit: 57603831d35Sstevel 57703831d35Sstevel mutex_exit(&master_iosram->iosram_lock); 57803831d35Sstevel 57903831d35Sstevel return (rc); 58003831d35Sstevel } 58103831d35Sstevel 58203831d35Sstevel /* 58303831d35Sstevel * Register an interrupt handler 58403831d35Sstevel */ 58503831d35Sstevel int 58603831d35Sstevel iosram_reg_intr(uint32_t intr_num, sbbc_intrfunc_t intr_handler, 58703831d35Sstevel caddr_t arg, uint_t *state, kmutex_t *lock) 58803831d35Sstevel { 58903831d35Sstevel sbbc_softstate_t *softsp; 59003831d35Sstevel int rc = 0; 59103831d35Sstevel sbbc_intrs_t *intr; 59203831d35Sstevel int intr_no; 59303831d35Sstevel uint32_t intr_enabled; 59403831d35Sstevel 59503831d35Sstevel /* 59603831d35Sstevel * Verify that we have already set up the master sbbc 59703831d35Sstevel */ 59803831d35Sstevel if (master_iosram == NULL) 59903831d35Sstevel return (ENXIO); 60003831d35Sstevel 60103831d35Sstevel /* 60203831d35Sstevel * determine which bit is this intr_num for ? 60303831d35Sstevel */ 60403831d35Sstevel for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) { 60503831d35Sstevel if (intr_num == (1 << intr_no)) 60603831d35Sstevel break; 60703831d35Sstevel } 60803831d35Sstevel 60903831d35Sstevel /* 61003831d35Sstevel * Check the parameters 61103831d35Sstevel */ 61203831d35Sstevel if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS) || 61303831d35Sstevel (intr_handler == NULL) || (state == NULL) || 61403831d35Sstevel (lock == NULL)) 61503831d35Sstevel return (EINVAL); 61603831d35Sstevel 61703831d35Sstevel mutex_enter(&master_iosram->iosram_lock); 61803831d35Sstevel 61903831d35Sstevel if ((softsp = master_iosram->iosram_sbbc) == NULL) { 62003831d35Sstevel mutex_exit(&master_iosram->iosram_lock); 62103831d35Sstevel return (ENXIO); 62203831d35Sstevel } 62303831d35Sstevel 62403831d35Sstevel mutex_enter(&softsp->sbbc_lock); 62503831d35Sstevel 62603831d35Sstevel intr = &master_iosram->intrs[intr_no]; 62703831d35Sstevel 62803831d35Sstevel if (intr->sbbc_handler != (sbbc_intrfunc_t)NULL) { 62903831d35Sstevel rc = EBUSY; 63003831d35Sstevel goto reg_intr_exit; 63103831d35Sstevel } 63203831d35Sstevel 63303831d35Sstevel intr->sbbc_handler = intr_handler; 63403831d35Sstevel intr->sbbc_arg = (void *)arg; 63503831d35Sstevel intr->sbbc_intr_state = state; 63603831d35Sstevel intr->sbbc_intr_lock = lock; 63703831d35Sstevel intr->sbbc_intr_next = (sbbc_intrs_t *)NULL; 63803831d35Sstevel 63903831d35Sstevel /* 64003831d35Sstevel * we need to make sure that the mutex is for 64103831d35Sstevel * an ADAPTIVE lock, so call mutex_init() again with 64203831d35Sstevel * the sbbc iblock cookie 64303831d35Sstevel */ 64403831d35Sstevel mutex_init(lock, NULL, MUTEX_DRIVER, 64503831d35Sstevel (void *)softsp->iblock); 64603831d35Sstevel 64703831d35Sstevel if (ddi_add_softintr(softsp->dip, DDI_SOFTINT_HIGH, 64803831d35Sstevel &intr->sbbc_intr_id, NULL, NULL, 64903831d35Sstevel intr_handler, (caddr_t)arg) != DDI_SUCCESS) { 65003831d35Sstevel 65103831d35Sstevel cmn_err(CE_WARN, "Can't add SBBC softint"); 65203831d35Sstevel rc = EAGAIN; 65303831d35Sstevel goto reg_intr_exit; 65403831d35Sstevel } 65503831d35Sstevel 65603831d35Sstevel /* 65703831d35Sstevel * Set the bit in the Interrupts Enabled Field for this 65803831d35Sstevel * interrupt 65903831d35Sstevel */ 66003831d35Sstevel if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0, 66103831d35Sstevel (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) { 66203831d35Sstevel 66303831d35Sstevel goto reg_intr_exit; 66403831d35Sstevel } 66503831d35Sstevel 66603831d35Sstevel intr_enabled |= intr_num; 66703831d35Sstevel 66803831d35Sstevel if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0, 66903831d35Sstevel (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) { 67003831d35Sstevel 67103831d35Sstevel goto reg_intr_exit; 67203831d35Sstevel } 67303831d35Sstevel 67403831d35Sstevel reg_intr_exit: 67503831d35Sstevel 67603831d35Sstevel mutex_exit(&softsp->sbbc_lock); 67703831d35Sstevel mutex_exit(&master_iosram->iosram_lock); 67803831d35Sstevel 67903831d35Sstevel return (rc); 68003831d35Sstevel } 68103831d35Sstevel 68203831d35Sstevel /* 68303831d35Sstevel * Remove an interrupt handler 68403831d35Sstevel */ 68503831d35Sstevel int 68603831d35Sstevel iosram_unreg_intr(uint32_t intr_num) 68703831d35Sstevel { 68803831d35Sstevel sbbc_softstate_t *softsp; 68903831d35Sstevel int rc = 0; 69003831d35Sstevel sbbc_intrs_t *intr; 69103831d35Sstevel int intr_no; 69203831d35Sstevel uint32_t intr_enabled; 69303831d35Sstevel 69403831d35Sstevel /* 69503831d35Sstevel * Verify that we have already set up the master sbbc 69603831d35Sstevel */ 69703831d35Sstevel if (master_iosram == NULL) 69803831d35Sstevel return (ENXIO); 69903831d35Sstevel 70003831d35Sstevel /* 70103831d35Sstevel * determine which bit is this intr_num for ? 70203831d35Sstevel */ 70303831d35Sstevel for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) { 70403831d35Sstevel if (intr_num == (1 << intr_no)) 70503831d35Sstevel break; 70603831d35Sstevel } 70703831d35Sstevel 70803831d35Sstevel if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS)) 70903831d35Sstevel return (EINVAL); 71003831d35Sstevel 71103831d35Sstevel mutex_enter(&master_iosram->iosram_lock); 71203831d35Sstevel 71303831d35Sstevel if ((softsp = master_iosram->iosram_sbbc) == NULL) { 71403831d35Sstevel mutex_exit(&master_iosram->iosram_lock); 71503831d35Sstevel return (ENXIO); 71603831d35Sstevel } 71703831d35Sstevel 71803831d35Sstevel mutex_enter(&softsp->sbbc_lock); 71903831d35Sstevel 72003831d35Sstevel intr = &master_iosram->intrs[intr_no]; 72103831d35Sstevel 72203831d35Sstevel /* 72303831d35Sstevel * No handler installed 72403831d35Sstevel */ 72503831d35Sstevel if (intr->sbbc_handler == (sbbc_intrfunc_t)NULL) { 72603831d35Sstevel rc = EINVAL; 72703831d35Sstevel goto unreg_intr_exit; 72803831d35Sstevel } 72903831d35Sstevel 73003831d35Sstevel /* 73103831d35Sstevel * Unset the bit in the Interrupts Enabled Field for this 73203831d35Sstevel * interrupt 73303831d35Sstevel */ 73403831d35Sstevel if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0, 73503831d35Sstevel (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) { 73603831d35Sstevel 73703831d35Sstevel goto unreg_intr_exit; 73803831d35Sstevel } 73903831d35Sstevel 74003831d35Sstevel intr_enabled &= ~intr_num; 74103831d35Sstevel 74203831d35Sstevel if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0, 74303831d35Sstevel (caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) { 74403831d35Sstevel 74503831d35Sstevel goto unreg_intr_exit; 74603831d35Sstevel } 74703831d35Sstevel 74803831d35Sstevel /* 74903831d35Sstevel * If handler is running, wait until it's done. 75003831d35Sstevel * It won't get triggered again because we disabled it above. 75103831d35Sstevel * When we wait, drop sbbc_lock so other interrupt handlers 75203831d35Sstevel * can still run. 75303831d35Sstevel */ 75403831d35Sstevel for (; ; ) { 75503831d35Sstevel mutex_enter(intr->sbbc_intr_lock); 75603831d35Sstevel if (*(intr->sbbc_intr_state) != SBBC_INTR_IDLE) { 75703831d35Sstevel mutex_exit(intr->sbbc_intr_lock); 75803831d35Sstevel mutex_exit(&softsp->sbbc_lock); 75903831d35Sstevel delay(drv_usectohz(10000)); 76003831d35Sstevel mutex_enter(&softsp->sbbc_lock); 76103831d35Sstevel mutex_enter(intr->sbbc_intr_lock); 76203831d35Sstevel } else { 76303831d35Sstevel break; 76403831d35Sstevel } 76503831d35Sstevel mutex_exit(intr->sbbc_intr_lock); 76603831d35Sstevel } 76703831d35Sstevel 76803831d35Sstevel if (intr->sbbc_intr_id) 76903831d35Sstevel ddi_remove_softintr(intr->sbbc_intr_id); 77003831d35Sstevel 77103831d35Sstevel intr->sbbc_handler = (sbbc_intrfunc_t)NULL; 77203831d35Sstevel intr->sbbc_arg = (void *)NULL; 77303831d35Sstevel intr->sbbc_intr_id = 0; 77403831d35Sstevel intr->sbbc_intr_state = NULL; 77503831d35Sstevel intr->sbbc_intr_lock = (kmutex_t *)NULL; 77603831d35Sstevel intr->sbbc_intr_next = (sbbc_intrs_t *)NULL; 77703831d35Sstevel 77803831d35Sstevel unreg_intr_exit: 77903831d35Sstevel 78003831d35Sstevel mutex_exit(&softsp->sbbc_lock); 78103831d35Sstevel mutex_exit(&master_iosram->iosram_lock); 78203831d35Sstevel 78303831d35Sstevel return (rc); 78403831d35Sstevel } 78503831d35Sstevel 78603831d35Sstevel /* 78703831d35Sstevel * sgsbbc_iosram_switchfrom(softsp) 78803831d35Sstevel * Switch master tunnel away from the specified instance. 78903831d35Sstevel */ 79003831d35Sstevel int 79103831d35Sstevel sgsbbc_iosram_switchfrom(struct sbbc_softstate *softsp) 79203831d35Sstevel { 79303831d35Sstevel struct sbbc_softstate *sp; 79403831d35Sstevel int rv = DDI_FAILURE; 79503831d35Sstevel int new_instance; 79603831d35Sstevel 79703831d35Sstevel /* 79803831d35Sstevel * Find the candidate target of tunnel from the linked list. 79903831d35Sstevel */ 80003831d35Sstevel mutex_enter(&chosen_lock); 80103831d35Sstevel ASSERT(sgsbbc_instances); 80203831d35Sstevel 80303831d35Sstevel for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) { 80403831d35Sstevel if (softsp == sp) 80503831d35Sstevel continue; 80603831d35Sstevel 80703831d35Sstevel if (sp->sbbc_state & SBBC_STATE_DETACH) 80803831d35Sstevel continue; 80903831d35Sstevel break; 81003831d35Sstevel } 81103831d35Sstevel if (sp == NULL) { 81203831d35Sstevel /* at least one IOSRAM should be attached */ 81303831d35Sstevel rv = DDI_FAILURE; 81403831d35Sstevel } else { 81503831d35Sstevel /* Do the tunnel switch */ 81603831d35Sstevel new_instance = ddi_get_instance(sp->dip); 81703831d35Sstevel rv = iosram_switch_tunnel(new_instance); 81803831d35Sstevel if (rv == DDI_SUCCESS) { 81903831d35Sstevel /* reset the chosen_iosram back ref */ 82003831d35Sstevel sp->iosram = master_iosram; 82103831d35Sstevel } 82203831d35Sstevel } 82303831d35Sstevel mutex_exit(&chosen_lock); 82403831d35Sstevel return (rv); 82503831d35Sstevel } 82603831d35Sstevel 82703831d35Sstevel 82803831d35Sstevel /* 82903831d35Sstevel * Switch the tunnel to a different I/O board. 83003831d35Sstevel * At the moment, we will say that this is 83103831d35Sstevel * called with the instance of the SBBC to switch 83203831d35Sstevel * to. This will probably change, but as long as we 83303831d35Sstevel * can get a devinfo/softstate for the target SBBC it 83403831d35Sstevel * doesn't matter what the parameter is. 83503831d35Sstevel */ 83603831d35Sstevel int 83703831d35Sstevel iosram_switch_tunnel(int instance) 83803831d35Sstevel { 83903831d35Sstevel 84003831d35Sstevel sbbc_softstate_t *to_softsp, *from_softsp; 84103831d35Sstevel dev_info_t *pdip; /* parent dip */ 84203831d35Sstevel tunnel_t *new_tunnel; /* new tunnel */ 84303831d35Sstevel int portid; 84403831d35Sstevel uint_t node; /* node id to pass to OBP */ 84503831d35Sstevel uint_t board; /* board number to pass to OBP */ 84603831d35Sstevel int rc = DDI_SUCCESS; 84703831d35Sstevel static fn_t f = "iosram_switch_tunnel"; 84803831d35Sstevel 84903831d35Sstevel /* Check the firmware for tunnel switch support */ 85003831d35Sstevel if (prom_test("SUNW,switch-tunnel") != 0) { 85103831d35Sstevel cmn_err(CE_WARN, "Firmware does not support tunnel switch"); 85203831d35Sstevel return (DDI_FAILURE); 85303831d35Sstevel } 85403831d35Sstevel 85503831d35Sstevel if ((master_iosram == NULL) || (master_mbox == NULL)) 85603831d35Sstevel return (DDI_FAILURE); 85703831d35Sstevel 85803831d35Sstevel if (!(to_softsp = sbbc_get_soft_state(instance))) 85903831d35Sstevel return (DDI_FAILURE); 86003831d35Sstevel 86103831d35Sstevel /* 86203831d35Sstevel * create the new tunnel 86303831d35Sstevel */ 86403831d35Sstevel if ((new_tunnel = kmem_zalloc(sizeof (tunnel_t), KM_NOSLEEP)) == NULL) { 86503831d35Sstevel cmn_err(CE_WARN, "Can't allocate space for new tunnel"); 86603831d35Sstevel return (DDI_FAILURE); 86703831d35Sstevel } 86803831d35Sstevel 86903831d35Sstevel pdip = ddi_get_parent(to_softsp->dip); 87003831d35Sstevel if ((portid = ddi_getprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS, 87103831d35Sstevel "portid", -1)) < 0) { 87203831d35Sstevel 87303831d35Sstevel SGSBBC_DBG_ALL("%s: couldn't get portid\n", f); 87403831d35Sstevel return (DDI_FAILURE); 87503831d35Sstevel } 87603831d35Sstevel 87703831d35Sstevel /* 87803831d35Sstevel * Compute node id and board number from port id 87903831d35Sstevel */ 88003831d35Sstevel node = SG_PORTID_TO_NODEID(portid); 88103831d35Sstevel board = SG_IO_BD_PORTID_TO_BD_NUM(portid); 88203831d35Sstevel 88303831d35Sstevel /* 88403831d35Sstevel * lock the chosen IOSRAM 88503831d35Sstevel */ 88603831d35Sstevel mutex_enter(&master_iosram->iosram_lock); 88703831d35Sstevel 88803831d35Sstevel if (master_iosram->iosram_sbbc == NULL) { 88903831d35Sstevel mutex_exit(&master_iosram->iosram_lock); 89003831d35Sstevel return (DDI_FAILURE); 89103831d35Sstevel } 89203831d35Sstevel 89303831d35Sstevel /* 89403831d35Sstevel * If the target SBBC has not mapped in its 89503831d35Sstevel * register address space, do it now 89603831d35Sstevel */ 89703831d35Sstevel mutex_enter(&to_softsp->sbbc_lock); 89803831d35Sstevel if (to_softsp->sbbc_regs == NULL) { 89903831d35Sstevel if (sbbc_map_regs(to_softsp) != DDI_SUCCESS) { 90003831d35Sstevel mutex_exit(&to_softsp->sbbc_lock); 90103831d35Sstevel mutex_exit(&master_iosram->iosram_lock); 90203831d35Sstevel return (DDI_FAILURE); 90303831d35Sstevel } 90403831d35Sstevel } 90503831d35Sstevel 90603831d35Sstevel /* 90703831d35Sstevel * Get a pointer to the current sbbc 90803831d35Sstevel */ 90903831d35Sstevel from_softsp = master_iosram->iosram_sbbc; 91003831d35Sstevel 91103831d35Sstevel mutex_enter(&from_softsp->sbbc_lock); 91203831d35Sstevel 91303831d35Sstevel /* 91403831d35Sstevel * Disable interrupts from the SC now 91503831d35Sstevel */ 91603831d35Sstevel sbbc_disable_intr(from_softsp); 91703831d35Sstevel 91803831d35Sstevel /* 91903831d35Sstevel * move SC interrupts to the new tunnel 92003831d35Sstevel */ 92103831d35Sstevel if ((rc = sbbc_add_intr(to_softsp)) == DDI_FAILURE) { 92203831d35Sstevel cmn_err(CE_WARN, "Failed to add new interrupt handler"); 92303831d35Sstevel } else if ((rc = tunnel_init(to_softsp, new_tunnel)) == DDI_FAILURE) { 92403831d35Sstevel cmn_err(CE_WARN, "Failed to initialize new tunnel"); 92503831d35Sstevel ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock); 92603831d35Sstevel } else { 92703831d35Sstevel rw_enter(&master_iosram->tunnel_lock, RW_WRITER); 92803831d35Sstevel 92903831d35Sstevel /* 93003831d35Sstevel * If OBP switch is unsuccessful, abort the switch. 93103831d35Sstevel */ 93203831d35Sstevel if ((rc = prom_serengeti_tunnel_switch(node, board)) 93303831d35Sstevel != DDI_SUCCESS) { 93403831d35Sstevel 93503831d35Sstevel /* 93603831d35Sstevel * Restart other CPUs. 93703831d35Sstevel */ 93803831d35Sstevel rw_exit(&master_iosram->tunnel_lock); 93903831d35Sstevel 94003831d35Sstevel cmn_err(CE_WARN, "OBP failed to switch tunnel"); 94103831d35Sstevel 94203831d35Sstevel /* 94303831d35Sstevel * Remove interrupt 94403831d35Sstevel */ 94503831d35Sstevel ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock); 94603831d35Sstevel 94703831d35Sstevel /* 94803831d35Sstevel * Unmap new tunnel 94903831d35Sstevel */ 95003831d35Sstevel tunnel_fini(new_tunnel); 95103831d35Sstevel } else { 95203831d35Sstevel tunnel_t *orig_tunnel; 95303831d35Sstevel 95403831d35Sstevel orig_tunnel = master_iosram->tunnel; 95503831d35Sstevel tunnel_commit(to_softsp, new_tunnel); 95603831d35Sstevel 95703831d35Sstevel rw_exit(&master_iosram->tunnel_lock); 95803831d35Sstevel 95903831d35Sstevel /* 96003831d35Sstevel * Remove interrupt from original softsp 96103831d35Sstevel */ 96203831d35Sstevel ddi_remove_intr(from_softsp->dip, 0, 96303831d35Sstevel from_softsp->iblock); 96403831d35Sstevel /* 96503831d35Sstevel * Unmap original tunnel 96603831d35Sstevel */ 96703831d35Sstevel tunnel_fini(orig_tunnel); 96803831d35Sstevel kmem_free(orig_tunnel, sizeof (tunnel_t)); 96903831d35Sstevel 97003831d35Sstevel /* 97103831d35Sstevel * Move the softintrs to the new dip. 97203831d35Sstevel */ 97303831d35Sstevel (void) iosram_switch_intr(); 97403831d35Sstevel (void) sbbc_mbox_switch(to_softsp); 97503831d35Sstevel 97603831d35Sstevel from_softsp->chosen = FALSE; 97703831d35Sstevel 97803831d35Sstevel } 97903831d35Sstevel } 98003831d35Sstevel 98103831d35Sstevel /* 98203831d35Sstevel * Enable interrupt. 98303831d35Sstevel */ 98403831d35Sstevel sbbc_enable_intr(master_iosram->iosram_sbbc); 98503831d35Sstevel 98603831d35Sstevel /* 98703831d35Sstevel * Unlock and get out 98803831d35Sstevel */ 98903831d35Sstevel mutex_exit(&from_softsp->sbbc_lock); 99003831d35Sstevel mutex_exit(&to_softsp->sbbc_lock); 99103831d35Sstevel mutex_exit(&master_iosram->iosram_lock); 99203831d35Sstevel 99303831d35Sstevel /* 99403831d35Sstevel * Call the interrupt handler directly in case 99503831d35Sstevel * we have missed an interrupt 99603831d35Sstevel */ 997*07d06da5SSurya Prakki (void) sbbc_intr_handler((caddr_t)master_iosram->iosram_sbbc); 99803831d35Sstevel 99903831d35Sstevel if (rc != DDI_SUCCESS) { 100003831d35Sstevel /* 100103831d35Sstevel * Free up the new_tunnel 100203831d35Sstevel */ 100303831d35Sstevel kmem_free(new_tunnel, sizeof (tunnel_t)); 100403831d35Sstevel cmn_err(CE_WARN, "Tunnel switch failed"); 100503831d35Sstevel } 100603831d35Sstevel 100703831d35Sstevel return (rc); 100803831d35Sstevel 100903831d35Sstevel } 101003831d35Sstevel 101103831d35Sstevel /* 101203831d35Sstevel * convert an alphanumeric OBP key to 101303831d35Sstevel * our defined numeric keys 101403831d35Sstevel */ 101503831d35Sstevel static int 101603831d35Sstevel iosram_convert_key(char *toc_key) 101703831d35Sstevel { 101803831d35Sstevel 101903831d35Sstevel if (strcmp(toc_key, TOCKEY_DOMSTAT) == 0) 102003831d35Sstevel return (SBBC_DOMAIN_KEY); 102103831d35Sstevel if (strcmp(toc_key, TOCKEY_KEYSWPO) == 0) 102203831d35Sstevel return (SBBC_KEYSWITCH_KEY); 102303831d35Sstevel if (strcmp(toc_key, TOCKEY_TODDATA) == 0) 102403831d35Sstevel return (SBBC_TOD_KEY); 102503831d35Sstevel if (strcmp(toc_key, TOCKEY_SOLCONS) == 0) 102603831d35Sstevel return (SBBC_CONSOLE_KEY); 102703831d35Sstevel if (strcmp(toc_key, TOCKEY_SOLMBOX) == 0) 102803831d35Sstevel return (SBBC_MAILBOX_KEY); 102903831d35Sstevel if (strcmp(toc_key, TOCKEY_SOLSCIR) == 0) 103003831d35Sstevel return (SBBC_INTR_SC_KEY); 103103831d35Sstevel if (strcmp(toc_key, TOCKEY_SCSOLIR) == 0) 103203831d35Sstevel return (SBBC_SC_INTR_KEY); 103303831d35Sstevel if (strcmp(toc_key, TOCKEY_ENVINFO) == 0) 103403831d35Sstevel return (SBBC_ENVCTRL_KEY); 103503831d35Sstevel if (strcmp(toc_key, TOCKEY_SOLSCIE) == 0) 103603831d35Sstevel return (SBBC_INTR_SC_ENABLED_KEY); 103703831d35Sstevel if (strcmp(toc_key, TOCKEY_SCSOLIE) == 0) 103803831d35Sstevel return (SBBC_SC_INTR_ENABLED_KEY); 103903831d35Sstevel if (strcmp(toc_key, TOCKEY_SIGBLCK) == 0) 104003831d35Sstevel return (SBBC_SIGBLCK_KEY); 104103831d35Sstevel 104203831d35Sstevel /* Unknown key */ 104303831d35Sstevel return (-1); 104403831d35Sstevel } 104503831d35Sstevel 104603831d35Sstevel /* 104703831d35Sstevel * Move the software interrupts from the old dip to the new dip 104803831d35Sstevel * when doing tunnel switch. 104903831d35Sstevel */ 105003831d35Sstevel static int 105103831d35Sstevel iosram_switch_intr() 105203831d35Sstevel { 105303831d35Sstevel sbbc_intrs_t *intr; 105403831d35Sstevel int intr_no; 105503831d35Sstevel int rc = 0; 105603831d35Sstevel 105703831d35Sstevel ASSERT(MUTEX_HELD(&master_iosram->iosram_lock)); 105803831d35Sstevel 105903831d35Sstevel for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) { 106003831d35Sstevel intr = &master_iosram->intrs[intr_no]; 106103831d35Sstevel 106203831d35Sstevel if (intr->sbbc_intr_id) { 106303831d35Sstevel ddi_remove_softintr(intr->sbbc_intr_id); 106403831d35Sstevel 106503831d35Sstevel if (ddi_add_softintr(master_iosram->iosram_sbbc->dip, 106603831d35Sstevel DDI_SOFTINT_HIGH, 106703831d35Sstevel &intr->sbbc_intr_id, NULL, NULL, 106803831d35Sstevel intr->sbbc_handler, intr->sbbc_arg) 106903831d35Sstevel != DDI_SUCCESS) { 107003831d35Sstevel 107103831d35Sstevel cmn_err(CE_WARN, "Can't add SBBC softint for " 107203831d35Sstevel "interrupt %x", intr_no << 1); 107303831d35Sstevel rc = EAGAIN; 107403831d35Sstevel } 107503831d35Sstevel } 107603831d35Sstevel } 107703831d35Sstevel 107803831d35Sstevel return (rc); 107903831d35Sstevel } 1080