12fcbc377Syt /* 22fcbc377Syt * CDDL HEADER START 32fcbc377Syt * 42fcbc377Syt * The contents of this file are subject to the terms of the 52fcbc377Syt * Common Development and Distribution License (the "License"). 62fcbc377Syt * You may not use this file except in compliance with the License. 72fcbc377Syt * 82fcbc377Syt * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92fcbc377Syt * or http://www.opensolaris.org/os/licensing. 102fcbc377Syt * See the License for the specific language governing permissions 112fcbc377Syt * and limitations under the License. 122fcbc377Syt * 132fcbc377Syt * When distributing Covered Code, include this CDDL HEADER in each 142fcbc377Syt * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152fcbc377Syt * If applicable, add the following below this CDDL HEADER, with the 162fcbc377Syt * fields enclosed by brackets "[]" replaced with your own identifying 172fcbc377Syt * information: Portions Copyright [yyyy] [name of copyright owner] 182fcbc377Syt * 192fcbc377Syt * CDDL HEADER END 202fcbc377Syt */ 212fcbc377Syt 222fcbc377Syt /* 232c742e1fSying tian - Beijing China * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 242fcbc377Syt * Use is subject to license terms. 252fcbc377Syt */ 262fcbc377Syt 272fcbc377Syt /* 282fcbc377Syt * AHCI (Advanced Host Controller Interface) SATA HBA Driver 2913bcbb7aSyt * 3013bcbb7aSyt * Power Management Support 3113bcbb7aSyt * ------------------------ 3213bcbb7aSyt * 3313bcbb7aSyt * At the moment, the ahci driver only implements suspend/resume to 3413bcbb7aSyt * support Suspend to RAM on X86 feature. Device power management isn't 3513bcbb7aSyt * implemented, link power management is disabled, and hot plug isn't 3613bcbb7aSyt * allowed during the period from suspend to resume. 3713bcbb7aSyt * 3813bcbb7aSyt * For s/r support, the ahci driver only need to implement DDI_SUSPEND 3913bcbb7aSyt * and DDI_RESUME entries, and don't need to take care of new requests 4013bcbb7aSyt * sent down after suspend because the target driver (sd) has already 4113bcbb7aSyt * handled these conditions, and blocked these requests. For the detailed 4213bcbb7aSyt * information, please check with sdopen, sdclose and sdioctl routines. 4313bcbb7aSyt * 442fcbc377Syt */ 452fcbc377Syt 46689d74b0Syt #include <sys/note.h> 472fcbc377Syt #include <sys/scsi/scsi.h> 482fcbc377Syt #include <sys/pci.h> 49b2e3645aSying tian - Beijing China #include <sys/disp.h> 502fcbc377Syt #include <sys/sata/sata_hba.h> 512fcbc377Syt #include <sys/sata/adapters/ahci/ahcireg.h> 522fcbc377Syt #include <sys/sata/adapters/ahci/ahcivar.h> 532fcbc377Syt 5419397407SSherry Moore /* 5519397407SSherry Moore * This is the string displayed by modinfo, etc. 5619397407SSherry Moore * Make sure you keep the version ID up to date! 5719397407SSherry Moore */ 5819397407SSherry Moore static char ahci_ident[] = "ahci driver"; 5919397407SSherry Moore 602fcbc377Syt /* 612fcbc377Syt * Function prototypes for driver entry points 622fcbc377Syt */ 632fcbc377Syt static int ahci_attach(dev_info_t *, ddi_attach_cmd_t); 642fcbc377Syt static int ahci_detach(dev_info_t *, ddi_detach_cmd_t); 652fcbc377Syt static int ahci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 6619397407SSherry Moore static int ahci_quiesce(dev_info_t *); 672fcbc377Syt 682fcbc377Syt /* 692fcbc377Syt * Function prototypes for SATA Framework interfaces 702fcbc377Syt */ 712fcbc377Syt static int ahci_register_sata_hba_tran(ahci_ctl_t *, uint32_t); 722fcbc377Syt static int ahci_unregister_sata_hba_tran(ahci_ctl_t *); 732fcbc377Syt 742fcbc377Syt static int ahci_tran_probe_port(dev_info_t *, sata_device_t *); 752fcbc377Syt static int ahci_tran_start(dev_info_t *, sata_pkt_t *spkt); 762fcbc377Syt static int ahci_tran_abort(dev_info_t *, sata_pkt_t *, int); 772fcbc377Syt static int ahci_tran_reset_dport(dev_info_t *, sata_device_t *); 782fcbc377Syt static int ahci_tran_hotplug_port_activate(dev_info_t *, sata_device_t *); 792fcbc377Syt static int ahci_tran_hotplug_port_deactivate(dev_info_t *, sata_device_t *); 802fcbc377Syt #if defined(__lock_lint) 812fcbc377Syt static int ahci_selftest(dev_info_t *, sata_device_t *); 822fcbc377Syt #endif 832fcbc377Syt 842fcbc377Syt /* 852fcbc377Syt * Local function prototypes 862fcbc377Syt */ 8768d33a25Syt static int ahci_alloc_ports_state(ahci_ctl_t *); 8868d33a25Syt static void ahci_dealloc_ports_state(ahci_ctl_t *); 892fcbc377Syt static int ahci_alloc_port_state(ahci_ctl_t *, uint8_t); 902fcbc377Syt static void ahci_dealloc_port_state(ahci_ctl_t *, uint8_t); 912fcbc377Syt static int ahci_alloc_rcvd_fis(ahci_ctl_t *, ahci_port_t *, uint8_t); 92689d74b0Syt static void ahci_dealloc_rcvd_fis(ahci_port_t *); 932fcbc377Syt static int ahci_alloc_cmd_list(ahci_ctl_t *, ahci_port_t *, uint8_t); 942fcbc377Syt static void ahci_dealloc_cmd_list(ahci_ctl_t *, ahci_port_t *); 952fcbc377Syt static int ahci_alloc_cmd_tables(ahci_ctl_t *, ahci_port_t *); 962fcbc377Syt static void ahci_dealloc_cmd_tables(ahci_ctl_t *, ahci_port_t *); 972fcbc377Syt 9868d33a25Syt static int ahci_initialize_controller(ahci_ctl_t *); 9968d33a25Syt static void ahci_uninitialize_controller(ahci_ctl_t *); 1002fcbc377Syt static int ahci_initialize_port(ahci_ctl_t *, ahci_port_t *, uint8_t); 10113bcbb7aSyt static int ahci_config_space_init(ahci_ctl_t *); 10268d33a25Syt 103f8a673adSying tian - Beijing China static void ahci_drain_ports_taskq(ahci_ctl_t *); 10413bcbb7aSyt static void ahci_disable_interface_pm(ahci_ctl_t *, uint8_t); 10568d33a25Syt static int ahci_start_port(ahci_ctl_t *, ahci_port_t *, uint8_t); 10668d33a25Syt static void ahci_find_dev_signature(ahci_ctl_t *, ahci_port_t *, uint8_t); 1072fcbc377Syt static void ahci_update_sata_registers(ahci_ctl_t *, uint8_t, sata_device_t *); 1082fcbc377Syt static int ahci_deliver_satapkt(ahci_ctl_t *, ahci_port_t *, 1092fcbc377Syt uint8_t, sata_pkt_t *); 11068d33a25Syt static int ahci_do_sync_start(ahci_ctl_t *, ahci_port_t *, 11168d33a25Syt uint8_t, sata_pkt_t *); 11282263d52Syt static int ahci_claim_free_slot(ahci_ctl_t *, ahci_port_t *, int); 11368d33a25Syt static void ahci_copy_err_cnxt(sata_cmd_t *, ahci_fis_d2h_register_t *); 11482263d52Syt static void ahci_copy_ncq_err_page(sata_cmd_t *, 11582263d52Syt struct sata_ncq_error_recovery_page *); 1162fcbc377Syt static void ahci_copy_out_regs(sata_cmd_t *, ahci_fis_d2h_register_t *); 1172fcbc377Syt 1182fcbc377Syt static int ahci_software_reset(ahci_ctl_t *, ahci_port_t *, uint8_t); 1192fcbc377Syt static int ahci_hba_reset(ahci_ctl_t *); 1202fcbc377Syt static int ahci_port_reset(ahci_ctl_t *, ahci_port_t *, uint8_t); 1212fcbc377Syt static void ahci_reject_all_abort_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t); 1222fcbc377Syt static int ahci_reset_device_reject_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t); 1232fcbc377Syt static int ahci_reset_port_reject_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t); 1242fcbc377Syt static int ahci_reset_hba_reject_pkts(ahci_ctl_t *); 12568d33a25Syt static int ahci_put_port_into_notrunning_state(ahci_ctl_t *, ahci_port_t *, 1262fcbc377Syt uint8_t); 1272fcbc377Syt static int ahci_restart_port_wait_till_ready(ahci_ctl_t *, ahci_port_t *, 12868d33a25Syt uint8_t, int, int *); 129689d74b0Syt static void ahci_mop_commands(ahci_ctl_t *, ahci_port_t *, uint32_t, 130689d74b0Syt uint32_t, uint32_t, uint32_t, uint32_t); 13182263d52Syt static uint32_t ahci_get_rdlogext_data(ahci_ctl_t *, ahci_port_t *, uint8_t); 13268d33a25Syt static void ahci_get_rqsense_data(ahci_ctl_t *, ahci_port_t *, 13368d33a25Syt uint8_t, sata_pkt_t *); 13468d33a25Syt static void ahci_fatal_error_recovery_handler(ahci_ctl_t *, ahci_port_t *, 13582263d52Syt uint8_t, uint32_t); 13668d33a25Syt static void ahci_timeout_pkts(ahci_ctl_t *, ahci_port_t *, 13782263d52Syt uint8_t, uint32_t); 13868d33a25Syt static void ahci_events_handler(void *); 1392fcbc377Syt static void ahci_watchdog_handler(ahci_ctl_t *); 1402fcbc377Syt 1412fcbc377Syt static uint_t ahci_intr(caddr_t, caddr_t); 14282263d52Syt static void ahci_port_intr(ahci_ctl_t *, ahci_port_t *, uint8_t); 1432c742e1fSying tian - Beijing China static int ahci_add_intrs(ahci_ctl_t *, int); 1442fcbc377Syt static void ahci_rem_intrs(ahci_ctl_t *); 1452fcbc377Syt static void ahci_enable_all_intrs(ahci_ctl_t *); 1462fcbc377Syt static void ahci_disable_all_intrs(ahci_ctl_t *); 147689d74b0Syt static void ahci_enable_port_intrs(ahci_ctl_t *, uint8_t); 148689d74b0Syt static void ahci_disable_port_intrs(ahci_ctl_t *, uint8_t); 1492fcbc377Syt 150689d74b0Syt static int ahci_intr_cmd_cmplt(ahci_ctl_t *, ahci_port_t *, uint8_t); 15168d33a25Syt static int ahci_intr_set_device_bits(ahci_ctl_t *, ahci_port_t *, uint8_t); 1522fcbc377Syt static int ahci_intr_port_connect_change(ahci_ctl_t *, ahci_port_t *, uint8_t); 1532fcbc377Syt static int ahci_intr_device_mechanical_presence_status(ahci_ctl_t *, 1542fcbc377Syt ahci_port_t *, uint8_t); 1552fcbc377Syt static int ahci_intr_phyrdy_change(ahci_ctl_t *, ahci_port_t *, uint8_t); 15668d33a25Syt static int ahci_intr_non_fatal_error(ahci_ctl_t *, ahci_port_t *, 15768d33a25Syt uint8_t, uint32_t); 15868d33a25Syt static int ahci_intr_fatal_error(ahci_ctl_t *, ahci_port_t *, 15982263d52Syt uint8_t, uint32_t); 1602fcbc377Syt static int ahci_intr_cold_port_detect(ahci_ctl_t *, ahci_port_t *, uint8_t); 1612fcbc377Syt 1622fcbc377Syt static int ahci_get_num_implemented_ports(uint32_t); 16368d33a25Syt static void ahci_log_fatal_error_message(ahci_ctl_t *, uint8_t port, 16468d33a25Syt uint32_t); 165a9440e8dSyt static void ahci_log_serror_message(ahci_ctl_t *, uint8_t, uint32_t, int); 166689d74b0Syt #if AHCI_DEBUG 1672fcbc377Syt static void ahci_log(ahci_ctl_t *, uint_t, char *, ...); 168689d74b0Syt #endif 1692fcbc377Syt 1702fcbc377Syt 1712fcbc377Syt /* 1722fcbc377Syt * DMA attributes for the data buffer 1732fcbc377Syt * 1742fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 1752fcbc377Syt * does not support 64-bit addressing 1762fcbc377Syt */ 1772fcbc377Syt static ddi_dma_attr_t buffer_dma_attr = { 1782fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 17968d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 1802fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 1812fcbc377Syt 0x3fffffull, /* dma_attr_count_max i.e. for one cookie */ 18268d33a25Syt 0x2ull, /* dma_attr_align: word aligned */ 1832fcbc377Syt 1, /* dma_attr_burstsizes */ 1842fcbc377Syt 1, /* dma_attr_minxfer */ 1852fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 1862fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 1872fcbc377Syt AHCI_PRDT_NUMBER, /* dma_attr_sgllen */ 1882fcbc377Syt 512, /* dma_attr_granular */ 1892fcbc377Syt 0, /* dma_attr_flags */ 1902fcbc377Syt }; 1912fcbc377Syt 1922fcbc377Syt /* 1932fcbc377Syt * DMA attributes for the rcvd FIS 1942fcbc377Syt * 1952fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 1962fcbc377Syt * does not support 64-bit addressing 1972fcbc377Syt */ 1982fcbc377Syt static ddi_dma_attr_t rcvd_fis_dma_attr = { 1992fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 20068d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 2012fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 2022fcbc377Syt 0xffffffffull, /* dma_attr_count_max i.e. for one cookie */ 20368d33a25Syt 0x100ull, /* dma_attr_align: 256-byte aligned */ 2042fcbc377Syt 1, /* dma_attr_burstsizes */ 2052fcbc377Syt 1, /* dma_attr_minxfer */ 2062fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2072fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2082fcbc377Syt 1, /* dma_attr_sgllen */ 2092fcbc377Syt 1, /* dma_attr_granular */ 2102fcbc377Syt 0, /* dma_attr_flags */ 2112fcbc377Syt }; 2122fcbc377Syt 2132fcbc377Syt /* 2142fcbc377Syt * DMA attributes for the command list 2152fcbc377Syt * 2162fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 2172fcbc377Syt * does not support 64-bit addressing 2182fcbc377Syt */ 2192fcbc377Syt static ddi_dma_attr_t cmd_list_dma_attr = { 2202fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 22168d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 2222fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 2232fcbc377Syt 0xffffffffull, /* dma_attr_count_max i.e. for one cookie */ 22468d33a25Syt 0x400ull, /* dma_attr_align: 1K-byte aligned */ 2252fcbc377Syt 1, /* dma_attr_burstsizes */ 2262fcbc377Syt 1, /* dma_attr_minxfer */ 2272fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2282fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2292fcbc377Syt 1, /* dma_attr_sgllen */ 2302fcbc377Syt 1, /* dma_attr_granular */ 2312fcbc377Syt 0, /* dma_attr_flags */ 2322fcbc377Syt }; 2332fcbc377Syt 2342fcbc377Syt /* 2352fcbc377Syt * DMA attributes for cmd tables 2362fcbc377Syt * 2372fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 2382fcbc377Syt * does not support 64-bit addressing 2392fcbc377Syt */ 2402fcbc377Syt static ddi_dma_attr_t cmd_table_dma_attr = { 2412fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 24268d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 2432fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 2442fcbc377Syt 0xffffffffull, /* dma_attr_count_max i.e. for one cookie */ 24568d33a25Syt 0x80ull, /* dma_attr_align: 128-byte aligned */ 2462fcbc377Syt 1, /* dma_attr_burstsizes */ 2472fcbc377Syt 1, /* dma_attr_minxfer */ 2482fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2492fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2502fcbc377Syt 1, /* dma_attr_sgllen */ 2512fcbc377Syt 1, /* dma_attr_granular */ 2522fcbc377Syt 0, /* dma_attr_flags */ 2532fcbc377Syt }; 2542fcbc377Syt 2552fcbc377Syt 2562fcbc377Syt /* Device access attributes */ 2572fcbc377Syt static ddi_device_acc_attr_t accattr = { 2582fcbc377Syt DDI_DEVICE_ATTR_V0, 2592fcbc377Syt DDI_STRUCTURE_LE_ACC, 2602fcbc377Syt DDI_STRICTORDER_ACC 2612fcbc377Syt }; 2622fcbc377Syt 2632fcbc377Syt 2642fcbc377Syt static struct dev_ops ahcictl_dev_ops = { 2652fcbc377Syt DEVO_REV, /* devo_rev */ 2662fcbc377Syt 0, /* refcnt */ 2672fcbc377Syt ahci_getinfo, /* info */ 2682fcbc377Syt nulldev, /* identify */ 2692fcbc377Syt nulldev, /* probe */ 2702fcbc377Syt ahci_attach, /* attach */ 2712fcbc377Syt ahci_detach, /* detach */ 2722fcbc377Syt nodev, /* no reset */ 2732fcbc377Syt (struct cb_ops *)0, /* driver operations */ 2742fcbc377Syt NULL, /* bus operations */ 27519397407SSherry Moore NULL, /* power */ 27619397407SSherry Moore ahci_quiesce, /* quiesce */ 2772fcbc377Syt }; 2782fcbc377Syt 2792fcbc377Syt static sata_tran_hotplug_ops_t ahci_tran_hotplug_ops = { 2802fcbc377Syt SATA_TRAN_HOTPLUG_OPS_REV_1, 2812fcbc377Syt ahci_tran_hotplug_port_activate, 2822fcbc377Syt ahci_tran_hotplug_port_deactivate 2832fcbc377Syt }; 2842fcbc377Syt 2852fcbc377Syt extern struct mod_ops mod_driverops; 2862fcbc377Syt 2872fcbc377Syt static struct modldrv modldrv = { 2882fcbc377Syt &mod_driverops, /* driverops */ 28919397407SSherry Moore ahci_ident, /* short description */ 2902fcbc377Syt &ahcictl_dev_ops, /* driver ops */ 2912fcbc377Syt }; 2922fcbc377Syt 2932fcbc377Syt static struct modlinkage modlinkage = { 2942fcbc377Syt MODREV_1, 2952fcbc377Syt &modldrv, 2962fcbc377Syt NULL 2972fcbc377Syt }; 2982fcbc377Syt 299db2cce03Sying tian - Beijing China /* The following variables are watchdog handler related */ 3002fcbc377Syt static int ahci_watchdog_timeout = 5; /* 5 seconds */ 3012fcbc377Syt static int ahci_watchdog_tick; 3022fcbc377Syt 303db2cce03Sying tian - Beijing China /* 304db2cce03Sying tian - Beijing China * This static variable indicates the size of command table, 305db2cce03Sying tian - Beijing China * and it's changeable with prdt number, which ahci_dma_prdt_number 306db2cce03Sying tian - Beijing China * indicates. 307db2cce03Sying tian - Beijing China */ 3082fcbc377Syt static size_t ahci_cmd_table_size; 3092fcbc377Syt 310db2cce03Sying tian - Beijing China /* 311259105bcSying tian - Beijing China * The below global variables are tunable via /etc/system 312259105bcSying tian - Beijing China * 313259105bcSying tian - Beijing China * ahci_dma_prdt_number 314259105bcSying tian - Beijing China * ahci_msi_enabled 315259105bcSying tian - Beijing China * ahci_buf_64bit_dma 316259105bcSying tian - Beijing China * ahci_commu_64bit_dma 317db2cce03Sying tian - Beijing China */ 318db2cce03Sying tian - Beijing China 3192fcbc377Syt /* The number of Physical Region Descriptor Table(PRDT) in Command Table */ 3202fcbc377Syt int ahci_dma_prdt_number = AHCI_PRDT_NUMBER; 3212fcbc377Syt 322db2cce03Sying tian - Beijing China /* AHCI MSI is tunable */ 3232c742e1fSying tian - Beijing China boolean_t ahci_msi_enabled = B_TRUE; 3242fcbc377Syt 325259105bcSying tian - Beijing China /* 326259105bcSying tian - Beijing China * 64-bit dma addressing for data buffer is tunable 327259105bcSying tian - Beijing China * 328259105bcSying tian - Beijing China * The variable controls only the below value: 329259105bcSying tian - Beijing China * DBAU (upper 32-bits physical address of data block) 330259105bcSying tian - Beijing China */ 331259105bcSying tian - Beijing China boolean_t ahci_buf_64bit_dma = B_TRUE; 332259105bcSying tian - Beijing China 333259105bcSying tian - Beijing China /* 334259105bcSying tian - Beijing China * 64-bit dma addressing for communication system descriptors is tunable 335259105bcSying tian - Beijing China * 336259105bcSying tian - Beijing China * The variable controls the below three values: 337259105bcSying tian - Beijing China * 338259105bcSying tian - Beijing China * PxCLBU (upper 32-bits for the command list base physical address) 339259105bcSying tian - Beijing China * PxFBU (upper 32-bits for the received FIS base physical address) 340259105bcSying tian - Beijing China * CTBAU (upper 32-bits of command table base) 341259105bcSying tian - Beijing China */ 342259105bcSying tian - Beijing China boolean_t ahci_commu_64bit_dma = B_TRUE; 343259105bcSying tian - Beijing China 344259105bcSying tian - Beijing China /* 345259105bcSying tian - Beijing China * End of global tunable variable definition 346259105bcSying tian - Beijing China */ 347db2cce03Sying tian - Beijing China 348*1fdcc913SFred Herard #if AHCI_DEBUG 349*1fdcc913SFred Herard uint32_t ahci_debug_flags = 0; 350*1fdcc913SFred Herard #else 351f5f2d263SFred Herard uint32_t ahci_debug_flags = (AHCIDBG_ERRS|AHCIDBG_TIMEOUT); 352*1fdcc913SFred Herard #endif 353*1fdcc913SFred Herard 354689d74b0Syt 355f5f2d263SFred Herard #if AHCI_DEBUG 356689d74b0Syt /* The following is needed for ahci_log() */ 357689d74b0Syt static kmutex_t ahci_log_mutex; 358689d74b0Syt static char ahci_log_buf[512]; 3592fcbc377Syt #endif 3602fcbc377Syt 3612fcbc377Syt /* Opaque state pointer initialized by ddi_soft_state_init() */ 3622fcbc377Syt static void *ahci_statep = NULL; 3632fcbc377Syt 3642fcbc377Syt /* 3652fcbc377Syt * ahci module initialization. 3662fcbc377Syt */ 3672fcbc377Syt int 3682fcbc377Syt _init(void) 3692fcbc377Syt { 3702fcbc377Syt int ret; 3712fcbc377Syt 3722fcbc377Syt ret = ddi_soft_state_init(&ahci_statep, sizeof (ahci_ctl_t), 0); 3732fcbc377Syt if (ret != 0) { 3742fcbc377Syt goto err_out; 3752fcbc377Syt } 3762fcbc377Syt 377689d74b0Syt #if AHCI_DEBUG 3782fcbc377Syt mutex_init(&ahci_log_mutex, NULL, MUTEX_DRIVER, NULL); 379689d74b0Syt #endif 3802fcbc377Syt 3812fcbc377Syt if ((ret = sata_hba_init(&modlinkage)) != 0) { 382689d74b0Syt #if AHCI_DEBUG 3832fcbc377Syt mutex_destroy(&ahci_log_mutex); 384689d74b0Syt #endif 3852fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3862fcbc377Syt goto err_out; 3872fcbc377Syt } 3882fcbc377Syt 3892fcbc377Syt ret = mod_install(&modlinkage); 3902fcbc377Syt if (ret != 0) { 3912fcbc377Syt sata_hba_fini(&modlinkage); 392689d74b0Syt #if AHCI_DEBUG 3932fcbc377Syt mutex_destroy(&ahci_log_mutex); 394689d74b0Syt #endif 3952fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3962fcbc377Syt goto err_out; 3972fcbc377Syt } 3982fcbc377Syt 3992fcbc377Syt /* watchdog tick */ 4002fcbc377Syt ahci_watchdog_tick = drv_usectohz( 4012fcbc377Syt (clock_t)ahci_watchdog_timeout * 1000000); 4022fcbc377Syt return (ret); 4032fcbc377Syt 4042fcbc377Syt err_out: 405a9440e8dSyt cmn_err(CE_WARN, "!ahci: Module init failed"); 4062fcbc377Syt return (ret); 4072fcbc377Syt } 4082fcbc377Syt 4092fcbc377Syt /* 4102fcbc377Syt * ahci module uninitialize. 4112fcbc377Syt */ 4122fcbc377Syt int 4132fcbc377Syt _fini(void) 4142fcbc377Syt { 4152fcbc377Syt int ret; 4162fcbc377Syt 4172fcbc377Syt ret = mod_remove(&modlinkage); 4182fcbc377Syt if (ret != 0) { 4192fcbc377Syt return (ret); 4202fcbc377Syt } 4212fcbc377Syt 4222fcbc377Syt /* Remove the resources allocated in _init(). */ 4232fcbc377Syt sata_hba_fini(&modlinkage); 424689d74b0Syt #if AHCI_DEBUG 4252fcbc377Syt mutex_destroy(&ahci_log_mutex); 426689d74b0Syt #endif 4272fcbc377Syt ddi_soft_state_fini(&ahci_statep); 4282fcbc377Syt 4292fcbc377Syt return (ret); 4302fcbc377Syt } 4312fcbc377Syt 4322fcbc377Syt /* 4332fcbc377Syt * _info entry point 4342fcbc377Syt */ 4352fcbc377Syt int 4362fcbc377Syt _info(struct modinfo *modinfop) 4372fcbc377Syt { 4382fcbc377Syt return (mod_info(&modlinkage, modinfop)); 4392fcbc377Syt } 4402fcbc377Syt 4412fcbc377Syt /* 4422fcbc377Syt * The attach entry point for dev_ops. 4432fcbc377Syt */ 4442fcbc377Syt static int 4452fcbc377Syt ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4462fcbc377Syt { 447f5f2d263SFred Herard ahci_ctl_t *ahci_ctlp = NULL; 4482fcbc377Syt int instance = ddi_get_instance(dip); 4492fcbc377Syt int status; 4502fcbc377Syt int attach_state; 4512fcbc377Syt uint32_t cap_status, ahci_version; 4522fcbc377Syt int intr_types; 45368d33a25Syt int i; 45495c11c1fSyt pci_regspec_t *regs; 45595c11c1fSyt int regs_length; 45695c11c1fSyt int rnumber; 45768d33a25Syt #if AHCI_DEBUG 45868d33a25Syt int speed; 45968d33a25Syt #endif 4602fcbc377Syt 461f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, "ahci_attach enter", 462f5f2d263SFred Herard NULL); 4632fcbc377Syt 4642fcbc377Syt switch (cmd) { 4652fcbc377Syt case DDI_ATTACH: 4662fcbc377Syt break; 4672fcbc377Syt 4682fcbc377Syt case DDI_RESUME: 46913bcbb7aSyt 47013bcbb7aSyt /* 47113bcbb7aSyt * During DDI_RESUME, the hardware state of the device 47213bcbb7aSyt * (power may have been removed from the device) must be 47313bcbb7aSyt * restored, allow pending requests to continue, and 47413bcbb7aSyt * service new requests. 47513bcbb7aSyt */ 47613bcbb7aSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 47713bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 47813bcbb7aSyt 47913bcbb7aSyt /* Restart watch thread */ 48013bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id == 0) 48113bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = timeout( 48213bcbb7aSyt (void (*)(void *))ahci_watchdog_handler, 48313bcbb7aSyt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 48413bcbb7aSyt 48513bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 48613bcbb7aSyt 48713bcbb7aSyt /* 48813bcbb7aSyt * Re-initialize the controller and enable the interrupts and 48913bcbb7aSyt * restart all the ports. 49013bcbb7aSyt * 49113bcbb7aSyt * Note that so far we don't support hot-plug during 49213bcbb7aSyt * suspend/resume. 49313bcbb7aSyt */ 49413bcbb7aSyt if (ahci_initialize_controller(ahci_ctlp) != AHCI_SUCCESS) { 495f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PM, ahci_ctlp, 496a9440e8dSyt "Failed to initialize the controller " 497f5f2d263SFred Herard "during DDI_RESUME", NULL); 49813bcbb7aSyt return (DDI_FAILURE); 49913bcbb7aSyt } 50013bcbb7aSyt 50113bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 50213bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~ AHCI_SUSPEND; 50313bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 50413bcbb7aSyt 50513bcbb7aSyt return (DDI_SUCCESS); 5062fcbc377Syt 5072fcbc377Syt default: 5082fcbc377Syt return (DDI_FAILURE); 5092fcbc377Syt } 5102fcbc377Syt 5112fcbc377Syt attach_state = AHCI_ATTACH_STATE_NONE; 5122fcbc377Syt 5132fcbc377Syt /* Allocate soft state */ 5142fcbc377Syt status = ddi_soft_state_zalloc(ahci_statep, instance); 5152fcbc377Syt if (status != DDI_SUCCESS) { 516a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate soft state", 517a9440e8dSyt instance); 5182fcbc377Syt goto err_out; 5192fcbc377Syt } 5202fcbc377Syt 5212fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 52213bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_ATTACH; 5232fcbc377Syt ahci_ctlp->ahcictl_dip = dip; 5242fcbc377Syt 52568d33a25Syt /* Initialize the cport/port mapping */ 52668d33a25Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 52768d33a25Syt ahci_ctlp->ahcictl_port_to_cport[i] = 0xff; 52868d33a25Syt ahci_ctlp->ahcictl_cport_to_port[i] = 0xff; 52968d33a25Syt } 53068d33a25Syt 5312fcbc377Syt attach_state |= AHCI_ATTACH_STATE_STATEP_ALLOC; 5322fcbc377Syt 5332fcbc377Syt /* 5342fcbc377Syt * Now map the AHCI base address; which includes global 5352fcbc377Syt * registers and port control registers 53695c11c1fSyt * 53795c11c1fSyt * According to the spec, the AHCI Base Address is BAR5, 53813bcbb7aSyt * but BAR0-BAR4 are optional, so we need to check which 53913bcbb7aSyt * rnumber is used for BAR5. 54095c11c1fSyt */ 54195c11c1fSyt 54295c11c1fSyt /* 54395c11c1fSyt * search through DDI "reg" property for the AHCI register set 5442fcbc377Syt */ 54595c11c1fSyt if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 54695c11c1fSyt DDI_PROP_DONTPASS, "reg", (int **)®s, 54795c11c1fSyt (uint_t *)®s_length) != DDI_PROP_SUCCESS) { 548a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot lookup reg property", 549a9440e8dSyt instance); 55095c11c1fSyt goto err_out; 55195c11c1fSyt } 55295c11c1fSyt 55395c11c1fSyt /* AHCI Base Address is located at 0x24 offset */ 55495c11c1fSyt for (rnumber = 0; rnumber < regs_length; ++rnumber) { 55595c11c1fSyt if ((regs[rnumber].pci_phys_hi & PCI_REG_REG_M) 55695c11c1fSyt == AHCI_PCI_RNUM) 55795c11c1fSyt break; 55895c11c1fSyt } 55995c11c1fSyt 56095c11c1fSyt ddi_prop_free(regs); 56195c11c1fSyt 56295c11c1fSyt if (rnumber == regs_length) { 563a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot find AHCI register set", 564a9440e8dSyt instance); 56595c11c1fSyt goto err_out; 56695c11c1fSyt } 56795c11c1fSyt 568f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "rnumber = %d", rnumber); 56995c11c1fSyt 5702fcbc377Syt status = ddi_regs_map_setup(dip, 57195c11c1fSyt rnumber, 5722fcbc377Syt (caddr_t *)&ahci_ctlp->ahcictl_ahci_addr, 5732fcbc377Syt 0, 5742fcbc377Syt 0, 5752fcbc377Syt &accattr, 5762fcbc377Syt &ahci_ctlp->ahcictl_ahci_acc_handle); 5772fcbc377Syt if (status != DDI_SUCCESS) { 578a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot map register space", 579a9440e8dSyt instance); 5802fcbc377Syt goto err_out; 5812fcbc377Syt } 5822fcbc377Syt 5832fcbc377Syt attach_state |= AHCI_ATTACH_STATE_REG_MAP; 5842fcbc377Syt 5852fcbc377Syt /* Get the AHCI version information */ 5862fcbc377Syt ahci_version = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5872fcbc377Syt (uint32_t *)AHCI_GLOBAL_VS(ahci_ctlp)); 5882fcbc377Syt 589a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: hba AHCI version = %x.%x", instance, 5902fcbc377Syt (ahci_version & 0xffff0000) >> 16, 5912fcbc377Syt ((ahci_version & 0x0000ff00) >> 4 | 5922fcbc377Syt (ahci_version & 0x000000ff))); 5932fcbc377Syt 5942fcbc377Syt /* We don't support controllers whose versions are lower than 1.0 */ 5952fcbc377Syt if (!(ahci_version & 0xffff0000)) { 596a9440e8dSyt cmn_err(CE_WARN, "ahci%d: Don't support AHCI HBA with lower " 597a9440e8dSyt "than version 1.0", instance); 5982fcbc377Syt goto err_out; 5992fcbc377Syt } 6002fcbc377Syt 6012fcbc377Syt /* Get the HBA capabilities information */ 6022fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 6032fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 6042fcbc377Syt 605f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba capabilites = 0x%x", 6062fcbc377Syt cap_status); 6072fcbc377Syt 60868d33a25Syt #if AHCI_DEBUG 60968d33a25Syt /* Get the interface speed supported by the HBA */ 61068d33a25Syt speed = (cap_status & AHCI_HBA_CAP_ISS) >> AHCI_HBA_CAP_ISS_SHIFT; 61168d33a25Syt if (speed == 0x01) { 612f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 613f5f2d263SFred Herard "hba interface speed support: Gen 1 (1.5Gbps)", NULL); 61468d33a25Syt } else if (speed == 0x10) { 615f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 616f5f2d263SFred Herard "hba interface speed support: Gen 2 (3 Gbps)", NULL); 61768d33a25Syt } else if (speed == 0x11) { 618f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 619f5f2d263SFred Herard "hba interface speed support: Gen 3 (6 Gbps)", NULL); 62068d33a25Syt } 62168d33a25Syt #endif 62268d33a25Syt 6232fcbc377Syt /* Get the number of command slots supported by the HBA */ 6242fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots = 6252fcbc377Syt ((cap_status & AHCI_HBA_CAP_NCS) >> 6262fcbc377Syt AHCI_HBA_CAP_NCS_SHIFT) + 1; 6272fcbc377Syt 628f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba number of cmd slots: %d", 6292fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots); 6302fcbc377Syt 6312fcbc377Syt /* Get the bit map which indicates ports implemented by the HBA */ 6322fcbc377Syt ahci_ctlp->ahcictl_ports_implemented = 6332fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 6342fcbc377Syt (uint32_t *)AHCI_GLOBAL_PI(ahci_ctlp)); 6352fcbc377Syt 636f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba implementation of ports: 0x%x", 6372fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 6382fcbc377Syt 63909121340Syt /* 64009121340Syt * According to the AHCI spec, CAP.NP should indicate the maximum 64109121340Syt * number of ports supported by the HBA silicon, but we found 64209121340Syt * this value of ICH8 chipset only indicates the number of ports 64309121340Syt * implemented (exposed) by it. Therefore, the driver should calculate 64409121340Syt * the potential maximum value by checking PI register, and use 64509121340Syt * the maximum of this value and CAP.NP. 64609121340Syt */ 64709121340Syt ahci_ctlp->ahcictl_num_ports = max( 64809121340Syt (cap_status & AHCI_HBA_CAP_NP) + 1, 64909121340Syt ddi_fls(ahci_ctlp->ahcictl_ports_implemented)); 65009121340Syt 651f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba number of ports: %d", 65209121340Syt ahci_ctlp->ahcictl_num_ports); 65309121340Syt 6542fcbc377Syt /* Get the number of implemented ports by the HBA */ 6552fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports = 6562fcbc377Syt ahci_get_num_implemented_ports( 6572fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 6582fcbc377Syt 659f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 66068d33a25Syt "hba number of implemented ports: %d", 6612fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports); 6622fcbc377Syt 663a9440e8dSyt /* Check whether HBA supports 64bit DMA addressing */ 6642fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_S64A)) { 665259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_BUF_32BIT_DMA; 666259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_COMMU_32BIT_DMA; 667f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 668f5f2d263SFred Herard "hba does not support 64-bit addressing", NULL); 6692fcbc377Syt } 6702fcbc377Syt 6710a4c4cecSXiao-Yu Zhang /* Checking for Support Command List Override */ 6720a4c4cecSXiao-Yu Zhang if (cap_status & AHCI_HBA_CAP_SCLO) { 6730a4c4cecSXiao-Yu Zhang ahci_ctlp->ahcictl_cap |= AHCI_CAP_SCLO; 674f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 675f5f2d263SFred Herard "hba supports command list override.", NULL); 6760a4c4cecSXiao-Yu Zhang } 6770a4c4cecSXiao-Yu Zhang 6782fcbc377Syt if (pci_config_setup(dip, &ahci_ctlp->ahcictl_pci_conf_handle) 6792fcbc377Syt != DDI_SUCCESS) { 680a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot set up pci configure space", 681a9440e8dSyt instance); 6822fcbc377Syt goto err_out; 6832fcbc377Syt } 6842fcbc377Syt 68568d33a25Syt attach_state |= AHCI_ATTACH_STATE_PCICFG_SETUP; 68668d33a25Syt 687db2cce03Sying tian - Beijing China /* 688db2cce03Sying tian - Beijing China * Check the pci configuration space, and set caps. We also 689db2cce03Sying tian - Beijing China * handle the hardware defect in this function. 690db2cce03Sying tian - Beijing China * 691259105bcSying tian - Beijing China * For example, force ATI SB600 to use 32-bit dma addressing 692259105bcSying tian - Beijing China * since it doesn't support 64-bit dma though its CAP register 693259105bcSying tian - Beijing China * declares it support. 694db2cce03Sying tian - Beijing China */ 69513bcbb7aSyt if (ahci_config_space_init(ahci_ctlp) == AHCI_FAILURE) { 696a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_config_space_init failed", 697a9440e8dSyt instance); 69813bcbb7aSyt goto err_out; 69968d33a25Syt } 7002fcbc377Syt 7012fcbc377Syt /* 7022fcbc377Syt * Disable the whole controller interrupts before adding 7032fcbc377Syt * interrupt handlers(s). 7042fcbc377Syt */ 7052fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 7062fcbc377Syt 7072fcbc377Syt /* Get supported interrupt types */ 7082fcbc377Syt if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { 709a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ddi_intr_get_supported_types failed", 710a9440e8dSyt instance); 7112fcbc377Syt goto err_out; 7122fcbc377Syt } 7132fcbc377Syt 714f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7152fcbc377Syt "ddi_intr_get_supported_types() returned: 0x%x", 7162fcbc377Syt intr_types); 7172fcbc377Syt 7182fcbc377Syt if (ahci_msi_enabled && (intr_types & DDI_INTR_TYPE_MSI)) { 7192fcbc377Syt /* 7202fcbc377Syt * Try MSI first, but fall back to FIXED if failed 7212fcbc377Syt */ 7222c742e1fSying tian - Beijing China if (ahci_add_intrs(ahci_ctlp, DDI_INTR_TYPE_MSI) == 7232c742e1fSying tian - Beijing China DDI_SUCCESS) { 7242fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_MSI; 725f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 726f5f2d263SFred Herard "Using MSI interrupt type", NULL); 7272fcbc377Syt goto intr_done; 7282fcbc377Syt } 7292fcbc377Syt 730f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7312fcbc377Syt "MSI registration failed, " 732f5f2d263SFred Herard "trying FIXED interrupts", NULL); 7332fcbc377Syt } 7342fcbc377Syt 7352fcbc377Syt if (intr_types & DDI_INTR_TYPE_FIXED) { 7362c742e1fSying tian - Beijing China if (ahci_add_intrs(ahci_ctlp, DDI_INTR_TYPE_FIXED) == 7372c742e1fSying tian - Beijing China DDI_SUCCESS) { 7382fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_FIXED; 739f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 740f5f2d263SFred Herard "Using FIXED interrupt type", NULL); 7412fcbc377Syt goto intr_done; 7422fcbc377Syt } 7432fcbc377Syt 744f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 745f5f2d263SFred Herard "FIXED interrupt registration failed", NULL); 7462fcbc377Syt } 7472fcbc377Syt 748a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Interrupt registration failed", instance); 7492fcbc377Syt 7502fcbc377Syt goto err_out; 7512fcbc377Syt 7522fcbc377Syt intr_done: 7532fcbc377Syt 7542fcbc377Syt attach_state |= AHCI_ATTACH_STATE_INTR_ADDED; 7552fcbc377Syt 7562fcbc377Syt /* Initialize the controller mutex */ 7572fcbc377Syt mutex_init(&ahci_ctlp->ahcictl_mutex, NULL, MUTEX_DRIVER, 7582fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 7592fcbc377Syt 7602fcbc377Syt attach_state |= AHCI_ATTACH_STATE_MUTEX_INIT; 7612fcbc377Syt 7622fcbc377Syt if (ahci_dma_prdt_number < AHCI_MIN_PRDT_NUMBER) { 7632fcbc377Syt ahci_dma_prdt_number = AHCI_MIN_PRDT_NUMBER; 7642fcbc377Syt } else if (ahci_dma_prdt_number > AHCI_MAX_PRDT_NUMBER) { 7652fcbc377Syt ahci_dma_prdt_number = AHCI_MAX_PRDT_NUMBER; 7662fcbc377Syt } 7672fcbc377Syt 7682fcbc377Syt ahci_cmd_table_size = (sizeof (ahci_cmd_table_t) + 7692fcbc377Syt (ahci_dma_prdt_number - AHCI_PRDT_NUMBER) * 7702fcbc377Syt sizeof (ahci_prdt_item_t)); 7712fcbc377Syt 772f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 7732fcbc377Syt "ahci_attach: ahci_dma_prdt_number set by user is 0x%x," 7742fcbc377Syt " ahci_cmd_table_size is 0x%x", 7752fcbc377Syt ahci_dma_prdt_number, ahci_cmd_table_size); 7762fcbc377Syt 7772fcbc377Syt if (ahci_dma_prdt_number != AHCI_PRDT_NUMBER) 7782fcbc377Syt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_sgllen = 7792fcbc377Syt ahci_dma_prdt_number; 7802fcbc377Syt 781a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr = buffer_dma_attr; 782a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr = rcvd_fis_dma_attr; 783a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr = cmd_list_dma_attr; 784a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr = cmd_table_dma_attr; 785a9440e8dSyt 786259105bcSying tian - Beijing China if ((ahci_buf_64bit_dma == B_FALSE) || 787259105bcSying tian - Beijing China (ahci_ctlp->ahcictl_cap & AHCI_CAP_BUF_32BIT_DMA)) { 788a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_addr_hi = 789a9440e8dSyt 0xffffffffull; 790259105bcSying tian - Beijing China } 791259105bcSying tian - Beijing China 792259105bcSying tian - Beijing China if ((ahci_commu_64bit_dma == B_FALSE) || 793259105bcSying tian - Beijing China (ahci_ctlp->ahcictl_cap & AHCI_CAP_COMMU_32BIT_DMA)) { 794a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr.dma_attr_addr_hi = 795a9440e8dSyt 0xffffffffull; 796a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr.dma_attr_addr_hi = 797a9440e8dSyt 0xffffffffull; 798a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr.dma_attr_addr_hi = 799a9440e8dSyt 0xffffffffull; 800a9440e8dSyt } 801a9440e8dSyt 80268d33a25Syt /* Allocate the ports structure */ 80368d33a25Syt status = ahci_alloc_ports_state(ahci_ctlp); 80468d33a25Syt if (status != AHCI_SUCCESS) { 805a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate ports structure", 806a9440e8dSyt instance); 80768d33a25Syt goto err_out; 80868d33a25Syt } 80968d33a25Syt 81068d33a25Syt attach_state |= AHCI_ATTACH_STATE_PORT_ALLOC; 81168d33a25Syt 8122fcbc377Syt /* 81368d33a25Syt * Initialize the controller and ports. 8142fcbc377Syt */ 8152fcbc377Syt status = ahci_initialize_controller(ahci_ctlp); 8162fcbc377Syt if (status != AHCI_SUCCESS) { 817a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: HBA initialization failed", 818a9440e8dSyt instance); 8192fcbc377Syt goto err_out; 8202fcbc377Syt } 8212fcbc377Syt 8222fcbc377Syt attach_state |= AHCI_ATTACH_STATE_HW_INIT; 8232fcbc377Syt 8242fcbc377Syt /* Start one thread to check packet timeouts */ 8252fcbc377Syt ahci_ctlp->ahcictl_timeout_id = timeout( 8262fcbc377Syt (void (*)(void *))ahci_watchdog_handler, 8272fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 8282fcbc377Syt 8292fcbc377Syt attach_state |= AHCI_ATTACH_STATE_TIMEOUT_ENABLED; 8302fcbc377Syt 8312fcbc377Syt if (ahci_register_sata_hba_tran(ahci_ctlp, cap_status)) { 832a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: sata hba tran registration failed", 833a9440e8dSyt instance); 8342fcbc377Syt goto err_out; 8352fcbc377Syt } 8362fcbc377Syt 83713bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~AHCI_ATTACH; 83813bcbb7aSyt 839f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "ahci_attach success!", NULL); 8402fcbc377Syt 8412fcbc377Syt return (DDI_SUCCESS); 8422fcbc377Syt 8432fcbc377Syt err_out: 8442fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_TIMEOUT_ENABLED) { 8452fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8462fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 8472fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 8482fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 8492fcbc377Syt } 8502fcbc377Syt 8512fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_HW_INIT) { 85268d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 8532fcbc377Syt } 8542fcbc377Syt 85568d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PORT_ALLOC) { 85668d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 85768d33a25Syt } 85868d33a25Syt 8592fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_MUTEX_INIT) { 8602fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 8612fcbc377Syt } 8622fcbc377Syt 8632fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_INTR_ADDED) { 8642fcbc377Syt ahci_rem_intrs(ahci_ctlp); 8652fcbc377Syt } 8662fcbc377Syt 86768d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PCICFG_SETUP) { 86868d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 86968d33a25Syt } 87068d33a25Syt 8712fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_REG_MAP) { 8722fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 8732fcbc377Syt } 8742fcbc377Syt 8752fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_STATEP_ALLOC) { 8762fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 8772fcbc377Syt } 8782fcbc377Syt 8792fcbc377Syt return (DDI_FAILURE); 8802fcbc377Syt } 8812fcbc377Syt 8822fcbc377Syt /* 8832fcbc377Syt * The detach entry point for dev_ops. 8842fcbc377Syt */ 8852fcbc377Syt static int 8862fcbc377Syt ahci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 8872fcbc377Syt { 8882fcbc377Syt ahci_ctl_t *ahci_ctlp; 8892fcbc377Syt int instance; 8902fcbc377Syt int ret; 8912fcbc377Syt 8922fcbc377Syt instance = ddi_get_instance(dip); 8932fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 8942fcbc377Syt 895f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_detach enter", NULL); 8962fcbc377Syt 8972fcbc377Syt switch (cmd) { 8982fcbc377Syt case DDI_DETACH: 89913bcbb7aSyt 9002fcbc377Syt /* disable the interrupts for an uninterrupted detach */ 9012fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9022fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 9032fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 9042fcbc377Syt 9052fcbc377Syt /* unregister from the sata framework. */ 9062fcbc377Syt ret = ahci_unregister_sata_hba_tran(ahci_ctlp); 9072fcbc377Syt if (ret != AHCI_SUCCESS) { 9082fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9092fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 9102fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 9112fcbc377Syt return (DDI_FAILURE); 9122fcbc377Syt } 9132fcbc377Syt 9142fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9152fcbc377Syt 9162fcbc377Syt /* stop the watchdog handler */ 9172fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 9182fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 9192fcbc377Syt 92068d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 92168d33a25Syt 92268d33a25Syt /* uninitialize the controller */ 92368d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 9242fcbc377Syt 92568d33a25Syt /* remove the interrupts */ 92668d33a25Syt ahci_rem_intrs(ahci_ctlp); 9272fcbc377Syt 92868d33a25Syt /* deallocate the ports structures */ 92968d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 93068d33a25Syt 93168d33a25Syt /* destroy mutex */ 9322fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 9332fcbc377Syt 93468d33a25Syt /* teardown the pci config */ 93568d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 9362fcbc377Syt 9372fcbc377Syt /* remove the reg maps. */ 9382fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 9392fcbc377Syt 9402fcbc377Syt /* free the soft state. */ 9412fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 9422fcbc377Syt 9432fcbc377Syt return (DDI_SUCCESS); 9442fcbc377Syt 9452fcbc377Syt case DDI_SUSPEND: 94613bcbb7aSyt 94713bcbb7aSyt /* 94813bcbb7aSyt * The steps associated with suspension must include putting 94913bcbb7aSyt * the underlying device into a quiescent state so that it 95013bcbb7aSyt * will not generate interrupts or modify or access memory. 95113bcbb7aSyt */ 95213bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 95313bcbb7aSyt if (ahci_ctlp->ahcictl_flags & AHCI_SUSPEND) { 95413bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 95513bcbb7aSyt return (DDI_SUCCESS); 95613bcbb7aSyt } 95713bcbb7aSyt 95813bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_SUSPEND; 95913bcbb7aSyt 96013bcbb7aSyt /* stop the watchdog handler */ 96113bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id) { 96213bcbb7aSyt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 96313bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = 0; 96413bcbb7aSyt } 96513bcbb7aSyt 96613bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 96713bcbb7aSyt 96813bcbb7aSyt /* 96913bcbb7aSyt * drain the taskq 97013bcbb7aSyt */ 971f8a673adSying tian - Beijing China ahci_drain_ports_taskq(ahci_ctlp); 97213bcbb7aSyt 97313bcbb7aSyt /* 97413bcbb7aSyt * Disable the interrupts and stop all the ports. 97513bcbb7aSyt */ 97613bcbb7aSyt ahci_uninitialize_controller(ahci_ctlp); 97713bcbb7aSyt 97813bcbb7aSyt return (DDI_SUCCESS); 9792fcbc377Syt 9802fcbc377Syt default: 9812fcbc377Syt return (DDI_FAILURE); 9822fcbc377Syt } 9832fcbc377Syt } 9842fcbc377Syt 9852fcbc377Syt /* 9862fcbc377Syt * The info entry point for dev_ops. 9872fcbc377Syt * 9882fcbc377Syt */ 9892fcbc377Syt static int 9902fcbc377Syt ahci_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 9912fcbc377Syt void *arg, void **result) 9922fcbc377Syt { 9932fcbc377Syt #ifndef __lock_lint 9942fcbc377Syt _NOTE(ARGUNUSED(dip)) 9952fcbc377Syt #endif /* __lock_lint */ 9962fcbc377Syt 9972fcbc377Syt ahci_ctl_t *ahci_ctlp; 9982fcbc377Syt int instance; 9992fcbc377Syt dev_t dev; 10002fcbc377Syt 10012fcbc377Syt dev = (dev_t)arg; 10022fcbc377Syt instance = getminor(dev); 10032fcbc377Syt 10042fcbc377Syt switch (infocmd) { 10052fcbc377Syt case DDI_INFO_DEVT2DEVINFO: 10062fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 10072fcbc377Syt if (ahci_ctlp != NULL) { 10082fcbc377Syt *result = ahci_ctlp->ahcictl_dip; 10092fcbc377Syt return (DDI_SUCCESS); 10102fcbc377Syt } else { 10112fcbc377Syt *result = NULL; 10122fcbc377Syt return (DDI_FAILURE); 10132fcbc377Syt } 10142fcbc377Syt case DDI_INFO_DEVT2INSTANCE: 10152fcbc377Syt *(int *)result = instance; 10162fcbc377Syt break; 10172fcbc377Syt default: 10182fcbc377Syt break; 10192fcbc377Syt } 10202fcbc377Syt 10212fcbc377Syt return (DDI_SUCCESS); 10222fcbc377Syt } 10232fcbc377Syt 10242fcbc377Syt /* 10252fcbc377Syt * Registers the ahci with sata framework. 10262fcbc377Syt */ 10272fcbc377Syt static int 10282fcbc377Syt ahci_register_sata_hba_tran(ahci_ctl_t *ahci_ctlp, uint32_t cap_status) 10292fcbc377Syt { 10302fcbc377Syt struct sata_hba_tran *sata_hba_tran; 10312fcbc377Syt 1032f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 1033f5f2d263SFred Herard "ahci_register_sata_hba_tran enter", NULL); 10342fcbc377Syt 10352fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 10362fcbc377Syt 10372fcbc377Syt /* Allocate memory for the sata_hba_tran */ 10382fcbc377Syt sata_hba_tran = kmem_zalloc(sizeof (sata_hba_tran_t), KM_SLEEP); 10392fcbc377Syt 104068d33a25Syt sata_hba_tran->sata_tran_hba_rev = SATA_TRAN_HBA_REV_2; 10412fcbc377Syt sata_hba_tran->sata_tran_hba_dip = ahci_ctlp->ahcictl_dip; 10422fcbc377Syt sata_hba_tran->sata_tran_hba_dma_attr = 10432fcbc377Syt &ahci_ctlp->ahcictl_buffer_dma_attr; 10442fcbc377Syt 10452fcbc377Syt /* Report the number of implemented ports */ 10462fcbc377Syt sata_hba_tran->sata_tran_hba_num_cports = 10472fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports; 10482fcbc377Syt 104968d33a25Syt /* Support ATAPI device */ 105068d33a25Syt sata_hba_tran->sata_tran_hba_features_support = SATA_CTLF_ATAPI; 10512fcbc377Syt 10522fcbc377Syt /* Get the data transfer capability for PIO command by the HBA */ 10532fcbc377Syt if (cap_status & AHCI_HBA_CAP_PMD) { 105482263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PIO_MDRQ; 1055f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "HBA supports multiple " 1056f5f2d263SFred Herard "DRQ block data transfer for PIO command protocol", NULL); 10572fcbc377Syt } 10582fcbc377Syt 105982263d52Syt /* 106082263d52Syt * According to the AHCI spec, the ATA/ATAPI-7 queued feature set 106182263d52Syt * is not supported by AHCI (including the READ QUEUED (EXT), WRITE 106282263d52Syt * QUEUED (EXT), and SERVICE commands). Queued operations are 106382263d52Syt * supported in AHCI using the READ FPDMA QUEUED and WRITE FPDMA 106482263d52Syt * QUEUED commands when the HBA and device support native command 106582263d52Syt * queuing(NCQ). 106682263d52Syt * 106782263d52Syt * SATA_CTLF_NCQ will be set to sata_tran_hba_features_support if the 106882263d52Syt * CAP register of the HBA indicates NCQ is supported. 106982263d52Syt * 107082263d52Syt * SATA_CTLF_NCQ cannot be set if AHCI_CAP_NO_MCMDLIST_NONQUEUE is 107182263d52Syt * set because the previous register content of PxCI can be re-written 107282263d52Syt * in the register write. 107382263d52Syt */ 107482263d52Syt if ((cap_status & AHCI_HBA_CAP_SNCQ) && 107582263d52Syt !(ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE)) { 107682263d52Syt sata_hba_tran->sata_tran_hba_features_support |= SATA_CTLF_NCQ; 107782263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_NCQ; 1078f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "HBA supports Native " 1079f5f2d263SFred Herard "Command Queuing", NULL); 108082263d52Syt } 108182263d52Syt 10822fcbc377Syt /* Report the number of command slots */ 10832fcbc377Syt sata_hba_tran->sata_tran_hba_qdepth = ahci_ctlp->ahcictl_num_cmd_slots; 10842fcbc377Syt 10852fcbc377Syt sata_hba_tran->sata_tran_probe_port = ahci_tran_probe_port; 10862fcbc377Syt sata_hba_tran->sata_tran_start = ahci_tran_start; 10872fcbc377Syt sata_hba_tran->sata_tran_abort = ahci_tran_abort; 10882fcbc377Syt sata_hba_tran->sata_tran_reset_dport = ahci_tran_reset_dport; 10892fcbc377Syt sata_hba_tran->sata_tran_hotplug_ops = &ahci_tran_hotplug_ops; 10902fcbc377Syt #ifdef __lock_lint 10912fcbc377Syt sata_hba_tran->sata_tran_selftest = ahci_selftest; 10922fcbc377Syt #endif 10932fcbc377Syt /* 10942fcbc377Syt * When SATA framework adds support for pwrmgt the 10952fcbc377Syt * pwrmgt_ops needs to be updated 10962fcbc377Syt */ 10972fcbc377Syt sata_hba_tran->sata_tran_pwrmgt_ops = NULL; 10982fcbc377Syt sata_hba_tran->sata_tran_ioctl = NULL; 10992fcbc377Syt 11002fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = sata_hba_tran; 11012fcbc377Syt 11022fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 11032fcbc377Syt 11042fcbc377Syt /* Attach it to SATA framework */ 11052fcbc377Syt if (sata_hba_attach(ahci_ctlp->ahcictl_dip, sata_hba_tran, DDI_ATTACH) 11062fcbc377Syt != DDI_SUCCESS) { 11072fcbc377Syt kmem_free((void *)sata_hba_tran, sizeof (sata_hba_tran_t)); 11082fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 11092fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 11102fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 11112fcbc377Syt return (AHCI_FAILURE); 11122fcbc377Syt } 11132fcbc377Syt 11142fcbc377Syt return (AHCI_SUCCESS); 11152fcbc377Syt } 11162fcbc377Syt 11172fcbc377Syt /* 11182fcbc377Syt * Unregisters the ahci with sata framework. 11192fcbc377Syt */ 11202fcbc377Syt static int 11212fcbc377Syt ahci_unregister_sata_hba_tran(ahci_ctl_t *ahci_ctlp) 11222fcbc377Syt { 1123f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 1124f5f2d263SFred Herard "ahci_unregister_sata_hba_tran enter", NULL); 11252fcbc377Syt 11262fcbc377Syt /* Detach from the SATA framework. */ 11272fcbc377Syt if (sata_hba_detach(ahci_ctlp->ahcictl_dip, DDI_DETACH) != 11282fcbc377Syt DDI_SUCCESS) { 11292fcbc377Syt return (AHCI_FAILURE); 11302fcbc377Syt } 11312fcbc377Syt 11322fcbc377Syt /* Deallocate sata_hba_tran. */ 11332fcbc377Syt kmem_free((void *)ahci_ctlp->ahcictl_sata_hba_tran, 11342fcbc377Syt sizeof (sata_hba_tran_t)); 11352fcbc377Syt 11362fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 11372fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 11382fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 11392fcbc377Syt 11402fcbc377Syt return (AHCI_SUCCESS); 11412fcbc377Syt } 11422fcbc377Syt 11432fcbc377Syt /* 11442fcbc377Syt * ahci_tran_probe_port is called by SATA framework. It returns port state, 11452fcbc377Syt * port status registers and an attached device type via sata_device 11462fcbc377Syt * structure. 11472fcbc377Syt * 11482fcbc377Syt * We return the cached information from a previous hardware probe. The 11492fcbc377Syt * actual hardware probing itself was done either from within 11502fcbc377Syt * ahci_initialize_controller() during the driver attach or from a phy 11512fcbc377Syt * ready change interrupt handler. 11522fcbc377Syt */ 11532fcbc377Syt static int 11542fcbc377Syt ahci_tran_probe_port(dev_info_t *dip, sata_device_t *sd) 11552fcbc377Syt { 11562fcbc377Syt ahci_ctl_t *ahci_ctlp; 11572fcbc377Syt ahci_port_t *ahci_portp; 11582fcbc377Syt uint8_t cport = sd->satadev_addr.cport; 11592fcbc377Syt uint8_t pmport = sd->satadev_addr.pmport; 11602fcbc377Syt uint8_t qual = sd->satadev_addr.qual; 11612fcbc377Syt uint8_t device_type; 11622fcbc377Syt uint32_t port_state; 11632fcbc377Syt uint8_t port; 11642fcbc377Syt 11652fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 11662fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 11672fcbc377Syt 1168f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 116968d33a25Syt "ahci_tran_probe_port enter: cport: %d, " 117068d33a25Syt "pmport: %d, qual: %d", cport, pmport, qual); 11712fcbc377Syt 11722fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 11732fcbc377Syt 11742fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 11752fcbc377Syt 11762fcbc377Syt port_state = ahci_portp->ahciport_port_state; 11772fcbc377Syt switch (port_state) { 11782fcbc377Syt 11792fcbc377Syt case SATA_PSTATE_FAILED: 11802fcbc377Syt sd->satadev_state = SATA_PSTATE_FAILED; 1181f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 118268d33a25Syt "ahci_tran_probe_port: port %d PORT FAILED", port); 11832fcbc377Syt goto out; 11842fcbc377Syt 11852fcbc377Syt case SATA_PSTATE_SHUTDOWN: 11862fcbc377Syt sd->satadev_state = SATA_PSTATE_SHUTDOWN; 1187f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 118868d33a25Syt "ahci_tran_probe_port: port %d PORT SHUTDOWN", port); 11892fcbc377Syt goto out; 11902fcbc377Syt 11912fcbc377Syt case SATA_PSTATE_PWROFF: 11922fcbc377Syt sd->satadev_state = SATA_PSTATE_PWROFF; 1193f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 119468d33a25Syt "ahci_tran_probe_port: port %d PORT PWROFF", port); 11952fcbc377Syt goto out; 11962fcbc377Syt 11972fcbc377Syt case SATA_PSTATE_PWRON: 11982fcbc377Syt sd->satadev_state = SATA_PSTATE_PWRON; 1199f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 120068d33a25Syt "ahci_tran_probe_port: port %d PORT PWRON", port); 12012fcbc377Syt break; 12022fcbc377Syt 12032fcbc377Syt default: 12042fcbc377Syt sd->satadev_state = port_state; 1205f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 120668d33a25Syt "ahci_tran_probe_port: port %d PORT NORMAL %x", 12072fcbc377Syt port, port_state); 12082fcbc377Syt break; 12092fcbc377Syt } 12102fcbc377Syt 12112fcbc377Syt device_type = ahci_portp->ahciport_device_type; 12122fcbc377Syt 12132fcbc377Syt switch (device_type) { 12142fcbc377Syt 12152fcbc377Syt case SATA_DTYPE_ATADISK: 12162fcbc377Syt sd->satadev_type = SATA_DTYPE_ATADISK; 1217f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 121868d33a25Syt "ahci_tran_probe_port: port %d DISK found", port); 12192fcbc377Syt break; 12202fcbc377Syt 122138547057Sying tian - Beijing China case SATA_DTYPE_ATAPI: 122238547057Sying tian - Beijing China /* 122338547057Sying tian - Beijing China * HBA driver only knows it's an ATAPI device, and don't know 1224f8a673adSying tian - Beijing China * it's CD/DVD, tape or ATAPI disk because the ATAPI device 1225f8a673adSying tian - Beijing China * type need to be determined by checking IDENTIFY PACKET 1226f8a673adSying tian - Beijing China * DEVICE data 122738547057Sying tian - Beijing China */ 122838547057Sying tian - Beijing China sd->satadev_type = SATA_DTYPE_ATAPI; 1229f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 123068d33a25Syt "ahci_tran_probe_port: port %d ATAPI found", port); 12312fcbc377Syt break; 12322fcbc377Syt 12332fcbc377Syt case SATA_DTYPE_PMULT: 12342fcbc377Syt sd->satadev_type = SATA_DTYPE_PMULT; 1235f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 123668d33a25Syt "ahci_tran_probe_port: port %d Port Multiplier found", 12372fcbc377Syt port); 12382fcbc377Syt break; 12392fcbc377Syt 12402fcbc377Syt case SATA_DTYPE_UNKNOWN: 12412fcbc377Syt sd->satadev_type = SATA_DTYPE_UNKNOWN; 1242f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 124368d33a25Syt "ahci_tran_probe_port: port %d Unknown device found", port); 12442fcbc377Syt break; 12452fcbc377Syt 12462fcbc377Syt default: 12472fcbc377Syt /* we don't support any other device types */ 12482fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 1249f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 125068d33a25Syt "ahci_tran_probe_port: port %d No device found", port); 12512fcbc377Syt break; 12522fcbc377Syt } 12532fcbc377Syt 12542fcbc377Syt out: 12552fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 12562fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 12572fcbc377Syt 12582fcbc377Syt return (SATA_SUCCESS); 12592fcbc377Syt } 12602fcbc377Syt 126168d33a25Syt /* 126268d33a25Syt * There are four operation modes in sata framework: 126368d33a25Syt * SATA_OPMODE_INTERRUPTS 126468d33a25Syt * SATA_OPMODE_POLLING 126568d33a25Syt * SATA_OPMODE_ASYNCH 126668d33a25Syt * SATA_OPMODE_SYNCH 126768d33a25Syt * 126868d33a25Syt * Their combined meanings as following: 126968d33a25Syt * 127068d33a25Syt * SATA_OPMODE_SYNCH 127168d33a25Syt * The command has to be completed before sata_tran_start functions returns. 127268d33a25Syt * Either interrupts or polling could be used - it's up to the driver. 127368d33a25Syt * Mode used currently for internal, sata-module initiated operations. 127468d33a25Syt * 127568d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS 127668d33a25Syt * It is the same as the one above. 127768d33a25Syt * 127868d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING 127968d33a25Syt * The command has to be completed before sata_tran_start function returns. 128068d33a25Syt * No interrupt used, polling only. This should be the mode used for scsi 128168d33a25Syt * packets with FLAG_NOINTR. 128268d33a25Syt * 128368d33a25Syt * SATA_OPMODE_ASYNCH | SATA_OPMODE_INTERRUPTS 128468d33a25Syt * The command may be queued (callback function specified). Interrupts could 128568d33a25Syt * be used. It's normal operation mode. 128668d33a25Syt */ 12872fcbc377Syt /* 12882fcbc377Syt * Called by sata framework to transport a sata packet down stream. 12892fcbc377Syt */ 12902fcbc377Syt static int 12912fcbc377Syt ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt) 12922fcbc377Syt { 12932fcbc377Syt ahci_ctl_t *ahci_ctlp; 12942fcbc377Syt ahci_port_t *ahci_portp; 12952fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 12962fcbc377Syt uint8_t port; 12972fcbc377Syt 12982fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 12992fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 13002fcbc377Syt 1301f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 13022fcbc377Syt "ahci_tran_start enter: cport %d satapkt 0x%p", 13032fcbc377Syt cport, (void *)spkt); 13042fcbc377Syt 13052fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 13062fcbc377Syt 13072fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 13082fcbc377Syt 13092fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 13102fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 13112fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 13122fcbc377Syt /* 13132fcbc377Syt * In case the targer driver would send the packet before 13142fcbc377Syt * sata framework can have the opportunity to process those 13152fcbc377Syt * event reports. 13162fcbc377Syt */ 13172fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 13182fcbc377Syt spkt->satapkt_device.satadev_state = 13192fcbc377Syt ahci_portp->ahciport_port_state; 13202fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 13212fcbc377Syt &spkt->satapkt_device); 1322f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 13232fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 13242fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 132568d33a25Syt "port: %d", port); 13262fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13272fcbc377Syt return (SATA_TRAN_PORT_ERROR); 13282fcbc377Syt } 13292fcbc377Syt 13302fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 13312fcbc377Syt /* 13322fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 13332fcbc377Syt * SATA_DTYPE_NONE. 13342fcbc377Syt */ 13352fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 13362fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 13372fcbc377Syt spkt->satapkt_device.satadev_state = 13382fcbc377Syt ahci_portp->ahciport_port_state; 13392fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 13402fcbc377Syt &spkt->satapkt_device); 1341f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 13422fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 134368d33a25Syt "no device attached: port: %d", port); 13442fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13452fcbc377Syt return (SATA_TRAN_PORT_ERROR); 13462fcbc377Syt } 13472fcbc377Syt 13482fcbc377Syt /* 13492fcbc377Syt * SATA HBA driver should remember that a device was reset and it 13502fcbc377Syt * is supposed to reject any packets which do not specify either 13512fcbc377Syt * SATA_IGNORE_DEV_RESET_STATE or SATA_CLEAR_DEV_RESET_STATE. 13522fcbc377Syt * 13532fcbc377Syt * This is to prevent a race condition when a device was arbitrarily 13542fcbc377Syt * reset by the HBA driver (and lost it's setting) and a target 13552fcbc377Syt * driver sending some commands to a device before the sata framework 13562fcbc377Syt * has a chance to restore the device setting (such as cache enable/ 13572fcbc377Syt * disable or other resettable stuff). 13582fcbc377Syt */ 13592fcbc377Syt if (spkt->satapkt_cmd.satacmd_flags.sata_clear_dev_reset) { 13602fcbc377Syt ahci_portp->ahciport_reset_in_progress = 0; 1361f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 13622fcbc377Syt "ahci_tran_start clearing the " 136368d33a25Syt "reset_in_progress for port: %d", port); 13642fcbc377Syt } 13652fcbc377Syt 13662fcbc377Syt if (ahci_portp->ahciport_reset_in_progress && 13672fcbc377Syt ! spkt->satapkt_cmd.satacmd_flags.sata_ignore_dev_reset && 13682fcbc377Syt ! ddi_in_panic()) { 13692fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 1370f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 13712fcbc377Syt "ahci_tran_start returning BUSY while " 137268d33a25Syt "reset in progress: port: %d", port); 13732fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13742fcbc377Syt return (SATA_TRAN_BUSY); 13752fcbc377Syt } 13762fcbc377Syt 137768d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 13782fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 1379f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 13802fcbc377Syt "ahci_tran_start returning BUSY while " 138168d33a25Syt "mopping in progress: port: %d", port); 13822fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13832fcbc377Syt return (SATA_TRAN_BUSY); 13842fcbc377Syt } 13852fcbc377Syt 13862fcbc377Syt if (spkt->satapkt_op_mode & 138768d33a25Syt (SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING)) { 1388b2e3645aSying tian - Beijing China /* 1389b2e3645aSying tian - Beijing China * If a SYNC command to be executed in interrupt context, 1390b2e3645aSying tian - Beijing China * bounce it back to sata module. 1391b2e3645aSying tian - Beijing China */ 1392b2e3645aSying tian - Beijing China if (!(spkt->satapkt_op_mode & SATA_OPMODE_POLLING) && 1393b2e3645aSying tian - Beijing China servicing_interrupt()) { 1394b2e3645aSying tian - Beijing China spkt->satapkt_reason = SATA_PKT_BUSY; 1395f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 1396b2e3645aSying tian - Beijing China "ahci_tran_start returning BUSY while " 1397b2e3645aSying tian - Beijing China "sending SYNC mode under interrupt context: " 1398b2e3645aSying tian - Beijing China "port : %d", port); 1399b2e3645aSying tian - Beijing China mutex_exit(&ahci_portp->ahciport_mutex); 1400b2e3645aSying tian - Beijing China return (SATA_TRAN_BUSY); 1401b2e3645aSying tian - Beijing China } 1402b2e3645aSying tian - Beijing China 140368d33a25Syt /* We need to do the sync start now */ 140468d33a25Syt if (ahci_do_sync_start(ahci_ctlp, ahci_portp, port, 140568d33a25Syt spkt) == AHCI_FAILURE) { 1406f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 140768d33a25Syt "return QUEUE_FULL: port %d", port); 140868d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 140968d33a25Syt return (SATA_TRAN_QUEUE_FULL); 141068d33a25Syt } 141168d33a25Syt } else { 141268d33a25Syt /* Async start, using interrupt */ 141368d33a25Syt if (ahci_deliver_satapkt(ahci_ctlp, ahci_portp, port, spkt) 141468d33a25Syt == AHCI_FAILURE) { 141568d33a25Syt spkt->satapkt_reason = SATA_PKT_QUEUE_FULL; 1416f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 141768d33a25Syt "returning QUEUE_FULL: port %d", port); 141868d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 141968d33a25Syt return (SATA_TRAN_QUEUE_FULL); 142068d33a25Syt } 14212fcbc377Syt } 14222fcbc377Syt 1423f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "ahci_tran_start " 14242fcbc377Syt "sata tran accepted: port %d", port); 14252fcbc377Syt 142668d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 14272fcbc377Syt return (SATA_TRAN_ACCEPTED); 14282fcbc377Syt } 14292fcbc377Syt 143068d33a25Syt /* 143168d33a25Syt * SATA_OPMODE_SYNCH flag is set 143268d33a25Syt * 143368d33a25Syt * If SATA_OPMODE_POLLING flag is set, then we must poll the command 143468d33a25Syt * without interrupt, otherwise we can still use the interrupt. 143568d33a25Syt * 143668d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function 143768d33a25Syt * is called. 143868d33a25Syt */ 143968d33a25Syt static int 144068d33a25Syt ahci_do_sync_start(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 144168d33a25Syt uint8_t port, sata_pkt_t *spkt) 144268d33a25Syt { 144368d33a25Syt int pkt_timeout_ticks; 144468d33a25Syt uint32_t timeout_tags; 144568d33a25Syt int rval; 1446a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 144768d33a25Syt 1448f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_do_sync_start enter: " 144968d33a25Syt "port %d spkt 0x%p", port, spkt); 145068d33a25Syt 145168d33a25Syt if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) { 145268d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_POLLING; 145368d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 145468d33a25Syt port, spkt)) == AHCI_FAILURE) { 145568d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_POLLING; 145668d33a25Syt return (rval); 145768d33a25Syt } 145868d33a25Syt 145968d33a25Syt pkt_timeout_ticks = 146068d33a25Syt drv_usectohz((clock_t)spkt->satapkt_time * 1000000); 146168d33a25Syt 146268d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) { 146368d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 146468d33a25Syt 146568d33a25Syt /* Simulate the interrupt */ 146682263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 146768d33a25Syt 146868d33a25Syt drv_usecwait(AHCI_1MS_USECS); 146968d33a25Syt 147068d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 147168d33a25Syt pkt_timeout_ticks -= AHCI_1MS_TICKS; 147268d33a25Syt if (pkt_timeout_ticks < 0) { 1473a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_do_sync_start " 147468d33a25Syt "port %d satapkt 0x%p timed out\n", 1475a9440e8dSyt instance, port, (void *)spkt); 147668d33a25Syt timeout_tags = (0x1 << rval); 147768d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 147868d33a25Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 147982263d52Syt port, timeout_tags); 148068d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 148168d33a25Syt } 148268d33a25Syt } 148368d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_POLLING; 148468d33a25Syt return (AHCI_SUCCESS); 148568d33a25Syt 148668d33a25Syt } else { 148768d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 148868d33a25Syt port, spkt)) == AHCI_FAILURE) 148968d33a25Syt return (rval); 149068d33a25Syt 149182263d52Syt #if AHCI_DEBUG 149282263d52Syt /* 149382263d52Syt * Note that the driver always uses the slot 0 to deliver 149482263d52Syt * REQUEST SENSE or READ LOG EXT command 149582263d52Syt */ 149682263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 149782263d52Syt ASSERT(rval == 0); 149882263d52Syt #endif 149982263d52Syt 150068d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) 150168d33a25Syt cv_wait(&ahci_portp->ahciport_cv, 150268d33a25Syt &ahci_portp->ahciport_mutex); 150368d33a25Syt 150468d33a25Syt return (AHCI_SUCCESS); 150568d33a25Syt } 150668d33a25Syt } 150768d33a25Syt 15082fcbc377Syt #define SENDUP_PACKET(ahci_portp, satapkt, reason) \ 15092fcbc377Syt if (satapkt) { \ 15102fcbc377Syt satapkt->satapkt_reason = reason; \ 15112fcbc377Syt /* \ 15122fcbc377Syt * We set the satapkt_reason in both sync and \ 15132fcbc377Syt * non-sync cases. \ 15142fcbc377Syt */ \ 15152fcbc377Syt } \ 15162fcbc377Syt if (satapkt && \ 151768d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 15182fcbc377Syt satapkt->satapkt_comp) { \ 15192fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); \ 15202fcbc377Syt (*satapkt->satapkt_comp)(satapkt); \ 15212fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); \ 152268d33a25Syt } else { \ 152368d33a25Syt if (satapkt && \ 152468d33a25Syt (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 152568d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_POLLING)) \ 1526f8a673adSying tian - Beijing China cv_broadcast(&ahci_portp->ahciport_cv); \ 15272fcbc377Syt } 15282fcbc377Syt 15292fcbc377Syt /* 153082263d52Syt * Searches for and claims a free command slot. 153182263d52Syt * 153282263d52Syt * Returns: 15332fcbc377Syt * 153482263d52Syt * AHCI_FAILURE if failed 153582263d52Syt * 1. if no empty slot left 153682263d52Syt * 2. non-queued command requested while queued command(s) is outstanding 153782263d52Syt * 3. queued command requested whild non-queued command(s) is outstanding 153882263d52Syt * 4. HBA doesn't support multiple-use of command list while already a 153982263d52Syt * non-queued command is oustanding 154082263d52Syt * 154182263d52Syt * claimed slot number if succeeded 154282263d52Syt * 154382263d52Syt * NOTE: it will always return slot 0 during error recovery process for 154482263d52Syt * REQUEST SENSE or READ LOG EXT command to simplify the algorithm. 15452fcbc377Syt * 15462fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 15472fcbc377Syt * is called. 15482fcbc377Syt */ 15492fcbc377Syt static int 155082263d52Syt ahci_claim_free_slot(ahci_ctl_t *ahci_ctlp, 155182263d52Syt ahci_port_t *ahci_portp, int command_type) 15522fcbc377Syt { 15532fcbc377Syt uint32_t free_slots; 15542fcbc377Syt int slot; 15552fcbc377Syt 1556f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_claim_free_slot enter " 155782263d52Syt "ahciport_pending_tags = 0x%x " 155882263d52Syt "ahciport_pending_ncq_tags = 0x%x", 155982263d52Syt ahci_portp->ahciport_pending_tags, 156082263d52Syt ahci_portp->ahciport_pending_ncq_tags); 15612fcbc377Syt 156282263d52Syt /* 156382263d52Syt * According to the AHCI spec, system software is responsible to 156482263d52Syt * ensure that queued and non-queued commands are not mixed in 156582263d52Syt * the command list. 156682263d52Syt */ 156782263d52Syt if (command_type == AHCI_NON_NCQ_CMD) { 156882263d52Syt /* Non-NCQ command request */ 156982263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 1570f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 157182263d52Syt "ahci_claim_free_slot: there is still pending " 157282263d52Syt "queued command(s) in the command list, " 157382263d52Syt "so no available slot for the non-queued " 1574f5f2d263SFred Herard "command", NULL); 157582263d52Syt return (AHCI_FAILURE); 157682263d52Syt } 157782263d52Syt if ((ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE) && 157882263d52Syt NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 1579f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 158082263d52Syt "ahci_claim_free_slot: HBA cannot support multiple-" 1581f5f2d263SFred Herard "use of the command list for non-queued commands", 1582f5f2d263SFred Herard NULL); 158382263d52Syt return (AHCI_FAILURE); 158482263d52Syt } 158582263d52Syt free_slots = (~ahci_portp->ahciport_pending_tags) & 158682263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 158782263d52Syt } else if (command_type == AHCI_NCQ_CMD) { 158882263d52Syt /* NCQ command request */ 158982263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 1590f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 159182263d52Syt "ahci_claim_free_slot: there is still pending " 159282263d52Syt "non-queued command(s) in the command list, " 1593f5f2d263SFred Herard "so no available slot for the queued command", 1594f5f2d263SFred Herard NULL); 159582263d52Syt return (AHCI_FAILURE); 159682263d52Syt } 159782263d52Syt free_slots = (~ahci_portp->ahciport_pending_ncq_tags) & 159882263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 159982263d52Syt } else if (command_type == AHCI_ERR_RETRI_CMD) { 160082263d52Syt /* Error retrieval command request */ 1601f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 160282263d52Syt "ahci_claim_free_slot: slot 0 is allocated for REQUEST " 1603f5f2d263SFred Herard "SENSE or READ LOG EXT command", NULL); 160482263d52Syt slot = 0; 160582263d52Syt goto out; 160682263d52Syt } 16072fcbc377Syt 16082fcbc377Syt slot = ddi_ffs(free_slots) - 1; 16092fcbc377Syt if (slot == -1) { 1610f5f2d263SFred Herard AHCIDBG(AHCIDBG_VERBOSE, ahci_ctlp, 1611f5f2d263SFred Herard "ahci_claim_free_slot: no empty slots", NULL); 16122fcbc377Syt return (AHCI_FAILURE); 16132fcbc377Syt } 16142fcbc377Syt 161582263d52Syt /* 161682263d52Syt * According to the AHCI spec, to allow a simple mechanism for the 161782263d52Syt * HBA to map command list slots to queue entries, software must 161882263d52Syt * match the tag number it uses to the slot it is placing the command 161982263d52Syt * in. For example, if a queued command is placed in slot 5, the tag 162082263d52Syt * for that command must be 5. 162182263d52Syt */ 162282263d52Syt if (command_type == AHCI_NCQ_CMD) { 162382263d52Syt ahci_portp->ahciport_pending_ncq_tags |= (0x1 << slot); 162482263d52Syt } 162582263d52Syt 16262fcbc377Syt ahci_portp->ahciport_pending_tags |= (0x1 << slot); 16272fcbc377Syt 162882263d52Syt out: 1629f5f2d263SFred Herard AHCIDBG(AHCIDBG_VERBOSE, ahci_ctlp, 16302fcbc377Syt "ahci_claim_free_slot: found slot: 0x%x", slot); 16312fcbc377Syt 16322fcbc377Syt return (slot); 16332fcbc377Syt } 16342fcbc377Syt 16352fcbc377Syt /* 16362fcbc377Syt * Builds the Command Table for the sata packet and delivers it to controller. 16372fcbc377Syt * 16382fcbc377Syt * Returns: 16392fcbc377Syt * slot number if we can obtain a slot successfully 16402fcbc377Syt * otherwise, return AHCI_FAILURE 16412fcbc377Syt * 16422fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 16432fcbc377Syt */ 16442fcbc377Syt static int 16452fcbc377Syt ahci_deliver_satapkt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 16462fcbc377Syt uint8_t port, sata_pkt_t *spkt) 16472fcbc377Syt { 164868d33a25Syt int cmd_slot; 164968d33a25Syt sata_cmd_t *scmd; 16502fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 16512fcbc377Syt ahci_cmd_table_t *cmd_table; 16522fcbc377Syt ahci_cmd_header_t *cmd_header; 16532fcbc377Syt int ncookies; 16542fcbc377Syt int i; 165582263d52Syt int command_type = AHCI_NON_NCQ_CMD; 165682263d52Syt int ncq_qdepth; 1657a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 165868d33a25Syt #if AHCI_DEBUG 165968d33a25Syt uint32_t *ptr; 166068d33a25Syt uint8_t *ptr2; 166168d33a25Syt #endif 16622fcbc377Syt 16632fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 16642fcbc377Syt 166568d33a25Syt scmd = &spkt->satapkt_cmd; 16662fcbc377Syt 166782263d52Syt /* Check if the command is a NCQ command */ 166882263d52Syt if (scmd->satacmd_cmd_reg == SATAC_READ_FPDMA_QUEUED || 166982263d52Syt scmd->satacmd_cmd_reg == SATAC_WRITE_FPDMA_QUEUED) { 167082263d52Syt command_type = AHCI_NCQ_CMD; 167182263d52Syt 167282263d52Syt /* 167382263d52Syt * When NCQ is support, system software must determine the 167482263d52Syt * maximum tag allowed by the device and the HBA, and it 167582263d52Syt * must use a value not beyond of the lower bound of the two. 167682263d52Syt * 167782263d52Syt * Sata module is going to calculate the qdepth and send 167882263d52Syt * down to HBA driver via sata_cmd. 167982263d52Syt */ 168082263d52Syt ncq_qdepth = scmd->satacmd_flags.sata_max_queue_depth + 1; 168182263d52Syt 168282263d52Syt /* 168382263d52Syt * At the moment, the driver doesn't support the dynamic 168482263d52Syt * setting of the maximum ncq depth, and the value can be 168582263d52Syt * set either during the attach or after hot-plug insertion. 168682263d52Syt */ 168782263d52Syt if (ahci_portp->ahciport_max_ncq_tags == 0) { 168882263d52Syt ahci_portp->ahciport_max_ncq_tags = ncq_qdepth; 1689f5f2d263SFred Herard AHCIDBG(AHCIDBG_NCQ, ahci_ctlp, 169082263d52Syt "ahci_deliver_satapkt: port %d the max tags for " 169182263d52Syt "NCQ command is %d", port, ncq_qdepth); 169282263d52Syt } else { 169382263d52Syt if (ncq_qdepth != ahci_portp->ahciport_max_ncq_tags) { 1694a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_deliver_satapkt" 1695a9440e8dSyt " port %d the max tag for NCQ command is " 169682263d52Syt "requested to change from %d to %d, at the" 169782263d52Syt " moment the driver doesn't support the " 169882263d52Syt "dynamic change so it's going to " 1699a9440e8dSyt "still use the previous tag value", 1700a9440e8dSyt instance, port, 170182263d52Syt ahci_portp->ahciport_max_ncq_tags, 170282263d52Syt ncq_qdepth); 170382263d52Syt } 170482263d52Syt } 170582263d52Syt } 170682263d52Syt 170782263d52Syt /* Check if the command is an error retrieval command */ 170882263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 170982263d52Syt command_type = AHCI_ERR_RETRI_CMD; 171082263d52Syt 171182263d52Syt /* Check if there is an empty command slot */ 171282263d52Syt cmd_slot = ahci_claim_free_slot(ahci_ctlp, ahci_portp, command_type); 171368d33a25Syt if (cmd_slot == AHCI_FAILURE) { 1714f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "no free command slot", NULL); 17152fcbc377Syt return (AHCI_FAILURE); 17162fcbc377Syt } 17172fcbc377Syt 1718f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INFO, ahci_ctlp, 171968d33a25Syt "ahci_deliver_satapkt enter: cmd_reg: 0x%x, cmd_slot: 0x%x, " 172068d33a25Syt "port: %d, satapkt: 0x%p", scmd->satacmd_cmd_reg, 172168d33a25Syt cmd_slot, port, (void *)spkt); 17222fcbc377Syt 172368d33a25Syt cmd_table = ahci_portp->ahciport_cmd_tables[cmd_slot]; 17242fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 17252fcbc377Syt 172682263d52Syt /* For data transfer operations, it is the H2D Register FIS */ 17272fcbc377Syt h2d_register_fisp = 17282fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 17292fcbc377Syt 17302fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 17312fcbc377Syt if ((spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_PMPORT) || 17322fcbc377Syt (spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_DPMPORT)) { 17332fcbc377Syt SET_FIS_PMP(h2d_register_fisp, 17342fcbc377Syt spkt->satapkt_device.satadev_addr.pmport); 17352fcbc377Syt } 17362fcbc377Syt 17372fcbc377Syt SET_FIS_CDMDEVCTL(h2d_register_fisp, 1); 173868d33a25Syt SET_FIS_COMMAND(h2d_register_fisp, scmd->satacmd_cmd_reg); 173968d33a25Syt SET_FIS_FEATURES(h2d_register_fisp, scmd->satacmd_features_reg); 174068d33a25Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, scmd->satacmd_sec_count_lsb); 17412fcbc377Syt 174268d33a25Syt switch (scmd->satacmd_addr_type) { 17432fcbc377Syt 174482263d52Syt case 0: 174582263d52Syt /* 174682263d52Syt * satacmd_addr_type will be 0 for the commands below: 174782263d52Syt * ATAPI command 174882263d52Syt * SATAC_IDLE_IM 174982263d52Syt * SATAC_STANDBY_IM 175082263d52Syt * SATAC_DOWNLOAD_MICROCODE 175182263d52Syt * SATAC_FLUSH_CACHE 175282263d52Syt * SATAC_SET_FEATURES 175382263d52Syt * SATAC_SMART 175482263d52Syt * SATAC_ID_PACKET_DEVICE 175582263d52Syt * SATAC_ID_DEVICE 175682263d52Syt */ 1757689d74b0Syt /* FALLTHRU */ 175882263d52Syt 17592fcbc377Syt case ATA_ADDR_LBA: 1760689d74b0Syt /* FALLTHRU */ 17612fcbc377Syt 17622fcbc377Syt case ATA_ADDR_LBA28: 17632fcbc377Syt /* LBA[7:0] */ 176468d33a25Syt SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb); 17652fcbc377Syt 17662fcbc377Syt /* LBA[15:8] */ 176768d33a25Syt SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb); 17682fcbc377Syt 17692fcbc377Syt /* LBA[23:16] */ 177068d33a25Syt SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb); 17712fcbc377Syt 17722fcbc377Syt /* LBA [27:24] (also called dev_head) */ 177368d33a25Syt SET_FIS_DEV_HEAD(h2d_register_fisp, scmd->satacmd_device_reg); 17742fcbc377Syt 17752fcbc377Syt break; 17762fcbc377Syt 17772fcbc377Syt case ATA_ADDR_LBA48: 17782fcbc377Syt /* LBA[7:0] */ 177968d33a25Syt SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb); 17802fcbc377Syt 17812fcbc377Syt /* LBA[15:8] */ 178268d33a25Syt SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb); 17832fcbc377Syt 17842fcbc377Syt /* LBA[23:16] */ 178568d33a25Syt SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb); 17862fcbc377Syt 17872fcbc377Syt /* LBA [31:24] */ 17882fcbc377Syt SET_FIS_SECTOR_EXP(h2d_register_fisp, 178968d33a25Syt scmd->satacmd_lba_low_msb); 17902fcbc377Syt 17912fcbc377Syt /* LBA [39:32] */ 17922fcbc377Syt SET_FIS_CYL_LOW_EXP(h2d_register_fisp, 179368d33a25Syt scmd->satacmd_lba_mid_msb); 17942fcbc377Syt 17952fcbc377Syt /* LBA [47:40] */ 17962fcbc377Syt SET_FIS_CYL_HI_EXP(h2d_register_fisp, 179768d33a25Syt scmd->satacmd_lba_high_msb); 17982fcbc377Syt 17992fcbc377Syt /* Set dev_head */ 18002fcbc377Syt SET_FIS_DEV_HEAD(h2d_register_fisp, 180168d33a25Syt scmd->satacmd_device_reg); 18022fcbc377Syt 18032fcbc377Syt /* Set the extended sector count and features */ 18042fcbc377Syt SET_FIS_SECTOR_COUNT_EXP(h2d_register_fisp, 180568d33a25Syt scmd->satacmd_sec_count_msb); 18062fcbc377Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 180768d33a25Syt scmd->satacmd_features_reg_ext); 18082fcbc377Syt break; 18092fcbc377Syt } 18102fcbc377Syt 181182263d52Syt /* 181282263d52Syt * For NCQ command (READ/WRITE FPDMA QUEUED), sector count 7:0 is 181382263d52Syt * filled into features field, and sector count 8:15 is filled into 181482263d52Syt * features (exp) field. TAG is filled into sector field. 181582263d52Syt */ 181682263d52Syt if (command_type == AHCI_NCQ_CMD) { 181782263d52Syt SET_FIS_FEATURES(h2d_register_fisp, 181882263d52Syt scmd->satacmd_sec_count_lsb); 181982263d52Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 182082263d52Syt scmd->satacmd_sec_count_msb); 182182263d52Syt 182282263d52Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, 182382263d52Syt (cmd_slot << SATA_TAG_QUEUING_SHIFT)); 182482263d52Syt } 182582263d52Syt 182668d33a25Syt ncookies = scmd->satacmd_num_dma_cookies; 1827f5f2d263SFred Herard AHCIDBG(AHCIDBG_PRDT, ahci_ctlp, 18282fcbc377Syt "ncookies = 0x%x, ahci_dma_prdt_number = 0x%x", 18292fcbc377Syt ncookies, ahci_dma_prdt_number); 18302fcbc377Syt 18312fcbc377Syt ASSERT(ncookies <= ahci_dma_prdt_number); 183238547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot] = 0; 18332fcbc377Syt 18342fcbc377Syt /* *** now fill the scatter gather list ******* */ 18352fcbc377Syt for (i = 0; i < ncookies; i++) { 18362fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr = 183768d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[0]; 18382fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr_upper = 183968d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[1]; 18402fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_descr_info = 184168d33a25Syt scmd->satacmd_dma_cookie_list[i].dmac_size - 1; 184238547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot] += 184338547057Sying tian - Beijing China scmd->satacmd_dma_cookie_list[i].dmac_size; 184468d33a25Syt } 184568d33a25Syt 1846f5f2d263SFred Herard AHCIDBG(AHCIDBG_PRDT, ahci_ctlp, 184738547057Sying tian - Beijing China "ahciport_prd_bytecounts 0x%x for cmd_slot 0x%x", 184838547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot], cmd_slot); 184938547057Sying tian - Beijing China 185068d33a25Syt /* The ACMD field is filled in for ATAPI command */ 185168d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) { 185268d33a25Syt bcopy(scmd->satacmd_acdb, cmd_table->ahcict_atapi_cmd, 185368d33a25Syt SATA_ATAPI_MAX_CDB_LEN); 18542fcbc377Syt } 18552fcbc377Syt 18562fcbc377Syt /* Set Command Header in Command List */ 185768d33a25Syt cmd_header = &ahci_portp->ahciport_cmd_list[cmd_slot]; 18582fcbc377Syt BZERO_DESCR_INFO(cmd_header); 18592fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 186068d33a25Syt 186168d33a25Syt /* Set the number of entries in the PRD table */ 18622fcbc377Syt SET_PRD_TABLE_LENGTH(cmd_header, ncookies); 186368d33a25Syt 186468d33a25Syt /* Set the length of the command in the CFIS area */ 18652fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, AHCI_H2D_REGISTER_FIS_LENGTH); 18662fcbc377Syt 1867f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "command data direction is " 18682fcbc377Syt "sata_data_direction = 0x%x", 186968d33a25Syt scmd->satacmd_flags.sata_data_direction); 187068d33a25Syt 187168d33a25Syt /* Set A bit if it is an ATAPI command */ 187268d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) 187368d33a25Syt SET_ATAPI(cmd_header, AHCI_CMDHEAD_ATAPI); 18742fcbc377Syt 187568d33a25Syt /* Set W bit if data is going to the device */ 187668d33a25Syt if (scmd->satacmd_flags.sata_data_direction == SATA_DIR_WRITE) 18772fcbc377Syt SET_WRITE(cmd_header, AHCI_CMDHEAD_DATA_WRITE); 18782fcbc377Syt 187968d33a25Syt /* 188068d33a25Syt * Set the prefetchable bit - this bit is only valid if the PRDTL 188168d33a25Syt * field is non-zero or the ATAPI 'A' bit is set in the command 188268d33a25Syt * header. This bit cannot be set when using native command 188368d33a25Syt * queuing commands or when using FIS-based switching with a Port 188482263d52Syt * multiplier. 188568d33a25Syt */ 188682263d52Syt if (command_type != AHCI_NCQ_CMD) 188782263d52Syt SET_PREFETCHABLE(cmd_header, AHCI_CMDHEAD_PREFETCHABLE); 18882fcbc377Syt 18892fcbc377Syt /* Now remember the sata packet in ahciport_slot_pkts[]. */ 189082263d52Syt if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 189182263d52Syt ahci_portp->ahciport_slot_pkts[cmd_slot] = spkt; 18922fcbc377Syt 18932fcbc377Syt /* 18942fcbc377Syt * We are overloading satapkt_hba_driver_private with 18952fcbc377Syt * watched_cycle count. 18962fcbc377Syt */ 18972fcbc377Syt spkt->satapkt_hba_driver_private = (void *)(intptr_t)0; 18982fcbc377Syt 189968d33a25Syt #if AHCI_DEBUG 190038547057Sying tian - Beijing China if (ahci_debug_flags & AHCIDBG_ATACMD && 190138547057Sying tian - Beijing China scmd->satacmd_cmd_reg != SATAC_PACKET || 190238547057Sying tian - Beijing China ahci_debug_flags & AHCIDBG_ATAPICMD && 190338547057Sying tian - Beijing China scmd->satacmd_cmd_reg == SATAC_PACKET) { 190438547057Sying tian - Beijing China 190538547057Sying tian - Beijing China /* Dump the command header and table */ 190638547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, "\n"); 190738547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, "Command header&table for spkt " 190838547057Sying tian - Beijing China "0x%p cmd_reg 0x%x port %d", spkt, 190938547057Sying tian - Beijing China scmd->satacmd_cmd_reg, port); 191038547057Sying tian - Beijing China ptr = (uint32_t *)cmd_header; 191138547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 191238547057Sying tian - Beijing China " Command Header:%8x %8x %8x %8x", 191338547057Sying tian - Beijing China ptr[0], ptr[1], ptr[2], ptr[3]); 191438547057Sying tian - Beijing China 191538547057Sying tian - Beijing China /* Dump the H2D register FIS */ 191638547057Sying tian - Beijing China ptr = (uint32_t *)h2d_register_fisp; 191738547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 191838547057Sying tian - Beijing China " Command FIS: %8x %8x %8x %8x", 191938547057Sying tian - Beijing China ptr[0], ptr[1], ptr[2], ptr[3]); 192038547057Sying tian - Beijing China 192138547057Sying tian - Beijing China /* Dump the ACMD register FIS */ 192238547057Sying tian - Beijing China ptr2 = (uint8_t *)&(cmd_table->ahcict_atapi_cmd); 192338547057Sying tian - Beijing China for (i = 0; i < SATA_ATAPI_MAX_CDB_LEN/8; i++) 192438547057Sying tian - Beijing China if (ahci_debug_flags & AHCIDBG_ATAPICMD) 192538547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 192638547057Sying tian - Beijing China " ATAPI command: %2x %2x %2x %2x " 192738547057Sying tian - Beijing China "%2x %2x %2x %2x", 192838547057Sying tian - Beijing China ptr2[8 * i], ptr2[8 * i + 1], 192938547057Sying tian - Beijing China ptr2[8 * i + 2], ptr2[8 * i + 3], 193038547057Sying tian - Beijing China ptr2[8 * i + 4], ptr2[8 * i + 5], 193138547057Sying tian - Beijing China ptr2[8 * i + 6], ptr2[8 * i + 7]); 193238547057Sying tian - Beijing China 193338547057Sying tian - Beijing China /* Dump the PRDT */ 193438547057Sying tian - Beijing China for (i = 0; i < ncookies; i++) { 193538547057Sying tian - Beijing China ptr = (uint32_t *)&(cmd_table->ahcict_prdt[i]); 193668d33a25Syt ahci_log(ahci_ctlp, CE_WARN, 193738547057Sying tian - Beijing China " Cookie %d: %8x %8x %8x %8x", 193838547057Sying tian - Beijing China i, ptr[0], ptr[1], ptr[2], ptr[3]); 193938547057Sying tian - Beijing China } 194068d33a25Syt } 194168d33a25Syt #endif 194268d33a25Syt 194368d33a25Syt (void) ddi_dma_sync( 194468d33a25Syt ahci_portp->ahciport_cmd_tables_dma_handle[cmd_slot], 19452fcbc377Syt 0, 19462fcbc377Syt ahci_cmd_table_size, 19472fcbc377Syt DDI_DMA_SYNC_FORDEV); 19482fcbc377Syt 19492fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 195068d33a25Syt cmd_slot * sizeof (ahci_cmd_header_t), 19512fcbc377Syt sizeof (ahci_cmd_header_t), 19522fcbc377Syt DDI_DMA_SYNC_FORDEV); 19532fcbc377Syt 195482263d52Syt /* Set the corresponding bit in the PxSACT.DS for queued command */ 195582263d52Syt if (command_type == AHCI_NCQ_CMD) { 195682263d52Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 195782263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port), 195882263d52Syt (0x1 << cmd_slot)); 195982263d52Syt } 196082263d52Syt 19612fcbc377Syt /* Indicate to the HBA that a command is active. */ 19622fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 19632fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 196468d33a25Syt (0x1 << cmd_slot)); 19652fcbc377Syt 1966f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "ahci_deliver_satapkt " 19672fcbc377Syt "exit: port %d", port); 19682fcbc377Syt 196968d33a25Syt return (cmd_slot); 19702fcbc377Syt } 19712fcbc377Syt 19722fcbc377Syt /* 19732fcbc377Syt * Called by the sata framework to abort the previously sent packet(s). 19742fcbc377Syt * 19752fcbc377Syt * Reset device to abort commands. 19762fcbc377Syt */ 19772fcbc377Syt static int 19782fcbc377Syt ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag) 19792fcbc377Syt { 19802fcbc377Syt ahci_ctl_t *ahci_ctlp; 19812fcbc377Syt ahci_port_t *ahci_portp; 198282263d52Syt uint32_t slot_status = 0; 198382263d52Syt uint32_t aborted_tags = 0; 198482263d52Syt uint32_t finished_tags = 0; 19852fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 19862fcbc377Syt uint8_t port; 19872fcbc377Syt int tmp_slot; 1988a9440e8dSyt int instance = ddi_get_instance(dip); 19892fcbc377Syt 1990a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 19912fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 19922fcbc377Syt 1993f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 199468d33a25Syt "ahci_tran_abort enter: port %d", port); 19952fcbc377Syt 19962fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 19972fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 19982fcbc377Syt 19992fcbc377Syt /* 200068d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 200168d33a25Syt * commands are being mopped, therefore there is nothing else to do 20022fcbc377Syt */ 200368d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 2004f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 20052fcbc377Syt "ahci_tran_abort: port %d is in " 20062fcbc377Syt "mopping process, so just return directly ", port); 20072fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20082fcbc377Syt return (SATA_SUCCESS); 20092fcbc377Syt } 20102fcbc377Syt 20112fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 20122fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 20132fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 20142fcbc377Syt /* 20152fcbc377Syt * In case the targer driver would send the request before 20162fcbc377Syt * sata framework can have the opportunity to process those 20172fcbc377Syt * event reports. 20182fcbc377Syt */ 20192fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 20202fcbc377Syt spkt->satapkt_device.satadev_state = 20212fcbc377Syt ahci_portp->ahciport_port_state; 20222fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 20232fcbc377Syt &spkt->satapkt_device); 2024f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 20252fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 20262fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 202768d33a25Syt "port: %d", port); 20282fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20292fcbc377Syt return (SATA_FAILURE); 20302fcbc377Syt } 20312fcbc377Syt 20322fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 20332fcbc377Syt /* 20342fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 20352fcbc377Syt * AHCI_PORT_TYPE_NODEV. 20362fcbc377Syt */ 20372fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 20382fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 20392fcbc377Syt spkt->satapkt_device.satadev_state = 20402fcbc377Syt ahci_portp->ahciport_port_state; 20412fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 20422fcbc377Syt &spkt->satapkt_device); 2043f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 20442fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 204568d33a25Syt "no device attached: port: %d", port); 20462fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20472fcbc377Syt return (SATA_FAILURE); 20482fcbc377Syt } 20492fcbc377Syt 20502fcbc377Syt if (flag == SATA_ABORT_ALL_PACKETS) { 205182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 205282263d52Syt aborted_tags = ahci_portp->ahciport_pending_tags; 2053a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 205482263d52Syt aborted_tags = ahci_portp->ahciport_pending_ncq_tags; 205582263d52Syt 2056a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort all packets", 2057a9440e8dSyt instance, port); 20582fcbc377Syt } else { 20592fcbc377Syt aborted_tags = 0xffffffff; 20602fcbc377Syt /* 206182263d52Syt * Aborting one specific packet, first search the 20622fcbc377Syt * ahciport_slot_pkts[] list for matching spkt. 20632fcbc377Syt */ 20642fcbc377Syt for (tmp_slot = 0; 20652fcbc377Syt tmp_slot < ahci_ctlp->ahcictl_num_cmd_slots; tmp_slot++) { 20662fcbc377Syt if (ahci_portp->ahciport_slot_pkts[tmp_slot] == spkt) { 20672fcbc377Syt aborted_tags = (0x1 << tmp_slot); 20682fcbc377Syt break; 20692fcbc377Syt } 20702fcbc377Syt } 20712fcbc377Syt 20722fcbc377Syt if (aborted_tags == 0xffffffff) { 20732fcbc377Syt /* request packet is not on the pending list */ 2074f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 20752fcbc377Syt "Cannot find the aborting pkt 0x%p on the " 20762fcbc377Syt "pending list", (void *)spkt); 20772fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 20782fcbc377Syt &spkt->satapkt_device); 20792fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20802fcbc377Syt return (SATA_FAILURE); 20812fcbc377Syt } 2082a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort satapkt 0x%p", 2083a9440e8dSyt instance, port, (void *)spkt); 20842fcbc377Syt } 20852fcbc377Syt 208682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 208782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 208882263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 2089a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 209082263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 209182263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 20922fcbc377Syt 209368d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 209468d33a25Syt ahci_portp->ahciport_mop_in_progress++; 20952fcbc377Syt 20962fcbc377Syt /* 20972fcbc377Syt * To abort the packet(s), first we are trying to clear PxCMD.ST 209868d33a25Syt * to stop the port, and if the port can be stopped 20992fcbc377Syt * successfully with PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0', 21002fcbc377Syt * then we just send back the aborted packet(s) with ABORTED flag 21012fcbc377Syt * and then restart the port by setting PxCMD.ST and PxCMD.FRE. 21022fcbc377Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then we 21032fcbc377Syt * perform a COMRESET. 21042fcbc377Syt */ 21052fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 210668d33a25Syt ahci_portp, port, NULL, NULL); 21072fcbc377Syt 21082fcbc377Syt /* 21092fcbc377Syt * Compute which have finished and which need to be retried. 21102fcbc377Syt * 211182263d52Syt * The finished tags are ahciport_pending_tags/ahciport_pending_ncq_tags 211282263d52Syt * minus the slot_status. The aborted_tags has to be deducted by 211382263d52Syt * finished_tags since we can't possibly abort a tag which had finished 211482263d52Syt * already. 21152fcbc377Syt */ 211682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 211782263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 211882263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2119a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 212082263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 212182263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 21222fcbc377Syt 21232fcbc377Syt aborted_tags &= ~finished_tags; 21242fcbc377Syt 21252fcbc377Syt ahci_mop_commands(ahci_ctlp, 21262fcbc377Syt ahci_portp, 21272fcbc377Syt slot_status, 21282fcbc377Syt 0, /* failed tags */ 21292fcbc377Syt 0, /* timeout tags */ 21302fcbc377Syt aborted_tags, 21312fcbc377Syt 0); /* reset tags */ 21322fcbc377Syt 21332fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, &spkt->satapkt_device); 21342fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 21352fcbc377Syt 21362fcbc377Syt return (SATA_SUCCESS); 21372fcbc377Syt } 21382fcbc377Syt 21392fcbc377Syt /* 21402fcbc377Syt * Used to do device reset and reject all the pending packets on a device 21412fcbc377Syt * during the reset operation. 21422fcbc377Syt * 21432fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 21442fcbc377Syt */ 21452fcbc377Syt static int 21462fcbc377Syt ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp, 21472fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 21482fcbc377Syt { 214982263d52Syt uint32_t slot_status = 0; 215082263d52Syt uint32_t reset_tags = 0; 215182263d52Syt uint32_t finished_tags = 0; 21522fcbc377Syt sata_device_t sdevice; 21532fcbc377Syt int ret; 21542fcbc377Syt 2155f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 21562fcbc377Syt "ahci_reset_device_reject_pkts on port: %d", port); 21572fcbc377Syt 21582fcbc377Syt /* 215968d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 216068d33a25Syt * commands are being mopped, therefore there is nothing else to do 21612fcbc377Syt */ 216268d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 2163f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 21642fcbc377Syt "ahci_reset_device_reject_pkts: port %d is in " 21652fcbc377Syt "mopping process, so return directly ", port); 21662fcbc377Syt return (SATA_SUCCESS); 21672fcbc377Syt } 21682fcbc377Syt 216982263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 217082263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 217182263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 217282263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2173a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 217482263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 217582263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 217682263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 217782263d52Syt } 21782fcbc377Syt 21792fcbc377Syt if (ahci_software_reset(ahci_ctlp, ahci_portp, port) 21802fcbc377Syt != AHCI_SUCCESS) { 2181f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 21822fcbc377Syt "Try to do a port reset after software " 21832fcbc377Syt "reset failed", port); 21842fcbc377Syt ret = ahci_port_reset(ahci_ctlp, ahci_portp, port); 21852fcbc377Syt if (ret != AHCI_SUCCESS) { 2186f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 21872fcbc377Syt "ahci_reset_device_reject_pkts: port %d " 21882fcbc377Syt "failed", port); 21892fcbc377Syt return (SATA_FAILURE); 21902fcbc377Syt } 21912fcbc377Syt } 21922fcbc377Syt /* Set the reset in progress flag */ 21932fcbc377Syt ahci_portp->ahciport_reset_in_progress = 1; 21942fcbc377Syt 219568d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 219668d33a25Syt ahci_portp->ahciport_mop_in_progress++; 21972fcbc377Syt 21982fcbc377Syt /* Indicate to the framework that a reset has happened */ 21992fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 220009121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 220168d33a25Syt sdevice.satadev_addr.pmport = 0; 220268d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 22032fcbc377Syt 22042fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 22052fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 22062fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 22072fcbc377Syt sata_hba_event_notify( 22082fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 22092fcbc377Syt &sdevice, 22102fcbc377Syt SATA_EVNT_DEVICE_RESET); 22112fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 22122fcbc377Syt 2213f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 22142fcbc377Syt "port %d sending event up: SATA_EVNT_RESET", port); 22152fcbc377Syt 22162fcbc377Syt /* Next try to mop the pending commands */ 221782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 221882263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 221982263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2220a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 222182263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 222282263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 22232fcbc377Syt 22242fcbc377Syt reset_tags &= ~finished_tags; 22252fcbc377Syt 22262fcbc377Syt ahci_mop_commands(ahci_ctlp, 22272fcbc377Syt ahci_portp, 22282fcbc377Syt slot_status, 22292fcbc377Syt 0, /* failed tags */ 22302fcbc377Syt 0, /* timeout tags */ 22312fcbc377Syt 0, /* aborted tags */ 22322fcbc377Syt reset_tags); /* reset tags */ 22332fcbc377Syt 22342fcbc377Syt return (SATA_SUCCESS); 22352fcbc377Syt } 22362fcbc377Syt 22372fcbc377Syt /* 22382fcbc377Syt * Used to do port reset and reject all the pending packets on a port during 22392fcbc377Syt * the reset operation. 22402fcbc377Syt * 22412fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 22422fcbc377Syt */ 22432fcbc377Syt static int 22442fcbc377Syt ahci_reset_port_reject_pkts(ahci_ctl_t *ahci_ctlp, 22452fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 22462fcbc377Syt { 224782263d52Syt uint32_t slot_status = 0; 224882263d52Syt uint32_t reset_tags = 0; 224982263d52Syt uint32_t finished_tags = 0; 22502fcbc377Syt 2251f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 22522fcbc377Syt "ahci_reset_port_reject_pkts on port: %d", port); 22532fcbc377Syt 22542fcbc377Syt /* 225568d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 225668d33a25Syt * commands are being mopped, therefore there is nothing else to do 22572fcbc377Syt */ 225868d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 2259f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 22602fcbc377Syt "ahci_reset_port_reject_pkts: port %d is in " 22612fcbc377Syt "mopping process, so return directly ", port); 22622fcbc377Syt return (SATA_SUCCESS); 22632fcbc377Syt } 22642fcbc377Syt 226568d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 226668d33a25Syt ahci_portp->ahciport_mop_in_progress++; 22672fcbc377Syt 226882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 226982263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 227082263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 227182263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2272a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 227382263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 227482263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 227582263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 227682263d52Syt } 22772fcbc377Syt 22782fcbc377Syt if (ahci_restart_port_wait_till_ready(ahci_ctlp, 227982263d52Syt ahci_portp, port, AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 228082263d52Syt NULL) != AHCI_SUCCESS) 22812fcbc377Syt return (SATA_FAILURE); 22822fcbc377Syt 228382263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 228482263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 228582263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2286a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 228782263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 228882263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 22892fcbc377Syt 22902fcbc377Syt reset_tags &= ~finished_tags; 22912fcbc377Syt 22922fcbc377Syt ahci_mop_commands(ahci_ctlp, 22932fcbc377Syt ahci_portp, 22942fcbc377Syt slot_status, 22952fcbc377Syt 0, /* failed tags */ 22962fcbc377Syt 0, /* timeout tags */ 22972fcbc377Syt 0, /* aborted tags */ 22982fcbc377Syt reset_tags); /* reset tags */ 22992fcbc377Syt 23002fcbc377Syt return (SATA_SUCCESS); 23012fcbc377Syt } 23022fcbc377Syt 23032fcbc377Syt /* 23042fcbc377Syt * Used to do hba reset and reject all the pending packets on all ports 23052fcbc377Syt * during the reset operation. 23062fcbc377Syt */ 23072fcbc377Syt static int 23082fcbc377Syt ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp) 23092fcbc377Syt { 23102fcbc377Syt ahci_port_t *ahci_portp; 23112fcbc377Syt uint32_t slot_status[AHCI_MAX_PORTS]; 23122fcbc377Syt uint32_t reset_tags[AHCI_MAX_PORTS]; 23132fcbc377Syt uint32_t finished_tags[AHCI_MAX_PORTS]; 23142fcbc377Syt uint8_t port; 23152fcbc377Syt int ret = SATA_SUCCESS; 23162fcbc377Syt 2317f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 2318f5f2d263SFred Herard "ahci_reset_hba_reject_pkts enter", NULL); 23192fcbc377Syt 23202fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 23212fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 23222fcbc377Syt continue; 23232fcbc377Syt } 23242fcbc377Syt 23252fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 23262fcbc377Syt 23272fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 232882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 232982263d52Syt slot_status[port] = ddi_get32( 233082263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 233182263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 233282263d52Syt reset_tags[port] = slot_status[port] & 233382263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 2334a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 233582263d52Syt slot_status[port] = ddi_get32( 233682263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 233782263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 233882263d52Syt reset_tags[port] = slot_status[port] & 233982263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 234082263d52Syt } 23412fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23422fcbc377Syt } 23432fcbc377Syt 23442fcbc377Syt if (ahci_hba_reset(ahci_ctlp) != AHCI_SUCCESS) { 23452fcbc377Syt ret = SATA_FAILURE; 23462fcbc377Syt goto out; 23472fcbc377Syt } 23482fcbc377Syt 23492fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 23502fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 23512fcbc377Syt continue; 23522fcbc377Syt } 23532fcbc377Syt 23542fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 23552fcbc377Syt 23562fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 23572fcbc377Syt /* 23582fcbc377Syt * To prevent recursive enter to ahci_mop_commands, we need 235968d33a25Syt * check AHCI_PORT_FLAG_MOPPING flag. 23602fcbc377Syt */ 236168d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 2362f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 23632fcbc377Syt "ahci_reset_hba_reject_pkts: port %d is in " 23642fcbc377Syt "mopping process, so return directly ", port); 23652fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23662fcbc377Syt continue; 23672fcbc377Syt } 23682fcbc377Syt 236968d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 237068d33a25Syt ahci_portp->ahciport_mop_in_progress++; 23712fcbc377Syt 237282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 237382263d52Syt finished_tags[port] = 237482263d52Syt ahci_portp->ahciport_pending_tags & 237582263d52Syt ~slot_status[port] & AHCI_SLOT_MASK(ahci_ctlp); 2376a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 237782263d52Syt finished_tags[port] = 237882263d52Syt ahci_portp->ahciport_pending_ncq_tags & 237982263d52Syt ~slot_status[port] & AHCI_NCQ_SLOT_MASK(ahci_portp); 23802fcbc377Syt 23812fcbc377Syt reset_tags[port] &= ~finished_tags[port]; 23822fcbc377Syt 23832fcbc377Syt ahci_mop_commands(ahci_ctlp, 23842fcbc377Syt ahci_portp, 23852fcbc377Syt slot_status[port], 23862fcbc377Syt 0, /* failed tags */ 23872fcbc377Syt 0, /* timeout tags */ 23882fcbc377Syt 0, /* aborted tags */ 23892fcbc377Syt reset_tags[port]); /* reset tags */ 239068d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 23912fcbc377Syt } 23922fcbc377Syt out: 23932fcbc377Syt return (ret); 23942fcbc377Syt } 23952fcbc377Syt 23962fcbc377Syt /* 23972fcbc377Syt * Called by sata framework to reset a port(s) or device. 23982fcbc377Syt */ 23992fcbc377Syt static int 24002fcbc377Syt ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd) 24012fcbc377Syt { 24022fcbc377Syt ahci_ctl_t *ahci_ctlp; 24032fcbc377Syt ahci_port_t *ahci_portp; 24042fcbc377Syt uint8_t cport = sd->satadev_addr.cport; 24052fcbc377Syt uint8_t port; 24062fcbc377Syt int ret = SATA_SUCCESS; 2407a9440e8dSyt int instance = ddi_get_instance(dip); 24082fcbc377Syt 2409a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 24102fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 24112fcbc377Syt 2412f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 241368d33a25Syt "ahci_tran_reset_port enter: cport: %d", cport); 24142fcbc377Syt 24152fcbc377Syt switch (sd->satadev_addr.qual) { 24162fcbc377Syt case SATA_ADDR_CPORT: 24172fcbc377Syt /* Port reset */ 24182fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2419a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2420a9440e8dSyt "reset port", instance, port); 24212fcbc377Syt 24222fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 24232fcbc377Syt ret = ahci_reset_port_reject_pkts(ahci_ctlp, ahci_portp, port); 24242fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24252fcbc377Syt 24262fcbc377Syt break; 24272fcbc377Syt 24282fcbc377Syt case SATA_ADDR_DCPORT: 24292fcbc377Syt /* Device reset */ 24302fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2431a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2432a9440e8dSyt "reset device", instance, port); 24332fcbc377Syt 24342fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 24352fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 24362fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 24372fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 24382fcbc377Syt /* 24392fcbc377Syt * In case the targer driver would send the request 24402fcbc377Syt * before sata framework can have the opportunity to 24412fcbc377Syt * process those event reports. 24422fcbc377Syt */ 24432fcbc377Syt sd->satadev_state = ahci_portp->ahciport_port_state; 24442fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 2445f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 24462fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 24472fcbc377Syt "while port in FAILED/SHUTDOWN/PWROFF state: " 244868d33a25Syt "port: %d", port); 24492fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24502fcbc377Syt ret = SATA_FAILURE; 24512fcbc377Syt break; 24522fcbc377Syt } 24532fcbc377Syt 24542fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 24552fcbc377Syt /* 24562fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 24572fcbc377Syt * AHCI_PORT_TYPE_NODEV. 24582fcbc377Syt */ 24592fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 24602fcbc377Syt sd->satadev_state = ahci_portp->ahciport_port_state; 24612fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 2462f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 24632fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 246468d33a25Syt "while no device attached: port: %d", port); 24652fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24662fcbc377Syt ret = SATA_FAILURE; 24672fcbc377Syt break; 24682fcbc377Syt } 24692fcbc377Syt 24702fcbc377Syt ret = ahci_reset_device_reject_pkts(ahci_ctlp, 24712fcbc377Syt ahci_portp, port); 24722fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24732fcbc377Syt break; 24742fcbc377Syt 24752fcbc377Syt case SATA_ADDR_CNTRL: 24762fcbc377Syt /* Reset the whole controller */ 2477a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2478a9440e8dSyt "reset the whole hba", instance, port); 24792fcbc377Syt ret = ahci_reset_hba_reject_pkts(ahci_ctlp); 24802fcbc377Syt break; 24812fcbc377Syt 24822fcbc377Syt case SATA_ADDR_PMPORT: 24832fcbc377Syt case SATA_ADDR_DPMPORT: 2484f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 248568d33a25Syt "ahci_tran_reset_dport: port multiplier will be " 2486f5f2d263SFred Herard "supported later", NULL); 2487689d74b0Syt /* FALLTHRU */ 24882fcbc377Syt default: 24892fcbc377Syt ret = SATA_FAILURE; 24902fcbc377Syt } 24912fcbc377Syt 24922fcbc377Syt return (ret); 24932fcbc377Syt } 24942fcbc377Syt 24952fcbc377Syt /* 24962fcbc377Syt * Called by sata framework to activate a port as part of hotplug. 24972fcbc377Syt * (cfgadm -c connect satax/y) 24982fcbc377Syt * Note: Not port-mult aware. 24992fcbc377Syt */ 25002fcbc377Syt static int 25012fcbc377Syt ahci_tran_hotplug_port_activate(dev_info_t *dip, sata_device_t *satadev) 25022fcbc377Syt { 25032fcbc377Syt ahci_ctl_t *ahci_ctlp; 25042fcbc377Syt ahci_port_t *ahci_portp; 25052fcbc377Syt uint8_t cport = satadev->satadev_addr.cport; 25062fcbc377Syt uint8_t port; 2507a9440e8dSyt int instance = ddi_get_instance(dip); 25082fcbc377Syt 2509a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 25102fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 25112fcbc377Syt 2512f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 25132fcbc377Syt "ahci_tran_hotplug_port_activate cport %d enter", cport); 25142fcbc377Syt 25152fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 25162fcbc377Syt 25172fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 2518689d74b0Syt ahci_enable_port_intrs(ahci_ctlp, port); 2519a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d is activated", instance, port); 25202fcbc377Syt 25212fcbc377Syt /* 25222fcbc377Syt * Reset the port so that the PHY communication would be re-established. 252368d33a25Syt * But this reset is an internal operation and the sata module doesn't 252468d33a25Syt * need to know about it. Moreover, the port with a device attached will 252568d33a25Syt * be started too. 25262fcbc377Syt */ 25272fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 252868d33a25Syt ahci_portp, port, 252968d33a25Syt AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 253068d33a25Syt NULL); 25312fcbc377Syt 25322fcbc377Syt /* 25332fcbc377Syt * Need to check the link status and device status of the port 25342fcbc377Syt * and consider raising power if the port was in D3 state 25352fcbc377Syt */ 253668d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_PWRON; 253768d33a25Syt ahci_portp->ahciport_port_state &= ~SATA_PSTATE_PWROFF; 253868d33a25Syt ahci_portp->ahciport_port_state &= ~SATA_PSTATE_SHUTDOWN; 253968d33a25Syt 254068d33a25Syt satadev->satadev_state = ahci_portp->ahciport_port_state; 25412fcbc377Syt 25422fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 25432fcbc377Syt 25442fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 25452fcbc377Syt return (SATA_SUCCESS); 25462fcbc377Syt } 25472fcbc377Syt 25482fcbc377Syt /* 25492fcbc377Syt * Called by sata framework to deactivate a port as part of hotplug. 25502fcbc377Syt * (cfgadm -c disconnect satax/y) 25512fcbc377Syt * Note: Not port-mult aware. 25522fcbc377Syt */ 25532fcbc377Syt static int 25542fcbc377Syt ahci_tran_hotplug_port_deactivate(dev_info_t *dip, sata_device_t *satadev) 25552fcbc377Syt { 25562fcbc377Syt ahci_ctl_t *ahci_ctlp; 25572fcbc377Syt ahci_port_t *ahci_portp; 25582fcbc377Syt uint8_t cport = satadev->satadev_addr.cport; 25592fcbc377Syt uint8_t port; 256068d33a25Syt uint32_t port_scontrol; 2561a9440e8dSyt int instance = ddi_get_instance(dip); 25622fcbc377Syt 2563a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 25642fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 25652fcbc377Syt 2566f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 25672fcbc377Syt "ahci_tran_hotplug_port_deactivate cport %d enter", cport); 25682fcbc377Syt 25692fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 25702fcbc377Syt 25712fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 2572a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d is deactivated", 2573a9440e8dSyt instance, port); 25742fcbc377Syt 257568d33a25Syt /* Disable the interrupts on the port */ 2576689d74b0Syt ahci_disable_port_intrs(ahci_ctlp, port); 25772fcbc377Syt 257868d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 257968d33a25Syt goto phy_offline; 258068d33a25Syt } 258168d33a25Syt 25822fcbc377Syt /* First to abort all the pending commands */ 25832fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 25842fcbc377Syt 258568d33a25Syt /* Then stop the port */ 258668d33a25Syt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 25872fcbc377Syt ahci_portp, port); 25882fcbc377Syt 258968d33a25Syt /* Next put the PHY offline */ 259068d33a25Syt 259168d33a25Syt phy_offline: 259268d33a25Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 259368d33a25Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 259482263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_DISABLE); 259568d33a25Syt 25962fcbc377Syt /* Update ahciport_port_state */ 25972fcbc377Syt ahci_portp->ahciport_port_state = SATA_PSTATE_SHUTDOWN; 259868d33a25Syt satadev->satadev_state = ahci_portp->ahciport_port_state; 25992fcbc377Syt 26002fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 26012fcbc377Syt 26022fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 26032fcbc377Syt return (SATA_SUCCESS); 26042fcbc377Syt } 26052fcbc377Syt 26062fcbc377Syt /* 260768d33a25Syt * To be used to mark all the outstanding pkts with SATA_PKT_ABORTED 26082fcbc377Syt * when a device is unplugged or a port is deactivated. 26092fcbc377Syt * 26102fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 26112fcbc377Syt */ 26122fcbc377Syt static void 26132fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctl_t *ahci_ctlp, 26142fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 26152fcbc377Syt { 261682263d52Syt uint32_t slot_status = 0; 261782263d52Syt uint32_t abort_tags = 0; 26182fcbc377Syt 2619f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 26202fcbc377Syt "ahci_reject_all_abort_pkts on port: %d", port); 26212fcbc377Syt 262282263d52Syt /* 262382263d52Syt * When AHCI_PORT_FLAG_MOPPING is set, we need to check whether a 262482263d52Syt * REQUEST SENSE command or READ LOG EXT command is delivered to HBA 262582263d52Syt * to get the error data, if yes when the device is removed, the 262682263d52Syt * command needs to be aborted too. 262782263d52Syt */ 262882263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 262982263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 263082263d52Syt slot_status = 0x1; 263182263d52Syt abort_tags = 0x1; 263282263d52Syt goto out; 263382263d52Syt } else { 2634f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 263582263d52Syt "ahci_reject_all_abort_pkts return directly " 263682263d52Syt "port %d no needs to reject any outstanding " 263782263d52Syt "commands", port); 263882263d52Syt return; 263982263d52Syt } 264082263d52Syt } 26412fcbc377Syt 264282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 264382263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 264482263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 264582263d52Syt abort_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2646a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 264782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 264882263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 264982263d52Syt abort_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 265082263d52Syt } 265182263d52Syt 265282263d52Syt out: 265368d33a25Syt /* No need to do mop when there is no outstanding commands */ 265468d33a25Syt if (slot_status != 0) { 265568d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 265668d33a25Syt ahci_portp->ahciport_mop_in_progress++; 26572fcbc377Syt 265868d33a25Syt ahci_mop_commands(ahci_ctlp, 265968d33a25Syt ahci_portp, 266068d33a25Syt slot_status, 266168d33a25Syt 0, /* failed tags */ 266268d33a25Syt 0, /* timeout tags */ 266368d33a25Syt abort_tags, /* aborting tags */ 266468d33a25Syt 0); /* reset tags */ 266568d33a25Syt } 266668d33a25Syt } 266768d33a25Syt 266868d33a25Syt #if defined(__lock_lint) 266968d33a25Syt static int 267068d33a25Syt ahci_selftest(dev_info_t *dip, sata_device_t *device) 267168d33a25Syt { 26722fcbc377Syt return (SATA_SUCCESS); 26732fcbc377Syt } 26742fcbc377Syt #endif 26752fcbc377Syt 26762fcbc377Syt /* 267768d33a25Syt * Allocate the ports structure, only called by ahci_attach 267868d33a25Syt */ 267968d33a25Syt static int 268068d33a25Syt ahci_alloc_ports_state(ahci_ctl_t *ahci_ctlp) 268168d33a25Syt { 2682f68cbde1Syt int port, cport = 0; 268368d33a25Syt 2684f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 2685f5f2d263SFred Herard "ahci_alloc_ports_state enter", NULL); 268668d33a25Syt 268768d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 268868d33a25Syt 268968d33a25Syt /* Allocate structures only for the implemented ports */ 2690f68cbde1Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 269168d33a25Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 2692f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 269368d33a25Syt "hba port %d not implemented", port); 269468d33a25Syt continue; 269568d33a25Syt } 269668d33a25Syt 269768d33a25Syt ahci_ctlp->ahcictl_cport_to_port[cport] = (uint8_t)port; 2698f68cbde1Syt ahci_ctlp->ahcictl_port_to_cport[port] = 2699f68cbde1Syt (uint8_t)cport++; 270068d33a25Syt 270168d33a25Syt if (ahci_alloc_port_state(ahci_ctlp, port) != AHCI_SUCCESS) { 270268d33a25Syt goto err_out; 270368d33a25Syt } 270468d33a25Syt } 270568d33a25Syt 270668d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 270768d33a25Syt return (AHCI_SUCCESS); 270868d33a25Syt 270968d33a25Syt err_out: 271068d33a25Syt for (port--; port >= 0; port--) { 271168d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 271268d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 271368d33a25Syt } 271468d33a25Syt } 271568d33a25Syt 271668d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 271768d33a25Syt return (AHCI_FAILURE); 271868d33a25Syt } 271968d33a25Syt 272068d33a25Syt /* 272168d33a25Syt * Reverse of ahci_alloc_ports_state(), only called by ahci_detach 272268d33a25Syt */ 272368d33a25Syt static void 272468d33a25Syt ahci_dealloc_ports_state(ahci_ctl_t *ahci_ctlp) 272568d33a25Syt { 272668d33a25Syt int port; 272768d33a25Syt 272868d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 272968d33a25Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 273068d33a25Syt /* if this port is implemented by the HBA */ 273168d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) 273268d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 273368d33a25Syt } 273468d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 273568d33a25Syt } 273668d33a25Syt 2737f8a673adSying tian - Beijing China /* 2738f8a673adSying tian - Beijing China * Drain the taskq. 2739f8a673adSying tian - Beijing China */ 2740f8a673adSying tian - Beijing China static void 2741f8a673adSying tian - Beijing China ahci_drain_ports_taskq(ahci_ctl_t *ahci_ctlp) 2742f8a673adSying tian - Beijing China { 2743f8a673adSying tian - Beijing China ahci_port_t *ahci_portp; 2744f8a673adSying tian - Beijing China int port; 2745f8a673adSying tian - Beijing China 2746f8a673adSying tian - Beijing China for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 2747f8a673adSying tian - Beijing China if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 2748f8a673adSying tian - Beijing China continue; 2749f8a673adSying tian - Beijing China } 2750f8a673adSying tian - Beijing China 2751f8a673adSying tian - Beijing China ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2752f8a673adSying tian - Beijing China 2753f8a673adSying tian - Beijing China mutex_enter(&ahci_portp->ahciport_mutex); 2754f8a673adSying tian - Beijing China ddi_taskq_wait(ahci_portp->ahciport_event_taskq); 2755f8a673adSying tian - Beijing China mutex_exit(&ahci_portp->ahciport_mutex); 2756f8a673adSying tian - Beijing China } 2757f8a673adSying tian - Beijing China } 2758f8a673adSying tian - Beijing China 275968d33a25Syt /* 276013bcbb7aSyt * Initialize the controller and all ports. And then try to start the ports 276113bcbb7aSyt * if there are devices attached. 27622fcbc377Syt * 27632fcbc377Syt * This routine can be called from three seperate cases: DDI_ATTACH, 27642fcbc377Syt * PM_LEVEL_D0 and DDI_RESUME. The DDI_ATTACH case is different from 276568d33a25Syt * other two cases; device signature probing are attempted only during 276668d33a25Syt * DDI_ATTACH case. 27672fcbc377Syt * 27682fcbc377Syt * WARNING!!! Disable the whole controller's interrupts before calling and 27692fcbc377Syt * the interrupts will be enabled upon successfully return. 27702fcbc377Syt */ 27712fcbc377Syt static int 27722fcbc377Syt ahci_initialize_controller(ahci_ctl_t *ahci_ctlp) 27732fcbc377Syt { 27742fcbc377Syt ahci_port_t *ahci_portp; 27752fcbc377Syt uint32_t ghc_control; 277668d33a25Syt int port; 27772fcbc377Syt 2778f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 2779f5f2d263SFred Herard "ahci_initialize_controller enter", NULL); 27802fcbc377Syt 27812fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 27822fcbc377Syt 27832fcbc377Syt /* 27842fcbc377Syt * Indicate that system software is AHCI aware by setting 27852fcbc377Syt * GHC.AE to 1 27862fcbc377Syt */ 27872fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 27882fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 27892fcbc377Syt 27902fcbc377Syt ghc_control |= AHCI_HBA_GHC_AE; 27912fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 27922fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), 27932fcbc377Syt ghc_control); 27942fcbc377Syt 279582263d52Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 279682263d52Syt 27972fcbc377Syt /* Initialize the implemented ports and structures */ 27982fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 27992fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 28002fcbc377Syt continue; 28012fcbc377Syt } 28022fcbc377Syt 28032fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 28042fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 28052fcbc377Syt 28062fcbc377Syt /* 28072fcbc377Syt * Ensure that the controller is not in the running state 28082fcbc377Syt * by checking every implemented port's PxCMD register 28092fcbc377Syt */ 28102fcbc377Syt if (ahci_initialize_port(ahci_ctlp, ahci_portp, port) 28112fcbc377Syt != AHCI_SUCCESS) { 2812f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 28132fcbc377Syt "ahci_initialize_controller: failed to " 28142fcbc377Syt "initialize port %d", port); 28152fcbc377Syt /* 28162fcbc377Syt * Set the port state to SATA_PSTATE_FAILED if 28172fcbc377Syt * failed to initialize it. 28182fcbc377Syt */ 281968d33a25Syt ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 28202fcbc377Syt } 28212fcbc377Syt 28222fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 28232fcbc377Syt } 28242fcbc377Syt 28252fcbc377Syt /* Enable the whole controller interrupts */ 282682263d52Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 28272fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 28282fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 28292fcbc377Syt 28302fcbc377Syt return (AHCI_SUCCESS); 28312fcbc377Syt } 28322fcbc377Syt 28332fcbc377Syt /* 283468d33a25Syt * Reverse of ahci_initialize_controller() 28352fcbc377Syt * 283613bcbb7aSyt * We only need to stop the ports and disable the interrupt. 28372fcbc377Syt */ 28382fcbc377Syt static void 283968d33a25Syt ahci_uninitialize_controller(ahci_ctl_t *ahci_ctlp) 28402fcbc377Syt { 284113bcbb7aSyt ahci_port_t *ahci_portp; 284213bcbb7aSyt int port; 284313bcbb7aSyt 2844f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 2845f5f2d263SFred Herard "ahci_uninitialize_controller enter", NULL); 28462fcbc377Syt 28472fcbc377Syt /* disable all the interrupts. */ 284813bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 28492fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 285013bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 285113bcbb7aSyt 285213bcbb7aSyt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 285313bcbb7aSyt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 285413bcbb7aSyt continue; 285513bcbb7aSyt } 285613bcbb7aSyt 285713bcbb7aSyt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 285813bcbb7aSyt 285913bcbb7aSyt /* Stop the port by clearing PxCMD.ST */ 286013bcbb7aSyt mutex_enter(&ahci_portp->ahciport_mutex); 286113bcbb7aSyt 286213bcbb7aSyt /* 286313bcbb7aSyt * Here we must disable the port interrupt because 286413bcbb7aSyt * ahci_disable_all_intrs only clear GHC.IE, and IS 286513bcbb7aSyt * register will be still set if PxIE is enabled. 286613bcbb7aSyt * When ahci shares one IRQ with other drivers, the 286713bcbb7aSyt * intr handler may claim the intr mistakenly. 286813bcbb7aSyt */ 2869689d74b0Syt ahci_disable_port_intrs(ahci_ctlp, port); 287013bcbb7aSyt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 287113bcbb7aSyt ahci_portp, port); 287213bcbb7aSyt mutex_exit(&ahci_portp->ahciport_mutex); 287313bcbb7aSyt } 28742fcbc377Syt } 28752fcbc377Syt 28762fcbc377Syt /* 28772fcbc377Syt * The routine is to initialize the port. First put the port in NOTRunning 28782fcbc377Syt * state, then enable port interrupt and clear Serror register. And under 28792fcbc377Syt * AHCI_ATTACH case, find device signature and then try to start the port. 28802fcbc377Syt * 288182263d52Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 28822fcbc377Syt */ 28832fcbc377Syt static int 28842fcbc377Syt ahci_initialize_port(ahci_ctl_t *ahci_ctlp, 28852fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 28862fcbc377Syt { 28870a4c4cecSXiao-Yu Zhang uint32_t port_sstatus, port_task_file, port_cmd_status; 28880a4c4cecSXiao-Yu Zhang int ret; 28892fcbc377Syt 28902fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 28912fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 28922fcbc377Syt 2893f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 28942fcbc377Syt "ahci_initialize_port: port %d " 28952fcbc377Syt "port_cmd_status = 0x%x", port, port_cmd_status); 28962fcbc377Syt /* 28972fcbc377Syt * Check whether the port is in NotRunning state, if not, 28982fcbc377Syt * put the port in NotRunning state 28992fcbc377Syt */ 29000a4c4cecSXiao-Yu Zhang if (port_cmd_status & 29012fcbc377Syt (AHCI_CMD_STATUS_ST | 29022fcbc377Syt AHCI_CMD_STATUS_CR | 29032fcbc377Syt AHCI_CMD_STATUS_FRE | 29040a4c4cecSXiao-Yu Zhang AHCI_CMD_STATUS_FR)) { 29050a4c4cecSXiao-Yu Zhang (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 29060a4c4cecSXiao-Yu Zhang ahci_portp, port); 29072fcbc377Syt } 29082fcbc377Syt 29090a4c4cecSXiao-Yu Zhang /* Device is unknown at first */ 29100a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 29112fcbc377Syt 2912e2d9bfa6Sying tian - Beijing China /* Disable the interface power management */ 2913e2d9bfa6Sying tian - Beijing China ahci_disable_interface_pm(ahci_ctlp, port); 2914e2d9bfa6Sying tian - Beijing China 29150a4c4cecSXiao-Yu Zhang port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 29160a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 29170a4c4cecSXiao-Yu Zhang port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 29180a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 29190a4c4cecSXiao-Yu Zhang 29200a4c4cecSXiao-Yu Zhang /* Check physcial link status */ 29210a4c4cecSXiao-Yu Zhang if (SSTATUS_GET_IPM(port_sstatus) == SSTATUS_IPM_NODEV_NOPHYCOM || 29220a4c4cecSXiao-Yu Zhang SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_NOPHYCOM || 29230a4c4cecSXiao-Yu Zhang 29240a4c4cecSXiao-Yu Zhang /* Check interface status */ 29250a4c4cecSXiao-Yu Zhang port_task_file & AHCI_TFD_STS_BSY || 29260a4c4cecSXiao-Yu Zhang port_task_file & AHCI_TFD_STS_DRQ) { 29270a4c4cecSXiao-Yu Zhang 29280a4c4cecSXiao-Yu Zhang /* Incorrect task file state, we need to reset port */ 29290a4c4cecSXiao-Yu Zhang ret = ahci_port_reset(ahci_ctlp, ahci_portp, port); 29300a4c4cecSXiao-Yu Zhang 29310a4c4cecSXiao-Yu Zhang /* Does port reset succeed on HBA port? */ 29320a4c4cecSXiao-Yu Zhang if (ret != AHCI_SUCCESS) { 2933f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ERRS, ahci_ctlp, 29340a4c4cecSXiao-Yu Zhang "ahci_initialize_port:" 29350a4c4cecSXiao-Yu Zhang "port reset faild at port %d", port); 29360a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 29370a4c4cecSXiao-Yu Zhang } 29380a4c4cecSXiao-Yu Zhang 29390a4c4cecSXiao-Yu Zhang /* Is port failed? */ 29400a4c4cecSXiao-Yu Zhang if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 2941f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ERRS, ahci_ctlp, 29420a4c4cecSXiao-Yu Zhang "ahci_initialize_port: port %d state 0x%x", 29430a4c4cecSXiao-Yu Zhang port, ahci_portp->ahciport_port_state); 29440a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 29450a4c4cecSXiao-Yu Zhang } 29460a4c4cecSXiao-Yu Zhang } 2947259105bcSying tian - Beijing China 29480a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_STATE_READY; 29490a4c4cecSXiao-Yu Zhang 2950f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "port %d is ready now.", port); 29512fcbc377Syt 29522fcbc377Syt /* 295368d33a25Syt * At the time being, only probe ports/devices and get the types of 295413bcbb7aSyt * attached devices during DDI_ATTACH. In fact, the device can be 295513bcbb7aSyt * changed during power state changes, but at the time being, we 295613bcbb7aSyt * don't support the situation. 29572fcbc377Syt */ 29582fcbc377Syt if (ahci_ctlp->ahcictl_flags & AHCI_ATTACH) { 29590a4c4cecSXiao-Yu Zhang /* 2960259105bcSying tian - Beijing China * Try to get the device signature if the port is 2961259105bcSying tian - Beijing China * not empty. 29620a4c4cecSXiao-Yu Zhang */ 2963259105bcSying tian - Beijing China if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) 2964259105bcSying tian - Beijing China ahci_find_dev_signature(ahci_ctlp, ahci_portp, port); 296513bcbb7aSyt } else { 29662fcbc377Syt 296713bcbb7aSyt /* 296813bcbb7aSyt * During the resume, we need to set the PxCLB, PxCLBU, PxFB 296913bcbb7aSyt * and PxFBU registers in case these registers were cleared 297013bcbb7aSyt * during the suspend. 297113bcbb7aSyt */ 2972f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 297313bcbb7aSyt "ahci_initialize_port: port %d " 297413bcbb7aSyt "reset the port during resume", port); 297513bcbb7aSyt (void) ahci_port_reset(ahci_ctlp, ahci_portp, port); 29762fcbc377Syt 2977f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 297813bcbb7aSyt "ahci_initialize_port: port %d " 297913bcbb7aSyt "set PxCLB, PxCLBU, PxFB and PxFBU " 298013bcbb7aSyt "during resume", port); 298113bcbb7aSyt 298213bcbb7aSyt /* Config Port Received FIS Base Address */ 298313bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 298413bcbb7aSyt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 298513bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 298613bcbb7aSyt 298713bcbb7aSyt /* Config Port Command List Base Address */ 298813bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 298913bcbb7aSyt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 299013bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 299113bcbb7aSyt } 299213bcbb7aSyt 299313bcbb7aSyt /* Return directly if no device connected */ 299413bcbb7aSyt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 2995f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 299613bcbb7aSyt "No device connected to port %d", port); 299713bcbb7aSyt goto out; 299813bcbb7aSyt } 299913bcbb7aSyt 300013bcbb7aSyt /* Try to start the port */ 300113bcbb7aSyt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 300213bcbb7aSyt != AHCI_SUCCESS) { 3003f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 300413bcbb7aSyt "failed to start port %d", port); 300513bcbb7aSyt return (AHCI_FAILURE); 30062fcbc377Syt } 300768d33a25Syt out: 300868d33a25Syt /* Enable port interrupts */ 3009689d74b0Syt ahci_enable_port_intrs(ahci_ctlp, port); 30102fcbc377Syt 30112fcbc377Syt return (AHCI_SUCCESS); 30122fcbc377Syt } 30132fcbc377Syt 301413bcbb7aSyt /* 3015259105bcSying tian - Beijing China * Handle hardware defect, and check the capabilities. For example, 3016259105bcSying tian - Beijing China * power management capabilty and MSI capability. 301713bcbb7aSyt */ 301813bcbb7aSyt static int 301913bcbb7aSyt ahci_config_space_init(ahci_ctl_t *ahci_ctlp) 302013bcbb7aSyt { 3021a9440e8dSyt ushort_t venid, devid; 3022689d74b0Syt ushort_t caps_ptr, cap_count, cap; 3023689d74b0Syt #if AHCI_DEBUG 3024689d74b0Syt ushort_t pmcap, pmcsr; 30252c742e1fSying tian - Beijing China ushort_t msimc; 3026689d74b0Syt #endif 302713bcbb7aSyt uint8_t revision; 302813bcbb7aSyt 302913bcbb7aSyt venid = pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 303013bcbb7aSyt PCI_CONF_VENID); 303113bcbb7aSyt 3032a9440e8dSyt devid = pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 3033a9440e8dSyt PCI_CONF_DEVID); 3034a9440e8dSyt 303513bcbb7aSyt /* 303613bcbb7aSyt * Modify dma_attr_align of ahcictl_buffer_dma_attr. For VT8251, those 303713bcbb7aSyt * controllers with 0x00 revision id work on 4-byte aligned buffer, 303813bcbb7aSyt * which is a bug and was fixed after 0x00 revision id controllers. 303913bcbb7aSyt * 304013bcbb7aSyt * Moreover, VT8251 cannot use multiple command slots in the command 304113bcbb7aSyt * list for non-queued commands because the previous register content 304213bcbb7aSyt * of PxCI can be re-written in the register write, so a flag will be 304313bcbb7aSyt * set to record this defect - AHCI_CAP_NO_MCMDLIST_NONQUEUE. 304413bcbb7aSyt */ 304513bcbb7aSyt if (venid == VIA_VENID) { 304613bcbb7aSyt revision = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 304713bcbb7aSyt PCI_CONF_REVID); 3048f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 304913bcbb7aSyt "revision id = 0x%x", revision); 305013bcbb7aSyt if (revision == 0x00) { 305113bcbb7aSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_align = 0x4; 3052f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 3053f5f2d263SFred Herard "change ddi_attr_align to 0x4", NULL); 305413bcbb7aSyt } 305513bcbb7aSyt 305613bcbb7aSyt ahci_ctlp->ahcictl_cap = AHCI_CAP_NO_MCMDLIST_NONQUEUE; 3057f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 305813bcbb7aSyt "VT8251 cannot use multiple command lists for " 3059f5f2d263SFred Herard "non-queued commands", NULL); 306013bcbb7aSyt } 306113bcbb7aSyt 3062a9440e8dSyt /* 3063259105bcSying tian - Beijing China * AMD/ATI SB600 (1002,4380) AHCI chipset doesn't support 64-bit DMA 3064259105bcSying tian - Beijing China * addressing for both data buffer and communication memory descriptors 3065259105bcSying tian - Beijing China * though S64A bit of CAP register declares the support. 3066259105bcSying tian - Beijing China */ 3067259105bcSying tian - Beijing China if (venid == 0x1002 && devid == 0x4380) { 3068f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 3069259105bcSying tian - Beijing China "ATI SB600 cannot do 64-bit DMA for both data buffer " 3070259105bcSying tian - Beijing China "and communication memory descriptors though CAP indicates " 3071f5f2d263SFred Herard "support, so force it to use 32-bit DMA", NULL); 3072259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_BUF_32BIT_DMA; 3073259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_COMMU_32BIT_DMA; 3074259105bcSying tian - Beijing China } 3075259105bcSying tian - Beijing China 3076259105bcSying tian - Beijing China /* 3077259105bcSying tian - Beijing China * AMD/ATI SB700/750 (1002,4391) AHCI chipset doesn't support 64-bit 3078259105bcSying tian - Beijing China * DMA addressing for communication memory descriptors though S64A bit 3079259105bcSying tian - Beijing China * of CAP register declares the support. However, it does support 3080259105bcSying tian - Beijing China * 64-bit DMA for data buffer. 3081a9440e8dSyt */ 3082259105bcSying tian - Beijing China if (venid == 0x1002 && devid == 0x4391) { 3083f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 3084259105bcSying tian - Beijing China "ATI SB700/750 cannot do 64-bit DMA for communication " 3085259105bcSying tian - Beijing China "memory descriptors though CAP indicates support, " 3086f5f2d263SFred Herard "so force it to use 32-bit DMA", NULL); 3087259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_COMMU_32BIT_DMA; 3088a9440e8dSyt } 3089a9440e8dSyt 3090259105bcSying tian - Beijing China /* 3091259105bcSying tian - Beijing China * nVidia MCP78 AHCI controller (pci10de,0ad4) will be forced to use 3092259105bcSying tian - Beijing China * Fixed interrupt until the known CR 6766472 (MSIs do not function 3093259105bcSying tian - Beijing China * on most Nvidia boards) is fixed. 3094259105bcSying tian - Beijing China */ 3095c6a9dbb6SXiao-Yu Zhang if (venid == 0x10de && devid == 0x0ad4) { 3096f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 3097259105bcSying tian - Beijing China "Force nVidia MCP78 AHCI controller to use " 3098f5f2d263SFred Herard "fixed interrupts", NULL); 3099c6a9dbb6SXiao-Yu Zhang ahci_msi_enabled = B_FALSE; 3100c6a9dbb6SXiao-Yu Zhang } 3101c6a9dbb6SXiao-Yu Zhang 310213bcbb7aSyt /* 310313bcbb7aSyt * Check if capabilities list is supported and if so, 310413bcbb7aSyt * get initial capabilities pointer and clear bits 0,1. 310513bcbb7aSyt */ 310613bcbb7aSyt if (pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 310713bcbb7aSyt PCI_CONF_STAT) & PCI_STAT_CAP) { 310813bcbb7aSyt caps_ptr = P2ALIGN(pci_config_get8( 310913bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 311013bcbb7aSyt PCI_CONF_CAP_PTR), 4); 311113bcbb7aSyt } else { 311213bcbb7aSyt caps_ptr = PCI_CAP_NEXT_PTR_NULL; 311313bcbb7aSyt } 311413bcbb7aSyt 311513bcbb7aSyt /* 311613bcbb7aSyt * Walk capabilities if supported. 311713bcbb7aSyt */ 311813bcbb7aSyt for (cap_count = 0; caps_ptr != PCI_CAP_NEXT_PTR_NULL; ) { 311913bcbb7aSyt 312013bcbb7aSyt /* 312113bcbb7aSyt * Check that we haven't exceeded the maximum number of 312213bcbb7aSyt * capabilities and that the pointer is in a valid range. 312313bcbb7aSyt */ 312413bcbb7aSyt if (++cap_count > PCI_CAP_MAX_PTR) { 3125f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 3126f5f2d263SFred Herard "too many device capabilities", NULL); 312713bcbb7aSyt return (AHCI_FAILURE); 312813bcbb7aSyt } 312913bcbb7aSyt if (caps_ptr < PCI_CAP_PTR_OFF) { 3130f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 313113bcbb7aSyt "capabilities pointer 0x%x out of range", 313213bcbb7aSyt caps_ptr); 313313bcbb7aSyt return (AHCI_FAILURE); 313413bcbb7aSyt } 313513bcbb7aSyt 313613bcbb7aSyt /* 313713bcbb7aSyt * Get next capability and check that it is valid. 313813bcbb7aSyt * For now, we only support power management. 313913bcbb7aSyt */ 314013bcbb7aSyt cap = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 314113bcbb7aSyt caps_ptr); 314213bcbb7aSyt switch (cap) { 314313bcbb7aSyt case PCI_CAP_ID_PM: 314413bcbb7aSyt 314513bcbb7aSyt /* power management supported */ 314613bcbb7aSyt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PM; 314713bcbb7aSyt 314813bcbb7aSyt /* Save PMCSR offset */ 314913bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset = caps_ptr + PCI_PMCSR; 315013bcbb7aSyt 3151689d74b0Syt #if AHCI_DEBUG 315213bcbb7aSyt pmcap = pci_config_get16( 315313bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 315413bcbb7aSyt caps_ptr + PCI_PMCAP); 315513bcbb7aSyt pmcsr = pci_config_get16( 315613bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 315713bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset); 3158f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 315913bcbb7aSyt "Power Management capability found PCI_PMCAP " 316013bcbb7aSyt "= 0x%x PCI_PMCSR = 0x%x", pmcap, pmcsr); 316113bcbb7aSyt if ((pmcap & 0x3) == 0x3) 3162f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 316313bcbb7aSyt "PCI Power Management Interface " 3164f5f2d263SFred Herard "spec 1.2 compliant", NULL); 316513bcbb7aSyt #endif 316613bcbb7aSyt break; 316713bcbb7aSyt 316813bcbb7aSyt case PCI_CAP_ID_MSI: 31692c742e1fSying tian - Beijing China #if AHCI_DEBUG 31702c742e1fSying tian - Beijing China msimc = pci_config_get16( 31712c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_pci_conf_handle, 31722c742e1fSying tian - Beijing China caps_ptr + PCI_MSI_CTRL); 3173f5f2d263SFred Herard AHCIDBG(AHCIDBG_MSI, ahci_ctlp, 31742c742e1fSying tian - Beijing China "Message Signaled Interrupt capability found " 31752c742e1fSying tian - Beijing China "MSICAP_MC.MMC = 0x%x", (msimc & 0xe) >> 1); 31762c742e1fSying tian - Beijing China #endif 3177f5f2d263SFred Herard AHCIDBG(AHCIDBG_MSI, ahci_ctlp, 3178f5f2d263SFred Herard "MSI capability found", NULL); 317913bcbb7aSyt break; 318013bcbb7aSyt 318113bcbb7aSyt case PCI_CAP_ID_PCIX: 3182f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3183f5f2d263SFred Herard "PCI-X capability found", NULL); 318413bcbb7aSyt break; 318513bcbb7aSyt 318613bcbb7aSyt case PCI_CAP_ID_PCI_E: 3187f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3188f5f2d263SFred Herard "PCI Express capability found", NULL); 318913bcbb7aSyt break; 319013bcbb7aSyt 319113bcbb7aSyt case PCI_CAP_ID_MSI_X: 3192f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3193f5f2d263SFred Herard "MSI-X capability found", NULL); 319413bcbb7aSyt break; 319513bcbb7aSyt 319613bcbb7aSyt case PCI_CAP_ID_SATA: 3197f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3198f5f2d263SFred Herard "SATA capability found", NULL); 319913bcbb7aSyt break; 320013bcbb7aSyt 3201a9440e8dSyt case PCI_CAP_ID_VS: 3202f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3203f5f2d263SFred Herard "Vendor Specific capability found", NULL); 3204a9440e8dSyt break; 3205a9440e8dSyt 320613bcbb7aSyt default: 3207f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 320813bcbb7aSyt "unrecognized capability 0x%x", cap); 320913bcbb7aSyt break; 321013bcbb7aSyt } 321113bcbb7aSyt 321213bcbb7aSyt /* 321313bcbb7aSyt * Get next capabilities pointer and clear bits 0,1. 321413bcbb7aSyt */ 321513bcbb7aSyt caps_ptr = P2ALIGN(pci_config_get8( 321613bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 321713bcbb7aSyt (caps_ptr + PCI_CAP_NEXT_PTR)), 4); 321813bcbb7aSyt } 321913bcbb7aSyt 322013bcbb7aSyt return (AHCI_SUCCESS); 322113bcbb7aSyt } 322213bcbb7aSyt 32232fcbc377Syt /* 32242fcbc377Syt * AHCI device reset ...; a single device on one of the ports is reset, 32252fcbc377Syt * but the HBA and physical communication remain intact. This is the 32262fcbc377Syt * least intrusive. 32272fcbc377Syt * 32282fcbc377Syt * When issuing a software reset sequence, there should not be other 32292fcbc377Syt * commands in the command list, so we will first clear and then re-set 32302fcbc377Syt * PxCMD.ST to clear PxCI. And before issuing the software reset, 32312fcbc377Syt * the port must be idle and PxTFD.STS.BSY and PxTFD.STS.DRQ must be 3232b2e3645aSying tian - Beijing China * cleared unless command list override (PxCMD.CLO) is supported. 32332fcbc377Syt * 32342fcbc377Syt * WARNING!!! ahciport_mutex should be acquired and PxCMD.FRE should be 32352fcbc377Syt * set before the function is called. 32362fcbc377Syt */ 32372fcbc377Syt static int 32382fcbc377Syt ahci_software_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 32392fcbc377Syt uint8_t port) 32402fcbc377Syt { 32412fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 32422fcbc377Syt ahci_cmd_table_t *cmd_table; 32432fcbc377Syt ahci_cmd_header_t *cmd_header; 324468d33a25Syt uint32_t port_cmd_status, port_cmd_issue, port_task_file; 32452fcbc377Syt int slot, loop_count; 32460a4c4cecSXiao-Yu Zhang int rval = AHCI_FAILURE; 32472fcbc377Syt 3248f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 32492fcbc377Syt "Port %d device resetting", port); 32502fcbc377Syt 32510a4c4cecSXiao-Yu Zhang /* First clear PxCMD.ST (AHCI v1.2 10.4.1) */ 32520a4c4cecSXiao-Yu Zhang if (ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, 32530a4c4cecSXiao-Yu Zhang port) != AHCI_SUCCESS) { 3254f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 32550a4c4cecSXiao-Yu Zhang "ahci_software_reset: cannot stop HBA port %d.", port); 32560a4c4cecSXiao-Yu Zhang goto out; 32570a4c4cecSXiao-Yu Zhang } 32582fcbc377Syt 32592fcbc377Syt /* Check PxTFD.STS.BSY and PxTFD.STS.DRQ */ 32602fcbc377Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 32612fcbc377Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 32622fcbc377Syt 32632fcbc377Syt if (port_task_file & AHCI_TFD_STS_BSY || 32642fcbc377Syt port_task_file & AHCI_TFD_STS_DRQ) { 32650a4c4cecSXiao-Yu Zhang if (!(ahci_ctlp->ahcictl_cap & AHCI_CAP_SCLO)) { 3266f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 32670a4c4cecSXiao-Yu Zhang "PxTFD.STS.BSY/DRQ is set (PxTFD=0x%x), " 32680a4c4cecSXiao-Yu Zhang "cannot issue a software reset.", port_task_file); 32690a4c4cecSXiao-Yu Zhang goto out; 32700a4c4cecSXiao-Yu Zhang } 32710a4c4cecSXiao-Yu Zhang 32720a4c4cecSXiao-Yu Zhang /* 32730a4c4cecSXiao-Yu Zhang * If HBA Support CLO, as Command List Override (CAP.SCLO is 32740a4c4cecSXiao-Yu Zhang * set), PxCMD.CLO bit should be set before set PxCMD.ST, in 32750a4c4cecSXiao-Yu Zhang * order to clear PxTFD.STS.BSY and PxTFD.STS.DRQ. 32760a4c4cecSXiao-Yu Zhang */ 3277f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 3278f5f2d263SFred Herard "PxTFD.STS.BSY/DRQ is set, try SCLO.", NULL) 32790a4c4cecSXiao-Yu Zhang 3280b2e3645aSying tian - Beijing China port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 3281b2e3645aSying tian - Beijing China (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 32820a4c4cecSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 32830a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 32840a4c4cecSXiao-Yu Zhang port_cmd_status|AHCI_CMD_STATUS_CLO); 32850a4c4cecSXiao-Yu Zhang 32860a4c4cecSXiao-Yu Zhang /* Waiting till PxCMD.SCLO bit is cleared */ 32870a4c4cecSXiao-Yu Zhang loop_count = 0; 32880a4c4cecSXiao-Yu Zhang do { 32890a4c4cecSXiao-Yu Zhang /* Wait for 10 millisec */ 32900a4c4cecSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 32910a4c4cecSXiao-Yu Zhang 32920a4c4cecSXiao-Yu Zhang /* We are effectively timing out after 1 sec. */ 32930a4c4cecSXiao-Yu Zhang if (loop_count++ > 100) { 3294f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 32950a4c4cecSXiao-Yu Zhang "SCLO time out. port %d is busy.", port); 32960a4c4cecSXiao-Yu Zhang goto out; 32970a4c4cecSXiao-Yu Zhang } 32980a4c4cecSXiao-Yu Zhang 32990a4c4cecSXiao-Yu Zhang port_cmd_status = 33000a4c4cecSXiao-Yu Zhang ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33010a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 33020a4c4cecSXiao-Yu Zhang } while (port_cmd_status & AHCI_CMD_STATUS_CLO); 33030a4c4cecSXiao-Yu Zhang 33040a4c4cecSXiao-Yu Zhang /* Re-check */ 33050a4c4cecSXiao-Yu Zhang port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33060a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 33070a4c4cecSXiao-Yu Zhang if (port_task_file & AHCI_TFD_STS_BSY || 33080a4c4cecSXiao-Yu Zhang port_task_file & AHCI_TFD_STS_DRQ) { 3309f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 33100a4c4cecSXiao-Yu Zhang "SCLO cannot clear PxTFD.STS.BSY/DRQ (PxTFD=0x%x)", 33110a4c4cecSXiao-Yu Zhang port_task_file); 33120a4c4cecSXiao-Yu Zhang goto out; 33132fcbc377Syt } 33142fcbc377Syt } 33152fcbc377Syt 33160a4c4cecSXiao-Yu Zhang /* Then start port */ 33170a4c4cecSXiao-Yu Zhang if (ahci_start_port(ahci_ctlp, ahci_portp, port) 33180a4c4cecSXiao-Yu Zhang != AHCI_SUCCESS) { 3319f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 33200a4c4cecSXiao-Yu Zhang "ahci_software_reset: cannot start AHCI port %d.", port); 33210a4c4cecSXiao-Yu Zhang goto out; 33222fcbc377Syt } 33232fcbc377Syt 33240a4c4cecSXiao-Yu Zhang /* 33250a4c4cecSXiao-Yu Zhang * When ahci_port.ahciport_mop_in_progress is set, A non-zero 33260a4c4cecSXiao-Yu Zhang * ahci_port.ahciport_pending_ncq_tags may fail 33270a4c4cecSXiao-Yu Zhang * ahci_claim_free_slot(). Actually according to spec, by clearing 33280a4c4cecSXiao-Yu Zhang * PxCMD.ST there is no command outstanding while executing software 33290a4c4cecSXiao-Yu Zhang * reseting. Hence we directly use slot 0 instead of 33300a4c4cecSXiao-Yu Zhang * ahci_claim_free_slot(). 33310a4c4cecSXiao-Yu Zhang */ 33320a4c4cecSXiao-Yu Zhang slot = 0; 33330a4c4cecSXiao-Yu Zhang 3334b2e3645aSying tian - Beijing China /* Now send the first H2D Register FIS with SRST set to 1 */ 33352fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 33362fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 33372fcbc377Syt 33382fcbc377Syt h2d_register_fisp = 33392fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 33402fcbc377Syt 33412fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 33422fcbc377Syt SET_FIS_DEVCTL(h2d_register_fisp, SATA_DEVCTL_SRST); 33432fcbc377Syt 33442fcbc377Syt /* Set Command Header in Command List */ 33452fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 33462fcbc377Syt BZERO_DESCR_INFO(cmd_header); 33472fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 33482fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 33492fcbc377Syt 33502fcbc377Syt SET_CLEAR_BUSY_UPON_R_OK(cmd_header, 1); 33512fcbc377Syt SET_RESET(cmd_header, 1); 33522fcbc377Syt SET_WRITE(cmd_header, 1); 33532fcbc377Syt 33542fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 33552fcbc377Syt 0, 33562fcbc377Syt ahci_cmd_table_size, 33572fcbc377Syt DDI_DMA_SYNC_FORDEV); 33582fcbc377Syt 33592fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 33602fcbc377Syt slot * sizeof (ahci_cmd_header_t), 33612fcbc377Syt sizeof (ahci_cmd_header_t), 33622fcbc377Syt DDI_DMA_SYNC_FORDEV); 33632fcbc377Syt 33642fcbc377Syt /* Indicate to the HBA that a command is active. */ 33652fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33662fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 33672fcbc377Syt (0x1 << slot)); 33682fcbc377Syt 33692fcbc377Syt loop_count = 0; 33702fcbc377Syt 33712fcbc377Syt /* Loop till the first command is finished */ 33722fcbc377Syt do { 33732fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33742fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 33752fcbc377Syt 3376b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 33772fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 3378f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 3379259105bcSying tian - Beijing China "the first SRST FIS is timed out, " 3380259105bcSying tian - Beijing China "loop_count = %d", loop_count); 33810a4c4cecSXiao-Yu Zhang goto out; 33822fcbc377Syt } 3383259105bcSying tian - Beijing China 33842fcbc377Syt /* Wait for 10 millisec */ 33852fcbc377Syt delay(AHCI_10MS_TICKS); 3386259105bcSying tian - Beijing China } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 33872fcbc377Syt 3388f5f2d263SFred Herard AHCIDBG(AHCIDBG_POLL_LOOP, ahci_ctlp, 33892fcbc377Syt "ahci_software_reset: 1st loop count: %d, " 33902fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 33912fcbc377Syt loop_count, port_cmd_issue, slot); 33922fcbc377Syt 3393a419422eSXiao-Yu Zhang /* According to ATA spec, we need wait at least 5 microsecs here. */ 3394a419422eSXiao-Yu Zhang drv_usecwait(AHCI_1MS_USECS); 3395a419422eSXiao-Yu Zhang 3396b2e3645aSying tian - Beijing China /* Now send the second H2D Register FIS with SRST cleard to zero */ 33972fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 33982fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 33992fcbc377Syt 34002fcbc377Syt h2d_register_fisp = 34012fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 34022fcbc377Syt 34032fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 34042fcbc377Syt 34052fcbc377Syt /* Set Command Header in Command List */ 34062fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 34072fcbc377Syt BZERO_DESCR_INFO(cmd_header); 34082fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 34092fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 34102fcbc377Syt 34112fcbc377Syt SET_WRITE(cmd_header, 1); 34122fcbc377Syt 34132fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 34142fcbc377Syt 0, 34152fcbc377Syt ahci_cmd_table_size, 34162fcbc377Syt DDI_DMA_SYNC_FORDEV); 34172fcbc377Syt 34182fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 34192fcbc377Syt slot * sizeof (ahci_cmd_header_t), 34202fcbc377Syt sizeof (ahci_cmd_header_t), 34212fcbc377Syt DDI_DMA_SYNC_FORDEV); 34222fcbc377Syt 34232fcbc377Syt /* Indicate to the HBA that a command is active. */ 34242fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 34252fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 34262fcbc377Syt (0x1 << slot)); 34272fcbc377Syt 34282fcbc377Syt loop_count = 0; 34292fcbc377Syt 34302fcbc377Syt /* Loop till the second command is finished */ 34312fcbc377Syt do { 34322fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34332fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 34342fcbc377Syt 3435b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 34362fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 3437f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 3438259105bcSying tian - Beijing China "the second SRST FIS is timed out, " 3439259105bcSying tian - Beijing China "loop_count = %d", loop_count); 34400a4c4cecSXiao-Yu Zhang goto out; 34412fcbc377Syt } 3442259105bcSying tian - Beijing China 34432fcbc377Syt /* Wait for 10 millisec */ 34442fcbc377Syt delay(AHCI_10MS_TICKS); 3445259105bcSying tian - Beijing China } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 34462fcbc377Syt 3447f5f2d263SFred Herard AHCIDBG(AHCIDBG_POLL_LOOP, ahci_ctlp, 34482fcbc377Syt "ahci_software_reset: 2nd loop count: %d, " 34492fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 34502fcbc377Syt loop_count, port_cmd_issue, slot); 34512fcbc377Syt 34522fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, slot); 34532fcbc377Syt 34540a4c4cecSXiao-Yu Zhang rval = AHCI_SUCCESS; 34550a4c4cecSXiao-Yu Zhang out: 3456f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 34570a4c4cecSXiao-Yu Zhang "ahci_software_reset: %s at port %d", 34580a4c4cecSXiao-Yu Zhang rval == AHCI_SUCCESS ? "succeed" : "failed", 34590a4c4cecSXiao-Yu Zhang port); 34600a4c4cecSXiao-Yu Zhang 34610a4c4cecSXiao-Yu Zhang return (rval); 34622fcbc377Syt } 34632fcbc377Syt 34642fcbc377Syt /* 34652fcbc377Syt * AHCI port reset ...; the physical communication between the HBA and device 34662fcbc377Syt * on a port are disabled. This is more intrusive. 34672fcbc377Syt * 346868d33a25Syt * When an HBA or port reset occurs, Phy communication is going to 34692fcbc377Syt * be re-established with the device through a COMRESET followed by the 34702fcbc377Syt * normal out-of-band communication sequence defined in Serial ATA. AT 34712fcbc377Syt * the end of reset, the device, if working properly, will send a D2H 34722fcbc377Syt * Register FIS, which contains the device signature. When the HBA receives 34732fcbc377Syt * this FIS, it updates PxTFD.STS and PxTFD.ERR register fields, and updates 34742fcbc377Syt * the PxSIG register with the signature. 34752fcbc377Syt * 34762fcbc377Syt * Staggered spin-up is an optional feature in SATA II, and it enables an HBA 34772fcbc377Syt * to individually spin-up attached devices. Please refer to chapter 10.9 of 347868d33a25Syt * AHCI 1.0 spec. 34792fcbc377Syt */ 34802fcbc377Syt /* 348182263d52Syt * WARNING!!! ahciport_mutex should be acquired, and PxCMD.ST should be also 348282263d52Syt * cleared before the function is called. 34832fcbc377Syt */ 34842fcbc377Syt static int 34852fcbc377Syt ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 34862fcbc377Syt { 34872fcbc377Syt uint32_t cap_status, port_cmd_status; 34880a4c4cecSXiao-Yu Zhang uint32_t port_scontrol, port_sstatus, port_serror; 3489689d74b0Syt uint32_t port_intr_status, port_task_file; 34900a4c4cecSXiao-Yu Zhang 34912fcbc377Syt int loop_count; 3492a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 34932fcbc377Syt 3494f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 34952fcbc377Syt "Port %d port resetting...", port); 349668d33a25Syt ahci_portp->ahciport_port_state = 0; 34972fcbc377Syt 34982fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34992fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 35002fcbc377Syt 35012fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35022fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 35032fcbc377Syt 35042fcbc377Syt if (cap_status & AHCI_HBA_CAP_SSS) { 35052fcbc377Syt /* 35062fcbc377Syt * HBA support staggered spin-up, if the port has 35072fcbc377Syt * not spin up yet, then force it to do spin-up 35082fcbc377Syt */ 35092fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) { 35102fcbc377Syt if (!(ahci_portp->ahciport_flags 351168d33a25Syt & AHCI_PORT_FLAG_SPINUP)) { 3512f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 35132fcbc377Syt "Port %d PxCMD.SUD is zero, force " 35142fcbc377Syt "it to do spin-up", port); 35152fcbc377Syt ahci_portp->ahciport_flags |= 351668d33a25Syt AHCI_PORT_FLAG_SPINUP; 35172fcbc377Syt } 35182fcbc377Syt } 35192fcbc377Syt } else { 35202fcbc377Syt /* 35212fcbc377Syt * HBA doesn't support stagger spin-up, force it 35222fcbc377Syt * to do normal COMRESET 35232fcbc377Syt */ 35242fcbc377Syt if (ahci_portp->ahciport_flags & 352568d33a25Syt AHCI_PORT_FLAG_SPINUP) { 3526f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 35272fcbc377Syt "HBA does not support staggered spin-up " 3528f5f2d263SFred Herard "force it to do normal COMRESET", NULL); 35292fcbc377Syt ahci_portp->ahciport_flags &= 353068d33a25Syt ~AHCI_PORT_FLAG_SPINUP; 35312fcbc377Syt } 35322fcbc377Syt } 35332fcbc377Syt 353468d33a25Syt if (!(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_SPINUP)) { 35352fcbc377Syt /* Do normal COMRESET */ 3536f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 35372fcbc377Syt "ahci_port_reset: do normal COMRESET", port); 35382fcbc377Syt 353995c11c1fSyt /* 354095c11c1fSyt * According to the spec, SUD bit should be set here, 354195c11c1fSyt * but JMicron JMB363 doesn't follow it, so remove 354295c11c1fSyt * the assertion, and just print a debug message. 354395c11c1fSyt */ 354495c11c1fSyt #if AHCI_DEBUG 354595c11c1fSyt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) 3546f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 354795c11c1fSyt "port %d SUD bit not set", port) 354895c11c1fSyt #endif 35492fcbc377Syt 35502fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35512fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 355282263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_COMRESET); 35532fcbc377Syt 35542fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35552fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 35562fcbc377Syt port_scontrol); 35572fcbc377Syt 35582fcbc377Syt /* Enable PxCMD.FRE to read device */ 35592fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35602fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 35612fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 35622fcbc377Syt 356368d33a25Syt /* 356468d33a25Syt * Give time for COMRESET to percolate, according to the AHCI 356568d33a25Syt * spec, software shall wait at least 1 millisecond before 356668d33a25Syt * clearing PxSCTL.DET 356768d33a25Syt */ 35682fcbc377Syt delay(AHCI_1MS_TICKS*2); 35692fcbc377Syt 35702fcbc377Syt /* Fetch the SCONTROL again and rewrite the DET part with 0 */ 35712fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35722fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 357382263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 35742fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35752fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 35762fcbc377Syt port_scontrol); 35772fcbc377Syt } else { 35782fcbc377Syt /* Do staggered spin-up */ 35792fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35802fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 358182263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 35822fcbc377Syt 35832fcbc377Syt /* PxSCTL.DET must be 0 */ 35842fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35852fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 35862fcbc377Syt port_scontrol); 35872fcbc377Syt 35882fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_SUD; 35892fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35902fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 35912fcbc377Syt port_cmd_status); 35922fcbc377Syt 35932fcbc377Syt /* 0 -> 1 edge */ 35942fcbc377Syt delay(AHCI_1MS_TICKS*2); 35952fcbc377Syt 35962fcbc377Syt /* Set PxCMD.SUD to 1 */ 35972fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35982fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 35992fcbc377Syt port_cmd_status |= AHCI_CMD_STATUS_SUD; 36002fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 36012fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 36022fcbc377Syt port_cmd_status); 36032fcbc377Syt 36042fcbc377Syt /* Enable PxCMD.FRE to read device */ 36052fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 36062fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 36072fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 36082fcbc377Syt } 36092fcbc377Syt 36102fcbc377Syt /* 361168d33a25Syt * The port enters P:StartComm state, and HBA tells link layer to 361268d33a25Syt * start communication, which involves sending COMRESET to device. 361368d33a25Syt * And the HBA resets PxTFD.STS to 7Fh. 361468d33a25Syt * 361568d33a25Syt * When a COMINIT is received from the device, then the port enters 361668d33a25Syt * P:ComInit state. And HBA sets PxTFD.STS to FFh or 80h. HBA sets 361768d33a25Syt * PxSSTS.DET to 1h to indicate a device is detected but communication 361868d33a25Syt * is not yet established. HBA sets PxSERR.DIAG.X to '1' to indicate 361968d33a25Syt * a COMINIT has been received. 36202fcbc377Syt */ 36212fcbc377Syt /* 36222fcbc377Syt * The DET field is valid only if IPM field indicates 36232fcbc377Syt * that the interface is in active state. 36242fcbc377Syt */ 36252fcbc377Syt loop_count = 0; 36262fcbc377Syt do { 36272fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 36282fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 36292fcbc377Syt 363082263d52Syt if (SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) { 36312fcbc377Syt /* 36322fcbc377Syt * If the interface is not active, the DET field 36332fcbc377Syt * is considered not accurate. So we want to 36342fcbc377Syt * continue looping. 36352fcbc377Syt */ 363682263d52Syt SSTATUS_SET_DET(port_sstatus, SSTATUS_DET_NODEV); 36372fcbc377Syt } 36382fcbc377Syt 36392fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SSTATUS) { 36402fcbc377Syt /* 36412fcbc377Syt * We are effectively timing out after 0.1 sec. 36422fcbc377Syt */ 36432fcbc377Syt break; 36442fcbc377Syt } 36452fcbc377Syt 36462fcbc377Syt /* Wait for 10 millisec */ 36472fcbc377Syt delay(AHCI_10MS_TICKS); 364882263d52Syt } while (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM); 36492fcbc377Syt 3650f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 365168d33a25Syt "ahci_port_reset: 1st loop count: %d, " 365268d33a25Syt "port_sstatus = 0x%x port %d", 365368d33a25Syt loop_count, port_sstatus, port); 36542fcbc377Syt 365582263d52Syt if ((SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) || 365682263d52Syt (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM)) { 36572fcbc377Syt /* 36582fcbc377Syt * Either the port is not active or there 36592fcbc377Syt * is no device present. 36602fcbc377Syt */ 36612fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_NONE; 36620a4c4cecSXiao-Yu Zhang return (AHCI_SUCCESS); 36632fcbc377Syt } 36642fcbc377Syt 366568d33a25Syt /* Now we can make sure there is a device connected to the port */ 366668d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 366768d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 36680a4c4cecSXiao-Yu Zhang port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 36690a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 36702fcbc377Syt 36710a4c4cecSXiao-Yu Zhang /* 36720a4c4cecSXiao-Yu Zhang * A COMINIT signal is supposed to be received 36730a4c4cecSXiao-Yu Zhang * PxSERR.DIAG.X or PxIS.PCS should be set 36740a4c4cecSXiao-Yu Zhang */ 36750a4c4cecSXiao-Yu Zhang if (!(port_intr_status & AHCI_INTR_STATUS_PCS) && 36760a4c4cecSXiao-Yu Zhang !(port_serror & SERROR_EXCHANGED_ERR)) { 3677a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d " 3678a9440e8dSyt "COMINIT signal from the device not received", 3679a9440e8dSyt instance, port); 368068d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED; 36810a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 368268d33a25Syt } 36832fcbc377Syt 368495c11c1fSyt /* 368595c11c1fSyt * According to the spec, when PxSCTL.DET is set to 0h, upon 368695c11c1fSyt * receiving a COMINIT from the attached device, PxTFD.STS.BSY 368795c11c1fSyt * shall be set to '1' by the HBA. 368895c11c1fSyt * 368995c11c1fSyt * However, we found JMicron JMB363 doesn't follow this, so 369095c11c1fSyt * remove this check, and just print a debug message. 369195c11c1fSyt */ 369295c11c1fSyt #if AHCI_DEBUG 369368d33a25Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 369468d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 369568d33a25Syt if (!(port_task_file & AHCI_TFD_STS_BSY)) { 3696f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: " 369795c11c1fSyt "port %d BSY bit is not set after COMINIT signal " 369895c11c1fSyt "is received", port); 369968d33a25Syt } 370095c11c1fSyt #endif 37012fcbc377Syt 370268d33a25Syt /* 370368d33a25Syt * PxSERR.DIAG.X has to be cleared in order to update PxTFD with 370468d33a25Syt * the D2H FIS received by HBA. 370568d33a25Syt */ 370668d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 370768d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 370882263d52Syt SERROR_EXCHANGED_ERR); 37092fcbc377Syt 371068d33a25Syt /* 37110a4c4cecSXiao-Yu Zhang * Devices should return a FIS contains its signature to HBA after 37120a4c4cecSXiao-Yu Zhang * COMINIT signal. Check whether a D2H FIS is received by polling 37130a4c4cecSXiao-Yu Zhang * PxTFD.STS.ERR bit. 371468d33a25Syt */ 371568d33a25Syt loop_count = 0; 371668d33a25Syt do { 37170a4c4cecSXiao-Yu Zhang /* Wait for 10 millisec */ 37180a4c4cecSXiao-Yu Zhang delay(AHCI_10MS_TICKS); 37192fcbc377Syt 372068d33a25Syt if (loop_count++ > AHCI_POLLRATE_PORT_TFD_ERROR) { 372168d33a25Syt /* 372268d33a25Syt * We are effectively timing out after 11 sec. 372368d33a25Syt */ 37240a4c4cecSXiao-Yu Zhang cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d " 37250a4c4cecSXiao-Yu Zhang "the device hardware has been initialized and " 37260a4c4cecSXiao-Yu Zhang "the power-up diagnostics failed", 37270a4c4cecSXiao-Yu Zhang instance, port); 37280a4c4cecSXiao-Yu Zhang 3729f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: " 37300a4c4cecSXiao-Yu Zhang "port %d PxTFD.STS.ERR is not set, we need another " 37310a4c4cecSXiao-Yu Zhang "software reset.", port); 37320a4c4cecSXiao-Yu Zhang 37330a4c4cecSXiao-Yu Zhang /* Clear port serror register for the port */ 37340a4c4cecSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 37350a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 37360a4c4cecSXiao-Yu Zhang AHCI_SERROR_CLEAR_ALL); 37370a4c4cecSXiao-Yu Zhang 37380a4c4cecSXiao-Yu Zhang /* Try another software reset. */ 37390a4c4cecSXiao-Yu Zhang if (ahci_software_reset(ahci_ctlp, ahci_portp, 37400a4c4cecSXiao-Yu Zhang port) != AHCI_SUCCESS) { 37410a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state |= 37420a4c4cecSXiao-Yu Zhang SATA_PSTATE_FAILED; 37430a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 37440a4c4cecSXiao-Yu Zhang } 374568d33a25Syt break; 374668d33a25Syt } 37472fcbc377Syt 37480a4c4cecSXiao-Yu Zhang /* 37490a4c4cecSXiao-Yu Zhang * The Error bit '1' means COMRESET is finished successfully 37500a4c4cecSXiao-Yu Zhang * The device hardware has been initialized and the power-up 37510a4c4cecSXiao-Yu Zhang * diagnostics successfully completed. The device requests 37520a4c4cecSXiao-Yu Zhang * that the Transport layer transmit a Register - D2H FIS to 37530a4c4cecSXiao-Yu Zhang * the host. (SATA spec 11.5, v2.6) 37540a4c4cecSXiao-Yu Zhang */ 37550a4c4cecSXiao-Yu Zhang port_task_file = 37560a4c4cecSXiao-Yu Zhang ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 37570a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 375868d33a25Syt } while (((port_task_file & AHCI_TFD_ERR_MASK) 37590a4c4cecSXiao-Yu Zhang >> AHCI_TFD_ERR_SHIFT) != AHCI_TFD_ERR_SGS); 37602fcbc377Syt 3761f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 37620a4c4cecSXiao-Yu Zhang "ahci_port_reset: 2nd loop count: %d, " 37630a4c4cecSXiao-Yu Zhang "port_task_file = 0x%x port %d", 376468d33a25Syt loop_count, port_task_file, port); 37652fcbc377Syt 376668d33a25Syt /* Clear port serror register for the port */ 376768d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 376868d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 376968d33a25Syt AHCI_SERROR_CLEAR_ALL); 377068d33a25Syt 37710a4c4cecSXiao-Yu Zhang /* Set port as ready */ 37720a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state |= SATA_STATE_READY; 377368d33a25Syt 3774f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO|AHCIDBG_ERRS, ahci_ctlp, 37750a4c4cecSXiao-Yu Zhang "ahci_port_reset: succeed at port %d.", port); 37760a4c4cecSXiao-Yu Zhang return (AHCI_SUCCESS); 37772fcbc377Syt } 37782fcbc377Syt 37792fcbc377Syt /* 37802fcbc377Syt * AHCI HBA reset ...; the entire HBA is reset, and all ports are disabled. 37812fcbc377Syt * This is the most intrusive. 37822fcbc377Syt * 378368d33a25Syt * When an HBA reset occurs, Phy communication will be re-established with 378468d33a25Syt * the device through a COMRESET followed by the normal out-of-band 378568d33a25Syt * communication sequence defined in Serial ATA. AT the end of reset, the 378668d33a25Syt * device, if working properly, will send a D2H Register FIS, which contains 378768d33a25Syt * the device signature. When the HBA receives this FIS, it updates PxTFD.STS 378868d33a25Syt * and PxTFD.ERR register fields, and updates the PxSIG register with the 378968d33a25Syt * signature. 37902fcbc377Syt * 37912fcbc377Syt * Remember to set GHC.AE to 1 before calling ahci_hba_reset. 37922fcbc377Syt */ 37932fcbc377Syt static int 37942fcbc377Syt ahci_hba_reset(ahci_ctl_t *ahci_ctlp) 37952fcbc377Syt { 37962fcbc377Syt ahci_port_t *ahci_portp; 37972fcbc377Syt uint32_t ghc_control; 37982fcbc377Syt uint8_t port; 37992fcbc377Syt int loop_count; 38002fcbc377Syt int rval = AHCI_SUCCESS; 38012fcbc377Syt 3802f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, "HBA resetting", 3803f5f2d263SFred Herard NULL); 38042fcbc377Syt 380568d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 380668d33a25Syt 38072fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 38082fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 38092fcbc377Syt 38102fcbc377Syt /* Setting GHC.HR to 1, remember GHC.AE is already set to 1 before */ 38112fcbc377Syt ghc_control |= AHCI_HBA_GHC_HR; 38122fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 38132fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 38142fcbc377Syt 38152fcbc377Syt /* 38162fcbc377Syt * Wait until HBA Reset complete or timeout 38172fcbc377Syt */ 38182fcbc377Syt loop_count = 0; 38192fcbc377Syt do { 38202fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 38212fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 38222fcbc377Syt 38232fcbc377Syt if (loop_count++ > AHCI_POLLRATE_HBA_RESET) { 3824f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 38252fcbc377Syt "ahci hba reset is timing out, " 38262fcbc377Syt "ghc_control = 0x%x", ghc_control); 38272fcbc377Syt /* We are effectively timing out after 1 sec. */ 38282fcbc377Syt break; 38292fcbc377Syt } 38302fcbc377Syt 38312fcbc377Syt /* Wait for 10 millisec */ 38322fcbc377Syt delay(AHCI_10MS_TICKS); 38332fcbc377Syt } while (ghc_control & AHCI_HBA_GHC_HR); 38342fcbc377Syt 3835f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 38362fcbc377Syt "ahci_hba_reset: 1st loop count: %d, " 38372fcbc377Syt "ghc_control = 0x%x", loop_count, ghc_control); 38382fcbc377Syt 38392fcbc377Syt if (ghc_control & AHCI_HBA_GHC_HR) { 38402fcbc377Syt /* The hba is not reset for some reasons */ 3841f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 3842f5f2d263SFred Herard "hba reset failed: HBA in a hung or locked state", NULL); 384368d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 38442fcbc377Syt return (AHCI_FAILURE); 38452fcbc377Syt } 38462fcbc377Syt 38472fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 38482fcbc377Syt /* Only check implemented ports */ 38492fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 38502fcbc377Syt continue; 38512fcbc377Syt } 38522fcbc377Syt 38532fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 38542fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 38552fcbc377Syt 38562fcbc377Syt if (ahci_port_reset(ahci_ctlp, ahci_portp, port) 38572fcbc377Syt != AHCI_SUCCESS) { 38582fcbc377Syt rval = AHCI_FAILURE; 3859f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 38602fcbc377Syt "ahci_hba_reset: port %d failed", port); 38612fcbc377Syt } 38622fcbc377Syt 38632fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 38642fcbc377Syt } 38652fcbc377Syt 38662fcbc377Syt /* 38672fcbc377Syt * Indicate that system software is AHCI aware by setting 38682fcbc377Syt * GHC.AE to 1 38692fcbc377Syt */ 38702fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 38712fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 38722fcbc377Syt 38732fcbc377Syt ghc_control |= AHCI_HBA_GHC_AE; 38742fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 38752fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 38762fcbc377Syt 387768d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 387868d33a25Syt 38792fcbc377Syt return (rval); 38802fcbc377Syt } 38812fcbc377Syt 38822fcbc377Syt /* 38832fcbc377Syt * This routine is only called from AHCI_ATTACH or phyrdy change 38840a4c4cecSXiao-Yu Zhang * case. It first calls software reset, then stop the port and try to 38850a4c4cecSXiao-Yu Zhang * read PxSIG register to find the type of device attached to the port. 38860a4c4cecSXiao-Yu Zhang * 38870a4c4cecSXiao-Yu Zhang * The caller should make sure a valid device exists on specified port and 38880a4c4cecSXiao-Yu Zhang * physical communication has been established so that the signature could 38890a4c4cecSXiao-Yu Zhang * be retrieved by software reset. 38902fcbc377Syt * 38912fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 389268d33a25Syt * is called. And the port interrupt is disabled. 38932fcbc377Syt */ 389468d33a25Syt static void 38952fcbc377Syt ahci_find_dev_signature(ahci_ctl_t *ahci_ctlp, 38962fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 38972fcbc377Syt { 38982fcbc377Syt uint32_t signature; 38992fcbc377Syt 3900f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 390168d33a25Syt "ahci_find_dev_signature enter: port %d", port); 39022fcbc377Syt 390368d33a25Syt ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 39042fcbc377Syt 39050a4c4cecSXiao-Yu Zhang /* Issue a software reset to get the signature */ 39060a4c4cecSXiao-Yu Zhang if (ahci_software_reset(ahci_ctlp, ahci_portp, port) 39070a4c4cecSXiao-Yu Zhang != AHCI_SUCCESS) { 3908f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 39090a4c4cecSXiao-Yu Zhang "ahci_find_dev_signature: software reset failed " 3910259105bcSying tian - Beijing China "at port %d, cannot get signature.", port); 39110a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 391268d33a25Syt return; 39132fcbc377Syt } 39142fcbc377Syt 39150a4c4cecSXiao-Yu Zhang /* 39160a4c4cecSXiao-Yu Zhang * ahci_software_reset has started the port, so we need manually stop 39170a4c4cecSXiao-Yu Zhang * the port again. 39180a4c4cecSXiao-Yu Zhang */ 39190a4c4cecSXiao-Yu Zhang if (ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, port) 39200a4c4cecSXiao-Yu Zhang != AHCI_SUCCESS) { 3921f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 39220a4c4cecSXiao-Yu Zhang "ahci_find_dev_signature: cannot stop port %d.", port); 39230a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 392468d33a25Syt return; 392568d33a25Syt } 39262fcbc377Syt 39270a4c4cecSXiao-Yu Zhang /* Now we can make sure that a valid signature is received. */ 39282fcbc377Syt signature = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 39292fcbc377Syt (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port)); 39302fcbc377Syt 3931f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INFO, ahci_ctlp, 393268d33a25Syt "ahci_find_dev_signature: port %d signature = 0x%x", 393368d33a25Syt port, signature); 39342fcbc377Syt 39352fcbc377Syt switch (signature) { 39362fcbc377Syt 39372fcbc377Syt case AHCI_SIGNATURE_DISK: 39382fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_ATADISK; 3939f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 39402fcbc377Syt "Disk is found at port: %d", port); 39412fcbc377Syt break; 39422fcbc377Syt 39432fcbc377Syt case AHCI_SIGNATURE_ATAPI: 394438547057Sying tian - Beijing China ahci_portp->ahciport_device_type = SATA_DTYPE_ATAPI; 3945f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 39462fcbc377Syt "ATAPI device is found at port: %d", port); 39472fcbc377Syt break; 39482fcbc377Syt 39492fcbc377Syt case AHCI_SIGNATURE_PORT_MULTIPLIER: 39502fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_PMULT; 3951f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 39522fcbc377Syt "Port Multiplier is found at port: %d", port); 39532fcbc377Syt break; 39542fcbc377Syt 39552fcbc377Syt default: 39562fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 3957f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 39582fcbc377Syt "Unknown device is found at port: %d", port); 39592fcbc377Syt } 39602fcbc377Syt } 39612fcbc377Syt 396213bcbb7aSyt /* 396313bcbb7aSyt * According to the spec, to reliably detect hot plug removals, software 396413bcbb7aSyt * must disable interface power management. Software should perform the 396513bcbb7aSyt * following initialization on a port after a device is attached: 396613bcbb7aSyt * Set PxSCTL.IPM to 3h to disable interface state transitions 396713bcbb7aSyt * Set PxCMD.ALPE to '0' to disable aggressive power management 396813bcbb7aSyt * Disable device initiated interface power management by SET FEATURE 396913bcbb7aSyt * 397013bcbb7aSyt * We can ignore the last item because by default the feature is disabled 397113bcbb7aSyt */ 397213bcbb7aSyt static void 397313bcbb7aSyt ahci_disable_interface_pm(ahci_ctl_t *ahci_ctlp, uint8_t port) 397413bcbb7aSyt { 397513bcbb7aSyt uint32_t port_scontrol, port_cmd_status; 397613bcbb7aSyt 397713bcbb7aSyt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 397813bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 397913bcbb7aSyt SCONTROL_SET_IPM(port_scontrol, SCONTROL_IPM_DISABLE_BOTH); 398013bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 398113bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), port_scontrol); 398213bcbb7aSyt 398313bcbb7aSyt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 398413bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 398513bcbb7aSyt port_cmd_status &= ~AHCI_CMD_STATUS_ALPE; 398613bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 398713bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 398813bcbb7aSyt } 398913bcbb7aSyt 39902fcbc377Syt /* 399168d33a25Syt * Start the port - set PxCMD.ST to 1, if PxCMD.FRE is not set 399268d33a25Syt * to 1, then set it firstly. 399368d33a25Syt * 399468d33a25Syt * Each port contains two major DMA engines. One DMA engine walks through 399568d33a25Syt * the command list, and is controlled by PxCMD.ST. The second DMA engine 399668d33a25Syt * copies received FISes into system memory, and is controlled by PxCMD.FRE. 399768d33a25Syt * 399868d33a25Syt * Software shall not set PxCMD.ST to '1' until it verifies that PxCMD.CR 399968d33a25Syt * is '0' and has set PxCMD.FRE is '1'. And software shall not clear 400068d33a25Syt * PxCMD.FRE while PxCMD.ST or PxCMD.CR is set '1'. 400168d33a25Syt * 400268d33a25Syt * Software shall not set PxCMD.ST to '1' unless a functional device is 400368d33a25Syt * present on the port(as determined by PxTFD.STS.BSY = '0', 400468d33a25Syt * PxTFD.STS.DRQ = '0', and PxSSTS.DET = 3h). 40052fcbc377Syt * 40062fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 40072fcbc377Syt * is called. 40082fcbc377Syt */ 40092fcbc377Syt static int 401068d33a25Syt ahci_start_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 40112fcbc377Syt { 401268d33a25Syt uint32_t port_cmd_status; 40132fcbc377Syt 4014f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_start_port: %d enter", port); 40152fcbc377Syt 401668d33a25Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 4017f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 401868d33a25Syt "the state for port %d is 0x%x", 401968d33a25Syt port, ahci_portp->ahciport_port_state); 40202fcbc377Syt return (AHCI_FAILURE); 402168d33a25Syt } 40222fcbc377Syt 402368d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 4024f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 402568d33a25Syt "no device is attached at port %d", port); 402668d33a25Syt return (AHCI_FAILURE); 402768d33a25Syt } 40282fcbc377Syt 402968d33a25Syt /* First to set PxCMD.FRE before setting PxCMD.ST. */ 403068d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 403168d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 40322fcbc377Syt 403368d33a25Syt if (!(port_cmd_status & AHCI_CMD_STATUS_FRE)) { 403468d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_FRE; 40352fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 40362fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 40372fcbc377Syt port_cmd_status); 40382fcbc377Syt } 403968d33a25Syt 404068d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 404168d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 404268d33a25Syt 404368d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_ST; 404468d33a25Syt 404568d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 404668d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 404768d33a25Syt port_cmd_status); 404868d33a25Syt 404968d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_STARTED; 405068d33a25Syt 4051f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port: " 40520a4c4cecSXiao-Yu Zhang "PxCMD.ST set to '1' at port %d", port); 40530a4c4cecSXiao-Yu Zhang 405468d33a25Syt return (AHCI_SUCCESS); 40552fcbc377Syt } 40562fcbc377Syt 40572fcbc377Syt /* 40582fcbc377Syt * Allocate the ahci_port_t including Received FIS and Command List. 40592fcbc377Syt * The argument - port is the physical port number, and not logical 40602fcbc377Syt * port number seen by the SATA framework. 40612fcbc377Syt * 40622fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 40632fcbc377Syt * is called. 40642fcbc377Syt */ 40652fcbc377Syt static int 40662fcbc377Syt ahci_alloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 40672fcbc377Syt { 4068f8a673adSying tian - Beijing China dev_info_t *dip = ahci_ctlp->ahcictl_dip; 40692fcbc377Syt ahci_port_t *ahci_portp; 4070f8a673adSying tian - Beijing China char taskq_name[64] = "event_handle_taskq"; 40712fcbc377Syt 40722fcbc377Syt ahci_portp = 40732fcbc377Syt (ahci_port_t *)kmem_zalloc(sizeof (ahci_port_t), KM_SLEEP); 40742fcbc377Syt 40752fcbc377Syt ahci_ctlp->ahcictl_ports[port] = ahci_portp; 40762fcbc377Syt ahci_portp->ahciport_port_num = port; 40772fcbc377Syt 407868d33a25Syt /* Intialize the port condition variable */ 407968d33a25Syt cv_init(&ahci_portp->ahciport_cv, NULL, CV_DRIVER, NULL); 408068d33a25Syt 408168d33a25Syt /* Initialize the port mutex */ 40822fcbc377Syt mutex_init(&ahci_portp->ahciport_mutex, NULL, MUTEX_DRIVER, 40832fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 408468d33a25Syt 40852fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 40862fcbc377Syt 40872fcbc377Syt /* 40882fcbc377Syt * Allocate memory for received FIS structure and 40892fcbc377Syt * command list for this port 40902fcbc377Syt */ 40912fcbc377Syt if (ahci_alloc_rcvd_fis(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 40922fcbc377Syt goto err_case1; 40932fcbc377Syt } 40942fcbc377Syt 40952fcbc377Syt if (ahci_alloc_cmd_list(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 40962fcbc377Syt goto err_case2; 40972fcbc377Syt } 40982fcbc377Syt 4099f8a673adSying tian - Beijing China (void) snprintf(taskq_name + strlen(taskq_name), 4100f8a673adSying tian - Beijing China sizeof (taskq_name) - strlen(taskq_name), 4101f8a673adSying tian - Beijing China "_port%d", port); 4102f8a673adSying tian - Beijing China 4103f8a673adSying tian - Beijing China /* Create the taskq for the port */ 4104f8a673adSying tian - Beijing China if ((ahci_portp->ahciport_event_taskq = ddi_taskq_create(dip, 4105f8a673adSying tian - Beijing China taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) { 4106f8a673adSying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ddi_taskq_create failed for event " 4107f8a673adSying tian - Beijing China "handle", ddi_get_instance(ahci_ctlp->ahcictl_dip)); 4108f8a673adSying tian - Beijing China goto err_case3; 4109f8a673adSying tian - Beijing China } 4110f8a673adSying tian - Beijing China 4111f8a673adSying tian - Beijing China /* Allocate the argument for the taskq */ 411268d33a25Syt ahci_portp->ahciport_event_args = 411368d33a25Syt kmem_zalloc(sizeof (ahci_event_arg_t), KM_SLEEP); 411468d33a25Syt 411568d33a25Syt if (ahci_portp->ahciport_event_args == NULL) 4116f8a673adSying tian - Beijing China goto err_case4; 411768d33a25Syt 41182fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 41192fcbc377Syt 41202fcbc377Syt return (AHCI_SUCCESS); 41212fcbc377Syt 4122f8a673adSying tian - Beijing China err_case4: 4123f8a673adSying tian - Beijing China ddi_taskq_destroy(ahci_portp->ahciport_event_taskq); 4124f8a673adSying tian - Beijing China 412568d33a25Syt err_case3: 412668d33a25Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 412768d33a25Syt 41282fcbc377Syt err_case2: 4129689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 41302fcbc377Syt 41312fcbc377Syt err_case1: 41322fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 41332fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 413468d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 41352fcbc377Syt 41362fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 41372fcbc377Syt 41382fcbc377Syt return (AHCI_FAILURE); 41392fcbc377Syt } 41402fcbc377Syt 41412fcbc377Syt /* 41422fcbc377Syt * Reverse of ahci_dealloc_port_state(). 41432fcbc377Syt * 41442fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 41452fcbc377Syt * is called. 41462fcbc377Syt */ 41472fcbc377Syt static void 41482fcbc377Syt ahci_dealloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 41492fcbc377Syt { 41502fcbc377Syt ahci_port_t *ahci_portp = ahci_ctlp->ahcictl_ports[port]; 41512fcbc377Syt 41522fcbc377Syt ASSERT(ahci_portp != NULL); 41532fcbc377Syt 41542fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 415568d33a25Syt kmem_free(ahci_portp->ahciport_event_args, sizeof (ahci_event_arg_t)); 415668d33a25Syt ahci_portp->ahciport_event_args = NULL; 4157f8a673adSying tian - Beijing China ddi_taskq_destroy(ahci_portp->ahciport_event_taskq); 41582fcbc377Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 4159689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 41602fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 41612fcbc377Syt 41622fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 416368d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 41642fcbc377Syt 41652fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 41662fcbc377Syt 41672fcbc377Syt ahci_ctlp->ahcictl_ports[port] = NULL; 41682fcbc377Syt } 41692fcbc377Syt 41702fcbc377Syt /* 41712fcbc377Syt * Allocates memory for the Received FIS Structure 41722fcbc377Syt * 41732fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 41742fcbc377Syt * is called. 41752fcbc377Syt */ 41762fcbc377Syt static int 41772fcbc377Syt ahci_alloc_rcvd_fis(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 41782fcbc377Syt uint8_t port) 41792fcbc377Syt { 41802fcbc377Syt size_t rcvd_fis_size; 41812fcbc377Syt size_t ret_len; 41822fcbc377Syt uint_t cookie_count; 41832fcbc377Syt 41842fcbc377Syt rcvd_fis_size = sizeof (ahci_rcvd_fis_t); 41852fcbc377Syt 41862fcbc377Syt /* allocate rcvd FIS dma handle. */ 41872fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 41882fcbc377Syt &ahci_ctlp->ahcictl_rcvd_fis_dma_attr, 41892fcbc377Syt DDI_DMA_SLEEP, 41902fcbc377Syt NULL, 41912fcbc377Syt &ahci_portp->ahciport_rcvd_fis_dma_handle) != 41922fcbc377Syt DDI_SUCCESS) { 4193f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 4194f5f2d263SFred Herard "rcvd FIS dma handle alloc failed", NULL); 41952fcbc377Syt 41962fcbc377Syt return (AHCI_FAILURE); 41972fcbc377Syt } 41982fcbc377Syt 41992fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_rcvd_fis_dma_handle, 42002fcbc377Syt rcvd_fis_size, 42012fcbc377Syt &accattr, 42022fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 42032fcbc377Syt DDI_DMA_SLEEP, 42042fcbc377Syt NULL, 42052fcbc377Syt (caddr_t *)&ahci_portp->ahciport_rcvd_fis, 42062fcbc377Syt &ret_len, 42072fcbc377Syt &ahci_portp->ahciport_rcvd_fis_acc_handle) != NULL) { 42082fcbc377Syt 4209f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 4210f5f2d263SFred Herard "rcvd FIS dma mem alloc fail", NULL); 42112fcbc377Syt /* error.. free the dma handle. */ 42122fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 42132fcbc377Syt return (AHCI_FAILURE); 42142fcbc377Syt } 42152fcbc377Syt 42162fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle, 42172fcbc377Syt NULL, 42182fcbc377Syt (caddr_t)ahci_portp->ahciport_rcvd_fis, 42192fcbc377Syt rcvd_fis_size, 42202fcbc377Syt DDI_DMA_CONSISTENT, 42212fcbc377Syt DDI_DMA_SLEEP, 42222fcbc377Syt NULL, 422313bcbb7aSyt &ahci_portp->ahciport_rcvd_fis_dma_cookie, 42242fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 42252fcbc377Syt 4226f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 4227f5f2d263SFred Herard "rcvd FIS dma handle bind fail", NULL); 42282fcbc377Syt /* error.. free the dma handle & free the memory. */ 42292fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 42302fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 42312fcbc377Syt return (AHCI_FAILURE); 42322fcbc377Syt } 42332fcbc377Syt 42342fcbc377Syt bzero((void *)ahci_portp->ahciport_rcvd_fis, rcvd_fis_size); 42352fcbc377Syt 42362fcbc377Syt /* Config Port Received FIS Base Address */ 42372fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 42382fcbc377Syt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 423913bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 42402fcbc377Syt 4241f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 424213bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 4243f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 424413bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_address); 42452fcbc377Syt 42462fcbc377Syt return (AHCI_SUCCESS); 42472fcbc377Syt } 42482fcbc377Syt 42492fcbc377Syt /* 42502fcbc377Syt * Deallocates the Received FIS Structure 42512fcbc377Syt * 42522fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 42532fcbc377Syt * is called. 42542fcbc377Syt */ 42552fcbc377Syt static void 4256689d74b0Syt ahci_dealloc_rcvd_fis(ahci_port_t *ahci_portp) 42572fcbc377Syt { 42582fcbc377Syt /* Unbind the cmd list dma handle first. */ 42592fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle); 42602fcbc377Syt 42612fcbc377Syt /* Then free the underlying memory. */ 42622fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 42632fcbc377Syt 42642fcbc377Syt /* Now free the handle itself. */ 42652fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 42662fcbc377Syt } 42672fcbc377Syt 42682fcbc377Syt /* 42692fcbc377Syt * Allocates memory for the Command List, which contains up to 32 entries. 42702fcbc377Syt * Each entry contains a command header, which is a 32-byte structure that 42712fcbc377Syt * includes the pointer to the command table. 42722fcbc377Syt * 42732fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 42742fcbc377Syt * is called. 42752fcbc377Syt */ 42762fcbc377Syt static int 42772fcbc377Syt ahci_alloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 42782fcbc377Syt uint8_t port) 42792fcbc377Syt { 42802fcbc377Syt size_t cmd_list_size; 42812fcbc377Syt size_t ret_len; 42822fcbc377Syt uint_t cookie_count; 42832fcbc377Syt 42842fcbc377Syt cmd_list_size = 42852fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots * sizeof (ahci_cmd_header_t); 42862fcbc377Syt 42872fcbc377Syt /* allocate cmd list dma handle. */ 42882fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 42892fcbc377Syt &ahci_ctlp->ahcictl_cmd_list_dma_attr, 42902fcbc377Syt DDI_DMA_SLEEP, 42912fcbc377Syt NULL, 42922fcbc377Syt &ahci_portp->ahciport_cmd_list_dma_handle) != DDI_SUCCESS) { 42932fcbc377Syt 4294f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 4295f5f2d263SFred Herard "cmd list dma handle alloc failed", NULL); 42962fcbc377Syt return (AHCI_FAILURE); 42972fcbc377Syt } 42982fcbc377Syt 42992fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_cmd_list_dma_handle, 43002fcbc377Syt cmd_list_size, 43012fcbc377Syt &accattr, 43022fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 43032fcbc377Syt DDI_DMA_SLEEP, 43042fcbc377Syt NULL, 43052fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_list, 43062fcbc377Syt &ret_len, 43072fcbc377Syt &ahci_portp->ahciport_cmd_list_acc_handle) != NULL) { 43082fcbc377Syt 4309f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 4310f5f2d263SFred Herard "cmd list dma mem alloc fail", NULL); 43112fcbc377Syt /* error.. free the dma handle. */ 43122fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 43132fcbc377Syt return (AHCI_FAILURE); 43142fcbc377Syt } 43152fcbc377Syt 43162fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_cmd_list_dma_handle, 43172fcbc377Syt NULL, 43182fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_list, 43192fcbc377Syt cmd_list_size, 43202fcbc377Syt DDI_DMA_CONSISTENT, 43212fcbc377Syt DDI_DMA_SLEEP, 43222fcbc377Syt NULL, 432313bcbb7aSyt &ahci_portp->ahciport_cmd_list_dma_cookie, 43242fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 43252fcbc377Syt 4326f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 4327f5f2d263SFred Herard "cmd list dma handle bind fail", NULL); 43282fcbc377Syt /* error.. free the dma handle & free the memory. */ 43292fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 43302fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 43312fcbc377Syt return (AHCI_FAILURE); 43322fcbc377Syt } 43332fcbc377Syt 43342fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_list, cmd_list_size); 43352fcbc377Syt 43362fcbc377Syt /* Config Port Command List Base Address */ 43372fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 43382fcbc377Syt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 433913bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 43402fcbc377Syt 4341f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 434213bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 43432fcbc377Syt 4344f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 434513bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_address); 43462fcbc377Syt 43472fcbc377Syt if (ahci_alloc_cmd_tables(ahci_ctlp, ahci_portp) != AHCI_SUCCESS) { 4348e2decd04Syt goto err_out; 43492fcbc377Syt } 43502fcbc377Syt 43512fcbc377Syt return (AHCI_SUCCESS); 4352e2decd04Syt 4353e2decd04Syt err_out: 4354e2decd04Syt /* Unbind the cmd list dma handle first. */ 4355e2decd04Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 4356e2decd04Syt 4357e2decd04Syt /* Then free the underlying memory. */ 4358e2decd04Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 4359e2decd04Syt 4360e2decd04Syt /* Now free the handle itself. */ 4361e2decd04Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 4362e2decd04Syt 4363e2decd04Syt return (AHCI_FAILURE); 43642fcbc377Syt } 43652fcbc377Syt 43662fcbc377Syt /* 43672fcbc377Syt * Deallocates the Command List 43682fcbc377Syt * 43692fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 43702fcbc377Syt * is called. 43712fcbc377Syt */ 43722fcbc377Syt static void 43732fcbc377Syt ahci_dealloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 43742fcbc377Syt { 43752fcbc377Syt /* First dealloc command table */ 43762fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctlp, ahci_portp); 43772fcbc377Syt 43782fcbc377Syt /* Unbind the cmd list dma handle first. */ 43792fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 43802fcbc377Syt 43812fcbc377Syt /* Then free the underlying memory. */ 43822fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 43832fcbc377Syt 43842fcbc377Syt /* Now free the handle itself. */ 43852fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 43862fcbc377Syt } 43872fcbc377Syt 43882fcbc377Syt /* 43892fcbc377Syt * Allocates memory for all Command Tables, which contains Command FIS, 43902fcbc377Syt * ATAPI Command and Physical Region Descriptor Table. 43912fcbc377Syt * 43922fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 43932fcbc377Syt * is called. 43942fcbc377Syt */ 43952fcbc377Syt static int 43962fcbc377Syt ahci_alloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 43972fcbc377Syt { 43982fcbc377Syt size_t ret_len; 43992fcbc377Syt ddi_dma_cookie_t cmd_table_dma_cookie; 44002fcbc377Syt uint_t cookie_count; 44012fcbc377Syt int slot; 44022fcbc377Syt 4403f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 44042fcbc377Syt "ahci_alloc_cmd_tables: port %d enter", 44052fcbc377Syt ahci_portp->ahciport_port_num); 44062fcbc377Syt 44072fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 44082fcbc377Syt /* Allocate cmd table dma handle. */ 44092fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 44102fcbc377Syt &ahci_ctlp->ahcictl_cmd_table_dma_attr, 44112fcbc377Syt DDI_DMA_SLEEP, 44122fcbc377Syt NULL, 44132fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]) != 44142fcbc377Syt DDI_SUCCESS) { 44152fcbc377Syt 4416f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 4417f5f2d263SFred Herard "cmd table dma handle alloc failed", NULL); 44182fcbc377Syt 44192fcbc377Syt goto err_out; 44202fcbc377Syt } 44212fcbc377Syt 44222fcbc377Syt if (ddi_dma_mem_alloc( 44232fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 44242fcbc377Syt ahci_cmd_table_size, 44252fcbc377Syt &accattr, 44262fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 44272fcbc377Syt DDI_DMA_SLEEP, 44282fcbc377Syt NULL, 44292fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_tables[slot], 44302fcbc377Syt &ret_len, 44312fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]) != 44322fcbc377Syt NULL) { 44332fcbc377Syt 4434f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 4435f5f2d263SFred Herard "cmd table dma mem alloc fail", NULL); 44362fcbc377Syt 44372fcbc377Syt /* error.. free the dma handle. */ 44382fcbc377Syt ddi_dma_free_handle( 44392fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 44402fcbc377Syt goto err_out; 44412fcbc377Syt } 44422fcbc377Syt 44432fcbc377Syt if (ddi_dma_addr_bind_handle( 44442fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 44452fcbc377Syt NULL, 44462fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_tables[slot], 44472fcbc377Syt ahci_cmd_table_size, 44482fcbc377Syt DDI_DMA_CONSISTENT, 44492fcbc377Syt DDI_DMA_SLEEP, 44502fcbc377Syt NULL, 44512fcbc377Syt &cmd_table_dma_cookie, 44522fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 44532fcbc377Syt 4454f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 4455f5f2d263SFred Herard "cmd table dma handle bind fail", NULL); 44562fcbc377Syt /* error.. free the dma handle & free the memory. */ 44572fcbc377Syt ddi_dma_mem_free( 44582fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 44592fcbc377Syt ddi_dma_free_handle( 44602fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 44612fcbc377Syt goto err_out; 44622fcbc377Syt } 44632fcbc377Syt 44642fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_tables[slot], 44652fcbc377Syt ahci_cmd_table_size); 44662fcbc377Syt 44672fcbc377Syt /* Config Port Command Table Base Address */ 44682fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR( 44692fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 44702fcbc377Syt cmd_table_dma_cookie.dmac_laddress & 0xffffffffull); 44712fcbc377Syt 44722fcbc377Syt #ifndef __lock_lint 44732fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR_UPPER( 44742fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 44752fcbc377Syt cmd_table_dma_cookie.dmac_laddress >> 32); 4476689d74b0Syt #endif 44772fcbc377Syt } 44782fcbc377Syt 44792fcbc377Syt return (AHCI_SUCCESS); 44802fcbc377Syt err_out: 44812fcbc377Syt 44822fcbc377Syt for (slot--; slot >= 0; slot--) { 44832fcbc377Syt /* Unbind the cmd table dma handle first */ 44842fcbc377Syt (void) ddi_dma_unbind_handle( 44852fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 44862fcbc377Syt 44872fcbc377Syt /* Then free the underlying memory */ 44882fcbc377Syt ddi_dma_mem_free( 44892fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 44902fcbc377Syt 44912fcbc377Syt /* Now free the handle itself */ 44922fcbc377Syt ddi_dma_free_handle( 44932fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 44942fcbc377Syt } 44952fcbc377Syt 44962fcbc377Syt return (AHCI_FAILURE); 44972fcbc377Syt } 44982fcbc377Syt 44992fcbc377Syt /* 45002fcbc377Syt * Deallocates memory for all Command Tables. 45012fcbc377Syt * 45022fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 45032fcbc377Syt * is called. 45042fcbc377Syt */ 45052fcbc377Syt static void 45062fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 45072fcbc377Syt { 45082fcbc377Syt int slot; 45092fcbc377Syt 4510f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 45112fcbc377Syt "ahci_dealloc_cmd_tables: %d enter", 45122fcbc377Syt ahci_portp->ahciport_port_num); 45132fcbc377Syt 45142fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 45152fcbc377Syt /* Unbind the cmd table dma handle first. */ 45162fcbc377Syt (void) ddi_dma_unbind_handle( 45172fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 45182fcbc377Syt 45192fcbc377Syt /* Then free the underlying memory. */ 45202fcbc377Syt ddi_dma_mem_free( 45212fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 45222fcbc377Syt 45232fcbc377Syt /* Now free the handle itself. */ 45242fcbc377Syt ddi_dma_free_handle( 45252fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 45262fcbc377Syt } 45272fcbc377Syt } 45282fcbc377Syt 45292fcbc377Syt /* 45302fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 45312fcbc377Syt * is called. 45322fcbc377Syt */ 45332fcbc377Syt static void 45342fcbc377Syt ahci_update_sata_registers(ahci_ctl_t *ahci_ctlp, uint8_t port, 45352fcbc377Syt sata_device_t *sd) 45362fcbc377Syt { 45372fcbc377Syt sd->satadev_scr.sstatus = 45382fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45392fcbc377Syt (uint32_t *)(AHCI_PORT_PxSSTS(ahci_ctlp, port))); 45402fcbc377Syt sd->satadev_scr.serror = 45412fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45422fcbc377Syt (uint32_t *)(AHCI_PORT_PxSERR(ahci_ctlp, port))); 45432fcbc377Syt sd->satadev_scr.scontrol = 45442fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45452fcbc377Syt (uint32_t *)(AHCI_PORT_PxSCTL(ahci_ctlp, port))); 45462fcbc377Syt sd->satadev_scr.sactive = 45472fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45482fcbc377Syt (uint32_t *)(AHCI_PORT_PxSACT(ahci_ctlp, port))); 45492fcbc377Syt } 45502fcbc377Syt 45512fcbc377Syt /* 455268d33a25Syt * For poll mode, ahci_port_intr will be called to emulate the interrupt 45532fcbc377Syt */ 45542fcbc377Syt static void 455582263d52Syt ahci_port_intr(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 45562fcbc377Syt { 455768d33a25Syt uint32_t port_intr_status; 455868d33a25Syt uint32_t port_intr_enable; 455968d33a25Syt 4560f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 456168d33a25Syt "ahci_port_intr enter: port %d", port); 456268d33a25Syt 456368d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 456468d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_POLLING) { 456568d33a25Syt /* For SATA_OPMODE_POLLING commands */ 456668d33a25Syt port_intr_enable = 456768d33a25Syt (AHCI_INTR_STATUS_DHRS | 456868d33a25Syt AHCI_INTR_STATUS_PSS | 456968d33a25Syt AHCI_INTR_STATUS_SDBS | 457068d33a25Syt AHCI_INTR_STATUS_UFS | 457168d33a25Syt AHCI_INTR_STATUS_PCS | 457268d33a25Syt AHCI_INTR_STATUS_PRCS | 457368d33a25Syt AHCI_INTR_STATUS_OFS | 457468d33a25Syt AHCI_INTR_STATUS_INFS | 457568d33a25Syt AHCI_INTR_STATUS_IFS | 457668d33a25Syt AHCI_INTR_STATUS_HBDS | 457768d33a25Syt AHCI_INTR_STATUS_HBFS | 457868d33a25Syt AHCI_INTR_STATUS_TFES); 457968d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 458068d33a25Syt goto next; 458168d33a25Syt } 458268d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 458368d33a25Syt 458468d33a25Syt /* 458568d33a25Syt * port_intr_enable indicates that the corresponding interrrupt 458668d33a25Syt * reporting is enabled. 458768d33a25Syt */ 458868d33a25Syt port_intr_enable = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 458968d33a25Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port)); 459068d33a25Syt next: 459168d33a25Syt /* 459268d33a25Syt * port_intr_stats indicates that the corresponding interrupt 459368d33a25Syt * condition is active. 459468d33a25Syt */ 459568d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 459668d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 459768d33a25Syt 4598f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 459968d33a25Syt "ahci_port_intr: port %d, port_intr_status = 0x%x, " 460082263d52Syt "port_intr_enable = 0x%x", 460182263d52Syt port, port_intr_status, port_intr_enable); 460268d33a25Syt 460368d33a25Syt port_intr_status &= port_intr_enable; 460468d33a25Syt 460568d33a25Syt /* First clear the port interrupts status */ 460668d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 460768d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 460868d33a25Syt port_intr_status); 460968d33a25Syt 461082263d52Syt /* Check the completed non-queued commands */ 461168d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_DHRS | 461268d33a25Syt AHCI_INTR_STATUS_PSS)) { 461368d33a25Syt (void) ahci_intr_cmd_cmplt(ahci_ctlp, 4614689d74b0Syt ahci_portp, port); 461568d33a25Syt } 461668d33a25Syt 461782263d52Syt /* Check the completed queued commands */ 461868d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_SDBS) { 461968d33a25Syt (void) ahci_intr_set_device_bits(ahci_ctlp, 462068d33a25Syt ahci_portp, port); 462168d33a25Syt } 462268d33a25Syt 462368d33a25Syt /* Check the port connect change status interrupt bit */ 462468d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PCS) { 462568d33a25Syt (void) ahci_intr_port_connect_change(ahci_ctlp, 462668d33a25Syt ahci_portp, port); 462768d33a25Syt } 462868d33a25Syt 462968d33a25Syt /* Check the device mechanical presence status interrupt bit */ 463068d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_DMPS) { 463168d33a25Syt (void) ahci_intr_device_mechanical_presence_status( 463268d33a25Syt ahci_ctlp, ahci_portp, port); 463368d33a25Syt } 463468d33a25Syt 463568d33a25Syt /* Check the PhyRdy change status interrupt bit */ 463668d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PRCS) { 463768d33a25Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, 463868d33a25Syt port); 46392fcbc377Syt } 464068d33a25Syt 464168d33a25Syt /* 464268d33a25Syt * Check the non-fatal error interrupt bits, there are three 464368d33a25Syt * kinds of non-fatal errors at the time being: 464468d33a25Syt * 464568d33a25Syt * PxIS.UFS - Unknown FIS Error 464668d33a25Syt * PxIS.OFS - Overflow Error 464768d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 464868d33a25Syt * 464968d33a25Syt * For these non-fatal errors, the HBA can continue to operate, 465068d33a25Syt * so the driver just log the error messages. 465168d33a25Syt */ 465268d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_UFS | 465368d33a25Syt AHCI_INTR_STATUS_OFS | 465468d33a25Syt AHCI_INTR_STATUS_INFS)) { 465568d33a25Syt (void) ahci_intr_non_fatal_error(ahci_ctlp, ahci_portp, 465668d33a25Syt port, port_intr_status); 465768d33a25Syt } 465868d33a25Syt 465968d33a25Syt /* 466068d33a25Syt * Check the fatal error interrupt bits, there are four kinds 466168d33a25Syt * of fatal errors for AHCI controllers: 466268d33a25Syt * 466368d33a25Syt * PxIS.HBFS - Host Bus Fatal Error 466468d33a25Syt * PxIS.HBDS - Host Bus Data Error 466568d33a25Syt * PxIS.IFS - Interface Fatal Error 466668d33a25Syt * PxIS.TFES - Task File Error 466768d33a25Syt * 466868d33a25Syt * The fatal error means the HBA can not recover from it by 466968d33a25Syt * itself, and it will try to abort the transfer, and the software 467068d33a25Syt * must intervene to restart the port. 467168d33a25Syt */ 467268d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_IFS | 467368d33a25Syt AHCI_INTR_STATUS_HBDS | 467468d33a25Syt AHCI_INTR_STATUS_HBFS | 467568d33a25Syt AHCI_INTR_STATUS_TFES)) 467668d33a25Syt (void) ahci_intr_fatal_error(ahci_ctlp, ahci_portp, 467782263d52Syt port, port_intr_status); 467868d33a25Syt 467968d33a25Syt /* Check the cold port detect interrupt bit */ 468068d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_CPDS) { 468168d33a25Syt (void) ahci_intr_cold_port_detect(ahci_ctlp, ahci_portp, port); 468268d33a25Syt } 468368d33a25Syt 468468d33a25Syt /* Second clear the corresponding bit in IS.IPS */ 468568d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 468668d33a25Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (0x1 << port)); 46872fcbc377Syt } 46882fcbc377Syt 46892fcbc377Syt /* 46902fcbc377Syt * Interrupt service handler 46912fcbc377Syt */ 46922fcbc377Syt static uint_t 46932fcbc377Syt ahci_intr(caddr_t arg1, caddr_t arg2) 46942fcbc377Syt { 4695689d74b0Syt #ifndef __lock_lint 4696689d74b0Syt _NOTE(ARGUNUSED(arg2)) 4697689d74b0Syt #endif 4698689d74b0Syt /* LINTED */ 46992fcbc377Syt ahci_ctl_t *ahci_ctlp = (ahci_ctl_t *)arg1; 47002fcbc377Syt ahci_port_t *ahci_portp; 470168d33a25Syt int32_t global_intr_status; 47022fcbc377Syt uint8_t port; 47032fcbc377Syt 47042fcbc377Syt /* 47052fcbc377Syt * global_intr_status indicates that the corresponding port has 47062fcbc377Syt * an interrupt pending. 47072fcbc377Syt */ 47082fcbc377Syt global_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 47092fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp)); 47102fcbc377Syt 47112fcbc377Syt if (!(global_intr_status & ahci_ctlp->ahcictl_ports_implemented)) { 47122fcbc377Syt /* The interrupt is not ours */ 47132fcbc377Syt return (DDI_INTR_UNCLAIMED); 47142fcbc377Syt } 47152fcbc377Syt 47162fcbc377Syt /* Loop for all the ports */ 47172fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 47182fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 47192fcbc377Syt continue; 47202fcbc377Syt } 47212fcbc377Syt if (!((0x1 << port) & global_intr_status)) { 47222fcbc377Syt continue; 47232fcbc377Syt } 47242fcbc377Syt 47252fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 47262fcbc377Syt 472768d33a25Syt /* Call ahci_port_intr */ 472882263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 47292fcbc377Syt } 47302fcbc377Syt 47312fcbc377Syt return (DDI_INTR_CLAIMED); 47322fcbc377Syt } 47332fcbc377Syt 47342fcbc377Syt /* 473568d33a25Syt * For non-queued commands, when the corresponding bit in the PxCI register 473668d33a25Syt * is cleared, it means the command is completed successfully. And according 473768d33a25Syt * to the HBA state machine, there are three conditions which possibly will 473868d33a25Syt * try to clear the PxCI register bit. 473968d33a25Syt * 1. Receive one D2H Register FIS which is with 'I' bit set 474068d33a25Syt * 2. Update PIO Setup FIS 474168d33a25Syt * 3. Transmit a command and receive R_OK if CTBA.C is set (software reset) 474268d33a25Syt * 474382263d52Syt * Process completed non-queued commands when the interrupt status bit - 474482263d52Syt * AHCI_INTR_STATUS_DHRS or AHCI_INTR_STATUS_PSS is set. 47452fcbc377Syt * 474668d33a25Syt * AHCI_INTR_STATUS_DHRS means a D2H Register FIS has been received 474768d33a25Syt * with the 'I' bit set. And the following commands will send thus 474868d33a25Syt * FIS with 'I' bit set upon the successful completion: 47492fcbc377Syt * 1. Non-data commands 47502fcbc377Syt * 2. DMA data-in command 47512fcbc377Syt * 3. DMA data-out command 47522fcbc377Syt * 4. PIO data-out command 475368d33a25Syt * 5. PACKET non-data commands 475468d33a25Syt * 6. PACKET PIO data-in command 475568d33a25Syt * 7. PACKET PIO data-out command 475668d33a25Syt * 8. PACKET DMA data-in command 475768d33a25Syt * 9. PACKET DMA data-out command 475868d33a25Syt * 475968d33a25Syt * AHCI_INTR_STATUS_PSS means a PIO Setup FIS has been received 476068d33a25Syt * with the 'I' bit set. And the following commands will send this 476168d33a25Syt * FIS upon the successful completion: 476268d33a25Syt * 1. PIO data-in command 47632fcbc377Syt */ 47642fcbc377Syt static int 476582263d52Syt ahci_intr_cmd_cmplt(ahci_ctl_t *ahci_ctlp, 4766689d74b0Syt ahci_port_t *ahci_portp, uint8_t port) 47672fcbc377Syt { 476868d33a25Syt uint32_t port_cmd_issue = 0; 47692fcbc377Syt uint32_t finished_tags; 477068d33a25Syt int finished_slot; 47712fcbc377Syt sata_pkt_t *satapkt; 47722fcbc377Syt ahci_fis_d2h_register_t *rcvd_fisp; 477338547057Sying tian - Beijing China #if AHCI_DEBUG 477438547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 477538547057Sying tian - Beijing China uint32_t cmd_dmacount; 477638547057Sying tian - Beijing China #endif 47772fcbc377Syt 477868d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 477982263d52Syt 478082263d52Syt if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 478182263d52Syt !NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 47822fcbc377Syt /* 47832fcbc377Syt * Spurious interrupt. Nothing to be done. 47842fcbc377Syt */ 47852fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 47862fcbc377Syt return (AHCI_SUCCESS); 47872fcbc377Syt } 47882fcbc377Syt 478968d33a25Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 479068d33a25Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 47912fcbc377Syt 479282263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 479382263d52Syt /* Slot 0 is always used during error recovery */ 479482263d52Syt finished_tags = 0x1 & ~port_cmd_issue; 4795f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 479682263d52Syt "ahci_intr_cmd_cmplt: port %d the sata pkt for error " 479782263d52Syt "retrieval is finished, and finished_tags = 0x%x", 479882263d52Syt port, finished_tags); 479968d33a25Syt } else { 480068d33a25Syt finished_tags = ahci_portp->ahciport_pending_tags & 480168d33a25Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 48022fcbc377Syt } 48032fcbc377Syt 4804f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 480582263d52Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x, " 480682263d52Syt "port_cmd_issue = 0x%x finished_tags = 0x%x", 480768d33a25Syt ahci_portp->ahciport_pending_tags, port_cmd_issue, 480882263d52Syt finished_tags); 48092fcbc377Syt 481082263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 481182263d52Syt (finished_tags == 0x1)) { 481282263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 481382263d52Syt ASSERT(satapkt != NULL); 481482263d52Syt 4815f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 481682263d52Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 481782263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 481882263d52Syt 481982263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 482082263d52Syt goto out; 482182263d52Syt } 48222fcbc377Syt 482368d33a25Syt while (finished_tags) { 482468d33a25Syt finished_slot = ddi_ffs(finished_tags) - 1; 482568d33a25Syt if (finished_slot == -1) { 482668d33a25Syt goto out; 482768d33a25Syt } 48282fcbc377Syt 482968d33a25Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 483068d33a25Syt ASSERT(satapkt != NULL); 483138547057Sying tian - Beijing China #if AHCI_DEBUG 483238547057Sying tian - Beijing China /* 483338547057Sying tian - Beijing China * For non-native queued commands, the PRD byte count field 483438547057Sying tian - Beijing China * shall contain an accurate count of the number of bytes 483538547057Sying tian - Beijing China * transferred for the command before the PxCI bit is cleared 483638547057Sying tian - Beijing China * to '0' for the command. 483738547057Sying tian - Beijing China * 483838547057Sying tian - Beijing China * The purpose of this field is to let software know how many 483938547057Sying tian - Beijing China * bytes transferred for a given operation in order to 484038547057Sying tian - Beijing China * determine if underflow occurred. When issuing native command 484138547057Sying tian - Beijing China * queuing commands, this field should not be used and is not 484238547057Sying tian - Beijing China * required to be valid since in this case underflow is always 484338547057Sying tian - Beijing China * illegal. 484438547057Sying tian - Beijing China * 484538547057Sying tian - Beijing China * For data reads, the HBA will update its PRD byte count with 484638547057Sying tian - Beijing China * the total number of bytes received from the last FIS, and 484738547057Sying tian - Beijing China * may be able to continue normally. For data writes, the 484838547057Sying tian - Beijing China * device will detect an error, and HBA most likely will get 484938547057Sying tian - Beijing China * a fatal error. 485038547057Sying tian - Beijing China * 485138547057Sying tian - Beijing China * Therefore, here just put code to debug part. And please 485238547057Sying tian - Beijing China * refer to the comment above ahci_intr_fatal_error for the 485338547057Sying tian - Beijing China * definition of underflow error. 485438547057Sying tian - Beijing China */ 485538547057Sying tian - Beijing China cmd_dmacount = 485638547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[finished_slot]; 485738547057Sying tian - Beijing China if (cmd_dmacount) { 485838547057Sying tian - Beijing China cmd_header = 485938547057Sying tian - Beijing China &ahci_portp->ahciport_cmd_list[finished_slot]; 4860f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_PRDT, ahci_ctlp, 486138547057Sying tian - Beijing China "ahci_intr_cmd_cmplt: port %d, " 486238547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 486338547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 486438547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 486538547057Sying tian - Beijing China cmd_dmacount); 486638547057Sying tian - Beijing China 486738547057Sying tian - Beijing China if (cmd_header->ahcich_prd_byte_count != cmd_dmacount) { 4868f5f2d263SFred Herard AHCIDBG(AHCIDBG_UNDERFLOW, ahci_ctlp, 486938547057Sying tian - Beijing China "ahci_intr_cmd_cmplt: port %d, " 487038547057Sying tian - Beijing China "an underflow occurred", port); 487138547057Sying tian - Beijing China } 487238547057Sying tian - Beijing China } 487338547057Sying tian - Beijing China #endif 48742fcbc377Syt 48752fcbc377Syt /* 487668d33a25Syt * For SATAC_SMART command with SATA_SMART_RETURN_STATUS 487768d33a25Syt * feature, sata_special_regs flag will be set, and the 487868d33a25Syt * driver should copy the status and the other corresponding 487968d33a25Syt * register values in the D2H Register FIS received (It's 488068d33a25Syt * working on Non-data protocol) from the device back to 488168d33a25Syt * the sata_cmd. 488268d33a25Syt * 488368d33a25Syt * For every AHCI port, there is only one Received FIS 488468d33a25Syt * structure, which contains the FISes received from the 488568d33a25Syt * device, So we're trying to copy the content of D2H 488668d33a25Syt * Register FIS in the Received FIS structure back to 488768d33a25Syt * the sata_cmd. 48882fcbc377Syt */ 488968d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 489068d33a25Syt rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 489168d33a25Syt ahcirf_d2h_register_fis); 489268d33a25Syt satapkt->satapkt_cmd.satacmd_status_reg = 489368d33a25Syt GET_RFIS_STATUS(rcvd_fisp); 489468d33a25Syt ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp); 489568d33a25Syt } 48962fcbc377Syt 4897f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 489868d33a25Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 489968d33a25Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 49002fcbc377Syt 490168d33a25Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, finished_slot); 490268d33a25Syt CLEAR_BIT(finished_tags, finished_slot); 490368d33a25Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 49042fcbc377Syt 490568d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 49062fcbc377Syt } 49072fcbc377Syt out: 4908f5f2d263SFred Herard AHCIDBG(AHCIDBG_PKTCOMP, ahci_ctlp, 490968d33a25Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x", 49102fcbc377Syt ahci_portp->ahciport_pending_tags); 49112fcbc377Syt 49122fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 49132fcbc377Syt 49142fcbc377Syt return (AHCI_SUCCESS); 49152fcbc377Syt } 49162fcbc377Syt 49172fcbc377Syt /* 491868d33a25Syt * AHCI_INTR_STATUS_SDBS means a Set Device Bits FIS has been received 491982263d52Syt * with the 'I' bit set and has been copied into system memory. It will 492082263d52Syt * be sent under the following situations: 492182263d52Syt * 492282263d52Syt * 1. NCQ command is completed 492382263d52Syt * 2. Asynchronous notification 492482263d52Syt * 492582263d52Syt * The completion of NCQ commands (READ/WRITE FPDMA QUEUED) is performed 492682263d52Syt * via the Set Device Bits FIS. When such event is generated, the software 492782263d52Syt * needs to read PxSACT register and compares the current value to the 492882263d52Syt * list of commands previously issue by software. ahciport_pending_ncq_tags 492982263d52Syt * keeps the tags of previously issued commands. 49302fcbc377Syt * 493168d33a25Syt * Asynchronous Notification is a feature in SATA II, which allows an 493268d33a25Syt * ATAPI device to send a signal to the host when media is inserted or 493368d33a25Syt * removed and avoids polling the device for media changes. The signal 493468d33a25Syt * sent to the host is a Set Device Bits FIS with the 'I' and 'N' bits 493582263d52Syt * set to '1'. At the moment, it's not supported yet. 49362fcbc377Syt */ 49372fcbc377Syt static int 493868d33a25Syt ahci_intr_set_device_bits(ahci_ctl_t *ahci_ctlp, 49392fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 49402fcbc377Syt { 494182263d52Syt uint32_t port_sactive; 494282263d52Syt uint32_t port_cmd_issue; 494382263d52Syt uint32_t issued_tags; 494482263d52Syt int issued_slot; 494582263d52Syt uint32_t finished_tags; 494682263d52Syt int finished_slot; 494782263d52Syt sata_pkt_t *satapkt; 49482fcbc377Syt 4949f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 495068d33a25Syt "ahci_intr_set_device_bits enter: port %d", port); 49512fcbc377Syt 495282263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 495382263d52Syt if (!NCQ_CMD_IN_PROGRESS(ahci_portp)) { 495482263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 495582263d52Syt return (AHCI_SUCCESS); 495682263d52Syt } 495782263d52Syt 495882263d52Syt /* 495982263d52Syt * First the handler got which commands are finished by checking 496082263d52Syt * PxSACT register 496182263d52Syt */ 496282263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 496382263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 496468d33a25Syt 496582263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 496682263d52Syt ~port_sactive & AHCI_NCQ_SLOT_MASK(ahci_portp); 496782263d52Syt 4968f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 496982263d52Syt "ahci_intr_set_device_bits: port %d pending_ncq_tags = 0x%x " 497082263d52Syt "port_sactive = 0x%x", port, 497182263d52Syt ahci_portp->ahciport_pending_ncq_tags, port_sactive); 497282263d52Syt 4973f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 497482263d52Syt "ahci_intr_set_device_bits: finished_tags = 0x%x", finished_tags); 497582263d52Syt 497682263d52Syt /* 497782263d52Syt * For NCQ commands, the software can determine which command has 497882263d52Syt * already been transmitted to the device by checking PxCI register. 497982263d52Syt */ 498082263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 498182263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 498282263d52Syt 498382263d52Syt issued_tags = ahci_portp->ahciport_pending_tags & 498482263d52Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 498582263d52Syt 4986f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 498782263d52Syt "ahci_intr_set_device_bits: port %d pending_tags = 0x%x " 498882263d52Syt "port_cmd_issue = 0x%x", port, 498982263d52Syt ahci_portp->ahciport_pending_tags, port_cmd_issue); 499082263d52Syt 4991f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 499282263d52Syt "ahci_intr_set_device_bits: issued_tags = 0x%x", issued_tags); 499382263d52Syt 499482263d52Syt /* 499582263d52Syt * Clear ahciport_pending_tags bit when the corresponding command 499682263d52Syt * is already sent down to the device. 499782263d52Syt */ 499882263d52Syt while (issued_tags) { 499982263d52Syt issued_slot = ddi_ffs(issued_tags) - 1; 500082263d52Syt if (issued_slot == -1) { 500182263d52Syt goto next; 500282263d52Syt } 500382263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, issued_slot); 500482263d52Syt CLEAR_BIT(issued_tags, issued_slot); 500582263d52Syt } 500682263d52Syt 500782263d52Syt next: 500882263d52Syt while (finished_tags) { 500982263d52Syt finished_slot = ddi_ffs(finished_tags) - 1; 501082263d52Syt if (finished_slot == -1) { 501182263d52Syt goto out; 501282263d52Syt } 501382263d52Syt 501482263d52Syt /* The command is certainly transmitted to the device */ 501582263d52Syt ASSERT(!(ahci_portp->ahciport_pending_tags & 501682263d52Syt (0x1 << finished_slot))); 501782263d52Syt 501882263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 501982263d52Syt ASSERT(satapkt != NULL); 502082263d52Syt 5021f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 502282263d52Syt "ahci_intr_set_device_bits: sending up pkt 0x%p " 502382263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 502482263d52Syt 502582263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, finished_slot); 502682263d52Syt CLEAR_BIT(finished_tags, finished_slot); 502782263d52Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 502882263d52Syt 502982263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 503068d33a25Syt } 503182263d52Syt out: 5032f5f2d263SFred Herard AHCIDBG(AHCIDBG_PKTCOMP|AHCIDBG_NCQ, ahci_ctlp, 503382263d52Syt "ahci_intr_set_device_bits: port %d " 503482263d52Syt "pending_ncq_tags = 0x%x pending_tags = 0x%x", 503582263d52Syt port, ahci_portp->ahciport_pending_ncq_tags, 503682263d52Syt ahci_portp->ahciport_pending_tags); 503782263d52Syt 503882263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 50392fcbc377Syt 50402fcbc377Syt return (AHCI_SUCCESS); 50412fcbc377Syt } 50422fcbc377Syt 50432fcbc377Syt /* 504468d33a25Syt * 1=Change in Current Connect Status. 0=No change in Current Connect Status. 504568d33a25Syt * This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared 504668d33a25Syt * when PxSERR.DIAG.X is cleared. When PxSERR.DIAG.X is set to one, it 504768d33a25Syt * indicates a COMINIT signal was received. 50482fcbc377Syt * 504968d33a25Syt * Hot plug insertion is detected by reception of a COMINIT signal from the 505068d33a25Syt * device. On reception of unsolicited COMINIT, the HBA shall generate a 505168d33a25Syt * COMRESET. If the COMINIT is in responce to a COMRESET, then the HBA shall 505268d33a25Syt * begin the normal communication negotiation sequence as outlined in the 505368d33a25Syt * Serial ATA 1.0a specification. When a COMRESET is sent to the device the 505468d33a25Syt * PxSSTS.DET field shall be cleared to 0h. When a COMINIT is received, the 505568d33a25Syt * PxSSTS.DET field shall be set to 1h. When the communication negotiation 505668d33a25Syt * sequence is complete and PhyRdy is true the PxSSTS.DET field shall be set 505768d33a25Syt * to 3h. Therefore, at the moment the ahci driver is going to check PhyRdy 505868d33a25Syt * to handle hot plug insertion. In this interrupt handler, just do nothing 505968d33a25Syt * but print some log message and clear the bit. 50602fcbc377Syt */ 50612fcbc377Syt static int 506268d33a25Syt ahci_intr_port_connect_change(ahci_ctl_t *ahci_ctlp, 50632fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 50642fcbc377Syt { 5065689d74b0Syt #if AHCI_DEBUG 50662fcbc377Syt uint32_t port_serror; 5067689d74b0Syt #endif 50682fcbc377Syt 50692fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 50702fcbc377Syt 5071689d74b0Syt #if AHCI_DEBUG 50722fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 50732fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 50742fcbc377Syt 5075f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 507668d33a25Syt "ahci_intr_port_connect_change: port %d, " 507768d33a25Syt "port_serror = 0x%x", port, port_serror); 5078689d74b0Syt #endif 50792fcbc377Syt 508068d33a25Syt /* Clear PxSERR.DIAG.X to clear the interrupt bit */ 50812fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 50822fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 508382263d52Syt SERROR_EXCHANGED_ERR); 50842fcbc377Syt 50852fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 50862fcbc377Syt 50872fcbc377Syt return (AHCI_SUCCESS); 50882fcbc377Syt } 50892fcbc377Syt 50902fcbc377Syt /* 50912fcbc377Syt * Hot Plug Operation for platforms that support Mechanical Presence 50922fcbc377Syt * Switches. 50932fcbc377Syt * 50942fcbc377Syt * When set, it indicates that a mechanical presence switch attached to this 50952fcbc377Syt * port has been opened or closed, which may lead to a change in the connection 50962fcbc377Syt * state of the device. This bit is only valid if both CAP.SMPS and PxCMD.MPSP 50972fcbc377Syt * are set to '1'. 50982fcbc377Syt * 509968d33a25Syt * At the moment, this interrupt is not needed and disabled and we just log 51002fcbc377Syt * the debug message. 51012fcbc377Syt */ 51022fcbc377Syt static int 51032fcbc377Syt ahci_intr_device_mechanical_presence_status(ahci_ctl_t *ahci_ctlp, 51042fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 51052fcbc377Syt { 51062fcbc377Syt uint32_t cap_status, port_cmd_status; 51072fcbc377Syt 5108f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 51092fcbc377Syt "ahci_intr_device_mechanical_presence_status enter, " 51102fcbc377Syt "port %d", port); 51112fcbc377Syt 51122fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 51132fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 51142fcbc377Syt 51152fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 51162fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 51172fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 51182fcbc377Syt 51192fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_SMPS) || 51202fcbc377Syt !(port_cmd_status & AHCI_CMD_STATUS_MPSP)) { 5121f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 51222fcbc377Syt "CAP.SMPS or PxCMD.MPSP is not set, so just ignore " 51232fcbc377Syt "the interrupt: cap_status = 0x%x, " 51242fcbc377Syt "port_cmd_status = 0x%x", cap_status, port_cmd_status); 51252fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 51262fcbc377Syt 51272fcbc377Syt return (AHCI_SUCCESS); 51282fcbc377Syt } 51292fcbc377Syt 5130689d74b0Syt #if AHCI_DEBUG 51312fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_MPSS) { 5132f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 51332fcbc377Syt "The mechanical presence switch is open: " 51342fcbc377Syt "port %d, port_cmd_status = 0x%x", 51352fcbc377Syt port, port_cmd_status); 51362fcbc377Syt } else { 5137f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 51382fcbc377Syt "The mechanical presence switch is close: " 51392fcbc377Syt "port %d, port_cmd_status = 0x%x", 51402fcbc377Syt port, port_cmd_status); 51412fcbc377Syt } 5142689d74b0Syt #endif 51432fcbc377Syt 51442fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 51452fcbc377Syt 51462fcbc377Syt return (AHCI_SUCCESS); 51472fcbc377Syt } 51482fcbc377Syt 51492fcbc377Syt /* 51502fcbc377Syt * Native Hot Plug Support. 51512fcbc377Syt * 51522fcbc377Syt * When set, it indicates that the internal PHYRDY signal changed state. 51532fcbc377Syt * This bit reflects the state of PxSERR.DIAG.N. 515468d33a25Syt * 515568d33a25Syt * There are three kinds of conditions to generate this interrupt event: 515668d33a25Syt * 1. a device is inserted 515768d33a25Syt * 2. a device is disconnected 515868d33a25Syt * 3. when the link enters/exits a Partial or Slumber interface power 515968d33a25Syt * management state 516068d33a25Syt * 516168d33a25Syt * If inteface power management is enabled for a port, the PxSERR.DIAG.N 516268d33a25Syt * bit may be set due to the link entering the Partial or Slumber power 516368d33a25Syt * management state, rather than due to a hot plug insertion or removal 516413bcbb7aSyt * event. So far, the interface power management is disabled, so the 516513bcbb7aSyt * driver can reliably get removal detection notification via the 516613bcbb7aSyt * PxSERR.DIAG.N bit. 51672fcbc377Syt */ 51682fcbc377Syt static int 51692fcbc377Syt ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp, 51702fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 51712fcbc377Syt { 51722fcbc377Syt uint32_t port_sstatus = 0; /* No dev present & PHY not established. */ 51732fcbc377Syt sata_device_t sdevice; 51742fcbc377Syt int dev_exists_now = 0; 51752fcbc377Syt int dev_existed_previously = 0; 51762fcbc377Syt 5177f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 51782fcbc377Syt "ahci_intr_phyrdy_change enter, port %d", port); 51792fcbc377Syt 51802fcbc377Syt /* Clear PxSERR.DIAG.N to clear the interrupt bit */ 51812fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 51822fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 51832fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 518482263d52Syt SERROR_PHY_RDY_CHG); 51852fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 51862fcbc377Syt 51872fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 51882fcbc377Syt if ((ahci_ctlp->ahcictl_sata_hba_tran == NULL) || 51892fcbc377Syt (ahci_portp == NULL)) { 51902fcbc377Syt /* The whole controller setup is not yet done. */ 51912fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 51922fcbc377Syt return (AHCI_SUCCESS); 51932fcbc377Syt } 51942fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 51952fcbc377Syt 51962fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 51972fcbc377Syt 51982fcbc377Syt /* SStatus tells the presence of device. */ 51992fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 52002fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 52012fcbc377Syt 520282263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 520368d33a25Syt dev_exists_now = 1; 520468d33a25Syt } 52052fcbc377Syt 52062fcbc377Syt if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) { 52072fcbc377Syt dev_existed_previously = 1; 52082fcbc377Syt } 52092fcbc377Syt 521082263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_NODEV) { 521182263d52Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_NODEV; 5212f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 521382263d52Syt "ahci_intr_phyrdy_change: port %d " 521482263d52Syt "AHCI_PORT_FLAG_NODEV is cleared", port); 521582263d52Syt if (dev_exists_now == 0) 521682263d52Syt dev_existed_previously = 1; 521782263d52Syt } 521882263d52Syt 52192fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 522009121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 52212fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 52222fcbc377Syt sdevice.satadev_addr.pmport = 0; 52232fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 52242fcbc377Syt ahci_portp->ahciport_port_state = SATA_PSTATE_PWRON; 52252fcbc377Syt 52262fcbc377Syt if (dev_exists_now) { 52272fcbc377Syt if (dev_existed_previously) { 52282fcbc377Syt /* Things are fine now. The loss was temporary. */ 5229f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 523068d33a25Syt "ahci_intr_phyrdy_change port %d " 52312fcbc377Syt "device link lost/established", port); 52322fcbc377Syt 52332fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 52342fcbc377Syt sata_hba_event_notify( 52352fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 52362fcbc377Syt &sdevice, 52372fcbc377Syt SATA_EVNT_LINK_LOST|SATA_EVNT_LINK_ESTABLISHED); 52382fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 52392fcbc377Syt 52402fcbc377Syt } else { 5241f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 524268d33a25Syt "ahci_intr_phyrdy_change: port %d " 52432fcbc377Syt "device link established", port); 52442fcbc377Syt 52452fcbc377Syt /* A new device has been detected. */ 52460a4c4cecSXiao-Yu Zhang (void) ahci_port_reset(ahci_ctlp, ahci_portp, port); 524768d33a25Syt ahci_find_dev_signature(ahci_ctlp, ahci_portp, port); 52482fcbc377Syt 524968d33a25Syt /* Try to start the port */ 525068d33a25Syt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 525168d33a25Syt != AHCI_SUCCESS) { 525268d33a25Syt sdevice.satadev_state |= SATA_PSTATE_FAILED; 5253f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 525468d33a25Syt "ahci_intr_phyrdy_change: port %d failed " 52552fcbc377Syt "at start port", port); 52562fcbc377Syt } 52572fcbc377Syt 525882263d52Syt /* Clear the max queue depth for inserted device */ 525982263d52Syt ahci_portp->ahciport_max_ncq_tags = 0; 526082263d52Syt 52612fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 52622fcbc377Syt sata_hba_event_notify( 52632fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 52642fcbc377Syt &sdevice, 52652fcbc377Syt SATA_EVNT_LINK_ESTABLISHED); 52662fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 52672fcbc377Syt 52682fcbc377Syt } 52692fcbc377Syt } else { /* No device exists now */ 52702fcbc377Syt 52712fcbc377Syt if (dev_existed_previously) { 5272f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 527368d33a25Syt "ahci_intr_phyrdy_change: port %d " 52742fcbc377Syt "device link lost", port); 52752fcbc377Syt 52762fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 527768d33a25Syt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 52782fcbc377Syt ahci_portp, port); 52792fcbc377Syt 52802fcbc377Syt /* An existing device is lost. */ 52812fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_NONE; 52822fcbc377Syt 52832fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 52842fcbc377Syt sata_hba_event_notify( 52852fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 52862fcbc377Syt &sdevice, 52872fcbc377Syt SATA_EVNT_LINK_LOST); 52882fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 52892fcbc377Syt } 52902fcbc377Syt } 52912fcbc377Syt 52922fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 52932fcbc377Syt 52942fcbc377Syt return (AHCI_SUCCESS); 52952fcbc377Syt } 52962fcbc377Syt 52972fcbc377Syt /* 529868d33a25Syt * PxIS.UFS - Unknown FIS Error 52992fcbc377Syt * 530068d33a25Syt * This interrupt event means an unknown FIS was received and has been 530168d33a25Syt * copied into system memory. An unknown FIS is not considered an illegal 530268d33a25Syt * FIS, unless the length received is more than 64 bytes. If an unknown 530368d33a25Syt * FIS arrives with length <= 64 bytes, it is posted and the HBA continues 530468d33a25Syt * normal operation. If the unknown FIS is more than 64 bytes, then it 530568d33a25Syt * won't be posted to memory and PxSERR.ERR.P will be set, which is then 530668d33a25Syt * a fatal error. 53072fcbc377Syt * 530868d33a25Syt * PxIS.OFS - Overflow Error 53092fcbc377Syt * 531068d33a25Syt * Command list overflow is defined as software building a command table 531168d33a25Syt * that has fewer total bytes than the transaction given to the device. 531268d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 531368d33a25Syt * will be no room to put the data. 53142fcbc377Syt * 531568d33a25Syt * For an overflow on data read, either PIO or DMA, the HBA will set 531668d33a25Syt * PxIS.OFS, and the HBA will do a best effort to continue, and it's a 531768d33a25Syt * non-fatal error when the HBA can continues. Sometimes, it will cause 531868d33a25Syt * a fatal error and need the software to do something. 53192fcbc377Syt * 532068d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 532168d33a25Syt * DMA and PIO, and it's a fatal error, and a COMRESET is required by 532268d33a25Syt * software to clean up from this serious error. 53232fcbc377Syt * 532468d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 532568d33a25Syt * 532668d33a25Syt * This interrupt event indicates that the HBA encountered an error on 532768d33a25Syt * the Serial ATA interface but was able to continue operation. The kind 532868d33a25Syt * of error usually occurred during a non-Data FIS, and under this condition 532968d33a25Syt * the FIS will be re-transmitted by HBA automatically. 53302fcbc377Syt * 533168d33a25Syt * When the FMA is implemented, there should be a stat structure to 533268d33a25Syt * record how many every kind of error happens. 53332fcbc377Syt */ 53342fcbc377Syt static int 533568d33a25Syt ahci_intr_non_fatal_error(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 533668d33a25Syt uint8_t port, uint32_t intr_status) 53372fcbc377Syt { 53382fcbc377Syt uint32_t port_serror; 533968d33a25Syt #if AHCI_DEBUG 53402fcbc377Syt uint32_t port_cmd_status; 534182263d52Syt uint32_t port_cmd_issue; 534282263d52Syt uint32_t port_sactive; 53432fcbc377Syt int current_slot; 534482263d52Syt uint32_t current_tags; 534568d33a25Syt sata_pkt_t *satapkt; 534638547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 534738547057Sying tian - Beijing China uint32_t cmd_dmacount; 534868d33a25Syt #endif 53492fcbc377Syt 53502fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 53512fcbc377Syt 53522fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 53532fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 53542fcbc377Syt 5355f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY|AHCIDBG_ERRS, ahci_ctlp, 535668d33a25Syt "ahci_intr_non_fatal_error: port %d, " 53572fcbc377Syt "port_serror = 0x%x", port, port_serror); 53582fcbc377Syt 5359a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 1); 536068d33a25Syt 536168d33a25Syt if (intr_status & AHCI_INTR_STATUS_UFS) { 5362f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 536368d33a25Syt "ahci port %d has unknown FIS error", port); 53642fcbc377Syt 536568d33a25Syt /* Clear the interrupt bit by clearing PxSERR.DIAG.F */ 536668d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 536768d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 536882263d52Syt SERROR_FIS_TYPE); 536968d33a25Syt } 537068d33a25Syt 537168d33a25Syt #if AHCI_DEBUG 537268d33a25Syt if (intr_status & AHCI_INTR_STATUS_OFS) { 5373f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 537468d33a25Syt "ahci port %d has overflow error", port); 537568d33a25Syt } 537668d33a25Syt 537768d33a25Syt if (intr_status & AHCI_INTR_STATUS_INFS) { 5378f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 537968d33a25Syt "ahci port %d has interface non fatal error", port); 538068d33a25Syt } 53812fcbc377Syt 53822fcbc377Syt /* 53832fcbc377Syt * Record the error occurred command's slot. 53842fcbc377Syt */ 538582263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 538682263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 538782263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 538882263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 538982263d52Syt 539082263d52Syt current_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 539182263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 53922fcbc377Syt 539382263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 539482263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 539582263d52Syt ASSERT(satapkt != NULL); 539682263d52Syt ASSERT(current_slot == 0); 539782263d52Syt } else { 539882263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 539982263d52Syt } 54002fcbc377Syt 540182263d52Syt if (satapkt != NULL) { 5402f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 540382263d52Syt "ahci_intr_non_fatal_error: pending_tags = 0x%x " 540482263d52Syt "cmd 0x%x", ahci_portp->ahciport_pending_tags, 540582263d52Syt satapkt->satapkt_cmd.satacmd_cmd_reg); 54062fcbc377Syt 5407f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 540882263d52Syt "ahci_intr_non_fatal_error: port %d, " 540982263d52Syt "satapkt 0x%p is being processed when error occurs", 541082263d52Syt port, (void *)satapkt); 541138547057Sying tian - Beijing China 541238547057Sying tian - Beijing China /* 541338547057Sying tian - Beijing China * PRD Byte Count field of command header is not 541438547057Sying tian - Beijing China * required to reflect the total number of bytes 541538547057Sying tian - Beijing China * transferred when an overflow occurs, so here 541638547057Sying tian - Beijing China * just log the value. 541738547057Sying tian - Beijing China */ 541838547057Sying tian - Beijing China cmd_dmacount = 541938547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[current_slot]; 542038547057Sying tian - Beijing China if (cmd_dmacount) { 542138547057Sying tian - Beijing China cmd_header = &ahci_portp-> 542238547057Sying tian - Beijing China ahciport_cmd_list[current_slot]; 5423f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 542438547057Sying tian - Beijing China "ahci_intr_non_fatal_error: port %d, " 542538547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 542638547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 542738547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 542838547057Sying tian - Beijing China cmd_dmacount); 542938547057Sying tian - Beijing China } 543082263d52Syt } 5431a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 543282263d52Syt /* 543382263d52Syt * For queued command, list those command which have already 543482263d52Syt * been transmitted to the device and still not completed. 543582263d52Syt */ 543682263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 543782263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 543882263d52Syt 543982263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 544082263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 544182263d52Syt 5442f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, ahci_ctlp, 544382263d52Syt "ahci_intr_non_fatal_error: pending_ncq_tags = 0x%x " 544482263d52Syt "port_sactive = 0x%x port_cmd_issue = 0x%x", 544582263d52Syt ahci_portp->ahciport_pending_ncq_tags, 544682263d52Syt port_sactive, port_cmd_issue); 544782263d52Syt 544882263d52Syt current_tags = ahci_portp->ahciport_pending_ncq_tags & 544982263d52Syt port_sactive & ~port_cmd_issue & 545082263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 545182263d52Syt 545282263d52Syt while (current_tags) { 545382263d52Syt current_slot = ddi_ffs(current_tags) - 1; 545482263d52Syt if (current_slot == -1) { 545582263d52Syt goto out; 545682263d52Syt } 545782263d52Syt 545882263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 5459f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, 546082263d52Syt ahci_ctlp, "ahci_intr_non_fatal_error: " 546182263d52Syt "port %d, satapkt 0x%p is outstanding when " 546282263d52Syt "error occurs", port, (void *)satapkt); 5463a419422eSXiao-Yu Zhang 5464a419422eSXiao-Yu Zhang CLEAR_BIT(current_tags, current_slot); 546582263d52Syt } 54662fcbc377Syt } 546782263d52Syt out: 54682fcbc377Syt #endif 54692fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 54702fcbc377Syt 54712fcbc377Syt return (AHCI_SUCCESS); 54722fcbc377Syt } 54732fcbc377Syt 54742fcbc377Syt /* 547568d33a25Syt * According to the AHCI spec, the error types include system memory 547668d33a25Syt * errors, interface errors, port multiplier errors, device errors, 547768d33a25Syt * command list overflow, command list underflow, native command 547868d33a25Syt * queuing tag errors and pio data transfer errors. 547968d33a25Syt * 548068d33a25Syt * System memory errors such as target abort, master abort, and parity 548168d33a25Syt * may cause the host to stop, and they are serious errors and needed 548268d33a25Syt * to be recovered with software intervention. When system software 548368d33a25Syt * has given a pointer to the HBA that doesn't exist in physical memory, 548468d33a25Syt * a master/target abort error occurs, and PxIS.HBFS will be set. A 548568d33a25Syt * data error such as CRC or parity occurs, the HBA aborts the transfer 548668d33a25Syt * (if necessary) and PxIS.HBDS will be set. 548768d33a25Syt * 548868d33a25Syt * Interface errors are errors that occur due to electrical issues on 548968d33a25Syt * the interface, or protocol miscommunication between the device and 549068d33a25Syt * HBA, and the respective PxSERR register bit will be set. And PxIS.IFS 549168d33a25Syt * (fatal) or PxIS.INFS (non-fatal) will be set. The conditions that 549268d33a25Syt * causes PxIS.IFS/PxIS.INFS to be set are 549368d33a25Syt * 1. in PxSERR.ERR, P bit is set to '1' 549468d33a25Syt * 2. in PxSERR.DIAG, C or H bit is set to '1' 549568d33a25Syt * 3. PhyRdy drop unexpectly, N bit is set to '1' 549668d33a25Syt * If the error occurred during a non-data FIS, the FIS must be 549768d33a25Syt * retransmitted, and the error is non-fatal and PxIS.INFS is set. If 549868d33a25Syt * the error occurred during a data FIS, the transfer will stop, so 549968d33a25Syt * the error is fatal and PxIS.IFS is set. 550068d33a25Syt * 550168d33a25Syt * When a FIS arrives that updates the taskfile, the HBA checks to see 550268d33a25Syt * if PxTFD.STS.ERR is set. If yes, PxIS.TFES will be set and the HBA 550368d33a25Syt * stops processing any more commands. 55042fcbc377Syt * 550568d33a25Syt * Command list overflow is defined as software building a command table 550668d33a25Syt * that has fewer total bytes than the transaction given to the device. 550768d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 550868d33a25Syt * will be no room to put the data. For an overflow on data read, either 550968d33a25Syt * PIO or DMA, the HBA will set PxIS.OFS, and it's a non-fatal error. 551068d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 551168d33a25Syt * DMA and PIO, and a COMRESET is required by software to clean up from 551268d33a25Syt * this serious error. 55132fcbc377Syt * 551468d33a25Syt * Command list underflow is defined as software building a command 551568d33a25Syt * table that has more total bytes than the transaction given to the 551668d33a25Syt * device. For data writes, both PIO and DMA, the device will detect 551768d33a25Syt * an error and end the transfer. And these errors are most likely going 551868d33a25Syt * to be fatal errors that will cause the port to be restarted. For 551968d33a25Syt * data reads, the HBA updates its PRD byte count, and may be 552068d33a25Syt * able to continue normally, but is not required to. And The HBA is 552168d33a25Syt * not required to detect underflow conditions for native command 552268d33a25Syt * queuing command. 552368d33a25Syt * 552468d33a25Syt * The HBA does not actively check incoming DMA Setup FISes to ensure 552568d33a25Syt * that the PxSACT register bit for that slot is set. Existing error 552668d33a25Syt * mechanisms, such as host bus failure, or bad protocol, are used to 552768d33a25Syt * recover from this case. 552868d33a25Syt * 552968d33a25Syt * In accordance with Serial ATA 1.0a, DATA FISes prior to the final 553068d33a25Syt * DATA FIS must be an integral number of Dwords. If the HBA receives 553168d33a25Syt * a request which is not an integral number of Dwords, the HBA 553268d33a25Syt * set PxSERR.ERR.P to '1', set PxIS.IFS to '1' and stop running until 553368d33a25Syt * software restarts the port. And the HBA ensures that the size 553468d33a25Syt * of the DATA FIS received during a PIO command matches the size in 553568d33a25Syt * the Transfer Cound field of the preceding PIO Setup FIS, if not, the 553668d33a25Syt * HBA sets PxSERR.ERR.P to '1', set PxIS.IFS to '1', and then 553768d33a25Syt * stop running until software restarts the port. 55382fcbc377Syt */ 55392fcbc377Syt /* 554068d33a25Syt * the fatal errors include PxIS.IFS, PxIS.HBDS, PxIS.HBFS and PxIS.TFES. 554168d33a25Syt * 554268d33a25Syt * PxIS.IFS indicates that the hba encountered an error on the serial ata 554368d33a25Syt * interface which caused the transfer to stop. 55442fcbc377Syt * 554568d33a25Syt * PxIS.HBDS indicates that the hba encountered a data error 554668d33a25Syt * (uncorrectable ecc/parity) when reading from or writing to system memory. 554768d33a25Syt * 554868d33a25Syt * PxIS.HBFS indicates that the hba encountered a host bus error that it 554968d33a25Syt * cannot recover from, such as a bad software pointer. 555068d33a25Syt * 555168d33a25Syt * PxIS.TFES is set whenever the status register is updated by the device 555268d33a25Syt * and the error bit (bit 0) is set. 55532fcbc377Syt */ 55542fcbc377Syt static int 555582263d52Syt ahci_intr_fatal_error(ahci_ctl_t *ahci_ctlp, 555682263d52Syt ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 55572fcbc377Syt { 555868d33a25Syt uint32_t port_cmd_status; 55592fcbc377Syt uint32_t port_serror; 556068d33a25Syt uint32_t task_file_status; 556168d33a25Syt int failed_slot; 556282263d52Syt sata_pkt_t *spkt = NULL; 556368d33a25Syt uint8_t err_byte; 556468d33a25Syt ahci_event_arg_t *args; 5565a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 55662fcbc377Syt 55672fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 55682fcbc377Syt 556968d33a25Syt /* 557068d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 557168d33a25Syt * SATA_DTYPE_NONE. 557268d33a25Syt */ 557368d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 5574f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 557568d33a25Syt "ahci_intr_fatal_error: port %d no device attached, " 557668d33a25Syt "and just return without doing anything", port); 557768d33a25Syt goto out0; 557868d33a25Syt } 55792fcbc377Syt 5580f8a673adSying tian - Beijing China if (intr_status & AHCI_INTR_STATUS_TFES) { 5581f8a673adSying tian - Beijing China task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5582f8a673adSying tian - Beijing China (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 5583f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 5584f8a673adSying tian - Beijing China "ahci_intr_fatal_error: port %d " 5585f8a673adSying tian - Beijing China "task_file_status = 0x%x", port, task_file_status); 5586f8a673adSying tian - Beijing China } 5587f8a673adSying tian - Beijing China 558882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 558982263d52Syt /* 559082263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 559182263d52Syt * was processing when the error occurred. 559282263d52Syt */ 559382263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 559482263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 559582263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 559682263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 55972fcbc377Syt 559882263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 5599f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 560082263d52Syt "ahci_intr_fatal_error: spkt 0x%p is being processed when " 560182263d52Syt "fatal error occurred for port %d", spkt, port); 56022fcbc377Syt 560382263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 560482263d52Syt err_byte = (task_file_status & AHCI_TFD_ERR_MASK) 560582263d52Syt >> AHCI_TFD_ERR_SHIFT; 56062fcbc377Syt 560782263d52Syt /* 560882263d52Syt * Won't emit the error message if it is an IDENTIFY 560982263d52Syt * DEVICE command sent to an ATAPI device. 561082263d52Syt */ 561182263d52Syt if ((spkt != NULL) && 561282263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == 561382263d52Syt SATAC_ID_DEVICE) && 561482263d52Syt (err_byte == SATA_ERROR_ABORT)) 561582263d52Syt goto out1; 561682263d52Syt 561782263d52Syt /* 561882263d52Syt * Won't emit the error message if it is an ATAPI PACKET 561982263d52Syt * command 562082263d52Syt */ 562182263d52Syt if ((spkt != NULL) && 562282263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_PACKET)) 562382263d52Syt goto out1; 562482263d52Syt } 562568d33a25Syt } 56262fcbc377Syt 56277095af19Sying tian - Beijing China /* print the fatal error type */ 562868d33a25Syt ahci_log_fatal_error_message(ahci_ctlp, port, intr_status); 56292fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 56302fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 56317095af19Sying tian - Beijing China 56327095af19Sying tian - Beijing China /* print PxSERR related error message */ 5633a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 0); 56347095af19Sying tian - Beijing China 56357095af19Sying tian - Beijing China /* print task file register value */ 56367095af19Sying tian - Beijing China if (intr_status & AHCI_INTR_STATUS_TFES) { 56377095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d task_file_status " 56387095af19Sying tian - Beijing China "= 0x%x", instance, port, task_file_status); 56397095af19Sying tian - Beijing China } 56407095af19Sying tian - Beijing China 5641a9440e8dSyt out1: 564268d33a25Syt /* Prepare the argument for the taskq */ 564368d33a25Syt args = ahci_portp->ahciport_event_args; 564468d33a25Syt args->ahciea_ctlp = (void *)ahci_ctlp; 564568d33a25Syt args->ahciea_portp = (void *)ahci_portp; 564668d33a25Syt args->ahciea_event = intr_status; 564768d33a25Syt 564868d33a25Syt /* Start the taskq to handle error recovery */ 5649f8a673adSying tian - Beijing China if ((ddi_taskq_dispatch(ahci_portp->ahciport_event_taskq, 565068d33a25Syt ahci_events_handler, 565168d33a25Syt (void *)args, DDI_NOSLEEP)) != DDI_SUCCESS) { 5652a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci start taskq for event handler " 5653a9440e8dSyt "failed", instance); 565468d33a25Syt } 565568d33a25Syt out0: 56562fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 56572fcbc377Syt 56582fcbc377Syt return (AHCI_SUCCESS); 56592fcbc377Syt } 56602fcbc377Syt 56612fcbc377Syt /* 56622fcbc377Syt * Hot Plug Operation for platforms that support Cold Presence Detect. 56632fcbc377Syt * 56642fcbc377Syt * When set, a device status has changed as detected by the cold presence 56652fcbc377Syt * detect logic. This bit can either be set due to a non-connected port 56662fcbc377Syt * receiving a device, or a connected port having its device removed. 56672fcbc377Syt * This bit is only valid if the port supports cold presence detect as 56682fcbc377Syt * indicated by PxCMD.CPD set to '1'. 56692fcbc377Syt * 567068d33a25Syt * At the moment, this interrupt is not needed and disabled and we just 567168d33a25Syt * log the debug message. 56722fcbc377Syt */ 56732fcbc377Syt static int 56742fcbc377Syt ahci_intr_cold_port_detect(ahci_ctl_t *ahci_ctlp, 56752fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 56762fcbc377Syt { 56772fcbc377Syt uint32_t port_cmd_status; 56782fcbc377Syt sata_device_t sdevice; 56792fcbc377Syt 5680f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 56812fcbc377Syt "ahci_intr_cold_port_detect enter, port %d", port); 56822fcbc377Syt 56832fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 56842fcbc377Syt 56852fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 56862fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 56872fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_CPD)) { 5688f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 56892fcbc377Syt "port %d does not support cold presence detect, so " 56902fcbc377Syt "we just ignore this interrupt", port); 56912fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 56922fcbc377Syt return (AHCI_SUCCESS); 56932fcbc377Syt } 56942fcbc377Syt 5695f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 56962fcbc377Syt "port %d device status has changed", port); 56972fcbc377Syt 56982fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 569909121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 57002fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 57012fcbc377Syt sdevice.satadev_addr.pmport = 0; 57022fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 57032fcbc377Syt 57042fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_CPS) { 5705f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 57062fcbc377Syt "port %d: a device is hot plugged", port); 57072fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 57082fcbc377Syt sata_hba_event_notify( 57092fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 57102fcbc377Syt &sdevice, 57112fcbc377Syt SATA_EVNT_DEVICE_ATTACHED); 57122fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 57132fcbc377Syt 57142fcbc377Syt } else { 5715f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 57162fcbc377Syt "port %d: a device is hot unplugged", port); 57172fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 57182fcbc377Syt sata_hba_event_notify( 57192fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 57202fcbc377Syt &sdevice, 57212fcbc377Syt SATA_EVNT_DEVICE_DETACHED); 57222fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 57232fcbc377Syt } 57242fcbc377Syt 57252fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 57262fcbc377Syt 57272fcbc377Syt return (AHCI_SUCCESS); 57282fcbc377Syt } 57292fcbc377Syt 57302fcbc377Syt /* 57312fcbc377Syt * Enable the interrupts for a particular port. 57322fcbc377Syt * 57332fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 57342fcbc377Syt * is called. 57352fcbc377Syt */ 57362fcbc377Syt static void 5737689d74b0Syt ahci_enable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 57382fcbc377Syt { 5739f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 57402fcbc377Syt "ahci_enable_port_intrs enter, port %d", port); 57412fcbc377Syt 57422fcbc377Syt /* 57432fcbc377Syt * Clear port interrupt status before enabling interrupt 57442fcbc377Syt */ 57452fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 57462fcbc377Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 57472fcbc377Syt AHCI_PORT_INTR_MASK); 57482fcbc377Syt 57492fcbc377Syt /* 57502fcbc377Syt * Clear the pending bit from IS.IPS 57512fcbc377Syt */ 57522fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 57532fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (1 << port)); 57542fcbc377Syt 57552fcbc377Syt /* 57562fcbc377Syt * Enable the following interrupts: 57572fcbc377Syt * Device to Host Register FIS Interrupt (DHRS) 57582fcbc377Syt * PIO Setup FIS Interrupt (PSS) 575982263d52Syt * Set Device Bits Interrupt (SDBS) 57602fcbc377Syt * Unknown FIS Interrupt (UFS) 57612fcbc377Syt * Port Connect Change Status (PCS) 57622fcbc377Syt * PhyRdy Change Status (PRCS) 57632fcbc377Syt * Overflow Status (OFS) 57642fcbc377Syt * Interface Non-fatal Error Status (INFS) 57652fcbc377Syt * Interface Fatal Error Status (IFS) 57662fcbc377Syt * Host Bus Data Error Status (HBDS) 57672fcbc377Syt * Host Bus Fatal Error Status (HBFS) 57682fcbc377Syt * Task File Error Status (TFES) 57692fcbc377Syt */ 57702fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 57712fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 57722fcbc377Syt (AHCI_INTR_STATUS_DHRS | 57732fcbc377Syt AHCI_INTR_STATUS_PSS | 577482263d52Syt AHCI_INTR_STATUS_SDBS | 57752fcbc377Syt AHCI_INTR_STATUS_UFS | 577668d33a25Syt AHCI_INTR_STATUS_DPS | 57772fcbc377Syt AHCI_INTR_STATUS_PCS | 57782fcbc377Syt AHCI_INTR_STATUS_PRCS | 57792fcbc377Syt AHCI_INTR_STATUS_OFS | 57802fcbc377Syt AHCI_INTR_STATUS_INFS | 57812fcbc377Syt AHCI_INTR_STATUS_IFS | 57822fcbc377Syt AHCI_INTR_STATUS_HBDS | 57832fcbc377Syt AHCI_INTR_STATUS_HBFS | 57842fcbc377Syt AHCI_INTR_STATUS_TFES)); 57852fcbc377Syt } 57862fcbc377Syt 57872fcbc377Syt /* 57882fcbc377Syt * Enable interrupts for all the ports. 57892fcbc377Syt * 57902fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 57912fcbc377Syt * is called. 57922fcbc377Syt */ 57932fcbc377Syt static void 57942fcbc377Syt ahci_enable_all_intrs(ahci_ctl_t *ahci_ctlp) 57952fcbc377Syt { 57962fcbc377Syt uint32_t ghc_control; 57972fcbc377Syt 5798f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_enable_all_intrs enter", NULL); 57992fcbc377Syt 58002fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 58012fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 58022fcbc377Syt 58032fcbc377Syt ghc_control |= AHCI_HBA_GHC_IE; 58042fcbc377Syt 58052fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 58062fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 58072fcbc377Syt } 58082fcbc377Syt 58092fcbc377Syt /* 58102fcbc377Syt * Disable interrupts for a particular port. 58112fcbc377Syt * 58122fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 58132fcbc377Syt * is called. 58142fcbc377Syt */ 58152fcbc377Syt static void 5816689d74b0Syt ahci_disable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 58172fcbc377Syt { 5818f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 58192fcbc377Syt "ahci_disable_port_intrs enter, port %d", port); 58202fcbc377Syt 58212fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 58222fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 0); 58232fcbc377Syt } 58242fcbc377Syt 58252fcbc377Syt /* 58262fcbc377Syt * Disable interrupts for the whole HBA. 58272fcbc377Syt * 58282fcbc377Syt * The global bit is cleared, then all interrupt sources from all 58292fcbc377Syt * ports are disabled. 58302fcbc377Syt * 58312fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 58322fcbc377Syt * is called. 58332fcbc377Syt */ 58342fcbc377Syt static void 58352fcbc377Syt ahci_disable_all_intrs(ahci_ctl_t *ahci_ctlp) 58362fcbc377Syt { 58372fcbc377Syt uint32_t ghc_control; 58382fcbc377Syt 5839f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_disable_all_intrs enter", 5840f5f2d263SFred Herard NULL); 58412fcbc377Syt 58422fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 58432fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 58442fcbc377Syt 58452fcbc377Syt ghc_control &= ~ AHCI_HBA_GHC_IE; 58462fcbc377Syt 58472fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 58482fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 58492fcbc377Syt } 58502fcbc377Syt 58512fcbc377Syt /* 58522c742e1fSying tian - Beijing China * Handle FIXED or MSI interrupts. 58532fcbc377Syt */ 58542fcbc377Syt /* 58552c742e1fSying tian - Beijing China * According to AHCI spec, the HBA may support several interrupt modes: 58562c742e1fSying tian - Beijing China * * pin based interrupts (FIXED) 58572c742e1fSying tian - Beijing China * * single MSI message interrupts 58582c742e1fSying tian - Beijing China * * multiple MSI based message interrupts 58592c742e1fSying tian - Beijing China * 58602c742e1fSying tian - Beijing China * For pin based interrupts, the software interrupt handler need to check IS 58612c742e1fSying tian - Beijing China * register to find out which port has pending interrupts. And then check 58622c742e1fSying tian - Beijing China * PxIS register to find out which interrupt events happened on that port. 58632c742e1fSying tian - Beijing China * 58642c742e1fSying tian - Beijing China * For single MSI message interrupts, MSICAP.MC.MSIE is set with '1', and 58652c742e1fSying tian - Beijing China * MSICAP.MC.MME is set with '0'. This mode is similar to pin based interrupts 58662c742e1fSying tian - Beijing China * in that software interrupt handler need to check IS register to determine 58672c742e1fSying tian - Beijing China * which port triggered the interrupts since it uses a single message for all 58682c742e1fSying tian - Beijing China * port interrupts. 58692c742e1fSying tian - Beijing China * 58702c742e1fSying tian - Beijing China * HBA may optionally support multiple MSI message for better performance. In 58712c742e1fSying tian - Beijing China * this mode, each port may have its own interrupt message, and thus generation 58722c742e1fSying tian - Beijing China * of interrupts is no longer controlled through the IS register. MSICAP.MC.MMC 58732c742e1fSying tian - Beijing China * represents a power-of-2 wrapper on the number of implemented ports, and 58742c742e1fSying tian - Beijing China * the mapping of ports to interrupts is done in a 1-1 relationship, up to the 58752c742e1fSying tian - Beijing China * maximum number of assigned interrupts. When the number of MSI messages 58762c742e1fSying tian - Beijing China * allocated is less than the number requested, then hardware may have two 58772c742e1fSying tian - Beijing China * implementation behaviors: 58782c742e1fSying tian - Beijing China * * assign each ports its own interrupt and then force all additional 58792c742e1fSying tian - Beijing China * ports to share the last interrupt message, and this condition is 58802c742e1fSying tian - Beijing China * indicated by clearing GHC.MRSM to '0' 58812c742e1fSying tian - Beijing China * * revert to single MSI mode, indicated by setting GHC.MRSM to '1' 58822c742e1fSying tian - Beijing China * When multiple-message MSI is enabled, hardware will still set IS register 58832c742e1fSying tian - Beijing China * as single message case. And this IS register may be used by software when 58842c742e1fSying tian - Beijing China * fewer than the requested number of messages is granted in order to determine 58852c742e1fSying tian - Beijing China * which port had the interrupt. 58862c742e1fSying tian - Beijing China * 58872c742e1fSying tian - Beijing China * Note: The current ahci driver only supports the first two interrupt modes: 58882c742e1fSying tian - Beijing China * pin based interrupts and single MSI message interrupts, and the reason 58892c742e1fSying tian - Beijing China * is indicated in below code. 58902fcbc377Syt */ 58912fcbc377Syt static int 58922c742e1fSying tian - Beijing China ahci_add_intrs(ahci_ctl_t *ahci_ctlp, int intr_type) 58932fcbc377Syt { 58942fcbc377Syt dev_info_t *dip = ahci_ctlp->ahcictl_dip; 58952fcbc377Syt int count, avail, actual; 58962c742e1fSying tian - Beijing China int i, rc; 58972fcbc377Syt 5898f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 58992c742e1fSying tian - Beijing China "ahci_add_intrs enter interrupt type 0x%x", intr_type); 59002fcbc377Syt 59012fcbc377Syt /* get number of interrupts. */ 59022c742e1fSying tian - Beijing China rc = ddi_intr_get_nintrs(dip, intr_type, &count); 59032fcbc377Syt if ((rc != DDI_SUCCESS) || (count == 0)) { 5904f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59052fcbc377Syt "ddi_intr_get_nintrs() failed, " 59062fcbc377Syt "rc %d count %d\n", rc, count); 59072fcbc377Syt return (DDI_FAILURE); 59082fcbc377Syt } 59092fcbc377Syt 59102fcbc377Syt /* get number of available interrupts. */ 59112c742e1fSying tian - Beijing China rc = ddi_intr_get_navail(dip, intr_type, &avail); 59122fcbc377Syt if ((rc != DDI_SUCCESS) || (avail == 0)) { 5913f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59142fcbc377Syt "ddi_intr_get_navail() failed, " 59152fcbc377Syt "rc %d avail %d\n", rc, avail); 59162fcbc377Syt return (DDI_FAILURE); 59172fcbc377Syt } 59182fcbc377Syt 5919689d74b0Syt #if AHCI_DEBUG 59202fcbc377Syt if (avail < count) { 5921f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59222c742e1fSying tian - Beijing China "ddi_intr_get_nintrs returned %d, navail() returned %d", 59232fcbc377Syt count, avail); 59242fcbc377Syt } 5925689d74b0Syt #endif 59262fcbc377Syt 59272c742e1fSying tian - Beijing China /* 59282c742e1fSying tian - Beijing China * Note: So far Solaris restricts the maximum number of messages for 59292c742e1fSying tian - Beijing China * x86 to 2, that is avail is 2, so here we set the count with 1 to 59302c742e1fSying tian - Beijing China * force the driver to use single MSI message interrupt. In future if 59312c742e1fSying tian - Beijing China * Solaris remove the restriction, then we need to delete the below 59322c742e1fSying tian - Beijing China * code and try to use multiple interrupt routine to gain better 59332c742e1fSying tian - Beijing China * performance. 59342c742e1fSying tian - Beijing China */ 59352c742e1fSying tian - Beijing China if ((intr_type == DDI_INTR_TYPE_MSI) && (count > 1)) { 5936f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 59372c742e1fSying tian - Beijing China "force to use one interrupt routine though the " 59382c742e1fSying tian - Beijing China "HBA supports %d interrupt", count); 59392c742e1fSying tian - Beijing China count = 1; 59402c742e1fSying tian - Beijing China } 59412c742e1fSying tian - Beijing China 59422fcbc377Syt /* Allocate an array of interrupt handles. */ 59432fcbc377Syt ahci_ctlp->ahcictl_intr_size = count * sizeof (ddi_intr_handle_t); 59442fcbc377Syt ahci_ctlp->ahcictl_intr_htable = 59452fcbc377Syt kmem_alloc(ahci_ctlp->ahcictl_intr_size, KM_SLEEP); 59462fcbc377Syt 59472fcbc377Syt /* call ddi_intr_alloc(). */ 59482fcbc377Syt rc = ddi_intr_alloc(dip, ahci_ctlp->ahcictl_intr_htable, 59492c742e1fSying tian - Beijing China intr_type, 0, count, &actual, DDI_INTR_ALLOC_NORMAL); 59502fcbc377Syt 59512fcbc377Syt if ((rc != DDI_SUCCESS) || (actual == 0)) { 5952f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59532c742e1fSying tian - Beijing China "ddi_intr_alloc() failed, rc %d count %d actual %d " 59542c742e1fSying tian - Beijing China "avail %d\n", rc, count, actual, avail); 59552fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 59562fcbc377Syt ahci_ctlp->ahcictl_intr_size); 59572fcbc377Syt return (DDI_FAILURE); 59582fcbc377Syt } 59592fcbc377Syt 59602fcbc377Syt /* use interrupt count returned */ 5961689d74b0Syt #if AHCI_DEBUG 59622fcbc377Syt if (actual < count) { 5963f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59642fcbc377Syt "Requested: %d, Received: %d", count, actual); 59652fcbc377Syt } 5966689d74b0Syt #endif 59672fcbc377Syt 59682fcbc377Syt ahci_ctlp->ahcictl_intr_cnt = actual; 59692fcbc377Syt 59702fcbc377Syt /* 59712c742e1fSying tian - Beijing China * Get priority for first, assume remaining are all the same. 59722fcbc377Syt */ 59732fcbc377Syt if (ddi_intr_get_pri(ahci_ctlp->ahcictl_intr_htable[0], 59742fcbc377Syt &ahci_ctlp->ahcictl_intr_pri) != DDI_SUCCESS) { 5975f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 5976f5f2d263SFred Herard "ddi_intr_get_pri() failed", NULL); 59772fcbc377Syt 59782fcbc377Syt /* Free already allocated intr. */ 59792c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 59802c742e1fSying tian - Beijing China (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[i]); 59812fcbc377Syt } 59822fcbc377Syt 59832fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 59842fcbc377Syt ahci_ctlp->ahcictl_intr_size); 59852fcbc377Syt return (DDI_FAILURE); 59862fcbc377Syt } 59872fcbc377Syt 59882fcbc377Syt /* Test for high level interrupt. */ 59892fcbc377Syt if (ahci_ctlp->ahcictl_intr_pri >= ddi_intr_get_hilevel_pri()) { 5990f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 5991f5f2d263SFred Herard "ahci_add_intrs: Hi level intr not supported", NULL); 59922fcbc377Syt 59932fcbc377Syt /* Free already allocated intr. */ 59942c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 59952c742e1fSying tian - Beijing China (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[i]); 59962fcbc377Syt } 59972fcbc377Syt 59982fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 59992fcbc377Syt sizeof (ddi_intr_handle_t)); 60002fcbc377Syt 60012fcbc377Syt return (DDI_FAILURE); 60022fcbc377Syt } 60032fcbc377Syt 60042fcbc377Syt /* Call ddi_intr_add_handler(). */ 60052c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 60062c742e1fSying tian - Beijing China if (ddi_intr_add_handler(ahci_ctlp->ahcictl_intr_htable[i], 60072fcbc377Syt ahci_intr, (caddr_t)ahci_ctlp, NULL) != DDI_SUCCESS) { 6008f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 6009f5f2d263SFred Herard "ddi_intr_add_handler() failed", NULL); 60102fcbc377Syt 60112fcbc377Syt /* Free already allocated intr. */ 60122c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 60132fcbc377Syt (void) ddi_intr_free( 60142c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 60152fcbc377Syt } 60162fcbc377Syt 60172fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 60182fcbc377Syt ahci_ctlp->ahcictl_intr_size); 60192fcbc377Syt return (DDI_FAILURE); 60202fcbc377Syt } 60212fcbc377Syt } 60222fcbc377Syt 60232c742e1fSying tian - Beijing China if (ddi_intr_get_cap(ahci_ctlp->ahcictl_intr_htable[0], 60242c742e1fSying tian - Beijing China &ahci_ctlp->ahcictl_intr_cap) != DDI_SUCCESS) { 6025f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 6026f5f2d263SFred Herard "ddi_intr_get_cap() failed", NULL); 60272fcbc377Syt 60282c742e1fSying tian - Beijing China /* Free already allocated intr. */ 60292c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 60302c742e1fSying tian - Beijing China (void) ddi_intr_free( 60312c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 60322c742e1fSying tian - Beijing China } 60332c742e1fSying tian - Beijing China 60342c742e1fSying tian - Beijing China kmem_free(ahci_ctlp->ahcictl_intr_htable, 60352c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_size); 60362c742e1fSying tian - Beijing China return (DDI_FAILURE); 60372c742e1fSying tian - Beijing China } 60382fcbc377Syt 60392fcbc377Syt if (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK) { 60402fcbc377Syt /* Call ddi_intr_block_enable() for MSI. */ 60412fcbc377Syt (void) ddi_intr_block_enable(ahci_ctlp->ahcictl_intr_htable, 60422fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 60432fcbc377Syt } else { 60442c742e1fSying tian - Beijing China /* Call ddi_intr_enable() for FIXED or MSI non block enable. */ 60452c742e1fSying tian - Beijing China for (i = 0; i < ahci_ctlp->ahcictl_intr_cnt; i++) { 60462fcbc377Syt (void) ddi_intr_enable( 60472c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 60482fcbc377Syt } 60492fcbc377Syt } 60502fcbc377Syt 60512fcbc377Syt return (DDI_SUCCESS); 60522fcbc377Syt } 60532fcbc377Syt 60542fcbc377Syt /* 60552fcbc377Syt * Removes the registered interrupts irrespective of whether they 60562fcbc377Syt * were legacy or MSI. 60572fcbc377Syt * 60582fcbc377Syt * WARNING!!! The controller interrupts must be disabled before calling 60592fcbc377Syt * this routine. 60602fcbc377Syt */ 60612fcbc377Syt static void 60622fcbc377Syt ahci_rem_intrs(ahci_ctl_t *ahci_ctlp) 60632fcbc377Syt { 60642fcbc377Syt int x; 60652fcbc377Syt 6066f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_rem_intrs entered", NULL); 60672fcbc377Syt 60682fcbc377Syt /* Disable all interrupts. */ 60692fcbc377Syt if ((ahci_ctlp->ahcictl_intr_type == DDI_INTR_TYPE_MSI) && 60702fcbc377Syt (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK)) { 60712fcbc377Syt /* Call ddi_intr_block_disable(). */ 60722fcbc377Syt (void) ddi_intr_block_disable(ahci_ctlp->ahcictl_intr_htable, 60732fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 60742fcbc377Syt } else { 60752fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 60762fcbc377Syt (void) ddi_intr_disable( 60772fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 60782fcbc377Syt } 60792fcbc377Syt } 60802fcbc377Syt 60812fcbc377Syt /* Call ddi_intr_remove_handler(). */ 60822fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 60832fcbc377Syt (void) ddi_intr_remove_handler( 60842fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 60852fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[x]); 60862fcbc377Syt } 60872fcbc377Syt 60882fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, ahci_ctlp->ahcictl_intr_size); 60892fcbc377Syt } 60902fcbc377Syt 60912fcbc377Syt /* 609268d33a25Syt * This routine tries to put port into P:NotRunning state by clearing 609368d33a25Syt * PxCMD.ST. HBA will clear PxCI to 0h, PxSACT to 0h, PxCMD.CCS to 0h 609468d33a25Syt * and PxCMD.CR to '0'. 60952fcbc377Syt * 60962fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 60972fcbc377Syt * is called. 60982fcbc377Syt */ 60992fcbc377Syt static int 610068d33a25Syt ahci_put_port_into_notrunning_state(ahci_ctl_t *ahci_ctlp, 610168d33a25Syt ahci_port_t *ahci_portp, uint8_t port) 61022fcbc377Syt { 61032fcbc377Syt uint32_t port_cmd_status; 61042fcbc377Syt int loop_count; 61052fcbc377Syt 6106f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 610768d33a25Syt "ahci_put_port_into_notrunning_state enter: port %d", port); 61082fcbc377Syt 61092fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 61102fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 61112fcbc377Syt 61122fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_ST; 61132fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 61142fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 61152fcbc377Syt 61162fcbc377Syt /* Wait until PxCMD.CR is cleared */ 61172fcbc377Syt loop_count = 0; 61182fcbc377Syt do { 61192fcbc377Syt port_cmd_status = 61202fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 61212fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 61222fcbc377Syt 61232fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_IDLE) { 6124f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 61252fcbc377Syt "clearing port %d CMD.CR timeout, " 61262fcbc377Syt "port_cmd_status = 0x%x", port, 61272fcbc377Syt port_cmd_status); 61282fcbc377Syt /* 61292fcbc377Syt * We are effectively timing out after 0.5 sec. 61302fcbc377Syt * This value is specified in AHCI spec. 61312fcbc377Syt */ 61322fcbc377Syt break; 61332fcbc377Syt } 61342fcbc377Syt 6135689d74b0Syt /* Wait for 10 millisec */ 613619397407SSherry Moore drv_usecwait(10000); 61372fcbc377Syt } while (port_cmd_status & AHCI_CMD_STATUS_CR); 61382fcbc377Syt 613968d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_STARTED; 61402fcbc377Syt 614168d33a25Syt if (port_cmd_status & AHCI_CMD_STATUS_CR) { 6142f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 614368d33a25Syt "ahci_put_port_into_notrunning_state: failed to clear " 614468d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 614568d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 61462fcbc377Syt return (AHCI_FAILURE); 61472fcbc377Syt } else { 6148f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 614968d33a25Syt "ahci_put_port_into_notrunning_state: succeeded to clear " 615068d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 615168d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 61522fcbc377Syt return (AHCI_SUCCESS); 61532fcbc377Syt } 61542fcbc377Syt } 61552fcbc377Syt 61562fcbc377Syt /* 615768d33a25Syt * First clear PxCMD.ST, and then check PxTFD. If both PxTFD.STS.BSY 615868d33a25Syt * and PxTFD.STS.DRQ cleared to '0', it means the device is in a 615968d33a25Syt * stable state, then set PxCMD.ST to '1' to start the port directly. 616068d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue a 616168d33a25Syt * COMRESET to the device to put it in an idle state. 616268d33a25Syt * 616368d33a25Syt * The fifth argument returns whether the port reset is involved during 616468d33a25Syt * the process. 616568d33a25Syt * 61660a4c4cecSXiao-Yu Zhang * The routine will be called under following scenarios: 61670a4c4cecSXiao-Yu Zhang * + To abort the packet(s) 61680a4c4cecSXiao-Yu Zhang * + To reset the port 61690a4c4cecSXiao-Yu Zhang * + To activate the port 61700a4c4cecSXiao-Yu Zhang * + Fatal error recovery 61710a4c4cecSXiao-Yu Zhang * + To abort the timeout packet(s) 61722fcbc377Syt * 61732fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 617468d33a25Syt * is called. And ahciport_mutex will be released before the reset 617568d33a25Syt * event is reported to sata module by calling sata_hba_event_notify, 617668d33a25Syt * and then be acquired again later. 617782263d52Syt * 617882263d52Syt * NOTES!!! During this procedure, PxSERR register will be cleared, and 617982263d52Syt * according to the spec, the clearance of three bits will also clear 618082263d52Syt * three interrupt status bits. 618182263d52Syt * 1. PxSERR.DIAG.F will clear PxIS.UFS 618282263d52Syt * 2. PxSERR.DIAG.X will clear PxIS.PCS 618382263d52Syt * 3. PxSERR.DIAG.N will clear PxIS.PRCS 618482263d52Syt * 618582263d52Syt * Among these three interrupt events, the driver needs to take care of 618682263d52Syt * PxIS.PRCS, which is the hot plug event. When the driver found out 618782263d52Syt * a device was unplugged, it will call the interrupt handler. 61882fcbc377Syt */ 61892fcbc377Syt static int 61902fcbc377Syt ahci_restart_port_wait_till_ready(ahci_ctl_t *ahci_ctlp, 619168d33a25Syt ahci_port_t *ahci_portp, uint8_t port, int flag, int *reset_flag) 61922fcbc377Syt { 619382263d52Syt uint32_t port_sstatus; 61942fcbc377Syt uint32_t task_file_status; 61952fcbc377Syt sata_device_t sdevice; 61962fcbc377Syt int rval; 619782263d52Syt int dev_exists_begin = 0; 619882263d52Syt int dev_exists_end = 0; 61992fcbc377Syt 6200f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 62012fcbc377Syt "ahci_restart_port_wait_till_ready: port %d enter", port); 62022fcbc377Syt 62030a4c4cecSXiao-Yu Zhang if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) 620482263d52Syt dev_exists_begin = 1; 62052fcbc377Syt 620682263d52Syt /* First clear PxCMD.ST */ 620768d33a25Syt rval = ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, 620868d33a25Syt port); 620968d33a25Syt if (rval != AHCI_SUCCESS) 621068d33a25Syt /* 621168d33a25Syt * If PxCMD.CR does not clear within a reasonable time, it 621268d33a25Syt * may assume the interface is in a hung condition and may 621368d33a25Syt * continue with issuing the port reset. 621468d33a25Syt */ 621568d33a25Syt goto reset; 62162fcbc377Syt 621768d33a25Syt /* Then clear PxSERR */ 621868d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 621968d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 622068d33a25Syt AHCI_SERROR_CLEAR_ALL); 62212fcbc377Syt 622282263d52Syt /* Then get PxTFD */ 622368d33a25Syt task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 622468d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 62252fcbc377Syt 622668d33a25Syt /* 622768d33a25Syt * Check whether the device is in a stable status, if yes, 622868d33a25Syt * then start the port directly. However for ahci_tran_dport_reset, 622968d33a25Syt * we may have to perform a port reset. 623068d33a25Syt */ 623168d33a25Syt if (!(task_file_status & (AHCI_TFD_STS_BSY | AHCI_TFD_STS_DRQ)) && 623268d33a25Syt !(flag & AHCI_PORT_RESET)) 62332fcbc377Syt goto out; 62342fcbc377Syt 623568d33a25Syt reset: 623668d33a25Syt /* 623768d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue 623868d33a25Syt * a COMRESET to the device 623968d33a25Syt */ 62402fcbc377Syt rval = ahci_port_reset(ahci_ctlp, ahci_portp, port); 624168d33a25Syt 624268d33a25Syt if (reset_flag != NULL) 624368d33a25Syt *reset_flag = 1; 62442fcbc377Syt 62452fcbc377Syt /* Indicate to the framework that a reset has happened. */ 624682263d52Syt if ((ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) && 624782263d52Syt !(flag & AHCI_RESET_NO_EVENTS_UP)) { 624882263d52Syt /* Set the reset in progress flag */ 624982263d52Syt ahci_portp->ahciport_reset_in_progress = 1; 62502fcbc377Syt 62512fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 625209121340Syt sdevice.satadev_addr.cport = 625309121340Syt ahci_ctlp->ahcictl_port_to_cport[port]; 625468d33a25Syt sdevice.satadev_addr.pmport = 0; 625568d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 62562fcbc377Syt 62572fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 62582fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 62592fcbc377Syt if (ahci_ctlp->ahcictl_sata_hba_tran) { 62602fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 62612fcbc377Syt sata_hba_event_notify( 62622fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 62632fcbc377Syt &sdevice, 62642fcbc377Syt SATA_EVNT_DEVICE_RESET); 62652fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 62662fcbc377Syt } 62672fcbc377Syt 6268f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 626968d33a25Syt "port %d sending event up: SATA_EVNT_RESET", port); 627082263d52Syt } else { 627182263d52Syt ahci_portp->ahciport_reset_in_progress = 0; 62722fcbc377Syt } 627382263d52Syt 62742fcbc377Syt out: 6275a9440e8dSyt (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 627682263d52Syt 627782263d52Syt /* SStatus tells the presence of device. */ 627882263d52Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 627982263d52Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 628082263d52Syt 628182263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 628282263d52Syt dev_exists_end = 1; 628382263d52Syt ASSERT(ahci_portp->ahciport_device_type != SATA_DTYPE_NONE); 628482263d52Syt } 628582263d52Syt 628682263d52Syt /* Check whether a hot plug event happened */ 628782263d52Syt if (dev_exists_begin == 1 && dev_exists_end == 0) { 6288f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 628982263d52Syt "ahci_restart_port_wait_till_ready: port %d " 629082263d52Syt "device is removed", port); 629182263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_NODEV; 6292f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 629382263d52Syt "ahci_restart_port_wait_till_ready: port %d " 629482263d52Syt "AHCI_PORT_FLAG_NODEV flag is set", port); 629582263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 629682263d52Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, port); 629782263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 62982fcbc377Syt } 62992fcbc377Syt 63002fcbc377Syt return (rval); 63012fcbc377Syt } 63022fcbc377Syt 63032fcbc377Syt /* 630468d33a25Syt * This routine may be called under four scenarios: 630568d33a25Syt * a) do the recovery from fatal error 63062fcbc377Syt * b) or we need to timeout some commands 63072fcbc377Syt * c) or we need to abort some commands 63082fcbc377Syt * d) or we need reset device/port/controller 63092fcbc377Syt * 63102fcbc377Syt * In all these scenarios, we need to send any pending unfinished 63112fcbc377Syt * commands up to sata framework. 63122fcbc377Syt * 631368d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 63142fcbc377Syt */ 63152fcbc377Syt static void 63162fcbc377Syt ahci_mop_commands(ahci_ctl_t *ahci_ctlp, 63172fcbc377Syt ahci_port_t *ahci_portp, 63182fcbc377Syt uint32_t slot_status, 63192fcbc377Syt uint32_t failed_tags, 63202fcbc377Syt uint32_t timeout_tags, 63212fcbc377Syt uint32_t aborted_tags, 63222fcbc377Syt uint32_t reset_tags) 63232fcbc377Syt { 632482263d52Syt uint32_t finished_tags = 0; 632582263d52Syt uint32_t unfinished_tags = 0; 63262fcbc377Syt int tmp_slot; 63272fcbc377Syt sata_pkt_t *satapkt; 632882263d52Syt int ncq_cmd_in_progress = 0; 632982263d52Syt int err_retri_cmd_in_progress = 0; 63302fcbc377Syt 6331f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 63322fcbc377Syt "ahci_mop_commands entered: port: %d slot_status: 0x%x", 6333689d74b0Syt ahci_portp->ahciport_port_num, slot_status); 63342fcbc377Syt 6335f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 63362fcbc377Syt "ahci_mop_commands: failed_tags: 0x%x, " 63372fcbc377Syt "timeout_tags: 0x%x aborted_tags: 0x%x, " 63382fcbc377Syt "reset_tags: 0x%x", failed_tags, 63392fcbc377Syt timeout_tags, aborted_tags, reset_tags); 63402fcbc377Syt 634182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 634282263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 634382263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 63442fcbc377Syt 634582263d52Syt unfinished_tags = slot_status & 634682263d52Syt AHCI_SLOT_MASK(ahci_ctlp) & 634782263d52Syt ~failed_tags & 634882263d52Syt ~aborted_tags & 634982263d52Syt ~reset_tags & 635082263d52Syt ~timeout_tags; 6351a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 635282263d52Syt ncq_cmd_in_progress = 1; 635382263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 635482263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 635582263d52Syt 635682263d52Syt unfinished_tags = slot_status & 635782263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp) & 635882263d52Syt ~failed_tags & 635982263d52Syt ~aborted_tags & 636082263d52Syt ~reset_tags & 636182263d52Syt ~timeout_tags; 6362a9440e8dSyt } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 63632fcbc377Syt 6364a9440e8dSyt /* 6365a9440e8dSyt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT is 6366a9440e8dSyt * set, it means REQUEST SENSE or READ LOG EXT command doesn't 6367a9440e8dSyt * complete successfully due to one of the following three 6368a9440e8dSyt * conditions: 6369a9440e8dSyt * 6370a9440e8dSyt * 1. Fatal error - failed_tags includes its slot 6371a9440e8dSyt * 2. Timed out - timeout_tags includes its slot 6372a9440e8dSyt * 3. Aborted when hot unplug - aborted_tags includes its 6373a9440e8dSyt * slot 6374a9440e8dSyt * 6375a9440e8dSyt * Please note that the command is always sent down in Slot 0 6376a9440e8dSyt */ 637782263d52Syt err_retri_cmd_in_progress = 1; 6378f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_NCQ, ahci_ctlp, 637982263d52Syt "ahci_mop_commands is called for port %d while " 638082263d52Syt "REQUEST SENSE or READ LOG EXT for error retrieval " 6381f8a673adSying tian - Beijing China "is being executed slot_status = 0x%x", 6382f8a673adSying tian - Beijing China ahci_portp->ahciport_port_num, slot_status); 638368d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress > 1); 638482263d52Syt ASSERT(slot_status == 0x1); 63852fcbc377Syt } 63862fcbc377Syt 638768d33a25Syt /* Send up finished packets with SATA_PKT_COMPLETED */ 63882fcbc377Syt while (finished_tags) { 63892fcbc377Syt tmp_slot = ddi_ffs(finished_tags) - 1; 63902fcbc377Syt if (tmp_slot == -1) { 63912fcbc377Syt break; 63922fcbc377Syt } 63932fcbc377Syt 63942fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 63952fcbc377Syt ASSERT(satapkt != NULL); 63962fcbc377Syt 6397f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "ahci_mop_commands: " 63982fcbc377Syt "sending up pkt 0x%p with SATA_PKT_COMPLETED", 63992fcbc377Syt (void *)satapkt); 64002fcbc377Syt 640168d33a25Syt /* 640268d33a25Syt * Cannot fetch the return register content since the port 640368d33a25Syt * was restarted, so the corresponding tag will be set to 640468d33a25Syt * aborted tags. 640568d33a25Syt */ 640668d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 640768d33a25Syt CLEAR_BIT(finished_tags, tmp_slot); 640868d33a25Syt aborted_tags |= tmp_slot; 640968d33a25Syt continue; 641068d33a25Syt } 641168d33a25Syt 641282263d52Syt if (ncq_cmd_in_progress) 641382263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 641482263d52Syt tmp_slot); 64152fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 64162fcbc377Syt CLEAR_BIT(finished_tags, tmp_slot); 64172fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 64182fcbc377Syt 64192fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 64202fcbc377Syt } 64212fcbc377Syt 642268d33a25Syt /* Send up failed packets with SATA_PKT_DEV_ERROR. */ 64232fcbc377Syt while (failed_tags) { 642482263d52Syt if (err_retri_cmd_in_progress) { 642582263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 642682263d52Syt ASSERT(satapkt != NULL); 642782263d52Syt ASSERT(failed_tags == 0x1); 642882263d52Syt 6429f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 643082263d52Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 643182263d52Syt (void *)satapkt); 643282263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 643382263d52Syt break; 643482263d52Syt } 643582263d52Syt 64362fcbc377Syt tmp_slot = ddi_ffs(failed_tags) - 1; 64372fcbc377Syt if (tmp_slot == -1) { 64382fcbc377Syt break; 64392fcbc377Syt } 64402fcbc377Syt 64412fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 64422fcbc377Syt ASSERT(satapkt != NULL); 64432fcbc377Syt 6444f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 64452fcbc377Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 64462fcbc377Syt (void *)satapkt); 64472fcbc377Syt 644882263d52Syt if (ncq_cmd_in_progress) 644982263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 645082263d52Syt tmp_slot); 64512fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 64522fcbc377Syt CLEAR_BIT(failed_tags, tmp_slot); 64532fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 64542fcbc377Syt 64552fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 64562fcbc377Syt } 64572fcbc377Syt 645868d33a25Syt /* Send up timeout packets with SATA_PKT_TIMEOUT. */ 64592fcbc377Syt while (timeout_tags) { 646082263d52Syt if (err_retri_cmd_in_progress) { 646182263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 646282263d52Syt ASSERT(satapkt != NULL); 646382263d52Syt ASSERT(timeout_tags == 0x1); 646482263d52Syt 6465f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 646682263d52Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 646782263d52Syt (void *)satapkt); 646882263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 646982263d52Syt break; 647082263d52Syt } 647182263d52Syt 64722fcbc377Syt tmp_slot = ddi_ffs(timeout_tags) - 1; 64732fcbc377Syt if (tmp_slot == -1) { 64742fcbc377Syt break; 64752fcbc377Syt } 64762fcbc377Syt 64772fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 64782fcbc377Syt ASSERT(satapkt != NULL); 64792fcbc377Syt 6480f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 64812fcbc377Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 64822fcbc377Syt (void *)satapkt); 64832fcbc377Syt 648482263d52Syt if (ncq_cmd_in_progress) 648582263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 648682263d52Syt tmp_slot); 64872fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 64882fcbc377Syt CLEAR_BIT(timeout_tags, tmp_slot); 64892fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 64902fcbc377Syt 64912fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 64922fcbc377Syt } 64932fcbc377Syt 649468d33a25Syt /* Send up aborted packets with SATA_PKT_ABORTED */ 64952fcbc377Syt while (aborted_tags) { 649682263d52Syt if (err_retri_cmd_in_progress) { 649782263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 649882263d52Syt ASSERT(satapkt != NULL); 649982263d52Syt ASSERT(aborted_tags == 0x1); 650082263d52Syt 6501f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 650282263d52Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 650382263d52Syt (void *)satapkt); 650482263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 650582263d52Syt break; 650682263d52Syt } 650782263d52Syt 65082fcbc377Syt tmp_slot = ddi_ffs(aborted_tags) - 1; 65092fcbc377Syt if (tmp_slot == -1) { 65102fcbc377Syt break; 65112fcbc377Syt } 65122fcbc377Syt 65132fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 65142fcbc377Syt ASSERT(satapkt != NULL); 65152fcbc377Syt 6516f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 65172fcbc377Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 65182fcbc377Syt (void *)satapkt); 65192fcbc377Syt 652082263d52Syt if (ncq_cmd_in_progress) 652182263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 652282263d52Syt tmp_slot); 65232fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 65242fcbc377Syt CLEAR_BIT(aborted_tags, tmp_slot); 65252fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 65262fcbc377Syt 65272fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 65282fcbc377Syt } 65292fcbc377Syt 653068d33a25Syt /* Send up reset packets with SATA_PKT_RESET. */ 65312fcbc377Syt while (reset_tags) { 65322fcbc377Syt tmp_slot = ddi_ffs(reset_tags) - 1; 65332fcbc377Syt if (tmp_slot == -1) { 65342fcbc377Syt break; 65352fcbc377Syt } 65362fcbc377Syt 65372fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 65382fcbc377Syt ASSERT(satapkt != NULL); 65392fcbc377Syt 6540f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 65412fcbc377Syt "sending up pkt 0x%p with SATA_PKT_RESET", 65422fcbc377Syt (void *)satapkt); 65432fcbc377Syt 654482263d52Syt if (ncq_cmd_in_progress) 654582263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 654682263d52Syt tmp_slot); 65472fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 65482fcbc377Syt CLEAR_BIT(reset_tags, tmp_slot); 65492fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 65502fcbc377Syt 65512fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 65522fcbc377Syt } 65532fcbc377Syt 655468d33a25Syt /* Send up unfinished packets with SATA_PKT_RESET */ 65552fcbc377Syt while (unfinished_tags) { 65562fcbc377Syt tmp_slot = ddi_ffs(unfinished_tags) - 1; 65572fcbc377Syt if (tmp_slot == -1) { 65582fcbc377Syt break; 65592fcbc377Syt } 65602fcbc377Syt 65612fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 65622fcbc377Syt ASSERT(satapkt != NULL); 65632fcbc377Syt 6564f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 656568d33a25Syt "sending up pkt 0x%p with SATA_PKT_RESET", 65662fcbc377Syt (void *)satapkt); 65672fcbc377Syt 656882263d52Syt if (ncq_cmd_in_progress) 656982263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 657082263d52Syt tmp_slot); 65712fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 65722fcbc377Syt CLEAR_BIT(unfinished_tags, tmp_slot); 65732fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 65742fcbc377Syt 657568d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 65762fcbc377Syt } 65772fcbc377Syt 657868d33a25Syt ahci_portp->ahciport_mop_in_progress--; 657968d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress >= 0); 65802fcbc377Syt 658168d33a25Syt if (ahci_portp->ahciport_mop_in_progress == 0) 658268d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_MOPPING; 658368d33a25Syt } 65842fcbc377Syt 658582263d52Syt /* 658682263d52Syt * This routine is going to first request a READ LOG EXT sata pkt from sata 658782263d52Syt * module, and then deliver it to the HBA to get the ncq failure context. 658882263d52Syt * The return value is the exactly failed tags. 658982263d52Syt */ 659082263d52Syt static uint32_t 659182263d52Syt ahci_get_rdlogext_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 659282263d52Syt uint8_t port) 659382263d52Syt { 659482263d52Syt sata_device_t sdevice; 659582263d52Syt sata_pkt_t *rdlog_spkt, *spkt; 659682263d52Syt ddi_dma_handle_t buf_dma_handle; 659782263d52Syt int loop_count; 659882263d52Syt int rval; 659982263d52Syt int failed_slot; 660082263d52Syt uint32_t failed_tags = 0; 660182263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page; 660282263d52Syt 6603f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_NCQ, ahci_ctlp, 660482263d52Syt "ahci_get_rdlogext_data enter: port %d", port); 660582263d52Syt 660682263d52Syt /* Prepare the sdevice data */ 660782263d52Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 660882263d52Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 660982263d52Syt 661082263d52Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 661182263d52Syt sdevice.satadev_addr.pmport = 0; 661282263d52Syt 661382263d52Syt /* 661482263d52Syt * Call the sata hba interface to get a rdlog spkt 661582263d52Syt */ 661682263d52Syt loop_count = 0; 661782263d52Syt loop: 661882263d52Syt rdlog_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 661982263d52Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_NCQ); 662082263d52Syt if (rdlog_spkt == NULL) { 662182263d52Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 662282263d52Syt /* Sleep for a while */ 662382263d52Syt delay(AHCI_10MS_TICKS); 662482263d52Syt goto loop; 662582263d52Syt } 662682263d52Syt /* Timed out after 1s */ 6627f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 662882263d52Syt "failed to get rdlog spkt for port %d", port); 662982263d52Syt return (failed_tags); 663082263d52Syt } 663182263d52Syt 663282263d52Syt ASSERT(rdlog_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 663382263d52Syt 663482263d52Syt /* 663582263d52Syt * This flag is used to handle the specific error recovery when the 663682263d52Syt * READ LOG EXT command gets a failure (fatal error or time-out). 663782263d52Syt */ 663882263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RDLOGEXT; 663982263d52Syt 664082263d52Syt /* 664182263d52Syt * This start is not supposed to fail because after port is restarted, 664282263d52Syt * the whole command list is empty. 664382263d52Syt */ 664482263d52Syt ahci_portp->ahciport_err_retri_pkt = rdlog_spkt; 664582263d52Syt (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, port, rdlog_spkt); 664682263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 664782263d52Syt 664882263d52Syt /* Remove the flag after READ LOG EXT command is completed */ 664982263d52Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RDLOGEXT; 665082263d52Syt 665182263d52Syt if (rdlog_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 665282263d52Syt /* Update the request log data */ 665382263d52Syt buf_dma_handle = *(ddi_dma_handle_t *) 665482263d52Syt (rdlog_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 665582263d52Syt rval = ddi_dma_sync(buf_dma_handle, 0, 0, 665682263d52Syt DDI_DMA_SYNC_FORKERNEL); 665782263d52Syt if (rval == DDI_SUCCESS) { 665882263d52Syt ncq_err_page = 665982263d52Syt (struct sata_ncq_error_recovery_page *)rdlog_spkt-> 666082263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr; 666182263d52Syt 666282263d52Syt /* Get the failed tag */ 666382263d52Syt failed_slot = ncq_err_page->ncq_tag; 6664f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 666582263d52Syt "ahci_get_rdlogext_data: port %d " 666682263d52Syt "failed slot %d", port, failed_slot); 666782263d52Syt if (failed_slot & NQ) { 6668f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 6669f5f2d263SFred Herard "the failed slot is not a valid tag", NULL); 667082263d52Syt goto out; 667182263d52Syt } 667282263d52Syt 667382263d52Syt failed_slot &= NCQ_TAG_MASK; 667482263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 6675f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 667682263d52Syt "ahci_get_rdlogext_data: failed spkt 0x%p", 667782263d52Syt (void *)spkt); 667882263d52Syt if (spkt == NULL) { 6679f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 6680f5f2d263SFred Herard "the failed slot spkt is NULL", NULL); 668182263d52Syt goto out; 668282263d52Syt } 668382263d52Syt 668482263d52Syt failed_tags = 0x1 << failed_slot; 668582263d52Syt 668682263d52Syt /* Fill out the error context */ 668782263d52Syt ahci_copy_ncq_err_page(&spkt->satapkt_cmd, 668882263d52Syt ncq_err_page); 668982263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 669082263d52Syt &spkt->satapkt_device); 669182263d52Syt } 669282263d52Syt } 669382263d52Syt out: 669482263d52Syt sata_free_error_retrieval_pkt(rdlog_spkt); 669582263d52Syt 669682263d52Syt return (failed_tags); 669782263d52Syt } 669882263d52Syt 669968d33a25Syt /* 670068d33a25Syt * This routine is going to first request a REQUEST SENSE sata pkt from sata 670168d33a25Syt * module, and then deliver it to the HBA to get the sense data and copy 670268d33a25Syt * the sense data back to the orignal failed sata pkt, and free the REQUEST 670368d33a25Syt * SENSE sata pkt later. 670468d33a25Syt */ 670568d33a25Syt static void 670668d33a25Syt ahci_get_rqsense_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 670768d33a25Syt uint8_t port, sata_pkt_t *spkt) 670868d33a25Syt { 670968d33a25Syt sata_device_t sdevice; 671068d33a25Syt sata_pkt_t *rs_spkt; 671168d33a25Syt sata_cmd_t *sata_cmd; 671268d33a25Syt ddi_dma_handle_t buf_dma_handle; 671368d33a25Syt int loop_count; 671468d33a25Syt #if AHCI_DEBUG 671568d33a25Syt struct scsi_extended_sense *rqsense; 671668d33a25Syt #endif 671768d33a25Syt 6718f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 671968d33a25Syt "ahci_get_rqsense_data enter: port %d", port); 672068d33a25Syt 672168d33a25Syt /* Prepare the sdevice data */ 672268d33a25Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 672368d33a25Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 672468d33a25Syt 672568d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 672668d33a25Syt sdevice.satadev_addr.pmport = 0; 672768d33a25Syt 672868d33a25Syt sata_cmd = &spkt->satapkt_cmd; 672968d33a25Syt 673068d33a25Syt /* 673168d33a25Syt * Call the sata hba interface to get a rs spkt 673268d33a25Syt */ 673368d33a25Syt loop_count = 0; 673468d33a25Syt loop: 673568d33a25Syt rs_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 673668d33a25Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_ATAPI); 673768d33a25Syt if (rs_spkt == NULL) { 673868d33a25Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 673968d33a25Syt /* Sleep for a while */ 674068d33a25Syt delay(AHCI_10MS_TICKS); 674168d33a25Syt goto loop; 674268d33a25Syt 674368d33a25Syt } 674468d33a25Syt /* Timed out after 1s */ 6745f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 674668d33a25Syt "failed to get rs spkt for port %d", port); 674768d33a25Syt return; 674868d33a25Syt } 674968d33a25Syt 675068d33a25Syt ASSERT(rs_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 675168d33a25Syt 675268d33a25Syt /* 675368d33a25Syt * This flag is used to handle the specific error recovery when the 675482263d52Syt * REQUEST SENSE command gets a faiure (fatal error or time-out). 675568d33a25Syt */ 675668d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RQSENSE; 675768d33a25Syt 675868d33a25Syt /* 675968d33a25Syt * This start is not supposed to fail because after port is restarted, 676082263d52Syt * the whole command list is empty. 676168d33a25Syt */ 676282263d52Syt ahci_portp->ahciport_err_retri_pkt = rs_spkt; 676382263d52Syt (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, port, rs_spkt); 676482263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 676568d33a25Syt 676668d33a25Syt /* Remove the flag after REQUEST SENSE command is completed */ 676768d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RQSENSE; 676868d33a25Syt 676968d33a25Syt if (rs_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 677068d33a25Syt /* Update the request sense data */ 677168d33a25Syt buf_dma_handle = *(ddi_dma_handle_t *) 677268d33a25Syt (rs_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 677382263d52Syt (void) ddi_dma_sync(buf_dma_handle, 0, 0, 677468d33a25Syt DDI_DMA_SYNC_FORKERNEL); 677582263d52Syt /* Copy the request sense data */ 677682263d52Syt bcopy(rs_spkt-> 677782263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr, 677882263d52Syt &sata_cmd->satacmd_rqsense, 677982263d52Syt SATA_ATAPI_MIN_RQSENSE_LEN); 678068d33a25Syt #if AHCI_DEBUG 678182263d52Syt rqsense = (struct scsi_extended_sense *) 678282263d52Syt sata_cmd->satacmd_rqsense; 678382263d52Syt 678482263d52Syt /* Dump the sense data */ 6785f5f2d263SFred Herard AHCIDBG(AHCIDBG_SENSEDATA, ahci_ctlp, "\n", NULL); 6786f5f2d263SFred Herard AHCIDBG(AHCIDBG_SENSEDATA, ahci_ctlp, 678782263d52Syt "Sense data for satapkt %p ATAPI cmd 0x%x", 678882263d52Syt spkt, sata_cmd->satacmd_acdb[0]); 6789f5f2d263SFred Herard AHCIDBG(AHCIDBG_SENSEDATA, ahci_ctlp, 679082263d52Syt " es_code 0x%x es_class 0x%x " 679182263d52Syt "es_key 0x%x es_add_code 0x%x " 679282263d52Syt "es_qual_code 0x%x", 679382263d52Syt rqsense->es_code, rqsense->es_class, 679482263d52Syt rqsense->es_key, rqsense->es_add_code, 679582263d52Syt rqsense->es_qual_code); 679668d33a25Syt #endif 679768d33a25Syt } 679868d33a25Syt 679968d33a25Syt sata_free_error_retrieval_pkt(rs_spkt); 68002fcbc377Syt } 68012fcbc377Syt 68022fcbc377Syt /* 68032fcbc377Syt * Fatal errors will cause the HBA to enter the ERR: Fatal state. To recover, 680468d33a25Syt * the port must be restarted. When the HBA detects thus error, it may try 680568d33a25Syt * to abort a transfer. And if the transfer was aborted, the device is 680668d33a25Syt * expected to send a D2H Register FIS with PxTFD.STS.ERR set to '1' and both 680768d33a25Syt * PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0'. Then system software knows 680868d33a25Syt * that the device is in a stable status and transfers may be restarted without 680968d33a25Syt * issuing a COMRESET to the device. If PxTFD.STS.BSY or PxTFD.STS.DRQ is set, 681068d33a25Syt * then the software will send the COMRESET to do the port reset. 681168d33a25Syt * 681268d33a25Syt * Software should perform the appropriate error recovery actions based on 681368d33a25Syt * whether non-queued commands were being issued or natived command queuing 681468d33a25Syt * commands were being issued. 681568d33a25Syt * 681668d33a25Syt * And software will complete the command that had the error with error mark 681768d33a25Syt * to higher level software. 68182fcbc377Syt * 68192fcbc377Syt * Fatal errors include the following: 682038547057Sying tian - Beijing China * PxIS.IFS - Interface Fatal Error Status 682138547057Sying tian - Beijing China * PxIS.HBDS - Host Bus Data Error Status 682238547057Sying tian - Beijing China * PxIS.HBFS - Host Bus Fatal Error Status 68232fcbc377Syt * PxIS.TFES - Task File Error Status 68242fcbc377Syt * 68252fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 68262fcbc377Syt */ 682768d33a25Syt static void 682868d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctl_t *ahci_ctlp, 682982263d52Syt ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 68302fcbc377Syt { 683182263d52Syt uint32_t port_cmd_status; 683282263d52Syt uint32_t slot_status = 0; 683368d33a25Syt uint32_t failed_tags = 0; 683468d33a25Syt int failed_slot; 683568d33a25Syt int reset_flag = 0; 683668d33a25Syt ahci_fis_d2h_register_t *ahci_rcvd_fisp; 683782263d52Syt sata_cmd_t *sata_cmd = NULL; 683882263d52Syt sata_pkt_t *spkt = NULL; 683938547057Sying tian - Beijing China #if AHCI_DEBUG 684038547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 684138547057Sying tian - Beijing China #endif 68422fcbc377Syt 6843f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 684468d33a25Syt "ahci_fatal_error_recovery_handler enter: port %d", port); 68452fcbc377Syt 684682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 684782263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 68482fcbc377Syt 684982263d52Syt /* Read PxCI to see which commands are still outstanding */ 685082263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 685182263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 68522fcbc377Syt 685382263d52Syt /* 685482263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 685582263d52Syt * was processing when the error occurred. 685682263d52Syt */ 685782263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 685882263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 685982263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 686082263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 68612fcbc377Syt 686282263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 686382263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 686482263d52Syt ASSERT(spkt != NULL); 686582263d52Syt } else { 686682263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 686782263d52Syt if (spkt == NULL) { 686882263d52Syt /* May happen when interface errors occur? */ 686982263d52Syt goto next; 687082263d52Syt } 687182263d52Syt } 68722fcbc377Syt 687338547057Sying tian - Beijing China #if AHCI_DEBUG 687438547057Sying tian - Beijing China /* 687538547057Sying tian - Beijing China * Debugging purpose... 687638547057Sying tian - Beijing China */ 687738547057Sying tian - Beijing China if (ahci_portp->ahciport_prd_bytecounts[failed_slot]) { 687838547057Sying tian - Beijing China cmd_header = 687938547057Sying tian - Beijing China &ahci_portp->ahciport_cmd_list[failed_slot]; 6880f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 688138547057Sying tian - Beijing China "ahci_fatal_error_recovery_handler: port %d, " 688238547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 688338547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 688438547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 688538547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[failed_slot]); 688638547057Sying tian - Beijing China } 688738547057Sying tian - Beijing China #endif 688838547057Sying tian - Beijing China 688982263d52Syt sata_cmd = &spkt->satapkt_cmd; 68902fcbc377Syt 689182263d52Syt /* Fill out the status and error registers for PxIS.TFES */ 689282263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 689382263d52Syt ahci_rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 689482263d52Syt ahcirf_d2h_register_fis); 689582263d52Syt 689682263d52Syt /* Copy the error context back to the sata_cmd */ 689782263d52Syt ahci_copy_err_cnxt(sata_cmd, ahci_rcvd_fisp); 689882263d52Syt } 68992fcbc377Syt 690082263d52Syt /* The failed command must be one of the outstanding commands */ 690182263d52Syt failed_tags = 0x1 << failed_slot; 690282263d52Syt ASSERT(failed_tags & slot_status); 690382263d52Syt 690482263d52Syt /* Update the sata registers, especially PxSERR register */ 690582263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 690682263d52Syt &spkt->satapkt_device); 69072fcbc377Syt 6908a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 690982263d52Syt /* Read PxSACT to see which commands are still outstanding */ 691082263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 691182263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 691282263d52Syt } 691368d33a25Syt next: 69142fcbc377Syt 691568d33a25Syt #if AHCI_DEBUG 691668d33a25Syt /* 691782263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 691882263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 691982263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 692082263d52Syt * recovery process. At this time, the only outstanding command is 692182263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 692268d33a25Syt */ 692382263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 6924f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 692568d33a25Syt "ahci_fatal_error_recovery_handler: port %d REQUEST SENSE " 692682263d52Syt "command or READ LOG EXT command for error data retrieval " 692782263d52Syt "failed", port); 692882263d52Syt ASSERT(slot_status == 0x1); 6929f8a673adSying tian - Beijing China ASSERT(failed_slot == 0); 693082263d52Syt ASSERT(spkt->satapkt_cmd.satacmd_acdb[0] == 693182263d52Syt SCMD_REQUEST_SENSE || 693282263d52Syt spkt->satapkt_cmd.satacmd_cmd_reg == 693382263d52Syt SATAC_READ_LOG_EXT); 69342fcbc377Syt } 693568d33a25Syt #endif 69362fcbc377Syt 693768d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 693868d33a25Syt ahci_portp->ahciport_mop_in_progress++; 69392fcbc377Syt 694068d33a25Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 694168d33a25Syt port, NULL, &reset_flag); 69422fcbc377Syt 694368d33a25Syt /* 694468d33a25Syt * Won't retrieve error information: 694568d33a25Syt * 1. Port reset was involved to recover 694682263d52Syt * 2. Device is gone 694768d33a25Syt * 3. IDENTIFY DEVICE command sent to ATAPI device 694882263d52Syt * 4. REQUEST SENSE or READ LOG EXT command during error recovery 694968d33a25Syt */ 695082263d52Syt if (reset_flag || 695182263d52Syt ahci_portp->ahciport_device_type == SATA_DTYPE_NONE || 695282263d52Syt spkt && spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_ID_DEVICE || 695382263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 695468d33a25Syt goto out; 69552fcbc377Syt 695682263d52Syt /* 695782263d52Syt * Deliver READ LOG EXT to gather information about the error when 695882263d52Syt * a COMRESET has not been performed as part of the error recovery 695982263d52Syt * during NCQ command processing. 696082263d52Syt */ 696182263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 696282263d52Syt failed_tags = ahci_get_rdlogext_data(ahci_ctlp, 696382263d52Syt ahci_portp, port); 696482263d52Syt goto out; 696582263d52Syt } 696682263d52Syt 696768d33a25Syt /* 696868d33a25Syt * Deliver REQUEST SENSE for ATAPI command to gather information about 696968d33a25Syt * the error when a COMRESET has not been performed as part of the 697068d33a25Syt * error recovery. 697168d33a25Syt */ 697238547057Sying tian - Beijing China if (spkt && ahci_portp->ahciport_device_type == SATA_DTYPE_ATAPI) 697368d33a25Syt ahci_get_rqsense_data(ahci_ctlp, ahci_portp, port, spkt); 697468d33a25Syt out: 6975f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 697638547057Sying tian - Beijing China "ahci_fatal_error_recovery_handler: port %d fatal error " 697782263d52Syt "occurred slot_status = 0x%x, pending_tags = 0x%x, " 697882263d52Syt "pending_ncq_tags = 0x%x failed_tags = 0x%x", 697982263d52Syt port, slot_status, ahci_portp->ahciport_pending_tags, 698082263d52Syt ahci_portp->ahciport_pending_ncq_tags, failed_tags); 698182263d52Syt 69822fcbc377Syt ahci_mop_commands(ahci_ctlp, 69832fcbc377Syt ahci_portp, 698482263d52Syt slot_status, 69852fcbc377Syt failed_tags, /* failed tags */ 69862fcbc377Syt 0, /* timeout tags */ 69872fcbc377Syt 0, /* aborted tags */ 69882fcbc377Syt 0); /* reset tags */ 698968d33a25Syt } 699068d33a25Syt 699168d33a25Syt /* 699268d33a25Syt * Handle events - fatal error recovery 699368d33a25Syt */ 699468d33a25Syt static void 699568d33a25Syt ahci_events_handler(void *args) 699668d33a25Syt { 699768d33a25Syt ahci_event_arg_t *ahci_event_arg; 699868d33a25Syt ahci_ctl_t *ahci_ctlp; 699968d33a25Syt ahci_port_t *ahci_portp; 700068d33a25Syt uint32_t event; 700168d33a25Syt uint8_t port; 700268d33a25Syt 700368d33a25Syt ahci_event_arg = (ahci_event_arg_t *)args; 700482263d52Syt 700568d33a25Syt ahci_ctlp = ahci_event_arg->ahciea_ctlp; 700668d33a25Syt ahci_portp = ahci_event_arg->ahciea_portp; 700768d33a25Syt event = ahci_event_arg->ahciea_event; 700868d33a25Syt port = ahci_portp->ahciport_port_num; 700968d33a25Syt 7010f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 701182263d52Syt "ahci_events_handler enter: port %d intr_status = 0x%x", 701282263d52Syt port, event); 701368d33a25Syt 70142fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 70152fcbc377Syt 701668d33a25Syt /* 701768d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 701868d33a25Syt * SATA_DTYPE_NONE. 701968d33a25Syt */ 702068d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 7021f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 702268d33a25Syt "ahci_events_handler: port %d no device attached, " 702368d33a25Syt "and just return without doing anything", port); 702468d33a25Syt goto out; 702568d33a25Syt } 702668d33a25Syt 702768d33a25Syt if (event & (AHCI_INTR_STATUS_IFS | 702868d33a25Syt AHCI_INTR_STATUS_HBDS | 702968d33a25Syt AHCI_INTR_STATUS_HBFS | 703068d33a25Syt AHCI_INTR_STATUS_TFES)) 703168d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctlp, ahci_portp, 703282263d52Syt port, event); 703368d33a25Syt 703468d33a25Syt out: 703568d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 70362fcbc377Syt } 70372fcbc377Syt 70382fcbc377Syt /* 703968d33a25Syt * ahci_watchdog_handler() and ahci_do_sync_start will call us if they 704068d33a25Syt * detect there are some commands which are timed out. 70412fcbc377Syt */ 70422fcbc377Syt static void 70432fcbc377Syt ahci_timeout_pkts(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 704482263d52Syt uint8_t port, uint32_t tmp_timeout_tags) 70452fcbc377Syt { 704682263d52Syt uint32_t slot_status = 0; 704782263d52Syt uint32_t finished_tags = 0; 704882263d52Syt uint32_t timeout_tags = 0; 70492fcbc377Syt 7050f5f2d263SFred Herard AHCIDBG(AHCIDBG_TIMEOUT|AHCIDBG_ENTRY, ahci_ctlp, 705168d33a25Syt "ahci_timeout_pkts enter: port %d", port); 70522fcbc377Syt 70532fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 70542fcbc377Syt 705582263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 705682263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 705782263d52Syt /* Read PxCI to see which commands are still outstanding */ 705882263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 705982263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 7060a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 706182263d52Syt /* Read PxSACT to see which commands are still outstanding */ 706282263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 706382263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 706482263d52Syt } 706568d33a25Syt 706668d33a25Syt #if AHCI_DEBUG 70672fcbc377Syt /* 706882263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 706982263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 707082263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 707182263d52Syt * recovery process. At this time, the only outstanding command is 707282263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 70732fcbc377Syt */ 707482263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 7075f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, ahci_ctlp, 707668d33a25Syt "ahci_timeout_pkts called while REQUEST SENSE " 707782263d52Syt "command or READ LOG EXT command for error recovery " 707882263d52Syt "timed out timeout_tags = 0x%x, slot_status = 0x%x, " 707982263d52Syt "pending_tags = 0x%x, pending_ncq_tags = 0x%x", 708082263d52Syt tmp_timeout_tags, slot_status, 708182263d52Syt ahci_portp->ahciport_pending_tags, 708282263d52Syt ahci_portp->ahciport_pending_ncq_tags); 708382263d52Syt ASSERT(slot_status == 0x1); 70842fcbc377Syt } 708568d33a25Syt #endif 70862fcbc377Syt 708768d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 708868d33a25Syt ahci_portp->ahciport_mop_in_progress++; 70892fcbc377Syt 70902fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 709168d33a25Syt port, NULL, NULL); 709268d33a25Syt 70932fcbc377Syt /* 70942fcbc377Syt * Re-identify timeout tags because some previously checked commands 70952fcbc377Syt * could already complete. 70962fcbc377Syt */ 709782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 709882263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 709982263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 710082263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 710182263d52Syt 7102f5f2d263SFred Herard AHCIDBG(AHCIDBG_TIMEOUT, ahci_ctlp, 710382263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 710482263d52Syt "timeout_tags = 0x%x, port_cmd_issue = 0x%x, " 710582263d52Syt "pending_tags = 0x%x ", 710682263d52Syt port, finished_tags, timeout_tags, 710782263d52Syt slot_status, ahci_portp->ahciport_pending_tags); 7108a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 710982263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 711082263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 711182263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 711282263d52Syt 7113f5f2d263SFred Herard AHCIDBG(AHCIDBG_TIMEOUT|AHCIDBG_NCQ, ahci_ctlp, 711482263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 711582263d52Syt "timeout_tags = 0x%x, port_sactive = 0x%x, " 711682263d52Syt "pending_ncq_tags = 0x%x ", 711782263d52Syt port, finished_tags, timeout_tags, 711882263d52Syt slot_status, ahci_portp->ahciport_pending_ncq_tags); 7119a9440e8dSyt } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 712082263d52Syt timeout_tags = tmp_timeout_tags; 712182263d52Syt } 71222fcbc377Syt 71232fcbc377Syt ahci_mop_commands(ahci_ctlp, 71242fcbc377Syt ahci_portp, 712582263d52Syt slot_status, 71262fcbc377Syt 0, /* failed tags */ 71272fcbc377Syt timeout_tags, /* timeout tags */ 71282fcbc377Syt 0, /* aborted tags */ 71292fcbc377Syt 0); /* reset tags */ 713068d33a25Syt 713168d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 71322fcbc377Syt } 71332fcbc377Syt 71342fcbc377Syt /* 71352fcbc377Syt * Watchdog handler kicks in every 5 seconds to timeout any commands pending 71362fcbc377Syt * for long time. 71372fcbc377Syt */ 71382fcbc377Syt static void 71392fcbc377Syt ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp) 71402fcbc377Syt { 71412fcbc377Syt ahci_port_t *ahci_portp; 714268d33a25Syt sata_pkt_t *spkt; 714382263d52Syt uint32_t pending_tags; 714482263d52Syt uint32_t timeout_tags; 714568d33a25Syt uint32_t port_cmd_status; 714682263d52Syt uint32_t port_sactive; 71472fcbc377Syt uint8_t port; 71482fcbc377Syt int tmp_slot; 714968d33a25Syt int current_slot; 715082263d52Syt uint32_t current_tags; 7151a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 71522fcbc377Syt /* max number of cycles this packet should survive */ 71532fcbc377Syt int max_life_cycles; 71542fcbc377Syt 71552fcbc377Syt /* how many cycles this packet survived so far */ 71562fcbc377Syt int watched_cycles; 71572fcbc377Syt 71582fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 71592fcbc377Syt 7160f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 7161f5f2d263SFred Herard "ahci_watchdog_handler entered", NULL); 71622fcbc377Syt 71632fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 71642fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 71652fcbc377Syt continue; 71662fcbc377Syt } 71672fcbc377Syt 71682fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 71692fcbc377Syt 71702fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 71712fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 71722fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 71732fcbc377Syt continue; 71742fcbc377Syt } 71752fcbc377Syt 717668d33a25Syt /* Skip the check for those ports in error recovery */ 717782263d52Syt if ((ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) && 717882263d52Syt !(ERR_RETRI_CMD_IN_PROGRESS(ahci_portp))) { 717968d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 718068d33a25Syt continue; 718168d33a25Syt } 718268d33a25Syt 718382263d52Syt pending_tags = 0; 718468d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 718568d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 718668d33a25Syt 718782263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 718882263d52Syt current_slot = 0; 718982263d52Syt pending_tags = 0x1; 7190a9440e8dSyt } else if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 719182263d52Syt current_slot = 719282263d52Syt (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 719382263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 719482263d52Syt pending_tags = ahci_portp->ahciport_pending_tags; 7195a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 719682263d52Syt port_sactive = ddi_get32( 719782263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 719882263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 719982263d52Syt current_tags = port_sactive & 720082263d52Syt ~port_cmd_status & 720182263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 720282263d52Syt pending_tags = ahci_portp->ahciport_pending_ncq_tags; 720382263d52Syt } 720482263d52Syt 72052fcbc377Syt timeout_tags = 0; 72062fcbc377Syt while (pending_tags) { 72072fcbc377Syt tmp_slot = ddi_ffs(pending_tags) - 1; 72082fcbc377Syt if (tmp_slot == -1) { 72092fcbc377Syt break; 72102fcbc377Syt } 72112fcbc377Syt 721282263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 721382263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 721482263d52Syt else 721582263d52Syt spkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 721682263d52Syt 721768d33a25Syt if ((spkt != NULL) && spkt->satapkt_time && 721868d33a25Syt !(spkt->satapkt_op_mode & SATA_OPMODE_POLLING)) { 72192fcbc377Syt /* 72202fcbc377Syt * We are overloading satapkt_hba_driver_private 72212fcbc377Syt * with watched_cycle count. 72222fcbc377Syt * 72232fcbc377Syt * If a packet has survived for more than it's 72242fcbc377Syt * max life cycles, it is a candidate for time 72252fcbc377Syt * out. 72262fcbc377Syt */ 72272fcbc377Syt watched_cycles = (int)(intptr_t) 722868d33a25Syt spkt->satapkt_hba_driver_private; 72292fcbc377Syt watched_cycles++; 723068d33a25Syt max_life_cycles = (spkt->satapkt_time + 72312fcbc377Syt ahci_watchdog_timeout - 1) / 72322fcbc377Syt ahci_watchdog_timeout; 723368d33a25Syt 723468d33a25Syt spkt->satapkt_hba_driver_private = 723568d33a25Syt (void *)(intptr_t)watched_cycles; 723668d33a25Syt 723768d33a25Syt if (watched_cycles <= max_life_cycles) 723868d33a25Syt goto next; 723968d33a25Syt 7240259105bcSying tian - Beijing China #if AHCI_DEBUG 7241259105bcSying tian - Beijing China if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 7242f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, 7243259105bcSying tian - Beijing China ahci_ctlp, "watchdog: the current " 7244259105bcSying tian - Beijing China "tags is 0x%x", current_tags); 7245259105bcSying tian - Beijing China } else { 7246f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, 7247259105bcSying tian - Beijing China ahci_ctlp, "watchdog: the current " 7248259105bcSying tian - Beijing China "slot is %d", current_slot); 7249259105bcSying tian - Beijing China } 7250259105bcSying tian - Beijing China #endif 7251259105bcSying tian - Beijing China 725268d33a25Syt /* 725368d33a25Syt * We need to check whether the HBA has 725468d33a25Syt * begun to execute the command, if not, 725568d33a25Syt * then re-set the timer of the command. 725668d33a25Syt */ 725782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) && 725882263d52Syt (tmp_slot != current_slot) || 725982263d52Syt NCQ_CMD_IN_PROGRESS(ahci_portp) && 726082263d52Syt ((0x1 << tmp_slot) & current_tags)) { 726168d33a25Syt spkt->satapkt_hba_driver_private = 726268d33a25Syt (void *)(intptr_t)0; 726368d33a25Syt } else { 72642fcbc377Syt timeout_tags |= (0x1 << tmp_slot); 7265a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: watchdog " 72662fcbc377Syt "port %d satapkt 0x%p timed out\n", 7267a9440e8dSyt instance, port, (void *)spkt); 72682fcbc377Syt } 72692fcbc377Syt } 727068d33a25Syt next: 72712fcbc377Syt CLEAR_BIT(pending_tags, tmp_slot); 72722fcbc377Syt } 72732fcbc377Syt 72742fcbc377Syt if (timeout_tags) { 72752fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 72762fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 72772fcbc377Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 727882263d52Syt port, timeout_tags); 72792fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 72802fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 72812fcbc377Syt } 72822fcbc377Syt 72832fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 72842fcbc377Syt } 72852fcbc377Syt 72862fcbc377Syt /* Re-install the watchdog timeout handler */ 72872fcbc377Syt if (ahci_ctlp->ahcictl_timeout_id != 0) { 72882fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 72892fcbc377Syt timeout((void (*)(void *))ahci_watchdog_handler, 72902fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 72912fcbc377Syt } 72922fcbc377Syt 72932fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 72942fcbc377Syt } 72952fcbc377Syt 72962fcbc377Syt /* 729768d33a25Syt * Fill the error context into sata_cmd for non-queued command error. 729868d33a25Syt */ 729968d33a25Syt static void 730068d33a25Syt ahci_copy_err_cnxt(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 730168d33a25Syt { 730268d33a25Syt scmd->satacmd_status_reg = GET_RFIS_STATUS(rfisp); 730368d33a25Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 730468d33a25Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 730568d33a25Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 730668d33a25Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 730768d33a25Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 730868d33a25Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 730968d33a25Syt 731068d33a25Syt if (scmd->satacmd_addr_type == ATA_ADDR_LBA48) { 731168d33a25Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 731268d33a25Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 731368d33a25Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 731468d33a25Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 731568d33a25Syt } 731668d33a25Syt } 731768d33a25Syt 731882263d52Syt /* 731982263d52Syt * Fill the ncq error page into sata_cmd for queued command error. 732082263d52Syt */ 732182263d52Syt static void 732282263d52Syt ahci_copy_ncq_err_page(sata_cmd_t *scmd, 732382263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page) 732482263d52Syt { 732582263d52Syt scmd->satacmd_sec_count_msb = ncq_err_page->ncq_sector_count_ext; 732682263d52Syt scmd->satacmd_sec_count_lsb = ncq_err_page->ncq_sector_count; 732782263d52Syt scmd->satacmd_lba_low_msb = ncq_err_page->ncq_sector_number_ext; 732882263d52Syt scmd->satacmd_lba_low_lsb = ncq_err_page->ncq_sector_number; 732982263d52Syt scmd->satacmd_lba_mid_msb = ncq_err_page->ncq_cyl_low_ext; 733082263d52Syt scmd->satacmd_lba_mid_lsb = ncq_err_page->ncq_cyl_low; 733182263d52Syt scmd->satacmd_lba_high_msb = ncq_err_page->ncq_cyl_high_ext; 733282263d52Syt scmd->satacmd_lba_high_lsb = ncq_err_page->ncq_cyl_high; 733382263d52Syt scmd->satacmd_device_reg = ncq_err_page->ncq_dev_head; 733482263d52Syt scmd->satacmd_status_reg = ncq_err_page->ncq_status; 733582263d52Syt scmd->satacmd_error_reg = ncq_err_page->ncq_error; 733682263d52Syt } 733782263d52Syt 733868d33a25Syt /* 733968d33a25Syt * Put the respective register value to sata_cmd_t for satacmd_flags. 73402fcbc377Syt */ 73412fcbc377Syt static void 73422fcbc377Syt ahci_copy_out_regs(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 73432fcbc377Syt { 73442fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_msb) 73452fcbc377Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 73462fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_msb) 73472fcbc377Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 73482fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_msb) 73492fcbc377Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 73502fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_msb) 73512fcbc377Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 73522fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_lsb) 73532fcbc377Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 73542fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_lsb) 73552fcbc377Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 73562fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_lsb) 73572fcbc377Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 73582fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_lsb) 73592fcbc377Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 73602fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_device_reg) 73612fcbc377Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 73622fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_error_reg) 73632fcbc377Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 73642fcbc377Syt } 73652fcbc377Syt 736668d33a25Syt static void 736768d33a25Syt ahci_log_fatal_error_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 736868d33a25Syt uint32_t intr_status) 736968d33a25Syt { 7370a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 737168d33a25Syt 737268d33a25Syt if (intr_status & AHCI_INTR_STATUS_IFS) 73737095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has interface fatal " 7374a9440e8dSyt "error", instance, port); 737568d33a25Syt 737668d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBDS) 73777095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has bus data error", 7378a9440e8dSyt instance, port); 737968d33a25Syt 738068d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBFS) 73817095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has bus fatal error", 7382a9440e8dSyt instance, port); 738368d33a25Syt 738468d33a25Syt if (intr_status & AHCI_INTR_STATUS_TFES) 73857095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has task file error", 7386a9440e8dSyt instance, port); 738768d33a25Syt 73887095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d is trying to do error " 7389a9440e8dSyt "recovery", instance, port); 739068d33a25Syt } 739168d33a25Syt 73922fcbc377Syt /* 73932fcbc377Syt * Dump the serror message to the log. 73942fcbc377Syt */ 73952fcbc377Syt static void 739668d33a25Syt ahci_log_serror_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 7397a9440e8dSyt uint32_t port_serror, int debug_only) 73982fcbc377Syt { 7399a9440e8dSyt static char err_buf[512]; 7400a9440e8dSyt static char err_msg_header[16]; 7401a9440e8dSyt char *err_msg = err_buf; 7402a9440e8dSyt 7403a9440e8dSyt *err_buf = '\0'; 7404a9440e8dSyt *err_msg_header = '\0'; 74052fcbc377Syt 740682263d52Syt if (port_serror & SERROR_DATA_ERR_FIXED) { 7407a9440e8dSyt err_msg = strcat(err_msg, 7408a9440e8dSyt "\tRecovered Data Integrity Error (I)\n"); 74092fcbc377Syt } 74102fcbc377Syt 741182263d52Syt if (port_serror & SERROR_COMM_ERR_FIXED) { 7412a9440e8dSyt err_msg = strcat(err_msg, 7413a9440e8dSyt "\tRecovered Communication Error (M)\n"); 74142fcbc377Syt } 74152fcbc377Syt 741682263d52Syt if (port_serror & SERROR_DATA_ERR) { 7417a9440e8dSyt err_msg = strcat(err_msg, 7418a9440e8dSyt "\tTransient Data Integrity Error (T)\n"); 74192fcbc377Syt } 74202fcbc377Syt 742182263d52Syt if (port_serror & SERROR_PERSISTENT_ERR) { 7422a9440e8dSyt err_msg = strcat(err_msg, 7423a9440e8dSyt "\tPersistent Communication or Data Integrity Error (C)\n"); 74242fcbc377Syt } 74252fcbc377Syt 742682263d52Syt if (port_serror & SERROR_PROTOCOL_ERR) { 7427a9440e8dSyt err_msg = strcat(err_msg, "\tProtocol Error (P)\n"); 74282fcbc377Syt } 74292fcbc377Syt 743082263d52Syt if (port_serror & SERROR_INT_ERR) { 7431a9440e8dSyt err_msg = strcat(err_msg, "\tInternal Error (E)\n"); 74322fcbc377Syt } 74332fcbc377Syt 743482263d52Syt if (port_serror & SERROR_PHY_RDY_CHG) { 7435a9440e8dSyt err_msg = strcat(err_msg, "\tPhyRdy Change (N)\n"); 74362fcbc377Syt } 74372fcbc377Syt 743882263d52Syt if (port_serror & SERROR_PHY_INT_ERR) { 7439a9440e8dSyt err_msg = strcat(err_msg, "\tPhy Internal Error (I)\n"); 74402fcbc377Syt } 74412fcbc377Syt 744282263d52Syt if (port_serror & SERROR_COMM_WAKE) { 7443a9440e8dSyt err_msg = strcat(err_msg, "\tComm Wake (W)\n"); 74442fcbc377Syt } 74452fcbc377Syt 744682263d52Syt if (port_serror & SERROR_10B_TO_8B_ERR) { 7447a9440e8dSyt err_msg = strcat(err_msg, "\t10B to 8B Decode Error (B)\n"); 74482fcbc377Syt } 74492fcbc377Syt 745082263d52Syt if (port_serror & SERROR_DISPARITY_ERR) { 7451a9440e8dSyt err_msg = strcat(err_msg, "\tDisparity Error (D)\n"); 74522fcbc377Syt } 74532fcbc377Syt 745482263d52Syt if (port_serror & SERROR_CRC_ERR) { 7455a9440e8dSyt err_msg = strcat(err_msg, "\tCRC Error (C)\n"); 74562fcbc377Syt } 74572fcbc377Syt 745882263d52Syt if (port_serror & SERROR_HANDSHAKE_ERR) { 7459a9440e8dSyt err_msg = strcat(err_msg, "\tHandshake Error (H)\n"); 74602fcbc377Syt } 74612fcbc377Syt 746282263d52Syt if (port_serror & SERROR_LINK_SEQ_ERR) { 7463a9440e8dSyt err_msg = strcat(err_msg, "\tLink Sequence Error (S)\n"); 74642fcbc377Syt } 74652fcbc377Syt 746682263d52Syt if (port_serror & SERROR_TRANS_ERR) { 7467a9440e8dSyt err_msg = strcat(err_msg, 7468a9440e8dSyt "\tTransport state transition error (T)\n"); 74692fcbc377Syt } 74702fcbc377Syt 747182263d52Syt if (port_serror & SERROR_FIS_TYPE) { 7472a9440e8dSyt err_msg = strcat(err_msg, "\tUnknown FIS Type (F)\n"); 74732fcbc377Syt } 74742fcbc377Syt 747582263d52Syt if (port_serror & SERROR_EXCHANGED_ERR) { 7476a9440e8dSyt err_msg = strcat(err_msg, "\tExchanged (X)\n"); 7477a9440e8dSyt } 7478a9440e8dSyt 7479a9440e8dSyt if (err_msg == NULL) 7480a9440e8dSyt return; 7481a9440e8dSyt 7482a9440e8dSyt if (debug_only) { 7483a9440e8dSyt (void) sprintf(err_msg_header, "port %d", port); 7484f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, err_msg_header, NULL); 7485f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, err_msg, NULL); 7486a9440e8dSyt } else if (ahci_ctlp) { 74877095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: %s %s", 7488a9440e8dSyt ddi_get_instance(ahci_ctlp->ahcictl_dip), 7489a9440e8dSyt err_msg_header, err_msg); 7490f5f2d263SFred Herard 7491f5f2d263SFred Herard /* sata trace debug */ 7492f5f2d263SFred Herard sata_trace_debug(ahci_ctlp->ahcictl_dip, 7493f5f2d263SFred Herard "ahci%d: %s %s", ddi_get_instance(ahci_ctlp->ahcictl_dip), 7494f5f2d263SFred Herard err_msg_header, err_msg); 7495a9440e8dSyt } else { 74967095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci: %s %s", err_msg_header, err_msg); 7497f5f2d263SFred Herard 7498f5f2d263SFred Herard /* sata trace debug */ 7499f5f2d263SFred Herard sata_trace_debug(NULL, "ahci: %s %s", err_msg_header, err_msg); 75002fcbc377Syt } 75012fcbc377Syt } 75022fcbc377Syt 75032fcbc377Syt /* 75042fcbc377Syt * This routine is to calculate the total number of ports implemented 75052fcbc377Syt * by the HBA. 75062fcbc377Syt */ 75072fcbc377Syt static int 75082fcbc377Syt ahci_get_num_implemented_ports(uint32_t ports_implemented) 75092fcbc377Syt { 75102fcbc377Syt uint8_t i; 75112fcbc377Syt int num = 0; 75122fcbc377Syt 75132fcbc377Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 75142fcbc377Syt if (((uint32_t)0x1 << i) & ports_implemented) 75152fcbc377Syt num++; 75162fcbc377Syt } 75172fcbc377Syt 75182fcbc377Syt return (num); 75192fcbc377Syt } 75202fcbc377Syt 7521689d74b0Syt #if AHCI_DEBUG 75222fcbc377Syt static void 75232fcbc377Syt ahci_log(ahci_ctl_t *ahci_ctlp, uint_t level, char *fmt, ...) 75242fcbc377Syt { 7525a9440e8dSyt static char name[16]; 75262fcbc377Syt va_list ap; 75272fcbc377Syt 75282fcbc377Syt mutex_enter(&ahci_log_mutex); 75292fcbc377Syt 75302fcbc377Syt va_start(ap, fmt); 75312fcbc377Syt if (ahci_ctlp) { 7532a9440e8dSyt (void) sprintf(name, "ahci%d: ", 75332fcbc377Syt ddi_get_instance(ahci_ctlp->ahcictl_dip)); 75342fcbc377Syt } else { 7535a9440e8dSyt (void) sprintf(name, "ahci: "); 75362fcbc377Syt } 75372fcbc377Syt 75382fcbc377Syt (void) vsprintf(ahci_log_buf, fmt, ap); 75392fcbc377Syt va_end(ap); 75402fcbc377Syt 7541a9440e8dSyt cmn_err(level, "%s%s", name, ahci_log_buf); 75422fcbc377Syt 75432fcbc377Syt mutex_exit(&ahci_log_mutex); 75442fcbc377Syt } 7545689d74b0Syt #endif 754619397407SSherry Moore 754719397407SSherry Moore /* 754819397407SSherry Moore * quiesce(9E) entry point. 754919397407SSherry Moore * 755019397407SSherry Moore * This function is called when the system is single-threaded at high 755119397407SSherry Moore * PIL with preemption disabled. Therefore, this function must not be 755219397407SSherry Moore * blocked. 755319397407SSherry Moore * 755419397407SSherry Moore * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 755519397407SSherry Moore * DDI_FAILURE indicates an error condition and should almost never happen. 755619397407SSherry Moore */ 755719397407SSherry Moore static int 755819397407SSherry Moore ahci_quiesce(dev_info_t *dip) 755919397407SSherry Moore { 756019397407SSherry Moore ahci_ctl_t *ahci_ctlp; 756119397407SSherry Moore ahci_port_t *ahci_portp; 756219397407SSherry Moore int instance, port; 756319397407SSherry Moore 756419397407SSherry Moore instance = ddi_get_instance(dip); 756519397407SSherry Moore ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 756619397407SSherry Moore 756719397407SSherry Moore if (ahci_ctlp == NULL) 756819397407SSherry Moore return (DDI_FAILURE); 756919397407SSherry Moore 757019397407SSherry Moore #if AHCI_DEBUG 757119397407SSherry Moore ahci_debug_flags = 0; 757219397407SSherry Moore #endif 757319397407SSherry Moore 757419397407SSherry Moore /* disable all the interrupts. */ 757519397407SSherry Moore ahci_disable_all_intrs(ahci_ctlp); 757619397407SSherry Moore 757719397407SSherry Moore for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 757819397407SSherry Moore if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 757919397407SSherry Moore continue; 758019397407SSherry Moore } 758119397407SSherry Moore 758219397407SSherry Moore ahci_portp = ahci_ctlp->ahcictl_ports[port]; 758319397407SSherry Moore 758419397407SSherry Moore /* 758519397407SSherry Moore * Stop the port by clearing PxCMD.ST 758619397407SSherry Moore * 758719397407SSherry Moore * Here we must disable the port interrupt because 758819397407SSherry Moore * ahci_disable_all_intrs only clear GHC.IE, and IS 758919397407SSherry Moore * register will be still set if PxIE is enabled. 759019397407SSherry Moore * When ahci shares one IRQ with other drivers, the 759119397407SSherry Moore * intr handler may claim the intr mistakenly. 759219397407SSherry Moore */ 759319397407SSherry Moore ahci_disable_port_intrs(ahci_ctlp, port); 759419397407SSherry Moore (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 759519397407SSherry Moore ahci_portp, port); 759619397407SSherry Moore } 759719397407SSherry Moore 759819397407SSherry Moore return (DDI_SUCCESS); 759919397407SSherry Moore } 7600