/*
* 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
* 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
*/
/*
*/
/*
* SiliconImage 3124/3132/3531 sata controller driver
*/
/*
*
*
* Few Design notes
*
*
* I. General notes
*
* Even though the driver is named as si3124, it is actually meant to
* work with SiI3124, SiI3132 and SiI3531 controllers.
*
* The current file si3124.c is the main driver code. The si3124reg.h
* holds the register definitions from SiI 3124/3132/3531 data sheets. The
* si3124var.h holds the driver specific definitions which are not
* directly derived from data sheets.
*
*
* II. Data structures
*
* si_ctl_state_t: This holds the driver private information for each
* controller instance. Each of the sata ports within a single
* controller are represented by si_port_state_t. The
* sictl_global_acc_handle and sictl_global_address map the
* controller-wide global register space and are derived from pci
* BAR 0. The sictl_port_acc_handle and sictl_port_addr map the
* per-port register space and are derived from pci BAR 1.
*
* si_port_state_t: This holds the per port information. The siport_mutex
* holds the per port mutex. The siport_pending_tags is the bit mask of
* commands posted to controller. The siport_slot_pkts[] holds the
* pending sata packets. The siport_port_type holds the device type
* connected directly to the port while the siport_portmult_state
* holds the similar information for the devices behind a port
* multiplier.
*
* si_prb_t: This contains the PRB being posted to the controller.
* The two SGE entries contained within si_prb_t itself are not
* really used to hold any scatter gather entries. The scatter gather
* list is maintained external to PRB and is linked from one
* of the contained SGEs inside the PRB. For atapi devices, the
* first contained SGE holds the PACKET and second contained
* SGE holds the link to an external SGT. For non-atapi devices,
* the first contained SGE works as link to external SGT while
* second SGE is blank.
*
* external SGT tables: The external SGT tables pointed to from
* within si_prb_t are actually abstracted as si_sgblock_t. Each
* si_sgblock_t contains si_dma_sg_number number of
* SGT tables linked in a chain. Currently this default value of
* SGT tables per block is at 85 as which translates
* to a maximum of 256 dma cookies per single dma transfer.
* This value can be changed through the global var: si_dma_sg_number
* dma cookies per single dma transfer.
*
*
* III. Driver operation
*
* Command Issuing: We use the "indirect method of command issuance". The
* PRB contains the command [and atapi PACKET] and a link to the
* external SGT chain. We write the physical address of the PRB into
* command activation register. There are 31 command slots for
* each port. After posting a command, we remember the posted slot &
* the sata packet in siport_pending_tags & siport_slot_pkts[]
* respectively.
*
* Command completion: On a successful completion, intr_command_complete()
* receives the control. The slot_status register holds the outstanding
* commands. Any reading of slot_status register automatically clears
* the interrupt. By comparing the slot_status register contents with
* per port siport_pending_tags, we determine which of the previously
* posted commands have finished.
*
* Timeout handling: Every 5 seconds, the watchdog handler scans thru the
* pending packets. The satapkt->satapkt_hba_driver_private field is
* overloaded with the count of watchdog cycles a packet has survived.
* If a packet has not completed within satapkt->satapkt_time, it is
* failed with error code of SATA_PKT_TIMEOUT. There is one watchdog
* handler running for each instance of controller.
*
* Error handling: For 3124, whenever any single command has encountered
* an error, the whole port execution completely stalls; there is no
* way of canceling or aborting the particular failed command. If
* the port is connected to a port multiplier, we can however RESUME
* other non-error devices connected to the port multiplier.
* The only way to recover the failed commands is to either initialize
* operations result in discarding any of pending commands on the port.
* All such discarded commands are sent up to framework with PKT_RESET
* satapkt_reason. The assumption is that framework [and sd] would
* retry these commands again. The failed command itself however is
* sent up with PKT_DEV_ERROR.
*
* Here is the implementation strategy based on SiliconImage email
* regarding how they handle the errors for their Windows driver:
*
* a) for DEVICEERROR:
* If the port is connected to port multiplier, then
* 1) Resume the port
* 2) Wait for all the non-failed commands to complete
* 3) Perform a Port Initialize
*
* If the port is not connected to port multiplier, issue
* a Port Initialize.
*
* b) for SDBERROR: [SDBERROR means failed command is an NCQ command]
* Handle exactly like DEVICEERROR handling.
* After the Port Initialize done, do a Read Log Extended.
*
* c) for SENDFISERROR:
* If the port is connected to port multiplier, then
* 1) Resume the port
* 2) Wait for all the non-failed commands to complete
* 3) Perform a Port Initialize
*
* If the port is not connected to port multiplier, issue
* a Device Reset.
*
* d) for DATAFISERROR:
* If the port was executing an NCQ command, issue a Device
* Reset.
*
* Otherwise, follow the same error recovery as DEVICEERROR.
*
* e) for any other error, simply issue a Device Reset.
*
* To synchronize the interactions between various control flows (e.g.
* error recovery, timeout handling, si_poll_timeout, incoming flow
* from framework etc.), the following precautions are taken care of:
* a) During mopping_in_progress, no more commands are
* accepted from the framework.
*
* b) While draining the port multiplier commands, we should
* handle the possibility of any of the other waited commands
* failing (possibly with a different error code)
*
* Atapi handling: For atapi devices, we use the first SGE within the PRB
* to fill the scsi cdb while the second SGE points to external SGT.
*
* Queuing: Queue management is achieved external to the driver inside sd.
* Based on sata_hba_tran->qdepth and IDENTIFY data, the framework
* enables or disables the queuing. The qdepth for si3124 is 31
* commands.
*
* Port Multiplier: Enumeration of port multiplier is handled during the
* controller initialization and also during the a hotplug operation.
* Current logic takes care of situation where a port multiplier
* is hotplugged into a port which had a cdisk connected previously
* and vice versa.
*
* Register poll timeouts: Currently most of poll timeouts on register
* reads is set to 0.5 seconds except for a value of 10 seconds
* while reading the device signature. [Such a big timeout values
* for device signature were found needed during cold reboots
* for devices behind port multiplier].
*
*
* IV. Known Issues
*
* 1) Currently the atapi packet length is hard coded to 12 bytes
* This is wrong. The framework should determine it just like they
* determine ad_cdb_len in legacy atapi.c. It should even reject
* init_pkt() for greater CDB lengths. See atapi.c. Revisit this
* in 2nd phase of framework project.
*
* 2) Do real REQUEST SENSE command instead of faking for ATAPI case.
*
*/
/*
* FMA header files
*/
/*
* Function prototypes for driver entry points
*/
static int si_power(dev_info_t *, int, int);
static int si_quiesce(dev_info_t *);
/*
* Function prototypes for SATA Framework interfaces
*/
static int si_register_sata_hba_tran(si_ctl_state_t *);
static int si_unregister_sata_hba_tran(si_ctl_state_t *);
/*
* Local function prototypes
*/
static int si_alloc_port_state(si_ctl_state_t *, int);
static void si_dealloc_port_state(si_ctl_state_t *, int);
static int si_alloc_sgbpool(si_ctl_state_t *, int);
static void si_dealloc_sgbpool(si_ctl_state_t *, int);
static int si_alloc_prbpool(si_ctl_state_t *, int);
static void si_dealloc_prbpool(si_ctl_state_t *, int);
int, int);
sata_pkt_t *);
sata_pkt_t *);
static int si_initialize_controller(si_ctl_state_t *);
static void si_deinitialize_controller(si_ctl_state_t *);
static void si_init_port(si_ctl_state_t *, int);
static int si_enumerate_port_multiplier(si_ctl_state_t *,
si_port_state_t *, int);
int, int, int, uint32_t *);
int, int, int, uint32_t);
static void si_set_sense_data(sata_pkt_t *, int);
static int si_intr_command_complete(si_ctl_state_t *,
si_port_state_t *, int);
static void si_schedule_intr_command_error(si_ctl_state_t *,
si_port_state_t *, int);
static void si_do_intr_command_error(void *);
static int si_intr_command_error(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_DEVICEERROR(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_SDBERROR(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_DATAFISERROR(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_SENDFISERROR(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_default(si_ctl_state_t *,
si_port_state_t *, int);
si_port_state_t *si_portp, int);
static int si_intr_decode_err_threshold(si_ctl_state_t *,
si_port_state_t *, int);
static int si_intr_handshake_err_threshold(si_ctl_state_t *,
si_port_state_t *, int);
static void si_enable_port_interrupts(si_ctl_state_t *, int);
static void si_enable_all_interrupts(si_ctl_state_t *);
static void si_disable_port_interrupts(si_ctl_state_t *, int);
static void si_disable_all_interrupts(si_ctl_state_t *);
static int si_add_legacy_intrs(si_ctl_state_t *);
static int si_add_msi_intrs(si_ctl_state_t *);
static void si_rem_intrs(si_ctl_state_t *);
static int si_reset_dport_wait_till_ready(si_ctl_state_t *,
si_port_state_t *, int, int);
static int si_clear_port(si_ctl_state_t *, int);
static void si_schedule_port_initialize(si_ctl_state_t *,
si_port_state_t *, int);
static void si_do_initialize_port(void *);
static int si_initialize_port_wait_till_ready(si_ctl_state_t *, int);
static void si_watchdog_handler(si_ctl_state_t *);
/*
* FMA Prototypes
*/
static void si_fm_init(si_ctl_state_t *);
static void si_fm_fini(si_ctl_state_t *);
static int si_check_acc_handle(ddi_acc_handle_t);
static int si_check_dma_handle(ddi_dma_handle_t);
static int si_check_ctl_handles(si_ctl_state_t *);
static int si_check_port_handles(si_port_state_t *);
static void si_fm_ereport(si_ctl_state_t *, char *, char *);
/*
* DMA attributes for the data buffer
*/
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo: lowest bus address */
0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */
0xffffffffull, /* dma_attr_count_max i.e. for one cookie */
1, /* dma_attr_align: single byte aligned */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */
0xffffffffull, /* dma_attr_seg */
SI_DEFAULT_SGL_LENGTH, /* dma_attr_sgllen */
512, /* dma_attr_granular */
0, /* dma_attr_flags */
};
/*
* DMA attributes for incore RPB and SGT pool
*/
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo: lowest bus address */
0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */
0xffffffffull, /* dma_attr_count_max i.e. for one cookie */
8, /* dma_attr_align: quad word aligned */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */
0xffffffffull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0, /* dma_attr_flags */
};
/* Device access attributes */
};
DEVO_REV, /* devo_rev */
0, /* refcnt */
si_getinfo, /* info */
nulldev, /* identify */
nulldev, /* probe */
si_attach, /* attach */
si_detach, /* detach */
nodev, /* no reset */
(struct cb_ops *)0, /* driver operations */
NULL, /* bus operations */
si_power, /* power */
si_quiesce, /* devo_quiesce */
};
};
static int si_watchdog_tick;
extern struct mod_ops mod_driverops;
&mod_driverops, /* driverops */
"si3124 driver",
&sictl_dev_ops, /* driver ops */
};
&modldrv,
};
/* The following are needed for si_log() */
static int is_msi_supported = 0;
/*
*
* si_dma_sg_number
*/
/* Opaque state pointer to be initialized by ddi_soft_state_init() */
/*
* si3124 module initialization.
*
*/
int
_init(void)
{
int error;
if (error != 0) {
return (error);
}
return (error);
}
if (error != 0) {
return (error);
}
return (error);
}
/*
* si3124 module uninitialize.
*
*/
int
_fini(void)
{
int error;
if (error != 0) {
return (error);
}
/* Remove the resources allocated in _init(). */
return (error);
}
/*
* _info entry point
*
*/
int
{
}
/*
* The attach entry point for dev_ops.
*
* We initialize the controller, initialize the soft state, register
* the interrupt handlers and then register ourselves with sata framework.
*/
static int
{
int instance;
int status;
int attach_state;
int intr_types;
switch (cmd) {
case DDI_ATTACH:
/* Allocate si_softc. */
if (status != DDI_SUCCESS) {
goto err_out;
}
/* Initialize FMA */
/* Configure pci config space handle. */
if (status != DDI_SUCCESS) {
goto err_out;
}
switch (si_ctlp->sictl_devid) {
case SI3124_DEV_ID:
break;
case SI3132_DEV_ID:
break;
case SI3531_DEV_ID:
break;
default:
/*
* Driver should not have attatched if device
* ID is not already known and is supported.
*/
goto err_out;
}
/* Now map the bar0; the bar0 contains the global registers. */
0,
0,
&accattr,
if (status != DDI_SUCCESS) {
goto err_out;
}
/* Now map bar1; the bar1 contains the port registers. */
0,
0,
&accattr,
if (status != DDI_SUCCESS) {
goto err_out;
}
/*
* Disable all the interrupts before adding interrupt
* handler(s). The interrupts shall be re-enabled selectively
* out of si_init_port().
*/
/* Get supported interrupt types. */
!= DDI_SUCCESS) {
"ddi_intr_get_supported_types failed", NULL);
goto err_out;
}
"ddi_intr_get_supported_types() returned: 0x%x",
"Using MSI interrupt type", NULL);
/*
* Try MSI first, but fall back to legacy if MSI
* attach fails.
*/
"MSI interrupt setup done", NULL);
} else {
"MSI registration failed "
"will try Legacy interrupts", NULL);
}
}
if (!(attach_state & ATTACH_PROGRESS_INTR_ADDED) &&
(intr_types & DDI_INTR_TYPE_FIXED)) {
/*
* Either the MSI interrupt setup has failed or only
* fixed interrupts are available on the system.
*/
"Using Legacy interrupt type", NULL);
"Legacy interrupt setup done", NULL);
} else {
"legacy interrupt setup failed", NULL);
goto err_out;
}
}
if (!(attach_state & ATTACH_PROGRESS_INTR_ADDED)) {
"si3124: No interrupts registered", NULL);
goto err_out;
}
/* Initialize the mutex. */
/*
* Initialize the controller and driver core.
*/
if (status) {
goto err_out;
}
if (si_register_sata_hba_tran(si_ctlp)) {
"si3124: setting sata hba tran failed", NULL);
goto err_out;
}
(void (*)(void *))si_watchdog_handler,
return (DDI_SUCCESS);
case DDI_RESUME:
if (status) {
return (DDI_FAILURE);
}
(void (*)(void *))si_watchdog_handler,
/* Notify SATA framework about RESUME. */
DDI_RESUME) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Notify the "framework" that it should reprobe ports to see
* if any device got changed while suspended.
*/
"sending event up: SATA_EVNT_PWR_LEVEL_CHANGED", NULL);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (attach_state & ATTACH_PROGRESS_HW_INIT) {
/* We want to set SI_DETACH to deallocate all memory */
}
if (attach_state & ATTACH_PROGRESS_MUTEX_INIT) {
}
if (attach_state & ATTACH_PROGRESS_INTR_ADDED) {
}
if (attach_state & ATTACH_PROGRESS_BAR1_MAP) {
}
if (attach_state & ATTACH_PROGRESS_BAR0_MAP) {
}
if (attach_state & ATTACH_PROGRESS_CONF_HANDLE) {
}
if (attach_state & ATTACH_PROGRESS_INIT_FMA) {
}
if (attach_state & ATTACH_PROGRESS_STATEP_ALLOC) {
}
return (DDI_FAILURE);
}
/*
* The detach entry point for dev_ops.
*
* We undo the things we did in si_attach().
*/
static int
{
int instance;
switch (cmd) {
case DDI_DETACH:
/* disable the interrupts for an uninterrupted detach */
/* unregister from the sata framework. */
return (DDI_FAILURE);
}
/* now cancel the timeout handler. */
/* de-initialize the controller. */
/* destroy any mutexes */
/* remove the interrupts */
/* remove the reg maps. */
/* deinit FMA */
/* free the soft state. */
return (DDI_SUCCESS);
case DDI_SUSPEND:
/* Inform SATA framework */
return (DDI_FAILURE);
}
/*
* Device needs to be at full power in case it is needed to
* handle dump(9e) to save CPR state after DDI_SUSPEND
* completes. This is OK since presumably power will be
* removed anyways. No outstanding transactions should be
* on the controller since the children are already quiesced.
*
* hardware, those entry points will need to check for
* suspend and then block or return errors until resume.
*
*/
DDI_SUCCESS) {
}
instance);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
#ifndef __lock_lint
#endif /* __lock_lint */
int old_level;
return (DDI_FAILURE);
}
switch (level) {
case PM_LEVEL_D0: /* fully on */
#ifndef __lock_lint
#endif /* __lock_lint */
"si3124%d: turning power ON. old level %d",
/*
* If called from attach, just raise device power,
* restore config registers (if they were saved
* from a previous detach that lowered power),
* and exit.
*/
break;
(void) si_initialize_controller(si_ctlp);
(void (*)(void *))si_watchdog_handler,
"sending event up: PWR_LEVEL_CHANGED", NULL);
break;
case PM_LEVEL_D3: /* fully off */
}
break;
default:
rval = DDI_FAILURE;
break;
}
return (rval);
}
/*
* The info entry point for dev_ops.
*
*/
static int
void *arg,
void **result)
{
#ifndef __lock_lint
#endif /* __lock_lint */
int instance;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_SUCCESS);
} else {
return (DDI_FAILURE);
}
case DDI_INFO_DEVT2INSTANCE:
break;
default:
break;
}
return (DDI_SUCCESS);
}
/*
* Registers the si3124 with sata framework.
*/
static int
{
"si_register_sata_hba_tran entry", NULL);
/* Allocate memory for the sata_hba_tran */
} else if (si_dma_sg_number < SI_MIN_SGT_TABLES_PER_PRB) {
}
}
/* Attach it to SATA framework */
!= DDI_SUCCESS) {
return (SI_FAILURE);
}
return (SI_SUCCESS);
}
/*
* Unregisters the si3124 with sata framework.
*/
static int
{
/* Detach from the SATA framework. */
DDI_SUCCESS) {
return (SI_FAILURE);
}
/* Deallocate sata_hba_tran. */
sizeof (sata_hba_tran_t));
return (SI_SUCCESS);
}
/*
* Called by sata framework to probe a port. We return the
* cached information from a previous hardware probe.
*
* The actual hardware probing itself was done either from within
* si_initialize_controller() during the driver attach or
* from a phy ready change interrupt handler.
*/
static int
{
"si_tran_probe_port: cport: 0x%x, pmport: 0x%x, qual: 0x%x",
if (cport >= SI_MAX_PORTS) {
return (SATA_FAILURE);
}
return (SATA_FAILURE);
}
if (qual == SATA_ADDR_PMPORT) {
return (SATA_FAILURE);
} else {
}
} else {
}
switch (port_type) {
case PORT_TYPE_DISK:
break;
case PORT_TYPE_ATAPI:
break;
case PORT_TYPE_MULTIPLIER:
break;
case PORT_TYPE_UNKNOWN:
break;
default:
/* we don't support any other device types. */
break;
}
if (qual == SATA_ADDR_PMPORT) {
} else {
if (!(si_portp->siport_active)) {
/*
* Since we are implementing the port deactivation
* in software only, we need to fake a valid value
* for sstatus when the device is in deactivated state.
*/
}
}
return (SATA_SUCCESS);
}
/*
* Called by sata framework to transport a sata packet down stream.
*
* The actual work of building the FIS & transporting it to the hardware
* is done out of the subroutine si_deliver_satapkt().
*/
static int
{
int slot;
"si_tran_start entry", NULL);
!si_portp->siport_active) {
/*
* si_intr_phy_ready_change() may have rendered it to
* PORT_TYPE_NODEV. cfgadm operation may have rendered
* it inactive.
*/
return (SATA_TRAN_PORT_ERROR);
}
"si_tran_start clearing the "
"reset_in_progress for port", NULL);
}
if (si_portp->siport_reset_in_progress &&
! ddi_in_panic()) {
"si_tran_start returning BUSY while "
"reset in progress for port", NULL);
return (SATA_TRAN_BUSY);
}
if (si_portp->mopping_in_progress > 0) {
"si_tran_start returning BUSY while "
"mopping in progress for port", NULL);
return (SATA_TRAN_BUSY);
}
== SI_FAILURE) {
"si_tran_start returning QUEUE_FULL",
NULL);
return (SATA_TRAN_QUEUE_FULL);
}
/* we need to poll now */
/*
* The command has completed, and spkt will be freed by the
* sata module, so don't keep a pointer to it lying around.
*/
}
return (SATA_TRAN_ACCEPTED);
}
if (satapkt) { \
SATAC_WRITE_FPDMA_QUEUED) || \
SATAC_READ_FPDMA_QUEUED)) { \
} \
/* \
* We set the satapkt_reason in both synch and \
* non-synch cases. \
*/ \
satapkt->satapkt_comp) { \
} \
}
/*
* Mopping is necessitated because of the si3124 hardware limitation.
* The only way to recover from errors or to abort a command is to
* away all the unfinished pending commands.
*
* A port or device is reset in four scenarios:
* a) some commands failed with errors
* b) or we need to timeout some commands
* c) or we need to abort some commands
* d) or we need reset the port at the request of sata framework
*
* In all these scenarios, we need to send any pending unfinished
* commands up to sata framework.
*
* WARNING!!! siport_mutex should be acquired before the function is called.
*/
static void
{
int tmpslot;
"si_mop_commands entered: slot_status: 0x%x",
"si_mop_commands: failed_tags: 0x%x, timedout_tags: 0x%x"
"aborting_tags: 0x%x, reset_tags: 0x%x",
/*
* We could be here for four reasons: abort, reset,
* timeout or error handling. Only one such mopping
* is allowed at a time.
*/
~failed_tags &
~reset_tags &
/* Send up the finished_tags with SATA_PKT_COMPLETED. */
while (finished_tags) {
if (tmpslot == -1) {
break;
}
}
"si_mop_commands sending up completed satapkt: %x",
satapkt);
}
ASSERT(finished_tags == 0);
/* Send up failed_tags with SATA_PKT_DEV_ERROR. */
while (failed_tags) {
if (tmpslot == -1) {
break;
}
"handling failed slot: 0x%x", tmpslot);
}
/*
* In the case of NCQ command failures, the error is
* overwritten by the one obtained from issuing of a
* READ LOG EXTENDED command.
*/
if (si_portp->siport_err_tags_SDBERROR &
(1 << tmpslot)) {
}
}
}
ASSERT(failed_tags == 0);
/* Send up timedout_tags with SATA_PKT_TIMEOUT. */
while (timedout_tags) {
if (tmpslot == -1) {
break;
}
"si_mop_commands sending "
"spkt up with PKT_TIMEOUT: %x",
satapkt);
}
ASSERT(timedout_tags == 0);
/* Send up aborting packets with SATA_PKT_ABORTED. */
while (aborting_tags) {
if (tmpslot == -1) {
break;
}
"si_mop_commands aborting spkt: %x",
satapkt);
}
}
ASSERT(aborting_tags == 0);
/* Reset tags are sent up to framework with SATA_PKT_RESET. */
while (reset_tags) {
if (tmpslot == -1) {
break;
}
"si_mop_commands sending PKT_RESET for "
"reset spkt: %x",
satapkt);
}
ASSERT(reset_tags == 0);
/* Send up the unfinished_tags with SATA_PKT_RESET. */
while (unfinished_tags) {
if (tmpslot == -1) {
break;
}
"si_mop_commands sending SATA_PKT_RESET for "
"retry spkt: %x",
satapkt);
}
ASSERT(unfinished_tags == 0);
}
/*
* Called by the sata framework to abort the previously sent packet(s).
*
* We reset the device and mop the commands on the port.
*/
static int
{
int tmpslot;
/*
* If already mopping, then no need to abort anything.
*/
if (si_portp->mopping_in_progress > 0) {
"si_tran_abort: port %d mopping "
"in progress, so just return", port);
return (SATA_SUCCESS);
}
!si_portp->siport_active) {
/*
* si_intr_phy_ready_change() may have rendered it to
* PORT_TYPE_NODEV. cfgadm operation may have rendered
* it inactive.
*/
return (SATA_FAILURE);
}
if (flag == SATA_ABORT_ALL_PACKETS) {
} else {
/*
* Need to abort a single packet.
* Search our siport_slot_pkts[] list for matching spkt.
*/
break;
}
}
if (aborting_tags == 0xffffffff) {
/* requested packet is not on pending list. */
&spkt->satapkt_device);
return (SATA_FAILURE);
}
}
/*
* Compute which have finished and which need to be retried.
*
* The finished tags are siport_pending_tags minus the slot_status.
* The aborting_tags have to be reduced by finished_tags since we
* can't possibly abort a tag which had finished already.
*/
port,
0, /* failed_tags */
0, /* timedout_tags */
0); /* reset_tags */
return (SATA_SUCCESS);
}
/*
* Used to reject all the pending packets on a port during a reset
* operation.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
int port)
{
"si_reject_all_reset_pkts on port: %x",
port);
/* Compute which tags need to be sent up. */
port,
0, /* failed_tags */
0, /* timedout_tags */
0, /* aborting_tags */
}
/*
* Called by sata framework to reset a port(s) or device.
*
*/
static int
{
int i;
"si_tran_reset_port entry: port: 0x%x",
port);
case SATA_ADDR_CPORT:
/*
* If already mopping, then no need to reset or mop again.
*/
if (si_portp->mopping_in_progress > 0) {
"si_tran_reset_dport: CPORT port %d mopping "
"in progress, so just return", port);
retval = SI_SUCCESS;
break;
}
break;
case SATA_ADDR_DCPORT:
!si_portp->siport_active) {
retval = SI_FAILURE;
break;
}
/*
* If already mopping, then no need to reset or mop again.
*/
if (si_portp->mopping_in_progress > 0) {
"si_tran_reset_dport: DCPORT port %d mopping "
"in progress, so just return", port);
retval = SI_SUCCESS;
break;
}
break;
case SATA_ADDR_CNTRL:
for (i = 0; i < si_ctlp->sictl_num_ports; i++) {
/*
* If mopping, then all the pending commands are being
* mopped, therefore there is nothing else to do.
*/
if (si_portp->mopping_in_progress > 0) {
"si_tran_reset_dport: CNTRL port %d mopping"
" in progress, so just return", i);
retval = SI_SUCCESS;
break;
}
si_portp, i, SI_PORT_RESET);
if (retval) {
break;
}
}
break;
case SATA_ADDR_PMPORT:
case SATA_ADDR_DPMPORT:
"port mult reset not implemented yet", NULL);
/* FALLSTHROUGH */
default:
retval = SI_FAILURE;
}
return (retval);
}
/*
* Called by sata framework to activate a port as part of hotplug.
*
* Note: Not port-mult aware.
*/
static int
{
NULL);
/*
* Reset the device so that a si_find_dev_signature() would trigger.
* But this reset is an internal operation; the sata framework does
* not need to know about it.
*/
return (SATA_SUCCESS);
}
/*
* Called by sata framework to deactivate a port as part of hotplug.
*
* Note: Not port-mult aware.
*/
static int
{
/*
* There are pending commands on this port.
* Fail the deactivate request.
*/
return (SATA_FAILURE);
}
/* mark the device as not accessible any more. */
/* disable the interrupts on the port. */
/*
* Since we are implementing the port deactivation in software only,
* we need to fake a valid value for sstatus.
*/
return (SATA_SUCCESS);
}
/*
* Allocates the si_port_state_t.
*/
static int
{
sizeof (si_port_state_t), KM_SLEEP);
/* allocate prb & sgt pkts for this port. */
return (SI_FAILURE);
}
return (SI_FAILURE);
}
/* Allocate the argument for the timeout */
return (SI_SUCCESS);
}
/*
* Deallocates the si_port_state_t.
*/
static void
{
}
/*
* Allocates the SGB (Scatter Gather Block) incore buffer.
*/
static int
{
/* allocate sgbpool dma handle. */
NULL,
DDI_SUCCESS) {
return (SI_FAILURE);
}
/* allocate the memory for sgbpool. */
&accattr,
NULL,
&ret_len,
/* error.. free the dma handle. */
return (SI_FAILURE);
}
/* now bind it */
NULL,
NULL,
&cookie_count) != DDI_DMA_MAPPED) {
/* error.. free the dma handle & free the memory. */
return (SI_FAILURE);
}
return (SI_SUCCESS);
}
/*
* Deallocates the SGB (Scatter Gather Block) incore buffer.
*/
static void
{
/* Unbind the dma handle first. */
/* Then free the underlying memory. */
/* Now free the handle itself. */
}
/*
* Allocates the PRB (Port Request Block) incore packets.
*/
static int
{
/* allocate prb pkts. */
NULL,
DDI_SUCCESS) {
return (SI_FAILURE);
}
&accattr,
NULL,
&ret_len,
/* error.. free the dma handle. */
return (SI_FAILURE);
}
NULL,
NULL,
&cookie_count) != DDI_DMA_MAPPED) {
/* error.. free the dma handle & free the memory. */
return (SI_FAILURE);
}
return (SI_SUCCESS);
}
/*
* Deallocates the PRB (Port Request Block) incore packets.
*/
static void
{
/* Unbind the prb dma handle first. */
/* Then free the underlying memory. */
/* Now free the handle itself. */
}
/*
* Soft-reset the port to find the signature of the device connected to
* the port.
*/
static void
int port,
int pmp)
{
"si_find_dev_signature enter: port: %x, pmp: %x",
/* Build a Soft Reset PRB in host memory. */
if (slot == SI_FAILURE) {
/* Empty slot could not be found. */
if (pmp != PORTMULT_CONTROL_PORT) {
/* We are behind port multiplier. */
} else {
}
return;
}
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
char *ptr;
int j;
for (j = 0; j < (sizeof (si_prb_t)); j++) {
if (j%4 == 0) {
}
}
}
#endif /* SI_DEBUG */
/* deliver soft reset prb to empty slot. */
loop_count = 0;
/* Loop till the soft reset is finished. */
do {
if (loop_count++ > SI_POLLRATE_SOFT_RESET) {
/* We are effectively timing out after 10 sec. */
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
"si_find_dev_signature: loop count: %d, slot_status: 0x%x",
/* Read device signature from command slot. */
signature <<= 8;
if (signature == SI_SIGNATURE_PORT_MULTIPLIER) {
"Found multiplier at cport: 0x%d, pmport: 0x%x",
if (pmp != PORTMULT_CONTROL_PORT) {
/*
* It is wrong to chain a port multiplier behind
* another port multiplier.
*/
} else {
(void) si_enumerate_port_multiplier(si_ctlp,
}
} else if (signature == SI_SIGNATURE_ATAPI) {
if (pmp != PORTMULT_CONTROL_PORT) {
/* We are behind port multiplier. */
} else {
}
"Found atapi at : cport: %x, pmport: %x",
} else if (signature == SI_SIGNATURE_DISK) {
if (pmp != PORTMULT_CONTROL_PORT) {
/* We are behind port multiplier. */
} else {
}
"found disk at : cport: %x, pmport: %x",
} else {
if (pmp != PORTMULT_CONTROL_PORT) {
/* We are behind port multiplier. */
} else {
}
"Found unknown signature 0x%x at: port: %x, pmp: %x",
}
}
/*
* Polls for the completion of the command. This is safe with both
* interrupts enabled or disabled.
*/
static void
int port,
int slot,
{
int pkt_timeout_ticks;
1000000);
/* we start out with SATA_PKT_COMPLETED as the satapkt_reason */
do {
if (in_panic) {
/*
* If we are in panic, we can't rely on
* timers; so, busy wait instead of delay().
*/
} else {
#ifndef __lock_lint
#endif /* __lock_lint */
}
} else {
break;
}
} while (pkt_timeout_ticks > 0);
/* The si_mop_command() got to our packet before us */
return;
}
/*
* Interrupts and timers may not be working properly in a crash dump
* situation; we may need to handle all the three conditions here:
* successful completion, packet failure and packet timeout.
*/
"si_poll_cmd: port_intr_status: 0x%x, port: %x",
if (port_intr_status & INTR_COMMAND_ERROR) {
return;
/*
* Why do we need to call si_intr_command_error() ?
*
* Answer: Even if the current packet is not the
* offending command, we need to restart the stalled
* port; (may be, the interrupts are not working well
* in panic condition). The call to routine
* si_intr_command_error() will achieve that.
*
* What if the interrupts are working fine and the
* si_intr_command_error() gets called once more from
* interrupt context ?
*
* Answer: The second instance of routine
* si_intr_command_error() will not mop anything
* since the first error handler has already blown
* away the hardware pending queues through reset.
*
* Will the si_intr_command_error() hurt current
* packet ?
*
* Answer: No.
*/
} else {
/* Ignore any non-error interrupts at this stage */
port)),
}
} /* else: the command completed successfully */
}
}
/*
* tidbit: What is the interaction of abort with polling ?
* What happens if the current polled pkt is aborted in parallel ?
*
* Answer: Assuming that the si_mop_commands() completes ahead
* of polling, all it does is to set the satapkt_reason to
* SPKT_PKT_ABORTED. That would be fine with us.
*
* The same logic applies to reset interacting with polling.
*/
}
/*
* Searches for and claims a free slot.
*
* Returns: SI_FAILURE if no slots found
* claimed slot number if successful
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
/*ARGSUSED*/
static int
{
int slot;
"si_claim_free_slot entry: siport_pending_tags: %x",
if (slot == -1) {
"si_claim_free_slot: no empty slots", NULL);
return (SI_FAILURE);
}
slot);
return (slot);
}
/*
* Builds the PRB for the sata packet and delivers it to controller.
*
* Returns:
* slot number if we can obtain a slot successfully
* otherwise, return SI_FAILURE
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static int
int port,
{
int slot;
int i, j, cookie_index;
int ncookies;
int is_atapi = 0;
if (slot == SI_FAILURE) {
return (SI_FAILURE);
}
is_atapi = 1;
}
!si_portp->siport_active) {
/*
* si_intr_phy_ready_change() may have rendered it to
* PORT_TYPE_NODEV. cfgadm operation may have rendered
* it inactive.
*/
return (SI_FAILURE);
}
"si_deliver_satpkt entry: cmd_reg: 0x%x, slot: 0x%x, \
port: %x, satapkt: %x",
/* Now fill the prb. */
if (is_atapi) {
== SATA_DIR_WRITE) {
}
}
}
switch (cmd->satacmd_addr_type) {
case 0:
/*
* satacmd_addr_type will be 0 for the commands below:
* SATAC_PACKET
* SATAC_IDLE_IM
* SATAC_STANDBY_IM
* SATAC_DOWNLOAD_MICROCODE
* SATAC_FLUSH_CACHE
* SATAC_SET_FEATURES
* SATAC_SMART
* SATAC_ID_PACKET_DEVICE
* SATAC_ID_DEVICE
* SATAC_READ_PORTMULT
* SATAC_WRITE_PORTMULT
*/
/* FALLTHRU */
case ATA_ADDR_LBA:
/* FALLTHRU */
case ATA_ADDR_LBA28:
/* LBA[7:0] */
/* LBA[15:8] */
/* LBA[23:16] */
/* LBA [27:24] (also called dev_head) */
break;
case ATA_ADDR_LBA48:
/* LBA[7:0] */
/* LBA[15:8] */
/* LBA[23:16] */
/* LBA [31:24] */
/* LBA [39:32] */
/* LBA [47:40] */
/* Set dev_head */
/* Set the extended sector count and features */
break;
}
/*
* For queued commands, the TAG for the sector count lsb is
* generated from current slot number.
*/
}
}
/* *** now fill the scatter gather list ******* */
if (is_atapi) { /* It is an ATAPI drive */
/* atapi command goes into sge0 */
/* Now fill sge1 with pointer to external SGT. */
} else {
}
} else {
/* Fill the sge0 */
} else {
}
/* sge1 is left empty in non-ATAPI case */
}
sizeof (si_sgblock_t) * si_dma_sg_number);
if (ncookies == 0) {
/* No cookies. Terminate the chain. */
NULL);
sgep->sge_addr_low = 0;
sgep->sge_addr_high = 0;
sgep->sge_data_count = 0;
SET_SGE_TRM((*sgep));
goto sgl_fill_done;
}
for (i = 0, cookie_index = 0,
i < si_dma_sg_number; i++) {
/* Now fill the first 3 entries of SGT in the loop below. */
j++, cookie_index++, sgep++) {
"inner loop: cookie_index: %d, ncookies: %d",
ncookies);
}
/*
* If this happens to be the last cookie, we terminate it here.
* Otherwise, we link to next SGT.
*/
/* This is the last cookie. Terminate the chain. */
"filling the last: cookie_index: %d, "
"ncookies: %d",
ncookies);
SET_SGE_TRM((*sgep));
break; /* we break the loop */
} else {
/* This is not the last one. So link it. */
"linking SGT: cookie_index: %d, ncookies: %d",
ncookies);
(i+1) * sizeof (si_sgt_t);
SET_SGE_LNK((*sgep));
}
}
/* *** finished filling the scatter gather list ******* */
/* Now remember the sata packet in siport_slot_pkts[]. */
/*
* We are overloading satapkt_hba_driver_private with
* watched_cycle count.
*/
if (is_atapi) {
/* program the packet_lenth if it is atapi device. */
#ifdef ATAPI_2nd_PHASE
/*
* Framework needs to calculate the acdb_len based on
* identify packet data. This needs to be accomplished
* in second phase of the project.
*/
} else {
}
#else /* ATAPI_2nd_PHASE */
/* hard coding for now to 12 bytes */
#endif /* ATAPI_2nd_PHASE */
}
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
/*
* Do not dump the atapi Test-Unit-Ready commands.
* The sd_media_watch spews too many of these.
*/
int *ptr;
int j;
for (j = 0; j < (sizeof (si_prb_t)/4); j++) {
}
"si_deliver_satpkt sgt: low, high, count link");
for (j = 0,
j < (sizeof (si_sgblock_t)/ sizeof (si_sge_t))
j++, tmpsgep++) {
ptr[0],
ptr[1],
ptr[2],
ptr[3]);
if (IS_SGE_TRM_SET((*tmpsgep))) {
break;
}
}
}
}
#endif /* SI_DEBUG */
/* Deliver PRB */
return (slot);
}
/*
* Initialize the controller and set up driver data structures.
*
* This routine can be called from three separate cases: DDI_ATTACH, PM_LEVEL_D0
* and DDI_RESUME. The DDI_ATTACH case is different from other two cases; the
* memory allocation & device signature probing are attempted only during
* DDI_ATTACH case. In the case of PM_LEVEL_D0 & DDI_RESUME, we are starting
* from a previously initialized state; so there is no need to allocate memory
* or to attempt probing the device signatures.
*/
static int
{
int loop_count = 0;
"si3124: si_initialize_controller entered", NULL);
/* Remove the Global Reset. */
/*
* We allocate the port state only during attach
* sequence. We don't want to do it during
*/
return (SI_FAILURE);
}
}
/* Clear Port Reset. */
/*
* Arm the interrupts for: Cmd completion, Cmd error,
* Port Ready, PM Change, PhyRdyChange, Commwake,
* UnrecFIS, Devxchanged, SDBNotify.
*/
/* Now enable the interrupts. */
/*
* The following PHY initialization is redundant in
* in x86 since the BIOS anyway does this as part of
* device enumeration during the power up. But this
* is a required step in sparc since there is no BIOS.
*
* The way to initialize the PHY is to write a 1 and then
* a 0 to DET field of SControl register.
*/
/*
* Fetch the current SControl before writing the
* DET part with 1
*/
SControl);
#ifndef __lock_lint
#endif /* __lock_lint */
/*
* Now fetch the SControl again and rewrite the
* DET part with 0
*/
SControl);
/*
* PHY may be initialized by now. Check the DET field of
* SStatus to determine if there is a device present.
*
* The DET field is valid only if IPM field indicates that
* the interface is in active state.
*/
loop_count = 0;
do {
if (SSTATUS_GET_IPM(SStatus) !=
/*
* If the interface is not active, the DET field
* is considered not accurate. So we want to
* continue looping.
*/
}
if (loop_count++ > SI_POLLRATE_SSTATUS) {
/*
* We are effectively timing out after 0.1 sec.
*/
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
} while (SSTATUS_GET_DET(SStatus) !=
"si_initialize_controller: 1st loop count: %d, "
"SStatus: 0x%x",
SStatus);
if ((SSTATUS_GET_IPM(SStatus) !=
(SSTATUS_GET_DET(SStatus) !=
/*
* Either the port is not active or there
* is no device present.
*/
continue;
}
/* Wait until Port Ready */
loop_count = 0;
do {
if (loop_count++ > SI_POLLRATE_PORTREADY) {
/*
* We are effectively timing out after 0.5 sec.
*/
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
} while (!(port_status & PORT_STATUS_BITS_PORT_READY));
"si_initialize_controller: 2nd loop count: %d",
/*
* We want to probe for dev signature only during attach
*/
if (port_status & PORT_STATUS_BITS_PORT_READY) {
} else {
}
}
return (SI_FAILURE);
}
}
return (SI_SUCCESS);
}
/*
* Reverse of si_initialize_controller().
*
* WARNING, WARNING: The caller is expected to obtain the sictl_mutex
* before calling us.
*/
static void
{
int port;
"si3124: si_deinitialize_controller entered", NULL);
/* disable all the interrupts. */
/*
* We want to dealloc all the memory in detach case.
*/
}
}
}
/*
* Prepare the port ready for usage.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
{
"si_init_port entered: port: 0x%x",
port);
/* Initialize the port. */
/*
* Clear the InterruptNCOR (Interrupt No Clear on Read).
* This step ensures that a mere reading of slot_status will clear
* the interrupt; no explicit clearing of interrupt condition
* will be needed for successful completion of commands.
*/
/* clear any pending interrupts at this point */
}
/*
* Enumerate the devices connected to the port multiplier.
* Once a device is detected, we call si_find_dev_signature()
* to find the type of device connected. Even though we are
* called from within si_find_dev_signature(), there is no
* recursion possible.
*/
static int
int port)
{
int pmport;
int loop_count = 0;
"si_enumerate_port_multiplier entered: port: %d",
port);
/* Enable Port Multiplier context switching. */
/*
* Read the num dev ports connected.
* GSCR[2] contains the number of device ports.
*/
PSCR_REG2, &num_dev_ports)) {
return (SI_FAILURE);
}
"si_enumerate_port_multiplier: ports found: %d",
/*
* Enable PHY by writing a 1, then a 0 to SControl
* (i.e. PSCR[2]) DET field.
*/
continue;
}
/* First write a 1 to DET field of SControl. */
continue;
}
#ifndef __lock_lint
#endif /* __lock_lint */
/* Then write a 0 to the DET field of SControl. */
continue;
}
/* Wait for PHYRDY by polling SStatus (i.e. PSCR[0]). */
loop_count = 0;
do {
break;
}
"looping for PHYRDY: SStatus: %x",
SStatus);
if (SSTATUS_GET_IPM(SStatus) !=
/*
* If the interface is not active, the DET field
* is considered not accurate. So we want to
* continue looping.
*/
}
if (loop_count++ > SI_POLLRATE_SSTATUS) {
/*
* We are effectively timing out after 0.1 sec.
*/
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
} while (SSTATUS_GET_DET(SStatus) !=
"si_enumerate_port_multiplier: "
"loop count: %d, SStatus: 0x%x",
SStatus);
if ((SSTATUS_GET_IPM(SStatus) ==
(SSTATUS_GET_DET(SStatus) ==
/* The interface is active and the device is present */
"Status: %x, device exists",
SStatus);
/*
* Clear error bits in SError register (i.e. PSCR[1]
* by writing back error bits.
*/
continue;
}
"SError bits are: %x", SError);
continue;
}
/* There exists a device. */
}
}
return (SI_SUCCESS);
}
/*
* Read a port multiplier register.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static int
int port,
int pmport,
int regnum,
{
int slot;
int i;
int loop_count = 0;
"pmport: %x, regnum: %x",
if (slot == SI_FAILURE) {
return (SI_FAILURE);
}
/* Now fill the prb. */
/* no real data transfer is involved. */
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
int *ptr;
int j;
for (j = 0; j < (sizeof (si_prb_t)/4); j++) {
}
}
#endif /* SI_DEBUG */
/* Deliver PRB */
/* Loop till the command is finished. */
do {
"looping read_pm slot_status: 0x%x",
if (loop_count++ > SI_POLLRATE_SLOTSTATUS) {
/* We are effectively timing out after 0.5 sec. */
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
"read_portmult_reg: loop count: %d",
/* Now inspect the port LRAM for the modified FIS. */
for (i = 0; i < (sizeof (si_prb_t)/4); i++) {
}
return (SI_FAILURE);
}
/* command failed. */
return (SI_FAILURE);
}
/* command succeeded. */
return (SI_SUCCESS);
}
/*
* Write a port multiplier register.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static int
int port,
int pmport,
int regnum,
{
int slot;
int i;
int loop_count = 0;
"si_write_portmult_reg: port: %x, pmport: %x,"
"regnum: %x, regval: %x",
if (slot == SI_FAILURE) {
return (SI_FAILURE);
}
/* Now fill the prb. */
/* no real data transfer is involved. */
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
int *ptr;
int j;
for (j = 0; j < (sizeof (si_prb_t)/4); j++) {
}
}
#endif /* SI_DEBUG */
/* Deliver PRB */
/* Loop till the command is finished. */
do {
"looping write_pmp slot_status: 0x%x",
if (loop_count++ > SI_POLLRATE_SLOTSTATUS) {
/* We are effectively timing out after 0.5 sec. */
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
"write_portmult_reg: loop count: %d",
/* Now inspect the port LRAM for the modified FIS. */
for (i = 0; i < (sizeof (si_prb_t)/4); i++) {
}
return (SI_FAILURE);
}
/* command failed */
return (SI_FAILURE);
}
/* command succeeded */
return (SI_SUCCESS);
}
/*
* Set the auto sense data for ATAPI devices.
*
* Note: Currently the sense data is simulated; this code will be enhanced
* in second phase to fetch the real sense data from the atapi device.
*/
static void
{
sense = (struct scsi_extended_sense *)
sense->es_cmd_info[0] = 0;
sense->es_add_code = 0;
sense->es_qual_code = 0;
}
}
/*
* Interrupt service handler. We loop through each of the ports to find
* if the interrupt belongs to any of them.
*
* Bulk of the interrupt handling is actually done out of subroutines
* like si_intr_command_complete() etc.
*/
/*ARGSUSED*/
static uint_t
{
int port;
"si_intr: global_int_status: 0x%x",
DDI_SUCCESS) {
return (DDI_INTR_UNCLAIMED);
}
if (!(global_intr_status & SI31xx_INTR_PORT_MASK)) {
/* Sorry, the interrupt is not ours. */
return (DDI_INTR_UNCLAIMED);
}
/* Loop for all the ports. */
if (!(global_intr_status & mask)) {
continue;
}
"s_intr: port_intr_status: 0x%x, port: %x",
port);
if (port_intr_status & INTR_COMMAND_COMPLETE) {
port);
port);
}
} else {
/* Clear the interrupts */
}
/*
* Note that we did not clear the interrupt for command
* completion interrupt. Reading of slot_status takes care
* of clearing the interrupt for command completion case.
*/
if (port_intr_status & INTR_COMMAND_ERROR) {
}
if (port_intr_status & INTR_PORT_READY) {
}
if (port_intr_status & INTR_POWER_CHANGE) {
}
if (port_intr_status & INTR_PHYRDY_CHANGE) {
port);
}
if (port_intr_status & INTR_COMWAKE_RECEIVED) {
port);
}
if (port_intr_status & INTR_UNRECOG_FIS) {
port);
}
if (port_intr_status & INTR_DEV_XCHANGED) {
}
if (port_intr_status & INTR_8B10B_DECODE_ERROR) {
port);
}
if (port_intr_status & INTR_CRC_ERROR) {
port);
}
if (port_intr_status & INTR_HANDSHAKE_ERROR) {
}
if (port_intr_status & INTR_SETDEVBITS_NOTIFY) {
port);
}
}
return (DDI_INTR_CLAIMED);
}
/*
* Interrupt which indicates that one or more commands have successfully
* completed.
*
* Since we disabled W1C (write-one-to-clear) previously, mere reading
* of slot_status register clears the interrupt. There is no need to
* explicitly clear the interrupt.
*/
static int
int port)
{
int finished_slot;
"si_intr_command_complete enter", NULL);
if (!si_portp->siport_pending_tags) {
/*
* Spurious interrupt. Nothing to be done.
* The interrupt was cleared when slot_status was read.
*/
return (SI_SUCCESS);
}
"pending_tags: %x, slot_status: %x",
while (finished_tags) {
if (finished_slot == -1) {
break;
}
}
}
"command_complete done: pend_tags: 0x%x, slot_status: 0x%x",
/*
* tidbit: no need to clear the interrupt since reading of
* slot_status automatically clears the interrupt in the case
* of a successful command completion.
*/
return (SI_SUCCESS);
}
/*
* Schedule a call to si_intr_command_error using a timeout to get it done
* off the interrupt thread.
*/
static void
int port)
{
"args->si_ctlp != NULL");
return;
}
}
/*
* Called from timeout()
* Unpack the arguments and call si_intr_command_error()
*/
static void
{
int port;
}
/*
* Interrupt which indicates that a command did not complete successfully.
*
* The port halts whenever a command error interrupt is received.
* The only way to restart it is to reset or reinitialize the port
* but such an operation throws away all the pending commands on
* the port.
*
* We reset the device and mop the commands on the port.
*/
static int
int port)
{
"si_intr_command_error: command_error: 0x%x",
/*
* Remember the slot_status since any of the recovery handler
* can blow it away with reset operation.
*/
switch (command_error) {
case CMD_ERR_DEVICEERRROR:
break;
case CMD_ERR_SDBERROR:
break;
case CMD_ERR_DATAFISERROR:
"Data FIS error");
break;
case CMD_ERR_SENDFISERROR:
"Send FIS error");
break;
default:
"Unknown error");
break;
}
/*
* Compute the failed_tags by adding up the error tags.
*
* The siport_err_tags_SDBERROR and siport_err_tags_nonSDBERROR
* were filled in by the si_error_recovery_* routines.
*/
"err_tags_SDBERROR: 0x%x, "
"err_tags_nonSDBERRROR: 0x%x, "
"failed_tags: 0x%x",
"si3124: si_intr_command_error: "
"slot_status:0x%x, pending_tags: 0x%x",
port,
0, /* timedout_tags */
0, /* aborting_tags */
0); /* reset_tags */
return (SI_SUCCESS);
}
/*
* There is a subtle difference between errors on a normal port and
* a port-mult port. When an error happens on a normal port, the port
* is halted effectively until the port is reset or initialized.
* However, in port-mult port errors, port does not get halted since
* other non-error devices behind the port multiplier can still
* continue to operate. So we wait till all the commands are drained
* instead of resetting it right away.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
int port)
{
int failed_slot;
int loop_count = 0;
"si_recover_portmult_errors: port: 0x%x",
port);
/* Resume the port */
if (command_error == CMD_ERR_SDBERROR) {
} else {
}
/* Now we drain the pending commands. */
do {
/*
* Since we have not yet returned DDI_INTR_CLAIMED,
* our interrupt handler is guaranteed not to be called again.
* So we need to check IS_ATTENTION_RAISED() for further
* decisions.
*
* This is a too big a delay for an interrupt context.
* But this is supposed to be a rare condition.
*/
if (IS_ATTENTION_RAISED(slot_status)) {
/* Resume again */
port)));
if (command_error == CMD_ERR_SDBERROR) {
(0x1 << failed_slot);
} else {
(0x1 << failed_slot);
}
}
if (loop_count++ > SI_POLLRATE_RECOVERPORTMULT) {
/* We are effectively timing out after 10 sec. */
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
} while (slot_status & SI_SLOT_MASK);
/*
* The above loop can be improved for 3132 since we could obtain the
* Port Multiplier Context of the device in error. Then we could
* do a better job in filtering out commands for the device in error.
* The loop could finish much earlier with such a logic.
*/
/* Clear the RESUME bit. */
}
/*
* If we are connected to port multiplier, drain the non-failed devices.
* Otherwise, we initialize the port (which effectively fails all the
* pending commands in the hope that sd would retry them later).
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
int port)
{
int failed_slot;
"si_error_recovery_DEVICEERROR: port: 0x%x",
port);
} else {
}
/* In either case (port-mult or not), we reinitialize the port. */
}
/*
* Handle exactly like DEVICEERROR. Remember the tags with SDBERROR
* to perform read_log_ext on them later. SDBERROR means that the
* error was for an NCQ command.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
int port)
{
int failed_slot;
"si3124: si_error_recovery_SDBERROR: port: 0x%x",
port);
} else {
}
/* In either case (port-mult or not), we reinitialize the port. */
}
/*
* Handle exactly like DEVICEERROR except resetting the port if there was
* an NCQ command on the port.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
int port)
{
int failed_slot;
"si3124: si_error_recovery_DATAFISERROR: port: 0x%x",
port);
/* reset device if we were waiting for any ncq commands. */
if (si_portp->siport_pending_ncq_count) {
return;
}
/*
* If we don't have any ncq commands pending, the rest of
* the process is similar to the one for DEVICEERROR.
*/
}
/*
* We handle just like DEVICERROR except that we reset the device instead
* of initializing the port.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
int port)
{
int failed_slot;
"si3124: si_error_recovery_SENDFISERROR: port: 0x%x",
port);
} else {
}
}
/*
* The default behavior for all other errors is to reset the device.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
int port)
{
int failed_slot;
"si3124: si_error_recovery_default: port: 0x%x",
port);
}
/*
* Read Log Ext with PAGE 10 to retrieve the error for an NCQ command.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static uint8_t
{
int slot;
int i;
int loop_count = 0;
"si_read_log_ext: port: %x", port);
if (slot == SI_FAILURE) {
return (0);
}
/* Now fill the prb */
/* no real data transfer is involved */
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
int *ptr;
int j;
for (j = 0; j < (sizeof (si_prb_t)/4); j++) {
}
}
#endif /* SI_DEBUG */
/* Deliver PRB */
/* Loop till the command is finished. */
do {
"looping read_log_ext slot_status: 0x%x",
if (loop_count++ > SI_POLLRATE_SLOTSTATUS) {
/* We are effectively timing out after 0.5 sec. */
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
/*
* If we fail with the READ LOG EXT command, we need to
* initialize the port to clear the slot_status register.
* We don't need to worry about any other valid commands
* being thrown away because we are already in recovery
* mode and READ LOG EXT is the only pending command.
*/
}
"read_portmult_reg: loop count: %d",
/*
* The LRAM contains the the modified FIS.
* Read the modified FIS to obtain the Error.
*/
for (i = 0; i < (sizeof (si_prb_t)/4); i++) {
}
}
return (error);
}
/*
* Dump the error message to the log.
*/
static void
{
#if SI_DEBUG
#ifndef __lock_lint
#endif /* __lock_lint */
char *errstr;
switch (command_error) {
case CMD_ERR_DEVICEERRROR:
errstr = "Standard Error: Error bit set in register - device"
" to host FIS";
break;
case CMD_ERR_SDBERROR:
errstr = "NCQ Error: Error bit set in register - device"
" to host FIS";
break;
case CMD_ERR_DATAFISERROR:
errstr = "Error in data FIS not detected by device";
break;
case CMD_ERR_SENDFISERROR:
errstr = "Initial command FIS transmission failed";
break;
errstr = "Inconsistency in protocol";
break;
case CMD_ERR_DIRECTIONERROR:
errstr = "DMA direction flag does not match the command";
break;
case CMD_ERR_UNDERRUNERROR:
errstr = "Run out of scatter gather entries while writing data";
break;
case CMD_ERR_OVERRUNERROR:
errstr = "Run out of scatter gather entries while reading data";
break;
errstr = "Packet protocol error";
break;
break;
" table";
break;
" table";
break;
" table";
break;
errstr = "PRB not on quadword boundary";
break;
errstr = "PCI(X) Target abort while fetching PRB";
break;
errstr = "PCI(X) Master abort while fetching PRB";
break;
case CMD_ERR_PLDCMDERORPCIERR:
errstr = "PCI(X) parity error while fetching PRB";
break;
errstr = "PCI(X) Target abort during data transfer";
break;
errstr = "PCI(X) Master abort during data transfer";
break;
case CMD_ERR_PSDERRORPCIERR:
errstr = "PCI(X) parity error during data transfer";
break;
case CMD_ERR_SENDSERVICEERROR:
errstr = "FIS received while sending service FIS in"
" legacy queuing operation";
break;
default:
errstr = "Unknown Error";
break;
}
"command error: error: %s",
errstr);
#else
#ifndef __lock_lint
#endif /* __lock_lint */
#endif /* SI_DEBUG */
}
/*
* Interrupt which indicates that the Port Ready state has changed
* from zero to one.
*
* We are not interested in this interrupt; we just log a debug message.
*/
/*ARGSUSED*/
static int
int port)
{
return (SI_SUCCESS);
}
/*
* Interrupt which indicates that the port power management state
* has been modified.
*
* We are not interested in this interrupt; we just log a debug message.
*/
/*ARGSUSED*/
static int
int port)
{
return (SI_SUCCESS);
}
/*
* Interrupt which indicates that the PHY state has changed either from
* Not-Ready to Ready or from Ready to Not-Ready.
*/
static int
int port)
{
int dev_exists_now = 0;
int dev_existed_previously = 0;
"si_intr_phy_rdy_change", NULL);
/* the whole controller setup is not yet done. */
return (SI_SUCCESS);
}
/* SStatus tells the presence of device. */
}
/* we don't have a way of determining the exact port-mult port. */
} else {
}
if (dev_exists_now) {
if (dev_existed_previously) {
/* Things are fine now. The loss was temporary. */
"phyrdy: doing BOTH EVENTS TOGETHER", NULL);
if (si_portp->siport_active) {
"sending event: LINK_LOST & "
"LINK_ESTABLISHED", NULL);
&sdevice,
}
} else {
/* A new device has been detected. */
"phyrdy: doing ATTACH event", NULL);
if (si_portp->siport_active) {
"sending event up: LINK_ESTABLISHED", NULL);
&sdevice,
}
}
} else { /* No device exists now */
if (dev_existed_previously) {
/* An existing device is lost. */
if (si_portp->siport_active) {
"sending event up: LINK_LOST", NULL);
&sdevice,
}
} else {
/* spurious interrupt */
"spurious phy ready interrupt", NULL);
}
}
return (SI_SUCCESS);
}
/*
* Interrupt which indicates that a COMWAKE OOB signal has been decoded
* on the receiver.
*
* We are not interested in this interrupt; we just log a debug message.
*/
/*ARGSUSED*/
static int
int port)
{
"si_intr_commwake_rcvd", NULL);
return (SI_SUCCESS);
}
/*
* Interrupt which indicates that the F-bit has been set in SError
* Diag field.
*
* We are not interested in this interrupt; we just log a debug message.
*/
/*ARGSUSED*/
static int
int port)
{
"si_intr_unrecognised_fis", NULL);
return (SI_SUCCESS);
}
/*
* Interrupt which indicates that the X-bit has been set in SError
* Diag field.
*
* We are not interested in this interrupt; we just log a debug message.
*/
/*ARGSUSED*/
static int
int port)
{
"si_intr_dev_xchanged", NULL);
return (SI_SUCCESS);
}
/*
* Interrupt which indicates that the 8b/10b Decode Error counter has
* exceeded the programmed non-zero threshold value.
*
* We are not interested in this interrupt; we just log a debug message.
*/
/*ARGSUSED*/
static int
int port)
{
"si_intr_err_threshold", NULL);
return (SI_SUCCESS);
}
/*
* Interrupt which indicates that the CRC Error counter has exceeded the
* programmed non-zero threshold value.
*
* We are not interested in this interrupt; we just log a debug message.
*/
/*ARGSUSED*/
static int
int port)
{
"si_intr_crc_threshold", NULL);
return (SI_SUCCESS);
}
/*
* Interrupt which indicates that the Handshake Error counter has
* exceeded the programmed non-zero threshold value.
*
* We are not interested in this interrupt; we just log a debug message.
*/
/*ARGSUSED*/
static int
int port)
{
"si_intr_handshake_err_threshold", NULL);
return (SI_SUCCESS);
}
/*
* Interrupt which indicates that a "Set Device Bits" FIS has been
* received with N-bit set in the control field.
*
* We are not interested in this interrupt; we just log a debug message.
*/
/*ARGSUSED*/
static int
int port)
{
"si_intr_set_devbits_notify", NULL);
return (SI_SUCCESS);
}
/*
* Enable the interrupts for a particular port.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
{
/* get the current settings first. */
"si_enable_port_interrupts: current mask: 0x%x",
mask);
/* enable the bit for current port. */
/* now use this mask to enable the interrupt. */
mask);
}
/*
* Enable interrupts for all the ports.
*/
static void
{
int port;
}
}
/*
* Disable interrupts for a particular port.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
{
/* get the current settings first. */
/* clear the bit for current port. */
/* now use this mask to disable the interrupt. */
mask);
}
/*
* Disable interrupts for all the ports.
*/
static void
{
int port;
}
}
/*
* Fetches the latest sstatus, scontrol, serror, sactive registers
* and stuffs them into sata_device_t structure.
*/
static void
{
}
/*
* si_add_legacy_intrs() handles INTx and legacy interrupts.
*/
static int
{
/* get number of interrupts. */
"ddi_intr_get_nintrs() failed, "
return (DDI_FAILURE);
}
/* Allocate an array of interrupt handles. */
/* call ddi_intr_alloc(). */
"ddi_intr_alloc() failed, rc %d\n", rc);
return (DDI_FAILURE);
}
for (x = 0; x < actual; x++) {
}
return (DDI_FAILURE);
}
/* Get intr priority. */
"ddi_intr_get_pri() failed", NULL);
for (x = 0; x < actual; x++) {
}
return (DDI_FAILURE);
}
/* Test for high level mutex. */
"si_add_legacy_intrs: Hi level intr not supported", NULL);
for (x = 0; x < actual; x++) {
}
return (DDI_FAILURE);
}
/* Call ddi_intr_add_handler(). */
for (x = 0; x < actual; x++) {
"ddi_intr_add_handler() failed", NULL);
for (y = 0; y < actual; y++) {
}
return (DDI_FAILURE);
}
}
/* Call ddi_intr_enable() for legacy interrupts. */
for (x = 0; x < si_ctlp->sictl_intr_cnt; x++) {
}
return (DDI_SUCCESS);
}
/*
* si_add_msictl_intrs() handles MSI interrupts.
*/
static int
{
/* get number of interrupts. */
"ddi_intr_get_nintrs() failed, "
return (DDI_FAILURE);
}
/* get number of available interrupts. */
"ddi_intr_get_navail() failed, "
return (DDI_FAILURE);
}
"ddi_intr_get_nvail returned %d, navail() returned %d",
}
/* Allocate an array of interrupt handles. */
/* call ddi_intr_alloc(). */
"ddi_intr_alloc() failed, rc %d\n", rc);
return (DDI_FAILURE);
}
/* use interrupt count returned */
}
/*
* Get priority for first msi, assume remaining are all the same.
*/
/* Free already allocated intr. */
for (y = 0; y < actual; y++) {
}
return (DDI_FAILURE);
}
/* Test for high level mutex. */
"si_add_msi_intrs: Hi level intr not supported", NULL);
/* Free already allocated intr. */
for (y = 0; y < actual; y++) {
}
return (DDI_FAILURE);
}
/* Call ddi_intr_add_handler(). */
for (x = 0; x < actual; x++) {
"ddi_intr_add_handler() failed", NULL);
/* Free already allocated intr. */
for (y = 0; y < actual; y++) {
}
return (DDI_FAILURE);
}
}
/* Call ddi_intr_block_enable() for MSI. */
} else {
/* Call ddi_intr_enable() for MSI non block enable. */
for (x = 0; x < si_ctlp->sictl_intr_cnt; x++) {
}
}
return (DDI_SUCCESS);
}
/*
* Removes the registered interrupts irrespective of whether they
* were legacy or MSI.
*/
static void
{
int x;
/* Disable all interrupts. */
/* Call ddi_intr_block_disable(). */
} else {
for (x = 0; x < si_ctlp->sictl_intr_cnt; x++) {
}
}
/* Call ddi_intr_remove_handler(). */
for (x = 0; x < si_ctlp->sictl_intr_cnt; x++) {
}
}
/*
* Resets either the port or the device connected to the port based on
* the flag variable.
*
* The reset effectively throws away all the pending commands. So, the caller
* has to make provision to handle the pending commands.
*
* After the reset, we wait till the port is ready again.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*
* Note: Not port-mult aware.
*/
static int
int port,
int flag)
{
int loop_count = 0;
if (flag == SI_PORT_RESET) {
/* Port reset is not self clearing. So clear it now. */
} else {
/* Reset the device. */
/*
* tidbit: this bit is self clearing; so there is no need
* for manual clear as we did for port reset.
*/
}
/* Set the reset in progress flag */
if (!(flag & SI_RESET_NO_EVENTS_UP)) {
}
/*
* Every reset needs a PHY initialization.
*
* The way to initialize the PHY is to write a 1 and then
* a 0 to DET field of SControl register.
*/
/* Fetch the current SControl before writing the DET part with 1. */
SControl);
#ifndef __lock_lint
#endif /* __lock_lint */
/* Now fetch the SControl again and rewrite the DET part with 0 */
SControl);
/*
* PHY may be initialized by now. Check the DET field of SStatus
* to determine if there is a device present.
*
* The DET field is valid only if IPM field indicates that
* the interface is in active state.
*/
loop_count = 0;
do {
if (SSTATUS_GET_IPM(SStatus) !=
/*
* If the interface is not active, the DET field
* is considered not accurate. So we want to
* continue looping.
*/
}
if (loop_count++ > SI_POLLRATE_SSTATUS) {
/* We are effectively timing out after 0.1 sec. */
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
"si_reset_dport_wait_till_ready: loop count: %d, \
SStatus: 0x%x",
SStatus);
/* Now check for port readiness. */
loop_count = 0;
do {
if (loop_count++ > SI_POLLRATE_PORTREADY) {
/* We are effectively timing out after 0.5 sec. */
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
} while (!(port_status & PORT_STATUS_BITS_PORT_READY));
"si_reset_dport_wait_till_ready: loop count: %d, \
port_status: 0x%x, SStatus: 0x%x",
SStatus);
/* Indicate to the framework that a reset has happened. */
if (!(flag & SI_RESET_NO_EVENTS_UP)) {
} else {
}
if (si_ctlp->sictl_sata_hba_tran) {
&sdevice,
}
"sending event up: SATA_EVNT_RESET", NULL);
}
(SSTATUS_GET_DET(SStatus) ==
/* The interface is active and the device is present */
if (!(port_status & PORT_STATUS_BITS_PORT_READY)) {
/* But the port is is not ready for some reason */
"si_reset_dport_wait_till_ready failed", NULL);
return (SI_FAILURE);
}
}
/*
* For some reason, we are losing the interrupt enablement after
* any reset condition. So restore them back now.
*/
"current interrupt enable set: 0x%x",
/*
* make sure interrupts are cleared
*/
port)),
"si_reset_dport_wait_till_ready returning success", NULL);
return (SI_SUCCESS);
}
/*
* Schedule an initialization of the port using a timeout to get it done
* off an interrupt thread.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static void
int port)
{
"args->si_ctlp != NULL");
return;
}
}
/*
* Called from timeout()
* Unpack the arguments and call si_initialize_port_wait_till_ready()
*/
static void
{
int port;
}
/*
* Initializes the port.
*
* Initialization effectively throws away all the pending commands on
* the port. So, the caller has to make provision to handle the pending
* commands.
*
* After the port initialization, we wait till the port is ready again.
*
* WARNING, WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static int
{
int loop_count = 0;
/* Initialize the port. */
/* Wait until Port Ready */
loop_count = 0;
do {
if (loop_count++ > SI_POLLRATE_PORTREADY) {
"si_initialize_port_wait is timing out: "
"port_status: %x",
/* We are effectively timing out after 0.5 sec. */
break;
}
/* Wait for 10 millisec */
#ifndef __lock_lint
#endif /* __lock_lint */
} while (!(port_status & PORT_STATUS_BITS_PORT_READY));
"si_initialize_port_wait_till_ready: loop count: %d",
(SSTATUS_GET_DET(SStatus) ==
/* The interface is active and the device is present */
if (!(port_status & PORT_STATUS_BITS_PORT_READY)) {
/* But the port is is not ready for some reason */
return (SI_FAILURE);
}
}
return (SI_SUCCESS);
}
/*
* si_watchdog_handler() calls us if it detects that there are some
* commands which timed out. We recalculate the timed out commands once
* again since some of them may have finished recently.
*/
static void
int port,
{
"si_timeout_pkts entry", NULL);
/*
* Initialize the controller. The only way to timeout the commands
* is to reset or initialize the controller. We mop commands after
* the initialization.
*/
/*
* Recompute the timedout tags since some of them may have finished
* meanwhile.
*/
"si_timeout_pkts: finished: %x, timeout: %x",
port,
0, /* failed_tags */
0, /* aborting_tags */
0); /* reset_tags */
}
/*
* Watchdog handler kicks in every 5 seconds to timeout any commands pending
* for long time.
*/
static void
{
int port;
int tmpslot;
/* max number of cycles this packet should survive */
int max_life_cycles;
/* how many cycles this packet survived so far */
int watched_cycles;
"si_watchdog_handler entered", NULL);
continue;
}
continue;
}
/* Skip the check for those ports in error recovery */
if (si_portp->mopping_in_progress > 0) {
"si_watchdog_handler: port %d mopping "
"in progress, so just return", port);
continue;
}
timedout_tags = 0;
while (pending_tags) {
if (tmpslot == -1) {
break;
}
/*
* We are overloading satapkt_hba_driver_private
* with watched_cycle count.
*
* If a packet has survived for more than it's
* max life cycles, it is a candidate for time
* out.
*/
watched_cycles = (int)(intptr_t)
si_watchdog_timeout - 1) /
if (watched_cycles > max_life_cycles) {
"watchdog: timedout_tags: 0x%x",
}
(void *)(intptr_t)watched_cycles;
}
}
if (timedout_tags) {
}
}
/* Reinstall the watchdog timeout handler. */
timeout((void (*)(void *))si_watchdog_handler,
}
}
/*
* FMA Functions
*/
/*
* The IO fault service error handling callback function
*/
/*ARGSUSED*/
static int
{
/*
* as the driver can always deal with an error in any dma or
* access handle, we can just return the fme_status value.
*/
return (err->fme_status);
}
/*
* si_fm_init - initialize fma capabilities and register with IO
* fault services.
*/
static void
{
/*
* Need to change iblock to priority for new MSI intr
*/
/* Only register with IO Fault Services if we have some capability */
if (si_ctlp->fm_capabilities) {
/* Adjust access and dma attributes for FMA */
/*
* Register capabilities with IO Fault Services.
* fm_capabilities will be updated to indicate
* capabilities actually supported (not requested.)
*/
&fm_ibc);
/*
* Initialize pci ereport capabilities if ereport
* capable (should always be.)
*/
}
/*
* Register error callback if error callback capable.
*/
si_fm_error_cb, (void *) si_ctlp);
}
}
}
/*
* si_fm_fini - Releases fma capabilities and un-registers with IO
* fault services.
*/
static void
{
/* Only unregister FMA capabilities if registered */
if (si_ctlp->fm_capabilities) {
/*
* Un-register error callback if error callback capable.
*/
}
/*
* Release any resources allocated by pci_ereport_setup()
*/
}
/* Unregister from IO Fault Services */
/* Adjust access and dma attributes for FMA */
}
}
static int
{
return (de.fme_status);
}
static int
{
return (de.fme_status);
}
static int
{
!= DDI_SUCCESS) ||
!= DDI_SUCCESS) ||
!= DDI_SUCCESS)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* WARNING: The caller is expected to obtain the siport_mutex
* before calling us.
*/
static int
{
!= DDI_SUCCESS) ||
!= DDI_SUCCESS) ||
!= DDI_SUCCESS) ||
!= DDI_SUCCESS)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
NULL);
}
}
/*
* Logs the message.
*/
static void
{
return;
}
return;
}
/*
* si_portp is not NULL, but si_ctlp might be.
* Reference si_portp for both port and dip.
*/
return;
}
si_log_buf, ap);
}
static void
{
int i;
/*
* The LRAM contains the the modified FIS after command completion, so
* first copy it back to the in-core PRB pool. To save read cycles,
* just copy over the FIS portion of the PRB pool.
*/
for (i = 0; i < (sizeof (fis_reg_h2d_t)/4); i++) {
fis_word_ptr[i] = ddi_get32(
}
/*
* always get the status register
*/
"copyout satacmd_sec_count_msb %x\n",
}
"copyout satacmd_lba_low_msb %x\n",
}
"copyout satacmd_lba_mid_msb %x\n",
}
"copyout satacmd_lba_high_msb %x\n",
}
"copyout satacmd_sec_count_lsb %x\n",
}
"copyout satacmd_lba_low_lsb %x\n",
}
"copyout satacmd_lba_mid_lsb %x\n",
}
"copyout satacmd_lba_high_lsb %x\n",
}
"copyout satacmd_device_reg %x\n",
}
"copyout satacmd_error_reg %x\n",
}
}
/*
* This function clear the special port by send the PORT RESET
* After reset was sent, all commands running on the port
* is aborted
*/
static int
{
return (SI_FAILURE);
/*
* reset this port so that all existing command
* is clear
*/
/* Port reset is not self clearing. So clear it now. */
return (SI_SUCCESS);
}
/*
* quiesce(9E) entry point.
* This function is called when the system is single-threaded at high
* PIL with preemption disabled. Therefore, this function must not be
* blocked.
*
* This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
* DDI_FAILURE indicates an error condition and should almost never happen.
*/
static int
{
int instance;
int port;
return (DDI_FAILURE);
/*
* Disable all the interrupts before quiesce
*/
}
return (DDI_SUCCESS);
}