view usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_def.h @ 10696:cd0f390dd9e2

PSARC 2008/672 thebe SAS/SATA driver PSARC 2008/755 ddi_ssoft_state(9F) and ddi_isoft_state(9F) PSARC 2008/764 Cfgadm SCSI-Plugin MPxIO Support PSARC 2009/125 scsi_device property interfaces 6726110 pmcs driver (driver for thebe) 6726867 SCSAv3
author dh142964 <David.Hollister@Sun.COM>
date Wed, 30 Sep 2009 13:40:27 -0600
parents
children 9bd3b79547a5
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
#ifndef	_PMCS_DEF_H
#define	_PMCS_DEF_H
#ifdef	__cplusplus
extern "C" {
#endif

typedef enum {
	NOTHING,	/* nothing connected here */
	SATA,		/* SATA connection */
	SAS,		/* direct or indirect SAS connection */
	EXPANDER,	/* connection to an expander */
	NEW		/* Brand new device (pending state) */
} pmcs_dtype_t;

/*
 * This structure defines a PHY device that represents what we
 * are connected to.
 *
 * The eight real physical PHYs that are in the PMC8X6G are represented
 * as an array of eight of these structures which define what these
 * real PHYs are connected to.
 *
 * Depending upon what is actually connected to each PHY, the
 * type set will define what we're connected to. If it is
 * a direct SATA connection, the phy will describe a SATA endpoint
 * If it is a direct SAS connection, it will describe a SAS
 * endpoint.
 *
 * If it is an EXPANDER, this will describe the edge of an expander.
 * As we perform discovery on what is in an EXPANDER we define an
 * additional list of phys that represent what the Expander is connected to.
 */
#define	PMCS_HW_MIN_LINK_RATE	SAS_LINK_RATE_1_5GBIT
#define	PMCS_HW_MAX_LINK_RATE	SAS_LINK_RATE_6GBIT

#define	PMCS_INVALID_DEVICE_ID	0xffffffff

struct pmcs_phy {
	pmcs_phy_t	*sibling;	/* sibling phy */
	pmcs_phy_t 	*parent;	/* parent phy */
	pmcs_phy_t 	*children;	/* head of list of children */
	pmcs_phy_t 	*dead_next;	/* dead PHY list link */
	list_node_t	list_node;	/* list element */
	uint32_t	device_id;	/* PMC8X6G device handle */
	uint32_t
		ncphy 		: 8,	/* # of contained phys for expander */
		hw_event_ack	: 24;	/* XXX: first level phy event acked */
	uint8_t		phynum;		/* phy number on parent expander */
	uint8_t		width;		/* how many phys wide */
	uint8_t		ds_recovery_retries; /* # error retry attempts */
	pmcs_dtype_t	dtype;		/* current dtype of the phy */
	pmcs_dtype_t	pend_dtype;	/* new dtype (pending change) */
	uint32_t
		level		: 8,	/* level in expander tree */
		tolerates_sas2	: 1,	/* tolerates SAS2 SMP */
		spinup_hold	: 1,	/* spinup hold needs releasing */
		atdt		: 3,	/* attached device type */
		portid		: 4,	/* PMC8X6G port context */
		link_rate   	: 4,	/* current supported speeds */
		valid_device_id	: 1,	/* device id is valid */
		abort_sent	: 1,	/* we've sent an abort */
		abort_pending	: 1,	/* we have an abort pending */
		need_rl_ext	: 1,	/* need SATA RL_EXT recocvery */
		subsidiary	: 1,	/* this is part of a wide phy */
		configured	: 1,	/* is configured */
		dead		: 1,	/* dead */
		changed		: 1;	/* this phy is changing */
	clock_t		config_stop;	/* When config attempts will stop */
	hrtime_t	abort_all_start;
	kcondvar_t	abort_all_cv;	/* Wait for ABORT_ALL completion */
	kmutex_t	phy_lock;
	volatile uint32_t ref_count;	/* Targets & work on this PHY */
	uint8_t 	sas_address[8];	/* SAS address for this PHY */
	struct {
	uint32_t
		prog_min_rate	:4,
		hw_min_rate	:4,
		prog_max_rate	:4,
		hw_max_rate	:4,
		reserved	:16;
	} state;
	char		path[32];	/* path name for this phy */
	pmcs_hw_t	*pwp;		/* back ptr to hba struct */
	pmcs_iport_t	*iport;		/* back ptr to the iport handle */
	pmcs_xscsi_t	*target;	/* back ptr to current target */
	kstat_t		*phy_stats;	/* kstats for this phy */
};

/* maximum number of ds recovery retries (ds_recovery_retries) */
#define	PMCS_MAX_DS_RECOVERY_RETRIES	4


/*
 * Inbound and Outbound Queue Related Definitions.
 *
 * The PMC8X6G has a programmable number of inbound and outbound circular
 * queues for use in message passing between the host and the PMC8X6G
 * (up to 64 queues for the Rev C Chip). This driver does not use all
 * possible queues.
 *
 * Each Queue is given 4K of consistent memory and we set a 64 byte size for
 * the queue entry size (this gives us 256 queue entries per queue).
 *
 * This allocation then continues up a further PMCS_SCRATCH_SIZE bytes
 * that the driver uses as a temporary scratch area for things like
 * SMP discovery.
 *
 * This control area looks like this:
 *
 * Offset			What
 * ------------------------------------------------
 * 0					IQ 0 Consumer Index
 * 4					IQ 1 Consumer Index
 * 8..255				...
 * 252..255				IQ 63 Consumer Index
 * 256					OQ 0 Producer Index
 * 260					OQ 1 Producer Index
 * 264..259				....
 * 508..511				OQ 63 Producer Index
 * 512..512+PMCS_SCRATCH_SIZE-1		Scratch area.
 */
#define	IQCI_BASE_OFFSET	0
#define	IQ_OFFSET(qnum)		(IQCI_BASE_OFFSET + (qnum << 2))
#define	OQPI_BASE_OFFSET	256
#define	OQ_OFFSET(qnum)		(OQPI_BASE_OFFSET + (qnum << 2))

/*
 * Work related structures. Each one of these structures is paired
 * with *any* command that is fed to the PMC8X6G via one of the
 * Inbound Queues. The work structure has a tag to compare with
 * the message that comes back out of an Outbound Queue. The
 * work structure also points to the phy which this command is
 * tied to. It also has a pointer a callback function (if defined).
 * See that TAG Architecture below for the various kinds of
 * dispositions of a work structure.
 */

/*
 * Work Structure States
 *
 * NIL			->	READY
 * READY		->	NIL
 * READY		->	ONCHIP
 * ONCHIP		->	INTR
 * INTR			->	READY
 * INTR			->	NIL
 * INTR			->	ABORTED
 * INTR			->	TIMED_OUT
 * ABORTED		->	NIL
 * TIMED_OUT		->	NIL
 */
typedef enum {
	PMCS_WORK_STATE_NIL = 0,
	PMCS_WORK_STATE_READY,
	PMCS_WORK_STATE_ONCHIP,
	PMCS_WORK_STATE_INTR,
	PMCS_WORK_STATE_IOCOMPQ,
	PMCS_WORK_STATE_ABORTED,
	PMCS_WORK_STATE_TIMED_OUT
} pmcs_work_state_t;

struct pmcwork {
	STAILQ_ENTRY(pmcwork)	next;
	kmutex_t		lock;
	kcondvar_t		sleep_cv;
	void			*ptr;	/* linkage or callback function */
	void 			*arg;	/* command specific data */
	pmcs_phy_t 		*phy;	/* phy who owns this command */
	pmcs_xscsi_t		*xp;	/* Back pointer to xscsi struct */
	volatile uint32_t	htag;	/* tag for this structure */
	uint32_t
			timer	:	27,
			onwire	:	1,
			dead	:	1,
			state	:	3;
	hrtime_t		start;	/* timestamp start */
	uint32_t		ssp_event; /* ssp event */
	pmcs_dtype_t		dtype;	/* stash, incase phy gets cleared */

	/* DEBUG-only fields from here on */
	void			*last_ptr;
	void			*last_arg;
	pmcs_phy_t		*last_phy;
	pmcs_xscsi_t		*last_xp;
	uint32_t		last_htag;
	pmcs_work_state_t	last_state;
	hrtime_t		finish;
};

#define	PMCS_REC_EVENT	0xffffffff	/* event recovery */

/*
 * This structure defines a PMC-Sierra defined firmware header.
 */
#pragma	pack(4)
typedef struct {
	char 		vendor_id[8];
	uint8_t		product_id;
	uint8_t		hwrev;
	uint8_t		destination_partition;
	uint8_t		reserved0;
	uint8_t		fwrev[4];
	uint32_t	firmware_length;
	uint32_t	crc;
	uint32_t	start_address;
	uint8_t		data[];
} pmcs_fw_hdr_t;
#pragma	pack()

/*
 * Offlevel work as a bit pattern.
 */
#define	PMCS_WORK_DISCOVER		0
#define	PMCS_WORK_REM_DEVICES		2
#define	PMCS_WORK_ABORT_HANDLE		3
#define	PMCS_WORK_SPINUP_RELEASE	4
#define	PMCS_WORK_SAS_HW_ACK		5
#define	PMCS_WORK_SATA_RUN		6
#define	PMCS_WORK_RUN_QUEUES		7
#define	PMCS_WORK_ADD_DMA_CHUNKS	8
#define	PMCS_WORK_DS_ERR_RECOVERY	9
#define	PMCS_WORK_SSP_EVT_RECOVERY	10

/*
 * The actual values as they appear in work_flags
 */
#define	PMCS_WORK_FLAG_DISCOVER		(1 << 0)
#define	PMCS_WORK_FLAG_REM_DEVICES	(1 << 2)
#define	PMCS_WORK_FLAG_ABORT_HANDLE	(1 << 3)
#define	PMCS_WORK_FLAG_SPINUP_RELEASE	(1 << 4)
#define	PMCS_WORK_FLAG_SAS_HW_ACK	(1 << 5)
#define	PMCS_WORK_FLAG_SATA_RUN		(1 << 6)
#define	PMCS_WORK_FLAG_RUN_QUEUES	(1 << 7)
#define	PMCS_WORK_FLAG_ADD_DMA_CHUNKS	(1 << 8)
#define	PMCS_WORK_FLAG_DS_ERR_RECOVERY	(1 << 9)
#define	PMCS_WORK_FLAG_SSP_EVT_RECOVERY (1 << 10)

/*
 * This structure is used by this function to test MPI (and interrupts)
 * after MPI has been started to make sure it's working reliably.
 */
typedef struct {
	uint32_t signature;
	uint32_t count;
	uint32_t *ptr;
} echo_test_t;
#define	ECHO_SIGNATURE	0xbebebeef

/*
 * Tag Architecture. The PMC has 32 bit tags for MPI messages.
 * We use this tag this way.
 *
 * bits		what
 * ------------------------
 * 31		done bit
 * 30..28	tag type
 * 27..12	rolling serial number
 * 11..0	index into work area to get pmcwork structure
 *
 * A tag type of NONE means that nobody is waiting on any results,
 * so the interrupt code frees the work structure that has this
 * tag.
 *
 * A tag type of CBACK means that the the interrupt handler
 * takes the tag 'arg' in the work structure to be a callback
 * function pointer (see pmcs_cb_t). The callee is responsible
 * for freeing the work structure that has this tag.
 *
 * A tag type of WAIT means that the issuer of the work needs
 * be woken up from interrupt level when the command completes
 * (or times out). If work structure tag 'arg' is non-null,
 * up to 2*PMCS_QENTRY_SIZE bits of data from the Outbound Queue
 * entry may be copied to the area pointed to by 'arg'. This
 * allows issuers to get directly at the results of the command
 * they issed. The synchronization point for the issuer and the
 * interrupt code for command done notification is the setting
 * of the 'DONE' bit in the tag as stored in the work structure.
 */
#define	PMCS_TAG_TYPE_FREE	0
#define	PMCS_TAG_TYPE_NONE	1
#define	PMCS_TAG_TYPE_CBACK  	2
#define	PMCS_TAG_TYPE_WAIT	3
#define	PMCS_TAG_TYPE_SHIFT	28
#define	PMCS_TAG_SERNO_SHIFT	12
#define	PMCS_TAG_INDEX_SHIFT	0
#define	PMCS_TAG_TYPE_MASK	0x70000000
#define	PMCS_TAG_DONE		0x80000000
#define	PMCS_TAG_SERNO_MASK	0x0ffff000
#define	PMCS_TAG_INDEX_MASK	0x00000fff
#define	PMCS_TAG_TYPE(x)		\
	(((x) & PMCS_TAG_TYPE_MASK) >> PMCS_TAG_TYPE_SHIFT)
#define	PMCS_TAG_SERNO(x)	\
	(((x) & PMCS_TAG_SERNO_MASK) >> PMCS_TAG_SERNO_SHIFT)
#define	PMCS_TAG_INDEX(x)	\
	(((x) & PMCS_TAG_INDEX_MASK) >> PMCS_TAG_INDEX_SHIFT)
#define	PMCS_TAG_FREE		0
#define	PMCS_COMMAND_DONE(x)	\
	(((x)->htag == PMCS_TAG_FREE) || (((x)->htag & PMCS_TAG_DONE) != 0))
#define	PMCS_COMMAND_ACTIVE(x)	\
	((x)->htag != PMCS_TAG_FREE && (x)->state == PMCS_WORK_STATE_ONCHIP)

/*
 * Miscellaneous Definitions
 */
#define	CLEAN_MESSAGE(m, x)	{	\
	int _j = x;			\
	while (_j < PMCS_MSG_SIZE) {	\
		m[_j++] = 0;		\
	}				\
}

