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 /* 2395c11c1fSyt * Copyright 2008 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> 49*b2e3645aSying 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 542fcbc377Syt /* 552fcbc377Syt * Function prototypes for driver entry points 562fcbc377Syt */ 572fcbc377Syt static int ahci_attach(dev_info_t *, ddi_attach_cmd_t); 582fcbc377Syt static int ahci_detach(dev_info_t *, ddi_detach_cmd_t); 592fcbc377Syt static int ahci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 602fcbc377Syt 612fcbc377Syt /* 622fcbc377Syt * Function prototypes for SATA Framework interfaces 632fcbc377Syt */ 642fcbc377Syt static int ahci_register_sata_hba_tran(ahci_ctl_t *, uint32_t); 652fcbc377Syt static int ahci_unregister_sata_hba_tran(ahci_ctl_t *); 662fcbc377Syt 672fcbc377Syt static int ahci_tran_probe_port(dev_info_t *, sata_device_t *); 682fcbc377Syt static int ahci_tran_start(dev_info_t *, sata_pkt_t *spkt); 692fcbc377Syt static int ahci_tran_abort(dev_info_t *, sata_pkt_t *, int); 702fcbc377Syt static int ahci_tran_reset_dport(dev_info_t *, sata_device_t *); 712fcbc377Syt static int ahci_tran_hotplug_port_activate(dev_info_t *, sata_device_t *); 722fcbc377Syt static int ahci_tran_hotplug_port_deactivate(dev_info_t *, sata_device_t *); 732fcbc377Syt #if defined(__lock_lint) 742fcbc377Syt static int ahci_selftest(dev_info_t *, sata_device_t *); 752fcbc377Syt #endif 762fcbc377Syt 772fcbc377Syt /* 782fcbc377Syt * Local function prototypes 792fcbc377Syt */ 8068d33a25Syt static int ahci_alloc_ports_state(ahci_ctl_t *); 8168d33a25Syt static void ahci_dealloc_ports_state(ahci_ctl_t *); 822fcbc377Syt static int ahci_alloc_port_state(ahci_ctl_t *, uint8_t); 832fcbc377Syt static void ahci_dealloc_port_state(ahci_ctl_t *, uint8_t); 842fcbc377Syt static int ahci_alloc_rcvd_fis(ahci_ctl_t *, ahci_port_t *, uint8_t); 85689d74b0Syt static void ahci_dealloc_rcvd_fis(ahci_port_t *); 862fcbc377Syt static int ahci_alloc_cmd_list(ahci_ctl_t *, ahci_port_t *, uint8_t); 872fcbc377Syt static void ahci_dealloc_cmd_list(ahci_ctl_t *, ahci_port_t *); 882fcbc377Syt static int ahci_alloc_cmd_tables(ahci_ctl_t *, ahci_port_t *); 892fcbc377Syt static void ahci_dealloc_cmd_tables(ahci_ctl_t *, ahci_port_t *); 902fcbc377Syt 9168d33a25Syt static int ahci_initialize_controller(ahci_ctl_t *); 9268d33a25Syt static void ahci_uninitialize_controller(ahci_ctl_t *); 932fcbc377Syt static int ahci_initialize_port(ahci_ctl_t *, ahci_port_t *, uint8_t); 9413bcbb7aSyt static int ahci_config_space_init(ahci_ctl_t *); 9568d33a25Syt 9613bcbb7aSyt static void ahci_disable_interface_pm(ahci_ctl_t *, uint8_t); 9768d33a25Syt static int ahci_start_port(ahci_ctl_t *, ahci_port_t *, uint8_t); 9868d33a25Syt static void ahci_find_dev_signature(ahci_ctl_t *, ahci_port_t *, uint8_t); 992fcbc377Syt static void ahci_update_sata_registers(ahci_ctl_t *, uint8_t, sata_device_t *); 1002fcbc377Syt static int ahci_deliver_satapkt(ahci_ctl_t *, ahci_port_t *, 1012fcbc377Syt uint8_t, sata_pkt_t *); 10268d33a25Syt static int ahci_do_sync_start(ahci_ctl_t *, ahci_port_t *, 10368d33a25Syt uint8_t, sata_pkt_t *); 10482263d52Syt static int ahci_claim_free_slot(ahci_ctl_t *, ahci_port_t *, int); 10568d33a25Syt static void ahci_copy_err_cnxt(sata_cmd_t *, ahci_fis_d2h_register_t *); 10682263d52Syt static void ahci_copy_ncq_err_page(sata_cmd_t *, 10782263d52Syt struct sata_ncq_error_recovery_page *); 1082fcbc377Syt static void ahci_copy_out_regs(sata_cmd_t *, ahci_fis_d2h_register_t *); 1092fcbc377Syt 1102fcbc377Syt static int ahci_software_reset(ahci_ctl_t *, ahci_port_t *, uint8_t); 1112fcbc377Syt static int ahci_hba_reset(ahci_ctl_t *); 1122fcbc377Syt static int ahci_port_reset(ahci_ctl_t *, ahci_port_t *, uint8_t); 1132fcbc377Syt static void ahci_reject_all_abort_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t); 1142fcbc377Syt static int ahci_reset_device_reject_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t); 1152fcbc377Syt static int ahci_reset_port_reject_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t); 1162fcbc377Syt static int ahci_reset_hba_reject_pkts(ahci_ctl_t *); 11768d33a25Syt static int ahci_put_port_into_notrunning_state(ahci_ctl_t *, ahci_port_t *, 1182fcbc377Syt uint8_t); 1192fcbc377Syt static int ahci_restart_port_wait_till_ready(ahci_ctl_t *, ahci_port_t *, 12068d33a25Syt uint8_t, int, int *); 121689d74b0Syt static void ahci_mop_commands(ahci_ctl_t *, ahci_port_t *, uint32_t, 122689d74b0Syt uint32_t, uint32_t, uint32_t, uint32_t); 12382263d52Syt static uint32_t ahci_get_rdlogext_data(ahci_ctl_t *, ahci_port_t *, uint8_t); 12468d33a25Syt static void ahci_get_rqsense_data(ahci_ctl_t *, ahci_port_t *, 12568d33a25Syt uint8_t, sata_pkt_t *); 12668d33a25Syt static void ahci_fatal_error_recovery_handler(ahci_ctl_t *, ahci_port_t *, 12782263d52Syt uint8_t, uint32_t); 12868d33a25Syt static void ahci_timeout_pkts(ahci_ctl_t *, ahci_port_t *, 12982263d52Syt uint8_t, uint32_t); 13068d33a25Syt static void ahci_events_handler(void *); 1312fcbc377Syt static void ahci_watchdog_handler(ahci_ctl_t *); 1322fcbc377Syt 1332fcbc377Syt static uint_t ahci_intr(caddr_t, caddr_t); 13482263d52Syt static void ahci_port_intr(ahci_ctl_t *, ahci_port_t *, uint8_t); 1352fcbc377Syt static int ahci_add_legacy_intrs(ahci_ctl_t *); 1362fcbc377Syt static int ahci_add_msi_intrs(ahci_ctl_t *); 1372fcbc377Syt static void ahci_rem_intrs(ahci_ctl_t *); 1382fcbc377Syt static void ahci_enable_all_intrs(ahci_ctl_t *); 1392fcbc377Syt static void ahci_disable_all_intrs(ahci_ctl_t *); 140689d74b0Syt static void ahci_enable_port_intrs(ahci_ctl_t *, uint8_t); 141689d74b0Syt static void ahci_disable_port_intrs(ahci_ctl_t *, uint8_t); 1422fcbc377Syt 143689d74b0Syt static int ahci_intr_cmd_cmplt(ahci_ctl_t *, ahci_port_t *, uint8_t); 14468d33a25Syt static int ahci_intr_set_device_bits(ahci_ctl_t *, ahci_port_t *, uint8_t); 1452fcbc377Syt static int ahci_intr_port_connect_change(ahci_ctl_t *, ahci_port_t *, uint8_t); 1462fcbc377Syt static int ahci_intr_device_mechanical_presence_status(ahci_ctl_t *, 1472fcbc377Syt ahci_port_t *, uint8_t); 1482fcbc377Syt static int ahci_intr_phyrdy_change(ahci_ctl_t *, ahci_port_t *, uint8_t); 14968d33a25Syt static int ahci_intr_non_fatal_error(ahci_ctl_t *, ahci_port_t *, 15068d33a25Syt uint8_t, uint32_t); 15168d33a25Syt static int ahci_intr_fatal_error(ahci_ctl_t *, ahci_port_t *, 15282263d52Syt uint8_t, uint32_t); 1532fcbc377Syt static int ahci_intr_cold_port_detect(ahci_ctl_t *, ahci_port_t *, uint8_t); 1542fcbc377Syt 1552fcbc377Syt static int ahci_get_num_implemented_ports(uint32_t); 15668d33a25Syt static void ahci_log_fatal_error_message(ahci_ctl_t *, uint8_t port, 15768d33a25Syt uint32_t); 158a9440e8dSyt static void ahci_log_serror_message(ahci_ctl_t *, uint8_t, uint32_t, int); 159689d74b0Syt #if AHCI_DEBUG 1602fcbc377Syt static void ahci_log(ahci_ctl_t *, uint_t, char *, ...); 161689d74b0Syt #endif 1622fcbc377Syt 1632fcbc377Syt 1642fcbc377Syt /* 1652fcbc377Syt * DMA attributes for the data buffer 1662fcbc377Syt * 1672fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 1682fcbc377Syt * does not support 64-bit addressing 1692fcbc377Syt */ 1702fcbc377Syt static ddi_dma_attr_t buffer_dma_attr = { 1712fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 17268d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 1732fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 1742fcbc377Syt 0x3fffffull, /* dma_attr_count_max i.e. for one cookie */ 17568d33a25Syt 0x2ull, /* dma_attr_align: word aligned */ 1762fcbc377Syt 1, /* dma_attr_burstsizes */ 1772fcbc377Syt 1, /* dma_attr_minxfer */ 1782fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 1792fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 1802fcbc377Syt AHCI_PRDT_NUMBER, /* dma_attr_sgllen */ 1812fcbc377Syt 512, /* dma_attr_granular */ 1822fcbc377Syt 0, /* dma_attr_flags */ 1832fcbc377Syt }; 1842fcbc377Syt 1852fcbc377Syt /* 1862fcbc377Syt * DMA attributes for the rcvd FIS 1872fcbc377Syt * 1882fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 1892fcbc377Syt * does not support 64-bit addressing 1902fcbc377Syt */ 1912fcbc377Syt static ddi_dma_attr_t rcvd_fis_dma_attr = { 1922fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 19368d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 1942fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 1952fcbc377Syt 0xffffffffull, /* dma_attr_count_max i.e. for one cookie */ 19668d33a25Syt 0x100ull, /* dma_attr_align: 256-byte aligned */ 1972fcbc377Syt 1, /* dma_attr_burstsizes */ 1982fcbc377Syt 1, /* dma_attr_minxfer */ 1992fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2002fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2012fcbc377Syt 1, /* dma_attr_sgllen */ 2022fcbc377Syt 1, /* dma_attr_granular */ 2032fcbc377Syt 0, /* dma_attr_flags */ 2042fcbc377Syt }; 2052fcbc377Syt 2062fcbc377Syt /* 2072fcbc377Syt * DMA attributes for the command list 2082fcbc377Syt * 2092fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 2102fcbc377Syt * does not support 64-bit addressing 2112fcbc377Syt */ 2122fcbc377Syt static ddi_dma_attr_t cmd_list_dma_attr = { 2132fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 21468d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 2152fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 2162fcbc377Syt 0xffffffffull, /* dma_attr_count_max i.e. for one cookie */ 21768d33a25Syt 0x400ull, /* dma_attr_align: 1K-byte aligned */ 2182fcbc377Syt 1, /* dma_attr_burstsizes */ 2192fcbc377Syt 1, /* dma_attr_minxfer */ 2202fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2212fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2222fcbc377Syt 1, /* dma_attr_sgllen */ 2232fcbc377Syt 1, /* dma_attr_granular */ 2242fcbc377Syt 0, /* dma_attr_flags */ 2252fcbc377Syt }; 2262fcbc377Syt 2272fcbc377Syt /* 2282fcbc377Syt * DMA attributes for cmd tables 2292fcbc377Syt * 2302fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 2312fcbc377Syt * does not support 64-bit addressing 2322fcbc377Syt */ 2332fcbc377Syt static ddi_dma_attr_t cmd_table_dma_attr = { 2342fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 23568d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 2362fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 2372fcbc377Syt 0xffffffffull, /* dma_attr_count_max i.e. for one cookie */ 23868d33a25Syt 0x80ull, /* dma_attr_align: 128-byte aligned */ 2392fcbc377Syt 1, /* dma_attr_burstsizes */ 2402fcbc377Syt 1, /* dma_attr_minxfer */ 2412fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2422fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2432fcbc377Syt 1, /* dma_attr_sgllen */ 2442fcbc377Syt 1, /* dma_attr_granular */ 2452fcbc377Syt 0, /* dma_attr_flags */ 2462fcbc377Syt }; 2472fcbc377Syt 2482fcbc377Syt 2492fcbc377Syt /* Device access attributes */ 2502fcbc377Syt static ddi_device_acc_attr_t accattr = { 2512fcbc377Syt DDI_DEVICE_ATTR_V0, 2522fcbc377Syt DDI_STRUCTURE_LE_ACC, 2532fcbc377Syt DDI_STRICTORDER_ACC 2542fcbc377Syt }; 2552fcbc377Syt 2562fcbc377Syt 2572fcbc377Syt static struct dev_ops ahcictl_dev_ops = { 2582fcbc377Syt DEVO_REV, /* devo_rev */ 2592fcbc377Syt 0, /* refcnt */ 2602fcbc377Syt ahci_getinfo, /* info */ 2612fcbc377Syt nulldev, /* identify */ 2622fcbc377Syt nulldev, /* probe */ 2632fcbc377Syt ahci_attach, /* attach */ 2642fcbc377Syt ahci_detach, /* detach */ 2652fcbc377Syt nodev, /* no reset */ 2662fcbc377Syt (struct cb_ops *)0, /* driver operations */ 2672fcbc377Syt NULL, /* bus operations */ 2682fcbc377Syt NULL /* power */ 2692fcbc377Syt }; 2702fcbc377Syt 2712fcbc377Syt static sata_tran_hotplug_ops_t ahci_tran_hotplug_ops = { 2722fcbc377Syt SATA_TRAN_HOTPLUG_OPS_REV_1, 2732fcbc377Syt ahci_tran_hotplug_port_activate, 2742fcbc377Syt ahci_tran_hotplug_port_deactivate 2752fcbc377Syt }; 2762fcbc377Syt 2772fcbc377Syt extern struct mod_ops mod_driverops; 2782fcbc377Syt 2792fcbc377Syt static struct modldrv modldrv = { 2802fcbc377Syt &mod_driverops, /* driverops */ 281*b2e3645aSying tian - Beijing China "ahci driver", 2822fcbc377Syt &ahcictl_dev_ops, /* driver ops */ 2832fcbc377Syt }; 2842fcbc377Syt 2852fcbc377Syt static struct modlinkage modlinkage = { 2862fcbc377Syt MODREV_1, 2872fcbc377Syt &modldrv, 2882fcbc377Syt NULL 2892fcbc377Syt }; 2902fcbc377Syt 2912fcbc377Syt static int ahci_watchdog_timeout = 5; /* 5 seconds */ 2922fcbc377Syt static int ahci_watchdog_tick; 2932fcbc377Syt 2942fcbc377Syt static size_t ahci_cmd_table_size; 2952fcbc377Syt 2962fcbc377Syt /* The number of Physical Region Descriptor Table(PRDT) in Command Table */ 2972fcbc377Syt int ahci_dma_prdt_number = AHCI_PRDT_NUMBER; 2982fcbc377Syt 2992fcbc377Syt /* 3002fcbc377Syt * AHCI MSI tunable: 3012fcbc377Syt * 3022fcbc377Syt * MSI will be enabled in phase 2. 3032fcbc377Syt */ 3042fcbc377Syt boolean_t ahci_msi_enabled = B_FALSE; 3052fcbc377Syt 3062fcbc377Syt #if AHCI_DEBUG 3072fcbc377Syt uint32_t ahci_debug_flags = 0; 308689d74b0Syt 309689d74b0Syt /* The following is needed for ahci_log() */ 310689d74b0Syt static kmutex_t ahci_log_mutex; 311689d74b0Syt static char ahci_log_buf[512]; 3122fcbc377Syt #endif 3132fcbc377Syt 3142fcbc377Syt /* Opaque state pointer initialized by ddi_soft_state_init() */ 3152fcbc377Syt static void *ahci_statep = NULL; 3162fcbc377Syt 3172fcbc377Syt /* 3182fcbc377Syt * ahci module initialization. 3192fcbc377Syt */ 3202fcbc377Syt int 3212fcbc377Syt _init(void) 3222fcbc377Syt { 3232fcbc377Syt int ret; 3242fcbc377Syt 3252fcbc377Syt ret = ddi_soft_state_init(&ahci_statep, sizeof (ahci_ctl_t), 0); 3262fcbc377Syt if (ret != 0) { 3272fcbc377Syt goto err_out; 3282fcbc377Syt } 3292fcbc377Syt 330689d74b0Syt #if AHCI_DEBUG 3312fcbc377Syt mutex_init(&ahci_log_mutex, NULL, MUTEX_DRIVER, NULL); 332689d74b0Syt #endif 3332fcbc377Syt 3342fcbc377Syt if ((ret = sata_hba_init(&modlinkage)) != 0) { 335689d74b0Syt #if AHCI_DEBUG 3362fcbc377Syt mutex_destroy(&ahci_log_mutex); 337689d74b0Syt #endif 3382fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3392fcbc377Syt goto err_out; 3402fcbc377Syt } 3412fcbc377Syt 3422fcbc377Syt ret = mod_install(&modlinkage); 3432fcbc377Syt if (ret != 0) { 3442fcbc377Syt sata_hba_fini(&modlinkage); 345689d74b0Syt #if AHCI_DEBUG 3462fcbc377Syt mutex_destroy(&ahci_log_mutex); 347689d74b0Syt #endif 3482fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3492fcbc377Syt goto err_out; 3502fcbc377Syt } 3512fcbc377Syt 3522fcbc377Syt /* watchdog tick */ 3532fcbc377Syt ahci_watchdog_tick = drv_usectohz( 3542fcbc377Syt (clock_t)ahci_watchdog_timeout * 1000000); 3552fcbc377Syt return (ret); 3562fcbc377Syt 3572fcbc377Syt err_out: 358a9440e8dSyt cmn_err(CE_WARN, "!ahci: Module init failed"); 3592fcbc377Syt return (ret); 3602fcbc377Syt } 3612fcbc377Syt 3622fcbc377Syt /* 3632fcbc377Syt * ahci module uninitialize. 3642fcbc377Syt */ 3652fcbc377Syt int 3662fcbc377Syt _fini(void) 3672fcbc377Syt { 3682fcbc377Syt int ret; 3692fcbc377Syt 3702fcbc377Syt ret = mod_remove(&modlinkage); 3712fcbc377Syt if (ret != 0) { 3722fcbc377Syt return (ret); 3732fcbc377Syt } 3742fcbc377Syt 3752fcbc377Syt /* Remove the resources allocated in _init(). */ 3762fcbc377Syt sata_hba_fini(&modlinkage); 377689d74b0Syt #if AHCI_DEBUG 3782fcbc377Syt mutex_destroy(&ahci_log_mutex); 379689d74b0Syt #endif 3802fcbc377Syt ddi_soft_state_fini(&ahci_statep); 3812fcbc377Syt 3822fcbc377Syt return (ret); 3832fcbc377Syt } 3842fcbc377Syt 3852fcbc377Syt /* 3862fcbc377Syt * _info entry point 3872fcbc377Syt */ 3882fcbc377Syt int 3892fcbc377Syt _info(struct modinfo *modinfop) 3902fcbc377Syt { 3912fcbc377Syt return (mod_info(&modlinkage, modinfop)); 3922fcbc377Syt } 3932fcbc377Syt 3942fcbc377Syt /* 3952fcbc377Syt * The attach entry point for dev_ops. 3962fcbc377Syt */ 3972fcbc377Syt static int 3982fcbc377Syt ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 3992fcbc377Syt { 4002fcbc377Syt ahci_ctl_t *ahci_ctlp; 4012fcbc377Syt int instance = ddi_get_instance(dip); 4022fcbc377Syt int status; 4032fcbc377Syt int attach_state; 4042fcbc377Syt uint32_t cap_status, ahci_version; 4052fcbc377Syt int intr_types; 40668d33a25Syt int i; 40795c11c1fSyt pci_regspec_t *regs; 40895c11c1fSyt int regs_length; 40995c11c1fSyt int rnumber; 41068d33a25Syt #if AHCI_DEBUG 41168d33a25Syt int speed; 41268d33a25Syt #endif 4132fcbc377Syt 4142fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, NULL, "ahci_attach enter"); 4152fcbc377Syt 4162fcbc377Syt switch (cmd) { 4172fcbc377Syt case DDI_ATTACH: 4182fcbc377Syt break; 4192fcbc377Syt 4202fcbc377Syt case DDI_RESUME: 42113bcbb7aSyt 42213bcbb7aSyt /* 42313bcbb7aSyt * During DDI_RESUME, the hardware state of the device 42413bcbb7aSyt * (power may have been removed from the device) must be 42513bcbb7aSyt * restored, allow pending requests to continue, and 42613bcbb7aSyt * service new requests. 42713bcbb7aSyt */ 42813bcbb7aSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 42913bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 43013bcbb7aSyt 43113bcbb7aSyt /* Restart watch thread */ 43213bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id == 0) 43313bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = timeout( 43413bcbb7aSyt (void (*)(void *))ahci_watchdog_handler, 43513bcbb7aSyt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 43613bcbb7aSyt 43713bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 43813bcbb7aSyt 43913bcbb7aSyt /* 44013bcbb7aSyt * Re-initialize the controller and enable the interrupts and 44113bcbb7aSyt * restart all the ports. 44213bcbb7aSyt * 44313bcbb7aSyt * Note that so far we don't support hot-plug during 44413bcbb7aSyt * suspend/resume. 44513bcbb7aSyt */ 44613bcbb7aSyt if (ahci_initialize_controller(ahci_ctlp) != AHCI_SUCCESS) { 447a9440e8dSyt AHCIDBG0(AHCIDBG_ERRS|AHCIDBG_PM, ahci_ctlp, 448a9440e8dSyt "Failed to initialize the controller " 449a9440e8dSyt "during DDI_RESUME"); 45013bcbb7aSyt return (DDI_FAILURE); 45113bcbb7aSyt } 45213bcbb7aSyt 45313bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 45413bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~ AHCI_SUSPEND; 45513bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 45613bcbb7aSyt 45713bcbb7aSyt return (DDI_SUCCESS); 4582fcbc377Syt 4592fcbc377Syt default: 4602fcbc377Syt return (DDI_FAILURE); 4612fcbc377Syt } 4622fcbc377Syt 4632fcbc377Syt attach_state = AHCI_ATTACH_STATE_NONE; 4642fcbc377Syt 4652fcbc377Syt /* Allocate soft state */ 4662fcbc377Syt status = ddi_soft_state_zalloc(ahci_statep, instance); 4672fcbc377Syt if (status != DDI_SUCCESS) { 468a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate soft state", 469a9440e8dSyt instance); 4702fcbc377Syt goto err_out; 4712fcbc377Syt } 4722fcbc377Syt 4732fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 47413bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_ATTACH; 4752fcbc377Syt ahci_ctlp->ahcictl_dip = dip; 4762fcbc377Syt 47768d33a25Syt /* Initialize the cport/port mapping */ 47868d33a25Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 47968d33a25Syt ahci_ctlp->ahcictl_port_to_cport[i] = 0xff; 48068d33a25Syt ahci_ctlp->ahcictl_cport_to_port[i] = 0xff; 48168d33a25Syt } 48268d33a25Syt 4832fcbc377Syt attach_state |= AHCI_ATTACH_STATE_STATEP_ALLOC; 4842fcbc377Syt 4852fcbc377Syt /* 4862fcbc377Syt * Now map the AHCI base address; which includes global 4872fcbc377Syt * registers and port control registers 48895c11c1fSyt * 48995c11c1fSyt * According to the spec, the AHCI Base Address is BAR5, 49013bcbb7aSyt * but BAR0-BAR4 are optional, so we need to check which 49113bcbb7aSyt * rnumber is used for BAR5. 49295c11c1fSyt */ 49395c11c1fSyt 49495c11c1fSyt /* 49595c11c1fSyt * search through DDI "reg" property for the AHCI register set 4962fcbc377Syt */ 49795c11c1fSyt if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 49895c11c1fSyt DDI_PROP_DONTPASS, "reg", (int **)®s, 49995c11c1fSyt (uint_t *)®s_length) != DDI_PROP_SUCCESS) { 500a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot lookup reg property", 501a9440e8dSyt instance); 50295c11c1fSyt goto err_out; 50395c11c1fSyt } 50495c11c1fSyt 50595c11c1fSyt /* AHCI Base Address is located at 0x24 offset */ 50695c11c1fSyt for (rnumber = 0; rnumber < regs_length; ++rnumber) { 50795c11c1fSyt if ((regs[rnumber].pci_phys_hi & PCI_REG_REG_M) 50895c11c1fSyt == AHCI_PCI_RNUM) 50995c11c1fSyt break; 51095c11c1fSyt } 51195c11c1fSyt 51295c11c1fSyt ddi_prop_free(regs); 51395c11c1fSyt 51495c11c1fSyt if (rnumber == regs_length) { 515a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot find AHCI register set", 516a9440e8dSyt instance); 51795c11c1fSyt goto err_out; 51895c11c1fSyt } 51995c11c1fSyt 52095c11c1fSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "rnumber = %d", rnumber); 52195c11c1fSyt 5222fcbc377Syt status = ddi_regs_map_setup(dip, 52395c11c1fSyt rnumber, 5242fcbc377Syt (caddr_t *)&ahci_ctlp->ahcictl_ahci_addr, 5252fcbc377Syt 0, 5262fcbc377Syt 0, 5272fcbc377Syt &accattr, 5282fcbc377Syt &ahci_ctlp->ahcictl_ahci_acc_handle); 5292fcbc377Syt if (status != DDI_SUCCESS) { 530a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot map register space", 531a9440e8dSyt instance); 5322fcbc377Syt goto err_out; 5332fcbc377Syt } 5342fcbc377Syt 5352fcbc377Syt attach_state |= AHCI_ATTACH_STATE_REG_MAP; 5362fcbc377Syt 5372fcbc377Syt /* Get the AHCI version information */ 5382fcbc377Syt ahci_version = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5392fcbc377Syt (uint32_t *)AHCI_GLOBAL_VS(ahci_ctlp)); 5402fcbc377Syt 541a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: hba AHCI version = %x.%x", instance, 5422fcbc377Syt (ahci_version & 0xffff0000) >> 16, 5432fcbc377Syt ((ahci_version & 0x0000ff00) >> 4 | 5442fcbc377Syt (ahci_version & 0x000000ff))); 5452fcbc377Syt 5462fcbc377Syt /* We don't support controllers whose versions are lower than 1.0 */ 5472fcbc377Syt if (!(ahci_version & 0xffff0000)) { 548a9440e8dSyt cmn_err(CE_WARN, "ahci%d: Don't support AHCI HBA with lower " 549a9440e8dSyt "than version 1.0", instance); 5502fcbc377Syt goto err_out; 5512fcbc377Syt } 5522fcbc377Syt 5532fcbc377Syt /* Get the HBA capabilities information */ 5542fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5552fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 5562fcbc377Syt 5572fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba capabilites = 0x%x", 5582fcbc377Syt cap_status); 5592fcbc377Syt 56068d33a25Syt #if AHCI_DEBUG 56168d33a25Syt /* Get the interface speed supported by the HBA */ 56268d33a25Syt speed = (cap_status & AHCI_HBA_CAP_ISS) >> AHCI_HBA_CAP_ISS_SHIFT; 56368d33a25Syt if (speed == 0x01) { 56468d33a25Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 56568d33a25Syt "hba interface speed support: Gen 1 (1.5Gbps)"); 56668d33a25Syt } else if (speed == 0x10) { 56768d33a25Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 56868d33a25Syt "hba interface speed support: Gen 2 (3 Gbps)"); 56968d33a25Syt } else if (speed == 0x11) { 57068d33a25Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 57168d33a25Syt "hba interface speed support: Gen 3 (6 Gbps)"); 57268d33a25Syt } 57368d33a25Syt #endif 57468d33a25Syt 5752fcbc377Syt /* Get the number of command slots supported by the HBA */ 5762fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots = 5772fcbc377Syt ((cap_status & AHCI_HBA_CAP_NCS) >> 5782fcbc377Syt AHCI_HBA_CAP_NCS_SHIFT) + 1; 5792fcbc377Syt 58068d33a25Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba number of cmd slots: %d", 5812fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots); 5822fcbc377Syt 5832fcbc377Syt /* Get the bit map which indicates ports implemented by the HBA */ 5842fcbc377Syt ahci_ctlp->ahcictl_ports_implemented = 5852fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 5862fcbc377Syt (uint32_t *)AHCI_GLOBAL_PI(ahci_ctlp)); 5872fcbc377Syt 5882fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba implementation of ports: 0x%x", 5892fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 5902fcbc377Syt 59109121340Syt /* 59209121340Syt * According to the AHCI spec, CAP.NP should indicate the maximum 59309121340Syt * number of ports supported by the HBA silicon, but we found 59409121340Syt * this value of ICH8 chipset only indicates the number of ports 59509121340Syt * implemented (exposed) by it. Therefore, the driver should calculate 59609121340Syt * the potential maximum value by checking PI register, and use 59709121340Syt * the maximum of this value and CAP.NP. 59809121340Syt */ 59909121340Syt ahci_ctlp->ahcictl_num_ports = max( 60009121340Syt (cap_status & AHCI_HBA_CAP_NP) + 1, 60109121340Syt ddi_fls(ahci_ctlp->ahcictl_ports_implemented)); 60209121340Syt 60309121340Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "hba number of ports: %d", 60409121340Syt ahci_ctlp->ahcictl_num_ports); 60509121340Syt 6062fcbc377Syt /* Get the number of implemented ports by the HBA */ 6072fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports = 6082fcbc377Syt ahci_get_num_implemented_ports( 6092fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 6102fcbc377Syt 6112fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 61268d33a25Syt "hba number of implemented ports: %d", 6132fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports); 6142fcbc377Syt 615a9440e8dSyt /* Check whether HBA supports 64bit DMA addressing */ 6162fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_S64A)) { 617a9440e8dSyt ahci_ctlp->ahcictl_cap |= AHCI_CAP_32BIT_DMA; 6182fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 6192fcbc377Syt "hba does not support 64-bit addressing"); 6202fcbc377Syt } 6212fcbc377Syt 6222fcbc377Syt if (pci_config_setup(dip, &ahci_ctlp->ahcictl_pci_conf_handle) 6232fcbc377Syt != DDI_SUCCESS) { 624a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot set up pci configure space", 625a9440e8dSyt instance); 6262fcbc377Syt goto err_out; 6272fcbc377Syt } 6282fcbc377Syt 62968d33a25Syt attach_state |= AHCI_ATTACH_STATE_PCICFG_SETUP; 63068d33a25Syt 63113bcbb7aSyt /* Check the pci configuration space, and set caps */ 63213bcbb7aSyt if (ahci_config_space_init(ahci_ctlp) == AHCI_FAILURE) { 633a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_config_space_init failed", 634a9440e8dSyt instance); 63513bcbb7aSyt goto err_out; 63668d33a25Syt } 6372fcbc377Syt 6382fcbc377Syt /* 6392fcbc377Syt * Disable the whole controller interrupts before adding 6402fcbc377Syt * interrupt handlers(s). 6412fcbc377Syt */ 6422fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 6432fcbc377Syt 6442fcbc377Syt /* Get supported interrupt types */ 6452fcbc377Syt if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { 646a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ddi_intr_get_supported_types failed", 647a9440e8dSyt instance); 6482fcbc377Syt goto err_out; 6492fcbc377Syt } 6502fcbc377Syt 6512fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 6522fcbc377Syt "ddi_intr_get_supported_types() returned: 0x%x", 6532fcbc377Syt intr_types); 6542fcbc377Syt 6552fcbc377Syt if (ahci_msi_enabled && (intr_types & DDI_INTR_TYPE_MSI)) { 6562fcbc377Syt /* 6572fcbc377Syt * Try MSI first, but fall back to FIXED if failed 6582fcbc377Syt */ 6592fcbc377Syt if (ahci_add_msi_intrs(ahci_ctlp) == DDI_SUCCESS) { 6602fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_MSI; 6612fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 6622fcbc377Syt "Using MSI interrupt type"); 6632fcbc377Syt goto intr_done; 6642fcbc377Syt } 6652fcbc377Syt 6662fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 6672fcbc377Syt "MSI registration failed, " 6682fcbc377Syt "trying FIXED interrupts"); 6692fcbc377Syt } 6702fcbc377Syt 6712fcbc377Syt if (intr_types & DDI_INTR_TYPE_FIXED) { 6722fcbc377Syt if (ahci_add_legacy_intrs(ahci_ctlp) == DDI_SUCCESS) { 6732fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_FIXED; 674a9440e8dSyt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 6752fcbc377Syt "Using FIXED interrupt type"); 6762fcbc377Syt goto intr_done; 6772fcbc377Syt } 6782fcbc377Syt 6792fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 6802fcbc377Syt "FIXED interrupt registration failed"); 6812fcbc377Syt } 6822fcbc377Syt 683a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Interrupt registration failed", instance); 6842fcbc377Syt 6852fcbc377Syt goto err_out; 6862fcbc377Syt 6872fcbc377Syt intr_done: 6882fcbc377Syt 6892fcbc377Syt attach_state |= AHCI_ATTACH_STATE_INTR_ADDED; 6902fcbc377Syt 6912fcbc377Syt /* Initialize the controller mutex */ 6922fcbc377Syt mutex_init(&ahci_ctlp->ahcictl_mutex, NULL, MUTEX_DRIVER, 6932fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 6942fcbc377Syt 6952fcbc377Syt attach_state |= AHCI_ATTACH_STATE_MUTEX_INIT; 6962fcbc377Syt 6972fcbc377Syt if (ahci_dma_prdt_number < AHCI_MIN_PRDT_NUMBER) { 6982fcbc377Syt ahci_dma_prdt_number = AHCI_MIN_PRDT_NUMBER; 6992fcbc377Syt } else if (ahci_dma_prdt_number > AHCI_MAX_PRDT_NUMBER) { 7002fcbc377Syt ahci_dma_prdt_number = AHCI_MAX_PRDT_NUMBER; 7012fcbc377Syt } 7022fcbc377Syt 7032fcbc377Syt ahci_cmd_table_size = (sizeof (ahci_cmd_table_t) + 7042fcbc377Syt (ahci_dma_prdt_number - AHCI_PRDT_NUMBER) * 7052fcbc377Syt sizeof (ahci_prdt_item_t)); 7062fcbc377Syt 7072fcbc377Syt AHCIDBG2(AHCIDBG_INIT, ahci_ctlp, 7082fcbc377Syt "ahci_attach: ahci_dma_prdt_number set by user is 0x%x," 7092fcbc377Syt " ahci_cmd_table_size is 0x%x", 7102fcbc377Syt ahci_dma_prdt_number, ahci_cmd_table_size); 7112fcbc377Syt 7122fcbc377Syt if (ahci_dma_prdt_number != AHCI_PRDT_NUMBER) 7132fcbc377Syt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_sgllen = 7142fcbc377Syt ahci_dma_prdt_number; 7152fcbc377Syt 716a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr = buffer_dma_attr; 717a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr = rcvd_fis_dma_attr; 718a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr = cmd_list_dma_attr; 719a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr = cmd_table_dma_attr; 720a9440e8dSyt 721a9440e8dSyt if (ahci_ctlp->ahcictl_cap & AHCI_CAP_32BIT_DMA) { 722a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_addr_hi = 723a9440e8dSyt 0xffffffffull; 724a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr.dma_attr_addr_hi = 725a9440e8dSyt 0xffffffffull; 726a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr.dma_attr_addr_hi = 727a9440e8dSyt 0xffffffffull; 728a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr.dma_attr_addr_hi = 729a9440e8dSyt 0xffffffffull; 730a9440e8dSyt } 731a9440e8dSyt 73268d33a25Syt /* Allocate the ports structure */ 73368d33a25Syt status = ahci_alloc_ports_state(ahci_ctlp); 73468d33a25Syt if (status != AHCI_SUCCESS) { 735a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate ports structure", 736a9440e8dSyt instance); 73768d33a25Syt goto err_out; 73868d33a25Syt } 73968d33a25Syt 74068d33a25Syt attach_state |= AHCI_ATTACH_STATE_PORT_ALLOC; 74168d33a25Syt 74268d33a25Syt /* 74368d33a25Syt * A taskq is created for dealing with events 74468d33a25Syt */ 74568d33a25Syt if ((ahci_ctlp->ahcictl_event_taskq = ddi_taskq_create(dip, 74668d33a25Syt "ahci_event_handle_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL) { 747a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ddi_taskq_create failed for event " 748a9440e8dSyt "handle", instance); 74968d33a25Syt goto err_out; 75068d33a25Syt } 75168d33a25Syt 75268d33a25Syt attach_state |= AHCI_ATTACH_STATE_ERR_RECV_TASKQ; 75368d33a25Syt 7542fcbc377Syt /* 75568d33a25Syt * Initialize the controller and ports. 7562fcbc377Syt */ 7572fcbc377Syt status = ahci_initialize_controller(ahci_ctlp); 7582fcbc377Syt if (status != AHCI_SUCCESS) { 759a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: HBA initialization failed", 760a9440e8dSyt instance); 7612fcbc377Syt goto err_out; 7622fcbc377Syt } 7632fcbc377Syt 7642fcbc377Syt attach_state |= AHCI_ATTACH_STATE_HW_INIT; 7652fcbc377Syt 7662fcbc377Syt /* Start one thread to check packet timeouts */ 7672fcbc377Syt ahci_ctlp->ahcictl_timeout_id = timeout( 7682fcbc377Syt (void (*)(void *))ahci_watchdog_handler, 7692fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 7702fcbc377Syt 7712fcbc377Syt attach_state |= AHCI_ATTACH_STATE_TIMEOUT_ENABLED; 7722fcbc377Syt 7732fcbc377Syt if (ahci_register_sata_hba_tran(ahci_ctlp, cap_status)) { 774a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: sata hba tran registration failed", 775a9440e8dSyt instance); 7762fcbc377Syt goto err_out; 7772fcbc377Syt } 7782fcbc377Syt 77913bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~AHCI_ATTACH; 78013bcbb7aSyt 7812fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, "ahci_attach success!"); 7822fcbc377Syt 7832fcbc377Syt return (DDI_SUCCESS); 7842fcbc377Syt 7852fcbc377Syt err_out: 7862fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_TIMEOUT_ENABLED) { 7872fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 7882fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 7892fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 7902fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 7912fcbc377Syt } 7922fcbc377Syt 7932fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_HW_INIT) { 79468d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 7952fcbc377Syt } 7962fcbc377Syt 79768d33a25Syt if (attach_state & AHCI_ATTACH_STATE_ERR_RECV_TASKQ) { 79868d33a25Syt ddi_taskq_destroy(ahci_ctlp->ahcictl_event_taskq); 79968d33a25Syt } 80068d33a25Syt 80168d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PORT_ALLOC) { 80268d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 80368d33a25Syt } 80468d33a25Syt 8052fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_MUTEX_INIT) { 8062fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 8072fcbc377Syt } 8082fcbc377Syt 8092fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_INTR_ADDED) { 8102fcbc377Syt ahci_rem_intrs(ahci_ctlp); 8112fcbc377Syt } 8122fcbc377Syt 81368d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PCICFG_SETUP) { 81468d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 81568d33a25Syt } 81668d33a25Syt 8172fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_REG_MAP) { 8182fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 8192fcbc377Syt } 8202fcbc377Syt 8212fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_STATEP_ALLOC) { 8222fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 8232fcbc377Syt } 8242fcbc377Syt 8252fcbc377Syt return (DDI_FAILURE); 8262fcbc377Syt } 8272fcbc377Syt 8282fcbc377Syt /* 8292fcbc377Syt * The detach entry point for dev_ops. 8302fcbc377Syt */ 8312fcbc377Syt static int 8322fcbc377Syt ahci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 8332fcbc377Syt { 8342fcbc377Syt ahci_ctl_t *ahci_ctlp; 8352fcbc377Syt int instance; 8362fcbc377Syt int ret; 8372fcbc377Syt 8382fcbc377Syt instance = ddi_get_instance(dip); 8392fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 8402fcbc377Syt 8412fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_detach enter"); 8422fcbc377Syt 8432fcbc377Syt switch (cmd) { 8442fcbc377Syt case DDI_DETACH: 84513bcbb7aSyt 8462fcbc377Syt /* disable the interrupts for an uninterrupted detach */ 8472fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8482fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 8492fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 8502fcbc377Syt 8512fcbc377Syt /* unregister from the sata framework. */ 8522fcbc377Syt ret = ahci_unregister_sata_hba_tran(ahci_ctlp); 8532fcbc377Syt if (ret != AHCI_SUCCESS) { 8542fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8552fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 8562fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 8572fcbc377Syt return (DDI_FAILURE); 8582fcbc377Syt } 8592fcbc377Syt 8602fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 8612fcbc377Syt 8622fcbc377Syt /* stop the watchdog handler */ 8632fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 8642fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 8652fcbc377Syt 86668d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 86768d33a25Syt 86868d33a25Syt /* uninitialize the controller */ 86968d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 8702fcbc377Syt 87168d33a25Syt /* remove the interrupts */ 87268d33a25Syt ahci_rem_intrs(ahci_ctlp); 8732fcbc377Syt 87468d33a25Syt /* destroy the taskq */ 87568d33a25Syt ddi_taskq_destroy(ahci_ctlp->ahcictl_event_taskq); 8762fcbc377Syt 87768d33a25Syt /* deallocate the ports structures */ 87868d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 87968d33a25Syt 88068d33a25Syt /* destroy mutex */ 8812fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 8822fcbc377Syt 88368d33a25Syt /* teardown the pci config */ 88468d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 8852fcbc377Syt 8862fcbc377Syt /* remove the reg maps. */ 8872fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 8882fcbc377Syt 8892fcbc377Syt /* free the soft state. */ 8902fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 8912fcbc377Syt 8922fcbc377Syt return (DDI_SUCCESS); 8932fcbc377Syt 8942fcbc377Syt case DDI_SUSPEND: 89513bcbb7aSyt 89613bcbb7aSyt /* 89713bcbb7aSyt * The steps associated with suspension must include putting 89813bcbb7aSyt * the underlying device into a quiescent state so that it 89913bcbb7aSyt * will not generate interrupts or modify or access memory. 90013bcbb7aSyt */ 90113bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 90213bcbb7aSyt if (ahci_ctlp->ahcictl_flags & AHCI_SUSPEND) { 90313bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 90413bcbb7aSyt return (DDI_SUCCESS); 90513bcbb7aSyt } 90613bcbb7aSyt 90713bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_SUSPEND; 90813bcbb7aSyt 90913bcbb7aSyt /* stop the watchdog handler */ 91013bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id) { 91113bcbb7aSyt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 91213bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = 0; 91313bcbb7aSyt } 91413bcbb7aSyt 91513bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 91613bcbb7aSyt 91713bcbb7aSyt /* 91813bcbb7aSyt * drain the taskq 91913bcbb7aSyt */ 92013bcbb7aSyt ddi_taskq_wait(ahci_ctlp->ahcictl_event_taskq); 92113bcbb7aSyt 92213bcbb7aSyt /* 92313bcbb7aSyt * Disable the interrupts and stop all the ports. 92413bcbb7aSyt */ 92513bcbb7aSyt ahci_uninitialize_controller(ahci_ctlp); 92613bcbb7aSyt 92713bcbb7aSyt return (DDI_SUCCESS); 9282fcbc377Syt 9292fcbc377Syt default: 9302fcbc377Syt return (DDI_FAILURE); 9312fcbc377Syt } 9322fcbc377Syt } 9332fcbc377Syt 9342fcbc377Syt /* 9352fcbc377Syt * The info entry point for dev_ops. 9362fcbc377Syt * 9372fcbc377Syt */ 9382fcbc377Syt static int 9392fcbc377Syt ahci_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 9402fcbc377Syt void *arg, void **result) 9412fcbc377Syt { 9422fcbc377Syt #ifndef __lock_lint 9432fcbc377Syt _NOTE(ARGUNUSED(dip)) 9442fcbc377Syt #endif /* __lock_lint */ 9452fcbc377Syt 9462fcbc377Syt ahci_ctl_t *ahci_ctlp; 9472fcbc377Syt int instance; 9482fcbc377Syt dev_t dev; 9492fcbc377Syt 9502fcbc377Syt dev = (dev_t)arg; 9512fcbc377Syt instance = getminor(dev); 9522fcbc377Syt 9532fcbc377Syt switch (infocmd) { 9542fcbc377Syt case DDI_INFO_DEVT2DEVINFO: 9552fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 9562fcbc377Syt if (ahci_ctlp != NULL) { 9572fcbc377Syt *result = ahci_ctlp->ahcictl_dip; 9582fcbc377Syt return (DDI_SUCCESS); 9592fcbc377Syt } else { 9602fcbc377Syt *result = NULL; 9612fcbc377Syt return (DDI_FAILURE); 9622fcbc377Syt } 9632fcbc377Syt case DDI_INFO_DEVT2INSTANCE: 9642fcbc377Syt *(int *)result = instance; 9652fcbc377Syt break; 9662fcbc377Syt default: 9672fcbc377Syt break; 9682fcbc377Syt } 9692fcbc377Syt 9702fcbc377Syt return (DDI_SUCCESS); 9712fcbc377Syt } 9722fcbc377Syt 9732fcbc377Syt /* 9742fcbc377Syt * Registers the ahci with sata framework. 9752fcbc377Syt */ 9762fcbc377Syt static int 9772fcbc377Syt ahci_register_sata_hba_tran(ahci_ctl_t *ahci_ctlp, uint32_t cap_status) 9782fcbc377Syt { 9792fcbc377Syt struct sata_hba_tran *sata_hba_tran; 9802fcbc377Syt 9812fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 9822fcbc377Syt "ahci_register_sata_hba_tran enter"); 9832fcbc377Syt 9842fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9852fcbc377Syt 9862fcbc377Syt /* Allocate memory for the sata_hba_tran */ 9872fcbc377Syt sata_hba_tran = kmem_zalloc(sizeof (sata_hba_tran_t), KM_SLEEP); 9882fcbc377Syt 98968d33a25Syt sata_hba_tran->sata_tran_hba_rev = SATA_TRAN_HBA_REV_2; 9902fcbc377Syt sata_hba_tran->sata_tran_hba_dip = ahci_ctlp->ahcictl_dip; 9912fcbc377Syt sata_hba_tran->sata_tran_hba_dma_attr = 9922fcbc377Syt &ahci_ctlp->ahcictl_buffer_dma_attr; 9932fcbc377Syt 9942fcbc377Syt /* Report the number of implemented ports */ 9952fcbc377Syt sata_hba_tran->sata_tran_hba_num_cports = 9962fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports; 9972fcbc377Syt 99868d33a25Syt /* Support ATAPI device */ 99968d33a25Syt sata_hba_tran->sata_tran_hba_features_support = SATA_CTLF_ATAPI; 10002fcbc377Syt 10012fcbc377Syt /* Get the data transfer capability for PIO command by the HBA */ 10022fcbc377Syt if (cap_status & AHCI_HBA_CAP_PMD) { 100382263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PIO_MDRQ; 10042fcbc377Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "HBA supports multiple " 10052fcbc377Syt "DRQ block data transfer for PIO command protocol"); 10062fcbc377Syt } 10072fcbc377Syt 100882263d52Syt /* 100982263d52Syt * According to the AHCI spec, the ATA/ATAPI-7 queued feature set 101082263d52Syt * is not supported by AHCI (including the READ QUEUED (EXT), WRITE 101182263d52Syt * QUEUED (EXT), and SERVICE commands). Queued operations are 101282263d52Syt * supported in AHCI using the READ FPDMA QUEUED and WRITE FPDMA 101382263d52Syt * QUEUED commands when the HBA and device support native command 101482263d52Syt * queuing(NCQ). 101582263d52Syt * 101682263d52Syt * SATA_CTLF_NCQ will be set to sata_tran_hba_features_support if the 101782263d52Syt * CAP register of the HBA indicates NCQ is supported. 101882263d52Syt * 101982263d52Syt * SATA_CTLF_NCQ cannot be set if AHCI_CAP_NO_MCMDLIST_NONQUEUE is 102082263d52Syt * set because the previous register content of PxCI can be re-written 102182263d52Syt * in the register write. 102282263d52Syt */ 102382263d52Syt if ((cap_status & AHCI_HBA_CAP_SNCQ) && 102482263d52Syt !(ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE)) { 102582263d52Syt sata_hba_tran->sata_tran_hba_features_support |= SATA_CTLF_NCQ; 102682263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_NCQ; 102782263d52Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "HBA supports Native " 102882263d52Syt "Command Queuing"); 102982263d52Syt } 103082263d52Syt 10312fcbc377Syt /* Report the number of command slots */ 10322fcbc377Syt sata_hba_tran->sata_tran_hba_qdepth = ahci_ctlp->ahcictl_num_cmd_slots; 10332fcbc377Syt 10342fcbc377Syt sata_hba_tran->sata_tran_probe_port = ahci_tran_probe_port; 10352fcbc377Syt sata_hba_tran->sata_tran_start = ahci_tran_start; 10362fcbc377Syt sata_hba_tran->sata_tran_abort = ahci_tran_abort; 10372fcbc377Syt sata_hba_tran->sata_tran_reset_dport = ahci_tran_reset_dport; 10382fcbc377Syt sata_hba_tran->sata_tran_hotplug_ops = &ahci_tran_hotplug_ops; 10392fcbc377Syt #ifdef __lock_lint 10402fcbc377Syt sata_hba_tran->sata_tran_selftest = ahci_selftest; 10412fcbc377Syt #endif 10422fcbc377Syt /* 10432fcbc377Syt * When SATA framework adds support for pwrmgt the 10442fcbc377Syt * pwrmgt_ops needs to be updated 10452fcbc377Syt */ 10462fcbc377Syt sata_hba_tran->sata_tran_pwrmgt_ops = NULL; 10472fcbc377Syt sata_hba_tran->sata_tran_ioctl = NULL; 10482fcbc377Syt 10492fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = sata_hba_tran; 10502fcbc377Syt 10512fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 10522fcbc377Syt 10532fcbc377Syt /* Attach it to SATA framework */ 10542fcbc377Syt if (sata_hba_attach(ahci_ctlp->ahcictl_dip, sata_hba_tran, DDI_ATTACH) 10552fcbc377Syt != DDI_SUCCESS) { 10562fcbc377Syt kmem_free((void *)sata_hba_tran, sizeof (sata_hba_tran_t)); 10572fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 10582fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 10592fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 10602fcbc377Syt return (AHCI_FAILURE); 10612fcbc377Syt } 10622fcbc377Syt 10632fcbc377Syt return (AHCI_SUCCESS); 10642fcbc377Syt } 10652fcbc377Syt 10662fcbc377Syt /* 10672fcbc377Syt * Unregisters the ahci with sata framework. 10682fcbc377Syt */ 10692fcbc377Syt static int 10702fcbc377Syt ahci_unregister_sata_hba_tran(ahci_ctl_t *ahci_ctlp) 10712fcbc377Syt { 10722fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, 10732fcbc377Syt "ahci_unregister_sata_hba_tran enter"); 10742fcbc377Syt 10752fcbc377Syt /* Detach from the SATA framework. */ 10762fcbc377Syt if (sata_hba_detach(ahci_ctlp->ahcictl_dip, DDI_DETACH) != 10772fcbc377Syt DDI_SUCCESS) { 10782fcbc377Syt return (AHCI_FAILURE); 10792fcbc377Syt } 10802fcbc377Syt 10812fcbc377Syt /* Deallocate sata_hba_tran. */ 10822fcbc377Syt kmem_free((void *)ahci_ctlp->ahcictl_sata_hba_tran, 10832fcbc377Syt sizeof (sata_hba_tran_t)); 10842fcbc377Syt 10852fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 10862fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 10872fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 10882fcbc377Syt 10892fcbc377Syt return (AHCI_SUCCESS); 10902fcbc377Syt } 10912fcbc377Syt 10922fcbc377Syt /* 10932fcbc377Syt * ahci_tran_probe_port is called by SATA framework. It returns port state, 10942fcbc377Syt * port status registers and an attached device type via sata_device 10952fcbc377Syt * structure. 10962fcbc377Syt * 10972fcbc377Syt * We return the cached information from a previous hardware probe. The 10982fcbc377Syt * actual hardware probing itself was done either from within 10992fcbc377Syt * ahci_initialize_controller() during the driver attach or from a phy 11002fcbc377Syt * ready change interrupt handler. 11012fcbc377Syt */ 11022fcbc377Syt static int 11032fcbc377Syt ahci_tran_probe_port(dev_info_t *dip, sata_device_t *sd) 11042fcbc377Syt { 11052fcbc377Syt ahci_ctl_t *ahci_ctlp; 11062fcbc377Syt ahci_port_t *ahci_portp; 11072fcbc377Syt uint8_t cport = sd->satadev_addr.cport; 1108689d74b0Syt #if AHCI_DEBUG 11092fcbc377Syt uint8_t pmport = sd->satadev_addr.pmport; 11102fcbc377Syt uint8_t qual = sd->satadev_addr.qual; 1111689d74b0Syt #endif 11122fcbc377Syt uint8_t device_type; 11132fcbc377Syt uint32_t port_state; 11142fcbc377Syt uint8_t port; 11152fcbc377Syt 11162fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 11172fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 11182fcbc377Syt 11192fcbc377Syt AHCIDBG3(AHCIDBG_ENTRY, ahci_ctlp, 112068d33a25Syt "ahci_tran_probe_port enter: cport: %d, " 112168d33a25Syt "pmport: %d, qual: %d", cport, pmport, qual); 11222fcbc377Syt 11232fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 11242fcbc377Syt 11252fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 11262fcbc377Syt 11272fcbc377Syt port_state = ahci_portp->ahciport_port_state; 11282fcbc377Syt switch (port_state) { 11292fcbc377Syt 11302fcbc377Syt case SATA_PSTATE_FAILED: 11312fcbc377Syt sd->satadev_state = SATA_PSTATE_FAILED; 11322fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 113368d33a25Syt "ahci_tran_probe_port: port %d PORT FAILED", port); 11342fcbc377Syt goto out; 11352fcbc377Syt 11362fcbc377Syt case SATA_PSTATE_SHUTDOWN: 11372fcbc377Syt sd->satadev_state = SATA_PSTATE_SHUTDOWN; 11382fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 113968d33a25Syt "ahci_tran_probe_port: port %d PORT SHUTDOWN", port); 11402fcbc377Syt goto out; 11412fcbc377Syt 11422fcbc377Syt case SATA_PSTATE_PWROFF: 11432fcbc377Syt sd->satadev_state = SATA_PSTATE_PWROFF; 11442fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 114568d33a25Syt "ahci_tran_probe_port: port %d PORT PWROFF", port); 11462fcbc377Syt goto out; 11472fcbc377Syt 11482fcbc377Syt case SATA_PSTATE_PWRON: 11492fcbc377Syt sd->satadev_state = SATA_PSTATE_PWRON; 11502fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 115168d33a25Syt "ahci_tran_probe_port: port %d PORT PWRON", port); 11522fcbc377Syt break; 11532fcbc377Syt 11542fcbc377Syt default: 11552fcbc377Syt sd->satadev_state = port_state; 11562fcbc377Syt AHCIDBG2(AHCIDBG_INFO, ahci_ctlp, 115768d33a25Syt "ahci_tran_probe_port: port %d PORT NORMAL %x", 11582fcbc377Syt port, port_state); 11592fcbc377Syt break; 11602fcbc377Syt } 11612fcbc377Syt 11622fcbc377Syt device_type = ahci_portp->ahciport_device_type; 11632fcbc377Syt 11642fcbc377Syt switch (device_type) { 11652fcbc377Syt 11662fcbc377Syt case SATA_DTYPE_ATADISK: 11672fcbc377Syt sd->satadev_type = SATA_DTYPE_ATADISK; 11682fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 116968d33a25Syt "ahci_tran_probe_port: port %d DISK found", port); 11702fcbc377Syt break; 11712fcbc377Syt 11722fcbc377Syt case SATA_DTYPE_ATAPICD: 11732fcbc377Syt sd->satadev_type = SATA_DTYPE_ATAPICD; 11742fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 117568d33a25Syt "ahci_tran_probe_port: port %d ATAPI found", port); 11762fcbc377Syt break; 11772fcbc377Syt 11782fcbc377Syt case SATA_DTYPE_PMULT: 11792fcbc377Syt sd->satadev_type = SATA_DTYPE_PMULT; 11802fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 118168d33a25Syt "ahci_tran_probe_port: port %d Port Multiplier found", 11822fcbc377Syt port); 11832fcbc377Syt break; 11842fcbc377Syt 11852fcbc377Syt case SATA_DTYPE_UNKNOWN: 11862fcbc377Syt sd->satadev_type = SATA_DTYPE_UNKNOWN; 11872fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 118868d33a25Syt "ahci_tran_probe_port: port %d Unknown device found", port); 11892fcbc377Syt break; 11902fcbc377Syt 11912fcbc377Syt default: 11922fcbc377Syt /* we don't support any other device types */ 11932fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 11942fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 119568d33a25Syt "ahci_tran_probe_port: port %d No device found", port); 11962fcbc377Syt break; 11972fcbc377Syt } 11982fcbc377Syt 11992fcbc377Syt out: 12002fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 12012fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 12022fcbc377Syt 12032fcbc377Syt return (SATA_SUCCESS); 12042fcbc377Syt } 12052fcbc377Syt 120668d33a25Syt /* 120768d33a25Syt * There are four operation modes in sata framework: 120868d33a25Syt * SATA_OPMODE_INTERRUPTS 120968d33a25Syt * SATA_OPMODE_POLLING 121068d33a25Syt * SATA_OPMODE_ASYNCH 121168d33a25Syt * SATA_OPMODE_SYNCH 121268d33a25Syt * 121368d33a25Syt * Their combined meanings as following: 121468d33a25Syt * 121568d33a25Syt * SATA_OPMODE_SYNCH 121668d33a25Syt * The command has to be completed before sata_tran_start functions returns. 121768d33a25Syt * Either interrupts or polling could be used - it's up to the driver. 121868d33a25Syt * Mode used currently for internal, sata-module initiated operations. 121968d33a25Syt * 122068d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS 122168d33a25Syt * It is the same as the one above. 122268d33a25Syt * 122368d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING 122468d33a25Syt * The command has to be completed before sata_tran_start function returns. 122568d33a25Syt * No interrupt used, polling only. This should be the mode used for scsi 122668d33a25Syt * packets with FLAG_NOINTR. 122768d33a25Syt * 122868d33a25Syt * SATA_OPMODE_ASYNCH | SATA_OPMODE_INTERRUPTS 122968d33a25Syt * The command may be queued (callback function specified). Interrupts could 123068d33a25Syt * be used. It's normal operation mode. 123168d33a25Syt */ 12322fcbc377Syt /* 12332fcbc377Syt * Called by sata framework to transport a sata packet down stream. 12342fcbc377Syt */ 12352fcbc377Syt static int 12362fcbc377Syt ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt) 12372fcbc377Syt { 12382fcbc377Syt ahci_ctl_t *ahci_ctlp; 12392fcbc377Syt ahci_port_t *ahci_portp; 12402fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 12412fcbc377Syt uint8_t port; 12422fcbc377Syt 12432fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 12442fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 12452fcbc377Syt 12462fcbc377Syt AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, 12472fcbc377Syt "ahci_tran_start enter: cport %d satapkt 0x%p", 12482fcbc377Syt cport, (void *)spkt); 12492fcbc377Syt 12502fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 12512fcbc377Syt 12522fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 12532fcbc377Syt 12542fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 12552fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 12562fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 12572fcbc377Syt /* 12582fcbc377Syt * In case the targer driver would send the packet before 12592fcbc377Syt * sata framework can have the opportunity to process those 12602fcbc377Syt * event reports. 12612fcbc377Syt */ 12622fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 12632fcbc377Syt spkt->satapkt_device.satadev_state = 12642fcbc377Syt ahci_portp->ahciport_port_state; 12652fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 12662fcbc377Syt &spkt->satapkt_device); 12672fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 12682fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 12692fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 127068d33a25Syt "port: %d", port); 12712fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 12722fcbc377Syt return (SATA_TRAN_PORT_ERROR); 12732fcbc377Syt } 12742fcbc377Syt 12752fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 12762fcbc377Syt /* 12772fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 12782fcbc377Syt * SATA_DTYPE_NONE. 12792fcbc377Syt */ 12802fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 12812fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 12822fcbc377Syt spkt->satapkt_device.satadev_state = 12832fcbc377Syt ahci_portp->ahciport_port_state; 12842fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 12852fcbc377Syt &spkt->satapkt_device); 12862fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 12872fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 128868d33a25Syt "no device attached: port: %d", port); 12892fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 12902fcbc377Syt return (SATA_TRAN_PORT_ERROR); 12912fcbc377Syt } 12922fcbc377Syt 12932fcbc377Syt /* 12942fcbc377Syt * SATA HBA driver should remember that a device was reset and it 12952fcbc377Syt * is supposed to reject any packets which do not specify either 12962fcbc377Syt * SATA_IGNORE_DEV_RESET_STATE or SATA_CLEAR_DEV_RESET_STATE. 12972fcbc377Syt * 12982fcbc377Syt * This is to prevent a race condition when a device was arbitrarily 12992fcbc377Syt * reset by the HBA driver (and lost it's setting) and a target 13002fcbc377Syt * driver sending some commands to a device before the sata framework 13012fcbc377Syt * has a chance to restore the device setting (such as cache enable/ 13022fcbc377Syt * disable or other resettable stuff). 13032fcbc377Syt */ 13042fcbc377Syt if (spkt->satapkt_cmd.satacmd_flags.sata_clear_dev_reset) { 13052fcbc377Syt ahci_portp->ahciport_reset_in_progress = 0; 13062fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13072fcbc377Syt "ahci_tran_start clearing the " 130868d33a25Syt "reset_in_progress for port: %d", port); 13092fcbc377Syt } 13102fcbc377Syt 13112fcbc377Syt if (ahci_portp->ahciport_reset_in_progress && 13122fcbc377Syt ! spkt->satapkt_cmd.satacmd_flags.sata_ignore_dev_reset && 13132fcbc377Syt ! ddi_in_panic()) { 13142fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 13152fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13162fcbc377Syt "ahci_tran_start returning BUSY while " 131768d33a25Syt "reset in progress: port: %d", port); 13182fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13192fcbc377Syt return (SATA_TRAN_BUSY); 13202fcbc377Syt } 13212fcbc377Syt 132268d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 13232fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 13242fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 13252fcbc377Syt "ahci_tran_start returning BUSY while " 132668d33a25Syt "mopping in progress: port: %d", port); 13272fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 13282fcbc377Syt return (SATA_TRAN_BUSY); 13292fcbc377Syt } 13302fcbc377Syt 13312fcbc377Syt if (spkt->satapkt_op_mode & 133268d33a25Syt (SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING)) { 1333*b2e3645aSying tian - Beijing China /* 1334*b2e3645aSying tian - Beijing China * If a SYNC command to be executed in interrupt context, 1335*b2e3645aSying tian - Beijing China * bounce it back to sata module. 1336*b2e3645aSying tian - Beijing China */ 1337*b2e3645aSying tian - Beijing China if (!(spkt->satapkt_op_mode & SATA_OPMODE_POLLING) && 1338*b2e3645aSying tian - Beijing China servicing_interrupt()) { 1339*b2e3645aSying tian - Beijing China spkt->satapkt_reason = SATA_PKT_BUSY; 1340*b2e3645aSying tian - Beijing China AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 1341*b2e3645aSying tian - Beijing China "ahci_tran_start returning BUSY while " 1342*b2e3645aSying tian - Beijing China "sending SYNC mode under interrupt context: " 1343*b2e3645aSying tian - Beijing China "port : %d", port); 1344*b2e3645aSying tian - Beijing China mutex_exit(&ahci_portp->ahciport_mutex); 1345*b2e3645aSying tian - Beijing China return (SATA_TRAN_BUSY); 1346*b2e3645aSying tian - Beijing China } 1347*b2e3645aSying tian - Beijing China 134868d33a25Syt /* We need to do the sync start now */ 134968d33a25Syt if (ahci_do_sync_start(ahci_ctlp, ahci_portp, port, 135068d33a25Syt spkt) == AHCI_FAILURE) { 135168d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 135268d33a25Syt "return QUEUE_FULL: port %d", port); 135368d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 135468d33a25Syt return (SATA_TRAN_QUEUE_FULL); 135568d33a25Syt } 135668d33a25Syt } else { 135768d33a25Syt /* Async start, using interrupt */ 135868d33a25Syt if (ahci_deliver_satapkt(ahci_ctlp, ahci_portp, port, spkt) 135968d33a25Syt == AHCI_FAILURE) { 136068d33a25Syt spkt->satapkt_reason = SATA_PKT_QUEUE_FULL; 136168d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 136268d33a25Syt "returning QUEUE_FULL: port %d", port); 136368d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 136468d33a25Syt return (SATA_TRAN_QUEUE_FULL); 136568d33a25Syt } 13662fcbc377Syt } 13672fcbc377Syt 13682fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_tran_start " 13692fcbc377Syt "sata tran accepted: port %d", port); 13702fcbc377Syt 137168d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 13722fcbc377Syt return (SATA_TRAN_ACCEPTED); 13732fcbc377Syt } 13742fcbc377Syt 137568d33a25Syt /* 137668d33a25Syt * SATA_OPMODE_SYNCH flag is set 137768d33a25Syt * 137868d33a25Syt * If SATA_OPMODE_POLLING flag is set, then we must poll the command 137968d33a25Syt * without interrupt, otherwise we can still use the interrupt. 138068d33a25Syt * 138168d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function 138268d33a25Syt * is called. 138368d33a25Syt */ 138468d33a25Syt static int 138568d33a25Syt ahci_do_sync_start(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 138668d33a25Syt uint8_t port, sata_pkt_t *spkt) 138768d33a25Syt { 138868d33a25Syt int pkt_timeout_ticks; 138968d33a25Syt uint32_t timeout_tags; 139068d33a25Syt int rval; 1391a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 139268d33a25Syt 139368d33a25Syt AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, "ahci_do_sync_start enter: " 139468d33a25Syt "port %d spkt 0x%p", port, spkt); 139568d33a25Syt 139668d33a25Syt if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) { 139768d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_POLLING; 139868d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 139968d33a25Syt port, spkt)) == AHCI_FAILURE) { 140068d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_POLLING; 140168d33a25Syt return (rval); 140268d33a25Syt } 140368d33a25Syt 140468d33a25Syt pkt_timeout_ticks = 140568d33a25Syt drv_usectohz((clock_t)spkt->satapkt_time * 1000000); 140668d33a25Syt 140768d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) { 140868d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 140968d33a25Syt 141068d33a25Syt /* Simulate the interrupt */ 141182263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 141268d33a25Syt 141368d33a25Syt drv_usecwait(AHCI_1MS_USECS); 141468d33a25Syt 141568d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 141668d33a25Syt pkt_timeout_ticks -= AHCI_1MS_TICKS; 141768d33a25Syt if (pkt_timeout_ticks < 0) { 1418a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_do_sync_start " 141968d33a25Syt "port %d satapkt 0x%p timed out\n", 1420a9440e8dSyt instance, port, (void *)spkt); 142168d33a25Syt timeout_tags = (0x1 << rval); 142268d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 142368d33a25Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 142482263d52Syt port, timeout_tags); 142568d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 142668d33a25Syt } 142768d33a25Syt } 142868d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_POLLING; 142968d33a25Syt return (AHCI_SUCCESS); 143068d33a25Syt 143168d33a25Syt } else { 143268d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 143368d33a25Syt port, spkt)) == AHCI_FAILURE) 143468d33a25Syt return (rval); 143568d33a25Syt 143682263d52Syt #if AHCI_DEBUG 143782263d52Syt /* 143882263d52Syt * Note that the driver always uses the slot 0 to deliver 143982263d52Syt * REQUEST SENSE or READ LOG EXT command 144082263d52Syt */ 144182263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 144282263d52Syt ASSERT(rval == 0); 144382263d52Syt #endif 144482263d52Syt 144568d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) 144668d33a25Syt cv_wait(&ahci_portp->ahciport_cv, 144768d33a25Syt &ahci_portp->ahciport_mutex); 144868d33a25Syt 144968d33a25Syt return (AHCI_SUCCESS); 145068d33a25Syt } 145168d33a25Syt } 145268d33a25Syt 14532fcbc377Syt #define SENDUP_PACKET(ahci_portp, satapkt, reason) \ 14542fcbc377Syt if (satapkt) { \ 14552fcbc377Syt satapkt->satapkt_reason = reason; \ 14562fcbc377Syt /* \ 14572fcbc377Syt * We set the satapkt_reason in both sync and \ 14582fcbc377Syt * non-sync cases. \ 14592fcbc377Syt */ \ 14602fcbc377Syt } \ 14612fcbc377Syt if (satapkt && \ 146268d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 14632fcbc377Syt satapkt->satapkt_comp) { \ 14642fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); \ 14652fcbc377Syt (*satapkt->satapkt_comp)(satapkt); \ 14662fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); \ 146768d33a25Syt } else { \ 146868d33a25Syt if (satapkt && \ 146968d33a25Syt (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 147068d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_POLLING)) \ 147168d33a25Syt cv_signal(&ahci_portp->ahciport_cv); \ 14722fcbc377Syt } 14732fcbc377Syt 14742fcbc377Syt /* 147582263d52Syt * Searches for and claims a free command slot. 147682263d52Syt * 147782263d52Syt * Returns: 14782fcbc377Syt * 147982263d52Syt * AHCI_FAILURE if failed 148082263d52Syt * 1. if no empty slot left 148182263d52Syt * 2. non-queued command requested while queued command(s) is outstanding 148282263d52Syt * 3. queued command requested whild non-queued command(s) is outstanding 148382263d52Syt * 4. HBA doesn't support multiple-use of command list while already a 148482263d52Syt * non-queued command is oustanding 148582263d52Syt * 148682263d52Syt * claimed slot number if succeeded 148782263d52Syt * 148882263d52Syt * NOTE: it will always return slot 0 during error recovery process for 148982263d52Syt * REQUEST SENSE or READ LOG EXT command to simplify the algorithm. 14902fcbc377Syt * 14912fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 14922fcbc377Syt * is called. 14932fcbc377Syt */ 14942fcbc377Syt static int 149582263d52Syt ahci_claim_free_slot(ahci_ctl_t *ahci_ctlp, 149682263d52Syt ahci_port_t *ahci_portp, int command_type) 14972fcbc377Syt { 14982fcbc377Syt uint32_t free_slots; 14992fcbc377Syt int slot; 15002fcbc377Syt 150182263d52Syt AHCIDBG2(AHCIDBG_ENTRY, ahci_ctlp, "ahci_claim_free_slot enter " 150282263d52Syt "ahciport_pending_tags = 0x%x " 150382263d52Syt "ahciport_pending_ncq_tags = 0x%x", 150482263d52Syt ahci_portp->ahciport_pending_tags, 150582263d52Syt ahci_portp->ahciport_pending_ncq_tags); 15062fcbc377Syt 150782263d52Syt /* 150882263d52Syt * According to the AHCI spec, system software is responsible to 150982263d52Syt * ensure that queued and non-queued commands are not mixed in 151082263d52Syt * the command list. 151182263d52Syt */ 151282263d52Syt if (command_type == AHCI_NON_NCQ_CMD) { 151382263d52Syt /* Non-NCQ command request */ 151482263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 151582263d52Syt AHCIDBG0(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 151682263d52Syt "ahci_claim_free_slot: there is still pending " 151782263d52Syt "queued command(s) in the command list, " 151882263d52Syt "so no available slot for the non-queued " 151982263d52Syt "command"); 152082263d52Syt return (AHCI_FAILURE); 152182263d52Syt } 152282263d52Syt if ((ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE) && 152382263d52Syt NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 152482263d52Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, 152582263d52Syt "ahci_claim_free_slot: HBA cannot support multiple-" 152682263d52Syt "use of the command list for non-queued commands"); 152782263d52Syt return (AHCI_FAILURE); 152882263d52Syt } 152982263d52Syt free_slots = (~ahci_portp->ahciport_pending_tags) & 153082263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 153182263d52Syt } else if (command_type == AHCI_NCQ_CMD) { 153282263d52Syt /* NCQ command request */ 153382263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 153482263d52Syt AHCIDBG0(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 153582263d52Syt "ahci_claim_free_slot: there is still pending " 153682263d52Syt "non-queued command(s) in the command list, " 153782263d52Syt "so no available slot for the queued command"); 153882263d52Syt return (AHCI_FAILURE); 153982263d52Syt } 154082263d52Syt free_slots = (~ahci_portp->ahciport_pending_ncq_tags) & 154182263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 154282263d52Syt } else if (command_type == AHCI_ERR_RETRI_CMD) { 154382263d52Syt /* Error retrieval command request */ 154482263d52Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 154582263d52Syt "ahci_claim_free_slot: slot 0 is allocated for REQUEST " 154682263d52Syt "SENSE or READ LOG EXT command"); 154782263d52Syt slot = 0; 154882263d52Syt goto out; 154982263d52Syt } 15502fcbc377Syt 15512fcbc377Syt slot = ddi_ffs(free_slots) - 1; 15522fcbc377Syt if (slot == -1) { 15532fcbc377Syt AHCIDBG0(AHCIDBG_VERBOSE, ahci_ctlp, 15542fcbc377Syt "ahci_claim_free_slot: no empty slots"); 15552fcbc377Syt return (AHCI_FAILURE); 15562fcbc377Syt } 15572fcbc377Syt 155882263d52Syt /* 155982263d52Syt * According to the AHCI spec, to allow a simple mechanism for the 156082263d52Syt * HBA to map command list slots to queue entries, software must 156182263d52Syt * match the tag number it uses to the slot it is placing the command 156282263d52Syt * in. For example, if a queued command is placed in slot 5, the tag 156382263d52Syt * for that command must be 5. 156482263d52Syt */ 156582263d52Syt if (command_type == AHCI_NCQ_CMD) { 156682263d52Syt ahci_portp->ahciport_pending_ncq_tags |= (0x1 << slot); 156782263d52Syt } 156882263d52Syt 15692fcbc377Syt ahci_portp->ahciport_pending_tags |= (0x1 << slot); 15702fcbc377Syt 157182263d52Syt out: 15722fcbc377Syt AHCIDBG1(AHCIDBG_VERBOSE, ahci_ctlp, 15732fcbc377Syt "ahci_claim_free_slot: found slot: 0x%x", slot); 15742fcbc377Syt 15752fcbc377Syt return (slot); 15762fcbc377Syt } 15772fcbc377Syt 15782fcbc377Syt /* 15792fcbc377Syt * Builds the Command Table for the sata packet and delivers it to controller. 15802fcbc377Syt * 15812fcbc377Syt * Returns: 15822fcbc377Syt * slot number if we can obtain a slot successfully 15832fcbc377Syt * otherwise, return AHCI_FAILURE 15842fcbc377Syt * 15852fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 15862fcbc377Syt */ 15872fcbc377Syt static int 15882fcbc377Syt ahci_deliver_satapkt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 15892fcbc377Syt uint8_t port, sata_pkt_t *spkt) 15902fcbc377Syt { 159168d33a25Syt int cmd_slot; 159268d33a25Syt sata_cmd_t *scmd; 15932fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 15942fcbc377Syt ahci_cmd_table_t *cmd_table; 15952fcbc377Syt ahci_cmd_header_t *cmd_header; 15962fcbc377Syt int ncookies; 15972fcbc377Syt int i; 159882263d52Syt int command_type = AHCI_NON_NCQ_CMD; 159982263d52Syt int ncq_qdepth; 1600a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 160168d33a25Syt #if AHCI_DEBUG 160268d33a25Syt uint32_t *ptr; 160368d33a25Syt uint8_t *ptr2; 160468d33a25Syt #endif 16052fcbc377Syt 16062fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 16072fcbc377Syt 160868d33a25Syt scmd = &spkt->satapkt_cmd; 16092fcbc377Syt 161082263d52Syt /* Check if the command is a NCQ command */ 161182263d52Syt if (scmd->satacmd_cmd_reg == SATAC_READ_FPDMA_QUEUED || 161282263d52Syt scmd->satacmd_cmd_reg == SATAC_WRITE_FPDMA_QUEUED) { 161382263d52Syt command_type = AHCI_NCQ_CMD; 161482263d52Syt 161582263d52Syt /* 161682263d52Syt * When NCQ is support, system software must determine the 161782263d52Syt * maximum tag allowed by the device and the HBA, and it 161882263d52Syt * must use a value not beyond of the lower bound of the two. 161982263d52Syt * 162082263d52Syt * Sata module is going to calculate the qdepth and send 162182263d52Syt * down to HBA driver via sata_cmd. 162282263d52Syt */ 162382263d52Syt ncq_qdepth = scmd->satacmd_flags.sata_max_queue_depth + 1; 162482263d52Syt 162582263d52Syt /* 162682263d52Syt * At the moment, the driver doesn't support the dynamic 162782263d52Syt * setting of the maximum ncq depth, and the value can be 162882263d52Syt * set either during the attach or after hot-plug insertion. 162982263d52Syt */ 163082263d52Syt if (ahci_portp->ahciport_max_ncq_tags == 0) { 163182263d52Syt ahci_portp->ahciport_max_ncq_tags = ncq_qdepth; 163282263d52Syt AHCIDBG2(AHCIDBG_NCQ, ahci_ctlp, 163382263d52Syt "ahci_deliver_satapkt: port %d the max tags for " 163482263d52Syt "NCQ command is %d", port, ncq_qdepth); 163582263d52Syt } else { 163682263d52Syt if (ncq_qdepth != ahci_portp->ahciport_max_ncq_tags) { 1637a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_deliver_satapkt" 1638a9440e8dSyt " port %d the max tag for NCQ command is " 163982263d52Syt "requested to change from %d to %d, at the" 164082263d52Syt " moment the driver doesn't support the " 164182263d52Syt "dynamic change so it's going to " 1642a9440e8dSyt "still use the previous tag value", 1643a9440e8dSyt instance, port, 164482263d52Syt ahci_portp->ahciport_max_ncq_tags, 164582263d52Syt ncq_qdepth); 164682263d52Syt } 164782263d52Syt } 164882263d52Syt } 164982263d52Syt 165082263d52Syt /* Check if the command is an error retrieval command */ 165182263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 165282263d52Syt command_type = AHCI_ERR_RETRI_CMD; 165382263d52Syt 165482263d52Syt /* Check if there is an empty command slot */ 165582263d52Syt cmd_slot = ahci_claim_free_slot(ahci_ctlp, ahci_portp, command_type); 165668d33a25Syt if (cmd_slot == AHCI_FAILURE) { 165782263d52Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, "no free command slot"); 16582fcbc377Syt return (AHCI_FAILURE); 16592fcbc377Syt } 16602fcbc377Syt 16612fcbc377Syt AHCIDBG4(AHCIDBG_ENTRY|AHCIDBG_INFO, ahci_ctlp, 166268d33a25Syt "ahci_deliver_satapkt enter: cmd_reg: 0x%x, cmd_slot: 0x%x, " 166368d33a25Syt "port: %d, satapkt: 0x%p", scmd->satacmd_cmd_reg, 166468d33a25Syt cmd_slot, port, (void *)spkt); 16652fcbc377Syt 166668d33a25Syt cmd_table = ahci_portp->ahciport_cmd_tables[cmd_slot]; 16672fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 16682fcbc377Syt 166982263d52Syt /* For data transfer operations, it is the H2D Register FIS */ 16702fcbc377Syt h2d_register_fisp = 16712fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 16722fcbc377Syt 16732fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 16742fcbc377Syt if ((spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_PMPORT) || 16752fcbc377Syt (spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_DPMPORT)) { 16762fcbc377Syt SET_FIS_PMP(h2d_register_fisp, 16772fcbc377Syt spkt->satapkt_device.satadev_addr.pmport); 16782fcbc377Syt } 16792fcbc377Syt 16802fcbc377Syt SET_FIS_CDMDEVCTL(h2d_register_fisp, 1); 168168d33a25Syt SET_FIS_COMMAND(h2d_register_fisp, scmd->satacmd_cmd_reg); 168268d33a25Syt SET_FIS_FEATURES(h2d_register_fisp, scmd->satacmd_features_reg); 168368d33a25Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, scmd->satacmd_sec_count_lsb); 16842fcbc377Syt 168568d33a25Syt switch (scmd->satacmd_addr_type) { 16862fcbc377Syt 168782263d52Syt case 0: 168882263d52Syt /* 168982263d52Syt * satacmd_addr_type will be 0 for the commands below: 169082263d52Syt * ATAPI command 169182263d52Syt * SATAC_IDLE_IM 169282263d52Syt * SATAC_STANDBY_IM 169382263d52Syt * SATAC_DOWNLOAD_MICROCODE 169482263d52Syt * SATAC_FLUSH_CACHE 169582263d52Syt * SATAC_SET_FEATURES 169682263d52Syt * SATAC_SMART 169782263d52Syt * SATAC_ID_PACKET_DEVICE 169882263d52Syt * SATAC_ID_DEVICE 169982263d52Syt */ 1700689d74b0Syt /* FALLTHRU */ 170182263d52Syt 17022fcbc377Syt case ATA_ADDR_LBA: 1703689d74b0Syt /* FALLTHRU */ 17042fcbc377Syt 17052fcbc377Syt case ATA_ADDR_LBA28: 17062fcbc377Syt /* LBA[7:0] */ 170768d33a25Syt SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb); 17082fcbc377Syt 17092fcbc377Syt /* LBA[15:8] */ 171068d33a25Syt SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb); 17112fcbc377Syt 17122fcbc377Syt /* LBA[23:16] */ 171368d33a25Syt SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb); 17142fcbc377Syt 17152fcbc377Syt /* LBA [27:24] (also called dev_head) */ 171668d33a25Syt SET_FIS_DEV_HEAD(h2d_register_fisp, scmd->satacmd_device_reg); 17172fcbc377Syt 17182fcbc377Syt break; 17192fcbc377Syt 17202fcbc377Syt case ATA_ADDR_LBA48: 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 [31:24] */ 17312fcbc377Syt SET_FIS_SECTOR_EXP(h2d_register_fisp, 173268d33a25Syt scmd->satacmd_lba_low_msb); 17332fcbc377Syt 17342fcbc377Syt /* LBA [39:32] */ 17352fcbc377Syt SET_FIS_CYL_LOW_EXP(h2d_register_fisp, 173668d33a25Syt scmd->satacmd_lba_mid_msb); 17372fcbc377Syt 17382fcbc377Syt /* LBA [47:40] */ 17392fcbc377Syt SET_FIS_CYL_HI_EXP(h2d_register_fisp, 174068d33a25Syt scmd->satacmd_lba_high_msb); 17412fcbc377Syt 17422fcbc377Syt /* Set dev_head */ 17432fcbc377Syt SET_FIS_DEV_HEAD(h2d_register_fisp, 174468d33a25Syt scmd->satacmd_device_reg); 17452fcbc377Syt 17462fcbc377Syt /* Set the extended sector count and features */ 17472fcbc377Syt SET_FIS_SECTOR_COUNT_EXP(h2d_register_fisp, 174868d33a25Syt scmd->satacmd_sec_count_msb); 17492fcbc377Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 175068d33a25Syt scmd->satacmd_features_reg_ext); 17512fcbc377Syt break; 17522fcbc377Syt } 17532fcbc377Syt 175482263d52Syt /* 175582263d52Syt * For NCQ command (READ/WRITE FPDMA QUEUED), sector count 7:0 is 175682263d52Syt * filled into features field, and sector count 8:15 is filled into 175782263d52Syt * features (exp) field. TAG is filled into sector field. 175882263d52Syt */ 175982263d52Syt if (command_type == AHCI_NCQ_CMD) { 176082263d52Syt SET_FIS_FEATURES(h2d_register_fisp, 176182263d52Syt scmd->satacmd_sec_count_lsb); 176282263d52Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 176382263d52Syt scmd->satacmd_sec_count_msb); 176482263d52Syt 176582263d52Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, 176682263d52Syt (cmd_slot << SATA_TAG_QUEUING_SHIFT)); 176782263d52Syt } 176882263d52Syt 176968d33a25Syt ncookies = scmd->satacmd_num_dma_cookies; 17702fcbc377Syt AHCIDBG2(AHCIDBG_INFO, ahci_ctlp, 17712fcbc377Syt "ncookies = 0x%x, ahci_dma_prdt_number = 0x%x", 17722fcbc377Syt ncookies, ahci_dma_prdt_number); 17732fcbc377Syt 17742fcbc377Syt ASSERT(ncookies <= ahci_dma_prdt_number); 17752fcbc377Syt 17762fcbc377Syt /* *** now fill the scatter gather list ******* */ 17772fcbc377Syt for (i = 0; i < ncookies; i++) { 17782fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr = 177968d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[0]; 17802fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr_upper = 178168d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[1]; 17822fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_descr_info = 178368d33a25Syt scmd->satacmd_dma_cookie_list[i].dmac_size - 1; 178468d33a25Syt } 178568d33a25Syt 178668d33a25Syt /* The ACMD field is filled in for ATAPI command */ 178768d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) { 178868d33a25Syt bcopy(scmd->satacmd_acdb, cmd_table->ahcict_atapi_cmd, 178968d33a25Syt SATA_ATAPI_MAX_CDB_LEN); 17902fcbc377Syt } 17912fcbc377Syt 17922fcbc377Syt /* Set Command Header in Command List */ 179368d33a25Syt cmd_header = &ahci_portp->ahciport_cmd_list[cmd_slot]; 17942fcbc377Syt BZERO_DESCR_INFO(cmd_header); 17952fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 179668d33a25Syt 179768d33a25Syt /* Set the number of entries in the PRD table */ 17982fcbc377Syt SET_PRD_TABLE_LENGTH(cmd_header, ncookies); 179968d33a25Syt 180068d33a25Syt /* Set the length of the command in the CFIS area */ 18012fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, AHCI_H2D_REGISTER_FIS_LENGTH); 18022fcbc377Syt 18032fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "command data direction is " 18042fcbc377Syt "sata_data_direction = 0x%x", 180568d33a25Syt scmd->satacmd_flags.sata_data_direction); 180668d33a25Syt 180768d33a25Syt /* Set A bit if it is an ATAPI command */ 180868d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) 180968d33a25Syt SET_ATAPI(cmd_header, AHCI_CMDHEAD_ATAPI); 18102fcbc377Syt 181168d33a25Syt /* Set W bit if data is going to the device */ 181268d33a25Syt if (scmd->satacmd_flags.sata_data_direction == SATA_DIR_WRITE) 18132fcbc377Syt SET_WRITE(cmd_header, AHCI_CMDHEAD_DATA_WRITE); 18142fcbc377Syt 181568d33a25Syt /* 181668d33a25Syt * Set the prefetchable bit - this bit is only valid if the PRDTL 181768d33a25Syt * field is non-zero or the ATAPI 'A' bit is set in the command 181868d33a25Syt * header. This bit cannot be set when using native command 181968d33a25Syt * queuing commands or when using FIS-based switching with a Port 182082263d52Syt * multiplier. 182168d33a25Syt */ 182282263d52Syt if (command_type != AHCI_NCQ_CMD) 182382263d52Syt SET_PREFETCHABLE(cmd_header, AHCI_CMDHEAD_PREFETCHABLE); 18242fcbc377Syt 18252fcbc377Syt /* Now remember the sata packet in ahciport_slot_pkts[]. */ 182682263d52Syt if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 182782263d52Syt ahci_portp->ahciport_slot_pkts[cmd_slot] = spkt; 18282fcbc377Syt 18292fcbc377Syt /* 18302fcbc377Syt * We are overloading satapkt_hba_driver_private with 18312fcbc377Syt * watched_cycle count. 18322fcbc377Syt */ 18332fcbc377Syt spkt->satapkt_hba_driver_private = (void *)(intptr_t)0; 18342fcbc377Syt 183568d33a25Syt #if AHCI_DEBUG 183668d33a25Syt /* Dump the command header and table */ 183768d33a25Syt AHCIDBG0(AHCIDBG_COMMAND, ahci_ctlp, "\n"); 183868d33a25Syt AHCIDBG3(AHCIDBG_COMMAND, ahci_ctlp, 183982263d52Syt "Command header&table for spkt 0x%p cmd_reg 0x%x port %d", 184068d33a25Syt spkt, scmd->satacmd_cmd_reg, port); 184168d33a25Syt ptr = (uint32_t *)cmd_header; 184268d33a25Syt AHCIDBG4(AHCIDBG_COMMAND, ahci_ctlp, 184368d33a25Syt " Command Header:%8x %8x %8x %8x", 184468d33a25Syt ptr[0], ptr[1], ptr[2], ptr[3]); 184568d33a25Syt 184668d33a25Syt /* Dump the H2D register FIS */ 184768d33a25Syt ptr = (uint32_t *)h2d_register_fisp; 184868d33a25Syt AHCIDBG4(AHCIDBG_COMMAND, ahci_ctlp, 184968d33a25Syt " Command FIS: %8x %8x %8x %8x", 185068d33a25Syt ptr[0], ptr[1], ptr[2], ptr[3]); 185168d33a25Syt 185268d33a25Syt /* Dump the ACMD register FIS */ 185368d33a25Syt ptr2 = (uint8_t *)&(cmd_table->ahcict_atapi_cmd); 185468d33a25Syt for (i = 0; i < SATA_ATAPI_MAX_CDB_LEN/8; i++) 185568d33a25Syt if (ahci_debug_flags & AHCIDBG_COMMAND) 185668d33a25Syt ahci_log(ahci_ctlp, CE_WARN, 185768d33a25Syt " ATAPI command: %2x %2x %2x %2x " 185868d33a25Syt "%2x %2x %2x %2x", 185968d33a25Syt ptr2[8 * i], ptr2[8 * i + 1], 186068d33a25Syt ptr2[8 * i + 2], ptr2[8 * i + 3], 186168d33a25Syt ptr2[8 * i + 4], ptr2[8 * i + 5], 186268d33a25Syt ptr2[8 * i + 6], ptr2[8 * i + 7]); 186368d33a25Syt 186468d33a25Syt /* Dump the PRDT */ 186568d33a25Syt for (i = 0; i < ncookies; i++) { 186668d33a25Syt ptr = (uint32_t *)&(cmd_table->ahcict_prdt[i]); 186768d33a25Syt AHCIDBG5(AHCIDBG_COMMAND, ahci_ctlp, 186868d33a25Syt " Cookie %d: %8x %8x %8x %8x", 186968d33a25Syt i, ptr[0], ptr[1], ptr[2], ptr[3]); 187068d33a25Syt } 187168d33a25Syt #endif 187268d33a25Syt 187368d33a25Syt (void) ddi_dma_sync( 187468d33a25Syt ahci_portp->ahciport_cmd_tables_dma_handle[cmd_slot], 18752fcbc377Syt 0, 18762fcbc377Syt ahci_cmd_table_size, 18772fcbc377Syt DDI_DMA_SYNC_FORDEV); 18782fcbc377Syt 18792fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 188068d33a25Syt cmd_slot * sizeof (ahci_cmd_header_t), 18812fcbc377Syt sizeof (ahci_cmd_header_t), 18822fcbc377Syt DDI_DMA_SYNC_FORDEV); 18832fcbc377Syt 188482263d52Syt /* Set the corresponding bit in the PxSACT.DS for queued command */ 188582263d52Syt if (command_type == AHCI_NCQ_CMD) { 188682263d52Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 188782263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port), 188882263d52Syt (0x1 << cmd_slot)); 188982263d52Syt } 189082263d52Syt 18912fcbc377Syt /* Indicate to the HBA that a command is active. */ 18922fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 18932fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 189468d33a25Syt (0x1 << cmd_slot)); 18952fcbc377Syt 18962fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_deliver_satapkt " 18972fcbc377Syt "exit: port %d", port); 18982fcbc377Syt 189968d33a25Syt return (cmd_slot); 19002fcbc377Syt } 19012fcbc377Syt 19022fcbc377Syt /* 19032fcbc377Syt * Called by the sata framework to abort the previously sent packet(s). 19042fcbc377Syt * 19052fcbc377Syt * Reset device to abort commands. 19062fcbc377Syt */ 19072fcbc377Syt static int 19082fcbc377Syt ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag) 19092fcbc377Syt { 19102fcbc377Syt ahci_ctl_t *ahci_ctlp; 19112fcbc377Syt ahci_port_t *ahci_portp; 191282263d52Syt uint32_t slot_status = 0; 191382263d52Syt uint32_t aborted_tags = 0; 191482263d52Syt uint32_t finished_tags = 0; 19152fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 19162fcbc377Syt uint8_t port; 19172fcbc377Syt int tmp_slot; 1918a9440e8dSyt int instance = ddi_get_instance(dip); 19192fcbc377Syt 1920a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 19212fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 19222fcbc377Syt 19232fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 192468d33a25Syt "ahci_tran_abort enter: port %d", port); 19252fcbc377Syt 19262fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 19272fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 19282fcbc377Syt 19292fcbc377Syt /* 193068d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 193168d33a25Syt * commands are being mopped, therefore there is nothing else to do 19322fcbc377Syt */ 193368d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 19342fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 19352fcbc377Syt "ahci_tran_abort: port %d is in " 19362fcbc377Syt "mopping process, so just return directly ", port); 19372fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 19382fcbc377Syt return (SATA_SUCCESS); 19392fcbc377Syt } 19402fcbc377Syt 19412fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 19422fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 19432fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 19442fcbc377Syt /* 19452fcbc377Syt * In case the targer driver would send the request before 19462fcbc377Syt * sata framework can have the opportunity to process those 19472fcbc377Syt * event reports. 19482fcbc377Syt */ 19492fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 19502fcbc377Syt spkt->satapkt_device.satadev_state = 19512fcbc377Syt ahci_portp->ahciport_port_state; 19522fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 19532fcbc377Syt &spkt->satapkt_device); 19542fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 19552fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 19562fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 195768d33a25Syt "port: %d", port); 19582fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 19592fcbc377Syt return (SATA_FAILURE); 19602fcbc377Syt } 19612fcbc377Syt 19622fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 19632fcbc377Syt /* 19642fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 19652fcbc377Syt * AHCI_PORT_TYPE_NODEV. 19662fcbc377Syt */ 19672fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 19682fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 19692fcbc377Syt spkt->satapkt_device.satadev_state = 19702fcbc377Syt ahci_portp->ahciport_port_state; 19712fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 19722fcbc377Syt &spkt->satapkt_device); 19732fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 19742fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 197568d33a25Syt "no device attached: port: %d", port); 19762fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 19772fcbc377Syt return (SATA_FAILURE); 19782fcbc377Syt } 19792fcbc377Syt 19802fcbc377Syt if (flag == SATA_ABORT_ALL_PACKETS) { 198182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 198282263d52Syt aborted_tags = ahci_portp->ahciport_pending_tags; 1983a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 198482263d52Syt aborted_tags = ahci_portp->ahciport_pending_ncq_tags; 198582263d52Syt 1986a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort all packets", 1987a9440e8dSyt instance, port); 19882fcbc377Syt } else { 19892fcbc377Syt aborted_tags = 0xffffffff; 19902fcbc377Syt /* 199182263d52Syt * Aborting one specific packet, first search the 19922fcbc377Syt * ahciport_slot_pkts[] list for matching spkt. 19932fcbc377Syt */ 19942fcbc377Syt for (tmp_slot = 0; 19952fcbc377Syt tmp_slot < ahci_ctlp->ahcictl_num_cmd_slots; tmp_slot++) { 19962fcbc377Syt if (ahci_portp->ahciport_slot_pkts[tmp_slot] == spkt) { 19972fcbc377Syt aborted_tags = (0x1 << tmp_slot); 19982fcbc377Syt break; 19992fcbc377Syt } 20002fcbc377Syt } 20012fcbc377Syt 20022fcbc377Syt if (aborted_tags == 0xffffffff) { 20032fcbc377Syt /* request packet is not on the pending list */ 20042fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 20052fcbc377Syt "Cannot find the aborting pkt 0x%p on the " 20062fcbc377Syt "pending list", (void *)spkt); 20072fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 20082fcbc377Syt &spkt->satapkt_device); 20092fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20102fcbc377Syt return (SATA_FAILURE); 20112fcbc377Syt } 2012a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort satapkt 0x%p", 2013a9440e8dSyt instance, port, (void *)spkt); 20142fcbc377Syt } 20152fcbc377Syt 201682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 201782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 201882263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 2019a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 202082263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 202182263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 20222fcbc377Syt 202368d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 202468d33a25Syt ahci_portp->ahciport_mop_in_progress++; 20252fcbc377Syt 20262fcbc377Syt /* 20272fcbc377Syt * To abort the packet(s), first we are trying to clear PxCMD.ST 202868d33a25Syt * to stop the port, and if the port can be stopped 20292fcbc377Syt * successfully with PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0', 20302fcbc377Syt * then we just send back the aborted packet(s) with ABORTED flag 20312fcbc377Syt * and then restart the port by setting PxCMD.ST and PxCMD.FRE. 20322fcbc377Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then we 20332fcbc377Syt * perform a COMRESET. 20342fcbc377Syt */ 20352fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 203668d33a25Syt ahci_portp, port, NULL, NULL); 20372fcbc377Syt 20382fcbc377Syt /* 20392fcbc377Syt * Compute which have finished and which need to be retried. 20402fcbc377Syt * 204182263d52Syt * The finished tags are ahciport_pending_tags/ahciport_pending_ncq_tags 204282263d52Syt * minus the slot_status. The aborted_tags has to be deducted by 204382263d52Syt * finished_tags since we can't possibly abort a tag which had finished 204482263d52Syt * already. 20452fcbc377Syt */ 204682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 204782263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 204882263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2049a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 205082263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 205182263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 20522fcbc377Syt 20532fcbc377Syt aborted_tags &= ~finished_tags; 20542fcbc377Syt 20552fcbc377Syt ahci_mop_commands(ahci_ctlp, 20562fcbc377Syt ahci_portp, 20572fcbc377Syt slot_status, 20582fcbc377Syt 0, /* failed tags */ 20592fcbc377Syt 0, /* timeout tags */ 20602fcbc377Syt aborted_tags, 20612fcbc377Syt 0); /* reset tags */ 20622fcbc377Syt 20632fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, &spkt->satapkt_device); 20642fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 20652fcbc377Syt 20662fcbc377Syt return (SATA_SUCCESS); 20672fcbc377Syt } 20682fcbc377Syt 20692fcbc377Syt /* 20702fcbc377Syt * Used to do device reset and reject all the pending packets on a device 20712fcbc377Syt * during the reset operation. 20722fcbc377Syt * 20732fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 20742fcbc377Syt */ 20752fcbc377Syt static int 20762fcbc377Syt ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp, 20772fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 20782fcbc377Syt { 207982263d52Syt uint32_t slot_status = 0; 208082263d52Syt uint32_t reset_tags = 0; 208182263d52Syt uint32_t finished_tags = 0; 20822fcbc377Syt sata_device_t sdevice; 20832fcbc377Syt int ret; 20842fcbc377Syt 20852fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 20862fcbc377Syt "ahci_reset_device_reject_pkts on port: %d", port); 20872fcbc377Syt 20882fcbc377Syt /* 208968d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 209068d33a25Syt * commands are being mopped, therefore there is nothing else to do 20912fcbc377Syt */ 209268d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 20932fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 20942fcbc377Syt "ahci_reset_device_reject_pkts: port %d is in " 20952fcbc377Syt "mopping process, so return directly ", port); 20962fcbc377Syt return (SATA_SUCCESS); 20972fcbc377Syt } 20982fcbc377Syt 209982263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 210082263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 210182263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 210282263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2103a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 210482263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 210582263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 210682263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 210782263d52Syt } 21082fcbc377Syt 21092fcbc377Syt if (ahci_software_reset(ahci_ctlp, ahci_portp, port) 21102fcbc377Syt != AHCI_SUCCESS) { 21112fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 21122fcbc377Syt "Try to do a port reset after software " 21132fcbc377Syt "reset failed", port); 21142fcbc377Syt ret = ahci_port_reset(ahci_ctlp, ahci_portp, port); 21152fcbc377Syt if (ret != AHCI_SUCCESS) { 21162fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 21172fcbc377Syt "ahci_reset_device_reject_pkts: port %d " 21182fcbc377Syt "failed", port); 21192fcbc377Syt return (SATA_FAILURE); 21202fcbc377Syt } 21212fcbc377Syt } 21222fcbc377Syt /* Set the reset in progress flag */ 21232fcbc377Syt ahci_portp->ahciport_reset_in_progress = 1; 21242fcbc377Syt 212568d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 212668d33a25Syt ahci_portp->ahciport_mop_in_progress++; 21272fcbc377Syt 21282fcbc377Syt /* Indicate to the framework that a reset has happened */ 21292fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 213009121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 213168d33a25Syt sdevice.satadev_addr.pmport = 0; 213268d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 21332fcbc377Syt 21342fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 21352fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 21362fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 21372fcbc377Syt sata_hba_event_notify( 21382fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 21392fcbc377Syt &sdevice, 21402fcbc377Syt SATA_EVNT_DEVICE_RESET); 21412fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 21422fcbc377Syt 21432fcbc377Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 21442fcbc377Syt "port %d sending event up: SATA_EVNT_RESET", port); 21452fcbc377Syt 21462fcbc377Syt /* Next try to mop the pending commands */ 214782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 214882263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 214982263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2150a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 215182263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 215282263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 21532fcbc377Syt 21542fcbc377Syt reset_tags &= ~finished_tags; 21552fcbc377Syt 21562fcbc377Syt ahci_mop_commands(ahci_ctlp, 21572fcbc377Syt ahci_portp, 21582fcbc377Syt slot_status, 21592fcbc377Syt 0, /* failed tags */ 21602fcbc377Syt 0, /* timeout tags */ 21612fcbc377Syt 0, /* aborted tags */ 21622fcbc377Syt reset_tags); /* reset tags */ 21632fcbc377Syt 21642fcbc377Syt return (SATA_SUCCESS); 21652fcbc377Syt } 21662fcbc377Syt 21672fcbc377Syt /* 21682fcbc377Syt * Used to do port reset and reject all the pending packets on a port during 21692fcbc377Syt * the reset operation. 21702fcbc377Syt * 21712fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 21722fcbc377Syt */ 21732fcbc377Syt static int 21742fcbc377Syt ahci_reset_port_reject_pkts(ahci_ctl_t *ahci_ctlp, 21752fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 21762fcbc377Syt { 217782263d52Syt uint32_t slot_status = 0; 217882263d52Syt uint32_t reset_tags = 0; 217982263d52Syt uint32_t finished_tags = 0; 21802fcbc377Syt 21812fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 21822fcbc377Syt "ahci_reset_port_reject_pkts on port: %d", port); 21832fcbc377Syt 21842fcbc377Syt /* 218568d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 218668d33a25Syt * commands are being mopped, therefore there is nothing else to do 21872fcbc377Syt */ 218868d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 21892fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 21902fcbc377Syt "ahci_reset_port_reject_pkts: port %d is in " 21912fcbc377Syt "mopping process, so return directly ", port); 21922fcbc377Syt return (SATA_SUCCESS); 21932fcbc377Syt } 21942fcbc377Syt 219568d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 219668d33a25Syt ahci_portp->ahciport_mop_in_progress++; 21972fcbc377Syt 219882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 219982263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 220082263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 220182263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2202a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 220382263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 220482263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 220582263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 220682263d52Syt } 22072fcbc377Syt 22082fcbc377Syt if (ahci_restart_port_wait_till_ready(ahci_ctlp, 220982263d52Syt ahci_portp, port, AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 221082263d52Syt NULL) != AHCI_SUCCESS) 22112fcbc377Syt return (SATA_FAILURE); 22122fcbc377Syt 221382263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 221482263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 221582263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2216a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 221782263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 221882263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 22192fcbc377Syt 22202fcbc377Syt reset_tags &= ~finished_tags; 22212fcbc377Syt 22222fcbc377Syt ahci_mop_commands(ahci_ctlp, 22232fcbc377Syt ahci_portp, 22242fcbc377Syt slot_status, 22252fcbc377Syt 0, /* failed tags */ 22262fcbc377Syt 0, /* timeout tags */ 22272fcbc377Syt 0, /* aborted tags */ 22282fcbc377Syt reset_tags); /* reset tags */ 22292fcbc377Syt 22302fcbc377Syt return (SATA_SUCCESS); 22312fcbc377Syt } 22322fcbc377Syt 22332fcbc377Syt /* 22342fcbc377Syt * Used to do hba reset and reject all the pending packets on all ports 22352fcbc377Syt * during the reset operation. 22362fcbc377Syt */ 22372fcbc377Syt static int 22382fcbc377Syt ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp) 22392fcbc377Syt { 22402fcbc377Syt ahci_port_t *ahci_portp; 22412fcbc377Syt uint32_t slot_status[AHCI_MAX_PORTS]; 22422fcbc377Syt uint32_t reset_tags[AHCI_MAX_PORTS]; 22432fcbc377Syt uint32_t finished_tags[AHCI_MAX_PORTS]; 22442fcbc377Syt uint8_t port; 22452fcbc377Syt int ret = SATA_SUCCESS; 22462fcbc377Syt 22472fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, 22482fcbc377Syt "ahci_reset_hba_reject_pkts enter"); 22492fcbc377Syt 22502fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 22512fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 22522fcbc377Syt continue; 22532fcbc377Syt } 22542fcbc377Syt 22552fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 22562fcbc377Syt 22572fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 225882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 225982263d52Syt slot_status[port] = ddi_get32( 226082263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 226182263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 226282263d52Syt reset_tags[port] = slot_status[port] & 226382263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 2264a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 226582263d52Syt slot_status[port] = ddi_get32( 226682263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 226782263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 226882263d52Syt reset_tags[port] = slot_status[port] & 226982263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 227082263d52Syt } 22712fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 22722fcbc377Syt } 22732fcbc377Syt 22742fcbc377Syt if (ahci_hba_reset(ahci_ctlp) != AHCI_SUCCESS) { 22752fcbc377Syt ret = SATA_FAILURE; 22762fcbc377Syt goto out; 22772fcbc377Syt } 22782fcbc377Syt 22792fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 22802fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 22812fcbc377Syt continue; 22822fcbc377Syt } 22832fcbc377Syt 22842fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 22852fcbc377Syt 22862fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 22872fcbc377Syt /* 22882fcbc377Syt * To prevent recursive enter to ahci_mop_commands, we need 228968d33a25Syt * check AHCI_PORT_FLAG_MOPPING flag. 22902fcbc377Syt */ 229168d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 22922fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 22932fcbc377Syt "ahci_reset_hba_reject_pkts: port %d is in " 22942fcbc377Syt "mopping process, so return directly ", port); 22952fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 22962fcbc377Syt continue; 22972fcbc377Syt } 22982fcbc377Syt 229968d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 230068d33a25Syt ahci_portp->ahciport_mop_in_progress++; 23012fcbc377Syt 230282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 230382263d52Syt finished_tags[port] = 230482263d52Syt ahci_portp->ahciport_pending_tags & 230582263d52Syt ~slot_status[port] & AHCI_SLOT_MASK(ahci_ctlp); 2306a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 230782263d52Syt finished_tags[port] = 230882263d52Syt ahci_portp->ahciport_pending_ncq_tags & 230982263d52Syt ~slot_status[port] & AHCI_NCQ_SLOT_MASK(ahci_portp); 23102fcbc377Syt 23112fcbc377Syt reset_tags[port] &= ~finished_tags[port]; 23122fcbc377Syt 23132fcbc377Syt ahci_mop_commands(ahci_ctlp, 23142fcbc377Syt ahci_portp, 23152fcbc377Syt slot_status[port], 23162fcbc377Syt 0, /* failed tags */ 23172fcbc377Syt 0, /* timeout tags */ 23182fcbc377Syt 0, /* aborted tags */ 23192fcbc377Syt reset_tags[port]); /* reset tags */ 232068d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 23212fcbc377Syt } 23222fcbc377Syt out: 23232fcbc377Syt return (ret); 23242fcbc377Syt } 23252fcbc377Syt 23262fcbc377Syt /* 23272fcbc377Syt * Called by sata framework to reset a port(s) or device. 23282fcbc377Syt */ 23292fcbc377Syt static int 23302fcbc377Syt ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd) 23312fcbc377Syt { 23322fcbc377Syt ahci_ctl_t *ahci_ctlp; 23332fcbc377Syt ahci_port_t *ahci_portp; 23342fcbc377Syt uint8_t cport = sd->satadev_addr.cport; 23352fcbc377Syt uint8_t port; 23362fcbc377Syt int ret = SATA_SUCCESS; 2337a9440e8dSyt int instance = ddi_get_instance(dip); 23382fcbc377Syt 2339a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 23402fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 23412fcbc377Syt 23422fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 234368d33a25Syt "ahci_tran_reset_port enter: cport: %d", cport); 23442fcbc377Syt 23452fcbc377Syt switch (sd->satadev_addr.qual) { 23462fcbc377Syt case SATA_ADDR_CPORT: 23472fcbc377Syt /* Port reset */ 23482fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2349a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2350a9440e8dSyt "reset port", instance, port); 23512fcbc377Syt 23522fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 23532fcbc377Syt ret = ahci_reset_port_reject_pkts(ahci_ctlp, ahci_portp, port); 23542fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23552fcbc377Syt 23562fcbc377Syt break; 23572fcbc377Syt 23582fcbc377Syt case SATA_ADDR_DCPORT: 23592fcbc377Syt /* Device reset */ 23602fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 2361a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2362a9440e8dSyt "reset device", instance, port); 23632fcbc377Syt 23642fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 23652fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 23662fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 23672fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 23682fcbc377Syt /* 23692fcbc377Syt * In case the targer driver would send the request 23702fcbc377Syt * before sata framework can have the opportunity to 23712fcbc377Syt * process those event reports. 23722fcbc377Syt */ 23732fcbc377Syt sd->satadev_state = ahci_portp->ahciport_port_state; 23742fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 23752fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 23762fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 23772fcbc377Syt "while port in FAILED/SHUTDOWN/PWROFF state: " 237868d33a25Syt "port: %d", port); 23792fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23802fcbc377Syt ret = SATA_FAILURE; 23812fcbc377Syt break; 23822fcbc377Syt } 23832fcbc377Syt 23842fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 23852fcbc377Syt /* 23862fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 23872fcbc377Syt * AHCI_PORT_TYPE_NODEV. 23882fcbc377Syt */ 23892fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 23902fcbc377Syt sd->satadev_state = ahci_portp->ahciport_port_state; 23912fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 23922fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 23932fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 239468d33a25Syt "while no device attached: port: %d", port); 23952fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23962fcbc377Syt ret = SATA_FAILURE; 23972fcbc377Syt break; 23982fcbc377Syt } 23992fcbc377Syt 24002fcbc377Syt ret = ahci_reset_device_reject_pkts(ahci_ctlp, 24012fcbc377Syt ahci_portp, port); 24022fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24032fcbc377Syt break; 24042fcbc377Syt 24052fcbc377Syt case SATA_ADDR_CNTRL: 24062fcbc377Syt /* Reset the whole controller */ 2407a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport port %d " 2408a9440e8dSyt "reset the whole hba", instance, port); 24092fcbc377Syt ret = ahci_reset_hba_reject_pkts(ahci_ctlp); 24102fcbc377Syt break; 24112fcbc377Syt 24122fcbc377Syt case SATA_ADDR_PMPORT: 24132fcbc377Syt case SATA_ADDR_DPMPORT: 24142fcbc377Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, 241568d33a25Syt "ahci_tran_reset_dport: port multiplier will be " 241668d33a25Syt "supported later"); 2417689d74b0Syt /* FALLTHRU */ 24182fcbc377Syt default: 24192fcbc377Syt ret = SATA_FAILURE; 24202fcbc377Syt } 24212fcbc377Syt 24222fcbc377Syt return (ret); 24232fcbc377Syt } 24242fcbc377Syt 24252fcbc377Syt /* 24262fcbc377Syt * Called by sata framework to activate a port as part of hotplug. 24272fcbc377Syt * (cfgadm -c connect satax/y) 24282fcbc377Syt * Note: Not port-mult aware. 24292fcbc377Syt */ 24302fcbc377Syt static int 24312fcbc377Syt ahci_tran_hotplug_port_activate(dev_info_t *dip, sata_device_t *satadev) 24322fcbc377Syt { 24332fcbc377Syt ahci_ctl_t *ahci_ctlp; 24342fcbc377Syt ahci_port_t *ahci_portp; 24352fcbc377Syt uint8_t cport = satadev->satadev_addr.cport; 24362fcbc377Syt uint8_t port; 2437a9440e8dSyt int instance = ddi_get_instance(dip); 24382fcbc377Syt 2439a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 24402fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 24412fcbc377Syt 24422fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 24432fcbc377Syt "ahci_tran_hotplug_port_activate cport %d enter", cport); 24442fcbc377Syt 24452fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 24462fcbc377Syt 24472fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 2448689d74b0Syt ahci_enable_port_intrs(ahci_ctlp, port); 2449a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d is activated", instance, port); 24502fcbc377Syt 24512fcbc377Syt /* 24522fcbc377Syt * Reset the port so that the PHY communication would be re-established. 245368d33a25Syt * But this reset is an internal operation and the sata module doesn't 245468d33a25Syt * need to know about it. Moreover, the port with a device attached will 245568d33a25Syt * be started too. 24562fcbc377Syt */ 24572fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 245868d33a25Syt ahci_portp, port, 245968d33a25Syt AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 246068d33a25Syt NULL); 24612fcbc377Syt 24622fcbc377Syt /* 24632fcbc377Syt * Need to check the link status and device status of the port 24642fcbc377Syt * and consider raising power if the port was in D3 state 24652fcbc377Syt */ 246668d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_PWRON; 246768d33a25Syt ahci_portp->ahciport_port_state &= ~SATA_PSTATE_PWROFF; 246868d33a25Syt ahci_portp->ahciport_port_state &= ~SATA_PSTATE_SHUTDOWN; 246968d33a25Syt 247068d33a25Syt satadev->satadev_state = ahci_portp->ahciport_port_state; 24712fcbc377Syt 24722fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 24732fcbc377Syt 24742fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24752fcbc377Syt return (SATA_SUCCESS); 24762fcbc377Syt } 24772fcbc377Syt 24782fcbc377Syt /* 24792fcbc377Syt * Called by sata framework to deactivate a port as part of hotplug. 24802fcbc377Syt * (cfgadm -c disconnect satax/y) 24812fcbc377Syt * Note: Not port-mult aware. 24822fcbc377Syt */ 24832fcbc377Syt static int 24842fcbc377Syt ahci_tran_hotplug_port_deactivate(dev_info_t *dip, sata_device_t *satadev) 24852fcbc377Syt { 24862fcbc377Syt ahci_ctl_t *ahci_ctlp; 24872fcbc377Syt ahci_port_t *ahci_portp; 24882fcbc377Syt uint8_t cport = satadev->satadev_addr.cport; 24892fcbc377Syt uint8_t port; 249068d33a25Syt uint32_t port_scontrol; 2491a9440e8dSyt int instance = ddi_get_instance(dip); 24922fcbc377Syt 2493a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 24942fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 24952fcbc377Syt 24962fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 24972fcbc377Syt "ahci_tran_hotplug_port_deactivate cport %d enter", cport); 24982fcbc377Syt 24992fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 25002fcbc377Syt 25012fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 2502a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d is deactivated", 2503a9440e8dSyt instance, port); 25042fcbc377Syt 250568d33a25Syt /* Disable the interrupts on the port */ 2506689d74b0Syt ahci_disable_port_intrs(ahci_ctlp, port); 25072fcbc377Syt 250868d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 250968d33a25Syt goto phy_offline; 251068d33a25Syt } 251168d33a25Syt 25122fcbc377Syt /* First to abort all the pending commands */ 25132fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 25142fcbc377Syt 251568d33a25Syt /* Then stop the port */ 251668d33a25Syt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 25172fcbc377Syt ahci_portp, port); 25182fcbc377Syt 251968d33a25Syt /* Next put the PHY offline */ 252068d33a25Syt 252168d33a25Syt phy_offline: 252268d33a25Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 252368d33a25Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 252482263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_DISABLE); 252568d33a25Syt 25262fcbc377Syt /* Update ahciport_port_state */ 25272fcbc377Syt ahci_portp->ahciport_port_state = SATA_PSTATE_SHUTDOWN; 252868d33a25Syt satadev->satadev_state = ahci_portp->ahciport_port_state; 25292fcbc377Syt 25302fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 25312fcbc377Syt 25322fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 25332fcbc377Syt return (SATA_SUCCESS); 25342fcbc377Syt } 25352fcbc377Syt 25362fcbc377Syt /* 253768d33a25Syt * To be used to mark all the outstanding pkts with SATA_PKT_ABORTED 25382fcbc377Syt * when a device is unplugged or a port is deactivated. 25392fcbc377Syt * 25402fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 25412fcbc377Syt */ 25422fcbc377Syt static void 25432fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctl_t *ahci_ctlp, 25442fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 25452fcbc377Syt { 254682263d52Syt uint32_t slot_status = 0; 254782263d52Syt uint32_t abort_tags = 0; 25482fcbc377Syt 254968d33a25Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 25502fcbc377Syt "ahci_reject_all_abort_pkts on port: %d", port); 25512fcbc377Syt 255282263d52Syt /* 255382263d52Syt * When AHCI_PORT_FLAG_MOPPING is set, we need to check whether a 255482263d52Syt * REQUEST SENSE command or READ LOG EXT command is delivered to HBA 255582263d52Syt * to get the error data, if yes when the device is removed, the 255682263d52Syt * command needs to be aborted too. 255782263d52Syt */ 255882263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 255982263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 256082263d52Syt slot_status = 0x1; 256182263d52Syt abort_tags = 0x1; 256282263d52Syt goto out; 256382263d52Syt } else { 256482263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 256582263d52Syt "ahci_reject_all_abort_pkts return directly " 256682263d52Syt "port %d no needs to reject any outstanding " 256782263d52Syt "commands", port); 256882263d52Syt return; 256982263d52Syt } 257082263d52Syt } 25712fcbc377Syt 257282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 257382263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 257482263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 257582263d52Syt abort_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2576a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 257782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 257882263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 257982263d52Syt abort_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 258082263d52Syt } 258182263d52Syt 258282263d52Syt out: 258368d33a25Syt /* No need to do mop when there is no outstanding commands */ 258468d33a25Syt if (slot_status != 0) { 258568d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 258668d33a25Syt ahci_portp->ahciport_mop_in_progress++; 25872fcbc377Syt 258868d33a25Syt ahci_mop_commands(ahci_ctlp, 258968d33a25Syt ahci_portp, 259068d33a25Syt slot_status, 259168d33a25Syt 0, /* failed tags */ 259268d33a25Syt 0, /* timeout tags */ 259368d33a25Syt abort_tags, /* aborting tags */ 259468d33a25Syt 0); /* reset tags */ 259568d33a25Syt } 259668d33a25Syt } 259768d33a25Syt 259868d33a25Syt #if defined(__lock_lint) 259968d33a25Syt static int 260068d33a25Syt ahci_selftest(dev_info_t *dip, sata_device_t *device) 260168d33a25Syt { 26022fcbc377Syt return (SATA_SUCCESS); 26032fcbc377Syt } 26042fcbc377Syt #endif 26052fcbc377Syt 26062fcbc377Syt /* 260768d33a25Syt * Allocate the ports structure, only called by ahci_attach 260868d33a25Syt */ 260968d33a25Syt static int 261068d33a25Syt ahci_alloc_ports_state(ahci_ctl_t *ahci_ctlp) 261168d33a25Syt { 2612f68cbde1Syt int port, cport = 0; 261368d33a25Syt 261468d33a25Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 261568d33a25Syt "ahci_alloc_ports_state enter"); 261668d33a25Syt 261768d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 261868d33a25Syt 261968d33a25Syt /* Allocate structures only for the implemented ports */ 2620f68cbde1Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 262168d33a25Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 262268d33a25Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 262368d33a25Syt "hba port %d not implemented", port); 262468d33a25Syt continue; 262568d33a25Syt } 262668d33a25Syt 262768d33a25Syt ahci_ctlp->ahcictl_cport_to_port[cport] = (uint8_t)port; 2628f68cbde1Syt ahci_ctlp->ahcictl_port_to_cport[port] = 2629f68cbde1Syt (uint8_t)cport++; 263068d33a25Syt 263168d33a25Syt if (ahci_alloc_port_state(ahci_ctlp, port) != AHCI_SUCCESS) { 263268d33a25Syt goto err_out; 263368d33a25Syt } 263468d33a25Syt } 263568d33a25Syt 263668d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 263768d33a25Syt return (AHCI_SUCCESS); 263868d33a25Syt 263968d33a25Syt err_out: 264068d33a25Syt for (port--; port >= 0; port--) { 264168d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 264268d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 264368d33a25Syt } 264468d33a25Syt } 264568d33a25Syt 264668d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 264768d33a25Syt return (AHCI_FAILURE); 264868d33a25Syt } 264968d33a25Syt 265068d33a25Syt /* 265168d33a25Syt * Reverse of ahci_alloc_ports_state(), only called by ahci_detach 265268d33a25Syt */ 265368d33a25Syt static void 265468d33a25Syt ahci_dealloc_ports_state(ahci_ctl_t *ahci_ctlp) 265568d33a25Syt { 265668d33a25Syt int port; 265768d33a25Syt 265868d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 265968d33a25Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 266068d33a25Syt /* if this port is implemented by the HBA */ 266168d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) 266268d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 266368d33a25Syt } 266468d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 266568d33a25Syt } 266668d33a25Syt 266768d33a25Syt /* 266813bcbb7aSyt * Initialize the controller and all ports. And then try to start the ports 266913bcbb7aSyt * if there are devices attached. 26702fcbc377Syt * 26712fcbc377Syt * This routine can be called from three seperate cases: DDI_ATTACH, 26722fcbc377Syt * PM_LEVEL_D0 and DDI_RESUME. The DDI_ATTACH case is different from 267368d33a25Syt * other two cases; device signature probing are attempted only during 267468d33a25Syt * DDI_ATTACH case. 26752fcbc377Syt * 26762fcbc377Syt * WARNING!!! Disable the whole controller's interrupts before calling and 26772fcbc377Syt * the interrupts will be enabled upon successfully return. 26782fcbc377Syt */ 26792fcbc377Syt static int 26802fcbc377Syt ahci_initialize_controller(ahci_ctl_t *ahci_ctlp) 26812fcbc377Syt { 26822fcbc377Syt ahci_port_t *ahci_portp; 26832fcbc377Syt uint32_t ghc_control; 268468d33a25Syt int port; 26852fcbc377Syt 26862fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 26872fcbc377Syt "ahci_initialize_controller enter"); 26882fcbc377Syt 26892fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 26902fcbc377Syt 26912fcbc377Syt /* 26922fcbc377Syt * Indicate that system software is AHCI aware by setting 26932fcbc377Syt * GHC.AE to 1 26942fcbc377Syt */ 26952fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 26962fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 26972fcbc377Syt 26982fcbc377Syt ghc_control |= AHCI_HBA_GHC_AE; 26992fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 27002fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), 27012fcbc377Syt ghc_control); 27022fcbc377Syt 270382263d52Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 270482263d52Syt 27052fcbc377Syt /* Initialize the implemented ports and structures */ 27062fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 27072fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 27082fcbc377Syt continue; 27092fcbc377Syt } 27102fcbc377Syt 27112fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 27122fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 27132fcbc377Syt 27142fcbc377Syt /* 27152fcbc377Syt * Ensure that the controller is not in the running state 27162fcbc377Syt * by checking every implemented port's PxCMD register 27172fcbc377Syt */ 27182fcbc377Syt if (ahci_initialize_port(ahci_ctlp, ahci_portp, port) 27192fcbc377Syt != AHCI_SUCCESS) { 27202fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 27212fcbc377Syt "ahci_initialize_controller: failed to " 27222fcbc377Syt "initialize port %d", port); 27232fcbc377Syt /* 27242fcbc377Syt * Set the port state to SATA_PSTATE_FAILED if 27252fcbc377Syt * failed to initialize it. 27262fcbc377Syt */ 272768d33a25Syt ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 27282fcbc377Syt } 27292fcbc377Syt 27302fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 27312fcbc377Syt } 27322fcbc377Syt 27332fcbc377Syt /* Enable the whole controller interrupts */ 273482263d52Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 27352fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 27362fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 27372fcbc377Syt 27382fcbc377Syt return (AHCI_SUCCESS); 27392fcbc377Syt } 27402fcbc377Syt 27412fcbc377Syt /* 274268d33a25Syt * Reverse of ahci_initialize_controller() 27432fcbc377Syt * 274413bcbb7aSyt * We only need to stop the ports and disable the interrupt. 27452fcbc377Syt */ 27462fcbc377Syt static void 274768d33a25Syt ahci_uninitialize_controller(ahci_ctl_t *ahci_ctlp) 27482fcbc377Syt { 274913bcbb7aSyt ahci_port_t *ahci_portp; 275013bcbb7aSyt int port; 275113bcbb7aSyt 27522fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 275368d33a25Syt "ahci_uninitialize_controller enter"); 27542fcbc377Syt 27552fcbc377Syt /* disable all the interrupts. */ 275613bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 27572fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 275813bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 275913bcbb7aSyt 276013bcbb7aSyt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 276113bcbb7aSyt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 276213bcbb7aSyt continue; 276313bcbb7aSyt } 276413bcbb7aSyt 276513bcbb7aSyt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 276613bcbb7aSyt 276713bcbb7aSyt /* Stop the port by clearing PxCMD.ST */ 276813bcbb7aSyt mutex_enter(&ahci_portp->ahciport_mutex); 276913bcbb7aSyt 277013bcbb7aSyt /* 277113bcbb7aSyt * Here we must disable the port interrupt because 277213bcbb7aSyt * ahci_disable_all_intrs only clear GHC.IE, and IS 277313bcbb7aSyt * register will be still set if PxIE is enabled. 277413bcbb7aSyt * When ahci shares one IRQ with other drivers, the 277513bcbb7aSyt * intr handler may claim the intr mistakenly. 277613bcbb7aSyt */ 2777689d74b0Syt ahci_disable_port_intrs(ahci_ctlp, port); 277813bcbb7aSyt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 277913bcbb7aSyt ahci_portp, port); 278013bcbb7aSyt mutex_exit(&ahci_portp->ahciport_mutex); 278113bcbb7aSyt } 27822fcbc377Syt } 27832fcbc377Syt 27842fcbc377Syt /* 27852fcbc377Syt * The routine is to initialize the port. First put the port in NOTRunning 27862fcbc377Syt * state, then enable port interrupt and clear Serror register. And under 27872fcbc377Syt * AHCI_ATTACH case, find device signature and then try to start the port. 27882fcbc377Syt * 278982263d52Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 27902fcbc377Syt */ 27912fcbc377Syt static int 27922fcbc377Syt ahci_initialize_port(ahci_ctl_t *ahci_ctlp, 27932fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 27942fcbc377Syt { 27952fcbc377Syt uint32_t port_cmd_status; 27962fcbc377Syt 27972fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 27982fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 27992fcbc377Syt 28002fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 28012fcbc377Syt "ahci_initialize_port: port %d " 28022fcbc377Syt "port_cmd_status = 0x%x", port, port_cmd_status); 28032fcbc377Syt /* 28042fcbc377Syt * Check whether the port is in NotRunning state, if not, 28052fcbc377Syt * put the port in NotRunning state 28062fcbc377Syt */ 28072fcbc377Syt if (!(port_cmd_status & 28082fcbc377Syt (AHCI_CMD_STATUS_ST | 28092fcbc377Syt AHCI_CMD_STATUS_CR | 28102fcbc377Syt AHCI_CMD_STATUS_FRE | 28112fcbc377Syt AHCI_CMD_STATUS_FR))) { 28122fcbc377Syt 281368d33a25Syt goto next; 28142fcbc377Syt } 28152fcbc377Syt 28162fcbc377Syt if (ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 281768d33a25Syt port, AHCI_RESET_NO_EVENTS_UP|AHCI_PORT_INIT, NULL) != AHCI_SUCCESS) 28182fcbc377Syt return (AHCI_FAILURE); 28192fcbc377Syt 282068d33a25Syt next: 28212fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 282268d33a25Syt "port %d is in NotRunning state now", port); 28232fcbc377Syt 28242fcbc377Syt /* 282568d33a25Syt * At the time being, only probe ports/devices and get the types of 282613bcbb7aSyt * attached devices during DDI_ATTACH. In fact, the device can be 282713bcbb7aSyt * changed during power state changes, but at the time being, we 282813bcbb7aSyt * don't support the situation. 28292fcbc377Syt */ 28302fcbc377Syt if (ahci_ctlp->ahcictl_flags & AHCI_ATTACH) { 283168d33a25Syt /* Try to get the device signature */ 283268d33a25Syt ahci_find_dev_signature(ahci_ctlp, ahci_portp, port); 283313bcbb7aSyt } else { 28342fcbc377Syt 283513bcbb7aSyt /* 283613bcbb7aSyt * During the resume, we need to set the PxCLB, PxCLBU, PxFB 283713bcbb7aSyt * and PxFBU registers in case these registers were cleared 283813bcbb7aSyt * during the suspend. 283913bcbb7aSyt */ 284013bcbb7aSyt AHCIDBG1(AHCIDBG_PM, ahci_ctlp, 284113bcbb7aSyt "ahci_initialize_port: port %d " 284213bcbb7aSyt "reset the port during resume", port); 284313bcbb7aSyt (void) ahci_port_reset(ahci_ctlp, ahci_portp, port); 28442fcbc377Syt 284513bcbb7aSyt AHCIDBG1(AHCIDBG_PM, ahci_ctlp, 284613bcbb7aSyt "ahci_initialize_port: port %d " 284713bcbb7aSyt "set PxCLB, PxCLBU, PxFB and PxFBU " 284813bcbb7aSyt "during resume", port); 284913bcbb7aSyt 285013bcbb7aSyt /* Config Port Received FIS Base Address */ 285113bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 285213bcbb7aSyt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 285313bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 285413bcbb7aSyt 285513bcbb7aSyt /* Config Port Command List Base Address */ 285613bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 285713bcbb7aSyt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 285813bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 285913bcbb7aSyt } 286013bcbb7aSyt 286113bcbb7aSyt /* Disable the interface power management */ 286213bcbb7aSyt ahci_disable_interface_pm(ahci_ctlp, port); 286313bcbb7aSyt 286413bcbb7aSyt /* Return directly if no device connected */ 286513bcbb7aSyt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 286613bcbb7aSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 286713bcbb7aSyt "No device connected to port %d", port); 286813bcbb7aSyt goto out; 286913bcbb7aSyt } 287013bcbb7aSyt 287113bcbb7aSyt /* Try to start the port */ 287213bcbb7aSyt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 287313bcbb7aSyt != AHCI_SUCCESS) { 287413bcbb7aSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 287513bcbb7aSyt "failed to start port %d", port); 287613bcbb7aSyt return (AHCI_FAILURE); 28772fcbc377Syt } 287868d33a25Syt out: 287968d33a25Syt /* Enable port interrupts */ 2880689d74b0Syt ahci_enable_port_intrs(ahci_ctlp, port); 28812fcbc377Syt 28822fcbc377Syt return (AHCI_SUCCESS); 28832fcbc377Syt } 28842fcbc377Syt 288513bcbb7aSyt /* 288613bcbb7aSyt * Figure out which chip and set flag for VT8251; Also check 288713bcbb7aSyt * the power management capability. 288813bcbb7aSyt */ 288913bcbb7aSyt static int 289013bcbb7aSyt ahci_config_space_init(ahci_ctl_t *ahci_ctlp) 289113bcbb7aSyt { 2892a9440e8dSyt ushort_t venid, devid; 2893689d74b0Syt ushort_t caps_ptr, cap_count, cap; 2894689d74b0Syt #if AHCI_DEBUG 2895689d74b0Syt ushort_t pmcap, pmcsr; 2896689d74b0Syt #endif 289713bcbb7aSyt uint8_t revision; 289813bcbb7aSyt 289913bcbb7aSyt venid = pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 290013bcbb7aSyt PCI_CONF_VENID); 290113bcbb7aSyt 2902a9440e8dSyt devid = pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 2903a9440e8dSyt PCI_CONF_DEVID); 2904a9440e8dSyt 290513bcbb7aSyt /* 290613bcbb7aSyt * Modify dma_attr_align of ahcictl_buffer_dma_attr. For VT8251, those 290713bcbb7aSyt * controllers with 0x00 revision id work on 4-byte aligned buffer, 290813bcbb7aSyt * which is a bug and was fixed after 0x00 revision id controllers. 290913bcbb7aSyt * 291013bcbb7aSyt * Moreover, VT8251 cannot use multiple command slots in the command 291113bcbb7aSyt * list for non-queued commands because the previous register content 291213bcbb7aSyt * of PxCI can be re-written in the register write, so a flag will be 291313bcbb7aSyt * set to record this defect - AHCI_CAP_NO_MCMDLIST_NONQUEUE. 291413bcbb7aSyt */ 291513bcbb7aSyt if (venid == VIA_VENID) { 291613bcbb7aSyt revision = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 291713bcbb7aSyt PCI_CONF_REVID); 291813bcbb7aSyt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 291913bcbb7aSyt "revision id = 0x%x", revision); 292013bcbb7aSyt if (revision == 0x00) { 292113bcbb7aSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_align = 0x4; 292213bcbb7aSyt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 292313bcbb7aSyt "change ddi_attr_align to 0x4"); 292413bcbb7aSyt } 292513bcbb7aSyt 292613bcbb7aSyt ahci_ctlp->ahcictl_cap = AHCI_CAP_NO_MCMDLIST_NONQUEUE; 292713bcbb7aSyt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 292813bcbb7aSyt "VT8251 cannot use multiple command lists for " 292913bcbb7aSyt "non-queued commands"); 293013bcbb7aSyt } 293113bcbb7aSyt 2932a9440e8dSyt /* 2933a9440e8dSyt * ATI SB600 (1002,4380) doesn't support 64-bit DMA addressing though it 2934a9440e8dSyt * declares support, so we need to set AHCI_CAP_32BIT_DMA flag to force 2935a9440e8dSyt * 32-bit DMA. 2936a9440e8dSyt */ 2937a9440e8dSyt if (venid == 0x1002 && devid == 0x4380) { 2938a9440e8dSyt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 2939a9440e8dSyt "ATI SB600 cannot do 64-bit DMA though CAP indicates " 2940a9440e8dSyt "support, so force it to use 32-bit DMA"); 2941a9440e8dSyt ahci_ctlp->ahcictl_cap |= AHCI_CAP_32BIT_DMA; 2942a9440e8dSyt } 2943a9440e8dSyt 294413bcbb7aSyt /* 294513bcbb7aSyt * Check if capabilities list is supported and if so, 294613bcbb7aSyt * get initial capabilities pointer and clear bits 0,1. 294713bcbb7aSyt */ 294813bcbb7aSyt if (pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 294913bcbb7aSyt PCI_CONF_STAT) & PCI_STAT_CAP) { 295013bcbb7aSyt caps_ptr = P2ALIGN(pci_config_get8( 295113bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 295213bcbb7aSyt PCI_CONF_CAP_PTR), 4); 295313bcbb7aSyt } else { 295413bcbb7aSyt caps_ptr = PCI_CAP_NEXT_PTR_NULL; 295513bcbb7aSyt } 295613bcbb7aSyt 295713bcbb7aSyt /* 295813bcbb7aSyt * Walk capabilities if supported. 295913bcbb7aSyt */ 296013bcbb7aSyt for (cap_count = 0; caps_ptr != PCI_CAP_NEXT_PTR_NULL; ) { 296113bcbb7aSyt 296213bcbb7aSyt /* 296313bcbb7aSyt * Check that we haven't exceeded the maximum number of 296413bcbb7aSyt * capabilities and that the pointer is in a valid range. 296513bcbb7aSyt */ 296613bcbb7aSyt if (++cap_count > PCI_CAP_MAX_PTR) { 296713bcbb7aSyt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 296813bcbb7aSyt "too many device capabilities"); 296913bcbb7aSyt return (AHCI_FAILURE); 297013bcbb7aSyt } 297113bcbb7aSyt if (caps_ptr < PCI_CAP_PTR_OFF) { 297213bcbb7aSyt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 297313bcbb7aSyt "capabilities pointer 0x%x out of range", 297413bcbb7aSyt caps_ptr); 297513bcbb7aSyt return (AHCI_FAILURE); 297613bcbb7aSyt } 297713bcbb7aSyt 297813bcbb7aSyt /* 297913bcbb7aSyt * Get next capability and check that it is valid. 298013bcbb7aSyt * For now, we only support power management. 298113bcbb7aSyt */ 298213bcbb7aSyt cap = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 298313bcbb7aSyt caps_ptr); 298413bcbb7aSyt switch (cap) { 298513bcbb7aSyt case PCI_CAP_ID_PM: 298613bcbb7aSyt 298713bcbb7aSyt /* power management supported */ 298813bcbb7aSyt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PM; 298913bcbb7aSyt 299013bcbb7aSyt /* Save PMCSR offset */ 299113bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset = caps_ptr + PCI_PMCSR; 299213bcbb7aSyt 2993689d74b0Syt #if AHCI_DEBUG 299413bcbb7aSyt pmcap = pci_config_get16( 299513bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 299613bcbb7aSyt caps_ptr + PCI_PMCAP); 299713bcbb7aSyt pmcsr = pci_config_get16( 299813bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 299913bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset); 300013bcbb7aSyt AHCIDBG2(AHCIDBG_PM, ahci_ctlp, 300113bcbb7aSyt "Power Management capability found PCI_PMCAP " 300213bcbb7aSyt "= 0x%x PCI_PMCSR = 0x%x", pmcap, pmcsr); 300313bcbb7aSyt if ((pmcap & 0x3) == 0x3) 300413bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 300513bcbb7aSyt "PCI Power Management Interface " 300613bcbb7aSyt "spec 1.2 compliant"); 300713bcbb7aSyt #endif 300813bcbb7aSyt break; 300913bcbb7aSyt 301013bcbb7aSyt case PCI_CAP_ID_MSI: 301113bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, "MSI capability found"); 301213bcbb7aSyt break; 301313bcbb7aSyt 301413bcbb7aSyt case PCI_CAP_ID_PCIX: 301513bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 301613bcbb7aSyt "PCI-X capability found"); 301713bcbb7aSyt break; 301813bcbb7aSyt 301913bcbb7aSyt case PCI_CAP_ID_PCI_E: 302013bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 302113bcbb7aSyt "PCI Express capability found"); 302213bcbb7aSyt break; 302313bcbb7aSyt 302413bcbb7aSyt case PCI_CAP_ID_MSI_X: 302513bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 302613bcbb7aSyt "MSI-X capability found"); 302713bcbb7aSyt break; 302813bcbb7aSyt 302913bcbb7aSyt case PCI_CAP_ID_SATA: 303013bcbb7aSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 303113bcbb7aSyt "SATA capability found"); 303213bcbb7aSyt break; 303313bcbb7aSyt 3034a9440e8dSyt case PCI_CAP_ID_VS: 3035a9440e8dSyt AHCIDBG0(AHCIDBG_PM, ahci_ctlp, 3036a9440e8dSyt "Vendor Specific capability found"); 3037a9440e8dSyt break; 3038a9440e8dSyt 303913bcbb7aSyt default: 304013bcbb7aSyt AHCIDBG1(AHCIDBG_PM, ahci_ctlp, 304113bcbb7aSyt "unrecognized capability 0x%x", cap); 304213bcbb7aSyt break; 304313bcbb7aSyt } 304413bcbb7aSyt 304513bcbb7aSyt /* 304613bcbb7aSyt * Get next capabilities pointer and clear bits 0,1. 304713bcbb7aSyt */ 304813bcbb7aSyt caps_ptr = P2ALIGN(pci_config_get8( 304913bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 305013bcbb7aSyt (caps_ptr + PCI_CAP_NEXT_PTR)), 4); 305113bcbb7aSyt } 305213bcbb7aSyt 305313bcbb7aSyt return (AHCI_SUCCESS); 305413bcbb7aSyt } 305513bcbb7aSyt 30562fcbc377Syt /* 30572fcbc377Syt * AHCI device reset ...; a single device on one of the ports is reset, 30582fcbc377Syt * but the HBA and physical communication remain intact. This is the 30592fcbc377Syt * least intrusive. 30602fcbc377Syt * 30612fcbc377Syt * When issuing a software reset sequence, there should not be other 30622fcbc377Syt * commands in the command list, so we will first clear and then re-set 30632fcbc377Syt * PxCMD.ST to clear PxCI. And before issuing the software reset, 30642fcbc377Syt * the port must be idle and PxTFD.STS.BSY and PxTFD.STS.DRQ must be 3065*b2e3645aSying tian - Beijing China * cleared unless command list override (PxCMD.CLO) is supported. 30662fcbc377Syt * 30672fcbc377Syt * WARNING!!! ahciport_mutex should be acquired and PxCMD.FRE should be 30682fcbc377Syt * set before the function is called. 30692fcbc377Syt */ 30702fcbc377Syt static int 30712fcbc377Syt ahci_software_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 30722fcbc377Syt uint8_t port) 30732fcbc377Syt { 30742fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 30752fcbc377Syt ahci_cmd_table_t *cmd_table; 30762fcbc377Syt ahci_cmd_header_t *cmd_header; 307768d33a25Syt uint32_t port_cmd_status, port_cmd_issue, port_task_file; 30782fcbc377Syt int slot, loop_count; 30792fcbc377Syt 30802fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 30812fcbc377Syt "Port %d device resetting", port); 30822fcbc377Syt 3083*b2e3645aSying tian - Beijing China /* First clear PxCMD.ST */ 3084*b2e3645aSying tian - Beijing China (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 3085*b2e3645aSying tian - Beijing China ahci_portp, port); 30862fcbc377Syt 3087*b2e3645aSying tian - Beijing China /* Then re-set PxCMD.ST */ 3088*b2e3645aSying tian - Beijing China (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 30892fcbc377Syt 30902fcbc377Syt /* Check PxTFD.STS.BSY and PxTFD.STS.DRQ */ 30912fcbc377Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 30922fcbc377Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 30932fcbc377Syt 30942fcbc377Syt if (port_task_file & AHCI_TFD_STS_BSY || 30952fcbc377Syt port_task_file & AHCI_TFD_STS_DRQ) { 3096*b2e3645aSying tian - Beijing China port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 3097*b2e3645aSying tian - Beijing China (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 30982fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_CLO)) { 30992fcbc377Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 31002fcbc377Syt "PxTFD.STS.BSY or PxTFD.STS.DRQ is still set, " 31012fcbc377Syt "but PxCMD.CLO isn't supported, so a port " 31022fcbc377Syt "reset is needed."); 31032fcbc377Syt return (AHCI_FAILURE); 31042fcbc377Syt } 31052fcbc377Syt } 31062fcbc377Syt 310782263d52Syt slot = ahci_claim_free_slot(ahci_ctlp, ahci_portp, AHCI_NON_NCQ_CMD); 31082fcbc377Syt if (slot == AHCI_FAILURE) { 31092fcbc377Syt AHCIDBG0(AHCIDBG_INFO, ahci_ctlp, 31102fcbc377Syt "ahci_software_reset: no free slot"); 31112fcbc377Syt return (AHCI_FAILURE); 31122fcbc377Syt } 31132fcbc377Syt 3114*b2e3645aSying tian - Beijing China /* Now send the first H2D Register FIS with SRST set to 1 */ 31152fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 31162fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 31172fcbc377Syt 31182fcbc377Syt h2d_register_fisp = 31192fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 31202fcbc377Syt 31212fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 31222fcbc377Syt SET_FIS_PMP(h2d_register_fisp, AHCI_PORTMULT_CONTROL_PORT); 31232fcbc377Syt SET_FIS_DEVCTL(h2d_register_fisp, SATA_DEVCTL_SRST); 31242fcbc377Syt 31252fcbc377Syt /* Set Command Header in Command List */ 31262fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 31272fcbc377Syt BZERO_DESCR_INFO(cmd_header); 31282fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 31292fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 31302fcbc377Syt 31312fcbc377Syt SET_CLEAR_BUSY_UPON_R_OK(cmd_header, 1); 31322fcbc377Syt SET_RESET(cmd_header, 1); 31332fcbc377Syt SET_WRITE(cmd_header, 1); 31342fcbc377Syt 31352fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 31362fcbc377Syt 0, 31372fcbc377Syt ahci_cmd_table_size, 31382fcbc377Syt DDI_DMA_SYNC_FORDEV); 31392fcbc377Syt 31402fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 31412fcbc377Syt slot * sizeof (ahci_cmd_header_t), 31422fcbc377Syt sizeof (ahci_cmd_header_t), 31432fcbc377Syt DDI_DMA_SYNC_FORDEV); 31442fcbc377Syt 31452fcbc377Syt /* Indicate to the HBA that a command is active. */ 31462fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 31472fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 31482fcbc377Syt (0x1 << slot)); 31492fcbc377Syt 31502fcbc377Syt loop_count = 0; 31512fcbc377Syt 31522fcbc377Syt /* Loop till the first command is finished */ 31532fcbc377Syt do { 31542fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 31552fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 31562fcbc377Syt 3157*b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 31582fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 31592fcbc377Syt break; 31602fcbc377Syt } 31612fcbc377Syt /* Wait for 10 millisec */ 31622fcbc377Syt delay(AHCI_10MS_TICKS); 31632fcbc377Syt } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 31642fcbc377Syt 31652fcbc377Syt AHCIDBG3(AHCIDBG_POLL_LOOP, ahci_ctlp, 31662fcbc377Syt "ahci_software_reset: 1st loop count: %d, " 31672fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 31682fcbc377Syt loop_count, port_cmd_issue, slot); 31692fcbc377Syt 31702fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, slot); 31712fcbc377Syt 3172*b2e3645aSying tian - Beijing China /* Now send the second H2D Register FIS with SRST cleard to zero */ 31732fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 31742fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 31752fcbc377Syt 31762fcbc377Syt h2d_register_fisp = 31772fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 31782fcbc377Syt 31792fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 31802fcbc377Syt SET_FIS_PMP(h2d_register_fisp, AHCI_PORTMULT_CONTROL_PORT); 31812fcbc377Syt 31822fcbc377Syt /* Set Command Header in Command List */ 31832fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 31842fcbc377Syt BZERO_DESCR_INFO(cmd_header); 31852fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 31862fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 31872fcbc377Syt 31882fcbc377Syt SET_WRITE(cmd_header, 1); 31892fcbc377Syt 31902fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 31912fcbc377Syt 0, 31922fcbc377Syt ahci_cmd_table_size, 31932fcbc377Syt DDI_DMA_SYNC_FORDEV); 31942fcbc377Syt 31952fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 31962fcbc377Syt slot * sizeof (ahci_cmd_header_t), 31972fcbc377Syt sizeof (ahci_cmd_header_t), 31982fcbc377Syt DDI_DMA_SYNC_FORDEV); 31992fcbc377Syt 32002fcbc377Syt /* Indicate to the HBA that a command is active. */ 32012fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 32022fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 32032fcbc377Syt (0x1 << slot)); 32042fcbc377Syt 32052fcbc377Syt loop_count = 0; 32062fcbc377Syt 32072fcbc377Syt /* Loop till the second command is finished */ 32082fcbc377Syt do { 32092fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 32102fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 32112fcbc377Syt 3212*b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 32132fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 32142fcbc377Syt break; 32152fcbc377Syt } 32162fcbc377Syt /* Wait for 10 millisec */ 32172fcbc377Syt delay(AHCI_10MS_TICKS); 32182fcbc377Syt } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 32192fcbc377Syt 32202fcbc377Syt AHCIDBG3(AHCIDBG_POLL_LOOP, ahci_ctlp, 32212fcbc377Syt "ahci_software_reset: 2nd loop count: %d, " 32222fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 32232fcbc377Syt loop_count, port_cmd_issue, slot); 32242fcbc377Syt 32252fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, slot); 32262fcbc377Syt 32272fcbc377Syt return (AHCI_SUCCESS); 32282fcbc377Syt } 32292fcbc377Syt 32302fcbc377Syt /* 32312fcbc377Syt * AHCI port reset ...; the physical communication between the HBA and device 32322fcbc377Syt * on a port are disabled. This is more intrusive. 32332fcbc377Syt * 323468d33a25Syt * When an HBA or port reset occurs, Phy communication is going to 32352fcbc377Syt * be re-established with the device through a COMRESET followed by the 32362fcbc377Syt * normal out-of-band communication sequence defined in Serial ATA. AT 32372fcbc377Syt * the end of reset, the device, if working properly, will send a D2H 32382fcbc377Syt * Register FIS, which contains the device signature. When the HBA receives 32392fcbc377Syt * this FIS, it updates PxTFD.STS and PxTFD.ERR register fields, and updates 32402fcbc377Syt * the PxSIG register with the signature. 32412fcbc377Syt * 32422fcbc377Syt * Staggered spin-up is an optional feature in SATA II, and it enables an HBA 32432fcbc377Syt * to individually spin-up attached devices. Please refer to chapter 10.9 of 324468d33a25Syt * AHCI 1.0 spec. 32452fcbc377Syt */ 32462fcbc377Syt /* 324782263d52Syt * WARNING!!! ahciport_mutex should be acquired, and PxCMD.ST should be also 324882263d52Syt * cleared before the function is called. 32492fcbc377Syt */ 32502fcbc377Syt static int 32512fcbc377Syt ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 32522fcbc377Syt { 32532fcbc377Syt uint32_t cap_status, port_cmd_status; 32542fcbc377Syt uint32_t port_scontrol, port_sstatus; 3255689d74b0Syt uint32_t port_intr_status, port_task_file; 3256689d74b0Syt #if AHCI_DEBUG 3257689d74b0Syt uint32_t port_signature; 3258689d74b0Syt #endif 32592fcbc377Syt int loop_count; 326068d33a25Syt int rval = AHCI_SUCCESS; 3261a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 32622fcbc377Syt 32632fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 32642fcbc377Syt "Port %d port resetting...", port); 326568d33a25Syt ahci_portp->ahciport_port_state = 0; 32662fcbc377Syt 32672fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 32682fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 32692fcbc377Syt 32702fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 32712fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 32722fcbc377Syt 32732fcbc377Syt if (cap_status & AHCI_HBA_CAP_SSS) { 32742fcbc377Syt /* 32752fcbc377Syt * HBA support staggered spin-up, if the port has 32762fcbc377Syt * not spin up yet, then force it to do spin-up 32772fcbc377Syt */ 32782fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) { 32792fcbc377Syt if (!(ahci_portp->ahciport_flags 328068d33a25Syt & AHCI_PORT_FLAG_SPINUP)) { 32812fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 32822fcbc377Syt "Port %d PxCMD.SUD is zero, force " 32832fcbc377Syt "it to do spin-up", port); 32842fcbc377Syt ahci_portp->ahciport_flags |= 328568d33a25Syt AHCI_PORT_FLAG_SPINUP; 32862fcbc377Syt } 32872fcbc377Syt } 32882fcbc377Syt } else { 32892fcbc377Syt /* 32902fcbc377Syt * HBA doesn't support stagger spin-up, force it 32912fcbc377Syt * to do normal COMRESET 32922fcbc377Syt */ 32932fcbc377Syt if (ahci_portp->ahciport_flags & 329468d33a25Syt AHCI_PORT_FLAG_SPINUP) { 32952fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 32962fcbc377Syt "HBA does not support staggered spin-up " 32972fcbc377Syt "force it to do normal COMRESET"); 32982fcbc377Syt ahci_portp->ahciport_flags &= 329968d33a25Syt ~AHCI_PORT_FLAG_SPINUP; 33002fcbc377Syt } 33012fcbc377Syt } 33022fcbc377Syt 330368d33a25Syt if (!(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_SPINUP)) { 33042fcbc377Syt /* Do normal COMRESET */ 33052fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 33062fcbc377Syt "ahci_port_reset: do normal COMRESET", port); 33072fcbc377Syt 330895c11c1fSyt /* 330995c11c1fSyt * According to the spec, SUD bit should be set here, 331095c11c1fSyt * but JMicron JMB363 doesn't follow it, so remove 331195c11c1fSyt * the assertion, and just print a debug message. 331295c11c1fSyt */ 331395c11c1fSyt #if AHCI_DEBUG 331495c11c1fSyt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) 331595c11c1fSyt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 331695c11c1fSyt "port %d SUD bit not set", port) 331795c11c1fSyt #endif 33182fcbc377Syt 33192fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33202fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 332182263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_COMRESET); 33222fcbc377Syt 33232fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33242fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 33252fcbc377Syt port_scontrol); 33262fcbc377Syt 33272fcbc377Syt /* Enable PxCMD.FRE to read device */ 33282fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33292fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 33302fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 33312fcbc377Syt 333268d33a25Syt /* 333368d33a25Syt * Give time for COMRESET to percolate, according to the AHCI 333468d33a25Syt * spec, software shall wait at least 1 millisecond before 333568d33a25Syt * clearing PxSCTL.DET 333668d33a25Syt */ 33372fcbc377Syt delay(AHCI_1MS_TICKS*2); 33382fcbc377Syt 33392fcbc377Syt /* Fetch the SCONTROL again and rewrite the DET part with 0 */ 33402fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33412fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 334282263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 33432fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33442fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 33452fcbc377Syt port_scontrol); 33462fcbc377Syt } else { 33472fcbc377Syt /* Do staggered spin-up */ 33482fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33492fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 335082263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 33512fcbc377Syt 33522fcbc377Syt /* PxSCTL.DET must be 0 */ 33532fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33542fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 33552fcbc377Syt port_scontrol); 33562fcbc377Syt 33572fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_SUD; 33582fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33592fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 33602fcbc377Syt port_cmd_status); 33612fcbc377Syt 33622fcbc377Syt /* 0 -> 1 edge */ 33632fcbc377Syt delay(AHCI_1MS_TICKS*2); 33642fcbc377Syt 33652fcbc377Syt /* Set PxCMD.SUD to 1 */ 33662fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33672fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 33682fcbc377Syt port_cmd_status |= AHCI_CMD_STATUS_SUD; 33692fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33702fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 33712fcbc377Syt port_cmd_status); 33722fcbc377Syt 33732fcbc377Syt /* Enable PxCMD.FRE to read device */ 33742fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33752fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 33762fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 33772fcbc377Syt } 33782fcbc377Syt 33792fcbc377Syt /* 338068d33a25Syt * The port enters P:StartComm state, and HBA tells link layer to 338168d33a25Syt * start communication, which involves sending COMRESET to device. 338268d33a25Syt * And the HBA resets PxTFD.STS to 7Fh. 338368d33a25Syt * 338468d33a25Syt * When a COMINIT is received from the device, then the port enters 338568d33a25Syt * P:ComInit state. And HBA sets PxTFD.STS to FFh or 80h. HBA sets 338668d33a25Syt * PxSSTS.DET to 1h to indicate a device is detected but communication 338768d33a25Syt * is not yet established. HBA sets PxSERR.DIAG.X to '1' to indicate 338868d33a25Syt * a COMINIT has been received. 33892fcbc377Syt */ 33902fcbc377Syt /* 33912fcbc377Syt * The DET field is valid only if IPM field indicates 33922fcbc377Syt * that the interface is in active state. 33932fcbc377Syt */ 33942fcbc377Syt loop_count = 0; 33952fcbc377Syt do { 33962fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33972fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 33982fcbc377Syt 339982263d52Syt if (SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) { 34002fcbc377Syt /* 34012fcbc377Syt * If the interface is not active, the DET field 34022fcbc377Syt * is considered not accurate. So we want to 34032fcbc377Syt * continue looping. 34042fcbc377Syt */ 340582263d52Syt SSTATUS_SET_DET(port_sstatus, SSTATUS_DET_NODEV); 34062fcbc377Syt } 34072fcbc377Syt 34082fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SSTATUS) { 34092fcbc377Syt /* 34102fcbc377Syt * We are effectively timing out after 0.1 sec. 34112fcbc377Syt */ 34122fcbc377Syt break; 34132fcbc377Syt } 34142fcbc377Syt 34152fcbc377Syt /* Wait for 10 millisec */ 34162fcbc377Syt delay(AHCI_10MS_TICKS); 341782263d52Syt } while (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM); 34182fcbc377Syt 341968d33a25Syt AHCIDBG3(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 342068d33a25Syt "ahci_port_reset: 1st loop count: %d, " 342168d33a25Syt "port_sstatus = 0x%x port %d", 342268d33a25Syt loop_count, port_sstatus, port); 34232fcbc377Syt 342482263d52Syt if ((SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) || 342582263d52Syt (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM)) { 34262fcbc377Syt /* 34272fcbc377Syt * Either the port is not active or there 34282fcbc377Syt * is no device present. 34292fcbc377Syt */ 34302fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_NONE; 343168d33a25Syt goto out; 34322fcbc377Syt } 34332fcbc377Syt 343468d33a25Syt /* Now we can make sure there is a device connected to the port */ 343568d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 343668d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 34372fcbc377Syt 343868d33a25Syt /* a COMINIT signal is supposed to be received */ 343968d33a25Syt if (!(port_intr_status & AHCI_INTR_STATUS_PCS)) { 3440a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d " 3441a9440e8dSyt "COMINIT signal from the device not received", 3442a9440e8dSyt instance, port); 344368d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED; 344468d33a25Syt rval = AHCI_FAILURE; 344568d33a25Syt goto out; 344668d33a25Syt } 34472fcbc377Syt 344895c11c1fSyt /* 344995c11c1fSyt * According to the spec, when PxSCTL.DET is set to 0h, upon 345095c11c1fSyt * receiving a COMINIT from the attached device, PxTFD.STS.BSY 345195c11c1fSyt * shall be set to '1' by the HBA. 345295c11c1fSyt * 345395c11c1fSyt * However, we found JMicron JMB363 doesn't follow this, so 345495c11c1fSyt * remove this check, and just print a debug message. 345595c11c1fSyt */ 345695c11c1fSyt #if AHCI_DEBUG 345768d33a25Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 345868d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 345968d33a25Syt if (!(port_task_file & AHCI_TFD_STS_BSY)) { 346095c11c1fSyt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: " 346195c11c1fSyt "port %d BSY bit is not set after COMINIT signal " 346295c11c1fSyt "is received", port); 346368d33a25Syt } 346495c11c1fSyt #endif 34652fcbc377Syt 346668d33a25Syt /* 346768d33a25Syt * PxSERR.DIAG.X has to be cleared in order to update PxTFD with 346868d33a25Syt * the D2H FIS received by HBA. 346968d33a25Syt */ 347068d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 347168d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 347282263d52Syt SERROR_EXCHANGED_ERR); 34732fcbc377Syt 347468d33a25Syt /* 347568d33a25Syt * Next check whether COMRESET is completed successfully 347668d33a25Syt */ 347768d33a25Syt loop_count = 0; 347868d33a25Syt do { 347968d33a25Syt port_task_file = 348068d33a25Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 348168d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 34822fcbc377Syt 34832fcbc377Syt /* 348468d33a25Syt * The Error bit '1' means COMRESET is finished successfully 348568d33a25Syt * The device hardware has been initialized and the power-up 348668d33a25Syt * diagnostics successfully completed. 34872fcbc377Syt */ 348868d33a25Syt if (((port_task_file & AHCI_TFD_ERR_MASK) 348968d33a25Syt >> AHCI_TFD_ERR_SHIFT) == 0x1) { 3490689d74b0Syt #if AHCI_DEBUG 349168d33a25Syt port_signature = ddi_get32( 349268d33a25Syt ahci_ctlp->ahcictl_ahci_acc_handle, 349368d33a25Syt (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port)); 349468d33a25Syt AHCIDBG2(AHCIDBG_INFO, ahci_ctlp, 349568d33a25Syt "COMRESET success, D2H register FIS " 349668d33a25Syt "post to received FIS structure " 349768d33a25Syt "port %d signature = 0x%x", 349868d33a25Syt port, port_signature); 3499689d74b0Syt #endif 350068d33a25Syt goto out_check; 350168d33a25Syt } 35022fcbc377Syt 350368d33a25Syt if (loop_count++ > AHCI_POLLRATE_PORT_TFD_ERROR) { 350468d33a25Syt /* 350568d33a25Syt * We are effectively timing out after 11 sec. 350668d33a25Syt */ 350768d33a25Syt break; 350868d33a25Syt } 35092fcbc377Syt 351068d33a25Syt /* Wait for 10 millisec */ 351168d33a25Syt delay(AHCI_10MS_TICKS); 351268d33a25Syt } while (((port_task_file & AHCI_TFD_ERR_MASK) 351368d33a25Syt >> AHCI_TFD_ERR_SHIFT) != 0x1); 35142fcbc377Syt 351568d33a25Syt AHCIDBG3(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: 2nd loop " 351668d33a25Syt "count: %d, port_task_file = 0x%x port %d", 351768d33a25Syt loop_count, port_task_file, port); 35182fcbc377Syt 3519a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d the device hardware " 352068d33a25Syt "has been initialized and the power-up diagnostics failed", 3521a9440e8dSyt instance, port); 35222fcbc377Syt 352368d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED; 352468d33a25Syt rval = AHCI_FAILURE; 35252fcbc377Syt 352668d33a25Syt out: 352768d33a25Syt /* Clear port serror register for the port */ 352868d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 352968d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 353068d33a25Syt AHCI_SERROR_CLEAR_ALL); 353168d33a25Syt 353268d33a25Syt return (rval); 353368d33a25Syt 353468d33a25Syt out_check: 353568d33a25Syt /* 353668d33a25Syt * Check device status, if keep busy or COMRESET error 353768d33a25Syt * do device reset to patch some SATA disks' issue 353868d33a25Syt * 353968d33a25Syt * For VT8251, sometimes need to do the device reset 354068d33a25Syt */ 354168d33a25Syt if ((port_task_file & AHCI_TFD_STS_BSY) || 354268d33a25Syt (port_task_file & AHCI_TFD_STS_DRQ)) { 354368d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "port %d keep BSY/DRQ set " 354468d33a25Syt "need to do device reset", port); 35452fcbc377Syt 354668d33a25Syt (void) ahci_software_reset(ahci_ctlp, ahci_portp, port); 35472fcbc377Syt 354868d33a25Syt port_task_file = 354968d33a25Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 355068d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 355168d33a25Syt 355268d33a25Syt if (port_task_file & AHCI_TFD_STS_BSY || 355368d33a25Syt port_task_file & AHCI_TFD_STS_DRQ) { 3554a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_port_reset: port %d " 355568d33a25Syt "BSY/DRQ still set after device reset " 3556a9440e8dSyt "port_task_file = 0x%x", instance, 355768d33a25Syt port, port_task_file); 355868d33a25Syt ahci_portp->ahciport_port_state |= SATA_PSTATE_FAILED; 355968d33a25Syt rval = AHCI_FAILURE; 35602fcbc377Syt } 35612fcbc377Syt } 35622fcbc377Syt 356368d33a25Syt goto out; 35642fcbc377Syt } 35652fcbc377Syt 35662fcbc377Syt /* 35672fcbc377Syt * AHCI HBA reset ...; the entire HBA is reset, and all ports are disabled. 35682fcbc377Syt * This is the most intrusive. 35692fcbc377Syt * 357068d33a25Syt * When an HBA reset occurs, Phy communication will be re-established with 357168d33a25Syt * the device through a COMRESET followed by the normal out-of-band 357268d33a25Syt * communication sequence defined in Serial ATA. AT the end of reset, the 357368d33a25Syt * device, if working properly, will send a D2H Register FIS, which contains 357468d33a25Syt * the device signature. When the HBA receives this FIS, it updates PxTFD.STS 357568d33a25Syt * and PxTFD.ERR register fields, and updates the PxSIG register with the 357668d33a25Syt * signature. 35772fcbc377Syt * 35782fcbc377Syt * Remember to set GHC.AE to 1 before calling ahci_hba_reset. 35792fcbc377Syt */ 35802fcbc377Syt static int 35812fcbc377Syt ahci_hba_reset(ahci_ctl_t *ahci_ctlp) 35822fcbc377Syt { 35832fcbc377Syt ahci_port_t *ahci_portp; 35842fcbc377Syt uint32_t ghc_control; 35852fcbc377Syt uint8_t port; 35862fcbc377Syt int loop_count; 35872fcbc377Syt int rval = AHCI_SUCCESS; 35882fcbc377Syt 35892fcbc377Syt AHCIDBG0(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, "HBA resetting"); 35902fcbc377Syt 359168d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 359268d33a25Syt 35932fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35942fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 35952fcbc377Syt 35962fcbc377Syt /* Setting GHC.HR to 1, remember GHC.AE is already set to 1 before */ 35972fcbc377Syt ghc_control |= AHCI_HBA_GHC_HR; 35982fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 35992fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 36002fcbc377Syt 36012fcbc377Syt /* 36022fcbc377Syt * Wait until HBA Reset complete or timeout 36032fcbc377Syt */ 36042fcbc377Syt loop_count = 0; 36052fcbc377Syt do { 36062fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 36072fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 36082fcbc377Syt 36092fcbc377Syt if (loop_count++ > AHCI_POLLRATE_HBA_RESET) { 36102fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, 36112fcbc377Syt "ahci hba reset is timing out, " 36122fcbc377Syt "ghc_control = 0x%x", ghc_control); 36132fcbc377Syt /* We are effectively timing out after 1 sec. */ 36142fcbc377Syt break; 36152fcbc377Syt } 36162fcbc377Syt 36172fcbc377Syt /* Wait for 10 millisec */ 36182fcbc377Syt delay(AHCI_10MS_TICKS); 36192fcbc377Syt } while (ghc_control & AHCI_HBA_GHC_HR); 36202fcbc377Syt 36212fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 36222fcbc377Syt "ahci_hba_reset: 1st loop count: %d, " 36232fcbc377Syt "ghc_control = 0x%x", loop_count, ghc_control); 36242fcbc377Syt 36252fcbc377Syt if (ghc_control & AHCI_HBA_GHC_HR) { 36262fcbc377Syt /* The hba is not reset for some reasons */ 36272fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 36282fcbc377Syt "hba reset failed: HBA in a hung or locked state"); 362968d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 36302fcbc377Syt return (AHCI_FAILURE); 36312fcbc377Syt } 36322fcbc377Syt 36332fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 36342fcbc377Syt /* Only check implemented ports */ 36352fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 36362fcbc377Syt continue; 36372fcbc377Syt } 36382fcbc377Syt 36392fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 36402fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 36412fcbc377Syt 36422fcbc377Syt if (ahci_port_reset(ahci_ctlp, ahci_portp, port) 36432fcbc377Syt != AHCI_SUCCESS) { 36442fcbc377Syt rval = AHCI_FAILURE; 36452fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 36462fcbc377Syt "ahci_hba_reset: port %d failed", port); 36472fcbc377Syt } 36482fcbc377Syt 36492fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 36502fcbc377Syt } 36512fcbc377Syt 36522fcbc377Syt /* 36532fcbc377Syt * Indicate that system software is AHCI aware by setting 36542fcbc377Syt * GHC.AE to 1 36552fcbc377Syt */ 36562fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 36572fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 36582fcbc377Syt 36592fcbc377Syt ghc_control |= AHCI_HBA_GHC_AE; 36602fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 36612fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 36622fcbc377Syt 366368d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 366468d33a25Syt 36652fcbc377Syt return (rval); 36662fcbc377Syt } 36672fcbc377Syt 36682fcbc377Syt /* 36692fcbc377Syt * This routine is only called from AHCI_ATTACH or phyrdy change 36702fcbc377Syt * case. It first calls port reset to initialize port, probe port and probe 367168d33a25Syt * device, then try to read PxSIG register to find the type of device 367268d33a25Syt * attached to the port. 36732fcbc377Syt * 36742fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 367568d33a25Syt * is called. And the port interrupt is disabled. 36762fcbc377Syt */ 367768d33a25Syt static void 36782fcbc377Syt ahci_find_dev_signature(ahci_ctl_t *ahci_ctlp, 36792fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 36802fcbc377Syt { 36812fcbc377Syt uint32_t signature; 36822fcbc377Syt 36832fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 368468d33a25Syt "ahci_find_dev_signature enter: port %d", port); 36852fcbc377Syt 368668d33a25Syt ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 36872fcbc377Syt 36882fcbc377Syt /* Call port reset to check link status and get device signature */ 368968d33a25Syt (void) ahci_port_reset(ahci_ctlp, ahci_portp, port); 369068d33a25Syt 369168d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 369268d33a25Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 369368d33a25Syt "ahci_find_dev_signature: No device is found " 369468d33a25Syt "at port %d", port); 369568d33a25Syt return; 36962fcbc377Syt } 36972fcbc377Syt 369868d33a25Syt /* Check the port state */ 369968d33a25Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 370068d33a25Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, 370168d33a25Syt "ahci_find_dev_signature: port %d state 0x%x", 370268d33a25Syt port, ahci_portp->ahciport_port_state); 370368d33a25Syt return; 370468d33a25Syt } 37052fcbc377Syt 37062fcbc377Syt signature = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 37072fcbc377Syt (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port)); 37082fcbc377Syt 37092fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_INFO, ahci_ctlp, 371068d33a25Syt "ahci_find_dev_signature: port %d signature = 0x%x", 371168d33a25Syt port, signature); 37122fcbc377Syt 37132fcbc377Syt switch (signature) { 37142fcbc377Syt 37152fcbc377Syt case AHCI_SIGNATURE_DISK: 37162fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_ATADISK; 37172fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 37182fcbc377Syt "Disk is found at port: %d", port); 37192fcbc377Syt break; 37202fcbc377Syt 37212fcbc377Syt case AHCI_SIGNATURE_ATAPI: 37222fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_ATAPICD; 37232fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 37242fcbc377Syt "ATAPI device is found at port: %d", port); 37252fcbc377Syt break; 37262fcbc377Syt 37272fcbc377Syt case AHCI_SIGNATURE_PORT_MULTIPLIER: 37282fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_PMULT; 37292fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 37302fcbc377Syt "Port Multiplier is found at port: %d", port); 37312fcbc377Syt break; 37322fcbc377Syt 37332fcbc377Syt default: 37342fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_UNKNOWN; 37352fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, 37362fcbc377Syt "Unknown device is found at port: %d", port); 37372fcbc377Syt } 37382fcbc377Syt } 37392fcbc377Syt 374013bcbb7aSyt /* 374113bcbb7aSyt * According to the spec, to reliably detect hot plug removals, software 374213bcbb7aSyt * must disable interface power management. Software should perform the 374313bcbb7aSyt * following initialization on a port after a device is attached: 374413bcbb7aSyt * Set PxSCTL.IPM to 3h to disable interface state transitions 374513bcbb7aSyt * Set PxCMD.ALPE to '0' to disable aggressive power management 374613bcbb7aSyt * Disable device initiated interface power management by SET FEATURE 374713bcbb7aSyt * 374813bcbb7aSyt * We can ignore the last item because by default the feature is disabled 374913bcbb7aSyt */ 375013bcbb7aSyt static void 375113bcbb7aSyt ahci_disable_interface_pm(ahci_ctl_t *ahci_ctlp, uint8_t port) 375213bcbb7aSyt { 375313bcbb7aSyt uint32_t port_scontrol, port_cmd_status; 375413bcbb7aSyt 375513bcbb7aSyt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 375613bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 375713bcbb7aSyt SCONTROL_SET_IPM(port_scontrol, SCONTROL_IPM_DISABLE_BOTH); 375813bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 375913bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), port_scontrol); 376013bcbb7aSyt 376113bcbb7aSyt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 376213bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 376313bcbb7aSyt port_cmd_status &= ~AHCI_CMD_STATUS_ALPE; 376413bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 376513bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 376613bcbb7aSyt } 376713bcbb7aSyt 37682fcbc377Syt /* 376968d33a25Syt * Start the port - set PxCMD.ST to 1, if PxCMD.FRE is not set 377068d33a25Syt * to 1, then set it firstly. 377168d33a25Syt * 377268d33a25Syt * Each port contains two major DMA engines. One DMA engine walks through 377368d33a25Syt * the command list, and is controlled by PxCMD.ST. The second DMA engine 377468d33a25Syt * copies received FISes into system memory, and is controlled by PxCMD.FRE. 377568d33a25Syt * 377668d33a25Syt * Software shall not set PxCMD.ST to '1' until it verifies that PxCMD.CR 377768d33a25Syt * is '0' and has set PxCMD.FRE is '1'. And software shall not clear 377868d33a25Syt * PxCMD.FRE while PxCMD.ST or PxCMD.CR is set '1'. 377968d33a25Syt * 378068d33a25Syt * Software shall not set PxCMD.ST to '1' unless a functional device is 378168d33a25Syt * present on the port(as determined by PxTFD.STS.BSY = '0', 378268d33a25Syt * PxTFD.STS.DRQ = '0', and PxSSTS.DET = 3h). 37832fcbc377Syt * 37842fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 37852fcbc377Syt * is called. 37862fcbc377Syt */ 37872fcbc377Syt static int 378868d33a25Syt ahci_start_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 37892fcbc377Syt { 379068d33a25Syt uint32_t port_cmd_status; 37912fcbc377Syt 379268d33a25Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, "ahci_start_port: %d enter", port); 37932fcbc377Syt 379468d33a25Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 379568d33a25Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 379668d33a25Syt "the state for port %d is 0x%x", 379768d33a25Syt port, ahci_portp->ahciport_port_state); 37982fcbc377Syt return (AHCI_FAILURE); 379968d33a25Syt } 38002fcbc377Syt 380168d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 380268d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 380368d33a25Syt "no device is attached at port %d", port); 380468d33a25Syt return (AHCI_FAILURE); 380568d33a25Syt } 38062fcbc377Syt 380768d33a25Syt /* First to set PxCMD.FRE before setting PxCMD.ST. */ 380868d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 380968d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 38102fcbc377Syt 381168d33a25Syt if (!(port_cmd_status & AHCI_CMD_STATUS_FRE)) { 381268d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_FRE; 38132fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 38142fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 38152fcbc377Syt port_cmd_status); 38162fcbc377Syt } 381768d33a25Syt 381868d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 381968d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 382068d33a25Syt 382168d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_ST; 382268d33a25Syt 382368d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 382468d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 382568d33a25Syt port_cmd_status); 382668d33a25Syt 382768d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_STARTED; 382868d33a25Syt 382968d33a25Syt return (AHCI_SUCCESS); 38302fcbc377Syt } 38312fcbc377Syt 38322fcbc377Syt /* 38332fcbc377Syt * Allocate the ahci_port_t including Received FIS and Command List. 38342fcbc377Syt * The argument - port is the physical port number, and not logical 38352fcbc377Syt * port number seen by the SATA framework. 38362fcbc377Syt * 38372fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 38382fcbc377Syt * is called. 38392fcbc377Syt */ 38402fcbc377Syt static int 38412fcbc377Syt ahci_alloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 38422fcbc377Syt { 38432fcbc377Syt ahci_port_t *ahci_portp; 38442fcbc377Syt 38452fcbc377Syt ahci_portp = 38462fcbc377Syt (ahci_port_t *)kmem_zalloc(sizeof (ahci_port_t), KM_SLEEP); 38472fcbc377Syt 38482fcbc377Syt ahci_ctlp->ahcictl_ports[port] = ahci_portp; 38492fcbc377Syt ahci_portp->ahciport_port_num = port; 38502fcbc377Syt 385168d33a25Syt /* Intialize the port condition variable */ 385268d33a25Syt cv_init(&ahci_portp->ahciport_cv, NULL, CV_DRIVER, NULL); 385368d33a25Syt 385468d33a25Syt /* Initialize the port mutex */ 38552fcbc377Syt mutex_init(&ahci_portp->ahciport_mutex, NULL, MUTEX_DRIVER, 38562fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 385768d33a25Syt 38582fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 38592fcbc377Syt 38602fcbc377Syt /* 38612fcbc377Syt * Allocate memory for received FIS structure and 38622fcbc377Syt * command list for this port 38632fcbc377Syt */ 38642fcbc377Syt if (ahci_alloc_rcvd_fis(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 38652fcbc377Syt goto err_case1; 38662fcbc377Syt } 38672fcbc377Syt 38682fcbc377Syt if (ahci_alloc_cmd_list(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 38692fcbc377Syt goto err_case2; 38702fcbc377Syt } 38712fcbc377Syt 387268d33a25Syt ahci_portp->ahciport_event_args = 387368d33a25Syt kmem_zalloc(sizeof (ahci_event_arg_t), KM_SLEEP); 387468d33a25Syt 387568d33a25Syt if (ahci_portp->ahciport_event_args == NULL) 387668d33a25Syt goto err_case3; 387768d33a25Syt 38782fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 38792fcbc377Syt 38802fcbc377Syt return (AHCI_SUCCESS); 38812fcbc377Syt 388268d33a25Syt err_case3: 388368d33a25Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 388468d33a25Syt 38852fcbc377Syt err_case2: 3886689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 38872fcbc377Syt 38882fcbc377Syt err_case1: 38892fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 38902fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 389168d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 38922fcbc377Syt 38932fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 38942fcbc377Syt 38952fcbc377Syt return (AHCI_FAILURE); 38962fcbc377Syt } 38972fcbc377Syt 38982fcbc377Syt /* 38992fcbc377Syt * Reverse of ahci_dealloc_port_state(). 39002fcbc377Syt * 39012fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 39022fcbc377Syt * is called. 39032fcbc377Syt */ 39042fcbc377Syt static void 39052fcbc377Syt ahci_dealloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 39062fcbc377Syt { 39072fcbc377Syt ahci_port_t *ahci_portp = ahci_ctlp->ahcictl_ports[port]; 39082fcbc377Syt 39092fcbc377Syt ASSERT(ahci_portp != NULL); 39102fcbc377Syt 39112fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 391268d33a25Syt kmem_free(ahci_portp->ahciport_event_args, sizeof (ahci_event_arg_t)); 391368d33a25Syt ahci_portp->ahciport_event_args = NULL; 39142fcbc377Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 3915689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 39162fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 39172fcbc377Syt 39182fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 391968d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 39202fcbc377Syt 39212fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 39222fcbc377Syt 39232fcbc377Syt ahci_ctlp->ahcictl_ports[port] = NULL; 39242fcbc377Syt } 39252fcbc377Syt 39262fcbc377Syt /* 39272fcbc377Syt * Allocates memory for the Received FIS Structure 39282fcbc377Syt * 39292fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 39302fcbc377Syt * is called. 39312fcbc377Syt */ 39322fcbc377Syt static int 39332fcbc377Syt ahci_alloc_rcvd_fis(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 39342fcbc377Syt uint8_t port) 39352fcbc377Syt { 39362fcbc377Syt size_t rcvd_fis_size; 39372fcbc377Syt size_t ret_len; 39382fcbc377Syt uint_t cookie_count; 39392fcbc377Syt 39402fcbc377Syt rcvd_fis_size = sizeof (ahci_rcvd_fis_t); 39412fcbc377Syt 39422fcbc377Syt /* allocate rcvd FIS dma handle. */ 39432fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 39442fcbc377Syt &ahci_ctlp->ahcictl_rcvd_fis_dma_attr, 39452fcbc377Syt DDI_DMA_SLEEP, 39462fcbc377Syt NULL, 39472fcbc377Syt &ahci_portp->ahciport_rcvd_fis_dma_handle) != 39482fcbc377Syt DDI_SUCCESS) { 39492fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 39502fcbc377Syt "rcvd FIS dma handle alloc failed"); 39512fcbc377Syt 39522fcbc377Syt return (AHCI_FAILURE); 39532fcbc377Syt } 39542fcbc377Syt 39552fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_rcvd_fis_dma_handle, 39562fcbc377Syt rcvd_fis_size, 39572fcbc377Syt &accattr, 39582fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 39592fcbc377Syt DDI_DMA_SLEEP, 39602fcbc377Syt NULL, 39612fcbc377Syt (caddr_t *)&ahci_portp->ahciport_rcvd_fis, 39622fcbc377Syt &ret_len, 39632fcbc377Syt &ahci_portp->ahciport_rcvd_fis_acc_handle) != NULL) { 39642fcbc377Syt 39652fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 39662fcbc377Syt "rcvd FIS dma mem alloc fail"); 39672fcbc377Syt /* error.. free the dma handle. */ 39682fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 39692fcbc377Syt return (AHCI_FAILURE); 39702fcbc377Syt } 39712fcbc377Syt 39722fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle, 39732fcbc377Syt NULL, 39742fcbc377Syt (caddr_t)ahci_portp->ahciport_rcvd_fis, 39752fcbc377Syt rcvd_fis_size, 39762fcbc377Syt DDI_DMA_CONSISTENT, 39772fcbc377Syt DDI_DMA_SLEEP, 39782fcbc377Syt NULL, 397913bcbb7aSyt &ahci_portp->ahciport_rcvd_fis_dma_cookie, 39802fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 39812fcbc377Syt 39822fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 39832fcbc377Syt "rcvd FIS dma handle bind fail"); 39842fcbc377Syt /* error.. free the dma handle & free the memory. */ 39852fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 39862fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 39872fcbc377Syt return (AHCI_FAILURE); 39882fcbc377Syt } 39892fcbc377Syt 39902fcbc377Syt bzero((void *)ahci_portp->ahciport_rcvd_fis, rcvd_fis_size); 39912fcbc377Syt 39922fcbc377Syt /* Config Port Received FIS Base Address */ 39932fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 39942fcbc377Syt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 399513bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 39962fcbc377Syt 39972fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 399813bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 39992fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 400013bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_address); 40012fcbc377Syt 40022fcbc377Syt return (AHCI_SUCCESS); 40032fcbc377Syt } 40042fcbc377Syt 40052fcbc377Syt /* 40062fcbc377Syt * Deallocates the Received FIS Structure 40072fcbc377Syt * 40082fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 40092fcbc377Syt * is called. 40102fcbc377Syt */ 40112fcbc377Syt static void 4012689d74b0Syt ahci_dealloc_rcvd_fis(ahci_port_t *ahci_portp) 40132fcbc377Syt { 40142fcbc377Syt /* Unbind the cmd list dma handle first. */ 40152fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle); 40162fcbc377Syt 40172fcbc377Syt /* Then free the underlying memory. */ 40182fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 40192fcbc377Syt 40202fcbc377Syt /* Now free the handle itself. */ 40212fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 40222fcbc377Syt } 40232fcbc377Syt 40242fcbc377Syt /* 40252fcbc377Syt * Allocates memory for the Command List, which contains up to 32 entries. 40262fcbc377Syt * Each entry contains a command header, which is a 32-byte structure that 40272fcbc377Syt * includes the pointer to the command table. 40282fcbc377Syt * 40292fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 40302fcbc377Syt * is called. 40312fcbc377Syt */ 40322fcbc377Syt static int 40332fcbc377Syt ahci_alloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 40342fcbc377Syt uint8_t port) 40352fcbc377Syt { 40362fcbc377Syt size_t cmd_list_size; 40372fcbc377Syt size_t ret_len; 40382fcbc377Syt uint_t cookie_count; 40392fcbc377Syt 40402fcbc377Syt cmd_list_size = 40412fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots * sizeof (ahci_cmd_header_t); 40422fcbc377Syt 40432fcbc377Syt /* allocate cmd list dma handle. */ 40442fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 40452fcbc377Syt &ahci_ctlp->ahcictl_cmd_list_dma_attr, 40462fcbc377Syt DDI_DMA_SLEEP, 40472fcbc377Syt NULL, 40482fcbc377Syt &ahci_portp->ahciport_cmd_list_dma_handle) != DDI_SUCCESS) { 40492fcbc377Syt 40502fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 40512fcbc377Syt "cmd list dma handle alloc failed"); 40522fcbc377Syt return (AHCI_FAILURE); 40532fcbc377Syt } 40542fcbc377Syt 40552fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_cmd_list_dma_handle, 40562fcbc377Syt cmd_list_size, 40572fcbc377Syt &accattr, 40582fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 40592fcbc377Syt DDI_DMA_SLEEP, 40602fcbc377Syt NULL, 40612fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_list, 40622fcbc377Syt &ret_len, 40632fcbc377Syt &ahci_portp->ahciport_cmd_list_acc_handle) != NULL) { 40642fcbc377Syt 40652fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 40662fcbc377Syt "cmd list dma mem alloc fail"); 40672fcbc377Syt /* error.. free the dma handle. */ 40682fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 40692fcbc377Syt return (AHCI_FAILURE); 40702fcbc377Syt } 40712fcbc377Syt 40722fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_cmd_list_dma_handle, 40732fcbc377Syt NULL, 40742fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_list, 40752fcbc377Syt cmd_list_size, 40762fcbc377Syt DDI_DMA_CONSISTENT, 40772fcbc377Syt DDI_DMA_SLEEP, 40782fcbc377Syt NULL, 407913bcbb7aSyt &ahci_portp->ahciport_cmd_list_dma_cookie, 40802fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 40812fcbc377Syt 40822fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 40832fcbc377Syt "cmd list dma handle bind fail"); 40842fcbc377Syt /* error.. free the dma handle & free the memory. */ 40852fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 40862fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 40872fcbc377Syt return (AHCI_FAILURE); 40882fcbc377Syt } 40892fcbc377Syt 40902fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_list, cmd_list_size); 40912fcbc377Syt 40922fcbc377Syt /* Config Port Command List Base Address */ 40932fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 40942fcbc377Syt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 409513bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 40962fcbc377Syt 40972fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 409813bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 40992fcbc377Syt 41002fcbc377Syt AHCIDBG1(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 410113bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_address); 41022fcbc377Syt 41032fcbc377Syt if (ahci_alloc_cmd_tables(ahci_ctlp, ahci_portp) != AHCI_SUCCESS) { 4104e2decd04Syt goto err_out; 41052fcbc377Syt } 41062fcbc377Syt 41072fcbc377Syt return (AHCI_SUCCESS); 4108e2decd04Syt 4109e2decd04Syt err_out: 4110e2decd04Syt /* Unbind the cmd list dma handle first. */ 4111e2decd04Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 4112e2decd04Syt 4113e2decd04Syt /* Then free the underlying memory. */ 4114e2decd04Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 4115e2decd04Syt 4116e2decd04Syt /* Now free the handle itself. */ 4117e2decd04Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 4118e2decd04Syt 4119e2decd04Syt return (AHCI_FAILURE); 41202fcbc377Syt } 41212fcbc377Syt 41222fcbc377Syt /* 41232fcbc377Syt * Deallocates the Command List 41242fcbc377Syt * 41252fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 41262fcbc377Syt * is called. 41272fcbc377Syt */ 41282fcbc377Syt static void 41292fcbc377Syt ahci_dealloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 41302fcbc377Syt { 41312fcbc377Syt /* First dealloc command table */ 41322fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctlp, ahci_portp); 41332fcbc377Syt 41342fcbc377Syt /* Unbind the cmd list dma handle first. */ 41352fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 41362fcbc377Syt 41372fcbc377Syt /* Then free the underlying memory. */ 41382fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 41392fcbc377Syt 41402fcbc377Syt /* Now free the handle itself. */ 41412fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 41422fcbc377Syt } 41432fcbc377Syt 41442fcbc377Syt /* 41452fcbc377Syt * Allocates memory for all Command Tables, which contains Command FIS, 41462fcbc377Syt * ATAPI Command and Physical Region Descriptor Table. 41472fcbc377Syt * 41482fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 41492fcbc377Syt * is called. 41502fcbc377Syt */ 41512fcbc377Syt static int 41522fcbc377Syt ahci_alloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 41532fcbc377Syt { 41542fcbc377Syt size_t ret_len; 41552fcbc377Syt ddi_dma_cookie_t cmd_table_dma_cookie; 41562fcbc377Syt uint_t cookie_count; 41572fcbc377Syt int slot; 41582fcbc377Syt 41592fcbc377Syt AHCIDBG1(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 41602fcbc377Syt "ahci_alloc_cmd_tables: port %d enter", 41612fcbc377Syt ahci_portp->ahciport_port_num); 41622fcbc377Syt 41632fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 41642fcbc377Syt /* Allocate cmd table dma handle. */ 41652fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 41662fcbc377Syt &ahci_ctlp->ahcictl_cmd_table_dma_attr, 41672fcbc377Syt DDI_DMA_SLEEP, 41682fcbc377Syt NULL, 41692fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]) != 41702fcbc377Syt DDI_SUCCESS) { 41712fcbc377Syt 41722fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 41732fcbc377Syt "cmd table dma handle alloc failed"); 41742fcbc377Syt 41752fcbc377Syt goto err_out; 41762fcbc377Syt } 41772fcbc377Syt 41782fcbc377Syt if (ddi_dma_mem_alloc( 41792fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 41802fcbc377Syt ahci_cmd_table_size, 41812fcbc377Syt &accattr, 41822fcbc377Syt DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 41832fcbc377Syt DDI_DMA_SLEEP, 41842fcbc377Syt NULL, 41852fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_tables[slot], 41862fcbc377Syt &ret_len, 41872fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]) != 41882fcbc377Syt NULL) { 41892fcbc377Syt 41902fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 41912fcbc377Syt "cmd table dma mem alloc fail"); 41922fcbc377Syt 41932fcbc377Syt /* error.. free the dma handle. */ 41942fcbc377Syt ddi_dma_free_handle( 41952fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 41962fcbc377Syt goto err_out; 41972fcbc377Syt } 41982fcbc377Syt 41992fcbc377Syt if (ddi_dma_addr_bind_handle( 42002fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 42012fcbc377Syt NULL, 42022fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_tables[slot], 42032fcbc377Syt ahci_cmd_table_size, 42042fcbc377Syt DDI_DMA_CONSISTENT, 42052fcbc377Syt DDI_DMA_SLEEP, 42062fcbc377Syt NULL, 42072fcbc377Syt &cmd_table_dma_cookie, 42082fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 42092fcbc377Syt 42102fcbc377Syt AHCIDBG0(AHCIDBG_INIT, ahci_ctlp, 42112fcbc377Syt "cmd table dma handle bind fail"); 42122fcbc377Syt /* error.. free the dma handle & free the memory. */ 42132fcbc377Syt ddi_dma_mem_free( 42142fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 42152fcbc377Syt ddi_dma_free_handle( 42162fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 42172fcbc377Syt goto err_out; 42182fcbc377Syt } 42192fcbc377Syt 42202fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_tables[slot], 42212fcbc377Syt ahci_cmd_table_size); 42222fcbc377Syt 42232fcbc377Syt /* Config Port Command Table Base Address */ 42242fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR( 42252fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 42262fcbc377Syt cmd_table_dma_cookie.dmac_laddress & 0xffffffffull); 42272fcbc377Syt 42282fcbc377Syt #ifndef __lock_lint 42292fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR_UPPER( 42302fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 42312fcbc377Syt cmd_table_dma_cookie.dmac_laddress >> 32); 4232689d74b0Syt #endif 42332fcbc377Syt } 42342fcbc377Syt 42352fcbc377Syt return (AHCI_SUCCESS); 42362fcbc377Syt err_out: 42372fcbc377Syt 42382fcbc377Syt for (slot--; slot >= 0; slot--) { 42392fcbc377Syt /* Unbind the cmd table dma handle first */ 42402fcbc377Syt (void) ddi_dma_unbind_handle( 42412fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 42422fcbc377Syt 42432fcbc377Syt /* Then free the underlying memory */ 42442fcbc377Syt ddi_dma_mem_free( 42452fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 42462fcbc377Syt 42472fcbc377Syt /* Now free the handle itself */ 42482fcbc377Syt ddi_dma_free_handle( 42492fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 42502fcbc377Syt } 42512fcbc377Syt 42522fcbc377Syt return (AHCI_FAILURE); 42532fcbc377Syt } 42542fcbc377Syt 42552fcbc377Syt /* 42562fcbc377Syt * Deallocates memory for all Command Tables. 42572fcbc377Syt * 42582fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 42592fcbc377Syt * is called. 42602fcbc377Syt */ 42612fcbc377Syt static void 42622fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 42632fcbc377Syt { 42642fcbc377Syt int slot; 42652fcbc377Syt 42662fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 42672fcbc377Syt "ahci_dealloc_cmd_tables: %d enter", 42682fcbc377Syt ahci_portp->ahciport_port_num); 42692fcbc377Syt 42702fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 42712fcbc377Syt /* Unbind the cmd table dma handle first. */ 42722fcbc377Syt (void) ddi_dma_unbind_handle( 42732fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 42742fcbc377Syt 42752fcbc377Syt /* Then free the underlying memory. */ 42762fcbc377Syt ddi_dma_mem_free( 42772fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 42782fcbc377Syt 42792fcbc377Syt /* Now free the handle itself. */ 42802fcbc377Syt ddi_dma_free_handle( 42812fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 42822fcbc377Syt } 42832fcbc377Syt } 42842fcbc377Syt 42852fcbc377Syt /* 42862fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 42872fcbc377Syt * is called. 42882fcbc377Syt */ 42892fcbc377Syt static void 42902fcbc377Syt ahci_update_sata_registers(ahci_ctl_t *ahci_ctlp, uint8_t port, 42912fcbc377Syt sata_device_t *sd) 42922fcbc377Syt { 42932fcbc377Syt sd->satadev_scr.sstatus = 42942fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 42952fcbc377Syt (uint32_t *)(AHCI_PORT_PxSSTS(ahci_ctlp, port))); 42962fcbc377Syt sd->satadev_scr.serror = 42972fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 42982fcbc377Syt (uint32_t *)(AHCI_PORT_PxSERR(ahci_ctlp, port))); 42992fcbc377Syt sd->satadev_scr.scontrol = 43002fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 43012fcbc377Syt (uint32_t *)(AHCI_PORT_PxSCTL(ahci_ctlp, port))); 43022fcbc377Syt sd->satadev_scr.sactive = 43032fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 43042fcbc377Syt (uint32_t *)(AHCI_PORT_PxSACT(ahci_ctlp, port))); 43052fcbc377Syt } 43062fcbc377Syt 43072fcbc377Syt /* 430868d33a25Syt * For poll mode, ahci_port_intr will be called to emulate the interrupt 43092fcbc377Syt */ 43102fcbc377Syt static void 431182263d52Syt ahci_port_intr(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 43122fcbc377Syt { 431368d33a25Syt uint32_t port_intr_status; 431468d33a25Syt uint32_t port_intr_enable; 431568d33a25Syt 431668d33a25Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 431768d33a25Syt "ahci_port_intr enter: port %d", port); 431868d33a25Syt 431968d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 432068d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_POLLING) { 432168d33a25Syt /* For SATA_OPMODE_POLLING commands */ 432268d33a25Syt port_intr_enable = 432368d33a25Syt (AHCI_INTR_STATUS_DHRS | 432468d33a25Syt AHCI_INTR_STATUS_PSS | 432568d33a25Syt AHCI_INTR_STATUS_SDBS | 432668d33a25Syt AHCI_INTR_STATUS_UFS | 432768d33a25Syt AHCI_INTR_STATUS_PCS | 432868d33a25Syt AHCI_INTR_STATUS_PRCS | 432968d33a25Syt AHCI_INTR_STATUS_OFS | 433068d33a25Syt AHCI_INTR_STATUS_INFS | 433168d33a25Syt AHCI_INTR_STATUS_IFS | 433268d33a25Syt AHCI_INTR_STATUS_HBDS | 433368d33a25Syt AHCI_INTR_STATUS_HBFS | 433468d33a25Syt AHCI_INTR_STATUS_TFES); 433568d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 433668d33a25Syt goto next; 433768d33a25Syt } 433868d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 433968d33a25Syt 434068d33a25Syt /* 434168d33a25Syt * port_intr_enable indicates that the corresponding interrrupt 434268d33a25Syt * reporting is enabled. 434368d33a25Syt */ 434468d33a25Syt port_intr_enable = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 434568d33a25Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port)); 434668d33a25Syt next: 434768d33a25Syt /* 434868d33a25Syt * port_intr_stats indicates that the corresponding interrupt 434968d33a25Syt * condition is active. 435068d33a25Syt */ 435168d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 435268d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 435368d33a25Syt 435482263d52Syt AHCIDBG3(AHCIDBG_INTR, ahci_ctlp, 435568d33a25Syt "ahci_port_intr: port %d, port_intr_status = 0x%x, " 435682263d52Syt "port_intr_enable = 0x%x", 435782263d52Syt port, port_intr_status, port_intr_enable); 435868d33a25Syt 435968d33a25Syt port_intr_status &= port_intr_enable; 436068d33a25Syt 436168d33a25Syt /* First clear the port interrupts status */ 436268d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 436368d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 436468d33a25Syt port_intr_status); 436568d33a25Syt 436682263d52Syt /* Check the completed non-queued commands */ 436768d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_DHRS | 436868d33a25Syt AHCI_INTR_STATUS_PSS)) { 436968d33a25Syt (void) ahci_intr_cmd_cmplt(ahci_ctlp, 4370689d74b0Syt ahci_portp, port); 437168d33a25Syt } 437268d33a25Syt 437382263d52Syt /* Check the completed queued commands */ 437468d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_SDBS) { 437568d33a25Syt (void) ahci_intr_set_device_bits(ahci_ctlp, 437668d33a25Syt ahci_portp, port); 437768d33a25Syt } 437868d33a25Syt 437968d33a25Syt /* Check the port connect change status interrupt bit */ 438068d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PCS) { 438168d33a25Syt (void) ahci_intr_port_connect_change(ahci_ctlp, 438268d33a25Syt ahci_portp, port); 438368d33a25Syt } 438468d33a25Syt 438568d33a25Syt /* Check the device mechanical presence status interrupt bit */ 438668d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_DMPS) { 438768d33a25Syt (void) ahci_intr_device_mechanical_presence_status( 438868d33a25Syt ahci_ctlp, ahci_portp, port); 438968d33a25Syt } 439068d33a25Syt 439168d33a25Syt /* Check the PhyRdy change status interrupt bit */ 439268d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PRCS) { 439368d33a25Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, 439468d33a25Syt port); 43952fcbc377Syt } 439668d33a25Syt 439768d33a25Syt /* 439868d33a25Syt * Check the non-fatal error interrupt bits, there are three 439968d33a25Syt * kinds of non-fatal errors at the time being: 440068d33a25Syt * 440168d33a25Syt * PxIS.UFS - Unknown FIS Error 440268d33a25Syt * PxIS.OFS - Overflow Error 440368d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 440468d33a25Syt * 440568d33a25Syt * For these non-fatal errors, the HBA can continue to operate, 440668d33a25Syt * so the driver just log the error messages. 440768d33a25Syt */ 440868d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_UFS | 440968d33a25Syt AHCI_INTR_STATUS_OFS | 441068d33a25Syt AHCI_INTR_STATUS_INFS)) { 441168d33a25Syt (void) ahci_intr_non_fatal_error(ahci_ctlp, ahci_portp, 441268d33a25Syt port, port_intr_status); 441368d33a25Syt } 441468d33a25Syt 441568d33a25Syt /* 441668d33a25Syt * Check the fatal error interrupt bits, there are four kinds 441768d33a25Syt * of fatal errors for AHCI controllers: 441868d33a25Syt * 441968d33a25Syt * PxIS.HBFS - Host Bus Fatal Error 442068d33a25Syt * PxIS.HBDS - Host Bus Data Error 442168d33a25Syt * PxIS.IFS - Interface Fatal Error 442268d33a25Syt * PxIS.TFES - Task File Error 442368d33a25Syt * 442468d33a25Syt * The fatal error means the HBA can not recover from it by 442568d33a25Syt * itself, and it will try to abort the transfer, and the software 442668d33a25Syt * must intervene to restart the port. 442768d33a25Syt */ 442868d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_IFS | 442968d33a25Syt AHCI_INTR_STATUS_HBDS | 443068d33a25Syt AHCI_INTR_STATUS_HBFS | 443168d33a25Syt AHCI_INTR_STATUS_TFES)) 443268d33a25Syt (void) ahci_intr_fatal_error(ahci_ctlp, ahci_portp, 443382263d52Syt port, port_intr_status); 443468d33a25Syt 443568d33a25Syt /* Check the cold port detect interrupt bit */ 443668d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_CPDS) { 443768d33a25Syt (void) ahci_intr_cold_port_detect(ahci_ctlp, ahci_portp, port); 443868d33a25Syt } 443968d33a25Syt 444068d33a25Syt /* Second clear the corresponding bit in IS.IPS */ 444168d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 444268d33a25Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (0x1 << port)); 44432fcbc377Syt } 44442fcbc377Syt 44452fcbc377Syt /* 44462fcbc377Syt * Interrupt service handler 44472fcbc377Syt */ 44482fcbc377Syt static uint_t 44492fcbc377Syt ahci_intr(caddr_t arg1, caddr_t arg2) 44502fcbc377Syt { 4451689d74b0Syt #ifndef __lock_lint 4452689d74b0Syt _NOTE(ARGUNUSED(arg2)) 4453689d74b0Syt #endif 4454689d74b0Syt /* LINTED */ 44552fcbc377Syt ahci_ctl_t *ahci_ctlp = (ahci_ctl_t *)arg1; 44562fcbc377Syt ahci_port_t *ahci_portp; 445768d33a25Syt int32_t global_intr_status; 44582fcbc377Syt uint8_t port; 44592fcbc377Syt 44602fcbc377Syt /* 44612fcbc377Syt * global_intr_status indicates that the corresponding port has 44622fcbc377Syt * an interrupt pending. 44632fcbc377Syt */ 44642fcbc377Syt global_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 44652fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp)); 44662fcbc377Syt 44672fcbc377Syt if (!(global_intr_status & ahci_ctlp->ahcictl_ports_implemented)) { 44682fcbc377Syt /* The interrupt is not ours */ 44692fcbc377Syt return (DDI_INTR_UNCLAIMED); 44702fcbc377Syt } 44712fcbc377Syt 44722fcbc377Syt /* Loop for all the ports */ 44732fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 44742fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 44752fcbc377Syt continue; 44762fcbc377Syt } 44772fcbc377Syt if (!((0x1 << port) & global_intr_status)) { 44782fcbc377Syt continue; 44792fcbc377Syt } 44802fcbc377Syt 44812fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 44822fcbc377Syt 448368d33a25Syt /* Call ahci_port_intr */ 448482263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 44852fcbc377Syt } 44862fcbc377Syt 44872fcbc377Syt return (DDI_INTR_CLAIMED); 44882fcbc377Syt } 44892fcbc377Syt 44902fcbc377Syt /* 449168d33a25Syt * For non-queued commands, when the corresponding bit in the PxCI register 449268d33a25Syt * is cleared, it means the command is completed successfully. And according 449368d33a25Syt * to the HBA state machine, there are three conditions which possibly will 449468d33a25Syt * try to clear the PxCI register bit. 449568d33a25Syt * 1. Receive one D2H Register FIS which is with 'I' bit set 449668d33a25Syt * 2. Update PIO Setup FIS 449768d33a25Syt * 3. Transmit a command and receive R_OK if CTBA.C is set (software reset) 449868d33a25Syt * 449982263d52Syt * Process completed non-queued commands when the interrupt status bit - 450082263d52Syt * AHCI_INTR_STATUS_DHRS or AHCI_INTR_STATUS_PSS is set. 45012fcbc377Syt * 450268d33a25Syt * AHCI_INTR_STATUS_DHRS means a D2H Register FIS has been received 450368d33a25Syt * with the 'I' bit set. And the following commands will send thus 450468d33a25Syt * FIS with 'I' bit set upon the successful completion: 45052fcbc377Syt * 1. Non-data commands 45062fcbc377Syt * 2. DMA data-in command 45072fcbc377Syt * 3. DMA data-out command 45082fcbc377Syt * 4. PIO data-out command 450968d33a25Syt * 5. PACKET non-data commands 451068d33a25Syt * 6. PACKET PIO data-in command 451168d33a25Syt * 7. PACKET PIO data-out command 451268d33a25Syt * 8. PACKET DMA data-in command 451368d33a25Syt * 9. PACKET DMA data-out command 451468d33a25Syt * 451568d33a25Syt * AHCI_INTR_STATUS_PSS means a PIO Setup FIS has been received 451668d33a25Syt * with the 'I' bit set. And the following commands will send this 451768d33a25Syt * FIS upon the successful completion: 451868d33a25Syt * 1. PIO data-in command 45192fcbc377Syt */ 45202fcbc377Syt static int 452182263d52Syt ahci_intr_cmd_cmplt(ahci_ctl_t *ahci_ctlp, 4522689d74b0Syt ahci_port_t *ahci_portp, uint8_t port) 45232fcbc377Syt { 452468d33a25Syt uint32_t port_cmd_issue = 0; 45252fcbc377Syt uint32_t finished_tags; 452668d33a25Syt int finished_slot; 45272fcbc377Syt sata_pkt_t *satapkt; 45282fcbc377Syt ahci_fis_d2h_register_t *rcvd_fisp; 45292fcbc377Syt 453068d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 453182263d52Syt 453282263d52Syt if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 453382263d52Syt !NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 45342fcbc377Syt /* 45352fcbc377Syt * Spurious interrupt. Nothing to be done. 45362fcbc377Syt */ 45372fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 45382fcbc377Syt return (AHCI_SUCCESS); 45392fcbc377Syt } 45402fcbc377Syt 454168d33a25Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 454268d33a25Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 45432fcbc377Syt 454482263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 454582263d52Syt /* Slot 0 is always used during error recovery */ 454682263d52Syt finished_tags = 0x1 & ~port_cmd_issue; 454782263d52Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, 454882263d52Syt "ahci_intr_cmd_cmplt: port %d the sata pkt for error " 454982263d52Syt "retrieval is finished, and finished_tags = 0x%x", 455082263d52Syt port, finished_tags); 455168d33a25Syt } else { 455268d33a25Syt finished_tags = ahci_portp->ahciport_pending_tags & 455368d33a25Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 45542fcbc377Syt } 45552fcbc377Syt 455668d33a25Syt AHCIDBG3(AHCIDBG_INTR, ahci_ctlp, 455782263d52Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x, " 455882263d52Syt "port_cmd_issue = 0x%x finished_tags = 0x%x", 455968d33a25Syt ahci_portp->ahciport_pending_tags, port_cmd_issue, 456082263d52Syt finished_tags); 45612fcbc377Syt 456282263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 456382263d52Syt (finished_tags == 0x1)) { 456482263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 456582263d52Syt ASSERT(satapkt != NULL); 456682263d52Syt 456782263d52Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 456882263d52Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 456982263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 457082263d52Syt 457182263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 457282263d52Syt goto out; 457382263d52Syt } 45742fcbc377Syt 457568d33a25Syt while (finished_tags) { 457668d33a25Syt finished_slot = ddi_ffs(finished_tags) - 1; 457768d33a25Syt if (finished_slot == -1) { 457868d33a25Syt goto out; 457968d33a25Syt } 45802fcbc377Syt 458168d33a25Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 458268d33a25Syt ASSERT(satapkt != NULL); 45832fcbc377Syt 45842fcbc377Syt /* 458568d33a25Syt * For SATAC_SMART command with SATA_SMART_RETURN_STATUS 458668d33a25Syt * feature, sata_special_regs flag will be set, and the 458768d33a25Syt * driver should copy the status and the other corresponding 458868d33a25Syt * register values in the D2H Register FIS received (It's 458968d33a25Syt * working on Non-data protocol) from the device back to 459068d33a25Syt * the sata_cmd. 459168d33a25Syt * 459268d33a25Syt * For every AHCI port, there is only one Received FIS 459368d33a25Syt * structure, which contains the FISes received from the 459468d33a25Syt * device, So we're trying to copy the content of D2H 459568d33a25Syt * Register FIS in the Received FIS structure back to 459668d33a25Syt * the sata_cmd. 45972fcbc377Syt */ 459868d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 459968d33a25Syt rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 460068d33a25Syt ahcirf_d2h_register_fis); 460168d33a25Syt satapkt->satapkt_cmd.satacmd_status_reg = 460268d33a25Syt GET_RFIS_STATUS(rcvd_fisp); 460368d33a25Syt ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp); 460468d33a25Syt } 46052fcbc377Syt 460668d33a25Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 460768d33a25Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 460868d33a25Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 46092fcbc377Syt 461068d33a25Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, finished_slot); 461168d33a25Syt CLEAR_BIT(finished_tags, finished_slot); 461268d33a25Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 46132fcbc377Syt 461468d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 46152fcbc377Syt } 46162fcbc377Syt out: 46172fcbc377Syt AHCIDBG1(AHCIDBG_PKTCOMP, ahci_ctlp, 461868d33a25Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x", 46192fcbc377Syt ahci_portp->ahciport_pending_tags); 46202fcbc377Syt 46212fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 46222fcbc377Syt 46232fcbc377Syt return (AHCI_SUCCESS); 46242fcbc377Syt } 46252fcbc377Syt 46262fcbc377Syt /* 462768d33a25Syt * AHCI_INTR_STATUS_SDBS means a Set Device Bits FIS has been received 462882263d52Syt * with the 'I' bit set and has been copied into system memory. It will 462982263d52Syt * be sent under the following situations: 463082263d52Syt * 463182263d52Syt * 1. NCQ command is completed 463282263d52Syt * 2. Asynchronous notification 463382263d52Syt * 463482263d52Syt * The completion of NCQ commands (READ/WRITE FPDMA QUEUED) is performed 463582263d52Syt * via the Set Device Bits FIS. When such event is generated, the software 463682263d52Syt * needs to read PxSACT register and compares the current value to the 463782263d52Syt * list of commands previously issue by software. ahciport_pending_ncq_tags 463882263d52Syt * keeps the tags of previously issued commands. 46392fcbc377Syt * 464068d33a25Syt * Asynchronous Notification is a feature in SATA II, which allows an 464168d33a25Syt * ATAPI device to send a signal to the host when media is inserted or 464268d33a25Syt * removed and avoids polling the device for media changes. The signal 464368d33a25Syt * sent to the host is a Set Device Bits FIS with the 'I' and 'N' bits 464482263d52Syt * set to '1'. At the moment, it's not supported yet. 46452fcbc377Syt */ 46462fcbc377Syt static int 464768d33a25Syt ahci_intr_set_device_bits(ahci_ctl_t *ahci_ctlp, 46482fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 46492fcbc377Syt { 465082263d52Syt uint32_t port_sactive; 465182263d52Syt uint32_t port_cmd_issue; 465282263d52Syt uint32_t issued_tags; 465382263d52Syt int issued_slot; 465482263d52Syt uint32_t finished_tags; 465582263d52Syt int finished_slot; 465682263d52Syt sata_pkt_t *satapkt; 46572fcbc377Syt 465882263d52Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 465968d33a25Syt "ahci_intr_set_device_bits enter: port %d", port); 46602fcbc377Syt 466182263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 466282263d52Syt if (!NCQ_CMD_IN_PROGRESS(ahci_portp)) { 466382263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 466482263d52Syt return (AHCI_SUCCESS); 466582263d52Syt } 466682263d52Syt 466782263d52Syt /* 466882263d52Syt * First the handler got which commands are finished by checking 466982263d52Syt * PxSACT register 467082263d52Syt */ 467182263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 467282263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 467368d33a25Syt 467482263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 467582263d52Syt ~port_sactive & AHCI_NCQ_SLOT_MASK(ahci_portp); 467682263d52Syt 467782263d52Syt AHCIDBG3(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 467882263d52Syt "ahci_intr_set_device_bits: port %d pending_ncq_tags = 0x%x " 467982263d52Syt "port_sactive = 0x%x", port, 468082263d52Syt ahci_portp->ahciport_pending_ncq_tags, port_sactive); 468182263d52Syt 468282263d52Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 468382263d52Syt "ahci_intr_set_device_bits: finished_tags = 0x%x", finished_tags); 468482263d52Syt 468582263d52Syt /* 468682263d52Syt * For NCQ commands, the software can determine which command has 468782263d52Syt * already been transmitted to the device by checking PxCI register. 468882263d52Syt */ 468982263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 469082263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 469182263d52Syt 469282263d52Syt issued_tags = ahci_portp->ahciport_pending_tags & 469382263d52Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 469482263d52Syt 469582263d52Syt AHCIDBG3(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 469682263d52Syt "ahci_intr_set_device_bits: port %d pending_tags = 0x%x " 469782263d52Syt "port_cmd_issue = 0x%x", port, 469882263d52Syt ahci_portp->ahciport_pending_tags, port_cmd_issue); 469982263d52Syt 470082263d52Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 470182263d52Syt "ahci_intr_set_device_bits: issued_tags = 0x%x", issued_tags); 470282263d52Syt 470382263d52Syt /* 470482263d52Syt * Clear ahciport_pending_tags bit when the corresponding command 470582263d52Syt * is already sent down to the device. 470682263d52Syt */ 470782263d52Syt while (issued_tags) { 470882263d52Syt issued_slot = ddi_ffs(issued_tags) - 1; 470982263d52Syt if (issued_slot == -1) { 471082263d52Syt goto next; 471182263d52Syt } 471282263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, issued_slot); 471382263d52Syt CLEAR_BIT(issued_tags, issued_slot); 471482263d52Syt } 471582263d52Syt 471682263d52Syt next: 471782263d52Syt while (finished_tags) { 471882263d52Syt finished_slot = ddi_ffs(finished_tags) - 1; 471982263d52Syt if (finished_slot == -1) { 472082263d52Syt goto out; 472182263d52Syt } 472282263d52Syt 472382263d52Syt /* The command is certainly transmitted to the device */ 472482263d52Syt ASSERT(!(ahci_portp->ahciport_pending_tags & 472582263d52Syt (0x1 << finished_slot))); 472682263d52Syt 472782263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 472882263d52Syt ASSERT(satapkt != NULL); 472982263d52Syt 473082263d52Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 473182263d52Syt "ahci_intr_set_device_bits: sending up pkt 0x%p " 473282263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 473382263d52Syt 473482263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, finished_slot); 473582263d52Syt CLEAR_BIT(finished_tags, finished_slot); 473682263d52Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 473782263d52Syt 473882263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 473968d33a25Syt } 474082263d52Syt out: 474182263d52Syt AHCIDBG3(AHCIDBG_PKTCOMP|AHCIDBG_NCQ, ahci_ctlp, 474282263d52Syt "ahci_intr_set_device_bits: port %d " 474382263d52Syt "pending_ncq_tags = 0x%x pending_tags = 0x%x", 474482263d52Syt port, ahci_portp->ahciport_pending_ncq_tags, 474582263d52Syt ahci_portp->ahciport_pending_tags); 474682263d52Syt 474782263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 47482fcbc377Syt 47492fcbc377Syt return (AHCI_SUCCESS); 47502fcbc377Syt } 47512fcbc377Syt 47522fcbc377Syt /* 475368d33a25Syt * 1=Change in Current Connect Status. 0=No change in Current Connect Status. 475468d33a25Syt * This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared 475568d33a25Syt * when PxSERR.DIAG.X is cleared. When PxSERR.DIAG.X is set to one, it 475668d33a25Syt * indicates a COMINIT signal was received. 47572fcbc377Syt * 475868d33a25Syt * Hot plug insertion is detected by reception of a COMINIT signal from the 475968d33a25Syt * device. On reception of unsolicited COMINIT, the HBA shall generate a 476068d33a25Syt * COMRESET. If the COMINIT is in responce to a COMRESET, then the HBA shall 476168d33a25Syt * begin the normal communication negotiation sequence as outlined in the 476268d33a25Syt * Serial ATA 1.0a specification. When a COMRESET is sent to the device the 476368d33a25Syt * PxSSTS.DET field shall be cleared to 0h. When a COMINIT is received, the 476468d33a25Syt * PxSSTS.DET field shall be set to 1h. When the communication negotiation 476568d33a25Syt * sequence is complete and PhyRdy is true the PxSSTS.DET field shall be set 476668d33a25Syt * to 3h. Therefore, at the moment the ahci driver is going to check PhyRdy 476768d33a25Syt * to handle hot plug insertion. In this interrupt handler, just do nothing 476868d33a25Syt * but print some log message and clear the bit. 47692fcbc377Syt */ 47702fcbc377Syt static int 477168d33a25Syt ahci_intr_port_connect_change(ahci_ctl_t *ahci_ctlp, 47722fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 47732fcbc377Syt { 4774689d74b0Syt #if AHCI_DEBUG 47752fcbc377Syt uint32_t port_serror; 4776689d74b0Syt #endif 47772fcbc377Syt 47782fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 47792fcbc377Syt 4780689d74b0Syt #if AHCI_DEBUG 47812fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 47822fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 47832fcbc377Syt 47842fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 478568d33a25Syt "ahci_intr_port_connect_change: port %d, " 478668d33a25Syt "port_serror = 0x%x", port, port_serror); 4787689d74b0Syt #endif 47882fcbc377Syt 478968d33a25Syt /* Clear PxSERR.DIAG.X to clear the interrupt bit */ 47902fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 47912fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 479282263d52Syt SERROR_EXCHANGED_ERR); 47932fcbc377Syt 47942fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 47952fcbc377Syt 47962fcbc377Syt return (AHCI_SUCCESS); 47972fcbc377Syt } 47982fcbc377Syt 47992fcbc377Syt /* 48002fcbc377Syt * Hot Plug Operation for platforms that support Mechanical Presence 48012fcbc377Syt * Switches. 48022fcbc377Syt * 48032fcbc377Syt * When set, it indicates that a mechanical presence switch attached to this 48042fcbc377Syt * port has been opened or closed, which may lead to a change in the connection 48052fcbc377Syt * state of the device. This bit is only valid if both CAP.SMPS and PxCMD.MPSP 48062fcbc377Syt * are set to '1'. 48072fcbc377Syt * 480868d33a25Syt * At the moment, this interrupt is not needed and disabled and we just log 48092fcbc377Syt * the debug message. 48102fcbc377Syt */ 48112fcbc377Syt static int 48122fcbc377Syt ahci_intr_device_mechanical_presence_status(ahci_ctl_t *ahci_ctlp, 48132fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 48142fcbc377Syt { 48152fcbc377Syt uint32_t cap_status, port_cmd_status; 48162fcbc377Syt 48172fcbc377Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 48182fcbc377Syt "ahci_intr_device_mechanical_presence_status enter, " 48192fcbc377Syt "port %d", port); 48202fcbc377Syt 48212fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 48222fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 48232fcbc377Syt 48242fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 48252fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 48262fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 48272fcbc377Syt 48282fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_SMPS) || 48292fcbc377Syt !(port_cmd_status & AHCI_CMD_STATUS_MPSP)) { 48302fcbc377Syt AHCIDBG2(AHCIDBG_INTR, ahci_ctlp, 48312fcbc377Syt "CAP.SMPS or PxCMD.MPSP is not set, so just ignore " 48322fcbc377Syt "the interrupt: cap_status = 0x%x, " 48332fcbc377Syt "port_cmd_status = 0x%x", cap_status, port_cmd_status); 48342fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 48352fcbc377Syt 48362fcbc377Syt return (AHCI_SUCCESS); 48372fcbc377Syt } 48382fcbc377Syt 4839689d74b0Syt #if AHCI_DEBUG 48402fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_MPSS) { 48412fcbc377Syt AHCIDBG2(AHCIDBG_INTR, ahci_ctlp, 48422fcbc377Syt "The mechanical presence switch is open: " 48432fcbc377Syt "port %d, port_cmd_status = 0x%x", 48442fcbc377Syt port, port_cmd_status); 48452fcbc377Syt } else { 48462fcbc377Syt AHCIDBG2(AHCIDBG_INTR, ahci_ctlp, 48472fcbc377Syt "The mechanical presence switch is close: " 48482fcbc377Syt "port %d, port_cmd_status = 0x%x", 48492fcbc377Syt port, port_cmd_status); 48502fcbc377Syt } 4851689d74b0Syt #endif 48522fcbc377Syt 48532fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 48542fcbc377Syt 48552fcbc377Syt return (AHCI_SUCCESS); 48562fcbc377Syt } 48572fcbc377Syt 48582fcbc377Syt /* 48592fcbc377Syt * Native Hot Plug Support. 48602fcbc377Syt * 48612fcbc377Syt * When set, it indicates that the internal PHYRDY signal changed state. 48622fcbc377Syt * This bit reflects the state of PxSERR.DIAG.N. 486368d33a25Syt * 486468d33a25Syt * There are three kinds of conditions to generate this interrupt event: 486568d33a25Syt * 1. a device is inserted 486668d33a25Syt * 2. a device is disconnected 486768d33a25Syt * 3. when the link enters/exits a Partial or Slumber interface power 486868d33a25Syt * management state 486968d33a25Syt * 487068d33a25Syt * If inteface power management is enabled for a port, the PxSERR.DIAG.N 487168d33a25Syt * bit may be set due to the link entering the Partial or Slumber power 487268d33a25Syt * management state, rather than due to a hot plug insertion or removal 487313bcbb7aSyt * event. So far, the interface power management is disabled, so the 487413bcbb7aSyt * driver can reliably get removal detection notification via the 487513bcbb7aSyt * PxSERR.DIAG.N bit. 48762fcbc377Syt */ 48772fcbc377Syt static int 48782fcbc377Syt ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp, 48792fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 48802fcbc377Syt { 48812fcbc377Syt uint32_t port_sstatus = 0; /* No dev present & PHY not established. */ 48822fcbc377Syt sata_device_t sdevice; 48832fcbc377Syt int dev_exists_now = 0; 48842fcbc377Syt int dev_existed_previously = 0; 48852fcbc377Syt 48862fcbc377Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 48872fcbc377Syt "ahci_intr_phyrdy_change enter, port %d", port); 48882fcbc377Syt 48892fcbc377Syt /* Clear PxSERR.DIAG.N to clear the interrupt bit */ 48902fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 48912fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 48922fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 489382263d52Syt SERROR_PHY_RDY_CHG); 48942fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 48952fcbc377Syt 48962fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 48972fcbc377Syt if ((ahci_ctlp->ahcictl_sata_hba_tran == NULL) || 48982fcbc377Syt (ahci_portp == NULL)) { 48992fcbc377Syt /* The whole controller setup is not yet done. */ 49002fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 49012fcbc377Syt return (AHCI_SUCCESS); 49022fcbc377Syt } 49032fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 49042fcbc377Syt 49052fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 49062fcbc377Syt 49072fcbc377Syt /* SStatus tells the presence of device. */ 49082fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 49092fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 49102fcbc377Syt 491182263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 491268d33a25Syt dev_exists_now = 1; 491368d33a25Syt } 49142fcbc377Syt 49152fcbc377Syt if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) { 49162fcbc377Syt dev_existed_previously = 1; 49172fcbc377Syt } 49182fcbc377Syt 491982263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_NODEV) { 492082263d52Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_NODEV; 492182263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 492282263d52Syt "ahci_intr_phyrdy_change: port %d " 492382263d52Syt "AHCI_PORT_FLAG_NODEV is cleared", port); 492482263d52Syt if (dev_exists_now == 0) 492582263d52Syt dev_existed_previously = 1; 492682263d52Syt } 492782263d52Syt 49282fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 492909121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 49302fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 49312fcbc377Syt sdevice.satadev_addr.pmport = 0; 49322fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 49332fcbc377Syt ahci_portp->ahciport_port_state = SATA_PSTATE_PWRON; 49342fcbc377Syt 49352fcbc377Syt if (dev_exists_now) { 49362fcbc377Syt if (dev_existed_previously) { 49372fcbc377Syt /* Things are fine now. The loss was temporary. */ 493868d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 493968d33a25Syt "ahci_intr_phyrdy_change port %d " 49402fcbc377Syt "device link lost/established", port); 49412fcbc377Syt 49422fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 49432fcbc377Syt sata_hba_event_notify( 49442fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 49452fcbc377Syt &sdevice, 49462fcbc377Syt SATA_EVNT_LINK_LOST|SATA_EVNT_LINK_ESTABLISHED); 49472fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 49482fcbc377Syt 49492fcbc377Syt } else { 495068d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 495168d33a25Syt "ahci_intr_phyrdy_change: port %d " 49522fcbc377Syt "device link established", port); 49532fcbc377Syt 49542fcbc377Syt /* A new device has been detected. */ 495568d33a25Syt ahci_find_dev_signature(ahci_ctlp, ahci_portp, port); 49562fcbc377Syt 495768d33a25Syt /* Try to start the port */ 495868d33a25Syt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 495968d33a25Syt != AHCI_SUCCESS) { 496068d33a25Syt sdevice.satadev_state |= SATA_PSTATE_FAILED; 49612fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 496268d33a25Syt "ahci_intr_phyrdy_change: port %d failed " 49632fcbc377Syt "at start port", port); 49642fcbc377Syt } 49652fcbc377Syt 496682263d52Syt /* Clear the max queue depth for inserted device */ 496782263d52Syt ahci_portp->ahciport_max_ncq_tags = 0; 496882263d52Syt 49692fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 49702fcbc377Syt sata_hba_event_notify( 49712fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 49722fcbc377Syt &sdevice, 49732fcbc377Syt SATA_EVNT_LINK_ESTABLISHED); 49742fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 49752fcbc377Syt 49762fcbc377Syt } 49772fcbc377Syt } else { /* No device exists now */ 49782fcbc377Syt 49792fcbc377Syt if (dev_existed_previously) { 498068d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 498168d33a25Syt "ahci_intr_phyrdy_change: port %d " 49822fcbc377Syt "device link lost", port); 49832fcbc377Syt 49842fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 498568d33a25Syt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 49862fcbc377Syt ahci_portp, port); 49872fcbc377Syt 49882fcbc377Syt /* An existing device is lost. */ 49892fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_NONE; 49902fcbc377Syt 49912fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 49922fcbc377Syt sata_hba_event_notify( 49932fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 49942fcbc377Syt &sdevice, 49952fcbc377Syt SATA_EVNT_LINK_LOST); 49962fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 49972fcbc377Syt } 49982fcbc377Syt } 49992fcbc377Syt 50002fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 50012fcbc377Syt 50022fcbc377Syt return (AHCI_SUCCESS); 50032fcbc377Syt } 50042fcbc377Syt 50052fcbc377Syt /* 500668d33a25Syt * PxIS.UFS - Unknown FIS Error 50072fcbc377Syt * 500868d33a25Syt * This interrupt event means an unknown FIS was received and has been 500968d33a25Syt * copied into system memory. An unknown FIS is not considered an illegal 501068d33a25Syt * FIS, unless the length received is more than 64 bytes. If an unknown 501168d33a25Syt * FIS arrives with length <= 64 bytes, it is posted and the HBA continues 501268d33a25Syt * normal operation. If the unknown FIS is more than 64 bytes, then it 501368d33a25Syt * won't be posted to memory and PxSERR.ERR.P will be set, which is then 501468d33a25Syt * a fatal error. 50152fcbc377Syt * 501668d33a25Syt * PxIS.OFS - Overflow Error 50172fcbc377Syt * 501868d33a25Syt * Command list overflow is defined as software building a command table 501968d33a25Syt * that has fewer total bytes than the transaction given to the device. 502068d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 502168d33a25Syt * will be no room to put the data. 50222fcbc377Syt * 502368d33a25Syt * For an overflow on data read, either PIO or DMA, the HBA will set 502468d33a25Syt * PxIS.OFS, and the HBA will do a best effort to continue, and it's a 502568d33a25Syt * non-fatal error when the HBA can continues. Sometimes, it will cause 502668d33a25Syt * a fatal error and need the software to do something. 50272fcbc377Syt * 502868d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 502968d33a25Syt * DMA and PIO, and it's a fatal error, and a COMRESET is required by 503068d33a25Syt * software to clean up from this serious error. 50312fcbc377Syt * 503268d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 503368d33a25Syt * 503468d33a25Syt * This interrupt event indicates that the HBA encountered an error on 503568d33a25Syt * the Serial ATA interface but was able to continue operation. The kind 503668d33a25Syt * of error usually occurred during a non-Data FIS, and under this condition 503768d33a25Syt * the FIS will be re-transmitted by HBA automatically. 50382fcbc377Syt * 503968d33a25Syt * When the FMA is implemented, there should be a stat structure to 504068d33a25Syt * record how many every kind of error happens. 50412fcbc377Syt */ 50422fcbc377Syt static int 504368d33a25Syt ahci_intr_non_fatal_error(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 504468d33a25Syt uint8_t port, uint32_t intr_status) 50452fcbc377Syt { 50462fcbc377Syt uint32_t port_serror; 504768d33a25Syt #if AHCI_DEBUG 50482fcbc377Syt uint32_t port_cmd_status; 504982263d52Syt uint32_t port_cmd_issue; 505082263d52Syt uint32_t port_sactive; 50512fcbc377Syt int current_slot; 505282263d52Syt uint32_t current_tags; 505368d33a25Syt sata_pkt_t *satapkt; 505468d33a25Syt #endif 50552fcbc377Syt 50562fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 50572fcbc377Syt 50582fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 50592fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 50602fcbc377Syt 506168d33a25Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ENTRY|AHCIDBG_ERRS, ahci_ctlp, 506268d33a25Syt "ahci_intr_non_fatal_error: port %d, " 50632fcbc377Syt "port_serror = 0x%x", port, port_serror); 50642fcbc377Syt 5065a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 1); 506668d33a25Syt 506768d33a25Syt if (intr_status & AHCI_INTR_STATUS_UFS) { 506868d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 506968d33a25Syt "ahci port %d has unknown FIS error", port); 50702fcbc377Syt 507168d33a25Syt /* Clear the interrupt bit by clearing PxSERR.DIAG.F */ 507268d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 507368d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 507482263d52Syt SERROR_FIS_TYPE); 507568d33a25Syt } 507668d33a25Syt 507768d33a25Syt #if AHCI_DEBUG 507868d33a25Syt if (intr_status & AHCI_INTR_STATUS_OFS) { 507968d33a25Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 508068d33a25Syt "ahci port %d has overflow error", port); 508168d33a25Syt } 508268d33a25Syt 508368d33a25Syt if (intr_status & AHCI_INTR_STATUS_INFS) { 508468d33a25Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 508568d33a25Syt "ahci port %d has interface non fatal error", port); 508668d33a25Syt } 50872fcbc377Syt 50882fcbc377Syt /* 50892fcbc377Syt * Record the error occurred command's slot. 50902fcbc377Syt */ 509182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 509282263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 509382263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 509482263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 509582263d52Syt 509682263d52Syt current_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 509782263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 50982fcbc377Syt 509982263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 510082263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 510182263d52Syt ASSERT(satapkt != NULL); 510282263d52Syt ASSERT(current_slot == 0); 510382263d52Syt } else { 510482263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 510582263d52Syt } 51062fcbc377Syt 510782263d52Syt if (satapkt != NULL) { 510882263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 510982263d52Syt "ahci_intr_non_fatal_error: pending_tags = 0x%x " 511082263d52Syt "cmd 0x%x", ahci_portp->ahciport_pending_tags, 511182263d52Syt satapkt->satapkt_cmd.satacmd_cmd_reg); 51122fcbc377Syt 511382263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 511482263d52Syt "ahci_intr_non_fatal_error: port %d, " 511582263d52Syt "satapkt 0x%p is being processed when error occurs", 511682263d52Syt port, (void *)satapkt); 511782263d52Syt } 5118a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 511982263d52Syt /* 512082263d52Syt * For queued command, list those command which have already 512182263d52Syt * been transmitted to the device and still not completed. 512282263d52Syt */ 512382263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 512482263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 512582263d52Syt 512682263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 512782263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 512882263d52Syt 512982263d52Syt AHCIDBG3(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, ahci_ctlp, 513082263d52Syt "ahci_intr_non_fatal_error: pending_ncq_tags = 0x%x " 513182263d52Syt "port_sactive = 0x%x port_cmd_issue = 0x%x", 513282263d52Syt ahci_portp->ahciport_pending_ncq_tags, 513382263d52Syt port_sactive, port_cmd_issue); 513482263d52Syt 513582263d52Syt current_tags = ahci_portp->ahciport_pending_ncq_tags & 513682263d52Syt port_sactive & ~port_cmd_issue & 513782263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 513882263d52Syt 513982263d52Syt while (current_tags) { 514082263d52Syt current_slot = ddi_ffs(current_tags) - 1; 514182263d52Syt if (current_slot == -1) { 514282263d52Syt goto out; 514382263d52Syt } 514482263d52Syt 514582263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 514682263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, 514782263d52Syt ahci_ctlp, "ahci_intr_non_fatal_error: " 514882263d52Syt "port %d, satapkt 0x%p is outstanding when " 514982263d52Syt "error occurs", port, (void *)satapkt); 515082263d52Syt } 51512fcbc377Syt } 515282263d52Syt out: 51532fcbc377Syt #endif 51542fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 51552fcbc377Syt 51562fcbc377Syt return (AHCI_SUCCESS); 51572fcbc377Syt } 51582fcbc377Syt 51592fcbc377Syt /* 516068d33a25Syt * According to the AHCI spec, the error types include system memory 516168d33a25Syt * errors, interface errors, port multiplier errors, device errors, 516268d33a25Syt * command list overflow, command list underflow, native command 516368d33a25Syt * queuing tag errors and pio data transfer errors. 516468d33a25Syt * 516568d33a25Syt * System memory errors such as target abort, master abort, and parity 516668d33a25Syt * may cause the host to stop, and they are serious errors and needed 516768d33a25Syt * to be recovered with software intervention. When system software 516868d33a25Syt * has given a pointer to the HBA that doesn't exist in physical memory, 516968d33a25Syt * a master/target abort error occurs, and PxIS.HBFS will be set. A 517068d33a25Syt * data error such as CRC or parity occurs, the HBA aborts the transfer 517168d33a25Syt * (if necessary) and PxIS.HBDS will be set. 517268d33a25Syt * 517368d33a25Syt * Interface errors are errors that occur due to electrical issues on 517468d33a25Syt * the interface, or protocol miscommunication between the device and 517568d33a25Syt * HBA, and the respective PxSERR register bit will be set. And PxIS.IFS 517668d33a25Syt * (fatal) or PxIS.INFS (non-fatal) will be set. The conditions that 517768d33a25Syt * causes PxIS.IFS/PxIS.INFS to be set are 517868d33a25Syt * 1. in PxSERR.ERR, P bit is set to '1' 517968d33a25Syt * 2. in PxSERR.DIAG, C or H bit is set to '1' 518068d33a25Syt * 3. PhyRdy drop unexpectly, N bit is set to '1' 518168d33a25Syt * If the error occurred during a non-data FIS, the FIS must be 518268d33a25Syt * retransmitted, and the error is non-fatal and PxIS.INFS is set. If 518368d33a25Syt * the error occurred during a data FIS, the transfer will stop, so 518468d33a25Syt * the error is fatal and PxIS.IFS is set. 518568d33a25Syt * 518668d33a25Syt * When a FIS arrives that updates the taskfile, the HBA checks to see 518768d33a25Syt * if PxTFD.STS.ERR is set. If yes, PxIS.TFES will be set and the HBA 518868d33a25Syt * stops processing any more commands. 51892fcbc377Syt * 519068d33a25Syt * Command list overflow is defined as software building a command table 519168d33a25Syt * that has fewer total bytes than the transaction given to the device. 519268d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 519368d33a25Syt * will be no room to put the data. For an overflow on data read, either 519468d33a25Syt * PIO or DMA, the HBA will set PxIS.OFS, and it's a non-fatal error. 519568d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 519668d33a25Syt * DMA and PIO, and a COMRESET is required by software to clean up from 519768d33a25Syt * this serious error. 51982fcbc377Syt * 519968d33a25Syt * Command list underflow is defined as software building a command 520068d33a25Syt * table that has more total bytes than the transaction given to the 520168d33a25Syt * device. For data writes, both PIO and DMA, the device will detect 520268d33a25Syt * an error and end the transfer. And these errors are most likely going 520368d33a25Syt * to be fatal errors that will cause the port to be restarted. For 520468d33a25Syt * data reads, the HBA updates its PRD byte count, and may be 520568d33a25Syt * able to continue normally, but is not required to. And The HBA is 520668d33a25Syt * not required to detect underflow conditions for native command 520768d33a25Syt * queuing command. 520868d33a25Syt * 520968d33a25Syt * The HBA does not actively check incoming DMA Setup FISes to ensure 521068d33a25Syt * that the PxSACT register bit for that slot is set. Existing error 521168d33a25Syt * mechanisms, such as host bus failure, or bad protocol, are used to 521268d33a25Syt * recover from this case. 521368d33a25Syt * 521468d33a25Syt * In accordance with Serial ATA 1.0a, DATA FISes prior to the final 521568d33a25Syt * DATA FIS must be an integral number of Dwords. If the HBA receives 521668d33a25Syt * a request which is not an integral number of Dwords, the HBA 521768d33a25Syt * set PxSERR.ERR.P to '1', set PxIS.IFS to '1' and stop running until 521868d33a25Syt * software restarts the port. And the HBA ensures that the size 521968d33a25Syt * of the DATA FIS received during a PIO command matches the size in 522068d33a25Syt * the Transfer Cound field of the preceding PIO Setup FIS, if not, the 522168d33a25Syt * HBA sets PxSERR.ERR.P to '1', set PxIS.IFS to '1', and then 522268d33a25Syt * stop running until software restarts the port. 52232fcbc377Syt */ 52242fcbc377Syt /* 522568d33a25Syt * the fatal errors include PxIS.IFS, PxIS.HBDS, PxIS.HBFS and PxIS.TFES. 522668d33a25Syt * 522768d33a25Syt * PxIS.IFS indicates that the hba encountered an error on the serial ata 522868d33a25Syt * interface which caused the transfer to stop. 52292fcbc377Syt * 523068d33a25Syt * PxIS.HBDS indicates that the hba encountered a data error 523168d33a25Syt * (uncorrectable ecc/parity) when reading from or writing to system memory. 523268d33a25Syt * 523368d33a25Syt * PxIS.HBFS indicates that the hba encountered a host bus error that it 523468d33a25Syt * cannot recover from, such as a bad software pointer. 523568d33a25Syt * 523668d33a25Syt * PxIS.TFES is set whenever the status register is updated by the device 523768d33a25Syt * and the error bit (bit 0) is set. 52382fcbc377Syt */ 52392fcbc377Syt static int 524082263d52Syt ahci_intr_fatal_error(ahci_ctl_t *ahci_ctlp, 524182263d52Syt ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 52422fcbc377Syt { 524368d33a25Syt uint32_t port_cmd_status; 52442fcbc377Syt uint32_t port_serror; 524568d33a25Syt uint32_t task_file_status; 524668d33a25Syt int failed_slot; 524782263d52Syt sata_pkt_t *spkt = NULL; 524868d33a25Syt uint8_t err_byte; 524968d33a25Syt ahci_event_arg_t *args; 5250a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 52512fcbc377Syt 52522fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 52532fcbc377Syt 525468d33a25Syt /* 525568d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 525668d33a25Syt * SATA_DTYPE_NONE. 525768d33a25Syt */ 525868d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 525968d33a25Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 526068d33a25Syt "ahci_intr_fatal_error: port %d no device attached, " 526168d33a25Syt "and just return without doing anything", port); 526268d33a25Syt goto out0; 526368d33a25Syt } 52642fcbc377Syt 526582263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 526682263d52Syt /* 526782263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 526882263d52Syt * was processing when the error occurred. 526982263d52Syt */ 527082263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 527182263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 527282263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 527382263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 52742fcbc377Syt 527582263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 527668d33a25Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 527782263d52Syt "ahci_intr_fatal_error: spkt 0x%p is being processed when " 527882263d52Syt "fatal error occurred for port %d", spkt, port); 52792fcbc377Syt 528082263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 528182263d52Syt task_file_status = ddi_get32( 528282263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 528382263d52Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 528482263d52Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 528582263d52Syt "ahci_intr_fatal_error: port %d " 528682263d52Syt "task_file_status = 0x%x", port, task_file_status); 52872fcbc377Syt 528882263d52Syt err_byte = (task_file_status & AHCI_TFD_ERR_MASK) 528982263d52Syt >> AHCI_TFD_ERR_SHIFT; 52902fcbc377Syt 529182263d52Syt /* 529282263d52Syt * Won't emit the error message if it is an IDENTIFY 529382263d52Syt * DEVICE command sent to an ATAPI device. 529482263d52Syt */ 529582263d52Syt if ((spkt != NULL) && 529682263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == 529782263d52Syt SATAC_ID_DEVICE) && 529882263d52Syt (err_byte == SATA_ERROR_ABORT)) 529982263d52Syt goto out1; 530082263d52Syt 530182263d52Syt /* 530282263d52Syt * Won't emit the error message if it is an ATAPI PACKET 530382263d52Syt * command 530482263d52Syt */ 530582263d52Syt if ((spkt != NULL) && 530682263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_PACKET)) 530782263d52Syt goto out1; 530882263d52Syt } 530968d33a25Syt } 53102fcbc377Syt 531168d33a25Syt ahci_log_fatal_error_message(ahci_ctlp, port, intr_status); 53122fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 53132fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 5314a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 0); 5315a9440e8dSyt out1: 531668d33a25Syt /* Prepare the argument for the taskq */ 531768d33a25Syt args = ahci_portp->ahciport_event_args; 531868d33a25Syt args->ahciea_ctlp = (void *)ahci_ctlp; 531968d33a25Syt args->ahciea_portp = (void *)ahci_portp; 532068d33a25Syt args->ahciea_event = intr_status; 532168d33a25Syt 532268d33a25Syt /* Start the taskq to handle error recovery */ 532368d33a25Syt if ((ddi_taskq_dispatch(ahci_ctlp->ahcictl_event_taskq, 532468d33a25Syt ahci_events_handler, 532568d33a25Syt (void *)args, DDI_NOSLEEP)) != DDI_SUCCESS) { 5326a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci start taskq for event handler " 5327a9440e8dSyt "failed", instance); 532868d33a25Syt } 532968d33a25Syt out0: 53302fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 53312fcbc377Syt 53322fcbc377Syt return (AHCI_SUCCESS); 53332fcbc377Syt } 53342fcbc377Syt 53352fcbc377Syt /* 53362fcbc377Syt * Hot Plug Operation for platforms that support Cold Presence Detect. 53372fcbc377Syt * 53382fcbc377Syt * When set, a device status has changed as detected by the cold presence 53392fcbc377Syt * detect logic. This bit can either be set due to a non-connected port 53402fcbc377Syt * receiving a device, or a connected port having its device removed. 53412fcbc377Syt * This bit is only valid if the port supports cold presence detect as 53422fcbc377Syt * indicated by PxCMD.CPD set to '1'. 53432fcbc377Syt * 534468d33a25Syt * At the moment, this interrupt is not needed and disabled and we just 534568d33a25Syt * log the debug message. 53462fcbc377Syt */ 53472fcbc377Syt static int 53482fcbc377Syt ahci_intr_cold_port_detect(ahci_ctl_t *ahci_ctlp, 53492fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 53502fcbc377Syt { 53512fcbc377Syt uint32_t port_cmd_status; 53522fcbc377Syt sata_device_t sdevice; 53532fcbc377Syt 53542fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 53552fcbc377Syt "ahci_intr_cold_port_detect enter, port %d", port); 53562fcbc377Syt 53572fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 53582fcbc377Syt 53592fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 53602fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 53612fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_CPD)) { 53622fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 53632fcbc377Syt "port %d does not support cold presence detect, so " 53642fcbc377Syt "we just ignore this interrupt", port); 53652fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 53662fcbc377Syt return (AHCI_SUCCESS); 53672fcbc377Syt } 53682fcbc377Syt 53692fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 53702fcbc377Syt "port %d device status has changed", port); 53712fcbc377Syt 53722fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 537309121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 53742fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 53752fcbc377Syt sdevice.satadev_addr.pmport = 0; 53762fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 53772fcbc377Syt 53782fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_CPS) { 53792fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 53802fcbc377Syt "port %d: a device is hot plugged", port); 53812fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 53822fcbc377Syt sata_hba_event_notify( 53832fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 53842fcbc377Syt &sdevice, 53852fcbc377Syt SATA_EVNT_DEVICE_ATTACHED); 53862fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 53872fcbc377Syt 53882fcbc377Syt } else { 53892fcbc377Syt AHCIDBG1(AHCIDBG_INTR, ahci_ctlp, 53902fcbc377Syt "port %d: a device is hot unplugged", port); 53912fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 53922fcbc377Syt sata_hba_event_notify( 53932fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 53942fcbc377Syt &sdevice, 53952fcbc377Syt SATA_EVNT_DEVICE_DETACHED); 53962fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 53972fcbc377Syt } 53982fcbc377Syt 53992fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 54002fcbc377Syt 54012fcbc377Syt return (AHCI_SUCCESS); 54022fcbc377Syt } 54032fcbc377Syt 54042fcbc377Syt /* 54052fcbc377Syt * Enable the interrupts for a particular port. 54062fcbc377Syt * 54072fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 54082fcbc377Syt * is called. 54092fcbc377Syt */ 54102fcbc377Syt static void 5411689d74b0Syt ahci_enable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 54122fcbc377Syt { 54132fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 54142fcbc377Syt "ahci_enable_port_intrs enter, port %d", port); 54152fcbc377Syt 54162fcbc377Syt /* 54172fcbc377Syt * Clear port interrupt status before enabling interrupt 54182fcbc377Syt */ 54192fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 54202fcbc377Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 54212fcbc377Syt AHCI_PORT_INTR_MASK); 54222fcbc377Syt 54232fcbc377Syt /* 54242fcbc377Syt * Clear the pending bit from IS.IPS 54252fcbc377Syt */ 54262fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 54272fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (1 << port)); 54282fcbc377Syt 54292fcbc377Syt /* 54302fcbc377Syt * Enable the following interrupts: 54312fcbc377Syt * Device to Host Register FIS Interrupt (DHRS) 54322fcbc377Syt * PIO Setup FIS Interrupt (PSS) 543382263d52Syt * Set Device Bits Interrupt (SDBS) 54342fcbc377Syt * Unknown FIS Interrupt (UFS) 54352fcbc377Syt * Port Connect Change Status (PCS) 54362fcbc377Syt * PhyRdy Change Status (PRCS) 54372fcbc377Syt * Overflow Status (OFS) 54382fcbc377Syt * Interface Non-fatal Error Status (INFS) 54392fcbc377Syt * Interface Fatal Error Status (IFS) 54402fcbc377Syt * Host Bus Data Error Status (HBDS) 54412fcbc377Syt * Host Bus Fatal Error Status (HBFS) 54422fcbc377Syt * Task File Error Status (TFES) 54432fcbc377Syt */ 54442fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 54452fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 54462fcbc377Syt (AHCI_INTR_STATUS_DHRS | 54472fcbc377Syt AHCI_INTR_STATUS_PSS | 544882263d52Syt AHCI_INTR_STATUS_SDBS | 54492fcbc377Syt AHCI_INTR_STATUS_UFS | 545068d33a25Syt AHCI_INTR_STATUS_DPS | 54512fcbc377Syt AHCI_INTR_STATUS_PCS | 54522fcbc377Syt AHCI_INTR_STATUS_PRCS | 54532fcbc377Syt AHCI_INTR_STATUS_OFS | 54542fcbc377Syt AHCI_INTR_STATUS_INFS | 54552fcbc377Syt AHCI_INTR_STATUS_IFS | 54562fcbc377Syt AHCI_INTR_STATUS_HBDS | 54572fcbc377Syt AHCI_INTR_STATUS_HBFS | 54582fcbc377Syt AHCI_INTR_STATUS_TFES)); 54592fcbc377Syt } 54602fcbc377Syt 54612fcbc377Syt /* 54622fcbc377Syt * Enable interrupts for all the ports. 54632fcbc377Syt * 54642fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 54652fcbc377Syt * is called. 54662fcbc377Syt */ 54672fcbc377Syt static void 54682fcbc377Syt ahci_enable_all_intrs(ahci_ctl_t *ahci_ctlp) 54692fcbc377Syt { 54702fcbc377Syt uint32_t ghc_control; 54712fcbc377Syt 54722fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_enable_all_intrs enter"); 54732fcbc377Syt 54742fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 54752fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 54762fcbc377Syt 54772fcbc377Syt ghc_control |= AHCI_HBA_GHC_IE; 54782fcbc377Syt 54792fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 54802fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 54812fcbc377Syt } 54822fcbc377Syt 54832fcbc377Syt /* 54842fcbc377Syt * Disable interrupts for a particular port. 54852fcbc377Syt * 54862fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 54872fcbc377Syt * is called. 54882fcbc377Syt */ 54892fcbc377Syt static void 5490689d74b0Syt ahci_disable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 54912fcbc377Syt { 54922fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 54932fcbc377Syt "ahci_disable_port_intrs enter, port %d", port); 54942fcbc377Syt 54952fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 54962fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 0); 54972fcbc377Syt } 54982fcbc377Syt 54992fcbc377Syt /* 55002fcbc377Syt * Disable interrupts for the whole HBA. 55012fcbc377Syt * 55022fcbc377Syt * The global bit is cleared, then all interrupt sources from all 55032fcbc377Syt * ports are disabled. 55042fcbc377Syt * 55052fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 55062fcbc377Syt * is called. 55072fcbc377Syt */ 55082fcbc377Syt static void 55092fcbc377Syt ahci_disable_all_intrs(ahci_ctl_t *ahci_ctlp) 55102fcbc377Syt { 55112fcbc377Syt uint32_t ghc_control; 55122fcbc377Syt 55132fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_disable_all_intrs enter"); 55142fcbc377Syt 55152fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 55162fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 55172fcbc377Syt 55182fcbc377Syt ghc_control &= ~ AHCI_HBA_GHC_IE; 55192fcbc377Syt 55202fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 55212fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 55222fcbc377Syt } 55232fcbc377Syt 55242fcbc377Syt /* 55252fcbc377Syt * Handle INTx and legacy interrupts. 55262fcbc377Syt */ 55272fcbc377Syt static int 55282fcbc377Syt ahci_add_legacy_intrs(ahci_ctl_t *ahci_ctlp) 55292fcbc377Syt { 55302fcbc377Syt dev_info_t *dip = ahci_ctlp->ahcictl_dip; 55312fcbc377Syt int actual, count = 0; 55322fcbc377Syt int x, y, rc, inum = 0; 55332fcbc377Syt 55342fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY|AHCIDBG_INIT, ahci_ctlp, 55352fcbc377Syt "ahci_add_legacy_intrs enter"); 55362fcbc377Syt 55372fcbc377Syt /* get number of interrupts. */ 55382fcbc377Syt rc = ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED, &count); 55392fcbc377Syt if ((rc != DDI_SUCCESS) || (count == 0)) { 55402fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 55412fcbc377Syt "ddi_intr_get_nintrs() failed, " 55422fcbc377Syt "rc %d count %d\n", rc, count); 55432fcbc377Syt return (DDI_FAILURE); 55442fcbc377Syt } 55452fcbc377Syt 55462fcbc377Syt /* Allocate an array of interrupt handles. */ 55472fcbc377Syt ahci_ctlp->ahcictl_intr_size = count * sizeof (ddi_intr_handle_t); 55482fcbc377Syt ahci_ctlp->ahcictl_intr_htable = 55492fcbc377Syt kmem_zalloc(ahci_ctlp->ahcictl_intr_size, KM_SLEEP); 55502fcbc377Syt 55512fcbc377Syt /* call ddi_intr_alloc(). */ 55522fcbc377Syt rc = ddi_intr_alloc(dip, ahci_ctlp->ahcictl_intr_htable, 55532fcbc377Syt DDI_INTR_TYPE_FIXED, 55542fcbc377Syt inum, count, &actual, DDI_INTR_ALLOC_STRICT); 55552fcbc377Syt 55562fcbc377Syt if ((rc != DDI_SUCCESS) || (actual == 0)) { 55572fcbc377Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 55582fcbc377Syt "ddi_intr_alloc() failed, rc %d\n", rc); 55592fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 55602fcbc377Syt ahci_ctlp->ahcictl_intr_size); 55612fcbc377Syt return (DDI_FAILURE); 55622fcbc377Syt } 55632fcbc377Syt 55642fcbc377Syt if (actual < count) { 55652fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 55662fcbc377Syt "Requested: %d, Received: %d", count, actual); 55672fcbc377Syt 55682fcbc377Syt for (x = 0; x < actual; x++) { 55692fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[x]); 55702fcbc377Syt } 55712fcbc377Syt 55722fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 55732fcbc377Syt ahci_ctlp->ahcictl_intr_size); 55742fcbc377Syt return (DDI_FAILURE); 55752fcbc377Syt } 55762fcbc377Syt 55772fcbc377Syt ahci_ctlp->ahcictl_intr_cnt = actual; 55782fcbc377Syt 55792fcbc377Syt /* Get intr priority. */ 55802fcbc377Syt if (ddi_intr_get_pri(ahci_ctlp->ahcictl_intr_htable[0], 55812fcbc377Syt &ahci_ctlp->ahcictl_intr_pri) != DDI_SUCCESS) { 55822fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 55832fcbc377Syt "ddi_intr_get_pri() failed"); 55842fcbc377Syt 55852fcbc377Syt for (x = 0; x < actual; x++) { 55862fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[x]); 55872fcbc377Syt } 55882fcbc377Syt 55892fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 55902fcbc377Syt ahci_ctlp->ahcictl_intr_size); 55912fcbc377Syt return (DDI_FAILURE); 55922fcbc377Syt } 55932fcbc377Syt 55942fcbc377Syt /* Test for high level interrupt. */ 55952fcbc377Syt if (ahci_ctlp->ahcictl_intr_pri >= ddi_intr_get_hilevel_pri()) { 55962fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 55972fcbc377Syt "ahci_add_legacy_intrs: Hi level intr not supported"); 55982fcbc377Syt 55992fcbc377Syt for (x = 0; x < actual; x++) { 56002fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[x]); 56012fcbc377Syt } 56022fcbc377Syt 56032fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 56042fcbc377Syt sizeof (ddi_intr_handle_t)); 56052fcbc377Syt 56062fcbc377Syt return (DDI_FAILURE); 56072fcbc377Syt } 56082fcbc377Syt 56092fcbc377Syt /* Call ddi_intr_add_handler(). */ 56102fcbc377Syt for (x = 0; x < actual; x++) { 56112fcbc377Syt if (ddi_intr_add_handler(ahci_ctlp->ahcictl_intr_htable[x], 56122fcbc377Syt ahci_intr, (caddr_t)ahci_ctlp, NULL) != DDI_SUCCESS) { 56132fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 56142fcbc377Syt "ddi_intr_add_handler() failed"); 56152fcbc377Syt 56162fcbc377Syt for (y = 0; y < actual; y++) { 56172fcbc377Syt (void) ddi_intr_free( 56182fcbc377Syt ahci_ctlp->ahcictl_intr_htable[y]); 56192fcbc377Syt } 56202fcbc377Syt 56212fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 56222fcbc377Syt ahci_ctlp->ahcictl_intr_size); 56232fcbc377Syt return (DDI_FAILURE); 56242fcbc377Syt } 56252fcbc377Syt } 56262fcbc377Syt 56272fcbc377Syt /* Call ddi_intr_enable() for legacy interrupts. */ 56282fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 56292fcbc377Syt (void) ddi_intr_enable(ahci_ctlp->ahcictl_intr_htable[x]); 56302fcbc377Syt } 56312fcbc377Syt 56322fcbc377Syt return (DDI_SUCCESS); 56332fcbc377Syt } 56342fcbc377Syt 56352fcbc377Syt /* 56362fcbc377Syt * Handle MSI interrupts. 56372fcbc377Syt */ 56382fcbc377Syt static int 56392fcbc377Syt ahci_add_msi_intrs(ahci_ctl_t *ahci_ctlp) 56402fcbc377Syt { 56412fcbc377Syt dev_info_t *dip = ahci_ctlp->ahcictl_dip; 56422fcbc377Syt int count, avail, actual; 56432fcbc377Syt int x, y, rc, inum = 0; 56442fcbc377Syt 56452fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY|AHCIDBG_INIT, ahci_ctlp, 56462fcbc377Syt "ahci_add_msi_intrs enter"); 56472fcbc377Syt 56482fcbc377Syt /* get number of interrupts. */ 56492fcbc377Syt rc = ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_MSI, &count); 56502fcbc377Syt if ((rc != DDI_SUCCESS) || (count == 0)) { 56512fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 56522fcbc377Syt "ddi_intr_get_nintrs() failed, " 56532fcbc377Syt "rc %d count %d\n", rc, count); 56542fcbc377Syt return (DDI_FAILURE); 56552fcbc377Syt } 56562fcbc377Syt 56572fcbc377Syt /* get number of available interrupts. */ 56582fcbc377Syt rc = ddi_intr_get_navail(dip, DDI_INTR_TYPE_MSI, &avail); 56592fcbc377Syt if ((rc != DDI_SUCCESS) || (avail == 0)) { 56602fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 56612fcbc377Syt "ddi_intr_get_navail() failed, " 56622fcbc377Syt "rc %d avail %d\n", rc, avail); 56632fcbc377Syt return (DDI_FAILURE); 56642fcbc377Syt } 56652fcbc377Syt 5666689d74b0Syt #if AHCI_DEBUG 56672fcbc377Syt if (avail < count) { 56682fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 56692fcbc377Syt "ddi_intr_get_nvail returned %d, navail() returned %d", 56702fcbc377Syt count, avail); 56712fcbc377Syt } 5672689d74b0Syt #endif 56732fcbc377Syt 56742fcbc377Syt /* Allocate an array of interrupt handles. */ 56752fcbc377Syt ahci_ctlp->ahcictl_intr_size = count * sizeof (ddi_intr_handle_t); 56762fcbc377Syt ahci_ctlp->ahcictl_intr_htable = 56772fcbc377Syt kmem_alloc(ahci_ctlp->ahcictl_intr_size, KM_SLEEP); 56782fcbc377Syt 56792fcbc377Syt /* call ddi_intr_alloc(). */ 56802fcbc377Syt rc = ddi_intr_alloc(dip, ahci_ctlp->ahcictl_intr_htable, 56812fcbc377Syt DDI_INTR_TYPE_MSI, inum, count, &actual, DDI_INTR_ALLOC_NORMAL); 56822fcbc377Syt 56832fcbc377Syt if ((rc != DDI_SUCCESS) || (actual == 0)) { 56842fcbc377Syt AHCIDBG1(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 56852fcbc377Syt "ddi_intr_alloc() failed, rc %d\n", rc); 56862fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 56872fcbc377Syt ahci_ctlp->ahcictl_intr_size); 56882fcbc377Syt return (DDI_FAILURE); 56892fcbc377Syt } 56902fcbc377Syt 56912fcbc377Syt /* use interrupt count returned */ 5692689d74b0Syt #if AHCI_DEBUG 56932fcbc377Syt if (actual < count) { 56942fcbc377Syt AHCIDBG2(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 56952fcbc377Syt "Requested: %d, Received: %d", count, actual); 56962fcbc377Syt } 5697689d74b0Syt #endif 56982fcbc377Syt 56992fcbc377Syt ahci_ctlp->ahcictl_intr_cnt = actual; 57002fcbc377Syt 57012fcbc377Syt /* 57022fcbc377Syt * Get priority for first msi, assume remaining are all the same. 57032fcbc377Syt */ 57042fcbc377Syt if (ddi_intr_get_pri(ahci_ctlp->ahcictl_intr_htable[0], 57052fcbc377Syt &ahci_ctlp->ahcictl_intr_pri) != DDI_SUCCESS) { 57062fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 57072fcbc377Syt "ddi_intr_get_pri() failed"); 57082fcbc377Syt 57092fcbc377Syt /* Free already allocated intr. */ 57102fcbc377Syt for (y = 0; y < actual; y++) { 57112fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[y]); 57122fcbc377Syt } 57132fcbc377Syt 57142fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 57152fcbc377Syt ahci_ctlp->ahcictl_intr_size); 57162fcbc377Syt return (DDI_FAILURE); 57172fcbc377Syt } 57182fcbc377Syt 57192fcbc377Syt /* Test for high level interrupt. */ 57202fcbc377Syt if (ahci_ctlp->ahcictl_intr_pri >= ddi_intr_get_hilevel_pri()) { 57212fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 57222fcbc377Syt "ahci_add_msi_intrs: Hi level intr not supported"); 57232fcbc377Syt 57242fcbc377Syt /* Free already allocated intr. */ 57252fcbc377Syt for (y = 0; y < actual; y++) { 57262fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[y]); 57272fcbc377Syt } 57282fcbc377Syt 57292fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 57302fcbc377Syt sizeof (ddi_intr_handle_t)); 57312fcbc377Syt 57322fcbc377Syt return (DDI_FAILURE); 57332fcbc377Syt } 57342fcbc377Syt 57352fcbc377Syt /* Call ddi_intr_add_handler(). */ 57362fcbc377Syt for (x = 0; x < actual; x++) { 57372fcbc377Syt if (ddi_intr_add_handler(ahci_ctlp->ahcictl_intr_htable[x], 57382fcbc377Syt ahci_intr, (caddr_t)ahci_ctlp, NULL) != DDI_SUCCESS) { 57392fcbc377Syt AHCIDBG0(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 57402fcbc377Syt "ddi_intr_add_handler() failed"); 57412fcbc377Syt 57422fcbc377Syt /* Free already allocated intr. */ 57432fcbc377Syt for (y = 0; y < actual; y++) { 57442fcbc377Syt (void) ddi_intr_free( 57452fcbc377Syt ahci_ctlp->ahcictl_intr_htable[y]); 57462fcbc377Syt } 57472fcbc377Syt 57482fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 57492fcbc377Syt ahci_ctlp->ahcictl_intr_size); 57502fcbc377Syt return (DDI_FAILURE); 57512fcbc377Syt } 57522fcbc377Syt } 57532fcbc377Syt 57542fcbc377Syt 57552fcbc377Syt (void) ddi_intr_get_cap(ahci_ctlp->ahcictl_intr_htable[0], 57562fcbc377Syt &ahci_ctlp->ahcictl_intr_cap); 57572fcbc377Syt 57582fcbc377Syt if (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK) { 57592fcbc377Syt /* Call ddi_intr_block_enable() for MSI. */ 57602fcbc377Syt (void) ddi_intr_block_enable(ahci_ctlp->ahcictl_intr_htable, 57612fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 57622fcbc377Syt } else { 57632fcbc377Syt /* Call ddi_intr_enable() for MSI non block enable. */ 57642fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 57652fcbc377Syt (void) ddi_intr_enable( 57662fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 57672fcbc377Syt } 57682fcbc377Syt } 57692fcbc377Syt 57702fcbc377Syt return (DDI_SUCCESS); 57712fcbc377Syt } 57722fcbc377Syt 57732fcbc377Syt /* 57742fcbc377Syt * Removes the registered interrupts irrespective of whether they 57752fcbc377Syt * were legacy or MSI. 57762fcbc377Syt * 57772fcbc377Syt * WARNING!!! The controller interrupts must be disabled before calling 57782fcbc377Syt * this routine. 57792fcbc377Syt */ 57802fcbc377Syt static void 57812fcbc377Syt ahci_rem_intrs(ahci_ctl_t *ahci_ctlp) 57822fcbc377Syt { 57832fcbc377Syt int x; 57842fcbc377Syt 57852fcbc377Syt AHCIDBG0(AHCIDBG_ENTRY, ahci_ctlp, "ahci_rem_intrs entered"); 57862fcbc377Syt 57872fcbc377Syt /* Disable all interrupts. */ 57882fcbc377Syt if ((ahci_ctlp->ahcictl_intr_type == DDI_INTR_TYPE_MSI) && 57892fcbc377Syt (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK)) { 57902fcbc377Syt /* Call ddi_intr_block_disable(). */ 57912fcbc377Syt (void) ddi_intr_block_disable(ahci_ctlp->ahcictl_intr_htable, 57922fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 57932fcbc377Syt } else { 57942fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 57952fcbc377Syt (void) ddi_intr_disable( 57962fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 57972fcbc377Syt } 57982fcbc377Syt } 57992fcbc377Syt 58002fcbc377Syt /* Call ddi_intr_remove_handler(). */ 58012fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 58022fcbc377Syt (void) ddi_intr_remove_handler( 58032fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 58042fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[x]); 58052fcbc377Syt } 58062fcbc377Syt 58072fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, ahci_ctlp->ahcictl_intr_size); 58082fcbc377Syt } 58092fcbc377Syt 58102fcbc377Syt /* 581168d33a25Syt * This routine tries to put port into P:NotRunning state by clearing 581268d33a25Syt * PxCMD.ST. HBA will clear PxCI to 0h, PxSACT to 0h, PxCMD.CCS to 0h 581368d33a25Syt * and PxCMD.CR to '0'. 58142fcbc377Syt * 58152fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 58162fcbc377Syt * is called. 58172fcbc377Syt */ 58182fcbc377Syt static int 581968d33a25Syt ahci_put_port_into_notrunning_state(ahci_ctl_t *ahci_ctlp, 582068d33a25Syt ahci_port_t *ahci_portp, uint8_t port) 58212fcbc377Syt { 58222fcbc377Syt uint32_t port_cmd_status; 58232fcbc377Syt int loop_count; 58242fcbc377Syt 58252fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 582668d33a25Syt "ahci_put_port_into_notrunning_state enter: port %d", port); 58272fcbc377Syt 58282fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 58292fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 58302fcbc377Syt 58312fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_ST; 58322fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 58332fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 58342fcbc377Syt 58352fcbc377Syt /* Wait until PxCMD.CR is cleared */ 58362fcbc377Syt loop_count = 0; 58372fcbc377Syt do { 58382fcbc377Syt port_cmd_status = 58392fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 58402fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 58412fcbc377Syt 58422fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_IDLE) { 58432fcbc377Syt AHCIDBG2(AHCIDBG_INIT, ahci_ctlp, 58442fcbc377Syt "clearing port %d CMD.CR timeout, " 58452fcbc377Syt "port_cmd_status = 0x%x", port, 58462fcbc377Syt port_cmd_status); 58472fcbc377Syt /* 58482fcbc377Syt * We are effectively timing out after 0.5 sec. 58492fcbc377Syt * This value is specified in AHCI spec. 58502fcbc377Syt */ 58512fcbc377Syt break; 58522fcbc377Syt } 58532fcbc377Syt 5854689d74b0Syt /* Wait for 10 millisec */ 58552fcbc377Syt delay(AHCI_10MS_TICKS); 58562fcbc377Syt } while (port_cmd_status & AHCI_CMD_STATUS_CR); 58572fcbc377Syt 585868d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_STARTED; 58592fcbc377Syt 586068d33a25Syt if (port_cmd_status & AHCI_CMD_STATUS_CR) { 58612fcbc377Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 586268d33a25Syt "ahci_put_port_into_notrunning_state: failed to clear " 586368d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 586468d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 58652fcbc377Syt return (AHCI_FAILURE); 58662fcbc377Syt } else { 586768d33a25Syt AHCIDBG2(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 586868d33a25Syt "ahci_put_port_into_notrunning_state: succeeded to clear " 586968d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 587068d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 58712fcbc377Syt return (AHCI_SUCCESS); 58722fcbc377Syt } 58732fcbc377Syt } 58742fcbc377Syt 58752fcbc377Syt /* 587668d33a25Syt * First clear PxCMD.ST, and then check PxTFD. If both PxTFD.STS.BSY 587768d33a25Syt * and PxTFD.STS.DRQ cleared to '0', it means the device is in a 587868d33a25Syt * stable state, then set PxCMD.ST to '1' to start the port directly. 587968d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue a 588068d33a25Syt * COMRESET to the device to put it in an idle state. 588168d33a25Syt * 588268d33a25Syt * The fifth argument returns whether the port reset is involved during 588368d33a25Syt * the process. 588468d33a25Syt * 588568d33a25Syt * The routine will be called under six scenarios: 58862fcbc377Syt * 1. Initialize the port 58872fcbc377Syt * 2. To abort the packet(s) 58882fcbc377Syt * 3. To reset the port 588968d33a25Syt * 4. To activate the port 589068d33a25Syt * 5. Fatal error recovery 589168d33a25Syt * 6. To abort the timeout packet(s) 58922fcbc377Syt * 58932fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 589468d33a25Syt * is called. And ahciport_mutex will be released before the reset 589568d33a25Syt * event is reported to sata module by calling sata_hba_event_notify, 589668d33a25Syt * and then be acquired again later. 589782263d52Syt * 589882263d52Syt * NOTES!!! During this procedure, PxSERR register will be cleared, and 589982263d52Syt * according to the spec, the clearance of three bits will also clear 590082263d52Syt * three interrupt status bits. 590182263d52Syt * 1. PxSERR.DIAG.F will clear PxIS.UFS 590282263d52Syt * 2. PxSERR.DIAG.X will clear PxIS.PCS 590382263d52Syt * 3. PxSERR.DIAG.N will clear PxIS.PRCS 590482263d52Syt * 590582263d52Syt * Among these three interrupt events, the driver needs to take care of 590682263d52Syt * PxIS.PRCS, which is the hot plug event. When the driver found out 590782263d52Syt * a device was unplugged, it will call the interrupt handler. 59082fcbc377Syt */ 59092fcbc377Syt static int 59102fcbc377Syt ahci_restart_port_wait_till_ready(ahci_ctl_t *ahci_ctlp, 591168d33a25Syt ahci_port_t *ahci_portp, uint8_t port, int flag, int *reset_flag) 59122fcbc377Syt { 591382263d52Syt uint32_t port_sstatus; 59142fcbc377Syt uint32_t task_file_status; 59152fcbc377Syt sata_device_t sdevice; 59162fcbc377Syt int rval; 591782263d52Syt int dev_exists_begin = 0; 591882263d52Syt int dev_exists_end = 0; 59192fcbc377Syt 59202fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 59212fcbc377Syt "ahci_restart_port_wait_till_ready: port %d enter", port); 59222fcbc377Syt 5923a9440e8dSyt if ((flag != AHCI_PORT_INIT) && 5924a9440e8dSyt (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE)) 592582263d52Syt dev_exists_begin = 1; 59262fcbc377Syt 592782263d52Syt /* First clear PxCMD.ST */ 592868d33a25Syt rval = ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, 592968d33a25Syt port); 593068d33a25Syt if (rval != AHCI_SUCCESS) 593168d33a25Syt /* 593268d33a25Syt * If PxCMD.CR does not clear within a reasonable time, it 593368d33a25Syt * may assume the interface is in a hung condition and may 593468d33a25Syt * continue with issuing the port reset. 593568d33a25Syt */ 593668d33a25Syt goto reset; 59372fcbc377Syt 593868d33a25Syt /* Then clear PxSERR */ 593968d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 594068d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 594168d33a25Syt AHCI_SERROR_CLEAR_ALL); 59422fcbc377Syt 594382263d52Syt /* Then get PxTFD */ 594468d33a25Syt task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 594568d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 59462fcbc377Syt 594768d33a25Syt /* 594868d33a25Syt * Check whether the device is in a stable status, if yes, 594968d33a25Syt * then start the port directly. However for ahci_tran_dport_reset, 595068d33a25Syt * we may have to perform a port reset. 595168d33a25Syt */ 595268d33a25Syt if (!(task_file_status & (AHCI_TFD_STS_BSY | AHCI_TFD_STS_DRQ)) && 595368d33a25Syt !(flag & AHCI_PORT_RESET)) 59542fcbc377Syt goto out; 59552fcbc377Syt 595668d33a25Syt reset: 595768d33a25Syt /* 595868d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue 595968d33a25Syt * a COMRESET to the device 596068d33a25Syt */ 59612fcbc377Syt rval = ahci_port_reset(ahci_ctlp, ahci_portp, port); 596268d33a25Syt 596368d33a25Syt if (reset_flag != NULL) 596468d33a25Syt *reset_flag = 1; 59652fcbc377Syt 59662fcbc377Syt /* Indicate to the framework that a reset has happened. */ 596782263d52Syt if ((ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) && 596882263d52Syt !(flag & AHCI_RESET_NO_EVENTS_UP)) { 596982263d52Syt /* Set the reset in progress flag */ 597082263d52Syt ahci_portp->ahciport_reset_in_progress = 1; 59712fcbc377Syt 59722fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 597309121340Syt sdevice.satadev_addr.cport = 597409121340Syt ahci_ctlp->ahcictl_port_to_cport[port]; 597568d33a25Syt sdevice.satadev_addr.pmport = 0; 597668d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 59772fcbc377Syt 59782fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 59792fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 59802fcbc377Syt if (ahci_ctlp->ahcictl_sata_hba_tran) { 59812fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 59822fcbc377Syt sata_hba_event_notify( 59832fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 59842fcbc377Syt &sdevice, 59852fcbc377Syt SATA_EVNT_DEVICE_RESET); 59862fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 59872fcbc377Syt } 59882fcbc377Syt 598968d33a25Syt AHCIDBG1(AHCIDBG_EVENT, ahci_ctlp, 599068d33a25Syt "port %d sending event up: SATA_EVNT_RESET", port); 599182263d52Syt } else { 599282263d52Syt ahci_portp->ahciport_reset_in_progress = 0; 59932fcbc377Syt } 599482263d52Syt 59952fcbc377Syt out: 5996a9440e8dSyt /* Start the port if not in initialization phase */ 5997a9440e8dSyt if (flag & AHCI_PORT_INIT) 5998a9440e8dSyt return (rval); 5999a9440e8dSyt 6000a9440e8dSyt (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 600182263d52Syt 600282263d52Syt /* SStatus tells the presence of device. */ 600382263d52Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 600482263d52Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 600582263d52Syt 600682263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 600782263d52Syt dev_exists_end = 1; 600882263d52Syt ASSERT(ahci_portp->ahciport_device_type != SATA_DTYPE_NONE); 600982263d52Syt } 601082263d52Syt 601182263d52Syt /* Check whether a hot plug event happened */ 601282263d52Syt if (dev_exists_begin == 1 && dev_exists_end == 0) { 601382263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 601482263d52Syt "ahci_restart_port_wait_till_ready: port %d " 601582263d52Syt "device is removed", port); 601682263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_NODEV; 601782263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 601882263d52Syt "ahci_restart_port_wait_till_ready: port %d " 601982263d52Syt "AHCI_PORT_FLAG_NODEV flag is set", port); 602082263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 602182263d52Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, port); 602282263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 60232fcbc377Syt } 60242fcbc377Syt 60252fcbc377Syt return (rval); 60262fcbc377Syt } 60272fcbc377Syt 60282fcbc377Syt /* 602968d33a25Syt * This routine may be called under four scenarios: 603068d33a25Syt * a) do the recovery from fatal error 60312fcbc377Syt * b) or we need to timeout some commands 60322fcbc377Syt * c) or we need to abort some commands 60332fcbc377Syt * d) or we need reset device/port/controller 60342fcbc377Syt * 60352fcbc377Syt * In all these scenarios, we need to send any pending unfinished 60362fcbc377Syt * commands up to sata framework. 60372fcbc377Syt * 603868d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 60392fcbc377Syt */ 60402fcbc377Syt static void 60412fcbc377Syt ahci_mop_commands(ahci_ctl_t *ahci_ctlp, 60422fcbc377Syt ahci_port_t *ahci_portp, 60432fcbc377Syt uint32_t slot_status, 60442fcbc377Syt uint32_t failed_tags, 60452fcbc377Syt uint32_t timeout_tags, 60462fcbc377Syt uint32_t aborted_tags, 60472fcbc377Syt uint32_t reset_tags) 60482fcbc377Syt { 604982263d52Syt uint32_t finished_tags = 0; 605082263d52Syt uint32_t unfinished_tags = 0; 60512fcbc377Syt int tmp_slot; 60522fcbc377Syt sata_pkt_t *satapkt; 605382263d52Syt int ncq_cmd_in_progress = 0; 605482263d52Syt int err_retri_cmd_in_progress = 0; 60552fcbc377Syt 60562fcbc377Syt AHCIDBG2(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 60572fcbc377Syt "ahci_mop_commands entered: port: %d slot_status: 0x%x", 6058689d74b0Syt ahci_portp->ahciport_port_num, slot_status); 60592fcbc377Syt 60602fcbc377Syt AHCIDBG4(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 60612fcbc377Syt "ahci_mop_commands: failed_tags: 0x%x, " 60622fcbc377Syt "timeout_tags: 0x%x aborted_tags: 0x%x, " 60632fcbc377Syt "reset_tags: 0x%x", failed_tags, 60642fcbc377Syt timeout_tags, aborted_tags, reset_tags); 60652fcbc377Syt 606682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 606782263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 606882263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 60692fcbc377Syt 607082263d52Syt unfinished_tags = slot_status & 607182263d52Syt AHCI_SLOT_MASK(ahci_ctlp) & 607282263d52Syt ~failed_tags & 607382263d52Syt ~aborted_tags & 607482263d52Syt ~reset_tags & 607582263d52Syt ~timeout_tags; 6076a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 607782263d52Syt ncq_cmd_in_progress = 1; 607882263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 607982263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 608082263d52Syt 608182263d52Syt unfinished_tags = slot_status & 608282263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp) & 608382263d52Syt ~failed_tags & 608482263d52Syt ~aborted_tags & 608582263d52Syt ~reset_tags & 608682263d52Syt ~timeout_tags; 6087a9440e8dSyt } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 60882fcbc377Syt 6089a9440e8dSyt /* 6090a9440e8dSyt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT is 6091a9440e8dSyt * set, it means REQUEST SENSE or READ LOG EXT command doesn't 6092a9440e8dSyt * complete successfully due to one of the following three 6093a9440e8dSyt * conditions: 6094a9440e8dSyt * 6095a9440e8dSyt * 1. Fatal error - failed_tags includes its slot 6096a9440e8dSyt * 2. Timed out - timeout_tags includes its slot 6097a9440e8dSyt * 3. Aborted when hot unplug - aborted_tags includes its 6098a9440e8dSyt * slot 6099a9440e8dSyt * 6100a9440e8dSyt * Please note that the command is always sent down in Slot 0 6101a9440e8dSyt */ 610282263d52Syt err_retri_cmd_in_progress = 1; 610382263d52Syt AHCIDBG1(AHCIDBG_ERRS|AHCIDBG_NCQ, ahci_ctlp, 610482263d52Syt "ahci_mop_commands is called for port %d while " 610582263d52Syt "REQUEST SENSE or READ LOG EXT for error retrieval " 6106689d74b0Syt "is being executed", ahci_portp->ahciport_port_num); 610768d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress > 1); 610882263d52Syt ASSERT(slot_status == 0x1); 61092fcbc377Syt } 61102fcbc377Syt 611168d33a25Syt /* Send up finished packets with SATA_PKT_COMPLETED */ 61122fcbc377Syt while (finished_tags) { 61132fcbc377Syt tmp_slot = ddi_ffs(finished_tags) - 1; 61142fcbc377Syt if (tmp_slot == -1) { 61152fcbc377Syt break; 61162fcbc377Syt } 61172fcbc377Syt 61182fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 61192fcbc377Syt ASSERT(satapkt != NULL); 61202fcbc377Syt 61212fcbc377Syt AHCIDBG1(AHCIDBG_INFO, ahci_ctlp, "ahci_mop_commands: " 61222fcbc377Syt "sending up pkt 0x%p with SATA_PKT_COMPLETED", 61232fcbc377Syt (void *)satapkt); 61242fcbc377Syt 612568d33a25Syt /* 612668d33a25Syt * Cannot fetch the return register content since the port 612768d33a25Syt * was restarted, so the corresponding tag will be set to 612868d33a25Syt * aborted tags. 612968d33a25Syt */ 613068d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 613168d33a25Syt CLEAR_BIT(finished_tags, tmp_slot); 613268d33a25Syt aborted_tags |= tmp_slot; 613368d33a25Syt continue; 613468d33a25Syt } 613568d33a25Syt 613682263d52Syt if (ncq_cmd_in_progress) 613782263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 613882263d52Syt tmp_slot); 61392fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 61402fcbc377Syt CLEAR_BIT(finished_tags, tmp_slot); 61412fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 61422fcbc377Syt 61432fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 61442fcbc377Syt } 61452fcbc377Syt 614668d33a25Syt /* Send up failed packets with SATA_PKT_DEV_ERROR. */ 61472fcbc377Syt while (failed_tags) { 614882263d52Syt if (err_retri_cmd_in_progress) { 614982263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 615082263d52Syt ASSERT(satapkt != NULL); 615182263d52Syt ASSERT(failed_tags == 0x1); 615282263d52Syt 615382263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 615482263d52Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 615582263d52Syt (void *)satapkt); 615682263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 615782263d52Syt break; 615882263d52Syt } 615982263d52Syt 61602fcbc377Syt tmp_slot = ddi_ffs(failed_tags) - 1; 61612fcbc377Syt if (tmp_slot == -1) { 61622fcbc377Syt break; 61632fcbc377Syt } 61642fcbc377Syt 61652fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 61662fcbc377Syt ASSERT(satapkt != NULL); 61672fcbc377Syt 61682fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 61692fcbc377Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 61702fcbc377Syt (void *)satapkt); 61712fcbc377Syt 617282263d52Syt if (ncq_cmd_in_progress) 617382263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 617482263d52Syt tmp_slot); 61752fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 61762fcbc377Syt CLEAR_BIT(failed_tags, tmp_slot); 61772fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 61782fcbc377Syt 61792fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 61802fcbc377Syt } 61812fcbc377Syt 618268d33a25Syt /* Send up timeout packets with SATA_PKT_TIMEOUT. */ 61832fcbc377Syt while (timeout_tags) { 618482263d52Syt if (err_retri_cmd_in_progress) { 618582263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 618682263d52Syt ASSERT(satapkt != NULL); 618782263d52Syt ASSERT(timeout_tags == 0x1); 618882263d52Syt 618982263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 619082263d52Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 619182263d52Syt (void *)satapkt); 619282263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 619382263d52Syt break; 619482263d52Syt } 619582263d52Syt 61962fcbc377Syt tmp_slot = ddi_ffs(timeout_tags) - 1; 61972fcbc377Syt if (tmp_slot == -1) { 61982fcbc377Syt break; 61992fcbc377Syt } 62002fcbc377Syt 62012fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 62022fcbc377Syt ASSERT(satapkt != NULL); 62032fcbc377Syt 62042fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 62052fcbc377Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 62062fcbc377Syt (void *)satapkt); 62072fcbc377Syt 620882263d52Syt if (ncq_cmd_in_progress) 620982263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 621082263d52Syt tmp_slot); 62112fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 62122fcbc377Syt CLEAR_BIT(timeout_tags, tmp_slot); 62132fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 62142fcbc377Syt 62152fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 62162fcbc377Syt } 62172fcbc377Syt 621868d33a25Syt /* Send up aborted packets with SATA_PKT_ABORTED */ 62192fcbc377Syt while (aborted_tags) { 622082263d52Syt if (err_retri_cmd_in_progress) { 622182263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 622282263d52Syt ASSERT(satapkt != NULL); 622382263d52Syt ASSERT(aborted_tags == 0x1); 622482263d52Syt 622582263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 622682263d52Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 622782263d52Syt (void *)satapkt); 622882263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 622982263d52Syt break; 623082263d52Syt } 623182263d52Syt 62322fcbc377Syt tmp_slot = ddi_ffs(aborted_tags) - 1; 62332fcbc377Syt if (tmp_slot == -1) { 62342fcbc377Syt break; 62352fcbc377Syt } 62362fcbc377Syt 62372fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 62382fcbc377Syt ASSERT(satapkt != NULL); 62392fcbc377Syt 62402fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 62412fcbc377Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 62422fcbc377Syt (void *)satapkt); 62432fcbc377Syt 624482263d52Syt if (ncq_cmd_in_progress) 624582263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 624682263d52Syt tmp_slot); 62472fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 62482fcbc377Syt CLEAR_BIT(aborted_tags, tmp_slot); 62492fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 62502fcbc377Syt 62512fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 62522fcbc377Syt } 62532fcbc377Syt 625468d33a25Syt /* Send up reset packets with SATA_PKT_RESET. */ 62552fcbc377Syt while (reset_tags) { 62562fcbc377Syt tmp_slot = ddi_ffs(reset_tags) - 1; 62572fcbc377Syt if (tmp_slot == -1) { 62582fcbc377Syt break; 62592fcbc377Syt } 62602fcbc377Syt 62612fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 62622fcbc377Syt ASSERT(satapkt != NULL); 62632fcbc377Syt 62642fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 62652fcbc377Syt "sending up pkt 0x%p with SATA_PKT_RESET", 62662fcbc377Syt (void *)satapkt); 62672fcbc377Syt 626882263d52Syt if (ncq_cmd_in_progress) 626982263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 627082263d52Syt tmp_slot); 62712fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 62722fcbc377Syt CLEAR_BIT(reset_tags, tmp_slot); 62732fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 62742fcbc377Syt 62752fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 62762fcbc377Syt } 62772fcbc377Syt 627868d33a25Syt /* Send up unfinished packets with SATA_PKT_RESET */ 62792fcbc377Syt while (unfinished_tags) { 62802fcbc377Syt tmp_slot = ddi_ffs(unfinished_tags) - 1; 62812fcbc377Syt if (tmp_slot == -1) { 62822fcbc377Syt break; 62832fcbc377Syt } 62842fcbc377Syt 62852fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 62862fcbc377Syt ASSERT(satapkt != NULL); 62872fcbc377Syt 62882fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 628968d33a25Syt "sending up pkt 0x%p with SATA_PKT_RESET", 62902fcbc377Syt (void *)satapkt); 62912fcbc377Syt 629282263d52Syt if (ncq_cmd_in_progress) 629382263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 629482263d52Syt tmp_slot); 62952fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 62962fcbc377Syt CLEAR_BIT(unfinished_tags, tmp_slot); 62972fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 62982fcbc377Syt 629968d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 63002fcbc377Syt } 63012fcbc377Syt 630268d33a25Syt ahci_portp->ahciport_mop_in_progress--; 630368d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress >= 0); 63042fcbc377Syt 630568d33a25Syt if (ahci_portp->ahciport_mop_in_progress == 0) 630668d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_MOPPING; 630768d33a25Syt } 63082fcbc377Syt 630982263d52Syt /* 631082263d52Syt * This routine is going to first request a READ LOG EXT sata pkt from sata 631182263d52Syt * module, and then deliver it to the HBA to get the ncq failure context. 631282263d52Syt * The return value is the exactly failed tags. 631382263d52Syt */ 631482263d52Syt static uint32_t 631582263d52Syt ahci_get_rdlogext_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 631682263d52Syt uint8_t port) 631782263d52Syt { 631882263d52Syt sata_device_t sdevice; 631982263d52Syt sata_pkt_t *rdlog_spkt, *spkt; 632082263d52Syt ddi_dma_handle_t buf_dma_handle; 632182263d52Syt int loop_count; 632282263d52Syt int rval; 632382263d52Syt int failed_slot; 632482263d52Syt uint32_t failed_tags = 0; 632582263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page; 632682263d52Syt 632782263d52Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_NCQ, ahci_ctlp, 632882263d52Syt "ahci_get_rdlogext_data enter: port %d", port); 632982263d52Syt 633082263d52Syt /* Prepare the sdevice data */ 633182263d52Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 633282263d52Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 633382263d52Syt 633482263d52Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 633582263d52Syt sdevice.satadev_addr.pmport = 0; 633682263d52Syt 633782263d52Syt /* 633882263d52Syt * Call the sata hba interface to get a rdlog spkt 633982263d52Syt */ 634082263d52Syt loop_count = 0; 634182263d52Syt loop: 634282263d52Syt rdlog_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 634382263d52Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_NCQ); 634482263d52Syt if (rdlog_spkt == NULL) { 634582263d52Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 634682263d52Syt /* Sleep for a while */ 634782263d52Syt delay(AHCI_10MS_TICKS); 634882263d52Syt goto loop; 634982263d52Syt } 635082263d52Syt /* Timed out after 1s */ 635182263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 635282263d52Syt "failed to get rdlog spkt for port %d", port); 635382263d52Syt return (failed_tags); 635482263d52Syt } 635582263d52Syt 635682263d52Syt ASSERT(rdlog_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 635782263d52Syt 635882263d52Syt /* 635982263d52Syt * This flag is used to handle the specific error recovery when the 636082263d52Syt * READ LOG EXT command gets a failure (fatal error or time-out). 636182263d52Syt */ 636282263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RDLOGEXT; 636382263d52Syt 636482263d52Syt /* 636582263d52Syt * This start is not supposed to fail because after port is restarted, 636682263d52Syt * the whole command list is empty. 636782263d52Syt */ 636882263d52Syt ahci_portp->ahciport_err_retri_pkt = rdlog_spkt; 636982263d52Syt (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, port, rdlog_spkt); 637082263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 637182263d52Syt 637282263d52Syt /* Remove the flag after READ LOG EXT command is completed */ 637382263d52Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RDLOGEXT; 637482263d52Syt 637582263d52Syt if (rdlog_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 637682263d52Syt /* Update the request log data */ 637782263d52Syt buf_dma_handle = *(ddi_dma_handle_t *) 637882263d52Syt (rdlog_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 637982263d52Syt rval = ddi_dma_sync(buf_dma_handle, 0, 0, 638082263d52Syt DDI_DMA_SYNC_FORKERNEL); 638182263d52Syt if (rval == DDI_SUCCESS) { 638282263d52Syt ncq_err_page = 638382263d52Syt (struct sata_ncq_error_recovery_page *)rdlog_spkt-> 638482263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr; 638582263d52Syt 638682263d52Syt /* Get the failed tag */ 638782263d52Syt failed_slot = ncq_err_page->ncq_tag; 638882263d52Syt AHCIDBG2(AHCIDBG_ERRS, ahci_ctlp, 638982263d52Syt "ahci_get_rdlogext_data: port %d " 639082263d52Syt "failed slot %d", port, failed_slot); 639182263d52Syt if (failed_slot & NQ) { 639282263d52Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 639382263d52Syt "the failed slot is not a valid tag"); 639482263d52Syt goto out; 639582263d52Syt } 639682263d52Syt 639782263d52Syt failed_slot &= NCQ_TAG_MASK; 639882263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 639982263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 640082263d52Syt "ahci_get_rdlogext_data: failed spkt 0x%p", 640182263d52Syt (void *)spkt); 640282263d52Syt if (spkt == NULL) { 640382263d52Syt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, 640482263d52Syt "the failed slot spkt is NULL") 640582263d52Syt goto out; 640682263d52Syt } 640782263d52Syt 640882263d52Syt failed_tags = 0x1 << failed_slot; 640982263d52Syt 641082263d52Syt /* Fill out the error context */ 641182263d52Syt ahci_copy_ncq_err_page(&spkt->satapkt_cmd, 641282263d52Syt ncq_err_page); 641382263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 641482263d52Syt &spkt->satapkt_device); 641582263d52Syt } 641682263d52Syt } 641782263d52Syt out: 641882263d52Syt sata_free_error_retrieval_pkt(rdlog_spkt); 641982263d52Syt 642082263d52Syt return (failed_tags); 642182263d52Syt } 642282263d52Syt 642368d33a25Syt /* 642468d33a25Syt * This routine is going to first request a REQUEST SENSE sata pkt from sata 642568d33a25Syt * module, and then deliver it to the HBA to get the sense data and copy 642668d33a25Syt * the sense data back to the orignal failed sata pkt, and free the REQUEST 642768d33a25Syt * SENSE sata pkt later. 642868d33a25Syt */ 642968d33a25Syt static void 643068d33a25Syt ahci_get_rqsense_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 643168d33a25Syt uint8_t port, sata_pkt_t *spkt) 643268d33a25Syt { 643368d33a25Syt sata_device_t sdevice; 643468d33a25Syt sata_pkt_t *rs_spkt; 643568d33a25Syt sata_cmd_t *sata_cmd; 643668d33a25Syt ddi_dma_handle_t buf_dma_handle; 643768d33a25Syt int loop_count; 643868d33a25Syt #if AHCI_DEBUG 643968d33a25Syt struct scsi_extended_sense *rqsense; 644068d33a25Syt #endif 644168d33a25Syt 644268d33a25Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 644368d33a25Syt "ahci_get_rqsense_data enter: port %d", port); 644468d33a25Syt 644568d33a25Syt /* Prepare the sdevice data */ 644668d33a25Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 644768d33a25Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 644868d33a25Syt 644968d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 645068d33a25Syt sdevice.satadev_addr.pmport = 0; 645168d33a25Syt 645268d33a25Syt sata_cmd = &spkt->satapkt_cmd; 645368d33a25Syt 645468d33a25Syt /* 645568d33a25Syt * Call the sata hba interface to get a rs spkt 645668d33a25Syt */ 645768d33a25Syt loop_count = 0; 645868d33a25Syt loop: 645968d33a25Syt rs_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 646068d33a25Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_ATAPI); 646168d33a25Syt if (rs_spkt == NULL) { 646268d33a25Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 646368d33a25Syt /* Sleep for a while */ 646468d33a25Syt delay(AHCI_10MS_TICKS); 646568d33a25Syt goto loop; 646668d33a25Syt 646768d33a25Syt } 646868d33a25Syt /* Timed out after 1s */ 646982263d52Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 647068d33a25Syt "failed to get rs spkt for port %d", port); 647168d33a25Syt return; 647268d33a25Syt } 647368d33a25Syt 647468d33a25Syt ASSERT(rs_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 647568d33a25Syt 647668d33a25Syt /* 647768d33a25Syt * This flag is used to handle the specific error recovery when the 647882263d52Syt * REQUEST SENSE command gets a faiure (fatal error or time-out). 647968d33a25Syt */ 648068d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RQSENSE; 648168d33a25Syt 648268d33a25Syt /* 648368d33a25Syt * This start is not supposed to fail because after port is restarted, 648482263d52Syt * the whole command list is empty. 648568d33a25Syt */ 648682263d52Syt ahci_portp->ahciport_err_retri_pkt = rs_spkt; 648782263d52Syt (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, port, rs_spkt); 648882263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 648968d33a25Syt 649068d33a25Syt /* Remove the flag after REQUEST SENSE command is completed */ 649168d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RQSENSE; 649268d33a25Syt 649368d33a25Syt if (rs_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 649468d33a25Syt /* Update the request sense data */ 649568d33a25Syt buf_dma_handle = *(ddi_dma_handle_t *) 649668d33a25Syt (rs_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 649782263d52Syt (void) ddi_dma_sync(buf_dma_handle, 0, 0, 649868d33a25Syt DDI_DMA_SYNC_FORKERNEL); 649982263d52Syt /* Copy the request sense data */ 650082263d52Syt bcopy(rs_spkt-> 650182263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr, 650282263d52Syt &sata_cmd->satacmd_rqsense, 650382263d52Syt SATA_ATAPI_MIN_RQSENSE_LEN); 650468d33a25Syt #if AHCI_DEBUG 650582263d52Syt rqsense = (struct scsi_extended_sense *) 650682263d52Syt sata_cmd->satacmd_rqsense; 650782263d52Syt 650882263d52Syt /* Dump the sense data */ 650982263d52Syt AHCIDBG0(AHCIDBG_SENSEDATA, ahci_ctlp, "\n"); 651082263d52Syt AHCIDBG2(AHCIDBG_SENSEDATA, ahci_ctlp, 651182263d52Syt "Sense data for satapkt %p ATAPI cmd 0x%x", 651282263d52Syt spkt, sata_cmd->satacmd_acdb[0]); 651382263d52Syt AHCIDBG5(AHCIDBG_SENSEDATA, ahci_ctlp, 651482263d52Syt " es_code 0x%x es_class 0x%x " 651582263d52Syt "es_key 0x%x es_add_code 0x%x " 651682263d52Syt "es_qual_code 0x%x", 651782263d52Syt rqsense->es_code, rqsense->es_class, 651882263d52Syt rqsense->es_key, rqsense->es_add_code, 651982263d52Syt rqsense->es_qual_code); 652068d33a25Syt #endif 652168d33a25Syt } 652268d33a25Syt 652368d33a25Syt sata_free_error_retrieval_pkt(rs_spkt); 65242fcbc377Syt } 65252fcbc377Syt 65262fcbc377Syt /* 65272fcbc377Syt * Fatal errors will cause the HBA to enter the ERR: Fatal state. To recover, 652868d33a25Syt * the port must be restarted. When the HBA detects thus error, it may try 652968d33a25Syt * to abort a transfer. And if the transfer was aborted, the device is 653068d33a25Syt * expected to send a D2H Register FIS with PxTFD.STS.ERR set to '1' and both 653168d33a25Syt * PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0'. Then system software knows 653268d33a25Syt * that the device is in a stable status and transfers may be restarted without 653368d33a25Syt * issuing a COMRESET to the device. If PxTFD.STS.BSY or PxTFD.STS.DRQ is set, 653468d33a25Syt * then the software will send the COMRESET to do the port reset. 653568d33a25Syt * 653668d33a25Syt * Software should perform the appropriate error recovery actions based on 653768d33a25Syt * whether non-queued commands were being issued or natived command queuing 653868d33a25Syt * commands were being issued. 653968d33a25Syt * 654068d33a25Syt * And software will complete the command that had the error with error mark 654168d33a25Syt * to higher level software. 65422fcbc377Syt * 65432fcbc377Syt * Fatal errors include the following: 65442fcbc377Syt * PxIS.IFS - Interface Fatal Error Status 65452fcbc377Syt * PxIS.HBDS - Host Bus Data Error Status 65462fcbc377Syt * PxIS.HBFS - Host Bus Fatal Error Status 65472fcbc377Syt * PxIS.TFES - Task File Error Status 65482fcbc377Syt * 65492fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 65502fcbc377Syt */ 655168d33a25Syt static void 655268d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctl_t *ahci_ctlp, 655382263d52Syt ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 65542fcbc377Syt { 655582263d52Syt uint32_t port_cmd_status; 655682263d52Syt uint32_t slot_status = 0; 655768d33a25Syt uint32_t failed_tags = 0; 655868d33a25Syt int failed_slot; 655968d33a25Syt int reset_flag = 0; 656068d33a25Syt ahci_fis_d2h_register_t *ahci_rcvd_fisp; 656182263d52Syt sata_cmd_t *sata_cmd = NULL; 656282263d52Syt sata_pkt_t *spkt = NULL; 65632fcbc377Syt 65642fcbc377Syt AHCIDBG1(AHCIDBG_ENTRY, ahci_ctlp, 656568d33a25Syt "ahci_fatal_error_recovery_handler enter: port %d", port); 65662fcbc377Syt 656782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 656882263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 65692fcbc377Syt 657082263d52Syt /* Read PxCI to see which commands are still outstanding */ 657182263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 657282263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 65732fcbc377Syt 657482263d52Syt /* 657582263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 657682263d52Syt * was processing when the error occurred. 657782263d52Syt */ 657882263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 657982263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 658082263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 658182263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 65822fcbc377Syt 658382263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 658482263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 658582263d52Syt ASSERT(spkt != NULL); 658682263d52Syt } else { 658782263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 658882263d52Syt if (spkt == NULL) { 658982263d52Syt /* May happen when interface errors occur? */ 659082263d52Syt goto next; 659182263d52Syt } 659282263d52Syt } 65932fcbc377Syt 659482263d52Syt sata_cmd = &spkt->satapkt_cmd; 65952fcbc377Syt 659682263d52Syt /* Fill out the status and error registers for PxIS.TFES */ 659782263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 659882263d52Syt ahci_rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 659982263d52Syt ahcirf_d2h_register_fis); 660082263d52Syt 660182263d52Syt /* Copy the error context back to the sata_cmd */ 660282263d52Syt ahci_copy_err_cnxt(sata_cmd, ahci_rcvd_fisp); 660382263d52Syt } 66042fcbc377Syt 660582263d52Syt /* The failed command must be one of the outstanding commands */ 660682263d52Syt failed_tags = 0x1 << failed_slot; 660782263d52Syt ASSERT(failed_tags & slot_status); 660882263d52Syt 660982263d52Syt /* Update the sata registers, especially PxSERR register */ 661082263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 661182263d52Syt &spkt->satapkt_device); 66122fcbc377Syt 6613a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 661482263d52Syt /* Read PxSACT to see which commands are still outstanding */ 661582263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 661682263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 661782263d52Syt } 661868d33a25Syt next: 66192fcbc377Syt 662068d33a25Syt #if AHCI_DEBUG 662168d33a25Syt /* 662282263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 662382263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 662482263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 662582263d52Syt * recovery process. At this time, the only outstanding command is 662682263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 662768d33a25Syt */ 662882263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 66292fcbc377Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 663068d33a25Syt "ahci_fatal_error_recovery_handler: port %d REQUEST SENSE " 663182263d52Syt "command or READ LOG EXT command for error data retrieval " 663282263d52Syt "failed", port); 663382263d52Syt ASSERT(slot_status == 0x1); 663482263d52Syt ASSERT(failed_slot == 0x1); 663582263d52Syt ASSERT(spkt->satapkt_cmd.satacmd_acdb[0] == 663682263d52Syt SCMD_REQUEST_SENSE || 663782263d52Syt spkt->satapkt_cmd.satacmd_cmd_reg == 663882263d52Syt SATAC_READ_LOG_EXT); 66392fcbc377Syt } 664068d33a25Syt #endif 66412fcbc377Syt 664268d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 664368d33a25Syt ahci_portp->ahciport_mop_in_progress++; 66442fcbc377Syt 664568d33a25Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 664668d33a25Syt port, NULL, &reset_flag); 66472fcbc377Syt 664868d33a25Syt /* 664968d33a25Syt * Won't retrieve error information: 665068d33a25Syt * 1. Port reset was involved to recover 665182263d52Syt * 2. Device is gone 665268d33a25Syt * 3. IDENTIFY DEVICE command sent to ATAPI device 665382263d52Syt * 4. REQUEST SENSE or READ LOG EXT command during error recovery 665468d33a25Syt */ 665582263d52Syt if (reset_flag || 665682263d52Syt ahci_portp->ahciport_device_type == SATA_DTYPE_NONE || 665782263d52Syt spkt && spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_ID_DEVICE || 665882263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 665968d33a25Syt goto out; 66602fcbc377Syt 666182263d52Syt /* 666282263d52Syt * Deliver READ LOG EXT to gather information about the error when 666382263d52Syt * a COMRESET has not been performed as part of the error recovery 666482263d52Syt * during NCQ command processing. 666582263d52Syt */ 666682263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 666782263d52Syt failed_tags = ahci_get_rdlogext_data(ahci_ctlp, 666882263d52Syt ahci_portp, port); 666982263d52Syt goto out; 667082263d52Syt } 667182263d52Syt 667268d33a25Syt /* 667368d33a25Syt * Deliver REQUEST SENSE for ATAPI command to gather information about 667468d33a25Syt * the error when a COMRESET has not been performed as part of the 667568d33a25Syt * error recovery. 667668d33a25Syt */ 667782263d52Syt if (spkt && ahci_portp->ahciport_device_type == SATA_DTYPE_ATAPICD) 667868d33a25Syt ahci_get_rqsense_data(ahci_ctlp, ahci_portp, port, spkt); 667968d33a25Syt out: 668082263d52Syt AHCIDBG5(AHCIDBG_ERRS, ahci_ctlp, 668182263d52Syt "ahci_fatal_error_recovery_handler: port %d interface error " 668282263d52Syt "occurred slot_status = 0x%x, pending_tags = 0x%x, " 668382263d52Syt "pending_ncq_tags = 0x%x failed_tags = 0x%x", 668482263d52Syt port, slot_status, ahci_portp->ahciport_pending_tags, 668582263d52Syt ahci_portp->ahciport_pending_ncq_tags, failed_tags); 668682263d52Syt 66872fcbc377Syt ahci_mop_commands(ahci_ctlp, 66882fcbc377Syt ahci_portp, 668982263d52Syt slot_status, 66902fcbc377Syt failed_tags, /* failed tags */ 66912fcbc377Syt 0, /* timeout tags */ 66922fcbc377Syt 0, /* aborted tags */ 66932fcbc377Syt 0); /* reset tags */ 669468d33a25Syt } 669568d33a25Syt 669668d33a25Syt /* 669768d33a25Syt * Handle events - fatal error recovery 669868d33a25Syt */ 669968d33a25Syt static void 670068d33a25Syt ahci_events_handler(void *args) 670168d33a25Syt { 670268d33a25Syt ahci_event_arg_t *ahci_event_arg; 670368d33a25Syt ahci_ctl_t *ahci_ctlp; 670468d33a25Syt ahci_port_t *ahci_portp; 670568d33a25Syt uint32_t event; 670668d33a25Syt uint8_t port; 670768d33a25Syt 670868d33a25Syt ahci_event_arg = (ahci_event_arg_t *)args; 670982263d52Syt 671068d33a25Syt ahci_ctlp = ahci_event_arg->ahciea_ctlp; 671168d33a25Syt ahci_portp = ahci_event_arg->ahciea_portp; 671268d33a25Syt event = ahci_event_arg->ahciea_event; 671368d33a25Syt port = ahci_portp->ahciport_port_num; 671468d33a25Syt 671582263d52Syt AHCIDBG2(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 671682263d52Syt "ahci_events_handler enter: port %d intr_status = 0x%x", 671782263d52Syt port, event); 671868d33a25Syt 67192fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 67202fcbc377Syt 672168d33a25Syt /* 672268d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 672368d33a25Syt * SATA_DTYPE_NONE. 672468d33a25Syt */ 672568d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 672668d33a25Syt AHCIDBG1(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 672768d33a25Syt "ahci_events_handler: port %d no device attached, " 672868d33a25Syt "and just return without doing anything", port); 672968d33a25Syt goto out; 673068d33a25Syt } 673168d33a25Syt 673268d33a25Syt if (event & (AHCI_INTR_STATUS_IFS | 673368d33a25Syt AHCI_INTR_STATUS_HBDS | 673468d33a25Syt AHCI_INTR_STATUS_HBFS | 673568d33a25Syt AHCI_INTR_STATUS_TFES)) 673668d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctlp, ahci_portp, 673782263d52Syt port, event); 673868d33a25Syt 673968d33a25Syt out: 674068d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 67412fcbc377Syt } 67422fcbc377Syt 67432fcbc377Syt /* 674468d33a25Syt * ahci_watchdog_handler() and ahci_do_sync_start will call us if they 674568d33a25Syt * detect there are some commands which are timed out. 67462fcbc377Syt */ 67472fcbc377Syt static void 67482fcbc377Syt ahci_timeout_pkts(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 674982263d52Syt uint8_t port, uint32_t tmp_timeout_tags) 67502fcbc377Syt { 675182263d52Syt uint32_t slot_status = 0; 675282263d52Syt uint32_t finished_tags = 0; 675382263d52Syt uint32_t timeout_tags = 0; 67542fcbc377Syt 675568d33a25Syt AHCIDBG1(AHCIDBG_TIMEOUT|AHCIDBG_ENTRY, ahci_ctlp, 675668d33a25Syt "ahci_timeout_pkts enter: port %d", port); 67572fcbc377Syt 67582fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 67592fcbc377Syt 676082263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 676182263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 676282263d52Syt /* Read PxCI to see which commands are still outstanding */ 676382263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 676482263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 6765a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 676682263d52Syt /* Read PxSACT to see which commands are still outstanding */ 676782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 676882263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 676982263d52Syt } 677068d33a25Syt 677168d33a25Syt #if AHCI_DEBUG 67722fcbc377Syt /* 677382263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 677482263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 677582263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 677682263d52Syt * recovery process. At this time, the only outstanding command is 677782263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 67782fcbc377Syt */ 677982263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 678082263d52Syt AHCIDBG4(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, ahci_ctlp, 678168d33a25Syt "ahci_timeout_pkts called while REQUEST SENSE " 678282263d52Syt "command or READ LOG EXT command for error recovery " 678382263d52Syt "timed out timeout_tags = 0x%x, slot_status = 0x%x, " 678482263d52Syt "pending_tags = 0x%x, pending_ncq_tags = 0x%x", 678582263d52Syt tmp_timeout_tags, slot_status, 678682263d52Syt ahci_portp->ahciport_pending_tags, 678782263d52Syt ahci_portp->ahciport_pending_ncq_tags); 678882263d52Syt ASSERT(slot_status == 0x1); 67892fcbc377Syt } 679068d33a25Syt #endif 67912fcbc377Syt 679268d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 679368d33a25Syt ahci_portp->ahciport_mop_in_progress++; 67942fcbc377Syt 67952fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 679668d33a25Syt port, NULL, NULL); 679768d33a25Syt 67982fcbc377Syt /* 67992fcbc377Syt * Re-identify timeout tags because some previously checked commands 68002fcbc377Syt * could already complete. 68012fcbc377Syt */ 680282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 680382263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 680482263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 680582263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 680682263d52Syt 680782263d52Syt AHCIDBG5(AHCIDBG_TIMEOUT, ahci_ctlp, 680882263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 680982263d52Syt "timeout_tags = 0x%x, port_cmd_issue = 0x%x, " 681082263d52Syt "pending_tags = 0x%x ", 681182263d52Syt port, finished_tags, timeout_tags, 681282263d52Syt slot_status, ahci_portp->ahciport_pending_tags); 6813a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 681482263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 681582263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 681682263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 681782263d52Syt 681882263d52Syt AHCIDBG5(AHCIDBG_TIMEOUT|AHCIDBG_NCQ, ahci_ctlp, 681982263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 682082263d52Syt "timeout_tags = 0x%x, port_sactive = 0x%x, " 682182263d52Syt "pending_ncq_tags = 0x%x ", 682282263d52Syt port, finished_tags, timeout_tags, 682382263d52Syt slot_status, ahci_portp->ahciport_pending_ncq_tags); 6824a9440e8dSyt } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 682582263d52Syt timeout_tags = tmp_timeout_tags; 682682263d52Syt } 68272fcbc377Syt 68282fcbc377Syt ahci_mop_commands(ahci_ctlp, 68292fcbc377Syt ahci_portp, 683082263d52Syt slot_status, 68312fcbc377Syt 0, /* failed tags */ 68322fcbc377Syt timeout_tags, /* timeout tags */ 68332fcbc377Syt 0, /* aborted tags */ 68342fcbc377Syt 0); /* reset tags */ 683568d33a25Syt 683668d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 68372fcbc377Syt } 68382fcbc377Syt 68392fcbc377Syt /* 68402fcbc377Syt * Watchdog handler kicks in every 5 seconds to timeout any commands pending 68412fcbc377Syt * for long time. 68422fcbc377Syt */ 68432fcbc377Syt static void 68442fcbc377Syt ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp) 68452fcbc377Syt { 68462fcbc377Syt ahci_port_t *ahci_portp; 684768d33a25Syt sata_pkt_t *spkt; 684882263d52Syt uint32_t pending_tags; 684982263d52Syt uint32_t timeout_tags; 685068d33a25Syt uint32_t port_cmd_status; 685182263d52Syt uint32_t port_sactive; 68522fcbc377Syt uint8_t port; 68532fcbc377Syt int tmp_slot; 685468d33a25Syt int current_slot; 685582263d52Syt uint32_t current_tags; 6856a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 68572fcbc377Syt /* max number of cycles this packet should survive */ 68582fcbc377Syt int max_life_cycles; 68592fcbc377Syt 68602fcbc377Syt /* how many cycles this packet survived so far */ 68612fcbc377Syt int watched_cycles; 68622fcbc377Syt 68632fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 68642fcbc377Syt 68652fcbc377Syt AHCIDBG0(AHCIDBG_TIMEOUT|AHCIDBG_ENTRY, ahci_ctlp, 68662fcbc377Syt "ahci_watchdog_handler entered"); 68672fcbc377Syt 68682fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 68692fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 68702fcbc377Syt continue; 68712fcbc377Syt } 68722fcbc377Syt 68732fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 68742fcbc377Syt 68752fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 68762fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 68772fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 68782fcbc377Syt continue; 68792fcbc377Syt } 68802fcbc377Syt 688168d33a25Syt /* Skip the check for those ports in error recovery */ 688282263d52Syt if ((ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) && 688382263d52Syt !(ERR_RETRI_CMD_IN_PROGRESS(ahci_portp))) { 688468d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 688568d33a25Syt continue; 688668d33a25Syt } 688768d33a25Syt 688882263d52Syt pending_tags = 0; 688968d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 689068d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 689168d33a25Syt 689282263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 689382263d52Syt current_slot = 0; 689482263d52Syt pending_tags = 0x1; 6895a9440e8dSyt } else if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 689682263d52Syt current_slot = 689782263d52Syt (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 689882263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 689982263d52Syt pending_tags = ahci_portp->ahciport_pending_tags; 6900a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 690182263d52Syt port_sactive = ddi_get32( 690282263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 690382263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 690482263d52Syt current_tags = port_sactive & 690582263d52Syt ~port_cmd_status & 690682263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 690782263d52Syt pending_tags = ahci_portp->ahciport_pending_ncq_tags; 690882263d52Syt } 690982263d52Syt 69102fcbc377Syt timeout_tags = 0; 69112fcbc377Syt while (pending_tags) { 69122fcbc377Syt tmp_slot = ddi_ffs(pending_tags) - 1; 69132fcbc377Syt if (tmp_slot == -1) { 69142fcbc377Syt break; 69152fcbc377Syt } 69162fcbc377Syt 691782263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 691882263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 691982263d52Syt else 692082263d52Syt spkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 692182263d52Syt 692268d33a25Syt if ((spkt != NULL) && spkt->satapkt_time && 692368d33a25Syt !(spkt->satapkt_op_mode & SATA_OPMODE_POLLING)) { 69242fcbc377Syt /* 69252fcbc377Syt * We are overloading satapkt_hba_driver_private 69262fcbc377Syt * with watched_cycle count. 69272fcbc377Syt * 69282fcbc377Syt * If a packet has survived for more than it's 69292fcbc377Syt * max life cycles, it is a candidate for time 69302fcbc377Syt * out. 69312fcbc377Syt */ 69322fcbc377Syt watched_cycles = (int)(intptr_t) 693368d33a25Syt spkt->satapkt_hba_driver_private; 69342fcbc377Syt watched_cycles++; 693568d33a25Syt max_life_cycles = (spkt->satapkt_time + 69362fcbc377Syt ahci_watchdog_timeout - 1) / 69372fcbc377Syt ahci_watchdog_timeout; 693868d33a25Syt 693968d33a25Syt spkt->satapkt_hba_driver_private = 694068d33a25Syt (void *)(intptr_t)watched_cycles; 694168d33a25Syt 694268d33a25Syt if (watched_cycles <= max_life_cycles) 694368d33a25Syt goto next; 694468d33a25Syt 694568d33a25Syt AHCIDBG1(AHCIDBG_ERRS, ahci_ctlp, 694668d33a25Syt "the current slot is %d", current_slot); 694768d33a25Syt /* 694868d33a25Syt * We need to check whether the HBA has 694968d33a25Syt * begun to execute the command, if not, 695068d33a25Syt * then re-set the timer of the command. 695168d33a25Syt */ 695282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) && 695382263d52Syt (tmp_slot != current_slot) || 695482263d52Syt NCQ_CMD_IN_PROGRESS(ahci_portp) && 695582263d52Syt ((0x1 << tmp_slot) & current_tags)) { 695668d33a25Syt spkt->satapkt_hba_driver_private = 695768d33a25Syt (void *)(intptr_t)0; 695868d33a25Syt } else { 69592fcbc377Syt timeout_tags |= (0x1 << tmp_slot); 6960a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: watchdog " 69612fcbc377Syt "port %d satapkt 0x%p timed out\n", 6962a9440e8dSyt instance, port, (void *)spkt); 69632fcbc377Syt } 69642fcbc377Syt } 696568d33a25Syt next: 69662fcbc377Syt CLEAR_BIT(pending_tags, tmp_slot); 69672fcbc377Syt } 69682fcbc377Syt 69692fcbc377Syt if (timeout_tags) { 69702fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 69712fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 69722fcbc377Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 697382263d52Syt port, timeout_tags); 69742fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 69752fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 69762fcbc377Syt } 69772fcbc377Syt 69782fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 69792fcbc377Syt } 69802fcbc377Syt 69812fcbc377Syt /* Re-install the watchdog timeout handler */ 69822fcbc377Syt if (ahci_ctlp->ahcictl_timeout_id != 0) { 69832fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 69842fcbc377Syt timeout((void (*)(void *))ahci_watchdog_handler, 69852fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 69862fcbc377Syt } 69872fcbc377Syt 69882fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 69892fcbc377Syt } 69902fcbc377Syt 69912fcbc377Syt /* 699268d33a25Syt * Fill the error context into sata_cmd for non-queued command error. 699368d33a25Syt */ 699468d33a25Syt static void 699568d33a25Syt ahci_copy_err_cnxt(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 699668d33a25Syt { 699768d33a25Syt scmd->satacmd_status_reg = GET_RFIS_STATUS(rfisp); 699868d33a25Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 699968d33a25Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 700068d33a25Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 700168d33a25Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 700268d33a25Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 700368d33a25Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 700468d33a25Syt 700568d33a25Syt if (scmd->satacmd_addr_type == ATA_ADDR_LBA48) { 700668d33a25Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 700768d33a25Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 700868d33a25Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 700968d33a25Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 701068d33a25Syt } 701168d33a25Syt } 701268d33a25Syt 701382263d52Syt /* 701482263d52Syt * Fill the ncq error page into sata_cmd for queued command error. 701582263d52Syt */ 701682263d52Syt static void 701782263d52Syt ahci_copy_ncq_err_page(sata_cmd_t *scmd, 701882263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page) 701982263d52Syt { 702082263d52Syt scmd->satacmd_sec_count_msb = ncq_err_page->ncq_sector_count_ext; 702182263d52Syt scmd->satacmd_sec_count_lsb = ncq_err_page->ncq_sector_count; 702282263d52Syt scmd->satacmd_lba_low_msb = ncq_err_page->ncq_sector_number_ext; 702382263d52Syt scmd->satacmd_lba_low_lsb = ncq_err_page->ncq_sector_number; 702482263d52Syt scmd->satacmd_lba_mid_msb = ncq_err_page->ncq_cyl_low_ext; 702582263d52Syt scmd->satacmd_lba_mid_lsb = ncq_err_page->ncq_cyl_low; 702682263d52Syt scmd->satacmd_lba_high_msb = ncq_err_page->ncq_cyl_high_ext; 702782263d52Syt scmd->satacmd_lba_high_lsb = ncq_err_page->ncq_cyl_high; 702882263d52Syt scmd->satacmd_device_reg = ncq_err_page->ncq_dev_head; 702982263d52Syt scmd->satacmd_status_reg = ncq_err_page->ncq_status; 703082263d52Syt scmd->satacmd_error_reg = ncq_err_page->ncq_error; 703182263d52Syt } 703282263d52Syt 703368d33a25Syt /* 703468d33a25Syt * Put the respective register value to sata_cmd_t for satacmd_flags. 70352fcbc377Syt */ 70362fcbc377Syt static void 70372fcbc377Syt ahci_copy_out_regs(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 70382fcbc377Syt { 70392fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_msb) 70402fcbc377Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 70412fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_msb) 70422fcbc377Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 70432fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_msb) 70442fcbc377Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 70452fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_msb) 70462fcbc377Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 70472fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_lsb) 70482fcbc377Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 70492fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_lsb) 70502fcbc377Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 70512fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_lsb) 70522fcbc377Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 70532fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_lsb) 70542fcbc377Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 70552fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_device_reg) 70562fcbc377Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 70572fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_error_reg) 70582fcbc377Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 70592fcbc377Syt } 70602fcbc377Syt 706168d33a25Syt static void 706268d33a25Syt ahci_log_fatal_error_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 706368d33a25Syt uint32_t intr_status) 706468d33a25Syt { 7065a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 706668d33a25Syt 706768d33a25Syt if (intr_status & AHCI_INTR_STATUS_IFS) 7068a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d has interface fatal " 7069a9440e8dSyt "error", instance, port); 707068d33a25Syt 707168d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBDS) 7072a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d has bus data error", 7073a9440e8dSyt instance, port); 707468d33a25Syt 707568d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBFS) 7076a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d has bus fatal error", 7077a9440e8dSyt instance, port); 707868d33a25Syt 707968d33a25Syt if (intr_status & AHCI_INTR_STATUS_TFES) 7080a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d has task file error", 7081a9440e8dSyt instance, port); 708268d33a25Syt 7083a9440e8dSyt cmn_err(CE_WARN, "ahci%d: ahci port %d is trying to do error " 7084a9440e8dSyt "recovery", instance, port); 708568d33a25Syt } 708668d33a25Syt 70872fcbc377Syt /* 70882fcbc377Syt * Dump the serror message to the log. 70892fcbc377Syt */ 70902fcbc377Syt static void 709168d33a25Syt ahci_log_serror_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 7092a9440e8dSyt uint32_t port_serror, int debug_only) 70932fcbc377Syt { 7094a9440e8dSyt static char err_buf[512]; 7095a9440e8dSyt static char err_msg_header[16]; 7096a9440e8dSyt char *err_msg = err_buf; 7097a9440e8dSyt 7098a9440e8dSyt *err_buf = '\0'; 7099a9440e8dSyt *err_msg_header = '\0'; 71002fcbc377Syt 710182263d52Syt if (port_serror & SERROR_DATA_ERR_FIXED) { 7102a9440e8dSyt err_msg = strcat(err_msg, 7103a9440e8dSyt "\tRecovered Data Integrity Error (I)\n"); 71042fcbc377Syt } 71052fcbc377Syt 710682263d52Syt if (port_serror & SERROR_COMM_ERR_FIXED) { 7107a9440e8dSyt err_msg = strcat(err_msg, 7108a9440e8dSyt "\tRecovered Communication Error (M)\n"); 71092fcbc377Syt } 71102fcbc377Syt 711182263d52Syt if (port_serror & SERROR_DATA_ERR) { 7112a9440e8dSyt err_msg = strcat(err_msg, 7113a9440e8dSyt "\tTransient Data Integrity Error (T)\n"); 71142fcbc377Syt } 71152fcbc377Syt 711682263d52Syt if (port_serror & SERROR_PERSISTENT_ERR) { 7117a9440e8dSyt err_msg = strcat(err_msg, 7118a9440e8dSyt "\tPersistent Communication or Data Integrity Error (C)\n"); 71192fcbc377Syt } 71202fcbc377Syt 712182263d52Syt if (port_serror & SERROR_PROTOCOL_ERR) { 7122a9440e8dSyt err_msg = strcat(err_msg, "\tProtocol Error (P)\n"); 71232fcbc377Syt } 71242fcbc377Syt 712582263d52Syt if (port_serror & SERROR_INT_ERR) { 7126a9440e8dSyt err_msg = strcat(err_msg, "\tInternal Error (E)\n"); 71272fcbc377Syt } 71282fcbc377Syt 712982263d52Syt if (port_serror & SERROR_PHY_RDY_CHG) { 7130a9440e8dSyt err_msg = strcat(err_msg, "\tPhyRdy Change (N)\n"); 71312fcbc377Syt } 71322fcbc377Syt 713382263d52Syt if (port_serror & SERROR_PHY_INT_ERR) { 7134a9440e8dSyt err_msg = strcat(err_msg, "\tPhy Internal Error (I)\n"); 71352fcbc377Syt } 71362fcbc377Syt 713782263d52Syt if (port_serror & SERROR_COMM_WAKE) { 7138a9440e8dSyt err_msg = strcat(err_msg, "\tComm Wake (W)\n"); 71392fcbc377Syt } 71402fcbc377Syt 714182263d52Syt if (port_serror & SERROR_10B_TO_8B_ERR) { 7142a9440e8dSyt err_msg = strcat(err_msg, "\t10B to 8B Decode Error (B)\n"); 71432fcbc377Syt } 71442fcbc377Syt 714582263d52Syt if (port_serror & SERROR_DISPARITY_ERR) { 7146a9440e8dSyt err_msg = strcat(err_msg, "\tDisparity Error (D)\n"); 71472fcbc377Syt } 71482fcbc377Syt 714982263d52Syt if (port_serror & SERROR_CRC_ERR) { 7150a9440e8dSyt err_msg = strcat(err_msg, "\tCRC Error (C)\n"); 71512fcbc377Syt } 71522fcbc377Syt 715382263d52Syt if (port_serror & SERROR_HANDSHAKE_ERR) { 7154a9440e8dSyt err_msg = strcat(err_msg, "\tHandshake Error (H)\n"); 71552fcbc377Syt } 71562fcbc377Syt 715782263d52Syt if (port_serror & SERROR_LINK_SEQ_ERR) { 7158a9440e8dSyt err_msg = strcat(err_msg, "\tLink Sequence Error (S)\n"); 71592fcbc377Syt } 71602fcbc377Syt 716182263d52Syt if (port_serror & SERROR_TRANS_ERR) { 7162a9440e8dSyt err_msg = strcat(err_msg, 7163a9440e8dSyt "\tTransport state transition error (T)\n"); 71642fcbc377Syt } 71652fcbc377Syt 716682263d52Syt if (port_serror & SERROR_FIS_TYPE) { 7167a9440e8dSyt err_msg = strcat(err_msg, "\tUnknown FIS Type (F)\n"); 71682fcbc377Syt } 71692fcbc377Syt 717082263d52Syt if (port_serror & SERROR_EXCHANGED_ERR) { 7171a9440e8dSyt err_msg = strcat(err_msg, "\tExchanged (X)\n"); 7172a9440e8dSyt } 7173a9440e8dSyt 7174a9440e8dSyt if (err_msg == NULL) 7175a9440e8dSyt return; 7176a9440e8dSyt 7177a9440e8dSyt if (debug_only) { 7178a9440e8dSyt (void) sprintf(err_msg_header, "port %d", port); 7179a9440e8dSyt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, err_msg_header); 7180a9440e8dSyt AHCIDBG0(AHCIDBG_ERRS, ahci_ctlp, err_msg); 7181a9440e8dSyt } else if (ahci_ctlp) { 7182a9440e8dSyt cmn_err(CE_WARN, "ahci%d: %s %s", 7183a9440e8dSyt ddi_get_instance(ahci_ctlp->ahcictl_dip), 7184a9440e8dSyt err_msg_header, err_msg); 7185a9440e8dSyt } else { 7186a9440e8dSyt cmn_err(CE_WARN, "ahci: %s %s", err_msg_header, err_msg); 71872fcbc377Syt } 71882fcbc377Syt } 71892fcbc377Syt 71902fcbc377Syt /* 71912fcbc377Syt * This routine is to calculate the total number of ports implemented 71922fcbc377Syt * by the HBA. 71932fcbc377Syt */ 71942fcbc377Syt static int 71952fcbc377Syt ahci_get_num_implemented_ports(uint32_t ports_implemented) 71962fcbc377Syt { 71972fcbc377Syt uint8_t i; 71982fcbc377Syt int num = 0; 71992fcbc377Syt 72002fcbc377Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 72012fcbc377Syt if (((uint32_t)0x1 << i) & ports_implemented) 72022fcbc377Syt num++; 72032fcbc377Syt } 72042fcbc377Syt 72052fcbc377Syt return (num); 72062fcbc377Syt } 72072fcbc377Syt 7208689d74b0Syt #if AHCI_DEBUG 72092fcbc377Syt static void 72102fcbc377Syt ahci_log(ahci_ctl_t *ahci_ctlp, uint_t level, char *fmt, ...) 72112fcbc377Syt { 7212a9440e8dSyt static char name[16]; 72132fcbc377Syt va_list ap; 72142fcbc377Syt 72152fcbc377Syt mutex_enter(&ahci_log_mutex); 72162fcbc377Syt 72172fcbc377Syt va_start(ap, fmt); 72182fcbc377Syt if (ahci_ctlp) { 7219a9440e8dSyt (void) sprintf(name, "ahci%d: ", 72202fcbc377Syt ddi_get_instance(ahci_ctlp->ahcictl_dip)); 72212fcbc377Syt } else { 7222a9440e8dSyt (void) sprintf(name, "ahci: "); 72232fcbc377Syt } 72242fcbc377Syt 72252fcbc377Syt (void) vsprintf(ahci_log_buf, fmt, ap); 72262fcbc377Syt va_end(ap); 72272fcbc377Syt 7228a9440e8dSyt cmn_err(level, "%s%s", name, ahci_log_buf); 72292fcbc377Syt 72302fcbc377Syt mutex_exit(&ahci_log_mutex); 72312fcbc377Syt } 7232689d74b0Syt #endif 7233