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 /* 23*2c742e1fSying 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); 143*2c742e1fSying 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 /* 311db2cce03Sying tian - Beijing China * ahci_dma_prdt_number, ahci_msi_enabled and ahci_64bit_dma_addressed 312db2cce03Sying tian - Beijing China * are global variables, and can be changed via /etc/system file. 313db2cce03Sying tian - Beijing China */ 314db2cce03Sying tian - Beijing China 3152fcbc377Syt /* The number of Physical Region Descriptor Table(PRDT) in Command Table */ 3162fcbc377Syt int ahci_dma_prdt_number = AHCI_PRDT_NUMBER; 3172fcbc377Syt 318db2cce03Sying tian - Beijing China /* AHCI MSI is tunable */ 319*2c742e1fSying tian - Beijing China boolean_t ahci_msi_enabled = B_TRUE; 3202fcbc377Syt 321db2cce03Sying tian - Beijing China /* 64-bit dma addressing is tunable */ 322db2cce03Sying tian - Beijing China boolean_t ahci_64bit_dma_addressed = B_TRUE; 323db2cce03Sying tian - Beijing China 3242fcbc377Syt #if AHCI_DEBUG 3252fcbc377Syt uint32_t ahci_debug_flags = 0; 326689d74b0Syt 327689d74b0Syt /* The following is needed for ahci_log() */ 328689d74b0Syt static kmutex_t ahci_log_mutex; 329689d74b0Syt static char ahci_log_buf[512]; 3302fcbc377Syt #endif 3312fcbc377Syt 3322fcbc377Syt /* Opaque state pointer initialized by ddi_soft_state_init() */ 3332fcbc377Syt static void *ahci_statep = NULL; 3342fcbc377Syt 3352fcbc377Syt /* 3362fcbc377Syt * ahci module initialization. 3372fcbc377Syt */ 3382fcbc377Syt int 3392fcbc377Syt _init(void) 3402fcbc377Syt { 3412fcbc377Syt int ret; 3422fcbc377Syt 3432fcbc377Syt ret = ddi_soft_state_init(&ahci_statep, sizeof (ahci_ctl_t), 0); 3442fcbc377Syt if (ret != 0) { 3452fcbc377Syt goto err_out; 3462fcbc377Syt } 3472fcbc377Syt 348689d74b0Syt #if AHCI_DEBUG 3492fcbc377Syt mutex_init(&ahci_log_mutex, NULL, MUTEX_DRIVER, NULL); 350689d74b0Syt #endif 3512fcbc377Syt 3522fcbc377Syt if ((ret = sata_hba_init(&modlinkage)) != 0) { 353689d74b0Syt #if AHCI_DEBUG 3542fcbc377Syt mutex_destroy(&ahci_log_mutex); 355689d74b0Syt #endif 3562fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3572fcbc377Syt goto err_out; 3582fcbc377Syt } 3592fcbc377Syt 3602fcbc377Syt ret = mod_install(&modlinkage); 3612fcbc377Syt if (ret != 0) { 3622fcbc377Syt sata_hba_fini(&modlinkage); 363689d74b0Syt #if AHCI_DEBUG 3642fcbc377Syt mutex_destroy(&ahci_log_mutex); 365689d74b0Syt #endif 3662fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3672fcbc377Syt goto err_out; 3682fcbc377Syt } 3692fcbc377Syt 3702fcbc377Syt /* watchdog tick */ 3712fcbc377Syt ahci_watchdog_tick = drv_usectohz( 3722fcbc377Syt (clock_t)ahci_watchdog_timeout * 1000000); 3732fcbc377Syt return (ret); 3742fcbc377Syt 3752fcbc377Syt err_out: 376a9440e8dSyt cmn_err(CE_WARN, "!ahci: Module init failed"); 3772fcbc377Syt return (ret); 3782fcbc377Syt } 3792fcbc377Syt 3802fcbc377Syt /* 3812fcbc377Syt * ahci module uninitialize. 3822fcbc377Syt */ 3832fcbc377Syt int 3842fcbc377Syt _fini(void) 3852fcbc377Syt { 3862fcbc377Syt int ret; 3872fcbc377Syt 3882fcbc377Syt ret = mod_remove(&modlinkage); 3892fcbc377Syt if (ret != 0) { 3902fcbc377Syt return (ret); 3912fcbc377Syt } 3922fcbc377Syt 3932fcbc377Syt /* Remove the resources allocated in _init(). */ 3942fcbc377Syt sata_hba_fini(&modlinkage); 395689d74b0Syt #if AHCI_DEBUG 3962fcbc377Syt mutex_destroy(&ahci_log_mutex); 397689d74b0Syt #endif 3982fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3992fcbc377Syt 4002fcbc377Syt return (ret); 4012fcbc377Syt } 4022fcbc377Syt 4032fcbc377Syt /* 4042fcbc377Syt * _info entry point 4052fcbc377Syt */ 4062fcbc377Syt int 4072fcbc377Syt _info(struct modinfo *modinfop) 4082fcbc377Syt { 4092fcbc377Syt return (mod_info(&modlinkage, modinfop)); 4102fcbc377Syt } 4112fcbc377Syt 4122fcbc377Syt /* 4132fcbc377Syt * The attach entry point for dev_ops. 4142fcbc377Syt */ 4152fcbc377Syt static int 4162fcbc377Syt ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4172fcbc377Syt { 4182fcbc377Syt ahci_ctl_t *ahci_ctlp; 4192fcbc377Syt int instance = ddi_get_instance(dip); 4202fcbc377Syt int status; 4212fcbc377Syt int attach_state; 4222fcbc377Syt uint32_t cap_status, ahci_version; 4232fcbc377Syt int intr_types; 42468d33a25Syt int i; 42595c11c1fSyt pci_regspec_t *regs; 42695c11c1fSyt int regs_length; 42795c11c1fSyt int rnumber; 42868d33a25Syt #if AHCI_DEBUG 42968d33a25Syt int speed; 43068d33a25Syt #endif 4312fcbc377Syt 4322fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, NULL, "ahci_attach enter"); 4332fcbc377Syt 4342fcbc377Syt switch (cmd) { 4352fcbc377Syt case DDI_ATTACH: 4362fcbc377Syt break; 4372fcbc377Syt 4382fcbc377Syt case DDI_RESUME: 43913bcbb7aSyt 44013bcbb7aSyt /* 44113bcbb7aSyt * During DDI_RESUME, the hardware state of the device 44213bcbb7aSyt * (power may have been removed from the device) must be 44313bcbb7aSyt * restored, allow pending requests to continue, and 44413bcbb7aSyt * service new requests. 44513bcbb7aSyt */ 44613bcbb7aSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 44713bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 44813bcbb7aSyt 44913bcbb7aSyt /* Restart watch thread */ 45013bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id == 0) 45113bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = timeout( 45213bcbb7aSyt (void (*)(void *))ahci_watchdog_handler, 45313bcbb7aSyt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 45413bcbb7aSyt 45513bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 45613bcbb7aSyt 45713bcbb7aSyt /* 45813bcbb7aSyt * Re-initialize the controller and enable the interrupts and 45913bcbb7aSyt * restart all the ports. 46013bcbb7aSyt * 46113bcbb7aSyt * Note that so far we don't support hot-plug during 46213bcbb7aSyt * suspend/resume. 46313bcbb7aSyt */ 46413bcbb7aSyt if (ahci_initialize_controller(ahci_ctlp) != AHCI_SUCCESS) { 465a9440e8dSyt AHCIDBG0(AHCIDBG_ERRS|AHCIDBG_PM, ahci_ctlp, 466a9440e8dSyt "Failed to initialize the controller " 467a9440e8dSyt "during DDI_RESUME"); 46813bcbb7aSyt return (DDI_FAILURE); 46913bcbb7aSyt } 47013bcbb7aSyt 47113bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 47213bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~ AHCI_SUSPEND; 47313bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 47413bcbb7aSyt 47513bcbb7aSyt return (DDI_SUCCESS); 4762fcbc377Syt 4772fcbc377Syt default: 4782fcbc377Syt return (DDI_FAILURE); 4792fcbc377Syt } 4802fcbc377Syt 4812fcbc377Syt attach_state = AHCI_ATTACH_STATE_NONE; 4822fcbc377Syt 4832fcbc377Syt /* Allocate soft state */ 4842fcbc377Syt status = ddi_soft_state_zalloc(ahci_statep, instance); 4852fcbc377Syt if (status != DDI_SUCCESS) { 486a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate soft state", 487a9440e8dSyt instance); 4882fcbc377Syt goto err_out; 4892fcbc377Syt } 4902fcbc377Syt 4912fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 49213bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_ATTACH; 4932fcbc377Syt ahci_ctlp->ahcictl_dip = dip; 4942fcbc377Syt 49568d33a25Syt /* Initialize the cport/port mapping */ 49668d33a25Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 49768d33a25Syt ahci_ctlp->ahcictl_port_to_cport[i] = 0xff; 49868d33a25Syt ahci_ctlp->ahcictl_cport_to_port[i] = 0xff; 49968d33a25Syt } 50068d33a25Syt 5012fcbc377Syt attach_state |= AHCI_ATTACH_STATE_STATEP_ALLOC; 5022fcbc377Syt 5032fcbc377Syt /* 5042fcbc377Syt * Now map the AHCI base address; which includes global 5052fcbc377Syt * registers and port control registers 50695c11c1fSyt * 50795c11c1fSyt * According to the spec, the AHCI Base Address is BAR5, 50813bcbb7aSyt * but BAR0-BAR4 are optional, so we need to check which 50913bcbb7aSyt * rnumber is used for BAR5. 51095c11c1fSyt */ 51195c11c1fSyt 51295c11c1fSyt /* 51395c11c1fSyt * search through DDI "reg" property for the AHCI register set 5142fcbc377Syt */ 51595c11c1fSyt if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 51695c11c1fSyt DDI_PROP_DONTPASS, "reg", (int **)®s, 51795c11c1fSyt (uint_t *)®s_length) != DDI_PROP_SUCCESS) { 518a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot lookup reg property", 519a9440e8dSyt instance); 52095c11c1fSyt goto err_out; 52195c11c1fSyt } 52295c11c1fSyt 52395c11c1fSyt /* AHCI Base Address is located at 0x24 offset */ 52495c11c1fSyt for (rnumber = 0; rnumber < regs_length; ++rnumber) { 52595c11c1fSyt if ((regs[rnumber].pci_phys_hi & PCI_REG_REG_M) 52695c11c1fSyt == AHCI_PCI_RNUM) 52795c11c1fSyt break; 52895c11c1fSyt } 52995c11c1fSyt 53095c11c1fSyt ddi_prop_free(regs); 53195c11c1fSyt 53295c11c1fSyt if (rnumber == regs_length) { 533a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot find AHCI register set", 534a9440e8dSyt instance); 53595c11c1fSyt goto err_out; 53695c11c1fSyt } 53795c11c1fSyt 53895c11c1fSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "rnumber = %d", rnumber); 53995c11c1fSyt 5402fcbc377Syt status = ddi_regs_map_setup(dip, 54195c11c1fSyt rnumber, 5422fcbc377Syt (caddr_t *)&ahci_ctlp->ahcictl_ahci_addr, 5432fcbc377Syt 0, 5442fcbc377Syt 0, 5452fcbc377Syt &accattr, 5462fcbc377Syt &ahci_ctlp->ahcictl_ahci_acc_handle); 5472fcbc377Syt if (status != DDI_SUCCESS) { 548a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot map register space", 549a9440e8dSyt instance); 5502fcbc377Syt goto err_out; 5512fcbc377Syt } 5522fcbc377Syt 5532fcbc377Syt attach_state |= AHCI_ATTACH_STATE_REG_MAP; 5542fcbc377Syt 5552fcbc377Syt /* Get the AHCI version information */ 5562fcbc377Syt ahci_version = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5572fcbc377Syt (uint32_t *)AHCI_GLOBAL_VS(ahci_ctlp)); 5582fcbc377Syt 559a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: hba AHCI version = %x.%x", instance, 5602fcbc377Syt (ahci_version & 0xffff0000) >> 16, 5612fcbc377Syt ((ahci_version & 0x0000ff00) >> 4 | 5622fcbc377Syt (ahci_version & 0x000000ff))); 5632fcbc377Syt 5642fcbc377Syt /* We don't support controllers whose versions are lower than 1.0 */ 5652fcbc377Syt if (!(ahci_version & 0xffff0000)) { 566a9440e8dSyt cmn_err(CE_WARN, "ahci%d: Don't support AHCI HBA with lower " 567a9440e8dSyt "than version 1.0", instance); 5682fcbc377Syt goto err_out; 5692fcbc377Syt } 5702fcbc377Syt 5712fcbc377Syt /* Get the HBA capabilities information */ 5722fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5732fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 5742fcbc377Syt 5752fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba capabilites = 0x%x", 5762fcbc377Syt cap_status); 5772fcbc377Syt 57868d33a25Syt #if AHCI_DEBUG 57968d33a25Syt /* Get the interface speed supported by the HBA */ 58068d33a25Syt speed = (cap_status & AHCI_HBA_CAP_ISS) >> AHCI_HBA_CAP_ISS_SHIFT; 58168d33a25Syt if (speed == 0x01) { 58268d33a25Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 58368d33a25Syt "hba interface speed support: Gen 1 (1.5Gbps)"); 58468d33a25Syt } else if (speed == 0x10) { 58568d33a25Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 58668d33a25Syt "hba interface speed support: Gen 2 (3 Gbps)"); 58768d33a25Syt } else if (speed == 0x11) { 58868d33a25Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 58968d33a25Syt "hba interface speed support: Gen 3 (6 Gbps)"); 59068d33a25Syt } 59168d33a25Syt #endif 59268d33a25Syt 5932fcbc377Syt /* Get the number of command slots supported by the HBA */ 5942fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots = 5952fcbc377Syt ((cap_status & AHCI_HBA_CAP_NCS) >> 5962fcbc377Syt AHCI_HBA_CAP_NCS_SHIFT) + 1; 5972fcbc377Syt 59868d33a25Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba number of cmd slots: %d", 5992fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots); 6002fcbc377Syt 6012fcbc377Syt /* Get the bit map which indicates ports implemented by the HBA */ 6022fcbc377Syt ahci_ctlp->ahcictl_ports_implemented = 6032fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 6042fcbc377Syt (uint32_t *)AHCI_GLOBAL_PI(ahci_ctlp)); 6052fcbc377Syt 6062fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba implementation of ports: 0x%x", 6072fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 6082fcbc377Syt 60909121340Syt /* 61009121340Syt * According to the AHCI spec, CAP.NP should indicate the maximum 61109121340Syt * number of ports supported by the HBA silicon, but we found 61209121340Syt * this value of ICH8 chipset only indicates the number of ports 61309121340Syt * implemented (exposed) by it. Therefore, the driver should calculate 61409121340Syt * the potential maximum value by checking PI register, and use 61509121340Syt * the maximum of this value and CAP.NP. 61609121340Syt */ 61709121340Syt ahci_ctlp->ahcictl_num_ports = max( 61809121340Syt (cap_status & AHCI_HBA_CAP_NP) + 1, 61909121340Syt ddi_fls(ahci_ctlp->ahcictl_ports_implemented)); 62009121340Syt 62109121340Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba number of ports: %d", 62209121340Syt ahci_ctlp->ahcictl_num_ports); 62309121340Syt 6242fcbc377Syt /* Get the number of implemented ports by the HBA */ 6252fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports = 6262fcbc377Syt ahci_get_num_implemented_ports( 6272fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 6282fcbc377Syt 6292fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 63068d33a25Syt "hba number of implemented ports: %d", 6312fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports); 6322fcbc377Syt 633a9440e8dSyt /* Check whether HBA supports 64bit DMA addressing */ 6342fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_S64A)) { 635a9440e8dSyt ahci_ctlp->ahcictl_cap |= AHCI_CAP_32BIT_DMA; 6362fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 6372fcbc377Syt "hba does not support 64-bit addressing"); 6382fcbc377Syt } 6392fcbc377Syt 6402fcbc377Syt if (pci_config_setup(dip, &ahci_ctlp->ahcictl_pci_conf_handle) 6412fcbc377Syt != DDI_SUCCESS) { 642a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot set up pci configure space", 643a9440e8dSyt instance); 6442fcbc377Syt goto err_out; 6452fcbc377Syt } 6462fcbc377Syt 64768d33a25Syt attach_state |= AHCI_ATTACH_STATE_PCICFG_SETUP; 64868d33a25Syt 649db2cce03Sying tian - Beijing China /* 650db2cce03Sying tian - Beijing China * Check the pci configuration space, and set caps. We also 651db2cce03Sying tian - Beijing China * handle the hardware defect in this function. 652db2cce03Sying tian - Beijing China * 653db2cce03Sying tian - Beijing China * For example, force ATI SB600/SB700/SB750 to use 32-bit dma 654db2cce03Sying tian - Beijing China * addressing since it doesn't support 64-bit dma though their 655db2cce03Sying tian - Beijing China * registers declare they support. 656db2cce03Sying tian - Beijing China */ 65713bcbb7aSyt if (ahci_config_space_init(ahci_ctlp) == AHCI_FAILURE) { 658a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_config_space_init failed", 659a9440e8dSyt instance); 66013bcbb7aSyt goto err_out; 66168d33a25Syt } 6622fcbc377Syt 6632fcbc377Syt /* 6642fcbc377Syt * Disable the whole controller interrupts before adding 6652fcbc377Syt * interrupt handlers(s). 6662fcbc377Syt */ 6672fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 6682fcbc377Syt 6692fcbc377Syt /* Get supported interrupt types */ 6702fcbc377Syt if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { 671a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ddi_intr_get_supported_types failed", 672a9440e8dSyt instance); 6732fcbc377Syt goto err_out; 6742fcbc377Syt } 6752fcbc377Syt 6762fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 6772fcbc377Syt "ddi_intr_get_supported_types() returned: 0x%x", 6782fcbc377Syt intr_types); 6792fcbc377Syt 6802fcbc377Syt if (ahci_msi_enabled && (intr_types & DDI_INTR_TYPE_MSI)) { 6812fcbc377Syt /* 6822fcbc377Syt * Try MSI first, but fall back to FIXED if failed 6832fcbc377Syt */ 684*2c742e1fSying tian - Beijing China if (ahci_add_intrs(ahci_ctlp, DDI_INTR_TYPE_MSI) == 685*2c742e1fSying tian - Beijing China DDI_SUCCESS) { 6862fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_MSI; 6872fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 6882fcbc377Syt "Using MSI interrupt type"); 6892fcbc377Syt goto intr_done; 6902fcbc377Syt } 6912fcbc377Syt 6922fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 6932fcbc377Syt "MSI registration failed, " 6942fcbc377Syt "trying FIXED interrupts"); 6952fcbc377Syt } 6962fcbc377Syt 6972fcbc377Syt if (intr_types & DDI_INTR_TYPE_FIXED) { 698*2c742e1fSying tian - Beijing China if (ahci_add_intrs(ahci_ctlp, DDI_INTR_TYPE_FIXED) == 699*2c742e1fSying tian - Beijing China DDI_SUCCESS) { 7002fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_FIXED; 701a9440e8dSyt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7022fcbc377Syt "Using FIXED interrupt type"); 7032fcbc377Syt goto intr_done; 7042fcbc377Syt } 7052fcbc377Syt 7062fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7072fcbc377Syt "FIXED interrupt registration failed"); 7082fcbc377Syt } 7092fcbc377Syt 710a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Interrupt registration failed", instance); 7112fcbc377Syt 7122fcbc377Syt goto err_out; 7132fcbc377Syt 7142fcbc377Syt intr_done: 7152fcbc377Syt 7162fcbc377Syt attach_state |= AHCI_ATTACH_STATE_INTR_ADDED; 7172fcbc377Syt 7182fcbc377Syt /* Initialize the controller mutex */ 7192fcbc377Syt mutex_init(&ahci_ctlp->ahcictl_mutex, NULL, MUTEX_DRIVER, 7202fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 7212fcbc377Syt 7222fcbc377Syt attach_state |= AHCI_ATTACH_STATE_MUTEX_INIT; 7232fcbc377Syt 7242fcbc377Syt if (ahci_dma_prdt_number < AHCI_MIN_PRDT_NUMBER) { 7252fcbc377Syt ahci_dma_prdt_number = AHCI_MIN_PRDT_NUMBER; 7262fcbc377Syt } else if (ahci_dma_prdt_number > AHCI_MAX_PRDT_NUMBER) { 7272fcbc377Syt ahci_dma_prdt_number = AHCI_MAX_PRDT_NUMBER; 7282fcbc377Syt } 7292fcbc377Syt 7302fcbc377Syt ahci_cmd_table_size = (sizeof (ahci_cmd_table_t) + 7312fcbc377Syt (ahci_dma_prdt_number - AHCI_PRDT_NUMBER) * 7322fcbc377Syt sizeof (ahci_prdt_item_t)); 7332fcbc377Syt 7342fcbc377Syt AHCIDBG2(AHCIDBG_INIT, ahci_ctlp, 7352fcbc377Syt "ahci_attach: ahci_dma_prdt_number set by user is 0x%x," 7362fcbc377Syt " ahci_cmd_table_size is 0x%x", 7372fcbc377Syt ahci_dma_prdt_number, ahci_cmd_table_size); 7382fcbc377Syt 7392fcbc377Syt if (ahci_dma_prdt_number != AHCI_PRDT_NUMBER) 7402fcbc377Syt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_sgllen = 7412fcbc377Syt ahci_dma_prdt_number; 7422fcbc377Syt 743a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr = buffer_dma_attr; 744a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr = rcvd_fis_dma_attr; 745a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr = cmd_list_dma_attr; 746a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr = cmd_table_dma_attr; 747a9440e8dSyt 748db2cce03Sying tian - Beijing China if ((ahci_64bit_dma_addressed == B_FALSE) || 749db2cce03Sying tian - Beijing China (ahci_ctlp->ahcictl_cap & AHCI_CAP_32BIT_DMA)) { 750a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_addr_hi = 751a9440e8dSyt 0xffffffffull; 752a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr.dma_attr_addr_hi = 753a9440e8dSyt 0xffffffffull; 754a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr.dma_attr_addr_hi = 755a9440e8dSyt 0xffffffffull; 756a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr.dma_attr_addr_hi = 757a9440e8dSyt 0xffffffffull; 758a9440e8dSyt } 759a9440e8dSyt 76068d33a25Syt /* Allocate the ports structure */ 76168d33a25Syt status = ahci_alloc_ports_state(ahci_ctlp); 76268d33a25Syt if (status != AHCI_SUCCESS) { 763a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate ports structure", 764a9440e8dSyt instance); 76568d33a25Syt goto err_out; 76668d33a25Syt } 76768d33a25Syt 76868d33a25Syt attach_state |= AHCI_ATTACH_STATE_PORT_ALLOC; 76968d33a25Syt 7702fcbc377Syt /* 77168d33a25Syt * Initialize the controller and ports. 7722fcbc377Syt */ 7732fcbc377Syt status = ahci_initialize_controller(ahci_ctlp); 7742fcbc377Syt if (status != AHCI_SUCCESS) { 775a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: HBA initialization failed", 776a9440e8dSyt instance); 7772fcbc377Syt goto err_out; 7782fcbc377Syt } 7792fcbc377Syt 7802fcbc377Syt attach_state |= AHCI_ATTACH_STATE_HW_INIT; 7812fcbc377Syt 7822fcbc377Syt /* Start one thread to check packet timeouts */ 7832fcbc377Syt ahci_ctlp->ahcictl_timeout_id = timeout( 7842fcbc377Syt (void (*)(void *))ahci_watchdog_handler, 7852fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 7862fcbc377Syt 7872fcbc377Syt attach_state |= AHCI_ATTACH_STATE_TIMEOUT_ENABLED; 7882fcbc377Syt 7892fcbc377Syt if (ahci_register_sata_hba_tran(ahci_ctlp, cap_status)) { 790a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: sata hba tran registration failed", 791a9440e8dSyt instance); 7922fcbc377Syt goto err_out; 7932fcbc377Syt } 7942fcbc377Syt 79513bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~AHCI_ATTACH; 79613bcbb7aSyt 7972fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, "ahci_attach success!"); 7982fcbc377Syt 7992fcbc377Syt return (DDI_SUCCESS); 8002fcbc377Syt 8012fcbc377Syt err_out: 8022fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_TIMEOUT_ENABLED) { 8032fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8042fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 8052fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 8062fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 8072fcbc377Syt } 8082fcbc377Syt 8092fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_HW_INIT) { 81068d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 8112fcbc377Syt } 8122fcbc377Syt 81368d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PORT_ALLOC) { 81468d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 81568d33a25Syt } 81668d33a25Syt 8172fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_MUTEX_INIT) { 8182fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 8192fcbc377Syt } 8202fcbc377Syt 8212fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_INTR_ADDED) { 8222fcbc377Syt ahci_rem_intrs(ahci_ctlp); 8232fcbc377Syt } 8242fcbc377Syt 82568d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PCICFG_SETUP) { 82668d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 82768d33a25Syt } 82868d33a25Syt 8292fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_REG_MAP) { 8302fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 8312fcbc377Syt } 8322fcbc377Syt 8332fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_STATEP_ALLOC) { 8342fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 8352fcbc377Syt } 8362fcbc377Syt 8372fcbc377Syt return (DDI_FAILURE); 8382fcbc377Syt } 8392fcbc377Syt 8402fcbc377Syt /* 8412fcbc377Syt * The detach entry point for dev_ops. 8422fcbc377Syt */ 8432fcbc377Syt static int 8442fcbc377Syt ahci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 8452fcbc377Syt { 8462fcbc377Syt ahci_ctl_t *ahci_ctlp; 8472fcbc377Syt int instance; 8482fcbc377Syt int ret; 8492fcbc377Syt 8502fcbc377Syt instance = ddi_get_instance(dip); 8512fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 8522fcbc377Syt 8532fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_detach enter"); 8542fcbc377Syt 8552fcbc377Syt switch (cmd) { 8562fcbc377Syt case DDI_DETACH: 85713bcbb7aSyt 8582fcbc377Syt /* disable the interrupts for an uninterrupted detach */ 8592fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8602fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 8612fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 8622fcbc377Syt 8632fcbc377Syt /* unregister from the sata framework. */ 8642fcbc377Syt ret = ahci_unregister_sata_hba_tran(ahci_ctlp); 8652fcbc377Syt if (ret != AHCI_SUCCESS) { 8662fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8672fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 8682fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 8692fcbc377Syt return (DDI_FAILURE); 8702fcbc377Syt } 8712fcbc377Syt 8722fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8732fcbc377Syt 8742fcbc377Syt /* stop the watchdog handler */ 8752fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 8762fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 8772fcbc377Syt 87868d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 87968d33a25Syt 88068d33a25Syt /* uninitialize the controller */ 88168d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 8822fcbc377Syt 88368d33a25Syt /* remove the interrupts */ 88468d33a25Syt ahci_rem_intrs(ahci_ctlp); 8852fcbc377Syt 88668d33a25Syt /* deallocate the ports structures */ 88768d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 88868d33a25Syt 88968d33a25Syt /* destroy mutex */ 8902fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 8912fcbc377Syt 89268d33a25Syt /* teardown the pci config */ 89368d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 8942fcbc377Syt 8952fcbc377Syt /* remove the reg maps. */ 8962fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 8972fcbc377Syt 8982fcbc377Syt /* free the soft state. */ 8992fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 9002fcbc377Syt 9012fcbc377Syt return (DDI_SUCCESS); 9022fcbc377Syt 9032fcbc377Syt case DDI_SUSPEND: 90413bcbb7aSyt 90513bcbb7aSyt /* 90613bcbb7aSyt * The steps associated with suspension must include putting 90713bcbb7aSyt * the underlying device into a quiescent state so that it 90813bcbb7aSyt * will not generate interrupts or modify or access memory. 90913bcbb7aSyt */ 91013bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 91113bcbb7aSyt if (ahci_ctlp->ahcictl_flags & AHCI_SUSPEND) { 91213bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 91313bcbb7aSyt return (DDI_SUCCESS); 91413bcbb7aSyt } 91513bcbb7aSyt 91613bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_SUSPEND; 91713bcbb7aSyt 91813bcbb7aSyt /* stop the watchdog handler */ 91913bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id) { 92013bcbb7aSyt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 92113bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = 0; 92213bcbb7aSyt } 92313bcbb7aSyt 92413bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 92513bcbb7aSyt 92613bcbb7aSyt /* 92713bcbb7aSyt * drain the taskq 92813bcbb7aSyt */ 929f8a673adSying tian - Beijing China ahci_drain_ports_taskq(ahci_ctlp); 93013bcbb7aSyt 93113bcbb7aSyt /* 93213bcbb7aSyt * Disable the interrupts and stop all the ports. 93313bcbb7aSyt */ 93413bcbb7aSyt ahci_uninitialize_controller(ahci_ctlp); 93513bcbb7aSyt 93613bcbb7aSyt return (DDI_SUCCESS); 9372fcbc377Syt 9382fcbc377Syt default: 9392fcbc377Syt return (DDI_FAILURE); 9402fcbc377Syt } 9412fcbc377Syt } 9422fcbc377Syt 9432fcbc377Syt /* 9442fcbc377Syt * The info entry point for dev_ops. 9452fcbc377Syt * 9462fcbc377Syt */ 9472fcbc377Syt static int 9482fcbc377Syt ahci_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 9492fcbc377Syt void *arg, void **result) 9502fcbc377Syt { 9512fcbc377Syt #ifndef __lock_lint 9522fcbc377Syt _NOTE(ARGUNUSED(dip)) 9532fcbc377Syt #endif /* __lock_lint */ 9542fcbc377Syt 9552fcbc377Syt ahci_ctl_t *ahci_ctlp; 9562fcbc377Syt int instance; 9572fcbc377Syt dev_t dev; 9582fcbc377Syt 9592fcbc377Syt dev = (dev_t)arg; 9602fcbc377Syt instance = getminor(dev); 9612fcbc377Syt 9622fcbc377Syt switch (infocmd) { 9632fcbc377Syt case DDI_INFO_DEVT2DEVINFO: 9642fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 9652fcbc377Syt if (ahci_ctlp != NULL) { 9662fcbc377Syt *result = ahci_ctlp->ahcictl_dip; 9672fcbc377Syt return (DDI_SUCCESS); 9682fcbc377Syt } else { 9692fcbc377Syt *result = NULL; 9702fcbc377Syt return (DDI_FAILURE); 9712fcbc377Syt } 9722fcbc377Syt case DDI_INFO_DEVT2INSTANCE: 9732fcbc377Syt *(int *)result = instance; 9742fcbc377Syt break; 9752fcbc377Syt default: 9762fcbc377Syt break; 9772fcbc377Syt } 9782fcbc377Syt 9792fcbc377Syt return (DDI_SUCCESS); 9802fcbc377Syt } 9812fcbc377Syt 9822fcbc377Syt /* 9832fcbc377Syt * Registers the ahci with sata framework. 9842fcbc377Syt */ 9852fcbc377Syt static int 9862fcbc377Syt ahci_register_sata_hba_tran(ahci_ctl_t *ahci_ctlp, uint32_t cap_status) 9872fcbc377Syt { 9882fcbc377Syt struct sata_hba_tran *sata_hba_tran; 9892fcbc377Syt 9902fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 9912fcbc377Syt "ahci_register_sata_hba_tran enter"); 9922fcbc377Syt 9932fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9942fcbc377Syt 9952fcbc377Syt /* Allocate memory for the sata_hba_tran */ 9962fcbc377Syt sata_hba_tran = kmem_zalloc(sizeof (sata_hba_tran_t), KM_SLEEP); 9972fcbc377Syt 99868d33a25Syt sata_hba_tran->sata_tran_hba_rev = SATA_TRAN_HBA_REV_2; 9992fcbc377Syt sata_hba_tran->sata_tran_hba_dip = ahci_ctlp->ahcictl_dip; 10002fcbc377Syt sata_hba_tran->sata_tran_hba_dma_attr = 10012fcbc377Syt &ahci_ctlp->ahcictl_buffer_dma_attr; 10022fcbc377Syt 10032fcbc377Syt /* Report the number of implemented ports */ 10042fcbc377Syt sata_hba_tran->sata_tran_hba_num_cports = 10052fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports; 10062fcbc377Syt 100768d33a25Syt /* Support ATAPI device */ 100868d33a25Syt sata_hba_tran->sata_tran_hba_features_support = SATA_CTLF_ATAPI; 10092fcbc377Syt 10102fcbc377Syt /* Get the data transfer capability for PIO command by the HBA */ 10112fcbc377Syt if (cap_status & AHCI_HBA_CAP_PMD) { 101282263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PIO_MDRQ; 10132fcbc377Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "HBA supports multiple " 10142fcbc377Syt "DRQ block data transfer for PIO command protocol"); 10152fcbc377Syt } 10162fcbc377Syt 101782263d52Syt /* 101882263d52Syt * According to the AHCI spec, the ATA/ATAPI-7 queued feature set 101982263d52Syt * is not supported by AHCI (including the READ QUEUED (EXT), WRITE 102082263d52Syt * QUEUED (EXT), and SERVICE commands). Queued operations are 102182263d52Syt * supported in AHCI using the READ FPDMA QUEUED and WRITE FPDMA 102282263d52Syt * QUEUED commands when the HBA and device support native command 102382263d52Syt * queuing(NCQ). 102482263d52Syt * 102582263d52Syt * SATA_CTLF_NCQ will be set to sata_tran_hba_features_support if the 102682263d52Syt * CAP register of the HBA indicates NCQ is supported. 102782263d52Syt * 102882263d52Syt * SATA_CTLF_NCQ cannot be set if AHCI_CAP_NO_MCMDLIST_NONQUEUE is 102982263d52Syt * set because the previous register content of PxCI can be re-written 103082263d52Syt * in the register write. 103182263d52Syt */ 103282263d52Syt if ((cap_status & AHCI_HBA_CAP_SNCQ) && 103382263d52Syt !(ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE)) { 103482263d52Syt sata_hba_tran->sata_tran_hba_features_support |= SATA_CTLF_NCQ; 103582263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_NCQ; 103682263d52Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "HBA supports Native " 103782263d52Syt "Command Queuing"); 103882263d52Syt } 103982263d52Syt 10402fcbc377Syt /* Report the number of command slots */ 10412fcbc377Syt sata_hba_tran->sata_tran_hba_qdepth = ahci_ctlp->ahcictl_num_cmd_slots; 10422fcbc377Syt 10432fcbc377Syt sata_hba_tran->sata_tran_probe_port = ahci_tran_probe_port; 10442fcbc377Syt sata_hba_tran->sata_tran_start = ahci_tran_start; 10452fcbc377Syt sata_hba_tran->sata_tran_abort = ahci_tran_abort; 10462fcbc377Syt sata_hba_tran->sata_tran_reset_dport = ahci_tran_reset_dport; 10472fcbc377Syt sata_hba_tran->sata_tran_hotplug_ops = &ahci_tran_hotplug_ops; 10482fcbc377Syt #ifdef __lock_lint 10492fcbc377Syt sata_hba_tran->sata_tran_selftest = ahci_selftest; 10502fcbc377Syt #endif 10512fcbc377Syt /* 10522fcbc377Syt * When SATA framework adds support for pwrmgt the 10532fcbc377Syt * pwrmgt_ops needs to be updated 10542fcbc377Syt */ 10552fcbc377Syt sata_hba_tran->sata_tran_pwrmgt_ops = NULL; 10562fcbc377Syt sata_hba_tran->sata_tran_ioctl = NULL; 10572fcbc377Syt 10582fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = sata_hba_tran; 10592fcbc377Syt 10602fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 10612fcbc377Syt 10622fcbc377Syt /* Attach it to SATA framework */ 10632fcbc377Syt if (sata_hba_attach(ahci_ctlp->ahcictl_dip, sata_hba_tran, DDI_ATTACH) 10642fcbc377Syt != DDI_SUCCESS) { 10652fcbc377Syt kmem_free((void *)sata_hba_tran, sizeof (sata_hba_tran_t)); 10662fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 10672fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 10682fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 10692fcbc377Syt return (AHCI_FAILURE); 10702fcbc377Syt } 10712fcbc377Syt 10722fcbc377Syt return (AHCI_SUCCESS); 10732fcbc377Syt } 10742fcbc377Syt 10752fcbc377Syt /* 10762fcbc377Syt * Unregisters the ahci with sata framework. 10772fcbc377Syt */ 10782fcbc377Syt static int 10792fcbc377Syt ahci_unregister_sata_hba_tran(ahci_ctl_t *ahci_ctlp) 10802fcbc377Syt { 10812fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, 10822fcbc377Syt "ahci_unregister_sata_hba_tran enter"); 10832fcbc377Syt 10842fcbc377Syt /* Detach from the SATA framework. */ 10852fcbc377Syt if (sata_hba_detach(ahci_ctlp->ahcictl_dip, DDI_DETACH) != 10862fcbc377Syt DDI_SUCCESS) { 10872fcbc377Syt return (AHCI_FAILURE); 10882fcbc377Syt } 10892fcbc377Syt 10902fcbc377Syt /* Deallocate sata_hba_tran. */ 10912fcbc377Syt kmem_free((void *)ahci_ctlp->ahcictl_sata_hba_tran, 10922fcbc377Syt sizeof (sata_hba_tran_t)); 10932fcbc377Syt 10942fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 10952fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 10962fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 10972fcbc377Syt 10982fcbc377Syt return (AHCI_SUCCESS); 10992fcbc377Syt } 11002fcbc377Syt 11012fcbc377Syt /* 11022fcbc377Syt * ahci_tran_probe_port is called by SATA framework. It returns port state, 11032fcbc377Syt * port status registers and an attached device type via sata_device 11042fcbc377Syt * structure. 11052fcbc377Syt * 11062fcbc377Syt * We return the cached information from a previous hardware probe. The 11072fcbc377Syt * actual hardware probing itself was done either from within 11082fcbc377Syt * ahci_initialize_controller() during the driver attach or from a phy 11092fcbc377Syt * ready change interrupt handler. 11102fcbc377Syt */ 11112fcbc377Syt static int 11122fcbc377Syt ahci_tran_probe_port(dev_info_t *dip, sata_device_t *sd) 11132fcbc377Syt { 11142fcbc377Syt ahci_ctl_t *ahci_ctlp; 11152fcbc377Syt ahci_port_t *ahci_portp; 11162fcbc377Syt uint8_t cport = sd->satadev_addr.cport; 1117689d74b0Syt #if AHCI_DEBUG 11182fcbc377Syt uint8_t pmport = sd->satadev_addr.pmport; 11192fcbc377Syt uint8_t qual = sd->satadev_addr.qual; 1120689d74b0Syt #endif 11212fcbc377Syt uint8_t device_type; 11222fcbc377Syt uint32_t port_state; 11232fcbc377Syt uint8_t port; 11242fcbc377Syt 11252fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 11262fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 11272fcbc377Syt 11282fcbc377Syt AHCIDBG3(AHCIDBG_ENTRY, ahci_ctlp, 112968d33a25Syt "ahci_tran_probe_port enter: cport: %d, " 113068d33a25Syt "pmport: %d, qual: %d", cport, pmport, qual); 11312fcbc377Syt 11322fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 11332fcbc377Syt 11342fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 11352fcbc377Syt 11362fcbc377Syt port_state = ahci_portp->ahciport_port_state; 11372fcbc377Syt switch (port_state) { 11382fcbc377Syt 11392fcbc377Syt case SATA_PSTATE_FAILED: 11402fcbc377Syt sd->satadev_state = SATA_PSTATE_FAILED; 11412fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 114268d33a25Syt "ahci_tran_probe_port: port %d PORT FAILED", port); 11432fcbc377Syt goto out; 11442fcbc377Syt 11452fcbc377Syt case SATA_PSTATE_SHUTDOWN: 11462fcbc377Syt sd->satadev_state = SATA_PSTATE_SHUTDOWN; 11472fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 114868d33a25Syt "ahci_tran_probe_port: port %d PORT SHUTDOWN", port); 11492fcbc377Syt goto out; 11502fcbc377Syt 11512fcbc377Syt case SATA_PSTATE_PWROFF: 11522fcbc377Syt sd->satadev_state = SATA_PSTATE_PWROFF; 11532fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 115468d33a25Syt "ahci_tran_probe_port: port %d PORT PWROFF", port); 11552fcbc377Syt goto out; 11562fcbc377Syt 11572fcbc377Syt case SATA_PSTATE_PWRON: 11582fcbc377Syt sd->satadev_state = SATA_PSTATE_PWRON; 11592fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 116068d33a25Syt "ahci_tran_probe_port: port %d PORT PWRON", port); 11612fcbc377Syt break; 11622fcbc377Syt 11632fcbc377Syt default: 11642fcbc377Syt sd->satadev_state = port_state; 11652fcbc377Syt AHCIDBG2(AHCIDBG_INFO, ahci_ctlp, 116668d33a25Syt "ahci_tran_probe_port: port %d PORT NORMAL %x", 11672fcbc377Syt port, port_state); 11682fcbc377Syt break; 11692fcbc377Syt } 11702fcbc377Syt 11712fcbc377Syt device_type = ahci_portp->ahciport_device_type; 11722fcbc377Syt 11732fcbc377Syt switch (device_type) { 11742fcbc377Syt 11752fcbc377Syt case SATA_DTYPE_ATADISK: 11762fcbc377Syt sd->satadev_type = SATA_DTYPE_ATADISK; 11772fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 117868d33a25Syt "ahci_tran_probe_port: port %d DISK found", port); 11792fcbc377Syt break; 11802fcbc377Syt 118138547057Sying tian - Beijing China case SATA_DTYPE_ATAPI: 118238547057Sying tian - Beijing China /* 118338547057Sying tian - Beijing China * HBA driver only knows it's an ATAPI device, and don't know 1184f8a673adSying tian - Beijing China * it's CD/DVD, tape or ATAPI disk because the ATAPI device 1185f8a673adSying tian - Beijing China * type need to be determined by checking IDENTIFY PACKET 1186f8a673adSying tian - Beijing China * DEVICE data 118738547057Sying tian - Beijing China */ 118838547057Sying tian - Beijing China sd->satadev_type = SATA_DTYPE_ATAPI; 11892fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 119068d33a25Syt "ahci_tran_probe_port: port %d ATAPI found", port); 11912fcbc377Syt break; 11922fcbc377Syt 11932fcbc377Syt case SATA_DTYPE_PMULT: 11942fcbc377Syt sd->satadev_type = SATA_DTYPE_PMULT; 11952fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 119668d33a25Syt "ahci_tran_probe_port: port %d Port Multiplier found", 11972fcbc377Syt port); 11982fcbc377Syt break; 11992fcbc377Syt 12002fcbc377Syt case SATA_DTYPE_UNKNOWN: 12012fcbc377Syt sd->satadev_type = SATA_DTYPE_UNKNOWN; 12022fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 120368d33a25Syt "ahci_tran_probe_port: port %d Unknown device found", port); 12042fcbc377Syt break; 12052fcbc377Syt 12062fcbc377Syt default: 12072fcbc377Syt /* we don't support any other device types */ 12082fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 12092fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 121068d33a25Syt "ahci_tran_probe_port: port %d No device found", port); 12112fcbc377Syt break; 12122fcbc377Syt } 12132fcbc377Syt 12142fcbc377Syt out: 12152fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 12162fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 12172fcbc377Syt 12182fcbc377Syt return (SATA_SUCCESS); 12192fcbc377Syt } 12202fcbc377Syt 122168d33a25Syt /* 122268d33a25Syt * There are four operation modes in sata framework: 122368d33a25Syt * SATA_OPMODE_INTERRUPTS 122468d33a25Syt * SATA_OPMODE_POLLING 122568d33a25Syt * SATA_OPMODE_ASYNCH 122668d33a25Syt * SATA_OPMODE_SYNCH 122768d33a25Syt * 122868d33a25Syt * Their combined meanings as following: 122968d33a25Syt * 123068d33a25Syt * SATA_OPMODE_SYNCH 123168d33a25Syt * The command has to be completed before sata_tran_start functions returns. 123268d33a25Syt * Either interrupts or polling could be used - it's up to the driver. 123368d33a25Syt * Mode used currently for internal, sata-module initiated operations. 123468d33a25Syt * 123568d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS 123668d33a25Syt * It is the same as the one above. 123768d33a25Syt * 123868d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING 123968d33a25Syt * The command has to be completed before sata_tran_start function returns. 124068d33a25Syt * No interrupt used, polling only. This should be the mode used for scsi 124168d33a25Syt * packets with FLAG_NOINTR. 124268d33a25Syt * 124368d33a25Syt * SATA_OPMODE_ASYNCH | SATA_OPMODE_INTERRUPTS 124468d33a25Syt * The command may be queued (callback function specified). Interrupts could 124568d33a25Syt * be used. It's normal operation mode. 124668d33a25Syt */ 12472fcbc377Syt /* 12482fcbc377Syt * Called by sata framework to transport a sata packet down stream. 12492fcbc377Syt */ 12502fcbc377Syt static int 12512fcbc377Syt ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt) 12522fcbc377Syt { 12532fcbc377Syt ahci_ctl_t *ahci_ctlp; 12542fcbc377Syt ahci_port_t *ahci_portp; 12552fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 12562fcbc377Syt uint8_t port; 12572fcbc377Syt 12582fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 12592fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 12602fcbc377Syt 12612fcbc377Syt AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, 12622fcbc377Syt "ahci_tran_start enter: cport %d satapkt 0x%p", 12632fcbc377Syt cport, (void *)spkt); 12642fcbc377Syt 12652fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 12662fcbc377Syt 12672fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 12682fcbc377Syt 12692fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 12702fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 12712fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 12722fcbc377Syt /* 12732fcbc377Syt * In case the targer driver would send the packet before 12742fcbc377Syt * sata framework can have the opportunity to process those 12752fcbc377Syt * event reports. 12762fcbc377Syt */ 12772fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 12782fcbc377Syt spkt->satapkt_device.satadev_state = 12792fcbc377Syt ahci_portp->ahciport_port_state; 12802fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 12812fcbc377Syt &spkt->satapkt_device); 12822fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 12832fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 12842fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 128568d33a25Syt "port: %d", port); 12862fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 12872fcbc377Syt return (SATA_TRAN_PORT_ERROR); 12882fcbc377Syt } 12892fcbc377Syt 12902fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 12912fcbc377Syt /* 12922fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 12932fcbc377Syt * SATA_DTYPE_NONE. 12942fcbc377Syt */ 12952fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 12962fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 12972fcbc377Syt spkt->satapkt_device.satadev_state = 12982fcbc377Syt ahci_portp->ahciport_port_state; 12992fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 13002fcbc377Syt &spkt->satapkt_device); 13012fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13022fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 130368d33a25Syt "no device attached: port: %d", port); 13042fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13052fcbc377Syt return (SATA_TRAN_PORT_ERROR); 13062fcbc377Syt } 13072fcbc377Syt 13082fcbc377Syt /* 13092fcbc377Syt * SATA HBA driver should remember that a device was reset and it 13102fcbc377Syt * is supposed to reject any packets which do not specify either 13112fcbc377Syt * SATA_IGNORE_DEV_RESET_STATE or SATA_CLEAR_DEV_RESET_STATE. 13122fcbc377Syt * 13132fcbc377Syt * This is to prevent a race condition when a device was arbitrarily 13142fcbc377Syt * reset by the HBA driver (and lost it's setting) and a target 13152fcbc377Syt * driver sending some commands to a device before the sata framework 13162fcbc377Syt * has a chance to restore the device setting (such as cache enable/ 13172fcbc377Syt * disable or other resettable stuff). 13182fcbc377Syt */ 13192fcbc377Syt if (spkt->satapkt_cmd.satacmd_flags.sata_clear_dev_reset) { 13202fcbc377Syt ahci_portp->ahciport_reset_in_progress = 0; 13212fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13222fcbc377Syt "ahci_tran_start clearing the " 132368d33a25Syt "reset_in_progress for port: %d", port); 13242fcbc377Syt } 13252fcbc377Syt 13262fcbc377Syt if (ahci_portp->ahciport_reset_in_progress && 13272fcbc377Syt ! spkt->satapkt_cmd.satacmd_flags.sata_ignore_dev_reset && 13282fcbc377Syt ! ddi_in_panic()) { 13292fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 13302fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13312fcbc377Syt "ahci_tran_start returning BUSY while " 133268d33a25Syt "reset in progress: port: %d", port); 13332fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13342fcbc377Syt return (SATA_TRAN_BUSY); 13352fcbc377Syt } 13362fcbc377Syt 133768d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 13382fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 13392fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13402fcbc377Syt "ahci_tran_start returning BUSY while " 134168d33a25Syt "mopping in progress: port: %d", port); 13422fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13432fcbc377Syt return (SATA_TRAN_BUSY); 13442fcbc377Syt } 13452fcbc377Syt 13462fcbc377Syt if (spkt->satapkt_op_mode & 134768d33a25Syt (SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING)) { 1348b2e3645aSying tian - Beijing China /* 1349b2e3645aSying tian - Beijing China * If a SYNC command to be executed in interrupt context, 1350b2e3645aSying tian - Beijing China * bounce it back to sata module. 1351b2e3645aSying tian - Beijing China */ 1352b2e3645aSying tian - Beijing China if (!(spkt->satapkt_op_mode & SATA_OPMODE_POLLING) && 1353b2e3645aSying tian - Beijing China servicing_interrupt()) { 1354b2e3645aSying tian - Beijing China spkt->satapkt_reason = SATA_PKT_BUSY; 1355b2e3645aSying tian - Beijing China AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 1356b2e3645aSying tian - Beijing China "ahci_tran_start returning BUSY while " 1357b2e3645aSying tian - Beijing China "sending SYNC mode under interrupt context: " 1358b2e3645aSying tian - Beijing China "port : %d", port); 1359b2e3645aSying tian - Beijing China mutex_exit(&ahci_portp->ahciport_mutex); 1360b2e3645aSying tian - Beijing China return (SATA_TRAN_BUSY); 1361b2e3645aSying tian - Beijing China } 1362b2e3645aSying tian - Beijing China 136368d33a25Syt /* We need to do the sync start now */ 136468d33a25Syt if (ahci_do_sync_start(ahci_ctlp, ahci_portp, port, 136568d33a25Syt spkt) == AHCI_FAILURE) { 136668d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 136768d33a25Syt "return QUEUE_FULL: port %d", port); 136868d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 136968d33a25Syt return (SATA_TRAN_QUEUE_FULL); 137068d33a25Syt } 137168d33a25Syt } else { 137268d33a25Syt /* Async start, using interrupt */ 137368d33a25Syt if (ahci_deliver_satapkt(ahci_ctlp, ahci_portp, port, spkt) 137468d33a25Syt == AHCI_FAILURE) { 137568d33a25Syt spkt->satapkt_reason = SATA_PKT_QUEUE_FULL; 137668d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 137768d33a25Syt "returning QUEUE_FULL: port %d", port); 137868d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 137968d33a25Syt return (SATA_TRAN_QUEUE_FULL); 138068d33a25Syt } 13812fcbc377Syt } 13822fcbc377Syt 13832fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_tran_start " 13842fcbc377Syt "sata tran accepted: port %d", port); 13852fcbc377Syt 138668d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 13872fcbc377Syt return (SATA_TRAN_ACCEPTED); 13882fcbc377Syt } 13892fcbc377Syt 139068d33a25Syt /* 139168d33a25Syt * SATA_OPMODE_SYNCH flag is set 139268d33a25Syt * 139368d33a25Syt * If SATA_OPMODE_POLLING flag is set, then we must poll the command 139468d33a25Syt * without interrupt, otherwise we can still use the interrupt. 139568d33a25Syt * 139668d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function 139768d33a25Syt * is called. 139868d33a25Syt */ 139968d33a25Syt static int 140068d33a25Syt ahci_do_sync_start(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 140168d33a25Syt uint8_t port, sata_pkt_t *spkt) 140268d33a25Syt { 140368d33a25Syt int pkt_timeout_ticks; 140468d33a25Syt uint32_t timeout_tags; 140568d33a25Syt int rval; 1406a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 140768d33a25Syt 140868d33a25Syt AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, "ahci_do_sync_start enter: " 140968d33a25Syt "port %d spkt 0x%p", port, spkt); 141068d33a25Syt 141168d33a25Syt if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) { 141268d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_POLLING; 141368d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 141468d33a25Syt port, spkt)) == AHCI_FAILURE) { 141568d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_POLLING; 141668d33a25Syt return (rval); 141768d33a25Syt } 141868d33a25Syt 141968d33a25Syt pkt_timeout_ticks = 142068d33a25Syt drv_usectohz((clock_t)spkt->satapkt_time * 1000000); 142168d33a25Syt 142268d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) { 142368d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 142468d33a25Syt 142568d33a25Syt /* Simulate the interrupt */ 142682263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 142768d33a25Syt 142868d33a25Syt drv_usecwait(AHCI_1MS_USECS); 142968d33a25Syt 143068d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 143168d33a25Syt pkt_timeout_ticks -= AHCI_1MS_TICKS; 143268d33a25Syt if (pkt_timeout_ticks < 0) { 1433a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_do_sync_start " 143468d33a25Syt "port %d satapkt 0x%p timed out\n", 1435a9440e8dSyt instance, port, (void *)spkt); 143668d33a25Syt timeout_tags = (0x1 << rval); 143768d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 143868d33a25Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 143982263d52Syt port, timeout_tags); 144068d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 144168d33a25Syt } 144268d33a25Syt } 144368d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_POLLING; 144468d33a25Syt return (AHCI_SUCCESS); 144568d33a25Syt 144668d33a25Syt } else { 144768d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 144868d33a25Syt port, spkt)) == AHCI_FAILURE) 144968d33a25Syt return (rval); 145068d33a25Syt 145182263d52Syt #if AHCI_DEBUG 145282263d52Syt /* 145382263d52Syt * Note that the driver always uses the slot 0 to deliver 145482263d52Syt * REQUEST SENSE or READ LOG EXT command 145582263d52Syt */ 145682263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 145782263d52Syt ASSERT(rval == 0); 145882263d52Syt #endif 145982263d52Syt 146068d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) 146168d33a25Syt cv_wait(&ahci_portp->ahciport_cv, 146268d33a25Syt &ahci_portp->ahciport_mutex); 146368d33a25Syt 146468d33a25Syt return (AHCI_SUCCESS); 146568d33a25Syt } 146668d33a25Syt } 146768d33a25Syt 14682fcbc377Syt #define SENDUP_PACKET(ahci_portp, satapkt, reason) \ 14692fcbc377Syt if (satapkt) { \ 14702fcbc377Syt satapkt->satapkt_reason = reason; \ 14712fcbc377Syt /* \ 14722fcbc377Syt * We set the satapkt_reason in both sync and \ 14732fcbc377Syt * non-sync cases. \ 14742fcbc377Syt */ \ 14752fcbc377Syt } \ 14762fcbc377Syt if (satapkt && \ 147768d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 14782fcbc377Syt satapkt->satapkt_comp) { \ 14792fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); \ 14802fcbc377Syt (*satapkt->satapkt_comp)(satapkt); \ 14812fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); \ 148268d33a25Syt } else { \ 148368d33a25Syt if (satapkt && \ 148468d33a25Syt (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 148568d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_POLLING)) \ 1486f8a673adSying tian - Beijing China cv_broadcast(&ahci_portp->ahciport_cv); \ 14872fcbc377Syt } 14882fcbc377Syt 14892fcbc377Syt /* 149082263d52Syt * Searches for and claims a free command slot. 149182263d52Syt * 149282263d52Syt * Returns: 14932fcbc377Syt * 149482263d52Syt * AHCI_FAILURE if failed 149582263d52Syt * 1. if no empty slot left 149682263d52Syt * 2. non-queued command requested while queued command(s) is outstanding 149782263d52Syt * 3. queued command requested whild non-queued command(s) is outstanding 149882263d52Syt * 4. HBA doesn't support multiple-use of command list while already a 149982263d52Syt * non-queued command is oustanding 150082263d52Syt * 150182263d52Syt * claimed slot number if succeeded 150282263d52Syt * 150382263d52Syt * NOTE: it will always return slot 0 during error recovery process for 150482263d52Syt * REQUEST SENSE or READ LOG EXT command to simplify the algorithm. 15052fcbc377Syt * 15062fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 15072fcbc377Syt * is called. 15082fcbc377Syt */ 15092fcbc377Syt static int 151082263d52Syt ahci_claim_free_slot(ahci_ctl_t *ahci_ctlp, 151182263d52Syt ahci_port_t *ahci_portp, int command_type) 15122fcbc377Syt { 15132fcbc377Syt uint32_t free_slots; 15142fcbc377Syt int slot; 15152fcbc377Syt 151682263d52Syt AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, "ahci_claim_free_slot enter " 151782263d52Syt "ahciport_pending_tags = 0x%x " 151882263d52Syt "ahciport_pending_ncq_tags = 0x%x", 151982263d52Syt ahci_portp->ahciport_pending_tags, 152082263d52Syt ahci_portp->ahciport_pending_ncq_tags); 15212fcbc377Syt 152282263d52Syt /* 152382263d52Syt * According to the AHCI spec, system software is responsible to 152482263d52Syt * ensure that queued and non-queued commands are not mixed in 152582263d52Syt * the command list. 152682263d52Syt */ 152782263d52Syt if (command_type == AHCI_NON_NCQ_CMD) { 152882263d52Syt /* Non-NCQ command request */ 152982263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 153082263d52Syt AHCIDBG0(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 153182263d52Syt "ahci_claim_free_slot: there is still pending " 153282263d52Syt "queued command(s) in the command list, " 153382263d52Syt "so no available slot for the non-queued " 153482263d52Syt "command"); 153582263d52Syt return (AHCI_FAILURE); 153682263d52Syt } 153782263d52Syt if ((ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE) && 153882263d52Syt NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 153982263d52Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, 154082263d52Syt "ahci_claim_free_slot: HBA cannot support multiple-" 154182263d52Syt "use of the command list for non-queued commands"); 154282263d52Syt return (AHCI_FAILURE); 154382263d52Syt } 154482263d52Syt free_slots = (~ahci_portp->ahciport_pending_tags) & 154582263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 154682263d52Syt } else if (command_type == AHCI_NCQ_CMD) { 154782263d52Syt /* NCQ command request */ 154882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 154982263d52Syt AHCIDBG0(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 155082263d52Syt "ahci_claim_free_slot: there is still pending " 155182263d52Syt "non-queued command(s) in the command list, " 155282263d52Syt "so no available slot for the queued command"); 155382263d52Syt return (AHCI_FAILURE); 155482263d52Syt } 155582263d52Syt free_slots = (~ahci_portp->ahciport_pending_ncq_tags) & 155682263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 155782263d52Syt } else if (command_type == AHCI_ERR_RETRI_CMD) { 155882263d52Syt /* Error retrieval command request */ 155982263d52Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 156082263d52Syt "ahci_claim_free_slot: slot 0 is allocated for REQUEST " 156182263d52Syt "SENSE or READ LOG EXT command"); 156282263d52Syt slot = 0; 156382263d52Syt goto out; 156482263d52Syt } 15652fcbc377Syt 15662fcbc377Syt slot = ddi_ffs(free_slots) - 1; 15672fcbc377Syt if (slot == -1) { 15682fcbc377Syt AHCIDBG0(AHCIDBG_VERBOSE, ahci_ctlp, 15692fcbc377Syt "ahci_claim_free_slot: no empty slots"); 15702fcbc377Syt return (AHCI_FAILURE); 15712fcbc377Syt } 15722fcbc377Syt 157382263d52Syt /* 157482263d52Syt * According to the AHCI spec, to allow a simple mechanism for the 157582263d52Syt * HBA to map command list slots to queue entries, software must 157682263d52Syt * match the tag number it uses to the slot it is placing the command 157782263d52Syt * in. For example, if a queued command is placed in slot 5, the tag 157882263d52Syt * for that command must be 5. 157982263d52Syt */ 158082263d52Syt if (command_type == AHCI_NCQ_CMD) { 158182263d52Syt ahci_portp->ahciport_pending_ncq_tags |= (0x1 << slot); 158282263d52Syt } 158382263d52Syt 15842fcbc377Syt ahci_portp->ahciport_pending_tags |= (0x1 << slot); 15852fcbc377Syt 158682263d52Syt out: 15872fcbc377Syt AHCIDBG1(AHCIDBG_VERBOSE, ahci_ctlp, 15882fcbc377Syt "ahci_claim_free_slot: found slot: 0x%x", slot); 15892fcbc377Syt 15902fcbc377Syt return (slot); 15912fcbc377Syt } 15922fcbc377Syt 15932fcbc377Syt /* 15942fcbc377Syt * Builds the Command Table for the sata packet and delivers it to controller. 15952fcbc377Syt * 15962fcbc377Syt * Returns: 15972fcbc377Syt * slot number if we can obtain a slot successfully 15982fcbc377Syt * otherwise, return AHCI_FAILURE 15992fcbc377Syt * 16002fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 16012fcbc377Syt */ 16022fcbc377Syt static int 16032fcbc377Syt ahci_deliver_satapkt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 16042fcbc377Syt uint8_t port, sata_pkt_t *spkt) 16052fcbc377Syt { 160668d33a25Syt int cmd_slot; 160768d33a25Syt sata_cmd_t *scmd; 16082fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 16092fcbc377Syt ahci_cmd_table_t *cmd_table; 16102fcbc377Syt ahci_cmd_header_t *cmd_header; 16112fcbc377Syt int ncookies; 16122fcbc377Syt int i; 161382263d52Syt int command_type = AHCI_NON_NCQ_CMD; 161482263d52Syt int ncq_qdepth; 1615a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 161668d33a25Syt #if AHCI_DEBUG 161768d33a25Syt uint32_t *ptr; 161868d33a25Syt uint8_t *ptr2; 161968d33a25Syt #endif 16202fcbc377Syt 16212fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 16222fcbc377Syt 162368d33a25Syt scmd = &spkt->satapkt_cmd; 16242fcbc377Syt 162582263d52Syt /* Check if the command is a NCQ command */ 162682263d52Syt if (scmd->satacmd_cmd_reg == SATAC_READ_FPDMA_QUEUED || 162782263d52Syt scmd->satacmd_cmd_reg == SATAC_WRITE_FPDMA_QUEUED) { 162882263d52Syt command_type = AHCI_NCQ_CMD; 162982263d52Syt 163082263d52Syt /* 163182263d52Syt * When NCQ is support, system software must determine the 163282263d52Syt * maximum tag allowed by the device and the HBA, and it 163382263d52Syt * must use a value not beyond of the lower bound of the two. 163482263d52Syt * 163582263d52Syt * Sata module is going to calculate the qdepth and send 163682263d52Syt * down to HBA driver via sata_cmd. 163782263d52Syt */ 163882263d52Syt ncq_qdepth = scmd->satacmd_flags.sata_max_queue_depth + 1; 163982263d52Syt 164082263d52Syt /* 164182263d52Syt * At the moment, the driver doesn't support the dynamic 164282263d52Syt * setting of the maximum ncq depth, and the value can be 164382263d52Syt * set either during the attach or after hot-plug insertion. 164482263d52Syt */ 164582263d52Syt if (ahci_portp->ahciport_max_ncq_tags == 0) { 164682263d52Syt ahci_portp->ahciport_max_ncq_tags = ncq_qdepth; 164782263d52Syt AHCIDBG2(AHCIDBG_NCQ, ahci_ctlp, 164882263d52Syt "ahci_deliver_satapkt: port %d the max tags for " 164982263d52Syt "NCQ command is %d", port, ncq_qdepth); 165082263d52Syt } else { 165182263d52Syt if (ncq_qdepth != ahci_portp->ahciport_max_ncq_tags) { 1652a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_deliver_satapkt" 1653a9440e8dSyt " port %d the max tag for NCQ command is " 165482263d52Syt "requested to change from %d to %d, at the" 165582263d52Syt " moment the driver doesn't support the " 165682263d52Syt "dynamic change so it's going to " 1657a9440e8dSyt "still use the previous tag value", 1658a9440e8dSyt instance, port, 165982263d52Syt ahci_portp->ahciport_max_ncq_tags, 166082263d52Syt ncq_qdepth); 166182263d52Syt } 166282263d52Syt } 166382263d52Syt } 166482263d52Syt 166582263d52Syt /* Check if the command is an error retrieval command */ 166682263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 166782263d52Syt command_type = AHCI_ERR_RETRI_CMD; 166882263d52Syt 166982263d52Syt /* Check if there is an empty command slot */ 167082263d52Syt cmd_slot = ahci_claim_free_slot(ahci_ctlp, ahci_portp, command_type); 167168d33a25Syt if (cmd_slot == AHCI_FAILURE) { 167282263d52Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "no free command slot"); 16732fcbc377Syt return (AHCI_FAILURE); 16742fcbc377Syt } 16752fcbc377Syt 16762fcbc377Syt AHCIDBG4(AHCIDBG_ENTRY|AHCIDBG_INFO, ahci_ctlp, 167768d33a25Syt "ahci_deliver_satapkt enter: cmd_reg: 0x%x, cmd_slot: 0x%x, " 167868d33a25Syt "port: %d, satapkt: 0x%p", scmd->satacmd_cmd_reg, 167968d33a25Syt cmd_slot, port, (void *)spkt); 16802fcbc377Syt 168168d33a25Syt cmd_table = ahci_portp->ahciport_cmd_tables[cmd_slot]; 16822fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 16832fcbc377Syt 168482263d52Syt /* For data transfer operations, it is the H2D Register FIS */ 16852fcbc377Syt h2d_register_fisp = 16862fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 16872fcbc377Syt 16882fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 16892fcbc377Syt if ((spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_PMPORT) || 16902fcbc377Syt (spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_DPMPORT)) { 16912fcbc377Syt SET_FIS_PMP(h2d_register_fisp, 16922fcbc377Syt spkt->satapkt_device.satadev_addr.pmport); 16932fcbc377Syt } 16942fcbc377Syt 16952fcbc377Syt SET_FIS_CDMDEVCTL(h2d_register_fisp, 1); 169668d33a25Syt SET_FIS_COMMAND(h2d_register_fisp, scmd->satacmd_cmd_reg); 169768d33a25Syt SET_FIS_FEATURES(h2d_register_fisp, scmd->satacmd_features_reg); 169868d33a25Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, scmd->satacmd_sec_count_lsb); 16992fcbc377Syt 170068d33a25Syt switch (scmd->satacmd_addr_type) { 17012fcbc377Syt 170282263d52Syt case 0: 170382263d52Syt /* 170482263d52Syt * satacmd_addr_type will be 0 for the commands below: 170582263d52Syt * ATAPI command 170682263d52Syt * SATAC_IDLE_IM 170782263d52Syt * SATAC_STANDBY_IM 170882263d52Syt * SATAC_DOWNLOAD_MICROCODE 170982263d52Syt * SATAC_FLUSH_CACHE 171082263d52Syt * SATAC_SET_FEATURES 171182263d52Syt * SATAC_SMART 171282263d52Syt * SATAC_ID_PACKET_DEVICE 171382263d52Syt * SATAC_ID_DEVICE 171482263d52Syt */ 1715689d74b0Syt /* FALLTHRU */ 171682263d52Syt 17172fcbc377Syt case ATA_ADDR_LBA: 1718689d74b0Syt /* FALLTHRU */ 17192fcbc377Syt 17202fcbc377Syt case ATA_ADDR_LBA28: 17212fcbc377Syt /* LBA[7:0] */ 172268d33a25Syt SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb); 17232fcbc377Syt 17242fcbc377Syt /* LBA[15:8] */ 172568d33a25Syt SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb); 17262fcbc377Syt 17272fcbc377Syt /* LBA[23:16] */ 172868d33a25Syt SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb); 17292fcbc377Syt 17302fcbc377Syt /* LBA [27:24] (also called dev_head) */ 173168d33a25Syt SET_FIS_DEV_HEAD(h2d_register_fisp, scmd->satacmd_device_reg); 17322fcbc377Syt 17332fcbc377Syt break; 17342fcbc377Syt 17352fcbc377Syt case ATA_ADDR_LBA48: 17362fcbc377Syt /* LBA[7:0] */ 173768d33a25Syt SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb); 17382fcbc377Syt 17392fcbc377Syt /* LBA[15:8] */ 174068d33a25Syt SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb); 17412fcbc377Syt 17422fcbc377Syt /* LBA[23:16] */ 174368d33a25Syt SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb); 17442fcbc377Syt 17452fcbc377Syt /* LBA [31:24] */ 17462fcbc377Syt SET_FIS_SECTOR_EXP(h2d_register_fisp, 174768d33a25Syt scmd->satacmd_lba_low_msb); 17482fcbc377Syt 17492fcbc377Syt /* LBA [39:32] */ 17502fcbc377Syt SET_FIS_CYL_LOW_EXP(h2d_register_fisp, 175168d33a25Syt scmd->satacmd_lba_mid_msb); 17522fcbc377Syt 17532fcbc377Syt /* LBA [47:40] */ 17542fcbc377Syt SET_FIS_CYL_HI_EXP(h2d_register_fisp, 175568d33a25Syt scmd->satacmd_lba_high_msb); 17562fcbc377Syt 17572fcbc377Syt /* Set dev_head */ 17582fcbc377Syt SET_FIS_DEV_HEAD(h2d_register_fisp, 175968d33a25Syt scmd->satacmd_device_reg); 17602fcbc377Syt 17612fcbc377Syt /* Set the extended sector count and features */ 17622fcbc377Syt SET_FIS_SECTOR_COUNT_EXP(h2d_register_fisp, 176368d33a25Syt scmd->satacmd_sec_count_msb); 17642fcbc377Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 176568d33a25Syt scmd->satacmd_features_reg_ext); 17662fcbc377Syt break; 17672fcbc377Syt } 17682fcbc377Syt 176982263d52Syt /* 177082263d52Syt * For NCQ command (READ/WRITE FPDMA QUEUED), sector count 7:0 is 177182263d52Syt * filled into features field, and sector count 8:15 is filled into 177282263d52Syt * features (exp) field. TAG is filled into sector field. 177382263d52Syt */ 177482263d52Syt if (command_type == AHCI_NCQ_CMD) { 177582263d52Syt SET_FIS_FEATURES(h2d_register_fisp, 177682263d52Syt scmd->satacmd_sec_count_lsb); 177782263d52Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 177882263d52Syt scmd->satacmd_sec_count_msb); 177982263d52Syt 178082263d52Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, 178182263d52Syt (cmd_slot << SATA_TAG_QUEUING_SHIFT)); 178282263d52Syt } 178382263d52Syt 178468d33a25Syt ncookies = scmd->satacmd_num_dma_cookies; 178538547057Sying tian - Beijing China AHCIDBG2(AHCIDBG_PRDT, ahci_ctlp, 17862fcbc377Syt "ncookies = 0x%x, ahci_dma_prdt_number = 0x%x", 17872fcbc377Syt ncookies, ahci_dma_prdt_number); 17882fcbc377Syt 17892fcbc377Syt ASSERT(ncookies <= ahci_dma_prdt_number); 179038547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot] = 0; 17912fcbc377Syt 17922fcbc377Syt /* *** now fill the scatter gather list ******* */ 17932fcbc377Syt for (i = 0; i < ncookies; i++) { 17942fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr = 179568d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[0]; 17962fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr_upper = 179768d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[1]; 17982fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_descr_info = 179968d33a25Syt scmd->satacmd_dma_cookie_list[i].dmac_size - 1; 180038547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot] += 180138547057Sying tian - Beijing China scmd->satacmd_dma_cookie_list[i].dmac_size; 180268d33a25Syt } 180368d33a25Syt 180438547057Sying tian - Beijing China AHCIDBG2(AHCIDBG_PRDT, ahci_ctlp, 180538547057Sying tian - Beijing China "ahciport_prd_bytecounts 0x%x for cmd_slot 0x%x", 180638547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot], cmd_slot); 180738547057Sying tian - Beijing China 180868d33a25Syt /* The ACMD field is filled in for ATAPI command */ 180968d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) { 181068d33a25Syt bcopy(scmd->satacmd_acdb, cmd_table->ahcict_atapi_cmd, 181168d33a25Syt SATA_ATAPI_MAX_CDB_LEN); 18122fcbc377Syt } 18132fcbc377Syt 18142fcbc377Syt /* Set Command Header in Command List */ 181568d33a25Syt cmd_header = &ahci_portp->ahciport_cmd_list[cmd_slot]; 18162fcbc377Syt BZERO_DESCR_INFO(cmd_header); 18172fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 181868d33a25Syt 181968d33a25Syt /* Set the number of entries in the PRD table */ 18202fcbc377Syt SET_PRD_TABLE_LENGTH(cmd_header, ncookies); 182168d33a25Syt 182268d33a25Syt /* Set the length of the command in the CFIS area */ 18232fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, AHCI_H2D_REGISTER_FIS_LENGTH); 18242fcbc377Syt 18252fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "command data direction is " 18262fcbc377Syt "sata_data_direction = 0x%x", 182768d33a25Syt scmd->satacmd_flags.sata_data_direction); 182868d33a25Syt 182968d33a25Syt /* Set A bit if it is an ATAPI command */ 183068d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) 183168d33a25Syt SET_ATAPI(cmd_header, AHCI_CMDHEAD_ATAPI); 18322fcbc377Syt 183368d33a25Syt /* Set W bit if data is going to the device */ 183468d33a25Syt if (scmd->satacmd_flags.sata_data_direction == SATA_DIR_WRITE) 18352fcbc377Syt SET_WRITE(cmd_header, AHCI_CMDHEAD_DATA_WRITE); 18362fcbc377Syt 183768d33a25Syt /* 183868d33a25Syt * Set the prefetchable bit - this bit is only valid if the PRDTL 183968d33a25Syt * field is non-zero or the ATAPI 'A' bit is set in the command 184068d33a25Syt * header. This bit cannot be set when using native command 184168d33a25Syt * queuing commands or when using FIS-based switching with a Port 184282263d52Syt * multiplier. 184368d33a25Syt */ 184482263d52Syt if (command_type != AHCI_NCQ_CMD) 184582263d52Syt SET_PREFETCHABLE(cmd_header, AHCI_CMDHEAD_PREFETCHABLE); 18462fcbc377Syt 18472fcbc377Syt /* Now remember the sata packet in ahciport_slot_pkts[]. */ 184882263d52Syt if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 184982263d52Syt ahci_portp->ahciport_slot_pkts[cmd_slot] = spkt; 18502fcbc377Syt 18512fcbc377Syt /* 18522fcbc377Syt * We are overloading satapkt_hba_driver_private with 18532fcbc377Syt * watched_cycle count. 18542fcbc377Syt */ 18552fcbc377Syt spkt->satapkt_hba_driver_private = (void *)(intptr_t)0; 18562fcbc377Syt 185768d33a25Syt #if AHCI_DEBUG 185838547057Sying tian - Beijing China if (ahci_debug_flags & AHCIDBG_ATACMD && 185938547057Sying tian - Beijing China scmd->satacmd_cmd_reg != SATAC_PACKET || 186038547057Sying tian - Beijing China ahci_debug_flags & AHCIDBG_ATAPICMD && 186138547057Sying tian - Beijing China scmd->satacmd_cmd_reg == SATAC_PACKET) { 186238547057Sying tian - Beijing China 186338547057Sying tian - Beijing China /* Dump the command header and table */ 186438547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, "\n"); 186538547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, "Command header&table for spkt " 186638547057Sying tian - Beijing China "0x%p cmd_reg 0x%x port %d", spkt, 186738547057Sying tian - Beijing China scmd->satacmd_cmd_reg, port); 186838547057Sying tian - Beijing China ptr = (uint32_t *)cmd_header; 186938547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 187038547057Sying tian - Beijing China " Command Header:%8x %8x %8x %8x", 187138547057Sying tian - Beijing China ptr[0], ptr[1], ptr[2], ptr[3]); 187238547057Sying tian - Beijing China 187338547057Sying tian - Beijing China /* Dump the H2D register FIS */ 187438547057Sying tian - Beijing China ptr = (uint32_t *)h2d_register_fisp; 187538547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 187638547057Sying tian - Beijing China " Command FIS: %8x %8x %8x %8x", 187738547057Sying tian - Beijing China ptr[0], ptr[1], ptr[2], ptr[3]); 187838547057Sying tian - Beijing China 187938547057Sying tian - Beijing China /* Dump the ACMD register FIS */ 188038547057Sying tian - Beijing China ptr2 = (uint8_t *)&(cmd_table->ahcict_atapi_cmd); 188138547057Sying tian - Beijing China for (i = 0; i < SATA_ATAPI_MAX_CDB_LEN/8; i++) 188238547057Sying tian - Beijing China if (ahci_debug_flags & AHCIDBG_ATAPICMD) 188338547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 188438547057Sying tian - Beijing China " ATAPI command: %2x %2x %2x %2x " 188538547057Sying tian - Beijing China "%2x %2x %2x %2x", 188638547057Sying tian - Beijing China ptr2[8 * i], ptr2[8 * i + 1], 188738547057Sying tian - Beijing China ptr2[8 * i + 2], ptr2[8 * i + 3], 188838547057Sying tian - Beijing China ptr2[8 * i + 4], ptr2[8 * i + 5], 188938547057Sying tian - Beijing China ptr2[8 * i + 6], ptr2[8 * i + 7]); 189038547057Sying tian - Beijing China 189138547057Sying tian - Beijing China /* Dump the PRDT */ 189238547057Sying tian - Beijing China for (i = 0; i < ncookies; i++) { 189338547057Sying tian - Beijing China ptr = (uint32_t *)&(cmd_table->ahcict_prdt[i]); 189468d33a25Syt ahci_log(ahci_ctlp, CE_WARN, 189538547057Sying tian - Beijing China " Cookie %d: %8x %8x %8x %8x", 189638547057Sying tian - Beijing China i, ptr[0], ptr[1], ptr[2], ptr[3]); 189738547057Sying tian - Beijing China } 189868d33a25Syt } 189968d33a25Syt #endif 190068d33a25Syt 190168d33a25Syt (void) ddi_dma_sync( 190268d33a25Syt ahci_portp->ahciport_cmd_tables_dma_handle[cmd_slot], 19032fcbc377Syt 0, 19042fcbc377Syt ahci_cmd_table_size, 19052fcbc377Syt DDI_DMA_SYNC_FORDEV); 19062fcbc377Syt 19072fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 190868d33a25Syt cmd_slot * sizeof (ahci_cmd_header_t), 19092fcbc377Syt sizeof (ahci_cmd_header_t), 19102fcbc377Syt DDI_DMA_SYNC_FORDEV); 19112fcbc377Syt 191282263d52Syt /* Set the corresponding bit in the PxSACT.DS for queued command */ 191382263d52Syt if (command_type == AHCI_NCQ_CMD) { 191482263d52Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 191582263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port), 191682263d52Syt (0x1 << cmd_slot)); 191782263d52Syt } 191882263d52Syt 19192fcbc377Syt /* Indicate to the HBA that a command is active. */ 19202fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 19212fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 192268d33a25Syt (0x1 << cmd_slot)); 19232fcbc377Syt 19242fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_deliver_satapkt " 19252fcbc377Syt "exit: port %d", port); 19262fcbc377Syt 192768d33a25Syt return (cmd_slot); 19282fcbc377Syt } 19292fcbc377Syt 19302fcbc377Syt /* 19312fcbc377Syt * Called by the sata framework to abort the previously sent packet(s). 19322fcbc377Syt * 19332fcbc377Syt * Reset device to abort commands. 19342fcbc377Syt */ 19352fcbc377Syt static int 19362fcbc377Syt ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag) 19372fcbc377Syt { 19382fcbc377Syt ahci_ctl_t *ahci_ctlp; 19392fcbc377Syt ahci_port_t *ahci_portp; 194082263d52Syt uint32_t slot_status = 0; 194182263d52Syt uint32_t aborted_tags = 0; 194282263d52Syt uint32_t finished_tags = 0; 19432fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 19442fcbc377Syt uint8_t port; 19452fcbc377Syt int tmp_slot; 1946a9440e8dSyt int instance = ddi_get_instance(dip); 19472fcbc377Syt 1948a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 19492fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 19502fcbc377Syt 19512fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 195268d33a25Syt "ahci_tran_abort enter: port %d", port); 19532fcbc377Syt 19542fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 19552fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 19562fcbc377Syt 19572fcbc377Syt /* 195868d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 195968d33a25Syt * commands are being mopped, therefore there is nothing else to do 19602fcbc377Syt */ 196168d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 19622fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 19632fcbc377Syt "ahci_tran_abort: port %d is in " 19642fcbc377Syt "mopping process, so just return directly ", port); 19652fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 19662fcbc377Syt return (SATA_SUCCESS); 19672fcbc377Syt } 19682fcbc377Syt 19692fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 19702fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 19712fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 19722fcbc377Syt /* 19732fcbc377Syt * In case the targer driver would send the request before 19742fcbc377Syt * sata framework can have the opportunity to process those 19752fcbc377Syt * event reports. 19762fcbc377Syt */ 19772fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 19782fcbc377Syt spkt->satapkt_device.satadev_state = 19792fcbc377Syt ahci_portp->ahciport_port_state; 19802fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 19812fcbc377Syt &spkt->satapkt_device); 19822fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 19832fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 19842fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 198568d33a25Syt "port: %d", port); 19862fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 19872fcbc377Syt return (SATA_FAILURE); 19882fcbc377Syt } 19892fcbc377Syt 19902fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 19912fcbc377Syt /* 19922fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 19932fcbc377Syt * AHCI_PORT_TYPE_NODEV. 19942fcbc377Syt */ 19952fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 19962fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 19972fcbc377Syt spkt->satapkt_device.satadev_state = 19982fcbc377Syt ahci_portp->ahciport_port_state; 19992fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 20002fcbc377Syt &spkt->satapkt_device); 20012fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 20022fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 200368d33a25Syt "no device attached: port: %d", port); 20042fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20052fcbc377Syt return (SATA_FAILURE); 20062fcbc377Syt } 20072fcbc377Syt 20082fcbc377Syt if (flag == SATA_ABORT_ALL_PACKETS) { 200982263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 201082263d52Syt aborted_tags = ahci_portp->ahciport_pending_tags; 2011a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 201282263d52Syt aborted_tags = ahci_portp->ahciport_pending_ncq_tags; 201382263d52Syt 2014a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort all packets", 2015a9440e8dSyt instance, port); 20162fcbc377Syt } else { 20172fcbc377Syt aborted_tags = 0xffffffff; 20182fcbc377Syt /* 201982263d52Syt * Aborting one specific packet, first search the 20202fcbc377Syt * ahciport_slot_pkts[] list for matching spkt. 20212fcbc377Syt */ 20222fcbc377Syt for (tmp_slot = 0; 20232fcbc377Syt tmp_slot < ahci_ctlp->ahcictl_num_cmd_slots; tmp_slot++) { 20242fcbc377Syt if (ahci_portp->ahciport_slot_pkts[tmp_slot] == spkt) { 20252fcbc377Syt aborted_tags = (0x1 << tmp_slot); 20262fcbc377Syt break; 20272fcbc377Syt } 20282fcbc377Syt } 20292fcbc377Syt 20302fcbc377Syt if (aborted_tags == 0xffffffff) { 20312fcbc377Syt /* request packet is not on the pending list */ 20322fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 20332fcbc377Syt "Cannot find the aborting pkt 0x%p on the " 20342fcbc377Syt "pending list", (void *)spkt); 20352fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 20362fcbc377Syt &spkt->satapkt_device); 20372fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20382fcbc377Syt return (SATA_FAILURE); 20392fcbc377Syt } 2040a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort satapkt 0x%p", 2041a9440e8dSyt instance, port, (void *)spkt); 20422fcbc377Syt } 20432fcbc377Syt 204482263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 204582263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 204682263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 2047a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 204882263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 204982263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 20502fcbc377Syt 205168d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 205268d33a25Syt ahci_portp->ahciport_mop_in_progress++; 20532fcbc377Syt 20542fcbc377Syt /* 20552fcbc377Syt * To abort the packet(s), first we are trying to clear PxCMD.ST 205668d33a25Syt * to stop the port, and if the port can be stopped 20572fcbc377Syt * successfully with PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0', 20582fcbc377Syt * then we just send back the aborted packet(s) with ABORTED flag 20592fcbc377Syt * and then restart the port by setting PxCMD.ST and PxCMD.FRE. 20602fcbc377Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then we 20612fcbc377Syt * perform a COMRESET. 20622fcbc377Syt */ 20632fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 206468d33a25Syt ahci_portp, port, NULL, NULL); 20652fcbc377Syt 20662fcbc377Syt /* 20672fcbc377Syt * Compute which have finished and which need to be retried. 20682fcbc377Syt * 206982263d52Syt * The finished tags are ahciport_pending_tags/ahciport_pending_ncq_tags 207082263d52Syt * minus the slot_status. The aborted_tags has to be deducted by 207182263d52Syt * finished_tags since we can't possibly abort a tag which had finished 207282263d52Syt * already. 20732fcbc377Syt */ 207482263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 207582263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 207682263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2077a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 207882263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 207982263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 20802fcbc377Syt 20812fcbc377Syt aborted_tags &= ~finished_tags; 20822fcbc377Syt 20832fcbc377Syt ahci_mop_commands(ahci_ctlp, 20842fcbc377Syt ahci_portp, 20852fcbc377Syt slot_status, 20862fcbc377Syt 0, /* failed tags */ 20872fcbc377Syt 0, /* timeout tags */ 20882fcbc377Syt aborted_tags, 20892fcbc377Syt 0); /* reset tags */ 20902fcbc377Syt 20912fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, &spkt->satapkt_device); 20922fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20932fcbc377Syt 20942fcbc377Syt return (SATA_SUCCESS); 20952fcbc377Syt } 20962fcbc377Syt 20972fcbc377Syt /* 20982fcbc377Syt * Used to do device reset and reject all the pending packets on a device 20992fcbc377Syt * during the reset operation. 21002fcbc377Syt * 21012fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 21022fcbc377Syt */ 21032fcbc377Syt static int 21042fcbc377Syt ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp, 21052fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 21062fcbc377Syt { 210782263d52Syt uint32_t slot_status = 0; 210882263d52Syt uint32_t reset_tags = 0; 210982263d52Syt uint32_t finished_tags = 0; 21102fcbc377Syt sata_device_t sdevice; 21112fcbc377Syt int ret; 21122fcbc377Syt 21132fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 21142fcbc377Syt "ahci_reset_device_reject_pkts on port: %d", port); 21152fcbc377Syt 21162fcbc377Syt /* 211768d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 211868d33a25Syt * commands are being mopped, therefore there is nothing else to do 21192fcbc377Syt */ 212068d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 21212fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 21222fcbc377Syt "ahci_reset_device_reject_pkts: port %d is in " 21232fcbc377Syt "mopping process, so return directly ", port); 21242fcbc377Syt return (SATA_SUCCESS); 21252fcbc377Syt } 21262fcbc377Syt 212782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 212882263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 212982263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 213082263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2131a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 213282263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 213382263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 213482263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 213582263d52Syt } 21362fcbc377Syt 21372fcbc377Syt if (ahci_software_reset(ahci_ctlp, ahci_portp, port) 21382fcbc377Syt != AHCI_SUCCESS) { 21392fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 21402fcbc377Syt "Try to do a port reset after software " 21412fcbc377Syt "reset failed", port); 21422fcbc377Syt ret = ahci_port_reset(ahci_ctlp, ahci_portp, port); 21432fcbc377Syt if (ret != AHCI_SUCCESS) { 21442fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 21452fcbc377Syt "ahci_reset_device_reject_pkts: port %d " 21462fcbc377Syt "failed", port); 21472fcbc377Syt return (SATA_FAILURE); 21482fcbc377Syt } 21492fcbc377Syt } 21502fcbc377Syt /* Set the reset in progress flag */ 21512fcbc377Syt ahci_portp->ahciport_reset_in_progress = 1; 21522fcbc377Syt 215368d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 215468d33a25Syt ahci_portp->ahciport_mop_in_progress++; 21552fcbc377Syt 21562fcbc377Syt /* Indicate to the framework that a reset has happened */ 21572fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 215809121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 215968d33a25Syt sdevice.satadev_addr.pmport = 0; 216068d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 21612fcbc377Syt 21622fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 21632fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 21642fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 21652fcbc377Syt sata_hba_event_notify( 21662fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 21672fcbc377Syt &sdevice, 21682fcbc377Syt SATA_EVNT_DEVICE_RESET); 21692fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 21702fcbc377Syt 21712fcbc377Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 21722fcbc377Syt "port %d sending event up: SATA_EVNT_RESET", port); 21732fcbc377Syt 21742fcbc377Syt /* Next try to mop the pending commands */ 217582263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 217682263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 217782263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2178a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 217982263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 218082263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 21812fcbc377Syt 21822fcbc377Syt reset_tags &= ~finished_tags; 21832fcbc377Syt 21842fcbc377Syt ahci_mop_commands(ahci_ctlp, 21852fcbc377Syt ahci_portp, 21862fcbc377Syt slot_status, 21872fcbc377Syt 0, /* failed tags */ 21882fcbc377Syt 0, /* timeout tags */ 21892fcbc377Syt 0, /* aborted tags */ 21902fcbc377Syt reset_tags); /* reset tags */ 21912fcbc377Syt 21922fcbc377Syt return (SATA_SUCCESS); 21932fcbc377Syt } 21942fcbc377Syt 21952fcbc377Syt /* 21962fcbc377Syt * Used to do port reset and reject all the pending packets on a port during 21972fcbc377Syt * the reset operation. 21982fcbc377Syt * 21992fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 22002fcbc377Syt */ 22012fcbc377Syt static int 22022fcbc377Syt ahci_reset_port_reject_pkts(ahci_ctl_t *ahci_ctlp, 22032fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 22042fcbc377Syt { 220582263d52Syt uint32_t slot_status = 0; 220682263d52Syt uint32_t reset_tags = 0; 220782263d52Syt uint32_t finished_tags = 0; 22082fcbc377Syt 22092fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 22102fcbc377Syt "ahci_reset_port_reject_pkts on port: %d", port); 22112fcbc377Syt 22122fcbc377Syt /* 221368d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 221468d33a25Syt * commands are being mopped, therefore there is nothing else to do 22152fcbc377Syt */ 221668d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 22172fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 22182fcbc377Syt "ahci_reset_port_reject_pkts: port %d is in " 22192fcbc377Syt "mopping process, so return directly ", port); 22202fcbc377Syt return (SATA_SUCCESS); 22212fcbc377Syt } 22222fcbc377Syt 222368d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 222468d33a25Syt ahci_portp->ahciport_mop_in_progress++; 22252fcbc377Syt 222682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 222782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 222882263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 222982263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2230a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 223182263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 223282263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 223382263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 223482263d52Syt } 22352fcbc377Syt 22362fcbc377Syt if (ahci_restart_port_wait_till_ready(ahci_ctlp, 223782263d52Syt ahci_portp, port, AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 223882263d52Syt NULL) != AHCI_SUCCESS) 22392fcbc377Syt return (SATA_FAILURE); 22402fcbc377Syt 224182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 224282263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 224382263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2244a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 224582263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 224682263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 22472fcbc377Syt 22482fcbc377Syt reset_tags &= ~finished_tags; 22492fcbc377Syt 22502fcbc377Syt ahci_mop_commands(ahci_ctlp, 22512fcbc377Syt ahci_portp, 22522fcbc377Syt slot_status, 22532fcbc377Syt 0, /* failed tags */ 22542fcbc377Syt 0, /* timeout tags */ 22552fcbc377Syt 0, /* aborted tags */ 22562fcbc377Syt reset_tags); /* reset tags */ 22572fcbc377Syt 22582fcbc377Syt return (SATA_SUCCESS); 22592fcbc377Syt } 22602fcbc377Syt 22612fcbc377Syt /* 22622fcbc377Syt * Used to do hba reset and reject all the pending packets on all ports 22632fcbc377Syt * during the reset operation. 22642fcbc377Syt */ 22652fcbc377Syt static int 22662fcbc377Syt ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp) 22672fcbc377Syt { 22682fcbc377Syt ahci_port_t *ahci_portp; 22692fcbc377Syt uint32_t slot_status[AHCI_MAX_PORTS]; 22702fcbc377Syt uint32_t reset_tags[AHCI_MAX_PORTS]; 22712fcbc377Syt uint32_t finished_tags[AHCI_MAX_PORTS]; 22722fcbc377Syt uint8_t port; 22732fcbc377Syt int ret = SATA_SUCCESS; 22742fcbc377Syt 22752fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, 22762fcbc377Syt "ahci_reset_hba_reject_pkts enter"); 22772fcbc377Syt 22782fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 22792fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 22802fcbc377Syt continue; 22812fcbc377Syt } 22822fcbc377Syt 22832fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 22842fcbc377Syt 22852fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 228682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 228782263d52Syt slot_status[port] = ddi_get32( 228882263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 228982263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 229082263d52Syt reset_tags[port] = slot_status[port] & 229182263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 2292a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 229382263d52Syt slot_status[port] = ddi_get32( 229482263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 229582263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 229682263d52Syt reset_tags[port] = slot_status[port] & 229782263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 229882263d52Syt } 22992fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23002fcbc377Syt } 23012fcbc377Syt 23022fcbc377Syt if (ahci_hba_reset(ahci_ctlp) != AHCI_SUCCESS) { 23032fcbc377Syt ret = SATA_FAILURE; 23042fcbc377Syt goto out; 23052fcbc377Syt } 23062fcbc377Syt 23072fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 23082fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 23092fcbc377Syt continue; 23102fcbc377Syt } 23112fcbc377Syt 23122fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 23132fcbc377Syt 23142fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 23152fcbc377Syt /* 23162fcbc377Syt * To prevent recursive enter to ahci_mop_commands, we need 231768d33a25Syt * check AHCI_PORT_FLAG_MOPPING flag. 23182fcbc377Syt */ 231968d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 23202fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 23212fcbc377Syt "ahci_reset_hba_reject_pkts: port %d is in " 23222fcbc377Syt "mopping process, so return directly ", port); 23232fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23242fcbc377Syt continue; 23252fcbc377Syt } 23262fcbc377Syt 232768d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 232868d33a25Syt ahci_portp->ahciport_mop_in_progress++; 23292fcbc377Syt 233082263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 233182263d52Syt finished_tags[port] = 233282263d52Syt ahci_portp->ahciport_pending_tags & 233382263d52Syt ~slot_status[port] & AHCI_SLOT_MASK(ahci_ctlp); 2334a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 233582263d52Syt finished_tags[port] = 233682263d52Syt ahci_portp->ahciport_pending_ncq_tags & 233782263d52Syt ~slot_status[port] & AHCI_NCQ_SLOT_MASK(ahci_portp); 23382fcbc377Syt 23392fcbc377Syt reset_tags[port] &= ~finished_tags[port]; 23402fcbc377Syt 23412fcbc377Syt ahci_mop_commands(ahci_ctlp, 23422fcbc377Syt ahci_portp, 23432fcbc377Syt slot_status[port], 23442fcbc377Syt 0, /* failed tags */ 23452fcbc377Syt 0, /* timeout tags */ 23462fcbc377Syt 0, /* aborted tags */ 23472fcbc377Syt reset_tags[port]); /* reset tags */ 234868d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 23492fcbc377Syt } 23502fcbc377Syt out: 23512fcbc377Syt return (ret); 23522fcbc377Syt } 23532fcbc377Syt 23542fcbc377Syt /* 23552fcbc377Syt * Called by sata framework to reset a port(s) or device. 23562fcbc377Syt */ 23572fcbc377Syt static int 23582fcbc377Syt ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd) 23592fcbc377Syt { 23602fcbc377Syt ahci_ctl_t *ahci_ctlp; 23612fcbc377Syt ahci_port_t *ahci_portp; 23622fcbc377Syt uint8_t cport = sd->satadev_addr.cport; 23632fcbc377Syt uint8_t port; 23642fcbc377Syt int ret = SATA_SUCCESS; 2365a9440e8dSyt int instance = ddi_get_instance(dip); 23662fcbc377Syt 2367a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 23682fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 23692fcbc377Syt 23702fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 237168d33a25Syt "ahci_tran_reset_port enter: cport: %d", cport); 23722fcbc377Syt 23732fcbc377Syt switch (sd->satadev_addr.qual) { 23742fcbc377Syt case SATA_ADDR_CPORT: 23752fcbc377Syt /* Port reset */ 23762fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2377a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2378a9440e8dSyt "reset port", instance, port); 23792fcbc377Syt 23802fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 23812fcbc377Syt ret = ahci_reset_port_reject_pkts(ahci_ctlp, ahci_portp, port); 23822fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23832fcbc377Syt 23842fcbc377Syt break; 23852fcbc377Syt 23862fcbc377Syt case SATA_ADDR_DCPORT: 23872fcbc377Syt /* Device reset */ 23882fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2389a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2390a9440e8dSyt "reset device", instance, port); 23912fcbc377Syt 23922fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 23932fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 23942fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 23952fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 23962fcbc377Syt /* 23972fcbc377Syt * In case the targer driver would send the request 23982fcbc377Syt * before sata framework can have the opportunity to 23992fcbc377Syt * process those event reports. 24002fcbc377Syt */ 24012fcbc377Syt sd->satadev_state = ahci_portp->ahciport_port_state; 24022fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 24032fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 24042fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 24052fcbc377Syt "while port in FAILED/SHUTDOWN/PWROFF state: " 240668d33a25Syt "port: %d", port); 24072fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24082fcbc377Syt ret = SATA_FAILURE; 24092fcbc377Syt break; 24102fcbc377Syt } 24112fcbc377Syt 24122fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 24132fcbc377Syt /* 24142fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 24152fcbc377Syt * AHCI_PORT_TYPE_NODEV. 24162fcbc377Syt */ 24172fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 24182fcbc377Syt sd->satadev_state = ahci_portp->ahciport_port_state; 24192fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 24202fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 24212fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 242268d33a25Syt "while no device attached: port: %d", port); 24232fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24242fcbc377Syt ret = SATA_FAILURE; 24252fcbc377Syt break; 24262fcbc377Syt } 24272fcbc377Syt 24282fcbc377Syt ret = ahci_reset_device_reject_pkts(ahci_ctlp, 24292fcbc377Syt ahci_portp, port); 24302fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24312fcbc377Syt break; 24322fcbc377Syt 24332fcbc377Syt case SATA_ADDR_CNTRL: 24342fcbc377Syt /* Reset the whole controller */ 2435a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2436a9440e8dSyt "reset the whole hba", instance, port); 24372fcbc377Syt ret = ahci_reset_hba_reject_pkts(ahci_ctlp); 24382fcbc377Syt break; 24392fcbc377Syt 24402fcbc377Syt case SATA_ADDR_PMPORT: 24412fcbc377Syt case SATA_ADDR_DPMPORT: 24422fcbc377Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, 244368d33a25Syt "ahci_tran_reset_dport: port multiplier will be " 244468d33a25Syt "supported later"); 2445689d74b0Syt /* FALLTHRU */ 24462fcbc377Syt default: 24472fcbc377Syt ret = SATA_FAILURE; 24482fcbc377Syt } 24492fcbc377Syt 24502fcbc377Syt return (ret); 24512fcbc377Syt } 24522fcbc377Syt 24532fcbc377Syt /* 24542fcbc377Syt * Called by sata framework to activate a port as part of hotplug. 24552fcbc377Syt * (cfgadm -c connect satax/y) 24562fcbc377Syt * Note: Not port-mult aware. 24572fcbc377Syt */ 24582fcbc377Syt static int 24592fcbc377Syt ahci_tran_hotplug_port_activate(dev_info_t *dip, sata_device_t *satadev) 24602fcbc377Syt { 24612fcbc377Syt ahci_ctl_t *ahci_ctlp; 24622fcbc377Syt ahci_port_t *ahci_portp; 24632fcbc377Syt uint8_t cport = satadev->satadev_addr.cport; 24642fcbc377Syt uint8_t port; 2465a9440e8dSyt int instance = ddi_get_instance(dip); 24662fcbc377Syt 2467a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 24682fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 24692fcbc377Syt 24702fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 24712fcbc377Syt "ahci_tran_hotplug_port_activate cport %d enter", cport); 24722fcbc377Syt 24732fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 24742fcbc377Syt 24752fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 2476689d74b0Syt ahci_enable_port_intrs(ahci_ctlp, port); 2477a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d is activated", instance, port); 24782fcbc377Syt 24792fcbc377Syt /* 24802fcbc377Syt * Reset the port so that the PHY communication would be re-established. 248168d33a25Syt * But this reset is an internal operation and the sata module doesn't 248268d33a25Syt * need to know about it. Moreover, the port with a device attached will 248368d33a25Syt * be started too. 24842fcbc377Syt */ 24852fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 248668d33a25Syt ahci_portp, port, 248768d33a25Syt AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 248868d33a25Syt NULL); 24892fcbc377Syt 24902fcbc377Syt /* 24912fcbc377Syt * Need to check the link status and device status of the port 24922fcbc377Syt * and consider raising power if the port was in D3 state 24932fcbc377Syt */ 249468d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_PWRON; 249568d33a25Syt ahci_portp->ahciport_port_state &= ~SATA_PSTATE_PWROFF; 249668d33a25Syt ahci_portp->ahciport_port_state &= ~SATA_PSTATE_SHUTDOWN; 249768d33a25Syt 249868d33a25Syt satadev->satadev_state = ahci_portp->ahciport_port_state; 24992fcbc377Syt 25002fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 25012fcbc377Syt 25022fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 25032fcbc377Syt return (SATA_SUCCESS); 25042fcbc377Syt } 25052fcbc377Syt 25062fcbc377Syt /* 25072fcbc377Syt * Called by sata framework to deactivate a port as part of hotplug. 25082fcbc377Syt * (cfgadm -c disconnect satax/y) 25092fcbc377Syt * Note: Not port-mult aware. 25102fcbc377Syt */ 25112fcbc377Syt static int 25122fcbc377Syt ahci_tran_hotplug_port_deactivate(dev_info_t *dip, sata_device_t *satadev) 25132fcbc377Syt { 25142fcbc377Syt ahci_ctl_t *ahci_ctlp; 25152fcbc377Syt ahci_port_t *ahci_portp; 25162fcbc377Syt uint8_t cport = satadev->satadev_addr.cport; 25172fcbc377Syt uint8_t port; 251868d33a25Syt uint32_t port_scontrol; 2519a9440e8dSyt int instance = ddi_get_instance(dip); 25202fcbc377Syt 2521a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 25222fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 25232fcbc377Syt 25242fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 25252fcbc377Syt "ahci_tran_hotplug_port_deactivate cport %d enter", cport); 25262fcbc377Syt 25272fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 25282fcbc377Syt 25292fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 2530a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d is deactivated", 2531a9440e8dSyt instance, port); 25322fcbc377Syt 253368d33a25Syt /* Disable the interrupts on the port */ 2534689d74b0Syt ahci_disable_port_intrs(ahci_ctlp, port); 25352fcbc377Syt 253668d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 253768d33a25Syt goto phy_offline; 253868d33a25Syt } 253968d33a25Syt 25402fcbc377Syt /* First to abort all the pending commands */ 25412fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 25422fcbc377Syt 254368d33a25Syt /* Then stop the port */ 254468d33a25Syt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 25452fcbc377Syt ahci_portp, port); 25462fcbc377Syt 254768d33a25Syt /* Next put the PHY offline */ 254868d33a25Syt 254968d33a25Syt phy_offline: 255068d33a25Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 255168d33a25Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 255282263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_DISABLE); 255368d33a25Syt 25542fcbc377Syt /* Update ahciport_port_state */ 25552fcbc377Syt ahci_portp->ahciport_port_state = SATA_PSTATE_SHUTDOWN; 255668d33a25Syt satadev->satadev_state = ahci_portp->ahciport_port_state; 25572fcbc377Syt 25582fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 25592fcbc377Syt 25602fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 25612fcbc377Syt return (SATA_SUCCESS); 25622fcbc377Syt } 25632fcbc377Syt 25642fcbc377Syt /* 256568d33a25Syt * To be used to mark all the outstanding pkts with SATA_PKT_ABORTED 25662fcbc377Syt * when a device is unplugged or a port is deactivated. 25672fcbc377Syt * 25682fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 25692fcbc377Syt */ 25702fcbc377Syt static void 25712fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctl_t *ahci_ctlp, 25722fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 25732fcbc377Syt { 257482263d52Syt uint32_t slot_status = 0; 257582263d52Syt uint32_t abort_tags = 0; 25762fcbc377Syt 257768d33a25Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 25782fcbc377Syt "ahci_reject_all_abort_pkts on port: %d", port); 25792fcbc377Syt 258082263d52Syt /* 258182263d52Syt * When AHCI_PORT_FLAG_MOPPING is set, we need to check whether a 258282263d52Syt * REQUEST SENSE command or READ LOG EXT command is delivered to HBA 258382263d52Syt * to get the error data, if yes when the device is removed, the 258482263d52Syt * command needs to be aborted too. 258582263d52Syt */ 258682263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 258782263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 258882263d52Syt slot_status = 0x1; 258982263d52Syt abort_tags = 0x1; 259082263d52Syt goto out; 259182263d52Syt } else { 259282263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 259382263d52Syt "ahci_reject_all_abort_pkts return directly " 259482263d52Syt "port %d no needs to reject any outstanding " 259582263d52Syt "commands", port); 259682263d52Syt return; 259782263d52Syt } 259882263d52Syt } 25992fcbc377Syt 260082263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 260182263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 260282263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 260382263d52Syt abort_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2604a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 260582263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 260682263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 260782263d52Syt abort_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 260882263d52Syt } 260982263d52Syt 261082263d52Syt out: 261168d33a25Syt /* No need to do mop when there is no outstanding commands */ 261268d33a25Syt if (slot_status != 0) { 261368d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 261468d33a25Syt ahci_portp->ahciport_mop_in_progress++; 26152fcbc377Syt 261668d33a25Syt ahci_mop_commands(ahci_ctlp, 261768d33a25Syt ahci_portp, 261868d33a25Syt slot_status, 261968d33a25Syt 0, /* failed tags */ 262068d33a25Syt 0, /* timeout tags */ 262168d33a25Syt abort_tags, /* aborting tags */ 262268d33a25Syt 0); /* reset tags */ 262368d33a25Syt } 262468d33a25Syt } 262568d33a25Syt 262668d33a25Syt #if defined(__lock_lint) 262768d33a25Syt static int 262868d33a25Syt ahci_selftest(dev_info_t *dip, sata_device_t *device) 262968d33a25Syt { 26302fcbc377Syt return (SATA_SUCCESS); 26312fcbc377Syt } 26322fcbc377Syt #endif 26332fcbc377Syt 26342fcbc377Syt /* 263568d33a25Syt * Allocate the ports structure, only called by ahci_attach 263668d33a25Syt */ 263768d33a25Syt static int 263868d33a25Syt ahci_alloc_ports_state(ahci_ctl_t *ahci_ctlp) 263968d33a25Syt { 2640f68cbde1Syt int port, cport = 0; 264168d33a25Syt 264268d33a25Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 264368d33a25Syt "ahci_alloc_ports_state enter"); 264468d33a25Syt 264568d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 264668d33a25Syt 264768d33a25Syt /* Allocate structures only for the implemented ports */ 2648f68cbde1Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 264968d33a25Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 265068d33a25Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 265168d33a25Syt "hba port %d not implemented", port); 265268d33a25Syt continue; 265368d33a25Syt } 265468d33a25Syt 265568d33a25Syt ahci_ctlp->ahcictl_cport_to_port[cport] = (uint8_t)port; 2656f68cbde1Syt ahci_ctlp->ahcictl_port_to_cport[port] = 2657f68cbde1Syt (uint8_t)cport++; 265868d33a25Syt 265968d33a25Syt if (ahci_alloc_port_state(ahci_ctlp, port) != AHCI_SUCCESS) { 266068d33a25Syt goto err_out; 266168d33a25Syt } 266268d33a25Syt } 266368d33a25Syt 266468d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 266568d33a25Syt return (AHCI_SUCCESS); 266668d33a25Syt 266768d33a25Syt err_out: 266868d33a25Syt for (port--; port >= 0; port--) { 266968d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 267068d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 267168d33a25Syt } 267268d33a25Syt } 267368d33a25Syt 267468d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 267568d33a25Syt return (AHCI_FAILURE); 267668d33a25Syt } 267768d33a25Syt 267868d33a25Syt /* 267968d33a25Syt * Reverse of ahci_alloc_ports_state(), only called by ahci_detach 268068d33a25Syt */ 268168d33a25Syt static void 268268d33a25Syt ahci_dealloc_ports_state(ahci_ctl_t *ahci_ctlp) 268368d33a25Syt { 268468d33a25Syt int port; 268568d33a25Syt 268668d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 268768d33a25Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 268868d33a25Syt /* if this port is implemented by the HBA */ 268968d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) 269068d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 269168d33a25Syt } 269268d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 269368d33a25Syt } 269468d33a25Syt 2695f8a673adSying tian - Beijing China /* 2696f8a673adSying tian - Beijing China * Drain the taskq. 2697f8a673adSying tian - Beijing China */ 2698f8a673adSying tian - Beijing China static void 2699f8a673adSying tian - Beijing China ahci_drain_ports_taskq(ahci_ctl_t *ahci_ctlp) 2700f8a673adSying tian - Beijing China { 2701f8a673adSying tian - Beijing China ahci_port_t *ahci_portp; 2702f8a673adSying tian - Beijing China int port; 2703f8a673adSying tian - Beijing China 2704f8a673adSying tian - Beijing China for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 2705f8a673adSying tian - Beijing China if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 2706f8a673adSying tian - Beijing China continue; 2707f8a673adSying tian - Beijing China } 2708f8a673adSying tian - Beijing China 2709f8a673adSying tian - Beijing China ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2710f8a673adSying tian - Beijing China 2711f8a673adSying tian - Beijing China mutex_enter(&ahci_portp->ahciport_mutex); 2712f8a673adSying tian - Beijing China ddi_taskq_wait(ahci_portp->ahciport_event_taskq); 2713f8a673adSying tian - Beijing China mutex_exit(&ahci_portp->ahciport_mutex); 2714f8a673adSying tian - Beijing China } 2715f8a673adSying tian - Beijing China } 2716f8a673adSying tian - Beijing China 271768d33a25Syt /* 271813bcbb7aSyt * Initialize the controller and all ports. And then try to start the ports 271913bcbb7aSyt * if there are devices attached. 27202fcbc377Syt * 27212fcbc377Syt * This routine can be called from three seperate cases: DDI_ATTACH, 27222fcbc377Syt * PM_LEVEL_D0 and DDI_RESUME. The DDI_ATTACH case is different from 272368d33a25Syt * other two cases; device signature probing are attempted only during 272468d33a25Syt * DDI_ATTACH case. 27252fcbc377Syt * 27262fcbc377Syt * WARNING!!! Disable the whole controller's interrupts before calling and 27272fcbc377Syt * the interrupts will be enabled upon successfully return. 27282fcbc377Syt */ 27292fcbc377Syt static int 27302fcbc377Syt ahci_initialize_controller(ahci_ctl_t *ahci_ctlp) 27312fcbc377Syt { 27322fcbc377Syt ahci_port_t *ahci_portp; 27332fcbc377Syt uint32_t ghc_control; 273468d33a25Syt int port; 27352fcbc377Syt 27362fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 27372fcbc377Syt "ahci_initialize_controller enter"); 27382fcbc377Syt 27392fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 27402fcbc377Syt 27412fcbc377Syt /* 27422fcbc377Syt * Indicate that system software is AHCI aware by setting 27432fcbc377Syt * GHC.AE to 1 27442fcbc377Syt */ 27452fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 27462fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 27472fcbc377Syt 27482fcbc377Syt ghc_control |= AHCI_HBA_GHC_AE; 27492fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 27502fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), 27512fcbc377Syt ghc_control); 27522fcbc377Syt 275382263d52Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 275482263d52Syt 27552fcbc377Syt /* Initialize the implemented ports and structures */ 27562fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 27572fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 27582fcbc377Syt continue; 27592fcbc377Syt } 27602fcbc377Syt 27612fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 27622fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 27632fcbc377Syt 27642fcbc377Syt /* 27652fcbc377Syt * Ensure that the controller is not in the running state 27662fcbc377Syt * by checking every implemented port's PxCMD register 27672fcbc377Syt */ 27682fcbc377Syt if (ahci_initialize_port(ahci_ctlp, ahci_portp, port) 27692fcbc377Syt != AHCI_SUCCESS) { 27702fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 27712fcbc377Syt "ahci_initialize_controller: failed to " 27722fcbc377Syt "initialize port %d", port); 27732fcbc377Syt /* 27742fcbc377Syt * Set the port state to SATA_PSTATE_FAILED if 27752fcbc377Syt * failed to initialize it. 27762fcbc377Syt */ 277768d33a25Syt ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 27782fcbc377Syt } 27792fcbc377Syt 27802fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 27812fcbc377Syt } 27822fcbc377Syt 27832fcbc377Syt /* Enable the whole controller interrupts */ 278482263d52Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 27852fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 27862fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 27872fcbc377Syt 27882fcbc377Syt return (AHCI_SUCCESS); 27892fcbc377Syt } 27902fcbc377Syt 27912fcbc377Syt /* 279268d33a25Syt * Reverse of ahci_initialize_controller() 27932fcbc377Syt * 279413bcbb7aSyt * We only need to stop the ports and disable the interrupt. 27952fcbc377Syt */ 27962fcbc377Syt static void 279768d33a25Syt ahci_uninitialize_controller(ahci_ctl_t *ahci_ctlp) 27982fcbc377Syt { 279913bcbb7aSyt ahci_port_t *ahci_portp; 280013bcbb7aSyt int port; 280113bcbb7aSyt 28022fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 280368d33a25Syt "ahci_uninitialize_controller enter"); 28042fcbc377Syt 28052fcbc377Syt /* disable all the interrupts. */ 280613bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 28072fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 280813bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 280913bcbb7aSyt 281013bcbb7aSyt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 281113bcbb7aSyt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 281213bcbb7aSyt continue; 281313bcbb7aSyt } 281413bcbb7aSyt 281513bcbb7aSyt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 281613bcbb7aSyt 281713bcbb7aSyt /* Stop the port by clearing PxCMD.ST */ 281813bcbb7aSyt mutex_enter(&ahci_portp->ahciport_mutex); 281913bcbb7aSyt 282013bcbb7aSyt /* 282113bcbb7aSyt * Here we must disable the port interrupt because 282213bcbb7aSyt * ahci_disable_all_intrs only clear GHC.IE, and IS 282313bcbb7aSyt * register will be still set if PxIE is enabled. 282413bcbb7aSyt * When ahci shares one IRQ with other drivers, the 282513bcbb7aSyt * intr handler may claim the intr mistakenly. 282613bcbb7aSyt */ 2827689d74b0Syt ahci_disable_port_intrs(ahci_ctlp, port); 282813bcbb7aSyt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 282913bcbb7aSyt ahci_portp, port); 283013bcbb7aSyt mutex_exit(&ahci_portp->ahciport_mutex); 283113bcbb7aSyt } 28322fcbc377Syt } 28332fcbc377Syt 28342fcbc377Syt /* 28352fcbc377Syt * The routine is to initialize the port. First put the port in NOTRunning 28362fcbc377Syt * state, then enable port interrupt and clear Serror register. And under 28372fcbc377Syt * AHCI_ATTACH case, find device signature and then try to start the port. 28382fcbc377Syt * 283982263d52Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 28402fcbc377Syt */ 28412fcbc377Syt static int 28422fcbc377Syt ahci_initialize_port(ahci_ctl_t *ahci_ctlp, 28432fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 28442fcbc377Syt { 28452fcbc377Syt uint32_t port_cmd_status; 28462fcbc377Syt 28472fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 28482fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 28492fcbc377Syt 28502fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 28512fcbc377Syt "ahci_initialize_port: port %d " 28522fcbc377Syt "port_cmd_status = 0x%x", port, port_cmd_status); 28532fcbc377Syt /* 28542fcbc377Syt * Check whether the port is in NotRunning state, if not, 28552fcbc377Syt * put the port in NotRunning state 28562fcbc377Syt */ 28572fcbc377Syt if (!(port_cmd_status & 28582fcbc377Syt (AHCI_CMD_STATUS_ST | 28592fcbc377Syt AHCI_CMD_STATUS_CR | 28602fcbc377Syt AHCI_CMD_STATUS_FRE | 28612fcbc377Syt AHCI_CMD_STATUS_FR))) { 28622fcbc377Syt 286368d33a25Syt goto next; 28642fcbc377Syt } 28652fcbc377Syt 28662fcbc377Syt if (ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 286768d33a25Syt port, AHCI_RESET_NO_EVENTS_UP|AHCI_PORT_INIT, NULL) != AHCI_SUCCESS) 28682fcbc377Syt return (AHCI_FAILURE); 28692fcbc377Syt 287068d33a25Syt next: 28712fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 287268d33a25Syt "port %d is in NotRunning state now", port); 28732fcbc377Syt 28742fcbc377Syt /* 287568d33a25Syt * At the time being, only probe ports/devices and get the types of 287613bcbb7aSyt * attached devices during DDI_ATTACH. In fact, the device can be 287713bcbb7aSyt * changed during power state changes, but at the time being, we 287813bcbb7aSyt * don't support the situation. 28792fcbc377Syt */ 28802fcbc377Syt if (ahci_ctlp->ahcictl_flags & AHCI_ATTACH) { 288168d33a25Syt /* Try to get the device signature */ 288268d33a25Syt ahci_find_dev_signature(ahci_ctlp, ahci_portp, port); 288313bcbb7aSyt } else { 28842fcbc377Syt 288513bcbb7aSyt /* 288613bcbb7aSyt * During the resume, we need to set the PxCLB, PxCLBU, PxFB 288713bcbb7aSyt * and PxFBU registers in case these registers were cleared 288813bcbb7aSyt * during the suspend. 288913bcbb7aSyt */ 289013bcbb7aSyt AHCIDBG1(AHCIDBG_PM, ahci_ctlp, 289113bcbb7aSyt "ahci_initialize_port: port %d " 289213bcbb7aSyt "reset the port during resume", port); 289313bcbb7aSyt (void) ahci_port_reset(ahci_ctlp, ahci_portp, port); 28942fcbc377Syt 289513bcbb7aSyt AHCIDBG1(AHCIDBG_PM, ahci_ctlp, 289613bcbb7aSyt "ahci_initialize_port: port %d " 289713bcbb7aSyt "set PxCLB, PxCLBU, PxFB and PxFBU " 289813bcbb7aSyt "during resume", port); 289913bcbb7aSyt 290013bcbb7aSyt /* Config Port Received FIS Base Address */ 290113bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 290213bcbb7aSyt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 290313bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 290413bcbb7aSyt 290513bcbb7aSyt /* Config Port Command List Base Address */ 290613bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 290713bcbb7aSyt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 290813bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 290913bcbb7aSyt } 291013bcbb7aSyt 291113bcbb7aSyt /* Disable the interface power management */ 291213bcbb7aSyt ahci_disable_interface_pm(ahci_ctlp, port); 291313bcbb7aSyt 291413bcbb7aSyt /* Return directly if no device connected */ 291513bcbb7aSyt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 291613bcbb7aSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 291713bcbb7aSyt "No device connected to port %d", port); 291813bcbb7aSyt goto out; 291913bcbb7aSyt } 292013bcbb7aSyt 292113bcbb7aSyt /* Try to start the port */ 292213bcbb7aSyt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 292313bcbb7aSyt != AHCI_SUCCESS) { 292413bcbb7aSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 292513bcbb7aSyt "failed to start port %d", port); 292613bcbb7aSyt return (AHCI_FAILURE); 29272fcbc377Syt } 292868d33a25Syt out: 292968d33a25Syt /* Enable port interrupts */ 2930689d74b0Syt ahci_enable_port_intrs(ahci_ctlp, port); 29312fcbc377Syt 29322fcbc377Syt return (AHCI_SUCCESS); 29332fcbc377Syt } 29342fcbc377Syt 293513bcbb7aSyt /* 2936db2cce03Sying tian - Beijing China * Check the hardware defects and the power management capability. 293713bcbb7aSyt */ 293813bcbb7aSyt static int 293913bcbb7aSyt ahci_config_space_init(ahci_ctl_t *ahci_ctlp) 294013bcbb7aSyt { 2941a9440e8dSyt ushort_t venid, devid; 2942689d74b0Syt ushort_t caps_ptr, cap_count, cap; 2943689d74b0Syt #if AHCI_DEBUG 2944689d74b0Syt ushort_t pmcap, pmcsr; 2945*2c742e1fSying tian - Beijing China ushort_t msimc; 2946689d74b0Syt #endif 294713bcbb7aSyt uint8_t revision; 294813bcbb7aSyt 294913bcbb7aSyt venid = pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 295013bcbb7aSyt PCI_CONF_VENID); 295113bcbb7aSyt 2952a9440e8dSyt devid = pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 2953a9440e8dSyt PCI_CONF_DEVID); 2954a9440e8dSyt 295513bcbb7aSyt /* 295613bcbb7aSyt * Modify dma_attr_align of ahcictl_buffer_dma_attr. For VT8251, those 295713bcbb7aSyt * controllers with 0x00 revision id work on 4-byte aligned buffer, 295813bcbb7aSyt * which is a bug and was fixed after 0x00 revision id controllers. 295913bcbb7aSyt * 296013bcbb7aSyt * Moreover, VT8251 cannot use multiple command slots in the command 296113bcbb7aSyt * list for non-queued commands because the previous register content 296213bcbb7aSyt * of PxCI can be re-written in the register write, so a flag will be 296313bcbb7aSyt * set to record this defect - AHCI_CAP_NO_MCMDLIST_NONQUEUE. 296413bcbb7aSyt */ 296513bcbb7aSyt if (venid == VIA_VENID) { 296613bcbb7aSyt revision = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 296713bcbb7aSyt PCI_CONF_REVID); 296813bcbb7aSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 296913bcbb7aSyt "revision id = 0x%x", revision); 297013bcbb7aSyt if (revision == 0x00) { 297113bcbb7aSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_align = 0x4; 297213bcbb7aSyt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 297313bcbb7aSyt "change ddi_attr_align to 0x4"); 297413bcbb7aSyt } 297513bcbb7aSyt 297613bcbb7aSyt ahci_ctlp->ahcictl_cap = AHCI_CAP_NO_MCMDLIST_NONQUEUE; 297713bcbb7aSyt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 297813bcbb7aSyt "VT8251 cannot use multiple command lists for " 297913bcbb7aSyt "non-queued commands"); 298013bcbb7aSyt } 298113bcbb7aSyt 2982a9440e8dSyt /* 2983db2cce03Sying tian - Beijing China * ATI SB600 (1002,4380) and SB700/750 (1002,4391) AHCI chipsets don't 2984db2cce03Sying tian - Beijing China * support 64-bit DMA addressing though they declare the support, 2985db2cce03Sying tian - Beijing China * so we need to set AHCI_CAP_32BIT_DMA flag to force 32-bit DMA. 2986a9440e8dSyt */ 2987db2cce03Sying tian - Beijing China if (venid == 0x1002 && (devid == 0x4380 || devid == 0x4391)) { 2988a9440e8dSyt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 2989db2cce03Sying tian - Beijing China "ATI SB600/700/750 cannot do 64-bit DMA though CAP " 2990db2cce03Sying tian - Beijing China "indicates support, so force it to use 32-bit DMA"); 2991a9440e8dSyt ahci_ctlp->ahcictl_cap |= AHCI_CAP_32BIT_DMA; 2992a9440e8dSyt } 2993a9440e8dSyt 299413bcbb7aSyt /* 299513bcbb7aSyt * Check if capabilities list is supported and if so, 299613bcbb7aSyt * get initial capabilities pointer and clear bits 0,1. 299713bcbb7aSyt */ 299813bcbb7aSyt if (pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 299913bcbb7aSyt PCI_CONF_STAT) & PCI_STAT_CAP) { 300013bcbb7aSyt caps_ptr = P2ALIGN(pci_config_get8( 300113bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 300213bcbb7aSyt PCI_CONF_CAP_PTR), 4); 300313bcbb7aSyt } else { 300413bcbb7aSyt caps_ptr = PCI_CAP_NEXT_PTR_NULL; 300513bcbb7aSyt } 300613bcbb7aSyt 300713bcbb7aSyt /* 300813bcbb7aSyt * Walk capabilities if supported. 300913bcbb7aSyt */ 301013bcbb7aSyt for (cap_count = 0; caps_ptr != PCI_CAP_NEXT_PTR_NULL; ) { 301113bcbb7aSyt 301213bcbb7aSyt /* 301313bcbb7aSyt * Check that we haven't exceeded the maximum number of 301413bcbb7aSyt * capabilities and that the pointer is in a valid range. 301513bcbb7aSyt */ 301613bcbb7aSyt if (++cap_count > PCI_CAP_MAX_PTR) { 301713bcbb7aSyt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 301813bcbb7aSyt "too many device capabilities"); 301913bcbb7aSyt return (AHCI_FAILURE); 302013bcbb7aSyt } 302113bcbb7aSyt if (caps_ptr < PCI_CAP_PTR_OFF) { 302213bcbb7aSyt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 302313bcbb7aSyt "capabilities pointer 0x%x out of range", 302413bcbb7aSyt caps_ptr); 302513bcbb7aSyt return (AHCI_FAILURE); 302613bcbb7aSyt } 302713bcbb7aSyt 302813bcbb7aSyt /* 302913bcbb7aSyt * Get next capability and check that it is valid. 303013bcbb7aSyt * For now, we only support power management. 303113bcbb7aSyt */ 303213bcbb7aSyt cap = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 303313bcbb7aSyt caps_ptr); 303413bcbb7aSyt switch (cap) { 303513bcbb7aSyt case PCI_CAP_ID_PM: 303613bcbb7aSyt 303713bcbb7aSyt /* power management supported */ 303813bcbb7aSyt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PM; 303913bcbb7aSyt 304013bcbb7aSyt /* Save PMCSR offset */ 304113bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset = caps_ptr + PCI_PMCSR; 304213bcbb7aSyt 3043689d74b0Syt #if AHCI_DEBUG 304413bcbb7aSyt pmcap = pci_config_get16( 304513bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 304613bcbb7aSyt caps_ptr + PCI_PMCAP); 304713bcbb7aSyt pmcsr = pci_config_get16( 304813bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 304913bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset); 305013bcbb7aSyt AHCIDBG2(AHCIDBG_PM, ahci_ctlp, 305113bcbb7aSyt "Power Management capability found PCI_PMCAP " 305213bcbb7aSyt "= 0x%x PCI_PMCSR = 0x%x", pmcap, pmcsr); 305313bcbb7aSyt if ((pmcap & 0x3) == 0x3) 305413bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 305513bcbb7aSyt "PCI Power Management Interface " 305613bcbb7aSyt "spec 1.2 compliant"); 305713bcbb7aSyt #endif 305813bcbb7aSyt break; 305913bcbb7aSyt 306013bcbb7aSyt case PCI_CAP_ID_MSI: 3061*2c742e1fSying tian - Beijing China #if AHCI_DEBUG 3062*2c742e1fSying tian - Beijing China msimc = pci_config_get16( 3063*2c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_pci_conf_handle, 3064*2c742e1fSying tian - Beijing China caps_ptr + PCI_MSI_CTRL); 3065*2c742e1fSying tian - Beijing China AHCIDBG1(AHCIDBG_MSI, ahci_ctlp, 3066*2c742e1fSying tian - Beijing China "Message Signaled Interrupt capability found " 3067*2c742e1fSying tian - Beijing China "MSICAP_MC.MMC = 0x%x", (msimc & 0xe) >> 1); 3068*2c742e1fSying tian - Beijing China #endif 3069*2c742e1fSying tian - Beijing China AHCIDBG0(AHCIDBG_MSI, ahci_ctlp, 3070*2c742e1fSying tian - Beijing China "MSI capability found"); 307113bcbb7aSyt break; 307213bcbb7aSyt 307313bcbb7aSyt case PCI_CAP_ID_PCIX: 307413bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 307513bcbb7aSyt "PCI-X capability found"); 307613bcbb7aSyt break; 307713bcbb7aSyt 307813bcbb7aSyt case PCI_CAP_ID_PCI_E: 307913bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 308013bcbb7aSyt "PCI Express capability found"); 308113bcbb7aSyt break; 308213bcbb7aSyt 308313bcbb7aSyt case PCI_CAP_ID_MSI_X: 308413bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 308513bcbb7aSyt "MSI-X capability found"); 308613bcbb7aSyt break; 308713bcbb7aSyt 308813bcbb7aSyt case PCI_CAP_ID_SATA: 308913bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 309013bcbb7aSyt "SATA capability found"); 309113bcbb7aSyt break; 309213bcbb7aSyt 3093a9440e8dSyt case PCI_CAP_ID_VS: 3094a9440e8dSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 3095a9440e8dSyt "Vendor Specific capability found"); 3096a9440e8dSyt break; 3097a9440e8dSyt 309813bcbb7aSyt default: 309913bcbb7aSyt AHCIDBG1(AHCIDBG_PM, ahci_ctlp, 310013bcbb7aSyt "unrecognized capability 0x%x", cap); 310113bcbb7aSyt break; 310213bcbb7aSyt } 310313bcbb7aSyt 310413bcbb7aSyt /* 310513bcbb7aSyt * Get next capabilities pointer and clear bits 0,1. 310613bcbb7aSyt */ 310713bcbb7aSyt caps_ptr = P2ALIGN(pci_config_get8( 310813bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 310913bcbb7aSyt (caps_ptr + PCI_CAP_NEXT_PTR)), 4); 311013bcbb7aSyt } 311113bcbb7aSyt 311213bcbb7aSyt return (AHCI_SUCCESS); 311313bcbb7aSyt } 311413bcbb7aSyt 31152fcbc377Syt /* 31162fcbc377Syt * AHCI device reset ...; a single device on one of the ports is reset, 31172fcbc377Syt * but the HBA and physical communication remain intact. This is the 31182fcbc377Syt * least intrusive. 31192fcbc377Syt * 31202fcbc377Syt * When issuing a software reset sequence, there should not be other 31212fcbc377Syt * commands in the command list, so we will first clear and then re-set 31222fcbc377Syt * PxCMD.ST to clear PxCI. And before issuing the software reset, 31232fcbc377Syt * the port must be idle and PxTFD.STS.BSY and PxTFD.STS.DRQ must be 3124b2e3645aSying tian - Beijing China * cleared unless command list override (PxCMD.CLO) is supported. 31252fcbc377Syt * 31262fcbc377Syt * WARNING!!! ahciport_mutex should be acquired and PxCMD.FRE should be 31272fcbc377Syt * set before the function is called. 31282fcbc377Syt */ 31292fcbc377Syt static int 31302fcbc377Syt ahci_software_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 31312fcbc377Syt uint8_t port) 31322fcbc377Syt { 31332fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 31342fcbc377Syt ahci_cmd_table_t *cmd_table; 31352fcbc377Syt ahci_cmd_header_t *cmd_header; 313668d33a25Syt uint32_t port_cmd_status, port_cmd_issue, port_task_file; 31372fcbc377Syt int slot, loop_count; 31382fcbc377Syt 31392fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 31402fcbc377Syt "Port %d device resetting", port); 31412fcbc377Syt 3142b2e3645aSying tian - Beijing China /* First clear PxCMD.ST */ 3143b2e3645aSying tian - Beijing China (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 3144b2e3645aSying tian - Beijing China ahci_portp, port); 31452fcbc377Syt 3146b2e3645aSying tian - Beijing China /* Then re-set PxCMD.ST */ 3147b2e3645aSying tian - Beijing China (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 31482fcbc377Syt 31492fcbc377Syt /* Check PxTFD.STS.BSY and PxTFD.STS.DRQ */ 31502fcbc377Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 31512fcbc377Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 31522fcbc377Syt 31532fcbc377Syt if (port_task_file & AHCI_TFD_STS_BSY || 31542fcbc377Syt port_task_file & AHCI_TFD_STS_DRQ) { 3155b2e3645aSying tian - Beijing China port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 3156b2e3645aSying tian - Beijing China (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 31572fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_CLO)) { 31582fcbc377Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 31592fcbc377Syt "PxTFD.STS.BSY or PxTFD.STS.DRQ is still set, " 31602fcbc377Syt "but PxCMD.CLO isn't supported, so a port " 31612fcbc377Syt "reset is needed."); 31622fcbc377Syt return (AHCI_FAILURE); 31632fcbc377Syt } 31642fcbc377Syt } 31652fcbc377Syt 316682263d52Syt slot = ahci_claim_free_slot(ahci_ctlp, ahci_portp, AHCI_NON_NCQ_CMD); 31672fcbc377Syt if (slot == AHCI_FAILURE) { 31682fcbc377Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, 31692fcbc377Syt "ahci_software_reset: no free slot"); 31702fcbc377Syt return (AHCI_FAILURE); 31712fcbc377Syt } 31722fcbc377Syt 3173b2e3645aSying tian - Beijing China /* Now send the first H2D Register FIS with SRST set to 1 */ 31742fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 31752fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 31762fcbc377Syt 31772fcbc377Syt h2d_register_fisp = 31782fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 31792fcbc377Syt 31802fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 31812fcbc377Syt SET_FIS_PMP(h2d_register_fisp, AHCI_PORTMULT_CONTROL_PORT); 31822fcbc377Syt SET_FIS_DEVCTL(h2d_register_fisp, SATA_DEVCTL_SRST); 31832fcbc377Syt 31842fcbc377Syt /* Set Command Header in Command List */ 31852fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 31862fcbc377Syt BZERO_DESCR_INFO(cmd_header); 31872fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 31882fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 31892fcbc377Syt 31902fcbc377Syt SET_CLEAR_BUSY_UPON_R_OK(cmd_header, 1); 31912fcbc377Syt SET_RESET(cmd_header, 1); 31922fcbc377Syt SET_WRITE(cmd_header, 1); 31932fcbc377Syt 31942fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 31952fcbc377Syt 0, 31962fcbc377Syt ahci_cmd_table_size, 31972fcbc377Syt DDI_DMA_SYNC_FORDEV); 31982fcbc377Syt 31992fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 32002fcbc377Syt slot * sizeof (ahci_cmd_header_t), 32012fcbc377Syt sizeof (ahci_cmd_header_t), 32022fcbc377Syt DDI_DMA_SYNC_FORDEV); 32032fcbc377Syt 32042fcbc377Syt /* Indicate to the HBA that a command is active. */ 32052fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 32062fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 32072fcbc377Syt (0x1 << slot)); 32082fcbc377Syt 32092fcbc377Syt loop_count = 0; 32102fcbc377Syt 32112fcbc377Syt /* Loop till the first command is finished */ 32122fcbc377Syt do { 32132fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 32142fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 32152fcbc377Syt 3216b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 32172fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 32182fcbc377Syt break; 32192fcbc377Syt } 32202fcbc377Syt /* Wait for 10 millisec */ 32212fcbc377Syt delay(AHCI_10MS_TICKS); 32222fcbc377Syt } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 32232fcbc377Syt 32242fcbc377Syt AHCIDBG3(AHCIDBG_POLL_LOOP, ahci_ctlp, 32252fcbc377Syt "ahci_software_reset: 1st loop count: %d, " 32262fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 32272fcbc377Syt loop_count, port_cmd_issue, slot); 32282fcbc377Syt 32292fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, slot); 32302fcbc377Syt 3231b2e3645aSying tian - Beijing China /* Now send the second H2D Register FIS with SRST cleard to zero */ 32322fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 32332fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 32342fcbc377Syt 32352fcbc377Syt h2d_register_fisp = 32362fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 32372fcbc377Syt 32382fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 32392fcbc377Syt SET_FIS_PMP(h2d_register_fisp, AHCI_PORTMULT_CONTROL_PORT); 32402fcbc377Syt 32412fcbc377Syt /* Set Command Header in Command List */ 32422fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 32432fcbc377Syt BZERO_DESCR_INFO(cmd_header); 32442fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 32452fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 32462fcbc377Syt 32472fcbc377Syt SET_WRITE(cmd_header, 1); 32482fcbc377Syt 32492fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 32502fcbc377Syt 0, 32512fcbc377Syt ahci_cmd_table_size, 32522fcbc377Syt DDI_DMA_SYNC_FORDEV); 32532fcbc377Syt 32542fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 32552fcbc377Syt slot * sizeof (ahci_cmd_header_t), 32562fcbc377Syt sizeof (ahci_cmd_header_t), 32572fcbc377Syt DDI_DMA_SYNC_FORDEV); 32582fcbc377Syt 32592fcbc377Syt /* Indicate to the HBA that a command is active. */ 32602fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 32612fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 32622fcbc377Syt (0x1 << slot)); 32632fcbc377Syt 32642fcbc377Syt loop_count = 0; 32652fcbc377Syt 32662fcbc377Syt /* Loop till the second command is finished */ 32672fcbc377Syt do { 32682fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 32692fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 32702fcbc377Syt 3271b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 32722fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 32732fcbc377Syt break; 32742fcbc377Syt } 32752fcbc377Syt /* Wait for 10 millisec */ 32762fcbc377Syt delay(AHCI_10MS_TICKS); 32772fcbc377Syt } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 32782fcbc377Syt 32792fcbc377Syt AHCIDBG3(AHCIDBG_POLL_LOOP, ahci_ctlp, 32802fcbc377Syt "ahci_software_reset: 2nd loop count: %d, " 32812fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 32822fcbc377Syt loop_count, port_cmd_issue, slot); 32832fcbc377Syt 32842fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, slot); 32852fcbc377Syt 32862fcbc377Syt return (AHCI_SUCCESS); 32872fcbc377Syt } 32882fcbc377Syt 32892fcbc377Syt /* 32902fcbc377Syt * AHCI port reset ...; the physical communication between the HBA and device 32912fcbc377Syt * on a port are disabled. This is more intrusive. 32922fcbc377Syt * 329368d33a25Syt * When an HBA or port reset occurs, Phy communication is going to 32942fcbc377Syt * be re-established with the device through a COMRESET followed by the 32952fcbc377Syt * normal out-of-band communication sequence defined in Serial ATA. AT 32962fcbc377Syt * the end of reset, the device, if working properly, will send a D2H 32972fcbc377Syt * Register FIS, which contains the device signature. When the HBA receives 32982fcbc377Syt * this FIS, it updates PxTFD.STS and PxTFD.ERR register fields, and updates 32992fcbc377Syt * the PxSIG register with the signature. 33002fcbc377Syt * 33012fcbc377Syt * Staggered spin-up is an optional feature in SATA II, and it enables an HBA 33022fcbc377Syt * to individually spin-up attached devices. Please refer to chapter 10.9 of 330368d33a25Syt * AHCI 1.0 spec. 33042fcbc377Syt */ 33052fcbc377Syt /* 330682263d52Syt * WARNING!!! ahciport_mutex should be acquired, and PxCMD.ST should be also 330782263d52Syt * cleared before the function is called. 33082fcbc377Syt */ 33092fcbc377Syt static int 33102fcbc377Syt ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 33112fcbc377Syt { 33122fcbc377Syt uint32_t cap_status, port_cmd_status; 33132fcbc377Syt uint32_t port_scontrol, port_sstatus; 3314689d74b0Syt uint32_t port_intr_status, port_task_file; 3315689d74b0Syt #if AHCI_DEBUG 3316689d74b0Syt uint32_t port_signature; 3317689d74b0Syt #endif 33182fcbc377Syt int loop_count; 331968d33a25Syt int rval = AHCI_SUCCESS; 3320a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 33212fcbc377Syt 33222fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 33232fcbc377Syt "Port %d port resetting...", port); 332468d33a25Syt ahci_portp->ahciport_port_state = 0; 33252fcbc377Syt 33262fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33272fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 33282fcbc377Syt 33292fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33302fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 33312fcbc377Syt 33322fcbc377Syt if (cap_status & AHCI_HBA_CAP_SSS) { 33332fcbc377Syt /* 33342fcbc377Syt * HBA support staggered spin-up, if the port has 33352fcbc377Syt * not spin up yet, then force it to do spin-up 33362fcbc377Syt */ 33372fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) { 33382fcbc377Syt if (!(ahci_portp->ahciport_flags 333968d33a25Syt & AHCI_PORT_FLAG_SPINUP)) { 33402fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 33412fcbc377Syt "Port %d PxCMD.SUD is zero, force " 33422fcbc377Syt "it to do spin-up", port); 33432fcbc377Syt ahci_portp->ahciport_flags |= 334468d33a25Syt AHCI_PORT_FLAG_SPINUP; 33452fcbc377Syt } 33462fcbc377Syt } 33472fcbc377Syt } else { 33482fcbc377Syt /* 33492fcbc377Syt * HBA doesn't support stagger spin-up, force it 33502fcbc377Syt * to do normal COMRESET 33512fcbc377Syt */ 33522fcbc377Syt if (ahci_portp->ahciport_flags & 335368d33a25Syt AHCI_PORT_FLAG_SPINUP) { 33542fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 33552fcbc377Syt "HBA does not support staggered spin-up " 33562fcbc377Syt "force it to do normal COMRESET"); 33572fcbc377Syt ahci_portp->ahciport_flags &= 335868d33a25Syt ~AHCI_PORT_FLAG_SPINUP; 33592fcbc377Syt } 33602fcbc377Syt } 33612fcbc377Syt 336268d33a25Syt if (!(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_SPINUP)) { 33632fcbc377Syt /* Do normal COMRESET */ 33642fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 33652fcbc377Syt "ahci_port_reset: do normal COMRESET", port); 33662fcbc377Syt 336795c11c1fSyt /* 336895c11c1fSyt * According to the spec, SUD bit should be set here, 336995c11c1fSyt * but JMicron JMB363 doesn't follow it, so remove 337095c11c1fSyt * the assertion, and just print a debug message. 337195c11c1fSyt */ 337295c11c1fSyt #if AHCI_DEBUG 337395c11c1fSyt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) 337495c11c1fSyt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 337595c11c1fSyt "port %d SUD bit not set", port) 337695c11c1fSyt #endif 33772fcbc377Syt 33782fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33792fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 338082263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_COMRESET); 33812fcbc377Syt 33822fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33832fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 33842fcbc377Syt port_scontrol); 33852fcbc377Syt 33862fcbc377Syt /* Enable PxCMD.FRE to read device */ 33872fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33882fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 33892fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 33902fcbc377Syt 339168d33a25Syt /* 339268d33a25Syt * Give time for COMRESET to percolate, according to the AHCI 339368d33a25Syt * spec, software shall wait at least 1 millisecond before 339468d33a25Syt * clearing PxSCTL.DET 339568d33a25Syt */ 33962fcbc377Syt delay(AHCI_1MS_TICKS*2); 33972fcbc377Syt 33982fcbc377Syt /* Fetch the SCONTROL again and rewrite the DET part with 0 */ 33992fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34002fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 340182263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 34022fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 34032fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 34042fcbc377Syt port_scontrol); 34052fcbc377Syt } else { 34062fcbc377Syt /* Do staggered spin-up */ 34072fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34082fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 340982263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 34102fcbc377Syt 34112fcbc377Syt /* PxSCTL.DET must be 0 */ 34122fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 34132fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 34142fcbc377Syt port_scontrol); 34152fcbc377Syt 34162fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_SUD; 34172fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 34182fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 34192fcbc377Syt port_cmd_status); 34202fcbc377Syt 34212fcbc377Syt /* 0 -> 1 edge */ 34222fcbc377Syt delay(AHCI_1MS_TICKS*2); 34232fcbc377Syt 34242fcbc377Syt /* Set PxCMD.SUD to 1 */ 34252fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34262fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 34272fcbc377Syt port_cmd_status |= AHCI_CMD_STATUS_SUD; 34282fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 34292fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 34302fcbc377Syt port_cmd_status); 34312fcbc377Syt 34322fcbc377Syt /* Enable PxCMD.FRE to read device */ 34332fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 34342fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 34352fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 34362fcbc377Syt } 34372fcbc377Syt 34382fcbc377Syt /* 343968d33a25Syt * The port enters P:StartComm state, and HBA tells link layer to 344068d33a25Syt * start communication, which involves sending COMRESET to device. 344168d33a25Syt * And the HBA resets PxTFD.STS to 7Fh. 344268d33a25Syt * 344368d33a25Syt * When a COMINIT is received from the device, then the port enters 344468d33a25Syt * P:ComInit state. And HBA sets PxTFD.STS to FFh or 80h. HBA sets 344568d33a25Syt * PxSSTS.DET to 1h to indicate a device is detected but communication 344668d33a25Syt * is not yet established. HBA sets PxSERR.DIAG.X to '1' to indicate 344768d33a25Syt * a COMINIT has been received. 34482fcbc377Syt */ 34492fcbc377Syt /* 34502fcbc377Syt * The DET field is valid only if IPM field indicates 34512fcbc377Syt * that the interface is in active state. 34522fcbc377Syt */ 34532fcbc377Syt loop_count = 0; 34542fcbc377Syt do { 34552fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34562fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 34572fcbc377Syt 345882263d52Syt if (SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) { 34592fcbc377Syt /* 34602fcbc377Syt * If the interface is not active, the DET field 34612fcbc377Syt * is considered not accurate. So we want to 34622fcbc377Syt * continue looping. 34632fcbc377Syt */ 346482263d52Syt SSTATUS_SET_DET(port_sstatus, SSTATUS_DET_NODEV); 34652fcbc377Syt } 34662fcbc377Syt 34672fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SSTATUS) { 34682fcbc377Syt /* 34692fcbc377Syt * We are effectively timing out after 0.1 sec. 34702fcbc377Syt */ 34712fcbc377Syt break; 34722fcbc377Syt } 34732fcbc377Syt 34742fcbc377Syt /* Wait for 10 millisec */ 34752fcbc377Syt delay(AHCI_10MS_TICKS); 347682263d52Syt } while (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM); 34772fcbc377Syt 347868d33a25Syt AHCIDBG3(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 347968d33a25Syt "ahci_port_reset: 1st loop count: %d, " 348068d33a25Syt "port_sstatus = 0x%x port %d", 348168d33a25Syt loop_count, port_sstatus, port); 34822fcbc377Syt 348382263d52Syt if ((SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) || 348482263d52Syt (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM)) { 34852fcbc377Syt /* 34862fcbc377Syt * Either the port is not active or there 34872fcbc377Syt * is no device present. 34882fcbc377Syt */ 34892fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_NONE; 349068d33a25Syt goto out; 34912fcbc377Syt } 34922fcbc377Syt 349368d33a25Syt /* Now we can make sure there is a device connected to the port */ 349468d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 349568d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 34962fcbc377Syt 349768d33a25Syt /* a COMINIT signal is supposed to be received */ 349868d33a25Syt if (!(port_intr_status & AHCI_INTR_STATUS_PCS)) { 3499a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d " 3500a9440e8dSyt "COMINIT signal from the device not received", 3501a9440e8dSyt instance, port); 350268d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED; 350368d33a25Syt rval = AHCI_FAILURE; 350468d33a25Syt goto out; 350568d33a25Syt } 35062fcbc377Syt 350795c11c1fSyt /* 350895c11c1fSyt * According to the spec, when PxSCTL.DET is set to 0h, upon 350995c11c1fSyt * receiving a COMINIT from the attached device, PxTFD.STS.BSY 351095c11c1fSyt * shall be set to '1' by the HBA. 351195c11c1fSyt * 351295c11c1fSyt * However, we found JMicron JMB363 doesn't follow this, so 351395c11c1fSyt * remove this check, and just print a debug message. 351495c11c1fSyt */ 351595c11c1fSyt #if AHCI_DEBUG 351668d33a25Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 351768d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 351868d33a25Syt if (!(port_task_file & AHCI_TFD_STS_BSY)) { 351995c11c1fSyt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: " 352095c11c1fSyt "port %d BSY bit is not set after COMINIT signal " 352195c11c1fSyt "is received", port); 352268d33a25Syt } 352395c11c1fSyt #endif 35242fcbc377Syt 352568d33a25Syt /* 352668d33a25Syt * PxSERR.DIAG.X has to be cleared in order to update PxTFD with 352768d33a25Syt * the D2H FIS received by HBA. 352868d33a25Syt */ 352968d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 353068d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 353182263d52Syt SERROR_EXCHANGED_ERR); 35322fcbc377Syt 353368d33a25Syt /* 353468d33a25Syt * Next check whether COMRESET is completed successfully 353568d33a25Syt */ 353668d33a25Syt loop_count = 0; 353768d33a25Syt do { 353868d33a25Syt port_task_file = 353968d33a25Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 354068d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 35412fcbc377Syt 35422fcbc377Syt /* 354368d33a25Syt * The Error bit '1' means COMRESET is finished successfully 354468d33a25Syt * The device hardware has been initialized and the power-up 354568d33a25Syt * diagnostics successfully completed. 35462fcbc377Syt */ 354768d33a25Syt if (((port_task_file & AHCI_TFD_ERR_MASK) 354868d33a25Syt >> AHCI_TFD_ERR_SHIFT) == 0x1) { 3549689d74b0Syt #if AHCI_DEBUG 355068d33a25Syt port_signature = ddi_get32( 355168d33a25Syt ahci_ctlp->ahcictl_ahci_acc_handle, 355268d33a25Syt (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port)); 355368d33a25Syt AHCIDBG2(AHCIDBG_INFO, ahci_ctlp, 355468d33a25Syt "COMRESET success, D2H register FIS " 355568d33a25Syt "post to received FIS structure " 355668d33a25Syt "port %d signature = 0x%x", 355768d33a25Syt port, port_signature); 3558689d74b0Syt #endif 355968d33a25Syt goto out_check; 356068d33a25Syt } 35612fcbc377Syt 356268d33a25Syt if (loop_count++ > AHCI_POLLRATE_PORT_TFD_ERROR) { 356368d33a25Syt /* 356468d33a25Syt * We are effectively timing out after 11 sec. 356568d33a25Syt */ 356668d33a25Syt break; 356768d33a25Syt } 35682fcbc377Syt 356968d33a25Syt /* Wait for 10 millisec */ 357068d33a25Syt delay(AHCI_10MS_TICKS); 357168d33a25Syt } while (((port_task_file & AHCI_TFD_ERR_MASK) 357268d33a25Syt >> AHCI_TFD_ERR_SHIFT) != 0x1); 35732fcbc377Syt 357468d33a25Syt AHCIDBG3(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: 2nd loop " 357568d33a25Syt "count: %d, port_task_file = 0x%x port %d", 357668d33a25Syt loop_count, port_task_file, port); 35772fcbc377Syt 3578a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d the device hardware " 357968d33a25Syt "has been initialized and the power-up diagnostics failed", 3580a9440e8dSyt instance, port); 35812fcbc377Syt 358268d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED; 358368d33a25Syt rval = AHCI_FAILURE; 35842fcbc377Syt 358568d33a25Syt out: 358668d33a25Syt /* Clear port serror register for the port */ 358768d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 358868d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 358968d33a25Syt AHCI_SERROR_CLEAR_ALL); 359068d33a25Syt 359168d33a25Syt return (rval); 359268d33a25Syt 359368d33a25Syt out_check: 359468d33a25Syt /* 359568d33a25Syt * Check device status, if keep busy or COMRESET error 359668d33a25Syt * do device reset to patch some SATA disks' issue 359768d33a25Syt * 359868d33a25Syt * For VT8251, sometimes need to do the device reset 359968d33a25Syt */ 360068d33a25Syt if ((port_task_file & AHCI_TFD_STS_BSY) || 360168d33a25Syt (port_task_file & AHCI_TFD_STS_DRQ)) { 360268d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "port %d keep BSY/DRQ set " 360368d33a25Syt "need to do device reset", port); 36042fcbc377Syt 360568d33a25Syt (void) ahci_software_reset(ahci_ctlp, ahci_portp, port); 36062fcbc377Syt 360768d33a25Syt port_task_file = 360868d33a25Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 360968d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 361068d33a25Syt 361168d33a25Syt if (port_task_file & AHCI_TFD_STS_BSY || 361268d33a25Syt port_task_file & AHCI_TFD_STS_DRQ) { 3613a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_port_reset: port %d " 361468d33a25Syt "BSY/DRQ still set after device reset " 3615a9440e8dSyt "port_task_file = 0x%x", instance, 361668d33a25Syt port, port_task_file); 361768d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED; 361868d33a25Syt rval = AHCI_FAILURE; 36192fcbc377Syt } 36202fcbc377Syt } 36212fcbc377Syt 362268d33a25Syt goto out; 36232fcbc377Syt } 36242fcbc377Syt 36252fcbc377Syt /* 36262fcbc377Syt * AHCI HBA reset ...; the entire HBA is reset, and all ports are disabled. 36272fcbc377Syt * This is the most intrusive. 36282fcbc377Syt * 362968d33a25Syt * When an HBA reset occurs, Phy communication will be re-established with 363068d33a25Syt * the device through a COMRESET followed by the normal out-of-band 363168d33a25Syt * communication sequence defined in Serial ATA. AT the end of reset, the 363268d33a25Syt * device, if working properly, will send a D2H Register FIS, which contains 363368d33a25Syt * the device signature. When the HBA receives this FIS, it updates PxTFD.STS 363468d33a25Syt * and PxTFD.ERR register fields, and updates the PxSIG register with the 363568d33a25Syt * signature. 36362fcbc377Syt * 36372fcbc377Syt * Remember to set GHC.AE to 1 before calling ahci_hba_reset. 36382fcbc377Syt */ 36392fcbc377Syt static int 36402fcbc377Syt ahci_hba_reset(ahci_ctl_t *ahci_ctlp) 36412fcbc377Syt { 36422fcbc377Syt ahci_port_t *ahci_portp; 36432fcbc377Syt uint32_t ghc_control; 36442fcbc377Syt uint8_t port; 36452fcbc377Syt int loop_count; 36462fcbc377Syt int rval = AHCI_SUCCESS; 36472fcbc377Syt 36482fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, "HBA resetting"); 36492fcbc377Syt 365068d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 365168d33a25Syt 36522fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 36532fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 36542fcbc377Syt 36552fcbc377Syt /* Setting GHC.HR to 1, remember GHC.AE is already set to 1 before */ 36562fcbc377Syt ghc_control |= AHCI_HBA_GHC_HR; 36572fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 36582fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 36592fcbc377Syt 36602fcbc377Syt /* 36612fcbc377Syt * Wait until HBA Reset complete or timeout 36622fcbc377Syt */ 36632fcbc377Syt loop_count = 0; 36642fcbc377Syt do { 36652fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 36662fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 36672fcbc377Syt 36682fcbc377Syt if (loop_count++ > AHCI_POLLRATE_HBA_RESET) { 36692fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 36702fcbc377Syt "ahci hba reset is timing out, " 36712fcbc377Syt "ghc_control = 0x%x", ghc_control); 36722fcbc377Syt /* We are effectively timing out after 1 sec. */ 36732fcbc377Syt break; 36742fcbc377Syt } 36752fcbc377Syt 36762fcbc377Syt /* Wait for 10 millisec */ 36772fcbc377Syt delay(AHCI_10MS_TICKS); 36782fcbc377Syt } while (ghc_control & AHCI_HBA_GHC_HR); 36792fcbc377Syt 36802fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 36812fcbc377Syt "ahci_hba_reset: 1st loop count: %d, " 36822fcbc377Syt "ghc_control = 0x%x", loop_count, ghc_control); 36832fcbc377Syt 36842fcbc377Syt if (ghc_control & AHCI_HBA_GHC_HR) { 36852fcbc377Syt /* The hba is not reset for some reasons */ 36862fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 36872fcbc377Syt "hba reset failed: HBA in a hung or locked state"); 368868d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 36892fcbc377Syt return (AHCI_FAILURE); 36902fcbc377Syt } 36912fcbc377Syt 36922fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 36932fcbc377Syt /* Only check implemented ports */ 36942fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 36952fcbc377Syt continue; 36962fcbc377Syt } 36972fcbc377Syt 36982fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 36992fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 37002fcbc377Syt 37012fcbc377Syt if (ahci_port_reset(ahci_ctlp, ahci_portp, port) 37022fcbc377Syt != AHCI_SUCCESS) { 37032fcbc377Syt rval = AHCI_FAILURE; 37042fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 37052fcbc377Syt "ahci_hba_reset: port %d failed", port); 37062fcbc377Syt } 37072fcbc377Syt 37082fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 37092fcbc377Syt } 37102fcbc377Syt 37112fcbc377Syt /* 37122fcbc377Syt * Indicate that system software is AHCI aware by setting 37132fcbc377Syt * GHC.AE to 1 37142fcbc377Syt */ 37152fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 37162fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 37172fcbc377Syt 37182fcbc377Syt ghc_control |= AHCI_HBA_GHC_AE; 37192fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 37202fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 37212fcbc377Syt 372268d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 372368d33a25Syt 37242fcbc377Syt return (rval); 37252fcbc377Syt } 37262fcbc377Syt 37272fcbc377Syt /* 37282fcbc377Syt * This routine is only called from AHCI_ATTACH or phyrdy change 37292fcbc377Syt * case. It first calls port reset to initialize port, probe port and probe 373068d33a25Syt * device, then try to read PxSIG register to find the type of device 373168d33a25Syt * attached to the port. 37322fcbc377Syt * 37332fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 373468d33a25Syt * is called. And the port interrupt is disabled. 37352fcbc377Syt */ 373668d33a25Syt static void 37372fcbc377Syt ahci_find_dev_signature(ahci_ctl_t *ahci_ctlp, 37382fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 37392fcbc377Syt { 37402fcbc377Syt uint32_t signature; 37412fcbc377Syt 37422fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 374368d33a25Syt "ahci_find_dev_signature enter: port %d", port); 37442fcbc377Syt 374568d33a25Syt ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 37462fcbc377Syt 37472fcbc377Syt /* Call port reset to check link status and get device signature */ 374868d33a25Syt (void) ahci_port_reset(ahci_ctlp, ahci_portp, port); 374968d33a25Syt 375068d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 375168d33a25Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 375268d33a25Syt "ahci_find_dev_signature: No device is found " 375368d33a25Syt "at port %d", port); 375468d33a25Syt return; 37552fcbc377Syt } 37562fcbc377Syt 375768d33a25Syt /* Check the port state */ 375868d33a25Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 375968d33a25Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, 376068d33a25Syt "ahci_find_dev_signature: port %d state 0x%x", 376168d33a25Syt port, ahci_portp->ahciport_port_state); 376268d33a25Syt return; 376368d33a25Syt } 37642fcbc377Syt 37652fcbc377Syt signature = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 37662fcbc377Syt (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port)); 37672fcbc377Syt 37682fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_INFO, ahci_ctlp, 376968d33a25Syt "ahci_find_dev_signature: port %d signature = 0x%x", 377068d33a25Syt port, signature); 37712fcbc377Syt 37722fcbc377Syt switch (signature) { 37732fcbc377Syt 37742fcbc377Syt case AHCI_SIGNATURE_DISK: 37752fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_ATADISK; 37762fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 37772fcbc377Syt "Disk is found at port: %d", port); 37782fcbc377Syt break; 37792fcbc377Syt 37802fcbc377Syt case AHCI_SIGNATURE_ATAPI: 378138547057Sying tian - Beijing China ahci_portp->ahciport_device_type = SATA_DTYPE_ATAPI; 37822fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 37832fcbc377Syt "ATAPI device is found at port: %d", port); 37842fcbc377Syt break; 37852fcbc377Syt 37862fcbc377Syt case AHCI_SIGNATURE_PORT_MULTIPLIER: 37872fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_PMULT; 37882fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 37892fcbc377Syt "Port Multiplier is found at port: %d", port); 37902fcbc377Syt break; 37912fcbc377Syt 37922fcbc377Syt default: 37932fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 37942fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 37952fcbc377Syt "Unknown device is found at port: %d", port); 37962fcbc377Syt } 37972fcbc377Syt } 37982fcbc377Syt 379913bcbb7aSyt /* 380013bcbb7aSyt * According to the spec, to reliably detect hot plug removals, software 380113bcbb7aSyt * must disable interface power management. Software should perform the 380213bcbb7aSyt * following initialization on a port after a device is attached: 380313bcbb7aSyt * Set PxSCTL.IPM to 3h to disable interface state transitions 380413bcbb7aSyt * Set PxCMD.ALPE to '0' to disable aggressive power management 380513bcbb7aSyt * Disable device initiated interface power management by SET FEATURE 380613bcbb7aSyt * 380713bcbb7aSyt * We can ignore the last item because by default the feature is disabled 380813bcbb7aSyt */ 380913bcbb7aSyt static void 381013bcbb7aSyt ahci_disable_interface_pm(ahci_ctl_t *ahci_ctlp, uint8_t port) 381113bcbb7aSyt { 381213bcbb7aSyt uint32_t port_scontrol, port_cmd_status; 381313bcbb7aSyt 381413bcbb7aSyt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 381513bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 381613bcbb7aSyt SCONTROL_SET_IPM(port_scontrol, SCONTROL_IPM_DISABLE_BOTH); 381713bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 381813bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), port_scontrol); 381913bcbb7aSyt 382013bcbb7aSyt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 382113bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 382213bcbb7aSyt port_cmd_status &= ~AHCI_CMD_STATUS_ALPE; 382313bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 382413bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 382513bcbb7aSyt } 382613bcbb7aSyt 38272fcbc377Syt /* 382868d33a25Syt * Start the port - set PxCMD.ST to 1, if PxCMD.FRE is not set 382968d33a25Syt * to 1, then set it firstly. 383068d33a25Syt * 383168d33a25Syt * Each port contains two major DMA engines. One DMA engine walks through 383268d33a25Syt * the command list, and is controlled by PxCMD.ST. The second DMA engine 383368d33a25Syt * copies received FISes into system memory, and is controlled by PxCMD.FRE. 383468d33a25Syt * 383568d33a25Syt * Software shall not set PxCMD.ST to '1' until it verifies that PxCMD.CR 383668d33a25Syt * is '0' and has set PxCMD.FRE is '1'. And software shall not clear 383768d33a25Syt * PxCMD.FRE while PxCMD.ST or PxCMD.CR is set '1'. 383868d33a25Syt * 383968d33a25Syt * Software shall not set PxCMD.ST to '1' unless a functional device is 384068d33a25Syt * present on the port(as determined by PxTFD.STS.BSY = '0', 384168d33a25Syt * PxTFD.STS.DRQ = '0', and PxSSTS.DET = 3h). 38422fcbc377Syt * 38432fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 38442fcbc377Syt * is called. 38452fcbc377Syt */ 38462fcbc377Syt static int 384768d33a25Syt ahci_start_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 38482fcbc377Syt { 384968d33a25Syt uint32_t port_cmd_status; 38502fcbc377Syt 385168d33a25Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, "ahci_start_port: %d enter", port); 38522fcbc377Syt 385368d33a25Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 385468d33a25Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 385568d33a25Syt "the state for port %d is 0x%x", 385668d33a25Syt port, ahci_portp->ahciport_port_state); 38572fcbc377Syt return (AHCI_FAILURE); 385868d33a25Syt } 38592fcbc377Syt 386068d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 386168d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 386268d33a25Syt "no device is attached at port %d", port); 386368d33a25Syt return (AHCI_FAILURE); 386468d33a25Syt } 38652fcbc377Syt 386668d33a25Syt /* First to set PxCMD.FRE before setting PxCMD.ST. */ 386768d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 386868d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 38692fcbc377Syt 387068d33a25Syt if (!(port_cmd_status & AHCI_CMD_STATUS_FRE)) { 387168d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_FRE; 38722fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 38732fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 38742fcbc377Syt port_cmd_status); 38752fcbc377Syt } 387668d33a25Syt 387768d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 387868d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 387968d33a25Syt 388068d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_ST; 388168d33a25Syt 388268d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 388368d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 388468d33a25Syt port_cmd_status); 388568d33a25Syt 388668d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_STARTED; 388768d33a25Syt 388868d33a25Syt return (AHCI_SUCCESS); 38892fcbc377Syt } 38902fcbc377Syt 38912fcbc377Syt /* 38922fcbc377Syt * Allocate the ahci_port_t including Received FIS and Command List. 38932fcbc377Syt * The argument - port is the physical port number, and not logical 38942fcbc377Syt * port number seen by the SATA framework. 38952fcbc377Syt * 38962fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 38972fcbc377Syt * is called. 38982fcbc377Syt */ 38992fcbc377Syt static int 39002fcbc377Syt ahci_alloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 39012fcbc377Syt { 3902f8a673adSying tian - Beijing China dev_info_t *dip = ahci_ctlp->ahcictl_dip; 39032fcbc377Syt ahci_port_t *ahci_portp; 3904f8a673adSying tian - Beijing China char taskq_name[64] = "event_handle_taskq"; 39052fcbc377Syt 39062fcbc377Syt ahci_portp = 39072fcbc377Syt (ahci_port_t *)kmem_zalloc(sizeof (ahci_port_t), KM_SLEEP); 39082fcbc377Syt 39092fcbc377Syt ahci_ctlp->ahcictl_ports[port] = ahci_portp; 39102fcbc377Syt ahci_portp->ahciport_port_num = port; 39112fcbc377Syt 391268d33a25Syt /* Intialize the port condition variable */ 391368d33a25Syt cv_init(&ahci_portp->ahciport_cv, NULL, CV_DRIVER, NULL); 391468d33a25Syt 391568d33a25Syt /* Initialize the port mutex */ 39162fcbc377Syt mutex_init(&ahci_portp->ahciport_mutex, NULL, MUTEX_DRIVER, 39172fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 391868d33a25Syt 39192fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 39202fcbc377Syt 39212fcbc377Syt /* 39222fcbc377Syt * Allocate memory for received FIS structure and 39232fcbc377Syt * command list for this port 39242fcbc377Syt */ 39252fcbc377Syt if (ahci_alloc_rcvd_fis(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 39262fcbc377Syt goto err_case1; 39272fcbc377Syt } 39282fcbc377Syt 39292fcbc377Syt if (ahci_alloc_cmd_list(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 39302fcbc377Syt goto err_case2; 39312fcbc377Syt } 39322fcbc377Syt 3933f8a673adSying tian - Beijing China (void) snprintf(taskq_name + strlen(taskq_name), 3934f8a673adSying tian - Beijing China sizeof (taskq_name) - strlen(taskq_name), 3935f8a673adSying tian - Beijing China "_port%d", port); 3936f8a673adSying tian - Beijing China 3937f8a673adSying tian - Beijing China /* Create the taskq for the port */ 3938f8a673adSying tian - Beijing China if ((ahci_portp->ahciport_event_taskq = ddi_taskq_create(dip, 3939f8a673adSying tian - Beijing China taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) { 3940f8a673adSying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ddi_taskq_create failed for event " 3941f8a673adSying tian - Beijing China "handle", ddi_get_instance(ahci_ctlp->ahcictl_dip)); 3942f8a673adSying tian - Beijing China goto err_case3; 3943f8a673adSying tian - Beijing China } 3944f8a673adSying tian - Beijing China 3945f8a673adSying tian - Beijing China /* Allocate the argument for the taskq */ 394668d33a25Syt ahci_portp->ahciport_event_args = 394768d33a25Syt kmem_zalloc(sizeof (ahci_event_arg_t), KM_SLEEP); 394868d33a25Syt 394968d33a25Syt if (ahci_portp->ahciport_event_args == NULL) 3950f8a673adSying tian - Beijing China goto err_case4; 395168d33a25Syt 39522fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 39532fcbc377Syt 39542fcbc377Syt return (AHCI_SUCCESS); 39552fcbc377Syt 3956f8a673adSying tian - Beijing China err_case4: 3957f8a673adSying tian - Beijing China ddi_taskq_destroy(ahci_portp->ahciport_event_taskq); 3958f8a673adSying tian - Beijing China 395968d33a25Syt err_case3: 396068d33a25Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 396168d33a25Syt 39622fcbc377Syt err_case2: 3963689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 39642fcbc377Syt 39652fcbc377Syt err_case1: 39662fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 39672fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 396868d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 39692fcbc377Syt 39702fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 39712fcbc377Syt 39722fcbc377Syt return (AHCI_FAILURE); 39732fcbc377Syt } 39742fcbc377Syt 39752fcbc377Syt /* 39762fcbc377Syt * Reverse of ahci_dealloc_port_state(). 39772fcbc377Syt * 39782fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 39792fcbc377Syt * is called. 39802fcbc377Syt */ 39812fcbc377Syt static void 39822fcbc377Syt ahci_dealloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 39832fcbc377Syt { 39842fcbc377Syt ahci_port_t *ahci_portp = ahci_ctlp->ahcictl_ports[port]; 39852fcbc377Syt 39862fcbc377Syt ASSERT(ahci_portp != NULL); 39872fcbc377Syt 39882fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 398968d33a25Syt kmem_free(ahci_portp->ahciport_event_args, sizeof (ahci_event_arg_t)); 399068d33a25Syt ahci_portp->ahciport_event_args = NULL; 3991f8a673adSying tian - Beijing China ddi_taskq_destroy(ahci_portp->ahciport_event_taskq); 39922fcbc377Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 3993689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 39942fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 39952fcbc377Syt 39962fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 399768d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 39982fcbc377Syt 39992fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 40002fcbc377Syt 40012fcbc377Syt ahci_ctlp->ahcictl_ports[port] = NULL; 40022fcbc377Syt } 40032fcbc377Syt 40042fcbc377Syt /* 40052fcbc377Syt * Allocates memory for the Received FIS Structure 40062fcbc377Syt * 40072fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 40082fcbc377Syt * is called. 40092fcbc377Syt */ 40102fcbc377Syt static int 40112fcbc377Syt ahci_alloc_rcvd_fis(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 40122fcbc377Syt uint8_t port) 40132fcbc377Syt { 40142fcbc377Syt size_t rcvd_fis_size; 40152fcbc377Syt size_t ret_len; 40162fcbc377Syt uint_t cookie_count; 40172fcbc377Syt 40182fcbc377Syt rcvd_fis_size = sizeof (ahci_rcvd_fis_t); 40192fcbc377Syt 40202fcbc377Syt /* allocate rcvd FIS dma handle. */ 40212fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 40222fcbc377Syt &ahci_ctlp->ahcictl_rcvd_fis_dma_attr, 40232fcbc377Syt DDI_DMA_SLEEP, 40242fcbc377Syt NULL, 40252fcbc377Syt &ahci_portp->ahciport_rcvd_fis_dma_handle) != 40262fcbc377Syt DDI_SUCCESS) { 40272fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 40282fcbc377Syt "rcvd FIS dma handle alloc failed"); 40292fcbc377Syt 40302fcbc377Syt return (AHCI_FAILURE); 40312fcbc377Syt } 40322fcbc377Syt 40332fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_rcvd_fis_dma_handle, 40342fcbc377Syt rcvd_fis_size, 40352fcbc377Syt &accattr, 40362fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 40372fcbc377Syt DDI_DMA_SLEEP, 40382fcbc377Syt NULL, 40392fcbc377Syt (caddr_t *)&ahci_portp->ahciport_rcvd_fis, 40402fcbc377Syt &ret_len, 40412fcbc377Syt &ahci_portp->ahciport_rcvd_fis_acc_handle) != NULL) { 40422fcbc377Syt 40432fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 40442fcbc377Syt "rcvd FIS dma mem alloc fail"); 40452fcbc377Syt /* error.. free the dma handle. */ 40462fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 40472fcbc377Syt return (AHCI_FAILURE); 40482fcbc377Syt } 40492fcbc377Syt 40502fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle, 40512fcbc377Syt NULL, 40522fcbc377Syt (caddr_t)ahci_portp->ahciport_rcvd_fis, 40532fcbc377Syt rcvd_fis_size, 40542fcbc377Syt DDI_DMA_CONSISTENT, 40552fcbc377Syt DDI_DMA_SLEEP, 40562fcbc377Syt NULL, 405713bcbb7aSyt &ahci_portp->ahciport_rcvd_fis_dma_cookie, 40582fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 40592fcbc377Syt 40602fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 40612fcbc377Syt "rcvd FIS dma handle bind fail"); 40622fcbc377Syt /* error.. free the dma handle & free the memory. */ 40632fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 40642fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 40652fcbc377Syt return (AHCI_FAILURE); 40662fcbc377Syt } 40672fcbc377Syt 40682fcbc377Syt bzero((void *)ahci_portp->ahciport_rcvd_fis, rcvd_fis_size); 40692fcbc377Syt 40702fcbc377Syt /* Config Port Received FIS Base Address */ 40712fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 40722fcbc377Syt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 407313bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 40742fcbc377Syt 40752fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 407613bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 40772fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 407813bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_address); 40792fcbc377Syt 40802fcbc377Syt return (AHCI_SUCCESS); 40812fcbc377Syt } 40822fcbc377Syt 40832fcbc377Syt /* 40842fcbc377Syt * Deallocates the Received FIS Structure 40852fcbc377Syt * 40862fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 40872fcbc377Syt * is called. 40882fcbc377Syt */ 40892fcbc377Syt static void 4090689d74b0Syt ahci_dealloc_rcvd_fis(ahci_port_t *ahci_portp) 40912fcbc377Syt { 40922fcbc377Syt /* Unbind the cmd list dma handle first. */ 40932fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle); 40942fcbc377Syt 40952fcbc377Syt /* Then free the underlying memory. */ 40962fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 40972fcbc377Syt 40982fcbc377Syt /* Now free the handle itself. */ 40992fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 41002fcbc377Syt } 41012fcbc377Syt 41022fcbc377Syt /* 41032fcbc377Syt * Allocates memory for the Command List, which contains up to 32 entries. 41042fcbc377Syt * Each entry contains a command header, which is a 32-byte structure that 41052fcbc377Syt * includes the pointer to the command table. 41062fcbc377Syt * 41072fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 41082fcbc377Syt * is called. 41092fcbc377Syt */ 41102fcbc377Syt static int 41112fcbc377Syt ahci_alloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 41122fcbc377Syt uint8_t port) 41132fcbc377Syt { 41142fcbc377Syt size_t cmd_list_size; 41152fcbc377Syt size_t ret_len; 41162fcbc377Syt uint_t cookie_count; 41172fcbc377Syt 41182fcbc377Syt cmd_list_size = 41192fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots * sizeof (ahci_cmd_header_t); 41202fcbc377Syt 41212fcbc377Syt /* allocate cmd list dma handle. */ 41222fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 41232fcbc377Syt &ahci_ctlp->ahcictl_cmd_list_dma_attr, 41242fcbc377Syt DDI_DMA_SLEEP, 41252fcbc377Syt NULL, 41262fcbc377Syt &ahci_portp->ahciport_cmd_list_dma_handle) != DDI_SUCCESS) { 41272fcbc377Syt 41282fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 41292fcbc377Syt "cmd list dma handle alloc failed"); 41302fcbc377Syt return (AHCI_FAILURE); 41312fcbc377Syt } 41322fcbc377Syt 41332fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_cmd_list_dma_handle, 41342fcbc377Syt cmd_list_size, 41352fcbc377Syt &accattr, 41362fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 41372fcbc377Syt DDI_DMA_SLEEP, 41382fcbc377Syt NULL, 41392fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_list, 41402fcbc377Syt &ret_len, 41412fcbc377Syt &ahci_portp->ahciport_cmd_list_acc_handle) != NULL) { 41422fcbc377Syt 41432fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 41442fcbc377Syt "cmd list dma mem alloc fail"); 41452fcbc377Syt /* error.. free the dma handle. */ 41462fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 41472fcbc377Syt return (AHCI_FAILURE); 41482fcbc377Syt } 41492fcbc377Syt 41502fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_cmd_list_dma_handle, 41512fcbc377Syt NULL, 41522fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_list, 41532fcbc377Syt cmd_list_size, 41542fcbc377Syt DDI_DMA_CONSISTENT, 41552fcbc377Syt DDI_DMA_SLEEP, 41562fcbc377Syt NULL, 415713bcbb7aSyt &ahci_portp->ahciport_cmd_list_dma_cookie, 41582fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 41592fcbc377Syt 41602fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 41612fcbc377Syt "cmd list dma handle bind fail"); 41622fcbc377Syt /* error.. free the dma handle & free the memory. */ 41632fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 41642fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 41652fcbc377Syt return (AHCI_FAILURE); 41662fcbc377Syt } 41672fcbc377Syt 41682fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_list, cmd_list_size); 41692fcbc377Syt 41702fcbc377Syt /* Config Port Command List Base Address */ 41712fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 41722fcbc377Syt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 417313bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 41742fcbc377Syt 41752fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 417613bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 41772fcbc377Syt 41782fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 417913bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_address); 41802fcbc377Syt 41812fcbc377Syt if (ahci_alloc_cmd_tables(ahci_ctlp, ahci_portp) != AHCI_SUCCESS) { 4182e2decd04Syt goto err_out; 41832fcbc377Syt } 41842fcbc377Syt 41852fcbc377Syt return (AHCI_SUCCESS); 4186e2decd04Syt 4187e2decd04Syt err_out: 4188e2decd04Syt /* Unbind the cmd list dma handle first. */ 4189e2decd04Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 4190e2decd04Syt 4191e2decd04Syt /* Then free the underlying memory. */ 4192e2decd04Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 4193e2decd04Syt 4194e2decd04Syt /* Now free the handle itself. */ 4195e2decd04Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 4196e2decd04Syt 4197e2decd04Syt return (AHCI_FAILURE); 41982fcbc377Syt } 41992fcbc377Syt 42002fcbc377Syt /* 42012fcbc377Syt * Deallocates the Command List 42022fcbc377Syt * 42032fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 42042fcbc377Syt * is called. 42052fcbc377Syt */ 42062fcbc377Syt static void 42072fcbc377Syt ahci_dealloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 42082fcbc377Syt { 42092fcbc377Syt /* First dealloc command table */ 42102fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctlp, ahci_portp); 42112fcbc377Syt 42122fcbc377Syt /* Unbind the cmd list dma handle first. */ 42132fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 42142fcbc377Syt 42152fcbc377Syt /* Then free the underlying memory. */ 42162fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 42172fcbc377Syt 42182fcbc377Syt /* Now free the handle itself. */ 42192fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 42202fcbc377Syt } 42212fcbc377Syt 42222fcbc377Syt /* 42232fcbc377Syt * Allocates memory for all Command Tables, which contains Command FIS, 42242fcbc377Syt * ATAPI Command and Physical Region Descriptor Table. 42252fcbc377Syt * 42262fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 42272fcbc377Syt * is called. 42282fcbc377Syt */ 42292fcbc377Syt static int 42302fcbc377Syt ahci_alloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 42312fcbc377Syt { 42322fcbc377Syt size_t ret_len; 42332fcbc377Syt ddi_dma_cookie_t cmd_table_dma_cookie; 42342fcbc377Syt uint_t cookie_count; 42352fcbc377Syt int slot; 42362fcbc377Syt 42372fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 42382fcbc377Syt "ahci_alloc_cmd_tables: port %d enter", 42392fcbc377Syt ahci_portp->ahciport_port_num); 42402fcbc377Syt 42412fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 42422fcbc377Syt /* Allocate cmd table dma handle. */ 42432fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 42442fcbc377Syt &ahci_ctlp->ahcictl_cmd_table_dma_attr, 42452fcbc377Syt DDI_DMA_SLEEP, 42462fcbc377Syt NULL, 42472fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]) != 42482fcbc377Syt DDI_SUCCESS) { 42492fcbc377Syt 42502fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 42512fcbc377Syt "cmd table dma handle alloc failed"); 42522fcbc377Syt 42532fcbc377Syt goto err_out; 42542fcbc377Syt } 42552fcbc377Syt 42562fcbc377Syt if (ddi_dma_mem_alloc( 42572fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 42582fcbc377Syt ahci_cmd_table_size, 42592fcbc377Syt &accattr, 42602fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 42612fcbc377Syt DDI_DMA_SLEEP, 42622fcbc377Syt NULL, 42632fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_tables[slot], 42642fcbc377Syt &ret_len, 42652fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]) != 42662fcbc377Syt NULL) { 42672fcbc377Syt 42682fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 42692fcbc377Syt "cmd table dma mem alloc fail"); 42702fcbc377Syt 42712fcbc377Syt /* error.. free the dma handle. */ 42722fcbc377Syt ddi_dma_free_handle( 42732fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 42742fcbc377Syt goto err_out; 42752fcbc377Syt } 42762fcbc377Syt 42772fcbc377Syt if (ddi_dma_addr_bind_handle( 42782fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 42792fcbc377Syt NULL, 42802fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_tables[slot], 42812fcbc377Syt ahci_cmd_table_size, 42822fcbc377Syt DDI_DMA_CONSISTENT, 42832fcbc377Syt DDI_DMA_SLEEP, 42842fcbc377Syt NULL, 42852fcbc377Syt &cmd_table_dma_cookie, 42862fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 42872fcbc377Syt 42882fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 42892fcbc377Syt "cmd table dma handle bind fail"); 42902fcbc377Syt /* error.. free the dma handle & free the memory. */ 42912fcbc377Syt ddi_dma_mem_free( 42922fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 42932fcbc377Syt ddi_dma_free_handle( 42942fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 42952fcbc377Syt goto err_out; 42962fcbc377Syt } 42972fcbc377Syt 42982fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_tables[slot], 42992fcbc377Syt ahci_cmd_table_size); 43002fcbc377Syt 43012fcbc377Syt /* Config Port Command Table Base Address */ 43022fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR( 43032fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 43042fcbc377Syt cmd_table_dma_cookie.dmac_laddress & 0xffffffffull); 43052fcbc377Syt 43062fcbc377Syt #ifndef __lock_lint 43072fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR_UPPER( 43082fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 43092fcbc377Syt cmd_table_dma_cookie.dmac_laddress >> 32); 4310689d74b0Syt #endif 43112fcbc377Syt } 43122fcbc377Syt 43132fcbc377Syt return (AHCI_SUCCESS); 43142fcbc377Syt err_out: 43152fcbc377Syt 43162fcbc377Syt for (slot--; slot >= 0; slot--) { 43172fcbc377Syt /* Unbind the cmd table dma handle first */ 43182fcbc377Syt (void) ddi_dma_unbind_handle( 43192fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 43202fcbc377Syt 43212fcbc377Syt /* Then free the underlying memory */ 43222fcbc377Syt ddi_dma_mem_free( 43232fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 43242fcbc377Syt 43252fcbc377Syt /* Now free the handle itself */ 43262fcbc377Syt ddi_dma_free_handle( 43272fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 43282fcbc377Syt } 43292fcbc377Syt 43302fcbc377Syt return (AHCI_FAILURE); 43312fcbc377Syt } 43322fcbc377Syt 43332fcbc377Syt /* 43342fcbc377Syt * Deallocates memory for all Command Tables. 43352fcbc377Syt * 43362fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 43372fcbc377Syt * is called. 43382fcbc377Syt */ 43392fcbc377Syt static void 43402fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 43412fcbc377Syt { 43422fcbc377Syt int slot; 43432fcbc377Syt 43442fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 43452fcbc377Syt "ahci_dealloc_cmd_tables: %d enter", 43462fcbc377Syt ahci_portp->ahciport_port_num); 43472fcbc377Syt 43482fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 43492fcbc377Syt /* Unbind the cmd table dma handle first. */ 43502fcbc377Syt (void) ddi_dma_unbind_handle( 43512fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 43522fcbc377Syt 43532fcbc377Syt /* Then free the underlying memory. */ 43542fcbc377Syt ddi_dma_mem_free( 43552fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 43562fcbc377Syt 43572fcbc377Syt /* Now free the handle itself. */ 43582fcbc377Syt ddi_dma_free_handle( 43592fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 43602fcbc377Syt } 43612fcbc377Syt } 43622fcbc377Syt 43632fcbc377Syt /* 43642fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 43652fcbc377Syt * is called. 43662fcbc377Syt */ 43672fcbc377Syt static void 43682fcbc377Syt ahci_update_sata_registers(ahci_ctl_t *ahci_ctlp, uint8_t port, 43692fcbc377Syt sata_device_t *sd) 43702fcbc377Syt { 43712fcbc377Syt sd->satadev_scr.sstatus = 43722fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 43732fcbc377Syt (uint32_t *)(AHCI_PORT_PxSSTS(ahci_ctlp, port))); 43742fcbc377Syt sd->satadev_scr.serror = 43752fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 43762fcbc377Syt (uint32_t *)(AHCI_PORT_PxSERR(ahci_ctlp, port))); 43772fcbc377Syt sd->satadev_scr.scontrol = 43782fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 43792fcbc377Syt (uint32_t *)(AHCI_PORT_PxSCTL(ahci_ctlp, port))); 43802fcbc377Syt sd->satadev_scr.sactive = 43812fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 43822fcbc377Syt (uint32_t *)(AHCI_PORT_PxSACT(ahci_ctlp, port))); 43832fcbc377Syt } 43842fcbc377Syt 43852fcbc377Syt /* 438668d33a25Syt * For poll mode, ahci_port_intr will be called to emulate the interrupt 43872fcbc377Syt */ 43882fcbc377Syt static void 438982263d52Syt ahci_port_intr(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 43902fcbc377Syt { 439168d33a25Syt uint32_t port_intr_status; 439268d33a25Syt uint32_t port_intr_enable; 439368d33a25Syt 439468d33a25Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 439568d33a25Syt "ahci_port_intr enter: port %d", port); 439668d33a25Syt 439768d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 439868d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_POLLING) { 439968d33a25Syt /* For SATA_OPMODE_POLLING commands */ 440068d33a25Syt port_intr_enable = 440168d33a25Syt (AHCI_INTR_STATUS_DHRS | 440268d33a25Syt AHCI_INTR_STATUS_PSS | 440368d33a25Syt AHCI_INTR_STATUS_SDBS | 440468d33a25Syt AHCI_INTR_STATUS_UFS | 440568d33a25Syt AHCI_INTR_STATUS_PCS | 440668d33a25Syt AHCI_INTR_STATUS_PRCS | 440768d33a25Syt AHCI_INTR_STATUS_OFS | 440868d33a25Syt AHCI_INTR_STATUS_INFS | 440968d33a25Syt AHCI_INTR_STATUS_IFS | 441068d33a25Syt AHCI_INTR_STATUS_HBDS | 441168d33a25Syt AHCI_INTR_STATUS_HBFS | 441268d33a25Syt AHCI_INTR_STATUS_TFES); 441368d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 441468d33a25Syt goto next; 441568d33a25Syt } 441668d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 441768d33a25Syt 441868d33a25Syt /* 441968d33a25Syt * port_intr_enable indicates that the corresponding interrrupt 442068d33a25Syt * reporting is enabled. 442168d33a25Syt */ 442268d33a25Syt port_intr_enable = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 442368d33a25Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port)); 442468d33a25Syt next: 442568d33a25Syt /* 442668d33a25Syt * port_intr_stats indicates that the corresponding interrupt 442768d33a25Syt * condition is active. 442868d33a25Syt */ 442968d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 443068d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 443168d33a25Syt 443282263d52Syt AHCIDBG3(AHCIDBG_INTR, ahci_ctlp, 443368d33a25Syt "ahci_port_intr: port %d, port_intr_status = 0x%x, " 443482263d52Syt "port_intr_enable = 0x%x", 443582263d52Syt port, port_intr_status, port_intr_enable); 443668d33a25Syt 443768d33a25Syt port_intr_status &= port_intr_enable; 443868d33a25Syt 443968d33a25Syt /* First clear the port interrupts status */ 444068d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 444168d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 444268d33a25Syt port_intr_status); 444368d33a25Syt 444482263d52Syt /* Check the completed non-queued commands */ 444568d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_DHRS | 444668d33a25Syt AHCI_INTR_STATUS_PSS)) { 444768d33a25Syt (void) ahci_intr_cmd_cmplt(ahci_ctlp, 4448689d74b0Syt ahci_portp, port); 444968d33a25Syt } 445068d33a25Syt 445182263d52Syt /* Check the completed queued commands */ 445268d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_SDBS) { 445368d33a25Syt (void) ahci_intr_set_device_bits(ahci_ctlp, 445468d33a25Syt ahci_portp, port); 445568d33a25Syt } 445668d33a25Syt 445768d33a25Syt /* Check the port connect change status interrupt bit */ 445868d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PCS) { 445968d33a25Syt (void) ahci_intr_port_connect_change(ahci_ctlp, 446068d33a25Syt ahci_portp, port); 446168d33a25Syt } 446268d33a25Syt 446368d33a25Syt /* Check the device mechanical presence status interrupt bit */ 446468d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_DMPS) { 446568d33a25Syt (void) ahci_intr_device_mechanical_presence_status( 446668d33a25Syt ahci_ctlp, ahci_portp, port); 446768d33a25Syt } 446868d33a25Syt 446968d33a25Syt /* Check the PhyRdy change status interrupt bit */ 447068d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PRCS) { 447168d33a25Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, 447268d33a25Syt port); 44732fcbc377Syt } 447468d33a25Syt 447568d33a25Syt /* 447668d33a25Syt * Check the non-fatal error interrupt bits, there are three 447768d33a25Syt * kinds of non-fatal errors at the time being: 447868d33a25Syt * 447968d33a25Syt * PxIS.UFS - Unknown FIS Error 448068d33a25Syt * PxIS.OFS - Overflow Error 448168d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 448268d33a25Syt * 448368d33a25Syt * For these non-fatal errors, the HBA can continue to operate, 448468d33a25Syt * so the driver just log the error messages. 448568d33a25Syt */ 448668d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_UFS | 448768d33a25Syt AHCI_INTR_STATUS_OFS | 448868d33a25Syt AHCI_INTR_STATUS_INFS)) { 448968d33a25Syt (void) ahci_intr_non_fatal_error(ahci_ctlp, ahci_portp, 449068d33a25Syt port, port_intr_status); 449168d33a25Syt } 449268d33a25Syt 449368d33a25Syt /* 449468d33a25Syt * Check the fatal error interrupt bits, there are four kinds 449568d33a25Syt * of fatal errors for AHCI controllers: 449668d33a25Syt * 449768d33a25Syt * PxIS.HBFS - Host Bus Fatal Error 449868d33a25Syt * PxIS.HBDS - Host Bus Data Error 449968d33a25Syt * PxIS.IFS - Interface Fatal Error 450068d33a25Syt * PxIS.TFES - Task File Error 450168d33a25Syt * 450268d33a25Syt * The fatal error means the HBA can not recover from it by 450368d33a25Syt * itself, and it will try to abort the transfer, and the software 450468d33a25Syt * must intervene to restart the port. 450568d33a25Syt */ 450668d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_IFS | 450768d33a25Syt AHCI_INTR_STATUS_HBDS | 450868d33a25Syt AHCI_INTR_STATUS_HBFS | 450968d33a25Syt AHCI_INTR_STATUS_TFES)) 451068d33a25Syt (void) ahci_intr_fatal_error(ahci_ctlp, ahci_portp, 451182263d52Syt port, port_intr_status); 451268d33a25Syt 451368d33a25Syt /* Check the cold port detect interrupt bit */ 451468d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_CPDS) { 451568d33a25Syt (void) ahci_intr_cold_port_detect(ahci_ctlp, ahci_portp, port); 451668d33a25Syt } 451768d33a25Syt 451868d33a25Syt /* Second clear the corresponding bit in IS.IPS */ 451968d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 452068d33a25Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (0x1 << port)); 45212fcbc377Syt } 45222fcbc377Syt 45232fcbc377Syt /* 45242fcbc377Syt * Interrupt service handler 45252fcbc377Syt */ 45262fcbc377Syt static uint_t 45272fcbc377Syt ahci_intr(caddr_t arg1, caddr_t arg2) 45282fcbc377Syt { 4529689d74b0Syt #ifndef __lock_lint 4530689d74b0Syt _NOTE(ARGUNUSED(arg2)) 4531689d74b0Syt #endif 4532689d74b0Syt /* LINTED */ 45332fcbc377Syt ahci_ctl_t *ahci_ctlp = (ahci_ctl_t *)arg1; 45342fcbc377Syt ahci_port_t *ahci_portp; 453568d33a25Syt int32_t global_intr_status; 45362fcbc377Syt uint8_t port; 45372fcbc377Syt 45382fcbc377Syt /* 45392fcbc377Syt * global_intr_status indicates that the corresponding port has 45402fcbc377Syt * an interrupt pending. 45412fcbc377Syt */ 45422fcbc377Syt global_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45432fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp)); 45442fcbc377Syt 45452fcbc377Syt if (!(global_intr_status & ahci_ctlp->ahcictl_ports_implemented)) { 45462fcbc377Syt /* The interrupt is not ours */ 45472fcbc377Syt return (DDI_INTR_UNCLAIMED); 45482fcbc377Syt } 45492fcbc377Syt 45502fcbc377Syt /* Loop for all the ports */ 45512fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 45522fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 45532fcbc377Syt continue; 45542fcbc377Syt } 45552fcbc377Syt if (!((0x1 << port) & global_intr_status)) { 45562fcbc377Syt continue; 45572fcbc377Syt } 45582fcbc377Syt 45592fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 45602fcbc377Syt 456168d33a25Syt /* Call ahci_port_intr */ 456282263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 45632fcbc377Syt } 45642fcbc377Syt 45652fcbc377Syt return (DDI_INTR_CLAIMED); 45662fcbc377Syt } 45672fcbc377Syt 45682fcbc377Syt /* 456968d33a25Syt * For non-queued commands, when the corresponding bit in the PxCI register 457068d33a25Syt * is cleared, it means the command is completed successfully. And according 457168d33a25Syt * to the HBA state machine, there are three conditions which possibly will 457268d33a25Syt * try to clear the PxCI register bit. 457368d33a25Syt * 1. Receive one D2H Register FIS which is with 'I' bit set 457468d33a25Syt * 2. Update PIO Setup FIS 457568d33a25Syt * 3. Transmit a command and receive R_OK if CTBA.C is set (software reset) 457668d33a25Syt * 457782263d52Syt * Process completed non-queued commands when the interrupt status bit - 457882263d52Syt * AHCI_INTR_STATUS_DHRS or AHCI_INTR_STATUS_PSS is set. 45792fcbc377Syt * 458068d33a25Syt * AHCI_INTR_STATUS_DHRS means a D2H Register FIS has been received 458168d33a25Syt * with the 'I' bit set. And the following commands will send thus 458268d33a25Syt * FIS with 'I' bit set upon the successful completion: 45832fcbc377Syt * 1. Non-data commands 45842fcbc377Syt * 2. DMA data-in command 45852fcbc377Syt * 3. DMA data-out command 45862fcbc377Syt * 4. PIO data-out command 458768d33a25Syt * 5. PACKET non-data commands 458868d33a25Syt * 6. PACKET PIO data-in command 458968d33a25Syt * 7. PACKET PIO data-out command 459068d33a25Syt * 8. PACKET DMA data-in command 459168d33a25Syt * 9. PACKET DMA data-out command 459268d33a25Syt * 459368d33a25Syt * AHCI_INTR_STATUS_PSS means a PIO Setup FIS has been received 459468d33a25Syt * with the 'I' bit set. And the following commands will send this 459568d33a25Syt * FIS upon the successful completion: 459668d33a25Syt * 1. PIO data-in command 45972fcbc377Syt */ 45982fcbc377Syt static int 459982263d52Syt ahci_intr_cmd_cmplt(ahci_ctl_t *ahci_ctlp, 4600689d74b0Syt ahci_port_t *ahci_portp, uint8_t port) 46012fcbc377Syt { 460268d33a25Syt uint32_t port_cmd_issue = 0; 46032fcbc377Syt uint32_t finished_tags; 460468d33a25Syt int finished_slot; 46052fcbc377Syt sata_pkt_t *satapkt; 46062fcbc377Syt ahci_fis_d2h_register_t *rcvd_fisp; 460738547057Sying tian - Beijing China #if AHCI_DEBUG 460838547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 460938547057Sying tian - Beijing China uint32_t cmd_dmacount; 461038547057Sying tian - Beijing China #endif 46112fcbc377Syt 461268d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 461382263d52Syt 461482263d52Syt if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 461582263d52Syt !NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 46162fcbc377Syt /* 46172fcbc377Syt * Spurious interrupt. Nothing to be done. 46182fcbc377Syt */ 46192fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 46202fcbc377Syt return (AHCI_SUCCESS); 46212fcbc377Syt } 46222fcbc377Syt 462368d33a25Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 462468d33a25Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 46252fcbc377Syt 462682263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 462782263d52Syt /* Slot 0 is always used during error recovery */ 462882263d52Syt finished_tags = 0x1 & ~port_cmd_issue; 462982263d52Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, 463082263d52Syt "ahci_intr_cmd_cmplt: port %d the sata pkt for error " 463182263d52Syt "retrieval is finished, and finished_tags = 0x%x", 463282263d52Syt port, finished_tags); 463368d33a25Syt } else { 463468d33a25Syt finished_tags = ahci_portp->ahciport_pending_tags & 463568d33a25Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 46362fcbc377Syt } 46372fcbc377Syt 463868d33a25Syt AHCIDBG3(AHCIDBG_INTR, ahci_ctlp, 463982263d52Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x, " 464082263d52Syt "port_cmd_issue = 0x%x finished_tags = 0x%x", 464168d33a25Syt ahci_portp->ahciport_pending_tags, port_cmd_issue, 464282263d52Syt finished_tags); 46432fcbc377Syt 464482263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 464582263d52Syt (finished_tags == 0x1)) { 464682263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 464782263d52Syt ASSERT(satapkt != NULL); 464882263d52Syt 464982263d52Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 465082263d52Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 465182263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 465282263d52Syt 465382263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 465482263d52Syt goto out; 465582263d52Syt } 46562fcbc377Syt 465768d33a25Syt while (finished_tags) { 465868d33a25Syt finished_slot = ddi_ffs(finished_tags) - 1; 465968d33a25Syt if (finished_slot == -1) { 466068d33a25Syt goto out; 466168d33a25Syt } 46622fcbc377Syt 466368d33a25Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 466468d33a25Syt ASSERT(satapkt != NULL); 466538547057Sying tian - Beijing China #if AHCI_DEBUG 466638547057Sying tian - Beijing China /* 466738547057Sying tian - Beijing China * For non-native queued commands, the PRD byte count field 466838547057Sying tian - Beijing China * shall contain an accurate count of the number of bytes 466938547057Sying tian - Beijing China * transferred for the command before the PxCI bit is cleared 467038547057Sying tian - Beijing China * to '0' for the command. 467138547057Sying tian - Beijing China * 467238547057Sying tian - Beijing China * The purpose of this field is to let software know how many 467338547057Sying tian - Beijing China * bytes transferred for a given operation in order to 467438547057Sying tian - Beijing China * determine if underflow occurred. When issuing native command 467538547057Sying tian - Beijing China * queuing commands, this field should not be used and is not 467638547057Sying tian - Beijing China * required to be valid since in this case underflow is always 467738547057Sying tian - Beijing China * illegal. 467838547057Sying tian - Beijing China * 467938547057Sying tian - Beijing China * For data reads, the HBA will update its PRD byte count with 468038547057Sying tian - Beijing China * the total number of bytes received from the last FIS, and 468138547057Sying tian - Beijing China * may be able to continue normally. For data writes, the 468238547057Sying tian - Beijing China * device will detect an error, and HBA most likely will get 468338547057Sying tian - Beijing China * a fatal error. 468438547057Sying tian - Beijing China * 468538547057Sying tian - Beijing China * Therefore, here just put code to debug part. And please 468638547057Sying tian - Beijing China * refer to the comment above ahci_intr_fatal_error for the 468738547057Sying tian - Beijing China * definition of underflow error. 468838547057Sying tian - Beijing China */ 468938547057Sying tian - Beijing China cmd_dmacount = 469038547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[finished_slot]; 469138547057Sying tian - Beijing China if (cmd_dmacount) { 469238547057Sying tian - Beijing China cmd_header = 469338547057Sying tian - Beijing China &ahci_portp->ahciport_cmd_list[finished_slot]; 469438547057Sying tian - Beijing China AHCIDBG3(AHCIDBG_INTR|AHCIDBG_PRDT, ahci_ctlp, 469538547057Sying tian - Beijing China "ahci_intr_cmd_cmplt: port %d, " 469638547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 469738547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 469838547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 469938547057Sying tian - Beijing China cmd_dmacount); 470038547057Sying tian - Beijing China 470138547057Sying tian - Beijing China if (cmd_header->ahcich_prd_byte_count != cmd_dmacount) { 470238547057Sying tian - Beijing China AHCIDBG1(AHCIDBG_UNDERFLOW, ahci_ctlp, 470338547057Sying tian - Beijing China "ahci_intr_cmd_cmplt: port %d, " 470438547057Sying tian - Beijing China "an underflow occurred", port); 470538547057Sying tian - Beijing China } 470638547057Sying tian - Beijing China } 470738547057Sying tian - Beijing China #endif 47082fcbc377Syt 47092fcbc377Syt /* 471068d33a25Syt * For SATAC_SMART command with SATA_SMART_RETURN_STATUS 471168d33a25Syt * feature, sata_special_regs flag will be set, and the 471268d33a25Syt * driver should copy the status and the other corresponding 471368d33a25Syt * register values in the D2H Register FIS received (It's 471468d33a25Syt * working on Non-data protocol) from the device back to 471568d33a25Syt * the sata_cmd. 471668d33a25Syt * 471768d33a25Syt * For every AHCI port, there is only one Received FIS 471868d33a25Syt * structure, which contains the FISes received from the 471968d33a25Syt * device, So we're trying to copy the content of D2H 472068d33a25Syt * Register FIS in the Received FIS structure back to 472168d33a25Syt * the sata_cmd. 47222fcbc377Syt */ 472368d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 472468d33a25Syt rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 472568d33a25Syt ahcirf_d2h_register_fis); 472668d33a25Syt satapkt->satapkt_cmd.satacmd_status_reg = 472768d33a25Syt GET_RFIS_STATUS(rcvd_fisp); 472868d33a25Syt ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp); 472968d33a25Syt } 47302fcbc377Syt 473168d33a25Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 473268d33a25Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 473368d33a25Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 47342fcbc377Syt 473568d33a25Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, finished_slot); 473668d33a25Syt CLEAR_BIT(finished_tags, finished_slot); 473768d33a25Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 47382fcbc377Syt 473968d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 47402fcbc377Syt } 47412fcbc377Syt out: 47422fcbc377Syt AHCIDBG1(AHCIDBG_PKTCOMP, ahci_ctlp, 474368d33a25Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x", 47442fcbc377Syt ahci_portp->ahciport_pending_tags); 47452fcbc377Syt 47462fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 47472fcbc377Syt 47482fcbc377Syt return (AHCI_SUCCESS); 47492fcbc377Syt } 47502fcbc377Syt 47512fcbc377Syt /* 475268d33a25Syt * AHCI_INTR_STATUS_SDBS means a Set Device Bits FIS has been received 475382263d52Syt * with the 'I' bit set and has been copied into system memory. It will 475482263d52Syt * be sent under the following situations: 475582263d52Syt * 475682263d52Syt * 1. NCQ command is completed 475782263d52Syt * 2. Asynchronous notification 475882263d52Syt * 475982263d52Syt * The completion of NCQ commands (READ/WRITE FPDMA QUEUED) is performed 476082263d52Syt * via the Set Device Bits FIS. When such event is generated, the software 476182263d52Syt * needs to read PxSACT register and compares the current value to the 476282263d52Syt * list of commands previously issue by software. ahciport_pending_ncq_tags 476382263d52Syt * keeps the tags of previously issued commands. 47642fcbc377Syt * 476568d33a25Syt * Asynchronous Notification is a feature in SATA II, which allows an 476668d33a25Syt * ATAPI device to send a signal to the host when media is inserted or 476768d33a25Syt * removed and avoids polling the device for media changes. The signal 476868d33a25Syt * sent to the host is a Set Device Bits FIS with the 'I' and 'N' bits 476982263d52Syt * set to '1'. At the moment, it's not supported yet. 47702fcbc377Syt */ 47712fcbc377Syt static int 477268d33a25Syt ahci_intr_set_device_bits(ahci_ctl_t *ahci_ctlp, 47732fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 47742fcbc377Syt { 477582263d52Syt uint32_t port_sactive; 477682263d52Syt uint32_t port_cmd_issue; 477782263d52Syt uint32_t issued_tags; 477882263d52Syt int issued_slot; 477982263d52Syt uint32_t finished_tags; 478082263d52Syt int finished_slot; 478182263d52Syt sata_pkt_t *satapkt; 47822fcbc377Syt 478382263d52Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 478468d33a25Syt "ahci_intr_set_device_bits enter: port %d", port); 47852fcbc377Syt 478682263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 478782263d52Syt if (!NCQ_CMD_IN_PROGRESS(ahci_portp)) { 478882263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 478982263d52Syt return (AHCI_SUCCESS); 479082263d52Syt } 479182263d52Syt 479282263d52Syt /* 479382263d52Syt * First the handler got which commands are finished by checking 479482263d52Syt * PxSACT register 479582263d52Syt */ 479682263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 479782263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 479868d33a25Syt 479982263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 480082263d52Syt ~port_sactive & AHCI_NCQ_SLOT_MASK(ahci_portp); 480182263d52Syt 480282263d52Syt AHCIDBG3(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 480382263d52Syt "ahci_intr_set_device_bits: port %d pending_ncq_tags = 0x%x " 480482263d52Syt "port_sactive = 0x%x", port, 480582263d52Syt ahci_portp->ahciport_pending_ncq_tags, port_sactive); 480682263d52Syt 480782263d52Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 480882263d52Syt "ahci_intr_set_device_bits: finished_tags = 0x%x", finished_tags); 480982263d52Syt 481082263d52Syt /* 481182263d52Syt * For NCQ commands, the software can determine which command has 481282263d52Syt * already been transmitted to the device by checking PxCI register. 481382263d52Syt */ 481482263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 481582263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 481682263d52Syt 481782263d52Syt issued_tags = ahci_portp->ahciport_pending_tags & 481882263d52Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 481982263d52Syt 482082263d52Syt AHCIDBG3(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 482182263d52Syt "ahci_intr_set_device_bits: port %d pending_tags = 0x%x " 482282263d52Syt "port_cmd_issue = 0x%x", port, 482382263d52Syt ahci_portp->ahciport_pending_tags, port_cmd_issue); 482482263d52Syt 482582263d52Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 482682263d52Syt "ahci_intr_set_device_bits: issued_tags = 0x%x", issued_tags); 482782263d52Syt 482882263d52Syt /* 482982263d52Syt * Clear ahciport_pending_tags bit when the corresponding command 483082263d52Syt * is already sent down to the device. 483182263d52Syt */ 483282263d52Syt while (issued_tags) { 483382263d52Syt issued_slot = ddi_ffs(issued_tags) - 1; 483482263d52Syt if (issued_slot == -1) { 483582263d52Syt goto next; 483682263d52Syt } 483782263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, issued_slot); 483882263d52Syt CLEAR_BIT(issued_tags, issued_slot); 483982263d52Syt } 484082263d52Syt 484182263d52Syt next: 484282263d52Syt while (finished_tags) { 484382263d52Syt finished_slot = ddi_ffs(finished_tags) - 1; 484482263d52Syt if (finished_slot == -1) { 484582263d52Syt goto out; 484682263d52Syt } 484782263d52Syt 484882263d52Syt /* The command is certainly transmitted to the device */ 484982263d52Syt ASSERT(!(ahci_portp->ahciport_pending_tags & 485082263d52Syt (0x1 << finished_slot))); 485182263d52Syt 485282263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 485382263d52Syt ASSERT(satapkt != NULL); 485482263d52Syt 485582263d52Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 485682263d52Syt "ahci_intr_set_device_bits: sending up pkt 0x%p " 485782263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 485882263d52Syt 485982263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, finished_slot); 486082263d52Syt CLEAR_BIT(finished_tags, finished_slot); 486182263d52Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 486282263d52Syt 486382263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 486468d33a25Syt } 486582263d52Syt out: 486682263d52Syt AHCIDBG3(AHCIDBG_PKTCOMP|AHCIDBG_NCQ, ahci_ctlp, 486782263d52Syt "ahci_intr_set_device_bits: port %d " 486882263d52Syt "pending_ncq_tags = 0x%x pending_tags = 0x%x", 486982263d52Syt port, ahci_portp->ahciport_pending_ncq_tags, 487082263d52Syt ahci_portp->ahciport_pending_tags); 487182263d52Syt 487282263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 48732fcbc377Syt 48742fcbc377Syt return (AHCI_SUCCESS); 48752fcbc377Syt } 48762fcbc377Syt 48772fcbc377Syt /* 487868d33a25Syt * 1=Change in Current Connect Status. 0=No change in Current Connect Status. 487968d33a25Syt * This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared 488068d33a25Syt * when PxSERR.DIAG.X is cleared. When PxSERR.DIAG.X is set to one, it 488168d33a25Syt * indicates a COMINIT signal was received. 48822fcbc377Syt * 488368d33a25Syt * Hot plug insertion is detected by reception of a COMINIT signal from the 488468d33a25Syt * device. On reception of unsolicited COMINIT, the HBA shall generate a 488568d33a25Syt * COMRESET. If the COMINIT is in responce to a COMRESET, then the HBA shall 488668d33a25Syt * begin the normal communication negotiation sequence as outlined in the 488768d33a25Syt * Serial ATA 1.0a specification. When a COMRESET is sent to the device the 488868d33a25Syt * PxSSTS.DET field shall be cleared to 0h. When a COMINIT is received, the 488968d33a25Syt * PxSSTS.DET field shall be set to 1h. When the communication negotiation 489068d33a25Syt * sequence is complete and PhyRdy is true the PxSSTS.DET field shall be set 489168d33a25Syt * to 3h. Therefore, at the moment the ahci driver is going to check PhyRdy 489268d33a25Syt * to handle hot plug insertion. In this interrupt handler, just do nothing 489368d33a25Syt * but print some log message and clear the bit. 48942fcbc377Syt */ 48952fcbc377Syt static int 489668d33a25Syt ahci_intr_port_connect_change(ahci_ctl_t *ahci_ctlp, 48972fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 48982fcbc377Syt { 4899689d74b0Syt #if AHCI_DEBUG 49002fcbc377Syt uint32_t port_serror; 4901689d74b0Syt #endif 49022fcbc377Syt 49032fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 49042fcbc377Syt 4905689d74b0Syt #if AHCI_DEBUG 49062fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 49072fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 49082fcbc377Syt 49092fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 491068d33a25Syt "ahci_intr_port_connect_change: port %d, " 491168d33a25Syt "port_serror = 0x%x", port, port_serror); 4912689d74b0Syt #endif 49132fcbc377Syt 491468d33a25Syt /* Clear PxSERR.DIAG.X to clear the interrupt bit */ 49152fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 49162fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 491782263d52Syt SERROR_EXCHANGED_ERR); 49182fcbc377Syt 49192fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 49202fcbc377Syt 49212fcbc377Syt return (AHCI_SUCCESS); 49222fcbc377Syt } 49232fcbc377Syt 49242fcbc377Syt /* 49252fcbc377Syt * Hot Plug Operation for platforms that support Mechanical Presence 49262fcbc377Syt * Switches. 49272fcbc377Syt * 49282fcbc377Syt * When set, it indicates that a mechanical presence switch attached to this 49292fcbc377Syt * port has been opened or closed, which may lead to a change in the connection 49302fcbc377Syt * state of the device. This bit is only valid if both CAP.SMPS and PxCMD.MPSP 49312fcbc377Syt * are set to '1'. 49322fcbc377Syt * 493368d33a25Syt * At the moment, this interrupt is not needed and disabled and we just log 49342fcbc377Syt * the debug message. 49352fcbc377Syt */ 49362fcbc377Syt static int 49372fcbc377Syt ahci_intr_device_mechanical_presence_status(ahci_ctl_t *ahci_ctlp, 49382fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 49392fcbc377Syt { 49402fcbc377Syt uint32_t cap_status, port_cmd_status; 49412fcbc377Syt 49422fcbc377Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 49432fcbc377Syt "ahci_intr_device_mechanical_presence_status enter, " 49442fcbc377Syt "port %d", port); 49452fcbc377Syt 49462fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 49472fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 49482fcbc377Syt 49492fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 49502fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 49512fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 49522fcbc377Syt 49532fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_SMPS) || 49542fcbc377Syt !(port_cmd_status & AHCI_CMD_STATUS_MPSP)) { 49552fcbc377Syt AHCIDBG2(AHCIDBG_INTR, ahci_ctlp, 49562fcbc377Syt "CAP.SMPS or PxCMD.MPSP is not set, so just ignore " 49572fcbc377Syt "the interrupt: cap_status = 0x%x, " 49582fcbc377Syt "port_cmd_status = 0x%x", cap_status, port_cmd_status); 49592fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 49602fcbc377Syt 49612fcbc377Syt return (AHCI_SUCCESS); 49622fcbc377Syt } 49632fcbc377Syt 4964689d74b0Syt #if AHCI_DEBUG 49652fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_MPSS) { 49662fcbc377Syt AHCIDBG2(AHCIDBG_INTR, ahci_ctlp, 49672fcbc377Syt "The mechanical presence switch is open: " 49682fcbc377Syt "port %d, port_cmd_status = 0x%x", 49692fcbc377Syt port, port_cmd_status); 49702fcbc377Syt } else { 49712fcbc377Syt AHCIDBG2(AHCIDBG_INTR, ahci_ctlp, 49722fcbc377Syt "The mechanical presence switch is close: " 49732fcbc377Syt "port %d, port_cmd_status = 0x%x", 49742fcbc377Syt port, port_cmd_status); 49752fcbc377Syt } 4976689d74b0Syt #endif 49772fcbc377Syt 49782fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 49792fcbc377Syt 49802fcbc377Syt return (AHCI_SUCCESS); 49812fcbc377Syt } 49822fcbc377Syt 49832fcbc377Syt /* 49842fcbc377Syt * Native Hot Plug Support. 49852fcbc377Syt * 49862fcbc377Syt * When set, it indicates that the internal PHYRDY signal changed state. 49872fcbc377Syt * This bit reflects the state of PxSERR.DIAG.N. 498868d33a25Syt * 498968d33a25Syt * There are three kinds of conditions to generate this interrupt event: 499068d33a25Syt * 1. a device is inserted 499168d33a25Syt * 2. a device is disconnected 499268d33a25Syt * 3. when the link enters/exits a Partial or Slumber interface power 499368d33a25Syt * management state 499468d33a25Syt * 499568d33a25Syt * If inteface power management is enabled for a port, the PxSERR.DIAG.N 499668d33a25Syt * bit may be set due to the link entering the Partial or Slumber power 499768d33a25Syt * management state, rather than due to a hot plug insertion or removal 499813bcbb7aSyt * event. So far, the interface power management is disabled, so the 499913bcbb7aSyt * driver can reliably get removal detection notification via the 500013bcbb7aSyt * PxSERR.DIAG.N bit. 50012fcbc377Syt */ 50022fcbc377Syt static int 50032fcbc377Syt ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp, 50042fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 50052fcbc377Syt { 50062fcbc377Syt uint32_t port_sstatus = 0; /* No dev present & PHY not established. */ 50072fcbc377Syt sata_device_t sdevice; 50082fcbc377Syt int dev_exists_now = 0; 50092fcbc377Syt int dev_existed_previously = 0; 50102fcbc377Syt 50112fcbc377Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 50122fcbc377Syt "ahci_intr_phyrdy_change enter, port %d", port); 50132fcbc377Syt 50142fcbc377Syt /* Clear PxSERR.DIAG.N to clear the interrupt bit */ 50152fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 50162fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 50172fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 501882263d52Syt SERROR_PHY_RDY_CHG); 50192fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 50202fcbc377Syt 50212fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 50222fcbc377Syt if ((ahci_ctlp->ahcictl_sata_hba_tran == NULL) || 50232fcbc377Syt (ahci_portp == NULL)) { 50242fcbc377Syt /* The whole controller setup is not yet done. */ 50252fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 50262fcbc377Syt return (AHCI_SUCCESS); 50272fcbc377Syt } 50282fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 50292fcbc377Syt 50302fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 50312fcbc377Syt 50322fcbc377Syt /* SStatus tells the presence of device. */ 50332fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 50342fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 50352fcbc377Syt 503682263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 503768d33a25Syt dev_exists_now = 1; 503868d33a25Syt } 50392fcbc377Syt 50402fcbc377Syt if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) { 50412fcbc377Syt dev_existed_previously = 1; 50422fcbc377Syt } 50432fcbc377Syt 504482263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_NODEV) { 504582263d52Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_NODEV; 504682263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 504782263d52Syt "ahci_intr_phyrdy_change: port %d " 504882263d52Syt "AHCI_PORT_FLAG_NODEV is cleared", port); 504982263d52Syt if (dev_exists_now == 0) 505082263d52Syt dev_existed_previously = 1; 505182263d52Syt } 505282263d52Syt 50532fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 505409121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 50552fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 50562fcbc377Syt sdevice.satadev_addr.pmport = 0; 50572fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 50582fcbc377Syt ahci_portp->ahciport_port_state = SATA_PSTATE_PWRON; 50592fcbc377Syt 50602fcbc377Syt if (dev_exists_now) { 50612fcbc377Syt if (dev_existed_previously) { 50622fcbc377Syt /* Things are fine now. The loss was temporary. */ 506368d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 506468d33a25Syt "ahci_intr_phyrdy_change port %d " 50652fcbc377Syt "device link lost/established", port); 50662fcbc377Syt 50672fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 50682fcbc377Syt sata_hba_event_notify( 50692fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 50702fcbc377Syt &sdevice, 50712fcbc377Syt SATA_EVNT_LINK_LOST|SATA_EVNT_LINK_ESTABLISHED); 50722fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 50732fcbc377Syt 50742fcbc377Syt } else { 507568d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 507668d33a25Syt "ahci_intr_phyrdy_change: port %d " 50772fcbc377Syt "device link established", port); 50782fcbc377Syt 50792fcbc377Syt /* A new device has been detected. */ 508068d33a25Syt ahci_find_dev_signature(ahci_ctlp, ahci_portp, port); 50812fcbc377Syt 508268d33a25Syt /* Try to start the port */ 508368d33a25Syt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 508468d33a25Syt != AHCI_SUCCESS) { 508568d33a25Syt sdevice.satadev_state |= SATA_PSTATE_FAILED; 50862fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 508768d33a25Syt "ahci_intr_phyrdy_change: port %d failed " 50882fcbc377Syt "at start port", port); 50892fcbc377Syt } 50902fcbc377Syt 509182263d52Syt /* Clear the max queue depth for inserted device */ 509282263d52Syt ahci_portp->ahciport_max_ncq_tags = 0; 509382263d52Syt 50942fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 50952fcbc377Syt sata_hba_event_notify( 50962fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 50972fcbc377Syt &sdevice, 50982fcbc377Syt SATA_EVNT_LINK_ESTABLISHED); 50992fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 51002fcbc377Syt 51012fcbc377Syt } 51022fcbc377Syt } else { /* No device exists now */ 51032fcbc377Syt 51042fcbc377Syt if (dev_existed_previously) { 510568d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 510668d33a25Syt "ahci_intr_phyrdy_change: port %d " 51072fcbc377Syt "device link lost", port); 51082fcbc377Syt 51092fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 511068d33a25Syt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 51112fcbc377Syt ahci_portp, port); 51122fcbc377Syt 51132fcbc377Syt /* An existing device is lost. */ 51142fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_NONE; 51152fcbc377Syt 51162fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 51172fcbc377Syt sata_hba_event_notify( 51182fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 51192fcbc377Syt &sdevice, 51202fcbc377Syt SATA_EVNT_LINK_LOST); 51212fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 51222fcbc377Syt } 51232fcbc377Syt } 51242fcbc377Syt 51252fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 51262fcbc377Syt 51272fcbc377Syt return (AHCI_SUCCESS); 51282fcbc377Syt } 51292fcbc377Syt 51302fcbc377Syt /* 513168d33a25Syt * PxIS.UFS - Unknown FIS Error 51322fcbc377Syt * 513368d33a25Syt * This interrupt event means an unknown FIS was received and has been 513468d33a25Syt * copied into system memory. An unknown FIS is not considered an illegal 513568d33a25Syt * FIS, unless the length received is more than 64 bytes. If an unknown 513668d33a25Syt * FIS arrives with length <= 64 bytes, it is posted and the HBA continues 513768d33a25Syt * normal operation. If the unknown FIS is more than 64 bytes, then it 513868d33a25Syt * won't be posted to memory and PxSERR.ERR.P will be set, which is then 513968d33a25Syt * a fatal error. 51402fcbc377Syt * 514168d33a25Syt * PxIS.OFS - Overflow Error 51422fcbc377Syt * 514368d33a25Syt * Command list overflow is defined as software building a command table 514468d33a25Syt * that has fewer total bytes than the transaction given to the device. 514568d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 514668d33a25Syt * will be no room to put the data. 51472fcbc377Syt * 514868d33a25Syt * For an overflow on data read, either PIO or DMA, the HBA will set 514968d33a25Syt * PxIS.OFS, and the HBA will do a best effort to continue, and it's a 515068d33a25Syt * non-fatal error when the HBA can continues. Sometimes, it will cause 515168d33a25Syt * a fatal error and need the software to do something. 51522fcbc377Syt * 515368d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 515468d33a25Syt * DMA and PIO, and it's a fatal error, and a COMRESET is required by 515568d33a25Syt * software to clean up from this serious error. 51562fcbc377Syt * 515768d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 515868d33a25Syt * 515968d33a25Syt * This interrupt event indicates that the HBA encountered an error on 516068d33a25Syt * the Serial ATA interface but was able to continue operation. The kind 516168d33a25Syt * of error usually occurred during a non-Data FIS, and under this condition 516268d33a25Syt * the FIS will be re-transmitted by HBA automatically. 51632fcbc377Syt * 516468d33a25Syt * When the FMA is implemented, there should be a stat structure to 516568d33a25Syt * record how many every kind of error happens. 51662fcbc377Syt */ 51672fcbc377Syt static int 516868d33a25Syt ahci_intr_non_fatal_error(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 516968d33a25Syt uint8_t port, uint32_t intr_status) 51702fcbc377Syt { 51712fcbc377Syt uint32_t port_serror; 517268d33a25Syt #if AHCI_DEBUG 51732fcbc377Syt uint32_t port_cmd_status; 517482263d52Syt uint32_t port_cmd_issue; 517582263d52Syt uint32_t port_sactive; 51762fcbc377Syt int current_slot; 517782263d52Syt uint32_t current_tags; 517868d33a25Syt sata_pkt_t *satapkt; 517938547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 518038547057Sying tian - Beijing China uint32_t cmd_dmacount; 518168d33a25Syt #endif 51822fcbc377Syt 51832fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 51842fcbc377Syt 51852fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 51862fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 51872fcbc377Syt 518868d33a25Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY|AHCIDBG_ERRS, ahci_ctlp, 518968d33a25Syt "ahci_intr_non_fatal_error: port %d, " 51902fcbc377Syt "port_serror = 0x%x", port, port_serror); 51912fcbc377Syt 5192a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 1); 519368d33a25Syt 519468d33a25Syt if (intr_status & AHCI_INTR_STATUS_UFS) { 519568d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 519668d33a25Syt "ahci port %d has unknown FIS error", port); 51972fcbc377Syt 519868d33a25Syt /* Clear the interrupt bit by clearing PxSERR.DIAG.F */ 519968d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 520068d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 520182263d52Syt SERROR_FIS_TYPE); 520268d33a25Syt } 520368d33a25Syt 520468d33a25Syt #if AHCI_DEBUG 520568d33a25Syt if (intr_status & AHCI_INTR_STATUS_OFS) { 520668d33a25Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 520768d33a25Syt "ahci port %d has overflow error", port); 520868d33a25Syt } 520968d33a25Syt 521068d33a25Syt if (intr_status & AHCI_INTR_STATUS_INFS) { 521168d33a25Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 521268d33a25Syt "ahci port %d has interface non fatal error", port); 521368d33a25Syt } 52142fcbc377Syt 52152fcbc377Syt /* 52162fcbc377Syt * Record the error occurred command's slot. 52172fcbc377Syt */ 521882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 521982263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 522082263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 522182263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 522282263d52Syt 522382263d52Syt current_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 522482263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 52252fcbc377Syt 522682263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 522782263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 522882263d52Syt ASSERT(satapkt != NULL); 522982263d52Syt ASSERT(current_slot == 0); 523082263d52Syt } else { 523182263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 523282263d52Syt } 52332fcbc377Syt 523482263d52Syt if (satapkt != NULL) { 523582263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 523682263d52Syt "ahci_intr_non_fatal_error: pending_tags = 0x%x " 523782263d52Syt "cmd 0x%x", ahci_portp->ahciport_pending_tags, 523882263d52Syt satapkt->satapkt_cmd.satacmd_cmd_reg); 52392fcbc377Syt 524082263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 524182263d52Syt "ahci_intr_non_fatal_error: port %d, " 524282263d52Syt "satapkt 0x%p is being processed when error occurs", 524382263d52Syt port, (void *)satapkt); 524438547057Sying tian - Beijing China 524538547057Sying tian - Beijing China /* 524638547057Sying tian - Beijing China * PRD Byte Count field of command header is not 524738547057Sying tian - Beijing China * required to reflect the total number of bytes 524838547057Sying tian - Beijing China * transferred when an overflow occurs, so here 524938547057Sying tian - Beijing China * just log the value. 525038547057Sying tian - Beijing China */ 525138547057Sying tian - Beijing China cmd_dmacount = 525238547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[current_slot]; 525338547057Sying tian - Beijing China if (cmd_dmacount) { 525438547057Sying tian - Beijing China cmd_header = &ahci_portp-> 525538547057Sying tian - Beijing China ahciport_cmd_list[current_slot]; 525638547057Sying tian - Beijing China AHCIDBG3(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 525738547057Sying tian - Beijing China "ahci_intr_non_fatal_error: port %d, " 525838547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 525938547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 526038547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 526138547057Sying tian - Beijing China cmd_dmacount); 526238547057Sying tian - Beijing China } 526382263d52Syt } 5264a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 526582263d52Syt /* 526682263d52Syt * For queued command, list those command which have already 526782263d52Syt * been transmitted to the device and still not completed. 526882263d52Syt */ 526982263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 527082263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 527182263d52Syt 527282263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 527382263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 527482263d52Syt 527582263d52Syt AHCIDBG3(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, ahci_ctlp, 527682263d52Syt "ahci_intr_non_fatal_error: pending_ncq_tags = 0x%x " 527782263d52Syt "port_sactive = 0x%x port_cmd_issue = 0x%x", 527882263d52Syt ahci_portp->ahciport_pending_ncq_tags, 527982263d52Syt port_sactive, port_cmd_issue); 528082263d52Syt 528182263d52Syt current_tags = ahci_portp->ahciport_pending_ncq_tags & 528282263d52Syt port_sactive & ~port_cmd_issue & 528382263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 528482263d52Syt 528582263d52Syt while (current_tags) { 528682263d52Syt current_slot = ddi_ffs(current_tags) - 1; 528782263d52Syt if (current_slot == -1) { 528882263d52Syt goto out; 528982263d52Syt } 529082263d52Syt 529182263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 529282263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, 529382263d52Syt ahci_ctlp, "ahci_intr_non_fatal_error: " 529482263d52Syt "port %d, satapkt 0x%p is outstanding when " 529582263d52Syt "error occurs", port, (void *)satapkt); 529682263d52Syt } 52972fcbc377Syt } 529882263d52Syt out: 52992fcbc377Syt #endif 53002fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 53012fcbc377Syt 53022fcbc377Syt return (AHCI_SUCCESS); 53032fcbc377Syt } 53042fcbc377Syt 53052fcbc377Syt /* 530668d33a25Syt * According to the AHCI spec, the error types include system memory 530768d33a25Syt * errors, interface errors, port multiplier errors, device errors, 530868d33a25Syt * command list overflow, command list underflow, native command 530968d33a25Syt * queuing tag errors and pio data transfer errors. 531068d33a25Syt * 531168d33a25Syt * System memory errors such as target abort, master abort, and parity 531268d33a25Syt * may cause the host to stop, and they are serious errors and needed 531368d33a25Syt * to be recovered with software intervention. When system software 531468d33a25Syt * has given a pointer to the HBA that doesn't exist in physical memory, 531568d33a25Syt * a master/target abort error occurs, and PxIS.HBFS will be set. A 531668d33a25Syt * data error such as CRC or parity occurs, the HBA aborts the transfer 531768d33a25Syt * (if necessary) and PxIS.HBDS will be set. 531868d33a25Syt * 531968d33a25Syt * Interface errors are errors that occur due to electrical issues on 532068d33a25Syt * the interface, or protocol miscommunication between the device and 532168d33a25Syt * HBA, and the respective PxSERR register bit will be set. And PxIS.IFS 532268d33a25Syt * (fatal) or PxIS.INFS (non-fatal) will be set. The conditions that 532368d33a25Syt * causes PxIS.IFS/PxIS.INFS to be set are 532468d33a25Syt * 1. in PxSERR.ERR, P bit is set to '1' 532568d33a25Syt * 2. in PxSERR.DIAG, C or H bit is set to '1' 532668d33a25Syt * 3. PhyRdy drop unexpectly, N bit is set to '1' 532768d33a25Syt * If the error occurred during a non-data FIS, the FIS must be 532868d33a25Syt * retransmitted, and the error is non-fatal and PxIS.INFS is set. If 532968d33a25Syt * the error occurred during a data FIS, the transfer will stop, so 533068d33a25Syt * the error is fatal and PxIS.IFS is set. 533168d33a25Syt * 533268d33a25Syt * When a FIS arrives that updates the taskfile, the HBA checks to see 533368d33a25Syt * if PxTFD.STS.ERR is set. If yes, PxIS.TFES will be set and the HBA 533468d33a25Syt * stops processing any more commands. 53352fcbc377Syt * 533668d33a25Syt * Command list overflow is defined as software building a command table 533768d33a25Syt * that has fewer total bytes than the transaction given to the device. 533868d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 533968d33a25Syt * will be no room to put the data. For an overflow on data read, either 534068d33a25Syt * PIO or DMA, the HBA will set PxIS.OFS, and it's a non-fatal error. 534168d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 534268d33a25Syt * DMA and PIO, and a COMRESET is required by software to clean up from 534368d33a25Syt * this serious error. 53442fcbc377Syt * 534568d33a25Syt * Command list underflow is defined as software building a command 534668d33a25Syt * table that has more total bytes than the transaction given to the 534768d33a25Syt * device. For data writes, both PIO and DMA, the device will detect 534868d33a25Syt * an error and end the transfer. And these errors are most likely going 534968d33a25Syt * to be fatal errors that will cause the port to be restarted. For 535068d33a25Syt * data reads, the HBA updates its PRD byte count, and may be 535168d33a25Syt * able to continue normally, but is not required to. And The HBA is 535268d33a25Syt * not required to detect underflow conditions for native command 535368d33a25Syt * queuing command. 535468d33a25Syt * 535568d33a25Syt * The HBA does not actively check incoming DMA Setup FISes to ensure 535668d33a25Syt * that the PxSACT register bit for that slot is set. Existing error 535768d33a25Syt * mechanisms, such as host bus failure, or bad protocol, are used to 535868d33a25Syt * recover from this case. 535968d33a25Syt * 536068d33a25Syt * In accordance with Serial ATA 1.0a, DATA FISes prior to the final 536168d33a25Syt * DATA FIS must be an integral number of Dwords. If the HBA receives 536268d33a25Syt * a request which is not an integral number of Dwords, the HBA 536368d33a25Syt * set PxSERR.ERR.P to '1', set PxIS.IFS to '1' and stop running until 536468d33a25Syt * software restarts the port. And the HBA ensures that the size 536568d33a25Syt * of the DATA FIS received during a PIO command matches the size in 536668d33a25Syt * the Transfer Cound field of the preceding PIO Setup FIS, if not, the 536768d33a25Syt * HBA sets PxSERR.ERR.P to '1', set PxIS.IFS to '1', and then 536868d33a25Syt * stop running until software restarts the port. 53692fcbc377Syt */ 53702fcbc377Syt /* 537168d33a25Syt * the fatal errors include PxIS.IFS, PxIS.HBDS, PxIS.HBFS and PxIS.TFES. 537268d33a25Syt * 537368d33a25Syt * PxIS.IFS indicates that the hba encountered an error on the serial ata 537468d33a25Syt * interface which caused the transfer to stop. 53752fcbc377Syt * 537668d33a25Syt * PxIS.HBDS indicates that the hba encountered a data error 537768d33a25Syt * (uncorrectable ecc/parity) when reading from or writing to system memory. 537868d33a25Syt * 537968d33a25Syt * PxIS.HBFS indicates that the hba encountered a host bus error that it 538068d33a25Syt * cannot recover from, such as a bad software pointer. 538168d33a25Syt * 538268d33a25Syt * PxIS.TFES is set whenever the status register is updated by the device 538368d33a25Syt * and the error bit (bit 0) is set. 53842fcbc377Syt */ 53852fcbc377Syt static int 538682263d52Syt ahci_intr_fatal_error(ahci_ctl_t *ahci_ctlp, 538782263d52Syt ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 53882fcbc377Syt { 538968d33a25Syt uint32_t port_cmd_status; 53902fcbc377Syt uint32_t port_serror; 539168d33a25Syt uint32_t task_file_status; 539268d33a25Syt int failed_slot; 539382263d52Syt sata_pkt_t *spkt = NULL; 539468d33a25Syt uint8_t err_byte; 539568d33a25Syt ahci_event_arg_t *args; 5396a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 53972fcbc377Syt 53982fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 53992fcbc377Syt 540068d33a25Syt /* 540168d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 540268d33a25Syt * SATA_DTYPE_NONE. 540368d33a25Syt */ 540468d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 540568d33a25Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 540668d33a25Syt "ahci_intr_fatal_error: port %d no device attached, " 540768d33a25Syt "and just return without doing anything", port); 540868d33a25Syt goto out0; 540968d33a25Syt } 54102fcbc377Syt 5411f8a673adSying tian - Beijing China if (intr_status & AHCI_INTR_STATUS_TFES) { 5412f8a673adSying tian - Beijing China task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5413f8a673adSying tian - Beijing China (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 5414f8a673adSying tian - Beijing China AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 5415f8a673adSying tian - Beijing China "ahci_intr_fatal_error: port %d " 5416f8a673adSying tian - Beijing China "task_file_status = 0x%x", port, task_file_status); 5417f8a673adSying tian - Beijing China } 5418f8a673adSying tian - Beijing China 541982263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 542082263d52Syt /* 542182263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 542282263d52Syt * was processing when the error occurred. 542382263d52Syt */ 542482263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 542582263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 542682263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 542782263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 54282fcbc377Syt 542982263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 543068d33a25Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 543182263d52Syt "ahci_intr_fatal_error: spkt 0x%p is being processed when " 543282263d52Syt "fatal error occurred for port %d", spkt, port); 54332fcbc377Syt 543482263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 543582263d52Syt err_byte = (task_file_status & AHCI_TFD_ERR_MASK) 543682263d52Syt >> AHCI_TFD_ERR_SHIFT; 54372fcbc377Syt 543882263d52Syt /* 543982263d52Syt * Won't emit the error message if it is an IDENTIFY 544082263d52Syt * DEVICE command sent to an ATAPI device. 544182263d52Syt */ 544282263d52Syt if ((spkt != NULL) && 544382263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == 544482263d52Syt SATAC_ID_DEVICE) && 544582263d52Syt (err_byte == SATA_ERROR_ABORT)) 544682263d52Syt goto out1; 544782263d52Syt 544882263d52Syt /* 544982263d52Syt * Won't emit the error message if it is an ATAPI PACKET 545082263d52Syt * command 545182263d52Syt */ 545282263d52Syt if ((spkt != NULL) && 545382263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_PACKET)) 545482263d52Syt goto out1; 545582263d52Syt } 545668d33a25Syt } 54572fcbc377Syt 545868d33a25Syt ahci_log_fatal_error_message(ahci_ctlp, port, intr_status); 54592fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 54602fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 5461a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 0); 5462a9440e8dSyt out1: 546368d33a25Syt /* Prepare the argument for the taskq */ 546468d33a25Syt args = ahci_portp->ahciport_event_args; 546568d33a25Syt args->ahciea_ctlp = (void *)ahci_ctlp; 546668d33a25Syt args->ahciea_portp = (void *)ahci_portp; 546768d33a25Syt args->ahciea_event = intr_status; 546868d33a25Syt 546968d33a25Syt /* Start the taskq to handle error recovery */ 5470f8a673adSying tian - Beijing China if ((ddi_taskq_dispatch(ahci_portp->ahciport_event_taskq, 547168d33a25Syt ahci_events_handler, 547268d33a25Syt (void *)args, DDI_NOSLEEP)) != DDI_SUCCESS) { 5473a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci start taskq for event handler " 5474a9440e8dSyt "failed", instance); 547568d33a25Syt } 547668d33a25Syt out0: 54772fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 54782fcbc377Syt 54792fcbc377Syt return (AHCI_SUCCESS); 54802fcbc377Syt } 54812fcbc377Syt 54822fcbc377Syt /* 54832fcbc377Syt * Hot Plug Operation for platforms that support Cold Presence Detect. 54842fcbc377Syt * 54852fcbc377Syt * When set, a device status has changed as detected by the cold presence 54862fcbc377Syt * detect logic. This bit can either be set due to a non-connected port 54872fcbc377Syt * receiving a device, or a connected port having its device removed. 54882fcbc377Syt * This bit is only valid if the port supports cold presence detect as 54892fcbc377Syt * indicated by PxCMD.CPD set to '1'. 54902fcbc377Syt * 549168d33a25Syt * At the moment, this interrupt is not needed and disabled and we just 549268d33a25Syt * log the debug message. 54932fcbc377Syt */ 54942fcbc377Syt static int 54952fcbc377Syt ahci_intr_cold_port_detect(ahci_ctl_t *ahci_ctlp, 54962fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 54972fcbc377Syt { 54982fcbc377Syt uint32_t port_cmd_status; 54992fcbc377Syt sata_device_t sdevice; 55002fcbc377Syt 55012fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 55022fcbc377Syt "ahci_intr_cold_port_detect enter, port %d", port); 55032fcbc377Syt 55042fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 55052fcbc377Syt 55062fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 55072fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 55082fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_CPD)) { 55092fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 55102fcbc377Syt "port %d does not support cold presence detect, so " 55112fcbc377Syt "we just ignore this interrupt", port); 55122fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 55132fcbc377Syt return (AHCI_SUCCESS); 55142fcbc377Syt } 55152fcbc377Syt 55162fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 55172fcbc377Syt "port %d device status has changed", port); 55182fcbc377Syt 55192fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 552009121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 55212fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 55222fcbc377Syt sdevice.satadev_addr.pmport = 0; 55232fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 55242fcbc377Syt 55252fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_CPS) { 55262fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 55272fcbc377Syt "port %d: a device is hot plugged", port); 55282fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 55292fcbc377Syt sata_hba_event_notify( 55302fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 55312fcbc377Syt &sdevice, 55322fcbc377Syt SATA_EVNT_DEVICE_ATTACHED); 55332fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 55342fcbc377Syt 55352fcbc377Syt } else { 55362fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 55372fcbc377Syt "port %d: a device is hot unplugged", port); 55382fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 55392fcbc377Syt sata_hba_event_notify( 55402fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 55412fcbc377Syt &sdevice, 55422fcbc377Syt SATA_EVNT_DEVICE_DETACHED); 55432fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 55442fcbc377Syt } 55452fcbc377Syt 55462fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 55472fcbc377Syt 55482fcbc377Syt return (AHCI_SUCCESS); 55492fcbc377Syt } 55502fcbc377Syt 55512fcbc377Syt /* 55522fcbc377Syt * Enable the interrupts for a particular port. 55532fcbc377Syt * 55542fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 55552fcbc377Syt * is called. 55562fcbc377Syt */ 55572fcbc377Syt static void 5558689d74b0Syt ahci_enable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 55592fcbc377Syt { 55602fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 55612fcbc377Syt "ahci_enable_port_intrs enter, port %d", port); 55622fcbc377Syt 55632fcbc377Syt /* 55642fcbc377Syt * Clear port interrupt status before enabling interrupt 55652fcbc377Syt */ 55662fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 55672fcbc377Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 55682fcbc377Syt AHCI_PORT_INTR_MASK); 55692fcbc377Syt 55702fcbc377Syt /* 55712fcbc377Syt * Clear the pending bit from IS.IPS 55722fcbc377Syt */ 55732fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 55742fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (1 << port)); 55752fcbc377Syt 55762fcbc377Syt /* 55772fcbc377Syt * Enable the following interrupts: 55782fcbc377Syt * Device to Host Register FIS Interrupt (DHRS) 55792fcbc377Syt * PIO Setup FIS Interrupt (PSS) 558082263d52Syt * Set Device Bits Interrupt (SDBS) 55812fcbc377Syt * Unknown FIS Interrupt (UFS) 55822fcbc377Syt * Port Connect Change Status (PCS) 55832fcbc377Syt * PhyRdy Change Status (PRCS) 55842fcbc377Syt * Overflow Status (OFS) 55852fcbc377Syt * Interface Non-fatal Error Status (INFS) 55862fcbc377Syt * Interface Fatal Error Status (IFS) 55872fcbc377Syt * Host Bus Data Error Status (HBDS) 55882fcbc377Syt * Host Bus Fatal Error Status (HBFS) 55892fcbc377Syt * Task File Error Status (TFES) 55902fcbc377Syt */ 55912fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 55922fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 55932fcbc377Syt (AHCI_INTR_STATUS_DHRS | 55942fcbc377Syt AHCI_INTR_STATUS_PSS | 559582263d52Syt AHCI_INTR_STATUS_SDBS | 55962fcbc377Syt AHCI_INTR_STATUS_UFS | 559768d33a25Syt AHCI_INTR_STATUS_DPS | 55982fcbc377Syt AHCI_INTR_STATUS_PCS | 55992fcbc377Syt AHCI_INTR_STATUS_PRCS | 56002fcbc377Syt AHCI_INTR_STATUS_OFS | 56012fcbc377Syt AHCI_INTR_STATUS_INFS | 56022fcbc377Syt AHCI_INTR_STATUS_IFS | 56032fcbc377Syt AHCI_INTR_STATUS_HBDS | 56042fcbc377Syt AHCI_INTR_STATUS_HBFS | 56052fcbc377Syt AHCI_INTR_STATUS_TFES)); 56062fcbc377Syt } 56072fcbc377Syt 56082fcbc377Syt /* 56092fcbc377Syt * Enable interrupts for all the ports. 56102fcbc377Syt * 56112fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 56122fcbc377Syt * is called. 56132fcbc377Syt */ 56142fcbc377Syt static void 56152fcbc377Syt ahci_enable_all_intrs(ahci_ctl_t *ahci_ctlp) 56162fcbc377Syt { 56172fcbc377Syt uint32_t ghc_control; 56182fcbc377Syt 56192fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_enable_all_intrs enter"); 56202fcbc377Syt 56212fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 56222fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 56232fcbc377Syt 56242fcbc377Syt ghc_control |= AHCI_HBA_GHC_IE; 56252fcbc377Syt 56262fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 56272fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 56282fcbc377Syt } 56292fcbc377Syt 56302fcbc377Syt /* 56312fcbc377Syt * Disable interrupts for a particular port. 56322fcbc377Syt * 56332fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 56342fcbc377Syt * is called. 56352fcbc377Syt */ 56362fcbc377Syt static void 5637689d74b0Syt ahci_disable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 56382fcbc377Syt { 56392fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 56402fcbc377Syt "ahci_disable_port_intrs enter, port %d", port); 56412fcbc377Syt 56422fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 56432fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 0); 56442fcbc377Syt } 56452fcbc377Syt 56462fcbc377Syt /* 56472fcbc377Syt * Disable interrupts for the whole HBA. 56482fcbc377Syt * 56492fcbc377Syt * The global bit is cleared, then all interrupt sources from all 56502fcbc377Syt * ports are disabled. 56512fcbc377Syt * 56522fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 56532fcbc377Syt * is called. 56542fcbc377Syt */ 56552fcbc377Syt static void 56562fcbc377Syt ahci_disable_all_intrs(ahci_ctl_t *ahci_ctlp) 56572fcbc377Syt { 56582fcbc377Syt uint32_t ghc_control; 56592fcbc377Syt 56602fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_disable_all_intrs enter"); 56612fcbc377Syt 56622fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 56632fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 56642fcbc377Syt 56652fcbc377Syt ghc_control &= ~ AHCI_HBA_GHC_IE; 56662fcbc377Syt 56672fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 56682fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 56692fcbc377Syt } 56702fcbc377Syt 56712fcbc377Syt /* 5672*2c742e1fSying tian - Beijing China * Handle FIXED or MSI interrupts. 56732fcbc377Syt */ 56742fcbc377Syt /* 5675*2c742e1fSying tian - Beijing China * According to AHCI spec, the HBA may support several interrupt modes: 5676*2c742e1fSying tian - Beijing China * * pin based interrupts (FIXED) 5677*2c742e1fSying tian - Beijing China * * single MSI message interrupts 5678*2c742e1fSying tian - Beijing China * * multiple MSI based message interrupts 5679*2c742e1fSying tian - Beijing China * 5680*2c742e1fSying tian - Beijing China * For pin based interrupts, the software interrupt handler need to check IS 5681*2c742e1fSying tian - Beijing China * register to find out which port has pending interrupts. And then check 5682*2c742e1fSying tian - Beijing China * PxIS register to find out which interrupt events happened on that port. 5683*2c742e1fSying tian - Beijing China * 5684*2c742e1fSying tian - Beijing China * For single MSI message interrupts, MSICAP.MC.MSIE is set with '1', and 5685*2c742e1fSying tian - Beijing China * MSICAP.MC.MME is set with '0'. This mode is similar to pin based interrupts 5686*2c742e1fSying tian - Beijing China * in that software interrupt handler need to check IS register to determine 5687*2c742e1fSying tian - Beijing China * which port triggered the interrupts since it uses a single message for all 5688*2c742e1fSying tian - Beijing China * port interrupts. 5689*2c742e1fSying tian - Beijing China * 5690*2c742e1fSying tian - Beijing China * HBA may optionally support multiple MSI message for better performance. In 5691*2c742e1fSying tian - Beijing China * this mode, each port may have its own interrupt message, and thus generation 5692*2c742e1fSying tian - Beijing China * of interrupts is no longer controlled through the IS register. MSICAP.MC.MMC 5693*2c742e1fSying tian - Beijing China * represents a power-of-2 wrapper on the number of implemented ports, and 5694*2c742e1fSying tian - Beijing China * the mapping of ports to interrupts is done in a 1-1 relationship, up to the 5695*2c742e1fSying tian - Beijing China * maximum number of assigned interrupts. When the number of MSI messages 5696*2c742e1fSying tian - Beijing China * allocated is less than the number requested, then hardware may have two 5697*2c742e1fSying tian - Beijing China * implementation behaviors: 5698*2c742e1fSying tian - Beijing China * * assign each ports its own interrupt and then force all additional 5699*2c742e1fSying tian - Beijing China * ports to share the last interrupt message, and this condition is 5700*2c742e1fSying tian - Beijing China * indicated by clearing GHC.MRSM to '0' 5701*2c742e1fSying tian - Beijing China * * revert to single MSI mode, indicated by setting GHC.MRSM to '1' 5702*2c742e1fSying tian - Beijing China * When multiple-message MSI is enabled, hardware will still set IS register 5703*2c742e1fSying tian - Beijing China * as single message case. And this IS register may be used by software when 5704*2c742e1fSying tian - Beijing China * fewer than the requested number of messages is granted in order to determine 5705*2c742e1fSying tian - Beijing China * which port had the interrupt. 5706*2c742e1fSying tian - Beijing China * 5707*2c742e1fSying tian - Beijing China * Note: The current ahci driver only supports the first two interrupt modes: 5708*2c742e1fSying tian - Beijing China * pin based interrupts and single MSI message interrupts, and the reason 5709*2c742e1fSying tian - Beijing China * is indicated in below code. 57102fcbc377Syt */ 57112fcbc377Syt static int 5712*2c742e1fSying tian - Beijing China ahci_add_intrs(ahci_ctl_t *ahci_ctlp, int intr_type) 57132fcbc377Syt { 57142fcbc377Syt dev_info_t *dip = ahci_ctlp->ahcictl_dip; 57152fcbc377Syt int count, avail, actual; 5716*2c742e1fSying tian - Beijing China int i, rc; 57172fcbc377Syt 5718*2c742e1fSying tian - Beijing China AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 5719*2c742e1fSying tian - Beijing China "ahci_add_intrs enter interrupt type 0x%x", intr_type); 57202fcbc377Syt 57212fcbc377Syt /* get number of interrupts. */ 5722*2c742e1fSying tian - Beijing China rc = ddi_intr_get_nintrs(dip, intr_type, &count); 57232fcbc377Syt if ((rc != DDI_SUCCESS) || (count == 0)) { 57242fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 57252fcbc377Syt "ddi_intr_get_nintrs() failed, " 57262fcbc377Syt "rc %d count %d\n", rc, count); 57272fcbc377Syt return (DDI_FAILURE); 57282fcbc377Syt } 57292fcbc377Syt 57302fcbc377Syt /* get number of available interrupts. */ 5731*2c742e1fSying tian - Beijing China rc = ddi_intr_get_navail(dip, intr_type, &avail); 57322fcbc377Syt if ((rc != DDI_SUCCESS) || (avail == 0)) { 57332fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 57342fcbc377Syt "ddi_intr_get_navail() failed, " 57352fcbc377Syt "rc %d avail %d\n", rc, avail); 57362fcbc377Syt return (DDI_FAILURE); 57372fcbc377Syt } 57382fcbc377Syt 5739689d74b0Syt #if AHCI_DEBUG 57402fcbc377Syt if (avail < count) { 57412fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 5742*2c742e1fSying tian - Beijing China "ddi_intr_get_nintrs returned %d, navail() returned %d", 57432fcbc377Syt count, avail); 57442fcbc377Syt } 5745689d74b0Syt #endif 57462fcbc377Syt 5747*2c742e1fSying tian - Beijing China /* 5748*2c742e1fSying tian - Beijing China * Note: So far Solaris restricts the maximum number of messages for 5749*2c742e1fSying tian - Beijing China * x86 to 2, that is avail is 2, so here we set the count with 1 to 5750*2c742e1fSying tian - Beijing China * force the driver to use single MSI message interrupt. In future if 5751*2c742e1fSying tian - Beijing China * Solaris remove the restriction, then we need to delete the below 5752*2c742e1fSying tian - Beijing China * code and try to use multiple interrupt routine to gain better 5753*2c742e1fSying tian - Beijing China * performance. 5754*2c742e1fSying tian - Beijing China */ 5755*2c742e1fSying tian - Beijing China if ((intr_type == DDI_INTR_TYPE_MSI) && (count > 1)) { 5756*2c742e1fSying tian - Beijing China AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 5757*2c742e1fSying tian - Beijing China "force to use one interrupt routine though the " 5758*2c742e1fSying tian - Beijing China "HBA supports %d interrupt", count); 5759*2c742e1fSying tian - Beijing China count = 1; 5760*2c742e1fSying tian - Beijing China } 5761*2c742e1fSying tian - Beijing China 57622fcbc377Syt /* Allocate an array of interrupt handles. */ 57632fcbc377Syt ahci_ctlp->ahcictl_intr_size = count * sizeof (ddi_intr_handle_t); 57642fcbc377Syt ahci_ctlp->ahcictl_intr_htable = 57652fcbc377Syt kmem_alloc(ahci_ctlp->ahcictl_intr_size, KM_SLEEP); 57662fcbc377Syt 57672fcbc377Syt /* call ddi_intr_alloc(). */ 57682fcbc377Syt rc = ddi_intr_alloc(dip, ahci_ctlp->ahcictl_intr_htable, 5769*2c742e1fSying tian - Beijing China intr_type, 0, count, &actual, DDI_INTR_ALLOC_NORMAL); 57702fcbc377Syt 57712fcbc377Syt if ((rc != DDI_SUCCESS) || (actual == 0)) { 5772*2c742e1fSying tian - Beijing China AHCIDBG4(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 5773*2c742e1fSying tian - Beijing China "ddi_intr_alloc() failed, rc %d count %d actual %d " 5774*2c742e1fSying tian - Beijing China "avail %d\n", rc, count, actual, avail); 57752fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 57762fcbc377Syt ahci_ctlp->ahcictl_intr_size); 57772fcbc377Syt return (DDI_FAILURE); 57782fcbc377Syt } 57792fcbc377Syt 57802fcbc377Syt /* use interrupt count returned */ 5781689d74b0Syt #if AHCI_DEBUG 57822fcbc377Syt if (actual < count) { 57832fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 57842fcbc377Syt "Requested: %d, Received: %d", count, actual); 57852fcbc377Syt } 5786689d74b0Syt #endif 57872fcbc377Syt 57882fcbc377Syt ahci_ctlp->ahcictl_intr_cnt = actual; 57892fcbc377Syt 57902fcbc377Syt /* 5791*2c742e1fSying tian - Beijing China * Get priority for first, assume remaining are all the same. 57922fcbc377Syt */ 57932fcbc377Syt if (ddi_intr_get_pri(ahci_ctlp->ahcictl_intr_htable[0], 57942fcbc377Syt &ahci_ctlp->ahcictl_intr_pri) != DDI_SUCCESS) { 57952fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 57962fcbc377Syt "ddi_intr_get_pri() failed"); 57972fcbc377Syt 57982fcbc377Syt /* Free already allocated intr. */ 5799*2c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 5800*2c742e1fSying tian - Beijing China (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[i]); 58012fcbc377Syt } 58022fcbc377Syt 58032fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 58042fcbc377Syt ahci_ctlp->ahcictl_intr_size); 58052fcbc377Syt return (DDI_FAILURE); 58062fcbc377Syt } 58072fcbc377Syt 58082fcbc377Syt /* Test for high level interrupt. */ 58092fcbc377Syt if (ahci_ctlp->ahcictl_intr_pri >= ddi_intr_get_hilevel_pri()) { 58102fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 5811*2c742e1fSying tian - Beijing China "ahci_add_intrs: Hi level intr not supported"); 58122fcbc377Syt 58132fcbc377Syt /* Free already allocated intr. */ 5814*2c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 5815*2c742e1fSying tian - Beijing China (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[i]); 58162fcbc377Syt } 58172fcbc377Syt 58182fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 58192fcbc377Syt sizeof (ddi_intr_handle_t)); 58202fcbc377Syt 58212fcbc377Syt return (DDI_FAILURE); 58222fcbc377Syt } 58232fcbc377Syt 58242fcbc377Syt /* Call ddi_intr_add_handler(). */ 5825*2c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 5826*2c742e1fSying tian - Beijing China if (ddi_intr_add_handler(ahci_ctlp->ahcictl_intr_htable[i], 58272fcbc377Syt ahci_intr, (caddr_t)ahci_ctlp, NULL) != DDI_SUCCESS) { 58282fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 58292fcbc377Syt "ddi_intr_add_handler() failed"); 58302fcbc377Syt 58312fcbc377Syt /* Free already allocated intr. */ 5832*2c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 58332fcbc377Syt (void) ddi_intr_free( 5834*2c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 58352fcbc377Syt } 58362fcbc377Syt 58372fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 58382fcbc377Syt ahci_ctlp->ahcictl_intr_size); 58392fcbc377Syt return (DDI_FAILURE); 58402fcbc377Syt } 58412fcbc377Syt } 58422fcbc377Syt 5843*2c742e1fSying tian - Beijing China if (ddi_intr_get_cap(ahci_ctlp->ahcictl_intr_htable[0], 5844*2c742e1fSying tian - Beijing China &ahci_ctlp->ahcictl_intr_cap) != DDI_SUCCESS) { 5845*2c742e1fSying tian - Beijing China AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 5846*2c742e1fSying tian - Beijing China "ddi_intr_get_cap() failed"); 58472fcbc377Syt 5848*2c742e1fSying tian - Beijing China /* Free already allocated intr. */ 5849*2c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 5850*2c742e1fSying tian - Beijing China (void) ddi_intr_free( 5851*2c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 5852*2c742e1fSying tian - Beijing China } 5853*2c742e1fSying tian - Beijing China 5854*2c742e1fSying tian - Beijing China kmem_free(ahci_ctlp->ahcictl_intr_htable, 5855*2c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_size); 5856*2c742e1fSying tian - Beijing China return (DDI_FAILURE); 5857*2c742e1fSying tian - Beijing China } 58582fcbc377Syt 58592fcbc377Syt if (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK) { 58602fcbc377Syt /* Call ddi_intr_block_enable() for MSI. */ 58612fcbc377Syt (void) ddi_intr_block_enable(ahci_ctlp->ahcictl_intr_htable, 58622fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 58632fcbc377Syt } else { 5864*2c742e1fSying tian - Beijing China /* Call ddi_intr_enable() for FIXED or MSI non block enable. */ 5865*2c742e1fSying tian - Beijing China for (i = 0; i < ahci_ctlp->ahcictl_intr_cnt; i++) { 58662fcbc377Syt (void) ddi_intr_enable( 5867*2c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 58682fcbc377Syt } 58692fcbc377Syt } 58702fcbc377Syt 58712fcbc377Syt return (DDI_SUCCESS); 58722fcbc377Syt } 58732fcbc377Syt 58742fcbc377Syt /* 58752fcbc377Syt * Removes the registered interrupts irrespective of whether they 58762fcbc377Syt * were legacy or MSI. 58772fcbc377Syt * 58782fcbc377Syt * WARNING!!! The controller interrupts must be disabled before calling 58792fcbc377Syt * this routine. 58802fcbc377Syt */ 58812fcbc377Syt static void 58822fcbc377Syt ahci_rem_intrs(ahci_ctl_t *ahci_ctlp) 58832fcbc377Syt { 58842fcbc377Syt int x; 58852fcbc377Syt 58862fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_rem_intrs entered"); 58872fcbc377Syt 58882fcbc377Syt /* Disable all interrupts. */ 58892fcbc377Syt if ((ahci_ctlp->ahcictl_intr_type == DDI_INTR_TYPE_MSI) && 58902fcbc377Syt (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK)) { 58912fcbc377Syt /* Call ddi_intr_block_disable(). */ 58922fcbc377Syt (void) ddi_intr_block_disable(ahci_ctlp->ahcictl_intr_htable, 58932fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 58942fcbc377Syt } else { 58952fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 58962fcbc377Syt (void) ddi_intr_disable( 58972fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 58982fcbc377Syt } 58992fcbc377Syt } 59002fcbc377Syt 59012fcbc377Syt /* Call ddi_intr_remove_handler(). */ 59022fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 59032fcbc377Syt (void) ddi_intr_remove_handler( 59042fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 59052fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[x]); 59062fcbc377Syt } 59072fcbc377Syt 59082fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, ahci_ctlp->ahcictl_intr_size); 59092fcbc377Syt } 59102fcbc377Syt 59112fcbc377Syt /* 591268d33a25Syt * This routine tries to put port into P:NotRunning state by clearing 591368d33a25Syt * PxCMD.ST. HBA will clear PxCI to 0h, PxSACT to 0h, PxCMD.CCS to 0h 591468d33a25Syt * and PxCMD.CR to '0'. 59152fcbc377Syt * 59162fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 59172fcbc377Syt * is called. 59182fcbc377Syt */ 59192fcbc377Syt static int 592068d33a25Syt ahci_put_port_into_notrunning_state(ahci_ctl_t *ahci_ctlp, 592168d33a25Syt ahci_port_t *ahci_portp, uint8_t port) 59222fcbc377Syt { 59232fcbc377Syt uint32_t port_cmd_status; 59242fcbc377Syt int loop_count; 59252fcbc377Syt 59262fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 592768d33a25Syt "ahci_put_port_into_notrunning_state enter: port %d", port); 59282fcbc377Syt 59292fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 59302fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 59312fcbc377Syt 59322fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_ST; 59332fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 59342fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 59352fcbc377Syt 59362fcbc377Syt /* Wait until PxCMD.CR is cleared */ 59372fcbc377Syt loop_count = 0; 59382fcbc377Syt do { 59392fcbc377Syt port_cmd_status = 59402fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 59412fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 59422fcbc377Syt 59432fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_IDLE) { 59442fcbc377Syt AHCIDBG2(AHCIDBG_INIT, ahci_ctlp, 59452fcbc377Syt "clearing port %d CMD.CR timeout, " 59462fcbc377Syt "port_cmd_status = 0x%x", port, 59472fcbc377Syt port_cmd_status); 59482fcbc377Syt /* 59492fcbc377Syt * We are effectively timing out after 0.5 sec. 59502fcbc377Syt * This value is specified in AHCI spec. 59512fcbc377Syt */ 59522fcbc377Syt break; 59532fcbc377Syt } 59542fcbc377Syt 5955689d74b0Syt /* Wait for 10 millisec */ 595619397407SSherry Moore drv_usecwait(10000); 59572fcbc377Syt } while (port_cmd_status & AHCI_CMD_STATUS_CR); 59582fcbc377Syt 595968d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_STARTED; 59602fcbc377Syt 596168d33a25Syt if (port_cmd_status & AHCI_CMD_STATUS_CR) { 59622fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 596368d33a25Syt "ahci_put_port_into_notrunning_state: failed to clear " 596468d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 596568d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 59662fcbc377Syt return (AHCI_FAILURE); 59672fcbc377Syt } else { 596868d33a25Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 596968d33a25Syt "ahci_put_port_into_notrunning_state: succeeded to clear " 597068d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 597168d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 59722fcbc377Syt return (AHCI_SUCCESS); 59732fcbc377Syt } 59742fcbc377Syt } 59752fcbc377Syt 59762fcbc377Syt /* 597768d33a25Syt * First clear PxCMD.ST, and then check PxTFD. If both PxTFD.STS.BSY 597868d33a25Syt * and PxTFD.STS.DRQ cleared to '0', it means the device is in a 597968d33a25Syt * stable state, then set PxCMD.ST to '1' to start the port directly. 598068d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue a 598168d33a25Syt * COMRESET to the device to put it in an idle state. 598268d33a25Syt * 598368d33a25Syt * The fifth argument returns whether the port reset is involved during 598468d33a25Syt * the process. 598568d33a25Syt * 598668d33a25Syt * The routine will be called under six scenarios: 59872fcbc377Syt * 1. Initialize the port 59882fcbc377Syt * 2. To abort the packet(s) 59892fcbc377Syt * 3. To reset the port 599068d33a25Syt * 4. To activate the port 599168d33a25Syt * 5. Fatal error recovery 599268d33a25Syt * 6. To abort the timeout packet(s) 59932fcbc377Syt * 59942fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 599568d33a25Syt * is called. And ahciport_mutex will be released before the reset 599668d33a25Syt * event is reported to sata module by calling sata_hba_event_notify, 599768d33a25Syt * and then be acquired again later. 599882263d52Syt * 599982263d52Syt * NOTES!!! During this procedure, PxSERR register will be cleared, and 600082263d52Syt * according to the spec, the clearance of three bits will also clear 600182263d52Syt * three interrupt status bits. 600282263d52Syt * 1. PxSERR.DIAG.F will clear PxIS.UFS 600382263d52Syt * 2. PxSERR.DIAG.X will clear PxIS.PCS 600482263d52Syt * 3. PxSERR.DIAG.N will clear PxIS.PRCS 600582263d52Syt * 600682263d52Syt * Among these three interrupt events, the driver needs to take care of 600782263d52Syt * PxIS.PRCS, which is the hot plug event. When the driver found out 600882263d52Syt * a device was unplugged, it will call the interrupt handler. 60092fcbc377Syt */ 60102fcbc377Syt static int 60112fcbc377Syt ahci_restart_port_wait_till_ready(ahci_ctl_t *ahci_ctlp, 601268d33a25Syt ahci_port_t *ahci_portp, uint8_t port, int flag, int *reset_flag) 60132fcbc377Syt { 601482263d52Syt uint32_t port_sstatus; 60152fcbc377Syt uint32_t task_file_status; 60162fcbc377Syt sata_device_t sdevice; 60172fcbc377Syt int rval; 601882263d52Syt int dev_exists_begin = 0; 601982263d52Syt int dev_exists_end = 0; 60202fcbc377Syt 60212fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 60222fcbc377Syt "ahci_restart_port_wait_till_ready: port %d enter", port); 60232fcbc377Syt 6024a9440e8dSyt if ((flag != AHCI_PORT_INIT) && 6025a9440e8dSyt (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE)) 602682263d52Syt dev_exists_begin = 1; 60272fcbc377Syt 602882263d52Syt /* First clear PxCMD.ST */ 602968d33a25Syt rval = ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, 603068d33a25Syt port); 603168d33a25Syt if (rval != AHCI_SUCCESS) 603268d33a25Syt /* 603368d33a25Syt * If PxCMD.CR does not clear within a reasonable time, it 603468d33a25Syt * may assume the interface is in a hung condition and may 603568d33a25Syt * continue with issuing the port reset. 603668d33a25Syt */ 603768d33a25Syt goto reset; 60382fcbc377Syt 603968d33a25Syt /* Then clear PxSERR */ 604068d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 604168d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 604268d33a25Syt AHCI_SERROR_CLEAR_ALL); 60432fcbc377Syt 604482263d52Syt /* Then get PxTFD */ 604568d33a25Syt task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 604668d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 60472fcbc377Syt 604868d33a25Syt /* 604968d33a25Syt * Check whether the device is in a stable status, if yes, 605068d33a25Syt * then start the port directly. However for ahci_tran_dport_reset, 605168d33a25Syt * we may have to perform a port reset. 605268d33a25Syt */ 605368d33a25Syt if (!(task_file_status & (AHCI_TFD_STS_BSY | AHCI_TFD_STS_DRQ)) && 605468d33a25Syt !(flag & AHCI_PORT_RESET)) 60552fcbc377Syt goto out; 60562fcbc377Syt 605768d33a25Syt reset: 605868d33a25Syt /* 605968d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue 606068d33a25Syt * a COMRESET to the device 606168d33a25Syt */ 60622fcbc377Syt rval = ahci_port_reset(ahci_ctlp, ahci_portp, port); 606368d33a25Syt 606468d33a25Syt if (reset_flag != NULL) 606568d33a25Syt *reset_flag = 1; 60662fcbc377Syt 60672fcbc377Syt /* Indicate to the framework that a reset has happened. */ 606882263d52Syt if ((ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) && 606982263d52Syt !(flag & AHCI_RESET_NO_EVENTS_UP)) { 607082263d52Syt /* Set the reset in progress flag */ 607182263d52Syt ahci_portp->ahciport_reset_in_progress = 1; 60722fcbc377Syt 60732fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 607409121340Syt sdevice.satadev_addr.cport = 607509121340Syt ahci_ctlp->ahcictl_port_to_cport[port]; 607668d33a25Syt sdevice.satadev_addr.pmport = 0; 607768d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 60782fcbc377Syt 60792fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 60802fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 60812fcbc377Syt if (ahci_ctlp->ahcictl_sata_hba_tran) { 60822fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 60832fcbc377Syt sata_hba_event_notify( 60842fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 60852fcbc377Syt &sdevice, 60862fcbc377Syt SATA_EVNT_DEVICE_RESET); 60872fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 60882fcbc377Syt } 60892fcbc377Syt 609068d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 609168d33a25Syt "port %d sending event up: SATA_EVNT_RESET", port); 609282263d52Syt } else { 609382263d52Syt ahci_portp->ahciport_reset_in_progress = 0; 60942fcbc377Syt } 609582263d52Syt 60962fcbc377Syt out: 6097a9440e8dSyt /* Start the port if not in initialization phase */ 6098a9440e8dSyt if (flag & AHCI_PORT_INIT) 6099a9440e8dSyt return (rval); 6100a9440e8dSyt 6101a9440e8dSyt (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 610282263d52Syt 610382263d52Syt /* SStatus tells the presence of device. */ 610482263d52Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 610582263d52Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 610682263d52Syt 610782263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 610882263d52Syt dev_exists_end = 1; 610982263d52Syt ASSERT(ahci_portp->ahciport_device_type != SATA_DTYPE_NONE); 611082263d52Syt } 611182263d52Syt 611282263d52Syt /* Check whether a hot plug event happened */ 611382263d52Syt if (dev_exists_begin == 1 && dev_exists_end == 0) { 611482263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 611582263d52Syt "ahci_restart_port_wait_till_ready: port %d " 611682263d52Syt "device is removed", port); 611782263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_NODEV; 611882263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 611982263d52Syt "ahci_restart_port_wait_till_ready: port %d " 612082263d52Syt "AHCI_PORT_FLAG_NODEV flag is set", port); 612182263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 612282263d52Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, port); 612382263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 61242fcbc377Syt } 61252fcbc377Syt 61262fcbc377Syt return (rval); 61272fcbc377Syt } 61282fcbc377Syt 61292fcbc377Syt /* 613068d33a25Syt * This routine may be called under four scenarios: 613168d33a25Syt * a) do the recovery from fatal error 61322fcbc377Syt * b) or we need to timeout some commands 61332fcbc377Syt * c) or we need to abort some commands 61342fcbc377Syt * d) or we need reset device/port/controller 61352fcbc377Syt * 61362fcbc377Syt * In all these scenarios, we need to send any pending unfinished 61372fcbc377Syt * commands up to sata framework. 61382fcbc377Syt * 613968d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 61402fcbc377Syt */ 61412fcbc377Syt static void 61422fcbc377Syt ahci_mop_commands(ahci_ctl_t *ahci_ctlp, 61432fcbc377Syt ahci_port_t *ahci_portp, 61442fcbc377Syt uint32_t slot_status, 61452fcbc377Syt uint32_t failed_tags, 61462fcbc377Syt uint32_t timeout_tags, 61472fcbc377Syt uint32_t aborted_tags, 61482fcbc377Syt uint32_t reset_tags) 61492fcbc377Syt { 615082263d52Syt uint32_t finished_tags = 0; 615182263d52Syt uint32_t unfinished_tags = 0; 61522fcbc377Syt int tmp_slot; 61532fcbc377Syt sata_pkt_t *satapkt; 615482263d52Syt int ncq_cmd_in_progress = 0; 615582263d52Syt int err_retri_cmd_in_progress = 0; 61562fcbc377Syt 61572fcbc377Syt AHCIDBG2(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 61582fcbc377Syt "ahci_mop_commands entered: port: %d slot_status: 0x%x", 6159689d74b0Syt ahci_portp->ahciport_port_num, slot_status); 61602fcbc377Syt 61612fcbc377Syt AHCIDBG4(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 61622fcbc377Syt "ahci_mop_commands: failed_tags: 0x%x, " 61632fcbc377Syt "timeout_tags: 0x%x aborted_tags: 0x%x, " 61642fcbc377Syt "reset_tags: 0x%x", failed_tags, 61652fcbc377Syt timeout_tags, aborted_tags, reset_tags); 61662fcbc377Syt 616782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 616882263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 616982263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 61702fcbc377Syt 617182263d52Syt unfinished_tags = slot_status & 617282263d52Syt AHCI_SLOT_MASK(ahci_ctlp) & 617382263d52Syt ~failed_tags & 617482263d52Syt ~aborted_tags & 617582263d52Syt ~reset_tags & 617682263d52Syt ~timeout_tags; 6177a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 617882263d52Syt ncq_cmd_in_progress = 1; 617982263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 618082263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 618182263d52Syt 618282263d52Syt unfinished_tags = slot_status & 618382263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp) & 618482263d52Syt ~failed_tags & 618582263d52Syt ~aborted_tags & 618682263d52Syt ~reset_tags & 618782263d52Syt ~timeout_tags; 6188a9440e8dSyt } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 61892fcbc377Syt 6190a9440e8dSyt /* 6191a9440e8dSyt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT is 6192a9440e8dSyt * set, it means REQUEST SENSE or READ LOG EXT command doesn't 6193a9440e8dSyt * complete successfully due to one of the following three 6194a9440e8dSyt * conditions: 6195a9440e8dSyt * 6196a9440e8dSyt * 1. Fatal error - failed_tags includes its slot 6197a9440e8dSyt * 2. Timed out - timeout_tags includes its slot 6198a9440e8dSyt * 3. Aborted when hot unplug - aborted_tags includes its 6199a9440e8dSyt * slot 6200a9440e8dSyt * 6201a9440e8dSyt * Please note that the command is always sent down in Slot 0 6202a9440e8dSyt */ 620382263d52Syt err_retri_cmd_in_progress = 1; 6204f8a673adSying tian - Beijing China AHCIDBG2(AHCIDBG_ERRS|AHCIDBG_NCQ, ahci_ctlp, 620582263d52Syt "ahci_mop_commands is called for port %d while " 620682263d52Syt "REQUEST SENSE or READ LOG EXT for error retrieval " 6207f8a673adSying tian - Beijing China "is being executed slot_status = 0x%x", 6208f8a673adSying tian - Beijing China ahci_portp->ahciport_port_num, slot_status); 620968d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress > 1); 621082263d52Syt ASSERT(slot_status == 0x1); 62112fcbc377Syt } 62122fcbc377Syt 621368d33a25Syt /* Send up finished packets with SATA_PKT_COMPLETED */ 62142fcbc377Syt while (finished_tags) { 62152fcbc377Syt tmp_slot = ddi_ffs(finished_tags) - 1; 62162fcbc377Syt if (tmp_slot == -1) { 62172fcbc377Syt break; 62182fcbc377Syt } 62192fcbc377Syt 62202fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 62212fcbc377Syt ASSERT(satapkt != NULL); 62222fcbc377Syt 62232fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_mop_commands: " 62242fcbc377Syt "sending up pkt 0x%p with SATA_PKT_COMPLETED", 62252fcbc377Syt (void *)satapkt); 62262fcbc377Syt 622768d33a25Syt /* 622868d33a25Syt * Cannot fetch the return register content since the port 622968d33a25Syt * was restarted, so the corresponding tag will be set to 623068d33a25Syt * aborted tags. 623168d33a25Syt */ 623268d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 623368d33a25Syt CLEAR_BIT(finished_tags, tmp_slot); 623468d33a25Syt aborted_tags |= tmp_slot; 623568d33a25Syt continue; 623668d33a25Syt } 623768d33a25Syt 623882263d52Syt if (ncq_cmd_in_progress) 623982263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 624082263d52Syt tmp_slot); 62412fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 62422fcbc377Syt CLEAR_BIT(finished_tags, tmp_slot); 62432fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 62442fcbc377Syt 62452fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 62462fcbc377Syt } 62472fcbc377Syt 624868d33a25Syt /* Send up failed packets with SATA_PKT_DEV_ERROR. */ 62492fcbc377Syt while (failed_tags) { 625082263d52Syt if (err_retri_cmd_in_progress) { 625182263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 625282263d52Syt ASSERT(satapkt != NULL); 625382263d52Syt ASSERT(failed_tags == 0x1); 625482263d52Syt 625582263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 625682263d52Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 625782263d52Syt (void *)satapkt); 625882263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 625982263d52Syt break; 626082263d52Syt } 626182263d52Syt 62622fcbc377Syt tmp_slot = ddi_ffs(failed_tags) - 1; 62632fcbc377Syt if (tmp_slot == -1) { 62642fcbc377Syt break; 62652fcbc377Syt } 62662fcbc377Syt 62672fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 62682fcbc377Syt ASSERT(satapkt != NULL); 62692fcbc377Syt 62702fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 62712fcbc377Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 62722fcbc377Syt (void *)satapkt); 62732fcbc377Syt 627482263d52Syt if (ncq_cmd_in_progress) 627582263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 627682263d52Syt tmp_slot); 62772fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 62782fcbc377Syt CLEAR_BIT(failed_tags, tmp_slot); 62792fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 62802fcbc377Syt 62812fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 62822fcbc377Syt } 62832fcbc377Syt 628468d33a25Syt /* Send up timeout packets with SATA_PKT_TIMEOUT. */ 62852fcbc377Syt while (timeout_tags) { 628682263d52Syt if (err_retri_cmd_in_progress) { 628782263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 628882263d52Syt ASSERT(satapkt != NULL); 628982263d52Syt ASSERT(timeout_tags == 0x1); 629082263d52Syt 629182263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 629282263d52Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 629382263d52Syt (void *)satapkt); 629482263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 629582263d52Syt break; 629682263d52Syt } 629782263d52Syt 62982fcbc377Syt tmp_slot = ddi_ffs(timeout_tags) - 1; 62992fcbc377Syt if (tmp_slot == -1) { 63002fcbc377Syt break; 63012fcbc377Syt } 63022fcbc377Syt 63032fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 63042fcbc377Syt ASSERT(satapkt != NULL); 63052fcbc377Syt 63062fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 63072fcbc377Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 63082fcbc377Syt (void *)satapkt); 63092fcbc377Syt 631082263d52Syt if (ncq_cmd_in_progress) 631182263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 631282263d52Syt tmp_slot); 63132fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 63142fcbc377Syt CLEAR_BIT(timeout_tags, tmp_slot); 63152fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 63162fcbc377Syt 63172fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 63182fcbc377Syt } 63192fcbc377Syt 632068d33a25Syt /* Send up aborted packets with SATA_PKT_ABORTED */ 63212fcbc377Syt while (aborted_tags) { 632282263d52Syt if (err_retri_cmd_in_progress) { 632382263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 632482263d52Syt ASSERT(satapkt != NULL); 632582263d52Syt ASSERT(aborted_tags == 0x1); 632682263d52Syt 632782263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 632882263d52Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 632982263d52Syt (void *)satapkt); 633082263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 633182263d52Syt break; 633282263d52Syt } 633382263d52Syt 63342fcbc377Syt tmp_slot = ddi_ffs(aborted_tags) - 1; 63352fcbc377Syt if (tmp_slot == -1) { 63362fcbc377Syt break; 63372fcbc377Syt } 63382fcbc377Syt 63392fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 63402fcbc377Syt ASSERT(satapkt != NULL); 63412fcbc377Syt 63422fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 63432fcbc377Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 63442fcbc377Syt (void *)satapkt); 63452fcbc377Syt 634682263d52Syt if (ncq_cmd_in_progress) 634782263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 634882263d52Syt tmp_slot); 63492fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 63502fcbc377Syt CLEAR_BIT(aborted_tags, tmp_slot); 63512fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 63522fcbc377Syt 63532fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 63542fcbc377Syt } 63552fcbc377Syt 635668d33a25Syt /* Send up reset packets with SATA_PKT_RESET. */ 63572fcbc377Syt while (reset_tags) { 63582fcbc377Syt tmp_slot = ddi_ffs(reset_tags) - 1; 63592fcbc377Syt if (tmp_slot == -1) { 63602fcbc377Syt break; 63612fcbc377Syt } 63622fcbc377Syt 63632fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 63642fcbc377Syt ASSERT(satapkt != NULL); 63652fcbc377Syt 63662fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 63672fcbc377Syt "sending up pkt 0x%p with SATA_PKT_RESET", 63682fcbc377Syt (void *)satapkt); 63692fcbc377Syt 637082263d52Syt if (ncq_cmd_in_progress) 637182263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 637282263d52Syt tmp_slot); 63732fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 63742fcbc377Syt CLEAR_BIT(reset_tags, tmp_slot); 63752fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 63762fcbc377Syt 63772fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 63782fcbc377Syt } 63792fcbc377Syt 638068d33a25Syt /* Send up unfinished packets with SATA_PKT_RESET */ 63812fcbc377Syt while (unfinished_tags) { 63822fcbc377Syt tmp_slot = ddi_ffs(unfinished_tags) - 1; 63832fcbc377Syt if (tmp_slot == -1) { 63842fcbc377Syt break; 63852fcbc377Syt } 63862fcbc377Syt 63872fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 63882fcbc377Syt ASSERT(satapkt != NULL); 63892fcbc377Syt 63902fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 639168d33a25Syt "sending up pkt 0x%p with SATA_PKT_RESET", 63922fcbc377Syt (void *)satapkt); 63932fcbc377Syt 639482263d52Syt if (ncq_cmd_in_progress) 639582263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 639682263d52Syt tmp_slot); 63972fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 63982fcbc377Syt CLEAR_BIT(unfinished_tags, tmp_slot); 63992fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 64002fcbc377Syt 640168d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 64022fcbc377Syt } 64032fcbc377Syt 640468d33a25Syt ahci_portp->ahciport_mop_in_progress--; 640568d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress >= 0); 64062fcbc377Syt 640768d33a25Syt if (ahci_portp->ahciport_mop_in_progress == 0) 640868d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_MOPPING; 640968d33a25Syt } 64102fcbc377Syt 641182263d52Syt /* 641282263d52Syt * This routine is going to first request a READ LOG EXT sata pkt from sata 641382263d52Syt * module, and then deliver it to the HBA to get the ncq failure context. 641482263d52Syt * The return value is the exactly failed tags. 641582263d52Syt */ 641682263d52Syt static uint32_t 641782263d52Syt ahci_get_rdlogext_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 641882263d52Syt uint8_t port) 641982263d52Syt { 642082263d52Syt sata_device_t sdevice; 642182263d52Syt sata_pkt_t *rdlog_spkt, *spkt; 642282263d52Syt ddi_dma_handle_t buf_dma_handle; 642382263d52Syt int loop_count; 642482263d52Syt int rval; 642582263d52Syt int failed_slot; 642682263d52Syt uint32_t failed_tags = 0; 642782263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page; 642882263d52Syt 642982263d52Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_NCQ, ahci_ctlp, 643082263d52Syt "ahci_get_rdlogext_data enter: port %d", port); 643182263d52Syt 643282263d52Syt /* Prepare the sdevice data */ 643382263d52Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 643482263d52Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 643582263d52Syt 643682263d52Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 643782263d52Syt sdevice.satadev_addr.pmport = 0; 643882263d52Syt 643982263d52Syt /* 644082263d52Syt * Call the sata hba interface to get a rdlog spkt 644182263d52Syt */ 644282263d52Syt loop_count = 0; 644382263d52Syt loop: 644482263d52Syt rdlog_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 644582263d52Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_NCQ); 644682263d52Syt if (rdlog_spkt == NULL) { 644782263d52Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 644882263d52Syt /* Sleep for a while */ 644982263d52Syt delay(AHCI_10MS_TICKS); 645082263d52Syt goto loop; 645182263d52Syt } 645282263d52Syt /* Timed out after 1s */ 645382263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 645482263d52Syt "failed to get rdlog spkt for port %d", port); 645582263d52Syt return (failed_tags); 645682263d52Syt } 645782263d52Syt 645882263d52Syt ASSERT(rdlog_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 645982263d52Syt 646082263d52Syt /* 646182263d52Syt * This flag is used to handle the specific error recovery when the 646282263d52Syt * READ LOG EXT command gets a failure (fatal error or time-out). 646382263d52Syt */ 646482263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RDLOGEXT; 646582263d52Syt 646682263d52Syt /* 646782263d52Syt * This start is not supposed to fail because after port is restarted, 646882263d52Syt * the whole command list is empty. 646982263d52Syt */ 647082263d52Syt ahci_portp->ahciport_err_retri_pkt = rdlog_spkt; 647182263d52Syt (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, port, rdlog_spkt); 647282263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 647382263d52Syt 647482263d52Syt /* Remove the flag after READ LOG EXT command is completed */ 647582263d52Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RDLOGEXT; 647682263d52Syt 647782263d52Syt if (rdlog_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 647882263d52Syt /* Update the request log data */ 647982263d52Syt buf_dma_handle = *(ddi_dma_handle_t *) 648082263d52Syt (rdlog_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 648182263d52Syt rval = ddi_dma_sync(buf_dma_handle, 0, 0, 648282263d52Syt DDI_DMA_SYNC_FORKERNEL); 648382263d52Syt if (rval == DDI_SUCCESS) { 648482263d52Syt ncq_err_page = 648582263d52Syt (struct sata_ncq_error_recovery_page *)rdlog_spkt-> 648682263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr; 648782263d52Syt 648882263d52Syt /* Get the failed tag */ 648982263d52Syt failed_slot = ncq_err_page->ncq_tag; 649082263d52Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, 649182263d52Syt "ahci_get_rdlogext_data: port %d " 649282263d52Syt "failed slot %d", port, failed_slot); 649382263d52Syt if (failed_slot & NQ) { 649482263d52Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 649582263d52Syt "the failed slot is not a valid tag"); 649682263d52Syt goto out; 649782263d52Syt } 649882263d52Syt 649982263d52Syt failed_slot &= NCQ_TAG_MASK; 650082263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 650182263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 650282263d52Syt "ahci_get_rdlogext_data: failed spkt 0x%p", 650382263d52Syt (void *)spkt); 650482263d52Syt if (spkt == NULL) { 650582263d52Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 650682263d52Syt "the failed slot spkt is NULL") 650782263d52Syt goto out; 650882263d52Syt } 650982263d52Syt 651082263d52Syt failed_tags = 0x1 << failed_slot; 651182263d52Syt 651282263d52Syt /* Fill out the error context */ 651382263d52Syt ahci_copy_ncq_err_page(&spkt->satapkt_cmd, 651482263d52Syt ncq_err_page); 651582263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 651682263d52Syt &spkt->satapkt_device); 651782263d52Syt } 651882263d52Syt } 651982263d52Syt out: 652082263d52Syt sata_free_error_retrieval_pkt(rdlog_spkt); 652182263d52Syt 652282263d52Syt return (failed_tags); 652382263d52Syt } 652482263d52Syt 652568d33a25Syt /* 652668d33a25Syt * This routine is going to first request a REQUEST SENSE sata pkt from sata 652768d33a25Syt * module, and then deliver it to the HBA to get the sense data and copy 652868d33a25Syt * the sense data back to the orignal failed sata pkt, and free the REQUEST 652968d33a25Syt * SENSE sata pkt later. 653068d33a25Syt */ 653168d33a25Syt static void 653268d33a25Syt ahci_get_rqsense_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 653368d33a25Syt uint8_t port, sata_pkt_t *spkt) 653468d33a25Syt { 653568d33a25Syt sata_device_t sdevice; 653668d33a25Syt sata_pkt_t *rs_spkt; 653768d33a25Syt sata_cmd_t *sata_cmd; 653868d33a25Syt ddi_dma_handle_t buf_dma_handle; 653968d33a25Syt int loop_count; 654068d33a25Syt #if AHCI_DEBUG 654168d33a25Syt struct scsi_extended_sense *rqsense; 654268d33a25Syt #endif 654368d33a25Syt 654468d33a25Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 654568d33a25Syt "ahci_get_rqsense_data enter: port %d", port); 654668d33a25Syt 654768d33a25Syt /* Prepare the sdevice data */ 654868d33a25Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 654968d33a25Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 655068d33a25Syt 655168d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 655268d33a25Syt sdevice.satadev_addr.pmport = 0; 655368d33a25Syt 655468d33a25Syt sata_cmd = &spkt->satapkt_cmd; 655568d33a25Syt 655668d33a25Syt /* 655768d33a25Syt * Call the sata hba interface to get a rs spkt 655868d33a25Syt */ 655968d33a25Syt loop_count = 0; 656068d33a25Syt loop: 656168d33a25Syt rs_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 656268d33a25Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_ATAPI); 656368d33a25Syt if (rs_spkt == NULL) { 656468d33a25Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 656568d33a25Syt /* Sleep for a while */ 656668d33a25Syt delay(AHCI_10MS_TICKS); 656768d33a25Syt goto loop; 656868d33a25Syt 656968d33a25Syt } 657068d33a25Syt /* Timed out after 1s */ 657182263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 657268d33a25Syt "failed to get rs spkt for port %d", port); 657368d33a25Syt return; 657468d33a25Syt } 657568d33a25Syt 657668d33a25Syt ASSERT(rs_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 657768d33a25Syt 657868d33a25Syt /* 657968d33a25Syt * This flag is used to handle the specific error recovery when the 658082263d52Syt * REQUEST SENSE command gets a faiure (fatal error or time-out). 658168d33a25Syt */ 658268d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RQSENSE; 658368d33a25Syt 658468d33a25Syt /* 658568d33a25Syt * This start is not supposed to fail because after port is restarted, 658682263d52Syt * the whole command list is empty. 658768d33a25Syt */ 658882263d52Syt ahci_portp->ahciport_err_retri_pkt = rs_spkt; 658982263d52Syt (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, port, rs_spkt); 659082263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 659168d33a25Syt 659268d33a25Syt /* Remove the flag after REQUEST SENSE command is completed */ 659368d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RQSENSE; 659468d33a25Syt 659568d33a25Syt if (rs_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 659668d33a25Syt /* Update the request sense data */ 659768d33a25Syt buf_dma_handle = *(ddi_dma_handle_t *) 659868d33a25Syt (rs_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 659982263d52Syt (void) ddi_dma_sync(buf_dma_handle, 0, 0, 660068d33a25Syt DDI_DMA_SYNC_FORKERNEL); 660182263d52Syt /* Copy the request sense data */ 660282263d52Syt bcopy(rs_spkt-> 660382263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr, 660482263d52Syt &sata_cmd->satacmd_rqsense, 660582263d52Syt SATA_ATAPI_MIN_RQSENSE_LEN); 660668d33a25Syt #if AHCI_DEBUG 660782263d52Syt rqsense = (struct scsi_extended_sense *) 660882263d52Syt sata_cmd->satacmd_rqsense; 660982263d52Syt 661082263d52Syt /* Dump the sense data */ 661182263d52Syt AHCIDBG0(AHCIDBG_SENSEDATA, ahci_ctlp, "\n"); 661282263d52Syt AHCIDBG2(AHCIDBG_SENSEDATA, ahci_ctlp, 661382263d52Syt "Sense data for satapkt %p ATAPI cmd 0x%x", 661482263d52Syt spkt, sata_cmd->satacmd_acdb[0]); 661582263d52Syt AHCIDBG5(AHCIDBG_SENSEDATA, ahci_ctlp, 661682263d52Syt " es_code 0x%x es_class 0x%x " 661782263d52Syt "es_key 0x%x es_add_code 0x%x " 661882263d52Syt "es_qual_code 0x%x", 661982263d52Syt rqsense->es_code, rqsense->es_class, 662082263d52Syt rqsense->es_key, rqsense->es_add_code, 662182263d52Syt rqsense->es_qual_code); 662268d33a25Syt #endif 662368d33a25Syt } 662468d33a25Syt 662568d33a25Syt sata_free_error_retrieval_pkt(rs_spkt); 66262fcbc377Syt } 66272fcbc377Syt 66282fcbc377Syt /* 66292fcbc377Syt * Fatal errors will cause the HBA to enter the ERR: Fatal state. To recover, 663068d33a25Syt * the port must be restarted. When the HBA detects thus error, it may try 663168d33a25Syt * to abort a transfer. And if the transfer was aborted, the device is 663268d33a25Syt * expected to send a D2H Register FIS with PxTFD.STS.ERR set to '1' and both 663368d33a25Syt * PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0'. Then system software knows 663468d33a25Syt * that the device is in a stable status and transfers may be restarted without 663568d33a25Syt * issuing a COMRESET to the device. If PxTFD.STS.BSY or PxTFD.STS.DRQ is set, 663668d33a25Syt * then the software will send the COMRESET to do the port reset. 663768d33a25Syt * 663868d33a25Syt * Software should perform the appropriate error recovery actions based on 663968d33a25Syt * whether non-queued commands were being issued or natived command queuing 664068d33a25Syt * commands were being issued. 664168d33a25Syt * 664268d33a25Syt * And software will complete the command that had the error with error mark 664368d33a25Syt * to higher level software. 66442fcbc377Syt * 66452fcbc377Syt * Fatal errors include the following: 664638547057Sying tian - Beijing China * PxIS.IFS - Interface Fatal Error Status 664738547057Sying tian - Beijing China * PxIS.HBDS - Host Bus Data Error Status 664838547057Sying tian - Beijing China * PxIS.HBFS - Host Bus Fatal Error Status 66492fcbc377Syt * PxIS.TFES - Task File Error Status 66502fcbc377Syt * 66512fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 66522fcbc377Syt */ 665368d33a25Syt static void 665468d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctl_t *ahci_ctlp, 665582263d52Syt ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 66562fcbc377Syt { 665782263d52Syt uint32_t port_cmd_status; 665882263d52Syt uint32_t slot_status = 0; 665968d33a25Syt uint32_t failed_tags = 0; 666068d33a25Syt int failed_slot; 666168d33a25Syt int reset_flag = 0; 666268d33a25Syt ahci_fis_d2h_register_t *ahci_rcvd_fisp; 666382263d52Syt sata_cmd_t *sata_cmd = NULL; 666482263d52Syt sata_pkt_t *spkt = NULL; 666538547057Sying tian - Beijing China #if AHCI_DEBUG 666638547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 666738547057Sying tian - Beijing China #endif 66682fcbc377Syt 66692fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 667068d33a25Syt "ahci_fatal_error_recovery_handler enter: port %d", port); 66712fcbc377Syt 667282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 667382263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 66742fcbc377Syt 667582263d52Syt /* Read PxCI to see which commands are still outstanding */ 667682263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 667782263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 66782fcbc377Syt 667982263d52Syt /* 668082263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 668182263d52Syt * was processing when the error occurred. 668282263d52Syt */ 668382263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 668482263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 668582263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 668682263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 66872fcbc377Syt 668882263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 668982263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 669082263d52Syt ASSERT(spkt != NULL); 669182263d52Syt } else { 669282263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 669382263d52Syt if (spkt == NULL) { 669482263d52Syt /* May happen when interface errors occur? */ 669582263d52Syt goto next; 669682263d52Syt } 669782263d52Syt } 66982fcbc377Syt 669938547057Sying tian - Beijing China #if AHCI_DEBUG 670038547057Sying tian - Beijing China /* 670138547057Sying tian - Beijing China * Debugging purpose... 670238547057Sying tian - Beijing China */ 670338547057Sying tian - Beijing China if (ahci_portp->ahciport_prd_bytecounts[failed_slot]) { 670438547057Sying tian - Beijing China cmd_header = 670538547057Sying tian - Beijing China &ahci_portp->ahciport_cmd_list[failed_slot]; 670638547057Sying tian - Beijing China AHCIDBG3(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 670738547057Sying tian - Beijing China "ahci_fatal_error_recovery_handler: port %d, " 670838547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 670938547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 671038547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 671138547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[failed_slot]); 671238547057Sying tian - Beijing China } 671338547057Sying tian - Beijing China #endif 671438547057Sying tian - Beijing China 671582263d52Syt sata_cmd = &spkt->satapkt_cmd; 67162fcbc377Syt 671782263d52Syt /* Fill out the status and error registers for PxIS.TFES */ 671882263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 671982263d52Syt ahci_rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 672082263d52Syt ahcirf_d2h_register_fis); 672182263d52Syt 672282263d52Syt /* Copy the error context back to the sata_cmd */ 672382263d52Syt ahci_copy_err_cnxt(sata_cmd, ahci_rcvd_fisp); 672482263d52Syt } 67252fcbc377Syt 672682263d52Syt /* The failed command must be one of the outstanding commands */ 672782263d52Syt failed_tags = 0x1 << failed_slot; 672882263d52Syt ASSERT(failed_tags & slot_status); 672982263d52Syt 673082263d52Syt /* Update the sata registers, especially PxSERR register */ 673182263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 673282263d52Syt &spkt->satapkt_device); 67332fcbc377Syt 6734a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 673582263d52Syt /* Read PxSACT to see which commands are still outstanding */ 673682263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 673782263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 673882263d52Syt } 673968d33a25Syt next: 67402fcbc377Syt 674168d33a25Syt #if AHCI_DEBUG 674268d33a25Syt /* 674382263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 674482263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 674582263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 674682263d52Syt * recovery process. At this time, the only outstanding command is 674782263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 674868d33a25Syt */ 674982263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 67502fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 675168d33a25Syt "ahci_fatal_error_recovery_handler: port %d REQUEST SENSE " 675282263d52Syt "command or READ LOG EXT command for error data retrieval " 675382263d52Syt "failed", port); 675482263d52Syt ASSERT(slot_status == 0x1); 6755f8a673adSying tian - Beijing China ASSERT(failed_slot == 0); 675682263d52Syt ASSERT(spkt->satapkt_cmd.satacmd_acdb[0] == 675782263d52Syt SCMD_REQUEST_SENSE || 675882263d52Syt spkt->satapkt_cmd.satacmd_cmd_reg == 675982263d52Syt SATAC_READ_LOG_EXT); 67602fcbc377Syt } 676168d33a25Syt #endif 67622fcbc377Syt 676368d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 676468d33a25Syt ahci_portp->ahciport_mop_in_progress++; 67652fcbc377Syt 676668d33a25Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 676768d33a25Syt port, NULL, &reset_flag); 67682fcbc377Syt 676968d33a25Syt /* 677068d33a25Syt * Won't retrieve error information: 677168d33a25Syt * 1. Port reset was involved to recover 677282263d52Syt * 2. Device is gone 677368d33a25Syt * 3. IDENTIFY DEVICE command sent to ATAPI device 677482263d52Syt * 4. REQUEST SENSE or READ LOG EXT command during error recovery 677568d33a25Syt */ 677682263d52Syt if (reset_flag || 677782263d52Syt ahci_portp->ahciport_device_type == SATA_DTYPE_NONE || 677882263d52Syt spkt && spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_ID_DEVICE || 677982263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 678068d33a25Syt goto out; 67812fcbc377Syt 678282263d52Syt /* 678382263d52Syt * Deliver READ LOG EXT to gather information about the error when 678482263d52Syt * a COMRESET has not been performed as part of the error recovery 678582263d52Syt * during NCQ command processing. 678682263d52Syt */ 678782263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 678882263d52Syt failed_tags = ahci_get_rdlogext_data(ahci_ctlp, 678982263d52Syt ahci_portp, port); 679082263d52Syt goto out; 679182263d52Syt } 679282263d52Syt 679368d33a25Syt /* 679468d33a25Syt * Deliver REQUEST SENSE for ATAPI command to gather information about 679568d33a25Syt * the error when a COMRESET has not been performed as part of the 679668d33a25Syt * error recovery. 679768d33a25Syt */ 679838547057Sying tian - Beijing China if (spkt && ahci_portp->ahciport_device_type == SATA_DTYPE_ATAPI) 679968d33a25Syt ahci_get_rqsense_data(ahci_ctlp, ahci_portp, port, spkt); 680068d33a25Syt out: 680182263d52Syt AHCIDBG5(AHCIDBG_ERRS, ahci_ctlp, 680238547057Sying tian - Beijing China "ahci_fatal_error_recovery_handler: port %d fatal error " 680382263d52Syt "occurred slot_status = 0x%x, pending_tags = 0x%x, " 680482263d52Syt "pending_ncq_tags = 0x%x failed_tags = 0x%x", 680582263d52Syt port, slot_status, ahci_portp->ahciport_pending_tags, 680682263d52Syt ahci_portp->ahciport_pending_ncq_tags, failed_tags); 680782263d52Syt 68082fcbc377Syt ahci_mop_commands(ahci_ctlp, 68092fcbc377Syt ahci_portp, 681082263d52Syt slot_status, 68112fcbc377Syt failed_tags, /* failed tags */ 68122fcbc377Syt 0, /* timeout tags */ 68132fcbc377Syt 0, /* aborted tags */ 68142fcbc377Syt 0); /* reset tags */ 681568d33a25Syt } 681668d33a25Syt 681768d33a25Syt /* 681868d33a25Syt * Handle events - fatal error recovery 681968d33a25Syt */ 682068d33a25Syt static void 682168d33a25Syt ahci_events_handler(void *args) 682268d33a25Syt { 682368d33a25Syt ahci_event_arg_t *ahci_event_arg; 682468d33a25Syt ahci_ctl_t *ahci_ctlp; 682568d33a25Syt ahci_port_t *ahci_portp; 682668d33a25Syt uint32_t event; 682768d33a25Syt uint8_t port; 682868d33a25Syt 682968d33a25Syt ahci_event_arg = (ahci_event_arg_t *)args; 683082263d52Syt 683168d33a25Syt ahci_ctlp = ahci_event_arg->ahciea_ctlp; 683268d33a25Syt ahci_portp = ahci_event_arg->ahciea_portp; 683368d33a25Syt event = ahci_event_arg->ahciea_event; 683468d33a25Syt port = ahci_portp->ahciport_port_num; 683568d33a25Syt 6836f8a673adSying tian - Beijing China AHCIDBG2(AHCIDBG_ENTRY|AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 683782263d52Syt "ahci_events_handler enter: port %d intr_status = 0x%x", 683882263d52Syt port, event); 683968d33a25Syt 68402fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 68412fcbc377Syt 684268d33a25Syt /* 684368d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 684468d33a25Syt * SATA_DTYPE_NONE. 684568d33a25Syt */ 684668d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 684768d33a25Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 684868d33a25Syt "ahci_events_handler: port %d no device attached, " 684968d33a25Syt "and just return without doing anything", port); 685068d33a25Syt goto out; 685168d33a25Syt } 685268d33a25Syt 685368d33a25Syt if (event & (AHCI_INTR_STATUS_IFS | 685468d33a25Syt AHCI_INTR_STATUS_HBDS | 685568d33a25Syt AHCI_INTR_STATUS_HBFS | 685668d33a25Syt AHCI_INTR_STATUS_TFES)) 685768d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctlp, ahci_portp, 685882263d52Syt port, event); 685968d33a25Syt 686068d33a25Syt out: 686168d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 68622fcbc377Syt } 68632fcbc377Syt 68642fcbc377Syt /* 686568d33a25Syt * ahci_watchdog_handler() and ahci_do_sync_start will call us if they 686668d33a25Syt * detect there are some commands which are timed out. 68672fcbc377Syt */ 68682fcbc377Syt static void 68692fcbc377Syt ahci_timeout_pkts(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 687082263d52Syt uint8_t port, uint32_t tmp_timeout_tags) 68712fcbc377Syt { 687282263d52Syt uint32_t slot_status = 0; 687382263d52Syt uint32_t finished_tags = 0; 687482263d52Syt uint32_t timeout_tags = 0; 68752fcbc377Syt 687668d33a25Syt AHCIDBG1(AHCIDBG_TIMEOUT|AHCIDBG_ENTRY, ahci_ctlp, 687768d33a25Syt "ahci_timeout_pkts enter: port %d", port); 68782fcbc377Syt 68792fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 68802fcbc377Syt 688182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 688282263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 688382263d52Syt /* Read PxCI to see which commands are still outstanding */ 688482263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 688582263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 6886a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 688782263d52Syt /* Read PxSACT to see which commands are still outstanding */ 688882263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 688982263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 689082263d52Syt } 689168d33a25Syt 689268d33a25Syt #if AHCI_DEBUG 68932fcbc377Syt /* 689482263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 689582263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 689682263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 689782263d52Syt * recovery process. At this time, the only outstanding command is 689882263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 68992fcbc377Syt */ 690082263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 690182263d52Syt AHCIDBG4(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, ahci_ctlp, 690268d33a25Syt "ahci_timeout_pkts called while REQUEST SENSE " 690382263d52Syt "command or READ LOG EXT command for error recovery " 690482263d52Syt "timed out timeout_tags = 0x%x, slot_status = 0x%x, " 690582263d52Syt "pending_tags = 0x%x, pending_ncq_tags = 0x%x", 690682263d52Syt tmp_timeout_tags, slot_status, 690782263d52Syt ahci_portp->ahciport_pending_tags, 690882263d52Syt ahci_portp->ahciport_pending_ncq_tags); 690982263d52Syt ASSERT(slot_status == 0x1); 69102fcbc377Syt } 691168d33a25Syt #endif 69122fcbc377Syt 691368d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 691468d33a25Syt ahci_portp->ahciport_mop_in_progress++; 69152fcbc377Syt 69162fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 691768d33a25Syt port, NULL, NULL); 691868d33a25Syt 69192fcbc377Syt /* 69202fcbc377Syt * Re-identify timeout tags because some previously checked commands 69212fcbc377Syt * could already complete. 69222fcbc377Syt */ 692382263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 692482263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 692582263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 692682263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 692782263d52Syt 692882263d52Syt AHCIDBG5(AHCIDBG_TIMEOUT, ahci_ctlp, 692982263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 693082263d52Syt "timeout_tags = 0x%x, port_cmd_issue = 0x%x, " 693182263d52Syt "pending_tags = 0x%x ", 693282263d52Syt port, finished_tags, timeout_tags, 693382263d52Syt slot_status, ahci_portp->ahciport_pending_tags); 6934a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 693582263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 693682263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 693782263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 693882263d52Syt 693982263d52Syt AHCIDBG5(AHCIDBG_TIMEOUT|AHCIDBG_NCQ, ahci_ctlp, 694082263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 694182263d52Syt "timeout_tags = 0x%x, port_sactive = 0x%x, " 694282263d52Syt "pending_ncq_tags = 0x%x ", 694382263d52Syt port, finished_tags, timeout_tags, 694482263d52Syt slot_status, ahci_portp->ahciport_pending_ncq_tags); 6945a9440e8dSyt } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 694682263d52Syt timeout_tags = tmp_timeout_tags; 694782263d52Syt } 69482fcbc377Syt 69492fcbc377Syt ahci_mop_commands(ahci_ctlp, 69502fcbc377Syt ahci_portp, 695182263d52Syt slot_status, 69522fcbc377Syt 0, /* failed tags */ 69532fcbc377Syt timeout_tags, /* timeout tags */ 69542fcbc377Syt 0, /* aborted tags */ 69552fcbc377Syt 0); /* reset tags */ 695668d33a25Syt 695768d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 69582fcbc377Syt } 69592fcbc377Syt 69602fcbc377Syt /* 69612fcbc377Syt * Watchdog handler kicks in every 5 seconds to timeout any commands pending 69622fcbc377Syt * for long time. 69632fcbc377Syt */ 69642fcbc377Syt static void 69652fcbc377Syt ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp) 69662fcbc377Syt { 69672fcbc377Syt ahci_port_t *ahci_portp; 696868d33a25Syt sata_pkt_t *spkt; 696982263d52Syt uint32_t pending_tags; 697082263d52Syt uint32_t timeout_tags; 697168d33a25Syt uint32_t port_cmd_status; 697282263d52Syt uint32_t port_sactive; 69732fcbc377Syt uint8_t port; 69742fcbc377Syt int tmp_slot; 697568d33a25Syt int current_slot; 697682263d52Syt uint32_t current_tags; 6977a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 69782fcbc377Syt /* max number of cycles this packet should survive */ 69792fcbc377Syt int max_life_cycles; 69802fcbc377Syt 69812fcbc377Syt /* how many cycles this packet survived so far */ 69822fcbc377Syt int watched_cycles; 69832fcbc377Syt 69842fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 69852fcbc377Syt 69862fcbc377Syt AHCIDBG0(AHCIDBG_TIMEOUT|AHCIDBG_ENTRY, ahci_ctlp, 69872fcbc377Syt "ahci_watchdog_handler entered"); 69882fcbc377Syt 69892fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 69902fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 69912fcbc377Syt continue; 69922fcbc377Syt } 69932fcbc377Syt 69942fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 69952fcbc377Syt 69962fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 69972fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 69982fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 69992fcbc377Syt continue; 70002fcbc377Syt } 70012fcbc377Syt 700268d33a25Syt /* Skip the check for those ports in error recovery */ 700382263d52Syt if ((ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) && 700482263d52Syt !(ERR_RETRI_CMD_IN_PROGRESS(ahci_portp))) { 700568d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 700668d33a25Syt continue; 700768d33a25Syt } 700868d33a25Syt 700982263d52Syt pending_tags = 0; 701068d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 701168d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 701268d33a25Syt 701382263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 701482263d52Syt current_slot = 0; 701582263d52Syt pending_tags = 0x1; 7016a9440e8dSyt } else if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 701782263d52Syt current_slot = 701882263d52Syt (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 701982263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 702082263d52Syt pending_tags = ahci_portp->ahciport_pending_tags; 7021a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 702282263d52Syt port_sactive = ddi_get32( 702382263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 702482263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 702582263d52Syt current_tags = port_sactive & 702682263d52Syt ~port_cmd_status & 702782263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 702882263d52Syt pending_tags = ahci_portp->ahciport_pending_ncq_tags; 702982263d52Syt } 703082263d52Syt 70312fcbc377Syt timeout_tags = 0; 70322fcbc377Syt while (pending_tags) { 70332fcbc377Syt tmp_slot = ddi_ffs(pending_tags) - 1; 70342fcbc377Syt if (tmp_slot == -1) { 70352fcbc377Syt break; 70362fcbc377Syt } 70372fcbc377Syt 703882263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 703982263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 704082263d52Syt else 704182263d52Syt spkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 704282263d52Syt 704368d33a25Syt if ((spkt != NULL) && spkt->satapkt_time && 704468d33a25Syt !(spkt->satapkt_op_mode & SATA_OPMODE_POLLING)) { 70452fcbc377Syt /* 70462fcbc377Syt * We are overloading satapkt_hba_driver_private 70472fcbc377Syt * with watched_cycle count. 70482fcbc377Syt * 70492fcbc377Syt * If a packet has survived for more than it's 70502fcbc377Syt * max life cycles, it is a candidate for time 70512fcbc377Syt * out. 70522fcbc377Syt */ 70532fcbc377Syt watched_cycles = (int)(intptr_t) 705468d33a25Syt spkt->satapkt_hba_driver_private; 70552fcbc377Syt watched_cycles++; 705668d33a25Syt max_life_cycles = (spkt->satapkt_time + 70572fcbc377Syt ahci_watchdog_timeout - 1) / 70582fcbc377Syt ahci_watchdog_timeout; 705968d33a25Syt 706068d33a25Syt spkt->satapkt_hba_driver_private = 706168d33a25Syt (void *)(intptr_t)watched_cycles; 706268d33a25Syt 706368d33a25Syt if (watched_cycles <= max_life_cycles) 706468d33a25Syt goto next; 706568d33a25Syt 7066f8a673adSying tian - Beijing China AHCIDBG1(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, 7067f8a673adSying tian - Beijing China ahci_ctlp, "the current slot is %d", 7068f8a673adSying tian - Beijing China current_slot); 706968d33a25Syt /* 707068d33a25Syt * We need to check whether the HBA has 707168d33a25Syt * begun to execute the command, if not, 707268d33a25Syt * then re-set the timer of the command. 707368d33a25Syt */ 707482263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) && 707582263d52Syt (tmp_slot != current_slot) || 707682263d52Syt NCQ_CMD_IN_PROGRESS(ahci_portp) && 707782263d52Syt ((0x1 << tmp_slot) & current_tags)) { 707868d33a25Syt spkt->satapkt_hba_driver_private = 707968d33a25Syt (void *)(intptr_t)0; 708068d33a25Syt } else { 70812fcbc377Syt timeout_tags |= (0x1 << tmp_slot); 7082a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: watchdog " 70832fcbc377Syt "port %d satapkt 0x%p timed out\n", 7084a9440e8dSyt instance, port, (void *)spkt); 70852fcbc377Syt } 70862fcbc377Syt } 708768d33a25Syt next: 70882fcbc377Syt CLEAR_BIT(pending_tags, tmp_slot); 70892fcbc377Syt } 70902fcbc377Syt 70912fcbc377Syt if (timeout_tags) { 70922fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 70932fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 70942fcbc377Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 709582263d52Syt port, timeout_tags); 70962fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 70972fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 70982fcbc377Syt } 70992fcbc377Syt 71002fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 71012fcbc377Syt } 71022fcbc377Syt 71032fcbc377Syt /* Re-install the watchdog timeout handler */ 71042fcbc377Syt if (ahci_ctlp->ahcictl_timeout_id != 0) { 71052fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 71062fcbc377Syt timeout((void (*)(void *))ahci_watchdog_handler, 71072fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 71082fcbc377Syt } 71092fcbc377Syt 71102fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 71112fcbc377Syt } 71122fcbc377Syt 71132fcbc377Syt /* 711468d33a25Syt * Fill the error context into sata_cmd for non-queued command error. 711568d33a25Syt */ 711668d33a25Syt static void 711768d33a25Syt ahci_copy_err_cnxt(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 711868d33a25Syt { 711968d33a25Syt scmd->satacmd_status_reg = GET_RFIS_STATUS(rfisp); 712068d33a25Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 712168d33a25Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 712268d33a25Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 712368d33a25Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 712468d33a25Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 712568d33a25Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 712668d33a25Syt 712768d33a25Syt if (scmd->satacmd_addr_type == ATA_ADDR_LBA48) { 712868d33a25Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 712968d33a25Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 713068d33a25Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 713168d33a25Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 713268d33a25Syt } 713368d33a25Syt } 713468d33a25Syt 713582263d52Syt /* 713682263d52Syt * Fill the ncq error page into sata_cmd for queued command error. 713782263d52Syt */ 713882263d52Syt static void 713982263d52Syt ahci_copy_ncq_err_page(sata_cmd_t *scmd, 714082263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page) 714182263d52Syt { 714282263d52Syt scmd->satacmd_sec_count_msb = ncq_err_page->ncq_sector_count_ext; 714382263d52Syt scmd->satacmd_sec_count_lsb = ncq_err_page->ncq_sector_count; 714482263d52Syt scmd->satacmd_lba_low_msb = ncq_err_page->ncq_sector_number_ext; 714582263d52Syt scmd->satacmd_lba_low_lsb = ncq_err_page->ncq_sector_number; 714682263d52Syt scmd->satacmd_lba_mid_msb = ncq_err_page->ncq_cyl_low_ext; 714782263d52Syt scmd->satacmd_lba_mid_lsb = ncq_err_page->ncq_cyl_low; 714882263d52Syt scmd->satacmd_lba_high_msb = ncq_err_page->ncq_cyl_high_ext; 714982263d52Syt scmd->satacmd_lba_high_lsb = ncq_err_page->ncq_cyl_high; 715082263d52Syt scmd->satacmd_device_reg = ncq_err_page->ncq_dev_head; 715182263d52Syt scmd->satacmd_status_reg = ncq_err_page->ncq_status; 715282263d52Syt scmd->satacmd_error_reg = ncq_err_page->ncq_error; 715382263d52Syt } 715482263d52Syt 715568d33a25Syt /* 715668d33a25Syt * Put the respective register value to sata_cmd_t for satacmd_flags. 71572fcbc377Syt */ 71582fcbc377Syt static void 71592fcbc377Syt ahci_copy_out_regs(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 71602fcbc377Syt { 71612fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_msb) 71622fcbc377Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 71632fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_msb) 71642fcbc377Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 71652fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_msb) 71662fcbc377Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 71672fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_msb) 71682fcbc377Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 71692fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_lsb) 71702fcbc377Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 71712fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_lsb) 71722fcbc377Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 71732fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_lsb) 71742fcbc377Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 71752fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_lsb) 71762fcbc377Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 71772fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_device_reg) 71782fcbc377Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 71792fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_error_reg) 71802fcbc377Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 71812fcbc377Syt } 71822fcbc377Syt 718368d33a25Syt static void 718468d33a25Syt ahci_log_fatal_error_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 718568d33a25Syt uint32_t intr_status) 718668d33a25Syt { 7187a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 718868d33a25Syt 718968d33a25Syt if (intr_status & AHCI_INTR_STATUS_IFS) 7190a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d has interface fatal " 7191a9440e8dSyt "error", instance, port); 719268d33a25Syt 719368d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBDS) 7194a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d has bus data error", 7195a9440e8dSyt instance, port); 719668d33a25Syt 719768d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBFS) 7198a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d has bus fatal error", 7199a9440e8dSyt instance, port); 720068d33a25Syt 720168d33a25Syt if (intr_status & AHCI_INTR_STATUS_TFES) 7202a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d has task file error", 7203a9440e8dSyt instance, port); 720468d33a25Syt 7205a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d is trying to do error " 7206a9440e8dSyt "recovery", instance, port); 720768d33a25Syt } 720868d33a25Syt 72092fcbc377Syt /* 72102fcbc377Syt * Dump the serror message to the log. 72112fcbc377Syt */ 72122fcbc377Syt static void 721368d33a25Syt ahci_log_serror_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 7214a9440e8dSyt uint32_t port_serror, int debug_only) 72152fcbc377Syt { 7216a9440e8dSyt static char err_buf[512]; 7217a9440e8dSyt static char err_msg_header[16]; 7218a9440e8dSyt char *err_msg = err_buf; 7219a9440e8dSyt 7220a9440e8dSyt *err_buf = '\0'; 7221a9440e8dSyt *err_msg_header = '\0'; 72222fcbc377Syt 722382263d52Syt if (port_serror & SERROR_DATA_ERR_FIXED) { 7224a9440e8dSyt err_msg = strcat(err_msg, 7225a9440e8dSyt "\tRecovered Data Integrity Error (I)\n"); 72262fcbc377Syt } 72272fcbc377Syt 722882263d52Syt if (port_serror & SERROR_COMM_ERR_FIXED) { 7229a9440e8dSyt err_msg = strcat(err_msg, 7230a9440e8dSyt "\tRecovered Communication Error (M)\n"); 72312fcbc377Syt } 72322fcbc377Syt 723382263d52Syt if (port_serror & SERROR_DATA_ERR) { 7234a9440e8dSyt err_msg = strcat(err_msg, 7235a9440e8dSyt "\tTransient Data Integrity Error (T)\n"); 72362fcbc377Syt } 72372fcbc377Syt 723882263d52Syt if (port_serror & SERROR_PERSISTENT_ERR) { 7239a9440e8dSyt err_msg = strcat(err_msg, 7240a9440e8dSyt "\tPersistent Communication or Data Integrity Error (C)\n"); 72412fcbc377Syt } 72422fcbc377Syt 724382263d52Syt if (port_serror & SERROR_PROTOCOL_ERR) { 7244a9440e8dSyt err_msg = strcat(err_msg, "\tProtocol Error (P)\n"); 72452fcbc377Syt } 72462fcbc377Syt 724782263d52Syt if (port_serror & SERROR_INT_ERR) { 7248a9440e8dSyt err_msg = strcat(err_msg, "\tInternal Error (E)\n"); 72492fcbc377Syt } 72502fcbc377Syt 725182263d52Syt if (port_serror & SERROR_PHY_RDY_CHG) { 7252a9440e8dSyt err_msg = strcat(err_msg, "\tPhyRdy Change (N)\n"); 72532fcbc377Syt } 72542fcbc377Syt 725582263d52Syt if (port_serror & SERROR_PHY_INT_ERR) { 7256a9440e8dSyt err_msg = strcat(err_msg, "\tPhy Internal Error (I)\n"); 72572fcbc377Syt } 72582fcbc377Syt 725982263d52Syt if (port_serror & SERROR_COMM_WAKE) { 7260a9440e8dSyt err_msg = strcat(err_msg, "\tComm Wake (W)\n"); 72612fcbc377Syt } 72622fcbc377Syt 726382263d52Syt if (port_serror & SERROR_10B_TO_8B_ERR) { 7264a9440e8dSyt err_msg = strcat(err_msg, "\t10B to 8B Decode Error (B)\n"); 72652fcbc377Syt } 72662fcbc377Syt 726782263d52Syt if (port_serror & SERROR_DISPARITY_ERR) { 7268a9440e8dSyt err_msg = strcat(err_msg, "\tDisparity Error (D)\n"); 72692fcbc377Syt } 72702fcbc377Syt 727182263d52Syt if (port_serror & SERROR_CRC_ERR) { 7272a9440e8dSyt err_msg = strcat(err_msg, "\tCRC Error (C)\n"); 72732fcbc377Syt } 72742fcbc377Syt 727582263d52Syt if (port_serror & SERROR_HANDSHAKE_ERR) { 7276a9440e8dSyt err_msg = strcat(err_msg, "\tHandshake Error (H)\n"); 72772fcbc377Syt } 72782fcbc377Syt 727982263d52Syt if (port_serror & SERROR_LINK_SEQ_ERR) { 7280a9440e8dSyt err_msg = strcat(err_msg, "\tLink Sequence Error (S)\n"); 72812fcbc377Syt } 72822fcbc377Syt 728382263d52Syt if (port_serror & SERROR_TRANS_ERR) { 7284a9440e8dSyt err_msg = strcat(err_msg, 7285a9440e8dSyt "\tTransport state transition error (T)\n"); 72862fcbc377Syt } 72872fcbc377Syt 728882263d52Syt if (port_serror & SERROR_FIS_TYPE) { 7289a9440e8dSyt err_msg = strcat(err_msg, "\tUnknown FIS Type (F)\n"); 72902fcbc377Syt } 72912fcbc377Syt 729282263d52Syt if (port_serror & SERROR_EXCHANGED_ERR) { 7293a9440e8dSyt err_msg = strcat(err_msg, "\tExchanged (X)\n"); 7294a9440e8dSyt } 7295a9440e8dSyt 7296a9440e8dSyt if (err_msg == NULL) 7297a9440e8dSyt return; 7298a9440e8dSyt 7299a9440e8dSyt if (debug_only) { 7300a9440e8dSyt (void) sprintf(err_msg_header, "port %d", port); 7301a9440e8dSyt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, err_msg_header); 7302a9440e8dSyt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, err_msg); 7303a9440e8dSyt } else if (ahci_ctlp) { 7304a9440e8dSyt cmn_err(CE_WARN, "ahci%d: %s %s", 7305a9440e8dSyt ddi_get_instance(ahci_ctlp->ahcictl_dip), 7306a9440e8dSyt err_msg_header, err_msg); 7307a9440e8dSyt } else { 7308a9440e8dSyt cmn_err(CE_WARN, "ahci: %s %s", err_msg_header, err_msg); 73092fcbc377Syt } 73102fcbc377Syt } 73112fcbc377Syt 73122fcbc377Syt /* 73132fcbc377Syt * This routine is to calculate the total number of ports implemented 73142fcbc377Syt * by the HBA. 73152fcbc377Syt */ 73162fcbc377Syt static int 73172fcbc377Syt ahci_get_num_implemented_ports(uint32_t ports_implemented) 73182fcbc377Syt { 73192fcbc377Syt uint8_t i; 73202fcbc377Syt int num = 0; 73212fcbc377Syt 73222fcbc377Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 73232fcbc377Syt if (((uint32_t)0x1 << i) & ports_implemented) 73242fcbc377Syt num++; 73252fcbc377Syt } 73262fcbc377Syt 73272fcbc377Syt return (num); 73282fcbc377Syt } 73292fcbc377Syt 7330689d74b0Syt #if AHCI_DEBUG 73312fcbc377Syt static void 73322fcbc377Syt ahci_log(ahci_ctl_t *ahci_ctlp, uint_t level, char *fmt, ...) 73332fcbc377Syt { 7334a9440e8dSyt static char name[16]; 73352fcbc377Syt va_list ap; 73362fcbc377Syt 73372fcbc377Syt mutex_enter(&ahci_log_mutex); 73382fcbc377Syt 73392fcbc377Syt va_start(ap, fmt); 73402fcbc377Syt if (ahci_ctlp) { 7341a9440e8dSyt (void) sprintf(name, "ahci%d: ", 73422fcbc377Syt ddi_get_instance(ahci_ctlp->ahcictl_dip)); 73432fcbc377Syt } else { 7344a9440e8dSyt (void) sprintf(name, "ahci: "); 73452fcbc377Syt } 73462fcbc377Syt 73472fcbc377Syt (void) vsprintf(ahci_log_buf, fmt, ap); 73482fcbc377Syt va_end(ap); 73492fcbc377Syt 7350a9440e8dSyt cmn_err(level, "%s%s", name, ahci_log_buf); 73512fcbc377Syt 73522fcbc377Syt mutex_exit(&ahci_log_mutex); 73532fcbc377Syt } 7354689d74b0Syt #endif 735519397407SSherry Moore 735619397407SSherry Moore /* 735719397407SSherry Moore * quiesce(9E) entry point. 735819397407SSherry Moore * 735919397407SSherry Moore * This function is called when the system is single-threaded at high 736019397407SSherry Moore * PIL with preemption disabled. Therefore, this function must not be 736119397407SSherry Moore * blocked. 736219397407SSherry Moore * 736319397407SSherry Moore * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 736419397407SSherry Moore * DDI_FAILURE indicates an error condition and should almost never happen. 736519397407SSherry Moore */ 736619397407SSherry Moore static int 736719397407SSherry Moore ahci_quiesce(dev_info_t *dip) 736819397407SSherry Moore { 736919397407SSherry Moore ahci_ctl_t *ahci_ctlp; 737019397407SSherry Moore ahci_port_t *ahci_portp; 737119397407SSherry Moore int instance, port; 737219397407SSherry Moore 737319397407SSherry Moore instance = ddi_get_instance(dip); 737419397407SSherry Moore ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 737519397407SSherry Moore 737619397407SSherry Moore if (ahci_ctlp == NULL) 737719397407SSherry Moore return (DDI_FAILURE); 737819397407SSherry Moore 737919397407SSherry Moore #if AHCI_DEBUG 738019397407SSherry Moore ahci_debug_flags = 0; 738119397407SSherry Moore #endif 738219397407SSherry Moore 738319397407SSherry Moore /* disable all the interrupts. */ 738419397407SSherry Moore ahci_disable_all_intrs(ahci_ctlp); 738519397407SSherry Moore 738619397407SSherry Moore for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 738719397407SSherry Moore if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 738819397407SSherry Moore continue; 738919397407SSherry Moore } 739019397407SSherry Moore 739119397407SSherry Moore ahci_portp = ahci_ctlp->ahcictl_ports[port]; 739219397407SSherry Moore 739319397407SSherry Moore /* 739419397407SSherry Moore * Stop the port by clearing PxCMD.ST 739519397407SSherry Moore * 739619397407SSherry Moore * Here we must disable the port interrupt because 739719397407SSherry Moore * ahci_disable_all_intrs only clear GHC.IE, and IS 739819397407SSherry Moore * register will be still set if PxIE is enabled. 739919397407SSherry Moore * When ahci shares one IRQ with other drivers, the 740019397407SSherry Moore * intr handler may claim the intr mistakenly. 740119397407SSherry Moore */ 740219397407SSherry Moore ahci_disable_port_intrs(ahci_ctlp, port); 740319397407SSherry Moore (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 740419397407SSherry Moore ahci_portp, port); 740519397407SSherry Moore } 740619397407SSherry Moore 740719397407SSherry Moore return (DDI_SUCCESS); 740819397407SSherry Moore } 7409