diff options
author | yt160523 <none@none> | 2007-08-16 18:51:39 -0700 |
---|---|---|
committer | yt160523 <none@none> | 2007-08-16 18:51:39 -0700 |
commit | 68d33a2562b5b41e4606c8b0f50f32fd26b05302 (patch) | |
tree | e0ab41b15bdf6dc1e900961cf3b714060dc9325a /usr/src | |
parent | d7d936556e5d09df139c86f1cbd138da800e2c7d (diff) | |
download | illumos-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.c | 3331 | ||||
-rw-r--r-- | usr/src/uts/common/io/warlock/ahci.wlcmd | 5 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h | 20 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h | 117 |
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 */ |