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
sgsbbc_iosram_is_chosen(sbbc_softstate_t * softsp)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,
121*07d06da5SSurya Prakki chosen? "MASTER" : "SLAVE");
12203831d35Sstevel SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance,
123*07d06da5SSurya Prakki (chosen ? "MASTER" : "SLAVE"));
12403831d35Sstevel
12503831d35Sstevel return (chosen);
12603831d35Sstevel }
12703831d35Sstevel
12803831d35Sstevel void
iosram_init()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
iosram_fini()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
check_iosram_ver(uint16_t version)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
tunnel_commit(sbbc_softstate_t * softsp,tunnel_t * new_tunnel)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
tunnel_init(sbbc_softstate_t * softsp,tunnel_t * new_tunnel)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",
283*07d06da5SSurya Prakki i, toc->iosram_keys[i].key,
284*07d06da5SSurya Prakki toc->iosram_keys[i].size,
285*07d06da5SSurya Prakki 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
tunnel_fini(tunnel_t * tunnel)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
clear_break()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
iosram_tunnel_init(sbbc_softstate_t * softsp)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
iosram_read(int key,uint32_t offset,caddr_t buf,uint32_t size)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
iosram_write(int key,uint32_t offset,caddr_t buf,uint32_t size)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
iosram_rw(int key,uint32_t offset,caddr_t buf,uint32_t size,int flag)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
iosram_size(int key)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
iosram_send_intr(uint32_t intr_num)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
iosram_reg_intr(uint32_t intr_num,sbbc_intrfunc_t intr_handler,caddr_t arg,uint_t * state,kmutex_t * lock)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
iosram_unreg_intr(uint32_t intr_num)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
sgsbbc_iosram_switchfrom(struct sbbc_softstate * softsp)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
iosram_switch_tunnel(int instance)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
iosram_convert_key(char * toc_key)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
iosram_switch_intr()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