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