#define	COPY_MESSAGE(t, f, a)	{	\
	int _j;				\
	for (_j = 0; _j < a; _j++) {	\
		t[_j] = f[_j];		\
	}				\
	while (_j < PMCS_MSG_SIZE) {	\
		t[_j++] = 0;		\
	}				\
}

#define	PMCS_PHY_ADDRESSABLE(pp)			\
	((pp)->level == 0 && (pp)->dtype == SATA &&	\
	    ((pp)->sas_address[0] >> 4) != 5)

#define	RESTART_DISCOVERY(pwp)				\
	ASSERT(!mutex_owned(&pwp->config_lock));	\
	mutex_enter(&pwp->config_lock);			\
	pwp->config_changed = B_TRUE;			\
	mutex_exit(&pwp->config_lock);			\
	SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);

#define	RESTART_DISCOVERY_LOCKED(pwp)			\
	ASSERT(mutex_owned(&pwp->config_lock));		\
	pwp->config_changed = B_TRUE;			\
	SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);

#define	PHY_CHANGED(pwp, p)						\
	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s changed in %s line %d", \
	    p->path, __func__, __LINE__);				\
	p->changed = 1

#define	PHY_CHANGED_AT_LOCATION(pwp, p, func, line)			\
	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s changed in %s line %d", \
	    p->path, func, line);					\
	p->changed = 1

