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 			 */
273