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 /* 311*259105bcSying tian - Beijing China * The below global variables are tunable via /etc/system 312*259105bcSying tian - Beijing China * 313*259105bcSying tian - Beijing China * ahci_dma_prdt_number 314*259105bcSying tian - Beijing China * ahci_msi_enabled 315*259105bcSying tian - Beijing China * ahci_buf_64bit_dma 316*259105bcSying 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 325*259105bcSying tian - Beijing China /* 326*259105bcSying tian - Beijing China * 64-bit dma addressing for data buffer is tunable 327*259105bcSying tian - Beijing China * 328*259105bcSying tian - Beijing China * The variable controls only the below value: 329*259105bcSying tian - Beijing China * DBAU (upper 32-bits physical address of data block) 330*259105bcSying tian - Beijing China */ 331*259105bcSying tian - Beijing China boolean_t ahci_buf_64bit_dma = B_TRUE; 332*259105bcSying tian - Beijing China 333*259105bcSying tian - Beijing China /* 334*259105bcSying tian - Beijing China * 64-bit dma addressing for communication system descriptors is tunable 335*259105bcSying tian - Beijing China * 336*259105bcSying tian - Beijing China * The variable controls the below three values: 337*259105bcSying tian - Beijing China * 338*259105bcSying tian - Beijing China * PxCLBU (upper 32-bits for the command list base physical address) 339*259105bcSying tian - Beijing China * PxFBU (upper 32-bits for the received FIS base physical address) 340*259105bcSying tian - Beijing China * CTBAU (upper 32-bits of command table base) 341*259105bcSying tian - Beijing China */ 342*259105bcSying tian - Beijing China boolean_t ahci_commu_64bit_dma = B_TRUE; 343*259105bcSying tian - Beijing China 344*259105bcSying tian - Beijing China /* 345*259105bcSying tian - Beijing China * End of global tunable variable definition 346*259105bcSying tian - Beijing China */ 347db2cce03Sying tian - Beijing China 3482fcbc377Syt #if AHCI_DEBUG 3492fcbc377Syt uint32_t ahci_debug_flags = 0; 350689d74b0Syt 351689d74b0Syt /* The following is needed for ahci_log() */ 352689d74b0Syt static kmutex_t ahci_log_mutex; 353689d74b0Syt static char ahci_log_buf[512]; 3542fcbc377Syt #endif 3552fcbc377Syt 3562fcbc377Syt /* Opaque state pointer initialized by ddi_soft_state_init() */ 3572fcbc377Syt static void *ahci_statep = NULL; 3582fcbc377Syt 3592fcbc377Syt /* 3602fcbc377Syt * ahci module initialization. 3612fcbc377Syt */ 3622fcbc377Syt int 3632fcbc377Syt _init(void) 3642fcbc377Syt { 3652fcbc377Syt int ret; 3662fcbc377Syt 3672fcbc377Syt ret = ddi_soft_state_init(&ahci_statep, sizeof (ahci_ctl_t), 0); 3682fcbc377Syt if (ret != 0) { 3692fcbc377Syt goto err_out; 3702fcbc377Syt } 3712fcbc377Syt 372689d74b0Syt #if AHCI_DEBUG 3732fcbc377Syt mutex_init(&ahci_log_mutex, NULL, MUTEX_DRIVER, NULL); 374689d74b0Syt #endif 3752fcbc377Syt 3762fcbc377Syt if ((ret = sata_hba_init(&modlinkage)) != 0) { 377689d74b0Syt #if AHCI_DEBUG 3782fcbc377Syt mutex_destroy(&ahci_log_mutex); 379689d74b0Syt #endif 3802fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3812fcbc377Syt goto err_out; 3822fcbc377Syt } 3832fcbc377Syt 3842fcbc377Syt ret = mod_install(&modlinkage); 3852fcbc377Syt if (ret != 0) { 3862fcbc377Syt sata_hba_fini(&modlinkage); 387689d74b0Syt #if AHCI_DEBUG 3882fcbc377Syt mutex_destroy(&ahci_log_mutex); 389689d74b0Syt #endif 3902fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3912fcbc377Syt goto err_out; 3922fcbc377Syt } 3932fcbc377Syt 3942fcbc377Syt /* watchdog tick */ 3952fcbc377Syt ahci_watchdog_tick = drv_usectohz( 3962fcbc377Syt (clock_t)ahci_watchdog_timeout * 1000000); 3972fcbc377Syt return (ret); 3982fcbc377Syt 3992fcbc377Syt err_out: 400a9440e8dSyt cmn_err(CE_WARN, "!ahci: Module init failed"); 4012fcbc377Syt return (ret); 4022fcbc377Syt } 4032fcbc377Syt 4042fcbc377Syt /* 4052fcbc377Syt * ahci module uninitialize. 4062fcbc377Syt */ 4072fcbc377Syt int 4082fcbc377Syt _fini(void) 4092fcbc377Syt { 4102fcbc377Syt int ret; 4112fcbc377Syt 4122fcbc377Syt ret = mod_remove(&modlinkage); 4132fcbc377Syt if (ret != 0) { 4142fcbc377Syt return (ret); 4152fcbc377Syt } 4162fcbc377Syt 4172fcbc377Syt /* Remove the resources allocated in _init(). */ 4182fcbc377Syt sata_hba_fini(&modlinkage); 419689d74b0Syt #if AHCI_DEBUG 4202fcbc377Syt mutex_destroy(&ahci_log_mutex); 421689d74b0Syt #endif 4222fcbc377Syt ddi_soft_state_fini(&ahci_statep); 4232fcbc377Syt 4242fcbc377Syt return (ret); 4252fcbc377Syt } 4262fcbc377Syt 4272fcbc377Syt /* 4282fcbc377Syt * _info entry point 4292fcbc377Syt */ 4302fcbc377Syt int 4312fcbc377Syt _info(struct modinfo *modinfop) 4322fcbc377Syt { 4332fcbc377Syt return (mod_info(&modlinkage, modinfop)); 4342fcbc377Syt } 4352fcbc377Syt 4362fcbc377Syt /* 4372fcbc377Syt * The attach entry point for dev_ops. 4382fcbc377Syt */ 4392fcbc377Syt static int 4402fcbc377Syt ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4412fcbc377Syt { 4422fcbc377Syt ahci_ctl_t *ahci_ctlp; 4432fcbc377Syt int instance = ddi_get_instance(dip); 4442fcbc377Syt int status; 4452fcbc377Syt int attach_state; 4462fcbc377Syt uint32_t cap_status, ahci_version; 4472fcbc377Syt int intr_types; 44868d33a25Syt int i; 44995c11c1fSyt pci_regspec_t *regs; 45095c11c1fSyt int regs_length; 45195c11c1fSyt int rnumber; 45268d33a25Syt #if AHCI_DEBUG 45368d33a25Syt int speed; 45468d33a25Syt #endif 4552fcbc377Syt 4562fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, NULL, "ahci_attach enter"); 4572fcbc377Syt 4582fcbc377Syt switch (cmd) { 4592fcbc377Syt case DDI_ATTACH: 4602fcbc377Syt break; 4612fcbc377Syt 4622fcbc377Syt case DDI_RESUME: 46313bcbb7aSyt 46413bcbb7aSyt /* 46513bcbb7aSyt * During DDI_RESUME, the hardware state of the device 46613bcbb7aSyt * (power may have been removed from the device) must be 46713bcbb7aSyt * restored, allow pending requests to continue, and 46813bcbb7aSyt * service new requests. 46913bcbb7aSyt */ 47013bcbb7aSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 47113bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 47213bcbb7aSyt 47313bcbb7aSyt /* Restart watch thread */ 47413bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id == 0) 47513bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = timeout( 47613bcbb7aSyt (void (*)(void *))ahci_watchdog_handler, 47713bcbb7aSyt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 47813bcbb7aSyt 47913bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 48013bcbb7aSyt 48113bcbb7aSyt /* 48213bcbb7aSyt * Re-initialize the controller and enable the interrupts and 48313bcbb7aSyt * restart all the ports. 48413bcbb7aSyt * 48513bcbb7aSyt * Note that so far we don't support hot-plug during 48613bcbb7aSyt * suspend/resume. 48713bcbb7aSyt */ 48813bcbb7aSyt if (ahci_initialize_controller(ahci_ctlp) != AHCI_SUCCESS) { 489a9440e8dSyt AHCIDBG0(AHCIDBG_ERRS|AHCIDBG_PM, ahci_ctlp, 490a9440e8dSyt "Failed to initialize the controller " 491a9440e8dSyt "during DDI_RESUME"); 49213bcbb7aSyt return (DDI_FAILURE); 49313bcbb7aSyt } 49413bcbb7aSyt 49513bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 49613bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~ AHCI_SUSPEND; 49713bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 49813bcbb7aSyt 49913bcbb7aSyt return (DDI_SUCCESS); 5002fcbc377Syt 5012fcbc377Syt default: 5022fcbc377Syt return (DDI_FAILURE); 5032fcbc377Syt } 5042fcbc377Syt 5052fcbc377Syt attach_state = AHCI_ATTACH_STATE_NONE; 5062fcbc377Syt 5072fcbc377Syt /* Allocate soft state */ 5082fcbc377Syt status = ddi_soft_state_zalloc(ahci_statep, instance); 5092fcbc377Syt if (status != DDI_SUCCESS) { 510a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate soft state", 511a9440e8dSyt instance); 5122fcbc377Syt goto err_out; 5132fcbc377Syt } 5142fcbc377Syt 5152fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 51613bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_ATTACH; 5172fcbc377Syt ahci_ctlp->ahcictl_dip = dip; 5182fcbc377Syt 51968d33a25Syt /* Initialize the cport/port mapping */ 52068d33a25Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 52168d33a25Syt ahci_ctlp->ahcictl_port_to_cport[i] = 0xff; 52268d33a25Syt ahci_ctlp->ahcictl_cport_to_port[i] = 0xff; 52368d33a25Syt } 52468d33a25Syt 5252fcbc377Syt attach_state |= AHCI_ATTACH_STATE_STATEP_ALLOC; 5262fcbc377Syt 5272fcbc377Syt /* 5282fcbc377Syt * Now map the AHCI base address; which includes global 5292fcbc377Syt * registers and port control registers 53095c11c1fSyt * 53195c11c1fSyt * According to the spec, the AHCI Base Address is BAR5, 53213bcbb7aSyt * but BAR0-BAR4 are optional, so we need to check which 53313bcbb7aSyt * rnumber is used for BAR5. 53495c11c1fSyt */ 53595c11c1fSyt 53695c11c1fSyt /* 53795c11c1fSyt * search through DDI "reg" property for the AHCI register set 5382fcbc377Syt */ 53995c11c1fSyt if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 54095c11c1fSyt DDI_PROP_DONTPASS, "reg", (int **)®s, 54195c11c1fSyt (uint_t *)®s_length) != DDI_PROP_SUCCESS) { 542a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot lookup reg property", 543a9440e8dSyt instance); 54495c11c1fSyt goto err_out; 54595c11c1fSyt } 54695c11c1fSyt 54795c11c1fSyt /* AHCI Base Address is located at 0x24 offset */ 54895c11c1fSyt for (rnumber = 0; rnumber < regs_length; ++rnumber) { 54995c11c1fSyt if ((regs[rnumber].pci_phys_hi & PCI_REG_REG_M) 55095c11c1fSyt == AHCI_PCI_RNUM) 55195c11c1fSyt break; 55295c11c1fSyt } 55395c11c1fSyt 55495c11c1fSyt ddi_prop_free(regs); 55595c11c1fSyt 55695c11c1fSyt if (rnumber == regs_length) { 557a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot find AHCI register set", 558a9440e8dSyt instance); 55995c11c1fSyt goto err_out; 56095c11c1fSyt } 56195c11c1fSyt 56295c11c1fSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "rnumber = %d", rnumber); 56395c11c1fSyt 5642fcbc377Syt status = ddi_regs_map_setup(dip, 56595c11c1fSyt rnumber, 5662fcbc377Syt (caddr_t *)&ahci_ctlp->ahcictl_ahci_addr, 5672fcbc377Syt 0, 5682fcbc377Syt 0, 5692fcbc377Syt &accattr, 5702fcbc377Syt &ahci_ctlp->ahcictl_ahci_acc_handle); 5712fcbc377Syt if (status != DDI_SUCCESS) { 572a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot map register space", 573a9440e8dSyt instance); 5742fcbc377Syt goto err_out; 5752fcbc377Syt } 5762fcbc377Syt 5772fcbc377Syt attach_state |= AHCI_ATTACH_STATE_REG_MAP; 5782fcbc377Syt 5792fcbc377Syt /* Get the AHCI version information */ 5802fcbc377Syt ahci_version = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5812fcbc377Syt (uint32_t *)AHCI_GLOBAL_VS(ahci_ctlp)); 5822fcbc377Syt 583a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: hba AHCI version = %x.%x", instance, 5842fcbc377Syt (ahci_version & 0xffff0000) >> 16, 5852fcbc377Syt ((ahci_version & 0x0000ff00) >> 4 | 5862fcbc377Syt (ahci_version & 0x000000ff))); 5872fcbc377Syt 5882fcbc377Syt /* We don't support controllers whose versions are lower than 1.0 */ 5892fcbc377Syt if (!(ahci_version & 0xffff0000)) { 590a9440e8dSyt cmn_err(CE_WARN, "ahci%d: Don't support AHCI HBA with lower " 591a9440e8dSyt "than version 1.0", instance); 5922fcbc377Syt goto err_out; 5932fcbc377Syt } 5942fcbc377Syt 5952fcbc377Syt /* Get the HBA capabilities information */ 5962fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5972fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 5982fcbc377Syt 5992fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba capabilites = 0x%x", 6002fcbc377Syt cap_status); 6012fcbc377Syt 60268d33a25Syt #if AHCI_DEBUG 60368d33a25Syt /* Get the interface speed supported by the HBA */ 60468d33a25Syt speed = (cap_status & AHCI_HBA_CAP_ISS) >> AHCI_HBA_CAP_ISS_SHIFT; 60568d33a25Syt if (speed == 0x01) { 60668d33a25Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 60768d33a25Syt "hba interface speed support: Gen 1 (1.5Gbps)"); 60868d33a25Syt } else if (speed == 0x10) { 60968d33a25Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 61068d33a25Syt "hba interface speed support: Gen 2 (3 Gbps)"); 61168d33a25Syt } else if (speed == 0x11) { 61268d33a25Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 61368d33a25Syt "hba interface speed support: Gen 3 (6 Gbps)"); 61468d33a25Syt } 61568d33a25Syt #endif 61668d33a25Syt 6172fcbc377Syt /* Get the number of command slots supported by the HBA */ 6182fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots = 6192fcbc377Syt ((cap_status & AHCI_HBA_CAP_NCS) >> 6202fcbc377Syt AHCI_HBA_CAP_NCS_SHIFT) + 1; 6212fcbc377Syt 62268d33a25Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba number of cmd slots: %d", 6232fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots); 6242fcbc377Syt 6252fcbc377Syt /* Get the bit map which indicates ports implemented by the HBA */ 6262fcbc377Syt ahci_ctlp->ahcictl_ports_implemented = 6272fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 6282fcbc377Syt (uint32_t *)AHCI_GLOBAL_PI(ahci_ctlp)); 6292fcbc377Syt 6302fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba implementation of ports: 0x%x", 6312fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 6322fcbc377Syt 63309121340Syt /* 63409121340Syt * According to the AHCI spec, CAP.NP should indicate the maximum 63509121340Syt * number of ports supported by the HBA silicon, but we found 63609121340Syt * this value of ICH8 chipset only indicates the number of ports 63709121340Syt * implemented (exposed) by it. Therefore, the driver should calculate 63809121340Syt * the potential maximum value by checking PI register, and use 63909121340Syt * the maximum of this value and CAP.NP. 64009121340Syt */ 64109121340Syt ahci_ctlp->ahcictl_num_ports = max( 64209121340Syt (cap_status & AHCI_HBA_CAP_NP) + 1, 64309121340Syt ddi_fls(ahci_ctlp->ahcictl_ports_implemented)); 64409121340Syt 64509121340Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba number of ports: %d", 64609121340Syt ahci_ctlp->ahcictl_num_ports); 64709121340Syt 6482fcbc377Syt /* Get the number of implemented ports by the HBA */ 6492fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports = 6502fcbc377Syt ahci_get_num_implemented_ports( 6512fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 6522fcbc377Syt 6532fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 65468d33a25Syt "hba number of implemented ports: %d", 6552fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports); 6562fcbc377Syt 657a9440e8dSyt /* Check whether HBA supports 64bit DMA addressing */ 6582fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_S64A)) { 659*259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_BUF_32BIT_DMA; 660*259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_COMMU_32BIT_DMA; 6612fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 6622fcbc377Syt "hba does not support 64-bit addressing"); 6632fcbc377Syt } 6642fcbc377Syt 6650a4c4cecSXiao-Yu Zhang /* Checking for Support Command List Override */ 6660a4c4cecSXiao-Yu Zhang if (cap_status & AHCI_HBA_CAP_SCLO) { 6670a4c4cecSXiao-Yu Zhang ahci_ctlp->ahcictl_cap |= AHCI_CAP_SCLO; 6680a4c4cecSXiao-Yu Zhang AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 6690a4c4cecSXiao-Yu Zhang "hba supports command list override."); 6700a4c4cecSXiao-Yu Zhang } 6710a4c4cecSXiao-Yu Zhang 6722fcbc377Syt if (pci_config_setup(dip, &ahci_ctlp->ahcictl_pci_conf_handle) 6732fcbc377Syt != DDI_SUCCESS) { 674a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot set up pci configure space", 675a9440e8dSyt instance); 6762fcbc377Syt goto err_out; 6772fcbc377Syt } 6782fcbc377Syt 67968d33a25Syt attach_state |= AHCI_ATTACH_STATE_PCICFG_SETUP; 68068d33a25Syt 681db2cce03Sying tian - Beijing China /* 682db2cce03Sying tian - Beijing China * Check the pci configuration space, and set caps. We also 683db2cce03Sying tian - Beijing China * handle the hardware defect in this function. 684db2cce03Sying tian - Beijing China * 685*259105bcSying tian - Beijing China * For example, force ATI SB600 to use 32-bit dma addressing 686*259105bcSying tian - Beijing China * since it doesn't support 64-bit dma though its CAP register 687*259105bcSying tian - Beijing China * declares it support. 688db2cce03Sying tian - Beijing China */ 68913bcbb7aSyt if (ahci_config_space_init(ahci_ctlp) == AHCI_FAILURE) { 690a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_config_space_init failed", 691a9440e8dSyt instance); 69213bcbb7aSyt goto err_out; 69368d33a25Syt } 6942fcbc377Syt 6952fcbc377Syt /* 6962fcbc377Syt * Disable the whole controller interrupts before adding 6972fcbc377Syt * interrupt handlers(s). 6982fcbc377Syt */ 6992fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 7002fcbc377Syt 7012fcbc377Syt /* Get supported interrupt types */ 7022fcbc377Syt if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { 703a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ddi_intr_get_supported_types failed", 704a9440e8dSyt instance); 7052fcbc377Syt goto err_out; 7062fcbc377Syt } 7072fcbc377Syt 7082fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7092fcbc377Syt "ddi_intr_get_supported_types() returned: 0x%x", 7102fcbc377Syt intr_types); 7112fcbc377Syt 7122fcbc377Syt if (ahci_msi_enabled && (intr_types & DDI_INTR_TYPE_MSI)) { 7132fcbc377Syt /* 7142fcbc377Syt * Try MSI first, but fall back to FIXED if failed 7152fcbc377Syt */ 7162c742e1fSying tian - Beijing China if (ahci_add_intrs(ahci_ctlp, DDI_INTR_TYPE_MSI) == 7172c742e1fSying tian - Beijing China DDI_SUCCESS) { 7182fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_MSI; 7192fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7202fcbc377Syt "Using MSI interrupt type"); 7212fcbc377Syt goto intr_done; 7222fcbc377Syt } 7232fcbc377Syt 7242fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7252fcbc377Syt "MSI registration failed, " 7262fcbc377Syt "trying FIXED interrupts"); 7272fcbc377Syt } 7282fcbc377Syt 7292fcbc377Syt if (intr_types & DDI_INTR_TYPE_FIXED) { 7302c742e1fSying tian - Beijing China if (ahci_add_intrs(ahci_ctlp, DDI_INTR_TYPE_FIXED) == 7312c742e1fSying tian - Beijing China DDI_SUCCESS) { 7322fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_FIXED; 733a9440e8dSyt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7342fcbc377Syt "Using FIXED interrupt type"); 7352fcbc377Syt goto intr_done; 7362fcbc377Syt } 7372fcbc377Syt 7382fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7392fcbc377Syt "FIXED interrupt registration failed"); 7402fcbc377Syt } 7412fcbc377Syt 742a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Interrupt registration failed", instance); 7432fcbc377Syt 7442fcbc377Syt goto err_out; 7452fcbc377Syt 7462fcbc377Syt intr_done: 7472fcbc377Syt 7482fcbc377Syt attach_state |= AHCI_ATTACH_STATE_INTR_ADDED; 7492fcbc377Syt 7502fcbc377Syt /* Initialize the controller mutex */ 7512fcbc377Syt mutex_init(&ahci_ctlp->ahcictl_mutex, NULL, MUTEX_DRIVER, 7522fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 7532fcbc377Syt 7542fcbc377Syt attach_state |= AHCI_ATTACH_STATE_MUTEX_INIT; 7552fcbc377Syt 7562fcbc377Syt if (ahci_dma_prdt_number < AHCI_MIN_PRDT_NUMBER) { 7572fcbc377Syt ahci_dma_prdt_number = AHCI_MIN_PRDT_NUMBER; 7582fcbc377Syt } else if (ahci_dma_prdt_number > AHCI_MAX_PRDT_NUMBER) { 7592fcbc377Syt ahci_dma_prdt_number = AHCI_MAX_PRDT_NUMBER; 7602fcbc377Syt } 7612fcbc377Syt 7622fcbc377Syt ahci_cmd_table_size = (sizeof (ahci_cmd_table_t) + 7632fcbc377Syt (ahci_dma_prdt_number - AHCI_PRDT_NUMBER) * 7642fcbc377Syt sizeof (ahci_prdt_item_t)); 7652fcbc377Syt 7662fcbc377Syt AHCIDBG2(AHCIDBG_INIT, ahci_ctlp, 7672fcbc377Syt "ahci_attach: ahci_dma_prdt_number set by user is 0x%x," 7682fcbc377Syt " ahci_cmd_table_size is 0x%x", 7692fcbc377Syt ahci_dma_prdt_number, ahci_cmd_table_size); 7702fcbc377Syt 7712fcbc377Syt if (ahci_dma_prdt_number != AHCI_PRDT_NUMBER) 7722fcbc377Syt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_sgllen = 7732fcbc377Syt ahci_dma_prdt_number; 7742fcbc377Syt 775a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr = buffer_dma_attr; 776a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr = rcvd_fis_dma_attr; 777a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr = cmd_list_dma_attr; 778a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr = cmd_table_dma_attr; 779a9440e8dSyt 780*259105bcSying tian - Beijing China if ((ahci_buf_64bit_dma == B_FALSE) || 781*259105bcSying tian - Beijing China (ahci_ctlp->ahcictl_cap & AHCI_CAP_BUF_32BIT_DMA)) { 782a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_addr_hi = 783a9440e8dSyt 0xffffffffull; 784*259105bcSying tian - Beijing China } 785*259105bcSying tian - Beijing China 786*259105bcSying tian - Beijing China if ((ahci_commu_64bit_dma == B_FALSE) || 787*259105bcSying tian - Beijing China (ahci_ctlp->ahcictl_cap & AHCI_CAP_COMMU_32BIT_DMA)) { 788a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr.dma_attr_addr_hi = 789a9440e8dSyt 0xffffffffull; 790a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr.dma_attr_addr_hi = 791a9440e8dSyt 0xffffffffull; 792a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr.dma_attr_addr_hi = 793a9440e8dSyt 0xffffffffull; 794a9440e8dSyt } 795a9440e8dSyt 79668d33a25Syt /* Allocate the ports structure */ 79768d33a25Syt status = ahci_alloc_ports_state(ahci_ctlp); 79868d33a25Syt if (status != AHCI_SUCCESS) { 799a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate ports structure", 800a9440e8dSyt instance); 80168d33a25Syt goto err_out; 80268d33a25Syt } 80368d33a25Syt 80468d33a25Syt attach_state |= AHCI_ATTACH_STATE_PORT_ALLOC; 80568d33a25Syt 8062fcbc377Syt /* 80768d33a25Syt * Initialize the controller and ports. 8082fcbc377Syt */ 8092fcbc377Syt status = ahci_initialize_controller(ahci_ctlp); 8102fcbc377Syt if (status != AHCI_SUCCESS) { 811a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: HBA initialization failed", 812a9440e8dSyt instance); 8132fcbc377Syt goto err_out; 8142fcbc377Syt } 8152fcbc377Syt 8162fcbc377Syt attach_state |= AHCI_ATTACH_STATE_HW_INIT; 8172fcbc377Syt 8182fcbc377Syt /* Start one thread to check packet timeouts */ 8192fcbc377Syt ahci_ctlp->ahcictl_timeout_id = timeout( 8202fcbc377Syt (void (*)(void *))ahci_watchdog_handler, 8212fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 8222fcbc377Syt 8232fcbc377Syt attach_state |= AHCI_ATTACH_STATE_TIMEOUT_ENABLED; 8242fcbc377Syt 8252fcbc377Syt if (ahci_register_sata_hba_tran(ahci_ctlp, cap_status)) { 826a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: sata hba tran registration failed", 827a9440e8dSyt instance); 8282fcbc377Syt goto err_out; 8292fcbc377Syt } 8302fcbc377Syt 83113bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~AHCI_ATTACH; 83213bcbb7aSyt 8332fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, "ahci_attach success!"); 8342fcbc377Syt 8352fcbc377Syt return (DDI_SUCCESS); 8362fcbc377Syt 8372fcbc377Syt err_out: 8382fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_TIMEOUT_ENABLED) { 8392fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8402fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 8412fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 8422fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 8432fcbc377Syt } 8442fcbc377Syt 8452fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_HW_INIT) { 84668d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 8472fcbc377Syt } 8482fcbc377Syt 84968d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PORT_ALLOC) { 85068d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 85168d33a25Syt } 85268d33a25Syt 8532fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_MUTEX_INIT) { 8542fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 8552fcbc377Syt } 8562fcbc377Syt 8572fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_INTR_ADDED) { 8582fcbc377Syt ahci_rem_intrs(ahci_ctlp); 8592fcbc377Syt } 8602fcbc377Syt 86168d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PCICFG_SETUP) { 86268d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 86368d33a25Syt } 86468d33a25Syt 8652fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_REG_MAP) { 8662fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 8672fcbc377Syt } 8682fcbc377Syt 8692fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_STATEP_ALLOC) { 8702fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 8712fcbc377Syt } 8722fcbc377Syt 8732fcbc377Syt return (DDI_FAILURE); 8742fcbc377Syt } 8752fcbc377Syt 8762fcbc377Syt /* 8772fcbc377Syt * The detach entry point for dev_ops. 8782fcbc377Syt */ 8792fcbc377Syt static int 8802fcbc377Syt ahci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 8812fcbc377Syt { 8822fcbc377Syt ahci_ctl_t *ahci_ctlp; 8832fcbc377Syt int instance; 8842fcbc377Syt int ret; 8852fcbc377Syt 8862fcbc377Syt instance = ddi_get_instance(dip); 8872fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 8882fcbc377Syt 8892fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_detach enter"); 8902fcbc377Syt 8912fcbc377Syt switch (cmd) { 8922fcbc377Syt case DDI_DETACH: 89313bcbb7aSyt 8942fcbc377Syt /* disable the interrupts for an uninterrupted detach */ 8952fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8962fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 8972fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 8982fcbc377Syt 8992fcbc377Syt /* unregister from the sata framework. */ 9002fcbc377Syt ret = ahci_unregister_sata_hba_tran(ahci_ctlp); 9012fcbc377Syt if (ret != AHCI_SUCCESS) { 9022fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9032fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 9042fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 9052fcbc377Syt return (DDI_FAILURE); 9062fcbc377Syt } 9072fcbc377Syt 9082fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9092fcbc377Syt 9102fcbc377Syt /* stop the watchdog handler */ 9112fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 9122fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 9132fcbc377Syt 91468d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 91568d33a25Syt 91668d33a25Syt /* uninitialize the controller */ 91768d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 9182fcbc377Syt 91968d33a25Syt /* remove the interrupts */ 92068d33a25Syt ahci_rem_intrs(ahci_ctlp); 9212fcbc377Syt 92268d33a25Syt /* deallocate the ports structures */ 92368d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 92468d33a25Syt 92568d33a25Syt /* destroy mutex */ 9262fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 9272fcbc377Syt 92868d33a25Syt /* teardown the pci config */ 92968d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 9302fcbc377Syt 9312fcbc377Syt /* remove the reg maps. */ 9322fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 9332fcbc377Syt 9342fcbc377Syt /* free the soft state. */ 9352fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 9362fcbc377Syt 9372fcbc377Syt return (DDI_SUCCESS); 9382fcbc377Syt 9392fcbc377Syt case DDI_SUSPEND: 94013bcbb7aSyt 94113bcbb7aSyt /* 94213bcbb7aSyt * The steps associated with suspension must include putting 94313bcbb7aSyt * the underlying device into a quiescent state so that it 94413bcbb7aSyt * will not generate interrupts or modify or access memory. 94513bcbb7aSyt */ 94613bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 94713bcbb7aSyt if (ahci_ctlp->ahcictl_flags & AHCI_SUSPEND) { 94813bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 94913bcbb7aSyt return (DDI_SUCCESS); 95013bcbb7aSyt } 95113bcbb7aSyt 95213bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_SUSPEND; 95313bcbb7aSyt 95413bcbb7aSyt /* stop the watchdog handler */ 95513bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id) { 95613bcbb7aSyt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 95713bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = 0; 95813bcbb7aSyt } 95913bcbb7aSyt 96013bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 96113bcbb7aSyt 96213bcbb7aSyt /* 96313bcbb7aSyt * drain the taskq 96413bcbb7aSyt */ 965f8a673adSying tian - Beijing China ahci_drain_ports_taskq(ahci_ctlp); 96613bcbb7aSyt 96713bcbb7aSyt /* 96813bcbb7aSyt * Disable the interrupts and stop all the ports. 96913bcbb7aSyt */ 97013bcbb7aSyt ahci_uninitialize_controller(ahci_ctlp); 97113bcbb7aSyt 97213bcbb7aSyt return (DDI_SUCCESS); 9732fcbc377Syt 9742fcbc377Syt default: 9752fcbc377Syt return (DDI_FAILURE); 9762fcbc377Syt } 9772fcbc377Syt } 9782fcbc377Syt 9792fcbc377Syt /* 9802fcbc377Syt * The info entry point for dev_ops. 9812fcbc377Syt * 9822fcbc377Syt */ 9832fcbc377Syt static int 9842fcbc377Syt ahci_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 9852fcbc377Syt void *arg, void **result) 9862fcbc377Syt { 9872fcbc377Syt #ifndef __lock_lint 9882fcbc377Syt _NOTE(ARGUNUSED(dip)) 9892fcbc377Syt #endif /* __lock_lint */ 9902fcbc377Syt 9912fcbc377Syt ahci_ctl_t *ahci_ctlp; 9922fcbc377Syt int instance; 9932fcbc377Syt dev_t dev; 9942fcbc377Syt 9952fcbc377Syt dev = (dev_t)arg; 9962fcbc377Syt instance = getminor(dev); 9972fcbc377Syt 9982fcbc377Syt switch (infocmd) { 9992fcbc377Syt case DDI_INFO_DEVT2DEVINFO: 10002fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 10012fcbc377Syt if (ahci_ctlp != NULL) { 10022fcbc377Syt *result = ahci_ctlp->ahcictl_dip; 10032fcbc377Syt return (DDI_SUCCESS); 10042fcbc377Syt } else { 10052fcbc377Syt *result = NULL; 10062fcbc377Syt return (DDI_FAILURE); 10072fcbc377Syt } 10082fcbc377Syt case DDI_INFO_DEVT2INSTANCE: 10092fcbc377Syt *(int *)result = instance; 10102fcbc377Syt break; 10112fcbc377Syt default: 10122fcbc377Syt break; 10132fcbc377Syt } 10142fcbc377Syt 10152fcbc377Syt return (DDI_SUCCESS); 10162fcbc377Syt } 10172fcbc377Syt 10182fcbc377Syt /* 10192fcbc377Syt * Registers the ahci with sata framework. 10202fcbc377Syt */ 10212fcbc377Syt static int 10222fcbc377Syt ahci_register_sata_hba_tran(ahci_ctl_t *ahci_ctlp, uint32_t cap_status) 10232fcbc377Syt { 10242fcbc377Syt struct sata_hba_tran *sata_hba_tran; 10252fcbc377Syt 10262fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 10272fcbc377Syt "ahci_register_sata_hba_tran enter"); 10282fcbc377Syt 10292fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 10302fcbc377Syt 10312fcbc377Syt /* Allocate memory for the sata_hba_tran */ 10322fcbc377Syt sata_hba_tran = kmem_zalloc(sizeof (sata_hba_tran_t), KM_SLEEP); 10332fcbc377Syt 103468d33a25Syt sata_hba_tran->sata_tran_hba_rev = SATA_TRAN_HBA_REV_2; 10352fcbc377Syt sata_hba_tran->sata_tran_hba_dip = ahci_ctlp->ahcictl_dip; 10362fcbc377Syt sata_hba_tran->sata_tran_hba_dma_attr = 10372fcbc377Syt &ahci_ctlp->ahcictl_buffer_dma_attr; 10382fcbc377Syt 10392fcbc377Syt /* Report the number of implemented ports */ 10402fcbc377Syt sata_hba_tran->sata_tran_hba_num_cports = 10412fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports; 10422fcbc377Syt 104368d33a25Syt /* Support ATAPI device */ 104468d33a25Syt sata_hba_tran->sata_tran_hba_features_support = SATA_CTLF_ATAPI; 10452fcbc377Syt 10462fcbc377Syt /* Get the data transfer capability for PIO command by the HBA */ 10472fcbc377Syt if (cap_status & AHCI_HBA_CAP_PMD) { 104882263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PIO_MDRQ; 10492fcbc377Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "HBA supports multiple " 10502fcbc377Syt "DRQ block data transfer for PIO command protocol"); 10512fcbc377Syt } 10522fcbc377Syt 105382263d52Syt /* 105482263d52Syt * According to the AHCI spec, the ATA/ATAPI-7 queued feature set 105582263d52Syt * is not supported by AHCI (including the READ QUEUED (EXT), WRITE 105682263d52Syt * QUEUED (EXT), and SERVICE commands). Queued operations are 105782263d52Syt * supported in AHCI using the READ FPDMA QUEUED and WRITE FPDMA 105882263d52Syt * QUEUED commands when the HBA and device support native command 105982263d52Syt * queuing(NCQ). 106082263d52Syt * 106182263d52Syt * SATA_CTLF_NCQ will be set to sata_tran_hba_features_support if the 106282263d52Syt * CAP register of the HBA indicates NCQ is supported. 106382263d52Syt * 106482263d52Syt * SATA_CTLF_NCQ cannot be set if AHCI_CAP_NO_MCMDLIST_NONQUEUE is 106582263d52Syt * set because the previous register content of PxCI can be re-written 106682263d52Syt * in the register write. 106782263d52Syt */ 106882263d52Syt if ((cap_status & AHCI_HBA_CAP_SNCQ) && 106982263d52Syt !(ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE)) { 107082263d52Syt sata_hba_tran->sata_tran_hba_features_support |= SATA_CTLF_NCQ; 107182263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_NCQ; 107282263d52Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "HBA supports Native " 107382263d52Syt "Command Queuing"); 107482263d52Syt } 107582263d52Syt 10762fcbc377Syt /* Report the number of command slots */ 10772fcbc377Syt sata_hba_tran->sata_tran_hba_qdepth = ahci_ctlp->ahcictl_num_cmd_slots; 10782fcbc377Syt 10792fcbc377Syt sata_hba_tran->sata_tran_probe_port = ahci_tran_probe_port; 10802fcbc377Syt sata_hba_tran->sata_tran_start = ahci_tran_start; 10812fcbc377Syt sata_hba_tran->sata_tran_abort = ahci_tran_abort; 10822fcbc377Syt sata_hba_tran->sata_tran_reset_dport = ahci_tran_reset_dport; 10832fcbc377Syt sata_hba_tran->sata_tran_hotplug_ops = &ahci_tran_hotplug_ops; 10842fcbc377Syt #ifdef __lock_lint 10852fcbc377Syt sata_hba_tran->sata_tran_selftest = ahci_selftest; 10862fcbc377Syt #endif 10872fcbc377Syt /* 10882fcbc377Syt * When SATA framework adds support for pwrmgt the 10892fcbc377Syt * pwrmgt_ops needs to be updated 10902fcbc377Syt */ 10912fcbc377Syt sata_hba_tran->sata_tran_pwrmgt_ops = NULL; 10922fcbc377Syt sata_hba_tran->sata_tran_ioctl = NULL; 10932fcbc377Syt 10942fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = sata_hba_tran; 10952fcbc377Syt 10962fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 10972fcbc377Syt 10982fcbc377Syt /* Attach it to SATA framework */ 10992fcbc377Syt if (sata_hba_attach(ahci_ctlp->ahcictl_dip, sata_hba_tran, DDI_ATTACH) 11002fcbc377Syt != DDI_SUCCESS) { 11012fcbc377Syt kmem_free((void *)sata_hba_tran, sizeof (sata_hba_tran_t)); 11022fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 11032fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 11042fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 11052fcbc377Syt return (AHCI_FAILURE); 11062fcbc377Syt } 11072fcbc377Syt 11082fcbc377Syt return (AHCI_SUCCESS); 11092fcbc377Syt } 11102fcbc377Syt 11112fcbc377Syt /* 11122fcbc377Syt * Unregisters the ahci with sata framework. 11132fcbc377Syt */ 11142fcbc377Syt static int 11152fcbc377Syt ahci_unregister_sata_hba_tran(ahci_ctl_t *ahci_ctlp) 11162fcbc377Syt { 11172fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, 11182fcbc377Syt "ahci_unregister_sata_hba_tran enter"); 11192fcbc377Syt 11202fcbc377Syt /* Detach from the SATA framework. */ 11212fcbc377Syt if (sata_hba_detach(ahci_ctlp->ahcictl_dip, DDI_DETACH) != 11222fcbc377Syt DDI_SUCCESS) { 11232fcbc377Syt return (AHCI_FAILURE); 11242fcbc377Syt } 11252fcbc377Syt 11262fcbc377Syt /* Deallocate sata_hba_tran. */ 11272fcbc377Syt kmem_free((void *)ahci_ctlp->ahcictl_sata_hba_tran, 11282fcbc377Syt sizeof (sata_hba_tran_t)); 11292fcbc377Syt 11302fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 11312fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 11322fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 11332fcbc377Syt 11342fcbc377Syt return (AHCI_SUCCESS); 11352fcbc377Syt } 11362fcbc377Syt 11372fcbc377Syt /* 11382fcbc377Syt * ahci_tran_probe_port is called by SATA framework. It returns port state, 11392fcbc377Syt * port status registers and an attached device type via sata_device 11402fcbc377Syt * structure. 11412fcbc377Syt * 11422fcbc377Syt * We return the cached information from a previous hardware probe. The 11432fcbc377Syt * actual hardware probing itself was done either from within 11442fcbc377Syt * ahci_initialize_controller() during the driver attach or from a phy 11452fcbc377Syt * ready change interrupt handler. 11462fcbc377Syt */ 11472fcbc377Syt static int 11482fcbc377Syt ahci_tran_probe_port(dev_info_t *dip, sata_device_t *sd) 11492fcbc377Syt { 11502fcbc377Syt ahci_ctl_t *ahci_ctlp; 11512fcbc377Syt ahci_port_t *ahci_portp; 11522fcbc377Syt uint8_t cport = sd->satadev_addr.cport; 1153689d74b0Syt #if AHCI_DEBUG 11542fcbc377Syt uint8_t pmport = sd->satadev_addr.pmport; 11552fcbc377Syt uint8_t qual = sd->satadev_addr.qual; 1156689d74b0Syt #endif 11572fcbc377Syt uint8_t device_type; 11582fcbc377Syt uint32_t port_state; 11592fcbc377Syt uint8_t port; 11602fcbc377Syt 11612fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 11622fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 11632fcbc377Syt 11642fcbc377Syt AHCIDBG3(AHCIDBG_ENTRY, ahci_ctlp, 116568d33a25Syt "ahci_tran_probe_port enter: cport: %d, " 116668d33a25Syt "pmport: %d, qual: %d", cport, pmport, qual); 11672fcbc377Syt 11682fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 11692fcbc377Syt 11702fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 11712fcbc377Syt 11722fcbc377Syt port_state = ahci_portp->ahciport_port_state; 11732fcbc377Syt switch (port_state) { 11742fcbc377Syt 11752fcbc377Syt case SATA_PSTATE_FAILED: 11762fcbc377Syt sd->satadev_state = SATA_PSTATE_FAILED; 11772fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 117868d33a25Syt "ahci_tran_probe_port: port %d PORT FAILED", port); 11792fcbc377Syt goto out; 11802fcbc377Syt 11812fcbc377Syt case SATA_PSTATE_SHUTDOWN: 11822fcbc377Syt sd->satadev_state = SATA_PSTATE_SHUTDOWN; 11832fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 118468d33a25Syt "ahci_tran_probe_port: port %d PORT SHUTDOWN", port); 11852fcbc377Syt goto out; 11862fcbc377Syt 11872fcbc377Syt case SATA_PSTATE_PWROFF: 11882fcbc377Syt sd->satadev_state = SATA_PSTATE_PWROFF; 11892fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 119068d33a25Syt "ahci_tran_probe_port: port %d PORT PWROFF", port); 11912fcbc377Syt goto out; 11922fcbc377Syt 11932fcbc377Syt case SATA_PSTATE_PWRON: 11942fcbc377Syt sd->satadev_state = SATA_PSTATE_PWRON; 11952fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 119668d33a25Syt "ahci_tran_probe_port: port %d PORT PWRON", port); 11972fcbc377Syt break; 11982fcbc377Syt 11992fcbc377Syt default: 12002fcbc377Syt sd->satadev_state = port_state; 12012fcbc377Syt AHCIDBG2(AHCIDBG_INFO, ahci_ctlp, 120268d33a25Syt "ahci_tran_probe_port: port %d PORT NORMAL %x", 12032fcbc377Syt port, port_state); 12042fcbc377Syt break; 12052fcbc377Syt } 12062fcbc377Syt 12072fcbc377Syt device_type = ahci_portp->ahciport_device_type; 12082fcbc377Syt 12092fcbc377Syt switch (device_type) { 12102fcbc377Syt 12112fcbc377Syt case SATA_DTYPE_ATADISK: 12122fcbc377Syt sd->satadev_type = SATA_DTYPE_ATADISK; 12132fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 121468d33a25Syt "ahci_tran_probe_port: port %d DISK found", port); 12152fcbc377Syt break; 12162fcbc377Syt 121738547057Sying tian - Beijing China case SATA_DTYPE_ATAPI: 121838547057Sying tian - Beijing China /* 121938547057Sying tian - Beijing China * HBA driver only knows it's an ATAPI device, and don't know 1220f8a673adSying tian - Beijing China * it's CD/DVD, tape or ATAPI disk because the ATAPI device 1221f8a673adSying tian - Beijing China * type need to be determined by checking IDENTIFY PACKET 1222f8a673adSying tian - Beijing China * DEVICE data 122338547057Sying tian - Beijing China */ 122438547057Sying tian - Beijing China sd->satadev_type = SATA_DTYPE_ATAPI; 12252fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 122668d33a25Syt "ahci_tran_probe_port: port %d ATAPI found", port); 12272fcbc377Syt break; 12282fcbc377Syt 12292fcbc377Syt case SATA_DTYPE_PMULT: 12302fcbc377Syt sd->satadev_type = SATA_DTYPE_PMULT; 12312fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 123268d33a25Syt "ahci_tran_probe_port: port %d Port Multiplier found", 12332fcbc377Syt port); 12342fcbc377Syt break; 12352fcbc377Syt 12362fcbc377Syt case SATA_DTYPE_UNKNOWN: 12372fcbc377Syt sd->satadev_type = SATA_DTYPE_UNKNOWN; 12382fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 123968d33a25Syt "ahci_tran_probe_port: port %d Unknown device found", port); 12402fcbc377Syt break; 12412fcbc377Syt 12422fcbc377Syt default: 12432fcbc377Syt /* we don't support any other device types */ 12442fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 12452fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 124668d33a25Syt "ahci_tran_probe_port: port %d No device found", port); 12472fcbc377Syt break; 12482fcbc377Syt } 12492fcbc377Syt 12502fcbc377Syt out: 12512fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 12522fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 12532fcbc377Syt 12542fcbc377Syt return (SATA_SUCCESS); 12552fcbc377Syt } 12562fcbc377Syt 125768d33a25Syt /* 125868d33a25Syt * There are four operation modes in sata framework: 125968d33a25Syt * SATA_OPMODE_INTERRUPTS 126068d33a25Syt * SATA_OPMODE_POLLING 126168d33a25Syt * SATA_OPMODE_ASYNCH 126268d33a25Syt * SATA_OPMODE_SYNCH 126368d33a25Syt * 126468d33a25Syt * Their combined meanings as following: 126568d33a25Syt * 126668d33a25Syt * SATA_OPMODE_SYNCH 126768d33a25Syt * The command has to be completed before sata_tran_start functions returns. 126868d33a25Syt * Either interrupts or polling could be used - it's up to the driver. 126968d33a25Syt * Mode used currently for internal, sata-module initiated operations. 127068d33a25Syt * 127168d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS 127268d33a25Syt * It is the same as the one above. 127368d33a25Syt * 127468d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING 127568d33a25Syt * The command has to be completed before sata_tran_start function returns. 127668d33a25Syt * No interrupt used, polling only. This should be the mode used for scsi 127768d33a25Syt * packets with FLAG_NOINTR. 127868d33a25Syt * 127968d33a25Syt * SATA_OPMODE_ASYNCH | SATA_OPMODE_INTERRUPTS 128068d33a25Syt * The command may be queued (callback function specified). Interrupts could 128168d33a25Syt * be used. It's normal operation mode. 128268d33a25Syt */ 12832fcbc377Syt /* 12842fcbc377Syt * Called by sata framework to transport a sata packet down stream. 12852fcbc377Syt */ 12862fcbc377Syt static int 12872fcbc377Syt ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt) 12882fcbc377Syt { 12892fcbc377Syt ahci_ctl_t *ahci_ctlp; 12902fcbc377Syt ahci_port_t *ahci_portp; 12912fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 12922fcbc377Syt uint8_t port; 12932fcbc377Syt 12942fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 12952fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 12962fcbc377Syt 12972fcbc377Syt AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, 12982fcbc377Syt "ahci_tran_start enter: cport %d satapkt 0x%p", 12992fcbc377Syt cport, (void *)spkt); 13002fcbc377Syt 13012fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 13022fcbc377Syt 13032fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 13042fcbc377Syt 13052fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 13062fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 13072fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 13082fcbc377Syt /* 13092fcbc377Syt * In case the targer driver would send the packet before 13102fcbc377Syt * sata framework can have the opportunity to process those 13112fcbc377Syt * event reports. 13122fcbc377Syt */ 13132fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 13142fcbc377Syt spkt->satapkt_device.satadev_state = 13152fcbc377Syt ahci_portp->ahciport_port_state; 13162fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 13172fcbc377Syt &spkt->satapkt_device); 13182fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13192fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 13202fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 132168d33a25Syt "port: %d", port); 13222fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13232fcbc377Syt return (SATA_TRAN_PORT_ERROR); 13242fcbc377Syt } 13252fcbc377Syt 13262fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 13272fcbc377Syt /* 13282fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 13292fcbc377Syt * SATA_DTYPE_NONE. 13302fcbc377Syt */ 13312fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 13322fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 13332fcbc377Syt spkt->satapkt_device.satadev_state = 13342fcbc377Syt ahci_portp->ahciport_port_state; 13352fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 13362fcbc377Syt &spkt->satapkt_device); 13372fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13382fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 133968d33a25Syt "no device attached: port: %d", port); 13402fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13412fcbc377Syt return (SATA_TRAN_PORT_ERROR); 13422fcbc377Syt } 13432fcbc377Syt 13442fcbc377Syt /* 13452fcbc377Syt * SATA HBA driver should remember that a device was reset and it 13462fcbc377Syt * is supposed to reject any packets which do not specify either 13472fcbc377Syt * SATA_IGNORE_DEV_RESET_STATE or SATA_CLEAR_DEV_RESET_STATE. 13482fcbc377Syt * 13492fcbc377Syt * This is to prevent a race condition when a device was arbitrarily 13502fcbc377Syt * reset by the HBA driver (and lost it's setting) and a target 13512fcbc377Syt * driver sending some commands to a device before the sata framework 13522fcbc377Syt * has a chance to restore the device setting (such as cache enable/ 13532fcbc377Syt * disable or other resettable stuff). 13542fcbc377Syt */ 13552fcbc377Syt if (spkt->satapkt_cmd.satacmd_flags.sata_clear_dev_reset) { 13562fcbc377Syt ahci_portp->ahciport_reset_in_progress = 0; 13572fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13582fcbc377Syt "ahci_tran_start clearing the " 135968d33a25Syt "reset_in_progress for port: %d", port); 13602fcbc377Syt } 13612fcbc377Syt 13622fcbc377Syt if (ahci_portp->ahciport_reset_in_progress && 13632fcbc377Syt ! spkt->satapkt_cmd.satacmd_flags.sata_ignore_dev_reset && 13642fcbc377Syt ! ddi_in_panic()) { 13652fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 13662fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13672fcbc377Syt "ahci_tran_start returning BUSY while " 136868d33a25Syt "reset in progress: port: %d", port); 13692fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13702fcbc377Syt return (SATA_TRAN_BUSY); 13712fcbc377Syt } 13722fcbc377Syt 137368d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 13742fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 13752fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13762fcbc377Syt "ahci_tran_start returning BUSY while " 137768d33a25Syt "mopping in progress: port: %d", port); 13782fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13792fcbc377Syt return (SATA_TRAN_BUSY); 13802fcbc377Syt } 13812fcbc377Syt 13822fcbc377Syt if (spkt->satapkt_op_mode & 138368d33a25Syt (SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING)) { 1384b2e3645aSying tian - Beijing China /* 1385b2e3645aSying tian - Beijing China * If a SYNC command to be executed in interrupt context, 1386b2e3645aSying tian - Beijing China * bounce it back to sata module. 1387b2e3645aSying tian - Beijing China */ 1388b2e3645aSying tian - Beijing China if (!(spkt->satapkt_op_mode & SATA_OPMODE_POLLING) && 1389b2e3645aSying tian - Beijing China servicing_interrupt()) { 1390b2e3645aSying tian - Beijing China spkt->satapkt_reason = SATA_PKT_BUSY; 1391b2e3645aSying tian - Beijing China AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 1392b2e3645aSying tian - Beijing China "ahci_tran_start returning BUSY while " 1393b2e3645aSying tian - Beijing China "sending SYNC mode under interrupt context: " 1394b2e3645aSying tian - Beijing China "port : %d", port); 1395b2e3645aSying tian - Beijing China mutex_exit(&ahci_portp->ahciport_mutex); 1396b2e3645aSying tian - Beijing China return (SATA_TRAN_BUSY); 1397b2e3645aSying tian - Beijing China } 1398b2e3645aSying tian - Beijing China 139968d33a25Syt /* We need to do the sync start now */ 140068d33a25Syt if (ahci_do_sync_start(ahci_ctlp, ahci_portp, port, 140168d33a25Syt spkt) == AHCI_FAILURE) { 140268d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 140368d33a25Syt "return QUEUE_FULL: port %d", port); 140468d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 140568d33a25Syt return (SATA_TRAN_QUEUE_FULL); 140668d33a25Syt } 140768d33a25Syt } else { 140868d33a25Syt /* Async start, using interrupt */ 140968d33a25Syt if (ahci_deliver_satapkt(ahci_ctlp, ahci_portp, port, spkt) 141068d33a25Syt == AHCI_FAILURE) { 141168d33a25Syt spkt->satapkt_reason = SATA_PKT_QUEUE_FULL; 141268d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 141368d33a25Syt "returning QUEUE_FULL: port %d", port); 141468d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 141568d33a25Syt return (SATA_TRAN_QUEUE_FULL); 141668d33a25Syt } 14172fcbc377Syt } 14182fcbc377Syt 14192fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_tran_start " 14202fcbc377Syt "sata tran accepted: port %d", port); 14212fcbc377Syt 142268d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 14232fcbc377Syt return (SATA_TRAN_ACCEPTED); 14242fcbc377Syt } 14252fcbc377Syt 142668d33a25Syt /* 142768d33a25Syt * SATA_OPMODE_SYNCH flag is set 142868d33a25Syt * 142968d33a25Syt * If SATA_OPMODE_POLLING flag is set, then we must poll the command 143068d33a25Syt * without interrupt, otherwise we can still use the interrupt. 143168d33a25Syt * 143268d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function 143368d33a25Syt * is called. 143468d33a25Syt */ 143568d33a25Syt static int 143668d33a25Syt ahci_do_sync_start(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 143768d33a25Syt uint8_t port, sata_pkt_t *spkt) 143868d33a25Syt { 143968d33a25Syt int pkt_timeout_ticks; 144068d33a25Syt uint32_t timeout_tags; 144168d33a25Syt int rval; 1442a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 144368d33a25Syt 144468d33a25Syt AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, "ahci_do_sync_start enter: " 144568d33a25Syt "port %d spkt 0x%p", port, spkt); 144668d33a25Syt 144768d33a25Syt if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) { 144868d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_POLLING; 144968d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 145068d33a25Syt port, spkt)) == AHCI_FAILURE) { 145168d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_POLLING; 145268d33a25Syt return (rval); 145368d33a25Syt } 145468d33a25Syt 145568d33a25Syt pkt_timeout_ticks = 145668d33a25Syt drv_usectohz((clock_t)spkt->satapkt_time * 1000000); 145768d33a25Syt 145868d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) { 145968d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 146068d33a25Syt 146168d33a25Syt /* Simulate the interrupt */ 146282263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 146368d33a25Syt 146468d33a25Syt drv_usecwait(AHCI_1MS_USECS); 146568d33a25Syt 146668d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 146768d33a25Syt pkt_timeout_ticks -= AHCI_1MS_TICKS; 146868d33a25Syt if (pkt_timeout_ticks < 0) { 1469a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_do_sync_start " 147068d33a25Syt "port %d satapkt 0x%p timed out\n", 1471a9440e8dSyt instance, port, (void *)spkt); 147268d33a25Syt timeout_tags = (0x1 << rval); 147368d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 147468d33a25Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 147582263d52Syt port, timeout_tags); 147668d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 147768d33a25Syt } 147868d33a25Syt } 147968d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_POLLING; 148068d33a25Syt return (AHCI_SUCCESS); 148168d33a25Syt 148268d33a25Syt } else { 148368d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 148468d33a25Syt port, spkt)) == AHCI_FAILURE) 148568d33a25Syt return (rval); 148668d33a25Syt 148782263d52Syt #if AHCI_DEBUG 148882263d52Syt /* 148982263d52Syt * Note that the driver always uses the slot 0 to deliver 149082263d52Syt * REQUEST SENSE or READ LOG EXT command 149182263d52Syt */ 149282263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 149382263d52Syt ASSERT(rval == 0); 149482263d52Syt #endif 149582263d52Syt 149668d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) 149768d33a25Syt cv_wait(&ahci_portp->ahciport_cv, 149868d33a25Syt &ahci_portp->ahciport_mutex); 149968d33a25Syt 150068d33a25Syt return (AHCI_SUCCESS); 150168d33a25Syt } 150268d33a25Syt } 150368d33a25Syt 15042fcbc377Syt #define SENDUP_PACKET(ahci_portp, satapkt, reason) \ 15052fcbc377Syt if (satapkt) { \ 15062fcbc377Syt satapkt->satapkt_reason = reason; \ 15072fcbc377Syt /* \ 15082fcbc377Syt * We set the satapkt_reason in both sync and \ 15092fcbc377Syt * non-sync cases. \ 15102fcbc377Syt */ \ 15112fcbc377Syt } \ 15122fcbc377Syt if (satapkt && \ 151368d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 15142fcbc377Syt satapkt->satapkt_comp) { \ 15152fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); \ 15162fcbc377Syt (*satapkt->satapkt_comp)(satapkt); \ 15172fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); \ 151868d33a25Syt } else { \ 151968d33a25Syt if (satapkt && \ 152068d33a25Syt (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 152168d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_POLLING)) \ 1522f8a673adSying tian - Beijing China cv_broadcast(&ahci_portp->ahciport_cv); \ 15232fcbc377Syt } 15242fcbc377Syt 15252fcbc377Syt /* 152682263d52Syt * Searches for and claims a free command slot. 152782263d52Syt * 152882263d52Syt * Returns: 15292fcbc377Syt * 153082263d52Syt * AHCI_FAILURE if failed 153182263d52Syt * 1. if no empty slot left 153282263d52Syt * 2. non-queued command requested while queued command(s) is outstanding 153382263d52Syt * 3. queued command requested whild non-queued command(s) is outstanding 153482263d52Syt * 4. HBA doesn't support multiple-use of command list while already a 153582263d52Syt * non-queued command is oustanding 153682263d52Syt * 153782263d52Syt * claimed slot number if succeeded 153882263d52Syt * 153982263d52Syt * NOTE: it will always return slot 0 during error recovery process for 154082263d52Syt * REQUEST SENSE or READ LOG EXT command to simplify the algorithm. 15412fcbc377Syt * 15422fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 15432fcbc377Syt * is called. 15442fcbc377Syt */ 15452fcbc377Syt static int 154682263d52Syt ahci_claim_free_slot(ahci_ctl_t *ahci_ctlp, 154782263d52Syt ahci_port_t *ahci_portp, int command_type) 15482fcbc377Syt { 15492fcbc377Syt uint32_t free_slots; 15502fcbc377Syt int slot; 15512fcbc377Syt 155282263d52Syt AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, "ahci_claim_free_slot enter " 155382263d52Syt "ahciport_pending_tags = 0x%x " 155482263d52Syt "ahciport_pending_ncq_tags = 0x%x", 155582263d52Syt ahci_portp->ahciport_pending_tags, 155682263d52Syt ahci_portp->ahciport_pending_ncq_tags); 15572fcbc377Syt 155882263d52Syt /* 155982263d52Syt * According to the AHCI spec, system software is responsible to 156082263d52Syt * ensure that queued and non-queued commands are not mixed in 156182263d52Syt * the command list. 156282263d52Syt */ 156382263d52Syt if (command_type == AHCI_NON_NCQ_CMD) { 156482263d52Syt /* Non-NCQ command request */ 156582263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 156682263d52Syt AHCIDBG0(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 156782263d52Syt "ahci_claim_free_slot: there is still pending " 156882263d52Syt "queued command(s) in the command list, " 156982263d52Syt "so no available slot for the non-queued " 157082263d52Syt "command"); 157182263d52Syt return (AHCI_FAILURE); 157282263d52Syt } 157382263d52Syt if ((ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE) && 157482263d52Syt NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 157582263d52Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, 157682263d52Syt "ahci_claim_free_slot: HBA cannot support multiple-" 157782263d52Syt "use of the command list for non-queued commands"); 157882263d52Syt return (AHCI_FAILURE); 157982263d52Syt } 158082263d52Syt free_slots = (~ahci_portp->ahciport_pending_tags) & 158182263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 158282263d52Syt } else if (command_type == AHCI_NCQ_CMD) { 158382263d52Syt /* NCQ command request */ 158482263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 158582263d52Syt AHCIDBG0(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 158682263d52Syt "ahci_claim_free_slot: there is still pending " 158782263d52Syt "non-queued command(s) in the command list, " 158882263d52Syt "so no available slot for the queued command"); 158982263d52Syt return (AHCI_FAILURE); 159082263d52Syt } 159182263d52Syt free_slots = (~ahci_portp->ahciport_pending_ncq_tags) & 159282263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 159382263d52Syt } else if (command_type == AHCI_ERR_RETRI_CMD) { 159482263d52Syt /* Error retrieval command request */ 159582263d52Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 159682263d52Syt "ahci_claim_free_slot: slot 0 is allocated for REQUEST " 159782263d52Syt "SENSE or READ LOG EXT command"); 159882263d52Syt slot = 0; 159982263d52Syt goto out; 160082263d52Syt } 16012fcbc377Syt 16022fcbc377Syt slot = ddi_ffs(free_slots) - 1; 16032fcbc377Syt if (slot == -1) { 16042fcbc377Syt AHCIDBG0(AHCIDBG_VERBOSE, ahci_ctlp, 16052fcbc377Syt "ahci_claim_free_slot: no empty slots"); 16062fcbc377Syt return (AHCI_FAILURE); 16072fcbc377Syt } 16082fcbc377Syt 160982263d52Syt /* 161082263d52Syt * According to the AHCI spec, to allow a simple mechanism for the 161182263d52Syt * HBA to map command list slots to queue entries, software must 161282263d52Syt * match the tag number it uses to the slot it is placing the command 161382263d52Syt * in. For example, if a queued command is placed in slot 5, the tag 161482263d52Syt * for that command must be 5. 161582263d52Syt */ 161682263d52Syt if (command_type == AHCI_NCQ_CMD) { 161782263d52Syt ahci_portp->ahciport_pending_ncq_tags |= (0x1 << slot); 161882263d52Syt } 161982263d52Syt 16202fcbc377Syt ahci_portp->ahciport_pending_tags |= (0x1 << slot); 16212fcbc377Syt 162282263d52Syt out: 16232fcbc377Syt AHCIDBG1(AHCIDBG_VERBOSE, ahci_ctlp, 16242fcbc377Syt "ahci_claim_free_slot: found slot: 0x%x", slot); 16252fcbc377Syt 16262fcbc377Syt return (slot); 16272fcbc377Syt } 16282fcbc377Syt 16292fcbc377Syt /* 16302fcbc377Syt * Builds the Command Table for the sata packet and delivers it to controller. 16312fcbc377Syt * 16322fcbc377Syt * Returns: 16332fcbc377Syt * slot number if we can obtain a slot successfully 16342fcbc377Syt * otherwise, return AHCI_FAILURE 16352fcbc377Syt * 16362fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 16372fcbc377Syt */ 16382fcbc377Syt static int 16392fcbc377Syt ahci_deliver_satapkt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 16402fcbc377Syt uint8_t port, sata_pkt_t *spkt) 16412fcbc377Syt { 164268d33a25Syt int cmd_slot; 164368d33a25Syt sata_cmd_t *scmd; 16442fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 16452fcbc377Syt ahci_cmd_table_t *cmd_table; 16462fcbc377Syt ahci_cmd_header_t *cmd_header; 16472fcbc377Syt int ncookies; 16482fcbc377Syt int i; 164982263d52Syt int command_type = AHCI_NON_NCQ_CMD; 165082263d52Syt int ncq_qdepth; 1651a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 165268d33a25Syt #if AHCI_DEBUG 165368d33a25Syt uint32_t *ptr; 165468d33a25Syt uint8_t *ptr2; 165568d33a25Syt #endif 16562fcbc377Syt 16572fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 16582fcbc377Syt 165968d33a25Syt scmd = &spkt->satapkt_cmd; 16602fcbc377Syt 166182263d52Syt /* Check if the command is a NCQ command */ 166282263d52Syt if (scmd->satacmd_cmd_reg == SATAC_READ_FPDMA_QUEUED || 166382263d52Syt scmd->satacmd_cmd_reg == SATAC_WRITE_FPDMA_QUEUED) { 166482263d52Syt command_type = AHCI_NCQ_CMD; 166582263d52Syt 166682263d52Syt /* 166782263d52Syt * When NCQ is support, system software must determine the 166882263d52Syt * maximum tag allowed by the device and the HBA, and it 166982263d52Syt * must use a value not beyond of the lower bound of the two. 167082263d52Syt * 167182263d52Syt * Sata module is going to calculate the qdepth and send 167282263d52Syt * down to HBA driver via sata_cmd. 167382263d52Syt */ 167482263d52Syt ncq_qdepth = scmd->satacmd_flags.sata_max_queue_depth + 1; 167582263d52Syt 167682263d52Syt /* 167782263d52Syt * At the moment, the driver doesn't support the dynamic 167882263d52Syt * setting of the maximum ncq depth, and the value can be 167982263d52Syt * set either during the attach or after hot-plug insertion. 168082263d52Syt */ 168182263d52Syt if (ahci_portp->ahciport_max_ncq_tags == 0) { 168282263d52Syt ahci_portp->ahciport_max_ncq_tags = ncq_qdepth; 168382263d52Syt AHCIDBG2(AHCIDBG_NCQ, ahci_ctlp, 168482263d52Syt "ahci_deliver_satapkt: port %d the max tags for " 168582263d52Syt "NCQ command is %d", port, ncq_qdepth); 168682263d52Syt } else { 168782263d52Syt if (ncq_qdepth != ahci_portp->ahciport_max_ncq_tags) { 1688a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_deliver_satapkt" 1689a9440e8dSyt " port %d the max tag for NCQ command is " 169082263d52Syt "requested to change from %d to %d, at the" 169182263d52Syt " moment the driver doesn't support the " 169282263d52Syt "dynamic change so it's going to " 1693a9440e8dSyt "still use the previous tag value", 1694a9440e8dSyt instance, port, 169582263d52Syt ahci_portp->ahciport_max_ncq_tags, 169682263d52Syt ncq_qdepth); 169782263d52Syt } 169882263d52Syt } 169982263d52Syt } 170082263d52Syt 170182263d52Syt /* Check if the command is an error retrieval command */ 170282263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 170382263d52Syt command_type = AHCI_ERR_RETRI_CMD; 170482263d52Syt 170582263d52Syt /* Check if there is an empty command slot */ 170682263d52Syt cmd_slot = ahci_claim_free_slot(ahci_ctlp, ahci_portp, command_type); 170768d33a25Syt if (cmd_slot == AHCI_FAILURE) { 170882263d52Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "no free command slot"); 17092fcbc377Syt return (AHCI_FAILURE); 17102fcbc377Syt } 17112fcbc377Syt 17122fcbc377Syt AHCIDBG4(AHCIDBG_ENTRY|AHCIDBG_INFO, ahci_ctlp, 171368d33a25Syt "ahci_deliver_satapkt enter: cmd_reg: 0x%x, cmd_slot: 0x%x, " 171468d33a25Syt "port: %d, satapkt: 0x%p", scmd->satacmd_cmd_reg, 171568d33a25Syt cmd_slot, port, (void *)spkt); 17162fcbc377Syt 171768d33a25Syt cmd_table = ahci_portp->ahciport_cmd_tables[cmd_slot]; 17182fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 17192fcbc377Syt 172082263d52Syt /* For data transfer operations, it is the H2D Register FIS */ 17212fcbc377Syt h2d_register_fisp = 17222fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 17232fcbc377Syt 17242fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 17252fcbc377Syt if ((spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_PMPORT) || 17262fcbc377Syt (spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_DPMPORT)) { 17272fcbc377Syt SET_FIS_PMP(h2d_register_fisp, 17282fcbc377Syt spkt->satapkt_device.satadev_addr.pmport); 17292fcbc377Syt } 17302fcbc377Syt 17312fcbc377Syt SET_FIS_CDMDEVCTL(h2d_register_fisp, 1); 173268d33a25Syt SET_FIS_COMMAND(h2d_register_fisp, scmd->satacmd_cmd_reg); 173368d33a25Syt SET_FIS_FEATURES(h2d_register_fisp, scmd->satacmd_features_reg); 173468d33a25Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, scmd->satacmd_sec_count_lsb); 17352fcbc377Syt 173668d33a25Syt switch (scmd->satacmd_addr_type) { 17372fcbc377Syt 173882263d52Syt case 0: 173982263d52Syt /* 174082263d52Syt * satacmd_addr_type will be 0 for the commands below: 174182263d52Syt * ATAPI command 174282263d52Syt * SATAC_IDLE_IM 174382263d52Syt * SATAC_STANDBY_IM 174482263d52Syt * SATAC_DOWNLOAD_MICROCODE 174582263d52Syt * SATAC_FLUSH_CACHE 174682263d52Syt * SATAC_SET_FEATURES 174782263d52Syt * SATAC_SMART 174882263d52Syt * SATAC_ID_PACKET_DEVICE 174982263d52Syt * SATAC_ID_DEVICE 175082263d52Syt */ 1751689d74b0Syt /* FALLTHRU */ 175282263d52Syt 17532fcbc377Syt case ATA_ADDR_LBA: 1754689d74b0Syt /* FALLTHRU */ 17552fcbc377Syt 17562fcbc377Syt case ATA_ADDR_LBA28: 17572fcbc377Syt /* LBA[7:0] */ 175868d33a25Syt SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb); 17592fcbc377Syt 17602fcbc377Syt /* LBA[15:8] */ 176168d33a25Syt SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb); 17622fcbc377Syt 17632fcbc377Syt /* LBA[23:16] */ 176468d33a25Syt SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb); 17652fcbc377Syt 17662fcbc377Syt /* LBA [27:24] (also called dev_head) */ 176768d33a25Syt SET_FIS_DEV_HEAD(h2d_register_fisp, scmd->satacmd_device_reg); 17682fcbc377Syt 17692fcbc377Syt break; 17702fcbc377Syt 17712fcbc377Syt case ATA_ADDR_LBA48: 17722fcbc377Syt /* LBA[7:0] */ 177368d33a25Syt SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb); 17742fcbc377Syt 17752fcbc377Syt /* LBA[15:8] */ 177668d33a25Syt SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb); 17772fcbc377Syt 17782fcbc377Syt /* LBA[23:16] */ 177968d33a25Syt SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb); 17802fcbc377Syt 17812fcbc377Syt /* LBA [31:24] */ 17822fcbc377Syt SET_FIS_SECTOR_EXP(h2d_register_fisp, 178368d33a25Syt scmd->satacmd_lba_low_msb); 17842fcbc377Syt 17852fcbc377Syt /* LBA [39:32] */ 17862fcbc377Syt SET_FIS_CYL_LOW_EXP(h2d_register_fisp, 178768d33a25Syt scmd->satacmd_lba_mid_msb); 17882fcbc377Syt 17892fcbc377Syt /* LBA [47:40] */ 17902fcbc377Syt SET_FIS_CYL_HI_EXP(h2d_register_fisp, 179168d33a25Syt scmd->satacmd_lba_high_msb); 17922fcbc377Syt 17932fcbc377Syt /* Set dev_head */ 17942fcbc377Syt SET_FIS_DEV_HEAD(h2d_register_fisp, 179568d33a25Syt scmd->satacmd_device_reg); 17962fcbc377Syt 17972fcbc377Syt /* Set the extended sector count and features */ 17982fcbc377Syt SET_FIS_SECTOR_COUNT_EXP(h2d_register_fisp, 179968d33a25Syt scmd->satacmd_sec_count_msb); 18002fcbc377Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 180168d33a25Syt scmd->satacmd_features_reg_ext); 18022fcbc377Syt break; 18032fcbc377Syt } 18042fcbc377Syt 180582263d52Syt /* 180682263d52Syt * For NCQ command (READ/WRITE FPDMA QUEUED), sector count 7:0 is 180782263d52Syt * filled into features field, and sector count 8:15 is filled into 180882263d52Syt * features (exp) field. TAG is filled into sector field. 180982263d52Syt */ 181082263d52Syt if (command_type == AHCI_NCQ_CMD) { 181182263d52Syt SET_FIS_FEATURES(h2d_register_fisp, 181282263d52Syt scmd->satacmd_sec_count_lsb); 181382263d52Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 181482263d52Syt scmd->satacmd_sec_count_msb); 181582263d52Syt 181682263d52Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, 181782263d52Syt (cmd_slot << SATA_TAG_QUEUING_SHIFT)); 181882263d52Syt } 181982263d52Syt 182068d33a25Syt ncookies = scmd->satacmd_num_dma_cookies; 182138547057Sying tian - Beijing China AHCIDBG2(AHCIDBG_PRDT, ahci_ctlp, 18222fcbc377Syt "ncookies = 0x%x, ahci_dma_prdt_number = 0x%x", 18232fcbc377Syt ncookies, ahci_dma_prdt_number); 18242fcbc377Syt 18252fcbc377Syt ASSERT(ncookies <= ahci_dma_prdt_number); 182638547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot] = 0; 18272fcbc377Syt 18282fcbc377Syt /* *** now fill the scatter gather list ******* */ 18292fcbc377Syt for (i = 0; i < ncookies; i++) { 18302fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr = 183168d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[0]; 18322fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr_upper = 183368d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[1]; 18342fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_descr_info = 183568d33a25Syt scmd->satacmd_dma_cookie_list[i].dmac_size - 1; 183638547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot] += 183738547057Sying tian - Beijing China scmd->satacmd_dma_cookie_list[i].dmac_size; 183868d33a25Syt } 183968d33a25Syt 184038547057Sying tian - Beijing China AHCIDBG2(AHCIDBG_PRDT, ahci_ctlp, 184138547057Sying tian - Beijing China "ahciport_prd_bytecounts 0x%x for cmd_slot 0x%x", 184238547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot], cmd_slot); 184338547057Sying tian - Beijing China 184468d33a25Syt /* The ACMD field is filled in for ATAPI command */ 184568d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) { 184668d33a25Syt bcopy(scmd->satacmd_acdb, cmd_table->ahcict_atapi_cmd, 184768d33a25Syt SATA_ATAPI_MAX_CDB_LEN); 18482fcbc377Syt } 18492fcbc377Syt 18502fcbc377Syt /* Set Command Header in Command List */ 185168d33a25Syt cmd_header = &ahci_portp->ahciport_cmd_list[cmd_slot]; 18522fcbc377Syt BZERO_DESCR_INFO(cmd_header); 18532fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 185468d33a25Syt 185568d33a25Syt /* Set the number of entries in the PRD table */ 18562fcbc377Syt SET_PRD_TABLE_LENGTH(cmd_header, ncookies); 185768d33a25Syt 185868d33a25Syt /* Set the length of the command in the CFIS area */ 18592fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, AHCI_H2D_REGISTER_FIS_LENGTH); 18602fcbc377Syt 18612fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "command data direction is " 18622fcbc377Syt "sata_data_direction = 0x%x", 186368d33a25Syt scmd->satacmd_flags.sata_data_direction); 186468d33a25Syt 186568d33a25Syt /* Set A bit if it is an ATAPI command */ 186668d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) 186768d33a25Syt SET_ATAPI(cmd_header, AHCI_CMDHEAD_ATAPI); 18682fcbc377Syt 186968d33a25Syt /* Set W bit if data is going to the device */ 187068d33a25Syt if (scmd->satacmd_flags.sata_data_direction == SATA_DIR_WRITE) 18712fcbc377Syt SET_WRITE(cmd_header, AHCI_CMDHEAD_DATA_WRITE); 18722fcbc377Syt 187368d33a25Syt /* 187468d33a25Syt * Set the prefetchable bit - this bit is only valid if the PRDTL 187568d33a25Syt * field is non-zero or the ATAPI 'A' bit is set in the command 187668d33a25Syt * header. This bit cannot be set when using native command 187768d33a25Syt * queuing commands or when using FIS-based switching with a Port 187882263d52Syt * multiplier. 187968d33a25Syt */ 188082263d52Syt if (command_type != AHCI_NCQ_CMD) 188182263d52Syt SET_PREFETCHABLE(cmd_header, AHCI_CMDHEAD_PREFETCHABLE); 18822fcbc377Syt 18832fcbc377Syt /* Now remember the sata packet in ahciport_slot_pkts[]. */ 188482263d52Syt if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 188582263d52Syt ahci_portp->ahciport_slot_pkts[cmd_slot] = spkt; 18862fcbc377Syt 18872fcbc377Syt /* 18882fcbc377Syt * We are overloading satapkt_hba_driver_private with 18892fcbc377Syt * watched_cycle count. 18902fcbc377Syt */ 18912fcbc377Syt spkt->satapkt_hba_driver_private = (void *)(intptr_t)0; 18922fcbc377Syt 189368d33a25Syt #if AHCI_DEBUG 189438547057Sying tian - Beijing China if (ahci_debug_flags & AHCIDBG_ATACMD && 189538547057Sying tian - Beijing China scmd->satacmd_cmd_reg != SATAC_PACKET || 189638547057Sying tian - Beijing China ahci_debug_flags & AHCIDBG_ATAPICMD && 189738547057Sying tian - Beijing China scmd->satacmd_cmd_reg == SATAC_PACKET) { 189838547057Sying tian - Beijing China 189938547057Sying tian - Beijing China /* Dump the command header and table */ 190038547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, "\n"); 190138547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, "Command header&table for spkt " 190238547057Sying tian - Beijing China "0x%p cmd_reg 0x%x port %d", spkt, 190338547057Sying tian - Beijing China scmd->satacmd_cmd_reg, port); 190438547057Sying tian - Beijing China ptr = (uint32_t *)cmd_header; 190538547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 190638547057Sying tian - Beijing China " Command Header:%8x %8x %8x %8x", 190738547057Sying tian - Beijing China ptr[0], ptr[1], ptr[2], ptr[3]); 190838547057Sying tian - Beijing China 190938547057Sying tian - Beijing China /* Dump the H2D register FIS */ 191038547057Sying tian - Beijing China ptr = (uint32_t *)h2d_register_fisp; 191138547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 191238547057Sying tian - Beijing China " Command FIS: %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 ACMD register FIS */ 191638547057Sying tian - Beijing China ptr2 = (uint8_t *)&(cmd_table->ahcict_atapi_cmd); 191738547057Sying tian - Beijing China for (i = 0; i < SATA_ATAPI_MAX_CDB_LEN/8; i++) 191838547057Sying tian - Beijing China if (ahci_debug_flags & AHCIDBG_ATAPICMD) 191938547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 192038547057Sying tian - Beijing China " ATAPI command: %2x %2x %2x %2x " 192138547057Sying tian - Beijing China "%2x %2x %2x %2x", 192238547057Sying tian - Beijing China ptr2[8 * i], ptr2[8 * i + 1], 192338547057Sying tian - Beijing China ptr2[8 * i + 2], ptr2[8 * i + 3], 192438547057Sying tian - Beijing China ptr2[8 * i + 4], ptr2[8 * i + 5], 192538547057Sying tian - Beijing China ptr2[8 * i + 6], ptr2[8 * i + 7]); 192638547057Sying tian - Beijing China 192738547057Sying tian - Beijing China /* Dump the PRDT */ 192838547057Sying tian - Beijing China for (i = 0; i < ncookies; i++) { 192938547057Sying tian - Beijing China ptr = (uint32_t *)&(cmd_table->ahcict_prdt[i]); 193068d33a25Syt ahci_log(ahci_ctlp, CE_WARN, 193138547057Sying tian - Beijing China " Cookie %d: %8x %8x %8x %8x", 193238547057Sying tian - Beijing China i, ptr[0], ptr[1], ptr[2], ptr[3]); 193338547057Sying tian - Beijing China } 193468d33a25Syt } 193568d33a25Syt #endif 193668d33a25Syt 193768d33a25Syt (void) ddi_dma_sync( 193868d33a25Syt ahci_portp->ahciport_cmd_tables_dma_handle[cmd_slot], 19392fcbc377Syt 0, 19402fcbc377Syt ahci_cmd_table_size, 19412fcbc377Syt DDI_DMA_SYNC_FORDEV); 19422fcbc377Syt 19432fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 194468d33a25Syt cmd_slot * sizeof (ahci_cmd_header_t), 19452fcbc377Syt sizeof (ahci_cmd_header_t), 19462fcbc377Syt DDI_DMA_SYNC_FORDEV); 19472fcbc377Syt 194882263d52Syt /* Set the corresponding bit in the PxSACT.DS for queued command */ 194982263d52Syt if (command_type == AHCI_NCQ_CMD) { 195082263d52Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 195182263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port), 195282263d52Syt (0x1 << cmd_slot)); 195382263d52Syt } 195482263d52Syt 19552fcbc377Syt /* Indicate to the HBA that a command is active. */ 19562fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 19572fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 195868d33a25Syt (0x1 << cmd_slot)); 19592fcbc377Syt 19602fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_deliver_satapkt " 19612fcbc377Syt "exit: port %d", port); 19622fcbc377Syt 196368d33a25Syt return (cmd_slot); 19642fcbc377Syt } 19652fcbc377Syt 19662fcbc377Syt /* 19672fcbc377Syt * Called by the sata framework to abort the previously sent packet(s). 19682fcbc377Syt * 19692fcbc377Syt * Reset device to abort commands. 19702fcbc377Syt */ 19712fcbc377Syt static int 19722fcbc377Syt ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag) 19732fcbc377Syt { 19742fcbc377Syt ahci_ctl_t *ahci_ctlp; 19752fcbc377Syt ahci_port_t *ahci_portp; 197682263d52Syt uint32_t slot_status = 0; 197782263d52Syt uint32_t aborted_tags = 0; 197882263d52Syt uint32_t finished_tags = 0; 19792fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 19802fcbc377Syt uint8_t port; 19812fcbc377Syt int tmp_slot; 1982a9440e8dSyt int instance = ddi_get_instance(dip); 19832fcbc377Syt 1984a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 19852fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 19862fcbc377Syt 19872fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 198868d33a25Syt "ahci_tran_abort enter: port %d", port); 19892fcbc377Syt 19902fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 19912fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 19922fcbc377Syt 19932fcbc377Syt /* 199468d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 199568d33a25Syt * commands are being mopped, therefore there is nothing else to do 19962fcbc377Syt */ 199768d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 19982fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 19992fcbc377Syt "ahci_tran_abort: port %d is in " 20002fcbc377Syt "mopping process, so just return directly ", port); 20012fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20022fcbc377Syt return (SATA_SUCCESS); 20032fcbc377Syt } 20042fcbc377Syt 20052fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 20062fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 20072fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 20082fcbc377Syt /* 20092fcbc377Syt * In case the targer driver would send the request before 20102fcbc377Syt * sata framework can have the opportunity to process those 20112fcbc377Syt * event reports. 20122fcbc377Syt */ 20132fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 20142fcbc377Syt spkt->satapkt_device.satadev_state = 20152fcbc377Syt ahci_portp->ahciport_port_state; 20162fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 20172fcbc377Syt &spkt->satapkt_device); 20182fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 20192fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 20202fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 202168d33a25Syt "port: %d", port); 20222fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20232fcbc377Syt return (SATA_FAILURE); 20242fcbc377Syt } 20252fcbc377Syt 20262fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 20272fcbc377Syt /* 20282fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 20292fcbc377Syt * AHCI_PORT_TYPE_NODEV. 20302fcbc377Syt */ 20312fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 20322fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 20332fcbc377Syt spkt->satapkt_device.satadev_state = 20342fcbc377Syt ahci_portp->ahciport_port_state; 20352fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 20362fcbc377Syt &spkt->satapkt_device); 20372fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 20382fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 203968d33a25Syt "no device attached: port: %d", port); 20402fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20412fcbc377Syt return (SATA_FAILURE); 20422fcbc377Syt } 20432fcbc377Syt 20442fcbc377Syt if (flag == SATA_ABORT_ALL_PACKETS) { 204582263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 204682263d52Syt aborted_tags = ahci_portp->ahciport_pending_tags; 2047a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 204882263d52Syt aborted_tags = ahci_portp->ahciport_pending_ncq_tags; 204982263d52Syt 2050a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort all packets", 2051a9440e8dSyt instance, port); 20522fcbc377Syt } else { 20532fcbc377Syt aborted_tags = 0xffffffff; 20542fcbc377Syt /* 205582263d52Syt * Aborting one specific packet, first search the 20562fcbc377Syt * ahciport_slot_pkts[] list for matching spkt. 20572fcbc377Syt */ 20582fcbc377Syt for (tmp_slot = 0; 20592fcbc377Syt tmp_slot < ahci_ctlp->ahcictl_num_cmd_slots; tmp_slot++) { 20602fcbc377Syt if (ahci_portp->ahciport_slot_pkts[tmp_slot] == spkt) { 20612fcbc377Syt aborted_tags = (0x1 << tmp_slot); 20622fcbc377Syt break; 20632fcbc377Syt } 20642fcbc377Syt } 20652fcbc377Syt 20662fcbc377Syt if (aborted_tags == 0xffffffff) { 20672fcbc377Syt /* request packet is not on the pending list */ 20682fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 20692fcbc377Syt "Cannot find the aborting pkt 0x%p on the " 20702fcbc377Syt "pending list", (void *)spkt); 20712fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 20722fcbc377Syt &spkt->satapkt_device); 20732fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20742fcbc377Syt return (SATA_FAILURE); 20752fcbc377Syt } 2076a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort satapkt 0x%p", 2077a9440e8dSyt instance, port, (void *)spkt); 20782fcbc377Syt } 20792fcbc377Syt 208082263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 208182263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 208282263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 2083a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 208482263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 208582263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 20862fcbc377Syt 208768d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 208868d33a25Syt ahci_portp->ahciport_mop_in_progress++; 20892fcbc377Syt 20902fcbc377Syt /* 20912fcbc377Syt * To abort the packet(s), first we are trying to clear PxCMD.ST 209268d33a25Syt * to stop the port, and if the port can be stopped 20932fcbc377Syt * successfully with PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0', 20942fcbc377Syt * then we just send back the aborted packet(s) with ABORTED flag 20952fcbc377Syt * and then restart the port by setting PxCMD.ST and PxCMD.FRE. 20962fcbc377Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then we 20972fcbc377Syt * perform a COMRESET. 20982fcbc377Syt */ 20992fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 210068d33a25Syt ahci_portp, port, NULL, NULL); 21012fcbc377Syt 21022fcbc377Syt /* 21032fcbc377Syt * Compute which have finished and which need to be retried. 21042fcbc377Syt * 210582263d52Syt * The finished tags are ahciport_pending_tags/ahciport_pending_ncq_tags 210682263d52Syt * minus the slot_status. The aborted_tags has to be deducted by 210782263d52Syt * finished_tags since we can't possibly abort a tag which had finished 210882263d52Syt * already. 21092fcbc377Syt */ 211082263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 211182263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 211282263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2113a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 211482263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 211582263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 21162fcbc377Syt 21172fcbc377Syt aborted_tags &= ~finished_tags; 21182fcbc377Syt 21192fcbc377Syt ahci_mop_commands(ahci_ctlp, 21202fcbc377Syt ahci_portp, 21212fcbc377Syt slot_status, 21222fcbc377Syt 0, /* failed tags */ 21232fcbc377Syt 0, /* timeout tags */ 21242fcbc377Syt aborted_tags, 21252fcbc377Syt 0); /* reset tags */ 21262fcbc377Syt 21272fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, &spkt->satapkt_device); 21282fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 21292fcbc377Syt 21302fcbc377Syt return (SATA_SUCCESS); 21312fcbc377Syt } 21322fcbc377Syt 21332fcbc377Syt /* 21342fcbc377Syt * Used to do device reset and reject all the pending packets on a device 21352fcbc377Syt * during the reset operation. 21362fcbc377Syt * 21372fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 21382fcbc377Syt */ 21392fcbc377Syt static int 21402fcbc377Syt ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp, 21412fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 21422fcbc377Syt { 214382263d52Syt uint32_t slot_status = 0; 214482263d52Syt uint32_t reset_tags = 0; 214582263d52Syt uint32_t finished_tags = 0; 21462fcbc377Syt sata_device_t sdevice; 21472fcbc377Syt int ret; 21482fcbc377Syt 21492fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 21502fcbc377Syt "ahci_reset_device_reject_pkts on port: %d", port); 21512fcbc377Syt 21522fcbc377Syt /* 215368d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 215468d33a25Syt * commands are being mopped, therefore there is nothing else to do 21552fcbc377Syt */ 215668d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 21572fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 21582fcbc377Syt "ahci_reset_device_reject_pkts: port %d is in " 21592fcbc377Syt "mopping process, so return directly ", port); 21602fcbc377Syt return (SATA_SUCCESS); 21612fcbc377Syt } 21622fcbc377Syt 216382263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 216482263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 216582263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 216682263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2167a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 216882263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 216982263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 217082263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 217182263d52Syt } 21722fcbc377Syt 21732fcbc377Syt if (ahci_software_reset(ahci_ctlp, ahci_portp, port) 21742fcbc377Syt != AHCI_SUCCESS) { 21752fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 21762fcbc377Syt "Try to do a port reset after software " 21772fcbc377Syt "reset failed", port); 21782fcbc377Syt ret = ahci_port_reset(ahci_ctlp, ahci_portp, port); 21792fcbc377Syt if (ret != AHCI_SUCCESS) { 21802fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 21812fcbc377Syt "ahci_reset_device_reject_pkts: port %d " 21822fcbc377Syt "failed", port); 21832fcbc377Syt return (SATA_FAILURE); 21842fcbc377Syt } 21852fcbc377Syt } 21862fcbc377Syt /* Set the reset in progress flag */ 21872fcbc377Syt ahci_portp->ahciport_reset_in_progress = 1; 21882fcbc377Syt 218968d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 219068d33a25Syt ahci_portp->ahciport_mop_in_progress++; 21912fcbc377Syt 21922fcbc377Syt /* Indicate to the framework that a reset has happened */ 21932fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 219409121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 219568d33a25Syt sdevice.satadev_addr.pmport = 0; 219668d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 21972fcbc377Syt 21982fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 21992fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 22002fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 22012fcbc377Syt sata_hba_event_notify( 22022fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 22032fcbc377Syt &sdevice, 22042fcbc377Syt SATA_EVNT_DEVICE_RESET); 22052fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 22062fcbc377Syt 22072fcbc377Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 22082fcbc377Syt "port %d sending event up: SATA_EVNT_RESET", port); 22092fcbc377Syt 22102fcbc377Syt /* Next try to mop the pending commands */ 221182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 221282263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 221382263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2214a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 221582263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 221682263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 22172fcbc377Syt 22182fcbc377Syt reset_tags &= ~finished_tags; 22192fcbc377Syt 22202fcbc377Syt ahci_mop_commands(ahci_ctlp, 22212fcbc377Syt ahci_portp, 22222fcbc377Syt slot_status, 22232fcbc377Syt 0, /* failed tags */ 22242fcbc377Syt 0, /* timeout tags */ 22252fcbc377Syt 0, /* aborted tags */ 22262fcbc377Syt reset_tags); /* reset tags */ 22272fcbc377Syt 22282fcbc377Syt return (SATA_SUCCESS); 22292fcbc377Syt } 22302fcbc377Syt 22312fcbc377Syt /* 22322fcbc377Syt * Used to do port reset and reject all the pending packets on a port during 22332fcbc377Syt * the reset operation. 22342fcbc377Syt * 22352fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 22362fcbc377Syt */ 22372fcbc377Syt static int 22382fcbc377Syt ahci_reset_port_reject_pkts(ahci_ctl_t *ahci_ctlp, 22392fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 22402fcbc377Syt { 224182263d52Syt uint32_t slot_status = 0; 224282263d52Syt uint32_t reset_tags = 0; 224382263d52Syt uint32_t finished_tags = 0; 22442fcbc377Syt 22452fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 22462fcbc377Syt "ahci_reset_port_reject_pkts on port: %d", port); 22472fcbc377Syt 22482fcbc377Syt /* 224968d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 225068d33a25Syt * commands are being mopped, therefore there is nothing else to do 22512fcbc377Syt */ 225268d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 22532fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 22542fcbc377Syt "ahci_reset_port_reject_pkts: port %d is in " 22552fcbc377Syt "mopping process, so return directly ", port); 22562fcbc377Syt return (SATA_SUCCESS); 22572fcbc377Syt } 22582fcbc377Syt 225968d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 226068d33a25Syt ahci_portp->ahciport_mop_in_progress++; 22612fcbc377Syt 226282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 226382263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 226482263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 226582263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2266a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 226782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 226882263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 226982263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 227082263d52Syt } 22712fcbc377Syt 22722fcbc377Syt if (ahci_restart_port_wait_till_ready(ahci_ctlp, 227382263d52Syt ahci_portp, port, AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 227482263d52Syt NULL) != AHCI_SUCCESS) 22752fcbc377Syt return (SATA_FAILURE); 22762fcbc377Syt 227782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 227882263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 227982263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2280a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 228182263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 228282263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 22832fcbc377Syt 22842fcbc377Syt reset_tags &= ~finished_tags; 22852fcbc377Syt 22862fcbc377Syt ahci_mop_commands(ahci_ctlp, 22872fcbc377Syt ahci_portp, 22882fcbc377Syt slot_status, 22892fcbc377Syt 0, /* failed tags */ 22902fcbc377Syt 0, /* timeout tags */ 22912fcbc377Syt 0, /* aborted tags */ 22922fcbc377Syt reset_tags); /* reset tags */ 22932fcbc377Syt 22942fcbc377Syt return (SATA_SUCCESS); 22952fcbc377Syt } 22962fcbc377Syt 22972fcbc377Syt /* 22982fcbc377Syt * Used to do hba reset and reject all the pending packets on all ports 22992fcbc377Syt * during the reset operation. 23002fcbc377Syt */ 23012fcbc377Syt static int 23022fcbc377Syt ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp) 23032fcbc377Syt { 23042fcbc377Syt ahci_port_t *ahci_portp; 23052fcbc377Syt uint32_t slot_status[AHCI_MAX_PORTS]; 23062fcbc377Syt uint32_t reset_tags[AHCI_MAX_PORTS]; 23072fcbc377Syt uint32_t finished_tags[AHCI_MAX_PORTS]; 23082fcbc377Syt uint8_t port; 23092fcbc377Syt int ret = SATA_SUCCESS; 23102fcbc377Syt 23112fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, 23122fcbc377Syt "ahci_reset_hba_reject_pkts enter"); 23132fcbc377Syt 23142fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 23152fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 23162fcbc377Syt continue; 23172fcbc377Syt } 23182fcbc377Syt 23192fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 23202fcbc377Syt 23212fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 232282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 232382263d52Syt slot_status[port] = ddi_get32( 232482263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 232582263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 232682263d52Syt reset_tags[port] = slot_status[port] & 232782263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 2328a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 232982263d52Syt slot_status[port] = ddi_get32( 233082263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 233182263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 233282263d52Syt reset_tags[port] = slot_status[port] & 233382263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 233482263d52Syt } 23352fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23362fcbc377Syt } 23372fcbc377Syt 23382fcbc377Syt if (ahci_hba_reset(ahci_ctlp) != AHCI_SUCCESS) { 23392fcbc377Syt ret = SATA_FAILURE; 23402fcbc377Syt goto out; 23412fcbc377Syt } 23422fcbc377Syt 23432fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 23442fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 23452fcbc377Syt continue; 23462fcbc377Syt } 23472fcbc377Syt 23482fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 23492fcbc377Syt 23502fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 23512fcbc377Syt /* 23522fcbc377Syt * To prevent recursive enter to ahci_mop_commands, we need 235368d33a25Syt * check AHCI_PORT_FLAG_MOPPING flag. 23542fcbc377Syt */ 235568d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 23562fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 23572fcbc377Syt "ahci_reset_hba_reject_pkts: port %d is in " 23582fcbc377Syt "mopping process, so return directly ", port); 23592fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23602fcbc377Syt continue; 23612fcbc377Syt } 23622fcbc377Syt 236368d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 236468d33a25Syt ahci_portp->ahciport_mop_in_progress++; 23652fcbc377Syt 236682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 236782263d52Syt finished_tags[port] = 236882263d52Syt ahci_portp->ahciport_pending_tags & 236982263d52Syt ~slot_status[port] & AHCI_SLOT_MASK(ahci_ctlp); 2370a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 237182263d52Syt finished_tags[port] = 237282263d52Syt ahci_portp->ahciport_pending_ncq_tags & 237382263d52Syt ~slot_status[port] & AHCI_NCQ_SLOT_MASK(ahci_portp); 23742fcbc377Syt 23752fcbc377Syt reset_tags[port] &= ~finished_tags[port]; 23762fcbc377Syt 23772fcbc377Syt ahci_mop_commands(ahci_ctlp, 23782fcbc377Syt ahci_portp, 23792fcbc377Syt slot_status[port], 23802fcbc377Syt 0, /* failed tags */ 23812fcbc377Syt 0, /* timeout tags */ 23822fcbc377Syt 0, /* aborted tags */ 23832fcbc377Syt reset_tags[port]); /* reset tags */ 238468d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 23852fcbc377Syt } 23862fcbc377Syt out: 23872fcbc377Syt return (ret); 23882fcbc377Syt } 23892fcbc377Syt 23902fcbc377Syt /* 23912fcbc377Syt * Called by sata framework to reset a port(s) or device. 23922fcbc377Syt */ 23932fcbc377Syt static int 23942fcbc377Syt ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd) 23952fcbc377Syt { 23962fcbc377Syt ahci_ctl_t *ahci_ctlp; 23972fcbc377Syt ahci_port_t *ahci_portp; 23982fcbc377Syt uint8_t cport = sd->satadev_addr.cport; 23992fcbc377Syt uint8_t port; 24002fcbc377Syt int ret = SATA_SUCCESS; 2401a9440e8dSyt int instance = ddi_get_instance(dip); 24022fcbc377Syt 2403a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 24042fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 24052fcbc377Syt 24062fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 240768d33a25Syt "ahci_tran_reset_port enter: cport: %d", cport); 24082fcbc377Syt 24092fcbc377Syt switch (sd->satadev_addr.qual) { 24102fcbc377Syt case SATA_ADDR_CPORT: 24112fcbc377Syt /* Port reset */ 24122fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2413a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2414a9440e8dSyt "reset port", instance, port); 24152fcbc377Syt 24162fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 24172fcbc377Syt ret = ahci_reset_port_reject_pkts(ahci_ctlp, ahci_portp, port); 24182fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24192fcbc377Syt 24202fcbc377Syt break; 24212fcbc377Syt 24222fcbc377Syt case SATA_ADDR_DCPORT: 24232fcbc377Syt /* Device reset */ 24242fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2425a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2426a9440e8dSyt "reset device", instance, port); 24272fcbc377Syt 24282fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 24292fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 24302fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 24312fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 24322fcbc377Syt /* 24332fcbc377Syt * In case the targer driver would send the request 24342fcbc377Syt * before sata framework can have the opportunity to 24352fcbc377Syt * process those event reports. 24362fcbc377Syt */ 24372fcbc377Syt sd->satadev_state = ahci_portp->ahciport_port_state; 24382fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 24392fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 24402fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 24412fcbc377Syt "while port in FAILED/SHUTDOWN/PWROFF state: " 244268d33a25Syt "port: %d", port); 24432fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24442fcbc377Syt ret = SATA_FAILURE; 24452fcbc377Syt break; 24462fcbc377Syt } 24472fcbc377Syt 24482fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 24492fcbc377Syt /* 24502fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 24512fcbc377Syt * AHCI_PORT_TYPE_NODEV. 24522fcbc377Syt */ 24532fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 24542fcbc377Syt sd->satadev_state = ahci_portp->ahciport_port_state; 24552fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 24562fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 24572fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 245868d33a25Syt "while no device attached: port: %d", port); 24592fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24602fcbc377Syt ret = SATA_FAILURE; 24612fcbc377Syt break; 24622fcbc377Syt } 24632fcbc377Syt 24642fcbc377Syt ret = ahci_reset_device_reject_pkts(ahci_ctlp, 24652fcbc377Syt ahci_portp, port); 24662fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24672fcbc377Syt break; 24682fcbc377Syt 24692fcbc377Syt case SATA_ADDR_CNTRL: 24702fcbc377Syt /* Reset the whole controller */ 2471a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2472a9440e8dSyt "reset the whole hba", instance, port); 24732fcbc377Syt ret = ahci_reset_hba_reject_pkts(ahci_ctlp); 24742fcbc377Syt break; 24752fcbc377Syt 24762fcbc377Syt case SATA_ADDR_PMPORT: 24772fcbc377Syt case SATA_ADDR_DPMPORT: 24782fcbc377Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, 247968d33a25Syt "ahci_tran_reset_dport: port multiplier will be " 248068d33a25Syt "supported later"); 2481689d74b0Syt /* FALLTHRU */ 24822fcbc377Syt default: 24832fcbc377Syt ret = SATA_FAILURE; 24842fcbc377Syt } 24852fcbc377Syt 24862fcbc377Syt return (ret); 24872fcbc377Syt } 24882fcbc377Syt 24892fcbc377Syt /* 24902fcbc377Syt * Called by sata framework to activate a port as part of hotplug. 24912fcbc377Syt * (cfgadm -c connect satax/y) 24922fcbc377Syt * Note: Not port-mult aware. 24932fcbc377Syt */ 24942fcbc377Syt static int 24952fcbc377Syt ahci_tran_hotplug_port_activate(dev_info_t *dip, sata_device_t *satadev) 24962fcbc377Syt { 24972fcbc377Syt ahci_ctl_t *ahci_ctlp; 24982fcbc377Syt ahci_port_t *ahci_portp; 24992fcbc377Syt uint8_t cport = satadev->satadev_addr.cport; 25002fcbc377Syt uint8_t port; 2501a9440e8dSyt int instance = ddi_get_instance(dip); 25022fcbc377Syt 2503a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 25042fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 25052fcbc377Syt 25062fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 25072fcbc377Syt "ahci_tran_hotplug_port_activate cport %d enter", cport); 25082fcbc377Syt 25092fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 25102fcbc377Syt 25112fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 2512689d74b0Syt ahci_enable_port_intrs(ahci_ctlp, port); 2513a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d is activated", instance, port); 25142fcbc377Syt 25152fcbc377Syt /* 25162fcbc377Syt * Reset the port so that the PHY communication would be re-established. 251768d33a25Syt * But this reset is an internal operation and the sata module doesn't 251868d33a25Syt * need to know about it. Moreover, the port with a device attached will 251968d33a25Syt * be started too. 25202fcbc377Syt */ 25212fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 252268d33a25Syt ahci_portp, port, 252368d33a25Syt AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 252468d33a25Syt NULL); 25252fcbc377Syt 25262fcbc377Syt /* 25272fcbc377Syt * Need to check the link status and device status of the port 25282fcbc377Syt * and consider raising power if the port was in D3 state 25292fcbc377Syt */ 253068d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_PWRON; 253168d33a25Syt ahci_portp->ahciport_port_state &= ~SATA_PSTATE_PWROFF; 253268d33a25Syt ahci_portp->ahciport_port_state &= ~SATA_PSTATE_SHUTDOWN; 253368d33a25Syt 253468d33a25Syt satadev->satadev_state = ahci_portp->ahciport_port_state; 25352fcbc377Syt 25362fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 25372fcbc377Syt 25382fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 25392fcbc377Syt return (SATA_SUCCESS); 25402fcbc377Syt } 25412fcbc377Syt 25422fcbc377Syt /* 25432fcbc377Syt * Called by sata framework to deactivate a port as part of hotplug. 25442fcbc377Syt * (cfgadm -c disconnect satax/y) 25452fcbc377Syt * Note: Not port-mult aware. 25462fcbc377Syt */ 25472fcbc377Syt static int 25482fcbc377Syt ahci_tran_hotplug_port_deactivate(dev_info_t *dip, sata_device_t *satadev) 25492fcbc377Syt { 25502fcbc377Syt ahci_ctl_t *ahci_ctlp; 25512fcbc377Syt ahci_port_t *ahci_portp; 25522fcbc377Syt uint8_t cport = satadev->satadev_addr.cport; 25532fcbc377Syt uint8_t port; 255468d33a25Syt uint32_t port_scontrol; 2555a9440e8dSyt int instance = ddi_get_instance(dip); 25562fcbc377Syt 2557a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 25582fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 25592fcbc377Syt 25602fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 25612fcbc377Syt "ahci_tran_hotplug_port_deactivate cport %d enter", cport); 25622fcbc377Syt 25632fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 25642fcbc377Syt 25652fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 2566a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d is deactivated", 2567a9440e8dSyt instance, port); 25682fcbc377Syt 256968d33a25Syt /* Disable the interrupts on the port */ 2570689d74b0Syt ahci_disable_port_intrs(ahci_ctlp, port); 25712fcbc377Syt 257268d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 257368d33a25Syt goto phy_offline; 257468d33a25Syt } 257568d33a25Syt 25762fcbc377Syt /* First to abort all the pending commands */ 25772fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 25782fcbc377Syt 257968d33a25Syt /* Then stop the port */ 258068d33a25Syt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 25812fcbc377Syt ahci_portp, port); 25822fcbc377Syt 258368d33a25Syt /* Next put the PHY offline */ 258468d33a25Syt 258568d33a25Syt phy_offline: 258668d33a25Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 258768d33a25Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 258882263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_DISABLE); 258968d33a25Syt 25902fcbc377Syt /* Update ahciport_port_state */ 25912fcbc377Syt ahci_portp->ahciport_port_state = SATA_PSTATE_SHUTDOWN; 259268d33a25Syt satadev->satadev_state = ahci_portp->ahciport_port_state; 25932fcbc377Syt 25942fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 25952fcbc377Syt 25962fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 25972fcbc377Syt return (SATA_SUCCESS); 25982fcbc377Syt } 25992fcbc377Syt 26002fcbc377Syt /* 260168d33a25Syt * To be used to mark all the outstanding pkts with SATA_PKT_ABORTED 26022fcbc377Syt * when a device is unplugged or a port is deactivated. 26032fcbc377Syt * 26042fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 26052fcbc377Syt */ 26062fcbc377Syt static void 26072fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctl_t *ahci_ctlp, 26082fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 26092fcbc377Syt { 261082263d52Syt uint32_t slot_status = 0; 261182263d52Syt uint32_t abort_tags = 0; 26122fcbc377Syt 261368d33a25Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 26142fcbc377Syt "ahci_reject_all_abort_pkts on port: %d", port); 26152fcbc377Syt 261682263d52Syt /* 261782263d52Syt * When AHCI_PORT_FLAG_MOPPING is set, we need to check whether a 261882263d52Syt * REQUEST SENSE command or READ LOG EXT command is delivered to HBA 261982263d52Syt * to get the error data, if yes when the device is removed, the 262082263d52Syt * command needs to be aborted too. 262182263d52Syt */ 262282263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 262382263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 262482263d52Syt slot_status = 0x1; 262582263d52Syt abort_tags = 0x1; 262682263d52Syt goto out; 262782263d52Syt } else { 262882263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 262982263d52Syt "ahci_reject_all_abort_pkts return directly " 263082263d52Syt "port %d no needs to reject any outstanding " 263182263d52Syt "commands", port); 263282263d52Syt return; 263382263d52Syt } 263482263d52Syt } 26352fcbc377Syt 263682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 263782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 263882263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 263982263d52Syt abort_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2640a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 264182263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 264282263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 264382263d52Syt abort_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 264482263d52Syt } 264582263d52Syt 264682263d52Syt out: 264768d33a25Syt /* No need to do mop when there is no outstanding commands */ 264868d33a25Syt if (slot_status != 0) { 264968d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 265068d33a25Syt ahci_portp->ahciport_mop_in_progress++; 26512fcbc377Syt 265268d33a25Syt ahci_mop_commands(ahci_ctlp, 265368d33a25Syt ahci_portp, 265468d33a25Syt slot_status, 265568d33a25Syt 0, /* failed tags */ 265668d33a25Syt 0, /* timeout tags */ 265768d33a25Syt abort_tags, /* aborting tags */ 265868d33a25Syt 0); /* reset tags */ 265968d33a25Syt } 266068d33a25Syt } 266168d33a25Syt 266268d33a25Syt #if defined(__lock_lint) 266368d33a25Syt static int 266468d33a25Syt ahci_selftest(dev_info_t *dip, sata_device_t *device) 266568d33a25Syt { 26662fcbc377Syt return (SATA_SUCCESS); 26672fcbc377Syt } 26682fcbc377Syt #endif 26692fcbc377Syt 26702fcbc377Syt /* 267168d33a25Syt * Allocate the ports structure, only called by ahci_attach 267268d33a25Syt */ 267368d33a25Syt static int 267468d33a25Syt ahci_alloc_ports_state(ahci_ctl_t *ahci_ctlp) 267568d33a25Syt { 2676f68cbde1Syt int port, cport = 0; 267768d33a25Syt 267868d33a25Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 267968d33a25Syt "ahci_alloc_ports_state enter"); 268068d33a25Syt 268168d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 268268d33a25Syt 268368d33a25Syt /* Allocate structures only for the implemented ports */ 2684f68cbde1Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 268568d33a25Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 268668d33a25Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 268768d33a25Syt "hba port %d not implemented", port); 268868d33a25Syt continue; 268968d33a25Syt } 269068d33a25Syt 269168d33a25Syt ahci_ctlp->ahcictl_cport_to_port[cport] = (uint8_t)port; 2692f68cbde1Syt ahci_ctlp->ahcictl_port_to_cport[port] = 2693f68cbde1Syt (uint8_t)cport++; 269468d33a25Syt 269568d33a25Syt if (ahci_alloc_port_state(ahci_ctlp, port) != AHCI_SUCCESS) { 269668d33a25Syt goto err_out; 269768d33a25Syt } 269868d33a25Syt } 269968d33a25Syt 270068d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 270168d33a25Syt return (AHCI_SUCCESS); 270268d33a25Syt 270368d33a25Syt err_out: 270468d33a25Syt for (port--; port >= 0; port--) { 270568d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 270668d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 270768d33a25Syt } 270868d33a25Syt } 270968d33a25Syt 271068d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 271168d33a25Syt return (AHCI_FAILURE); 271268d33a25Syt } 271368d33a25Syt 271468d33a25Syt /* 271568d33a25Syt * Reverse of ahci_alloc_ports_state(), only called by ahci_detach 271668d33a25Syt */ 271768d33a25Syt static void 271868d33a25Syt ahci_dealloc_ports_state(ahci_ctl_t *ahci_ctlp) 271968d33a25Syt { 272068d33a25Syt int port; 272168d33a25Syt 272268d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 272368d33a25Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 272468d33a25Syt /* if this port is implemented by the HBA */ 272568d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) 272668d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 272768d33a25Syt } 272868d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 272968d33a25Syt } 273068d33a25Syt 2731f8a673adSying tian - Beijing China /* 2732f8a673adSying tian - Beijing China * Drain the taskq. 2733f8a673adSying tian - Beijing China */ 2734f8a673adSying tian - Beijing China static void 2735f8a673adSying tian - Beijing China ahci_drain_ports_taskq(ahci_ctl_t *ahci_ctlp) 2736f8a673adSying tian - Beijing China { 2737f8a673adSying tian - Beijing China ahci_port_t *ahci_portp; 2738f8a673adSying tian - Beijing China int port; 2739f8a673adSying tian - Beijing China 2740f8a673adSying tian - Beijing China for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 2741f8a673adSying tian - Beijing China if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 2742f8a673adSying tian - Beijing China continue; 2743f8a673adSying tian - Beijing China } 2744f8a673adSying tian - Beijing China 2745f8a673adSying tian - Beijing China ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2746f8a673adSying tian - Beijing China 2747f8a673adSying tian - Beijing China mutex_enter(&ahci_portp->ahciport_mutex); 2748f8a673adSying tian - Beijing China ddi_taskq_wait(ahci_portp->ahciport_event_taskq); 2749f8a673adSying tian - Beijing China mutex_exit(&ahci_portp->ahciport_mutex); 2750f8a673adSying tian - Beijing China } 2751f8a673adSying tian - Beijing China } 2752f8a673adSying tian - Beijing China 275368d33a25Syt /* 275413bcbb7aSyt * Initialize the controller and all ports. And then try to start the ports 275513bcbb7aSyt * if there are devices attached. 27562fcbc377Syt * 27572fcbc377Syt * This routine can be called from three seperate cases: DDI_ATTACH, 27582fcbc377Syt * PM_LEVEL_D0 and DDI_RESUME. The DDI_ATTACH case is different from 275968d33a25Syt * other two cases; device signature probing are attempted only during 276068d33a25Syt * DDI_ATTACH case. 27612fcbc377Syt * 27622fcbc377Syt * WARNING!!! Disable the whole controller's interrupts before calling and 27632fcbc377Syt * the interrupts will be enabled upon successfully return. 27642fcbc377Syt */ 27652fcbc377Syt static int 27662fcbc377Syt ahci_initialize_controller(ahci_ctl_t *ahci_ctlp) 27672fcbc377Syt { 27682fcbc377Syt ahci_port_t *ahci_portp; 27692fcbc377Syt uint32_t ghc_control; 277068d33a25Syt int port; 27712fcbc377Syt 27722fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 27732fcbc377Syt "ahci_initialize_controller enter"); 27742fcbc377Syt 27752fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 27762fcbc377Syt 27772fcbc377Syt /* 27782fcbc377Syt * Indicate that system software is AHCI aware by setting 27792fcbc377Syt * GHC.AE to 1 27802fcbc377Syt */ 27812fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 27822fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 27832fcbc377Syt 27842fcbc377Syt ghc_control |= AHCI_HBA_GHC_AE; 27852fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 27862fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), 27872fcbc377Syt ghc_control); 27882fcbc377Syt 278982263d52Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 279082263d52Syt 27912fcbc377Syt /* Initialize the implemented ports and structures */ 27922fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 27932fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 27942fcbc377Syt continue; 27952fcbc377Syt } 27962fcbc377Syt 27972fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 27982fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 27992fcbc377Syt 28002fcbc377Syt /* 28012fcbc377Syt * Ensure that the controller is not in the running state 28022fcbc377Syt * by checking every implemented port's PxCMD register 28032fcbc377Syt */ 28042fcbc377Syt if (ahci_initialize_port(ahci_ctlp, ahci_portp, port) 28052fcbc377Syt != AHCI_SUCCESS) { 28062fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 28072fcbc377Syt "ahci_initialize_controller: failed to " 28082fcbc377Syt "initialize port %d", port); 28092fcbc377Syt /* 28102fcbc377Syt * Set the port state to SATA_PSTATE_FAILED if 28112fcbc377Syt * failed to initialize it. 28122fcbc377Syt */ 281368d33a25Syt ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 28142fcbc377Syt } 28152fcbc377Syt 28162fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 28172fcbc377Syt } 28182fcbc377Syt 28192fcbc377Syt /* Enable the whole controller interrupts */ 282082263d52Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 28212fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 28222fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 28232fcbc377Syt 28242fcbc377Syt return (AHCI_SUCCESS); 28252fcbc377Syt } 28262fcbc377Syt 28272fcbc377Syt /* 282868d33a25Syt * Reverse of ahci_initialize_controller() 28292fcbc377Syt * 283013bcbb7aSyt * We only need to stop the ports and disable the interrupt. 28312fcbc377Syt */ 28322fcbc377Syt static void 283368d33a25Syt ahci_uninitialize_controller(ahci_ctl_t *ahci_ctlp) 28342fcbc377Syt { 283513bcbb7aSyt ahci_port_t *ahci_portp; 283613bcbb7aSyt int port; 283713bcbb7aSyt 28382fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 283968d33a25Syt "ahci_uninitialize_controller enter"); 28402fcbc377Syt 28412fcbc377Syt /* disable all the interrupts. */ 284213bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 28432fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 284413bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 284513bcbb7aSyt 284613bcbb7aSyt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 284713bcbb7aSyt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 284813bcbb7aSyt continue; 284913bcbb7aSyt } 285013bcbb7aSyt 285113bcbb7aSyt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 285213bcbb7aSyt 285313bcbb7aSyt /* Stop the port by clearing PxCMD.ST */ 285413bcbb7aSyt mutex_enter(&ahci_portp->ahciport_mutex); 285513bcbb7aSyt 285613bcbb7aSyt /* 285713bcbb7aSyt * Here we must disable the port interrupt because 285813bcbb7aSyt * ahci_disable_all_intrs only clear GHC.IE, and IS 285913bcbb7aSyt * register will be still set if PxIE is enabled. 286013bcbb7aSyt * When ahci shares one IRQ with other drivers, the 286113bcbb7aSyt * intr handler may claim the intr mistakenly. 286213bcbb7aSyt */ 2863689d74b0Syt ahci_disable_port_intrs(ahci_ctlp, port); 286413bcbb7aSyt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 286513bcbb7aSyt ahci_portp, port); 286613bcbb7aSyt mutex_exit(&ahci_portp->ahciport_mutex); 286713bcbb7aSyt } 28682fcbc377Syt } 28692fcbc377Syt 28702fcbc377Syt /* 28712fcbc377Syt * The routine is to initialize the port. First put the port in NOTRunning 28722fcbc377Syt * state, then enable port interrupt and clear Serror register. And under 28732fcbc377Syt * AHCI_ATTACH case, find device signature and then try to start the port. 28742fcbc377Syt * 287582263d52Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 28762fcbc377Syt */ 28772fcbc377Syt static int 28782fcbc377Syt ahci_initialize_port(ahci_ctl_t *ahci_ctlp, 28792fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 28802fcbc377Syt { 28810a4c4cecSXiao-Yu Zhang uint32_t port_sstatus, port_task_file, port_cmd_status; 28820a4c4cecSXiao-Yu Zhang int ret; 28832fcbc377Syt 28842fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 28852fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 28862fcbc377Syt 28872fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 28882fcbc377Syt "ahci_initialize_port: port %d " 28892fcbc377Syt "port_cmd_status = 0x%x", port, port_cmd_status); 28902fcbc377Syt /* 28912fcbc377Syt * Check whether the port is in NotRunning state, if not, 28922fcbc377Syt * put the port in NotRunning state 28932fcbc377Syt */ 28940a4c4cecSXiao-Yu Zhang if (port_cmd_status & 28952fcbc377Syt (AHCI_CMD_STATUS_ST | 28962fcbc377Syt AHCI_CMD_STATUS_CR | 28972fcbc377Syt AHCI_CMD_STATUS_FRE | 28980a4c4cecSXiao-Yu Zhang AHCI_CMD_STATUS_FR)) { 28990a4c4cecSXiao-Yu Zhang (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 29000a4c4cecSXiao-Yu Zhang ahci_portp, port); 29012fcbc377Syt } 29022fcbc377Syt 29030a4c4cecSXiao-Yu Zhang /* Device is unknown at first */ 29040a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 29052fcbc377Syt 2906e2d9bfa6Sying tian - Beijing China /* Disable the interface power management */ 2907e2d9bfa6Sying tian - Beijing China ahci_disable_interface_pm(ahci_ctlp, port); 2908e2d9bfa6Sying tian - Beijing China 29090a4c4cecSXiao-Yu Zhang port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 29100a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 29110a4c4cecSXiao-Yu Zhang port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 29120a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 29130a4c4cecSXiao-Yu Zhang 29140a4c4cecSXiao-Yu Zhang /* Check physcial link status */ 29150a4c4cecSXiao-Yu Zhang if (SSTATUS_GET_IPM(port_sstatus) == SSTATUS_IPM_NODEV_NOPHYCOM || 29160a4c4cecSXiao-Yu Zhang SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_NOPHYCOM || 29170a4c4cecSXiao-Yu Zhang 29180a4c4cecSXiao-Yu Zhang /* Check interface status */ 29190a4c4cecSXiao-Yu Zhang port_task_file & AHCI_TFD_STS_BSY || 29200a4c4cecSXiao-Yu Zhang port_task_file & AHCI_TFD_STS_DRQ) { 29210a4c4cecSXiao-Yu Zhang 29220a4c4cecSXiao-Yu Zhang /* Incorrect task file state, we need to reset port */ 29230a4c4cecSXiao-Yu Zhang ret = ahci_port_reset(ahci_ctlp, ahci_portp, port); 29240a4c4cecSXiao-Yu Zhang 29250a4c4cecSXiao-Yu Zhang /* Does port reset succeed on HBA port? */ 29260a4c4cecSXiao-Yu Zhang if (ret != AHCI_SUCCESS) { 29270a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ERRS, ahci_ctlp, 29280a4c4cecSXiao-Yu Zhang "ahci_initialize_port:" 29290a4c4cecSXiao-Yu Zhang "port reset faild at port %d", port); 29300a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 29310a4c4cecSXiao-Yu Zhang } 29320a4c4cecSXiao-Yu Zhang 29330a4c4cecSXiao-Yu Zhang /* Is port failed? */ 29340a4c4cecSXiao-Yu Zhang if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 29350a4c4cecSXiao-Yu Zhang AHCIDBG2(AHCIDBG_INIT|AHCIDBG_ERRS, ahci_ctlp, 29360a4c4cecSXiao-Yu Zhang "ahci_initialize_port: port %d state 0x%x", 29370a4c4cecSXiao-Yu Zhang port, ahci_portp->ahciport_port_state); 29380a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 29390a4c4cecSXiao-Yu Zhang } 29400a4c4cecSXiao-Yu Zhang } 2941*259105bcSying tian - Beijing China 29420a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_STATE_READY; 29430a4c4cecSXiao-Yu Zhang 29440a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "port %d is ready now.", port); 29452fcbc377Syt 29462fcbc377Syt /* 294768d33a25Syt * At the time being, only probe ports/devices and get the types of 294813bcbb7aSyt * attached devices during DDI_ATTACH. In fact, the device can be 294913bcbb7aSyt * changed during power state changes, but at the time being, we 295013bcbb7aSyt * don't support the situation. 29512fcbc377Syt */ 29522fcbc377Syt if (ahci_ctlp->ahcictl_flags & AHCI_ATTACH) { 29530a4c4cecSXiao-Yu Zhang /* 2954*259105bcSying tian - Beijing China * Try to get the device signature if the port is 2955*259105bcSying tian - Beijing China * not empty. 29560a4c4cecSXiao-Yu Zhang */ 2957*259105bcSying tian - Beijing China if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) 2958*259105bcSying tian - Beijing China ahci_find_dev_signature(ahci_ctlp, ahci_portp, port); 295913bcbb7aSyt } else { 29602fcbc377Syt 296113bcbb7aSyt /* 296213bcbb7aSyt * During the resume, we need to set the PxCLB, PxCLBU, PxFB 296313bcbb7aSyt * and PxFBU registers in case these registers were cleared 296413bcbb7aSyt * during the suspend. 296513bcbb7aSyt */ 296613bcbb7aSyt AHCIDBG1(AHCIDBG_PM, ahci_ctlp, 296713bcbb7aSyt "ahci_initialize_port: port %d " 296813bcbb7aSyt "reset the port during resume", port); 296913bcbb7aSyt (void) ahci_port_reset(ahci_ctlp, ahci_portp, port); 29702fcbc377Syt 297113bcbb7aSyt AHCIDBG1(AHCIDBG_PM, ahci_ctlp, 297213bcbb7aSyt "ahci_initialize_port: port %d " 297313bcbb7aSyt "set PxCLB, PxCLBU, PxFB and PxFBU " 297413bcbb7aSyt "during resume", port); 297513bcbb7aSyt 297613bcbb7aSyt /* Config Port Received FIS Base Address */ 297713bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 297813bcbb7aSyt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 297913bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 298013bcbb7aSyt 298113bcbb7aSyt /* Config Port Command List Base Address */ 298213bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 298313bcbb7aSyt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 298413bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 298513bcbb7aSyt } 298613bcbb7aSyt 298713bcbb7aSyt /* Return directly if no device connected */ 298813bcbb7aSyt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 298913bcbb7aSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 299013bcbb7aSyt "No device connected to port %d", port); 299113bcbb7aSyt goto out; 299213bcbb7aSyt } 299313bcbb7aSyt 299413bcbb7aSyt /* Try to start the port */ 299513bcbb7aSyt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 299613bcbb7aSyt != AHCI_SUCCESS) { 299713bcbb7aSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 299813bcbb7aSyt "failed to start port %d", port); 299913bcbb7aSyt return (AHCI_FAILURE); 30002fcbc377Syt } 300168d33a25Syt out: 300268d33a25Syt /* Enable port interrupts */ 3003689d74b0Syt ahci_enable_port_intrs(ahci_ctlp, port); 30042fcbc377Syt 30052fcbc377Syt return (AHCI_SUCCESS); 30062fcbc377Syt } 30072fcbc377Syt 300813bcbb7aSyt /* 3009*259105bcSying tian - Beijing China * Handle hardware defect, and check the capabilities. For example, 3010*259105bcSying tian - Beijing China * power management capabilty and MSI capability. 301113bcbb7aSyt */ 301213bcbb7aSyt static int 301313bcbb7aSyt ahci_config_space_init(ahci_ctl_t *ahci_ctlp) 301413bcbb7aSyt { 3015a9440e8dSyt ushort_t venid, devid; 3016689d74b0Syt ushort_t caps_ptr, cap_count, cap; 3017689d74b0Syt #if AHCI_DEBUG 3018689d74b0Syt ushort_t pmcap, pmcsr; 30192c742e1fSying tian - Beijing China ushort_t msimc; 3020689d74b0Syt #endif 302113bcbb7aSyt uint8_t revision; 302213bcbb7aSyt 302313bcbb7aSyt venid = pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 302413bcbb7aSyt PCI_CONF_VENID); 302513bcbb7aSyt 3026a9440e8dSyt devid = pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 3027a9440e8dSyt PCI_CONF_DEVID); 3028a9440e8dSyt 302913bcbb7aSyt /* 303013bcbb7aSyt * Modify dma_attr_align of ahcictl_buffer_dma_attr. For VT8251, those 303113bcbb7aSyt * controllers with 0x00 revision id work on 4-byte aligned buffer, 303213bcbb7aSyt * which is a bug and was fixed after 0x00 revision id controllers. 303313bcbb7aSyt * 303413bcbb7aSyt * Moreover, VT8251 cannot use multiple command slots in the command 303513bcbb7aSyt * list for non-queued commands because the previous register content 303613bcbb7aSyt * of PxCI can be re-written in the register write, so a flag will be 303713bcbb7aSyt * set to record this defect - AHCI_CAP_NO_MCMDLIST_NONQUEUE. 303813bcbb7aSyt */ 303913bcbb7aSyt if (venid == VIA_VENID) { 304013bcbb7aSyt revision = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 304113bcbb7aSyt PCI_CONF_REVID); 304213bcbb7aSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 304313bcbb7aSyt "revision id = 0x%x", revision); 304413bcbb7aSyt if (revision == 0x00) { 304513bcbb7aSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_align = 0x4; 304613bcbb7aSyt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 304713bcbb7aSyt "change ddi_attr_align to 0x4"); 304813bcbb7aSyt } 304913bcbb7aSyt 305013bcbb7aSyt ahci_ctlp->ahcictl_cap = AHCI_CAP_NO_MCMDLIST_NONQUEUE; 305113bcbb7aSyt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 305213bcbb7aSyt "VT8251 cannot use multiple command lists for " 305313bcbb7aSyt "non-queued commands"); 305413bcbb7aSyt } 305513bcbb7aSyt 3056a9440e8dSyt /* 3057*259105bcSying tian - Beijing China * AMD/ATI SB600 (1002,4380) AHCI chipset doesn't support 64-bit DMA 3058*259105bcSying tian - Beijing China * addressing for both data buffer and communication memory descriptors 3059*259105bcSying tian - Beijing China * though S64A bit of CAP register declares the support. 3060*259105bcSying tian - Beijing China */ 3061*259105bcSying tian - Beijing China if (venid == 0x1002 && devid == 0x4380) { 3062*259105bcSying tian - Beijing China AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 3063*259105bcSying tian - Beijing China "ATI SB600 cannot do 64-bit DMA for both data buffer " 3064*259105bcSying tian - Beijing China "and communication memory descriptors though CAP indicates " 3065*259105bcSying tian - Beijing China "support, so force it to use 32-bit DMA"); 3066*259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_BUF_32BIT_DMA; 3067*259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_COMMU_32BIT_DMA; 3068*259105bcSying tian - Beijing China } 3069*259105bcSying tian - Beijing China 3070*259105bcSying tian - Beijing China /* 3071*259105bcSying tian - Beijing China * AMD/ATI SB700/750 (1002,4391) AHCI chipset doesn't support 64-bit 3072*259105bcSying tian - Beijing China * DMA addressing for communication memory descriptors though S64A bit 3073*259105bcSying tian - Beijing China * of CAP register declares the support. However, it does support 3074*259105bcSying tian - Beijing China * 64-bit DMA for data buffer. 3075a9440e8dSyt */ 3076*259105bcSying tian - Beijing China if (venid == 0x1002 && devid == 0x4391) { 3077a9440e8dSyt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 3078*259105bcSying tian - Beijing China "ATI SB700/750 cannot do 64-bit DMA for communication " 3079*259105bcSying tian - Beijing China "memory descriptors though CAP indicates support, " 3080*259105bcSying tian - Beijing China "so force it to use 32-bit DMA"); 3081*259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_COMMU_32BIT_DMA; 3082a9440e8dSyt } 3083a9440e8dSyt 3084*259105bcSying tian - Beijing China /* 3085*259105bcSying tian - Beijing China * nVidia MCP78 AHCI controller (pci10de,0ad4) will be forced to use 3086*259105bcSying tian - Beijing China * Fixed interrupt until the known CR 6766472 (MSIs do not function 3087*259105bcSying tian - Beijing China * on most Nvidia boards) is fixed. 3088*259105bcSying tian - Beijing China */ 3089c6a9dbb6SXiao-Yu Zhang if (venid == 0x10de && devid == 0x0ad4) { 3090c6a9dbb6SXiao-Yu Zhang AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 3091*259105bcSying tian - Beijing China "Force nVidia MCP78 AHCI controller to use " 3092*259105bcSying tian - Beijing China "fixed interrupts"); 3093c6a9dbb6SXiao-Yu Zhang ahci_msi_enabled = B_FALSE; 3094c6a9dbb6SXiao-Yu Zhang } 3095c6a9dbb6SXiao-Yu Zhang 309613bcbb7aSyt /* 309713bcbb7aSyt * Check if capabilities list is supported and if so, 309813bcbb7aSyt * get initial capabilities pointer and clear bits 0,1. 309913bcbb7aSyt */ 310013bcbb7aSyt if (pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 310113bcbb7aSyt PCI_CONF_STAT) & PCI_STAT_CAP) { 310213bcbb7aSyt caps_ptr = P2ALIGN(pci_config_get8( 310313bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 310413bcbb7aSyt PCI_CONF_CAP_PTR), 4); 310513bcbb7aSyt } else { 310613bcbb7aSyt caps_ptr = PCI_CAP_NEXT_PTR_NULL; 310713bcbb7aSyt } 310813bcbb7aSyt 310913bcbb7aSyt /* 311013bcbb7aSyt * Walk capabilities if supported. 311113bcbb7aSyt */ 311213bcbb7aSyt for (cap_count = 0; caps_ptr != PCI_CAP_NEXT_PTR_NULL; ) { 311313bcbb7aSyt 311413bcbb7aSyt /* 311513bcbb7aSyt * Check that we haven't exceeded the maximum number of 311613bcbb7aSyt * capabilities and that the pointer is in a valid range. 311713bcbb7aSyt */ 311813bcbb7aSyt if (++cap_count > PCI_CAP_MAX_PTR) { 311913bcbb7aSyt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 312013bcbb7aSyt "too many device capabilities"); 312113bcbb7aSyt return (AHCI_FAILURE); 312213bcbb7aSyt } 312313bcbb7aSyt if (caps_ptr < PCI_CAP_PTR_OFF) { 312413bcbb7aSyt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 312513bcbb7aSyt "capabilities pointer 0x%x out of range", 312613bcbb7aSyt caps_ptr); 312713bcbb7aSyt return (AHCI_FAILURE); 312813bcbb7aSyt } 312913bcbb7aSyt 313013bcbb7aSyt /* 313113bcbb7aSyt * Get next capability and check that it is valid. 313213bcbb7aSyt * For now, we only support power management. 313313bcbb7aSyt */ 313413bcbb7aSyt cap = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 313513bcbb7aSyt caps_ptr); 313613bcbb7aSyt switch (cap) { 313713bcbb7aSyt case PCI_CAP_ID_PM: 313813bcbb7aSyt 313913bcbb7aSyt /* power management supported */ 314013bcbb7aSyt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PM; 314113bcbb7aSyt 314213bcbb7aSyt /* Save PMCSR offset */ 314313bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset = caps_ptr + PCI_PMCSR; 314413bcbb7aSyt 3145689d74b0Syt #if AHCI_DEBUG 314613bcbb7aSyt pmcap = pci_config_get16( 314713bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 314813bcbb7aSyt caps_ptr + PCI_PMCAP); 314913bcbb7aSyt pmcsr = pci_config_get16( 315013bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 315113bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset); 315213bcbb7aSyt AHCIDBG2(AHCIDBG_PM, ahci_ctlp, 315313bcbb7aSyt "Power Management capability found PCI_PMCAP " 315413bcbb7aSyt "= 0x%x PCI_PMCSR = 0x%x", pmcap, pmcsr); 315513bcbb7aSyt if ((pmcap & 0x3) == 0x3) 315613bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 315713bcbb7aSyt "PCI Power Management Interface " 315813bcbb7aSyt "spec 1.2 compliant"); 315913bcbb7aSyt #endif 316013bcbb7aSyt break; 316113bcbb7aSyt 316213bcbb7aSyt case PCI_CAP_ID_MSI: 31632c742e1fSying tian - Beijing China #if AHCI_DEBUG 31642c742e1fSying tian - Beijing China msimc = pci_config_get16( 31652c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_pci_conf_handle, 31662c742e1fSying tian - Beijing China caps_ptr + PCI_MSI_CTRL); 31672c742e1fSying tian - Beijing China AHCIDBG1(AHCIDBG_MSI, ahci_ctlp, 31682c742e1fSying tian - Beijing China "Message Signaled Interrupt capability found " 31692c742e1fSying tian - Beijing China "MSICAP_MC.MMC = 0x%x", (msimc & 0xe) >> 1); 31702c742e1fSying tian - Beijing China #endif 31712c742e1fSying tian - Beijing China AHCIDBG0(AHCIDBG_MSI, ahci_ctlp, 31722c742e1fSying tian - Beijing China "MSI capability found"); 317313bcbb7aSyt break; 317413bcbb7aSyt 317513bcbb7aSyt case PCI_CAP_ID_PCIX: 317613bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 317713bcbb7aSyt "PCI-X capability found"); 317813bcbb7aSyt break; 317913bcbb7aSyt 318013bcbb7aSyt case PCI_CAP_ID_PCI_E: 318113bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 318213bcbb7aSyt "PCI Express capability found"); 318313bcbb7aSyt break; 318413bcbb7aSyt 318513bcbb7aSyt case PCI_CAP_ID_MSI_X: 318613bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 318713bcbb7aSyt "MSI-X capability found"); 318813bcbb7aSyt break; 318913bcbb7aSyt 319013bcbb7aSyt case PCI_CAP_ID_SATA: 319113bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 319213bcbb7aSyt "SATA capability found"); 319313bcbb7aSyt break; 319413bcbb7aSyt 3195a9440e8dSyt case PCI_CAP_ID_VS: 3196a9440e8dSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 3197a9440e8dSyt "Vendor Specific capability found"); 3198a9440e8dSyt break; 3199a9440e8dSyt 320013bcbb7aSyt default: 320113bcbb7aSyt AHCIDBG1(AHCIDBG_PM, ahci_ctlp, 320213bcbb7aSyt "unrecognized capability 0x%x", cap); 320313bcbb7aSyt break; 320413bcbb7aSyt } 320513bcbb7aSyt 320613bcbb7aSyt /* 320713bcbb7aSyt * Get next capabilities pointer and clear bits 0,1. 320813bcbb7aSyt */ 320913bcbb7aSyt caps_ptr = P2ALIGN(pci_config_get8( 321013bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 321113bcbb7aSyt (caps_ptr + PCI_CAP_NEXT_PTR)), 4); 321213bcbb7aSyt } 321313bcbb7aSyt 321413bcbb7aSyt return (AHCI_SUCCESS); 321513bcbb7aSyt } 321613bcbb7aSyt 32172fcbc377Syt /* 32182fcbc377Syt * AHCI device reset ...; a single device on one of the ports is reset, 32192fcbc377Syt * but the HBA and physical communication remain intact. This is the 32202fcbc377Syt * least intrusive. 32212fcbc377Syt * 32222fcbc377Syt * When issuing a software reset sequence, there should not be other 32232fcbc377Syt * commands in the command list, so we will first clear and then re-set 32242fcbc377Syt * PxCMD.ST to clear PxCI. And before issuing the software reset, 32252fcbc377Syt * the port must be idle and PxTFD.STS.BSY and PxTFD.STS.DRQ must be 3226b2e3645aSying tian - Beijing China * cleared unless command list override (PxCMD.CLO) is supported. 32272fcbc377Syt * 32282fcbc377Syt * WARNING!!! ahciport_mutex should be acquired and PxCMD.FRE should be 32292fcbc377Syt * set before the function is called. 32302fcbc377Syt */ 32312fcbc377Syt static int 32322fcbc377Syt ahci_software_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 32332fcbc377Syt uint8_t port) 32342fcbc377Syt { 32352fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 32362fcbc377Syt ahci_cmd_table_t *cmd_table; 32372fcbc377Syt ahci_cmd_header_t *cmd_header; 323868d33a25Syt uint32_t port_cmd_status, port_cmd_issue, port_task_file; 32392fcbc377Syt int slot, loop_count; 32400a4c4cecSXiao-Yu Zhang int rval = AHCI_FAILURE; 32412fcbc377Syt 32422fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 32432fcbc377Syt "Port %d device resetting", port); 32442fcbc377Syt 32450a4c4cecSXiao-Yu Zhang /* First clear PxCMD.ST (AHCI v1.2 10.4.1) */ 32460a4c4cecSXiao-Yu Zhang if (ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, 32470a4c4cecSXiao-Yu Zhang port) != AHCI_SUCCESS) { 32480a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 32490a4c4cecSXiao-Yu Zhang "ahci_software_reset: cannot stop HBA port %d.", port); 32500a4c4cecSXiao-Yu Zhang goto out; 32510a4c4cecSXiao-Yu Zhang } 32522fcbc377Syt 32532fcbc377Syt /* Check PxTFD.STS.BSY and PxTFD.STS.DRQ */ 32542fcbc377Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 32552fcbc377Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 32562fcbc377Syt 32572fcbc377Syt if (port_task_file & AHCI_TFD_STS_BSY || 32582fcbc377Syt port_task_file & AHCI_TFD_STS_DRQ) { 32590a4c4cecSXiao-Yu Zhang if (!(ahci_ctlp->ahcictl_cap & AHCI_CAP_SCLO)) { 32600a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 32610a4c4cecSXiao-Yu Zhang "PxTFD.STS.BSY/DRQ is set (PxTFD=0x%x), " 32620a4c4cecSXiao-Yu Zhang "cannot issue a software reset.", port_task_file); 32630a4c4cecSXiao-Yu Zhang goto out; 32640a4c4cecSXiao-Yu Zhang } 32650a4c4cecSXiao-Yu Zhang 32660a4c4cecSXiao-Yu Zhang /* 32670a4c4cecSXiao-Yu Zhang * If HBA Support CLO, as Command List Override (CAP.SCLO is 32680a4c4cecSXiao-Yu Zhang * set), PxCMD.CLO bit should be set before set PxCMD.ST, in 32690a4c4cecSXiao-Yu Zhang * order to clear PxTFD.STS.BSY and PxTFD.STS.DRQ. 32700a4c4cecSXiao-Yu Zhang */ 32710a4c4cecSXiao-Yu Zhang AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 32720a4c4cecSXiao-Yu Zhang "PxTFD.STS.BSY/DRQ is set, try SCLO.") 32730a4c4cecSXiao-Yu Zhang 3274b2e3645aSying tian - Beijing China port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 3275b2e3645aSying tian - Beijing China (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 32760a4c4cecSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 32770a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 32780a4c4cecSXiao-Yu Zhang port_cmd_status|AHCI_CMD_STATUS_CLO); 32790a4c4cecSXiao-Yu Zhang 32800a4c4cecSXiao-Yu Zhang /* Waiting till PxCMD.SCLO bit is cleared */ 32810a4c4cecSXiao-Yu Zhang loop_count = 0; 32820a4c4cecSXiao-Yu Zhang do { 32830a4c4cecSXiao-Yu Zhang /* Wait for 10 millisec */ 32840a4c4cecSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 32850a4c4cecSXiao-Yu Zhang 32860a4c4cecSXiao-Yu Zhang /* We are effectively timing out after 1 sec. */ 32870a4c4cecSXiao-Yu Zhang if (loop_count++ > 100) { 32880a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 32890a4c4cecSXiao-Yu Zhang "SCLO time out. port %d is busy.", port); 32900a4c4cecSXiao-Yu Zhang goto out; 32910a4c4cecSXiao-Yu Zhang } 32920a4c4cecSXiao-Yu Zhang 32930a4c4cecSXiao-Yu Zhang port_cmd_status = 32940a4c4cecSXiao-Yu Zhang ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 32950a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 32960a4c4cecSXiao-Yu Zhang } while (port_cmd_status & AHCI_CMD_STATUS_CLO); 32970a4c4cecSXiao-Yu Zhang 32980a4c4cecSXiao-Yu Zhang /* Re-check */ 32990a4c4cecSXiao-Yu Zhang port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33000a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 33010a4c4cecSXiao-Yu Zhang if (port_task_file & AHCI_TFD_STS_BSY || 33020a4c4cecSXiao-Yu Zhang port_task_file & AHCI_TFD_STS_DRQ) { 33030a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 33040a4c4cecSXiao-Yu Zhang "SCLO cannot clear PxTFD.STS.BSY/DRQ (PxTFD=0x%x)", 33050a4c4cecSXiao-Yu Zhang port_task_file); 33060a4c4cecSXiao-Yu Zhang goto out; 33072fcbc377Syt } 33082fcbc377Syt } 33092fcbc377Syt 33100a4c4cecSXiao-Yu Zhang /* Then start port */ 33110a4c4cecSXiao-Yu Zhang if (ahci_start_port(ahci_ctlp, ahci_portp, port) 33120a4c4cecSXiao-Yu Zhang != AHCI_SUCCESS) { 33130a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 33140a4c4cecSXiao-Yu Zhang "ahci_software_reset: cannot start AHCI port %d.", port); 33150a4c4cecSXiao-Yu Zhang goto out; 33162fcbc377Syt } 33172fcbc377Syt 33180a4c4cecSXiao-Yu Zhang /* 33190a4c4cecSXiao-Yu Zhang * When ahci_port.ahciport_mop_in_progress is set, A non-zero 33200a4c4cecSXiao-Yu Zhang * ahci_port.ahciport_pending_ncq_tags may fail 33210a4c4cecSXiao-Yu Zhang * ahci_claim_free_slot(). Actually according to spec, by clearing 33220a4c4cecSXiao-Yu Zhang * PxCMD.ST there is no command outstanding while executing software 33230a4c4cecSXiao-Yu Zhang * reseting. Hence we directly use slot 0 instead of 33240a4c4cecSXiao-Yu Zhang * ahci_claim_free_slot(). 33250a4c4cecSXiao-Yu Zhang */ 33260a4c4cecSXiao-Yu Zhang slot = 0; 33270a4c4cecSXiao-Yu Zhang 3328b2e3645aSying tian - Beijing China /* Now send the first H2D Register FIS with SRST set to 1 */ 33292fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 33302fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 33312fcbc377Syt 33322fcbc377Syt h2d_register_fisp = 33332fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 33342fcbc377Syt 33352fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 33362fcbc377Syt SET_FIS_DEVCTL(h2d_register_fisp, SATA_DEVCTL_SRST); 33372fcbc377Syt 33382fcbc377Syt /* Set Command Header in Command List */ 33392fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 33402fcbc377Syt BZERO_DESCR_INFO(cmd_header); 33412fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 33422fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 33432fcbc377Syt 33442fcbc377Syt SET_CLEAR_BUSY_UPON_R_OK(cmd_header, 1); 33452fcbc377Syt SET_RESET(cmd_header, 1); 33462fcbc377Syt SET_WRITE(cmd_header, 1); 33472fcbc377Syt 33482fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 33492fcbc377Syt 0, 33502fcbc377Syt ahci_cmd_table_size, 33512fcbc377Syt DDI_DMA_SYNC_FORDEV); 33522fcbc377Syt 33532fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 33542fcbc377Syt slot * sizeof (ahci_cmd_header_t), 33552fcbc377Syt sizeof (ahci_cmd_header_t), 33562fcbc377Syt DDI_DMA_SYNC_FORDEV); 33572fcbc377Syt 33582fcbc377Syt /* Indicate to the HBA that a command is active. */ 33592fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33602fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 33612fcbc377Syt (0x1 << slot)); 33622fcbc377Syt 33632fcbc377Syt loop_count = 0; 33642fcbc377Syt 33652fcbc377Syt /* Loop till the first command is finished */ 33662fcbc377Syt do { 33672fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33682fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 33692fcbc377Syt 3370b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 33712fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 3372*259105bcSying tian - Beijing China AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 3373*259105bcSying tian - Beijing China "the first SRST FIS is timed out, " 3374*259105bcSying tian - Beijing China "loop_count = %d", loop_count); 33750a4c4cecSXiao-Yu Zhang goto out; 33762fcbc377Syt } 3377*259105bcSying tian - Beijing China 33782fcbc377Syt /* Wait for 10 millisec */ 33792fcbc377Syt delay(AHCI_10MS_TICKS); 3380*259105bcSying tian - Beijing China } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 33812fcbc377Syt 33822fcbc377Syt AHCIDBG3(AHCIDBG_POLL_LOOP, ahci_ctlp, 33832fcbc377Syt "ahci_software_reset: 1st loop count: %d, " 33842fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 33852fcbc377Syt loop_count, port_cmd_issue, slot); 33862fcbc377Syt 3387a419422eSXiao-Yu Zhang /* According to ATA spec, we need wait at least 5 microsecs here. */ 3388a419422eSXiao-Yu Zhang drv_usecwait(AHCI_1MS_USECS); 3389a419422eSXiao-Yu Zhang 3390b2e3645aSying tian - Beijing China /* Now send the second H2D Register FIS with SRST cleard to zero */ 33912fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 33922fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 33932fcbc377Syt 33942fcbc377Syt h2d_register_fisp = 33952fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 33962fcbc377Syt 33972fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 33982fcbc377Syt 33992fcbc377Syt /* Set Command Header in Command List */ 34002fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 34012fcbc377Syt BZERO_DESCR_INFO(cmd_header); 34022fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 34032fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 34042fcbc377Syt 34052fcbc377Syt SET_WRITE(cmd_header, 1); 34062fcbc377Syt 34072fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 34082fcbc377Syt 0, 34092fcbc377Syt ahci_cmd_table_size, 34102fcbc377Syt DDI_DMA_SYNC_FORDEV); 34112fcbc377Syt 34122fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 34132fcbc377Syt slot * sizeof (ahci_cmd_header_t), 34142fcbc377Syt sizeof (ahci_cmd_header_t), 34152fcbc377Syt DDI_DMA_SYNC_FORDEV); 34162fcbc377Syt 34172fcbc377Syt /* Indicate to the HBA that a command is active. */ 34182fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 34192fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 34202fcbc377Syt (0x1 << slot)); 34212fcbc377Syt 34222fcbc377Syt loop_count = 0; 34232fcbc377Syt 34242fcbc377Syt /* Loop till the second command is finished */ 34252fcbc377Syt do { 34262fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34272fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 34282fcbc377Syt 3429b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 34302fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 3431*259105bcSying tian - Beijing China AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 3432*259105bcSying tian - Beijing China "the second SRST FIS is timed out, " 3433*259105bcSying tian - Beijing China "loop_count = %d", loop_count); 34340a4c4cecSXiao-Yu Zhang goto out; 34352fcbc377Syt } 3436*259105bcSying tian - Beijing China 34372fcbc377Syt /* Wait for 10 millisec */ 34382fcbc377Syt delay(AHCI_10MS_TICKS); 3439*259105bcSying tian - Beijing China } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 34402fcbc377Syt 34412fcbc377Syt AHCIDBG3(AHCIDBG_POLL_LOOP, ahci_ctlp, 34422fcbc377Syt "ahci_software_reset: 2nd loop count: %d, " 34432fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 34442fcbc377Syt loop_count, port_cmd_issue, slot); 34452fcbc377Syt 34462fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, slot); 34472fcbc377Syt 34480a4c4cecSXiao-Yu Zhang rval = AHCI_SUCCESS; 34490a4c4cecSXiao-Yu Zhang out: 34500a4c4cecSXiao-Yu Zhang AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, 34510a4c4cecSXiao-Yu Zhang "ahci_software_reset: %s at port %d", 34520a4c4cecSXiao-Yu Zhang rval == AHCI_SUCCESS ? "succeed" : "failed", 34530a4c4cecSXiao-Yu Zhang port); 34540a4c4cecSXiao-Yu Zhang 34550a4c4cecSXiao-Yu Zhang return (rval); 34562fcbc377Syt } 34572fcbc377Syt 34582fcbc377Syt /* 34592fcbc377Syt * AHCI port reset ...; the physical communication between the HBA and device 34602fcbc377Syt * on a port are disabled. This is more intrusive. 34612fcbc377Syt * 346268d33a25Syt * When an HBA or port reset occurs, Phy communication is going to 34632fcbc377Syt * be re-established with the device through a COMRESET followed by the 34642fcbc377Syt * normal out-of-band communication sequence defined in Serial ATA. AT 34652fcbc377Syt * the end of reset, the device, if working properly, will send a D2H 34662fcbc377Syt * Register FIS, which contains the device signature. When the HBA receives 34672fcbc377Syt * this FIS, it updates PxTFD.STS and PxTFD.ERR register fields, and updates 34682fcbc377Syt * the PxSIG register with the signature. 34692fcbc377Syt * 34702fcbc377Syt * Staggered spin-up is an optional feature in SATA II, and it enables an HBA 34712fcbc377Syt * to individually spin-up attached devices. Please refer to chapter 10.9 of 347268d33a25Syt * AHCI 1.0 spec. 34732fcbc377Syt */ 34742fcbc377Syt /* 347582263d52Syt * WARNING!!! ahciport_mutex should be acquired, and PxCMD.ST should be also 347682263d52Syt * cleared before the function is called. 34772fcbc377Syt */ 34782fcbc377Syt static int 34792fcbc377Syt ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 34802fcbc377Syt { 34812fcbc377Syt uint32_t cap_status, port_cmd_status; 34820a4c4cecSXiao-Yu Zhang uint32_t port_scontrol, port_sstatus, port_serror; 3483689d74b0Syt uint32_t port_intr_status, port_task_file; 34840a4c4cecSXiao-Yu Zhang 34852fcbc377Syt int loop_count; 3486a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 34872fcbc377Syt 34882fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 34892fcbc377Syt "Port %d port resetting...", port); 349068d33a25Syt ahci_portp->ahciport_port_state = 0; 34912fcbc377Syt 34922fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34932fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 34942fcbc377Syt 34952fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34962fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 34972fcbc377Syt 34982fcbc377Syt if (cap_status & AHCI_HBA_CAP_SSS) { 34992fcbc377Syt /* 35002fcbc377Syt * HBA support staggered spin-up, if the port has 35012fcbc377Syt * not spin up yet, then force it to do spin-up 35022fcbc377Syt */ 35032fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) { 35042fcbc377Syt if (!(ahci_portp->ahciport_flags 350568d33a25Syt & AHCI_PORT_FLAG_SPINUP)) { 35062fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 35072fcbc377Syt "Port %d PxCMD.SUD is zero, force " 35082fcbc377Syt "it to do spin-up", port); 35092fcbc377Syt ahci_portp->ahciport_flags |= 351068d33a25Syt AHCI_PORT_FLAG_SPINUP; 35112fcbc377Syt } 35122fcbc377Syt } 35132fcbc377Syt } else { 35142fcbc377Syt /* 35152fcbc377Syt * HBA doesn't support stagger spin-up, force it 35162fcbc377Syt * to do normal COMRESET 35172fcbc377Syt */ 35182fcbc377Syt if (ahci_portp->ahciport_flags & 351968d33a25Syt AHCI_PORT_FLAG_SPINUP) { 35202fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 35212fcbc377Syt "HBA does not support staggered spin-up " 35222fcbc377Syt "force it to do normal COMRESET"); 35232fcbc377Syt ahci_portp->ahciport_flags &= 352468d33a25Syt ~AHCI_PORT_FLAG_SPINUP; 35252fcbc377Syt } 35262fcbc377Syt } 35272fcbc377Syt 352868d33a25Syt if (!(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_SPINUP)) { 35292fcbc377Syt /* Do normal COMRESET */ 35302fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 35312fcbc377Syt "ahci_port_reset: do normal COMRESET", port); 35322fcbc377Syt 353395c11c1fSyt /* 353495c11c1fSyt * According to the spec, SUD bit should be set here, 353595c11c1fSyt * but JMicron JMB363 doesn't follow it, so remove 353695c11c1fSyt * the assertion, and just print a debug message. 353795c11c1fSyt */ 353895c11c1fSyt #if AHCI_DEBUG 353995c11c1fSyt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) 354095c11c1fSyt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 354195c11c1fSyt "port %d SUD bit not set", port) 354295c11c1fSyt #endif 35432fcbc377Syt 35442fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35452fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 354682263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_COMRESET); 35472fcbc377Syt 35482fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35492fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 35502fcbc377Syt port_scontrol); 35512fcbc377Syt 35522fcbc377Syt /* Enable PxCMD.FRE to read device */ 35532fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35542fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 35552fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 35562fcbc377Syt 355768d33a25Syt /* 355868d33a25Syt * Give time for COMRESET to percolate, according to the AHCI 355968d33a25Syt * spec, software shall wait at least 1 millisecond before 356068d33a25Syt * clearing PxSCTL.DET 356168d33a25Syt */ 35622fcbc377Syt delay(AHCI_1MS_TICKS*2); 35632fcbc377Syt 35642fcbc377Syt /* Fetch the SCONTROL again and rewrite the DET part with 0 */ 35652fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35662fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 356782263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 35682fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35692fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 35702fcbc377Syt port_scontrol); 35712fcbc377Syt } else { 35722fcbc377Syt /* Do staggered spin-up */ 35732fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35742fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 357582263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 35762fcbc377Syt 35772fcbc377Syt /* PxSCTL.DET must be 0 */ 35782fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35792fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 35802fcbc377Syt port_scontrol); 35812fcbc377Syt 35822fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_SUD; 35832fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35842fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 35852fcbc377Syt port_cmd_status); 35862fcbc377Syt 35872fcbc377Syt /* 0 -> 1 edge */ 35882fcbc377Syt delay(AHCI_1MS_TICKS*2); 35892fcbc377Syt 35902fcbc377Syt /* Set PxCMD.SUD to 1 */ 35912fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35922fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 35932fcbc377Syt port_cmd_status |= AHCI_CMD_STATUS_SUD; 35942fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35952fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 35962fcbc377Syt port_cmd_status); 35972fcbc377Syt 35982fcbc377Syt /* Enable PxCMD.FRE to read device */ 35992fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 36002fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 36012fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 36022fcbc377Syt } 36032fcbc377Syt 36042fcbc377Syt /* 360568d33a25Syt * The port enters P:StartComm state, and HBA tells link layer to 360668d33a25Syt * start communication, which involves sending COMRESET to device. 360768d33a25Syt * And the HBA resets PxTFD.STS to 7Fh. 360868d33a25Syt * 360968d33a25Syt * When a COMINIT is received from the device, then the port enters 361068d33a25Syt * P:ComInit state. And HBA sets PxTFD.STS to FFh or 80h. HBA sets 361168d33a25Syt * PxSSTS.DET to 1h to indicate a device is detected but communication 361268d33a25Syt * is not yet established. HBA sets PxSERR.DIAG.X to '1' to indicate 361368d33a25Syt * a COMINIT has been received. 36142fcbc377Syt */ 36152fcbc377Syt /* 36162fcbc377Syt * The DET field is valid only if IPM field indicates 36172fcbc377Syt * that the interface is in active state. 36182fcbc377Syt */ 36192fcbc377Syt loop_count = 0; 36202fcbc377Syt do { 36212fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 36222fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 36232fcbc377Syt 362482263d52Syt if (SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) { 36252fcbc377Syt /* 36262fcbc377Syt * If the interface is not active, the DET field 36272fcbc377Syt * is considered not accurate. So we want to 36282fcbc377Syt * continue looping. 36292fcbc377Syt */ 363082263d52Syt SSTATUS_SET_DET(port_sstatus, SSTATUS_DET_NODEV); 36312fcbc377Syt } 36322fcbc377Syt 36332fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SSTATUS) { 36342fcbc377Syt /* 36352fcbc377Syt * We are effectively timing out after 0.1 sec. 36362fcbc377Syt */ 36372fcbc377Syt break; 36382fcbc377Syt } 36392fcbc377Syt 36402fcbc377Syt /* Wait for 10 millisec */ 36412fcbc377Syt delay(AHCI_10MS_TICKS); 364282263d52Syt } while (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM); 36432fcbc377Syt 364468d33a25Syt AHCIDBG3(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 364568d33a25Syt "ahci_port_reset: 1st loop count: %d, " 364668d33a25Syt "port_sstatus = 0x%x port %d", 364768d33a25Syt loop_count, port_sstatus, port); 36482fcbc377Syt 364982263d52Syt if ((SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) || 365082263d52Syt (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM)) { 36512fcbc377Syt /* 36522fcbc377Syt * Either the port is not active or there 36532fcbc377Syt * is no device present. 36542fcbc377Syt */ 36552fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_NONE; 36560a4c4cecSXiao-Yu Zhang return (AHCI_SUCCESS); 36572fcbc377Syt } 36582fcbc377Syt 365968d33a25Syt /* Now we can make sure there is a device connected to the port */ 366068d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 366168d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 36620a4c4cecSXiao-Yu Zhang port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 36630a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 36642fcbc377Syt 36650a4c4cecSXiao-Yu Zhang /* 36660a4c4cecSXiao-Yu Zhang * A COMINIT signal is supposed to be received 36670a4c4cecSXiao-Yu Zhang * PxSERR.DIAG.X or PxIS.PCS should be set 36680a4c4cecSXiao-Yu Zhang */ 36690a4c4cecSXiao-Yu Zhang if (!(port_intr_status & AHCI_INTR_STATUS_PCS) && 36700a4c4cecSXiao-Yu Zhang !(port_serror & SERROR_EXCHANGED_ERR)) { 3671a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d " 3672a9440e8dSyt "COMINIT signal from the device not received", 3673a9440e8dSyt instance, port); 367468d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED; 36750a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 367668d33a25Syt } 36772fcbc377Syt 367895c11c1fSyt /* 367995c11c1fSyt * According to the spec, when PxSCTL.DET is set to 0h, upon 368095c11c1fSyt * receiving a COMINIT from the attached device, PxTFD.STS.BSY 368195c11c1fSyt * shall be set to '1' by the HBA. 368295c11c1fSyt * 368395c11c1fSyt * However, we found JMicron JMB363 doesn't follow this, so 368495c11c1fSyt * remove this check, and just print a debug message. 368595c11c1fSyt */ 368695c11c1fSyt #if AHCI_DEBUG 368768d33a25Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 368868d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 368968d33a25Syt if (!(port_task_file & AHCI_TFD_STS_BSY)) { 369095c11c1fSyt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: " 369195c11c1fSyt "port %d BSY bit is not set after COMINIT signal " 369295c11c1fSyt "is received", port); 369368d33a25Syt } 369495c11c1fSyt #endif 36952fcbc377Syt 369668d33a25Syt /* 369768d33a25Syt * PxSERR.DIAG.X has to be cleared in order to update PxTFD with 369868d33a25Syt * the D2H FIS received by HBA. 369968d33a25Syt */ 370068d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 370168d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 370282263d52Syt SERROR_EXCHANGED_ERR); 37032fcbc377Syt 370468d33a25Syt /* 37050a4c4cecSXiao-Yu Zhang * Devices should return a FIS contains its signature to HBA after 37060a4c4cecSXiao-Yu Zhang * COMINIT signal. Check whether a D2H FIS is received by polling 37070a4c4cecSXiao-Yu Zhang * PxTFD.STS.ERR bit. 370868d33a25Syt */ 370968d33a25Syt loop_count = 0; 371068d33a25Syt do { 37110a4c4cecSXiao-Yu Zhang /* Wait for 10 millisec */ 37120a4c4cecSXiao-Yu Zhang delay(AHCI_10MS_TICKS); 37132fcbc377Syt 371468d33a25Syt if (loop_count++ > AHCI_POLLRATE_PORT_TFD_ERROR) { 371568d33a25Syt /* 371668d33a25Syt * We are effectively timing out after 11 sec. 371768d33a25Syt */ 37180a4c4cecSXiao-Yu Zhang cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d " 37190a4c4cecSXiao-Yu Zhang "the device hardware has been initialized and " 37200a4c4cecSXiao-Yu Zhang "the power-up diagnostics failed", 37210a4c4cecSXiao-Yu Zhang instance, port); 37220a4c4cecSXiao-Yu Zhang 37230a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: " 37240a4c4cecSXiao-Yu Zhang "port %d PxTFD.STS.ERR is not set, we need another " 37250a4c4cecSXiao-Yu Zhang "software reset.", port); 37260a4c4cecSXiao-Yu Zhang 37270a4c4cecSXiao-Yu Zhang /* Clear port serror register for the port */ 37280a4c4cecSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 37290a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 37300a4c4cecSXiao-Yu Zhang AHCI_SERROR_CLEAR_ALL); 37310a4c4cecSXiao-Yu Zhang 37320a4c4cecSXiao-Yu Zhang /* Try another software reset. */ 37330a4c4cecSXiao-Yu Zhang if (ahci_software_reset(ahci_ctlp, ahci_portp, 37340a4c4cecSXiao-Yu Zhang port) != AHCI_SUCCESS) { 37350a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state |= 37360a4c4cecSXiao-Yu Zhang SATA_PSTATE_FAILED; 37370a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 37380a4c4cecSXiao-Yu Zhang } 373968d33a25Syt break; 374068d33a25Syt } 37412fcbc377Syt 37420a4c4cecSXiao-Yu Zhang /* 37430a4c4cecSXiao-Yu Zhang * The Error bit '1' means COMRESET is finished successfully 37440a4c4cecSXiao-Yu Zhang * The device hardware has been initialized and the power-up 37450a4c4cecSXiao-Yu Zhang * diagnostics successfully completed. The device requests 37460a4c4cecSXiao-Yu Zhang * that the Transport layer transmit a Register - D2H FIS to 37470a4c4cecSXiao-Yu Zhang * the host. (SATA spec 11.5, v2.6) 37480a4c4cecSXiao-Yu Zhang */ 37490a4c4cecSXiao-Yu Zhang port_task_file = 37500a4c4cecSXiao-Yu Zhang ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 37510a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 375268d33a25Syt } while (((port_task_file & AHCI_TFD_ERR_MASK) 37530a4c4cecSXiao-Yu Zhang >> AHCI_TFD_ERR_SHIFT) != AHCI_TFD_ERR_SGS); 37542fcbc377Syt 37550a4c4cecSXiao-Yu Zhang AHCIDBG3(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 37560a4c4cecSXiao-Yu Zhang "ahci_port_reset: 2nd loop count: %d, " 37570a4c4cecSXiao-Yu Zhang "port_task_file = 0x%x port %d", 375868d33a25Syt loop_count, port_task_file, port); 37592fcbc377Syt 376068d33a25Syt /* Clear port serror register for the port */ 376168d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 376268d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 376368d33a25Syt AHCI_SERROR_CLEAR_ALL); 376468d33a25Syt 37650a4c4cecSXiao-Yu Zhang /* Set port as ready */ 37660a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state |= SATA_STATE_READY; 376768d33a25Syt 37680a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_INFO|AHCIDBG_ERRS, ahci_ctlp, 37690a4c4cecSXiao-Yu Zhang "ahci_port_reset: succeed at port %d.", port); 37700a4c4cecSXiao-Yu Zhang return (AHCI_SUCCESS); 37712fcbc377Syt } 37722fcbc377Syt 37732fcbc377Syt /* 37742fcbc377Syt * AHCI HBA reset ...; the entire HBA is reset, and all ports are disabled. 37752fcbc377Syt * This is the most intrusive. 37762fcbc377Syt * 377768d33a25Syt * When an HBA reset occurs, Phy communication will be re-established with 377868d33a25Syt * the device through a COMRESET followed by the normal out-of-band 377968d33a25Syt * communication sequence defined in Serial ATA. AT the end of reset, the 378068d33a25Syt * device, if working properly, will send a D2H Register FIS, which contains 378168d33a25Syt * the device signature. When the HBA receives this FIS, it updates PxTFD.STS 378268d33a25Syt * and PxTFD.ERR register fields, and updates the PxSIG register with the 378368d33a25Syt * signature. 37842fcbc377Syt * 37852fcbc377Syt * Remember to set GHC.AE to 1 before calling ahci_hba_reset. 37862fcbc377Syt */ 37872fcbc377Syt static int 37882fcbc377Syt ahci_hba_reset(ahci_ctl_t *ahci_ctlp) 37892fcbc377Syt { 37902fcbc377Syt ahci_port_t *ahci_portp; 37912fcbc377Syt uint32_t ghc_control; 37922fcbc377Syt uint8_t port; 37932fcbc377Syt int loop_count; 37942fcbc377Syt int rval = AHCI_SUCCESS; 37952fcbc377Syt 37962fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, "HBA resetting"); 37972fcbc377Syt 379868d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 379968d33a25Syt 38002fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 38012fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 38022fcbc377Syt 38032fcbc377Syt /* Setting GHC.HR to 1, remember GHC.AE is already set to 1 before */ 38042fcbc377Syt ghc_control |= AHCI_HBA_GHC_HR; 38052fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 38062fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 38072fcbc377Syt 38082fcbc377Syt /* 38092fcbc377Syt * Wait until HBA Reset complete or timeout 38102fcbc377Syt */ 38112fcbc377Syt loop_count = 0; 38122fcbc377Syt do { 38132fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 38142fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 38152fcbc377Syt 38162fcbc377Syt if (loop_count++ > AHCI_POLLRATE_HBA_RESET) { 38172fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 38182fcbc377Syt "ahci hba reset is timing out, " 38192fcbc377Syt "ghc_control = 0x%x", ghc_control); 38202fcbc377Syt /* We are effectively timing out after 1 sec. */ 38212fcbc377Syt break; 38222fcbc377Syt } 38232fcbc377Syt 38242fcbc377Syt /* Wait for 10 millisec */ 38252fcbc377Syt delay(AHCI_10MS_TICKS); 38262fcbc377Syt } while (ghc_control & AHCI_HBA_GHC_HR); 38272fcbc377Syt 38282fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 38292fcbc377Syt "ahci_hba_reset: 1st loop count: %d, " 38302fcbc377Syt "ghc_control = 0x%x", loop_count, ghc_control); 38312fcbc377Syt 38322fcbc377Syt if (ghc_control & AHCI_HBA_GHC_HR) { 38332fcbc377Syt /* The hba is not reset for some reasons */ 38342fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 38352fcbc377Syt "hba reset failed: HBA in a hung or locked state"); 383668d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 38372fcbc377Syt return (AHCI_FAILURE); 38382fcbc377Syt } 38392fcbc377Syt 38402fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 38412fcbc377Syt /* Only check implemented ports */ 38422fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 38432fcbc377Syt continue; 38442fcbc377Syt } 38452fcbc377Syt 38462fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 38472fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 38482fcbc377Syt 38492fcbc377Syt if (ahci_port_reset(ahci_ctlp, ahci_portp, port) 38502fcbc377Syt != AHCI_SUCCESS) { 38512fcbc377Syt rval = AHCI_FAILURE; 38522fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 38532fcbc377Syt "ahci_hba_reset: port %d failed", port); 38542fcbc377Syt } 38552fcbc377Syt 38562fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 38572fcbc377Syt } 38582fcbc377Syt 38592fcbc377Syt /* 38602fcbc377Syt * Indicate that system software is AHCI aware by setting 38612fcbc377Syt * GHC.AE to 1 38622fcbc377Syt */ 38632fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 38642fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 38652fcbc377Syt 38662fcbc377Syt ghc_control |= AHCI_HBA_GHC_AE; 38672fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 38682fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 38692fcbc377Syt 387068d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 387168d33a25Syt 38722fcbc377Syt return (rval); 38732fcbc377Syt } 38742fcbc377Syt 38752fcbc377Syt /* 38762fcbc377Syt * This routine is only called from AHCI_ATTACH or phyrdy change 38770a4c4cecSXiao-Yu Zhang * case. It first calls software reset, then stop the port and try to 38780a4c4cecSXiao-Yu Zhang * read PxSIG register to find the type of device attached to the port. 38790a4c4cecSXiao-Yu Zhang * 38800a4c4cecSXiao-Yu Zhang * The caller should make sure a valid device exists on specified port and 38810a4c4cecSXiao-Yu Zhang * physical communication has been established so that the signature could 38820a4c4cecSXiao-Yu Zhang * be retrieved by software reset. 38832fcbc377Syt * 38842fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 388568d33a25Syt * is called. And the port interrupt is disabled. 38862fcbc377Syt */ 388768d33a25Syt static void 38882fcbc377Syt ahci_find_dev_signature(ahci_ctl_t *ahci_ctlp, 38892fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 38902fcbc377Syt { 38912fcbc377Syt uint32_t signature; 38922fcbc377Syt 38932fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 389468d33a25Syt "ahci_find_dev_signature enter: port %d", port); 38952fcbc377Syt 389668d33a25Syt ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 38972fcbc377Syt 38980a4c4cecSXiao-Yu Zhang /* Issue a software reset to get the signature */ 38990a4c4cecSXiao-Yu Zhang if (ahci_software_reset(ahci_ctlp, ahci_portp, port) 39000a4c4cecSXiao-Yu Zhang != AHCI_SUCCESS) { 3901*259105bcSying tian - Beijing China AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 39020a4c4cecSXiao-Yu Zhang "ahci_find_dev_signature: software reset failed " 3903*259105bcSying tian - Beijing China "at port %d, cannot get signature.", port); 39040a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 390568d33a25Syt return; 39062fcbc377Syt } 39072fcbc377Syt 39080a4c4cecSXiao-Yu Zhang /* 39090a4c4cecSXiao-Yu Zhang * ahci_software_reset has started the port, so we need manually stop 39100a4c4cecSXiao-Yu Zhang * the port again. 39110a4c4cecSXiao-Yu Zhang */ 39120a4c4cecSXiao-Yu Zhang if (ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, port) 39130a4c4cecSXiao-Yu Zhang != AHCI_SUCCESS) { 39140a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 39150a4c4cecSXiao-Yu Zhang "ahci_find_dev_signature: cannot stop port %d.", port); 39160a4c4cecSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 391768d33a25Syt return; 391868d33a25Syt } 39192fcbc377Syt 39200a4c4cecSXiao-Yu Zhang /* Now we can make sure that a valid signature is received. */ 39212fcbc377Syt signature = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 39222fcbc377Syt (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port)); 39232fcbc377Syt 39242fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_INFO, ahci_ctlp, 392568d33a25Syt "ahci_find_dev_signature: port %d signature = 0x%x", 392668d33a25Syt port, signature); 39272fcbc377Syt 39282fcbc377Syt switch (signature) { 39292fcbc377Syt 39302fcbc377Syt case AHCI_SIGNATURE_DISK: 39312fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_ATADISK; 39322fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 39332fcbc377Syt "Disk is found at port: %d", port); 39342fcbc377Syt break; 39352fcbc377Syt 39362fcbc377Syt case AHCI_SIGNATURE_ATAPI: 393738547057Sying tian - Beijing China ahci_portp->ahciport_device_type = SATA_DTYPE_ATAPI; 39382fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 39392fcbc377Syt "ATAPI device is found at port: %d", port); 39402fcbc377Syt break; 39412fcbc377Syt 39422fcbc377Syt case AHCI_SIGNATURE_PORT_MULTIPLIER: 39432fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_PMULT; 39442fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 39452fcbc377Syt "Port Multiplier is found at port: %d", port); 39462fcbc377Syt break; 39472fcbc377Syt 39482fcbc377Syt default: 39492fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 39502fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 39512fcbc377Syt "Unknown device is found at port: %d", port); 39522fcbc377Syt } 39532fcbc377Syt } 39542fcbc377Syt 395513bcbb7aSyt /* 395613bcbb7aSyt * According to the spec, to reliably detect hot plug removals, software 395713bcbb7aSyt * must disable interface power management. Software should perform the 395813bcbb7aSyt * following initialization on a port after a device is attached: 395913bcbb7aSyt * Set PxSCTL.IPM to 3h to disable interface state transitions 396013bcbb7aSyt * Set PxCMD.ALPE to '0' to disable aggressive power management 396113bcbb7aSyt * Disable device initiated interface power management by SET FEATURE 396213bcbb7aSyt * 396313bcbb7aSyt * We can ignore the last item because by default the feature is disabled 396413bcbb7aSyt */ 396513bcbb7aSyt static void 396613bcbb7aSyt ahci_disable_interface_pm(ahci_ctl_t *ahci_ctlp, uint8_t port) 396713bcbb7aSyt { 396813bcbb7aSyt uint32_t port_scontrol, port_cmd_status; 396913bcbb7aSyt 397013bcbb7aSyt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 397113bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 397213bcbb7aSyt SCONTROL_SET_IPM(port_scontrol, SCONTROL_IPM_DISABLE_BOTH); 397313bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 397413bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), port_scontrol); 397513bcbb7aSyt 397613bcbb7aSyt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 397713bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 397813bcbb7aSyt port_cmd_status &= ~AHCI_CMD_STATUS_ALPE; 397913bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 398013bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 398113bcbb7aSyt } 398213bcbb7aSyt 39832fcbc377Syt /* 398468d33a25Syt * Start the port - set PxCMD.ST to 1, if PxCMD.FRE is not set 398568d33a25Syt * to 1, then set it firstly. 398668d33a25Syt * 398768d33a25Syt * Each port contains two major DMA engines. One DMA engine walks through 398868d33a25Syt * the command list, and is controlled by PxCMD.ST. The second DMA engine 398968d33a25Syt * copies received FISes into system memory, and is controlled by PxCMD.FRE. 399068d33a25Syt * 399168d33a25Syt * Software shall not set PxCMD.ST to '1' until it verifies that PxCMD.CR 399268d33a25Syt * is '0' and has set PxCMD.FRE is '1'. And software shall not clear 399368d33a25Syt * PxCMD.FRE while PxCMD.ST or PxCMD.CR is set '1'. 399468d33a25Syt * 399568d33a25Syt * Software shall not set PxCMD.ST to '1' unless a functional device is 399668d33a25Syt * present on the port(as determined by PxTFD.STS.BSY = '0', 399768d33a25Syt * PxTFD.STS.DRQ = '0', and PxSSTS.DET = 3h). 39982fcbc377Syt * 39992fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 40002fcbc377Syt * is called. 40012fcbc377Syt */ 40022fcbc377Syt static int 400368d33a25Syt ahci_start_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 40042fcbc377Syt { 400568d33a25Syt uint32_t port_cmd_status; 40062fcbc377Syt 400768d33a25Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, "ahci_start_port: %d enter", port); 40082fcbc377Syt 400968d33a25Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 401068d33a25Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 401168d33a25Syt "the state for port %d is 0x%x", 401268d33a25Syt port, ahci_portp->ahciport_port_state); 40132fcbc377Syt return (AHCI_FAILURE); 401468d33a25Syt } 40152fcbc377Syt 401668d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 401768d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 401868d33a25Syt "no device is attached at port %d", port); 401968d33a25Syt return (AHCI_FAILURE); 402068d33a25Syt } 40212fcbc377Syt 402268d33a25Syt /* First to set PxCMD.FRE before setting PxCMD.ST. */ 402368d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 402468d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 40252fcbc377Syt 402668d33a25Syt if (!(port_cmd_status & AHCI_CMD_STATUS_FRE)) { 402768d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_FRE; 40282fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 40292fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 40302fcbc377Syt port_cmd_status); 40312fcbc377Syt } 403268d33a25Syt 403368d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 403468d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 403568d33a25Syt 403668d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_ST; 403768d33a25Syt 403868d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 403968d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 404068d33a25Syt port_cmd_status); 404168d33a25Syt 404268d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_STARTED; 404368d33a25Syt 40440a4c4cecSXiao-Yu Zhang AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port: " 40450a4c4cecSXiao-Yu Zhang "PxCMD.ST set to '1' at port %d", port); 40460a4c4cecSXiao-Yu Zhang 404768d33a25Syt return (AHCI_SUCCESS); 40482fcbc377Syt } 40492fcbc377Syt 40502fcbc377Syt /* 40512fcbc377Syt * Allocate the ahci_port_t including Received FIS and Command List. 40522fcbc377Syt * The argument - port is the physical port number, and not logical 40532fcbc377Syt * port number seen by the SATA framework. 40542fcbc377Syt * 40552fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 40562fcbc377Syt * is called. 40572fcbc377Syt */ 40582fcbc377Syt static int 40592fcbc377Syt ahci_alloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 40602fcbc377Syt { 4061f8a673adSying tian - Beijing China dev_info_t *dip = ahci_ctlp->ahcictl_dip; 40622fcbc377Syt ahci_port_t *ahci_portp; 4063f8a673adSying tian - Beijing China char taskq_name[64] = "event_handle_taskq"; 40642fcbc377Syt 40652fcbc377Syt ahci_portp = 40662fcbc377Syt (ahci_port_t *)kmem_zalloc(sizeof (ahci_port_t), KM_SLEEP); 40672fcbc377Syt 40682fcbc377Syt ahci_ctlp->ahcictl_ports[port] = ahci_portp; 40692fcbc377Syt ahci_portp->ahciport_port_num = port; 40702fcbc377Syt 407168d33a25Syt /* Intialize the port condition variable */ 407268d33a25Syt cv_init(&ahci_portp->ahciport_cv, NULL, CV_DRIVER, NULL); 407368d33a25Syt 407468d33a25Syt /* Initialize the port mutex */ 40752fcbc377Syt mutex_init(&ahci_portp->ahciport_mutex, NULL, MUTEX_DRIVER, 40762fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 407768d33a25Syt 40782fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 40792fcbc377Syt 40802fcbc377Syt /* 40812fcbc377Syt * Allocate memory for received FIS structure and 40822fcbc377Syt * command list for this port 40832fcbc377Syt */ 40842fcbc377Syt if (ahci_alloc_rcvd_fis(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 40852fcbc377Syt goto err_case1; 40862fcbc377Syt } 40872fcbc377Syt 40882fcbc377Syt if (ahci_alloc_cmd_list(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 40892fcbc377Syt goto err_case2; 40902fcbc377Syt } 40912fcbc377Syt 4092f8a673adSying tian - Beijing China (void) snprintf(taskq_name + strlen(taskq_name), 4093f8a673adSying tian - Beijing China sizeof (taskq_name) - strlen(taskq_name), 4094f8a673adSying tian - Beijing China "_port%d", port); 4095f8a673adSying tian - Beijing China 4096f8a673adSying tian - Beijing China /* Create the taskq for the port */ 4097f8a673adSying tian - Beijing China if ((ahci_portp->ahciport_event_taskq = ddi_taskq_create(dip, 4098f8a673adSying tian - Beijing China taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) { 4099f8a673adSying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ddi_taskq_create failed for event " 4100f8a673adSying tian - Beijing China "handle", ddi_get_instance(ahci_ctlp->ahcictl_dip)); 4101f8a673adSying tian - Beijing China goto err_case3; 4102f8a673adSying tian - Beijing China } 4103f8a673adSying tian - Beijing China 4104f8a673adSying tian - Beijing China /* Allocate the argument for the taskq */ 410568d33a25Syt ahci_portp->ahciport_event_args = 410668d33a25Syt kmem_zalloc(sizeof (ahci_event_arg_t), KM_SLEEP); 410768d33a25Syt 410868d33a25Syt if (ahci_portp->ahciport_event_args == NULL) 4109f8a673adSying tian - Beijing China goto err_case4; 411068d33a25Syt 41112fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 41122fcbc377Syt 41132fcbc377Syt return (AHCI_SUCCESS); 41142fcbc377Syt 4115f8a673adSying tian - Beijing China err_case4: 4116f8a673adSying tian - Beijing China ddi_taskq_destroy(ahci_portp->ahciport_event_taskq); 4117f8a673adSying tian - Beijing China 411868d33a25Syt err_case3: 411968d33a25Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 412068d33a25Syt 41212fcbc377Syt err_case2: 4122689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 41232fcbc377Syt 41242fcbc377Syt err_case1: 41252fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 41262fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 412768d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 41282fcbc377Syt 41292fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 41302fcbc377Syt 41312fcbc377Syt return (AHCI_FAILURE); 41322fcbc377Syt } 41332fcbc377Syt 41342fcbc377Syt /* 41352fcbc377Syt * Reverse of ahci_dealloc_port_state(). 41362fcbc377Syt * 41372fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 41382fcbc377Syt * is called. 41392fcbc377Syt */ 41402fcbc377Syt static void 41412fcbc377Syt ahci_dealloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 41422fcbc377Syt { 41432fcbc377Syt ahci_port_t *ahci_portp = ahci_ctlp->ahcictl_ports[port]; 41442fcbc377Syt 41452fcbc377Syt ASSERT(ahci_portp != NULL); 41462fcbc377Syt 41472fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 414868d33a25Syt kmem_free(ahci_portp->ahciport_event_args, sizeof (ahci_event_arg_t)); 414968d33a25Syt ahci_portp->ahciport_event_args = NULL; 4150f8a673adSying tian - Beijing China ddi_taskq_destroy(ahci_portp->ahciport_event_taskq); 41512fcbc377Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 4152689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 41532fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 41542fcbc377Syt 41552fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 415668d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 41572fcbc377Syt 41582fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 41592fcbc377Syt 41602fcbc377Syt ahci_ctlp->ahcictl_ports[port] = NULL; 41612fcbc377Syt } 41622fcbc377Syt 41632fcbc377Syt /* 41642fcbc377Syt * Allocates memory for the Received FIS Structure 41652fcbc377Syt * 41662fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 41672fcbc377Syt * is called. 41682fcbc377Syt */ 41692fcbc377Syt static int 41702fcbc377Syt ahci_alloc_rcvd_fis(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 41712fcbc377Syt uint8_t port) 41722fcbc377Syt { 41732fcbc377Syt size_t rcvd_fis_size; 41742fcbc377Syt size_t ret_len; 41752fcbc377Syt uint_t cookie_count; 41762fcbc377Syt 41772fcbc377Syt rcvd_fis_size = sizeof (ahci_rcvd_fis_t); 41782fcbc377Syt 41792fcbc377Syt /* allocate rcvd FIS dma handle. */ 41802fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 41812fcbc377Syt &ahci_ctlp->ahcictl_rcvd_fis_dma_attr, 41822fcbc377Syt DDI_DMA_SLEEP, 41832fcbc377Syt NULL, 41842fcbc377Syt &ahci_portp->ahciport_rcvd_fis_dma_handle) != 41852fcbc377Syt DDI_SUCCESS) { 41862fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 41872fcbc377Syt "rcvd FIS dma handle alloc failed"); 41882fcbc377Syt 41892fcbc377Syt return (AHCI_FAILURE); 41902fcbc377Syt } 41912fcbc377Syt 41922fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_rcvd_fis_dma_handle, 41932fcbc377Syt rcvd_fis_size, 41942fcbc377Syt &accattr, 41952fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 41962fcbc377Syt DDI_DMA_SLEEP, 41972fcbc377Syt NULL, 41982fcbc377Syt (caddr_t *)&ahci_portp->ahciport_rcvd_fis, 41992fcbc377Syt &ret_len, 42002fcbc377Syt &ahci_portp->ahciport_rcvd_fis_acc_handle) != NULL) { 42012fcbc377Syt 42022fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 42032fcbc377Syt "rcvd FIS dma mem alloc fail"); 42042fcbc377Syt /* error.. free the dma handle. */ 42052fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 42062fcbc377Syt return (AHCI_FAILURE); 42072fcbc377Syt } 42082fcbc377Syt 42092fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle, 42102fcbc377Syt NULL, 42112fcbc377Syt (caddr_t)ahci_portp->ahciport_rcvd_fis, 42122fcbc377Syt rcvd_fis_size, 42132fcbc377Syt DDI_DMA_CONSISTENT, 42142fcbc377Syt DDI_DMA_SLEEP, 42152fcbc377Syt NULL, 421613bcbb7aSyt &ahci_portp->ahciport_rcvd_fis_dma_cookie, 42172fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 42182fcbc377Syt 42192fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 42202fcbc377Syt "rcvd FIS dma handle bind fail"); 42212fcbc377Syt /* error.. free the dma handle & free the memory. */ 42222fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 42232fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 42242fcbc377Syt return (AHCI_FAILURE); 42252fcbc377Syt } 42262fcbc377Syt 42272fcbc377Syt bzero((void *)ahci_portp->ahciport_rcvd_fis, rcvd_fis_size); 42282fcbc377Syt 42292fcbc377Syt /* Config Port Received FIS Base Address */ 42302fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 42312fcbc377Syt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 423213bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 42332fcbc377Syt 42342fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 423513bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 42362fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 423713bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_address); 42382fcbc377Syt 42392fcbc377Syt return (AHCI_SUCCESS); 42402fcbc377Syt } 42412fcbc377Syt 42422fcbc377Syt /* 42432fcbc377Syt * Deallocates the Received FIS Structure 42442fcbc377Syt * 42452fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 42462fcbc377Syt * is called. 42472fcbc377Syt */ 42482fcbc377Syt static void 4249689d74b0Syt ahci_dealloc_rcvd_fis(ahci_port_t *ahci_portp) 42502fcbc377Syt { 42512fcbc377Syt /* Unbind the cmd list dma handle first. */ 42522fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle); 42532fcbc377Syt 42542fcbc377Syt /* Then free the underlying memory. */ 42552fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 42562fcbc377Syt 42572fcbc377Syt /* Now free the handle itself. */ 42582fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 42592fcbc377Syt } 42602fcbc377Syt 42612fcbc377Syt /* 42622fcbc377Syt * Allocates memory for the Command List, which contains up to 32 entries. 42632fcbc377Syt * Each entry contains a command header, which is a 32-byte structure that 42642fcbc377Syt * includes the pointer to the command table. 42652fcbc377Syt * 42662fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 42672fcbc377Syt * is called. 42682fcbc377Syt */ 42692fcbc377Syt static int 42702fcbc377Syt ahci_alloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 42712fcbc377Syt uint8_t port) 42722fcbc377Syt { 42732fcbc377Syt size_t cmd_list_size; 42742fcbc377Syt size_t ret_len; 42752fcbc377Syt uint_t cookie_count; 42762fcbc377Syt 42772fcbc377Syt cmd_list_size = 42782fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots * sizeof (ahci_cmd_header_t); 42792fcbc377Syt 42802fcbc377Syt /* allocate cmd list dma handle. */ 42812fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 42822fcbc377Syt &ahci_ctlp->ahcictl_cmd_list_dma_attr, 42832fcbc377Syt DDI_DMA_SLEEP, 42842fcbc377Syt NULL, 42852fcbc377Syt &ahci_portp->ahciport_cmd_list_dma_handle) != DDI_SUCCESS) { 42862fcbc377Syt 42872fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 42882fcbc377Syt "cmd list dma handle alloc failed"); 42892fcbc377Syt return (AHCI_FAILURE); 42902fcbc377Syt } 42912fcbc377Syt 42922fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_cmd_list_dma_handle, 42932fcbc377Syt cmd_list_size, 42942fcbc377Syt &accattr, 42952fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 42962fcbc377Syt DDI_DMA_SLEEP, 42972fcbc377Syt NULL, 42982fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_list, 42992fcbc377Syt &ret_len, 43002fcbc377Syt &ahci_portp->ahciport_cmd_list_acc_handle) != NULL) { 43012fcbc377Syt 43022fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 43032fcbc377Syt "cmd list dma mem alloc fail"); 43042fcbc377Syt /* error.. free the dma handle. */ 43052fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 43062fcbc377Syt return (AHCI_FAILURE); 43072fcbc377Syt } 43082fcbc377Syt 43092fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_cmd_list_dma_handle, 43102fcbc377Syt NULL, 43112fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_list, 43122fcbc377Syt cmd_list_size, 43132fcbc377Syt DDI_DMA_CONSISTENT, 43142fcbc377Syt DDI_DMA_SLEEP, 43152fcbc377Syt NULL, 431613bcbb7aSyt &ahci_portp->ahciport_cmd_list_dma_cookie, 43172fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 43182fcbc377Syt 43192fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 43202fcbc377Syt "cmd list dma handle bind fail"); 43212fcbc377Syt /* error.. free the dma handle & free the memory. */ 43222fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 43232fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 43242fcbc377Syt return (AHCI_FAILURE); 43252fcbc377Syt } 43262fcbc377Syt 43272fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_list, cmd_list_size); 43282fcbc377Syt 43292fcbc377Syt /* Config Port Command List Base Address */ 43302fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 43312fcbc377Syt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 433213bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 43332fcbc377Syt 43342fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 433513bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 43362fcbc377Syt 43372fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 433813bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_address); 43392fcbc377Syt 43402fcbc377Syt if (ahci_alloc_cmd_tables(ahci_ctlp, ahci_portp) != AHCI_SUCCESS) { 4341e2decd04Syt goto err_out; 43422fcbc377Syt } 43432fcbc377Syt 43442fcbc377Syt return (AHCI_SUCCESS); 4345e2decd04Syt 4346e2decd04Syt err_out: 4347e2decd04Syt /* Unbind the cmd list dma handle first. */ 4348e2decd04Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 4349e2decd04Syt 4350e2decd04Syt /* Then free the underlying memory. */ 4351e2decd04Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 4352e2decd04Syt 4353e2decd04Syt /* Now free the handle itself. */ 4354e2decd04Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 4355e2decd04Syt 4356e2decd04Syt return (AHCI_FAILURE); 43572fcbc377Syt } 43582fcbc377Syt 43592fcbc377Syt /* 43602fcbc377Syt * Deallocates the Command List 43612fcbc377Syt * 43622fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 43632fcbc377Syt * is called. 43642fcbc377Syt */ 43652fcbc377Syt static void 43662fcbc377Syt ahci_dealloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 43672fcbc377Syt { 43682fcbc377Syt /* First dealloc command table */ 43692fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctlp, ahci_portp); 43702fcbc377Syt 43712fcbc377Syt /* Unbind the cmd list dma handle first. */ 43722fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 43732fcbc377Syt 43742fcbc377Syt /* Then free the underlying memory. */ 43752fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 43762fcbc377Syt 43772fcbc377Syt /* Now free the handle itself. */ 43782fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 43792fcbc377Syt } 43802fcbc377Syt 43812fcbc377Syt /* 43822fcbc377Syt * Allocates memory for all Command Tables, which contains Command FIS, 43832fcbc377Syt * ATAPI Command and Physical Region Descriptor Table. 43842fcbc377Syt * 43852fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 43862fcbc377Syt * is called. 43872fcbc377Syt */ 43882fcbc377Syt static int 43892fcbc377Syt ahci_alloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 43902fcbc377Syt { 43912fcbc377Syt size_t ret_len; 43922fcbc377Syt ddi_dma_cookie_t cmd_table_dma_cookie; 43932fcbc377Syt uint_t cookie_count; 43942fcbc377Syt int slot; 43952fcbc377Syt 43962fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 43972fcbc377Syt "ahci_alloc_cmd_tables: port %d enter", 43982fcbc377Syt ahci_portp->ahciport_port_num); 43992fcbc377Syt 44002fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 44012fcbc377Syt /* Allocate cmd table dma handle. */ 44022fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 44032fcbc377Syt &ahci_ctlp->ahcictl_cmd_table_dma_attr, 44042fcbc377Syt DDI_DMA_SLEEP, 44052fcbc377Syt NULL, 44062fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]) != 44072fcbc377Syt DDI_SUCCESS) { 44082fcbc377Syt 44092fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 44102fcbc377Syt "cmd table dma handle alloc failed"); 44112fcbc377Syt 44122fcbc377Syt goto err_out; 44132fcbc377Syt } 44142fcbc377Syt 44152fcbc377Syt if (ddi_dma_mem_alloc( 44162fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 44172fcbc377Syt ahci_cmd_table_size, 44182fcbc377Syt &accattr, 44192fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 44202fcbc377Syt DDI_DMA_SLEEP, 44212fcbc377Syt NULL, 44222fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_tables[slot], 44232fcbc377Syt &ret_len, 44242fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]) != 44252fcbc377Syt NULL) { 44262fcbc377Syt 44272fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 44282fcbc377Syt "cmd table dma mem alloc fail"); 44292fcbc377Syt 44302fcbc377Syt /* error.. free the dma handle. */ 44312fcbc377Syt ddi_dma_free_handle( 44322fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 44332fcbc377Syt goto err_out; 44342fcbc377Syt } 44352fcbc377Syt 44362fcbc377Syt if (ddi_dma_addr_bind_handle( 44372fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 44382fcbc377Syt NULL, 44392fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_tables[slot], 44402fcbc377Syt ahci_cmd_table_size, 44412fcbc377Syt DDI_DMA_CONSISTENT, 44422fcbc377Syt DDI_DMA_SLEEP, 44432fcbc377Syt NULL, 44442fcbc377Syt &cmd_table_dma_cookie, 44452fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 44462fcbc377Syt 44472fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 44482fcbc377Syt "cmd table dma handle bind fail"); 44492fcbc377Syt /* error.. free the dma handle & free the memory. */ 44502fcbc377Syt ddi_dma_mem_free( 44512fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 44522fcbc377Syt ddi_dma_free_handle( 44532fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 44542fcbc377Syt goto err_out; 44552fcbc377Syt } 44562fcbc377Syt 44572fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_tables[slot], 44582fcbc377Syt ahci_cmd_table_size); 44592fcbc377Syt 44602fcbc377Syt /* Config Port Command Table Base Address */ 44612fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR( 44622fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 44632fcbc377Syt cmd_table_dma_cookie.dmac_laddress & 0xffffffffull); 44642fcbc377Syt 44652fcbc377Syt #ifndef __lock_lint 44662fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR_UPPER( 44672fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 44682fcbc377Syt cmd_table_dma_cookie.dmac_laddress >> 32); 4469689d74b0Syt #endif 44702fcbc377Syt } 44712fcbc377Syt 44722fcbc377Syt return (AHCI_SUCCESS); 44732fcbc377Syt err_out: 44742fcbc377Syt 44752fcbc377Syt for (slot--; slot >= 0; slot--) { 44762fcbc377Syt /* Unbind the cmd table dma handle first */ 44772fcbc377Syt (void) ddi_dma_unbind_handle( 44782fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 44792fcbc377Syt 44802fcbc377Syt /* Then free the underlying memory */ 44812fcbc377Syt ddi_dma_mem_free( 44822fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 44832fcbc377Syt 44842fcbc377Syt /* Now free the handle itself */ 44852fcbc377Syt ddi_dma_free_handle( 44862fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 44872fcbc377Syt } 44882fcbc377Syt 44892fcbc377Syt return (AHCI_FAILURE); 44902fcbc377Syt } 44912fcbc377Syt 44922fcbc377Syt /* 44932fcbc377Syt * Deallocates memory for all Command Tables. 44942fcbc377Syt * 44952fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 44962fcbc377Syt * is called. 44972fcbc377Syt */ 44982fcbc377Syt static void 44992fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 45002fcbc377Syt { 45012fcbc377Syt int slot; 45022fcbc377Syt 45032fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 45042fcbc377Syt "ahci_dealloc_cmd_tables: %d enter", 45052fcbc377Syt ahci_portp->ahciport_port_num); 45062fcbc377Syt 45072fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 45082fcbc377Syt /* Unbind the cmd table dma handle first. */ 45092fcbc377Syt (void) ddi_dma_unbind_handle( 45102fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 45112fcbc377Syt 45122fcbc377Syt /* Then free the underlying memory. */ 45132fcbc377Syt ddi_dma_mem_free( 45142fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 45152fcbc377Syt 45162fcbc377Syt /* Now free the handle itself. */ 45172fcbc377Syt ddi_dma_free_handle( 45182fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 45192fcbc377Syt } 45202fcbc377Syt } 45212fcbc377Syt 45222fcbc377Syt /* 45232fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 45242fcbc377Syt * is called. 45252fcbc377Syt */ 45262fcbc377Syt static void 45272fcbc377Syt ahci_update_sata_registers(ahci_ctl_t *ahci_ctlp, uint8_t port, 45282fcbc377Syt sata_device_t *sd) 45292fcbc377Syt { 45302fcbc377Syt sd->satadev_scr.sstatus = 45312fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45322fcbc377Syt (uint32_t *)(AHCI_PORT_PxSSTS(ahci_ctlp, port))); 45332fcbc377Syt sd->satadev_scr.serror = 45342fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45352fcbc377Syt (uint32_t *)(AHCI_PORT_PxSERR(ahci_ctlp, port))); 45362fcbc377Syt sd->satadev_scr.scontrol = 45372fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45382fcbc377Syt (uint32_t *)(AHCI_PORT_PxSCTL(ahci_ctlp, port))); 45392fcbc377Syt sd->satadev_scr.sactive = 45402fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45412fcbc377Syt (uint32_t *)(AHCI_PORT_PxSACT(ahci_ctlp, port))); 45422fcbc377Syt } 45432fcbc377Syt 45442fcbc377Syt /* 454568d33a25Syt * For poll mode, ahci_port_intr will be called to emulate the interrupt 45462fcbc377Syt */ 45472fcbc377Syt static void 454882263d52Syt ahci_port_intr(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 45492fcbc377Syt { 455068d33a25Syt uint32_t port_intr_status; 455168d33a25Syt uint32_t port_intr_enable; 455268d33a25Syt 455368d33a25Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 455468d33a25Syt "ahci_port_intr enter: port %d", port); 455568d33a25Syt 455668d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 455768d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_POLLING) { 455868d33a25Syt /* For SATA_OPMODE_POLLING commands */ 455968d33a25Syt port_intr_enable = 456068d33a25Syt (AHCI_INTR_STATUS_DHRS | 456168d33a25Syt AHCI_INTR_STATUS_PSS | 456268d33a25Syt AHCI_INTR_STATUS_SDBS | 456368d33a25Syt AHCI_INTR_STATUS_UFS | 456468d33a25Syt AHCI_INTR_STATUS_PCS | 456568d33a25Syt AHCI_INTR_STATUS_PRCS | 456668d33a25Syt AHCI_INTR_STATUS_OFS | 456768d33a25Syt AHCI_INTR_STATUS_INFS | 456868d33a25Syt AHCI_INTR_STATUS_IFS | 456968d33a25Syt AHCI_INTR_STATUS_HBDS | 457068d33a25Syt AHCI_INTR_STATUS_HBFS | 457168d33a25Syt AHCI_INTR_STATUS_TFES); 457268d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 457368d33a25Syt goto next; 457468d33a25Syt } 457568d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 457668d33a25Syt 457768d33a25Syt /* 457868d33a25Syt * port_intr_enable indicates that the corresponding interrrupt 457968d33a25Syt * reporting is enabled. 458068d33a25Syt */ 458168d33a25Syt port_intr_enable = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 458268d33a25Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port)); 458368d33a25Syt next: 458468d33a25Syt /* 458568d33a25Syt * port_intr_stats indicates that the corresponding interrupt 458668d33a25Syt * condition is active. 458768d33a25Syt */ 458868d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 458968d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 459068d33a25Syt 459182263d52Syt AHCIDBG3(AHCIDBG_INTR, ahci_ctlp, 459268d33a25Syt "ahci_port_intr: port %d, port_intr_status = 0x%x, " 459382263d52Syt "port_intr_enable = 0x%x", 459482263d52Syt port, port_intr_status, port_intr_enable); 459568d33a25Syt 459668d33a25Syt port_intr_status &= port_intr_enable; 459768d33a25Syt 459868d33a25Syt /* First clear the port interrupts status */ 459968d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 460068d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 460168d33a25Syt port_intr_status); 460268d33a25Syt 460382263d52Syt /* Check the completed non-queued commands */ 460468d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_DHRS | 460568d33a25Syt AHCI_INTR_STATUS_PSS)) { 460668d33a25Syt (void) ahci_intr_cmd_cmplt(ahci_ctlp, 4607689d74b0Syt ahci_portp, port); 460868d33a25Syt } 460968d33a25Syt 461082263d52Syt /* Check the completed queued commands */ 461168d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_SDBS) { 461268d33a25Syt (void) ahci_intr_set_device_bits(ahci_ctlp, 461368d33a25Syt ahci_portp, port); 461468d33a25Syt } 461568d33a25Syt 461668d33a25Syt /* Check the port connect change status interrupt bit */ 461768d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PCS) { 461868d33a25Syt (void) ahci_intr_port_connect_change(ahci_ctlp, 461968d33a25Syt ahci_portp, port); 462068d33a25Syt } 462168d33a25Syt 462268d33a25Syt /* Check the device mechanical presence status interrupt bit */ 462368d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_DMPS) { 462468d33a25Syt (void) ahci_intr_device_mechanical_presence_status( 462568d33a25Syt ahci_ctlp, ahci_portp, port); 462668d33a25Syt } 462768d33a25Syt 462868d33a25Syt /* Check the PhyRdy change status interrupt bit */ 462968d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PRCS) { 463068d33a25Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, 463168d33a25Syt port); 46322fcbc377Syt } 463368d33a25Syt 463468d33a25Syt /* 463568d33a25Syt * Check the non-fatal error interrupt bits, there are three 463668d33a25Syt * kinds of non-fatal errors at the time being: 463768d33a25Syt * 463868d33a25Syt * PxIS.UFS - Unknown FIS Error 463968d33a25Syt * PxIS.OFS - Overflow Error 464068d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 464168d33a25Syt * 464268d33a25Syt * For these non-fatal errors, the HBA can continue to operate, 464368d33a25Syt * so the driver just log the error messages. 464468d33a25Syt */ 464568d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_UFS | 464668d33a25Syt AHCI_INTR_STATUS_OFS | 464768d33a25Syt AHCI_INTR_STATUS_INFS)) { 464868d33a25Syt (void) ahci_intr_non_fatal_error(ahci_ctlp, ahci_portp, 464968d33a25Syt port, port_intr_status); 465068d33a25Syt } 465168d33a25Syt 465268d33a25Syt /* 465368d33a25Syt * Check the fatal error interrupt bits, there are four kinds 465468d33a25Syt * of fatal errors for AHCI controllers: 465568d33a25Syt * 465668d33a25Syt * PxIS.HBFS - Host Bus Fatal Error 465768d33a25Syt * PxIS.HBDS - Host Bus Data Error 465868d33a25Syt * PxIS.IFS - Interface Fatal Error 465968d33a25Syt * PxIS.TFES - Task File Error 466068d33a25Syt * 466168d33a25Syt * The fatal error means the HBA can not recover from it by 466268d33a25Syt * itself, and it will try to abort the transfer, and the software 466368d33a25Syt * must intervene to restart the port. 466468d33a25Syt */ 466568d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_IFS | 466668d33a25Syt AHCI_INTR_STATUS_HBDS | 466768d33a25Syt AHCI_INTR_STATUS_HBFS | 466868d33a25Syt AHCI_INTR_STATUS_TFES)) 466968d33a25Syt (void) ahci_intr_fatal_error(ahci_ctlp, ahci_portp, 467082263d52Syt port, port_intr_status); 467168d33a25Syt 467268d33a25Syt /* Check the cold port detect interrupt bit */ 467368d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_CPDS) { 467468d33a25Syt (void) ahci_intr_cold_port_detect(ahci_ctlp, ahci_portp, port); 467568d33a25Syt } 467668d33a25Syt 467768d33a25Syt /* Second clear the corresponding bit in IS.IPS */ 467868d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 467968d33a25Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (0x1 << port)); 46802fcbc377Syt } 46812fcbc377Syt 46822fcbc377Syt /* 46832fcbc377Syt * Interrupt service handler 46842fcbc377Syt */ 46852fcbc377Syt static uint_t 46862fcbc377Syt ahci_intr(caddr_t arg1, caddr_t arg2) 46872fcbc377Syt { 4688689d74b0Syt #ifndef __lock_lint 4689689d74b0Syt _NOTE(ARGUNUSED(arg2)) 4690689d74b0Syt #endif 4691689d74b0Syt /* LINTED */ 46922fcbc377Syt ahci_ctl_t *ahci_ctlp = (ahci_ctl_t *)arg1; 46932fcbc377Syt ahci_port_t *ahci_portp; 469468d33a25Syt int32_t global_intr_status; 46952fcbc377Syt uint8_t port; 46962fcbc377Syt 46972fcbc377Syt /* 46982fcbc377Syt * global_intr_status indicates that the corresponding port has 46992fcbc377Syt * an interrupt pending. 47002fcbc377Syt */ 47012fcbc377Syt global_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 47022fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp)); 47032fcbc377Syt 47042fcbc377Syt if (!(global_intr_status & ahci_ctlp->ahcictl_ports_implemented)) { 47052fcbc377Syt /* The interrupt is not ours */ 47062fcbc377Syt return (DDI_INTR_UNCLAIMED); 47072fcbc377Syt } 47082fcbc377Syt 47092fcbc377Syt /* Loop for all the ports */ 47102fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 47112fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 47122fcbc377Syt continue; 47132fcbc377Syt } 47142fcbc377Syt if (!((0x1 << port) & global_intr_status)) { 47152fcbc377Syt continue; 47162fcbc377Syt } 47172fcbc377Syt 47182fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 47192fcbc377Syt 472068d33a25Syt /* Call ahci_port_intr */ 472182263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 47222fcbc377Syt } 47232fcbc377Syt 47242fcbc377Syt return (DDI_INTR_CLAIMED); 47252fcbc377Syt } 47262fcbc377Syt 47272fcbc377Syt /* 472868d33a25Syt * For non-queued commands, when the corresponding bit in the PxCI register 472968d33a25Syt * is cleared, it means the command is completed successfully. And according 473068d33a25Syt * to the HBA state machine, there are three conditions which possibly will 473168d33a25Syt * try to clear the PxCI register bit. 473268d33a25Syt * 1. Receive one D2H Register FIS which is with 'I' bit set 473368d33a25Syt * 2. Update PIO Setup FIS 473468d33a25Syt * 3. Transmit a command and receive R_OK if CTBA.C is set (software reset) 473568d33a25Syt * 473682263d52Syt * Process completed non-queued commands when the interrupt status bit - 473782263d52Syt * AHCI_INTR_STATUS_DHRS or AHCI_INTR_STATUS_PSS is set. 47382fcbc377Syt * 473968d33a25Syt * AHCI_INTR_STATUS_DHRS means a D2H Register FIS has been received 474068d33a25Syt * with the 'I' bit set. And the following commands will send thus 474168d33a25Syt * FIS with 'I' bit set upon the successful completion: 47422fcbc377Syt * 1. Non-data commands 47432fcbc377Syt * 2. DMA data-in command 47442fcbc377Syt * 3. DMA data-out command 47452fcbc377Syt * 4. PIO data-out command 474668d33a25Syt * 5. PACKET non-data commands 474768d33a25Syt * 6. PACKET PIO data-in command 474868d33a25Syt * 7. PACKET PIO data-out command 474968d33a25Syt * 8. PACKET DMA data-in command 475068d33a25Syt * 9. PACKET DMA data-out command 475168d33a25Syt * 475268d33a25Syt * AHCI_INTR_STATUS_PSS means a PIO Setup FIS has been received 475368d33a25Syt * with the 'I' bit set. And the following commands will send this 475468d33a25Syt * FIS upon the successful completion: 475568d33a25Syt * 1. PIO data-in command 47562fcbc377Syt */ 47572fcbc377Syt static int 475882263d52Syt ahci_intr_cmd_cmplt(ahci_ctl_t *ahci_ctlp, 4759689d74b0Syt ahci_port_t *ahci_portp, uint8_t port) 47602fcbc377Syt { 476168d33a25Syt uint32_t port_cmd_issue = 0; 47622fcbc377Syt uint32_t finished_tags; 476368d33a25Syt int finished_slot; 47642fcbc377Syt sata_pkt_t *satapkt; 47652fcbc377Syt ahci_fis_d2h_register_t *rcvd_fisp; 476638547057Sying tian - Beijing China #if AHCI_DEBUG 476738547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 476838547057Sying tian - Beijing China uint32_t cmd_dmacount; 476938547057Sying tian - Beijing China #endif 47702fcbc377Syt 477168d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 477282263d52Syt 477382263d52Syt if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 477482263d52Syt !NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 47752fcbc377Syt /* 47762fcbc377Syt * Spurious interrupt. Nothing to be done. 47772fcbc377Syt */ 47782fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 47792fcbc377Syt return (AHCI_SUCCESS); 47802fcbc377Syt } 47812fcbc377Syt 478268d33a25Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 478368d33a25Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 47842fcbc377Syt 478582263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 478682263d52Syt /* Slot 0 is always used during error recovery */ 478782263d52Syt finished_tags = 0x1 & ~port_cmd_issue; 478882263d52Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, 478982263d52Syt "ahci_intr_cmd_cmplt: port %d the sata pkt for error " 479082263d52Syt "retrieval is finished, and finished_tags = 0x%x", 479182263d52Syt port, finished_tags); 479268d33a25Syt } else { 479368d33a25Syt finished_tags = ahci_portp->ahciport_pending_tags & 479468d33a25Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 47952fcbc377Syt } 47962fcbc377Syt 479768d33a25Syt AHCIDBG3(AHCIDBG_INTR, ahci_ctlp, 479882263d52Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x, " 479982263d52Syt "port_cmd_issue = 0x%x finished_tags = 0x%x", 480068d33a25Syt ahci_portp->ahciport_pending_tags, port_cmd_issue, 480182263d52Syt finished_tags); 48022fcbc377Syt 480382263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 480482263d52Syt (finished_tags == 0x1)) { 480582263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 480682263d52Syt ASSERT(satapkt != NULL); 480782263d52Syt 480882263d52Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 480982263d52Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 481082263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 481182263d52Syt 481282263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 481382263d52Syt goto out; 481482263d52Syt } 48152fcbc377Syt 481668d33a25Syt while (finished_tags) { 481768d33a25Syt finished_slot = ddi_ffs(finished_tags) - 1; 481868d33a25Syt if (finished_slot == -1) { 481968d33a25Syt goto out; 482068d33a25Syt } 48212fcbc377Syt 482268d33a25Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 482368d33a25Syt ASSERT(satapkt != NULL); 482438547057Sying tian - Beijing China #if AHCI_DEBUG 482538547057Sying tian - Beijing China /* 482638547057Sying tian - Beijing China * For non-native queued commands, the PRD byte count field 482738547057Sying tian - Beijing China * shall contain an accurate count of the number of bytes 482838547057Sying tian - Beijing China * transferred for the command before the PxCI bit is cleared 482938547057Sying tian - Beijing China * to '0' for the command. 483038547057Sying tian - Beijing China * 483138547057Sying tian - Beijing China * The purpose of this field is to let software know how many 483238547057Sying tian - Beijing China * bytes transferred for a given operation in order to 483338547057Sying tian - Beijing China * determine if underflow occurred. When issuing native command 483438547057Sying tian - Beijing China * queuing commands, this field should not be used and is not 483538547057Sying tian - Beijing China * required to be valid since in this case underflow is always 483638547057Sying tian - Beijing China * illegal. 483738547057Sying tian - Beijing China * 483838547057Sying tian - Beijing China * For data reads, the HBA will update its PRD byte count with 483938547057Sying tian - Beijing China * the total number of bytes received from the last FIS, and 484038547057Sying tian - Beijing China * may be able to continue normally. For data writes, the 484138547057Sying tian - Beijing China * device will detect an error, and HBA most likely will get 484238547057Sying tian - Beijing China * a fatal error. 484338547057Sying tian - Beijing China * 484438547057Sying tian - Beijing China * Therefore, here just put code to debug part. And please 484538547057Sying tian - Beijing China * refer to the comment above ahci_intr_fatal_error for the 484638547057Sying tian - Beijing China * definition of underflow error. 484738547057Sying tian - Beijing China */ 484838547057Sying tian - Beijing China cmd_dmacount = 484938547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[finished_slot]; 485038547057Sying tian - Beijing China if (cmd_dmacount) { 485138547057Sying tian - Beijing China cmd_header = 485238547057Sying tian - Beijing China &ahci_portp->ahciport_cmd_list[finished_slot]; 485338547057Sying tian - Beijing China AHCIDBG3(AHCIDBG_INTR|AHCIDBG_PRDT, ahci_ctlp, 485438547057Sying tian - Beijing China "ahci_intr_cmd_cmplt: port %d, " 485538547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 485638547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 485738547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 485838547057Sying tian - Beijing China cmd_dmacount); 485938547057Sying tian - Beijing China 486038547057Sying tian - Beijing China if (cmd_header->ahcich_prd_byte_count != cmd_dmacount) { 486138547057Sying tian - Beijing China AHCIDBG1(AHCIDBG_UNDERFLOW, ahci_ctlp, 486238547057Sying tian - Beijing China "ahci_intr_cmd_cmplt: port %d, " 486338547057Sying tian - Beijing China "an underflow occurred", port); 486438547057Sying tian - Beijing China } 486538547057Sying tian - Beijing China } 486638547057Sying tian - Beijing China #endif 48672fcbc377Syt 48682fcbc377Syt /* 486968d33a25Syt * For SATAC_SMART command with SATA_SMART_RETURN_STATUS 487068d33a25Syt * feature, sata_special_regs flag will be set, and the 487168d33a25Syt * driver should copy the status and the other corresponding 487268d33a25Syt * register values in the D2H Register FIS received (It's 487368d33a25Syt * working on Non-data protocol) from the device back to 487468d33a25Syt * the sata_cmd. 487568d33a25Syt * 487668d33a25Syt * For every AHCI port, there is only one Received FIS 487768d33a25Syt * structure, which contains the FISes received from the 487868d33a25Syt * device, So we're trying to copy the content of D2H 487968d33a25Syt * Register FIS in the Received FIS structure back to 488068d33a25Syt * the sata_cmd. 48812fcbc377Syt */ 488268d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 488368d33a25Syt rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 488468d33a25Syt ahcirf_d2h_register_fis); 488568d33a25Syt satapkt->satapkt_cmd.satacmd_status_reg = 488668d33a25Syt GET_RFIS_STATUS(rcvd_fisp); 488768d33a25Syt ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp); 488868d33a25Syt } 48892fcbc377Syt 489068d33a25Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 489168d33a25Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 489268d33a25Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 48932fcbc377Syt 489468d33a25Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, finished_slot); 489568d33a25Syt CLEAR_BIT(finished_tags, finished_slot); 489668d33a25Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 48972fcbc377Syt 489868d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 48992fcbc377Syt } 49002fcbc377Syt out: 49012fcbc377Syt AHCIDBG1(AHCIDBG_PKTCOMP, ahci_ctlp, 490268d33a25Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x", 49032fcbc377Syt ahci_portp->ahciport_pending_tags); 49042fcbc377Syt 49052fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 49062fcbc377Syt 49072fcbc377Syt return (AHCI_SUCCESS); 49082fcbc377Syt } 49092fcbc377Syt 49102fcbc377Syt /* 491168d33a25Syt * AHCI_INTR_STATUS_SDBS means a Set Device Bits FIS has been received 491282263d52Syt * with the 'I' bit set and has been copied into system memory. It will 491382263d52Syt * be sent under the following situations: 491482263d52Syt * 491582263d52Syt * 1. NCQ command is completed 491682263d52Syt * 2. Asynchronous notification 491782263d52Syt * 491882263d52Syt * The completion of NCQ commands (READ/WRITE FPDMA QUEUED) is performed 491982263d52Syt * via the Set Device Bits FIS. When such event is generated, the software 492082263d52Syt * needs to read PxSACT register and compares the current value to the 492182263d52Syt * list of commands previously issue by software. ahciport_pending_ncq_tags 492282263d52Syt * keeps the tags of previously issued commands. 49232fcbc377Syt * 492468d33a25Syt * Asynchronous Notification is a feature in SATA II, which allows an 492568d33a25Syt * ATAPI device to send a signal to the host when media is inserted or 492668d33a25Syt * removed and avoids polling the device for media changes. The signal 492768d33a25Syt * sent to the host is a Set Device Bits FIS with the 'I' and 'N' bits 492882263d52Syt * set to '1'. At the moment, it's not supported yet. 49292fcbc377Syt */ 49302fcbc377Syt static int 493168d33a25Syt ahci_intr_set_device_bits(ahci_ctl_t *ahci_ctlp, 49322fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 49332fcbc377Syt { 493482263d52Syt uint32_t port_sactive; 493582263d52Syt uint32_t port_cmd_issue; 493682263d52Syt uint32_t issued_tags; 493782263d52Syt int issued_slot; 493882263d52Syt uint32_t finished_tags; 493982263d52Syt int finished_slot; 494082263d52Syt sata_pkt_t *satapkt; 49412fcbc377Syt 494282263d52Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 494368d33a25Syt "ahci_intr_set_device_bits enter: port %d", port); 49442fcbc377Syt 494582263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 494682263d52Syt if (!NCQ_CMD_IN_PROGRESS(ahci_portp)) { 494782263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 494882263d52Syt return (AHCI_SUCCESS); 494982263d52Syt } 495082263d52Syt 495182263d52Syt /* 495282263d52Syt * First the handler got which commands are finished by checking 495382263d52Syt * PxSACT register 495482263d52Syt */ 495582263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 495682263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 495768d33a25Syt 495882263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 495982263d52Syt ~port_sactive & AHCI_NCQ_SLOT_MASK(ahci_portp); 496082263d52Syt 496182263d52Syt AHCIDBG3(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 496282263d52Syt "ahci_intr_set_device_bits: port %d pending_ncq_tags = 0x%x " 496382263d52Syt "port_sactive = 0x%x", port, 496482263d52Syt ahci_portp->ahciport_pending_ncq_tags, port_sactive); 496582263d52Syt 496682263d52Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 496782263d52Syt "ahci_intr_set_device_bits: finished_tags = 0x%x", finished_tags); 496882263d52Syt 496982263d52Syt /* 497082263d52Syt * For NCQ commands, the software can determine which command has 497182263d52Syt * already been transmitted to the device by checking PxCI register. 497282263d52Syt */ 497382263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 497482263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 497582263d52Syt 497682263d52Syt issued_tags = ahci_portp->ahciport_pending_tags & 497782263d52Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 497882263d52Syt 497982263d52Syt AHCIDBG3(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 498082263d52Syt "ahci_intr_set_device_bits: port %d pending_tags = 0x%x " 498182263d52Syt "port_cmd_issue = 0x%x", port, 498282263d52Syt ahci_portp->ahciport_pending_tags, port_cmd_issue); 498382263d52Syt 498482263d52Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 498582263d52Syt "ahci_intr_set_device_bits: issued_tags = 0x%x", issued_tags); 498682263d52Syt 498782263d52Syt /* 498882263d52Syt * Clear ahciport_pending_tags bit when the corresponding command 498982263d52Syt * is already sent down to the device. 499082263d52Syt */ 499182263d52Syt while (issued_tags) { 499282263d52Syt issued_slot = ddi_ffs(issued_tags) - 1; 499382263d52Syt if (issued_slot == -1) { 499482263d52Syt goto next; 499582263d52Syt } 499682263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, issued_slot); 499782263d52Syt CLEAR_BIT(issued_tags, issued_slot); 499882263d52Syt } 499982263d52Syt 500082263d52Syt next: 500182263d52Syt while (finished_tags) { 500282263d52Syt finished_slot = ddi_ffs(finished_tags) - 1; 500382263d52Syt if (finished_slot == -1) { 500482263d52Syt goto out; 500582263d52Syt } 500682263d52Syt 500782263d52Syt /* The command is certainly transmitted to the device */ 500882263d52Syt ASSERT(!(ahci_portp->ahciport_pending_tags & 500982263d52Syt (0x1 << finished_slot))); 501082263d52Syt 501182263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 501282263d52Syt ASSERT(satapkt != NULL); 501382263d52Syt 501482263d52Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 501582263d52Syt "ahci_intr_set_device_bits: sending up pkt 0x%p " 501682263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 501782263d52Syt 501882263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, finished_slot); 501982263d52Syt CLEAR_BIT(finished_tags, finished_slot); 502082263d52Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 502182263d52Syt 502282263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 502368d33a25Syt } 502482263d52Syt out: 502582263d52Syt AHCIDBG3(AHCIDBG_PKTCOMP|AHCIDBG_NCQ, ahci_ctlp, 502682263d52Syt "ahci_intr_set_device_bits: port %d " 502782263d52Syt "pending_ncq_tags = 0x%x pending_tags = 0x%x", 502882263d52Syt port, ahci_portp->ahciport_pending_ncq_tags, 502982263d52Syt ahci_portp->ahciport_pending_tags); 503082263d52Syt 503182263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 50322fcbc377Syt 50332fcbc377Syt return (AHCI_SUCCESS); 50342fcbc377Syt } 50352fcbc377Syt 50362fcbc377Syt /* 503768d33a25Syt * 1=Change in Current Connect Status. 0=No change in Current Connect Status. 503868d33a25Syt * This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared 503968d33a25Syt * when PxSERR.DIAG.X is cleared. When PxSERR.DIAG.X is set to one, it 504068d33a25Syt * indicates a COMINIT signal was received. 50412fcbc377Syt * 504268d33a25Syt * Hot plug insertion is detected by reception of a COMINIT signal from the 504368d33a25Syt * device. On reception of unsolicited COMINIT, the HBA shall generate a 504468d33a25Syt * COMRESET. If the COMINIT is in responce to a COMRESET, then the HBA shall 504568d33a25Syt * begin the normal communication negotiation sequence as outlined in the 504668d33a25Syt * Serial ATA 1.0a specification. When a COMRESET is sent to the device the 504768d33a25Syt * PxSSTS.DET field shall be cleared to 0h. When a COMINIT is received, the 504868d33a25Syt * PxSSTS.DET field shall be set to 1h. When the communication negotiation 504968d33a25Syt * sequence is complete and PhyRdy is true the PxSSTS.DET field shall be set 505068d33a25Syt * to 3h. Therefore, at the moment the ahci driver is going to check PhyRdy 505168d33a25Syt * to handle hot plug insertion. In this interrupt handler, just do nothing 505268d33a25Syt * but print some log message and clear the bit. 50532fcbc377Syt */ 50542fcbc377Syt static int 505568d33a25Syt ahci_intr_port_connect_change(ahci_ctl_t *ahci_ctlp, 50562fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 50572fcbc377Syt { 5058689d74b0Syt #if AHCI_DEBUG 50592fcbc377Syt uint32_t port_serror; 5060689d74b0Syt #endif 50612fcbc377Syt 50622fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 50632fcbc377Syt 5064689d74b0Syt #if AHCI_DEBUG 50652fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 50662fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 50672fcbc377Syt 50682fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 506968d33a25Syt "ahci_intr_port_connect_change: port %d, " 507068d33a25Syt "port_serror = 0x%x", port, port_serror); 5071689d74b0Syt #endif 50722fcbc377Syt 507368d33a25Syt /* Clear PxSERR.DIAG.X to clear the interrupt bit */ 50742fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 50752fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 507682263d52Syt SERROR_EXCHANGED_ERR); 50772fcbc377Syt 50782fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 50792fcbc377Syt 50802fcbc377Syt return (AHCI_SUCCESS); 50812fcbc377Syt } 50822fcbc377Syt 50832fcbc377Syt /* 50842fcbc377Syt * Hot Plug Operation for platforms that support Mechanical Presence 50852fcbc377Syt * Switches. 50862fcbc377Syt * 50872fcbc377Syt * When set, it indicates that a mechanical presence switch attached to this 50882fcbc377Syt * port has been opened or closed, which may lead to a change in the connection 50892fcbc377Syt * state of the device. This bit is only valid if both CAP.SMPS and PxCMD.MPSP 50902fcbc377Syt * are set to '1'. 50912fcbc377Syt * 509268d33a25Syt * At the moment, this interrupt is not needed and disabled and we just log 50932fcbc377Syt * the debug message. 50942fcbc377Syt */ 50952fcbc377Syt static int 50962fcbc377Syt ahci_intr_device_mechanical_presence_status(ahci_ctl_t *ahci_ctlp, 50972fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 50982fcbc377Syt { 50992fcbc377Syt uint32_t cap_status, port_cmd_status; 51002fcbc377Syt 51012fcbc377Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 51022fcbc377Syt "ahci_intr_device_mechanical_presence_status enter, " 51032fcbc377Syt "port %d", port); 51042fcbc377Syt 51052fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 51062fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 51072fcbc377Syt 51082fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 51092fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 51102fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 51112fcbc377Syt 51122fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_SMPS) || 51132fcbc377Syt !(port_cmd_status & AHCI_CMD_STATUS_MPSP)) { 51142fcbc377Syt AHCIDBG2(AHCIDBG_INTR, ahci_ctlp, 51152fcbc377Syt "CAP.SMPS or PxCMD.MPSP is not set, so just ignore " 51162fcbc377Syt "the interrupt: cap_status = 0x%x, " 51172fcbc377Syt "port_cmd_status = 0x%x", cap_status, port_cmd_status); 51182fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 51192fcbc377Syt 51202fcbc377Syt return (AHCI_SUCCESS); 51212fcbc377Syt } 51222fcbc377Syt 5123689d74b0Syt #if AHCI_DEBUG 51242fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_MPSS) { 51252fcbc377Syt AHCIDBG2(AHCIDBG_INTR, ahci_ctlp, 51262fcbc377Syt "The mechanical presence switch is open: " 51272fcbc377Syt "port %d, port_cmd_status = 0x%x", 51282fcbc377Syt port, port_cmd_status); 51292fcbc377Syt } else { 51302fcbc377Syt AHCIDBG2(AHCIDBG_INTR, ahci_ctlp, 51312fcbc377Syt "The mechanical presence switch is close: " 51322fcbc377Syt "port %d, port_cmd_status = 0x%x", 51332fcbc377Syt port, port_cmd_status); 51342fcbc377Syt } 5135689d74b0Syt #endif 51362fcbc377Syt 51372fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 51382fcbc377Syt 51392fcbc377Syt return (AHCI_SUCCESS); 51402fcbc377Syt } 51412fcbc377Syt 51422fcbc377Syt /* 51432fcbc377Syt * Native Hot Plug Support. 51442fcbc377Syt * 51452fcbc377Syt * When set, it indicates that the internal PHYRDY signal changed state. 51462fcbc377Syt * This bit reflects the state of PxSERR.DIAG.N. 514768d33a25Syt * 514868d33a25Syt * There are three kinds of conditions to generate this interrupt event: 514968d33a25Syt * 1. a device is inserted 515068d33a25Syt * 2. a device is disconnected 515168d33a25Syt * 3. when the link enters/exits a Partial or Slumber interface power 515268d33a25Syt * management state 515368d33a25Syt * 515468d33a25Syt * If inteface power management is enabled for a port, the PxSERR.DIAG.N 515568d33a25Syt * bit may be set due to the link entering the Partial or Slumber power 515668d33a25Syt * management state, rather than due to a hot plug insertion or removal 515713bcbb7aSyt * event. So far, the interface power management is disabled, so the 515813bcbb7aSyt * driver can reliably get removal detection notification via the 515913bcbb7aSyt * PxSERR.DIAG.N bit. 51602fcbc377Syt */ 51612fcbc377Syt static int 51622fcbc377Syt ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp, 51632fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 51642fcbc377Syt { 51652fcbc377Syt uint32_t port_sstatus = 0; /* No dev present & PHY not established. */ 51662fcbc377Syt sata_device_t sdevice; 51672fcbc377Syt int dev_exists_now = 0; 51682fcbc377Syt int dev_existed_previously = 0; 51692fcbc377Syt 51702fcbc377Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 51712fcbc377Syt "ahci_intr_phyrdy_change enter, port %d", port); 51722fcbc377Syt 51732fcbc377Syt /* Clear PxSERR.DIAG.N to clear the interrupt bit */ 51742fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 51752fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 51762fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 517782263d52Syt SERROR_PHY_RDY_CHG); 51782fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 51792fcbc377Syt 51802fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 51812fcbc377Syt if ((ahci_ctlp->ahcictl_sata_hba_tran == NULL) || 51822fcbc377Syt (ahci_portp == NULL)) { 51832fcbc377Syt /* The whole controller setup is not yet done. */ 51842fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 51852fcbc377Syt return (AHCI_SUCCESS); 51862fcbc377Syt } 51872fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 51882fcbc377Syt 51892fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 51902fcbc377Syt 51912fcbc377Syt /* SStatus tells the presence of device. */ 51922fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 51932fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 51942fcbc377Syt 519582263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 519668d33a25Syt dev_exists_now = 1; 519768d33a25Syt } 51982fcbc377Syt 51992fcbc377Syt if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) { 52002fcbc377Syt dev_existed_previously = 1; 52012fcbc377Syt } 52022fcbc377Syt 520382263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_NODEV) { 520482263d52Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_NODEV; 520582263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 520682263d52Syt "ahci_intr_phyrdy_change: port %d " 520782263d52Syt "AHCI_PORT_FLAG_NODEV is cleared", port); 520882263d52Syt if (dev_exists_now == 0) 520982263d52Syt dev_existed_previously = 1; 521082263d52Syt } 521182263d52Syt 52122fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 521309121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 52142fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 52152fcbc377Syt sdevice.satadev_addr.pmport = 0; 52162fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 52172fcbc377Syt ahci_portp->ahciport_port_state = SATA_PSTATE_PWRON; 52182fcbc377Syt 52192fcbc377Syt if (dev_exists_now) { 52202fcbc377Syt if (dev_existed_previously) { 52212fcbc377Syt /* Things are fine now. The loss was temporary. */ 522268d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 522368d33a25Syt "ahci_intr_phyrdy_change port %d " 52242fcbc377Syt "device link lost/established", port); 52252fcbc377Syt 52262fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 52272fcbc377Syt sata_hba_event_notify( 52282fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 52292fcbc377Syt &sdevice, 52302fcbc377Syt SATA_EVNT_LINK_LOST|SATA_EVNT_LINK_ESTABLISHED); 52312fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 52322fcbc377Syt 52332fcbc377Syt } else { 523468d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 523568d33a25Syt "ahci_intr_phyrdy_change: port %d " 52362fcbc377Syt "device link established", port); 52372fcbc377Syt 52382fcbc377Syt /* A new device has been detected. */ 52390a4c4cecSXiao-Yu Zhang (void) ahci_port_reset(ahci_ctlp, ahci_portp, port); 524068d33a25Syt ahci_find_dev_signature(ahci_ctlp, ahci_portp, port); 52412fcbc377Syt 524268d33a25Syt /* Try to start the port */ 524368d33a25Syt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 524468d33a25Syt != AHCI_SUCCESS) { 524568d33a25Syt sdevice.satadev_state |= SATA_PSTATE_FAILED; 52462fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 524768d33a25Syt "ahci_intr_phyrdy_change: port %d failed " 52482fcbc377Syt "at start port", port); 52492fcbc377Syt } 52502fcbc377Syt 525182263d52Syt /* Clear the max queue depth for inserted device */ 525282263d52Syt ahci_portp->ahciport_max_ncq_tags = 0; 525382263d52Syt 52542fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 52552fcbc377Syt sata_hba_event_notify( 52562fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 52572fcbc377Syt &sdevice, 52582fcbc377Syt SATA_EVNT_LINK_ESTABLISHED); 52592fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 52602fcbc377Syt 52612fcbc377Syt } 52622fcbc377Syt } else { /* No device exists now */ 52632fcbc377Syt 52642fcbc377Syt if (dev_existed_previously) { 526568d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 526668d33a25Syt "ahci_intr_phyrdy_change: port %d " 52672fcbc377Syt "device link lost", port); 52682fcbc377Syt 52692fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 527068d33a25Syt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 52712fcbc377Syt ahci_portp, port); 52722fcbc377Syt 52732fcbc377Syt /* An existing device is lost. */ 52742fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_NONE; 52752fcbc377Syt 52762fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 52772fcbc377Syt sata_hba_event_notify( 52782fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 52792fcbc377Syt &sdevice, 52802fcbc377Syt SATA_EVNT_LINK_LOST); 52812fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 52822fcbc377Syt } 52832fcbc377Syt } 52842fcbc377Syt 52852fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 52862fcbc377Syt 52872fcbc377Syt return (AHCI_SUCCESS); 52882fcbc377Syt } 52892fcbc377Syt 52902fcbc377Syt /* 529168d33a25Syt * PxIS.UFS - Unknown FIS Error 52922fcbc377Syt * 529368d33a25Syt * This interrupt event means an unknown FIS was received and has been 529468d33a25Syt * copied into system memory. An unknown FIS is not considered an illegal 529568d33a25Syt * FIS, unless the length received is more than 64 bytes. If an unknown 529668d33a25Syt * FIS arrives with length <= 64 bytes, it is posted and the HBA continues 529768d33a25Syt * normal operation. If the unknown FIS is more than 64 bytes, then it 529868d33a25Syt * won't be posted to memory and PxSERR.ERR.P will be set, which is then 529968d33a25Syt * a fatal error. 53002fcbc377Syt * 530168d33a25Syt * PxIS.OFS - Overflow Error 53022fcbc377Syt * 530368d33a25Syt * Command list overflow is defined as software building a command table 530468d33a25Syt * that has fewer total bytes than the transaction given to the device. 530568d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 530668d33a25Syt * will be no room to put the data. 53072fcbc377Syt * 530868d33a25Syt * For an overflow on data read, either PIO or DMA, the HBA will set 530968d33a25Syt * PxIS.OFS, and the HBA will do a best effort to continue, and it's a 531068d33a25Syt * non-fatal error when the HBA can continues. Sometimes, it will cause 531168d33a25Syt * a fatal error and need the software to do something. 53122fcbc377Syt * 531368d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 531468d33a25Syt * DMA and PIO, and it's a fatal error, and a COMRESET is required by 531568d33a25Syt * software to clean up from this serious error. 53162fcbc377Syt * 531768d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 531868d33a25Syt * 531968d33a25Syt * This interrupt event indicates that the HBA encountered an error on 532068d33a25Syt * the Serial ATA interface but was able to continue operation. The kind 532168d33a25Syt * of error usually occurred during a non-Data FIS, and under this condition 532268d33a25Syt * the FIS will be re-transmitted by HBA automatically. 53232fcbc377Syt * 532468d33a25Syt * When the FMA is implemented, there should be a stat structure to 532568d33a25Syt * record how many every kind of error happens. 53262fcbc377Syt */ 53272fcbc377Syt static int 532868d33a25Syt ahci_intr_non_fatal_error(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 532968d33a25Syt uint8_t port, uint32_t intr_status) 53302fcbc377Syt { 53312fcbc377Syt uint32_t port_serror; 533268d33a25Syt #if AHCI_DEBUG 53332fcbc377Syt uint32_t port_cmd_status; 533482263d52Syt uint32_t port_cmd_issue; 533582263d52Syt uint32_t port_sactive; 53362fcbc377Syt int current_slot; 533782263d52Syt uint32_t current_tags; 533868d33a25Syt sata_pkt_t *satapkt; 533938547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 534038547057Sying tian - Beijing China uint32_t cmd_dmacount; 534168d33a25Syt #endif 53422fcbc377Syt 53432fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 53442fcbc377Syt 53452fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 53462fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 53472fcbc377Syt 534868d33a25Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY|AHCIDBG_ERRS, ahci_ctlp, 534968d33a25Syt "ahci_intr_non_fatal_error: port %d, " 53502fcbc377Syt "port_serror = 0x%x", port, port_serror); 53512fcbc377Syt 5352a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 1); 535368d33a25Syt 535468d33a25Syt if (intr_status & AHCI_INTR_STATUS_UFS) { 535568d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 535668d33a25Syt "ahci port %d has unknown FIS error", port); 53572fcbc377Syt 535868d33a25Syt /* Clear the interrupt bit by clearing PxSERR.DIAG.F */ 535968d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 536068d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 536182263d52Syt SERROR_FIS_TYPE); 536268d33a25Syt } 536368d33a25Syt 536468d33a25Syt #if AHCI_DEBUG 536568d33a25Syt if (intr_status & AHCI_INTR_STATUS_OFS) { 536668d33a25Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 536768d33a25Syt "ahci port %d has overflow error", port); 536868d33a25Syt } 536968d33a25Syt 537068d33a25Syt if (intr_status & AHCI_INTR_STATUS_INFS) { 537168d33a25Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 537268d33a25Syt "ahci port %d has interface non fatal error", port); 537368d33a25Syt } 53742fcbc377Syt 53752fcbc377Syt /* 53762fcbc377Syt * Record the error occurred command's slot. 53772fcbc377Syt */ 537882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 537982263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 538082263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 538182263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 538282263d52Syt 538382263d52Syt current_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 538482263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 53852fcbc377Syt 538682263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 538782263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 538882263d52Syt ASSERT(satapkt != NULL); 538982263d52Syt ASSERT(current_slot == 0); 539082263d52Syt } else { 539182263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 539282263d52Syt } 53932fcbc377Syt 539482263d52Syt if (satapkt != NULL) { 539582263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 539682263d52Syt "ahci_intr_non_fatal_error: pending_tags = 0x%x " 539782263d52Syt "cmd 0x%x", ahci_portp->ahciport_pending_tags, 539882263d52Syt satapkt->satapkt_cmd.satacmd_cmd_reg); 53992fcbc377Syt 540082263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 540182263d52Syt "ahci_intr_non_fatal_error: port %d, " 540282263d52Syt "satapkt 0x%p is being processed when error occurs", 540382263d52Syt port, (void *)satapkt); 540438547057Sying tian - Beijing China 540538547057Sying tian - Beijing China /* 540638547057Sying tian - Beijing China * PRD Byte Count field of command header is not 540738547057Sying tian - Beijing China * required to reflect the total number of bytes 540838547057Sying tian - Beijing China * transferred when an overflow occurs, so here 540938547057Sying tian - Beijing China * just log the value. 541038547057Sying tian - Beijing China */ 541138547057Sying tian - Beijing China cmd_dmacount = 541238547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[current_slot]; 541338547057Sying tian - Beijing China if (cmd_dmacount) { 541438547057Sying tian - Beijing China cmd_header = &ahci_portp-> 541538547057Sying tian - Beijing China ahciport_cmd_list[current_slot]; 541638547057Sying tian - Beijing China AHCIDBG3(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 541738547057Sying tian - Beijing China "ahci_intr_non_fatal_error: port %d, " 541838547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 541938547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 542038547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 542138547057Sying tian - Beijing China cmd_dmacount); 542238547057Sying tian - Beijing China } 542382263d52Syt } 5424a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 542582263d52Syt /* 542682263d52Syt * For queued command, list those command which have already 542782263d52Syt * been transmitted to the device and still not completed. 542882263d52Syt */ 542982263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 543082263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 543182263d52Syt 543282263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 543382263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 543482263d52Syt 543582263d52Syt AHCIDBG3(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, ahci_ctlp, 543682263d52Syt "ahci_intr_non_fatal_error: pending_ncq_tags = 0x%x " 543782263d52Syt "port_sactive = 0x%x port_cmd_issue = 0x%x", 543882263d52Syt ahci_portp->ahciport_pending_ncq_tags, 543982263d52Syt port_sactive, port_cmd_issue); 544082263d52Syt 544182263d52Syt current_tags = ahci_portp->ahciport_pending_ncq_tags & 544282263d52Syt port_sactive & ~port_cmd_issue & 544382263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 544482263d52Syt 544582263d52Syt while (current_tags) { 544682263d52Syt current_slot = ddi_ffs(current_tags) - 1; 544782263d52Syt if (current_slot == -1) { 544882263d52Syt goto out; 544982263d52Syt } 545082263d52Syt 545182263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 545282263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, 545382263d52Syt ahci_ctlp, "ahci_intr_non_fatal_error: " 545482263d52Syt "port %d, satapkt 0x%p is outstanding when " 545582263d52Syt "error occurs", port, (void *)satapkt); 5456a419422eSXiao-Yu Zhang 5457a419422eSXiao-Yu Zhang CLEAR_BIT(current_tags, current_slot); 545882263d52Syt } 54592fcbc377Syt } 546082263d52Syt out: 54612fcbc377Syt #endif 54622fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 54632fcbc377Syt 54642fcbc377Syt return (AHCI_SUCCESS); 54652fcbc377Syt } 54662fcbc377Syt 54672fcbc377Syt /* 546868d33a25Syt * According to the AHCI spec, the error types include system memory 546968d33a25Syt * errors, interface errors, port multiplier errors, device errors, 547068d33a25Syt * command list overflow, command list underflow, native command 547168d33a25Syt * queuing tag errors and pio data transfer errors. 547268d33a25Syt * 547368d33a25Syt * System memory errors such as target abort, master abort, and parity 547468d33a25Syt * may cause the host to stop, and they are serious errors and needed 547568d33a25Syt * to be recovered with software intervention. When system software 547668d33a25Syt * has given a pointer to the HBA that doesn't exist in physical memory, 547768d33a25Syt * a master/target abort error occurs, and PxIS.HBFS will be set. A 547868d33a25Syt * data error such as CRC or parity occurs, the HBA aborts the transfer 547968d33a25Syt * (if necessary) and PxIS.HBDS will be set. 548068d33a25Syt * 548168d33a25Syt * Interface errors are errors that occur due to electrical issues on 548268d33a25Syt * the interface, or protocol miscommunication between the device and 548368d33a25Syt * HBA, and the respective PxSERR register bit will be set. And PxIS.IFS 548468d33a25Syt * (fatal) or PxIS.INFS (non-fatal) will be set. The conditions that 548568d33a25Syt * causes PxIS.IFS/PxIS.INFS to be set are 548668d33a25Syt * 1. in PxSERR.ERR, P bit is set to '1' 548768d33a25Syt * 2. in PxSERR.DIAG, C or H bit is set to '1' 548868d33a25Syt * 3. PhyRdy drop unexpectly, N bit is set to '1' 548968d33a25Syt * If the error occurred during a non-data FIS, the FIS must be 549068d33a25Syt * retransmitted, and the error is non-fatal and PxIS.INFS is set. If 549168d33a25Syt * the error occurred during a data FIS, the transfer will stop, so 549268d33a25Syt * the error is fatal and PxIS.IFS is set. 549368d33a25Syt * 549468d33a25Syt * When a FIS arrives that updates the taskfile, the HBA checks to see 549568d33a25Syt * if PxTFD.STS.ERR is set. If yes, PxIS.TFES will be set and the HBA 549668d33a25Syt * stops processing any more commands. 54972fcbc377Syt * 549868d33a25Syt * Command list overflow is defined as software building a command table 549968d33a25Syt * that has fewer total bytes than the transaction given to the device. 550068d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 550168d33a25Syt * will be no room to put the data. For an overflow on data read, either 550268d33a25Syt * PIO or DMA, the HBA will set PxIS.OFS, and it's a non-fatal error. 550368d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 550468d33a25Syt * DMA and PIO, and a COMRESET is required by software to clean up from 550568d33a25Syt * this serious error. 55062fcbc377Syt * 550768d33a25Syt * Command list underflow is defined as software building a command 550868d33a25Syt * table that has more total bytes than the transaction given to the 550968d33a25Syt * device. For data writes, both PIO and DMA, the device will detect 551068d33a25Syt * an error and end the transfer. And these errors are most likely going 551168d33a25Syt * to be fatal errors that will cause the port to be restarted. For 551268d33a25Syt * data reads, the HBA updates its PRD byte count, and may be 551368d33a25Syt * able to continue normally, but is not required to. And The HBA is 551468d33a25Syt * not required to detect underflow conditions for native command 551568d33a25Syt * queuing command. 551668d33a25Syt * 551768d33a25Syt * The HBA does not actively check incoming DMA Setup FISes to ensure 551868d33a25Syt * that the PxSACT register bit for that slot is set. Existing error 551968d33a25Syt * mechanisms, such as host bus failure, or bad protocol, are used to 552068d33a25Syt * recover from this case. 552168d33a25Syt * 552268d33a25Syt * In accordance with Serial ATA 1.0a, DATA FISes prior to the final 552368d33a25Syt * DATA FIS must be an integral number of Dwords. If the HBA receives 552468d33a25Syt * a request which is not an integral number of Dwords, the HBA 552568d33a25Syt * set PxSERR.ERR.P to '1', set PxIS.IFS to '1' and stop running until 552668d33a25Syt * software restarts the port. And the HBA ensures that the size 552768d33a25Syt * of the DATA FIS received during a PIO command matches the size in 552868d33a25Syt * the Transfer Cound field of the preceding PIO Setup FIS, if not, the 552968d33a25Syt * HBA sets PxSERR.ERR.P to '1', set PxIS.IFS to '1', and then 553068d33a25Syt * stop running until software restarts the port. 55312fcbc377Syt */ 55322fcbc377Syt /* 553368d33a25Syt * the fatal errors include PxIS.IFS, PxIS.HBDS, PxIS.HBFS and PxIS.TFES. 553468d33a25Syt * 553568d33a25Syt * PxIS.IFS indicates that the hba encountered an error on the serial ata 553668d33a25Syt * interface which caused the transfer to stop. 55372fcbc377Syt * 553868d33a25Syt * PxIS.HBDS indicates that the hba encountered a data error 553968d33a25Syt * (uncorrectable ecc/parity) when reading from or writing to system memory. 554068d33a25Syt * 554168d33a25Syt * PxIS.HBFS indicates that the hba encountered a host bus error that it 554268d33a25Syt * cannot recover from, such as a bad software pointer. 554368d33a25Syt * 554468d33a25Syt * PxIS.TFES is set whenever the status register is updated by the device 554568d33a25Syt * and the error bit (bit 0) is set. 55462fcbc377Syt */ 55472fcbc377Syt static int 554882263d52Syt ahci_intr_fatal_error(ahci_ctl_t *ahci_ctlp, 554982263d52Syt ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 55502fcbc377Syt { 555168d33a25Syt uint32_t port_cmd_status; 55522fcbc377Syt uint32_t port_serror; 555368d33a25Syt uint32_t task_file_status; 555468d33a25Syt int failed_slot; 555582263d52Syt sata_pkt_t *spkt = NULL; 555668d33a25Syt uint8_t err_byte; 555768d33a25Syt ahci_event_arg_t *args; 5558a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 55592fcbc377Syt 55602fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 55612fcbc377Syt 556268d33a25Syt /* 556368d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 556468d33a25Syt * SATA_DTYPE_NONE. 556568d33a25Syt */ 556668d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 556768d33a25Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 556868d33a25Syt "ahci_intr_fatal_error: port %d no device attached, " 556968d33a25Syt "and just return without doing anything", port); 557068d33a25Syt goto out0; 557168d33a25Syt } 55722fcbc377Syt 5573f8a673adSying tian - Beijing China if (intr_status & AHCI_INTR_STATUS_TFES) { 5574f8a673adSying tian - Beijing China task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5575f8a673adSying tian - Beijing China (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 5576f8a673adSying tian - Beijing China AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 5577f8a673adSying tian - Beijing China "ahci_intr_fatal_error: port %d " 5578f8a673adSying tian - Beijing China "task_file_status = 0x%x", port, task_file_status); 5579f8a673adSying tian - Beijing China } 5580f8a673adSying tian - Beijing China 558182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 558282263d52Syt /* 558382263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 558482263d52Syt * was processing when the error occurred. 558582263d52Syt */ 558682263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 558782263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 558882263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 558982263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 55902fcbc377Syt 559182263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 559268d33a25Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 559382263d52Syt "ahci_intr_fatal_error: spkt 0x%p is being processed when " 559482263d52Syt "fatal error occurred for port %d", spkt, port); 55952fcbc377Syt 559682263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 559782263d52Syt err_byte = (task_file_status & AHCI_TFD_ERR_MASK) 559882263d52Syt >> AHCI_TFD_ERR_SHIFT; 55992fcbc377Syt 560082263d52Syt /* 560182263d52Syt * Won't emit the error message if it is an IDENTIFY 560282263d52Syt * DEVICE command sent to an ATAPI device. 560382263d52Syt */ 560482263d52Syt if ((spkt != NULL) && 560582263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == 560682263d52Syt SATAC_ID_DEVICE) && 560782263d52Syt (err_byte == SATA_ERROR_ABORT)) 560882263d52Syt goto out1; 560982263d52Syt 561082263d52Syt /* 561182263d52Syt * Won't emit the error message if it is an ATAPI PACKET 561282263d52Syt * command 561382263d52Syt */ 561482263d52Syt if ((spkt != NULL) && 561582263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_PACKET)) 561682263d52Syt goto out1; 561782263d52Syt } 561868d33a25Syt } 56192fcbc377Syt 56207095af19Sying tian - Beijing China /* print the fatal error type */ 562168d33a25Syt ahci_log_fatal_error_message(ahci_ctlp, port, intr_status); 56222fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 56232fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 56247095af19Sying tian - Beijing China 56257095af19Sying tian - Beijing China /* print PxSERR related error message */ 5626a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 0); 56277095af19Sying tian - Beijing China 56287095af19Sying tian - Beijing China /* print task file register value */ 56297095af19Sying tian - Beijing China if (intr_status & AHCI_INTR_STATUS_TFES) { 56307095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d task_file_status " 56317095af19Sying tian - Beijing China "= 0x%x", instance, port, task_file_status); 56327095af19Sying tian - Beijing China } 56337095af19Sying tian - Beijing China 5634a9440e8dSyt out1: 563568d33a25Syt /* Prepare the argument for the taskq */ 563668d33a25Syt args = ahci_portp->ahciport_event_args; 563768d33a25Syt args->ahciea_ctlp = (void *)ahci_ctlp; 563868d33a25Syt args->ahciea_portp = (void *)ahci_portp; 563968d33a25Syt args->ahciea_event = intr_status; 564068d33a25Syt 564168d33a25Syt /* Start the taskq to handle error recovery */ 5642f8a673adSying tian - Beijing China if ((ddi_taskq_dispatch(ahci_portp->ahciport_event_taskq, 564368d33a25Syt ahci_events_handler, 564468d33a25Syt (void *)args, DDI_NOSLEEP)) != DDI_SUCCESS) { 5645a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci start taskq for event handler " 5646a9440e8dSyt "failed", instance); 564768d33a25Syt } 564868d33a25Syt out0: 56492fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 56502fcbc377Syt 56512fcbc377Syt return (AHCI_SUCCESS); 56522fcbc377Syt } 56532fcbc377Syt 56542fcbc377Syt /* 56552fcbc377Syt * Hot Plug Operation for platforms that support Cold Presence Detect. 56562fcbc377Syt * 56572fcbc377Syt * When set, a device status has changed as detected by the cold presence 56582fcbc377Syt * detect logic. This bit can either be set due to a non-connected port 56592fcbc377Syt * receiving a device, or a connected port having its device removed. 56602fcbc377Syt * This bit is only valid if the port supports cold presence detect as 56612fcbc377Syt * indicated by PxCMD.CPD set to '1'. 56622fcbc377Syt * 566368d33a25Syt * At the moment, this interrupt is not needed and disabled and we just 566468d33a25Syt * log the debug message. 56652fcbc377Syt */ 56662fcbc377Syt static int 56672fcbc377Syt ahci_intr_cold_port_detect(ahci_ctl_t *ahci_ctlp, 56682fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 56692fcbc377Syt { 56702fcbc377Syt uint32_t port_cmd_status; 56712fcbc377Syt sata_device_t sdevice; 56722fcbc377Syt 56732fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 56742fcbc377Syt "ahci_intr_cold_port_detect enter, port %d", port); 56752fcbc377Syt 56762fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 56772fcbc377Syt 56782fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 56792fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 56802fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_CPD)) { 56812fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 56822fcbc377Syt "port %d does not support cold presence detect, so " 56832fcbc377Syt "we just ignore this interrupt", port); 56842fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 56852fcbc377Syt return (AHCI_SUCCESS); 56862fcbc377Syt } 56872fcbc377Syt 56882fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 56892fcbc377Syt "port %d device status has changed", port); 56902fcbc377Syt 56912fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 569209121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 56932fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 56942fcbc377Syt sdevice.satadev_addr.pmport = 0; 56952fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 56962fcbc377Syt 56972fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_CPS) { 56982fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 56992fcbc377Syt "port %d: a device is hot plugged", port); 57002fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 57012fcbc377Syt sata_hba_event_notify( 57022fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 57032fcbc377Syt &sdevice, 57042fcbc377Syt SATA_EVNT_DEVICE_ATTACHED); 57052fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 57062fcbc377Syt 57072fcbc377Syt } else { 57082fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 57092fcbc377Syt "port %d: a device is hot unplugged", port); 57102fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 57112fcbc377Syt sata_hba_event_notify( 57122fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 57132fcbc377Syt &sdevice, 57142fcbc377Syt SATA_EVNT_DEVICE_DETACHED); 57152fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 57162fcbc377Syt } 57172fcbc377Syt 57182fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 57192fcbc377Syt 57202fcbc377Syt return (AHCI_SUCCESS); 57212fcbc377Syt } 57222fcbc377Syt 57232fcbc377Syt /* 57242fcbc377Syt * Enable the interrupts for a particular port. 57252fcbc377Syt * 57262fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 57272fcbc377Syt * is called. 57282fcbc377Syt */ 57292fcbc377Syt static void 5730689d74b0Syt ahci_enable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 57312fcbc377Syt { 57322fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 57332fcbc377Syt "ahci_enable_port_intrs enter, port %d", port); 57342fcbc377Syt 57352fcbc377Syt /* 57362fcbc377Syt * Clear port interrupt status before enabling interrupt 57372fcbc377Syt */ 57382fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 57392fcbc377Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 57402fcbc377Syt AHCI_PORT_INTR_MASK); 57412fcbc377Syt 57422fcbc377Syt /* 57432fcbc377Syt * Clear the pending bit from IS.IPS 57442fcbc377Syt */ 57452fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 57462fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (1 << port)); 57472fcbc377Syt 57482fcbc377Syt /* 57492fcbc377Syt * Enable the following interrupts: 57502fcbc377Syt * Device to Host Register FIS Interrupt (DHRS) 57512fcbc377Syt * PIO Setup FIS Interrupt (PSS) 575282263d52Syt * Set Device Bits Interrupt (SDBS) 57532fcbc377Syt * Unknown FIS Interrupt (UFS) 57542fcbc377Syt * Port Connect Change Status (PCS) 57552fcbc377Syt * PhyRdy Change Status (PRCS) 57562fcbc377Syt * Overflow Status (OFS) 57572fcbc377Syt * Interface Non-fatal Error Status (INFS) 57582fcbc377Syt * Interface Fatal Error Status (IFS) 57592fcbc377Syt * Host Bus Data Error Status (HBDS) 57602fcbc377Syt * Host Bus Fatal Error Status (HBFS) 57612fcbc377Syt * Task File Error Status (TFES) 57622fcbc377Syt */ 57632fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 57642fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 57652fcbc377Syt (AHCI_INTR_STATUS_DHRS | 57662fcbc377Syt AHCI_INTR_STATUS_PSS | 576782263d52Syt AHCI_INTR_STATUS_SDBS | 57682fcbc377Syt AHCI_INTR_STATUS_UFS | 576968d33a25Syt AHCI_INTR_STATUS_DPS | 57702fcbc377Syt AHCI_INTR_STATUS_PCS | 57712fcbc377Syt AHCI_INTR_STATUS_PRCS | 57722fcbc377Syt AHCI_INTR_STATUS_OFS | 57732fcbc377Syt AHCI_INTR_STATUS_INFS | 57742fcbc377Syt AHCI_INTR_STATUS_IFS | 57752fcbc377Syt AHCI_INTR_STATUS_HBDS | 57762fcbc377Syt AHCI_INTR_STATUS_HBFS | 57772fcbc377Syt AHCI_INTR_STATUS_TFES)); 57782fcbc377Syt } 57792fcbc377Syt 57802fcbc377Syt /* 57812fcbc377Syt * Enable interrupts for all the ports. 57822fcbc377Syt * 57832fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 57842fcbc377Syt * is called. 57852fcbc377Syt */ 57862fcbc377Syt static void 57872fcbc377Syt ahci_enable_all_intrs(ahci_ctl_t *ahci_ctlp) 57882fcbc377Syt { 57892fcbc377Syt uint32_t ghc_control; 57902fcbc377Syt 57912fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_enable_all_intrs enter"); 57922fcbc377Syt 57932fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 57942fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 57952fcbc377Syt 57962fcbc377Syt ghc_control |= AHCI_HBA_GHC_IE; 57972fcbc377Syt 57982fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 57992fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 58002fcbc377Syt } 58012fcbc377Syt 58022fcbc377Syt /* 58032fcbc377Syt * Disable interrupts for a particular port. 58042fcbc377Syt * 58052fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 58062fcbc377Syt * is called. 58072fcbc377Syt */ 58082fcbc377Syt static void 5809689d74b0Syt ahci_disable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 58102fcbc377Syt { 58112fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 58122fcbc377Syt "ahci_disable_port_intrs enter, port %d", port); 58132fcbc377Syt 58142fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 58152fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 0); 58162fcbc377Syt } 58172fcbc377Syt 58182fcbc377Syt /* 58192fcbc377Syt * Disable interrupts for the whole HBA. 58202fcbc377Syt * 58212fcbc377Syt * The global bit is cleared, then all interrupt sources from all 58222fcbc377Syt * ports are disabled. 58232fcbc377Syt * 58242fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 58252fcbc377Syt * is called. 58262fcbc377Syt */ 58272fcbc377Syt static void 58282fcbc377Syt ahci_disable_all_intrs(ahci_ctl_t *ahci_ctlp) 58292fcbc377Syt { 58302fcbc377Syt uint32_t ghc_control; 58312fcbc377Syt 58322fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_disable_all_intrs enter"); 58332fcbc377Syt 58342fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 58352fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 58362fcbc377Syt 58372fcbc377Syt ghc_control &= ~ AHCI_HBA_GHC_IE; 58382fcbc377Syt 58392fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 58402fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 58412fcbc377Syt } 58422fcbc377Syt 58432fcbc377Syt /* 58442c742e1fSying tian - Beijing China * Handle FIXED or MSI interrupts. 58452fcbc377Syt */ 58462fcbc377Syt /* 58472c742e1fSying tian - Beijing China * According to AHCI spec, the HBA may support several interrupt modes: 58482c742e1fSying tian - Beijing China * * pin based interrupts (FIXED) 58492c742e1fSying tian - Beijing China * * single MSI message interrupts 58502c742e1fSying tian - Beijing China * * multiple MSI based message interrupts 58512c742e1fSying tian - Beijing China * 58522c742e1fSying tian - Beijing China * For pin based interrupts, the software interrupt handler need to check IS 58532c742e1fSying tian - Beijing China * register to find out which port has pending interrupts. And then check 58542c742e1fSying tian - Beijing China * PxIS register to find out which interrupt events happened on that port. 58552c742e1fSying tian - Beijing China * 58562c742e1fSying tian - Beijing China * For single MSI message interrupts, MSICAP.MC.MSIE is set with '1', and 58572c742e1fSying tian - Beijing China * MSICAP.MC.MME is set with '0'. This mode is similar to pin based interrupts 58582c742e1fSying tian - Beijing China * in that software interrupt handler need to check IS register to determine 58592c742e1fSying tian - Beijing China * which port triggered the interrupts since it uses a single message for all 58602c742e1fSying tian - Beijing China * port interrupts. 58612c742e1fSying tian - Beijing China * 58622c742e1fSying tian - Beijing China * HBA may optionally support multiple MSI message for better performance. In 58632c742e1fSying tian - Beijing China * this mode, each port may have its own interrupt message, and thus generation 58642c742e1fSying tian - Beijing China * of interrupts is no longer controlled through the IS register. MSICAP.MC.MMC 58652c742e1fSying tian - Beijing China * represents a power-of-2 wrapper on the number of implemented ports, and 58662c742e1fSying tian - Beijing China * the mapping of ports to interrupts is done in a 1-1 relationship, up to the 58672c742e1fSying tian - Beijing China * maximum number of assigned interrupts. When the number of MSI messages 58682c742e1fSying tian - Beijing China * allocated is less than the number requested, then hardware may have two 58692c742e1fSying tian - Beijing China * implementation behaviors: 58702c742e1fSying tian - Beijing China * * assign each ports its own interrupt and then force all additional 58712c742e1fSying tian - Beijing China * ports to share the last interrupt message, and this condition is 58722c742e1fSying tian - Beijing China * indicated by clearing GHC.MRSM to '0' 58732c742e1fSying tian - Beijing China * * revert to single MSI mode, indicated by setting GHC.MRSM to '1' 58742c742e1fSying tian - Beijing China * When multiple-message MSI is enabled, hardware will still set IS register 58752c742e1fSying tian - Beijing China * as single message case. And this IS register may be used by software when 58762c742e1fSying tian - Beijing China * fewer than the requested number of messages is granted in order to determine 58772c742e1fSying tian - Beijing China * which port had the interrupt. 58782c742e1fSying tian - Beijing China * 58792c742e1fSying tian - Beijing China * Note: The current ahci driver only supports the first two interrupt modes: 58802c742e1fSying tian - Beijing China * pin based interrupts and single MSI message interrupts, and the reason 58812c742e1fSying tian - Beijing China * is indicated in below code. 58822fcbc377Syt */ 58832fcbc377Syt static int 58842c742e1fSying tian - Beijing China ahci_add_intrs(ahci_ctl_t *ahci_ctlp, int intr_type) 58852fcbc377Syt { 58862fcbc377Syt dev_info_t *dip = ahci_ctlp->ahcictl_dip; 58872fcbc377Syt int count, avail, actual; 58882c742e1fSying tian - Beijing China int i, rc; 58892fcbc377Syt 58902c742e1fSying tian - Beijing China AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 58912c742e1fSying tian - Beijing China "ahci_add_intrs enter interrupt type 0x%x", intr_type); 58922fcbc377Syt 58932fcbc377Syt /* get number of interrupts. */ 58942c742e1fSying tian - Beijing China rc = ddi_intr_get_nintrs(dip, intr_type, &count); 58952fcbc377Syt if ((rc != DDI_SUCCESS) || (count == 0)) { 58962fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 58972fcbc377Syt "ddi_intr_get_nintrs() failed, " 58982fcbc377Syt "rc %d count %d\n", rc, count); 58992fcbc377Syt return (DDI_FAILURE); 59002fcbc377Syt } 59012fcbc377Syt 59022fcbc377Syt /* get number of available interrupts. */ 59032c742e1fSying tian - Beijing China rc = ddi_intr_get_navail(dip, intr_type, &avail); 59042fcbc377Syt if ((rc != DDI_SUCCESS) || (avail == 0)) { 59052fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59062fcbc377Syt "ddi_intr_get_navail() failed, " 59072fcbc377Syt "rc %d avail %d\n", rc, avail); 59082fcbc377Syt return (DDI_FAILURE); 59092fcbc377Syt } 59102fcbc377Syt 5911689d74b0Syt #if AHCI_DEBUG 59122fcbc377Syt if (avail < count) { 59132fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59142c742e1fSying tian - Beijing China "ddi_intr_get_nintrs returned %d, navail() returned %d", 59152fcbc377Syt count, avail); 59162fcbc377Syt } 5917689d74b0Syt #endif 59182fcbc377Syt 59192c742e1fSying tian - Beijing China /* 59202c742e1fSying tian - Beijing China * Note: So far Solaris restricts the maximum number of messages for 59212c742e1fSying tian - Beijing China * x86 to 2, that is avail is 2, so here we set the count with 1 to 59222c742e1fSying tian - Beijing China * force the driver to use single MSI message interrupt. In future if 59232c742e1fSying tian - Beijing China * Solaris remove the restriction, then we need to delete the below 59242c742e1fSying tian - Beijing China * code and try to use multiple interrupt routine to gain better 59252c742e1fSying tian - Beijing China * performance. 59262c742e1fSying tian - Beijing China */ 59272c742e1fSying tian - Beijing China if ((intr_type == DDI_INTR_TYPE_MSI) && (count > 1)) { 59282c742e1fSying tian - Beijing China AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 59292c742e1fSying tian - Beijing China "force to use one interrupt routine though the " 59302c742e1fSying tian - Beijing China "HBA supports %d interrupt", count); 59312c742e1fSying tian - Beijing China count = 1; 59322c742e1fSying tian - Beijing China } 59332c742e1fSying tian - Beijing China 59342fcbc377Syt /* Allocate an array of interrupt handles. */ 59352fcbc377Syt ahci_ctlp->ahcictl_intr_size = count * sizeof (ddi_intr_handle_t); 59362fcbc377Syt ahci_ctlp->ahcictl_intr_htable = 59372fcbc377Syt kmem_alloc(ahci_ctlp->ahcictl_intr_size, KM_SLEEP); 59382fcbc377Syt 59392fcbc377Syt /* call ddi_intr_alloc(). */ 59402fcbc377Syt rc = ddi_intr_alloc(dip, ahci_ctlp->ahcictl_intr_htable, 59412c742e1fSying tian - Beijing China intr_type, 0, count, &actual, DDI_INTR_ALLOC_NORMAL); 59422fcbc377Syt 59432fcbc377Syt if ((rc != DDI_SUCCESS) || (actual == 0)) { 59442c742e1fSying tian - Beijing China AHCIDBG4(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59452c742e1fSying tian - Beijing China "ddi_intr_alloc() failed, rc %d count %d actual %d " 59462c742e1fSying tian - Beijing China "avail %d\n", rc, count, actual, avail); 59472fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 59482fcbc377Syt ahci_ctlp->ahcictl_intr_size); 59492fcbc377Syt return (DDI_FAILURE); 59502fcbc377Syt } 59512fcbc377Syt 59522fcbc377Syt /* use interrupt count returned */ 5953689d74b0Syt #if AHCI_DEBUG 59542fcbc377Syt if (actual < count) { 59552fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59562fcbc377Syt "Requested: %d, Received: %d", count, actual); 59572fcbc377Syt } 5958689d74b0Syt #endif 59592fcbc377Syt 59602fcbc377Syt ahci_ctlp->ahcictl_intr_cnt = actual; 59612fcbc377Syt 59622fcbc377Syt /* 59632c742e1fSying tian - Beijing China * Get priority for first, assume remaining are all the same. 59642fcbc377Syt */ 59652fcbc377Syt if (ddi_intr_get_pri(ahci_ctlp->ahcictl_intr_htable[0], 59662fcbc377Syt &ahci_ctlp->ahcictl_intr_pri) != DDI_SUCCESS) { 59672fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59682fcbc377Syt "ddi_intr_get_pri() failed"); 59692fcbc377Syt 59702fcbc377Syt /* Free already allocated intr. */ 59712c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 59722c742e1fSying tian - Beijing China (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[i]); 59732fcbc377Syt } 59742fcbc377Syt 59752fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 59762fcbc377Syt ahci_ctlp->ahcictl_intr_size); 59772fcbc377Syt return (DDI_FAILURE); 59782fcbc377Syt } 59792fcbc377Syt 59802fcbc377Syt /* Test for high level interrupt. */ 59812fcbc377Syt if (ahci_ctlp->ahcictl_intr_pri >= ddi_intr_get_hilevel_pri()) { 59822fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 59832c742e1fSying tian - Beijing China "ahci_add_intrs: Hi level intr not supported"); 59842fcbc377Syt 59852fcbc377Syt /* Free already allocated intr. */ 59862c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 59872c742e1fSying tian - Beijing China (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[i]); 59882fcbc377Syt } 59892fcbc377Syt 59902fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 59912fcbc377Syt sizeof (ddi_intr_handle_t)); 59922fcbc377Syt 59932fcbc377Syt return (DDI_FAILURE); 59942fcbc377Syt } 59952fcbc377Syt 59962fcbc377Syt /* Call ddi_intr_add_handler(). */ 59972c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 59982c742e1fSying tian - Beijing China if (ddi_intr_add_handler(ahci_ctlp->ahcictl_intr_htable[i], 59992fcbc377Syt ahci_intr, (caddr_t)ahci_ctlp, NULL) != DDI_SUCCESS) { 60002fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 60012fcbc377Syt "ddi_intr_add_handler() failed"); 60022fcbc377Syt 60032fcbc377Syt /* Free already allocated intr. */ 60042c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 60052fcbc377Syt (void) ddi_intr_free( 60062c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 60072fcbc377Syt } 60082fcbc377Syt 60092fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 60102fcbc377Syt ahci_ctlp->ahcictl_intr_size); 60112fcbc377Syt return (DDI_FAILURE); 60122fcbc377Syt } 60132fcbc377Syt } 60142fcbc377Syt 60152c742e1fSying tian - Beijing China if (ddi_intr_get_cap(ahci_ctlp->ahcictl_intr_htable[0], 60162c742e1fSying tian - Beijing China &ahci_ctlp->ahcictl_intr_cap) != DDI_SUCCESS) { 60172c742e1fSying tian - Beijing China AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 60182c742e1fSying tian - Beijing China "ddi_intr_get_cap() failed"); 60192fcbc377Syt 60202c742e1fSying tian - Beijing China /* Free already allocated intr. */ 60212c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 60222c742e1fSying tian - Beijing China (void) ddi_intr_free( 60232c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 60242c742e1fSying tian - Beijing China } 60252c742e1fSying tian - Beijing China 60262c742e1fSying tian - Beijing China kmem_free(ahci_ctlp->ahcictl_intr_htable, 60272c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_size); 60282c742e1fSying tian - Beijing China return (DDI_FAILURE); 60292c742e1fSying tian - Beijing China } 60302fcbc377Syt 60312fcbc377Syt if (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK) { 60322fcbc377Syt /* Call ddi_intr_block_enable() for MSI. */ 60332fcbc377Syt (void) ddi_intr_block_enable(ahci_ctlp->ahcictl_intr_htable, 60342fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 60352fcbc377Syt } else { 60362c742e1fSying tian - Beijing China /* Call ddi_intr_enable() for FIXED or MSI non block enable. */ 60372c742e1fSying tian - Beijing China for (i = 0; i < ahci_ctlp->ahcictl_intr_cnt; i++) { 60382fcbc377Syt (void) ddi_intr_enable( 60392c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 60402fcbc377Syt } 60412fcbc377Syt } 60422fcbc377Syt 60432fcbc377Syt return (DDI_SUCCESS); 60442fcbc377Syt } 60452fcbc377Syt 60462fcbc377Syt /* 60472fcbc377Syt * Removes the registered interrupts irrespective of whether they 60482fcbc377Syt * were legacy or MSI. 60492fcbc377Syt * 60502fcbc377Syt * WARNING!!! The controller interrupts must be disabled before calling 60512fcbc377Syt * this routine. 60522fcbc377Syt */ 60532fcbc377Syt static void 60542fcbc377Syt ahci_rem_intrs(ahci_ctl_t *ahci_ctlp) 60552fcbc377Syt { 60562fcbc377Syt int x; 60572fcbc377Syt 60582fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_rem_intrs entered"); 60592fcbc377Syt 60602fcbc377Syt /* Disable all interrupts. */ 60612fcbc377Syt if ((ahci_ctlp->ahcictl_intr_type == DDI_INTR_TYPE_MSI) && 60622fcbc377Syt (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK)) { 60632fcbc377Syt /* Call ddi_intr_block_disable(). */ 60642fcbc377Syt (void) ddi_intr_block_disable(ahci_ctlp->ahcictl_intr_htable, 60652fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 60662fcbc377Syt } else { 60672fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 60682fcbc377Syt (void) ddi_intr_disable( 60692fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 60702fcbc377Syt } 60712fcbc377Syt } 60722fcbc377Syt 60732fcbc377Syt /* Call ddi_intr_remove_handler(). */ 60742fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 60752fcbc377Syt (void) ddi_intr_remove_handler( 60762fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 60772fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[x]); 60782fcbc377Syt } 60792fcbc377Syt 60802fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, ahci_ctlp->ahcictl_intr_size); 60812fcbc377Syt } 60822fcbc377Syt 60832fcbc377Syt /* 608468d33a25Syt * This routine tries to put port into P:NotRunning state by clearing 608568d33a25Syt * PxCMD.ST. HBA will clear PxCI to 0h, PxSACT to 0h, PxCMD.CCS to 0h 608668d33a25Syt * and PxCMD.CR to '0'. 60872fcbc377Syt * 60882fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 60892fcbc377Syt * is called. 60902fcbc377Syt */ 60912fcbc377Syt static int 609268d33a25Syt ahci_put_port_into_notrunning_state(ahci_ctl_t *ahci_ctlp, 609368d33a25Syt ahci_port_t *ahci_portp, uint8_t port) 60942fcbc377Syt { 60952fcbc377Syt uint32_t port_cmd_status; 60962fcbc377Syt int loop_count; 60972fcbc377Syt 60982fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 609968d33a25Syt "ahci_put_port_into_notrunning_state enter: port %d", port); 61002fcbc377Syt 61012fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 61022fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 61032fcbc377Syt 61042fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_ST; 61052fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 61062fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 61072fcbc377Syt 61082fcbc377Syt /* Wait until PxCMD.CR is cleared */ 61092fcbc377Syt loop_count = 0; 61102fcbc377Syt do { 61112fcbc377Syt port_cmd_status = 61122fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 61132fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 61142fcbc377Syt 61152fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_IDLE) { 61162fcbc377Syt AHCIDBG2(AHCIDBG_INIT, ahci_ctlp, 61172fcbc377Syt "clearing port %d CMD.CR timeout, " 61182fcbc377Syt "port_cmd_status = 0x%x", port, 61192fcbc377Syt port_cmd_status); 61202fcbc377Syt /* 61212fcbc377Syt * We are effectively timing out after 0.5 sec. 61222fcbc377Syt * This value is specified in AHCI spec. 61232fcbc377Syt */ 61242fcbc377Syt break; 61252fcbc377Syt } 61262fcbc377Syt 6127689d74b0Syt /* Wait for 10 millisec */ 612819397407SSherry Moore drv_usecwait(10000); 61292fcbc377Syt } while (port_cmd_status & AHCI_CMD_STATUS_CR); 61302fcbc377Syt 613168d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_STARTED; 61322fcbc377Syt 613368d33a25Syt if (port_cmd_status & AHCI_CMD_STATUS_CR) { 61342fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 613568d33a25Syt "ahci_put_port_into_notrunning_state: failed to clear " 613668d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 613768d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 61382fcbc377Syt return (AHCI_FAILURE); 61392fcbc377Syt } else { 614068d33a25Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 614168d33a25Syt "ahci_put_port_into_notrunning_state: succeeded to clear " 614268d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 614368d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 61442fcbc377Syt return (AHCI_SUCCESS); 61452fcbc377Syt } 61462fcbc377Syt } 61472fcbc377Syt 61482fcbc377Syt /* 614968d33a25Syt * First clear PxCMD.ST, and then check PxTFD. If both PxTFD.STS.BSY 615068d33a25Syt * and PxTFD.STS.DRQ cleared to '0', it means the device is in a 615168d33a25Syt * stable state, then set PxCMD.ST to '1' to start the port directly. 615268d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue a 615368d33a25Syt * COMRESET to the device to put it in an idle state. 615468d33a25Syt * 615568d33a25Syt * The fifth argument returns whether the port reset is involved during 615668d33a25Syt * the process. 615768d33a25Syt * 61580a4c4cecSXiao-Yu Zhang * The routine will be called under following scenarios: 61590a4c4cecSXiao-Yu Zhang * + To abort the packet(s) 61600a4c4cecSXiao-Yu Zhang * + To reset the port 61610a4c4cecSXiao-Yu Zhang * + To activate the port 61620a4c4cecSXiao-Yu Zhang * + Fatal error recovery 61630a4c4cecSXiao-Yu Zhang * + To abort the timeout packet(s) 61642fcbc377Syt * 61652fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 616668d33a25Syt * is called. And ahciport_mutex will be released before the reset 616768d33a25Syt * event is reported to sata module by calling sata_hba_event_notify, 616868d33a25Syt * and then be acquired again later. 616982263d52Syt * 617082263d52Syt * NOTES!!! During this procedure, PxSERR register will be cleared, and 617182263d52Syt * according to the spec, the clearance of three bits will also clear 617282263d52Syt * three interrupt status bits. 617382263d52Syt * 1. PxSERR.DIAG.F will clear PxIS.UFS 617482263d52Syt * 2. PxSERR.DIAG.X will clear PxIS.PCS 617582263d52Syt * 3. PxSERR.DIAG.N will clear PxIS.PRCS 617682263d52Syt * 617782263d52Syt * Among these three interrupt events, the driver needs to take care of 617882263d52Syt * PxIS.PRCS, which is the hot plug event. When the driver found out 617982263d52Syt * a device was unplugged, it will call the interrupt handler. 61802fcbc377Syt */ 61812fcbc377Syt static int 61822fcbc377Syt ahci_restart_port_wait_till_ready(ahci_ctl_t *ahci_ctlp, 618368d33a25Syt ahci_port_t *ahci_portp, uint8_t port, int flag, int *reset_flag) 61842fcbc377Syt { 618582263d52Syt uint32_t port_sstatus; 61862fcbc377Syt uint32_t task_file_status; 61872fcbc377Syt sata_device_t sdevice; 61882fcbc377Syt int rval; 618982263d52Syt int dev_exists_begin = 0; 619082263d52Syt int dev_exists_end = 0; 61912fcbc377Syt 61922fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 61932fcbc377Syt "ahci_restart_port_wait_till_ready: port %d enter", port); 61942fcbc377Syt 61950a4c4cecSXiao-Yu Zhang if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) 619682263d52Syt dev_exists_begin = 1; 61972fcbc377Syt 619882263d52Syt /* First clear PxCMD.ST */ 619968d33a25Syt rval = ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, 620068d33a25Syt port); 620168d33a25Syt if (rval != AHCI_SUCCESS) 620268d33a25Syt /* 620368d33a25Syt * If PxCMD.CR does not clear within a reasonable time, it 620468d33a25Syt * may assume the interface is in a hung condition and may 620568d33a25Syt * continue with issuing the port reset. 620668d33a25Syt */ 620768d33a25Syt goto reset; 62082fcbc377Syt 620968d33a25Syt /* Then clear PxSERR */ 621068d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 621168d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 621268d33a25Syt AHCI_SERROR_CLEAR_ALL); 62132fcbc377Syt 621482263d52Syt /* Then get PxTFD */ 621568d33a25Syt task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 621668d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 62172fcbc377Syt 621868d33a25Syt /* 621968d33a25Syt * Check whether the device is in a stable status, if yes, 622068d33a25Syt * then start the port directly. However for ahci_tran_dport_reset, 622168d33a25Syt * we may have to perform a port reset. 622268d33a25Syt */ 622368d33a25Syt if (!(task_file_status & (AHCI_TFD_STS_BSY | AHCI_TFD_STS_DRQ)) && 622468d33a25Syt !(flag & AHCI_PORT_RESET)) 62252fcbc377Syt goto out; 62262fcbc377Syt 622768d33a25Syt reset: 622868d33a25Syt /* 622968d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue 623068d33a25Syt * a COMRESET to the device 623168d33a25Syt */ 62322fcbc377Syt rval = ahci_port_reset(ahci_ctlp, ahci_portp, port); 623368d33a25Syt 623468d33a25Syt if (reset_flag != NULL) 623568d33a25Syt *reset_flag = 1; 62362fcbc377Syt 62372fcbc377Syt /* Indicate to the framework that a reset has happened. */ 623882263d52Syt if ((ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) && 623982263d52Syt !(flag & AHCI_RESET_NO_EVENTS_UP)) { 624082263d52Syt /* Set the reset in progress flag */ 624182263d52Syt ahci_portp->ahciport_reset_in_progress = 1; 62422fcbc377Syt 62432fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 624409121340Syt sdevice.satadev_addr.cport = 624509121340Syt ahci_ctlp->ahcictl_port_to_cport[port]; 624668d33a25Syt sdevice.satadev_addr.pmport = 0; 624768d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 62482fcbc377Syt 62492fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 62502fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 62512fcbc377Syt if (ahci_ctlp->ahcictl_sata_hba_tran) { 62522fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 62532fcbc377Syt sata_hba_event_notify( 62542fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 62552fcbc377Syt &sdevice, 62562fcbc377Syt SATA_EVNT_DEVICE_RESET); 62572fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 62582fcbc377Syt } 62592fcbc377Syt 626068d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 626168d33a25Syt "port %d sending event up: SATA_EVNT_RESET", port); 626282263d52Syt } else { 626382263d52Syt ahci_portp->ahciport_reset_in_progress = 0; 62642fcbc377Syt } 626582263d52Syt 62662fcbc377Syt out: 6267a9440e8dSyt (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 626882263d52Syt 626982263d52Syt /* SStatus tells the presence of device. */ 627082263d52Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 627182263d52Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 627282263d52Syt 627382263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 627482263d52Syt dev_exists_end = 1; 627582263d52Syt ASSERT(ahci_portp->ahciport_device_type != SATA_DTYPE_NONE); 627682263d52Syt } 627782263d52Syt 627882263d52Syt /* Check whether a hot plug event happened */ 627982263d52Syt if (dev_exists_begin == 1 && dev_exists_end == 0) { 628082263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 628182263d52Syt "ahci_restart_port_wait_till_ready: port %d " 628282263d52Syt "device is removed", port); 628382263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_NODEV; 628482263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 628582263d52Syt "ahci_restart_port_wait_till_ready: port %d " 628682263d52Syt "AHCI_PORT_FLAG_NODEV flag is set", port); 628782263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 628882263d52Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, port); 628982263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 62902fcbc377Syt } 62912fcbc377Syt 62922fcbc377Syt return (rval); 62932fcbc377Syt } 62942fcbc377Syt 62952fcbc377Syt /* 629668d33a25Syt * This routine may be called under four scenarios: 629768d33a25Syt * a) do the recovery from fatal error 62982fcbc377Syt * b) or we need to timeout some commands 62992fcbc377Syt * c) or we need to abort some commands 63002fcbc377Syt * d) or we need reset device/port/controller 63012fcbc377Syt * 63022fcbc377Syt * In all these scenarios, we need to send any pending unfinished 63032fcbc377Syt * commands up to sata framework. 63042fcbc377Syt * 630568d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 63062fcbc377Syt */ 63072fcbc377Syt static void 63082fcbc377Syt ahci_mop_commands(ahci_ctl_t *ahci_ctlp, 63092fcbc377Syt ahci_port_t *ahci_portp, 63102fcbc377Syt uint32_t slot_status, 63112fcbc377Syt uint32_t failed_tags, 63122fcbc377Syt uint32_t timeout_tags, 63132fcbc377Syt uint32_t aborted_tags, 63142fcbc377Syt uint32_t reset_tags) 63152fcbc377Syt { 631682263d52Syt uint32_t finished_tags = 0; 631782263d52Syt uint32_t unfinished_tags = 0; 63182fcbc377Syt int tmp_slot; 63192fcbc377Syt sata_pkt_t *satapkt; 632082263d52Syt int ncq_cmd_in_progress = 0; 632182263d52Syt int err_retri_cmd_in_progress = 0; 63222fcbc377Syt 63232fcbc377Syt AHCIDBG2(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 63242fcbc377Syt "ahci_mop_commands entered: port: %d slot_status: 0x%x", 6325689d74b0Syt ahci_portp->ahciport_port_num, slot_status); 63262fcbc377Syt 63272fcbc377Syt AHCIDBG4(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 63282fcbc377Syt "ahci_mop_commands: failed_tags: 0x%x, " 63292fcbc377Syt "timeout_tags: 0x%x aborted_tags: 0x%x, " 63302fcbc377Syt "reset_tags: 0x%x", failed_tags, 63312fcbc377Syt timeout_tags, aborted_tags, reset_tags); 63322fcbc377Syt 633382263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 633482263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 633582263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 63362fcbc377Syt 633782263d52Syt unfinished_tags = slot_status & 633882263d52Syt AHCI_SLOT_MASK(ahci_ctlp) & 633982263d52Syt ~failed_tags & 634082263d52Syt ~aborted_tags & 634182263d52Syt ~reset_tags & 634282263d52Syt ~timeout_tags; 6343a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 634482263d52Syt ncq_cmd_in_progress = 1; 634582263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 634682263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 634782263d52Syt 634882263d52Syt unfinished_tags = slot_status & 634982263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp) & 635082263d52Syt ~failed_tags & 635182263d52Syt ~aborted_tags & 635282263d52Syt ~reset_tags & 635382263d52Syt ~timeout_tags; 6354a9440e8dSyt } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 63552fcbc377Syt 6356a9440e8dSyt /* 6357a9440e8dSyt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT is 6358a9440e8dSyt * set, it means REQUEST SENSE or READ LOG EXT command doesn't 6359a9440e8dSyt * complete successfully due to one of the following three 6360a9440e8dSyt * conditions: 6361a9440e8dSyt * 6362a9440e8dSyt * 1. Fatal error - failed_tags includes its slot 6363a9440e8dSyt * 2. Timed out - timeout_tags includes its slot 6364a9440e8dSyt * 3. Aborted when hot unplug - aborted_tags includes its 6365a9440e8dSyt * slot 6366a9440e8dSyt * 6367a9440e8dSyt * Please note that the command is always sent down in Slot 0 6368a9440e8dSyt */ 636982263d52Syt err_retri_cmd_in_progress = 1; 6370f8a673adSying tian - Beijing China AHCIDBG2(AHCIDBG_ERRS|AHCIDBG_NCQ, ahci_ctlp, 637182263d52Syt "ahci_mop_commands is called for port %d while " 637282263d52Syt "REQUEST SENSE or READ LOG EXT for error retrieval " 6373f8a673adSying tian - Beijing China "is being executed slot_status = 0x%x", 6374f8a673adSying tian - Beijing China ahci_portp->ahciport_port_num, slot_status); 637568d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress > 1); 637682263d52Syt ASSERT(slot_status == 0x1); 63772fcbc377Syt } 63782fcbc377Syt 637968d33a25Syt /* Send up finished packets with SATA_PKT_COMPLETED */ 63802fcbc377Syt while (finished_tags) { 63812fcbc377Syt tmp_slot = ddi_ffs(finished_tags) - 1; 63822fcbc377Syt if (tmp_slot == -1) { 63832fcbc377Syt break; 63842fcbc377Syt } 63852fcbc377Syt 63862fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 63872fcbc377Syt ASSERT(satapkt != NULL); 63882fcbc377Syt 63892fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_mop_commands: " 63902fcbc377Syt "sending up pkt 0x%p with SATA_PKT_COMPLETED", 63912fcbc377Syt (void *)satapkt); 63922fcbc377Syt 639368d33a25Syt /* 639468d33a25Syt * Cannot fetch the return register content since the port 639568d33a25Syt * was restarted, so the corresponding tag will be set to 639668d33a25Syt * aborted tags. 639768d33a25Syt */ 639868d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 639968d33a25Syt CLEAR_BIT(finished_tags, tmp_slot); 640068d33a25Syt aborted_tags |= tmp_slot; 640168d33a25Syt continue; 640268d33a25Syt } 640368d33a25Syt 640482263d52Syt if (ncq_cmd_in_progress) 640582263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 640682263d52Syt tmp_slot); 64072fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 64082fcbc377Syt CLEAR_BIT(finished_tags, tmp_slot); 64092fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 64102fcbc377Syt 64112fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 64122fcbc377Syt } 64132fcbc377Syt 641468d33a25Syt /* Send up failed packets with SATA_PKT_DEV_ERROR. */ 64152fcbc377Syt while (failed_tags) { 641682263d52Syt if (err_retri_cmd_in_progress) { 641782263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 641882263d52Syt ASSERT(satapkt != NULL); 641982263d52Syt ASSERT(failed_tags == 0x1); 642082263d52Syt 642182263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 642282263d52Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 642382263d52Syt (void *)satapkt); 642482263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 642582263d52Syt break; 642682263d52Syt } 642782263d52Syt 64282fcbc377Syt tmp_slot = ddi_ffs(failed_tags) - 1; 64292fcbc377Syt if (tmp_slot == -1) { 64302fcbc377Syt break; 64312fcbc377Syt } 64322fcbc377Syt 64332fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 64342fcbc377Syt ASSERT(satapkt != NULL); 64352fcbc377Syt 64362fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 64372fcbc377Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 64382fcbc377Syt (void *)satapkt); 64392fcbc377Syt 644082263d52Syt if (ncq_cmd_in_progress) 644182263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 644282263d52Syt tmp_slot); 64432fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 64442fcbc377Syt CLEAR_BIT(failed_tags, tmp_slot); 64452fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 64462fcbc377Syt 64472fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 64482fcbc377Syt } 64492fcbc377Syt 645068d33a25Syt /* Send up timeout packets with SATA_PKT_TIMEOUT. */ 64512fcbc377Syt while (timeout_tags) { 645282263d52Syt if (err_retri_cmd_in_progress) { 645382263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 645482263d52Syt ASSERT(satapkt != NULL); 645582263d52Syt ASSERT(timeout_tags == 0x1); 645682263d52Syt 645782263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 645882263d52Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 645982263d52Syt (void *)satapkt); 646082263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 646182263d52Syt break; 646282263d52Syt } 646382263d52Syt 64642fcbc377Syt tmp_slot = ddi_ffs(timeout_tags) - 1; 64652fcbc377Syt if (tmp_slot == -1) { 64662fcbc377Syt break; 64672fcbc377Syt } 64682fcbc377Syt 64692fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 64702fcbc377Syt ASSERT(satapkt != NULL); 64712fcbc377Syt 64722fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 64732fcbc377Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 64742fcbc377Syt (void *)satapkt); 64752fcbc377Syt 647682263d52Syt if (ncq_cmd_in_progress) 647782263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 647882263d52Syt tmp_slot); 64792fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 64802fcbc377Syt CLEAR_BIT(timeout_tags, tmp_slot); 64812fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 64822fcbc377Syt 64832fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 64842fcbc377Syt } 64852fcbc377Syt 648668d33a25Syt /* Send up aborted packets with SATA_PKT_ABORTED */ 64872fcbc377Syt while (aborted_tags) { 648882263d52Syt if (err_retri_cmd_in_progress) { 648982263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 649082263d52Syt ASSERT(satapkt != NULL); 649182263d52Syt ASSERT(aborted_tags == 0x1); 649282263d52Syt 649382263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 649482263d52Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 649582263d52Syt (void *)satapkt); 649682263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 649782263d52Syt break; 649882263d52Syt } 649982263d52Syt 65002fcbc377Syt tmp_slot = ddi_ffs(aborted_tags) - 1; 65012fcbc377Syt if (tmp_slot == -1) { 65022fcbc377Syt break; 65032fcbc377Syt } 65042fcbc377Syt 65052fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 65062fcbc377Syt ASSERT(satapkt != NULL); 65072fcbc377Syt 65082fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 65092fcbc377Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 65102fcbc377Syt (void *)satapkt); 65112fcbc377Syt 651282263d52Syt if (ncq_cmd_in_progress) 651382263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 651482263d52Syt tmp_slot); 65152fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 65162fcbc377Syt CLEAR_BIT(aborted_tags, tmp_slot); 65172fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 65182fcbc377Syt 65192fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 65202fcbc377Syt } 65212fcbc377Syt 652268d33a25Syt /* Send up reset packets with SATA_PKT_RESET. */ 65232fcbc377Syt while (reset_tags) { 65242fcbc377Syt tmp_slot = ddi_ffs(reset_tags) - 1; 65252fcbc377Syt if (tmp_slot == -1) { 65262fcbc377Syt break; 65272fcbc377Syt } 65282fcbc377Syt 65292fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 65302fcbc377Syt ASSERT(satapkt != NULL); 65312fcbc377Syt 65322fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 65332fcbc377Syt "sending up pkt 0x%p with SATA_PKT_RESET", 65342fcbc377Syt (void *)satapkt); 65352fcbc377Syt 653682263d52Syt if (ncq_cmd_in_progress) 653782263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 653882263d52Syt tmp_slot); 65392fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 65402fcbc377Syt CLEAR_BIT(reset_tags, tmp_slot); 65412fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 65422fcbc377Syt 65432fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 65442fcbc377Syt } 65452fcbc377Syt 654668d33a25Syt /* Send up unfinished packets with SATA_PKT_RESET */ 65472fcbc377Syt while (unfinished_tags) { 65482fcbc377Syt tmp_slot = ddi_ffs(unfinished_tags) - 1; 65492fcbc377Syt if (tmp_slot == -1) { 65502fcbc377Syt break; 65512fcbc377Syt } 65522fcbc377Syt 65532fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 65542fcbc377Syt ASSERT(satapkt != NULL); 65552fcbc377Syt 65562fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 655768d33a25Syt "sending up pkt 0x%p with SATA_PKT_RESET", 65582fcbc377Syt (void *)satapkt); 65592fcbc377Syt 656082263d52Syt if (ncq_cmd_in_progress) 656182263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 656282263d52Syt tmp_slot); 65632fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 65642fcbc377Syt CLEAR_BIT(unfinished_tags, tmp_slot); 65652fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 65662fcbc377Syt 656768d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 65682fcbc377Syt } 65692fcbc377Syt 657068d33a25Syt ahci_portp->ahciport_mop_in_progress--; 657168d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress >= 0); 65722fcbc377Syt 657368d33a25Syt if (ahci_portp->ahciport_mop_in_progress == 0) 657468d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_MOPPING; 657568d33a25Syt } 65762fcbc377Syt 657782263d52Syt /* 657882263d52Syt * This routine is going to first request a READ LOG EXT sata pkt from sata 657982263d52Syt * module, and then deliver it to the HBA to get the ncq failure context. 658082263d52Syt * The return value is the exactly failed tags. 658182263d52Syt */ 658282263d52Syt static uint32_t 658382263d52Syt ahci_get_rdlogext_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 658482263d52Syt uint8_t port) 658582263d52Syt { 658682263d52Syt sata_device_t sdevice; 658782263d52Syt sata_pkt_t *rdlog_spkt, *spkt; 658882263d52Syt ddi_dma_handle_t buf_dma_handle; 658982263d52Syt int loop_count; 659082263d52Syt int rval; 659182263d52Syt int failed_slot; 659282263d52Syt uint32_t failed_tags = 0; 659382263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page; 659482263d52Syt 659582263d52Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_NCQ, ahci_ctlp, 659682263d52Syt "ahci_get_rdlogext_data enter: port %d", port); 659782263d52Syt 659882263d52Syt /* Prepare the sdevice data */ 659982263d52Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 660082263d52Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 660182263d52Syt 660282263d52Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 660382263d52Syt sdevice.satadev_addr.pmport = 0; 660482263d52Syt 660582263d52Syt /* 660682263d52Syt * Call the sata hba interface to get a rdlog spkt 660782263d52Syt */ 660882263d52Syt loop_count = 0; 660982263d52Syt loop: 661082263d52Syt rdlog_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 661182263d52Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_NCQ); 661282263d52Syt if (rdlog_spkt == NULL) { 661382263d52Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 661482263d52Syt /* Sleep for a while */ 661582263d52Syt delay(AHCI_10MS_TICKS); 661682263d52Syt goto loop; 661782263d52Syt } 661882263d52Syt /* Timed out after 1s */ 661982263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 662082263d52Syt "failed to get rdlog spkt for port %d", port); 662182263d52Syt return (failed_tags); 662282263d52Syt } 662382263d52Syt 662482263d52Syt ASSERT(rdlog_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 662582263d52Syt 662682263d52Syt /* 662782263d52Syt * This flag is used to handle the specific error recovery when the 662882263d52Syt * READ LOG EXT command gets a failure (fatal error or time-out). 662982263d52Syt */ 663082263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RDLOGEXT; 663182263d52Syt 663282263d52Syt /* 663382263d52Syt * This start is not supposed to fail because after port is restarted, 663482263d52Syt * the whole command list is empty. 663582263d52Syt */ 663682263d52Syt ahci_portp->ahciport_err_retri_pkt = rdlog_spkt; 663782263d52Syt (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, port, rdlog_spkt); 663882263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 663982263d52Syt 664082263d52Syt /* Remove the flag after READ LOG EXT command is completed */ 664182263d52Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RDLOGEXT; 664282263d52Syt 664382263d52Syt if (rdlog_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 664482263d52Syt /* Update the request log data */ 664582263d52Syt buf_dma_handle = *(ddi_dma_handle_t *) 664682263d52Syt (rdlog_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 664782263d52Syt rval = ddi_dma_sync(buf_dma_handle, 0, 0, 664882263d52Syt DDI_DMA_SYNC_FORKERNEL); 664982263d52Syt if (rval == DDI_SUCCESS) { 665082263d52Syt ncq_err_page = 665182263d52Syt (struct sata_ncq_error_recovery_page *)rdlog_spkt-> 665282263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr; 665382263d52Syt 665482263d52Syt /* Get the failed tag */ 665582263d52Syt failed_slot = ncq_err_page->ncq_tag; 665682263d52Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, 665782263d52Syt "ahci_get_rdlogext_data: port %d " 665882263d52Syt "failed slot %d", port, failed_slot); 665982263d52Syt if (failed_slot & NQ) { 666082263d52Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 666182263d52Syt "the failed slot is not a valid tag"); 666282263d52Syt goto out; 666382263d52Syt } 666482263d52Syt 666582263d52Syt failed_slot &= NCQ_TAG_MASK; 666682263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 666782263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 666882263d52Syt "ahci_get_rdlogext_data: failed spkt 0x%p", 666982263d52Syt (void *)spkt); 667082263d52Syt if (spkt == NULL) { 667182263d52Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 667282263d52Syt "the failed slot spkt is NULL") 667382263d52Syt goto out; 667482263d52Syt } 667582263d52Syt 667682263d52Syt failed_tags = 0x1 << failed_slot; 667782263d52Syt 667882263d52Syt /* Fill out the error context */ 667982263d52Syt ahci_copy_ncq_err_page(&spkt->satapkt_cmd, 668082263d52Syt ncq_err_page); 668182263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 668282263d52Syt &spkt->satapkt_device); 668382263d52Syt } 668482263d52Syt } 668582263d52Syt out: 668682263d52Syt sata_free_error_retrieval_pkt(rdlog_spkt); 668782263d52Syt 668882263d52Syt return (failed_tags); 668982263d52Syt } 669082263d52Syt 669168d33a25Syt /* 669268d33a25Syt * This routine is going to first request a REQUEST SENSE sata pkt from sata 669368d33a25Syt * module, and then deliver it to the HBA to get the sense data and copy 669468d33a25Syt * the sense data back to the orignal failed sata pkt, and free the REQUEST 669568d33a25Syt * SENSE sata pkt later. 669668d33a25Syt */ 669768d33a25Syt static void 669868d33a25Syt ahci_get_rqsense_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 669968d33a25Syt uint8_t port, sata_pkt_t *spkt) 670068d33a25Syt { 670168d33a25Syt sata_device_t sdevice; 670268d33a25Syt sata_pkt_t *rs_spkt; 670368d33a25Syt sata_cmd_t *sata_cmd; 670468d33a25Syt ddi_dma_handle_t buf_dma_handle; 670568d33a25Syt int loop_count; 670668d33a25Syt #if AHCI_DEBUG 670768d33a25Syt struct scsi_extended_sense *rqsense; 670868d33a25Syt #endif 670968d33a25Syt 671068d33a25Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 671168d33a25Syt "ahci_get_rqsense_data enter: port %d", port); 671268d33a25Syt 671368d33a25Syt /* Prepare the sdevice data */ 671468d33a25Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 671568d33a25Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 671668d33a25Syt 671768d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 671868d33a25Syt sdevice.satadev_addr.pmport = 0; 671968d33a25Syt 672068d33a25Syt sata_cmd = &spkt->satapkt_cmd; 672168d33a25Syt 672268d33a25Syt /* 672368d33a25Syt * Call the sata hba interface to get a rs spkt 672468d33a25Syt */ 672568d33a25Syt loop_count = 0; 672668d33a25Syt loop: 672768d33a25Syt rs_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 672868d33a25Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_ATAPI); 672968d33a25Syt if (rs_spkt == NULL) { 673068d33a25Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 673168d33a25Syt /* Sleep for a while */ 673268d33a25Syt delay(AHCI_10MS_TICKS); 673368d33a25Syt goto loop; 673468d33a25Syt 673568d33a25Syt } 673668d33a25Syt /* Timed out after 1s */ 673782263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 673868d33a25Syt "failed to get rs spkt for port %d", port); 673968d33a25Syt return; 674068d33a25Syt } 674168d33a25Syt 674268d33a25Syt ASSERT(rs_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 674368d33a25Syt 674468d33a25Syt /* 674568d33a25Syt * This flag is used to handle the specific error recovery when the 674682263d52Syt * REQUEST SENSE command gets a faiure (fatal error or time-out). 674768d33a25Syt */ 674868d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RQSENSE; 674968d33a25Syt 675068d33a25Syt /* 675168d33a25Syt * This start is not supposed to fail because after port is restarted, 675282263d52Syt * the whole command list is empty. 675368d33a25Syt */ 675482263d52Syt ahci_portp->ahciport_err_retri_pkt = rs_spkt; 675582263d52Syt (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, port, rs_spkt); 675682263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 675768d33a25Syt 675868d33a25Syt /* Remove the flag after REQUEST SENSE command is completed */ 675968d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RQSENSE; 676068d33a25Syt 676168d33a25Syt if (rs_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 676268d33a25Syt /* Update the request sense data */ 676368d33a25Syt buf_dma_handle = *(ddi_dma_handle_t *) 676468d33a25Syt (rs_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 676582263d52Syt (void) ddi_dma_sync(buf_dma_handle, 0, 0, 676668d33a25Syt DDI_DMA_SYNC_FORKERNEL); 676782263d52Syt /* Copy the request sense data */ 676882263d52Syt bcopy(rs_spkt-> 676982263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr, 677082263d52Syt &sata_cmd->satacmd_rqsense, 677182263d52Syt SATA_ATAPI_MIN_RQSENSE_LEN); 677268d33a25Syt #if AHCI_DEBUG 677382263d52Syt rqsense = (struct scsi_extended_sense *) 677482263d52Syt sata_cmd->satacmd_rqsense; 677582263d52Syt 677682263d52Syt /* Dump the sense data */ 677782263d52Syt AHCIDBG0(AHCIDBG_SENSEDATA, ahci_ctlp, "\n"); 677882263d52Syt AHCIDBG2(AHCIDBG_SENSEDATA, ahci_ctlp, 677982263d52Syt "Sense data for satapkt %p ATAPI cmd 0x%x", 678082263d52Syt spkt, sata_cmd->satacmd_acdb[0]); 678182263d52Syt AHCIDBG5(AHCIDBG_SENSEDATA, ahci_ctlp, 678282263d52Syt " es_code 0x%x es_class 0x%x " 678382263d52Syt "es_key 0x%x es_add_code 0x%x " 678482263d52Syt "es_qual_code 0x%x", 678582263d52Syt rqsense->es_code, rqsense->es_class, 678682263d52Syt rqsense->es_key, rqsense->es_add_code, 678782263d52Syt rqsense->es_qual_code); 678868d33a25Syt #endif 678968d33a25Syt } 679068d33a25Syt 679168d33a25Syt sata_free_error_retrieval_pkt(rs_spkt); 67922fcbc377Syt } 67932fcbc377Syt 67942fcbc377Syt /* 67952fcbc377Syt * Fatal errors will cause the HBA to enter the ERR: Fatal state. To recover, 679668d33a25Syt * the port must be restarted. When the HBA detects thus error, it may try 679768d33a25Syt * to abort a transfer. And if the transfer was aborted, the device is 679868d33a25Syt * expected to send a D2H Register FIS with PxTFD.STS.ERR set to '1' and both 679968d33a25Syt * PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0'. Then system software knows 680068d33a25Syt * that the device is in a stable status and transfers may be restarted without 680168d33a25Syt * issuing a COMRESET to the device. If PxTFD.STS.BSY or PxTFD.STS.DRQ is set, 680268d33a25Syt * then the software will send the COMRESET to do the port reset. 680368d33a25Syt * 680468d33a25Syt * Software should perform the appropriate error recovery actions based on 680568d33a25Syt * whether non-queued commands were being issued or natived command queuing 680668d33a25Syt * commands were being issued. 680768d33a25Syt * 680868d33a25Syt * And software will complete the command that had the error with error mark 680968d33a25Syt * to higher level software. 68102fcbc377Syt * 68112fcbc377Syt * Fatal errors include the following: 681238547057Sying tian - Beijing China * PxIS.IFS - Interface Fatal Error Status 681338547057Sying tian - Beijing China * PxIS.HBDS - Host Bus Data Error Status 681438547057Sying tian - Beijing China * PxIS.HBFS - Host Bus Fatal Error Status 68152fcbc377Syt * PxIS.TFES - Task File Error Status 68162fcbc377Syt * 68172fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 68182fcbc377Syt */ 681968d33a25Syt static void 682068d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctl_t *ahci_ctlp, 682182263d52Syt ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 68222fcbc377Syt { 682382263d52Syt uint32_t port_cmd_status; 682482263d52Syt uint32_t slot_status = 0; 682568d33a25Syt uint32_t failed_tags = 0; 682668d33a25Syt int failed_slot; 682768d33a25Syt int reset_flag = 0; 682868d33a25Syt ahci_fis_d2h_register_t *ahci_rcvd_fisp; 682982263d52Syt sata_cmd_t *sata_cmd = NULL; 683082263d52Syt sata_pkt_t *spkt = NULL; 683138547057Sying tian - Beijing China #if AHCI_DEBUG 683238547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 683338547057Sying tian - Beijing China #endif 68342fcbc377Syt 68352fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 683668d33a25Syt "ahci_fatal_error_recovery_handler enter: port %d", port); 68372fcbc377Syt 683882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 683982263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 68402fcbc377Syt 684182263d52Syt /* Read PxCI to see which commands are still outstanding */ 684282263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 684382263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 68442fcbc377Syt 684582263d52Syt /* 684682263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 684782263d52Syt * was processing when the error occurred. 684882263d52Syt */ 684982263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 685082263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 685182263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 685282263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 68532fcbc377Syt 685482263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 685582263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 685682263d52Syt ASSERT(spkt != NULL); 685782263d52Syt } else { 685882263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 685982263d52Syt if (spkt == NULL) { 686082263d52Syt /* May happen when interface errors occur? */ 686182263d52Syt goto next; 686282263d52Syt } 686382263d52Syt } 68642fcbc377Syt 686538547057Sying tian - Beijing China #if AHCI_DEBUG 686638547057Sying tian - Beijing China /* 686738547057Sying tian - Beijing China * Debugging purpose... 686838547057Sying tian - Beijing China */ 686938547057Sying tian - Beijing China if (ahci_portp->ahciport_prd_bytecounts[failed_slot]) { 687038547057Sying tian - Beijing China cmd_header = 687138547057Sying tian - Beijing China &ahci_portp->ahciport_cmd_list[failed_slot]; 687238547057Sying tian - Beijing China AHCIDBG3(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 687338547057Sying tian - Beijing China "ahci_fatal_error_recovery_handler: port %d, " 687438547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 687538547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 687638547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 687738547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[failed_slot]); 687838547057Sying tian - Beijing China } 687938547057Sying tian - Beijing China #endif 688038547057Sying tian - Beijing China 688182263d52Syt sata_cmd = &spkt->satapkt_cmd; 68822fcbc377Syt 688382263d52Syt /* Fill out the status and error registers for PxIS.TFES */ 688482263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 688582263d52Syt ahci_rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 688682263d52Syt ahcirf_d2h_register_fis); 688782263d52Syt 688882263d52Syt /* Copy the error context back to the sata_cmd */ 688982263d52Syt ahci_copy_err_cnxt(sata_cmd, ahci_rcvd_fisp); 689082263d52Syt } 68912fcbc377Syt 689282263d52Syt /* The failed command must be one of the outstanding commands */ 689382263d52Syt failed_tags = 0x1 << failed_slot; 689482263d52Syt ASSERT(failed_tags & slot_status); 689582263d52Syt 689682263d52Syt /* Update the sata registers, especially PxSERR register */ 689782263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 689882263d52Syt &spkt->satapkt_device); 68992fcbc377Syt 6900a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 690182263d52Syt /* Read PxSACT to see which commands are still outstanding */ 690282263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 690382263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 690482263d52Syt } 690568d33a25Syt next: 69062fcbc377Syt 690768d33a25Syt #if AHCI_DEBUG 690868d33a25Syt /* 690982263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 691082263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 691182263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 691282263d52Syt * recovery process. At this time, the only outstanding command is 691382263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 691468d33a25Syt */ 691582263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 69162fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 691768d33a25Syt "ahci_fatal_error_recovery_handler: port %d REQUEST SENSE " 691882263d52Syt "command or READ LOG EXT command for error data retrieval " 691982263d52Syt "failed", port); 692082263d52Syt ASSERT(slot_status == 0x1); 6921f8a673adSying tian - Beijing China ASSERT(failed_slot == 0); 692282263d52Syt ASSERT(spkt->satapkt_cmd.satacmd_acdb[0] == 692382263d52Syt SCMD_REQUEST_SENSE || 692482263d52Syt spkt->satapkt_cmd.satacmd_cmd_reg == 692582263d52Syt SATAC_READ_LOG_EXT); 69262fcbc377Syt } 692768d33a25Syt #endif 69282fcbc377Syt 692968d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 693068d33a25Syt ahci_portp->ahciport_mop_in_progress++; 69312fcbc377Syt 693268d33a25Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 693368d33a25Syt port, NULL, &reset_flag); 69342fcbc377Syt 693568d33a25Syt /* 693668d33a25Syt * Won't retrieve error information: 693768d33a25Syt * 1. Port reset was involved to recover 693882263d52Syt * 2. Device is gone 693968d33a25Syt * 3. IDENTIFY DEVICE command sent to ATAPI device 694082263d52Syt * 4. REQUEST SENSE or READ LOG EXT command during error recovery 694168d33a25Syt */ 694282263d52Syt if (reset_flag || 694382263d52Syt ahci_portp->ahciport_device_type == SATA_DTYPE_NONE || 694482263d52Syt spkt && spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_ID_DEVICE || 694582263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 694668d33a25Syt goto out; 69472fcbc377Syt 694882263d52Syt /* 694982263d52Syt * Deliver READ LOG EXT to gather information about the error when 695082263d52Syt * a COMRESET has not been performed as part of the error recovery 695182263d52Syt * during NCQ command processing. 695282263d52Syt */ 695382263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 695482263d52Syt failed_tags = ahci_get_rdlogext_data(ahci_ctlp, 695582263d52Syt ahci_portp, port); 695682263d52Syt goto out; 695782263d52Syt } 695882263d52Syt 695968d33a25Syt /* 696068d33a25Syt * Deliver REQUEST SENSE for ATAPI command to gather information about 696168d33a25Syt * the error when a COMRESET has not been performed as part of the 696268d33a25Syt * error recovery. 696368d33a25Syt */ 696438547057Sying tian - Beijing China if (spkt && ahci_portp->ahciport_device_type == SATA_DTYPE_ATAPI) 696568d33a25Syt ahci_get_rqsense_data(ahci_ctlp, ahci_portp, port, spkt); 696668d33a25Syt out: 696782263d52Syt AHCIDBG5(AHCIDBG_ERRS, ahci_ctlp, 696838547057Sying tian - Beijing China "ahci_fatal_error_recovery_handler: port %d fatal error " 696982263d52Syt "occurred slot_status = 0x%x, pending_tags = 0x%x, " 697082263d52Syt "pending_ncq_tags = 0x%x failed_tags = 0x%x", 697182263d52Syt port, slot_status, ahci_portp->ahciport_pending_tags, 697282263d52Syt ahci_portp->ahciport_pending_ncq_tags, failed_tags); 697382263d52Syt 69742fcbc377Syt ahci_mop_commands(ahci_ctlp, 69752fcbc377Syt ahci_portp, 697682263d52Syt slot_status, 69772fcbc377Syt failed_tags, /* failed tags */ 69782fcbc377Syt 0, /* timeout tags */ 69792fcbc377Syt 0, /* aborted tags */ 69802fcbc377Syt 0); /* reset tags */ 698168d33a25Syt } 698268d33a25Syt 698368d33a25Syt /* 698468d33a25Syt * Handle events - fatal error recovery 698568d33a25Syt */ 698668d33a25Syt static void 698768d33a25Syt ahci_events_handler(void *args) 698868d33a25Syt { 698968d33a25Syt ahci_event_arg_t *ahci_event_arg; 699068d33a25Syt ahci_ctl_t *ahci_ctlp; 699168d33a25Syt ahci_port_t *ahci_portp; 699268d33a25Syt uint32_t event; 699368d33a25Syt uint8_t port; 699468d33a25Syt 699568d33a25Syt ahci_event_arg = (ahci_event_arg_t *)args; 699682263d52Syt 699768d33a25Syt ahci_ctlp = ahci_event_arg->ahciea_ctlp; 699868d33a25Syt ahci_portp = ahci_event_arg->ahciea_portp; 699968d33a25Syt event = ahci_event_arg->ahciea_event; 700068d33a25Syt port = ahci_portp->ahciport_port_num; 700168d33a25Syt 7002f8a673adSying tian - Beijing China AHCIDBG2(AHCIDBG_ENTRY|AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 700382263d52Syt "ahci_events_handler enter: port %d intr_status = 0x%x", 700482263d52Syt port, event); 700568d33a25Syt 70062fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 70072fcbc377Syt 700868d33a25Syt /* 700968d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 701068d33a25Syt * SATA_DTYPE_NONE. 701168d33a25Syt */ 701268d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 701368d33a25Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 701468d33a25Syt "ahci_events_handler: port %d no device attached, " 701568d33a25Syt "and just return without doing anything", port); 701668d33a25Syt goto out; 701768d33a25Syt } 701868d33a25Syt 701968d33a25Syt if (event & (AHCI_INTR_STATUS_IFS | 702068d33a25Syt AHCI_INTR_STATUS_HBDS | 702168d33a25Syt AHCI_INTR_STATUS_HBFS | 702268d33a25Syt AHCI_INTR_STATUS_TFES)) 702368d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctlp, ahci_portp, 702482263d52Syt port, event); 702568d33a25Syt 702668d33a25Syt out: 702768d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 70282fcbc377Syt } 70292fcbc377Syt 70302fcbc377Syt /* 703168d33a25Syt * ahci_watchdog_handler() and ahci_do_sync_start will call us if they 703268d33a25Syt * detect there are some commands which are timed out. 70332fcbc377Syt */ 70342fcbc377Syt static void 70352fcbc377Syt ahci_timeout_pkts(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 703682263d52Syt uint8_t port, uint32_t tmp_timeout_tags) 70372fcbc377Syt { 703882263d52Syt uint32_t slot_status = 0; 703982263d52Syt uint32_t finished_tags = 0; 704082263d52Syt uint32_t timeout_tags = 0; 70412fcbc377Syt 704268d33a25Syt AHCIDBG1(AHCIDBG_TIMEOUT|AHCIDBG_ENTRY, ahci_ctlp, 704368d33a25Syt "ahci_timeout_pkts enter: port %d", port); 70442fcbc377Syt 70452fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 70462fcbc377Syt 704782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 704882263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 704982263d52Syt /* Read PxCI to see which commands are still outstanding */ 705082263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 705182263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 7052a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 705382263d52Syt /* Read PxSACT to see which commands are still outstanding */ 705482263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 705582263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 705682263d52Syt } 705768d33a25Syt 705868d33a25Syt #if AHCI_DEBUG 70592fcbc377Syt /* 706082263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 706182263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 706282263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 706382263d52Syt * recovery process. At this time, the only outstanding command is 706482263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 70652fcbc377Syt */ 706682263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 706782263d52Syt AHCIDBG4(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, ahci_ctlp, 706868d33a25Syt "ahci_timeout_pkts called while REQUEST SENSE " 706982263d52Syt "command or READ LOG EXT command for error recovery " 707082263d52Syt "timed out timeout_tags = 0x%x, slot_status = 0x%x, " 707182263d52Syt "pending_tags = 0x%x, pending_ncq_tags = 0x%x", 707282263d52Syt tmp_timeout_tags, slot_status, 707382263d52Syt ahci_portp->ahciport_pending_tags, 707482263d52Syt ahci_portp->ahciport_pending_ncq_tags); 707582263d52Syt ASSERT(slot_status == 0x1); 70762fcbc377Syt } 707768d33a25Syt #endif 70782fcbc377Syt 707968d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 708068d33a25Syt ahci_portp->ahciport_mop_in_progress++; 70812fcbc377Syt 70822fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 708368d33a25Syt port, NULL, NULL); 708468d33a25Syt 70852fcbc377Syt /* 70862fcbc377Syt * Re-identify timeout tags because some previously checked commands 70872fcbc377Syt * could already complete. 70882fcbc377Syt */ 708982263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 709082263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 709182263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 709282263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 709382263d52Syt 709482263d52Syt AHCIDBG5(AHCIDBG_TIMEOUT, ahci_ctlp, 709582263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 709682263d52Syt "timeout_tags = 0x%x, port_cmd_issue = 0x%x, " 709782263d52Syt "pending_tags = 0x%x ", 709882263d52Syt port, finished_tags, timeout_tags, 709982263d52Syt slot_status, ahci_portp->ahciport_pending_tags); 7100a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 710182263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 710282263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 710382263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 710482263d52Syt 710582263d52Syt AHCIDBG5(AHCIDBG_TIMEOUT|AHCIDBG_NCQ, ahci_ctlp, 710682263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 710782263d52Syt "timeout_tags = 0x%x, port_sactive = 0x%x, " 710882263d52Syt "pending_ncq_tags = 0x%x ", 710982263d52Syt port, finished_tags, timeout_tags, 711082263d52Syt slot_status, ahci_portp->ahciport_pending_ncq_tags); 7111a9440e8dSyt } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 711282263d52Syt timeout_tags = tmp_timeout_tags; 711382263d52Syt } 71142fcbc377Syt 71152fcbc377Syt ahci_mop_commands(ahci_ctlp, 71162fcbc377Syt ahci_portp, 711782263d52Syt slot_status, 71182fcbc377Syt 0, /* failed tags */ 71192fcbc377Syt timeout_tags, /* timeout tags */ 71202fcbc377Syt 0, /* aborted tags */ 71212fcbc377Syt 0); /* reset tags */ 712268d33a25Syt 712368d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 71242fcbc377Syt } 71252fcbc377Syt 71262fcbc377Syt /* 71272fcbc377Syt * Watchdog handler kicks in every 5 seconds to timeout any commands pending 71282fcbc377Syt * for long time. 71292fcbc377Syt */ 71302fcbc377Syt static void 71312fcbc377Syt ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp) 71322fcbc377Syt { 71332fcbc377Syt ahci_port_t *ahci_portp; 713468d33a25Syt sata_pkt_t *spkt; 713582263d52Syt uint32_t pending_tags; 713682263d52Syt uint32_t timeout_tags; 713768d33a25Syt uint32_t port_cmd_status; 713882263d52Syt uint32_t port_sactive; 71392fcbc377Syt uint8_t port; 71402fcbc377Syt int tmp_slot; 714168d33a25Syt int current_slot; 714282263d52Syt uint32_t current_tags; 7143a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 71442fcbc377Syt /* max number of cycles this packet should survive */ 71452fcbc377Syt int max_life_cycles; 71462fcbc377Syt 71472fcbc377Syt /* how many cycles this packet survived so far */ 71482fcbc377Syt int watched_cycles; 71492fcbc377Syt 71502fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 71512fcbc377Syt 7152*259105bcSying tian - Beijing China AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, 71532fcbc377Syt "ahci_watchdog_handler entered"); 71542fcbc377Syt 71552fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 71562fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 71572fcbc377Syt continue; 71582fcbc377Syt } 71592fcbc377Syt 71602fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 71612fcbc377Syt 71622fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 71632fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 71642fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 71652fcbc377Syt continue; 71662fcbc377Syt } 71672fcbc377Syt 716868d33a25Syt /* Skip the check for those ports in error recovery */ 716982263d52Syt if ((ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) && 717082263d52Syt !(ERR_RETRI_CMD_IN_PROGRESS(ahci_portp))) { 717168d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 717268d33a25Syt continue; 717368d33a25Syt } 717468d33a25Syt 717582263d52Syt pending_tags = 0; 717668d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 717768d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 717868d33a25Syt 717982263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 718082263d52Syt current_slot = 0; 718182263d52Syt pending_tags = 0x1; 7182a9440e8dSyt } else if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 718382263d52Syt current_slot = 718482263d52Syt (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 718582263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 718682263d52Syt pending_tags = ahci_portp->ahciport_pending_tags; 7187a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 718882263d52Syt port_sactive = ddi_get32( 718982263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 719082263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 719182263d52Syt current_tags = port_sactive & 719282263d52Syt ~port_cmd_status & 719382263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 719482263d52Syt pending_tags = ahci_portp->ahciport_pending_ncq_tags; 719582263d52Syt } 719682263d52Syt 71972fcbc377Syt timeout_tags = 0; 71982fcbc377Syt while (pending_tags) { 71992fcbc377Syt tmp_slot = ddi_ffs(pending_tags) - 1; 72002fcbc377Syt if (tmp_slot == -1) { 72012fcbc377Syt break; 72022fcbc377Syt } 72032fcbc377Syt 720482263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 720582263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 720682263d52Syt else 720782263d52Syt spkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 720882263d52Syt 720968d33a25Syt if ((spkt != NULL) && spkt->satapkt_time && 721068d33a25Syt !(spkt->satapkt_op_mode & SATA_OPMODE_POLLING)) { 72112fcbc377Syt /* 72122fcbc377Syt * We are overloading satapkt_hba_driver_private 72132fcbc377Syt * with watched_cycle count. 72142fcbc377Syt * 72152fcbc377Syt * If a packet has survived for more than it's 72162fcbc377Syt * max life cycles, it is a candidate for time 72172fcbc377Syt * out. 72182fcbc377Syt */ 72192fcbc377Syt watched_cycles = (int)(intptr_t) 722068d33a25Syt spkt->satapkt_hba_driver_private; 72212fcbc377Syt watched_cycles++; 722268d33a25Syt max_life_cycles = (spkt->satapkt_time + 72232fcbc377Syt ahci_watchdog_timeout - 1) / 72242fcbc377Syt ahci_watchdog_timeout; 722568d33a25Syt 722668d33a25Syt spkt->satapkt_hba_driver_private = 722768d33a25Syt (void *)(intptr_t)watched_cycles; 722868d33a25Syt 722968d33a25Syt if (watched_cycles <= max_life_cycles) 723068d33a25Syt goto next; 723168d33a25Syt 7232*259105bcSying tian - Beijing China #if AHCI_DEBUG 7233*259105bcSying tian - Beijing China if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 7234*259105bcSying tian - Beijing China AHCIDBG1(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, 7235*259105bcSying tian - Beijing China ahci_ctlp, "watchdog: the current " 7236*259105bcSying tian - Beijing China "tags is 0x%x", current_tags); 7237*259105bcSying tian - Beijing China } else { 7238*259105bcSying tian - Beijing China AHCIDBG1(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, 7239*259105bcSying tian - Beijing China ahci_ctlp, "watchdog: the current " 7240*259105bcSying tian - Beijing China "slot is %d", current_slot); 7241*259105bcSying tian - Beijing China } 7242*259105bcSying tian - Beijing China #endif 7243*259105bcSying tian - Beijing China 724468d33a25Syt /* 724568d33a25Syt * We need to check whether the HBA has 724668d33a25Syt * begun to execute the command, if not, 724768d33a25Syt * then re-set the timer of the command. 724868d33a25Syt */ 724982263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) && 725082263d52Syt (tmp_slot != current_slot) || 725182263d52Syt NCQ_CMD_IN_PROGRESS(ahci_portp) && 725282263d52Syt ((0x1 << tmp_slot) & current_tags)) { 725368d33a25Syt spkt->satapkt_hba_driver_private = 725468d33a25Syt (void *)(intptr_t)0; 725568d33a25Syt } else { 72562fcbc377Syt timeout_tags |= (0x1 << tmp_slot); 7257a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: watchdog " 72582fcbc377Syt "port %d satapkt 0x%p timed out\n", 7259a9440e8dSyt instance, port, (void *)spkt); 72602fcbc377Syt } 72612fcbc377Syt } 726268d33a25Syt next: 72632fcbc377Syt CLEAR_BIT(pending_tags, tmp_slot); 72642fcbc377Syt } 72652fcbc377Syt 72662fcbc377Syt if (timeout_tags) { 72672fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 72682fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 72692fcbc377Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 727082263d52Syt port, timeout_tags); 72712fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 72722fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 72732fcbc377Syt } 72742fcbc377Syt 72752fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 72762fcbc377Syt } 72772fcbc377Syt 72782fcbc377Syt /* Re-install the watchdog timeout handler */ 72792fcbc377Syt if (ahci_ctlp->ahcictl_timeout_id != 0) { 72802fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 72812fcbc377Syt timeout((void (*)(void *))ahci_watchdog_handler, 72822fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 72832fcbc377Syt } 72842fcbc377Syt 72852fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 72862fcbc377Syt } 72872fcbc377Syt 72882fcbc377Syt /* 728968d33a25Syt * Fill the error context into sata_cmd for non-queued command error. 729068d33a25Syt */ 729168d33a25Syt static void 729268d33a25Syt ahci_copy_err_cnxt(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 729368d33a25Syt { 729468d33a25Syt scmd->satacmd_status_reg = GET_RFIS_STATUS(rfisp); 729568d33a25Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 729668d33a25Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 729768d33a25Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 729868d33a25Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 729968d33a25Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 730068d33a25Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 730168d33a25Syt 730268d33a25Syt if (scmd->satacmd_addr_type == ATA_ADDR_LBA48) { 730368d33a25Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 730468d33a25Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 730568d33a25Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 730668d33a25Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 730768d33a25Syt } 730868d33a25Syt } 730968d33a25Syt 731082263d52Syt /* 731182263d52Syt * Fill the ncq error page into sata_cmd for queued command error. 731282263d52Syt */ 731382263d52Syt static void 731482263d52Syt ahci_copy_ncq_err_page(sata_cmd_t *scmd, 731582263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page) 731682263d52Syt { 731782263d52Syt scmd->satacmd_sec_count_msb = ncq_err_page->ncq_sector_count_ext; 731882263d52Syt scmd->satacmd_sec_count_lsb = ncq_err_page->ncq_sector_count; 731982263d52Syt scmd->satacmd_lba_low_msb = ncq_err_page->ncq_sector_number_ext; 732082263d52Syt scmd->satacmd_lba_low_lsb = ncq_err_page->ncq_sector_number; 732182263d52Syt scmd->satacmd_lba_mid_msb = ncq_err_page->ncq_cyl_low_ext; 732282263d52Syt scmd->satacmd_lba_mid_lsb = ncq_err_page->ncq_cyl_low; 732382263d52Syt scmd->satacmd_lba_high_msb = ncq_err_page->ncq_cyl_high_ext; 732482263d52Syt scmd->satacmd_lba_high_lsb = ncq_err_page->ncq_cyl_high; 732582263d52Syt scmd->satacmd_device_reg = ncq_err_page->ncq_dev_head; 732682263d52Syt scmd->satacmd_status_reg = ncq_err_page->ncq_status; 732782263d52Syt scmd->satacmd_error_reg = ncq_err_page->ncq_error; 732882263d52Syt } 732982263d52Syt 733068d33a25Syt /* 733168d33a25Syt * Put the respective register value to sata_cmd_t for satacmd_flags. 73322fcbc377Syt */ 73332fcbc377Syt static void 73342fcbc377Syt ahci_copy_out_regs(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 73352fcbc377Syt { 73362fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_msb) 73372fcbc377Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 73382fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_msb) 73392fcbc377Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 73402fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_msb) 73412fcbc377Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 73422fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_msb) 73432fcbc377Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 73442fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_lsb) 73452fcbc377Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 73462fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_lsb) 73472fcbc377Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 73482fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_lsb) 73492fcbc377Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 73502fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_lsb) 73512fcbc377Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 73522fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_device_reg) 73532fcbc377Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 73542fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_error_reg) 73552fcbc377Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 73562fcbc377Syt } 73572fcbc377Syt 735868d33a25Syt static void 735968d33a25Syt ahci_log_fatal_error_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 736068d33a25Syt uint32_t intr_status) 736168d33a25Syt { 7362a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 736368d33a25Syt 736468d33a25Syt if (intr_status & AHCI_INTR_STATUS_IFS) 73657095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has interface fatal " 7366a9440e8dSyt "error", instance, port); 736768d33a25Syt 736868d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBDS) 73697095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has bus data error", 7370a9440e8dSyt instance, port); 737168d33a25Syt 737268d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBFS) 73737095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has bus fatal error", 7374a9440e8dSyt instance, port); 737568d33a25Syt 737668d33a25Syt if (intr_status & AHCI_INTR_STATUS_TFES) 73777095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has task file error", 7378a9440e8dSyt instance, port); 737968d33a25Syt 73807095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d is trying to do error " 7381a9440e8dSyt "recovery", instance, port); 738268d33a25Syt } 738368d33a25Syt 73842fcbc377Syt /* 73852fcbc377Syt * Dump the serror message to the log. 73862fcbc377Syt */ 73872fcbc377Syt static void 738868d33a25Syt ahci_log_serror_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 7389a9440e8dSyt uint32_t port_serror, int debug_only) 73902fcbc377Syt { 7391a9440e8dSyt static char err_buf[512]; 7392a9440e8dSyt static char err_msg_header[16]; 7393a9440e8dSyt char *err_msg = err_buf; 7394a9440e8dSyt 7395a9440e8dSyt *err_buf = '\0'; 7396a9440e8dSyt *err_msg_header = '\0'; 73972fcbc377Syt 739882263d52Syt if (port_serror & SERROR_DATA_ERR_FIXED) { 7399a9440e8dSyt err_msg = strcat(err_msg, 7400a9440e8dSyt "\tRecovered Data Integrity Error (I)\n"); 74012fcbc377Syt } 74022fcbc377Syt 740382263d52Syt if (port_serror & SERROR_COMM_ERR_FIXED) { 7404a9440e8dSyt err_msg = strcat(err_msg, 7405a9440e8dSyt "\tRecovered Communication Error (M)\n"); 74062fcbc377Syt } 74072fcbc377Syt 740882263d52Syt if (port_serror & SERROR_DATA_ERR) { 7409a9440e8dSyt err_msg = strcat(err_msg, 7410a9440e8dSyt "\tTransient Data Integrity Error (T)\n"); 74112fcbc377Syt } 74122fcbc377Syt 741382263d52Syt if (port_serror & SERROR_PERSISTENT_ERR) { 7414a9440e8dSyt err_msg = strcat(err_msg, 7415a9440e8dSyt "\tPersistent Communication or Data Integrity Error (C)\n"); 74162fcbc377Syt } 74172fcbc377Syt 741882263d52Syt if (port_serror & SERROR_PROTOCOL_ERR) { 7419a9440e8dSyt err_msg = strcat(err_msg, "\tProtocol Error (P)\n"); 74202fcbc377Syt } 74212fcbc377Syt 742282263d52Syt if (port_serror & SERROR_INT_ERR) { 7423a9440e8dSyt err_msg = strcat(err_msg, "\tInternal Error (E)\n"); 74242fcbc377Syt } 74252fcbc377Syt 742682263d52Syt if (port_serror & SERROR_PHY_RDY_CHG) { 7427a9440e8dSyt err_msg = strcat(err_msg, "\tPhyRdy Change (N)\n"); 74282fcbc377Syt } 74292fcbc377Syt 743082263d52Syt if (port_serror & SERROR_PHY_INT_ERR) { 7431a9440e8dSyt err_msg = strcat(err_msg, "\tPhy Internal Error (I)\n"); 74322fcbc377Syt } 74332fcbc377Syt 743482263d52Syt if (port_serror & SERROR_COMM_WAKE) { 7435a9440e8dSyt err_msg = strcat(err_msg, "\tComm Wake (W)\n"); 74362fcbc377Syt } 74372fcbc377Syt 743882263d52Syt if (port_serror & SERROR_10B_TO_8B_ERR) { 7439a9440e8dSyt err_msg = strcat(err_msg, "\t10B to 8B Decode Error (B)\n"); 74402fcbc377Syt } 74412fcbc377Syt 744282263d52Syt if (port_serror & SERROR_DISPARITY_ERR) { 7443a9440e8dSyt err_msg = strcat(err_msg, "\tDisparity Error (D)\n"); 74442fcbc377Syt } 74452fcbc377Syt 744682263d52Syt if (port_serror & SERROR_CRC_ERR) { 7447a9440e8dSyt err_msg = strcat(err_msg, "\tCRC Error (C)\n"); 74482fcbc377Syt } 74492fcbc377Syt 745082263d52Syt if (port_serror & SERROR_HANDSHAKE_ERR) { 7451a9440e8dSyt err_msg = strcat(err_msg, "\tHandshake Error (H)\n"); 74522fcbc377Syt } 74532fcbc377Syt 745482263d52Syt if (port_serror & SERROR_LINK_SEQ_ERR) { 7455a9440e8dSyt err_msg = strcat(err_msg, "\tLink Sequence Error (S)\n"); 74562fcbc377Syt } 74572fcbc377Syt 745882263d52Syt if (port_serror & SERROR_TRANS_ERR) { 7459a9440e8dSyt err_msg = strcat(err_msg, 7460a9440e8dSyt "\tTransport state transition error (T)\n"); 74612fcbc377Syt } 74622fcbc377Syt 746382263d52Syt if (port_serror & SERROR_FIS_TYPE) { 7464a9440e8dSyt err_msg = strcat(err_msg, "\tUnknown FIS Type (F)\n"); 74652fcbc377Syt } 74662fcbc377Syt 746782263d52Syt if (port_serror & SERROR_EXCHANGED_ERR) { 7468a9440e8dSyt err_msg = strcat(err_msg, "\tExchanged (X)\n"); 7469a9440e8dSyt } 7470a9440e8dSyt 7471a9440e8dSyt if (err_msg == NULL) 7472a9440e8dSyt return; 7473a9440e8dSyt 7474a9440e8dSyt if (debug_only) { 7475a9440e8dSyt (void) sprintf(err_msg_header, "port %d", port); 7476a9440e8dSyt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, err_msg_header); 7477a9440e8dSyt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, err_msg); 7478a9440e8dSyt } else if (ahci_ctlp) { 74797095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: %s %s", 7480a9440e8dSyt ddi_get_instance(ahci_ctlp->ahcictl_dip), 7481a9440e8dSyt err_msg_header, err_msg); 7482a9440e8dSyt } else { 74837095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci: %s %s", err_msg_header, err_msg); 74842fcbc377Syt } 74852fcbc377Syt } 74862fcbc377Syt 74872fcbc377Syt /* 74882fcbc377Syt * This routine is to calculate the total number of ports implemented 74892fcbc377Syt * by the HBA. 74902fcbc377Syt */ 74912fcbc377Syt static int 74922fcbc377Syt ahci_get_num_implemented_ports(uint32_t ports_implemented) 74932fcbc377Syt { 74942fcbc377Syt uint8_t i; 74952fcbc377Syt int num = 0; 74962fcbc377Syt 74972fcbc377Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 74982fcbc377Syt if (((uint32_t)0x1 << i) & ports_implemented) 74992fcbc377Syt num++; 75002fcbc377Syt } 75012fcbc377Syt 75022fcbc377Syt return (num); 75032fcbc377Syt } 75042fcbc377Syt 7505689d74b0Syt #if AHCI_DEBUG 75062fcbc377Syt static void 75072fcbc377Syt ahci_log(ahci_ctl_t *ahci_ctlp, uint_t level, char *fmt, ...) 75082fcbc377Syt { 7509a9440e8dSyt static char name[16]; 75102fcbc377Syt va_list ap; 75112fcbc377Syt 75122fcbc377Syt mutex_enter(&ahci_log_mutex); 75132fcbc377Syt 75142fcbc377Syt va_start(ap, fmt); 75152fcbc377Syt if (ahci_ctlp) { 7516a9440e8dSyt (void) sprintf(name, "ahci%d: ", 75172fcbc377Syt ddi_get_instance(ahci_ctlp->ahcictl_dip)); 75182fcbc377Syt } else { 7519a9440e8dSyt (void) sprintf(name, "ahci: "); 75202fcbc377Syt } 75212fcbc377Syt 75222fcbc377Syt (void) vsprintf(ahci_log_buf, fmt, ap); 75232fcbc377Syt va_end(ap); 75242fcbc377Syt 7525a9440e8dSyt cmn_err(level, "%s%s", name, ahci_log_buf); 75262fcbc377Syt 75272fcbc377Syt mutex_exit(&ahci_log_mutex); 75282fcbc377Syt } 7529689d74b0Syt #endif 753019397407SSherry Moore 753119397407SSherry Moore /* 753219397407SSherry Moore * quiesce(9E) entry point. 753319397407SSherry Moore * 753419397407SSherry Moore * This function is called when the system is single-threaded at high 753519397407SSherry Moore * PIL with preemption disabled. Therefore, this function must not be 753619397407SSherry Moore * blocked. 753719397407SSherry Moore * 753819397407SSherry Moore * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 753919397407SSherry Moore * DDI_FAILURE indicates an error condition and should almost never happen. 754019397407SSherry Moore */ 754119397407SSherry Moore static int 754219397407SSherry Moore ahci_quiesce(dev_info_t *dip) 754319397407SSherry Moore { 754419397407SSherry Moore ahci_ctl_t *ahci_ctlp; 754519397407SSherry Moore ahci_port_t *ahci_portp; 754619397407SSherry Moore int instance, port; 754719397407SSherry Moore 754819397407SSherry Moore instance = ddi_get_instance(dip); 754919397407SSherry Moore ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 755019397407SSherry Moore 755119397407SSherry Moore if (ahci_ctlp == NULL) 755219397407SSherry Moore return (DDI_FAILURE); 755319397407SSherry Moore 755419397407SSherry Moore #if AHCI_DEBUG 755519397407SSherry Moore ahci_debug_flags = 0; 755619397407SSherry Moore #endif 755719397407SSherry Moore 755819397407SSherry Moore /* disable all the interrupts. */ 755919397407SSherry Moore ahci_disable_all_intrs(ahci_ctlp); 756019397407SSherry Moore 756119397407SSherry Moore for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 756219397407SSherry Moore if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 756319397407SSherry Moore continue; 756419397407SSherry Moore } 756519397407SSherry Moore 756619397407SSherry Moore ahci_portp = ahci_ctlp->ahcictl_ports[port]; 756719397407SSherry Moore 756819397407SSherry Moore /* 756919397407SSherry Moore * Stop the port by clearing PxCMD.ST 757019397407SSherry Moore * 757119397407SSherry Moore * Here we must disable the port interrupt because 757219397407SSherry Moore * ahci_disable_all_intrs only clear GHC.IE, and IS 757319397407SSherry Moore * register will be still set if PxIE is enabled. 757419397407SSherry Moore * When ahci shares one IRQ with other drivers, the 757519397407SSherry Moore * intr handler may claim the intr mistakenly. 757619397407SSherry Moore */ 757719397407SSherry Moore ahci_disable_port_intrs(ahci_ctlp, port); 757819397407SSherry Moore (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 757919397407SSherry Moore ahci_portp, port); 758019397407SSherry Moore } 758119397407SSherry Moore 758219397407SSherry Moore return (DDI_SUCCESS); 758319397407SSherry Moore } 7584