#define	PHY_TYPE(pptr)					\
	(((pptr)->dtype == NOTHING)?  "NOTHING" :	\
	(((pptr)->dtype == SATA)? "SATA" :		\
	(((pptr)->dtype == SAS)? "SAS" : "EXPANDER")))

#define	IS_ROOT_PHY(pptr)	(pptr->parent == NULL)

#define	PMCS_HIPRI(pwp, oq, c)				\
	(pwp->hipri_queue & (1 << PMCS_IQ_OTHER)) ?	\
	(PMCS_IOMB_HIPRI | PMCS_IOMB_IN_SAS(oq, c)) :	\
	(PMCS_IOMB_IN_SAS(oq, c))

#define	SCHEDULE_WORK(hwp, wrk)		\
	(void) atomic_set_long_excl(&hwp->work_flags, wrk)

/*
 * Check to see if the requested work bit is set.  Either way, the bit will
 * be cleared upon return.
 */
#define	WORK_SCHEDULED(hwp, wrk)	\
	(atomic_clear_long_excl(&hwp->work_flags, wrk) == 0)

/*
 * Check to see if the requested work bit is set.  The value will not be
 * changed in this case.  The atomic_xx_nv operations can be quite expensive
 * so this should not be used in non-DEBUG code.
 */
#define	WORK_IS_SCHEDULED(hwp, wrk)	\
	((atomic_and_ulong_nv(&hwp->work_flags, (ulong_t)-1) & (1 << wrk)) != 0)

