summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authoryt160523 <none@none>2007-08-16 18:51:39 -0700
committeryt160523 <none@none>2007-08-16 18:51:39 -0700
commit68d33a2562b5b41e4606c8b0f50f32fd26b05302 (patch)
treee0ab41b15bdf6dc1e900961cf3b714060dc9325a /usr/src
parentd7d936556e5d09df139c86f1cbd138da800e2c7d (diff)
downloadillumos-gate-68d33a2562b5b41e4606c8b0f50f32fd26b05302.tar.gz
6550832 sata AHCI driver has to support SATA ATAPI CD/DVD devices
6558736 ahci sata hba driver doesn't fetch the device signature correctly 6559336 ahci sata hba driver doesn't handle correctly for POLLING/SYNC mode commands 6583174 ahci driver: re-organize the interrupt handlers
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/io/sata/adapters/ahci/ahci.c3331
-rw-r--r--usr/src/uts/common/io/warlock/ahci.wlcmd5
-rw-r--r--usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h20
-rw-r--r--usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h117
4 files changed, 1962 insertions, 1511 deletions
diff --git a/usr/src/uts/common/io/sata/adapters/ahci/ahci.c b/usr/src/uts/common/io/sata/adapters/ahci/ahci.c
index bb0182f2c2..dd289cfa62 100644
--- a/usr/src/uts/common/io/sata/adapters/ahci/ahci.c
+++ b/usr/src/uts/common/io/sata/adapters/ahci/ahci.c
@@ -62,10 +62,8 @@ static int ahci_selftest(dev_info_t *, sata_device_t *);
/*
* Local function prototypes
*/
-static int ahci_initialize_controller(ahci_ctl_t *);
-static void ahci_deallocate_controller(ahci_ctl_t *);
-
-
+static int ahci_alloc_ports_state(ahci_ctl_t *);
+static void ahci_dealloc_ports_state(ahci_ctl_t *);
static int ahci_alloc_port_state(ahci_ctl_t *, uint8_t);
static void ahci_dealloc_port_state(ahci_ctl_t *, uint8_t);
static int ahci_alloc_rcvd_fis(ahci_ctl_t *, ahci_port_t *, uint8_t);
@@ -75,17 +73,20 @@ static void ahci_dealloc_cmd_list(ahci_ctl_t *, ahci_port_t *);
static int ahci_alloc_cmd_tables(ahci_ctl_t *, ahci_port_t *);
static void ahci_dealloc_cmd_tables(ahci_ctl_t *, ahci_port_t *);
+static int ahci_initialize_controller(ahci_ctl_t *);
+static void ahci_uninitialize_controller(ahci_ctl_t *);
static int ahci_initialize_port(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_start_port(ahci_ctl_t *, uint8_t);
-static int ahci_find_dev_signature(ahci_ctl_t *, ahci_port_t *, uint8_t);
+
+static int ahci_start_port(ahci_ctl_t *, ahci_port_t *, uint8_t);
+static void ahci_find_dev_signature(ahci_ctl_t *, ahci_port_t *, uint8_t);
static void ahci_update_sata_registers(ahci_ctl_t *, uint8_t, sata_device_t *);
static int ahci_deliver_satapkt(ahci_ctl_t *, ahci_port_t *,
uint8_t, sata_pkt_t *);
-static void ahci_poll_cmd(ahci_ctl_t *, ahci_port_t *, uint8_t,
- int, sata_pkt_t *);
+static int ahci_do_sync_start(ahci_ctl_t *, ahci_port_t *,
+ uint8_t, sata_pkt_t *);
static int ahci_claim_free_slot(ahci_ctl_t *, ahci_port_t *);
+static void ahci_copy_err_cnxt(sata_cmd_t *, ahci_fis_d2h_register_t *);
static void ahci_copy_out_regs(sata_cmd_t *, ahci_fis_d2h_register_t *);
-static void ahci_set_sense_data(sata_pkt_t *, int);
static int ahci_software_reset(ahci_ctl_t *, ahci_port_t *, uint8_t);
static int ahci_hba_reset(ahci_ctl_t *);
@@ -94,18 +95,23 @@ static void ahci_reject_all_abort_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t);
static int ahci_reset_device_reject_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t);
static int ahci_reset_port_reject_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t);
static int ahci_reset_hba_reject_pkts(ahci_ctl_t *);
-static int ahci_port_into_notrunning_state(ahci_ctl_t *, ahci_port_t *,
+static int ahci_put_port_into_notrunning_state(ahci_ctl_t *, ahci_port_t *,
uint8_t);
static int ahci_restart_port_wait_till_ready(ahci_ctl_t *, ahci_port_t *,
- uint8_t, int);
+ uint8_t, int, int *);
static void ahci_mop_commands(ahci_ctl_t *, ahci_port_t *, uint8_t,
uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
-static int ahci_recovery_fatal_error(ahci_ctl_t *,
- ahci_port_t *, uint8_t, uint32_t);
-static void ahci_timeout_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t, uint32_t);
+static void ahci_get_rqsense_data(ahci_ctl_t *, ahci_port_t *,
+ uint8_t, sata_pkt_t *);
+static void ahci_fatal_error_recovery_handler(ahci_ctl_t *, ahci_port_t *,
+ uint8_t, uint32_t, uint32_t);
+static void ahci_timeout_pkts(ahci_ctl_t *, ahci_port_t *,
+ uint8_t, uint32_t, uint32_t);
+static void ahci_events_handler(void *);
static void ahci_watchdog_handler(ahci_ctl_t *);
static uint_t ahci_intr(caddr_t, caddr_t);
+static void ahci_port_intr(ahci_ctl_t *, ahci_port_t *, uint8_t, uint32_t);
static int ahci_add_legacy_intrs(ahci_ctl_t *);
static int ahci_add_msi_intrs(ahci_ctl_t *);
static void ahci_rem_intrs(ahci_ctl_t *);
@@ -114,32 +120,23 @@ static void ahci_disable_all_intrs(ahci_ctl_t *);
static void ahci_enable_port_intrs(ahci_ctl_t *, ahci_port_t *, uint8_t);
static void ahci_disable_port_intrs(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_intr_d2h_register_fis(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_intr_pio_setup_fis(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_intr_dma_setup_fis(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_intr_set_device_bits_fis(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_intr_unknown_fis(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_intr_descriptor_processed(ahci_ctl_t *, ahci_port_t *,
- uint8_t);
+static int ahci_intr_cmd_cmplt(ahci_ctl_t *, ahci_port_t *, uint8_t,
+ uint32_t, uint32_t);
+static int ahci_intr_set_device_bits(ahci_ctl_t *, ahci_port_t *, uint8_t);
static int ahci_intr_port_connect_change(ahci_ctl_t *, ahci_port_t *, uint8_t);
static int ahci_intr_device_mechanical_presence_status(ahci_ctl_t *,
ahci_port_t *, uint8_t);
static int ahci_intr_phyrdy_change(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_intr_incorrect_port_multiplier(ahci_ctl_t *,
- ahci_port_t *, uint8_t);
-static int ahci_intr_overflow(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_intr_interface_non_fatal_error(ahci_ctl_t *,
- ahci_port_t *, uint8_t);
-static int ahci_intr_interface_fatal_error(ahci_ctl_t *, ahci_port_t *,
- uint8_t);
-static int ahci_intr_host_bus_data_error(ahci_ctl_t *, ahci_port_t *, uint8_t);
-static int ahci_intr_host_bus_fatal_error(ahci_ctl_t *, ahci_port_t *,
- uint8_t);
-static int ahci_intr_task_file_error(ahci_ctl_t *, ahci_port_t *, uint8_t);
+static int ahci_intr_non_fatal_error(ahci_ctl_t *, ahci_port_t *,
+ uint8_t, uint32_t);
+static int ahci_intr_fatal_error(ahci_ctl_t *, ahci_port_t *,
+ uint8_t, uint32_t, uint32_t);
static int ahci_intr_cold_port_detect(ahci_ctl_t *, ahci_port_t *, uint8_t);
static int ahci_get_num_implemented_ports(uint32_t);
-static void ahci_log_error_message(ahci_ctl_t *, uint8_t, uint32_t);
+static void ahci_log_fatal_error_message(ahci_ctl_t *, uint8_t port,
+ uint32_t);
+static void ahci_log_serror_message(ahci_ctl_t *, uint8_t, uint32_t);
static void ahci_log(ahci_ctl_t *, uint_t, char *, ...);
@@ -151,10 +148,10 @@ static void ahci_log(ahci_ctl_t *, uint_t, char *, ...);
*/
static ddi_dma_attr_t buffer_dma_attr = {
DMA_ATTR_V0, /* dma_attr_version */
- 0, /* dma_attr_addr_lo: lowest bus address */
+ 0x0ull, /* dma_attr_addr_lo: lowest bus address */
0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */
0x3fffffull, /* dma_attr_count_max i.e. for one cookie */
- 2, /* dma_attr_align: word aligned */
+ 0x2ull, /* dma_attr_align: word aligned */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */
@@ -172,10 +169,10 @@ static ddi_dma_attr_t buffer_dma_attr = {
*/
static ddi_dma_attr_t rcvd_fis_dma_attr = {
DMA_ATTR_V0, /* dma_attr_version */
- 0, /* dma_attr_addr_lo: lowest bus address */
+ 0x0ull, /* 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 */
- 0x100, /* dma_attr_align: 256-byte aligned */
+ 0x100ull, /* dma_attr_align: 256-byte aligned */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */
@@ -193,10 +190,10 @@ static ddi_dma_attr_t rcvd_fis_dma_attr = {
*/
static ddi_dma_attr_t cmd_list_dma_attr = {
DMA_ATTR_V0, /* dma_attr_version */
- 0, /* dma_attr_addr_lo: lowest bus address */
+ 0x0ull, /* 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 */
- 0x400, /* dma_attr_align: 1K-byte aligned */
+ 0x400ull, /* dma_attr_align: 1K-byte aligned */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */
@@ -214,10 +211,10 @@ static ddi_dma_attr_t cmd_list_dma_attr = {
*/
static ddi_dma_attr_t cmd_table_dma_attr = {
DMA_ATTR_V0, /* dma_attr_version */
- 0, /* dma_attr_addr_lo: lowest bus address */
+ 0x0ull, /* 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 */
- 0x80, /* dma_attr_align: 128-byte aligned */
+ 0x80ull, /* dma_attr_align: 128-byte aligned */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */
@@ -379,6 +376,10 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
int intr_types;
ushort_t venid;
uint8_t revision;
+ int i;
+#if AHCI_DEBUG
+ int speed;
+#endif
AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, NULL, "ahci_attach enter");
@@ -394,18 +395,24 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
return (DDI_FAILURE);
}
- instance = ddi_get_instance(dip);
attach_state = AHCI_ATTACH_STATE_NONE;
/* Allocate soft state */
status = ddi_soft_state_zalloc(ahci_statep, instance);
if (status != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "!Cannot allocate soft state");
goto err_out;
}
ahci_ctlp = ddi_get_soft_state(ahci_statep, instance);
ahci_ctlp->ahcictl_dip = dip;
+ /* Initialize the cport/port mapping */
+ for (i = 0; i < AHCI_MAX_PORTS; i++) {
+ ahci_ctlp->ahcictl_port_to_cport[i] = 0xff;
+ ahci_ctlp->ahcictl_cport_to_port[i] = 0xff;
+ }
+
attach_state |= AHCI_ATTACH_STATE_STATEP_ALLOC;
/*
@@ -420,6 +427,7 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
&accattr,
&ahci_ctlp->ahcictl_ahci_acc_handle);
if (status != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "!Cannot map register space");
goto err_out;
}
@@ -429,15 +437,15 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
ahci_version = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_GLOBAL_VS(ahci_ctlp));
- AHCIDBG2(AHCIDBG_INIT, ahci_ctlp, "hba AHCI version = %x.%x",
+ cmn_err(CE_NOTE, "!hba AHCI version = %x.%x",
(ahci_version & 0xffff0000) >> 16,
((ahci_version & 0x0000ff00) >> 4 |
(ahci_version & 0x000000ff)));
/* We don't support controllers whose versions are lower than 1.0 */
if (!(ahci_version & 0xffff0000)) {
- AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, "No support for lower than "
- "AHCI version 1.0");
+ cmn_err(CE_WARN, "Don't support AHCI HBA with lower than "
+ "version 1.0");
goto err_out;
}
@@ -448,12 +456,27 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba capabilites = 0x%x",
cap_status);
+#if AHCI_DEBUG
+ /* Get the interface speed supported by the HBA */
+ speed = (cap_status & AHCI_HBA_CAP_ISS) >> AHCI_HBA_CAP_ISS_SHIFT;
+ if (speed == 0x01) {
+ AHCIDBG0(AHCIDBG_INIT, ahci_ctlp,
+ "hba interface speed support: Gen 1 (1.5Gbps)");
+ } else if (speed == 0x10) {
+ AHCIDBG0(AHCIDBG_INIT, ahci_ctlp,
+ "hba interface speed support: Gen 2 (3 Gbps)");
+ } else if (speed == 0x11) {
+ AHCIDBG0(AHCIDBG_INIT, ahci_ctlp,
+ "hba interface speed support: Gen 3 (6 Gbps)");
+ }
+#endif
+
/* Get the number of command slots supported by the HBA */
ahci_ctlp->ahcictl_num_cmd_slots =
((cap_status & AHCI_HBA_CAP_NCS) >>
AHCI_HBA_CAP_NCS_SHIFT) + 1;
- AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba number of cmd slots: 0x%x",
+ AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba number of cmd slots: %d",
ahci_ctlp->ahcictl_num_cmd_slots);
/* Get the bit map which indicates ports implemented by the HBA */
@@ -485,7 +508,7 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
ahci_ctlp->ahcictl_ports_implemented);
AHCIDBG1(AHCIDBG_INIT, ahci_ctlp,
- "hba number of implemented ports: 0x%x",
+ "hba number of implemented ports: %d",
ahci_ctlp->ahcictl_num_implemented_ports);
ahci_ctlp->ahcictl_buffer_dma_attr = buffer_dma_attr;
@@ -497,17 +520,23 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
0xffffffffull;
}
- /*
- * Modify dma_attr_align of ahcictl_buffer_dma_attr. For VT8251, those
- * controllers with 0x00 revision id work on 4-byte aligned buffer,
- * which is a bug and was fixed after 0x00 revision id controllers.
- */
if (pci_config_setup(dip, &ahci_ctlp->ahcictl_pci_conf_handle)
!= DDI_SUCCESS) {
- AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, "pci_config_setup failed");
+ cmn_err(CE_WARN, "!Cannot set up pci configure space");
goto err_out;
}
+ attach_state |= AHCI_ATTACH_STATE_PCICFG_SETUP;
+
+ /*
+ * Modify dma_attr_align of ahcictl_buffer_dma_attr. For VT8251, those
+ * controllers with 0x00 revision id work on 4-byte aligned buffer,
+ * which is a bug and was fixed after 0x00 revision id controllers.
+ *
+ * And VT8251 cannot use multiple command lists for non-queued commands
+ * because the Command Issue register bit can be cleared by software
+ * set.
+ */
venid = pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle,
PCI_CONF_VENID);
@@ -521,9 +550,12 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
AHCIDBG0(AHCIDBG_INIT, ahci_ctlp,
"change ddi_attr_align to 0x4");
}
- }
- pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle);
+ ahci_ctlp->ahcictl_cap = AHCI_CAP_NO_MCMDLIST_NONQUEUE;
+ AHCIDBG0(AHCIDBG_INIT, ahci_ctlp,
+ "VT8251 cannot use multiple command lists for "
+ "non-queued commands");
+ }
/*
* Disable the whole controller interrupts before adding
@@ -533,8 +565,7 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
/* Get supported interrupt types */
if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) {
- AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp,
- "ddi_intr_get_supported_types failed");
+ cmn_err(CE_WARN, "!ddi_intr_get_supported_types failed");
goto err_out;
}
@@ -603,13 +634,34 @@ intr_done:
ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_sgllen =
ahci_dma_prdt_number;
+ /* Allocate the ports structure */
+ status = ahci_alloc_ports_state(ahci_ctlp);
+ if (status != AHCI_SUCCESS) {
+ cmn_err(CE_WARN, "!Cannot allocate ports structure");
+ goto err_out;
+ }
+
+ attach_state |= AHCI_ATTACH_STATE_PORT_ALLOC;
+
+ /*
+ * A taskq is created for dealing with events
+ */
+ if ((ahci_ctlp->ahcictl_event_taskq = ddi_taskq_create(dip,
+ "ahci_event_handle_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL) {
+ cmn_err(CE_WARN, "!ddi_taskq_create failed for event handle");
+ goto err_out;
+ }
+
+ attach_state |= AHCI_ATTACH_STATE_ERR_RECV_TASKQ;
+
/*
- * Initialize the controller and driver core.
+ * Initialize the controller and ports.
*/
ahci_ctlp->ahcictl_flags |= AHCI_ATTACH;
status = ahci_initialize_controller(ahci_ctlp);
ahci_ctlp->ahcictl_flags &= ~AHCI_ATTACH;
if (status != AHCI_SUCCESS) {
+ cmn_err(CE_WARN, "!HBA initialization failed");
goto err_out;
}
@@ -623,8 +675,7 @@ intr_done:
attach_state |= AHCI_ATTACH_STATE_TIMEOUT_ENABLED;
if (ahci_register_sata_hba_tran(ahci_ctlp, cap_status)) {
- AHCIDBG0(AHCIDBG_INIT, ahci_ctlp,
- "ahci: setting sata hba tran failed");
+ cmn_err(CE_WARN, "!sata hba tran registration failed");
goto err_out;
}
@@ -642,12 +693,18 @@ err_out:
if (attach_state & AHCI_ATTACH_STATE_HW_INIT) {
mutex_enter(&ahci_ctlp->ahcictl_mutex);
- ahci_ctlp->ahcictl_flags |= AHCI_DETACH;
- ahci_deallocate_controller(ahci_ctlp);
- ahci_ctlp->ahcictl_flags &= ~AHCI_DETACH;
+ ahci_uninitialize_controller(ahci_ctlp);
mutex_exit(&ahci_ctlp->ahcictl_mutex);
}
+ if (attach_state & AHCI_ATTACH_STATE_ERR_RECV_TASKQ) {
+ ddi_taskq_destroy(ahci_ctlp->ahcictl_event_taskq);
+ }
+
+ if (attach_state & AHCI_ATTACH_STATE_PORT_ALLOC) {
+ ahci_dealloc_ports_state(ahci_ctlp);
+ }
+
if (attach_state & AHCI_ATTACH_STATE_MUTEX_INIT) {
mutex_destroy(&ahci_ctlp->ahcictl_mutex);
}
@@ -656,6 +713,10 @@ err_out:
ahci_rem_intrs(ahci_ctlp);
}
+ if (attach_state & AHCI_ATTACH_STATE_PCICFG_SETUP) {
+ pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle);
+ }
+
if (attach_state & AHCI_ATTACH_STATE_REG_MAP) {
ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle);
}
@@ -704,21 +765,27 @@ ahci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
(void) untimeout(ahci_ctlp->ahcictl_timeout_id);
ahci_ctlp->ahcictl_timeout_id = 0;
- /* deallocate the controller structures */
+ mutex_exit(&ahci_ctlp->ahcictl_mutex);
+
+ /* uninitialize the controller */
ahci_ctlp->ahcictl_flags |= AHCI_DETACH;
- ahci_deallocate_controller(ahci_ctlp);
+ ahci_uninitialize_controller(ahci_ctlp);
ahci_ctlp->ahcictl_flags &= ~AHCI_DETACH;
- /* destroy any mutexes */
- mutex_exit(&ahci_ctlp->ahcictl_mutex);
+ /* remove the interrupts */
+ ahci_rem_intrs(ahci_ctlp);
- (void) pm_lower_power(ahci_ctlp->ahcictl_dip, 0,
- PM_LEVEL_D3);
+ /* destroy the taskq */
+ ddi_taskq_destroy(ahci_ctlp->ahcictl_event_taskq);
+ /* deallocate the ports structures */
+ ahci_dealloc_ports_state(ahci_ctlp);
+
+ /* destroy mutex */
mutex_destroy(&ahci_ctlp->ahcictl_mutex);
- /* remove the interrupts */
- ahci_rem_intrs(ahci_ctlp);
+ /* teardown the pci config */
+ pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle);
/* remove the reg maps. */
ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle);
@@ -792,7 +859,7 @@ ahci_register_sata_hba_tran(ahci_ctl_t *ahci_ctlp, uint32_t cap_status)
/* Allocate memory for the sata_hba_tran */
sata_hba_tran = kmem_zalloc(sizeof (sata_hba_tran_t), KM_SLEEP);
- sata_hba_tran->sata_tran_hba_rev = SATA_TRAN_HBA_REV;
+ sata_hba_tran->sata_tran_hba_rev = SATA_TRAN_HBA_REV_2;
sata_hba_tran->sata_tran_hba_dip = ahci_ctlp->ahcictl_dip;
sata_hba_tran->sata_tran_hba_dma_attr =
&ahci_ctlp->ahcictl_buffer_dma_attr;
@@ -801,11 +868,12 @@ ahci_register_sata_hba_tran(ahci_ctl_t *ahci_ctlp, uint32_t cap_status)
sata_hba_tran->sata_tran_hba_num_cports =
ahci_ctlp->ahcictl_num_implemented_ports;
- sata_hba_tran->sata_tran_hba_features_support = 0;
+ /* Support ATAPI device */
+ sata_hba_tran->sata_tran_hba_features_support = SATA_CTLF_ATAPI;
/* Get the data transfer capability for PIO command by the HBA */
if (cap_status & AHCI_HBA_CAP_PMD) {
- ahci_ctlp->ahcictl_flags |= AHCI_PMD;
+ ahci_ctlp->ahcictl_flags |= AHCI_CAP_PIO_MDRQ;
AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "HBA supports multiple "
"DRQ block data transfer for PIO command protocol");
} else {
@@ -900,8 +968,8 @@ ahci_tran_probe_port(dev_info_t *dip, sata_device_t *sd)
port = ahci_ctlp->ahcictl_cport_to_port[cport];
AHCIDBG3(AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_tran_probe_port enter: cport: 0x%x, "
- "pmport: 0x%x, qual: 0x%x", cport, pmport, qual);
+ "ahci_tran_probe_port enter: cport: %d, "
+ "pmport: %d, qual: %d", cport, pmport, qual);
ahci_portp = ahci_ctlp->ahcictl_ports[port];
@@ -913,31 +981,31 @@ ahci_tran_probe_port(dev_info_t *dip, sata_device_t *sd)
case SATA_PSTATE_FAILED:
sd->satadev_state = SATA_PSTATE_FAILED;
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
- "ahci_tran_probe_port %d: PORT FAILED", port);
+ "ahci_tran_probe_port: port %d PORT FAILED", port);
goto out;
case SATA_PSTATE_SHUTDOWN:
sd->satadev_state = SATA_PSTATE_SHUTDOWN;
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
- "ahci_tran_probe_port %d: PORT SHUTDOWN", port);
+ "ahci_tran_probe_port: port %d PORT SHUTDOWN", port);
goto out;
case SATA_PSTATE_PWROFF:
sd->satadev_state = SATA_PSTATE_PWROFF;
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
- "ahci_tran_probe_port %d: PORT PWROFF", port);
+ "ahci_tran_probe_port: port %d PORT PWROFF", port);
goto out;
case SATA_PSTATE_PWRON:
sd->satadev_state = SATA_PSTATE_PWRON;
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "ahci_tran_probe_port %d: PORT PWRON", port);
+ "ahci_tran_probe_port: port %d PORT PWRON", port);
break;
default:
sd->satadev_state = port_state;
AHCIDBG2(AHCIDBG_INFO, ahci_ctlp,
- "ahci_tran_probe_port %d: PORT NORMAL %x",
+ "ahci_tran_probe_port: port %d PORT NORMAL %x",
port, port_state);
break;
}
@@ -949,33 +1017,33 @@ ahci_tran_probe_port(dev_info_t *dip, sata_device_t *sd)
case SATA_DTYPE_ATADISK:
sd->satadev_type = SATA_DTYPE_ATADISK;
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "ahci_tran_probe_port %d: DISK found", port);
+ "ahci_tran_probe_port: port %d DISK found", port);
break;
case SATA_DTYPE_ATAPICD:
sd->satadev_type = SATA_DTYPE_ATAPICD;
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "ahci_tran_probe_port %d: ATAPI found", port);
+ "ahci_tran_probe_port: port %d ATAPI found", port);
break;
case SATA_DTYPE_PMULT:
sd->satadev_type = SATA_DTYPE_PMULT;
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "ahci_tran_probe_port %d: Port Multiplier found",
+ "ahci_tran_probe_port: port %d Port Multiplier found",
port);
break;
case SATA_DTYPE_UNKNOWN:
sd->satadev_type = SATA_DTYPE_UNKNOWN;
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "ahci_tran_probe_port %d: Unknown device found", port);
+ "ahci_tran_probe_port: port %d Unknown device found", port);
break;
default:
/* we don't support any other device types */
sd->satadev_type = SATA_DTYPE_NONE;
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "ahci_tran_probe_port %d: No device found", port);
+ "ahci_tran_probe_port: port %d No device found", port);
break;
}
@@ -987,6 +1055,32 @@ out:
}
/*
+ * There are four operation modes in sata framework:
+ * SATA_OPMODE_INTERRUPTS
+ * SATA_OPMODE_POLLING
+ * SATA_OPMODE_ASYNCH
+ * SATA_OPMODE_SYNCH
+ *
+ * Their combined meanings as following:
+ *
+ * SATA_OPMODE_SYNCH
+ * The command has to be completed before sata_tran_start functions returns.
+ * Either interrupts or polling could be used - it's up to the driver.
+ * Mode used currently for internal, sata-module initiated operations.
+ *
+ * SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS
+ * It is the same as the one above.
+ *
+ * SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING
+ * The command has to be completed before sata_tran_start function returns.
+ * No interrupt used, polling only. This should be the mode used for scsi
+ * packets with FLAG_NOINTR.
+ *
+ * SATA_OPMODE_ASYNCH | SATA_OPMODE_INTERRUPTS
+ * The command may be queued (callback function specified). Interrupts could
+ * be used. It's normal operation mode.
+ */
+/*
* Called by sata framework to transport a sata packet down stream.
*/
static int
@@ -996,7 +1090,6 @@ ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt)
ahci_port_t *ahci_portp;
uint8_t cport = spkt->satapkt_device.satadev_addr.cport;
uint8_t port;
- int slot;
ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip));
port = ahci_ctlp->ahcictl_cport_to_port[cport];
@@ -1025,7 +1118,7 @@ ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt)
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_tran_start returning PORT_ERROR while "
"port in FAILED/SHUTDOWN/PWROFF state: "
- "cport: 0x%x", port);
+ "port: %d", port);
mutex_exit(&ahci_portp->ahciport_mutex);
return (SATA_TRAN_PORT_ERROR);
}
@@ -1043,7 +1136,7 @@ ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt)
&spkt->satapkt_device);
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_tran_start returning PORT_ERROR while "
- "no device attached: cport: 0x%x", port);
+ "no device attached: port: %d", port);
mutex_exit(&ahci_portp->ahciport_mutex);
return (SATA_TRAN_PORT_ERROR);
}
@@ -1063,7 +1156,7 @@ ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt)
ahci_portp->ahciport_reset_in_progress = 0;
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_tran_start clearing the "
- "reset_in_progress for port: 0x%x", port);
+ "reset_in_progress for port: %d", port);
}
if (ahci_portp->ahciport_reset_in_progress &&
@@ -1072,45 +1165,147 @@ ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt)
spkt->satapkt_reason = SATA_PKT_BUSY;
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_tran_start returning BUSY while "
- "reset in progress: cport: 0x%x", port);
+ "reset in progress: port: %d", port);
mutex_exit(&ahci_portp->ahciport_mutex);
return (SATA_TRAN_BUSY);
}
- if (ahci_portp->ahciport_flags & AHCI_PORT_STATE_MOPPING) {
+ if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) {
spkt->satapkt_reason = SATA_PKT_BUSY;
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_tran_start returning BUSY while "
- "mopping in progress: port: 0x%x", port);
+ "mopping in progress: port: %d", port);
mutex_exit(&ahci_portp->ahciport_mutex);
return (SATA_TRAN_BUSY);
}
- if ((slot = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, port, spkt))
- == AHCI_FAILURE) {
- spkt->satapkt_reason = SATA_PKT_QUEUE_FULL;
- AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start "
- "returning QUEUE_FULL: port %d", port);
+ if ((ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE) &&
+ (ahci_portp->ahciport_pending_tags != 0)) {
+ AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_tran_start "
+ "return QUEUE_FULL: port %d because HBA cannot "
+ "use multiple command lists for non-queued commands",
+ port);
mutex_exit(&ahci_portp->ahciport_mutex);
return (SATA_TRAN_QUEUE_FULL);
}
if (spkt->satapkt_op_mode &
- (SATA_OPMODE_POLLING | SATA_OPMODE_SYNCH)) {
- /* we need to poll now */
- mutex_exit(&ahci_portp->ahciport_mutex);
- ahci_poll_cmd(ahci_ctlp, ahci_portp, port, slot, spkt);
- mutex_enter(&ahci_portp->ahciport_mutex);
+ (SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING)) {
+ /* We need to do the sync start now */
+ if (ahci_do_sync_start(ahci_ctlp, ahci_portp, port,
+ spkt) == AHCI_FAILURE) {
+ AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start "
+ "return QUEUE_FULL: port %d", port);
+ mutex_exit(&ahci_portp->ahciport_mutex);
+ return (SATA_TRAN_QUEUE_FULL);
+ }
+ } else {
+ /* Async start, using interrupt */
+ if (ahci_deliver_satapkt(ahci_ctlp, ahci_portp, port, spkt)
+ == AHCI_FAILURE) {
+ spkt->satapkt_reason = SATA_PKT_QUEUE_FULL;
+ AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start "
+ "returning QUEUE_FULL: port %d", port);
+ mutex_exit(&ahci_portp->ahciport_mutex);
+ return (SATA_TRAN_QUEUE_FULL);
+ }
}
- mutex_exit(&ahci_portp->ahciport_mutex);
-
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_tran_start "
"sata tran accepted: port %d", port);
+ mutex_exit(&ahci_portp->ahciport_mutex);
return (SATA_TRAN_ACCEPTED);
}
+/*
+ * SATA_OPMODE_SYNCH flag is set
+ *
+ * If SATA_OPMODE_POLLING flag is set, then we must poll the command
+ * without interrupt, otherwise we can still use the interrupt.
+ *
+ * WARNING!!! ahciport_mutex should be acquired before the function
+ * is called.
+ */
+static int
+ahci_do_sync_start(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
+ uint8_t port, sata_pkt_t *spkt)
+{
+ int pkt_timeout_ticks;
+ uint32_t timeout_tags;
+ uint32_t retrieve_errinfo_slot_status = 0;
+ int rval;
+
+ AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, "ahci_do_sync_start enter: "
+ "port %d spkt 0x%p", port, spkt);
+
+ if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) {
+ /* Disable the port interrupt */
+ ahci_disable_port_intrs(ahci_ctlp, ahci_portp, port);
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_POLLING;
+
+ if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp,
+ port, spkt)) == AHCI_FAILURE) {
+ ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_POLLING;
+ ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
+ return (rval);
+ }
+
+ pkt_timeout_ticks =
+ drv_usectohz((clock_t)spkt->satapkt_time * 1000000);
+
+ /*
+ * AHCI_PORT_FLAG_RQSENSE means the command is the REQUEST
+ * SENSE which is sent down to retrieve sense data during
+ * error recovery, so we need to keep the slot number of it
+ * in order to handle it's completion.
+ */
+ if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_RQSENSE) {
+ /* rval is the allocated command slot */
+ retrieve_errinfo_slot_status = 0x1 << rval;
+ }
+
+ while (spkt->satapkt_reason == SATA_PKT_BUSY) {
+ mutex_exit(&ahci_portp->ahciport_mutex);
+
+ /* Simulate the interrupt */
+ ahci_port_intr(ahci_ctlp, ahci_portp, port,
+ retrieve_errinfo_slot_status);
+
+ drv_usecwait(AHCI_1MS_USECS);
+
+ mutex_enter(&ahci_portp->ahciport_mutex);
+ pkt_timeout_ticks -= AHCI_1MS_TICKS;
+ if (pkt_timeout_ticks < 0) {
+ cmn_err(CE_NOTE, "!ahci_do_sync_start: "
+ "port %d satapkt 0x%p timed out\n",
+ port, (void *)spkt);
+ timeout_tags = (0x1 << rval);
+ mutex_exit(&ahci_portp->ahciport_mutex);
+ ahci_timeout_pkts(ahci_ctlp, ahci_portp,
+ port, timeout_tags,
+ retrieve_errinfo_slot_status);
+ mutex_enter(&ahci_portp->ahciport_mutex);
+ }
+ }
+ ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_POLLING;
+ /* Enable the port interrupt */
+ ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
+ return (AHCI_SUCCESS);
+
+ } else {
+ if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp,
+ port, spkt)) == AHCI_FAILURE)
+ return (rval);
+
+ while (spkt->satapkt_reason == SATA_PKT_BUSY)
+ cv_wait(&ahci_portp->ahciport_cv,
+ &ahci_portp->ahciport_mutex);
+
+ return (AHCI_SUCCESS);
+ }
+}
+
#define SENDUP_PACKET(ahci_portp, satapkt, reason) \
if (satapkt) { \
satapkt->satapkt_reason = reason; \
@@ -1120,11 +1315,16 @@ ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt)
*/ \
} \
if (satapkt && \
- !(satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \
+ ! (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \
satapkt->satapkt_comp) { \
mutex_exit(&ahci_portp->ahciport_mutex); \
(*satapkt->satapkt_comp)(satapkt); \
mutex_enter(&ahci_portp->ahciport_mutex); \
+ } else { \
+ if (satapkt && \
+ (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \
+ ! (satapkt->satapkt_op_mode & SATA_OPMODE_POLLING)) \
+ cv_signal(&ahci_portp->ahciport_cv); \
}
/*
@@ -1178,34 +1378,38 @@ static int
ahci_deliver_satapkt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
uint8_t port, sata_pkt_t *spkt)
{
- int slot;
- sata_cmd_t *cmd;
+ int cmd_slot;
+ sata_cmd_t *scmd;
ahci_fis_h2d_register_t *h2d_register_fisp;
ahci_cmd_table_t *cmd_table;
ahci_cmd_header_t *cmd_header;
int ncookies;
int i;
+#if AHCI_DEBUG
+ uint32_t *ptr;
+ uint8_t *ptr2;
+#endif
spkt->satapkt_reason = SATA_PKT_BUSY;
- cmd = &spkt->satapkt_cmd;
+ scmd = &spkt->satapkt_cmd;
- /* Check if there is an empty command slot */
- slot = ahci_claim_free_slot(ahci_ctlp, ahci_portp);
- if (slot == AHCI_FAILURE) {
+ /* check if there is an empty command slot */
+ cmd_slot = ahci_claim_free_slot(ahci_ctlp, ahci_portp);
+ if (cmd_slot == AHCI_FAILURE) {
AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "no free slot");
return (AHCI_FAILURE);
}
AHCIDBG4(AHCIDBG_ENTRY|AHCIDBG_INFO, ahci_ctlp,
- "ahci_deliver_satapkt enter: cmd_reg: 0x%x, slot: 0x%x, "
- "port: %d, satapkt: 0x%p", cmd->satacmd_cmd_reg,
- slot, port, (void *)spkt);
+ "ahci_deliver_satapkt enter: cmd_reg: 0x%x, cmd_slot: 0x%x, "
+ "port: %d, satapkt: 0x%p", scmd->satacmd_cmd_reg,
+ cmd_slot, port, (void *)spkt);
- cmd_table = ahci_portp->ahciport_cmd_tables[slot];
+ cmd_table = ahci_portp->ahciport_cmd_tables[cmd_slot];
bzero((void *)cmd_table, ahci_cmd_table_size);
- /* For data transfer operations, this is the H2D Register FIS */
+ /* for data transfer operations, this is the h2d register fis */
h2d_register_fisp =
&(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register);
@@ -1217,65 +1421,65 @@ ahci_deliver_satapkt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
}
SET_FIS_CDMDEVCTL(h2d_register_fisp, 1);
- SET_FIS_COMMAND(h2d_register_fisp, cmd->satacmd_cmd_reg);
- SET_FIS_FEATURES(h2d_register_fisp, cmd->satacmd_features_reg);
- SET_FIS_SECTOR_COUNT(h2d_register_fisp, cmd->satacmd_sec_count_lsb);
+ SET_FIS_COMMAND(h2d_register_fisp, scmd->satacmd_cmd_reg);
+ SET_FIS_FEATURES(h2d_register_fisp, scmd->satacmd_features_reg);
+ SET_FIS_SECTOR_COUNT(h2d_register_fisp, scmd->satacmd_sec_count_lsb);
- switch (cmd->satacmd_addr_type) {
+ switch (scmd->satacmd_addr_type) {
case ATA_ADDR_LBA:
/* fallthrough */
case ATA_ADDR_LBA28:
/* LBA[7:0] */
- SET_FIS_SECTOR(h2d_register_fisp, cmd->satacmd_lba_low_lsb);
+ SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb);
/* LBA[15:8] */
- SET_FIS_CYL_LOW(h2d_register_fisp, cmd->satacmd_lba_mid_lsb);
+ SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb);
/* LBA[23:16] */
- SET_FIS_CYL_HI(h2d_register_fisp, cmd->satacmd_lba_high_lsb);
+ SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb);
/* LBA [27:24] (also called dev_head) */
- SET_FIS_DEV_HEAD(h2d_register_fisp, cmd->satacmd_device_reg);
+ SET_FIS_DEV_HEAD(h2d_register_fisp, scmd->satacmd_device_reg);
break;
case ATA_ADDR_LBA48:
/* LBA[7:0] */
- SET_FIS_SECTOR(h2d_register_fisp, cmd->satacmd_lba_low_lsb);
+ SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb);
/* LBA[15:8] */
- SET_FIS_CYL_LOW(h2d_register_fisp, cmd->satacmd_lba_mid_lsb);
+ SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb);
/* LBA[23:16] */
- SET_FIS_CYL_HI(h2d_register_fisp, cmd->satacmd_lba_high_lsb);
+ SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb);
/* LBA [31:24] */
SET_FIS_SECTOR_EXP(h2d_register_fisp,
- cmd->satacmd_lba_low_msb);
+ scmd->satacmd_lba_low_msb);
/* LBA [39:32] */
SET_FIS_CYL_LOW_EXP(h2d_register_fisp,
- cmd->satacmd_lba_mid_msb);
+ scmd->satacmd_lba_mid_msb);
/* LBA [47:40] */
SET_FIS_CYL_HI_EXP(h2d_register_fisp,
- cmd->satacmd_lba_high_msb);
+ scmd->satacmd_lba_high_msb);
/* Set dev_head */
SET_FIS_DEV_HEAD(h2d_register_fisp,
- cmd->satacmd_device_reg);
+ scmd->satacmd_device_reg);
/* Set the extended sector count and features */
SET_FIS_SECTOR_COUNT_EXP(h2d_register_fisp,
- cmd->satacmd_sec_count_msb);
+ scmd->satacmd_sec_count_msb);
SET_FIS_FEATURES_EXP(h2d_register_fisp,
- cmd->satacmd_features_reg_ext);
+ scmd->satacmd_features_reg_ext);
break;
}
- ncookies = cmd->satacmd_num_dma_cookies;
+ ncookies = scmd->satacmd_num_dma_cookies;
AHCIDBG2(AHCIDBG_INFO, ahci_ctlp,
"ncookies = 0x%x, ahci_dma_prdt_number = 0x%x",
ncookies, ahci_dma_prdt_number);
@@ -1285,31 +1489,59 @@ ahci_deliver_satapkt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
/* *** now fill the scatter gather list ******* */
for (i = 0; i < ncookies; i++) {
cmd_table->ahcict_prdt[i].ahcipi_data_base_addr =
- cmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[0];
+ scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[0];
cmd_table->ahcict_prdt[i].ahcipi_data_base_addr_upper =
- cmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[1];
+ scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[1];
cmd_table->ahcict_prdt[i].ahcipi_descr_info =
- cmd->satacmd_dma_cookie_list[i].dmac_size - 1;
+ scmd->satacmd_dma_cookie_list[i].dmac_size - 1;
+ }
+
+ /* The ACMD field is filled in for ATAPI command */
+ if (scmd->satacmd_cmd_reg == SATAC_PACKET) {
+ bcopy(scmd->satacmd_acdb, cmd_table->ahcict_atapi_cmd,
+ SATA_ATAPI_MAX_CDB_LEN);
+ /*
+ * For ATAPI command, scmd->satacmd_addr_type is 0
+ */
+ SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb);
+ SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb);
}
/* Set Command Header in Command List */
- cmd_header = &ahci_portp->ahciport_cmd_list[slot];
+ cmd_header = &ahci_portp->ahciport_cmd_list[cmd_slot];
BZERO_DESCR_INFO(cmd_header);
BZERO_PRD_BYTE_COUNT(cmd_header);
+
+ /* Set the number of entries in the PRD table */
SET_PRD_TABLE_LENGTH(cmd_header, ncookies);
+
+ /* Set the length of the command in the CFIS area */
SET_COMMAND_FIS_LENGTH(cmd_header, AHCI_H2D_REGISTER_FIS_LENGTH);
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "command data direction is "
"sata_data_direction = 0x%x",
- cmd->satacmd_flags.sata_data_direction);
+ scmd->satacmd_flags.sata_data_direction);
- if (cmd->satacmd_flags.sata_data_direction == SATA_DIR_WRITE)
+ /* Set A bit if it is an ATAPI command */
+ if (scmd->satacmd_cmd_reg == SATAC_PACKET)
+ SET_ATAPI(cmd_header, AHCI_CMDHEAD_ATAPI);
+
+ /* Set W bit if data is going to the device */
+ if (scmd->satacmd_flags.sata_data_direction == SATA_DIR_WRITE)
SET_WRITE(cmd_header, AHCI_CMDHEAD_DATA_WRITE);
+ /*
+ * Set the prefetchable bit - this bit is only valid if the PRDTL
+ * field is non-zero or the ATAPI 'A' bit is set in the command
+ * header. This bit cannot be set when using native command
+ * queuing commands or when using FIS-based switching with a Port
+ * Multiplier. At the moment, the driver doesn't support these two
+ * functions, so it's always setting the 'P' bit.
+ */
SET_PREFETCHABLE(cmd_header, AHCI_CMDHEAD_PREFETCHABLE);
/* Now remember the sata packet in ahciport_slot_pkts[]. */
- ahci_portp->ahciport_slot_pkts[slot] = spkt;
+ ahci_portp->ahciport_slot_pkts[cmd_slot] = spkt;
/*
* We are overloading satapkt_hba_driver_private with
@@ -1317,100 +1549,64 @@ ahci_deliver_satapkt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
*/
spkt->satapkt_hba_driver_private = (void *)(intptr_t)0;
- /* *** finished filling the scatter gather list , sync******* */
- (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot],
+#if AHCI_DEBUG
+ /* Dump the command header and table */
+ AHCIDBG0(AHCIDBG_COMMAND, ahci_ctlp, "\n");
+ AHCIDBG3(AHCIDBG_COMMAND, ahci_ctlp,
+ "Command header&table for spkt 0x%p cmd_reg 0x%x port%d",
+ spkt, scmd->satacmd_cmd_reg, port);
+ ptr = (uint32_t *)cmd_header;
+ AHCIDBG4(AHCIDBG_COMMAND, ahci_ctlp,
+ " Command Header:%8x %8x %8x %8x",
+ ptr[0], ptr[1], ptr[2], ptr[3]);
+
+ /* Dump the H2D register FIS */
+ ptr = (uint32_t *)h2d_register_fisp;
+ AHCIDBG4(AHCIDBG_COMMAND, ahci_ctlp,
+ " Command FIS: %8x %8x %8x %8x",
+ ptr[0], ptr[1], ptr[2], ptr[3]);
+
+ /* Dump the ACMD register FIS */
+ ptr2 = (uint8_t *)&(cmd_table->ahcict_atapi_cmd);
+ for (i = 0; i < SATA_ATAPI_MAX_CDB_LEN/8; i++)
+ if (ahci_debug_flags & AHCIDBG_COMMAND)
+ ahci_log(ahci_ctlp, CE_WARN,
+ " ATAPI command: %2x %2x %2x %2x "
+ "%2x %2x %2x %2x",
+ ptr2[8 * i], ptr2[8 * i + 1],
+ ptr2[8 * i + 2], ptr2[8 * i + 3],
+ ptr2[8 * i + 4], ptr2[8 * i + 5],
+ ptr2[8 * i + 6], ptr2[8 * i + 7]);
+
+ /* Dump the PRDT */
+ for (i = 0; i < ncookies; i++) {
+ ptr = (uint32_t *)&(cmd_table->ahcict_prdt[i]);
+ AHCIDBG5(AHCIDBG_COMMAND, ahci_ctlp,
+ " Cookie %d: %8x %8x %8x %8x",
+ i, ptr[0], ptr[1], ptr[2], ptr[3]);
+ }
+#endif
+
+ (void) ddi_dma_sync(
+ ahci_portp->ahciport_cmd_tables_dma_handle[cmd_slot],
0,
ahci_cmd_table_size,
DDI_DMA_SYNC_FORDEV);
(void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle,
- slot * sizeof (ahci_cmd_header_t),
+ cmd_slot * sizeof (ahci_cmd_header_t),
sizeof (ahci_cmd_header_t),
DDI_DMA_SYNC_FORDEV);
/* Indicate to the HBA that a command is active. */
ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port),
- (0x1 << slot));
+ (0x1 << cmd_slot));
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_deliver_satapkt "
"exit: port %d", port);
- return (slot);
-}
-
-/*
- * Polls for the completion of the command. This is safe with both
- * interrupts enabled or disabled.
- */
-static void
-ahci_poll_cmd(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
- uint8_t port, int slot, sata_pkt_t *spkt)
-{
- uint32_t slot_status;
- int pkt_timeout_ticks;
- int in_panic = ddi_in_panic();
-
- AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INFO, ahci_ctlp,
- "ahci_poll_cmd entered, port: %x", port);
-
- pkt_timeout_ticks =
- drv_usectohz((clock_t)spkt->satapkt_time * 1000000);
-
- mutex_enter(&ahci_portp->ahciport_mutex);
-
- /* we start out with SATA_PKT_COMPLETED as the satapkt_reason */
- spkt->satapkt_reason = SATA_PKT_COMPLETED;
-
- /*
- * The interrupt handler will return directly for poll commands in case
- * the interrupt has been generated before interrupts were disabled.
- */
- ahci_disable_port_intrs(ahci_ctlp, ahci_portp, port);
-
- do {
- slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
-
- if (slot_status & (0x1 << slot)) {
- if (in_panic) {
- /*
- * If we are in panic, we can't rely on
- * timers; so, busy wait instead of delay().
- */
- mutex_exit(&ahci_portp->ahciport_mutex);
- drv_usecwait(AHCI_1MS_USECS);
- mutex_enter(&ahci_portp->ahciport_mutex);
- } else {
- mutex_exit(&ahci_portp->ahciport_mutex);
-#ifndef __lock_lint
- delay(AHCI_1MS_TICKS);
-#endif /* __lock_lint */
- mutex_enter(&ahci_portp->ahciport_mutex);
- }
- } else {
- AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "poll_cmd finished");
- break;
- }
-
- pkt_timeout_ticks -= AHCI_1MS_TICKS;
-
- } while (pkt_timeout_ticks > 0);
-
- if (spkt->satapkt_reason != SATA_PKT_COMPLETED) {
- goto poll_done;
- }
-
- if (slot_status & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)) {
- spkt->satapkt_reason = SATA_PKT_TIMEOUT;
- }
-
- CLEAR_BIT(ahci_portp->ahciport_pending_tags, slot);
- ahci_portp->ahciport_slot_pkts[slot] = NULL;
-
-poll_done:
- ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
- mutex_exit(&ahci_portp->ahciport_mutex);
+ return (cmd_slot);
}
/*
@@ -1433,17 +1629,16 @@ ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag)
port = ahci_ctlp->ahcictl_cport_to_port[cport];
AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_tran_abort on cport %d", cport);
+ "ahci_tran_abort enter: port %d", port);
ahci_portp = ahci_ctlp->ahcictl_ports[port];
-
mutex_enter(&ahci_portp->ahciport_mutex);
/*
- * To prevent recursive enter to ahci_mop_commands, we need
- * check AHCI_PORT_STATE_MOPPING flag.
+ * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending
+ * commands are being mopped, therefore there is nothing else to do
*/
- if (ahci_portp->ahciport_flags & AHCI_PORT_STATE_MOPPING) {
+ if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) {
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
"ahci_tran_abort: port %d is in "
"mopping process, so just return directly ", port);
@@ -1467,7 +1662,7 @@ ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag)
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_tran_abort returning SATA_FAILURE while "
"port in FAILED/SHUTDOWN/PWROFF state: "
- "cport: 0x%x", port);
+ "port: %d", port);
mutex_exit(&ahci_portp->ahciport_mutex);
return (SATA_FAILURE);
}
@@ -1485,7 +1680,7 @@ ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag)
&spkt->satapkt_device);
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_tran_abort returning SATA_FAILURE while "
- "no device attached: cport: 0x%x", port);
+ "no device attached: port: %d", port);
mutex_exit(&ahci_portp->ahciport_mutex);
return (SATA_FAILURE);
}
@@ -1524,11 +1719,12 @@ ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag)
slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
- ahci_portp->ahciport_flags |= AHCI_PORT_STATE_MOPPING;
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING;
+ ahci_portp->ahciport_mop_in_progress++;
/*
* To abort the packet(s), first we are trying to clear PxCMD.ST
- * and PxCMD.FRE to stop the port, and if the port can be stopped
+ * to stop the port, and if the port can be stopped
* successfully with PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0',
* then we just send back the aborted packet(s) with ABORTED flag
* and then restart the port by setting PxCMD.ST and PxCMD.FRE.
@@ -1536,7 +1732,7 @@ ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag)
* perform a COMRESET.
*/
(void) ahci_restart_port_wait_till_ready(ahci_ctlp,
- ahci_portp, port, NULL);
+ ahci_portp, port, NULL, NULL);
/*
* Compute which have finished and which need to be retried.
@@ -1550,7 +1746,6 @@ ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag)
aborted_tags &= ~finished_tags;
- mutex_exit(&ahci_portp->ahciport_mutex);
ahci_mop_commands(ahci_ctlp,
ahci_portp,
port,
@@ -1560,7 +1755,6 @@ ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag)
aborted_tags,
0); /* reset tags */
- mutex_enter(&ahci_portp->ahciport_mutex);
ahci_update_sata_registers(ahci_ctlp, port, &spkt->satapkt_device);
mutex_exit(&ahci_portp->ahciport_mutex);
@@ -1586,10 +1780,10 @@ ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp,
"ahci_reset_device_reject_pkts on port: %d", port);
/*
- * To prevent recursive enter to ahci_mop_commands, we need
- * check AHCI_PORT_STATE_MOPPING flag.
+ * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending
+ * commands are being mopped, therefore there is nothing else to do
*/
- if (ahci_portp->ahciport_flags & AHCI_PORT_STATE_MOPPING) {
+ if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) {
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_reset_device_reject_pkts: port %d is in "
"mopping process, so return directly ", port);
@@ -1608,7 +1802,6 @@ ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp,
"reset failed", port);
ret = ahci_port_reset(ahci_ctlp, ahci_portp, port);
if (ret != AHCI_SUCCESS) {
- ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED;
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_reset_device_reject_pkts: port %d "
"failed", port);
@@ -1618,18 +1811,14 @@ ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp,
/* Set the reset in progress flag */
ahci_portp->ahciport_reset_in_progress = 1;
- ahci_portp->ahciport_flags |= AHCI_PORT_STATE_MOPPING;
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING;
+ ahci_portp->ahciport_mop_in_progress++;
/* Indicate to the framework that a reset has happened */
bzero((void *)&sdevice, sizeof (sata_device_t));
sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port];
- sdevice.satadev_addr.pmport = AHCI_PORTMULT_CONTROL_PORT;
-
- if (ahci_portp->ahciport_device_type == SATA_DTYPE_PMULT) {
- sdevice.satadev_addr.qual = SATA_ADDR_DPMPORT;
- } else {
- sdevice.satadev_addr.qual = SATA_ADDR_DCPORT;
- }
+ sdevice.satadev_addr.pmport = 0;
+ sdevice.satadev_addr.qual = SATA_ADDR_DCPORT;
sdevice.satadev_state = SATA_DSTATE_RESET |
SATA_DSTATE_PWR_ACTIVE;
@@ -1649,7 +1838,6 @@ ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp,
reset_tags &= ~finished_tags;
- mutex_exit(&ahci_portp->ahciport_mutex);
ahci_mop_commands(ahci_ctlp,
ahci_portp,
port,
@@ -1658,7 +1846,6 @@ ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp,
0, /* timeout tags */
0, /* aborted tags */
reset_tags); /* reset tags */
- mutex_enter(&ahci_portp->ahciport_mutex);
return (SATA_SUCCESS);
}
@@ -1680,17 +1867,18 @@ ahci_reset_port_reject_pkts(ahci_ctl_t *ahci_ctlp,
"ahci_reset_port_reject_pkts on port: %d", port);
/*
- * To prevent recursive enter to ahci_mop_commands, we need
- * check AHCI_PORT_STATE_MOPPING flag.
+ * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending
+ * commands are being mopped, therefore there is nothing else to do
*/
- if (ahci_portp->ahciport_flags & AHCI_PORT_STATE_MOPPING) {
+ if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) {
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_reset_port_reject_pkts: port %d is in "
"mopping process, so return directly ", port);
return (SATA_SUCCESS);
}
- ahci_portp->ahciport_flags |= AHCI_PORT_STATE_MOPPING;
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING;
+ ahci_portp->ahciport_mop_in_progress++;
slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
@@ -1698,7 +1886,7 @@ ahci_reset_port_reject_pkts(ahci_ctl_t *ahci_ctlp,
reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp);
if (ahci_restart_port_wait_till_ready(ahci_ctlp,
- ahci_portp, port, AHCI_PORT_RESET) != AHCI_SUCCESS)
+ ahci_portp, port, AHCI_PORT_RESET, NULL) != AHCI_SUCCESS)
return (SATA_FAILURE);
finished_tags = ahci_portp->ahciport_pending_tags &
@@ -1706,7 +1894,6 @@ ahci_reset_port_reject_pkts(ahci_ctl_t *ahci_ctlp,
reset_tags &= ~finished_tags;
- mutex_exit(&ahci_portp->ahciport_mutex);
ahci_mop_commands(ahci_ctlp,
ahci_portp,
port,
@@ -1715,7 +1902,6 @@ ahci_reset_port_reject_pkts(ahci_ctl_t *ahci_ctlp,
0, /* timeout tags */
0, /* aborted tags */
reset_tags); /* reset tags */
- mutex_enter(&ahci_portp->ahciport_mutex);
return (SATA_SUCCESS);
}
@@ -1738,8 +1924,6 @@ ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp)
AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp,
"ahci_reset_hba_reject_pkts enter");
- mutex_enter(&ahci_ctlp->ahcictl_mutex);
-
for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) {
if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) {
continue;
@@ -1771,9 +1955,9 @@ ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp)
mutex_enter(&ahci_portp->ahciport_mutex);
/*
* To prevent recursive enter to ahci_mop_commands, we need
- * check AHCI_PORT_STATE_MOPPING flag.
+ * check AHCI_PORT_FLAG_MOPPING flag.
*/
- if (ahci_portp->ahciport_flags & AHCI_PORT_STATE_MOPPING) {
+ if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) {
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_reset_hba_reject_pkts: port %d is in "
"mopping process, so return directly ", port);
@@ -1781,19 +1965,16 @@ ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp)
continue;
}
- ahci_portp->ahciport_flags |= AHCI_PORT_STATE_MOPPING;
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING;
+ ahci_portp->ahciport_mop_in_progress++;
/* Indicate to the framework that a reset has happened */
bzero((void *)&sdevice[port], sizeof (sata_device_t));
sdevice[port].satadev_addr.cport =
ahci_ctlp->ahcictl_port_to_cport[port];
- sdevice[port].satadev_addr.pmport = AHCI_PORTMULT_CONTROL_PORT;
+ sdevice[port].satadev_addr.pmport = 0;
+ sdevice[port].satadev_addr.qual = SATA_ADDR_DCPORT;
- if (ahci_portp->ahciport_device_type == SATA_DTYPE_PMULT) {
- sdevice[port].satadev_addr.qual = SATA_ADDR_DPMPORT;
- } else {
- sdevice[port].satadev_addr.qual = SATA_ADDR_DCPORT;
- }
sdevice[port].satadev_state = SATA_DSTATE_RESET |
SATA_DSTATE_PWR_ACTIVE;
mutex_exit(&ahci_portp->ahciport_mutex);
@@ -1812,7 +1993,6 @@ ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp)
reset_tags[port] &= ~finished_tags[port];
- mutex_exit(&ahci_portp->ahciport_mutex);
ahci_mop_commands(ahci_ctlp,
ahci_portp,
port,
@@ -1821,10 +2001,9 @@ ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp)
0, /* timeout tags */
0, /* aborted tags */
reset_tags[port]); /* reset tags */
+ mutex_exit(&ahci_portp->ahciport_mutex);
}
out:
- mutex_exit(&ahci_ctlp->ahcictl_mutex);
-
return (ret);
}
@@ -1844,13 +2023,14 @@ ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd)
port = ahci_ctlp->ahcictl_cport_to_port[cport];
AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_tran_reset_port enter: cport: 0x%x", cport);
+ "ahci_tran_reset_port enter: cport: %d", cport);
switch (sd->satadev_addr.qual) {
case SATA_ADDR_CPORT:
/* Port reset */
ahci_portp = ahci_ctlp->ahcictl_ports[port];
- cmn_err(CE_NOTE, "!ahci port %d reset port", port);
+ cmn_err(CE_NOTE, "!ahci_tran_reset_dport: port %d "
+ "reset port", port);
mutex_enter(&ahci_portp->ahciport_mutex);
ret = ahci_reset_port_reject_pkts(ahci_ctlp, ahci_portp, port);
@@ -1861,7 +2041,8 @@ ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd)
case SATA_ADDR_DCPORT:
/* Device reset */
ahci_portp = ahci_ctlp->ahcictl_ports[port];
- cmn_err(CE_NOTE, "!ahci port %d reset device", port);
+ cmn_err(CE_NOTE, "!ahci_tran_reset_dport: port %d "
+ "reset device", port);
mutex_enter(&ahci_portp->ahciport_mutex);
if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED |
@@ -1877,7 +2058,7 @@ ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd)
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_tran_reset_dport returning SATA_FAILURE "
"while port in FAILED/SHUTDOWN/PWROFF state: "
- "cport: 0x%x", port);
+ "port: %d", port);
mutex_exit(&ahci_portp->ahciport_mutex);
ret = SATA_FAILURE;
break;
@@ -1893,7 +2074,7 @@ ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd)
ahci_update_sata_registers(ahci_ctlp, port, sd);
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_tran_reset_dport returning SATA_FAILURE "
- "while no device attached: cport: 0x%x", port);
+ "while no device attached: port: %d", port);
mutex_exit(&ahci_portp->ahciport_mutex);
ret = SATA_FAILURE;
break;
@@ -1906,14 +2087,16 @@ ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd)
case SATA_ADDR_CNTRL:
/* Reset the whole controller */
- cmn_err(CE_NOTE, "!ahci port %d reset the whole hba", port);
+ cmn_err(CE_NOTE, "!ahci_tran_reset_dport: port %d "
+ "reset the whole hba", port);
ret = ahci_reset_hba_reject_pkts(ahci_ctlp);
break;
case SATA_ADDR_PMPORT:
case SATA_ADDR_DPMPORT:
AHCIDBG0(AHCIDBG_INFO, ahci_ctlp,
- "port multiplier will be supported later");
+ "ahci_tran_reset_dport: port multiplier will be "
+ "supported later");
/* FALLSTHROUGH */
default:
ret = SATA_FAILURE;
@@ -1944,25 +2127,28 @@ ahci_tran_hotplug_port_activate(dev_info_t *dip, sata_device_t *satadev)
ahci_portp = ahci_ctlp->ahcictl_ports[port];
mutex_enter(&ahci_portp->ahciport_mutex);
- cmn_err(CE_NOTE, "!ahci port %d activate", port);
-
- /* Enable the interrupts on the port. */
- ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
+ cmn_err(CE_NOTE, "!ahci port %d is activated", port);
/*
* Reset the port so that the PHY communication would be re-established.
- * But this reset is an internal operation; the sata framework does
- * not need to know about it.
+ * But this reset is an internal operation and the sata module doesn't
+ * need to know about it. Moreover, the port with a device attached will
+ * be started too.
*/
(void) ahci_restart_port_wait_till_ready(ahci_ctlp,
- ahci_portp, port, AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP);
+ ahci_portp, port,
+ AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP,
+ NULL);
/*
* Need to check the link status and device status of the port
* and consider raising power if the port was in D3 state
*/
- ahci_portp->ahciport_port_state = SATA_PSTATE_PWRON;
- satadev->satadev_state = SATA_PSTATE_PWRON;
+ ahci_portp->ahciport_port_state |= SATA_PSTATE_PWRON;
+ ahci_portp->ahciport_port_state &= ~SATA_PSTATE_PWROFF;
+ ahci_portp->ahciport_port_state &= ~SATA_PSTATE_SHUTDOWN;
+
+ satadev->satadev_state = ahci_portp->ahciport_port_state;
ahci_update_sata_registers(ahci_ctlp, port, satadev);
@@ -1982,6 +2168,7 @@ ahci_tran_hotplug_port_deactivate(dev_info_t *dip, sata_device_t *satadev)
ahci_port_t *ahci_portp;
uint8_t cport = satadev->satadev_addr.cport;
uint8_t port;
+ uint32_t port_scontrol;
ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip));
port = ahci_ctlp->ahcictl_cport_to_port[cport];
@@ -1992,22 +2179,32 @@ ahci_tran_hotplug_port_deactivate(dev_info_t *dip, sata_device_t *satadev)
ahci_portp = ahci_ctlp->ahcictl_ports[port];
mutex_enter(&ahci_portp->ahciport_mutex);
- cmn_err(CE_NOTE, "!ahci port %d deactivate", port);
+ cmn_err(CE_NOTE, "!ahci port %d is deactivated", port);
- /* Disable the interrupts on the port. */
+ /* Disable the interrupts on the port */
ahci_disable_port_intrs(ahci_ctlp, ahci_portp, port);
+ if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) {
+ goto phy_offline;
+ }
+
/* First to abort all the pending commands */
ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port);
- /* Then shut down the port */
- (void) ahci_port_into_notrunning_state(ahci_ctlp,
+ /* Then stop the port */
+ (void) ahci_put_port_into_notrunning_state(ahci_ctlp,
ahci_portp, port);
+ /* Next put the PHY offline */
+
+phy_offline:
+ port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port));
+ AHCI_SCONTROL_SET_DET(port_scontrol, AHCI_SCONTROL_DET_PHYOFFLINE);
+
/* Update ahciport_port_state */
ahci_portp->ahciport_port_state = SATA_PSTATE_SHUTDOWN;
-
- satadev->satadev_state = SATA_PSTATE_SHUTDOWN;
+ satadev->satadev_state = ahci_portp->ahciport_port_state;
ahci_update_sata_registers(ahci_ctlp, port, satadev);
@@ -2016,7 +2213,7 @@ ahci_tran_hotplug_port_deactivate(dev_info_t *dip, sata_device_t *satadev)
}
/*
- * To be used to mark all the pending pkts with ABORTED
+ * To be used to mark all the outstanding pkts with SATA_PKT_ABORTED
* when a device is unplugged or a port is deactivated.
*
* WARNING!!! ahciport_mutex should be acquired before the function is called.
@@ -2028,37 +2225,36 @@ ahci_reject_all_abort_pkts(ahci_ctl_t *ahci_ctlp,
uint32_t slot_status;
uint32_t abort_tags;
- AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
+ AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp,
"ahci_reject_all_abort_pkts on port: %d", port);
- /*
- * To prevent recursive enter to ahci_mop_commands, we need
- * check AHCI_PORT_STATE_MOPPING flag.
- */
- if (ahci_portp->ahciport_flags & AHCI_PORT_STATE_MOPPING) {
- AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "ahci_reject_all_abort_pkts: port %d is in "
- "mopping process, so return directly ", port);
- return;
- }
-
- ahci_portp->ahciport_flags |= AHCI_PORT_STATE_MOPPING;
-
slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
-
abort_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp);
- mutex_exit(&ahci_portp->ahciport_mutex);
- ahci_mop_commands(ahci_ctlp,
- ahci_portp,
- port,
- slot_status,
- 0, /* failed tags */
- 0, /* timeout tags */
- abort_tags, /* aborting tags */
- 0); /* reset tags */
- mutex_enter(&ahci_portp->ahciport_mutex);
+ /* No need to do mop when there is no outstanding commands */
+ if (slot_status != 0) {
+ /*
+ * We need to do the mop even AHCI_PORT_FLAG_MOPPING is
+ * already set because during error recovery process, the
+ * REQUEST SENSE command can be delivered to HBA to get
+ * sense data, so when the device is removed, the command
+ * need to be aborted too. And for this kind of condition,
+ * we can make sure the abort_tags is just the REQUEST
+ * SENSE slot number.
+ */
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING;
+ ahci_portp->ahciport_mop_in_progress++;
+
+ ahci_mop_commands(ahci_ctlp,
+ ahci_portp,
+ port,
+ slot_status,
+ 0, /* failed tags */
+ 0, /* timeout tags */
+ abort_tags, /* aborting tags */
+ 0); /* reset tags */
+ }
}
#if defined(__lock_lint)
@@ -2070,12 +2266,75 @@ ahci_selftest(dev_info_t *dip, sata_device_t *device)
#endif
/*
- * Initialize the controller and set up driver data structures.
+ * Allocate the ports structure, only called by ahci_attach
+ */
+static int
+ahci_alloc_ports_state(ahci_ctl_t *ahci_ctlp)
+{
+ int port, cport = 0;
+
+ AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp,
+ "ahci_alloc_ports_state enter");
+
+ mutex_enter(&ahci_ctlp->ahcictl_mutex);
+
+ /* Allocate structures only for the implemented ports */
+ for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) {
+ if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) {
+ AHCIDBG1(AHCIDBG_INIT, ahci_ctlp,
+ "hba port %d not implemented", port);
+ continue;
+ }
+
+#ifndef __lock_lint
+ ahci_ctlp->ahcictl_cport_to_port[cport] = (uint8_t)port;
+ ahci_ctlp->ahcictl_port_to_cport[port] =
+ (uint8_t)cport++;
+#endif /* __lock_lint */
+
+ if (ahci_alloc_port_state(ahci_ctlp, port) != AHCI_SUCCESS) {
+ goto err_out;
+ }
+ }
+
+ mutex_exit(&ahci_ctlp->ahcictl_mutex);
+ return (AHCI_SUCCESS);
+
+err_out:
+ for (port--; port >= 0; port--) {
+ if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) {
+ ahci_dealloc_port_state(ahci_ctlp, port);
+ }
+ }
+
+ mutex_exit(&ahci_ctlp->ahcictl_mutex);
+ return (AHCI_FAILURE);
+}
+
+/*
+ * Reverse of ahci_alloc_ports_state(), only called by ahci_detach
+ */
+static void
+ahci_dealloc_ports_state(ahci_ctl_t *ahci_ctlp)
+{
+ int port;
+
+ mutex_enter(&ahci_ctlp->ahcictl_mutex);
+ for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) {
+ /* if this port is implemented by the HBA */
+ if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port))
+ ahci_dealloc_port_state(ahci_ctlp, port);
+ }
+ mutex_exit(&ahci_ctlp->ahcictl_mutex);
+}
+
+/*
+ * Initialize the controller.
*
* This routine can be called from three seperate cases: DDI_ATTACH,
* PM_LEVEL_D0 and DDI_RESUME. The DDI_ATTACH case is different from
- * other two cases; the memory allocation and device signature probing
- * are attempted only during DDI_ATTACH case.
+ * other two cases; device signature probing are attempted only during
+ * DDI_ATTACH case.
*
* WARNING!!! Disable the whole controller's interrupts before calling and
* the interrupts will be enabled upon successfully return.
@@ -2085,7 +2344,7 @@ ahci_initialize_controller(ahci_ctl_t *ahci_ctlp)
{
ahci_port_t *ahci_portp;
uint32_t ghc_control;
- int port, cport = 0;
+ int port;
AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp,
"ahci_initialize_controller enter");
@@ -2107,24 +2366,9 @@ ahci_initialize_controller(ahci_ctl_t *ahci_ctlp)
/* Initialize the implemented ports and structures */
for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) {
if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) {
- AHCIDBG1(AHCIDBG_INIT, ahci_ctlp,
- "hba port %d not implemented", port);
continue;
}
- /* only allocate port during attach */
- if (ahci_ctlp->ahcictl_flags & AHCI_ATTACH) {
-#ifndef __lock_lint
- ahci_ctlp->ahcictl_cport_to_port[cport] = (uint8_t)port;
- ahci_ctlp->ahcictl_port_to_cport[port] =
- (uint8_t)cport++;
-#endif /* __lock_lint */
- if (ahci_alloc_port_state(ahci_ctlp, port) !=
- AHCI_SUCCESS) {
- goto err_out;
- }
- }
-
ahci_portp = ahci_ctlp->ahcictl_ports[port];
mutex_enter(&ahci_portp->ahciport_mutex);
@@ -2141,7 +2385,7 @@ ahci_initialize_controller(ahci_ctl_t *ahci_ctlp)
* Set the port state to SATA_PSTATE_FAILED if
* failed to initialize it.
*/
- ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED;
+ ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED;
}
mutex_exit(&ahci_portp->ahciport_mutex);
@@ -2152,44 +2396,21 @@ ahci_initialize_controller(ahci_ctl_t *ahci_ctlp)
mutex_exit(&ahci_ctlp->ahcictl_mutex);
return (AHCI_SUCCESS);
-err_out:
-
- for (port--; port >= 0; port--) {
- if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) {
- ahci_dealloc_port_state(ahci_ctlp, port);
- }
- }
-
- mutex_exit(&ahci_ctlp->ahcictl_mutex);
-
- return (AHCI_FAILURE);
}
/*
- * Reverse of ahci_initialize_controller(), only need to de-allocate
- * all ports' state structures for AHCI_DETACH case.
+ * Reverse of ahci_initialize_controller()
*
* WARNING!!! ahcictl_mutex should be acquired before the function is called.
*/
static void
-ahci_deallocate_controller(ahci_ctl_t *ahci_ctlp)
+ahci_uninitialize_controller(ahci_ctl_t *ahci_ctlp)
{
- uint8_t port;
-
AHCIDBG0(AHCIDBG_INIT, ahci_ctlp,
- "ahci_deallocate_controller enter");
+ "ahci_uninitialize_controller enter");
/* disable all the interrupts. */
ahci_disable_all_intrs(ahci_ctlp);
-
- if (ahci_ctlp->ahcictl_flags & AHCI_DETACH) {
- for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) {
-
- /* if this port is implemented by the HBA */
- if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port))
- ahci_dealloc_port_state(ahci_ctlp, port);
- }
- }
}
/*
@@ -2222,40 +2443,45 @@ ahci_initialize_port(ahci_ctl_t *ahci_ctlp,
AHCI_CMD_STATUS_FRE |
AHCI_CMD_STATUS_FR))) {
- goto done_out;
+ goto next;
}
if (ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp,
- port, AHCI_RESET_NO_EVENTS_UP|AHCI_PORT_INIT) != AHCI_SUCCESS)
+ port, AHCI_RESET_NO_EVENTS_UP|AHCI_PORT_INIT, NULL) != AHCI_SUCCESS)
return (AHCI_FAILURE);
-done_out:
+next:
AHCIDBG1(AHCIDBG_INIT, ahci_ctlp,
- "port %d is in NotRunning state", port);
-
- /* Enable port interrupts */
- ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
+ "port %d is in NotRunning state now", port);
/*
- * Only probe ports/devices and get the types of attached
- * devices during attach.
+ * At the time being, only probe ports/devices and get the types of
+ * attached devices during attach. In fact, the device can be changed
+ * during power state changes, but I would like to postpone this part
+ * when the power management is supported.
*/
if (ahci_ctlp->ahcictl_flags & AHCI_ATTACH) {
- if (ahci_find_dev_signature(ahci_ctlp,
- ahci_portp, port) != AHCI_SUCCESS) {
+ /* Try to get the device signature */
+ ahci_find_dev_signature(ahci_ctlp, ahci_portp, port);
- return (AHCI_FAILURE);
+ /* Return directly if no device connected */
+ if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) {
+ AHCIDBG1(AHCIDBG_INIT, ahci_ctlp,
+ "No device connected to port %d", port);
+ goto out;
}
/* Try to start the port */
- if ((ahci_portp->ahciport_device_type
- == SATA_DTYPE_ATADISK) &&
- (ahci_start_port(ahci_ctlp, port)
- != AHCI_SUCCESS)) {
-
+ if (ahci_start_port(ahci_ctlp, ahci_portp, port)
+ != AHCI_SUCCESS) {
+ AHCIDBG1(AHCIDBG_INIT, ahci_ctlp,
+ "failed to start port %d", port);
return (AHCI_FAILURE);
}
}
+out:
+ /* Enable port interrupts */
+ ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
return (AHCI_SUCCESS);
}
@@ -2281,7 +2507,7 @@ ahci_software_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
ahci_fis_h2d_register_t *h2d_register_fisp;
ahci_cmd_table_t *cmd_table;
ahci_cmd_header_t *cmd_header;
- int32_t port_cmd_status, port_cmd_issue, port_task_file;
+ uint32_t port_cmd_status, port_cmd_issue, port_task_file;
int slot, loop_count;
AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
@@ -2453,7 +2679,7 @@ ahci_software_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
* AHCI port reset ...; the physical communication between the HBA and device
* on a port are disabled. This is more intrusive.
*
- * When an HBA or port reset occurs, Phy communication shall
+ * When an HBA or port reset occurs, Phy communication is going to
* be re-established with the device through a COMRESET followed by the
* normal out-of-band communication sequence defined in Serial ATA. AT
* the end of reset, the device, if working properly, will send a D2H
@@ -2463,7 +2689,7 @@ ahci_software_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
*
* Staggered spin-up is an optional feature in SATA II, and it enables an HBA
* to individually spin-up attached devices. Please refer to chapter 10.9 of
- * AHCI 1.1 spec.
+ * AHCI 1.0 spec.
*/
/*
* WARNING!!! ahciport_mutex should be acquired, intr should be disabled,
@@ -2477,9 +2703,11 @@ ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port)
uint32_t port_scontrol, port_sstatus;
uint32_t port_signature, port_intr_status, port_task_file;
int loop_count;
+ int rval = AHCI_SUCCESS;
AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp,
"Port %d port resetting...", port);
+ ahci_portp->ahciport_port_state = 0;
cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp));
@@ -2494,12 +2722,12 @@ ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port)
*/
if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) {
if (!(ahci_portp->ahciport_flags
- & AHCI_PORT_STATE_SPINUP)) {
+ & AHCI_PORT_FLAG_SPINUP)) {
AHCIDBG1(AHCIDBG_INIT, ahci_ctlp,
"Port %d PxCMD.SUD is zero, force "
"it to do spin-up", port);
ahci_portp->ahciport_flags |=
- AHCI_PORT_STATE_SPINUP;
+ AHCI_PORT_FLAG_SPINUP;
}
}
} else {
@@ -2509,16 +2737,16 @@ ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port)
*/
ASSERT(port_cmd_status & AHCI_CMD_STATUS_SUD);
if (ahci_portp->ahciport_flags &
- AHCI_PORT_STATE_SPINUP) {
+ AHCI_PORT_FLAG_SPINUP) {
AHCIDBG0(AHCIDBG_INIT, ahci_ctlp,
"HBA does not support staggered spin-up "
"force it to do normal COMRESET");
ahci_portp->ahciport_flags &=
- ~AHCI_PORT_STATE_SPINUP;
+ ~AHCI_PORT_FLAG_SPINUP;
}
}
- if (!(ahci_portp->ahciport_flags & AHCI_PORT_STATE_SPINUP)) {
+ if (!(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_SPINUP)) {
/* Do normal COMRESET */
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
"ahci_port_reset: do normal COMRESET", port);
@@ -2539,7 +2767,11 @@ ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port)
(uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
port_cmd_status|AHCI_CMD_STATUS_FRE);
- /* give time for COMRESET to percolate */
+ /*
+ * Give time for COMRESET to percolate, according to the AHCI
+ * spec, software shall wait at least 1 millisecond before
+ * clearing PxSCTL.DET
+ */
#ifndef __lock_lint
delay(AHCI_1MS_TICKS*2);
#endif /* __lock_lint */
@@ -2589,45 +2821,16 @@ ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port)
}
/*
- * After PxSCTL.DET is set to 0h, software should wait for
- * communication to be re-established as indicated by bit 0
- * of PxSSTS.DET being set to '1'.
+ * The port enters P:StartComm state, and HBA tells link layer to
+ * start communication, which involves sending COMRESET to device.
+ * And the HBA resets PxTFD.STS to 7Fh.
+ *
+ * When a COMINIT is received from the device, then the port enters
+ * P:ComInit state. And HBA sets PxTFD.STS to FFh or 80h. HBA sets
+ * PxSSTS.DET to 1h to indicate a device is detected but communication
+ * is not yet established. HBA sets PxSERR.DIAG.X to '1' to indicate
+ * a COMINIT has been received.
*/
- loop_count = 0;
- do {
- port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port));
-
- if (port_intr_status & AHCI_INTR_STATUS_PCS) {
- AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "COMINIT signal is received", port);
- /*
- * Clear PxSERR.DIAG.X to update PxTFD by the D2H FIS
- * received by HBA
- */
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port),
- AHCI_SERROR_DIAG_X);
- }
-
- if (loop_count++ > AHCI_POLLRATE_PORT_COMRESET) {
- /*
- * We are effectively timing out after 0.1 sec.
- */
- break;
- }
-
- /* Wait for 10 millisec */
-#ifndef __lock_lint
- delay(AHCI_10MS_TICKS);
-#endif /* __lock_lint */
-
- } while (!(port_intr_status & AHCI_INTR_STATUS_PCS));
-
- AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp,
- "ahci_port_reset: 1st loop count: %d, "
- "port_intr_status = 0x%x", loop_count, port_intr_status);
-
/*
* The DET field is valid only if IPM field indicates
* that the interface is in active state.
@@ -2663,9 +2866,10 @@ ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port)
} while (AHCI_SSTATUS_GET_DET(port_sstatus) !=
AHCI_SSTATUS_DET_DEVPRESENT_PHYONLINE);
- AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp,
- "ahci_port_reset: 2nd loop count: %d, "
- "port_sstatus = 0x%x", loop_count, port_sstatus);
+ AHCIDBG3(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp,
+ "ahci_port_reset: 1st loop count: %d, "
+ "port_sstatus = 0x%x port %d",
+ loop_count, port_sstatus, port);
if ((AHCI_SSTATUS_GET_IPM(port_sstatus) !=
AHCI_SSTATUS_IPM_INTERFACE_ACTIVE) ||
@@ -2676,152 +2880,148 @@ ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port)
* is no device present.
*/
ahci_portp->ahciport_device_type = SATA_DTYPE_NONE;
-
- (void) ahci_port_into_notrunning_state(ahci_ctlp,
- ahci_portp, port);
+ goto out;
}
- if (AHCI_SSTATUS_GET_DET(port_sstatus) ==
- AHCI_SSTATUS_DET_DEVPRESENT_PHYONLINE) {
- /*
- * If device exist, then first check PxTFD.STS.BSY
- */
- loop_count = 0;
- do {
- port_task_file =
- ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
-
- if (loop_count++ > AHCI_POLLRATE_PORT_TFD_BSY) {
- /*
- * We are effectively timing out after 11 sec.
- */
- break;
- }
-
- port_intr_status =
- ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port));
+ /* Now we can make sure there is a device connected to the port */
+ port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port));
- if (port_intr_status & AHCI_INTR_STATUS_PCS) {
- /*
- * Clear PxSERR.DIAG.X to update PxTFD by
- * the D2H FIS received by HBA.
- */
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp,
- port), AHCI_SERROR_DIAG_X);
- }
+ /* a COMINIT signal is supposed to be received */
+ if (!(port_intr_status & AHCI_INTR_STATUS_PCS)) {
+ cmn_err(CE_WARN, "ahci_port_reset: port %d COMINIT signal "
+ "from the device not received", port);
+ ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED;
+ rval = AHCI_FAILURE;
+ goto out;
+ }
- /* Wait for 10 millisec */
-#ifndef __lock_lint
- delay(AHCI_10MS_TICKS);
-#endif /* __lock_lint */
+ /* PxTFD.STS.BSY is supposed to be set */
+ port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
+ if (!(port_task_file & AHCI_TFD_STS_BSY)) {
+ cmn_err(CE_WARN, "ahci_port_reset: port %d BSY bit "
+ "is not set after COMINIT signal is received", port);
+ ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED;
+ rval = AHCI_FAILURE;
+ goto out;
+ }
- } while (port_task_file & AHCI_TFD_STS_BSY);
+ /*
+ * PxSERR.DIAG.X has to be cleared in order to update PxTFD with
+ * the D2H FIS received by HBA.
+ */
+ ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port),
+ AHCI_SERROR_DIAG_X);
- AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp,
- "ahci_port_reset: 3rd loop count: %d, "
- "port_task_file = 0x%x",
- loop_count, port_task_file);
+ /*
+ * Next check whether COMRESET is completed successfully
+ */
+ loop_count = 0;
+ do {
+ port_task_file =
+ ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
/*
- * Next check COMRESET is completed successfully
+ * The Error bit '1' means COMRESET is finished successfully
+ * The device hardware has been initialized and the power-up
+ * diagnostics successfully completed.
*/
- loop_count = 0;
- do {
- port_task_file =
- ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
-
- if (((port_task_file & AHCI_TFD_ERR_MASK)
- >> AHCI_TFD_ERR_SHIFT) == 0x1) {
- AHCIDBG0(AHCIDBG_INFO, ahci_ctlp,
- "COMRESET success, D2H register FIS "
- "post to received FIS structure");
- break;
- }
+ if (((port_task_file & AHCI_TFD_ERR_MASK)
+ >> AHCI_TFD_ERR_SHIFT) == 0x1) {
+
+ port_signature = ddi_get32(
+ ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port));
+ AHCIDBG2(AHCIDBG_INFO, ahci_ctlp,
+ "COMRESET success, D2H register FIS "
+ "post to received FIS structure "
+ "port %d signature = 0x%x",
+ port, port_signature);
+ goto out_check;
+ }
- if (loop_count++ > AHCI_POLLRATE_PORT_TFD_ERROR) {
- /*
- * We are effectively timing out after 0.1 sec.
- */
- break;
- }
+ if (loop_count++ > AHCI_POLLRATE_PORT_TFD_ERROR) {
+ /*
+ * We are effectively timing out after 11 sec.
+ */
+ break;
+ }
- /* Wait for 10 millisec */
+ /* Wait for 10 millisec */
#ifndef __lock_lint
- delay(AHCI_10MS_TICKS);
+ delay(AHCI_10MS_TICKS);
#endif /* __lock_lint */
- } while (((port_task_file & AHCI_TFD_ERR_MASK)
- >> AHCI_TFD_ERR_SHIFT) != 0x1);
+ } while (((port_task_file & AHCI_TFD_ERR_MASK)
+ >> AHCI_TFD_ERR_SHIFT) != 0x1);
- AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp,
- "ahci_port_reset: 4th loop count: %d, "
- "port_task_file = 0x%x",
- loop_count, port_task_file);
+ AHCIDBG3(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: 2nd loop "
+ "count: %d, port_task_file = 0x%x port %d",
+ loop_count, port_task_file, port);
- port_signature = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port));
- AHCIDBG2(AHCIDBG_INFO, ahci_ctlp, "Port %d signature = 0x%x",
- port, port_signature);
+ cmn_err(CE_WARN, "ahci_port_reset: port %d the device hardware "
+ "has been initialized and the power-up diagnostics failed",
+ port);
- /*
- * Check device status, if keep busy or COMRESET error
- * do device reset to patch some SATA disks' issue
- *
- * For VT8251, sometimes need to do the device reset
- */
- if (port_task_file & AHCI_TFD_STS_BSY) {
- AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "port %d keep busy "
- "need to do device reset", port);
+ ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED;
+ rval = AHCI_FAILURE;
- if (ahci_software_reset(ahci_ctlp, ahci_portp, port)
- != AHCI_SUCCESS) {
- AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "port %d device reset failed", port);
- return (AHCI_FAILURE);
- }
+out:
+ /* Clear port serror register for the port */
+ ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port),
+ AHCI_SERROR_CLEAR_ALL);
+
+ return (rval);
- port_task_file =
- ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
+out_check:
+ /*
+ * Check device status, if keep busy or COMRESET error
+ * do device reset to patch some SATA disks' issue
+ *
+ * For VT8251, sometimes need to do the device reset
+ */
+ if ((port_task_file & AHCI_TFD_STS_BSY) ||
+ (port_task_file & AHCI_TFD_STS_DRQ)) {
+ AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "port %d keep BSY/DRQ set "
+ "need to do device reset", port);
- if (port_task_file & AHCI_TFD_STS_BSY) {
- AHCIDBG2(AHCIDBG_INFO, ahci_ctlp,
- "port %d keep busy after device reset "
- "port_task_file = 0x%x",
- port, port_task_file);
- return (AHCI_FAILURE);
- }
+ (void) ahci_software_reset(ahci_ctlp, ahci_portp, port);
+
+ port_task_file =
+ ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
+
+ if (port_task_file & AHCI_TFD_STS_BSY ||
+ port_task_file & AHCI_TFD_STS_DRQ) {
+ cmn_err(CE_WARN, "ahci_port_reset: port %d "
+ "BSY/DRQ still set after device reset "
+ "port_task_file = 0x%x",
+ port, port_task_file);
+ ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED;
+ rval = AHCI_FAILURE;
}
}
- /* Clear port serror register for each implemented port. */
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port),
- AHCI_SERROR_CLEAR_ALL);
-
- return (AHCI_SUCCESS);
+ goto out;
}
/*
* AHCI HBA reset ...; the entire HBA is reset, and all ports are disabled.
* This is the most intrusive.
*
- * When an HBA reset occurs, Phy communication shall
- * be re-established with the device through a COMRESET followed by the
- * normal out-of-band communication sequence defined in Serial ATA. AT
- * the end of reset, the device, if working properly, will send a D2H
- * Register FIS, which contains the device signature. When the HBA receives
- * this FIS, it updates PxTFD.STS and PxTFD.ERR register fields, and updates
- * the PxSIG register with the signature.
+ * When an HBA reset occurs, Phy communication will be re-established with
+ * the device through a COMRESET followed by the normal out-of-band
+ * communication sequence defined in Serial ATA. AT the end of reset, the
+ * device, if working properly, will send a D2H Register FIS, which contains
+ * the device signature. When the HBA receives this FIS, it updates PxTFD.STS
+ * and PxTFD.ERR register fields, and updates the PxSIG register with the
+ * signature.
*
* Remember to set GHC.AE to 1 before calling ahci_hba_reset.
- *
- * WARNING!!! ahcictl_mutex should be already held before the function
- * is called.
*/
static int
ahci_hba_reset(ahci_ctl_t *ahci_ctlp)
@@ -2834,6 +3034,8 @@ ahci_hba_reset(ahci_ctl_t *ahci_ctlp)
AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, "HBA resetting");
+ mutex_enter(&ahci_ctlp->ahcictl_mutex);
+
ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp));
@@ -2873,6 +3075,7 @@ ahci_hba_reset(ahci_ctl_t *ahci_ctlp)
/* The hba is not reset for some reasons */
AHCIDBG0(AHCIDBG_INIT, ahci_ctlp,
"hba reset failed: HBA in a hung or locked state");
+ mutex_exit(&ahci_ctlp->ahcictl_mutex);
return (AHCI_FAILURE);
}
@@ -2887,7 +3090,6 @@ ahci_hba_reset(ahci_ctl_t *ahci_ctlp)
if (ahci_port_reset(ahci_ctlp, ahci_portp, port)
!= AHCI_SUCCESS) {
- ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED;
rval = AHCI_FAILURE;
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_hba_reset: port %d failed", port);
@@ -2907,45 +3109,55 @@ ahci_hba_reset(ahci_ctl_t *ahci_ctlp)
ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control);
+ mutex_exit(&ahci_ctlp->ahcictl_mutex);
+
return (rval);
}
/*
* This routine is only called from AHCI_ATTACH or phyrdy change
* case. It first calls port reset to initialize port, probe port and probe
- * device, then read PxSIG register to find the type of device attached to
- * the port.
+ * device, then try to read PxSIG register to find the type of device
+ * attached to the port.
*
* WARNING!!! ahciport_mutex should be acquired before the function
- * is called.
+ * is called. And the port interrupt is disabled.
*/
-static int
+static void
ahci_find_dev_signature(ahci_ctl_t *ahci_ctlp,
ahci_port_t *ahci_portp, uint8_t port)
{
uint32_t signature;
- int ret = AHCI_SUCCESS;
AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp,
- "port %d: ahci_find_dev_signature enter", port);
+ "ahci_find_dev_signature enter: port %d", port);
- ahci_disable_port_intrs(ahci_ctlp, ahci_portp, port);
+ ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN;
/* Call port reset to check link status and get device signature */
- if (ahci_port_reset(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) {
- AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
- "ahci_find_dev_signature failed for port %d: ", port);
- ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
- return (AHCI_FAILURE);
+ (void) ahci_port_reset(ahci_ctlp, ahci_portp, port);
+
+ if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) {
+ AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
+ "ahci_find_dev_signature: No device is found "
+ "at port %d", port);
+ return;
}
- ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
+ /* Check the port state */
+ if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) {
+ AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp,
+ "ahci_find_dev_signature: port %d state 0x%x",
+ port, ahci_portp->ahciport_port_state);
+ return;
+ }
signature = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port));
AHCIDBG2(AHCIDBG_INIT|AHCIDBG_INFO, ahci_ctlp,
- "port %d signature = 0x%x", port, signature);
+ "ahci_find_dev_signature: port %d signature = 0x%x",
+ port, signature);
switch (signature) {
@@ -2967,76 +3179,75 @@ ahci_find_dev_signature(ahci_ctl_t *ahci_ctlp,
"Port Multiplier is found at port: %d", port);
break;
- case AHCI_SIGNATURE_NONE:
- ahci_portp->ahciport_device_type = SATA_DTYPE_NONE;
- AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "No device is found at port: %d", port);
- break;
-
default:
ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN;
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
"Unknown device is found at port: %d", port);
}
-
- return (ret);
}
/*
- * Try to start the port - set PxCMD.ST to 1, if PxCMD.FRE is not set
- * to 1, then set it.
+ * Start the port - set PxCMD.ST to 1, if PxCMD.FRE is not set
+ * to 1, then set it firstly.
+ *
+ * Each port contains two major DMA engines. One DMA engine walks through
+ * the command list, and is controlled by PxCMD.ST. The second DMA engine
+ * copies received FISes into system memory, and is controlled by PxCMD.FRE.
+ *
+ * Software shall not set PxCMD.ST to '1' until it verifies that PxCMD.CR
+ * is '0' and has set PxCMD.FRE is '1'. And software shall not clear
+ * PxCMD.FRE while PxCMD.ST or PxCMD.CR is set '1'.
+ *
+ * Software shall not set PxCMD.ST to '1' unless a functional device is
+ * present on the port(as determined by PxTFD.STS.BSY = '0',
+ * PxTFD.STS.DRQ = '0', and PxSSTS.DET = 3h).
*
* WARNING!!! ahciport_mutex should be acquired before the function
* is called.
*/
static int
-ahci_start_port(ahci_ctl_t *ahci_ctlp, uint8_t port)
+ahci_start_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port)
{
- uint32_t port_task_file, port_cmd_status;
-
- AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_start_port: %d enter", port);
+ uint32_t port_cmd_status;
- port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
+ AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, "ahci_start_port: %d enter", port);
- if (port_task_file & AHCI_TFD_STS_BSY ||
- port_task_file & AHCI_TFD_STS_DRQ) {
+ if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) {
+ AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed "
+ "the state for port %d is 0x%x",
+ port, ahci_portp->ahciport_port_state);
+ return (AHCI_FAILURE);
+ }
- AHCIDBG2(AHCIDBG_INIT|AHCIDBG_INFO, ahci_ctlp,
- "Port %d cannot start!!! port_task_file = 0x%x",
- port, port_task_file);
+ if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) {
+ AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed "
+ "no device is attached at port %d", port);
return (AHCI_FAILURE);
+ }
- } else {
- AHCIDBG2(AHCIDBG_INIT|AHCIDBG_INFO, ahci_ctlp,
- "Port %d start!!! port_task_file = 0x%x",
- port, port_task_file);
+ /* First to set PxCMD.FRE before setting PxCMD.ST. */
+ port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
- /* First to set PxCMD.FRE before setting PxCMD.ST. */
- port_cmd_status =
- ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
+ if (!(port_cmd_status & AHCI_CMD_STATUS_FRE)) {
+ port_cmd_status |= AHCI_CMD_STATUS_FRE;
+ ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
+ port_cmd_status);
+ }
- if (!(port_cmd_status & AHCI_CMD_STATUS_FRE)) {
- port_cmd_status |= AHCI_CMD_STATUS_FRE;
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
- port_cmd_status);
- }
+ port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
- port_cmd_status =
- ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
+ port_cmd_status |= AHCI_CMD_STATUS_ST;
- port_cmd_status |= AHCI_CMD_STATUS_ST;
+ ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
+ port_cmd_status);
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
- port_cmd_status);
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_STARTED;
- return (AHCI_SUCCESS);
- }
+ return (AHCI_SUCCESS);
}
/*
@@ -3061,8 +3272,13 @@ ahci_alloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port)
ahci_portp->ahciport_port_num = port;
+ /* Intialize the port condition variable */
+ cv_init(&ahci_portp->ahciport_cv, NULL, CV_DRIVER, NULL);
+
+ /* Initialize the port mutex */
mutex_init(&ahci_portp->ahciport_mutex, NULL, MUTEX_DRIVER,
(void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri);
+
mutex_enter(&ahci_portp->ahciport_mutex);
/*
@@ -3077,17 +3293,26 @@ ahci_alloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port)
goto err_case2;
}
+ ahci_portp->ahciport_event_args =
+ kmem_zalloc(sizeof (ahci_event_arg_t), KM_SLEEP);
+
+ if (ahci_portp->ahciport_event_args == NULL)
+ goto err_case3;
+
mutex_exit(&ahci_portp->ahciport_mutex);
return (AHCI_SUCCESS);
+err_case3:
+ ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp);
+
err_case2:
ahci_dealloc_rcvd_fis(ahci_ctlp, ahci_portp);
err_case1:
mutex_exit(&ahci_portp->ahciport_mutex);
-
mutex_destroy(&ahci_portp->ahciport_mutex);
+ cv_destroy(&ahci_portp->ahciport_cv);
kmem_free(ahci_portp, sizeof (ahci_port_t));
@@ -3108,11 +3333,14 @@ ahci_dealloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port)
ASSERT(ahci_portp != NULL);
mutex_enter(&ahci_portp->ahciport_mutex);
+ kmem_free(ahci_portp->ahciport_event_args, sizeof (ahci_event_arg_t));
+ ahci_portp->ahciport_event_args = NULL;
ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp);
ahci_dealloc_rcvd_fis(ahci_ctlp, ahci_portp);
mutex_exit(&ahci_portp->ahciport_mutex);
mutex_destroy(&ahci_portp->ahciport_mutex);
+ cv_destroy(&ahci_portp->ahciport_cv);
kmem_free(ahci_portp, sizeof (ahci_port_t));
@@ -3551,37 +3779,147 @@ ahci_update_sata_registers(ahci_ctl_t *ahci_ctlp, uint8_t port,
}
/*
- * Set the auto sense data for ATAPI devices.
+ * For poll mode, ahci_port_intr will be called to emulate the interrupt
*
- * 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.
+ * retrieve_errinfo_slot_status keeps the slot number of REQUEST SENSE command
+ * which is sent down to get sense data for a failed PACKET command.
*/
static void
-ahci_set_sense_data(sata_pkt_t *satapkt, int reason)
+ahci_port_intr(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
+ uint8_t port, uint32_t retrieve_errinfo_slot_status)
{
- struct scsi_extended_sense *sense;
-
- sense = (struct scsi_extended_sense *)
- satapkt->satapkt_cmd.satacmd_rqsense;
- bzero(sense, sizeof (struct scsi_extended_sense));
- sense->es_valid = 1; /* Valid sense */
- sense->es_class = 7; /* Response code 0x70 - current err */
- sense->es_key = 0;
- sense->es_info_1 = 0;
- sense->es_info_2 = 0;
- sense->es_info_3 = 0;
- sense->es_info_4 = 0;
- sense->es_add_len = 6; /* Additional length */
- sense->es_cmd_info[0] = 0;
- sense->es_cmd_info[1] = 0;
- sense->es_cmd_info[2] = 0;
- sense->es_cmd_info[3] = 0;
- sense->es_add_code = 0;
- sense->es_qual_code = 0;
-
- if ((reason == SATA_PKT_DEV_ERROR) || (reason == SATA_PKT_TIMEOUT)) {
- sense->es_key = KEY_HARDWARE_ERROR;
+ uint32_t port_intr_status;
+ uint32_t port_intr_enable;
+
+ AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
+ "ahci_port_intr enter: port %d", port);
+
+ mutex_enter(&ahci_portp->ahciport_mutex);
+ if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_POLLING) {
+ /* For SATA_OPMODE_POLLING commands */
+ port_intr_enable =
+ (AHCI_INTR_STATUS_DHRS |
+ AHCI_INTR_STATUS_PSS |
+ AHCI_INTR_STATUS_SDBS |
+ AHCI_INTR_STATUS_UFS |
+ AHCI_INTR_STATUS_PCS |
+ AHCI_INTR_STATUS_PRCS |
+ AHCI_INTR_STATUS_OFS |
+ AHCI_INTR_STATUS_INFS |
+ AHCI_INTR_STATUS_IFS |
+ AHCI_INTR_STATUS_HBDS |
+ AHCI_INTR_STATUS_HBFS |
+ AHCI_INTR_STATUS_TFES);
+ mutex_exit(&ahci_portp->ahciport_mutex);
+ goto next;
}
+ mutex_exit(&ahci_portp->ahciport_mutex);
+
+ /*
+ * port_intr_enable indicates that the corresponding interrrupt
+ * reporting is enabled.
+ */
+ port_intr_enable = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port));
+next:
+ /*
+ * port_intr_stats indicates that the corresponding interrupt
+ * condition is active.
+ */
+ port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port));
+
+ AHCIDBG4(AHCIDBG_INTR, ahci_ctlp,
+ "ahci_port_intr: port %d, port_intr_status = 0x%x, "
+ "port_intr_enable = 0x%x, retrieve_errinfo_slot_status = 0x%x",
+ port, port_intr_status, port_intr_enable,
+ retrieve_errinfo_slot_status);
+
+ port_intr_status &= port_intr_enable;
+
+ /* First clear the port interrupts status */
+ ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port),
+ port_intr_status);
+
+ /* Check the completed commands */
+ if (port_intr_status & (AHCI_INTR_STATUS_DHRS |
+ AHCI_INTR_STATUS_PSS)) {
+ (void) ahci_intr_cmd_cmplt(ahci_ctlp,
+ ahci_portp, port, port_intr_status,
+ retrieve_errinfo_slot_status);
+ }
+
+ /* Check the asynchronous notification */
+ if (port_intr_status & AHCI_INTR_STATUS_SDBS) {
+ (void) ahci_intr_set_device_bits(ahci_ctlp,
+ ahci_portp, port);
+ }
+
+ /* Check the port connect change status interrupt bit */
+ if (port_intr_status & AHCI_INTR_STATUS_PCS) {
+ (void) ahci_intr_port_connect_change(ahci_ctlp,
+ ahci_portp, port);
+ }
+
+ /* Check the device mechanical presence status interrupt bit */
+ if (port_intr_status & AHCI_INTR_STATUS_DMPS) {
+ (void) ahci_intr_device_mechanical_presence_status(
+ ahci_ctlp, ahci_portp, port);
+ }
+
+ /* Check the PhyRdy change status interrupt bit */
+ if (port_intr_status & AHCI_INTR_STATUS_PRCS) {
+ (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp,
+ port);
+ }
+
+ /*
+ * Check the non-fatal error interrupt bits, there are three
+ * kinds of non-fatal errors at the time being:
+ *
+ * PxIS.UFS - Unknown FIS Error
+ * PxIS.OFS - Overflow Error
+ * PxIS.INFS - Interface Non-Fatal Error
+ *
+ * For these non-fatal errors, the HBA can continue to operate,
+ * so the driver just log the error messages.
+ */
+ if (port_intr_status & (AHCI_INTR_STATUS_UFS |
+ AHCI_INTR_STATUS_OFS |
+ AHCI_INTR_STATUS_INFS)) {
+ (void) ahci_intr_non_fatal_error(ahci_ctlp, ahci_portp,
+ port, port_intr_status);
+ }
+
+ /*
+ * Check the fatal error interrupt bits, there are four kinds
+ * of fatal errors for AHCI controllers:
+ *
+ * PxIS.HBFS - Host Bus Fatal Error
+ * PxIS.HBDS - Host Bus Data Error
+ * PxIS.IFS - Interface Fatal Error
+ * PxIS.TFES - Task File Error
+ *
+ * The fatal error means the HBA can not recover from it by
+ * itself, and it will try to abort the transfer, and the software
+ * must intervene to restart the port.
+ */
+ if (port_intr_status & (AHCI_INTR_STATUS_IFS |
+ AHCI_INTR_STATUS_HBDS |
+ AHCI_INTR_STATUS_HBFS |
+ AHCI_INTR_STATUS_TFES))
+ (void) ahci_intr_fatal_error(ahci_ctlp, ahci_portp,
+ port, port_intr_status, retrieve_errinfo_slot_status);
+
+ /* Check the cold port detect interrupt bit */
+ if (port_intr_status & AHCI_INTR_STATUS_CPDS) {
+ (void) ahci_intr_cold_port_detect(ahci_ctlp, ahci_portp, port);
+ }
+
+ /* Second clear the corresponding bit in IS.IPS */
+ ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (0x1 << port));
}
/*
@@ -3593,8 +3931,7 @@ ahci_intr(caddr_t arg1, caddr_t arg2)
{
ahci_ctl_t *ahci_ctlp = (ahci_ctl_t *)arg1;
ahci_port_t *ahci_portp;
- int32_t global_intr_status, port_intr_status;
- int32_t port_intr_enable;
+ int32_t global_intr_status;
uint8_t port;
/*
@@ -3604,9 +3941,6 @@ ahci_intr(caddr_t arg1, caddr_t arg2)
global_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp));
- AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr enter: global_intr_status = 0x%x", global_intr_status);
-
if (!(global_intr_status & ahci_ctlp->ahcictl_ports_implemented)) {
/* The interrupt is not ours */
return (DDI_INTR_UNCLAIMED);
@@ -3617,158 +3951,73 @@ ahci_intr(caddr_t arg1, caddr_t arg2)
if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) {
continue;
}
-
if (!((0x1 << port) & global_intr_status)) {
continue;
}
ahci_portp = ahci_ctlp->ahcictl_ports[port];
- /*
- * port_intr_stats indicates that the corresponding interrupt
- * condition is active.
- */
- port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port));
-
- /*
- * port_intr_enable indicates that the corresponding interrrupt
- * reporting is enabled.
- */
- port_intr_enable = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port));
-
- AHCIDBG3(AHCIDBG_INTR, ahci_ctlp,
- "ahci_intr: port %d, port_intr_status = 0x%x, "
- "port_intr_enable = 0x%x", port,
- port_intr_status, port_intr_enable);
-
- port_intr_status &= port_intr_enable;
-
- /* First clear the port interrupts status */
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port),
- port_intr_status);
-
- if (port_intr_status & AHCI_INTR_STATUS_DHRS) {
- (void) ahci_intr_d2h_register_fis(ahci_ctlp, ahci_portp,
- port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_PSS) {
- (void) ahci_intr_pio_setup_fis(ahci_ctlp, ahci_portp,
- port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_DSS) {
- (void) ahci_intr_dma_setup_fis(ahci_ctlp, ahci_portp,
- port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_SDBS) {
- (void) ahci_intr_set_device_bits_fis(ahci_ctlp,
- ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_UFS) {
- (void) ahci_intr_unknown_fis(ahci_ctlp, ahci_portp,
- port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_DPS) {
- (void) ahci_intr_descriptor_processed(ahci_ctlp,
- ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_PCS) {
- (void) ahci_intr_port_connect_change(ahci_ctlp,
- ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_DMPS) {
- (void) ahci_intr_device_mechanical_presence_status(
- ahci_ctlp, ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_PRCS) {
- (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp,
- port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_IPMS) {
- (void) ahci_intr_incorrect_port_multiplier(ahci_ctlp,
- ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_OFS) {
- (void) ahci_intr_overflow(ahci_ctlp, ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_INFS) {
- (void) ahci_intr_interface_non_fatal_error(ahci_ctlp,
- ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_IFS) {
- (void) ahci_intr_interface_fatal_error(ahci_ctlp,
- ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_HBDS) {
- (void) ahci_intr_host_bus_data_error(ahci_ctlp,
- ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_HBFS) {
- (void) ahci_intr_host_bus_fatal_error(ahci_ctlp,
- ahci_portp, port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_TFES) {
- (void) ahci_intr_task_file_error(ahci_ctlp, ahci_portp,
- port);
- }
-
- if (port_intr_status & AHCI_INTR_STATUS_CPDS) {
- (void) ahci_intr_cold_port_detect(ahci_ctlp,
- ahci_portp, port);
- }
-
- /* Second clear the corresponding bit in IS.IPS */
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (0x1 << port));
+ /* Call ahci_port_intr */
+ ahci_port_intr(ahci_ctlp, ahci_portp, port, 0);
}
return (DDI_INTR_CLAIMED);
}
/*
- * A D2H Register FIS has been received with the 'I' bit set.
+ * For non-queued commands, when the corresponding bit in the PxCI register
+ * is cleared, it means the command is completed successfully. And according
+ * to the HBA state machine, there are three conditions which possibly will
+ * try to clear the PxCI register bit.
+ * 1. Receive one D2H Register FIS which is with 'I' bit set
+ * 2. Update PIO Setup FIS
+ * 3. Transmit a command and receive R_OK if CTBA.C is set (software reset)
+ *
+ * Process completed commands including non-queued commands when the
+ * interrupt status bit - AHCI_INTR_STATUS_DHRS or AHCI_INTR_STATUS_PSS
+ * is set, and queued commands when the interrupt bit -
+ * AHCI_INTR_STATUS_SDBS is set. Currently, the driver doesn't support
+ * queued commands yet.
*
- * The following commands will send this FIS upon the successful completion:
+ * AHCI_INTR_STATUS_DHRS means a D2H Register FIS has been received
+ * with the 'I' bit set. And the following commands will send thus
+ * FIS with 'I' bit set upon the successful completion:
* 1. Non-data commands
* 2. DMA data-in command
* 3. DMA data-out command
* 4. PIO data-out command
+ * 5. PACKET non-data commands
+ * 6. PACKET PIO data-in command
+ * 7. PACKET PIO data-out command
+ * 8. PACKET DMA data-in command
+ * 9. PACKET DMA data-out command
+ *
+ * AHCI_INTR_STATUS_PSS means a PIO Setup FIS has been received
+ * with the 'I' bit set. And the following commands will send this
+ * FIS upon the successful completion:
+ * 1. PIO data-in command
*/
static int
-ahci_intr_d2h_register_fis(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
+ahci_intr_cmd_cmplt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
+ uint8_t port, uint32_t intr_status, uint32_t retrieve_errinfo_slot_status)
{
- uint32_t port_cmd_issue;
+ uint32_t port_cmd_issue = 0;
uint32_t finished_tags;
- int finished_slot, i, num = 0;
+ int finished_slot;
sata_pkt_t *satapkt;
ahci_fis_d2h_register_t *rcvd_fisp;
- AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_d2h_register_fis enter, port %d", port);
+ if (intr_status & AHCI_INTR_STATUS_DHRS)
+ AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
+ "ahci_intr_cmd_cmplt enter: port %d "
+ "a d2h register fis is received", port);
- mutex_enter(&ahci_portp->ahciport_mutex);
-
- port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
+ if (intr_status & AHCI_INTR_STATUS_PSS)
+ AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
+ "ahci_intr_cmd_cmplt enter: port %d "
+ "a pio setup fis is received", port);
+ mutex_enter(&ahci_portp->ahciport_mutex);
if (!ahci_portp->ahciport_pending_tags) {
/*
* Spurious interrupt. Nothing to be done.
@@ -3777,147 +4026,79 @@ ahci_intr_d2h_register_fis(ahci_ctl_t *ahci_ctlp,
return (AHCI_SUCCESS);
}
- finished_tags = ahci_portp->ahciport_pending_tags &
- ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp);
-
- AHCIDBG3(AHCIDBG_INTR, ahci_ctlp,
- "ahci_intr_d2h_reigster_fis: pending_tags = 0x%x, "
- "port_cmd_issue = 0x%x, finished_tags = 0x%x,",
- ahci_portp->ahciport_pending_tags, port_cmd_issue,
- finished_tags);
-
- for (i = 0; i < ahci_ctlp->ahcictl_num_cmd_slots; i++) {
- if ((0x1 << i) & finished_tags)
- num++;
- }
-
- ASSERT(num <= 1);
-
- finished_slot = ddi_ffs(finished_tags) - 1;
- if (finished_slot == -1) {
- goto out;
- }
-
- satapkt = ahci_portp->ahciport_slot_pkts[finished_slot];
- ASSERT(satapkt != NULL);
+ port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
/*
- * In case the interrupt generates before disabling for poll commands.
+ * When AHCI_PORT_FLAG_RQSENSE is set, the port is doing error recovery,
+ * and REQUEST SENSE for sense data retrieval is sent down to get the
+ * sense data. At this time, the only pending command is REQUESR SENSE,
+ * and retrieve_errinfo_slot_status contains the slot number of it.
*/
- if (satapkt->satapkt_op_mode &
- (SATA_OPMODE_POLLING | SATA_OPMODE_SYNCH)) {
- goto out;
+ if (retrieve_errinfo_slot_status != 0) {
+ ASSERT(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_RQSENSE);
+ finished_tags = ahci_portp->ahciport_pending_tags &
+ retrieve_errinfo_slot_status &
+ ~port_cmd_issue &
+ AHCI_SLOT_MASK(ahci_ctlp);
+ } else {
+ finished_tags = ahci_portp->ahciport_pending_tags &
+ ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp);
}
- rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis->ahcirf_d2h_register_fis);
-
- /* Return the status */
- satapkt->satapkt_cmd.satacmd_status_reg = GET_RFIS_STATUS(rcvd_fisp);
-
- if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs)
- ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp);
+ AHCIDBG3(AHCIDBG_INTR, ahci_ctlp,
+ "ahci_intr_cmd_cmplt: pending_tags = 0x%x, port_cmd_issue = 0x%x "
+ "retrieve_errinfo_slot_status = 0x%x",
+ ahci_portp->ahciport_pending_tags, port_cmd_issue,
+ retrieve_errinfo_slot_status);
AHCIDBG1(AHCIDBG_INTR, ahci_ctlp,
- "ahci_intr_d2h_register_fis: sending up pkt 0x%p "
- "with SATA_PKT_COMPLETED", (void *)satapkt);
-
- CLEAR_BIT(ahci_portp->ahciport_pending_tags, finished_slot);
- ahci_portp->ahciport_slot_pkts[finished_slot] = NULL;
+ "ahci_intr_cmd_cmplt: finished_tags = 0x%x", finished_tags);
- SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED);
-out:
- AHCIDBG1(AHCIDBG_PKTCOMP, ahci_ctlp,
- "ahci_intr_d2h_register_fis pending_tags = 0x%x",
- ahci_portp->ahciport_pending_tags);
-
- mutex_exit(&ahci_portp->ahciport_mutex);
-
- return (AHCI_SUCCESS);
-}
-
-/*
- * A PIO Setup FIS has been received with the 'I' bit set.
- *
- * The following commands will send this FIS upon the successful completion:
- * PIO data-in command
- */
-static int
-ahci_intr_pio_setup_fis(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
-{
- uint32_t port_cmd_issue;
- uint32_t finished_tags;
- int finished_slot, i, num = 0;
- sata_pkt_t *satapkt;
- ahci_fis_d2h_register_t *rcvd_fisp;
-
- AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_pio_setup_fis enter, port %d", port);
-
- mutex_enter(&ahci_portp->ahciport_mutex);
+ while (finished_tags) {
+ finished_slot = ddi_ffs(finished_tags) - 1;
+ if (finished_slot == -1) {
+ goto out;
+ }
- port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
+ satapkt = ahci_portp->ahciport_slot_pkts[finished_slot];
+ ASSERT(satapkt != NULL);
- if (!ahci_portp->ahciport_pending_tags) {
/*
- * Spurious interrupt. Nothing to be done.
+ * For SATAC_SMART command with SATA_SMART_RETURN_STATUS
+ * feature, sata_special_regs flag will be set, and the
+ * driver should copy the status and the other corresponding
+ * register values in the D2H Register FIS received (It's
+ * working on Non-data protocol) from the device back to
+ * the sata_cmd.
+ *
+ * For every AHCI port, there is only one Received FIS
+ * structure, which contains the FISes received from the
+ * device, So we're trying to copy the content of D2H
+ * Register FIS in the Received FIS structure back to
+ * the sata_cmd.
*/
- mutex_exit(&ahci_portp->ahciport_mutex);
- return (AHCI_SUCCESS);
- }
-
- finished_tags = ahci_portp->ahciport_pending_tags &
- ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp);
-
- AHCIDBG3(AHCIDBG_INTR, ahci_ctlp,
- "ahci_intr_pio_setup_fis: pending_tags = 0x%x, "
- "port_cmd_issue = 0x%x, finished_tags = 0x%x,",
- ahci_portp->ahciport_pending_tags, port_cmd_issue,
- finished_tags);
-
- for (i = 0; i < ahci_ctlp->ahcictl_num_cmd_slots; i++) {
- if ((0x1 << i) & finished_tags)
- num++;
- }
-
- ASSERT(num <= 1);
+ if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) {
+ rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis->
+ ahcirf_d2h_register_fis);
+ satapkt->satapkt_cmd.satacmd_status_reg =
+ GET_RFIS_STATUS(rcvd_fisp);
+ ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp);
+ }
- finished_slot = ddi_ffs(finished_tags) - 1;
- if (finished_slot == -1) {
- goto out;
- }
+ AHCIDBG1(AHCIDBG_INTR, ahci_ctlp,
+ "ahci_intr_cmd_cmplt: sending up pkt 0x%p "
+ "with SATA_PKT_COMPLETED", (void *)satapkt);
- satapkt = ahci_portp->ahciport_slot_pkts[finished_slot];
- ASSERT(satapkt != NULL);
+ CLEAR_BIT(ahci_portp->ahciport_pending_tags, finished_slot);
+ CLEAR_BIT(finished_tags, finished_slot);
+ ahci_portp->ahciport_slot_pkts[finished_slot] = NULL;
- /*
- * In case the interrupt generates before disabling.
- */
- if (satapkt->satapkt_op_mode &
- (SATA_OPMODE_POLLING | SATA_OPMODE_SYNCH)) {
- goto out;
+ SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED);
}
-
- rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis->ahcirf_d2h_register_fis);
-
- /* Return the status */
- satapkt->satapkt_cmd.satacmd_status_reg = GET_RFIS_STATUS(rcvd_fisp);
-
- if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs)
- ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp);
-
- AHCIDBG1(AHCIDBG_INTR, ahci_ctlp,
- "ahci_intr_pio_setup_fis: sending up pkt 0x%p "
- "with SATA_PKT_COMPLETED", (void *)satapkt);
-
- CLEAR_BIT(ahci_portp->ahciport_pending_tags, finished_slot);
- ahci_portp->ahciport_slot_pkts[finished_slot] = NULL;
-
- SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED);
out:
AHCIDBG1(AHCIDBG_PKTCOMP, ahci_ctlp,
- "ahci_intr_pio_setup_fis pending_tags = 0x%x",
+ "ahci_intr_cmd_cmplt: pending_tags = 0x%x",
ahci_portp->ahciport_pending_tags);
mutex_exit(&ahci_portp->ahciport_mutex);
@@ -3926,98 +4107,32 @@ out:
}
/*
- * A DMA Setup FIS has been received with the 'I' bit set
- *
- * DMA Setup FIS will be used during NCQ transfers to activate data
- * transfer.
- *
- * For the first phase, this interrupt is disabled and we just
- * log a debug message.
- */
-/* ARGSUSED */
-static int
-ahci_intr_dma_setup_fis(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
-{
- AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_dma_setup_fis enter, port %d", port);
-
- return (AHCI_SUCCESS);
-}
-
-/*
- * A Set Device Bits FIS has been received with the 'I' bit set
+ * AHCI_INTR_STATUS_SDBS means a Set Device Bits FIS has been received
+ * with the 'I' bit set and has been copied into system memory.
*
- * Set Device Bits FIS will be sent during NCQ completion.
- *
- * For the first phase, this interrupt is disabled and we just log
- * the debug message.
+ * Asynchronous Notification is a feature in SATA II, which allows an
+ * ATAPI device to send a signal to the host when media is inserted or
+ * removed and avoids polling the device for media changes. The signal
+ * sent to the host is a Set Device Bits FIS with the 'I' and 'N' bits
+ * set to '1'.
*/
-/* ARGSUSED */
static int
-ahci_intr_set_device_bits_fis(ahci_ctl_t *ahci_ctlp,
+ahci_intr_set_device_bits(ahci_ctl_t *ahci_ctlp,
ahci_port_t *ahci_portp, uint8_t port)
{
- AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_set_device_bits_fis enter, port %d", port);
-
- return (AHCI_SUCCESS);
-}
+ ahci_fis_set_device_bits_t *rcvd_fisp;
-/*
- * An unknown FIS was received and has been copied into system memory.
- *
- * An unknown FIS is not considered an illegal FIS, unless the length
- * received is more than 64 bytes. If an unknown FIS arrives with length
- * <= 64 bytes, it is posted and the HBA continues normal operation.
- *
- * Therefore we just need to clear PxSERR.DIAG.F to clear the intr bit
- * and log the debug message.
- */
-static int
-ahci_intr_unknown_fis(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
-{
- uint32_t port_serror;
-
- mutex_enter(&ahci_portp->ahciport_mutex);
-
- port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port));
-
- AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_unknown_fis: port %d, port_serror = 0x%x",
- port, port_serror);
-
- /* Clear the interrupt bit by clearing PxSERR.DIAG.F */
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port),
- AHCI_SERROR_DIAG_F);
-
- mutex_exit(&ahci_portp->ahciport_mutex);
+ AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
+ "ahci_intr_set_device_bits enter: port %d", port);
- return (AHCI_SUCCESS);
-}
+ rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis->
+ ahcirf_set_device_bits_fis);
-/*
- * A PRD with the 'I' bit set has tranfered all of its data.
- *
- * The PRD Interrupt is an opportunistic interrupt. And it should not
- * be used to definitively indicate the end of a transfer since two
- * PRD interrupts could happen close in time together such that the
- * second interrupt is missed when the first PRD interrupt is being
- * cleared.
- *
- * For the first phase, this interrupt is disabled and we just
- * log the debug message.
- */
-/* ARGSUSED */
-static int
-ahci_intr_descriptor_processed(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
-{
- AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_descriptor_processed enter, port %d", port);
+ if (GET_N_BIT_OF_SET_DEV_BITS(rcvd_fisp)) {
+ AHCIDBG1(AHCIDBG_INTR, ahci_ctlp,
+ "ahci_intr_set_device_bits N bit is set for "
+ "port %d", port);
+ }
return (AHCI_SUCCESS);
}
@@ -4025,17 +4140,26 @@ ahci_intr_descriptor_processed(ahci_ctl_t *ahci_ctlp,
/*
* 1=Change in Current Connect Status. 0=No change in Current Connect Status.
* This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared
- * when PxSERR.DIAG.X is cleared.
- *
- * When PxSERR.DIAG.X is set to one, it indicates a COMINIT signal was received.
+ * when PxSERR.DIAG.X is cleared. When PxSERR.DIAG.X is set to one, it
+ * indicates a COMINIT signal was received.
*
- * For the first phase, this interrupt is disabled.
+ * Hot plug insertion is detected by reception of a COMINIT signal from the
+ * device. On reception of unsolicited COMINIT, the HBA shall generate a
+ * COMRESET. If the COMINIT is in responce to a COMRESET, then the HBA shall
+ * begin the normal communication negotiation sequence as outlined in the
+ * Serial ATA 1.0a specification. When a COMRESET is sent to the device the
+ * PxSSTS.DET field shall be cleared to 0h. When a COMINIT is received, the
+ * PxSSTS.DET field shall be set to 1h. When the communication negotiation
+ * sequence is complete and PhyRdy is true the PxSSTS.DET field shall be set
+ * to 3h. Therefore, at the moment the ahci driver is going to check PhyRdy
+ * to handle hot plug insertion. In this interrupt handler, just do nothing
+ * but print some log message and clear the bit.
*/
static int
ahci_intr_port_connect_change(ahci_ctl_t *ahci_ctlp,
ahci_port_t *ahci_portp, uint8_t port)
{
- uint32_t port_serror, port_cmd_status;
+ uint32_t port_serror;
mutex_enter(&ahci_portp->ahciport_mutex);
@@ -4046,13 +4170,6 @@ ahci_intr_port_connect_change(ahci_ctl_t *ahci_ctlp,
"ahci_intr_port_connect_change: port %d, "
"port_serror = 0x%x", port, port_serror);
- port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
-
- AHCIDBG2(AHCIDBG_INTR, ahci_ctlp,
- "ahci_intr_port_connect_change: port %d, "
- "port_command_status = 0x%x", port, port_cmd_status);
-
/* Clear PxSERR.DIAG.X to clear the interrupt bit */
ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port),
@@ -4072,7 +4189,7 @@ ahci_intr_port_connect_change(ahci_ctl_t *ahci_ctlp,
* state of the device. This bit is only valid if both CAP.SMPS and PxCMD.MPSP
* are set to '1'.
*
- * For the first phase, this interrupt is disabled and we just log
+ * At the moment, this interrupt is not needed and disabled and we just log
* the debug message.
*/
/* ARGSUSED */
@@ -4126,6 +4243,18 @@ ahci_intr_device_mechanical_presence_status(ahci_ctl_t *ahci_ctlp,
*
* When set, it indicates that the internal PHYRDY signal changed state.
* This bit reflects the state of PxSERR.DIAG.N.
+ *
+ * There are three kinds of conditions to generate this interrupt event:
+ * 1. a device is inserted
+ * 2. a device is disconnected
+ * 3. when the link enters/exits a Partial or Slumber interface power
+ * management state
+ *
+ * If inteface power management is enabled for a port, the PxSERR.DIAG.N
+ * bit may be set due to the link entering the Partial or Slumber power
+ * management state, rather than due to a hot plug insertion or removal
+ * event. So far, the power management is disabled, so the driver can
+ * reliably get removal detection notification via the PxSERR.DIAG.N bit.
*/
static int
ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp,
@@ -4161,8 +4290,10 @@ ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp,
port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port));
- dev_exists_now = (AHCI_SSTATUS_GET_DET(port_sstatus) ==
- AHCI_SSTATUS_DET_DEVPRESENT_PHYONLINE);
+ if (AHCI_SSTATUS_GET_DET(port_sstatus) ==
+ AHCI_SSTATUS_DET_DEVPRESENT_PHYONLINE) {
+ dev_exists_now = 1;
+ }
if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) {
dev_existed_previously = 1;
@@ -4178,7 +4309,8 @@ ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp,
if (dev_exists_now) {
if (dev_existed_previously) {
/* Things are fine now. The loss was temporary. */
- cmn_err(CE_NOTE, "!ahci phyrdy: port %d "
+ AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp,
+ "ahci_intr_phyrdy_change port %d "
"device link lost/established", port);
mutex_exit(&ahci_portp->ahciport_mutex);
@@ -4189,27 +4321,23 @@ ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp,
mutex_enter(&ahci_portp->ahciport_mutex);
} else {
- cmn_err(CE_NOTE, "!ahci phyrdy: port %d "
+ AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp,
+ "ahci_intr_phyrdy_change: port %d "
"device link established", port);
+ ahci_disable_port_intrs(ahci_ctlp, ahci_portp, port);
+
/* A new device has been detected. */
- if (ahci_find_dev_signature(ahci_ctlp, ahci_portp, port)
- != AHCI_SUCCESS) {
- ahci_portp->ahciport_port_state =
- SATA_PSTATE_FAILED;
- AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
- "phyrdy: port %d failed "
- "at find dev signature", port);
- }
+ ahci_find_dev_signature(ahci_ctlp, ahci_portp, port);
+
+ ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
- if ((ahci_portp->ahciport_device_type
- == SATA_DTYPE_ATADISK) &&
- (ahci_start_port(ahci_ctlp, port)
- != AHCI_SUCCESS)) {
- ahci_portp->ahciport_port_state =
- SATA_PSTATE_FAILED;
+ /* Try to start the port */
+ if (ahci_start_port(ahci_ctlp, ahci_portp, port)
+ != AHCI_SUCCESS) {
+ sdevice.satadev_state |= SATA_PSTATE_FAILED;
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
- "phyrdy: port %d failed "
+ "ahci_intr_phyrdy_change: port %d failed "
"at start port", port);
}
@@ -4224,12 +4352,12 @@ ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp,
} else { /* No device exists now */
if (dev_existed_previously) {
-
- cmn_err(CE_NOTE, "!ahci phyrdy: port %d "
+ AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp,
+ "ahci_intr_phyrdy_change: port %d "
"device link lost", port);
ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port);
- (void) ahci_port_into_notrunning_state(ahci_ctlp,
+ (void) ahci_put_port_into_notrunning_state(ahci_ctlp,
ahci_portp, port);
/* An existing device is lost. */
@@ -4246,6 +4374,7 @@ ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp,
/* Spurious interrupt */
AHCIDBG0(AHCIDBG_INTR, ahci_ctlp,
+ "ahci_intr_phyrdy_change: "
"spurious phy ready interrupt");
}
}
@@ -4256,100 +4385,88 @@ ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp,
}
/*
- * PxIS.IPMS - Port Multiplier Errors.
+ * PxIS.UFS - Unknown FIS Error
*
- * Which indicates that the HBA received a FIS from a device whose Port
- * Multiplier field did not match what was expected.
+ * This interrupt event means an unknown FIS was received and has been
+ * copied into system memory. An unknown FIS is not considered an illegal
+ * FIS, unless the length received is more than 64 bytes. If an unknown
+ * FIS arrives with length <= 64 bytes, it is posted and the HBA continues
+ * normal operation. If the unknown FIS is more than 64 bytes, then it
+ * won't be posted to memory and PxSERR.ERR.P will be set, which is then
+ * a fatal error.
*
- * For the first phase, this interrupt is disabled, and we just log
- * the debug message.
- */
-/* ARGSUSED */
-static int
-ahci_intr_incorrect_port_multiplier(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
-{
- AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_incorrect_port_multiplier enter, port %d", port);
-
- return (AHCI_SUCCESS);
-}
-
-/*
- * PxIS.OFS - Fatal Error
+ * PxIS.OFS - Overflow Error
*
- * Which indicates that the HBA received more bytes from a device than
- * was specified in the PRD table for the command.
+ * Command list overflow is defined as software building a command table
+ * that has fewer total bytes than the transaction given to the device.
+ * On device writes, the HBA will run out of data, and on reads, there
+ * will be no room to put the data.
*
- * Overflow is a serious error, thus software should perform a fatal error
- * recovery procedure to ensure that the HBA is brought back to a known
- * condition before continuing.
- */
-static int
-ahci_intr_overflow(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
-{
- AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_overflow enter, port %d", port);
-
- mutex_enter(&ahci_portp->ahciport_mutex);
-
- cmn_err(CE_NOTE, "!ahci port %d has overflow error", port);
-
- (void) ahci_recovery_fatal_error(ahci_ctlp, ahci_portp,
- port, AHCI_INTR_STATUS_OFS);
-
- mutex_exit(&ahci_portp->ahciport_mutex);
-
- return (AHCI_SUCCESS);
-}
-
-/*
- * PxIS.IFS and PxIS.INFS are errors that occur due to eletrical issues
- * on the interface, or protocol miscommunication between the device
- * and HBA.
+ * For an overflow on data read, either PIO or DMA, the HBA will set
+ * PxIS.OFS, and the HBA will do a best effort to continue, and it's a
+ * non-fatal error when the HBA can continues. Sometimes, it will cause
+ * a fatal error and need the software to do something.
*
- * The conditions that causes PxIS.IFS/PxIS.INFS to be set are
- * 1. in PxSERR.ERR, P bit is set to '1'
- * 2. in PxSERR.DIAG, C or H bit is set to '1'
- * 3. PhyRdy drop unexpectly, N bit is set to '1'
- */
-/*
- * PxIS.INFS - Interface Error
+ * For an overflow on data write, setting PxIS.OFS is optional for both
+ * DMA and PIO, and it's a fatal error, and a COMRESET is required by
+ * software to clean up from this serious error.
*
- * Which indicates that the HBA encountered an error on the Serial ATA interface
- * but was able to continue operation. It's non-fatal error, so we just log
- * the debug message.
+ * PxIS.INFS - Interface Non-Fatal Error
+ *
+ * This interrupt event indicates that the HBA encountered an error on
+ * the Serial ATA interface but was able to continue operation. The kind
+ * of error usually occurred during a non-Data FIS, and under this condition
+ * the FIS will be re-transmitted by HBA automatically.
*
- * The error occurred during a non-Data FIS, and the FIS will be
- * re-transmitted by HBA automatically.
+ * When the FMA is implemented, there should be a stat structure to
+ * record how many every kind of error happens.
*/
static int
-ahci_intr_interface_non_fatal_error(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
+ahci_intr_non_fatal_error(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
+ uint8_t port, uint32_t intr_status)
{
- sata_pkt_t *satapkt;
uint32_t port_serror;
+#if AHCI_DEBUG
uint32_t port_cmd_status;
int current_slot;
+ sata_pkt_t *satapkt;
+#endif
mutex_enter(&ahci_portp->ahciport_mutex);
port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port));
- AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_interface_non_fatal_error: port %d, "
+ AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY|AHCIDBG_ERRS, ahci_ctlp,
+ "ahci_intr_non_fatal_error: port %d, "
"port_serror = 0x%x", port, port_serror);
- ahci_log_error_message(ahci_ctlp, port, port_serror);
+ ahci_log_serror_message(ahci_ctlp, port, port_serror);
+
+ if (intr_status & AHCI_INTR_STATUS_UFS) {
+ AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
+ "ahci port %d has unknown FIS error", port);
- cmn_err(CE_NOTE, "!ahci port %d has interface non fatal error", port);
+ /* Clear the interrupt bit by clearing PxSERR.DIAG.F */
+ ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port),
+ AHCI_SERROR_DIAG_F);
+ }
+
+#if AHCI_DEBUG
+ if (intr_status & AHCI_INTR_STATUS_OFS) {
+ AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp,
+ "ahci port %d has overflow error", port);
+ }
+
+ if (intr_status & AHCI_INTR_STATUS_INFS) {
+ AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp,
+ "ahci port %d has interface non fatal error", port);
+ }
/*
* Record the error occurred command's slot.
*/
-#if AHCI_DEBUG
port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
@@ -4357,20 +4474,15 @@ ahci_intr_interface_non_fatal_error(ahci_ctl_t *ahci_ctlp,
AHCI_CMD_STATUS_CCS_SHIFT;
satapkt = ahci_portp->ahciport_slot_pkts[current_slot];
-
if (satapkt != NULL) {
- AHCIDBG1(AHCIDBG_INTR, ahci_ctlp,
- "ahci_intr_interface_non_fatal_error: "
- "pending_tags= 0x%x ",
- ahci_portp->ahciport_pending_tags);
-
- AHCIDBG1(AHCIDBG_INTR, ahci_ctlp,
- "ahci_intr_interface_non_fatal_error: cmd= 0x%x ",
+ AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp,
+ "ahci_intr_non_fatal_error: pending_tags 0x%x "
+ "cmd 0x%x", ahci_portp->ahciport_pending_tags,
satapkt->satapkt_cmd.satacmd_cmd_reg);
- AHCIDBG2(AHCIDBG_INTR, ahci_ctlp,
- "ahci_intr_interface_non_fatal_error: port %d, "
- "satapkt = %p will be re-transmitted",
+ AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp,
+ "ahci_intr_non_fatal_error: port %d, "
+ "satapkt 0x%p is being processed when error occurs",
port, (void *)satapkt);
}
#endif
@@ -4380,132 +4492,172 @@ ahci_intr_interface_non_fatal_error(ahci_ctl_t *ahci_ctlp,
}
/*
- * PxIS.IFS - Interface Error and Fatal Error
+ * According to the AHCI spec, the error types include system memory
+ * errors, interface errors, port multiplier errors, device errors,
+ * command list overflow, command list underflow, native command
+ * queuing tag errors and pio data transfer errors.
+ *
+ * System memory errors such as target abort, master abort, and parity
+ * may cause the host to stop, and they are serious errors and needed
+ * to be recovered with software intervention. When system software
+ * has given a pointer to the HBA that doesn't exist in physical memory,
+ * a master/target abort error occurs, and PxIS.HBFS will be set. A
+ * data error such as CRC or parity occurs, the HBA aborts the transfer
+ * (if necessary) and PxIS.HBDS will be set.
+ *
+ * Interface errors are errors that occur due to electrical issues on
+ * the interface, or protocol miscommunication between the device and
+ * HBA, and the respective PxSERR register bit will be set. And PxIS.IFS
+ * (fatal) or PxIS.INFS (non-fatal) will be set. The conditions that
+ * causes PxIS.IFS/PxIS.INFS to be set are
+ * 1. in PxSERR.ERR, P bit is set to '1'
+ * 2. in PxSERR.DIAG, C or H bit is set to '1'
+ * 3. PhyRdy drop unexpectly, N bit is set to '1'
+ * If the error occurred during a non-data FIS, the FIS must be
+ * retransmitted, and the error is non-fatal and PxIS.INFS is set. If
+ * the error occurred during a data FIS, the transfer will stop, so
+ * the error is fatal and PxIS.IFS is set.
+ *
+ * When a FIS arrives that updates the taskfile, the HBA checks to see
+ * if PxTFD.STS.ERR is set. If yes, PxIS.TFES will be set and the HBA
+ * stops processing any more commands.
+ *
+ * Command list overflow is defined as software building a command table
+ * that has fewer total bytes than the transaction given to the device.
+ * On device writes, the HBA will run out of data, and on reads, there
+ * will be no room to put the data. For an overflow on data read, either
+ * PIO or DMA, the HBA will set PxIS.OFS, and it's a non-fatal error.
+ * For an overflow on data write, setting PxIS.OFS is optional for both
+ * DMA and PIO, and a COMRESET is required by software to clean up from
+ * this serious error.
+ *
+ * Command list underflow is defined as software building a command
+ * table that has more total bytes than the transaction given to the
+ * device. For data writes, both PIO and DMA, the device will detect
+ * an error and end the transfer. And these errors are most likely going
+ * to be fatal errors that will cause the port to be restarted. For
+ * data reads, the HBA updates its PRD byte count, and may be
+ * able to continue normally, but is not required to. And The HBA is
+ * not required to detect underflow conditions for native command
+ * queuing command.
*
- * Which indicates that the HBA encountered an error on the Serial ATA interface
- * which caused the transfer to stop.
+ * The HBA does not actively check incoming DMA Setup FISes to ensure
+ * that the PxSACT register bit for that slot is set. Existing error
+ * mechanisms, such as host bus failure, or bad protocol, are used to
+ * recover from this case.
*
- * The error occurred during a Data FIS, the transfer shall stop.
+ * In accordance with Serial ATA 1.0a, DATA FISes prior to the final
+ * DATA FIS must be an integral number of Dwords. If the HBA receives
+ * a request which is not an integral number of Dwords, the HBA
+ * set PxSERR.ERR.P to '1', set PxIS.IFS to '1' and stop running until
+ * software restarts the port. And the HBA ensures that the size
+ * of the DATA FIS received during a PIO command matches the size in
+ * the Transfer Cound field of the preceding PIO Setup FIS, if not, the
+ * HBA sets PxSERR.ERR.P to '1', set PxIS.IFS to '1', and then
+ * stop running until software restarts the port.
*/
-static int
-ahci_intr_interface_fatal_error(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
-{
- uint32_t port_serror;
-
- mutex_enter(&ahci_portp->ahciport_mutex);
-
- port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port));
-
- AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_interface_fatal_error: port %d, "
- "port_serror = 0x%x", port, port_serror);
-
- ahci_log_error_message(ahci_ctlp, port, port_serror);
-
- cmn_err(CE_NOTE, "!ahci port %d has interface fatal error", port);
-
- (void) ahci_recovery_fatal_error(ahci_ctlp, ahci_portp,
- port, AHCI_INTR_STATUS_IFS);
-
- mutex_exit(&ahci_portp->ahciport_mutex);
-
- return (AHCI_SUCCESS);
-}
-
/*
- * PxIS.HBDS - System Memory Error and Fatal Error
+ * the fatal errors include PxIS.IFS, PxIS.HBDS, PxIS.HBFS and PxIS.TFES.
+ *
+ * PxIS.IFS indicates that the hba encountered an error on the serial ata
+ * interface which caused the transfer to stop.
+ *
+ * PxIS.HBDS indicates that the hba encountered a data error
+ * (uncorrectable ecc/parity) when reading from or writing to system memory.
+ *
+ * PxIS.HBFS indicates that the hba encountered a host bus error that it
+ * cannot recover from, such as a bad software pointer.
*
- * Which indicates that the HBA encountered a data error
- * (uncorrectable ECC/parity) when reading from or writing to system memory.
+ * PxIS.TFES is set whenever the status register is updated by the device
+ * and the error bit (bit 0) is set.
*/
static int
-ahci_intr_host_bus_data_error(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
+ahci_intr_fatal_error(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
+ uint8_t port, uint32_t intr_status, uint32_t retrieve_errinfo_slot_status)
{
+ uint32_t port_cmd_status;
uint32_t port_serror;
+ uint32_t task_file_status;
+ int failed_slot;
+ sata_pkt_t *spkt;
+ uint8_t err_byte;
+ ahci_event_arg_t *args;
mutex_enter(&ahci_portp->ahciport_mutex);
- port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port));
-
- AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_host_bus_data_error: port %d, "
- "port_serror = 0x%x", port, port_serror);
+ /*
+ * ahci_intr_phyrdy_change() may have rendered it to
+ * SATA_DTYPE_NONE.
+ */
+ if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) {
+ AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp,
+ "ahci_intr_fatal_error: port %d no device attached, "
+ "and just return without doing anything", port);
+ goto out0;
+ }
- ahci_log_error_message(ahci_ctlp, port, port_serror);
+ /*
+ * Read PxCMD.CCS to determine the slot that the HBA
+ * was processing when the error occurred.
+ */
+ port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
+ failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >>
+ AHCI_CMD_STATUS_CCS_SHIFT;
- cmn_err(CE_NOTE, "!ahci port %d has bus data error", port);
+ spkt = ahci_portp->ahciport_slot_pkts[failed_slot];
+ AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp,
+ "ahci_intr_fatal_error: spkt 0x%p is being processed when "
+ "fatal error occurred for port %d", spkt, port);
- (void) ahci_recovery_fatal_error(ahci_ctlp, ahci_portp,
- port, AHCI_INTR_STATUS_HBDS);
+ if (intr_status & AHCI_INTR_STATUS_TFES) {
+ task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
+ AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp,
+ "ahci_intr_fatal_error: port %d "
+ "task_file_status = 0x%x", port, task_file_status);
- mutex_exit(&ahci_portp->ahciport_mutex);
+ err_byte = (task_file_status & AHCI_TFD_ERR_MASK)
+ >> AHCI_TFD_ERR_SHIFT;
- return (AHCI_SUCCESS);
-}
+ /*
+ * Won't emit the error message if it is an IDENTIFY DEVICE
+ * command sent to an ATAPI device.
+ */
+ if ((spkt != NULL) &&
+ (spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_ID_DEVICE) &&
+ (err_byte == SATA_ERROR_ABORT))
+ goto out1;
-/*
- * PxIS.HBFS - System Memory Error and Fatal Error
- *
- * Which indicates that the HBA encountered a host bus error that it cannot
- * recover from, such as a bad software pointer.
- */
-static int
-ahci_intr_host_bus_fatal_error(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
-{
- uint32_t port_serror;
+ /*
+ * Won't emit the error message if it is an ATAPI PACKET command
+ */
+ if ((spkt != NULL) &&
+ (spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_PACKET))
+ goto out1;
+ }
- mutex_enter(&ahci_portp->ahciport_mutex);
+ ahci_log_fatal_error_message(ahci_ctlp, port, intr_status);
+out1:
port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port));
-
- AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_host_bus_fatal_error: port %d, "
- "port_serror = 0x%x", port, port_serror);
-
- ahci_log_error_message(ahci_ctlp, port, port_serror);
-
- cmn_err(CE_NOTE, "!ahci port %d has bus fatal error", port);
-
- (void) ahci_recovery_fatal_error(ahci_ctlp, ahci_portp,
- port, AHCI_INTR_STATUS_HBFS);
-
- mutex_exit(&ahci_portp->ahciport_mutex);
-
- return (AHCI_SUCCESS);
-}
-
-/*
- * PxIS.TFES - Device Error and Fatal Error
- *
- * This bit is set whenever the status register is updated by the device
- * and the error bit (bit 0) is set.
- */
-static int
-ahci_intr_task_file_error(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port)
-{
- uint32_t task_file_status;
-
- mutex_enter(&ahci_portp->ahciport_mutex);
-
- task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
-
- AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_intr_task_file_error: port %d: "
- "task_file_status = 0x%x", port, task_file_status);
-
- cmn_err(CE_NOTE, "!ahci port %d has task file error", port);
-
- (void) ahci_recovery_fatal_error(ahci_ctlp, ahci_portp,
- port, AHCI_INTR_STATUS_TFES);
-
+ ahci_log_serror_message(ahci_ctlp, port, port_serror);
+
+ /* Prepare the argument for the taskq */
+ args = ahci_portp->ahciport_event_args;
+ args->ahciea_ctlp = (void *)ahci_ctlp;
+ args->ahciea_portp = (void *)ahci_portp;
+ args->ahciea_event = intr_status;
+ args->ahciea_retrierr_slot = retrieve_errinfo_slot_status;
+
+ /* Start the taskq to handle error recovery */
+ if ((ddi_taskq_dispatch(ahci_ctlp->ahcictl_event_taskq,
+ ahci_events_handler,
+ (void *)args, DDI_NOSLEEP)) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "ahci start taskq for event handler failed");
+ }
+out0:
mutex_exit(&ahci_portp->ahciport_mutex);
return (AHCI_SUCCESS);
@@ -4520,8 +4672,8 @@ ahci_intr_task_file_error(ahci_ctl_t *ahci_ctlp,
* This bit is only valid if the port supports cold presence detect as
* indicated by PxCMD.CPD set to '1'.
*
- * For the first phase, this interrupt is disabled and we just log
- * the debug message.
+ * At the moment, this interrupt is not needed and disabled and we just
+ * log the debug message.
*/
/* ARGSUSED */
static int
@@ -4627,6 +4779,7 @@ ahci_enable_port_intrs(ahci_ctl_t *ahci_ctlp,
(AHCI_INTR_STATUS_DHRS |
AHCI_INTR_STATUS_PSS |
AHCI_INTR_STATUS_UFS |
+ AHCI_INTR_STATUS_DPS |
AHCI_INTR_STATUS_PCS |
AHCI_INTR_STATUS_PRCS |
AHCI_INTR_STATUS_OFS |
@@ -4985,30 +5138,27 @@ ahci_rem_intrs(ahci_ctl_t *ahci_ctlp)
}
/*
- * This routine tries to put port into NotRunning state by clearing
- * PxCMD.ST and PxCMD.FRE. Remember to clear PxCMD.FRE before clearing
- * PxCMD.ST.
+ * This routine tries to put port into P:NotRunning state by clearing
+ * PxCMD.ST. HBA will clear PxCI to 0h, PxSACT to 0h, PxCMD.CCS to 0h
+ * and PxCMD.CR to '0'.
*
* WARNING!!! ahciport_mutex should be acquired before the function
* is called.
*/
/* ARGSUSED */
static int
-ahci_port_into_notrunning_state(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_port, uint8_t port)
+ahci_put_port_into_notrunning_state(ahci_ctl_t *ahci_ctlp,
+ ahci_port_t *ahci_portp, uint8_t port)
{
uint32_t port_cmd_status;
int loop_count;
AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_port_into_notrunning_state enter: port %d", port);
+ "ahci_put_port_into_notrunning_state enter: port %d", port);
port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
- if (!(port_cmd_status & AHCI_CMD_STATUS_ST))
- goto clear_fre;
-
port_cmd_status &= ~AHCI_CMD_STATUS_ST;
ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status);
@@ -5039,81 +5189,49 @@ ahci_port_into_notrunning_state(ahci_ctl_t *ahci_ctlp,
} while (port_cmd_status & AHCI_CMD_STATUS_CR);
- AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp,
- "ahci_port_into_notrunning_state: 1st loop count: %d, "
- "port_cmd_status = 0x%x", loop_count, port_cmd_status);
-
-clear_fre:
- if (port_cmd_status & AHCI_CMD_STATUS_FRE) {
-
- port_cmd_status &= ~AHCI_CMD_STATUS_FRE;
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
- port_cmd_status);
-
- /* Wait until PxCMD.FR is cleared */
- loop_count = 0;
- do {
- port_cmd_status =
- ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
-
- if (loop_count++ > AHCI_POLLRATE_PORT_IDLE) {
- AHCIDBG2(AHCIDBG_INIT, ahci_ctlp,
- "clearing port %d CMD.FR "
- "timeout, port_cmd_status = 0x%x",
- port, port_cmd_status);
- /*
- * We are effectively timing out after 0.5 sec.
- * This value is specified in AHCI spec.
- */
- break;
- }
-
- /* Wait for 10 millisec */
-#ifndef __lock_lint
- delay(AHCI_10MS_TICKS);
-#endif /* __lock_lint */
- } while (port_cmd_status & AHCI_CMD_STATUS_FR);
+ ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_STARTED;
+ if (port_cmd_status & AHCI_CMD_STATUS_CR) {
AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp,
- "ahci_port_into_notrunning_state: 2nd loop count: "
- "%d, port_cmd_status = 0x%x",
- loop_count, port_cmd_status);
- }
-
- if (port_cmd_status &
- (AHCI_CMD_STATUS_ST |
- AHCI_CMD_STATUS_CR |
- AHCI_CMD_STATUS_FRE |
- AHCI_CMD_STATUS_FR)) {
-
- AHCIDBG1(AHCIDBG_INIT, ahci_ctlp,
- "port %d cannot be put in NotRunning state", port);
+ "ahci_put_port_into_notrunning_state: failed to clear "
+ "PxCMD.CR to '0' after loop count: %d, and "
+ "port_cmd_status = 0x%x", loop_count, port_cmd_status);
return (AHCI_FAILURE);
-
} else {
-
- AHCIDBG1(AHCIDBG_INIT, ahci_ctlp,
- "port %d is put in NotRunning state", port);
+ AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp,
+ "ahci_put_port_into_notrunning_state: succeeded to clear "
+ "PxCMD.CR to '0' after loop count: %d, and "
+ "port_cmd_status = 0x%x", loop_count, port_cmd_status);
return (AHCI_SUCCESS);
}
}
/*
- * This routine will be called under five scenarios:
+ * First clear PxCMD.ST, and then check PxTFD. If both PxTFD.STS.BSY
+ * and PxTFD.STS.DRQ cleared to '0', it means the device is in a
+ * stable state, then set PxCMD.ST to '1' to start the port directly.
+ * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue a
+ * COMRESET to the device to put it in an idle state.
+ *
+ * The fifth argument returns whether the port reset is involved during
+ * the process.
+ *
+ * The routine will be called under six scenarios:
* 1. Initialize the port
* 2. To abort the packet(s)
* 3. To reset the port
- * 4. Fatal error recovery
- * 5. To abort the timeout packet(s)
+ * 4. To activate the port
+ * 5. Fatal error recovery
+ * 6. To abort the timeout packet(s)
*
* WARNING!!! ahciport_mutex should be acquired before the function
- * is called.
+ * is called. And ahciport_mutex will be released before the reset
+ * event is reported to sata module by calling sata_hba_event_notify,
+ * and then be acquired again later.
*/
static int
ahci_restart_port_wait_till_ready(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port, int flag)
+ ahci_port_t *ahci_portp, uint8_t port, int flag, int *reset_flag)
{
uint32_t task_file_status;
sata_device_t sdevice;
@@ -5122,35 +5240,56 @@ ahci_restart_port_wait_till_ready(ahci_ctl_t *ahci_ctlp,
AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
"ahci_restart_port_wait_till_ready: port %d enter", port);
+ /* First disable the interrupt */
ahci_disable_port_intrs(ahci_ctlp, ahci_portp, port);
- rval = ahci_port_into_notrunning_state(ahci_ctlp, ahci_portp, port);
+ /* Then clear PxCMD.ST */
+ rval = ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp,
+ port);
+ if (rval != AHCI_SUCCESS)
+ /*
+ * If PxCMD.CR does not clear within a reasonable time, it
+ * may assume the interface is in a hung condition and may
+ * continue with issuing the port reset.
+ */
+ goto reset;
- if (rval == AHCI_SUCCESS) {
- task_file_status = ddi_get32(
- ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
+ /* Then clear PxSERR */
+ ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port),
+ AHCI_SERROR_CLEAR_ALL);
- if (task_file_status & AHCI_TFD_STS_BSY ||
- task_file_status & AHCI_TFD_STS_DRQ)
- rval = AHCI_FAILURE;
- }
+ /* The get PxTFD */
+ task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port));
- if (!(flag & AHCI_PORT_RESET) && (rval == AHCI_SUCCESS))
+ /*
+ * Check whether the device is in a stable status, if yes,
+ * then start the port directly. However for ahci_tran_dport_reset,
+ * we may have to perform a port reset.
+ */
+ if (!(task_file_status & (AHCI_TFD_STS_BSY | AHCI_TFD_STS_DRQ)) &&
+ !(flag & AHCI_PORT_RESET))
goto out;
+reset:
/* Set the reset in progress flag */
if (!(flag & AHCI_RESET_NO_EVENTS_UP)) {
ahci_portp->ahciport_reset_in_progress = 1;
}
+ /*
+ * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue
+ * a COMRESET to the device
+ */
rval = ahci_port_reset(ahci_ctlp, ahci_portp, port);
- if (rval != AHCI_SUCCESS) {
- ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED;
+ if (rval != AHCI_SUCCESS)
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
"ahci_restart_port_wait_till_ready: port %d failed",
port);
- }
+
+ if (reset_flag != NULL)
+ *reset_flag = 1;
/* Indicate to the framework that a reset has happened. */
if (!(flag & AHCI_RESET_NO_EVENTS_UP)) {
@@ -5158,13 +5297,9 @@ ahci_restart_port_wait_till_ready(ahci_ctl_t *ahci_ctlp,
bzero((void *)&sdevice, sizeof (sata_device_t));
sdevice.satadev_addr.cport =
ahci_ctlp->ahcictl_port_to_cport[port];
- sdevice.satadev_addr.pmport = AHCI_PORTMULT_CONTROL_PORT;
+ sdevice.satadev_addr.pmport = 0;
+ sdevice.satadev_addr.qual = SATA_ADDR_DCPORT;
- if (ahci_portp->ahciport_device_type == SATA_DTYPE_PMULT) {
- sdevice.satadev_addr.qual = SATA_ADDR_DPMPORT;
- } else {
- sdevice.satadev_addr.qual = SATA_ADDR_DCPORT;
- }
sdevice.satadev_state = SATA_DSTATE_RESET |
SATA_DSTATE_PWR_ACTIVE;
if (ahci_ctlp->ahcictl_sata_hba_tran) {
@@ -5176,12 +5311,13 @@ ahci_restart_port_wait_till_ready(ahci_ctl_t *ahci_ctlp,
mutex_enter(&ahci_portp->ahciport_mutex);
}
- AHCIDBG0(AHCIDBG_EVENT, ahci_ctlp,
- "sending event up: SATA_EVNT_RESET");
+ AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp,
+ "port %d sending event up: SATA_EVNT_RESET", port);
}
out:
+ /* Start the port */
if (!(flag & AHCI_PORT_INIT)) {
- (void) ahci_start_port(ahci_ctlp, port);
+ (void) ahci_start_port(ahci_ctlp, ahci_portp, port);
ahci_enable_port_intrs(ahci_ctlp, ahci_portp, port);
}
@@ -5189,8 +5325,8 @@ out:
}
/*
- * This routine is called under four scenarios:
- * a) some commands failed with errors
+ * This routine may be called under four scenarios:
+ * a) do the recovery from fatal error
* b) or we need to timeout some commands
* c) or we need to abort some commands
* d) or we need reset device/port/controller
@@ -5198,8 +5334,7 @@ out:
* 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 checking AHCI_PORT_STATE_MOPPING by the caller.
+ * WARNING!!! ahciport_mutex should be acquired before the function is called.
*/
static void
ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
@@ -5214,8 +5349,6 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
uint32_t finished_tags, unfinished_tags;
int tmp_slot;
sata_pkt_t *satapkt;
- int i, num = 0;
- ahci_fis_d2h_register_t *rcvd_fisp;
AHCIDBG2(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp,
"ahci_mop_commands entered: port: %d slot_status: 0x%x",
@@ -5227,13 +5360,6 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
"reset_tags: 0x%x", failed_tags,
timeout_tags, aborted_tags, reset_tags);
- /*
- * We could be here for four reasons: abort, reset,
- * timeout or error handling. Only one such mopping
- * is allowed at a time.
- */
- mutex_enter(&ahci_portp->ahciport_mutex);
-
finished_tags = ahci_portp->ahciport_pending_tags &
~slot_status & AHCI_SLOT_MASK(ahci_ctlp);
@@ -5245,16 +5371,24 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
~timeout_tags;
/*
- * Try to check if there are more than one command which have
- * been finished.
+ * When AHCI_PORT_FLAG_RQSENSE is set, it means REQUEST SENSE
+ * command doesn't complete successfully due to one of the
+ * following three conditions:
+ *
+ * 1. Fatal error - failed_tags includes its slot
+ * 2. Timed out - timeout_tags includes its slot
+ * 3. Aborted when hot unplug - aborted_tags includes its slot
*/
- for (i = 0; i < ahci_ctlp->ahcictl_num_cmd_slots; i++) {
- if ((0x1 << i) & finished_tags)
- num++;
+ if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_RQSENSE) {
+ AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands "
+ "is called for port %d while REQUEST SENSE for error "
+ "retrieval is being executed", port);
+ ASSERT(ahci_portp->ahciport_mop_in_progress > 1);
+ finished_tags = 0;
+ unfinished_tags = 0;
}
- ASSERT(num <= 1);
- /* Set finished packets with SATA_PKT_COMPLETED */
+ /* Send up finished packets with SATA_PKT_COMPLETED */
while (finished_tags) {
tmp_slot = ddi_ffs(finished_tags) - 1;
if (tmp_slot == -1) {
@@ -5264,20 +5398,21 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot];
ASSERT(satapkt != NULL);
- rcvd_fisp =
- &(ahci_portp->ahciport_rcvd_fis->ahcirf_d2h_register_fis);
-
- /* Return the status */
- satapkt->satapkt_cmd.satacmd_status_reg =
- GET_RFIS_STATUS(rcvd_fisp);
-
- if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs)
- ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp);
-
AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_mop_commands: "
"sending up pkt 0x%p with SATA_PKT_COMPLETED",
(void *)satapkt);
+ /*
+ * Cannot fetch the return register content since the port
+ * was restarted, so the corresponding tag will be set to
+ * aborted tags.
+ */
+ if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) {
+ CLEAR_BIT(finished_tags, tmp_slot);
+ aborted_tags |= tmp_slot;
+ continue;
+ }
+
CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot);
CLEAR_BIT(finished_tags, tmp_slot);
ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL;
@@ -5285,9 +5420,7 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED);
}
- ASSERT(finished_tags == 0);
-
- /* Send up failed_tags with SATA_PKT_DEV_ERROR. */
+ /* Send up failed packets with SATA_PKT_DEV_ERROR. */
while (failed_tags) {
tmp_slot = ddi_ffs(failed_tags) - 1;
if (tmp_slot == -1) {
@@ -5297,21 +5430,6 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot];
ASSERT(satapkt != NULL);
- if (satapkt->satapkt_device.satadev_type ==
- SATA_DTYPE_ATAPICD) {
- ahci_set_sense_data(satapkt, SATA_PKT_ABORTED);
- }
-
- rcvd_fisp =
- &(ahci_portp->ahciport_rcvd_fis->ahcirf_d2h_register_fis);
-
- /* Return the status */
- satapkt->satapkt_cmd.satacmd_status_reg =
- GET_RFIS_STATUS(rcvd_fisp);
-
- if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs)
- ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp);
-
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: "
"sending up pkt 0x%p with SATA_PKT_DEV_ERROR",
(void *)satapkt);
@@ -5323,9 +5441,7 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR);
}
- ASSERT(failed_tags == 0);
-
- /* Send up timeout_tags with SATA_PKT_TIMEOUT. */
+ /* Send up timeout packets with SATA_PKT_TIMEOUT. */
while (timeout_tags) {
tmp_slot = ddi_ffs(timeout_tags) - 1;
if (tmp_slot == -1) {
@@ -5346,9 +5462,7 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT);
}
- ASSERT(timeout_tags == 0);
-
- /* Set aborted packets with SATA_PKT_ABORTED */
+ /* Send up aborted packets with SATA_PKT_ABORTED */
while (aborted_tags) {
tmp_slot = ddi_ffs(aborted_tags) - 1;
if (tmp_slot == -1) {
@@ -5358,11 +5472,6 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot];
ASSERT(satapkt != NULL);
- if (satapkt->satapkt_device.satadev_type ==
- SATA_DTYPE_ATAPICD) {
- ahci_set_sense_data(satapkt, SATA_PKT_ABORTED);
- }
-
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: "
"sending up pkt 0x%p with SATA_PKT_ABORTED",
(void *)satapkt);
@@ -5374,9 +5483,7 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED);
}
- ASSERT(aborted_tags == 0);
-
- /* Reset tags are sent up to framework with SATA_PKT_RESET. */
+ /* Send up reset packets with SATA_PKT_RESET. */
while (reset_tags) {
tmp_slot = ddi_ffs(reset_tags) - 1;
if (tmp_slot == -1) {
@@ -5397,9 +5504,7 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET);
}
- ASSERT(reset_tags == 0);
-
- /* Set unfinished packets with SATA_PKT_BUSY */
+ /* Send up unfinished packets with SATA_PKT_RESET */
while (unfinished_tags) {
tmp_slot = ddi_ffs(unfinished_tags) - 1;
if (tmp_slot == -1) {
@@ -5410,29 +5515,149 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
ASSERT(satapkt != NULL);
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: "
- "sending up pkt 0x%p with SATA_PKT_BUSY",
+ "sending up pkt 0x%p with SATA_PKT_RESET",
(void *)satapkt);
CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot);
CLEAR_BIT(unfinished_tags, tmp_slot);
ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL;
- SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_BUSY);
+ SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET);
}
- ASSERT(unfinished_tags == 0);
+ ahci_portp->ahciport_mop_in_progress--;
+ ASSERT(ahci_portp->ahciport_mop_in_progress >= 0);
- ahci_portp->ahciport_flags &= ~AHCI_PORT_STATE_MOPPING;
+ if (ahci_portp->ahciport_mop_in_progress == 0)
+ ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_MOPPING;
+}
- mutex_exit(&ahci_portp->ahciport_mutex);
+/*
+ * This routine is going to first request a REQUEST SENSE sata pkt from sata
+ * module, and then deliver it to the HBA to get the sense data and copy
+ * the sense data back to the orignal failed sata pkt, and free the REQUEST
+ * SENSE sata pkt later.
+ */
+static void
+ahci_get_rqsense_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
+ uint8_t port, sata_pkt_t *spkt)
+{
+ sata_device_t sdevice;
+ sata_pkt_t *rs_spkt;
+ sata_cmd_t *sata_cmd;
+ ddi_dma_handle_t buf_dma_handle;
+ int loop_count;
+ int rval;
+#if AHCI_DEBUG
+ struct scsi_extended_sense *rqsense;
+#endif
+
+ AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
+ "ahci_get_rqsense_data enter: port %d", port);
+
+ /* Prepare the sdevice data */
+ bzero((void *)&sdevice, sizeof (sata_device_t));
+ sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port];
+
+ sdevice.satadev_addr.qual = SATA_ADDR_DCPORT;
+ sdevice.satadev_addr.pmport = 0;
+
+ sata_cmd = &spkt->satapkt_cmd;
+
+ /*
+ * Call the sata hba interface to get a rs spkt
+ */
+ loop_count = 0;
+loop:
+ rs_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip,
+ &sdevice, SATA_ERR_RETR_PKT_TYPE_ATAPI);
+ if (rs_spkt == NULL) {
+ if (loop_count++ < AHCI_POLLRATE_GET_SPKT) {
+ /* Sleep for a while */
+ delay(AHCI_10MS_TICKS);
+ goto loop;
+
+ }
+ /* Timed out after 1s */
+ AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
+ "failed to get rs spkt for port %d", port);
+ return;
+ }
+
+ ASSERT(rs_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH);
+
+ /*
+ * This flag is used to handle the specific error recovery when the
+ * REQUEST SENSE command gets a faiure (failure error or time-out).
+ */
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RQSENSE;
+
+ /* The driver would like to do it in POLL mode */
+ rs_spkt->satapkt_op_mode |= SATA_OPMODE_POLLING;
+
+ /*
+ * This start is not supposed to fail because after port is restarted,
+ * the command slot is empty
+ */
+ rval = ahci_do_sync_start(ahci_ctlp, ahci_portp, port, rs_spkt);
+ ASSERT(rval != AHCI_FAILURE);
+
+ /* Remove the flag after REQUEST SENSE command is completed */
+ ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RQSENSE;
+
+ if (rs_spkt->satapkt_reason == SATA_PKT_COMPLETED) {
+ /* Update the request sense data */
+ buf_dma_handle = *(ddi_dma_handle_t *)
+ (rs_spkt->satapkt_cmd.satacmd_err_ret_buf_handle);
+ rval = ddi_dma_sync(buf_dma_handle, 0, 0,
+ DDI_DMA_SYNC_FORKERNEL);
+ if (rval == DDI_SUCCESS) {
+ /* Copy the request sense data */
+ bcopy(rs_spkt->
+ satapkt_cmd.satacmd_bp->b_un.b_addr,
+ &sata_cmd->satacmd_rqsense,
+ SATA_ATAPI_MIN_RQSENSE_LEN);
+#if AHCI_DEBUG
+ rqsense = (struct scsi_extended_sense *)
+ sata_cmd->satacmd_rqsense;
+
+ /* Dump the sense data */
+ AHCIDBG0(AHCIDBG_SENSEDATA, ahci_ctlp, "\n");
+ AHCIDBG2(AHCIDBG_SENSEDATA, ahci_ctlp,
+ "Sense data for satapkt %p ATAPI cmd 0x%x",
+ spkt, sata_cmd->satacmd_acdb[0]);
+ AHCIDBG5(AHCIDBG_SENSEDATA, ahci_ctlp,
+ " es_code 0x%x es_class 0x%x "
+ "es_key 0x%x es_add_code 0x%x "
+ "es_qual_code 0x%x",
+ rqsense->es_code, rqsense->es_class,
+ rqsense->es_key, rqsense->es_add_code,
+ rqsense->es_qual_code);
+#endif
+ }
+ }
+
+ sata_free_error_retrieval_pkt(rs_spkt);
}
/*
* Fatal errors will cause the HBA to enter the ERR: Fatal state. To recover,
- * the port must be restarted.
+ * the port must be restarted. When the HBA detects thus error, it may try
+ * to abort a transfer. And if the transfer was aborted, the device is
+ * expected to send a D2H Register FIS with PxTFD.STS.ERR set to '1' and both
+ * PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0'. Then system software knows
+ * that the device is in a stable status and transfers may be restarted without
+ * issuing a COMRESET to the device. If PxTFD.STS.BSY or PxTFD.STS.DRQ is set,
+ * then the software will send the COMRESET to do the port reset.
+ *
+ * Software should perform the appropriate error recovery actions based on
+ * whether non-queued commands were being issued or natived command queuing
+ * commands were being issued.
+ *
+ * And software will complete the command that had the error with error mark
+ * to higher level software.
*
* Fatal errors include the following:
- * PxIS.OFS - Overflow Status
* PxIS.IFS - Interface Fatal Error Status
* PxIS.HBDS - Host Bus Data Error Status
* PxIS.HBFS - Host Bus Fatal Error Status
@@ -5440,191 +5665,264 @@ ahci_mop_commands(ahci_ctl_t *ahci_ctlp,
*
* WARNING!!! ahciport_mutex should be acquired before the function is called.
*/
-static int
-ahci_recovery_fatal_error(ahci_ctl_t *ahci_ctlp,
- ahci_port_t *ahci_portp, uint8_t port, uint32_t error)
+static void
+ahci_fatal_error_recovery_handler(ahci_ctl_t *ahci_ctlp,
+ ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status,
+ uint32_t retrieve_errinfo_slot_status)
{
- uint32_t port_cmd_status, slot_status;
- uint32_t failed_tags;
- int current_slot;
- sata_pkt_t *spkt;
- ahci_fis_d2h_register_t *ahci_rcvd_fisp;
+ uint32_t port_cmd_status, port_cmd_issue;
+ uint32_t failed_tags = 0;
+ int failed_slot;
+ int reset_flag = 0;
+ ahci_fis_d2h_register_t *ahci_rcvd_fisp;
+ sata_cmd_t *sata_cmd;
+ sata_pkt_t *spkt;
AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_recovery_fatal_error enter port %d", port);
+ "ahci_fatal_error_recovery_handler enter: port %d", port);
- if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) {
- /*
- * ahci_intr_phyrdy_change() may have rendered it to
- * SATA_DTYPE_NONE.
- */
- AHCIDBG1(AHCIDBG_INTR, ahci_ctlp,
- "ahci_recovery_fatal_error: no device attached, and just "
- "return without doing anything", port);
- return (SATA_SUCCESS);
- }
+ /* Read PxCI to see which commands are still outstanding */
+ port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
- /* Record the error occurred command slot */
+ /*
+ * Read PxCMD.CCS to determine the slot that the HBA
+ * was processing when the error occurred.
+ */
port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
(uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
-
- current_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >>
+ failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >>
AHCI_CMD_STATUS_CCS_SHIFT;
- spkt = ahci_portp->ahciport_slot_pkts[current_slot];
+ spkt = ahci_portp->ahciport_slot_pkts[failed_slot];
if (spkt == NULL) {
- /*
- * For example, sometimes PxIS.OFS and PxIS.IFS can be set
- * concurrently.
- */
- AHCIDBG1(AHCIDBG_INTR, ahci_ctlp,
- "ahci_recovery_fatal_error: port %d has done "
- "fatal error recovery, so return directory", port);
- return (SATA_SUCCESS);
+ /* This may happen when interface errors occur */
+ goto next;
}
+ sata_cmd = &spkt->satapkt_cmd;
/*
- * To prevent recursive enter to ahci_mop_commands, we need
- * check AHCI_PORT_STATE_MOPPING flag.
+ * We need to set retrieve_errinfo_slot_status to failed_slot
+ * in case it's an interface error before REQUEST SENSE slot
+ * is executed, that is spkt is NULL.
*/
- if (ahci_portp->ahciport_flags & AHCI_PORT_STATE_MOPPING) {
- AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "ahci_recovery_fatal_error: port %d is in "
- "mopping process, so return directly", port);
- return (SATA_SUCCESS);
+ if (retrieve_errinfo_slot_status != 0) {
+ failed_slot = retrieve_errinfo_slot_status;
}
- ahci_portp->ahciport_flags |= AHCI_PORT_STATE_MOPPING;
+ /* The failed command must be one of the outstanding commands */
+ failed_tags = 0x1 << failed_slot;
+ ASSERT(failed_tags & port_cmd_issue);
- failed_tags = 0x1 << current_slot;
+ /* Update the sata registers, especially PxSERR register */
+ ahci_update_sata_registers(ahci_ctlp, port,
+ &spkt->satapkt_device);
- switch (error) {
- case AHCI_INTR_STATUS_IFS:
- case AHCI_INTR_STATUS_HBFS:
- case AHCI_INTR_STATUS_HBDS:
- /*
- * For PxIS.IFS, PxIS.HBFS and PxIS.HBDS, we need to return
- * PxSERR in satapkt.
- */
- ahci_update_sata_registers(ahci_ctlp, port,
- &spkt->satapkt_device);
- break;
+ /* Fill out the status and error registers for PxIS.TFES */
+ if (intr_status & AHCI_INTR_STATUS_TFES) {
+ ahci_rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis->
+ ahcirf_d2h_register_fis);
- case AHCI_INTR_STATUS_TFES:
- /*
- * For PxIS.TFES, we need to return the status and error
- * registers in sata_cmd structure
- */
- ahci_rcvd_fisp =
- &(ahci_portp->ahciport_rcvd_fis->ahcirf_d2h_register_fis);
+ /* Copy the error context back to the sata_cmd */
+ ahci_copy_err_cnxt(sata_cmd, ahci_rcvd_fisp);
+ }
- spkt->satapkt_cmd.satacmd_status_reg =
- GET_RFIS_STATUS(ahci_rcvd_fisp);
+next:
- spkt->satapkt_cmd.satacmd_error_reg =
- GET_RFIS_ERROR(ahci_rcvd_fisp);
- break;
+#if AHCI_DEBUG
+ if (failed_tags == 0) {
+ AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp,
+ "ahci_fatal_error_recovery_handler: port %d interface "
+ "error occurred while no command was processing "
+ "port_cmd_issue = 0x%x", port, port_cmd_issue);
+ } else {
+ AHCIDBG3(AHCIDBG_ERRS, ahci_ctlp,
+ "ahci_fatal_error_recovery_handler: port %d "
+ "failed_tags = 0x%x, port_cmd_issue = 0x%x",
+ port, failed_tags, port_cmd_issue);
+ }
- case AHCI_INTR_STATUS_OFS:
- /*
- * For PxIS.OFS, until now have no way to send the error
- * to sata framework.
- */
+ /*
+ * retrieve_errinfo_slot_status contains the slot number of REQUEST
+ * SENSE command. If it is not 0, it means a fatal error happens after
+ * REQUEST SENSE command is delivered to the HBA during the error
+ * recovery process. At this time, AHCI_PORT_FLAG_RQSENSE is supposed
+ * to be set, and the only outstanding command is REQUEST SENSE.
+ */
+ if (retrieve_errinfo_slot_status != 0) {
AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
- "ahci_recovery_fatal_error: "
- "PxOFS occured for port %d", port);
-
- break;
-
- default:
- AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp,
- "Not supported error 0x%x for port %d",
- error, port);
- break;
+ "ahci_fatal_error_recovery_handler: port %d REQUEST SENSE "
+ "command for error retrieval failed", port);
+ ASSERT(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_RQSENSE);
+ ASSERT(retrieve_errinfo_slot_status == port_cmd_issue);
+ ASSERT(spkt && spkt->satapkt_cmd.satacmd_acdb[0] ==
+ SCMD_REQUEST_SENSE);
}
+#endif
- slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING;
+ ahci_portp->ahciport_mop_in_progress++;
- AHCIDBG2(AHCIDBG_INFO, ahci_ctlp,
- "ahci_recovery_fatal_error: current_slot = 0x%x "
- "slot_status = 0x%x", current_slot, slot_status);
+ (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp,
+ port, NULL, &reset_flag);
- (void) ahci_restart_port_wait_till_ready(ahci_ctlp,
- ahci_portp, port, NULL);
+ /*
+ * Won't retrieve error information:
+ * 1. Port reset was involved to recover
+ * 2. No spkt is being executed
+ * 3. IDENTIFY DEVICE command sent to ATAPI device
+ * 4. REQUEST SENSE command during error recovery
+ */
+ if (reset_flag || spkt == NULL ||
+ spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_ID_DEVICE ||
+ retrieve_errinfo_slot_status != 0)
+ goto out;
- mutex_exit(&ahci_portp->ahciport_mutex);
+ /*
+ * Deliver REQUEST SENSE for ATAPI command to gather information about
+ * the error when a COMRESET has not been performed as part of the
+ * error recovery.
+ */
+ if (ahci_portp->ahciport_device_type == SATA_DTYPE_ATAPICD)
+ ahci_get_rqsense_data(ahci_ctlp, ahci_portp, port, spkt);
+
+out:
ahci_mop_commands(ahci_ctlp,
ahci_portp,
port,
- slot_status,
+ port_cmd_issue,
failed_tags, /* failed tags */
0, /* timeout tags */
0, /* aborted tags */
0); /* reset tags */
+}
+
+/*
+ * Handle events - fatal error recovery
+ */
+static void
+ahci_events_handler(void *args)
+{
+ ahci_event_arg_t *ahci_event_arg;
+ ahci_ctl_t *ahci_ctlp;
+ ahci_port_t *ahci_portp;
+ uint32_t event;
+ uint32_t retrieve_errinfo_slot_status;
+ uint8_t port;
+
+ ahci_event_arg = (ahci_event_arg_t *)args;
+ ahci_ctlp = ahci_event_arg->ahciea_ctlp;
+ ahci_portp = ahci_event_arg->ahciea_portp;
+ event = ahci_event_arg->ahciea_event;
+ retrieve_errinfo_slot_status = ahci_event_arg->ahciea_retrierr_slot;
+ port = ahci_portp->ahciport_port_num;
+
+ AHCIDBG3(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp,
+ "ahci_events_handler enter: "
+ "port %d intr_status = 0x%x retrieve_errinfo_slot_status = 0x%x",
+ port, event, retrieve_errinfo_slot_status);
+
mutex_enter(&ahci_portp->ahciport_mutex);
- return (SATA_SUCCESS);
+ /*
+ * ahci_intr_phyrdy_change() may have rendered it to
+ * SATA_DTYPE_NONE.
+ */
+ if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) {
+ AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp,
+ "ahci_events_handler: port %d no device attached, "
+ "and just return without doing anything", port);
+ goto out;
+ }
+
+ if (event & (AHCI_INTR_STATUS_IFS |
+ AHCI_INTR_STATUS_HBDS |
+ AHCI_INTR_STATUS_HBFS |
+ AHCI_INTR_STATUS_TFES))
+ ahci_fatal_error_recovery_handler(ahci_ctlp, ahci_portp,
+ port, event, retrieve_errinfo_slot_status);
+
+out:
+ mutex_exit(&ahci_portp->ahciport_mutex);
}
/*
- * ahci_watchdog_handler() calls us if it detects that there are some
- * commands which timed out.
+ * ahci_watchdog_handler() and ahci_do_sync_start will call us if they
+ * detect there are some commands which are timed out.
*/
static void
ahci_timeout_pkts(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
- uint8_t port, uint32_t tmp_timeout_tags)
+ uint8_t port, uint32_t tmp_timeout_tags,
+ uint32_t retrieve_errinfo_slot_status)
{
- uint32_t slot_status;
+ uint32_t port_cmd_issue;
uint32_t finished_tags, timeout_tags;
+#ifndef __lock_lint
+ _NOTE(ARGUNUSED(retrieve_errinfo_slot_status))
+#endif
- AHCIDBG0(AHCIDBG_TIMEOUT|AHCIDBG_ENTRY, ahci_ctlp,
- "ahci_timeout_pkts entered");
+ AHCIDBG1(AHCIDBG_TIMEOUT|AHCIDBG_ENTRY, ahci_ctlp,
+ "ahci_timeout_pkts enter: port %d", port);
mutex_enter(&ahci_portp->ahciport_mutex);
+ /* Read PxCI to see which commands are still outstanding */
+ port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
+ ASSERT(port_cmd_issue != 0);
+ ASSERT(tmp_timeout_tags & port_cmd_issue);
+
+#if AHCI_DEBUG
/*
- * To prevent recursive enter to ahci_mop_commands, we need
- * check AHCI_PORT_STATE_MOPPING flag.
+ * Retrieve_errinfo_slot_status contains the slot number for
+ * REQUEST SENSE, and if it is not 0, it means REQUEST SENSE
+ * gets time out. At this time, AHCI_PORT_FLAG_RQSENSE is
+ * supposed to be set, and REQUEST SENSE is supposed to be
+ * the only outstanding command. And we can make sure the timed
+ * out command must be REQUEST SENSE when AHCI_PORT_FLAG_RQSENSE
+ * is set.
*/
- if (ahci_portp->ahciport_flags & AHCI_PORT_STATE_MOPPING) {
- AHCIDBG1(AHCIDBG_INFO, ahci_ctlp,
- "ahci_timeout_pkts: port %d is in "
- "mopping process, so return directly ", port);
- mutex_exit(&ahci_portp->ahciport_mutex);
- return;
+ if (retrieve_errinfo_slot_status != 0) {
+ AHCIDBG2(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, ahci_ctlp,
+ "ahci_timeout_pkts called while REQUEST SENSE "
+ "command for errror recovery timed out "
+ "timeout_tags = 0x%x port_cmd_issue = 0x%x",
+ tmp_timeout_tags, port_cmd_issue);
+ ASSERT(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_RQSENSE);
+ ASSERT(port_cmd_issue == retrieve_errinfo_slot_status);
}
+#endif
- ahci_portp->ahciport_flags |= AHCI_PORT_STATE_MOPPING;
-
- slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port));
+ ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING;
+ ahci_portp->ahciport_mop_in_progress++;
(void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp,
- port, NULL);
+ port, NULL, NULL);
+
/*
* Re-identify timeout tags because some previously checked commands
* could already complete.
*/
finished_tags = ahci_portp->ahciport_pending_tags &
- ~slot_status & AHCI_SLOT_MASK(ahci_ctlp);
-
+ ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp);
timeout_tags = tmp_timeout_tags & ~finished_tags;
- AHCIDBG3(AHCIDBG_TIMEOUT, ahci_ctlp,
+ AHCIDBG5(AHCIDBG_TIMEOUT, ahci_ctlp,
"ahci_timeout_pkts: port %d, finished_tags = 0x%x, "
- "timeout_tags = 0x%x", port,
- finished_tags, timeout_tags);
+ "timeout_tags = 0x%x port_cmd_issue = 0x%x pending_tags = 0x%x ",
+ port, finished_tags, timeout_tags,
+ port_cmd_issue, ahci_portp->ahciport_pending_tags);
- mutex_exit(&ahci_portp->ahciport_mutex);
ahci_mop_commands(ahci_ctlp,
ahci_portp,
port,
- slot_status,
+ port_cmd_issue,
0, /* failed tags */
timeout_tags, /* timeout tags */
0, /* aborted tags */
0); /* reset tags */
+
+ mutex_exit(&ahci_portp->ahciport_mutex);
}
/*
@@ -5635,11 +5933,13 @@ static void
ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp)
{
ahci_port_t *ahci_portp;
- sata_pkt_t *satapkt;
+ sata_pkt_t *spkt;
uint32_t pending_tags = 0;
uint32_t timeout_tags = 0;
+ uint32_t port_cmd_status;
uint8_t port;
int tmp_slot;
+ int current_slot;
/* max number of cycles this packet should survive */
int max_life_cycles;
@@ -5664,6 +5964,17 @@ ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp)
continue;
}
+ /* Skip the check for those ports in error recovery */
+ if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) {
+ mutex_exit(&ahci_portp->ahciport_mutex);
+ continue;
+ }
+
+ port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
+ current_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >>
+ AHCI_CMD_STATUS_CCS_SHIFT;
+
pending_tags = ahci_portp->ahciport_pending_tags;
timeout_tags = 0;
while (pending_tags) {
@@ -5672,8 +5983,9 @@ ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp)
break;
}
- satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot];
- if ((satapkt != NULL) && satapkt->satapkt_time) {
+ spkt = ahci_portp->ahciport_slot_pkts[tmp_slot];
+ if ((spkt != NULL) && spkt->satapkt_time &&
+ !(spkt->satapkt_op_mode & SATA_OPMODE_POLLING)) {
/*
* We are overloading satapkt_hba_driver_private
* with watched_cycle count.
@@ -5683,21 +5995,36 @@ ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp)
* out.
*/
watched_cycles = (int)(intptr_t)
- satapkt->satapkt_hba_driver_private;
+ spkt->satapkt_hba_driver_private;
watched_cycles++;
- max_life_cycles = (satapkt->satapkt_time +
+ max_life_cycles = (spkt->satapkt_time +
ahci_watchdog_timeout - 1) /
ahci_watchdog_timeout;
- if (watched_cycles > max_life_cycles) {
+
+ spkt->satapkt_hba_driver_private =
+ (void *)(intptr_t)watched_cycles;
+
+ if (watched_cycles <= max_life_cycles)
+ goto next;
+
+ AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp,
+ "the current slot is %d", current_slot);
+ /*
+ * We need to check whether the HBA has
+ * begun to execute the command, if not,
+ * then re-set the timer of the command.
+ */
+ if (tmp_slot != current_slot) {
+ spkt->satapkt_hba_driver_private =
+ (void *)(intptr_t)0;
+ } else {
timeout_tags |= (0x1 << tmp_slot);
cmn_err(CE_NOTE, "!ahci watchdog: "
"port %d satapkt 0x%p timed out\n",
- port, (void *)satapkt);
+ port, (void *)spkt);
}
- satapkt->satapkt_hba_driver_private =
- (void *)(intptr_t)watched_cycles;
}
-
+next:
CLEAR_BIT(pending_tags, tmp_slot);
}
@@ -5705,7 +6032,7 @@ ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp)
mutex_exit(&ahci_portp->ahciport_mutex);
mutex_exit(&ahci_ctlp->ahcictl_mutex);
ahci_timeout_pkts(ahci_ctlp, ahci_portp,
- port, timeout_tags);
+ port, timeout_tags, 0);
mutex_enter(&ahci_ctlp->ahcictl_mutex);
mutex_enter(&ahci_portp->ahciport_mutex);
}
@@ -5724,7 +6051,29 @@ ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp)
}
/*
- * Put a copy back to sata_cmd_t.
+ * Fill the error context into sata_cmd for non-queued command error.
+ */
+static void
+ahci_copy_err_cnxt(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp)
+{
+ scmd->satacmd_status_reg = GET_RFIS_STATUS(rfisp);
+ scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp);
+ scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp);
+ scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp);
+ scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp);
+ scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp);
+ scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp);
+
+ if (scmd->satacmd_addr_type == ATA_ADDR_LBA48) {
+ scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp);
+ scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp);
+ scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp);
+ scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp);
+ }
+}
+
+/*
+ * Put the respective register value to sata_cmd_t for satacmd_flags.
*/
static void
ahci_copy_out_regs(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp)
@@ -5751,13 +6100,41 @@ ahci_copy_out_regs(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp)
scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp);
}
+static void
+ahci_log_fatal_error_message(ahci_ctl_t *ahci_ctlp, uint8_t port,
+ uint32_t intr_status)
+{
+#ifndef __lock_lint
+ _NOTE(ARGUNUSED(ahci_ctlp))
+#endif
+
+ if (intr_status & AHCI_INTR_STATUS_IFS)
+ cmn_err(CE_NOTE, "!ahci port %d has interface fatal "
+ "error", port);
+
+ if (intr_status & AHCI_INTR_STATUS_HBDS)
+ cmn_err(CE_NOTE, "!ahci port %d has bus data error", port);
+
+ if (intr_status & AHCI_INTR_STATUS_HBFS)
+ cmn_err(CE_NOTE, "!ahci port %d has bus fatal error", port);
+
+ if (intr_status & AHCI_INTR_STATUS_TFES)
+ cmn_err(CE_NOTE, "!ahci port %d has task file error", port);
+
+ cmn_err(CE_NOTE, "!ahci port %d is trying to do error "
+ "recovery", port);
+}
+
/*
* Dump the serror message to the log.
*/
static void
-ahci_log_error_message(ahci_ctl_t *ahci_ctlp, uint8_t port,
+ahci_log_serror_message(ahci_ctl_t *ahci_ctlp, uint8_t port,
uint32_t port_serror)
{
+#ifndef __lock_lint
+ _NOTE(ARGUNUSED(ahci_ctlp))
+#endif
char *err_str;
if (port_serror & AHCI_SERROR_ERR_I) {
diff --git a/usr/src/uts/common/io/warlock/ahci.wlcmd b/usr/src/uts/common/io/warlock/ahci.wlcmd
index 898665b33f..26551e44dd 100644
--- a/usr/src/uts/common/io/warlock/ahci.wlcmd
+++ b/usr/src/uts/common/io/warlock/ahci.wlcmd
@@ -20,7 +20,7 @@
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -38,6 +38,7 @@ one sd_resv_reclaim_request
root ahci_intr
root ahci_watchdog_handler
+root ahci_events_handler
add sd.c:sd_start_cmds/funcp target sd_initpkt_for_buf sd_initpkt_for_uscsi
root sd.c:sd_handle_mchange sd_media_change_task sd_start_stop_unit_task
@@ -106,6 +107,6 @@ add scsi_pkt::pkt_comp targets \
add __ddi_xbuf_attr::xa_strategy targets sd_xbuf_strategy
ignore sd.c:sd_mhd_reset_notify_cb
-ignore ahci_mop_commands
+ignore delay
assert order ahci_ctl::ahcictl_mutex ahci_port::ahciport_mutex
diff --git a/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h b/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h
index 64eb0b39c8..70966ae541 100644
--- a/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h
+++ b/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -344,8 +344,6 @@ extern "C" {
#define AHCI_SIGNATURE_PORT_MULTIPLIER 0x96690101
#define AHCI_SIGNATURE_ATAPI 0xeb140101
#define AHCI_SIGNATURE_DISK 0x00000101
-#define AHCI_SIGNATURE_NONE 0xffffffff
-
/*
* The address of the control port for the port multiplier, which is
@@ -357,6 +355,7 @@ extern "C" {
#define AHCI_H2D_REGISTER_FIS_TYPE 0x27
#define AHCI_H2D_REGISTER_FIS_LENGTH 5
+#define AHCI_CMDHEAD_ATAPI 0x1 /* set to 1 for ATAPI command */
#define AHCI_CMDHEAD_DATA_WRITE 0x1 /* From system memory to device */
#define AHCI_CMDHEAD_DATA_READ 0x0 /* From device to system memory */
#define AHCI_CMDHEAD_PREFETCHABLE 0x1 /* if set, HBA prefetch PRDs */
@@ -527,6 +526,9 @@ typedef struct ahci_fis_set_device_bits {
/* offset 0x00 */
uint32_t ahcifsdb_type_rsvd_intr_status_error;
+#define GET_N_BIT_OF_SET_DEV_BITS(fis) \
+ ((fis->ahcifsdb_type_rsvd_intr_status_error >> 15) & 0x1)
+
/* offset 0x04 */
uint32_t ahcifsdb_rsvd;
} ahci_fis_set_device_bits_t;
@@ -629,16 +631,6 @@ typedef struct ahci_rcvd_fis {
uint32_t ahcirf_fis_rsvd4[15];
} ahci_rcvd_fis_t;
-/*
- * XXX to be supported in second phase
- *
- * ATAPI command structure - 12 or 16 bytes
- */
-typedef struct atapi_cmd {
- uint32_t atapi_cmd_head;
- uint32_t atapi_pad[3];
-} atapi_cmd_t;
-
/* physical region description table (PRDT) item structure */
typedef struct ahci_prdt_item {
/* DW 0 - Data Base Address */
@@ -667,7 +659,7 @@ typedef struct ahci_cmd_table {
ahci_fis_command_t ahcict_command_fis;
/* offset 0x40 - ATAPI Command */
- atapi_cmd_t ahcict_atapi_cmd;
+ uint8_t ahcict_atapi_cmd[SATA_ATAPI_MAX_CDB_LEN];
/* offset 0x50 - Reserved */
uint32_t ahcict_rsvd[12];
diff --git a/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h b/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h
index ada2f845ee..e2e142b1ac 100644
--- a/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h
+++ b/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -34,14 +34,62 @@
extern "C" {
#endif
+/* Type for argument of event handler */
+typedef struct ahci_event_arg {
+ void *ahciea_ctlp;
+ void *ahciea_portp;
+ uint32_t ahciea_event;
+ uint32_t ahciea_retrierr_slot;
+} ahci_event_arg_t;
+
+/* Warlock annotation */
+_NOTE(DATA_READABLE_WITHOUT_LOCK(ahci_event_arg_t::ahciea_ctlp))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(ahci_event_arg_t::ahciea_portp))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(ahci_event_arg_t::ahciea_event))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(ahci_event_arg_t::ahciea_retrierr_slot))
+
+/*
+ * flags for ahciport_flags
+ *
+ * AHCI_PORT_FLAG_SPINUP: this flag will be set when a HBA which supports
+ * staggered spin-up needs to do a spin-up.
+ *
+ * AHCI_PORT_FLAG_MOPPING: this flag will be set when the HBA is stopped,
+ * and all the outstanding commands need to be aborted and sent to upper
+ * layers.
+ *
+ * AHCI_PORT_FLAG_POLLING: this flag will be set when the interrupt is
+ * disabled, and the command is executed in POLLING mode.
+ *
+ * AHCI_PORT_FLAG_RQSENSE: this flag will be set when a REQUEST SENSE which
+ * is used to retrieve sense data is being executed.
+ *
+ * AHCI_PORT_FLAG_STARTED: this flag will be set when the port is started,
+ * that is PxCMD.ST is set with '1', and be cleared when the port is put into
+ * idle, that is PxCMD.ST is changed from '1' to '0'.
+ */
+#define AHCI_PORT_FLAG_SPINUP 0x01
+#define AHCI_PORT_FLAG_MOPPING 0x02
+#define AHCI_PORT_FLAG_POLLING 0x04
+#define AHCI_PORT_FLAG_RQSENSE 0x08
+#define AHCI_PORT_FLAG_STARTED 0x10
+
typedef struct ahci_port {
- /* The physical port number - for debug message */
+ /* The physical port number */
uint8_t ahciport_port_num;
+
/* Type of the device attached to the port */
uint8_t ahciport_device_type;
/* State of the port */
uint32_t ahciport_port_state;
- /* Only used for staggered spin-up */
+
+ /*
+ * AHCI_PORT_FLAG_SPINUP
+ * AHCI_PORT_FLAG_MOPPING
+ * AHCI_PORT_FLAG_POLLING
+ * AHCI_PORT_FLAG_RQSENSE
+ * AHCI_PORT_FLAG_STARTED
+ */
int ahciport_flags;
/* Pointer to received FIS structure */
@@ -62,8 +110,16 @@ typedef struct ahci_port {
ddi_acc_handle_t \
ahciport_cmd_tables_acc_handle[AHCI_PORT_MAX_CMD_SLOTS];
+ /* Condition variable used for sync mode commands */
+ kcondvar_t ahciport_cv;
+
+ /* The whole mutex for the port structure */
kmutex_t ahciport_mutex;
+
+ /* Keep the tags of all the pending commands */
uint32_t ahciport_pending_tags;
+
+ /* Keep all the pending sata packets */
sata_pkt_t *ahciport_slot_pkts[AHCI_PORT_MAX_CMD_SLOTS];
/*
@@ -74,6 +130,11 @@ typedef struct ahci_port {
*/
int ahciport_reset_in_progress;
+ /* This is for error recovery handler */
+ ahci_event_arg_t *ahciport_event_args;
+
+ /* This is to calculate how many mops are in progress */
+ int ahciport_mop_in_progress;
} ahci_port_t;
/* Warlock annotation */
@@ -92,6 +153,8 @@ _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex,
ahci_port_t::ahciport_slot_pkts))
_NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex,
ahci_port_t::ahciport_reset_in_progress))
+_NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex,
+ ahci_port_t::ahciport_mop_in_progress))
typedef struct ahci_ctl {
dev_info_t *ahcictl_dip;
@@ -114,6 +177,12 @@ typedef struct ahci_ctl {
int ahcictl_power_level;
off_t ahcictl_pmcsr_offset;
+ /*
+ * AHCI_CAP_PIO_MDRQ
+ * AHCI_CAP_MCMDLIST_NONQUEUE
+ */
+ int ahcictl_cap;
+
/* Pci configuration space handle */
ddi_acc_handle_t ahcictl_pci_conf_handle;
@@ -146,6 +215,9 @@ typedef struct ahci_ctl {
size_t ahcictl_intr_size; /* Size of intr array */
uint_t ahcictl_intr_pri; /* Intr priority */
int ahcictl_intr_cap; /* Intr capabilities */
+
+ /* Taskq for handling event */
+ ddi_taskq_t *ahcictl_event_taskq;
} ahci_ctl_t;
/* Warlock annotation */
@@ -164,48 +236,49 @@ _NOTE(MUTEX_PROTECTS_DATA(ahci_ctl_t::ahcictl_mutex,
#define AHCI_TIMEOUT (1) /* Timed out */
#define AHCI_FAILURE (-1) /* Unsuccessful return */
-/* Port flags */
-#define AHCI_PORT_STATE_SPINUP 0x1
-#define AHCI_PORT_STATE_MOPPING 0x2
-
/* Flags for ahcictl_flags */
#define AHCI_PM 0x1
#define AHCI_ATTACH 0x2
#define AHCI_DETACH 0x4
-/* PIO Multiple DRQ Block */
-#define AHCI_PMD 0x8
+/* Values for ahcictl_cap */
+/* PIO Multiple DRQ Block */
+#define AHCI_CAP_PIO_MDRQ 0x1
+/* Multiple command lists cannot be used for non queued commands */
+#define AHCI_CAP_NO_MCMDLIST_NONQUEUE 0x2
-/* Flags controlling the reset behavior */
+/* Flags controlling the restart port behavior */
#define AHCI_PORT_RESET 0x0001 /* Reset the port */
#define AHCI_PORT_INIT 0x0002 /* Initialize port */
#define AHCI_RESET_NO_EVENTS_UP 0x0004 /* Don't send reset events up */
-
/* State values for ahci_attach */
#define AHCI_ATTACH_STATE_NONE (0x1 << 0)
#define AHCI_ATTACH_STATE_STATEP_ALLOC (0x1 << 1)
#define AHCI_ATTACH_STATE_REG_MAP (0x1 << 2)
-#define AHCI_ATTACH_STATE_INTR_ADDED (0x1 << 3)
-#define AHCI_ATTACH_STATE_MUTEX_INIT (0x1 << 4)
-#define AHCI_ATTACH_STATE_HW_INIT (0x1 << 5)
-#define AHCI_ATTACH_STATE_TIMEOUT_ENABLED (0x1 << 6)
+#define AHCI_ATTACH_STATE_PCICFG_SETUP (0x1 << 3)
+#define AHCI_ATTACH_STATE_INTR_ADDED (0x1 << 4)
+#define AHCI_ATTACH_STATE_MUTEX_INIT (0x1 << 5)
+#define AHCI_ATTACH_STATE_PORT_ALLOC (0x1 << 6)
+#define AHCI_ATTACH_STATE_ERR_RECV_TASKQ (0x1 << 7)
+#define AHCI_ATTACH_STATE_HW_INIT (0x1 << 8)
+#define AHCI_ATTACH_STATE_TIMEOUT_ENABLED (0x1 << 9)
/* Interval used for delay */
#define AHCI_10MS_TICKS (drv_usectohz(10000)) /* ticks in 10 millisec */
#define AHCI_1MS_TICKS (drv_usectohz(1000)) /* ticks in 1 millisec */
+#define AHCI_100US_TICKS (drv_usectohz(100)) /* ticks in 100 */
#define AHCI_1MS_USECS (1000) /* usecs in 1 millisec */
/*
* The following values are the numbers of times to retry polled requests.
*/
#define AHCI_POLLRATE_HBA_RESET 100
-#define AHCI_POLLRATE_PORT_COMRESET 10
#define AHCI_POLLRATE_PORT_SSTATUS 10
-#define AHCI_POLLRATE_PORT_TFD_BSY 1100
-#define AHCI_POLLRATE_PORT_TFD_ERROR 10
+#define AHCI_POLLRATE_PORT_TFD_ERROR 1100
#define AHCI_POLLRATE_PORT_IDLE 50
#define AHCI_POLLRATE_PORT_SOFTRESET 100
+#define AHCI_POLLRATE_GET_SPKT 100
/* Clearing & setting the n'th bit in a given tag */
@@ -230,6 +303,8 @@ _NOTE(MUTEX_PROTECTS_DATA(ahci_ctl_t::ahcictl_mutex,
#define AHCIDBG_ERRS 0x0400
#define AHCIDBG_COOKIES 0x0800
#define AHCIDBG_POWER 0x1000
+#define AHCIDBG_COMMAND 0x2000
+#define AHCIDBG_SENSEDATA 0x4000
extern int ahci_debug_flag;
@@ -258,6 +333,11 @@ extern int ahci_debug_flag;
ahci_log(ahci_ctlp, CE_WARN, format, arg1, arg2, arg3, arg4); \
}
+#define AHCIDBG5(flag, ahci_ctlp, format, arg1, arg2, arg3, arg4, arg5) \
+ if (ahci_debug_flags & (flag)) { \
+ ahci_log(ahci_ctlp, CE_WARN, format, arg1, arg2, \
+ arg3, arg4, arg5); \
+ }
#else
#define AHCIDBG0(flag, dip, frmt)
@@ -265,6 +345,7 @@ extern int ahci_debug_flag;
#define AHCIDBG2(flag, dip, frmt, arg1, arg2)
#define AHCIDBG3(flag, dip, frmt, arg1, arg2, arg3)
#define AHCIDBG4(flag, dip, frmt, arg1, arg2, arg3, arg4)
+#define AHCIDBG5(flag, dip, frmt, arg1, arg2, arg3, arg4, arg5)
#endif /* DEBUG */