si3124.c revision 4372d277de71fc7c19cb9f41df9b8757b34e2cc5
2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License (the "License"). 2N/A * You may not use this file except in compliance with the License. 2N/A * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 2N/A * If applicable, add the following below this CDDL HEADER, with the 2N/A * fields enclosed by brackets "[]" replaced with your own identifying 2N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 2N/A * Use is subject to license terms. 2N/A#
pragma ident "%Z%%M% %I% %E% SMI" 2N/A * SiliconImage 3124/3132 sata controller driver 2N/A * Even though the driver is named as si3124, it is actually meant to 2N/A * work with both 3124 and 3132 controllers. 2N/A * holds the register definitions from SiI 3124/3132 data sheets. The 2N/A * directly derived from data sheets. 2N/A * II. Data structures 2N/A * si_ctl_state_t: This holds the driver private information for each 2N/A * controller instance. Each of the sata ports within a single 2N/A * controller are represented by si_port_state_t. The 2N/A * sictl_global_acc_handle and sictl_global_address map the 2N/A * controller-wide global register space and are derived from pci 2N/A * BAR 0. The sictl_port_acc_handle and sictl_port_addr map the 2N/A * per-port register space and are derived from pci BAR 1. 2N/A * si_port_state_t: This holds the per port information. The siport_mutex 2N/A * holds the per port mutex. The siport_pending_tags is the bit mask of 2N/A * commands posted to controller. The siport_slot_pkts[] holds the 2N/A * pending sata packets. The siport_port_type holds the device type 2N/A * connected directly to the port while the siport_portmult_state 2N/A * holds the similar information for the devices behind a port 2N/A * si_prb_t: This contains the PRB being posted to the controller. 2N/A * The two SGE entries contained within si_prb_t itself are not 2N/A * really used to hold any scatter gather entries. The scatter gather 2N/A * list is maintained external to PRB and is linked from one 2N/A * of the contained SGEs inside the PRB. For atapi devices, the 2N/A * first contained SGE holds the PACKET and second contained 2N/A * SGE holds the link to an external SGT. For non-atapi devices, 2N/A * the first contained SGE works as link to external SGT while 2N/A * second SGE is blank. 2N/A * external SGT tables: The external SGT tables pointed to from 2N/A * within si_prb_t are actually abstracted as si_sgblock_t. Each 2N/A * si_sgblock_t contains SI_MAX_SGT_TABLES_PER_PRB number of 2N/A * SGT tables linked in a chain. Currently this max value of 2N/A * SGT tables per block is hard coded as 10 which translates 2N/A * to a maximum of 31 dma cookies per single dma transfer. 2N/A * III. Driver operation 2N/A * Command Issuing: We use the "indirect method of command issuance". The 2N/A * PRB contains the command [and atapi PACKET] and a link to the 2N/A * external SGT chain. We write the physical address of the PRB into 2N/A * command activation register. There are 31 command slots for 2N/A * each port. After posting a command, we remember the posted slot & 2N/A * the sata packet in siport_pending_tags & siport_slot_pkts[] 2N/A * Command completion: On a successful completion, intr_command_complete() 2N/A * 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 * the port or reset the port/device. Both port initialize and reset * 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: * If the port is connected to port multiplier, then * 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 * 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. * If the port is connected to port multiplier, then * 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 * If the port was executing an NCQ command, issue a Device * 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 * 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 * 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]. * 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. * Function prototypes for driver entry points * Function prototypes for SATA Framework interfaces * Local function prototypes * DMA attributes for the data buffer 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 */ 512,
/* dma_attr_granular */ * DMA attributes for incore RPB and SGT pool 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_granular */ /* Device access attributes */ (
struct cb_ops *)0,
/* driver operations */ NULL,
/* bus operations */ /* The following are needed for si_log() */ /* Opaque state pointer to be initialized by ddi_soft_state_init() */ * si3124 module initialization. * si3124 module uninitialize. /* Remove the resources allocated in _init(). */ * 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. /* Configure pci config space handle. */ /* Now map the bar0; the bar0 contains the global registers. */ /* Now map bar1; the bar1 contains the port registers. */ * Disable all the interrupts before adding interrupt * handler(s). The interrupts shall be re-enabled selectively /* Get supported interrupt types. */ "ddi_intr_get_supported_types failed");
"ddi_intr_get_supported_types() returned: 0x%x",
* Try MSI first, but fall back to legacy if MSI "MSI interrupt setup done");
"MSI registration failed " "will try Legacy interrupts");
* Either the MSI interrupt setup has failed or only * fixed interrupts are available on the system. "Legacy interrupt setup done");
"legacy interrupt setup failed");
"si3124: No interrupts registered");
/* Initialize the mutex. */ * Initialize the controller and driver core. "si3124: setting sata hba tran failed");
/* Notify SATA framework about RESUME. */ * 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");
/* We want to set SI_DETACH to deallocate all memory */ * The detach entry point for dev_ops. * We undo the things we did in si_attach(). /* disable the interrupts for an uninterrupted detach */ /* unregister from the sata framework. */ /* now cancel the timeout handler. */ /* deinitialize the controller. */ /* destroy any mutexes */ /* remove the interrupts */ /* remove the reg maps. */ /* free the soft state. */ /* Inform SATA framework */ * 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 quiesed. * hardware, those entry points will need to check for * suspend and then block or return errors until resume. "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), "sending event up: PWR_LEVEL_CHANGED");
* The info entry point for dev_ops. * Registers the si3124 with sata framework. "si_register_sata_hba_tran entry");
/* Allocate memory for the sata_hba_tran */ /* Attach it to SATA framework */ * Unregisters the si3124 with sata framework. /* Detach from the SATA framework. */ /* Deallocate sata_hba_tran. */ * 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. "si_tran_probe_port: cport: 0x%x, pmport: 0x%x, qual: 0x%x",
/* we don't support any other device types. */ * 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. * 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(). "si_tran_start entry: port: 0x%x",
cport);
* si_intr_phy_ready_change() may have rendered it to * PORT_TYPE_NODEV. cfgadm operation may have rendered "si_tran_start clearing the " "reset_in_progress for port: 0x%x",
cport);
"si_tran_start returning BUSY while " "reset in progress: port: 0x%x",
cport);
"si_tran_start returning BUSY while " "mopping in progress: port: 0x%x",
cport);
"si_tran_start returning QUEUE_FULL: port: 0x%x",
/* we need to poll now */ * We set the satapkt_reason in both synch and \ * Mopping is necessitated because of the si3124 hardware limitation. * The only way to recover from errors or to abort a command is to * reset the port/device but such a reset also results in throwing * 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. * Only one mopping process at a time is allowed; this is achieved * by using siport_mop_mutex. "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 * Note that we are already holding the main per port * mutex; all we need now is siport_mop_mutex. /* Send up the finished_tags with SATA_PKT_COMPLETED. */ "si_mop_commands sending up completed satapkt: %x",
/* Send up failed_tags with SATA_PKT_DEV_ERROR. */ "handling failed slot: 0x%x",
tmpslot);
* The LRAM contains the the modified FIS. * Read the modified FIS to obtain the Error & Status. for (i = 0; i < (
sizeof (
si_prb_t)/
4); i++) {
* In the case of NCQ command failures, the error is * overwritten by the one obtained from issuing of a * READ LOG EXTENDED command. /* Send up timedout_tags with SATA_PKT_TIMEOUT. */ "si_mop_commands sending " "spkt up with PKT_TIMEOUT: %x",
/* Send up aborting packets with SATA_PKT_ABORTED. */ "si_mop_commands aborting spkt: %x",
/* Reset tags are sent up to framework with SATA_PKT_RESET. */ "si_mop_commands sending PKT_RESET for " /* Send up the unfinished_tags with SATA_PKT_BUSY. */ "si_mop_commands sending PKT_BUSY for " * Called by the sata framework to abort the previously sent packet(s). * We reset the device and mop the commands on the port. * si_intr_phy_ready_change() may have rendered it to * PORT_TYPE_NODEV. cfgadm operation may have rendered * Need to abort a single packet. * Search our siport_slot_pkts[] list for matching spkt. /* requested packet is not on pending list. */ * 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. * Used to reject all the pending packets on a port during a reset * WARNING, WARNING: The caller is expected to obtain the siport_mutex "si_reject_all_reset_pkts on port: %x",
/* Compute which tags need to be sent up. */ * Called by sata framework to reset a port(s) or device. "si_tran_reset_port entry: port: 0x%x",
"port mult reset not implemented yet");
* Called by sata framework to activate a port as part of hotplug. * Note: Not port-mult aware. * 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. * Called by sata framework to deactivate a port as part of hotplug. * Note: Not port-mult aware. * There are pending commands on this port. * Fail the deactivate request. /* 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. * Allocates the si_port_state_t. /* allocate prb & sgt pkts for this port. */ * Deallocates the si_port_state_t. * Allocates the SGB (Scatter Gather Block) incore buffer. /* allocate sgbpool dma handle. */ /* allocate the memory for sgbpool. */ /* error.. free the dma handle. */ /* error.. free the dma handle & free the memory. */ * Deallocates the SGB (Scatter Gather Block) incore buffer. /* Unbind the dma handle first. */ /* Then free the underlying memory. */ /* Now free the handle itself. */ * Allocates the PRB (Port Request Block) incore packets. /* error.. free the dma handle. */ /* error.. free the dma handle & free the memory. */ * Deallocates the PRB (Port Request Block) incore packets. /* 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 "si_find_dev_signature enter: port: %x, pmp: %x",
/* Build a Soft Reset PRB in host memory. */ /* Empty slot could not be found. */ /* We are behind port multiplier. */ for (j = 0; j < (
sizeof (
si_prb_t)); j++) {
/* deliver soft reset prb to empty slot. */ /* Loop till the soft reset is finished. */ /* We are effectively timing out after 10 sec. */ /* Wait for 10 millisec */ "si_find_dev_signature: loop count: %d, slot_status: 0x%x",
/* Read device signature from command slot. */ "Found multiplier at cport: 0x%d, pmport: 0x%x",
* It is wrong to chain a port multiplier behind * another port multiplier. /* We are behind port multiplier. */ "Found atapi at : cport: %x, pmport: %x",
/* We are behind port multiplier. */ "found disk at : cport: %x, pmport: %x",
/* We are behind port multiplier. */ "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. /* we start out with SATA_PKT_COMPLETED as the satapkt_reason */ * If we are in panic, we can't rely on * timers; so, busy wait instead of delay(). /* The si_mop_command() got to our packet before us */ * 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",
* 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 * 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 /* Ignore any non-error interrupts at this stage */ }
/* 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 "si_claim_free_slot entry: siport_pending_tags: %x",
"si_claim_free_slot: no empty slots");
* Builds the PRB for the sata packet and delivers it to controller. * slot number if we can obtain a slot successfully * otherwise, return SI_FAILURE * WARNING, WARNING: The caller is expected to obtain the siport_mutex * si_intr_phy_ready_change() may have rendered it to * PORT_TYPE_NODEV. cfgadm operation may have rendered "si_deliver_satpkt entry: cmd_reg: 0x%x, slot: 0x%x, \ /* LBA [27:24] (also called dev_head) */ /* Set the extended sector count and features */ * 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. */ /* sge1 is left empty in non-ATAPI case */ /* No cookies. Terminate the chain. */ /* Now fill the first 3 entries of SGT in the loop below. */ "inner loop: cookie_index: %d, ncookies: %d",
* 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, " break;
/* we break the loop */ /* This is not the last one. So link it. */ "linking SGT: cookie_index: %d, ncookies: %d",
/* *** finished filling the scatter gather list ******* */ /* Now remember the sata packet in siport_slot_pkts[]. */ * We are overloading satapkt_hba_driver_private with /* program the packet_lenth if it is atapi device. */ * Framework needs to calculate the acdb_len based on * identify packet data. This needs to be accomplished * in second phase of the project. #
else /* ATAPI_2nd_PHASE */ /* hard coding for now to 12 bytes */ #
endif /* ATAPI_2nd_PHASE */ * Do not dump the atapi Test-Unit-Ready commands. * The sd_media_watch spews too many of these. for (j = 0; j < (
sizeof (
si_prb_t)/
4); j++) {
"si_deliver_satpkt sgt: low, high, count link");
* 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. "si3124: si_initialize_controller entered");
/* Remove the Global Reset. */ * We allocate the port state only during attach * sequence. We don't want to do it during * 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 * Now fetch the SControl again and rewrite the * 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. * If the interface is not active, the DET field * is considered not accurate. So we want to * We are effectively timing out after 0.1 sec. /* Wait for 10 millisec */ "si_initialize_controller: 1st loop count: %d, " * Either the port is not active or there /* Wait until Port Ready */ * We are effectively timing out after 0.5 sec. /* Wait for 10 millisec */ "si_initialize_controller: 2nd loop count: %d",
* We want to probe for dev signature only during attach * Reverse of si_initialize_controller(). * WARNING, WARNING: The caller is expected to obtain the sictl_mutex "si3124: si_deinititalize_controller entered");
/* 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 "si_init_port entered: port: 0x%x",
/* Initialize the port. */ * Clear the InterruptNCOR (Interupt 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 "si_enumerate_port_multiplier entered: port: %d",
/* Enable Port Multiplier context switching. */ * Read the num dev ports connected. * GSCR[2] contains the number of device ports. "si_enumerate_port_multiplier: ports found: %d",
* Enable PHY by writing a 1, then a 0 to SControl * (i.e. PSCR[2]) DET field. /* First write a 1 to DET field of SControl. */ /* Then write a 0 to the DET field of SControl. */ /* Wait for PHYRDY by polling SStatus (i.e. PSCR[0]). */ "looping for PHYRDY: SStatus: %x",
* If the interface is not active, the DET field * is considered not accurate. So we want to * We are effectively timing out after 0.1 sec. /* Wait for 10 millisec */ "si_enumerate_port_multiplier: " "loop count: %d, SStatus: 0x%x",
/* The interface is active and the device is present */ "Status: %x, device exists",
* Clear error bits in SError register (i.e. PSCR[1] * by writing back error bits. "SError bits are: %x",
SError);
/* There exists a device. */ * Read a port multiplier register. * WARNING, WARNING: The caller is expected to obtain the siport_mutex "pmport: %x, regnum: %x",
/* no real data transfer is involved. */ for (j = 0; j < (
sizeof (
si_prb_t)/
4); j++) {
/* Loop till the command is finished. */ "looping read_pm slot_status: 0x%x",
/* We are effectively timing out after 0.5 sec. */ /* Wait for 10 millisec */ "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++) {
* Write a port multiplier register. * WARNING, WARNING: The caller is expected to obtain the siport_mutex "si_write_portmult_reg: port: %x, pmport: %x," "regnum: %x, regval: %x",
/* no real data transfer is involved. */ for (j = 0; j < (
sizeof (
si_prb_t)/
4); j++) {
/* Loop till the command is finished. */ "looping write_pmp slot_status: 0x%x",
/* We are effectively timing out after 0.5 sec. */ /* Wait for 10 millisec */ "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++) {
* 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. * 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. "si_intr: global_int_status: 0x%x",
/* Sorry, the interrupt is not ours. */ /* Loop for all the ports. */ "s_intr: port_intr_status: 0x%x, port: %x",
/* 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. * Interrupt which indicates that one or more commands have successfully * 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. "si_intr_command_complete enter");
* Spurious interrupt. Nothing to be done. * The interrupt was cleared when slot_status was read. "pending_tags: %x, slot_status: %x",
"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. * 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 * We reset the device and mop the commands on the 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. * 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, " "slot_status:0x%x, pending_tags: 0x%x",
* 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 "si_recover_portmult_errors: port: 0x%x",
/* Now we drain the pending commands. */ * 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 * This is a too big a delay for an interrupt context. * But this is supposed to be a rare condition. /* We are effectively timing out after 10 sec. */ /* Wait for 10 millisec */ * 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 "si_error_recovery_DEVICEERROR: port: 0x%x",
/* 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 "si3124: si_error_recovery_SDBERROR: port: 0x%x",
/* 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 "si3124: si_error_recovery_DATAFISERROR: port: 0x%x",
/* reset device if we were waiting for any ncq commands. */ * 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 "si3124: si_error_recovery_SENDFISERROR: port: 0x%x",
* The default behavior for all other errors is to reset the device. * WARNING, WARNING: The caller is expected to obtain the siport_mutex "si3124: si_error_recovery_default: port: 0x%x",
* 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 "si_read_log_ext: port: %x",
port);
/* no real data transfer is involved */ for (j = 0; j < (
sizeof (
si_prb_t)/
4); j++) {
/* Loop till the command is finished. */ "looping read_log_ext slot_status: 0x%x",
/* We are effectively timing out after 0.5 sec. */ /* Wait for 10 millisec */ * 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++) {
* Dump the error message to the log. errstr =
"Standard Error: Error bit set in register - device" errstr =
"NCQ Error: Error bit set in register - device" errstr =
"Error in data FIS not detected by device";
errstr =
"Initial command FIS transmission failed";
errstr =
"Inconsistency in protocol";
errstr =
"DMA direction flag does not match the command";
errstr =
"Run out of scatter gather entries while writing data";
errstr =
"Run out of scatter gather entries while reading data";
errstr =
"Packet protocol error";
errstr =
"PRB not on quadword boundary";
errstr =
"PCI(X) Target abort while fetching PRB";
errstr =
"PCI(X) Master abort while fetching PRB";
errstr =
"PCI(X) parity error while fetching PRB";
errstr =
"PCI(X) Target abort during data transfer";
errstr =
"PCI(X) Master abort during data transfer";
errstr =
"PCI(X) parity error during data transfer";
errstr =
"FIS received while sending service FIS in" " legacy queuing operation";
"command error: port: 0x%x, error: %s",
* Interrupt which indicates that the Port Ready state has changed * We are not interested in this interrupt; we just log a debug message. * Interrupt which indicates that the port power management state * We are not interested in this interrupt; we just log a debug message. * Interrupt which indicates that the PHY sate has changed either from * Not-Ready to Ready or from Ready to Not-Ready. /* the whole controller setup is not yet done. */ /* SStatus tells the presence of device. */ /* we don't have a way of determining the exact port-mult port. */ /* Things are fine now. The loss was temporary. */ "phyrdy: doing BOTH EVENTS TOGETHER");
"sending event: LINK_LOST & " /* A new device has been detected. */ "sending event up: LINK_ESTABLISHED");
}
else {
/* No device exists now */ /* An existing device is lost. */ "sending event up: LINK_LOST");
"spurious phy ready interrupt");
* Interrupt which indicates that a COMWAKE OOB signal has been decoded * We are not interested in this interrupt; we just log a debug message. * Interrupt which indicates that the F-bit has been set in SError * We are not interested in this interrupt; we just log a debug message. * Interrupt which indicates that the X-bit has been set in SError * We are not interested in this interrupt; we just log a debug message. * Interrupt which indicates that the 8b/10 Decode Error counter has * exceeded the programmed non-zero threshold value. * We are not interested in this interrupt; we just log a debug message. * 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. * 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. "si_intr_handshake_err_threshold");
* 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. * Enable the interrupts for a particular port. * WARNING, WARNING: The caller is expected to obtain the siport_mutex /* get the current settings first. */ "si_enable_port_interrupts: current mask: 0x%x",
/* enable the bit for current port. */ /* now use this mask to enable the interrupt. */ * Enable interrupts for all the ports. * Disable interrupts for a particular port. * WARNING, WARNING: The caller is expected to obtain the siport_mutex /* get the current settings first. */ /* clear the bit for current port. */ /* now use this mask to disable the interrupt. */ * Disable interrupts for all the ports. * Fetches the latest sstatus, scontrol, serror, sactive registers * and stuffs them into sata_device_t structure. * si_add_legacy_intrs() handles INTx and legacy interrupts. /* get number of interrupts. */ "ddi_intr_get_nintrs() failed, " /* Allocate an array of interrupt handles. */ /* call ddi_intr_alloc(). */ "ddi_intr_alloc() failed, rc %d\n",
rc);
for (x = 0; x <
actual; x++) {
"ddi_intr_get_pri() failed");
for (x = 0; x <
actual; x++) {
/* Test for high level mutex. */ "si_add_legacy_intrs: Hi level intr not supported");
for (x = 0; x <
actual; x++) {
/* Call ddi_intr_add_handler(). */ for (x = 0; x <
actual; x++) {
"ddi_intr_add_handler() failed");
for (y = 0; y <
actual; y++) {
/* Call ddi_intr_enable() for legacy interrupts. */ * si_add_msictl_intrs() handles MSI interrupts. /* get number of interrupts. */ "ddi_intr_get_nintrs() failed, " /* get number of available interrupts. */ "ddi_intr_get_navail() failed, " "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);
/* 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++) {
/* Test for high level mutex. */ "si_add_msi_intrs: Hi level intr not supported");
/* Free already allocated intr. */ for (y = 0; y <
actual; y++) {
/* Call ddi_intr_add_handler(). */ for (x = 0; x <
actual; x++) {
"ddi_intr_add_handler() failed");
/* Free already allocated intr. */ for (y = 0; y <
actual; y++) {
/* Call ddi_intr_block_enable() for MSI. */ /* Call ddi_intr_enable() for MSI non block enable. */ * Removes the registered interrupts irrespective of whether they /* Disable all interrupts. */ /* Call ddi_intr_block_disable(). */ /* Call ddi_intr_remove_handler(). */ * Resets either the port or the device connected to the port based on * 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 * Note: Not port-mult aware. /* Port reset is not self clearing. So clear it now. */ * 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 */ * For some reason, we are losing the interrupt enablement after * any reset condition. So restore them back now. "current interrupt enable set: 0x%x",
* 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. */ /* Now fetch the SControl again and rewrite the DET part with 0 */ * 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. * If the interface is not active, the DET field * is considered not accurate. So we want to /* We are effectively timing out after 0.1 sec. */ /* Wait for 10 millisec */ "si_reset_dport_wait_till_ready: loop count: %d, \ /* Now check for port readiness. */ /* We are effectively timing out after 0.5 sec. */ /* Wait for 10 millisec */ "si_reset_dport_wait_till_ready: loop count: %d, \ port_status: 0x%x, SStatus: 0x%x",
/* Indicate to the framework that a reset has happened. */ "sending event up: SATA_EVNT_RESET");
/* The interface is active and the device is present */ /* But the port is is not ready for some reason */ "si_reset_dport_wait_till_ready failed");
"si_reset_dport_wait_till_ready returning success");
* Initialization effectively throws away all the pending commands on * the port. So, the caller has to make provision to handle the pending * After the port initialization, we wait till the port is ready again. * WARNING, WARNING: The caller is expected to obtain the siport_mutex /* Initialize the port. */ /* Wait until Port Ready */ "si_initialize_port_wait is timing out: " /* We are effectively timing out after 0.5 sec. */ /* Wait for 10 millisec */ "si_initialize_port_wait_till_ready: loop count: %d",
/* The interface is active and the device is present */ /* But the port is is not ready for some reason */ * 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. * Initialize the controller. The only way to timeout the commands * is to reset or initialize the controller. We mop commands after * Recompute the timedout tags since some of them may have finished "si_timeout_pkts: finished: %x, timeout: %x",
* Watchdog handler kicks in every 5 seconds to timeout any commands pending /* max number of cycles this packet should survive */ /* how many cycles this packet survived so far */ "si_watchdog_handler entered");
* 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 "watchdog: timedout_tags: 0x%x",
/* Reinstall the watchdog timeout handler. */