#define	WAIT_FOR(p, t, r)					\
	r = 0;							\
	while (!PMCS_COMMAND_DONE(p)) {				\
		clock_t tmp = cv_timedwait(&p->sleep_cv,	\
		    &p->lock, ddi_get_lbolt() +			\
		    drv_usectohz(t * 1000));			\
		if (!PMCS_COMMAND_DONE(p) && tmp < 0) {		\
			r = 1;					\
			break;					\
		}						\
	}

/*
 * Signal the next I/O completion thread to start running.
 */

#define	PMCS_CQ_RUN_LOCKED(hwp)						\
	if (!STAILQ_EMPTY(&hwp->cq) || hwp->iocomp_cb_head) {		\
		pmcs_cq_thr_info_t *cqti;				\
		cqti = &hwp->cq_info.cq_thr_info			\
		    [hwp->cq_info.cq_next_disp_thr];			\
		hwp->cq_info.cq_next_disp_thr++;			\
		if (hwp->cq_info.cq_next_disp_thr ==			\
		    hwp->cq_info.cq_threads) {				\
			hwp->cq_info.cq_next_disp_thr = 0;		\
		}							\
		mutex_enter(&cqti->cq_thr_lock);			\
		cv_signal(&cqti->cq_cv);				\
		mutex_exit(&cqti->cq_thr_lock);				\
	}								\

#define	PMCS_CQ_RUN(hwp)						\
	mutex_enter(&hwp->cq_lock);					\
	PMCS_CQ_RUN_LOCKED(hwp);					\
	mutex_exit(&hwp->cq_lock);


/*
 * Watchdog/SCSA timer definitions
 */
/* usecs to SCSA watchdog ticks */
#define	US2WT(x)	(x)/10

/*
 * More misc
 */
#define	BYTE0(x)	(((x) >>  0) & 0xff)
#define	BYTE1(x)	(((x) >>  8) & 0xff)
#define	BYTE2(x)	(((x) >> 16) & 0xff)
#define	BYTE3(x)	(((x) >> 24) & 0xff)
#define	BYTE4(x)	(((x) >> 32) & 0xff)
#define	BYTE5(x)	(((x) >> 40) & 0xff)
#define	BYTE6(x)	(((x) >> 48) & 0xff)
#define	BYTE7(x)	(((x) >> 56) & 0xff)
#define	WORD0(x)	(((x) >>  0) & 0xffff)
#define	WORD1(x)	(((x) >> 16) & 0xffff)
#define	WORD2(x)	(((x) >> 32) & 0xffff)
#define	WORD3(x)	(((x) >> 48) & 0xffff)
#define	DWORD0(x)	((uint32_t)(x))
#define	DWORD1(x)	((uint32_t)(((uint64_t)x) >> 32))

#define	SAS_ADDR_FMT	"0x%02x%02x%02x%02x%02x%02x%02x%02x"
#define	SAS_ADDR_PRT(x)	x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]

#define	PMCS_VALID_LINK_RATE(r) \
	((r == SAS_LINK_RATE_1_5GBIT) || (r == SAS_LINK_RATE_3GBIT) || \
	(r == SAS_LINK_RATE_6GBIT))

/*
 * This is here to avoid inclusion of <sys/ctype.h> which is not lint clean.
 */
#define	HEXDIGIT(x)	(((x) >= '0' && (x) <= '9') || \
	((x) >= 'a' && (x) <= 'f') || ((x) >= 'A' && (x) <= 'F'))


typedef void (*pmcs_cb_t) (pmcs_hw_t *, pmcwork_t *, uint32_t *);

/*
 * Defines and structure used for tracing/logging information
 */

#define	PMCS_TBUF_ELEM_SIZE	120

#ifdef DEBUG
#define	PMCS_TBUF_NUM_ELEMS_DEF	100000
#else
#define	PMCS_TBUF_NUM_ELEMS_DEF	15000
#endif

typedef struct {
	timespec_t	timestamp;
	char		buf[PMCS_TBUF_ELEM_SIZE];
} pmcs_tbuf_t;

/*
 * Firmware event log header format
 */

typedef struct pmcs_fw_event_hdr_s {
	uint32_t	fw_el_signature;
	uint32_t	fw_el_entry_start_offset;
	uint32_t	fw_el_rsvd1;
	uint32_t	fw_el_buf_size;
	uint32_t	fw_el_rsvd2;
	uint32_t	fw_el_oldest_idx;
	uint32_t	fw_el_latest_idx;
	uint32_t	fw_el_entry_size;
} pmcs_fw_event_hdr_t;

#ifdef	__cplusplus
}
#endif
#endif	/* _PMCS_DEF_H */