12fcbc377Syt /* 22fcbc377Syt * CDDL HEADER START 32fcbc377Syt * 42fcbc377Syt * The contents of this file are subject to the terms of the 52fcbc377Syt * Common Development and Distribution License (the "License"). 62fcbc377Syt * You may not use this file except in compliance with the License. 72fcbc377Syt * 82fcbc377Syt * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92fcbc377Syt * or http://www.opensolaris.org/os/licensing. 102fcbc377Syt * See the License for the specific language governing permissions 112fcbc377Syt * and limitations under the License. 122fcbc377Syt * 132fcbc377Syt * When distributing Covered Code, include this CDDL HEADER in each 142fcbc377Syt * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152fcbc377Syt * If applicable, add the following below this CDDL HEADER, with the 162fcbc377Syt * fields enclosed by brackets "[]" replaced with your own identifying 172fcbc377Syt * information: Portions Copyright [yyyy] [name of copyright owner] 182fcbc377Syt * 192fcbc377Syt * CDDL HEADER END 202fcbc377Syt */ 212fcbc377Syt 222fcbc377Syt /* 232c742e1fSying tian - Beijing China * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 242fcbc377Syt * Use is subject to license terms. 252fcbc377Syt */ 262fcbc377Syt 272fcbc377Syt /* 282fcbc377Syt * AHCI (Advanced Host Controller Interface) SATA HBA Driver 2913bcbb7aSyt * 3013bcbb7aSyt * Power Management Support 3113bcbb7aSyt * ------------------------ 3213bcbb7aSyt * 3313bcbb7aSyt * At the moment, the ahci driver only implements suspend/resume to 3413bcbb7aSyt * support Suspend to RAM on X86 feature. Device power management isn't 3513bcbb7aSyt * implemented, link power management is disabled, and hot plug isn't 3613bcbb7aSyt * allowed during the period from suspend to resume. 3713bcbb7aSyt * 3813bcbb7aSyt * For s/r support, the ahci driver only need to implement DDI_SUSPEND 3913bcbb7aSyt * and DDI_RESUME entries, and don't need to take care of new requests 4013bcbb7aSyt * sent down after suspend because the target driver (sd) has already 4113bcbb7aSyt * handled these conditions, and blocked these requests. For the detailed 4213bcbb7aSyt * information, please check with sdopen, sdclose and sdioctl routines. 4313bcbb7aSyt * 442fcbc377Syt */ 452fcbc377Syt 46689d74b0Syt #include <sys/note.h> 472fcbc377Syt #include <sys/scsi/scsi.h> 482fcbc377Syt #include <sys/pci.h> 49b2e3645aSying tian - Beijing China #include <sys/disp.h> 502fcbc377Syt #include <sys/sata/sata_hba.h> 512fcbc377Syt #include <sys/sata/adapters/ahci/ahcireg.h> 522fcbc377Syt #include <sys/sata/adapters/ahci/ahcivar.h> 532fcbc377Syt 5419397407SSherry Moore /* 5519397407SSherry Moore * This is the string displayed by modinfo, etc. 5619397407SSherry Moore * Make sure you keep the version ID up to date! 5719397407SSherry Moore */ 5819397407SSherry Moore static char ahci_ident[] = "ahci driver"; 5919397407SSherry Moore 602fcbc377Syt /* 612fcbc377Syt * Function prototypes for driver entry points 622fcbc377Syt */ 632fcbc377Syt static int ahci_attach(dev_info_t *, ddi_attach_cmd_t); 642fcbc377Syt static int ahci_detach(dev_info_t *, ddi_detach_cmd_t); 652fcbc377Syt static int ahci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 6619397407SSherry Moore static int ahci_quiesce(dev_info_t *); 672fcbc377Syt 682fcbc377Syt /* 692fcbc377Syt * Function prototypes for SATA Framework interfaces 702fcbc377Syt */ 712fcbc377Syt static int ahci_register_sata_hba_tran(ahci_ctl_t *, uint32_t); 722fcbc377Syt static int ahci_unregister_sata_hba_tran(ahci_ctl_t *); 732fcbc377Syt 742fcbc377Syt static int ahci_tran_probe_port(dev_info_t *, sata_device_t *); 752fcbc377Syt static int ahci_tran_start(dev_info_t *, sata_pkt_t *spkt); 762fcbc377Syt static int ahci_tran_abort(dev_info_t *, sata_pkt_t *, int); 772fcbc377Syt static int ahci_tran_reset_dport(dev_info_t *, sata_device_t *); 782fcbc377Syt static int ahci_tran_hotplug_port_activate(dev_info_t *, sata_device_t *); 792fcbc377Syt static int ahci_tran_hotplug_port_deactivate(dev_info_t *, sata_device_t *); 802fcbc377Syt #if defined(__lock_lint) 812fcbc377Syt static int ahci_selftest(dev_info_t *, sata_device_t *); 822fcbc377Syt #endif 832fcbc377Syt 842fcbc377Syt /* 852fcbc377Syt * Local function prototypes 862fcbc377Syt */ 8768d33a25Syt static int ahci_alloc_ports_state(ahci_ctl_t *); 8868d33a25Syt static void ahci_dealloc_ports_state(ahci_ctl_t *); 892fcbc377Syt static int ahci_alloc_port_state(ahci_ctl_t *, uint8_t); 902fcbc377Syt static void ahci_dealloc_port_state(ahci_ctl_t *, uint8_t); 912fcbc377Syt static int ahci_alloc_rcvd_fis(ahci_ctl_t *, ahci_port_t *, uint8_t); 92689d74b0Syt static void ahci_dealloc_rcvd_fis(ahci_port_t *); 932fcbc377Syt static int ahci_alloc_cmd_list(ahci_ctl_t *, ahci_port_t *, uint8_t); 942fcbc377Syt static void ahci_dealloc_cmd_list(ahci_ctl_t *, ahci_port_t *); 952fcbc377Syt static int ahci_alloc_cmd_tables(ahci_ctl_t *, ahci_port_t *); 962fcbc377Syt static void ahci_dealloc_cmd_tables(ahci_ctl_t *, ahci_port_t *); 978aa6aadbSXiao-Yu Zhang static void ahci_alloc_pmult(ahci_ctl_t *, ahci_port_t *); 988aa6aadbSXiao-Yu Zhang static void ahci_dealloc_pmult(ahci_ctl_t *, ahci_port_t *); 992fcbc377Syt 10068d33a25Syt static int ahci_initialize_controller(ahci_ctl_t *); 10168d33a25Syt static void ahci_uninitialize_controller(ahci_ctl_t *); 1028aa6aadbSXiao-Yu Zhang static int ahci_initialize_port(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *); 10313bcbb7aSyt static int ahci_config_space_init(ahci_ctl_t *); 10468d33a25Syt 105f8a673adSying tian - Beijing China static void ahci_drain_ports_taskq(ahci_ctl_t *); 1068aa6aadbSXiao-Yu Zhang static int ahci_rdwr_pmult(ahci_ctl_t *, ahci_addr_t *, uint8_t, uint32_t *, 1078aa6aadbSXiao-Yu Zhang uint8_t); 1088aa6aadbSXiao-Yu Zhang static int ahci_read_pmult(ahci_ctl_t *, ahci_addr_t *, uint8_t, uint32_t *); 1098aa6aadbSXiao-Yu Zhang static int ahci_write_pmult(ahci_ctl_t *, ahci_addr_t *, uint8_t, uint32_t); 1108aa6aadbSXiao-Yu Zhang static int ahci_update_pmult_pscr(ahci_ctl_t *, ahci_addr_t *, 1118aa6aadbSXiao-Yu Zhang sata_device_t *); 1128aa6aadbSXiao-Yu Zhang static int ahci_update_pmult_gscr(ahci_ctl_t *, ahci_addr_t *, 113918304a3SXiao-Yu Zhang sata_pmult_gscr_t *); 1148aa6aadbSXiao-Yu Zhang static int ahci_initialize_pmult(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *, 1158aa6aadbSXiao-Yu Zhang sata_device_t *); 1168aa6aadbSXiao-Yu Zhang static int ahci_initialize_pmport(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *); 1178aa6aadbSXiao-Yu Zhang static int ahci_probe_pmult(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *); 1188aa6aadbSXiao-Yu Zhang static int ahci_probe_pmport(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *, 1198aa6aadbSXiao-Yu Zhang sata_device_t *); 1208aa6aadbSXiao-Yu Zhang 12113bcbb7aSyt static void ahci_disable_interface_pm(ahci_ctl_t *, uint8_t); 12268d33a25Syt static int ahci_start_port(ahci_ctl_t *, ahci_port_t *, uint8_t); 1238aa6aadbSXiao-Yu Zhang static void ahci_find_dev_signature(ahci_ctl_t *, ahci_port_t *, 1248aa6aadbSXiao-Yu Zhang ahci_addr_t *); 1252fcbc377Syt static void ahci_update_sata_registers(ahci_ctl_t *, uint8_t, sata_device_t *); 1262fcbc377Syt static int ahci_deliver_satapkt(ahci_ctl_t *, ahci_port_t *, 1278aa6aadbSXiao-Yu Zhang ahci_addr_t *, sata_pkt_t *); 12868d33a25Syt static int ahci_do_sync_start(ahci_ctl_t *, ahci_port_t *, 1298aa6aadbSXiao-Yu Zhang ahci_addr_t *, sata_pkt_t *); 1308aa6aadbSXiao-Yu Zhang static int ahci_claim_free_slot(ahci_ctl_t *, ahci_port_t *, 1318aa6aadbSXiao-Yu Zhang ahci_addr_t *, int); 13268d33a25Syt static void ahci_copy_err_cnxt(sata_cmd_t *, ahci_fis_d2h_register_t *); 13382263d52Syt static void ahci_copy_ncq_err_page(sata_cmd_t *, 13482263d52Syt struct sata_ncq_error_recovery_page *); 1352fcbc377Syt static void ahci_copy_out_regs(sata_cmd_t *, ahci_fis_d2h_register_t *); 1362fcbc377Syt 1378aa6aadbSXiao-Yu Zhang static int ahci_software_reset(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *); 1382fcbc377Syt static int ahci_hba_reset(ahci_ctl_t *); 1398aa6aadbSXiao-Yu Zhang static int ahci_port_reset(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *); 1408aa6aadbSXiao-Yu Zhang static int ahci_pmport_reset(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *); 1412fcbc377Syt static void ahci_reject_all_abort_pkts(ahci_ctl_t *, ahci_port_t *, uint8_t); 1428aa6aadbSXiao-Yu Zhang static int ahci_reset_device_reject_pkts(ahci_ctl_t *, ahci_port_t *, 1438aa6aadbSXiao-Yu Zhang ahci_addr_t *); 1448aa6aadbSXiao-Yu Zhang static int ahci_reset_pmdevice_reject_pkts(ahci_ctl_t *, ahci_port_t *, 1458aa6aadbSXiao-Yu Zhang ahci_addr_t *); 1468aa6aadbSXiao-Yu Zhang static int ahci_reset_port_reject_pkts(ahci_ctl_t *, ahci_port_t *, 1478aa6aadbSXiao-Yu Zhang ahci_addr_t *); 1482fcbc377Syt static int ahci_reset_hba_reject_pkts(ahci_ctl_t *); 14968d33a25Syt static int ahci_put_port_into_notrunning_state(ahci_ctl_t *, ahci_port_t *, 1502fcbc377Syt uint8_t); 1512fcbc377Syt static int ahci_restart_port_wait_till_ready(ahci_ctl_t *, ahci_port_t *, 15268d33a25Syt uint8_t, int, int *); 153689d74b0Syt static void ahci_mop_commands(ahci_ctl_t *, ahci_port_t *, uint32_t, 154689d74b0Syt uint32_t, uint32_t, uint32_t, uint32_t); 15582263d52Syt static uint32_t ahci_get_rdlogext_data(ahci_ctl_t *, ahci_port_t *, uint8_t); 15668d33a25Syt static void ahci_get_rqsense_data(ahci_ctl_t *, ahci_port_t *, 15768d33a25Syt uint8_t, sata_pkt_t *); 15868d33a25Syt static void ahci_fatal_error_recovery_handler(ahci_ctl_t *, ahci_port_t *, 1598aa6aadbSXiao-Yu Zhang ahci_addr_t *, uint32_t); 1608aa6aadbSXiao-Yu Zhang static void ahci_pmult_error_recovery_handler(ahci_ctl_t *, ahci_port_t *, 16182263d52Syt uint8_t, uint32_t); 16268d33a25Syt static void ahci_timeout_pkts(ahci_ctl_t *, ahci_port_t *, 16382263d52Syt uint8_t, uint32_t); 16468d33a25Syt static void ahci_events_handler(void *); 1652fcbc377Syt static void ahci_watchdog_handler(ahci_ctl_t *); 1662fcbc377Syt 1672fcbc377Syt static uint_t ahci_intr(caddr_t, caddr_t); 16882263d52Syt static void ahci_port_intr(ahci_ctl_t *, ahci_port_t *, uint8_t); 1692c742e1fSying tian - Beijing China static int ahci_add_intrs(ahci_ctl_t *, int); 1702fcbc377Syt static void ahci_rem_intrs(ahci_ctl_t *); 1712fcbc377Syt static void ahci_enable_all_intrs(ahci_ctl_t *); 1722fcbc377Syt static void ahci_disable_all_intrs(ahci_ctl_t *); 173689d74b0Syt static void ahci_enable_port_intrs(ahci_ctl_t *, uint8_t); 174689d74b0Syt static void ahci_disable_port_intrs(ahci_ctl_t *, uint8_t); 1752fcbc377Syt 176689d74b0Syt static int ahci_intr_cmd_cmplt(ahci_ctl_t *, ahci_port_t *, uint8_t); 17768d33a25Syt static int ahci_intr_set_device_bits(ahci_ctl_t *, ahci_port_t *, uint8_t); 1788aa6aadbSXiao-Yu Zhang static int ahci_intr_ncq_events(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *); 1798aa6aadbSXiao-Yu Zhang static int ahci_intr_pmult_sntf_events(ahci_ctl_t *, ahci_port_t *, uint8_t); 1802fcbc377Syt static int ahci_intr_port_connect_change(ahci_ctl_t *, ahci_port_t *, uint8_t); 1812fcbc377Syt static int ahci_intr_device_mechanical_presence_status(ahci_ctl_t *, 1822fcbc377Syt ahci_port_t *, uint8_t); 1832fcbc377Syt static int ahci_intr_phyrdy_change(ahci_ctl_t *, ahci_port_t *, uint8_t); 18468d33a25Syt static int ahci_intr_non_fatal_error(ahci_ctl_t *, ahci_port_t *, 18568d33a25Syt uint8_t, uint32_t); 18668d33a25Syt static int ahci_intr_fatal_error(ahci_ctl_t *, ahci_port_t *, 18782263d52Syt uint8_t, uint32_t); 1882fcbc377Syt static int ahci_intr_cold_port_detect(ahci_ctl_t *, ahci_port_t *, uint8_t); 1892fcbc377Syt 1908aa6aadbSXiao-Yu Zhang static void ahci_get_ahci_addr(ahci_ctl_t *, sata_device_t *, ahci_addr_t *); 1912fcbc377Syt static int ahci_get_num_implemented_ports(uint32_t); 19268d33a25Syt static void ahci_log_fatal_error_message(ahci_ctl_t *, uint8_t port, 19368d33a25Syt uint32_t); 194a9440e8dSyt static void ahci_log_serror_message(ahci_ctl_t *, uint8_t, uint32_t, int); 195689d74b0Syt #if AHCI_DEBUG 1962fcbc377Syt static void ahci_log(ahci_ctl_t *, uint_t, char *, ...); 197689d74b0Syt #endif 1982fcbc377Syt 1992fcbc377Syt 2002fcbc377Syt /* 2012fcbc377Syt * DMA attributes for the data buffer 2022fcbc377Syt * 2032fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 2042fcbc377Syt * does not support 64-bit addressing 2052fcbc377Syt */ 2062fcbc377Syt static ddi_dma_attr_t buffer_dma_attr = { 2072fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 20868d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 2092fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 2102fcbc377Syt 0x3fffffull, /* dma_attr_count_max i.e. for one cookie */ 21168d33a25Syt 0x2ull, /* dma_attr_align: word aligned */ 2122fcbc377Syt 1, /* dma_attr_burstsizes */ 2132fcbc377Syt 1, /* dma_attr_minxfer */ 2142fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2152fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2162fcbc377Syt AHCI_PRDT_NUMBER, /* dma_attr_sgllen */ 2172fcbc377Syt 512, /* dma_attr_granular */ 2182fcbc377Syt 0, /* dma_attr_flags */ 2192fcbc377Syt }; 2202fcbc377Syt 2212fcbc377Syt /* 2222fcbc377Syt * DMA attributes for the rcvd FIS 2232fcbc377Syt * 2242fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 2252fcbc377Syt * does not support 64-bit addressing 2262fcbc377Syt */ 2272fcbc377Syt static ddi_dma_attr_t rcvd_fis_dma_attr = { 2282fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 22968d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 2302fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 2312fcbc377Syt 0xffffffffull, /* dma_attr_count_max i.e. for one cookie */ 23268d33a25Syt 0x100ull, /* dma_attr_align: 256-byte aligned */ 2332fcbc377Syt 1, /* dma_attr_burstsizes */ 2342fcbc377Syt 1, /* dma_attr_minxfer */ 2352fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2362fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2372fcbc377Syt 1, /* dma_attr_sgllen */ 2382fcbc377Syt 1, /* dma_attr_granular */ 2392fcbc377Syt 0, /* dma_attr_flags */ 2402fcbc377Syt }; 2412fcbc377Syt 2422fcbc377Syt /* 2432fcbc377Syt * DMA attributes for the command list 2442fcbc377Syt * 2452fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 2462fcbc377Syt * does not support 64-bit addressing 2472fcbc377Syt */ 2482fcbc377Syt static ddi_dma_attr_t cmd_list_dma_attr = { 2492fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 25068d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 2512fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 2522fcbc377Syt 0xffffffffull, /* dma_attr_count_max i.e. for one cookie */ 25368d33a25Syt 0x400ull, /* dma_attr_align: 1K-byte aligned */ 2542fcbc377Syt 1, /* dma_attr_burstsizes */ 2552fcbc377Syt 1, /* dma_attr_minxfer */ 2562fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2572fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2582fcbc377Syt 1, /* dma_attr_sgllen */ 2592fcbc377Syt 1, /* dma_attr_granular */ 2602fcbc377Syt 0, /* dma_attr_flags */ 2612fcbc377Syt }; 2622fcbc377Syt 2632fcbc377Syt /* 2642fcbc377Syt * DMA attributes for cmd tables 2652fcbc377Syt * 2662fcbc377Syt * dma_attr_addr_hi will be changed to 0xffffffffull if the HBA 2672fcbc377Syt * does not support 64-bit addressing 2682fcbc377Syt */ 2692fcbc377Syt static ddi_dma_attr_t cmd_table_dma_attr = { 2702fcbc377Syt DMA_ATTR_V0, /* dma_attr_version */ 27168d33a25Syt 0x0ull, /* dma_attr_addr_lo: lowest bus address */ 2722fcbc377Syt 0xffffffffffffffffull, /* dma_attr_addr_hi: highest bus address */ 2732fcbc377Syt 0xffffffffull, /* dma_attr_count_max i.e. for one cookie */ 27468d33a25Syt 0x80ull, /* dma_attr_align: 128-byte aligned */ 2752fcbc377Syt 1, /* dma_attr_burstsizes */ 2762fcbc377Syt 1, /* dma_attr_minxfer */ 2772fcbc377Syt 0xffffffffull, /* dma_attr_maxxfer i.e. includes all cookies */ 2782fcbc377Syt 0xffffffffull, /* dma_attr_seg */ 2792fcbc377Syt 1, /* dma_attr_sgllen */ 2802fcbc377Syt 1, /* dma_attr_granular */ 2812fcbc377Syt 0, /* dma_attr_flags */ 2822fcbc377Syt }; 2832fcbc377Syt 2842fcbc377Syt 2852fcbc377Syt /* Device access attributes */ 2862fcbc377Syt static ddi_device_acc_attr_t accattr = { 2872fcbc377Syt DDI_DEVICE_ATTR_V0, 2882fcbc377Syt DDI_STRUCTURE_LE_ACC, 2892fcbc377Syt DDI_STRICTORDER_ACC 2902fcbc377Syt }; 2912fcbc377Syt 2922fcbc377Syt 2932fcbc377Syt static struct dev_ops ahcictl_dev_ops = { 2942fcbc377Syt DEVO_REV, /* devo_rev */ 2952fcbc377Syt 0, /* refcnt */ 2962fcbc377Syt ahci_getinfo, /* info */ 2972fcbc377Syt nulldev, /* identify */ 2982fcbc377Syt nulldev, /* probe */ 2992fcbc377Syt ahci_attach, /* attach */ 3002fcbc377Syt ahci_detach, /* detach */ 3012fcbc377Syt nodev, /* no reset */ 3022fcbc377Syt (struct cb_ops *)0, /* driver operations */ 3032fcbc377Syt NULL, /* bus operations */ 30419397407SSherry Moore NULL, /* power */ 30519397407SSherry Moore ahci_quiesce, /* quiesce */ 3062fcbc377Syt }; 3072fcbc377Syt 3082fcbc377Syt static sata_tran_hotplug_ops_t ahci_tran_hotplug_ops = { 3092fcbc377Syt SATA_TRAN_HOTPLUG_OPS_REV_1, 3102fcbc377Syt ahci_tran_hotplug_port_activate, 3112fcbc377Syt ahci_tran_hotplug_port_deactivate 3122fcbc377Syt }; 3132fcbc377Syt 3142fcbc377Syt extern struct mod_ops mod_driverops; 3152fcbc377Syt 3162fcbc377Syt static struct modldrv modldrv = { 3172fcbc377Syt &mod_driverops, /* driverops */ 31819397407SSherry Moore ahci_ident, /* short description */ 3192fcbc377Syt &ahcictl_dev_ops, /* driver ops */ 3202fcbc377Syt }; 3212fcbc377Syt 3222fcbc377Syt static struct modlinkage modlinkage = { 3232fcbc377Syt MODREV_1, 3242fcbc377Syt &modldrv, 3252fcbc377Syt NULL 3262fcbc377Syt }; 3272fcbc377Syt 328db2cce03Sying tian - Beijing China /* The following variables are watchdog handler related */ 3292fcbc377Syt static int ahci_watchdog_timeout = 5; /* 5 seconds */ 3302fcbc377Syt static int ahci_watchdog_tick; 3312fcbc377Syt 332db2cce03Sying tian - Beijing China /* 333db2cce03Sying tian - Beijing China * This static variable indicates the size of command table, 334db2cce03Sying tian - Beijing China * and it's changeable with prdt number, which ahci_dma_prdt_number 335db2cce03Sying tian - Beijing China * indicates. 336db2cce03Sying tian - Beijing China */ 3372fcbc377Syt static size_t ahci_cmd_table_size; 3382fcbc377Syt 339db2cce03Sying tian - Beijing China /* 340259105bcSying tian - Beijing China * The below global variables are tunable via /etc/system 341259105bcSying tian - Beijing China * 342259105bcSying tian - Beijing China * ahci_dma_prdt_number 343259105bcSying tian - Beijing China * ahci_msi_enabled 344259105bcSying tian - Beijing China * ahci_buf_64bit_dma 345259105bcSying tian - Beijing China * ahci_commu_64bit_dma 346db2cce03Sying tian - Beijing China */ 347db2cce03Sying tian - Beijing China 3482fcbc377Syt /* The number of Physical Region Descriptor Table(PRDT) in Command Table */ 3492fcbc377Syt int ahci_dma_prdt_number = AHCI_PRDT_NUMBER; 3502fcbc377Syt 351db2cce03Sying tian - Beijing China /* AHCI MSI is tunable */ 3522c742e1fSying tian - Beijing China boolean_t ahci_msi_enabled = B_TRUE; 3532fcbc377Syt 354259105bcSying tian - Beijing China /* 355259105bcSying tian - Beijing China * 64-bit dma addressing for data buffer is tunable 356259105bcSying tian - Beijing China * 357259105bcSying tian - Beijing China * The variable controls only the below value: 358259105bcSying tian - Beijing China * DBAU (upper 32-bits physical address of data block) 359259105bcSying tian - Beijing China */ 360259105bcSying tian - Beijing China boolean_t ahci_buf_64bit_dma = B_TRUE; 361259105bcSying tian - Beijing China 362259105bcSying tian - Beijing China /* 363259105bcSying tian - Beijing China * 64-bit dma addressing for communication system descriptors is tunable 364259105bcSying tian - Beijing China * 365259105bcSying tian - Beijing China * The variable controls the below three values: 366259105bcSying tian - Beijing China * 367259105bcSying tian - Beijing China * PxCLBU (upper 32-bits for the command list base physical address) 368259105bcSying tian - Beijing China * PxFBU (upper 32-bits for the received FIS base physical address) 369259105bcSying tian - Beijing China * CTBAU (upper 32-bits of command table base) 370259105bcSying tian - Beijing China */ 371259105bcSying tian - Beijing China boolean_t ahci_commu_64bit_dma = B_TRUE; 372259105bcSying tian - Beijing China 373*17ac46baSying tian - Beijing China /* 374*17ac46baSying tian - Beijing China * By default, 64-bit dma for data buffer will be disabled for AMD/ATI SB600 375*17ac46baSying tian - Beijing China * chipset. If the users want to have a try with 64-bit dma, please change 376*17ac46baSying tian - Beijing China * the below variable value to enable it. 377*17ac46baSying tian - Beijing China */ 378*17ac46baSying tian - Beijing China boolean_t sb600_buf_64bit_dma_disable = B_TRUE; 379*17ac46baSying tian - Beijing China 380*17ac46baSying tian - Beijing China /* 381*17ac46baSying tian - Beijing China * By default, 64-bit dma for command buffer will be disabled for AMD/ATI 382*17ac46baSying tian - Beijing China * SB600/700/710/750/800. If the users want to have a try with 64-bit dma, 383*17ac46baSying tian - Beijing China * please change the below value to enable it. 384*17ac46baSying tian - Beijing China */ 385*17ac46baSying tian - Beijing China boolean_t sbxxx_commu_64bit_dma_disable = B_TRUE; 386*17ac46baSying tian - Beijing China 387*17ac46baSying tian - Beijing China 388259105bcSying tian - Beijing China /* 389259105bcSying tian - Beijing China * End of global tunable variable definition 390259105bcSying tian - Beijing China */ 391db2cce03Sying tian - Beijing China 3921fdcc913SFred Herard #if AHCI_DEBUG 3931fdcc913SFred Herard uint32_t ahci_debug_flags = 0; 3941fdcc913SFred Herard #else 395f5f2d263SFred Herard uint32_t ahci_debug_flags = (AHCIDBG_ERRS|AHCIDBG_TIMEOUT); 3961fdcc913SFred Herard #endif 3971fdcc913SFred Herard 398689d74b0Syt 399f5f2d263SFred Herard #if AHCI_DEBUG 400689d74b0Syt /* The following is needed for ahci_log() */ 401689d74b0Syt static kmutex_t ahci_log_mutex; 402689d74b0Syt static char ahci_log_buf[512]; 4032fcbc377Syt #endif 4042fcbc377Syt 4052fcbc377Syt /* Opaque state pointer initialized by ddi_soft_state_init() */ 4062fcbc377Syt static void *ahci_statep = NULL; 4072fcbc377Syt 4082fcbc377Syt /* 4092fcbc377Syt * ahci module initialization. 4102fcbc377Syt */ 4112fcbc377Syt int 4122fcbc377Syt _init(void) 4132fcbc377Syt { 4142fcbc377Syt int ret; 4152fcbc377Syt 4162fcbc377Syt ret = ddi_soft_state_init(&ahci_statep, sizeof (ahci_ctl_t), 0); 4172fcbc377Syt if (ret != 0) { 4182fcbc377Syt goto err_out; 4192fcbc377Syt } 4202fcbc377Syt 421689d74b0Syt #if AHCI_DEBUG 4222fcbc377Syt mutex_init(&ahci_log_mutex, NULL, MUTEX_DRIVER, NULL); 423689d74b0Syt #endif 4242fcbc377Syt 4252fcbc377Syt if ((ret = sata_hba_init(&modlinkage)) != 0) { 426689d74b0Syt #if AHCI_DEBUG 4272fcbc377Syt mutex_destroy(&ahci_log_mutex); 428689d74b0Syt #endif 4292fcbc377Syt ddi_soft_state_fini(&ahci_statep); 4302fcbc377Syt goto err_out; 4312fcbc377Syt } 4322fcbc377Syt 4332fcbc377Syt ret = mod_install(&modlinkage); 4342fcbc377Syt if (ret != 0) { 4352fcbc377Syt sata_hba_fini(&modlinkage); 436689d74b0Syt #if AHCI_DEBUG 4372fcbc377Syt mutex_destroy(&ahci_log_mutex); 438689d74b0Syt #endif 4392fcbc377Syt ddi_soft_state_fini(&ahci_statep); 4402fcbc377Syt goto err_out; 4412fcbc377Syt } 4422fcbc377Syt 4432fcbc377Syt /* watchdog tick */ 4442fcbc377Syt ahci_watchdog_tick = drv_usectohz( 4452fcbc377Syt (clock_t)ahci_watchdog_timeout * 1000000); 4462fcbc377Syt return (ret); 4472fcbc377Syt 4482fcbc377Syt err_out: 449a9440e8dSyt cmn_err(CE_WARN, "!ahci: Module init failed"); 4502fcbc377Syt return (ret); 4512fcbc377Syt } 4522fcbc377Syt 4532fcbc377Syt /* 4542fcbc377Syt * ahci module uninitialize. 4552fcbc377Syt */ 4562fcbc377Syt int 4572fcbc377Syt _fini(void) 4582fcbc377Syt { 4592fcbc377Syt int ret; 4602fcbc377Syt 4612fcbc377Syt ret = mod_remove(&modlinkage); 4622fcbc377Syt if (ret != 0) { 4632fcbc377Syt return (ret); 4642fcbc377Syt } 4652fcbc377Syt 4662fcbc377Syt /* Remove the resources allocated in _init(). */ 4672fcbc377Syt sata_hba_fini(&modlinkage); 468689d74b0Syt #if AHCI_DEBUG 4692fcbc377Syt mutex_destroy(&ahci_log_mutex); 470689d74b0Syt #endif 4712fcbc377Syt ddi_soft_state_fini(&ahci_statep); 4722fcbc377Syt 4732fcbc377Syt return (ret); 4742fcbc377Syt } 4752fcbc377Syt 4762fcbc377Syt /* 4772fcbc377Syt * _info entry point 4782fcbc377Syt */ 4792fcbc377Syt int 4802fcbc377Syt _info(struct modinfo *modinfop) 4812fcbc377Syt { 4822fcbc377Syt return (mod_info(&modlinkage, modinfop)); 4832fcbc377Syt } 4842fcbc377Syt 4852fcbc377Syt /* 4862fcbc377Syt * The attach entry point for dev_ops. 4872fcbc377Syt */ 4882fcbc377Syt static int 4892fcbc377Syt ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4902fcbc377Syt { 491f5f2d263SFred Herard ahci_ctl_t *ahci_ctlp = NULL; 4922fcbc377Syt int instance = ddi_get_instance(dip); 4932fcbc377Syt int status; 4942fcbc377Syt int attach_state; 4952fcbc377Syt uint32_t cap_status, ahci_version; 4962fcbc377Syt int intr_types; 49768d33a25Syt int i; 49895c11c1fSyt pci_regspec_t *regs; 49995c11c1fSyt int regs_length; 50095c11c1fSyt int rnumber; 50168d33a25Syt #if AHCI_DEBUG 50268d33a25Syt int speed; 50368d33a25Syt #endif 5042fcbc377Syt 505f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, "ahci_attach enter", 506f5f2d263SFred Herard NULL); 5072fcbc377Syt 5082fcbc377Syt switch (cmd) { 5092fcbc377Syt case DDI_ATTACH: 5102fcbc377Syt break; 5112fcbc377Syt 5122fcbc377Syt case DDI_RESUME: 51313bcbb7aSyt 51413bcbb7aSyt /* 51513bcbb7aSyt * During DDI_RESUME, the hardware state of the device 51613bcbb7aSyt * (power may have been removed from the device) must be 51713bcbb7aSyt * restored, allow pending requests to continue, and 51813bcbb7aSyt * service new requests. 51913bcbb7aSyt */ 52013bcbb7aSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 52113bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 52213bcbb7aSyt 52313bcbb7aSyt /* Restart watch thread */ 52413bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id == 0) 52513bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = timeout( 52613bcbb7aSyt (void (*)(void *))ahci_watchdog_handler, 52713bcbb7aSyt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 52813bcbb7aSyt 52913bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 53013bcbb7aSyt 53113bcbb7aSyt /* 53213bcbb7aSyt * Re-initialize the controller and enable the interrupts and 53313bcbb7aSyt * restart all the ports. 53413bcbb7aSyt * 53513bcbb7aSyt * Note that so far we don't support hot-plug during 53613bcbb7aSyt * suspend/resume. 53713bcbb7aSyt */ 53813bcbb7aSyt if (ahci_initialize_controller(ahci_ctlp) != AHCI_SUCCESS) { 539f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PM, ahci_ctlp, 540a9440e8dSyt "Failed to initialize the controller " 541f5f2d263SFred Herard "during DDI_RESUME", NULL); 54213bcbb7aSyt return (DDI_FAILURE); 54313bcbb7aSyt } 54413bcbb7aSyt 54513bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 54613bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~ AHCI_SUSPEND; 54713bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 54813bcbb7aSyt 54913bcbb7aSyt return (DDI_SUCCESS); 5502fcbc377Syt 5512fcbc377Syt default: 5522fcbc377Syt return (DDI_FAILURE); 5532fcbc377Syt } 5542fcbc377Syt 5552fcbc377Syt attach_state = AHCI_ATTACH_STATE_NONE; 5562fcbc377Syt 5572fcbc377Syt /* Allocate soft state */ 5582fcbc377Syt status = ddi_soft_state_zalloc(ahci_statep, instance); 5592fcbc377Syt if (status != DDI_SUCCESS) { 560a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate soft state", 561a9440e8dSyt instance); 5622fcbc377Syt goto err_out; 5632fcbc377Syt } 5642fcbc377Syt 5652fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 56613bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_ATTACH; 5672fcbc377Syt ahci_ctlp->ahcictl_dip = dip; 5682fcbc377Syt 56968d33a25Syt /* Initialize the cport/port mapping */ 57068d33a25Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 57168d33a25Syt ahci_ctlp->ahcictl_port_to_cport[i] = 0xff; 57268d33a25Syt ahci_ctlp->ahcictl_cport_to_port[i] = 0xff; 57368d33a25Syt } 57468d33a25Syt 5752fcbc377Syt attach_state |= AHCI_ATTACH_STATE_STATEP_ALLOC; 5762fcbc377Syt 5772fcbc377Syt /* 5782fcbc377Syt * Now map the AHCI base address; which includes global 5792fcbc377Syt * registers and port control registers 58095c11c1fSyt * 58195c11c1fSyt * According to the spec, the AHCI Base Address is BAR5, 58213bcbb7aSyt * but BAR0-BAR4 are optional, so we need to check which 58313bcbb7aSyt * rnumber is used for BAR5. 58495c11c1fSyt */ 58595c11c1fSyt 58695c11c1fSyt /* 58795c11c1fSyt * search through DDI "reg" property for the AHCI register set 5882fcbc377Syt */ 58995c11c1fSyt if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 59095c11c1fSyt DDI_PROP_DONTPASS, "reg", (int **)®s, 59195c11c1fSyt (uint_t *)®s_length) != DDI_PROP_SUCCESS) { 592a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot lookup reg property", 593a9440e8dSyt instance); 59495c11c1fSyt goto err_out; 59595c11c1fSyt } 59695c11c1fSyt 59795c11c1fSyt /* AHCI Base Address is located at 0x24 offset */ 59895c11c1fSyt for (rnumber = 0; rnumber < regs_length; ++rnumber) { 59995c11c1fSyt if ((regs[rnumber].pci_phys_hi & PCI_REG_REG_M) 60095c11c1fSyt == AHCI_PCI_RNUM) 60195c11c1fSyt break; 60295c11c1fSyt } 60395c11c1fSyt 60495c11c1fSyt ddi_prop_free(regs); 60595c11c1fSyt 60695c11c1fSyt if (rnumber == regs_length) { 607a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot find AHCI register set", 608a9440e8dSyt instance); 60995c11c1fSyt goto err_out; 61095c11c1fSyt } 61195c11c1fSyt 612f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "rnumber = %d", rnumber); 61395c11c1fSyt 6142fcbc377Syt status = ddi_regs_map_setup(dip, 61595c11c1fSyt rnumber, 6162fcbc377Syt (caddr_t *)&ahci_ctlp->ahcictl_ahci_addr, 6172fcbc377Syt 0, 6182fcbc377Syt 0, 6192fcbc377Syt &accattr, 6202fcbc377Syt &ahci_ctlp->ahcictl_ahci_acc_handle); 6212fcbc377Syt if (status != DDI_SUCCESS) { 622a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot map register space", 623a9440e8dSyt instance); 6242fcbc377Syt goto err_out; 6252fcbc377Syt } 6262fcbc377Syt 6272fcbc377Syt attach_state |= AHCI_ATTACH_STATE_REG_MAP; 6282fcbc377Syt 6292fcbc377Syt /* Get the AHCI version information */ 6302fcbc377Syt ahci_version = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 6312fcbc377Syt (uint32_t *)AHCI_GLOBAL_VS(ahci_ctlp)); 6322fcbc377Syt 633a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: hba AHCI version = %x.%x", instance, 6342fcbc377Syt (ahci_version & 0xffff0000) >> 16, 6352fcbc377Syt ((ahci_version & 0x0000ff00) >> 4 | 6362fcbc377Syt (ahci_version & 0x000000ff))); 6372fcbc377Syt 6382fcbc377Syt /* We don't support controllers whose versions are lower than 1.0 */ 6392fcbc377Syt if (!(ahci_version & 0xffff0000)) { 640a9440e8dSyt cmn_err(CE_WARN, "ahci%d: Don't support AHCI HBA with lower " 641a9440e8dSyt "than version 1.0", instance); 6422fcbc377Syt goto err_out; 6432fcbc377Syt } 6442fcbc377Syt 6452fcbc377Syt /* Get the HBA capabilities information */ 6462fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 6472fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 6482fcbc377Syt 649f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba capabilites = 0x%x", 6502fcbc377Syt cap_status); 6512fcbc377Syt 65268d33a25Syt #if AHCI_DEBUG 65368d33a25Syt /* Get the interface speed supported by the HBA */ 65468d33a25Syt speed = (cap_status & AHCI_HBA_CAP_ISS) >> AHCI_HBA_CAP_ISS_SHIFT; 65568d33a25Syt if (speed == 0x01) { 656f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 657f5f2d263SFred Herard "hba interface speed support: Gen 1 (1.5Gbps)", NULL); 65868d33a25Syt } else if (speed == 0x10) { 659f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 660f5f2d263SFred Herard "hba interface speed support: Gen 2 (3 Gbps)", NULL); 66168d33a25Syt } else if (speed == 0x11) { 662f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 663f5f2d263SFred Herard "hba interface speed support: Gen 3 (6 Gbps)", NULL); 66468d33a25Syt } 66568d33a25Syt #endif 66668d33a25Syt 6672fcbc377Syt /* Get the number of command slots supported by the HBA */ 6682fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots = 6692fcbc377Syt ((cap_status & AHCI_HBA_CAP_NCS) >> 6702fcbc377Syt AHCI_HBA_CAP_NCS_SHIFT) + 1; 6712fcbc377Syt 672f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba number of cmd slots: %d", 6732fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots); 6742fcbc377Syt 6752fcbc377Syt /* Get the bit map which indicates ports implemented by the HBA */ 6762fcbc377Syt ahci_ctlp->ahcictl_ports_implemented = 6772fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 6782fcbc377Syt (uint32_t *)AHCI_GLOBAL_PI(ahci_ctlp)); 6792fcbc377Syt 680f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba implementation of ports: 0x%x", 6812fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 6822fcbc377Syt 68309121340Syt /* 68409121340Syt * According to the AHCI spec, CAP.NP should indicate the maximum 68509121340Syt * number of ports supported by the HBA silicon, but we found 68609121340Syt * this value of ICH8 chipset only indicates the number of ports 68709121340Syt * implemented (exposed) by it. Therefore, the driver should calculate 68809121340Syt * the potential maximum value by checking PI register, and use 68909121340Syt * the maximum of this value and CAP.NP. 69009121340Syt */ 69109121340Syt ahci_ctlp->ahcictl_num_ports = max( 69209121340Syt (cap_status & AHCI_HBA_CAP_NP) + 1, 69309121340Syt ddi_fls(ahci_ctlp->ahcictl_ports_implemented)); 69409121340Syt 695f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba number of ports: %d", 69609121340Syt ahci_ctlp->ahcictl_num_ports); 69709121340Syt 6982fcbc377Syt /* Get the number of implemented ports by the HBA */ 6992fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports = 7002fcbc377Syt ahci_get_num_implemented_ports( 7012fcbc377Syt ahci_ctlp->ahcictl_ports_implemented); 7022fcbc377Syt 703f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 70468d33a25Syt "hba number of implemented ports: %d", 7052fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports); 7062fcbc377Syt 707a9440e8dSyt /* Check whether HBA supports 64bit DMA addressing */ 7082fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_S64A)) { 709259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_BUF_32BIT_DMA; 710259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_COMMU_32BIT_DMA; 711f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 712f5f2d263SFred Herard "hba does not support 64-bit addressing", NULL); 7132fcbc377Syt } 7142fcbc377Syt 7158aa6aadbSXiao-Yu Zhang /* Checking for the support of Port Multiplier */ 7168aa6aadbSXiao-Yu Zhang if (cap_status & AHCI_HBA_CAP_SPM) { 7178aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_cap |= AHCI_CAP_PMULT_CBSS; 7188aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_PMULT, ahci_ctlp, 7198aa6aadbSXiao-Yu Zhang "hba supports port multiplier (CBSS)", NULL); 7208aa6aadbSXiao-Yu Zhang 7218aa6aadbSXiao-Yu Zhang /* Support FIS-based switching ? */ 7228aa6aadbSXiao-Yu Zhang if (cap_status & AHCI_HBA_CAP_FBSS) { 7238aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_cap |= AHCI_CAP_PMULT_FBSS; 7248aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_PMULT, ahci_ctlp, 7258aa6aadbSXiao-Yu Zhang "hba supports FIS-based switching (FBSS)", NULL); 7268aa6aadbSXiao-Yu Zhang } 7278aa6aadbSXiao-Yu Zhang } 7288aa6aadbSXiao-Yu Zhang 7290a4c4cecSXiao-Yu Zhang /* Checking for Support Command List Override */ 7300a4c4cecSXiao-Yu Zhang if (cap_status & AHCI_HBA_CAP_SCLO) { 7310a4c4cecSXiao-Yu Zhang ahci_ctlp->ahcictl_cap |= AHCI_CAP_SCLO; 7328aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_PMULT, ahci_ctlp, 733f5f2d263SFred Herard "hba supports command list override.", NULL); 7340a4c4cecSXiao-Yu Zhang } 7350a4c4cecSXiao-Yu Zhang 7368aa6aadbSXiao-Yu Zhang /* Checking for Asynchronous Notification */ 7378aa6aadbSXiao-Yu Zhang if (cap_status & AHCI_HBA_CAP_SSNTF) { 7388aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_cap |= AHCI_CAP_SNTF; 7398aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_PMULT, ahci_ctlp, 7408aa6aadbSXiao-Yu Zhang "hba supports asynchronous notification.", NULL); 7418aa6aadbSXiao-Yu Zhang } 7428aa6aadbSXiao-Yu Zhang 7432fcbc377Syt if (pci_config_setup(dip, &ahci_ctlp->ahcictl_pci_conf_handle) 7442fcbc377Syt != DDI_SUCCESS) { 745a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot set up pci configure space", 746a9440e8dSyt instance); 7472fcbc377Syt goto err_out; 7482fcbc377Syt } 7492fcbc377Syt 75068d33a25Syt attach_state |= AHCI_ATTACH_STATE_PCICFG_SETUP; 75168d33a25Syt 752db2cce03Sying tian - Beijing China /* 753db2cce03Sying tian - Beijing China * Check the pci configuration space, and set caps. We also 754db2cce03Sying tian - Beijing China * handle the hardware defect in this function. 755db2cce03Sying tian - Beijing China * 756259105bcSying tian - Beijing China * For example, force ATI SB600 to use 32-bit dma addressing 757259105bcSying tian - Beijing China * since it doesn't support 64-bit dma though its CAP register 758259105bcSying tian - Beijing China * declares it support. 759db2cce03Sying tian - Beijing China */ 76013bcbb7aSyt if (ahci_config_space_init(ahci_ctlp) == AHCI_FAILURE) { 761a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_config_space_init failed", 762a9440e8dSyt instance); 76313bcbb7aSyt goto err_out; 76468d33a25Syt } 7652fcbc377Syt 7662fcbc377Syt /* 7672fcbc377Syt * Disable the whole controller interrupts before adding 7682fcbc377Syt * interrupt handlers(s). 7692fcbc377Syt */ 7702fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 7712fcbc377Syt 7722fcbc377Syt /* Get supported interrupt types */ 7732fcbc377Syt if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { 774a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ddi_intr_get_supported_types failed", 775a9440e8dSyt instance); 7762fcbc377Syt goto err_out; 7772fcbc377Syt } 7782fcbc377Syt 779f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7802fcbc377Syt "ddi_intr_get_supported_types() returned: 0x%x", 7812fcbc377Syt intr_types); 7822fcbc377Syt 7832fcbc377Syt if (ahci_msi_enabled && (intr_types & DDI_INTR_TYPE_MSI)) { 7842fcbc377Syt /* 7852fcbc377Syt * Try MSI first, but fall back to FIXED if failed 7862fcbc377Syt */ 7872c742e1fSying tian - Beijing China if (ahci_add_intrs(ahci_ctlp, DDI_INTR_TYPE_MSI) == 7882c742e1fSying tian - Beijing China DDI_SUCCESS) { 7892fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_MSI; 790f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 791f5f2d263SFred Herard "Using MSI interrupt type", NULL); 7922fcbc377Syt goto intr_done; 7932fcbc377Syt } 7942fcbc377Syt 795f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 7962fcbc377Syt "MSI registration failed, " 797f5f2d263SFred Herard "trying FIXED interrupts", NULL); 7982fcbc377Syt } 7992fcbc377Syt 8002fcbc377Syt if (intr_types & DDI_INTR_TYPE_FIXED) { 8012c742e1fSying tian - Beijing China if (ahci_add_intrs(ahci_ctlp, DDI_INTR_TYPE_FIXED) == 8022c742e1fSying tian - Beijing China DDI_SUCCESS) { 8032fcbc377Syt ahci_ctlp->ahcictl_intr_type = DDI_INTR_TYPE_FIXED; 804f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 805f5f2d263SFred Herard "Using FIXED interrupt type", NULL); 8062fcbc377Syt goto intr_done; 8072fcbc377Syt } 8082fcbc377Syt 809f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 810f5f2d263SFred Herard "FIXED interrupt registration failed", NULL); 8112fcbc377Syt } 8122fcbc377Syt 813a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Interrupt registration failed", instance); 8142fcbc377Syt 8152fcbc377Syt goto err_out; 8162fcbc377Syt 8172fcbc377Syt intr_done: 8182fcbc377Syt 8192fcbc377Syt attach_state |= AHCI_ATTACH_STATE_INTR_ADDED; 8202fcbc377Syt 8212fcbc377Syt /* Initialize the controller mutex */ 8222fcbc377Syt mutex_init(&ahci_ctlp->ahcictl_mutex, NULL, MUTEX_DRIVER, 8232fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 8242fcbc377Syt 8252fcbc377Syt attach_state |= AHCI_ATTACH_STATE_MUTEX_INIT; 8262fcbc377Syt 8272fcbc377Syt if (ahci_dma_prdt_number < AHCI_MIN_PRDT_NUMBER) { 8282fcbc377Syt ahci_dma_prdt_number = AHCI_MIN_PRDT_NUMBER; 8292fcbc377Syt } else if (ahci_dma_prdt_number > AHCI_MAX_PRDT_NUMBER) { 8302fcbc377Syt ahci_dma_prdt_number = AHCI_MAX_PRDT_NUMBER; 8312fcbc377Syt } 8322fcbc377Syt 8332fcbc377Syt ahci_cmd_table_size = (sizeof (ahci_cmd_table_t) + 8342fcbc377Syt (ahci_dma_prdt_number - AHCI_PRDT_NUMBER) * 8352fcbc377Syt sizeof (ahci_prdt_item_t)); 8362fcbc377Syt 837f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 8382fcbc377Syt "ahci_attach: ahci_dma_prdt_number set by user is 0x%x," 8392fcbc377Syt " ahci_cmd_table_size is 0x%x", 8402fcbc377Syt ahci_dma_prdt_number, ahci_cmd_table_size); 8412fcbc377Syt 8422fcbc377Syt if (ahci_dma_prdt_number != AHCI_PRDT_NUMBER) 8432fcbc377Syt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_sgllen = 8442fcbc377Syt ahci_dma_prdt_number; 8452fcbc377Syt 846a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr = buffer_dma_attr; 847a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr = rcvd_fis_dma_attr; 848a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr = cmd_list_dma_attr; 849a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr = cmd_table_dma_attr; 850a9440e8dSyt 851*17ac46baSying tian - Beijing China /* 852*17ac46baSying tian - Beijing China * enable 64bit dma for data buffer for SB600 if 853*17ac46baSying tian - Beijing China * sb600_buf_64bit_dma_disable is B_FALSE 854*17ac46baSying tian - Beijing China */ 855259105bcSying tian - Beijing China if ((ahci_buf_64bit_dma == B_FALSE) || 856*17ac46baSying tian - Beijing China ((ahci_ctlp->ahcictl_cap & AHCI_CAP_BUF_32BIT_DMA) && 857*17ac46baSying tian - Beijing China !(sb600_buf_64bit_dma_disable == B_FALSE && 858*17ac46baSying tian - Beijing China ahci_ctlp->ahcictl_venid == 0x1002 && 859*17ac46baSying tian - Beijing China ahci_ctlp->ahcictl_devid == 0x4380))) { 860a9440e8dSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_addr_hi = 861a9440e8dSyt 0xffffffffull; 862259105bcSying tian - Beijing China } 863259105bcSying tian - Beijing China 864*17ac46baSying tian - Beijing China /* 865*17ac46baSying tian - Beijing China * enable 64bit dma for command buffer for SB600/700/710/800 866*17ac46baSying tian - Beijing China * if sbxxx_commu_64bit_dma_disable is B_FALSE 867*17ac46baSying tian - Beijing China */ 868259105bcSying tian - Beijing China if ((ahci_commu_64bit_dma == B_FALSE) || 869*17ac46baSying tian - Beijing China ((ahci_ctlp->ahcictl_cap & AHCI_CAP_COMMU_32BIT_DMA) && 870*17ac46baSying tian - Beijing China !(sbxxx_commu_64bit_dma_disable == B_FALSE && 871*17ac46baSying tian - Beijing China ahci_ctlp->ahcictl_venid == 0x1002 && 872*17ac46baSying tian - Beijing China (ahci_ctlp->ahcictl_devid == 0x4380 || 873*17ac46baSying tian - Beijing China ahci_ctlp->ahcictl_devid == 0x4391)))) { 874a9440e8dSyt ahci_ctlp->ahcictl_rcvd_fis_dma_attr.dma_attr_addr_hi = 875a9440e8dSyt 0xffffffffull; 876a9440e8dSyt ahci_ctlp->ahcictl_cmd_list_dma_attr.dma_attr_addr_hi = 877a9440e8dSyt 0xffffffffull; 878a9440e8dSyt ahci_ctlp->ahcictl_cmd_table_dma_attr.dma_attr_addr_hi = 879a9440e8dSyt 0xffffffffull; 880a9440e8dSyt } 881a9440e8dSyt 88268d33a25Syt /* Allocate the ports structure */ 88368d33a25Syt status = ahci_alloc_ports_state(ahci_ctlp); 88468d33a25Syt if (status != AHCI_SUCCESS) { 885a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: Cannot allocate ports structure", 886a9440e8dSyt instance); 88768d33a25Syt goto err_out; 88868d33a25Syt } 88968d33a25Syt 89068d33a25Syt attach_state |= AHCI_ATTACH_STATE_PORT_ALLOC; 89168d33a25Syt 8922fcbc377Syt /* 89368d33a25Syt * Initialize the controller and ports. 8942fcbc377Syt */ 8952fcbc377Syt status = ahci_initialize_controller(ahci_ctlp); 8962fcbc377Syt if (status != AHCI_SUCCESS) { 897a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: HBA initialization failed", 898a9440e8dSyt instance); 8992fcbc377Syt goto err_out; 9002fcbc377Syt } 9012fcbc377Syt 9022fcbc377Syt attach_state |= AHCI_ATTACH_STATE_HW_INIT; 9032fcbc377Syt 9042fcbc377Syt /* Start one thread to check packet timeouts */ 9052fcbc377Syt ahci_ctlp->ahcictl_timeout_id = timeout( 9062fcbc377Syt (void (*)(void *))ahci_watchdog_handler, 9072fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 9082fcbc377Syt 9092fcbc377Syt attach_state |= AHCI_ATTACH_STATE_TIMEOUT_ENABLED; 9102fcbc377Syt 9112fcbc377Syt if (ahci_register_sata_hba_tran(ahci_ctlp, cap_status)) { 912a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: sata hba tran registration failed", 913a9440e8dSyt instance); 9142fcbc377Syt goto err_out; 9152fcbc377Syt } 9162fcbc377Syt 91713bcbb7aSyt ahci_ctlp->ahcictl_flags &= ~AHCI_ATTACH; 91813bcbb7aSyt 919f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "ahci_attach success!", NULL); 9202fcbc377Syt 9212fcbc377Syt return (DDI_SUCCESS); 9222fcbc377Syt 9232fcbc377Syt err_out: 9242fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_TIMEOUT_ENABLED) { 9252fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9262fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 9272fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 9282fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 9292fcbc377Syt } 9302fcbc377Syt 9312fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_HW_INIT) { 93268d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 9332fcbc377Syt } 9342fcbc377Syt 93568d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PORT_ALLOC) { 93668d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 93768d33a25Syt } 93868d33a25Syt 9392fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_MUTEX_INIT) { 9402fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 9412fcbc377Syt } 9422fcbc377Syt 9432fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_INTR_ADDED) { 9442fcbc377Syt ahci_rem_intrs(ahci_ctlp); 9452fcbc377Syt } 9462fcbc377Syt 94768d33a25Syt if (attach_state & AHCI_ATTACH_STATE_PCICFG_SETUP) { 94868d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 94968d33a25Syt } 95068d33a25Syt 9512fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_REG_MAP) { 9522fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 9532fcbc377Syt } 9542fcbc377Syt 9552fcbc377Syt if (attach_state & AHCI_ATTACH_STATE_STATEP_ALLOC) { 9562fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 9572fcbc377Syt } 9582fcbc377Syt 9592fcbc377Syt return (DDI_FAILURE); 9602fcbc377Syt } 9612fcbc377Syt 9622fcbc377Syt /* 9632fcbc377Syt * The detach entry point for dev_ops. 9642fcbc377Syt */ 9652fcbc377Syt static int 9662fcbc377Syt ahci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 9672fcbc377Syt { 9682fcbc377Syt ahci_ctl_t *ahci_ctlp; 9692fcbc377Syt int instance; 9702fcbc377Syt int ret; 9712fcbc377Syt 9722fcbc377Syt instance = ddi_get_instance(dip); 9732fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 9742fcbc377Syt 975f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_detach enter", NULL); 9762fcbc377Syt 9772fcbc377Syt switch (cmd) { 9782fcbc377Syt case DDI_DETACH: 97913bcbb7aSyt 9802fcbc377Syt /* disable the interrupts for an uninterrupted detach */ 9812fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9822fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 9832fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 9842fcbc377Syt 9852fcbc377Syt /* unregister from the sata framework. */ 9862fcbc377Syt ret = ahci_unregister_sata_hba_tran(ahci_ctlp); 9872fcbc377Syt if (ret != AHCI_SUCCESS) { 9882fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9892fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 9902fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 9912fcbc377Syt return (DDI_FAILURE); 9922fcbc377Syt } 9932fcbc377Syt 9942fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 9952fcbc377Syt 9962fcbc377Syt /* stop the watchdog handler */ 9972fcbc377Syt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 9982fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 0; 9992fcbc377Syt 100068d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 100168d33a25Syt 100268d33a25Syt /* uninitialize the controller */ 100368d33a25Syt ahci_uninitialize_controller(ahci_ctlp); 10042fcbc377Syt 100568d33a25Syt /* remove the interrupts */ 100668d33a25Syt ahci_rem_intrs(ahci_ctlp); 10072fcbc377Syt 100868d33a25Syt /* deallocate the ports structures */ 100968d33a25Syt ahci_dealloc_ports_state(ahci_ctlp); 101068d33a25Syt 101168d33a25Syt /* destroy mutex */ 10122fcbc377Syt mutex_destroy(&ahci_ctlp->ahcictl_mutex); 10132fcbc377Syt 101468d33a25Syt /* teardown the pci config */ 101568d33a25Syt pci_config_teardown(&ahci_ctlp->ahcictl_pci_conf_handle); 10162fcbc377Syt 10172fcbc377Syt /* remove the reg maps. */ 10182fcbc377Syt ddi_regs_map_free(&ahci_ctlp->ahcictl_ahci_acc_handle); 10192fcbc377Syt 10202fcbc377Syt /* free the soft state. */ 10212fcbc377Syt ddi_soft_state_free(ahci_statep, instance); 10222fcbc377Syt 10232fcbc377Syt return (DDI_SUCCESS); 10242fcbc377Syt 10252fcbc377Syt case DDI_SUSPEND: 102613bcbb7aSyt 102713bcbb7aSyt /* 102813bcbb7aSyt * The steps associated with suspension must include putting 102913bcbb7aSyt * the underlying device into a quiescent state so that it 103013bcbb7aSyt * will not generate interrupts or modify or access memory. 103113bcbb7aSyt */ 103213bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 103313bcbb7aSyt if (ahci_ctlp->ahcictl_flags & AHCI_SUSPEND) { 103413bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 103513bcbb7aSyt return (DDI_SUCCESS); 103613bcbb7aSyt } 103713bcbb7aSyt 103813bcbb7aSyt ahci_ctlp->ahcictl_flags |= AHCI_SUSPEND; 103913bcbb7aSyt 104013bcbb7aSyt /* stop the watchdog handler */ 104113bcbb7aSyt if (ahci_ctlp->ahcictl_timeout_id) { 104213bcbb7aSyt (void) untimeout(ahci_ctlp->ahcictl_timeout_id); 104313bcbb7aSyt ahci_ctlp->ahcictl_timeout_id = 0; 104413bcbb7aSyt } 104513bcbb7aSyt 104613bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 104713bcbb7aSyt 104813bcbb7aSyt /* 104913bcbb7aSyt * drain the taskq 105013bcbb7aSyt */ 1051f8a673adSying tian - Beijing China ahci_drain_ports_taskq(ahci_ctlp); 105213bcbb7aSyt 105313bcbb7aSyt /* 105413bcbb7aSyt * Disable the interrupts and stop all the ports. 105513bcbb7aSyt */ 105613bcbb7aSyt ahci_uninitialize_controller(ahci_ctlp); 105713bcbb7aSyt 105813bcbb7aSyt return (DDI_SUCCESS); 10592fcbc377Syt 10602fcbc377Syt default: 10612fcbc377Syt return (DDI_FAILURE); 10622fcbc377Syt } 10632fcbc377Syt } 10642fcbc377Syt 10652fcbc377Syt /* 10662fcbc377Syt * The info entry point for dev_ops. 10672fcbc377Syt * 10682fcbc377Syt */ 10692fcbc377Syt static int 10702fcbc377Syt ahci_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 10712fcbc377Syt void *arg, void **result) 10722fcbc377Syt { 10732fcbc377Syt #ifndef __lock_lint 10742fcbc377Syt _NOTE(ARGUNUSED(dip)) 10752fcbc377Syt #endif /* __lock_lint */ 10762fcbc377Syt 10772fcbc377Syt ahci_ctl_t *ahci_ctlp; 10782fcbc377Syt int instance; 10792fcbc377Syt dev_t dev; 10802fcbc377Syt 10812fcbc377Syt dev = (dev_t)arg; 10822fcbc377Syt instance = getminor(dev); 10832fcbc377Syt 10842fcbc377Syt switch (infocmd) { 10852fcbc377Syt case DDI_INFO_DEVT2DEVINFO: 10862fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 10872fcbc377Syt if (ahci_ctlp != NULL) { 10882fcbc377Syt *result = ahci_ctlp->ahcictl_dip; 10892fcbc377Syt return (DDI_SUCCESS); 10902fcbc377Syt } else { 10912fcbc377Syt *result = NULL; 10922fcbc377Syt return (DDI_FAILURE); 10932fcbc377Syt } 10942fcbc377Syt case DDI_INFO_DEVT2INSTANCE: 10952fcbc377Syt *(int *)result = instance; 10962fcbc377Syt break; 10972fcbc377Syt default: 10982fcbc377Syt break; 10992fcbc377Syt } 11002fcbc377Syt 11012fcbc377Syt return (DDI_SUCCESS); 11022fcbc377Syt } 11032fcbc377Syt 11042fcbc377Syt /* 11052fcbc377Syt * Registers the ahci with sata framework. 11062fcbc377Syt */ 11072fcbc377Syt static int 11082fcbc377Syt ahci_register_sata_hba_tran(ahci_ctl_t *ahci_ctlp, uint32_t cap_status) 11092fcbc377Syt { 11102fcbc377Syt struct sata_hba_tran *sata_hba_tran; 11112fcbc377Syt 1112f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 1113f5f2d263SFred Herard "ahci_register_sata_hba_tran enter", NULL); 11142fcbc377Syt 11152fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 11162fcbc377Syt 11172fcbc377Syt /* Allocate memory for the sata_hba_tran */ 11182fcbc377Syt sata_hba_tran = kmem_zalloc(sizeof (sata_hba_tran_t), KM_SLEEP); 11192fcbc377Syt 1120918304a3SXiao-Yu Zhang sata_hba_tran->sata_tran_hba_rev = SATA_TRAN_HBA_REV; 11212fcbc377Syt sata_hba_tran->sata_tran_hba_dip = ahci_ctlp->ahcictl_dip; 11222fcbc377Syt sata_hba_tran->sata_tran_hba_dma_attr = 11232fcbc377Syt &ahci_ctlp->ahcictl_buffer_dma_attr; 11242fcbc377Syt 11252fcbc377Syt /* Report the number of implemented ports */ 11262fcbc377Syt sata_hba_tran->sata_tran_hba_num_cports = 11272fcbc377Syt ahci_ctlp->ahcictl_num_implemented_ports; 11282fcbc377Syt 112968d33a25Syt /* Support ATAPI device */ 113068d33a25Syt sata_hba_tran->sata_tran_hba_features_support = SATA_CTLF_ATAPI; 11312fcbc377Syt 11322fcbc377Syt /* Get the data transfer capability for PIO command by the HBA */ 11332fcbc377Syt if (cap_status & AHCI_HBA_CAP_PMD) { 113482263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PIO_MDRQ; 1135f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "HBA supports multiple " 1136f5f2d263SFred Herard "DRQ block data transfer for PIO command protocol", NULL); 11372fcbc377Syt } 11382fcbc377Syt 113982263d52Syt /* 114082263d52Syt * According to the AHCI spec, the ATA/ATAPI-7 queued feature set 114182263d52Syt * is not supported by AHCI (including the READ QUEUED (EXT), WRITE 114282263d52Syt * QUEUED (EXT), and SERVICE commands). Queued operations are 114382263d52Syt * supported in AHCI using the READ FPDMA QUEUED and WRITE FPDMA 114482263d52Syt * QUEUED commands when the HBA and device support native command 114582263d52Syt * queuing(NCQ). 114682263d52Syt * 114782263d52Syt * SATA_CTLF_NCQ will be set to sata_tran_hba_features_support if the 114882263d52Syt * CAP register of the HBA indicates NCQ is supported. 114982263d52Syt * 115082263d52Syt * SATA_CTLF_NCQ cannot be set if AHCI_CAP_NO_MCMDLIST_NONQUEUE is 115182263d52Syt * set because the previous register content of PxCI can be re-written 115282263d52Syt * in the register write. 115382263d52Syt */ 115482263d52Syt if ((cap_status & AHCI_HBA_CAP_SNCQ) && 115582263d52Syt !(ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE)) { 115682263d52Syt sata_hba_tran->sata_tran_hba_features_support |= SATA_CTLF_NCQ; 115782263d52Syt ahci_ctlp->ahcictl_cap |= AHCI_CAP_NCQ; 1158f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "HBA supports Native " 1159f5f2d263SFred Herard "Command Queuing", NULL); 116082263d52Syt } 116182263d52Syt 11628aa6aadbSXiao-Yu Zhang /* Support port multiplier? */ 11638aa6aadbSXiao-Yu Zhang if (cap_status & AHCI_HBA_CAP_SPM) { 11648aa6aadbSXiao-Yu Zhang sata_hba_tran->sata_tran_hba_features_support |= 11658aa6aadbSXiao-Yu Zhang SATA_CTLF_PORT_MULTIPLIER; 11668aa6aadbSXiao-Yu Zhang 11678aa6aadbSXiao-Yu Zhang /* Support FIS-based switching for port multiplier? */ 11688aa6aadbSXiao-Yu Zhang if (cap_status & AHCI_HBA_CAP_FBSS) { 11698aa6aadbSXiao-Yu Zhang sata_hba_tran->sata_tran_hba_features_support |= 11708aa6aadbSXiao-Yu Zhang SATA_CTLF_PMULT_FBS; 11718aa6aadbSXiao-Yu Zhang } 11728aa6aadbSXiao-Yu Zhang } 11738aa6aadbSXiao-Yu Zhang 11742fcbc377Syt /* Report the number of command slots */ 11752fcbc377Syt sata_hba_tran->sata_tran_hba_qdepth = ahci_ctlp->ahcictl_num_cmd_slots; 11762fcbc377Syt 11772fcbc377Syt sata_hba_tran->sata_tran_probe_port = ahci_tran_probe_port; 11782fcbc377Syt sata_hba_tran->sata_tran_start = ahci_tran_start; 11792fcbc377Syt sata_hba_tran->sata_tran_abort = ahci_tran_abort; 11802fcbc377Syt sata_hba_tran->sata_tran_reset_dport = ahci_tran_reset_dport; 11812fcbc377Syt sata_hba_tran->sata_tran_hotplug_ops = &ahci_tran_hotplug_ops; 11822fcbc377Syt #ifdef __lock_lint 11832fcbc377Syt sata_hba_tran->sata_tran_selftest = ahci_selftest; 11842fcbc377Syt #endif 11852fcbc377Syt /* 11862fcbc377Syt * When SATA framework adds support for pwrmgt the 11872fcbc377Syt * pwrmgt_ops needs to be updated 11882fcbc377Syt */ 11892fcbc377Syt sata_hba_tran->sata_tran_pwrmgt_ops = NULL; 11902fcbc377Syt sata_hba_tran->sata_tran_ioctl = NULL; 11912fcbc377Syt 11922fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = sata_hba_tran; 11932fcbc377Syt 11942fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 11952fcbc377Syt 11962fcbc377Syt /* Attach it to SATA framework */ 11972fcbc377Syt if (sata_hba_attach(ahci_ctlp->ahcictl_dip, sata_hba_tran, DDI_ATTACH) 11982fcbc377Syt != DDI_SUCCESS) { 11992fcbc377Syt kmem_free((void *)sata_hba_tran, sizeof (sata_hba_tran_t)); 12002fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 12012fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 12022fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 12032fcbc377Syt return (AHCI_FAILURE); 12042fcbc377Syt } 12052fcbc377Syt 12062fcbc377Syt return (AHCI_SUCCESS); 12072fcbc377Syt } 12082fcbc377Syt 12092fcbc377Syt /* 12102fcbc377Syt * Unregisters the ahci with sata framework. 12112fcbc377Syt */ 12122fcbc377Syt static int 12132fcbc377Syt ahci_unregister_sata_hba_tran(ahci_ctl_t *ahci_ctlp) 12142fcbc377Syt { 1215f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 1216f5f2d263SFred Herard "ahci_unregister_sata_hba_tran enter", NULL); 12172fcbc377Syt 12182fcbc377Syt /* Detach from the SATA framework. */ 12192fcbc377Syt if (sata_hba_detach(ahci_ctlp->ahcictl_dip, DDI_DETACH) != 12202fcbc377Syt DDI_SUCCESS) { 12212fcbc377Syt return (AHCI_FAILURE); 12222fcbc377Syt } 12232fcbc377Syt 12242fcbc377Syt /* Deallocate sata_hba_tran. */ 12252fcbc377Syt kmem_free((void *)ahci_ctlp->ahcictl_sata_hba_tran, 12262fcbc377Syt sizeof (sata_hba_tran_t)); 12272fcbc377Syt 12282fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 12292fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran = NULL; 12302fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 12312fcbc377Syt 12322fcbc377Syt return (AHCI_SUCCESS); 12332fcbc377Syt } 12342fcbc377Syt 12358aa6aadbSXiao-Yu Zhang #define SET_PORTSTR(str, addrp) \ 12368aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PORT(addrp)) \ 12378aa6aadbSXiao-Yu Zhang (void) sprintf((str), "%d", (addrp)->aa_port); \ 12388aa6aadbSXiao-Yu Zhang else if (AHCI_ADDR_IS_PMULT(addrp)) \ 12398aa6aadbSXiao-Yu Zhang (void) sprintf((str), "%d (pmult)", (addrp)->aa_port); \ 12408aa6aadbSXiao-Yu Zhang else \ 12418aa6aadbSXiao-Yu Zhang (void) sprintf((str), "%d:%d", (addrp)->aa_port, \ 12428aa6aadbSXiao-Yu Zhang (addrp)->aa_pmport); 12438aa6aadbSXiao-Yu Zhang 12442fcbc377Syt /* 12452fcbc377Syt * ahci_tran_probe_port is called by SATA framework. It returns port state, 12462fcbc377Syt * port status registers and an attached device type via sata_device 12472fcbc377Syt * structure. 12482fcbc377Syt * 12492fcbc377Syt * We return the cached information from a previous hardware probe. The 12502fcbc377Syt * actual hardware probing itself was done either from within 12512fcbc377Syt * ahci_initialize_controller() during the driver attach or from a phy 12522fcbc377Syt * ready change interrupt handler. 12532fcbc377Syt */ 12542fcbc377Syt static int 12552fcbc377Syt ahci_tran_probe_port(dev_info_t *dip, sata_device_t *sd) 12562fcbc377Syt { 12572fcbc377Syt ahci_ctl_t *ahci_ctlp; 12582fcbc377Syt ahci_port_t *ahci_portp; 12598aa6aadbSXiao-Yu Zhang ahci_addr_t addr, pmult_addr; 12608aa6aadbSXiao-Yu Zhang uint8_t cport = sd->satadev_addr.cport; 12618aa6aadbSXiao-Yu Zhang char portstr[10]; 12628aa6aadbSXiao-Yu Zhang uint8_t device_type; 12632fcbc377Syt uint32_t port_state; 12642fcbc377Syt uint8_t port; 12658aa6aadbSXiao-Yu Zhang int rval = SATA_SUCCESS, rval_init; 12662fcbc377Syt 12672fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 12682fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 12692fcbc377Syt 12702fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 12712fcbc377Syt 12722fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 12732fcbc377Syt 12748aa6aadbSXiao-Yu Zhang ahci_get_ahci_addr(ahci_ctlp, sd, &addr); 12758aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_VALID(&addr)); 12768aa6aadbSXiao-Yu Zhang SET_PORTSTR(portstr, &addr); 12778aa6aadbSXiao-Yu Zhang 12788aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 12798aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port enter: port %s", portstr); 12808aa6aadbSXiao-Yu Zhang 12818aa6aadbSXiao-Yu Zhang if ((AHCI_ADDR_IS_PMULT(&addr) || AHCI_ADDR_IS_PMPORT(&addr)) && 12828aa6aadbSXiao-Yu Zhang (ahci_portp->ahciport_device_type != SATA_DTYPE_PMULT || 12838aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pmult_info == NULL)) { 12848aa6aadbSXiao-Yu Zhang /* port mutliplier is removed. */ 12858aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_PMULT, ahci_ctlp, 12868aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: " 1287918304a3SXiao-Yu Zhang "pmult is removed from port %s", portstr); 12888aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 12898aa6aadbSXiao-Yu Zhang return (SATA_FAILURE); 12908aa6aadbSXiao-Yu Zhang } 12918aa6aadbSXiao-Yu Zhang 12928aa6aadbSXiao-Yu Zhang /* 12938aa6aadbSXiao-Yu Zhang * The sata_device may refer to 12948aa6aadbSXiao-Yu Zhang * 1. A controller port. 12958aa6aadbSXiao-Yu Zhang * A controller port should be ready here. 12968aa6aadbSXiao-Yu Zhang * 2. A port multiplier. 1297918304a3SXiao-Yu Zhang * SATA_ADDR_PMULT_SPEC - if it is not initialized yet, initialize 1298918304a3SXiao-Yu Zhang * it and register the port multiplier to the framework. 1299918304a3SXiao-Yu Zhang * SATA_ADDR_PMULT - check the status of all its device ports. 13008aa6aadbSXiao-Yu Zhang * 3. A port multiplier port. 13018aa6aadbSXiao-Yu Zhang * If it has not been initialized, initialized it. 13028aa6aadbSXiao-Yu Zhang * 13038aa6aadbSXiao-Yu Zhang * A port multiplier or a port multiplier port may require some 13048aa6aadbSXiao-Yu Zhang * initialization because we cannot do these time-consuming jobs in an 13058aa6aadbSXiao-Yu Zhang * interrupt context. 13068aa6aadbSXiao-Yu Zhang */ 1307918304a3SXiao-Yu Zhang if (sd->satadev_addr.qual & SATA_ADDR_PMULT_SPEC) { 1308918304a3SXiao-Yu Zhang AHCI_ADDR_SET_PMULT(&pmult_addr, port); 1309918304a3SXiao-Yu Zhang /* Initialize registers on a port multiplier */ 1310918304a3SXiao-Yu Zhang rval_init = ahci_initialize_pmult(ahci_ctlp, 1311918304a3SXiao-Yu Zhang ahci_portp, &pmult_addr, sd); 1312918304a3SXiao-Yu Zhang if (rval_init != AHCI_SUCCESS) { 1313918304a3SXiao-Yu Zhang AHCIDBG(AHCIDBG_PMULT, ahci_ctlp, 1314918304a3SXiao-Yu Zhang "ahci_tran_probe_port: " 1315918304a3SXiao-Yu Zhang "pmult initialization failed.", NULL); 1316918304a3SXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 1317918304a3SXiao-Yu Zhang return (SATA_FAILURE); 13188aa6aadbSXiao-Yu Zhang } 1319918304a3SXiao-Yu Zhang } else if (sd->satadev_addr.qual & SATA_ADDR_PMULT) { 13208aa6aadbSXiao-Yu Zhang /* Check pmports hotplug events */ 13218aa6aadbSXiao-Yu Zhang (void) ahci_probe_pmult(ahci_ctlp, ahci_portp, &addr); 1322918304a3SXiao-Yu Zhang } else if (sd->satadev_addr.qual & SATA_ADDR_PMPORT) { 13238aa6aadbSXiao-Yu Zhang if (ahci_probe_pmport(ahci_ctlp, ahci_portp, 13248aa6aadbSXiao-Yu Zhang &addr, sd) != AHCI_SUCCESS) { 13258aa6aadbSXiao-Yu Zhang rval = SATA_FAILURE; 13268aa6aadbSXiao-Yu Zhang goto out; 13278aa6aadbSXiao-Yu Zhang } 13288aa6aadbSXiao-Yu Zhang } 13298aa6aadbSXiao-Yu Zhang 13308aa6aadbSXiao-Yu Zhang /* Update port state and device type */ 13318aa6aadbSXiao-Yu Zhang port_state = AHCIPORT_GET_STATE(ahci_portp, &addr); 13328aa6aadbSXiao-Yu Zhang 13332fcbc377Syt switch (port_state) { 13342fcbc377Syt 13352fcbc377Syt case SATA_PSTATE_FAILED: 13362fcbc377Syt sd->satadev_state = SATA_PSTATE_FAILED; 1337f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 13388aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s PORT FAILED", portstr); 13392fcbc377Syt goto out; 13402fcbc377Syt 13412fcbc377Syt case SATA_PSTATE_SHUTDOWN: 13422fcbc377Syt sd->satadev_state = SATA_PSTATE_SHUTDOWN; 1343f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 13448aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s PORT SHUTDOWN", portstr); 13452fcbc377Syt goto out; 13462fcbc377Syt 13472fcbc377Syt case SATA_PSTATE_PWROFF: 13482fcbc377Syt sd->satadev_state = SATA_PSTATE_PWROFF; 1349f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 13508aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s PORT PWROFF", portstr); 13512fcbc377Syt goto out; 13522fcbc377Syt 13532fcbc377Syt case SATA_PSTATE_PWRON: 13542fcbc377Syt sd->satadev_state = SATA_PSTATE_PWRON; 1355f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 13568aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s PORT PWRON", portstr); 13572fcbc377Syt break; 13582fcbc377Syt 13592fcbc377Syt default: 13602fcbc377Syt sd->satadev_state = port_state; 1361f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 13628aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s PORT NORMAL %x", 13638aa6aadbSXiao-Yu Zhang portstr, port_state); 13642fcbc377Syt break; 13652fcbc377Syt } 13662fcbc377Syt 13678aa6aadbSXiao-Yu Zhang device_type = AHCIPORT_GET_DEV_TYPE(ahci_portp, &addr); 13682fcbc377Syt 13692fcbc377Syt switch (device_type) { 13702fcbc377Syt 13712fcbc377Syt case SATA_DTYPE_ATADISK: 13722fcbc377Syt sd->satadev_type = SATA_DTYPE_ATADISK; 1373f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 13748aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s DISK found", portstr); 13752fcbc377Syt break; 13762fcbc377Syt 137738547057Sying tian - Beijing China case SATA_DTYPE_ATAPI: 137838547057Sying tian - Beijing China /* 137938547057Sying tian - Beijing China * HBA driver only knows it's an ATAPI device, and don't know 1380f8a673adSying tian - Beijing China * it's CD/DVD, tape or ATAPI disk because the ATAPI device 1381f8a673adSying tian - Beijing China * type need to be determined by checking IDENTIFY PACKET 1382f8a673adSying tian - Beijing China * DEVICE data 138338547057Sying tian - Beijing China */ 138438547057Sying tian - Beijing China sd->satadev_type = SATA_DTYPE_ATAPI; 1385f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 13868aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s ATAPI found", portstr); 13872fcbc377Syt break; 13882fcbc377Syt 13892fcbc377Syt case SATA_DTYPE_PMULT: 13908aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_PORT(&addr)||AHCI_ADDR_IS_PMULT(&addr)); 13912fcbc377Syt sd->satadev_type = SATA_DTYPE_PMULT; 13928aa6aadbSXiao-Yu Zhang 13938aa6aadbSXiao-Yu Zhang /* Update the number of pmports. */ 13948aa6aadbSXiao-Yu Zhang ASSERT(ahci_portp->ahciport_pmult_info != NULL); 13958aa6aadbSXiao-Yu Zhang sd->satadev_add_info = ahci_portp-> 13968aa6aadbSXiao-Yu Zhang ahciport_pmult_info->ahcipmi_num_dev_ports; 13978aa6aadbSXiao-Yu Zhang 1398f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 13998aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s Port Multiplier found", 14008aa6aadbSXiao-Yu Zhang portstr); 14012fcbc377Syt break; 14022fcbc377Syt 14032fcbc377Syt case SATA_DTYPE_UNKNOWN: 14042fcbc377Syt sd->satadev_type = SATA_DTYPE_UNKNOWN; 1405f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 14068aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s Unknown device found", 14078aa6aadbSXiao-Yu Zhang portstr); 14082fcbc377Syt break; 14092fcbc377Syt 14102fcbc377Syt default: 14112fcbc377Syt /* we don't support any other device types */ 14122fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 1413f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 14148aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: port %s No device found", portstr); 14152fcbc377Syt break; 14162fcbc377Syt } 14172fcbc377Syt 14182fcbc377Syt out: 14198aa6aadbSXiao-Yu Zhang /* Register update only fails while probing a pmult/pmport */ 1420918304a3SXiao-Yu Zhang if (AHCI_ADDR_IS_PORT(&addr) || AHCI_ADDR_IS_PMULT(&addr)) { 14218aa6aadbSXiao-Yu Zhang ahci_update_sata_registers(ahci_ctlp, port, sd); 14228aa6aadbSXiao-Yu Zhang } else if (AHCI_ADDR_IS_PMPORT(&addr)) { 14238aa6aadbSXiao-Yu Zhang if (port_state & SATA_STATE_READY) 14248aa6aadbSXiao-Yu Zhang if (ahci_update_pmult_pscr(ahci_ctlp, 14258aa6aadbSXiao-Yu Zhang &addr, sd) != AHCI_SUCCESS) 14268aa6aadbSXiao-Yu Zhang rval = SATA_FAILURE; 14278aa6aadbSXiao-Yu Zhang } 14282fcbc377Syt 14298aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 14308aa6aadbSXiao-Yu Zhang return (rval); 14312fcbc377Syt } 14322fcbc377Syt 143368d33a25Syt /* 143468d33a25Syt * There are four operation modes in sata framework: 143568d33a25Syt * SATA_OPMODE_INTERRUPTS 143668d33a25Syt * SATA_OPMODE_POLLING 143768d33a25Syt * SATA_OPMODE_ASYNCH 143868d33a25Syt * SATA_OPMODE_SYNCH 143968d33a25Syt * 144068d33a25Syt * Their combined meanings as following: 144168d33a25Syt * 144268d33a25Syt * SATA_OPMODE_SYNCH 144368d33a25Syt * The command has to be completed before sata_tran_start functions returns. 144468d33a25Syt * Either interrupts or polling could be used - it's up to the driver. 144568d33a25Syt * Mode used currently for internal, sata-module initiated operations. 144668d33a25Syt * 144768d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS 144868d33a25Syt * It is the same as the one above. 144968d33a25Syt * 145068d33a25Syt * SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING 145168d33a25Syt * The command has to be completed before sata_tran_start function returns. 145268d33a25Syt * No interrupt used, polling only. This should be the mode used for scsi 145368d33a25Syt * packets with FLAG_NOINTR. 145468d33a25Syt * 145568d33a25Syt * SATA_OPMODE_ASYNCH | SATA_OPMODE_INTERRUPTS 145668d33a25Syt * The command may be queued (callback function specified). Interrupts could 145768d33a25Syt * be used. It's normal operation mode. 145868d33a25Syt */ 14592fcbc377Syt /* 14602fcbc377Syt * Called by sata framework to transport a sata packet down stream. 14612fcbc377Syt */ 14622fcbc377Syt static int 14632fcbc377Syt ahci_tran_start(dev_info_t *dip, sata_pkt_t *spkt) 14642fcbc377Syt { 14652fcbc377Syt ahci_ctl_t *ahci_ctlp; 14662fcbc377Syt ahci_port_t *ahci_portp; 1467918304a3SXiao-Yu Zhang ahci_addr_t addr; 14682fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 14692fcbc377Syt uint8_t port; 14708aa6aadbSXiao-Yu Zhang char portstr[10]; 14712fcbc377Syt 14722fcbc377Syt ahci_ctlp = ddi_get_soft_state(ahci_statep, ddi_get_instance(dip)); 14732fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 14742fcbc377Syt 1475f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 14762fcbc377Syt "ahci_tran_start enter: cport %d satapkt 0x%p", 14772fcbc377Syt cport, (void *)spkt); 14782fcbc377Syt 14792fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 14802fcbc377Syt 14812fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 14828aa6aadbSXiao-Yu Zhang ahci_get_ahci_addr(ahci_ctlp, &spkt->satapkt_device, &addr); 14838aa6aadbSXiao-Yu Zhang SET_PORTSTR(portstr, &addr); 14842fcbc377Syt 14858aa6aadbSXiao-Yu Zhang /* Sanity check */ 14868aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PMPORT(&addr)) { 14878aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type != SATA_DTYPE_PMULT || 14888aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pmult_info == NULL) { 14898aa6aadbSXiao-Yu Zhang 14908aa6aadbSXiao-Yu Zhang spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 14918aa6aadbSXiao-Yu Zhang spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 14928aa6aadbSXiao-Yu Zhang spkt->satapkt_device.satadev_state = SATA_STATE_UNKNOWN; 14938aa6aadbSXiao-Yu Zhang ahci_update_sata_registers(ahci_ctlp, port, 14948aa6aadbSXiao-Yu Zhang &spkt->satapkt_device); 14958aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 14968aa6aadbSXiao-Yu Zhang "ahci_tran_start returning PORT_ERROR while " 14978aa6aadbSXiao-Yu Zhang "pmult removed: port: %s", portstr); 14988aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 14998aa6aadbSXiao-Yu Zhang return (SATA_TRAN_PORT_ERROR); 15008aa6aadbSXiao-Yu Zhang } 15018aa6aadbSXiao-Yu Zhang 15028aa6aadbSXiao-Yu Zhang if (!(AHCIPORT_GET_STATE(ahci_portp, &addr) & 15038aa6aadbSXiao-Yu Zhang SATA_STATE_READY)) { 15048aa6aadbSXiao-Yu Zhang if (!ddi_in_panic() || 15058aa6aadbSXiao-Yu Zhang ahci_initialize_pmport(ahci_ctlp, 15068aa6aadbSXiao-Yu Zhang ahci_portp, &addr) != AHCI_SUCCESS) { 15078aa6aadbSXiao-Yu Zhang spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 15088aa6aadbSXiao-Yu Zhang spkt->satapkt_device.satadev_type = 15098aa6aadbSXiao-Yu Zhang AHCIPORT_GET_DEV_TYPE(ahci_portp, &addr); 15108aa6aadbSXiao-Yu Zhang spkt->satapkt_device.satadev_state = 15118aa6aadbSXiao-Yu Zhang AHCIPORT_GET_STATE(ahci_portp, &addr); 15128aa6aadbSXiao-Yu Zhang ahci_update_sata_registers(ahci_ctlp, port, 15138aa6aadbSXiao-Yu Zhang &spkt->satapkt_device); 15148aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 15158aa6aadbSXiao-Yu Zhang "ahci_tran_start returning PORT_ERROR " 15168aa6aadbSXiao-Yu Zhang "while sub-link is not initialized " 15178aa6aadbSXiao-Yu Zhang "at port: %s", portstr); 15188aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 15198aa6aadbSXiao-Yu Zhang return (SATA_TRAN_PORT_ERROR); 15208aa6aadbSXiao-Yu Zhang } 15218aa6aadbSXiao-Yu Zhang } 15228aa6aadbSXiao-Yu Zhang } 15238aa6aadbSXiao-Yu Zhang 15248aa6aadbSXiao-Yu Zhang if (AHCIPORT_GET_STATE(ahci_portp, &addr) & SATA_PSTATE_FAILED || 15258aa6aadbSXiao-Yu Zhang AHCIPORT_GET_STATE(ahci_portp, &addr) & SATA_PSTATE_SHUTDOWN|| 15268aa6aadbSXiao-Yu Zhang AHCIPORT_GET_STATE(ahci_portp, &addr) & SATA_PSTATE_PWROFF) { 15272fcbc377Syt /* 15288aa6aadbSXiao-Yu Zhang * In case the target driver would send the packet before 15292fcbc377Syt * sata framework can have the opportunity to process those 15302fcbc377Syt * event reports. 15312fcbc377Syt */ 15322fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 15332fcbc377Syt spkt->satapkt_device.satadev_state = 15342fcbc377Syt ahci_portp->ahciport_port_state; 15352fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 15362fcbc377Syt &spkt->satapkt_device); 1537f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 15382fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 15392fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 15408aa6aadbSXiao-Yu Zhang "port: %s", portstr); 15412fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 15422fcbc377Syt return (SATA_TRAN_PORT_ERROR); 15432fcbc377Syt } 15442fcbc377Syt 15458aa6aadbSXiao-Yu Zhang if (AHCIPORT_GET_DEV_TYPE(ahci_portp, &addr) == SATA_DTYPE_NONE) { 15462fcbc377Syt /* 15472fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 15482fcbc377Syt * SATA_DTYPE_NONE. 15492fcbc377Syt */ 15502fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 15512fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 15522fcbc377Syt spkt->satapkt_device.satadev_state = 15532fcbc377Syt ahci_portp->ahciport_port_state; 15542fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 15552fcbc377Syt &spkt->satapkt_device); 1556f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 15572fcbc377Syt "ahci_tran_start returning PORT_ERROR while " 15588aa6aadbSXiao-Yu Zhang "no device attached: port: %s", portstr); 15592fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 15602fcbc377Syt return (SATA_TRAN_PORT_ERROR); 15612fcbc377Syt } 15622fcbc377Syt 15638aa6aadbSXiao-Yu Zhang /* R/W PMULT command will occupy the whole HBA port */ 15648aa6aadbSXiao-Yu Zhang if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 15658aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 15668aa6aadbSXiao-Yu Zhang "ahci_tran_start returning BUSY while " 15678aa6aadbSXiao-Yu Zhang "executing READ/WRITE PORT-MULT command: " 15688aa6aadbSXiao-Yu Zhang "port: %s", portstr); 15698aa6aadbSXiao-Yu Zhang spkt->satapkt_reason = SATA_PKT_BUSY; 15708aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 15718aa6aadbSXiao-Yu Zhang return (SATA_TRAN_BUSY); 15728aa6aadbSXiao-Yu Zhang } 15738aa6aadbSXiao-Yu Zhang 15748aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_HOTPLUG) { 15758aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 15768aa6aadbSXiao-Yu Zhang "ahci_tran_start returning BUSY while " 15778aa6aadbSXiao-Yu Zhang "hot-plug in progress: port: %s", portstr); 15788aa6aadbSXiao-Yu Zhang spkt->satapkt_reason = SATA_PKT_BUSY; 15798aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 15808aa6aadbSXiao-Yu Zhang return (SATA_TRAN_BUSY); 15818aa6aadbSXiao-Yu Zhang } 15828aa6aadbSXiao-Yu Zhang 15832fcbc377Syt /* 15842fcbc377Syt * SATA HBA driver should remember that a device was reset and it 15852fcbc377Syt * is supposed to reject any packets which do not specify either 15862fcbc377Syt * SATA_IGNORE_DEV_RESET_STATE or SATA_CLEAR_DEV_RESET_STATE. 15872fcbc377Syt * 15882fcbc377Syt * This is to prevent a race condition when a device was arbitrarily 15892fcbc377Syt * reset by the HBA driver (and lost it's setting) and a target 15902fcbc377Syt * driver sending some commands to a device before the sata framework 15912fcbc377Syt * has a chance to restore the device setting (such as cache enable/ 15922fcbc377Syt * disable or other resettable stuff). 15932fcbc377Syt */ 15948aa6aadbSXiao-Yu Zhang /* 15958aa6aadbSXiao-Yu Zhang * It is unnecessary to use specific flags to indicate 15968aa6aadbSXiao-Yu Zhang * reset_in_progress for a pmport. While mopping, all command will be 15978aa6aadbSXiao-Yu Zhang * mopped so that the entire HBA port is being dealt as a single 15988aa6aadbSXiao-Yu Zhang * object. 15998aa6aadbSXiao-Yu Zhang */ 16002fcbc377Syt if (spkt->satapkt_cmd.satacmd_flags.sata_clear_dev_reset) { 16012fcbc377Syt ahci_portp->ahciport_reset_in_progress = 0; 1602f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 16038aa6aadbSXiao-Yu Zhang "ahci_tran_start [CLEAR] the " 160468d33a25Syt "reset_in_progress for port: %d", port); 16052fcbc377Syt } 16062fcbc377Syt 16072fcbc377Syt if (ahci_portp->ahciport_reset_in_progress && 16082fcbc377Syt ! spkt->satapkt_cmd.satacmd_flags.sata_ignore_dev_reset && 16092fcbc377Syt ! ddi_in_panic()) { 16102fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 1611f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 16122fcbc377Syt "ahci_tran_start returning BUSY while " 161368d33a25Syt "reset in progress: port: %d", port); 16142fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 16152fcbc377Syt return (SATA_TRAN_BUSY); 16162fcbc377Syt } 16172fcbc377Syt 16188aa6aadbSXiao-Yu Zhang #ifdef AHCI_DEBUG 16198aa6aadbSXiao-Yu Zhang if (spkt->satapkt_cmd.satacmd_flags.sata_ignore_dev_reset) { 16208aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 16218aa6aadbSXiao-Yu Zhang "ahci_tran_start: packet 0x%p [PASSTHRU] at port %d", 16228aa6aadbSXiao-Yu Zhang spkt, port); 16238aa6aadbSXiao-Yu Zhang } 16248aa6aadbSXiao-Yu Zhang #endif 16258aa6aadbSXiao-Yu Zhang 162668d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 16272fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 1628f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 16292fcbc377Syt "ahci_tran_start returning BUSY while " 163068d33a25Syt "mopping in progress: port: %d", port); 16312fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 16322fcbc377Syt return (SATA_TRAN_BUSY); 16332fcbc377Syt } 16342fcbc377Syt 16352fcbc377Syt if (spkt->satapkt_op_mode & 163668d33a25Syt (SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING)) { 1637b2e3645aSying tian - Beijing China /* 1638b2e3645aSying tian - Beijing China * If a SYNC command to be executed in interrupt context, 1639b2e3645aSying tian - Beijing China * bounce it back to sata module. 1640b2e3645aSying tian - Beijing China */ 1641b2e3645aSying tian - Beijing China if (!(spkt->satapkt_op_mode & SATA_OPMODE_POLLING) && 1642b2e3645aSying tian - Beijing China servicing_interrupt()) { 1643b2e3645aSying tian - Beijing China spkt->satapkt_reason = SATA_PKT_BUSY; 1644f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 1645b2e3645aSying tian - Beijing China "ahci_tran_start returning BUSY while " 1646b2e3645aSying tian - Beijing China "sending SYNC mode under interrupt context: " 1647b2e3645aSying tian - Beijing China "port : %d", port); 1648b2e3645aSying tian - Beijing China mutex_exit(&ahci_portp->ahciport_mutex); 1649b2e3645aSying tian - Beijing China return (SATA_TRAN_BUSY); 1650b2e3645aSying tian - Beijing China } 1651b2e3645aSying tian - Beijing China 165268d33a25Syt /* We need to do the sync start now */ 16538aa6aadbSXiao-Yu Zhang if (ahci_do_sync_start(ahci_ctlp, ahci_portp, &addr, 165468d33a25Syt spkt) == AHCI_FAILURE) { 1655f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 165668d33a25Syt "return QUEUE_FULL: port %d", port); 165768d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 165868d33a25Syt return (SATA_TRAN_QUEUE_FULL); 165968d33a25Syt } 166068d33a25Syt } else { 166168d33a25Syt /* Async start, using interrupt */ 16628aa6aadbSXiao-Yu Zhang if (ahci_deliver_satapkt(ahci_ctlp, ahci_portp, &addr, spkt) 166368d33a25Syt == AHCI_FAILURE) { 166468d33a25Syt spkt->satapkt_reason = SATA_PKT_QUEUE_FULL; 1665f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_tran_start " 166668d33a25Syt "returning QUEUE_FULL: port %d", port); 166768d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 166868d33a25Syt return (SATA_TRAN_QUEUE_FULL); 166968d33a25Syt } 16702fcbc377Syt } 16712fcbc377Syt 1672f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "ahci_tran_start " 16738aa6aadbSXiao-Yu Zhang "sata tran accepted: port %s", portstr); 16742fcbc377Syt 167568d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 16762fcbc377Syt return (SATA_TRAN_ACCEPTED); 16772fcbc377Syt } 16782fcbc377Syt 167968d33a25Syt /* 168068d33a25Syt * SATA_OPMODE_SYNCH flag is set 168168d33a25Syt * 168268d33a25Syt * If SATA_OPMODE_POLLING flag is set, then we must poll the command 168368d33a25Syt * without interrupt, otherwise we can still use the interrupt. 168468d33a25Syt * 168568d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function 168668d33a25Syt * is called. 168768d33a25Syt */ 168868d33a25Syt static int 168968d33a25Syt ahci_do_sync_start(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 16908aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp, sata_pkt_t *spkt) 169168d33a25Syt { 169268d33a25Syt int pkt_timeout_ticks; 169368d33a25Syt uint32_t timeout_tags; 169468d33a25Syt int rval; 1695a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 16968aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 169768d33a25Syt 1698f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_do_sync_start enter: " 16998aa6aadbSXiao-Yu Zhang "port %d:%d spkt 0x%p", port, addrp->aa_pmport, spkt); 170068d33a25Syt 170168d33a25Syt if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) { 170268d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_POLLING; 170368d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 17048aa6aadbSXiao-Yu Zhang addrp, spkt)) == AHCI_FAILURE) { 170568d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_POLLING; 170668d33a25Syt return (rval); 170768d33a25Syt } 170868d33a25Syt 170968d33a25Syt pkt_timeout_ticks = 171068d33a25Syt drv_usectohz((clock_t)spkt->satapkt_time * 1000000); 171168d33a25Syt 171268d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) { 171368d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 171468d33a25Syt 171568d33a25Syt /* Simulate the interrupt */ 171682263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 171768d33a25Syt 17188aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 171968d33a25Syt 172068d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 17218aa6aadbSXiao-Yu Zhang pkt_timeout_ticks -= AHCI_10MS_TICKS; 172268d33a25Syt if (pkt_timeout_ticks < 0) { 1723a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_do_sync_start " 172468d33a25Syt "port %d satapkt 0x%p timed out\n", 1725a9440e8dSyt instance, port, (void *)spkt); 172668d33a25Syt timeout_tags = (0x1 << rval); 172768d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 172868d33a25Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 172982263d52Syt port, timeout_tags); 173068d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 173168d33a25Syt } 173268d33a25Syt } 173368d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_POLLING; 173468d33a25Syt return (AHCI_SUCCESS); 173568d33a25Syt 173668d33a25Syt } else { 173768d33a25Syt if ((rval = ahci_deliver_satapkt(ahci_ctlp, ahci_portp, 17388aa6aadbSXiao-Yu Zhang addrp, spkt)) == AHCI_FAILURE) 173968d33a25Syt return (rval); 174068d33a25Syt 174182263d52Syt #if AHCI_DEBUG 174282263d52Syt /* 174382263d52Syt * Note that the driver always uses the slot 0 to deliver 174482263d52Syt * REQUEST SENSE or READ LOG EXT command 174582263d52Syt */ 174682263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 174782263d52Syt ASSERT(rval == 0); 174882263d52Syt #endif 174982263d52Syt 175068d33a25Syt while (spkt->satapkt_reason == SATA_PKT_BUSY) 175168d33a25Syt cv_wait(&ahci_portp->ahciport_cv, 175268d33a25Syt &ahci_portp->ahciport_mutex); 175368d33a25Syt 175468d33a25Syt return (AHCI_SUCCESS); 175568d33a25Syt } 175668d33a25Syt } 175768d33a25Syt 17582fcbc377Syt #define SENDUP_PACKET(ahci_portp, satapkt, reason) \ 17592fcbc377Syt if (satapkt) { \ 17602fcbc377Syt satapkt->satapkt_reason = reason; \ 17612fcbc377Syt /* \ 17622fcbc377Syt * We set the satapkt_reason in both sync and \ 17632fcbc377Syt * non-sync cases. \ 17642fcbc377Syt */ \ 17652fcbc377Syt } \ 17662fcbc377Syt if (satapkt && \ 176768d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 17682fcbc377Syt satapkt->satapkt_comp) { \ 17692fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); \ 17702fcbc377Syt (*satapkt->satapkt_comp)(satapkt); \ 17712fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); \ 177268d33a25Syt } else { \ 177368d33a25Syt if (satapkt && \ 177468d33a25Syt (satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \ 177568d33a25Syt ! (satapkt->satapkt_op_mode & SATA_OPMODE_POLLING)) \ 1776f8a673adSying tian - Beijing China cv_broadcast(&ahci_portp->ahciport_cv); \ 17772fcbc377Syt } 17782fcbc377Syt 17792fcbc377Syt /* 178082263d52Syt * Searches for and claims a free command slot. 178182263d52Syt * 17828aa6aadbSXiao-Yu Zhang * Returns value: 17832fcbc377Syt * 17848aa6aadbSXiao-Yu Zhang * AHCI_FAILURE returned only if 17858aa6aadbSXiao-Yu Zhang * 1. No empty slot left 17868aa6aadbSXiao-Yu Zhang * 2. Non-queued command requested while queued command(s) is outstanding 17878aa6aadbSXiao-Yu Zhang * 3. Queued command requested while non-queued command(s) is outstanding 178882263d52Syt * 4. HBA doesn't support multiple-use of command list while already a 17898aa6aadbSXiao-Yu Zhang * non-queued command is oustanding 17908aa6aadbSXiao-Yu Zhang * 5. Queued command requested while some queued command(s) has been 17918aa6aadbSXiao-Yu Zhang * outstanding on a different port multiplier port. (AHCI spec 1.2, 17928aa6aadbSXiao-Yu Zhang * 9.1.2) 179382263d52Syt * 17948aa6aadbSXiao-Yu Zhang * claimed slot number returned if succeeded 179582263d52Syt * 17968aa6aadbSXiao-Yu Zhang * NOTE: it will always return slot 0 for following commands to simplify the 17978aa6aadbSXiao-Yu Zhang * algorithm. 17988aa6aadbSXiao-Yu Zhang * 1. REQUEST SENSE or READ LOG EXT command during error recovery process 17998aa6aadbSXiao-Yu Zhang * 2. READ/WRITE PORTMULT command 18002fcbc377Syt * 18012fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 18022fcbc377Syt * is called. 18032fcbc377Syt */ 18042fcbc377Syt static int 18058aa6aadbSXiao-Yu Zhang ahci_claim_free_slot(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 18068aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp, int command_type) 18072fcbc377Syt { 18088aa6aadbSXiao-Yu Zhang uint32_t port_cmd_issue; 18092fcbc377Syt uint32_t free_slots; 18102fcbc377Syt int slot; 18112fcbc377Syt 1812f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_claim_free_slot enter " 181382263d52Syt "ahciport_pending_tags = 0x%x " 181482263d52Syt "ahciport_pending_ncq_tags = 0x%x", 181582263d52Syt ahci_portp->ahciport_pending_tags, 181682263d52Syt ahci_portp->ahciport_pending_ncq_tags); 18172fcbc377Syt 181882263d52Syt /* 181982263d52Syt * According to the AHCI spec, system software is responsible to 182082263d52Syt * ensure that queued and non-queued commands are not mixed in 182182263d52Syt * the command list. 182282263d52Syt */ 182382263d52Syt if (command_type == AHCI_NON_NCQ_CMD) { 182482263d52Syt /* Non-NCQ command request */ 182582263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 1826f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 182782263d52Syt "ahci_claim_free_slot: there is still pending " 182882263d52Syt "queued command(s) in the command list, " 182982263d52Syt "so no available slot for the non-queued " 1830f5f2d263SFred Herard "command", NULL); 183182263d52Syt return (AHCI_FAILURE); 183282263d52Syt } 18338aa6aadbSXiao-Yu Zhang if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 18348aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 18358aa6aadbSXiao-Yu Zhang "ahci_claim_free_slot: there is still pending " 18368aa6aadbSXiao-Yu Zhang "read/write port-mult command(s) in command list, " 18378aa6aadbSXiao-Yu Zhang "so no available slot for the non-queued command", 18388aa6aadbSXiao-Yu Zhang NULL); 18398aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 18408aa6aadbSXiao-Yu Zhang } 184182263d52Syt if ((ahci_ctlp->ahcictl_cap & AHCI_CAP_NO_MCMDLIST_NONQUEUE) && 184282263d52Syt NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 1843f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 184482263d52Syt "ahci_claim_free_slot: HBA cannot support multiple-" 1845f5f2d263SFred Herard "use of the command list for non-queued commands", 1846f5f2d263SFred Herard NULL); 184782263d52Syt return (AHCI_FAILURE); 184882263d52Syt } 184982263d52Syt free_slots = (~ahci_portp->ahciport_pending_tags) & 185082263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 185182263d52Syt } else if (command_type == AHCI_NCQ_CMD) { 185282263d52Syt /* NCQ command request */ 185382263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 1854f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO|AHCIDBG_NCQ, ahci_ctlp, 185582263d52Syt "ahci_claim_free_slot: there is still pending " 185682263d52Syt "non-queued command(s) in the command list, " 1857f5f2d263SFred Herard "so no available slot for the queued command", 1858f5f2d263SFred Herard NULL); 185982263d52Syt return (AHCI_FAILURE); 186082263d52Syt } 18618aa6aadbSXiao-Yu Zhang 18628aa6aadbSXiao-Yu Zhang /* 18638aa6aadbSXiao-Yu Zhang * NCQ commands cannot be sent to different port multiplier 18648aa6aadbSXiao-Yu Zhang * ports in Command-Based Switching mode 18658aa6aadbSXiao-Yu Zhang */ 18668aa6aadbSXiao-Yu Zhang /* 18678aa6aadbSXiao-Yu Zhang * NOTE: In Command-Based Switching mode, AHCI controller 18688aa6aadbSXiao-Yu Zhang * usually reports a 'Handshake Error' when multiple NCQ 18698aa6aadbSXiao-Yu Zhang * commands are outstanding simultaneously. 18708aa6aadbSXiao-Yu Zhang */ 18718aa6aadbSXiao-Yu Zhang if (AHCIPORT_DEV_TYPE(ahci_portp, addrp) == SATA_DTYPE_PMULT) { 18728aa6aadbSXiao-Yu Zhang ASSERT(ahci_portp->ahciport_pmult_info != NULL); 18738aa6aadbSXiao-Yu Zhang if (!(ahci_ctlp->ahcictl_cap & AHCI_CAP_PMULT_FBSS) && 18748aa6aadbSXiao-Yu Zhang NCQ_CMD_IN_PROGRESS(ahci_portp) && 18758aa6aadbSXiao-Yu Zhang AHCIPORT_NCQ_PMPORT(ahci_portp) != 18768aa6aadbSXiao-Yu Zhang addrp->aa_pmport) { 18778aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 18788aa6aadbSXiao-Yu Zhang "ahci_claim_free_slot: there is still " 18798aa6aadbSXiao-Yu Zhang "pending queued command(s) in the " 18808aa6aadbSXiao-Yu Zhang "command list for another Port Multiplier " 18818aa6aadbSXiao-Yu Zhang "port, so no available slot.", NULL); 18828aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 18838aa6aadbSXiao-Yu Zhang } 18848aa6aadbSXiao-Yu Zhang } 18858aa6aadbSXiao-Yu Zhang 188682263d52Syt free_slots = (~ahci_portp->ahciport_pending_ncq_tags) & 188782263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 188882263d52Syt } else if (command_type == AHCI_ERR_RETRI_CMD) { 188982263d52Syt /* Error retrieval command request */ 1890f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 189182263d52Syt "ahci_claim_free_slot: slot 0 is allocated for REQUEST " 1892f5f2d263SFred Herard "SENSE or READ LOG EXT command", NULL); 189382263d52Syt slot = 0; 189482263d52Syt goto out; 18958aa6aadbSXiao-Yu Zhang } else if (command_type == AHCI_RDWR_PMULT_CMD) { 18968aa6aadbSXiao-Yu Zhang /* 18978aa6aadbSXiao-Yu Zhang * An extra check on PxCI. Sometimes PxCI bits may not be 18988aa6aadbSXiao-Yu Zhang * cleared during hot-plug or error recovery process. 18998aa6aadbSXiao-Yu Zhang */ 19008aa6aadbSXiao-Yu Zhang port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 19018aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, addrp->aa_port)); 19028aa6aadbSXiao-Yu Zhang 19038aa6aadbSXiao-Yu Zhang if (port_cmd_issue != 0) { 19048aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 19058aa6aadbSXiao-Yu Zhang "ahci_claim_free_slot: there is still pending " 19068aa6aadbSXiao-Yu Zhang "command(s) in command list (0x%x/0x%x, PxCI %x)," 19078aa6aadbSXiao-Yu Zhang "so no available slot for R/W PMULT command.", 19088aa6aadbSXiao-Yu Zhang NON_NCQ_CMD_IN_PROGRESS(ahci_portp), 19098aa6aadbSXiao-Yu Zhang NCQ_CMD_IN_PROGRESS(ahci_portp), 19108aa6aadbSXiao-Yu Zhang port_cmd_issue); 19118aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 19128aa6aadbSXiao-Yu Zhang } 19138aa6aadbSXiao-Yu Zhang 19148aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 19158aa6aadbSXiao-Yu Zhang "ahci_claim_free_slot: slot 0 is allocated for " 19168aa6aadbSXiao-Yu Zhang "READ/WRITE PORTMULT command", NULL); 19178aa6aadbSXiao-Yu Zhang slot = 0; 19188aa6aadbSXiao-Yu Zhang goto out; 191982263d52Syt } 19202fcbc377Syt 19212fcbc377Syt slot = ddi_ffs(free_slots) - 1; 19222fcbc377Syt if (slot == -1) { 1923f5f2d263SFred Herard AHCIDBG(AHCIDBG_VERBOSE, ahci_ctlp, 1924f5f2d263SFred Herard "ahci_claim_free_slot: no empty slots", NULL); 19252fcbc377Syt return (AHCI_FAILURE); 19262fcbc377Syt } 19272fcbc377Syt 192882263d52Syt /* 192982263d52Syt * According to the AHCI spec, to allow a simple mechanism for the 193082263d52Syt * HBA to map command list slots to queue entries, software must 193182263d52Syt * match the tag number it uses to the slot it is placing the command 193282263d52Syt * in. For example, if a queued command is placed in slot 5, the tag 193382263d52Syt * for that command must be 5. 193482263d52Syt */ 193582263d52Syt if (command_type == AHCI_NCQ_CMD) { 193682263d52Syt ahci_portp->ahciport_pending_ncq_tags |= (0x1 << slot); 19378aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PMPORT(addrp)) { 19388aa6aadbSXiao-Yu Zhang ASSERT(ahci_portp->ahciport_pmult_info != NULL); 19398aa6aadbSXiao-Yu Zhang AHCIPORT_NCQ_PMPORT(ahci_portp) = addrp->aa_pmport; 19408aa6aadbSXiao-Yu Zhang } 194182263d52Syt } 194282263d52Syt 19432fcbc377Syt ahci_portp->ahciport_pending_tags |= (0x1 << slot); 19442fcbc377Syt 194582263d52Syt out: 1946f5f2d263SFred Herard AHCIDBG(AHCIDBG_VERBOSE, ahci_ctlp, 19472fcbc377Syt "ahci_claim_free_slot: found slot: 0x%x", slot); 19482fcbc377Syt 19492fcbc377Syt return (slot); 19502fcbc377Syt } 19512fcbc377Syt 19522fcbc377Syt /* 19532fcbc377Syt * Builds the Command Table for the sata packet and delivers it to controller. 19542fcbc377Syt * 19552fcbc377Syt * Returns: 19562fcbc377Syt * slot number if we can obtain a slot successfully 19572fcbc377Syt * otherwise, return AHCI_FAILURE 19582fcbc377Syt * 19592fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 19602fcbc377Syt */ 19612fcbc377Syt static int 19622fcbc377Syt ahci_deliver_satapkt(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 19638aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp, sata_pkt_t *spkt) 19642fcbc377Syt { 196568d33a25Syt int cmd_slot; 196668d33a25Syt sata_cmd_t *scmd; 19672fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 19682fcbc377Syt ahci_cmd_table_t *cmd_table; 19692fcbc377Syt ahci_cmd_header_t *cmd_header; 19702fcbc377Syt int ncookies; 19712fcbc377Syt int i; 197282263d52Syt int command_type = AHCI_NON_NCQ_CMD; 197382263d52Syt int ncq_qdepth; 1974a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 19758aa6aadbSXiao-Yu Zhang uint8_t port, pmport; 197668d33a25Syt #if AHCI_DEBUG 197768d33a25Syt uint32_t *ptr; 197868d33a25Syt uint8_t *ptr2; 197968d33a25Syt #endif 19802fcbc377Syt 19818aa6aadbSXiao-Yu Zhang port = addrp->aa_port; 19828aa6aadbSXiao-Yu Zhang pmport = addrp->aa_pmport; 19838aa6aadbSXiao-Yu Zhang 19842fcbc377Syt spkt->satapkt_reason = SATA_PKT_BUSY; 19852fcbc377Syt 198668d33a25Syt scmd = &spkt->satapkt_cmd; 19872fcbc377Syt 198882263d52Syt /* Check if the command is a NCQ command */ 198982263d52Syt if (scmd->satacmd_cmd_reg == SATAC_READ_FPDMA_QUEUED || 199082263d52Syt scmd->satacmd_cmd_reg == SATAC_WRITE_FPDMA_QUEUED) { 199182263d52Syt command_type = AHCI_NCQ_CMD; 199282263d52Syt 199382263d52Syt /* 199482263d52Syt * When NCQ is support, system software must determine the 199582263d52Syt * maximum tag allowed by the device and the HBA, and it 199682263d52Syt * must use a value not beyond of the lower bound of the two. 199782263d52Syt * 199882263d52Syt * Sata module is going to calculate the qdepth and send 199982263d52Syt * down to HBA driver via sata_cmd. 200082263d52Syt */ 200182263d52Syt ncq_qdepth = scmd->satacmd_flags.sata_max_queue_depth + 1; 200282263d52Syt 200382263d52Syt /* 200482263d52Syt * At the moment, the driver doesn't support the dynamic 200582263d52Syt * setting of the maximum ncq depth, and the value can be 200682263d52Syt * set either during the attach or after hot-plug insertion. 200782263d52Syt */ 200882263d52Syt if (ahci_portp->ahciport_max_ncq_tags == 0) { 200982263d52Syt ahci_portp->ahciport_max_ncq_tags = ncq_qdepth; 2010f5f2d263SFred Herard AHCIDBG(AHCIDBG_NCQ, ahci_ctlp, 201182263d52Syt "ahci_deliver_satapkt: port %d the max tags for " 201282263d52Syt "NCQ command is %d", port, ncq_qdepth); 201382263d52Syt } else { 201482263d52Syt if (ncq_qdepth != ahci_portp->ahciport_max_ncq_tags) { 2015a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_deliver_satapkt" 2016a9440e8dSyt " port %d the max tag for NCQ command is " 201782263d52Syt "requested to change from %d to %d, at the" 201882263d52Syt " moment the driver doesn't support the " 201982263d52Syt "dynamic change so it's going to " 2020a9440e8dSyt "still use the previous tag value", 2021a9440e8dSyt instance, port, 202282263d52Syt ahci_portp->ahciport_max_ncq_tags, 202382263d52Syt ncq_qdepth); 202482263d52Syt } 202582263d52Syt } 202682263d52Syt } 202782263d52Syt 202882263d52Syt /* Check if the command is an error retrieval command */ 202982263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 203082263d52Syt command_type = AHCI_ERR_RETRI_CMD; 203182263d52Syt 20328aa6aadbSXiao-Yu Zhang /* Check if the command is an read/write pmult command */ 20338aa6aadbSXiao-Yu Zhang if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) 20348aa6aadbSXiao-Yu Zhang command_type = AHCI_RDWR_PMULT_CMD; 20358aa6aadbSXiao-Yu Zhang 203682263d52Syt /* Check if there is an empty command slot */ 20378aa6aadbSXiao-Yu Zhang cmd_slot = ahci_claim_free_slot(ahci_ctlp, ahci_portp, 20388aa6aadbSXiao-Yu Zhang addrp, command_type); 203968d33a25Syt if (cmd_slot == AHCI_FAILURE) { 2040f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "no free command slot", NULL); 20412fcbc377Syt return (AHCI_FAILURE); 20422fcbc377Syt } 20432fcbc377Syt 2044f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INFO, ahci_ctlp, 204568d33a25Syt "ahci_deliver_satapkt enter: cmd_reg: 0x%x, cmd_slot: 0x%x, " 204668d33a25Syt "port: %d, satapkt: 0x%p", scmd->satacmd_cmd_reg, 204768d33a25Syt cmd_slot, port, (void *)spkt); 20482fcbc377Syt 204968d33a25Syt cmd_table = ahci_portp->ahciport_cmd_tables[cmd_slot]; 20502fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 20512fcbc377Syt 205282263d52Syt /* For data transfer operations, it is the H2D Register FIS */ 20532fcbc377Syt h2d_register_fisp = 20542fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 20552fcbc377Syt 20562fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 20578aa6aadbSXiao-Yu Zhang 20588aa6aadbSXiao-Yu Zhang /* 20598aa6aadbSXiao-Yu Zhang * PMP field only make sense when target is a port multiplier or a 20608aa6aadbSXiao-Yu Zhang * device behind a port multiplier. Otherwise should set it to 0. 20618aa6aadbSXiao-Yu Zhang */ 20628aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PMULT(addrp) || AHCI_ADDR_IS_PMPORT(addrp)) 20638aa6aadbSXiao-Yu Zhang SET_FIS_PMP(h2d_register_fisp, pmport); 20642fcbc377Syt 20652fcbc377Syt SET_FIS_CDMDEVCTL(h2d_register_fisp, 1); 206668d33a25Syt SET_FIS_COMMAND(h2d_register_fisp, scmd->satacmd_cmd_reg); 206768d33a25Syt SET_FIS_FEATURES(h2d_register_fisp, scmd->satacmd_features_reg); 206868d33a25Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, scmd->satacmd_sec_count_lsb); 20692fcbc377Syt 207068d33a25Syt switch (scmd->satacmd_addr_type) { 20712fcbc377Syt 207282263d52Syt case 0: 207382263d52Syt /* 207482263d52Syt * satacmd_addr_type will be 0 for the commands below: 207582263d52Syt * ATAPI command 207682263d52Syt * SATAC_IDLE_IM 207782263d52Syt * SATAC_STANDBY_IM 207882263d52Syt * SATAC_DOWNLOAD_MICROCODE 207982263d52Syt * SATAC_FLUSH_CACHE 208082263d52Syt * SATAC_SET_FEATURES 208182263d52Syt * SATAC_SMART 208282263d52Syt * SATAC_ID_PACKET_DEVICE 208382263d52Syt * SATAC_ID_DEVICE 20848aa6aadbSXiao-Yu Zhang * SATAC_READ_PORTMULT 20858aa6aadbSXiao-Yu Zhang * SATAC_WRITE_PORTMULT 208682263d52Syt */ 2087689d74b0Syt /* FALLTHRU */ 208882263d52Syt 20892fcbc377Syt case ATA_ADDR_LBA: 2090689d74b0Syt /* FALLTHRU */ 20912fcbc377Syt 20922fcbc377Syt case ATA_ADDR_LBA28: 20932fcbc377Syt /* LBA[7:0] */ 209468d33a25Syt SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb); 20952fcbc377Syt 20962fcbc377Syt /* LBA[15:8] */ 209768d33a25Syt SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb); 20982fcbc377Syt 20992fcbc377Syt /* LBA[23:16] */ 210068d33a25Syt SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb); 21012fcbc377Syt 21022fcbc377Syt /* LBA [27:24] (also called dev_head) */ 210368d33a25Syt SET_FIS_DEV_HEAD(h2d_register_fisp, scmd->satacmd_device_reg); 21042fcbc377Syt 21052fcbc377Syt break; 21062fcbc377Syt 21072fcbc377Syt case ATA_ADDR_LBA48: 21082fcbc377Syt /* LBA[7:0] */ 210968d33a25Syt SET_FIS_SECTOR(h2d_register_fisp, scmd->satacmd_lba_low_lsb); 21102fcbc377Syt 21112fcbc377Syt /* LBA[15:8] */ 211268d33a25Syt SET_FIS_CYL_LOW(h2d_register_fisp, scmd->satacmd_lba_mid_lsb); 21132fcbc377Syt 21142fcbc377Syt /* LBA[23:16] */ 211568d33a25Syt SET_FIS_CYL_HI(h2d_register_fisp, scmd->satacmd_lba_high_lsb); 21162fcbc377Syt 21172fcbc377Syt /* LBA [31:24] */ 21182fcbc377Syt SET_FIS_SECTOR_EXP(h2d_register_fisp, 211968d33a25Syt scmd->satacmd_lba_low_msb); 21202fcbc377Syt 21212fcbc377Syt /* LBA [39:32] */ 21222fcbc377Syt SET_FIS_CYL_LOW_EXP(h2d_register_fisp, 212368d33a25Syt scmd->satacmd_lba_mid_msb); 21242fcbc377Syt 21252fcbc377Syt /* LBA [47:40] */ 21262fcbc377Syt SET_FIS_CYL_HI_EXP(h2d_register_fisp, 212768d33a25Syt scmd->satacmd_lba_high_msb); 21282fcbc377Syt 21292fcbc377Syt /* Set dev_head */ 21302fcbc377Syt SET_FIS_DEV_HEAD(h2d_register_fisp, 213168d33a25Syt scmd->satacmd_device_reg); 21322fcbc377Syt 21332fcbc377Syt /* Set the extended sector count and features */ 21342fcbc377Syt SET_FIS_SECTOR_COUNT_EXP(h2d_register_fisp, 213568d33a25Syt scmd->satacmd_sec_count_msb); 21362fcbc377Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 213768d33a25Syt scmd->satacmd_features_reg_ext); 21382fcbc377Syt break; 21392fcbc377Syt } 21402fcbc377Syt 214182263d52Syt /* 214282263d52Syt * For NCQ command (READ/WRITE FPDMA QUEUED), sector count 7:0 is 214382263d52Syt * filled into features field, and sector count 8:15 is filled into 214482263d52Syt * features (exp) field. TAG is filled into sector field. 214582263d52Syt */ 214682263d52Syt if (command_type == AHCI_NCQ_CMD) { 214782263d52Syt SET_FIS_FEATURES(h2d_register_fisp, 214882263d52Syt scmd->satacmd_sec_count_lsb); 214982263d52Syt SET_FIS_FEATURES_EXP(h2d_register_fisp, 215082263d52Syt scmd->satacmd_sec_count_msb); 215182263d52Syt 215282263d52Syt SET_FIS_SECTOR_COUNT(h2d_register_fisp, 215382263d52Syt (cmd_slot << SATA_TAG_QUEUING_SHIFT)); 215482263d52Syt } 215582263d52Syt 215668d33a25Syt ncookies = scmd->satacmd_num_dma_cookies; 2157f5f2d263SFred Herard AHCIDBG(AHCIDBG_PRDT, ahci_ctlp, 21582fcbc377Syt "ncookies = 0x%x, ahci_dma_prdt_number = 0x%x", 21592fcbc377Syt ncookies, ahci_dma_prdt_number); 21602fcbc377Syt 21612fcbc377Syt ASSERT(ncookies <= ahci_dma_prdt_number); 216238547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot] = 0; 21632fcbc377Syt 21642fcbc377Syt /* *** now fill the scatter gather list ******* */ 21652fcbc377Syt for (i = 0; i < ncookies; i++) { 21662fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr = 216768d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[0]; 21682fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_data_base_addr_upper = 216968d33a25Syt scmd->satacmd_dma_cookie_list[i]._dmu._dmac_la[1]; 21702fcbc377Syt cmd_table->ahcict_prdt[i].ahcipi_descr_info = 217168d33a25Syt scmd->satacmd_dma_cookie_list[i].dmac_size - 1; 217238547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot] += 217338547057Sying tian - Beijing China scmd->satacmd_dma_cookie_list[i].dmac_size; 217468d33a25Syt } 217568d33a25Syt 2176f5f2d263SFred Herard AHCIDBG(AHCIDBG_PRDT, ahci_ctlp, 217738547057Sying tian - Beijing China "ahciport_prd_bytecounts 0x%x for cmd_slot 0x%x", 217838547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[cmd_slot], cmd_slot); 217938547057Sying tian - Beijing China 218068d33a25Syt /* The ACMD field is filled in for ATAPI command */ 218168d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) { 218268d33a25Syt bcopy(scmd->satacmd_acdb, cmd_table->ahcict_atapi_cmd, 218368d33a25Syt SATA_ATAPI_MAX_CDB_LEN); 21842fcbc377Syt } 21852fcbc377Syt 21862fcbc377Syt /* Set Command Header in Command List */ 218768d33a25Syt cmd_header = &ahci_portp->ahciport_cmd_list[cmd_slot]; 21882fcbc377Syt BZERO_DESCR_INFO(cmd_header); 21892fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 219068d33a25Syt 219168d33a25Syt /* Set the number of entries in the PRD table */ 21922fcbc377Syt SET_PRD_TABLE_LENGTH(cmd_header, ncookies); 219368d33a25Syt 219468d33a25Syt /* Set the length of the command in the CFIS area */ 21952fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, AHCI_H2D_REGISTER_FIS_LENGTH); 21962fcbc377Syt 21978aa6aadbSXiao-Yu Zhang /* 21988aa6aadbSXiao-Yu Zhang * PMP field only make sense when target is a port multiplier or a 21998aa6aadbSXiao-Yu Zhang * device behind a port multiplier. Otherwise should set it to 0. 22008aa6aadbSXiao-Yu Zhang */ 22018aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PMULT(addrp) || AHCI_ADDR_IS_PMPORT(addrp)) 22028aa6aadbSXiao-Yu Zhang SET_PORT_MULTI_PORT(cmd_header, pmport); 22038aa6aadbSXiao-Yu Zhang 2204f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "command data direction is " 22052fcbc377Syt "sata_data_direction = 0x%x", 220668d33a25Syt scmd->satacmd_flags.sata_data_direction); 220768d33a25Syt 220868d33a25Syt /* Set A bit if it is an ATAPI command */ 220968d33a25Syt if (scmd->satacmd_cmd_reg == SATAC_PACKET) 221068d33a25Syt SET_ATAPI(cmd_header, AHCI_CMDHEAD_ATAPI); 22112fcbc377Syt 221268d33a25Syt /* Set W bit if data is going to the device */ 221368d33a25Syt if (scmd->satacmd_flags.sata_data_direction == SATA_DIR_WRITE) 22142fcbc377Syt SET_WRITE(cmd_header, AHCI_CMDHEAD_DATA_WRITE); 22152fcbc377Syt 221668d33a25Syt /* 221768d33a25Syt * Set the prefetchable bit - this bit is only valid if the PRDTL 221868d33a25Syt * field is non-zero or the ATAPI 'A' bit is set in the command 221968d33a25Syt * header. This bit cannot be set when using native command 222068d33a25Syt * queuing commands or when using FIS-based switching with a Port 222182263d52Syt * multiplier. 222268d33a25Syt */ 222382263d52Syt if (command_type != AHCI_NCQ_CMD) 222482263d52Syt SET_PREFETCHABLE(cmd_header, AHCI_CMDHEAD_PREFETCHABLE); 22252fcbc377Syt 22268aa6aadbSXiao-Yu Zhang /* 22278aa6aadbSXiao-Yu Zhang * Now remember the sata packet in ahciport_slot_pkts[]. 22288aa6aadbSXiao-Yu Zhang * Error retrieval command and r/w port multiplier command will 22298aa6aadbSXiao-Yu Zhang * be stored specifically for each port. 22308aa6aadbSXiao-Yu Zhang */ 22318aa6aadbSXiao-Yu Zhang if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 22328aa6aadbSXiao-Yu Zhang !RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) 223382263d52Syt ahci_portp->ahciport_slot_pkts[cmd_slot] = spkt; 22342fcbc377Syt 22352fcbc377Syt /* 22362fcbc377Syt * We are overloading satapkt_hba_driver_private with 22372fcbc377Syt * watched_cycle count. 22382fcbc377Syt */ 22392fcbc377Syt spkt->satapkt_hba_driver_private = (void *)(intptr_t)0; 22402fcbc377Syt 224168d33a25Syt #if AHCI_DEBUG 224238547057Sying tian - Beijing China if (ahci_debug_flags & AHCIDBG_ATACMD && 224338547057Sying tian - Beijing China scmd->satacmd_cmd_reg != SATAC_PACKET || 224438547057Sying tian - Beijing China ahci_debug_flags & AHCIDBG_ATAPICMD && 224538547057Sying tian - Beijing China scmd->satacmd_cmd_reg == SATAC_PACKET) { 224638547057Sying tian - Beijing China 224738547057Sying tian - Beijing China /* Dump the command header and table */ 224838547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, "\n"); 224938547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, "Command header&table for spkt " 225038547057Sying tian - Beijing China "0x%p cmd_reg 0x%x port %d", spkt, 225138547057Sying tian - Beijing China scmd->satacmd_cmd_reg, port); 225238547057Sying tian - Beijing China ptr = (uint32_t *)cmd_header; 225338547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 225438547057Sying tian - Beijing China " Command Header:%8x %8x %8x %8x", 225538547057Sying tian - Beijing China ptr[0], ptr[1], ptr[2], ptr[3]); 225638547057Sying tian - Beijing China 225738547057Sying tian - Beijing China /* Dump the H2D register FIS */ 225838547057Sying tian - Beijing China ptr = (uint32_t *)h2d_register_fisp; 225938547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 226038547057Sying tian - Beijing China " Command FIS: %8x %8x %8x %8x", 226138547057Sying tian - Beijing China ptr[0], ptr[1], ptr[2], ptr[3]); 226238547057Sying tian - Beijing China 226338547057Sying tian - Beijing China /* Dump the ACMD register FIS */ 226438547057Sying tian - Beijing China ptr2 = (uint8_t *)&(cmd_table->ahcict_atapi_cmd); 226538547057Sying tian - Beijing China for (i = 0; i < SATA_ATAPI_MAX_CDB_LEN/8; i++) 226638547057Sying tian - Beijing China if (ahci_debug_flags & AHCIDBG_ATAPICMD) 226738547057Sying tian - Beijing China ahci_log(ahci_ctlp, CE_WARN, 226838547057Sying tian - Beijing China " ATAPI command: %2x %2x %2x %2x " 226938547057Sying tian - Beijing China "%2x %2x %2x %2x", 227038547057Sying tian - Beijing China ptr2[8 * i], ptr2[8 * i + 1], 227138547057Sying tian - Beijing China ptr2[8 * i + 2], ptr2[8 * i + 3], 227238547057Sying tian - Beijing China ptr2[8 * i + 4], ptr2[8 * i + 5], 227338547057Sying tian - Beijing China ptr2[8 * i + 6], ptr2[8 * i + 7]); 227438547057Sying tian - Beijing China 227538547057Sying tian - Beijing China /* Dump the PRDT */ 227638547057Sying tian - Beijing China for (i = 0; i < ncookies; i++) { 227738547057Sying tian - Beijing China ptr = (uint32_t *)&(cmd_table->ahcict_prdt[i]); 227868d33a25Syt ahci_log(ahci_ctlp, CE_WARN, 227938547057Sying tian - Beijing China " Cookie %d: %8x %8x %8x %8x", 228038547057Sying tian - Beijing China i, ptr[0], ptr[1], ptr[2], ptr[3]); 228138547057Sying tian - Beijing China } 228268d33a25Syt } 228368d33a25Syt #endif 228468d33a25Syt 228568d33a25Syt (void) ddi_dma_sync( 228668d33a25Syt ahci_portp->ahciport_cmd_tables_dma_handle[cmd_slot], 22872fcbc377Syt 0, 22882fcbc377Syt ahci_cmd_table_size, 22892fcbc377Syt DDI_DMA_SYNC_FORDEV); 22902fcbc377Syt 22912fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 229268d33a25Syt cmd_slot * sizeof (ahci_cmd_header_t), 22932fcbc377Syt sizeof (ahci_cmd_header_t), 22942fcbc377Syt DDI_DMA_SYNC_FORDEV); 22952fcbc377Syt 229682263d52Syt /* Set the corresponding bit in the PxSACT.DS for queued command */ 229782263d52Syt if (command_type == AHCI_NCQ_CMD) { 229882263d52Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 229982263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port), 230082263d52Syt (0x1 << cmd_slot)); 230182263d52Syt } 230282263d52Syt 23032fcbc377Syt /* Indicate to the HBA that a command is active. */ 23042fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 23052fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 230668d33a25Syt (0x1 << cmd_slot)); 23072fcbc377Syt 2308f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "ahci_deliver_satapkt " 23092fcbc377Syt "exit: port %d", port); 23102fcbc377Syt 231168d33a25Syt return (cmd_slot); 23122fcbc377Syt } 23132fcbc377Syt 23142fcbc377Syt /* 23152fcbc377Syt * Called by the sata framework to abort the previously sent packet(s). 23162fcbc377Syt * 23172fcbc377Syt * Reset device to abort commands. 23182fcbc377Syt */ 23192fcbc377Syt static int 23202fcbc377Syt ahci_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag) 23212fcbc377Syt { 23222fcbc377Syt ahci_ctl_t *ahci_ctlp; 23232fcbc377Syt ahci_port_t *ahci_portp; 232482263d52Syt uint32_t slot_status = 0; 232582263d52Syt uint32_t aborted_tags = 0; 232682263d52Syt uint32_t finished_tags = 0; 23272fcbc377Syt uint8_t cport = spkt->satapkt_device.satadev_addr.cport; 23282fcbc377Syt uint8_t port; 23292fcbc377Syt int tmp_slot; 2330a9440e8dSyt int instance = ddi_get_instance(dip); 23312fcbc377Syt 2332a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 23332fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 23342fcbc377Syt 2335f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 233668d33a25Syt "ahci_tran_abort enter: port %d", port); 23372fcbc377Syt 23382fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 23392fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 23402fcbc377Syt 23412fcbc377Syt /* 234268d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 234368d33a25Syt * commands are being mopped, therefore there is nothing else to do 23442fcbc377Syt */ 234568d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 2346f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 23472fcbc377Syt "ahci_tran_abort: port %d is in " 23482fcbc377Syt "mopping process, so just return directly ", port); 23492fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23502fcbc377Syt return (SATA_SUCCESS); 23512fcbc377Syt } 23522fcbc377Syt 23538aa6aadbSXiao-Yu Zhang /* 23548aa6aadbSXiao-Yu Zhang * If AHCI_PORT_FLAG_RDWR_PMULT flag is set, it means a R/W PMULT 23558aa6aadbSXiao-Yu Zhang * command is being executed so no other commands is outstanding, 23568aa6aadbSXiao-Yu Zhang * nothing to do. 23578aa6aadbSXiao-Yu Zhang */ 23588aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_RDWR_PMULT) { 23598aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 23608aa6aadbSXiao-Yu Zhang "ahci_tran_abort: port %d is reading/writing " 23618aa6aadbSXiao-Yu Zhang "port multiplier, so just return directly ", port); 23628aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 23638aa6aadbSXiao-Yu Zhang return (SATA_SUCCESS); 23648aa6aadbSXiao-Yu Zhang } 23658aa6aadbSXiao-Yu Zhang 23662fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 23672fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 23682fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 23692fcbc377Syt /* 23702fcbc377Syt * In case the targer driver would send the request before 23712fcbc377Syt * sata framework can have the opportunity to process those 23722fcbc377Syt * event reports. 23732fcbc377Syt */ 23742fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 23752fcbc377Syt spkt->satapkt_device.satadev_state = 23762fcbc377Syt ahci_portp->ahciport_port_state; 23772fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 23782fcbc377Syt &spkt->satapkt_device); 2379f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 23802fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 23812fcbc377Syt "port in FAILED/SHUTDOWN/PWROFF state: " 238268d33a25Syt "port: %d", port); 23832fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 23842fcbc377Syt return (SATA_FAILURE); 23852fcbc377Syt } 23862fcbc377Syt 23872fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 23882fcbc377Syt /* 23892fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 23902fcbc377Syt * AHCI_PORT_TYPE_NODEV. 23912fcbc377Syt */ 23922fcbc377Syt spkt->satapkt_reason = SATA_PKT_PORT_ERROR; 23932fcbc377Syt spkt->satapkt_device.satadev_type = SATA_DTYPE_NONE; 23942fcbc377Syt spkt->satapkt_device.satadev_state = 23952fcbc377Syt ahci_portp->ahciport_port_state; 23962fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 23972fcbc377Syt &spkt->satapkt_device); 2398f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 23992fcbc377Syt "ahci_tran_abort returning SATA_FAILURE while " 240068d33a25Syt "no device attached: port: %d", port); 24012fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24022fcbc377Syt return (SATA_FAILURE); 24032fcbc377Syt } 24042fcbc377Syt 24052fcbc377Syt if (flag == SATA_ABORT_ALL_PACKETS) { 240682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 240782263d52Syt aborted_tags = ahci_portp->ahciport_pending_tags; 2408a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 240982263d52Syt aborted_tags = ahci_portp->ahciport_pending_ncq_tags; 241082263d52Syt 2411a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort all packets", 2412a9440e8dSyt instance, port); 24132fcbc377Syt } else { 24142fcbc377Syt aborted_tags = 0xffffffff; 24152fcbc377Syt /* 241682263d52Syt * Aborting one specific packet, first search the 24172fcbc377Syt * ahciport_slot_pkts[] list for matching spkt. 24182fcbc377Syt */ 24192fcbc377Syt for (tmp_slot = 0; 24202fcbc377Syt tmp_slot < ahci_ctlp->ahcictl_num_cmd_slots; tmp_slot++) { 24212fcbc377Syt if (ahci_portp->ahciport_slot_pkts[tmp_slot] == spkt) { 24222fcbc377Syt aborted_tags = (0x1 << tmp_slot); 24232fcbc377Syt break; 24242fcbc377Syt } 24252fcbc377Syt } 24262fcbc377Syt 24272fcbc377Syt if (aborted_tags == 0xffffffff) { 24282fcbc377Syt /* request packet is not on the pending list */ 2429f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 24302fcbc377Syt "Cannot find the aborting pkt 0x%p on the " 24312fcbc377Syt "pending list", (void *)spkt); 24322fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, 24332fcbc377Syt &spkt->satapkt_device); 24342fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24352fcbc377Syt return (SATA_FAILURE); 24362fcbc377Syt } 2437a9440e8dSyt cmn_err(CE_NOTE, "!ahci%d: ahci port %d abort satapkt 0x%p", 2438a9440e8dSyt instance, port, (void *)spkt); 24392fcbc377Syt } 24402fcbc377Syt 244182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 244282263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 244382263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 2444a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 244582263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 244682263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 24472fcbc377Syt 244868d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 244968d33a25Syt ahci_portp->ahciport_mop_in_progress++; 24502fcbc377Syt 24512fcbc377Syt /* 24522fcbc377Syt * To abort the packet(s), first we are trying to clear PxCMD.ST 245368d33a25Syt * to stop the port, and if the port can be stopped 24542fcbc377Syt * successfully with PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0', 24552fcbc377Syt * then we just send back the aborted packet(s) with ABORTED flag 24562fcbc377Syt * and then restart the port by setting PxCMD.ST and PxCMD.FRE. 24572fcbc377Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then we 24582fcbc377Syt * perform a COMRESET. 24592fcbc377Syt */ 24602fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 246168d33a25Syt ahci_portp, port, NULL, NULL); 24622fcbc377Syt 24632fcbc377Syt /* 24642fcbc377Syt * Compute which have finished and which need to be retried. 24652fcbc377Syt * 246682263d52Syt * The finished tags are ahciport_pending_tags/ahciport_pending_ncq_tags 246782263d52Syt * minus the slot_status. The aborted_tags has to be deducted by 246882263d52Syt * finished_tags since we can't possibly abort a tag which had finished 246982263d52Syt * already. 24702fcbc377Syt */ 247182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 247282263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 247382263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2474a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 247582263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 247682263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 24772fcbc377Syt 24782fcbc377Syt aborted_tags &= ~finished_tags; 24792fcbc377Syt 24802fcbc377Syt ahci_mop_commands(ahci_ctlp, 24812fcbc377Syt ahci_portp, 24822fcbc377Syt slot_status, 24832fcbc377Syt 0, /* failed tags */ 24842fcbc377Syt 0, /* timeout tags */ 24852fcbc377Syt aborted_tags, 24862fcbc377Syt 0); /* reset tags */ 24872fcbc377Syt 24882fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, &spkt->satapkt_device); 24892fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 24902fcbc377Syt 24912fcbc377Syt return (SATA_SUCCESS); 24922fcbc377Syt } 24932fcbc377Syt 24942fcbc377Syt /* 24952fcbc377Syt * Used to do device reset and reject all the pending packets on a device 24962fcbc377Syt * during the reset operation. 24972fcbc377Syt * 24988aa6aadbSXiao-Yu Zhang * NOTE: ONLY called by ahci_tran_reset_dport 24992fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 25002fcbc377Syt */ 25012fcbc377Syt static int 25022fcbc377Syt ahci_reset_device_reject_pkts(ahci_ctl_t *ahci_ctlp, 25038aa6aadbSXiao-Yu Zhang ahci_port_t *ahci_portp, ahci_addr_t *addrp) 25042fcbc377Syt { 250582263d52Syt uint32_t slot_status = 0; 250682263d52Syt uint32_t reset_tags = 0; 250782263d52Syt uint32_t finished_tags = 0; 25088aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 25092fcbc377Syt sata_device_t sdevice; 25102fcbc377Syt int ret; 25112fcbc377Syt 2512f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 25132fcbc377Syt "ahci_reset_device_reject_pkts on port: %d", port); 25142fcbc377Syt 25152fcbc377Syt /* 251668d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 251768d33a25Syt * commands are being mopped, therefore there is nothing else to do 25182fcbc377Syt */ 251968d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 2520f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 25212fcbc377Syt "ahci_reset_device_reject_pkts: port %d is in " 25222fcbc377Syt "mopping process, so return directly ", port); 25232fcbc377Syt return (SATA_SUCCESS); 25242fcbc377Syt } 25252fcbc377Syt 252682263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 252782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 252882263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 252982263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2530a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 253182263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 253282263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 253382263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 253482263d52Syt } 25352fcbc377Syt 25368aa6aadbSXiao-Yu Zhang if (ahci_software_reset(ahci_ctlp, ahci_portp, addrp) 25372fcbc377Syt != AHCI_SUCCESS) { 2538f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 25392fcbc377Syt "Try to do a port reset after software " 25402fcbc377Syt "reset failed", port); 25418aa6aadbSXiao-Yu Zhang ret = ahci_port_reset(ahci_ctlp, ahci_portp, addrp); 25422fcbc377Syt if (ret != AHCI_SUCCESS) { 2543f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 25442fcbc377Syt "ahci_reset_device_reject_pkts: port %d " 25452fcbc377Syt "failed", port); 25462fcbc377Syt return (SATA_FAILURE); 25472fcbc377Syt } 25482fcbc377Syt } 25492fcbc377Syt /* Set the reset in progress flag */ 25502fcbc377Syt ahci_portp->ahciport_reset_in_progress = 1; 25512fcbc377Syt 255268d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 255368d33a25Syt ahci_portp->ahciport_mop_in_progress++; 25542fcbc377Syt 25552fcbc377Syt /* Indicate to the framework that a reset has happened */ 25562fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 255709121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 255868d33a25Syt sdevice.satadev_addr.pmport = 0; 255968d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 25602fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 25612fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 25622fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 25632fcbc377Syt sata_hba_event_notify( 25642fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 25652fcbc377Syt &sdevice, 25662fcbc377Syt SATA_EVNT_DEVICE_RESET); 25672fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 25682fcbc377Syt 2569f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 25708aa6aadbSXiao-Yu Zhang "port %d sending event up: SATA_EVNT_DEVICE_RESET", port); 25712fcbc377Syt 25722fcbc377Syt /* Next try to mop the pending commands */ 257382263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 257482263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 257582263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2576a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 257782263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 257882263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 25792fcbc377Syt 25802fcbc377Syt reset_tags &= ~finished_tags; 25812fcbc377Syt 25822fcbc377Syt ahci_mop_commands(ahci_ctlp, 25832fcbc377Syt ahci_portp, 25842fcbc377Syt slot_status, 25852fcbc377Syt 0, /* failed tags */ 25862fcbc377Syt 0, /* timeout tags */ 25872fcbc377Syt 0, /* aborted tags */ 25882fcbc377Syt reset_tags); /* reset tags */ 25892fcbc377Syt 25902fcbc377Syt return (SATA_SUCCESS); 25912fcbc377Syt } 25922fcbc377Syt 25938aa6aadbSXiao-Yu Zhang /* 25948aa6aadbSXiao-Yu Zhang * Used to do device reset and reject all the pending packets on a device 25958aa6aadbSXiao-Yu Zhang * during the reset operation. 25968aa6aadbSXiao-Yu Zhang * 25978aa6aadbSXiao-Yu Zhang * NOTE: ONLY called by ahci_tran_reset_dport 25988aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before the function is called. 25998aa6aadbSXiao-Yu Zhang */ 26008aa6aadbSXiao-Yu Zhang static int 26018aa6aadbSXiao-Yu Zhang ahci_reset_pmdevice_reject_pkts(ahci_ctl_t *ahci_ctlp, 26028aa6aadbSXiao-Yu Zhang ahci_port_t *ahci_portp, ahci_addr_t *addrp) 26038aa6aadbSXiao-Yu Zhang { 26048aa6aadbSXiao-Yu Zhang uint32_t finished_tags = 0, reset_tags = 0, slot_status = 0; 26058aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 26068aa6aadbSXiao-Yu Zhang uint8_t pmport = addrp->aa_pmport; 26078aa6aadbSXiao-Yu Zhang sata_device_t sdevice; 26088aa6aadbSXiao-Yu Zhang 26098aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_PMULT, ahci_ctlp, 26108aa6aadbSXiao-Yu Zhang "ahci_reset_pmdevice_reject_pkts at port %d:%d", port, pmport); 26118aa6aadbSXiao-Yu Zhang 26128aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 26138aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 26148aa6aadbSXiao-Yu Zhang "ahci_reset_pmdevice_reject_pkts: port %d is in " 26158aa6aadbSXiao-Yu Zhang "mopping process, so return directly ", port); 26168aa6aadbSXiao-Yu Zhang return (SATA_SUCCESS); 26178aa6aadbSXiao-Yu Zhang } 26188aa6aadbSXiao-Yu Zhang 26198aa6aadbSXiao-Yu Zhang /* Checking for outstanding commands */ 26208aa6aadbSXiao-Yu Zhang if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 26218aa6aadbSXiao-Yu Zhang slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 26228aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 26238aa6aadbSXiao-Yu Zhang reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 26248aa6aadbSXiao-Yu Zhang } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 26258aa6aadbSXiao-Yu Zhang slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 26268aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 26278aa6aadbSXiao-Yu Zhang reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 26288aa6aadbSXiao-Yu Zhang } 26298aa6aadbSXiao-Yu Zhang 26308aa6aadbSXiao-Yu Zhang /* Issue SOFTWARE reset command. */ 26318aa6aadbSXiao-Yu Zhang if (ahci_software_reset(ahci_ctlp, ahci_portp, addrp) 26328aa6aadbSXiao-Yu Zhang != AHCI_SUCCESS) { 26338aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 26348aa6aadbSXiao-Yu Zhang "Try to do a port reset after software " 26358aa6aadbSXiao-Yu Zhang "reset failed", port); 26368aa6aadbSXiao-Yu Zhang return (SATA_FAILURE); 26378aa6aadbSXiao-Yu Zhang } 26388aa6aadbSXiao-Yu Zhang 26398aa6aadbSXiao-Yu Zhang /* Set the reset in progress flag */ 26408aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_reset_in_progress = 1; 26418aa6aadbSXiao-Yu Zhang 26428aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 26438aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_mop_in_progress++; 26448aa6aadbSXiao-Yu Zhang 26458aa6aadbSXiao-Yu Zhang /* Indicate to the framework that a reset has happened */ 26468aa6aadbSXiao-Yu Zhang bzero((void *)&sdevice, sizeof (sata_device_t)); 26478aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 26488aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.pmport = pmport; 26498aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PMULT(addrp)) 26508aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.qual = SATA_ADDR_PMULT; 26518aa6aadbSXiao-Yu Zhang else 26528aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.qual = SATA_ADDR_DPMPORT; 26538aa6aadbSXiao-Yu Zhang sdevice.satadev_state = SATA_DSTATE_RESET | 26548aa6aadbSXiao-Yu Zhang SATA_DSTATE_PWR_ACTIVE; 26558aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 26568aa6aadbSXiao-Yu Zhang sata_hba_event_notify( 26578aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 26588aa6aadbSXiao-Yu Zhang &sdevice, 26598aa6aadbSXiao-Yu Zhang SATA_EVNT_DEVICE_RESET); 26608aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 26618aa6aadbSXiao-Yu Zhang 26628aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 26638aa6aadbSXiao-Yu Zhang "port %d:%d sending event up: SATA_EVNT_DEVICE_RESET", 26648aa6aadbSXiao-Yu Zhang port, pmport); 26658aa6aadbSXiao-Yu Zhang 26668aa6aadbSXiao-Yu Zhang /* Next try to mop the pending commands */ 26678aa6aadbSXiao-Yu Zhang if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 26688aa6aadbSXiao-Yu Zhang finished_tags = ahci_portp->ahciport_pending_tags & 26698aa6aadbSXiao-Yu Zhang ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 26708aa6aadbSXiao-Yu Zhang else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 26718aa6aadbSXiao-Yu Zhang finished_tags = ahci_portp->ahciport_pending_ncq_tags & 26728aa6aadbSXiao-Yu Zhang ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 26738aa6aadbSXiao-Yu Zhang reset_tags &= ~finished_tags; 26748aa6aadbSXiao-Yu Zhang 26758aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_EVENT|AHCIDBG_PMULT, ahci_ctlp, 26768aa6aadbSXiao-Yu Zhang "reset_tags = %x, finished_tags = %x, slot_status = %x", 26778aa6aadbSXiao-Yu Zhang reset_tags, finished_tags, slot_status); 26788aa6aadbSXiao-Yu Zhang 26798aa6aadbSXiao-Yu Zhang /* 26808aa6aadbSXiao-Yu Zhang * NOTE: Because PxCI be only erased by unset PxCMD.ST bit, so even we 26818aa6aadbSXiao-Yu Zhang * try to reset a single device behind a port multiplier will 26828aa6aadbSXiao-Yu Zhang * terminate all the commands on that HBA port. We need mop these 26838aa6aadbSXiao-Yu Zhang * commands as well. 26848aa6aadbSXiao-Yu Zhang */ 26858aa6aadbSXiao-Yu Zhang ahci_mop_commands(ahci_ctlp, 26868aa6aadbSXiao-Yu Zhang ahci_portp, 26878aa6aadbSXiao-Yu Zhang slot_status, 26888aa6aadbSXiao-Yu Zhang 0, /* failed tags */ 26898aa6aadbSXiao-Yu Zhang 0, /* timeout tags */ 26908aa6aadbSXiao-Yu Zhang 0, /* aborted tags */ 26918aa6aadbSXiao-Yu Zhang reset_tags); /* reset tags */ 26928aa6aadbSXiao-Yu Zhang 26938aa6aadbSXiao-Yu Zhang return (SATA_SUCCESS); 26948aa6aadbSXiao-Yu Zhang } 26958aa6aadbSXiao-Yu Zhang 26962fcbc377Syt /* 26972fcbc377Syt * Used to do port reset and reject all the pending packets on a port during 26982fcbc377Syt * the reset operation. 26992fcbc377Syt * 27002fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 27012fcbc377Syt */ 27022fcbc377Syt static int 27032fcbc377Syt ahci_reset_port_reject_pkts(ahci_ctl_t *ahci_ctlp, 27048aa6aadbSXiao-Yu Zhang ahci_port_t *ahci_portp, ahci_addr_t *addrp) 27052fcbc377Syt { 270682263d52Syt uint32_t slot_status = 0; 270782263d52Syt uint32_t reset_tags = 0; 270882263d52Syt uint32_t finished_tags = 0; 27098aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 27102fcbc377Syt 2711f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 27128aa6aadbSXiao-Yu Zhang "ahci_reset_port_reject_pkts at port: %d", port); 27132fcbc377Syt 27142fcbc377Syt /* 271568d33a25Syt * If AHCI_PORT_FLAG_MOPPING flag is set, it means all the pending 271668d33a25Syt * commands are being mopped, therefore there is nothing else to do 27172fcbc377Syt */ 271868d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 2719f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 27202fcbc377Syt "ahci_reset_port_reject_pkts: port %d is in " 27212fcbc377Syt "mopping process, so return directly ", port); 27222fcbc377Syt return (SATA_SUCCESS); 27232fcbc377Syt } 27242fcbc377Syt 272568d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 272668d33a25Syt ahci_portp->ahciport_mop_in_progress++; 27272fcbc377Syt 272882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 272982263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 273082263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 273182263d52Syt reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2732a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 273382263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 273482263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 273582263d52Syt reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 273682263d52Syt } 27372fcbc377Syt 27382fcbc377Syt if (ahci_restart_port_wait_till_ready(ahci_ctlp, 273982263d52Syt ahci_portp, port, AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 27408aa6aadbSXiao-Yu Zhang NULL) != AHCI_SUCCESS) { 27418aa6aadbSXiao-Yu Zhang 27428aa6aadbSXiao-Yu Zhang /* Clear mop flag */ 27438aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_mop_in_progress--; 27448aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_mop_in_progress == 0) 27458aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags &= 27468aa6aadbSXiao-Yu Zhang ~AHCI_PORT_FLAG_MOPPING; 27472fcbc377Syt return (SATA_FAILURE); 27488aa6aadbSXiao-Yu Zhang } 27492fcbc377Syt 275082263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 275182263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 275282263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 2753a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 275482263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 275582263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 27562fcbc377Syt 27572fcbc377Syt reset_tags &= ~finished_tags; 27582fcbc377Syt 27592fcbc377Syt ahci_mop_commands(ahci_ctlp, 27602fcbc377Syt ahci_portp, 27612fcbc377Syt slot_status, 27622fcbc377Syt 0, /* failed tags */ 27632fcbc377Syt 0, /* timeout tags */ 27642fcbc377Syt 0, /* aborted tags */ 27652fcbc377Syt reset_tags); /* reset tags */ 27662fcbc377Syt 27672fcbc377Syt return (SATA_SUCCESS); 27682fcbc377Syt } 27692fcbc377Syt 27702fcbc377Syt /* 27712fcbc377Syt * Used to do hba reset and reject all the pending packets on all ports 27722fcbc377Syt * during the reset operation. 27732fcbc377Syt */ 27742fcbc377Syt static int 27752fcbc377Syt ahci_reset_hba_reject_pkts(ahci_ctl_t *ahci_ctlp) 27762fcbc377Syt { 27772fcbc377Syt ahci_port_t *ahci_portp; 27782fcbc377Syt uint32_t slot_status[AHCI_MAX_PORTS]; 27792fcbc377Syt uint32_t reset_tags[AHCI_MAX_PORTS]; 27802fcbc377Syt uint32_t finished_tags[AHCI_MAX_PORTS]; 27818aa6aadbSXiao-Yu Zhang int port; 27822fcbc377Syt int ret = SATA_SUCCESS; 27832fcbc377Syt 2784f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 2785f5f2d263SFred Herard "ahci_reset_hba_reject_pkts enter", NULL); 27862fcbc377Syt 27878aa6aadbSXiao-Yu Zhang bzero(slot_status, sizeof (slot_status)); 27888aa6aadbSXiao-Yu Zhang bzero(reset_tags, sizeof (reset_tags)); 27898aa6aadbSXiao-Yu Zhang bzero(finished_tags, sizeof (finished_tags)); 27908aa6aadbSXiao-Yu Zhang 27912fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 27922fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 27932fcbc377Syt continue; 27942fcbc377Syt } 27952fcbc377Syt 27962fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 27972fcbc377Syt 27982fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 27998aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_reset_in_progress = 1; 280082263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 280182263d52Syt slot_status[port] = ddi_get32( 280282263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 280382263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 280482263d52Syt reset_tags[port] = slot_status[port] & 280582263d52Syt AHCI_SLOT_MASK(ahci_ctlp); 28068aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 28078aa6aadbSXiao-Yu Zhang "port %d: reset_tags = 0x%x pending_tags = 0x%x", 28088aa6aadbSXiao-Yu Zhang port, reset_tags[port], 28098aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pending_tags); 2810a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 281182263d52Syt slot_status[port] = ddi_get32( 281282263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 281382263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 281482263d52Syt reset_tags[port] = slot_status[port] & 281582263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 28168aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 28178aa6aadbSXiao-Yu Zhang "port %d: reset_tags = 0x%x pending_tags = 0x%x", 28188aa6aadbSXiao-Yu Zhang port, reset_tags[port], 28198aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pending_tags); 282082263d52Syt } 28212fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 28222fcbc377Syt } 28232fcbc377Syt 28242fcbc377Syt if (ahci_hba_reset(ahci_ctlp) != AHCI_SUCCESS) { 28252fcbc377Syt ret = SATA_FAILURE; 28262fcbc377Syt } 28272fcbc377Syt 28282fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 28292fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 28302fcbc377Syt continue; 28312fcbc377Syt } 28322fcbc377Syt 28332fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 28342fcbc377Syt 28352fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 28362fcbc377Syt /* 28372fcbc377Syt * To prevent recursive enter to ahci_mop_commands, we need 283868d33a25Syt * check AHCI_PORT_FLAG_MOPPING flag. 28392fcbc377Syt */ 284068d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 2841f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 28422fcbc377Syt "ahci_reset_hba_reject_pkts: port %d is in " 28432fcbc377Syt "mopping process, so return directly ", port); 28442fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 28452fcbc377Syt continue; 28462fcbc377Syt } 28472fcbc377Syt 284868d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 284968d33a25Syt ahci_portp->ahciport_mop_in_progress++; 28502fcbc377Syt 285182263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 285282263d52Syt finished_tags[port] = 285382263d52Syt ahci_portp->ahciport_pending_tags & 285482263d52Syt ~slot_status[port] & AHCI_SLOT_MASK(ahci_ctlp); 2855a9440e8dSyt else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 285682263d52Syt finished_tags[port] = 285782263d52Syt ahci_portp->ahciport_pending_ncq_tags & 285882263d52Syt ~slot_status[port] & AHCI_NCQ_SLOT_MASK(ahci_portp); 28592fcbc377Syt 28602fcbc377Syt reset_tags[port] &= ~finished_tags[port]; 28612fcbc377Syt 28622fcbc377Syt ahci_mop_commands(ahci_ctlp, 28632fcbc377Syt ahci_portp, 28642fcbc377Syt slot_status[port], 28652fcbc377Syt 0, /* failed tags */ 28662fcbc377Syt 0, /* timeout tags */ 28672fcbc377Syt 0, /* aborted tags */ 28682fcbc377Syt reset_tags[port]); /* reset tags */ 286968d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 28702fcbc377Syt } 28712fcbc377Syt out: 28722fcbc377Syt return (ret); 28732fcbc377Syt } 28742fcbc377Syt 28752fcbc377Syt /* 28762fcbc377Syt * Called by sata framework to reset a port(s) or device. 28772fcbc377Syt */ 28782fcbc377Syt static int 28792fcbc377Syt ahci_tran_reset_dport(dev_info_t *dip, sata_device_t *sd) 28802fcbc377Syt { 28812fcbc377Syt ahci_ctl_t *ahci_ctlp; 28822fcbc377Syt ahci_port_t *ahci_portp; 28838aa6aadbSXiao-Yu Zhang ahci_addr_t addr; 28842fcbc377Syt uint8_t cport = sd->satadev_addr.cport; 28858aa6aadbSXiao-Yu Zhang uint8_t pmport = sd->satadev_addr.pmport; 28862fcbc377Syt uint8_t port; 28872fcbc377Syt int ret = SATA_SUCCESS; 2888a9440e8dSyt int instance = ddi_get_instance(dip); 28892fcbc377Syt 2890a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 28912fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 28928aa6aadbSXiao-Yu Zhang ahci_portp = ahci_ctlp->ahcictl_ports[port]; 28938aa6aadbSXiao-Yu Zhang 28948aa6aadbSXiao-Yu Zhang ahci_get_ahci_addr(ahci_ctlp, sd, &addr); 28952fcbc377Syt 2896f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 28978aa6aadbSXiao-Yu Zhang "ahci_tran_reset_dport enter: cport %d", cport); 28982fcbc377Syt 28992fcbc377Syt switch (sd->satadev_addr.qual) { 29008aa6aadbSXiao-Yu Zhang case SATA_ADDR_PMPORT: 29018aa6aadbSXiao-Yu Zhang /* 29028aa6aadbSXiao-Yu Zhang * If we want to issue a COMRESET on a pmport, we need to 29038aa6aadbSXiao-Yu Zhang * reject the outstanding commands on that pmport. According 29048aa6aadbSXiao-Yu Zhang * to AHCI spec, PxCI register could only be cleared by 29058aa6aadbSXiao-Yu Zhang * clearing PxCMD.ST, which will halt the controller port - as 29068aa6aadbSXiao-Yu Zhang * well as other pmports. 29078aa6aadbSXiao-Yu Zhang * 29088aa6aadbSXiao-Yu Zhang * Therefore we directly reset the controller port for 29098aa6aadbSXiao-Yu Zhang * simplicity. ahci_tran_probe_port() will handle reset stuff 29108aa6aadbSXiao-Yu Zhang * like initializing the given pmport. 29118aa6aadbSXiao-Yu Zhang */ 29128aa6aadbSXiao-Yu Zhang /* FALLTHRU */ 29132fcbc377Syt case SATA_ADDR_CPORT: 29142fcbc377Syt /* Port reset */ 29152fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 29168aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport " 29178aa6aadbSXiao-Yu Zhang "port %d reset port", instance, port); 29182fcbc377Syt 29192fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 29208aa6aadbSXiao-Yu Zhang ret = ahci_reset_port_reject_pkts(ahci_ctlp, ahci_portp, &addr); 29212fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 29222fcbc377Syt 29232fcbc377Syt break; 29242fcbc377Syt 29258aa6aadbSXiao-Yu Zhang case SATA_ADDR_DPMPORT: 29268aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport " 29278aa6aadbSXiao-Yu Zhang "port %d:%d reset device", instance, port, pmport); 29288aa6aadbSXiao-Yu Zhang /* FALLTHRU */ 29292fcbc377Syt case SATA_ADDR_DCPORT: 29302fcbc377Syt /* Device reset */ 29318aa6aadbSXiao-Yu Zhang if (sd->satadev_addr.qual == SATA_ADDR_DCPORT) 29328aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport " 29338aa6aadbSXiao-Yu Zhang "port %d reset device", instance, port); 29342fcbc377Syt 29352fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 29368aa6aadbSXiao-Yu Zhang /* 29378aa6aadbSXiao-Yu Zhang * software reset request must be sent to SATA_PMULT_HOSTPORT 29388aa6aadbSXiao-Yu Zhang * if target is a port multiplier: 29398aa6aadbSXiao-Yu Zhang */ 29408aa6aadbSXiao-Yu Zhang if (sd->satadev_addr.qual == SATA_ADDR_DCPORT && 29418aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_device_type == SATA_DTYPE_PMULT) 29428aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PMULT(&addr, port); 29438aa6aadbSXiao-Yu Zhang 29442fcbc377Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED | 29452fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_SHUTDOWN | 29462fcbc377Syt ahci_portp->ahciport_port_state & SATA_PSTATE_PWROFF) { 29472fcbc377Syt /* 29482fcbc377Syt * In case the targer driver would send the request 29492fcbc377Syt * before sata framework can have the opportunity to 29502fcbc377Syt * process those event reports. 29512fcbc377Syt */ 29522fcbc377Syt sd->satadev_state = ahci_portp->ahciport_port_state; 29532fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 2954f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 29552fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 29562fcbc377Syt "while port in FAILED/SHUTDOWN/PWROFF state: " 295768d33a25Syt "port: %d", port); 29582fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 29592fcbc377Syt ret = SATA_FAILURE; 29602fcbc377Syt break; 29612fcbc377Syt } 29622fcbc377Syt 29638aa6aadbSXiao-Yu Zhang if (AHCIPORT_GET_DEV_TYPE(ahci_portp, &addr) == 29648aa6aadbSXiao-Yu Zhang SATA_DTYPE_NONE) { 29652fcbc377Syt /* 29662fcbc377Syt * ahci_intr_phyrdy_change() may have rendered it to 29672fcbc377Syt * AHCI_PORT_TYPE_NODEV. 29682fcbc377Syt */ 29692fcbc377Syt sd->satadev_type = SATA_DTYPE_NONE; 29708aa6aadbSXiao-Yu Zhang sd->satadev_state = AHCIPORT_GET_STATE(ahci_portp, 29718aa6aadbSXiao-Yu Zhang &addr); 29722fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, sd); 2973f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 29742fcbc377Syt "ahci_tran_reset_dport returning SATA_FAILURE " 297568d33a25Syt "while no device attached: port: %d", port); 29762fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 29772fcbc377Syt ret = SATA_FAILURE; 29782fcbc377Syt break; 29792fcbc377Syt } 29802fcbc377Syt 29818aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PORT(&addr)) { 29828aa6aadbSXiao-Yu Zhang ret = ahci_reset_device_reject_pkts(ahci_ctlp, 29838aa6aadbSXiao-Yu Zhang ahci_portp, &addr); 29848aa6aadbSXiao-Yu Zhang } else { 29858aa6aadbSXiao-Yu Zhang ret = ahci_reset_pmdevice_reject_pkts(ahci_ctlp, 29868aa6aadbSXiao-Yu Zhang ahci_portp, &addr); 29878aa6aadbSXiao-Yu Zhang } 29888aa6aadbSXiao-Yu Zhang 29892fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 29902fcbc377Syt break; 29912fcbc377Syt 29922fcbc377Syt case SATA_ADDR_CNTRL: 29932fcbc377Syt /* Reset the whole controller */ 29948aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "!ahci%d: ahci_tran_reset_dport " 29958aa6aadbSXiao-Yu Zhang "reset the whole hba", instance); 29962fcbc377Syt ret = ahci_reset_hba_reject_pkts(ahci_ctlp); 29972fcbc377Syt break; 29982fcbc377Syt 29992fcbc377Syt default: 30002fcbc377Syt ret = SATA_FAILURE; 30012fcbc377Syt } 30022fcbc377Syt 30032fcbc377Syt return (ret); 30042fcbc377Syt } 30052fcbc377Syt 30062fcbc377Syt /* 30072fcbc377Syt * Called by sata framework to activate a port as part of hotplug. 30082fcbc377Syt * (cfgadm -c connect satax/y) 30098aa6aadbSXiao-Yu Zhang * Support port multiplier. 30102fcbc377Syt */ 30112fcbc377Syt static int 30122fcbc377Syt ahci_tran_hotplug_port_activate(dev_info_t *dip, sata_device_t *satadev) 30132fcbc377Syt { 30142fcbc377Syt ahci_ctl_t *ahci_ctlp; 30152fcbc377Syt ahci_port_t *ahci_portp; 30168aa6aadbSXiao-Yu Zhang ahci_addr_t addr; 30172fcbc377Syt uint8_t cport = satadev->satadev_addr.cport; 30188aa6aadbSXiao-Yu Zhang uint8_t pmport = satadev->satadev_addr.pmport; 30192fcbc377Syt uint8_t port; 3020a9440e8dSyt int instance = ddi_get_instance(dip); 30212fcbc377Syt 3022a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 30232fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 30242fcbc377Syt 3025f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 30268aa6aadbSXiao-Yu Zhang "ahci_tran_hotplug_port_activate enter: cport %d", cport); 30272fcbc377Syt 30282fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 30292fcbc377Syt 30302fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 30318aa6aadbSXiao-Yu Zhang ahci_get_ahci_addr(ahci_ctlp, satadev, &addr); 30328aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_PORT(&addr) || AHCI_ADDR_IS_PMPORT(&addr)); 30332fcbc377Syt 30348aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PORT(&addr)) { 30358aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "!ahci%d: ahci port %d is activated", 30368aa6aadbSXiao-Yu Zhang instance, port); 30372fcbc377Syt 30388aa6aadbSXiao-Yu Zhang /* Enable the interrupts on the port */ 30398aa6aadbSXiao-Yu Zhang ahci_enable_port_intrs(ahci_ctlp, port); 30408aa6aadbSXiao-Yu Zhang 30418aa6aadbSXiao-Yu Zhang /* 30428aa6aadbSXiao-Yu Zhang * Reset the port so that the PHY communication would be 30438aa6aadbSXiao-Yu Zhang * re-established. But this reset is an internal operation 30448aa6aadbSXiao-Yu Zhang * and the sata module doesn't need to know about it. 30458aa6aadbSXiao-Yu Zhang * Moreover, the port with a device attached will be started 30468aa6aadbSXiao-Yu Zhang * too. 30478aa6aadbSXiao-Yu Zhang */ 30488aa6aadbSXiao-Yu Zhang (void) ahci_restart_port_wait_till_ready(ahci_ctlp, 30498aa6aadbSXiao-Yu Zhang ahci_portp, port, 30508aa6aadbSXiao-Yu Zhang AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, 30518aa6aadbSXiao-Yu Zhang NULL); 30528aa6aadbSXiao-Yu Zhang 30538aa6aadbSXiao-Yu Zhang /* 30548aa6aadbSXiao-Yu Zhang * Need to check the link status and device status of the port 30558aa6aadbSXiao-Yu Zhang * and consider raising power if the port was in D3 state 30568aa6aadbSXiao-Yu Zhang */ 30578aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_state |= SATA_PSTATE_PWRON; 30588aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_state &= ~SATA_PSTATE_PWROFF; 30598aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_state &= ~SATA_PSTATE_SHUTDOWN; 30608aa6aadbSXiao-Yu Zhang } else if (AHCI_ADDR_IS_PMPORT(&addr)) { 30618aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "!ahci%d: ahci port %d:%d is activated", 30628aa6aadbSXiao-Yu Zhang instance, port, pmport); 30638aa6aadbSXiao-Yu Zhang /* AHCI_ADDR_PMPORT */ 30648aa6aadbSXiao-Yu Zhang AHCIPORT_PMSTATE(ahci_portp, &addr) |= SATA_PSTATE_PWRON; 30658aa6aadbSXiao-Yu Zhang AHCIPORT_PMSTATE(ahci_portp, &addr) &= 30668aa6aadbSXiao-Yu Zhang ~(SATA_PSTATE_PWROFF|SATA_PSTATE_SHUTDOWN); 30678aa6aadbSXiao-Yu Zhang } 306868d33a25Syt 306968d33a25Syt satadev->satadev_state = ahci_portp->ahciport_port_state; 30702fcbc377Syt 30712fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 30722fcbc377Syt 30732fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 30742fcbc377Syt return (SATA_SUCCESS); 30752fcbc377Syt } 30762fcbc377Syt 30772fcbc377Syt /* 30782fcbc377Syt * Called by sata framework to deactivate a port as part of hotplug. 30792fcbc377Syt * (cfgadm -c disconnect satax/y) 30808aa6aadbSXiao-Yu Zhang * Support port multiplier. 30812fcbc377Syt */ 30822fcbc377Syt static int 30832fcbc377Syt ahci_tran_hotplug_port_deactivate(dev_info_t *dip, sata_device_t *satadev) 30842fcbc377Syt { 30852fcbc377Syt ahci_ctl_t *ahci_ctlp; 30862fcbc377Syt ahci_port_t *ahci_portp; 30878aa6aadbSXiao-Yu Zhang ahci_addr_t addr; 30888aa6aadbSXiao-Yu Zhang uint8_t cport = satadev->satadev_addr.cport; 30898aa6aadbSXiao-Yu Zhang uint8_t pmport = satadev->satadev_addr.pmport; 30902fcbc377Syt uint8_t port; 309168d33a25Syt uint32_t port_scontrol; 3092a9440e8dSyt int instance = ddi_get_instance(dip); 30932fcbc377Syt 3094a9440e8dSyt ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 30952fcbc377Syt port = ahci_ctlp->ahcictl_cport_to_port[cport]; 30962fcbc377Syt 3097f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 30988aa6aadbSXiao-Yu Zhang "ahci_tran_hotplug_port_deactivate enter: cport %d", cport); 30992fcbc377Syt 31002fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 31012fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 31028aa6aadbSXiao-Yu Zhang ahci_get_ahci_addr(ahci_ctlp, satadev, &addr); 31038aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_PORT(&addr) || AHCI_ADDR_IS_PMPORT(&addr)); 31042fcbc377Syt 31058aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PORT(&addr)) { 31068aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "!ahci%d: ahci port %d is deactivated", 31078aa6aadbSXiao-Yu Zhang instance, port); 31082fcbc377Syt 31098aa6aadbSXiao-Yu Zhang /* Disable the interrupts on the port */ 31108aa6aadbSXiao-Yu Zhang ahci_disable_port_intrs(ahci_ctlp, port); 311168d33a25Syt 31128aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) { 31132fcbc377Syt 31148aa6aadbSXiao-Yu Zhang /* First to abort all the pending commands */ 31158aa6aadbSXiao-Yu Zhang ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 31162fcbc377Syt 31178aa6aadbSXiao-Yu Zhang /* Then stop the port */ 31188aa6aadbSXiao-Yu Zhang (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 31198aa6aadbSXiao-Yu Zhang ahci_portp, port); 31208aa6aadbSXiao-Yu Zhang } 312168d33a25Syt 31228aa6aadbSXiao-Yu Zhang /* Next put the PHY offline */ 31238aa6aadbSXiao-Yu Zhang port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 31248aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 31258aa6aadbSXiao-Yu Zhang SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_DISABLE); 31268aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, (uint32_t *) 31278aa6aadbSXiao-Yu Zhang AHCI_PORT_PxSCTL(ahci_ctlp, port), port_scontrol); 31288aa6aadbSXiao-Yu Zhang } else if (AHCI_ADDR_IS_PMPORT(&addr)) { 31298aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "!ahci%d: ahci port %d:%d is deactivated", 31308aa6aadbSXiao-Yu Zhang instance, port, pmport); 313168d33a25Syt 31328aa6aadbSXiao-Yu Zhang ahci_disable_port_intrs(ahci_ctlp, port); 31338aa6aadbSXiao-Yu Zhang if (AHCIPORT_GET_DEV_TYPE(ahci_portp, &addr) 31348aa6aadbSXiao-Yu Zhang != SATA_DTYPE_NONE) 31358aa6aadbSXiao-Yu Zhang ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 31368aa6aadbSXiao-Yu Zhang 31378aa6aadbSXiao-Yu Zhang /* Re-enable the interrupts for the other pmports */ 31388aa6aadbSXiao-Yu Zhang ahci_enable_port_intrs(ahci_ctlp, port); 31398aa6aadbSXiao-Yu Zhang } 31408aa6aadbSXiao-Yu Zhang 31418aa6aadbSXiao-Yu Zhang /* Update port state */ 31428aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, &addr, SATA_PSTATE_SHUTDOWN); 31438aa6aadbSXiao-Yu Zhang satadev->satadev_state = SATA_PSTATE_SHUTDOWN; 31442fcbc377Syt 31452fcbc377Syt ahci_update_sata_registers(ahci_ctlp, port, satadev); 31462fcbc377Syt 31472fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 31482fcbc377Syt return (SATA_SUCCESS); 31492fcbc377Syt } 31502fcbc377Syt 31512fcbc377Syt /* 315268d33a25Syt * To be used to mark all the outstanding pkts with SATA_PKT_ABORTED 31532fcbc377Syt * when a device is unplugged or a port is deactivated. 31542fcbc377Syt * 31552fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 31562fcbc377Syt */ 31572fcbc377Syt static void 31582fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctl_t *ahci_ctlp, 31592fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 31602fcbc377Syt { 316182263d52Syt uint32_t slot_status = 0; 316282263d52Syt uint32_t abort_tags = 0; 31632fcbc377Syt 3164f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 31658aa6aadbSXiao-Yu Zhang "ahci_reject_all_abort_pkts at port: %d", port); 31668aa6aadbSXiao-Yu Zhang 31678aa6aadbSXiao-Yu Zhang /* Read/write port multiplier command takes highest priority */ 31688aa6aadbSXiao-Yu Zhang if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 31698aa6aadbSXiao-Yu Zhang slot_status = 0x1; 31708aa6aadbSXiao-Yu Zhang abort_tags = 0x1; 31718aa6aadbSXiao-Yu Zhang goto out; 31728aa6aadbSXiao-Yu Zhang } 31732fcbc377Syt 317482263d52Syt /* 317582263d52Syt * When AHCI_PORT_FLAG_MOPPING is set, we need to check whether a 317682263d52Syt * REQUEST SENSE command or READ LOG EXT command is delivered to HBA 317782263d52Syt * to get the error data, if yes when the device is removed, the 317882263d52Syt * command needs to be aborted too. 317982263d52Syt */ 318082263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) { 318182263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 318282263d52Syt slot_status = 0x1; 318382263d52Syt abort_tags = 0x1; 318482263d52Syt goto out; 318582263d52Syt } else { 3186f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 318782263d52Syt "ahci_reject_all_abort_pkts return directly " 318882263d52Syt "port %d no needs to reject any outstanding " 318982263d52Syt "commands", port); 319082263d52Syt return; 319182263d52Syt } 319282263d52Syt } 31932fcbc377Syt 319482263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 319582263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 319682263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 319782263d52Syt abort_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 3198a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 319982263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 320082263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 320182263d52Syt abort_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 320282263d52Syt } 320382263d52Syt 320482263d52Syt out: 320568d33a25Syt /* No need to do mop when there is no outstanding commands */ 320668d33a25Syt if (slot_status != 0) { 320768d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 320868d33a25Syt ahci_portp->ahciport_mop_in_progress++; 32092fcbc377Syt 321068d33a25Syt ahci_mop_commands(ahci_ctlp, 321168d33a25Syt ahci_portp, 321268d33a25Syt slot_status, 321368d33a25Syt 0, /* failed tags */ 321468d33a25Syt 0, /* timeout tags */ 321568d33a25Syt abort_tags, /* aborting tags */ 321668d33a25Syt 0); /* reset tags */ 321768d33a25Syt } 321868d33a25Syt } 321968d33a25Syt 322068d33a25Syt #if defined(__lock_lint) 322168d33a25Syt static int 322268d33a25Syt ahci_selftest(dev_info_t *dip, sata_device_t *device) 322368d33a25Syt { 32242fcbc377Syt return (SATA_SUCCESS); 32252fcbc377Syt } 32262fcbc377Syt #endif 32272fcbc377Syt 32282fcbc377Syt /* 322968d33a25Syt * Allocate the ports structure, only called by ahci_attach 323068d33a25Syt */ 323168d33a25Syt static int 323268d33a25Syt ahci_alloc_ports_state(ahci_ctl_t *ahci_ctlp) 323368d33a25Syt { 3234f68cbde1Syt int port, cport = 0; 323568d33a25Syt 3236f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 3237f5f2d263SFred Herard "ahci_alloc_ports_state enter", NULL); 323868d33a25Syt 323968d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 324068d33a25Syt 324168d33a25Syt /* Allocate structures only for the implemented ports */ 3242f68cbde1Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 324368d33a25Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 3244f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 324568d33a25Syt "hba port %d not implemented", port); 324668d33a25Syt continue; 324768d33a25Syt } 324868d33a25Syt 324968d33a25Syt ahci_ctlp->ahcictl_cport_to_port[cport] = (uint8_t)port; 3250f68cbde1Syt ahci_ctlp->ahcictl_port_to_cport[port] = 3251f68cbde1Syt (uint8_t)cport++; 325268d33a25Syt 325368d33a25Syt if (ahci_alloc_port_state(ahci_ctlp, port) != AHCI_SUCCESS) { 325468d33a25Syt goto err_out; 325568d33a25Syt } 325668d33a25Syt } 325768d33a25Syt 325868d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 325968d33a25Syt return (AHCI_SUCCESS); 326068d33a25Syt 326168d33a25Syt err_out: 326268d33a25Syt for (port--; port >= 0; port--) { 326368d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 326468d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 326568d33a25Syt } 326668d33a25Syt } 326768d33a25Syt 326868d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 326968d33a25Syt return (AHCI_FAILURE); 327068d33a25Syt } 327168d33a25Syt 327268d33a25Syt /* 327368d33a25Syt * Reverse of ahci_alloc_ports_state(), only called by ahci_detach 327468d33a25Syt */ 327568d33a25Syt static void 327668d33a25Syt ahci_dealloc_ports_state(ahci_ctl_t *ahci_ctlp) 327768d33a25Syt { 327868d33a25Syt int port; 327968d33a25Syt 328068d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 328168d33a25Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 328268d33a25Syt /* if this port is implemented by the HBA */ 328368d33a25Syt if (AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) 328468d33a25Syt ahci_dealloc_port_state(ahci_ctlp, port); 328568d33a25Syt } 328668d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 328768d33a25Syt } 328868d33a25Syt 3289f8a673adSying tian - Beijing China /* 3290f8a673adSying tian - Beijing China * Drain the taskq. 3291f8a673adSying tian - Beijing China */ 3292f8a673adSying tian - Beijing China static void 3293f8a673adSying tian - Beijing China ahci_drain_ports_taskq(ahci_ctl_t *ahci_ctlp) 3294f8a673adSying tian - Beijing China { 3295f8a673adSying tian - Beijing China ahci_port_t *ahci_portp; 3296f8a673adSying tian - Beijing China int port; 3297f8a673adSying tian - Beijing China 3298f8a673adSying tian - Beijing China for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 3299f8a673adSying tian - Beijing China if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 3300f8a673adSying tian - Beijing China continue; 3301f8a673adSying tian - Beijing China } 3302f8a673adSying tian - Beijing China 3303f8a673adSying tian - Beijing China ahci_portp = ahci_ctlp->ahcictl_ports[port]; 3304f8a673adSying tian - Beijing China 3305f8a673adSying tian - Beijing China mutex_enter(&ahci_portp->ahciport_mutex); 3306f8a673adSying tian - Beijing China ddi_taskq_wait(ahci_portp->ahciport_event_taskq); 3307f8a673adSying tian - Beijing China mutex_exit(&ahci_portp->ahciport_mutex); 3308f8a673adSying tian - Beijing China } 3309f8a673adSying tian - Beijing China } 3310f8a673adSying tian - Beijing China 331168d33a25Syt /* 331213bcbb7aSyt * Initialize the controller and all ports. And then try to start the ports 331313bcbb7aSyt * if there are devices attached. 33142fcbc377Syt * 33152fcbc377Syt * This routine can be called from three seperate cases: DDI_ATTACH, 33162fcbc377Syt * PM_LEVEL_D0 and DDI_RESUME. The DDI_ATTACH case is different from 331768d33a25Syt * other two cases; device signature probing are attempted only during 331868d33a25Syt * DDI_ATTACH case. 33192fcbc377Syt * 33202fcbc377Syt * WARNING!!! Disable the whole controller's interrupts before calling and 33212fcbc377Syt * the interrupts will be enabled upon successfully return. 33222fcbc377Syt */ 33232fcbc377Syt static int 33242fcbc377Syt ahci_initialize_controller(ahci_ctl_t *ahci_ctlp) 33252fcbc377Syt { 33262fcbc377Syt ahci_port_t *ahci_portp; 33278aa6aadbSXiao-Yu Zhang ahci_addr_t addr; 33282fcbc377Syt uint32_t ghc_control; 332968d33a25Syt int port; 33302fcbc377Syt 3331f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 3332f5f2d263SFred Herard "ahci_initialize_controller enter", NULL); 33332fcbc377Syt 33342fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 33352fcbc377Syt 33362fcbc377Syt /* 33372fcbc377Syt * Indicate that system software is AHCI aware by setting 33382fcbc377Syt * GHC.AE to 1 33392fcbc377Syt */ 33402fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 33412fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 33422fcbc377Syt 33432fcbc377Syt ghc_control |= AHCI_HBA_GHC_AE; 33442fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 33452fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), 33462fcbc377Syt ghc_control); 33472fcbc377Syt 334882263d52Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 334982263d52Syt 33502fcbc377Syt /* Initialize the implemented ports and structures */ 33512fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 33522fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 33532fcbc377Syt continue; 33542fcbc377Syt } 33552fcbc377Syt 33562fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 33572fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 33582fcbc377Syt 33592fcbc377Syt /* 33602fcbc377Syt * Ensure that the controller is not in the running state 33612fcbc377Syt * by checking every implemented port's PxCMD register 33622fcbc377Syt */ 33638aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PORT(&addr, (uint8_t)port); 33648aa6aadbSXiao-Yu Zhang 33658aa6aadbSXiao-Yu Zhang if (ahci_initialize_port(ahci_ctlp, ahci_portp, &addr) 33662fcbc377Syt != AHCI_SUCCESS) { 3367f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 33682fcbc377Syt "ahci_initialize_controller: failed to " 33692fcbc377Syt "initialize port %d", port); 33702fcbc377Syt /* 33712fcbc377Syt * Set the port state to SATA_PSTATE_FAILED if 33722fcbc377Syt * failed to initialize it. 33732fcbc377Syt */ 337468d33a25Syt ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 33752fcbc377Syt } 33762fcbc377Syt 33772fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 33782fcbc377Syt } 33792fcbc377Syt 33802fcbc377Syt /* Enable the whole controller interrupts */ 338182263d52Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 33822fcbc377Syt ahci_enable_all_intrs(ahci_ctlp); 33832fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 33842fcbc377Syt 33852fcbc377Syt return (AHCI_SUCCESS); 33862fcbc377Syt } 33872fcbc377Syt 33882fcbc377Syt /* 338968d33a25Syt * Reverse of ahci_initialize_controller() 33902fcbc377Syt * 339113bcbb7aSyt * We only need to stop the ports and disable the interrupt. 33922fcbc377Syt */ 33932fcbc377Syt static void 339468d33a25Syt ahci_uninitialize_controller(ahci_ctl_t *ahci_ctlp) 33952fcbc377Syt { 339613bcbb7aSyt ahci_port_t *ahci_portp; 339713bcbb7aSyt int port; 339813bcbb7aSyt 3399f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 3400f5f2d263SFred Herard "ahci_uninitialize_controller enter", NULL); 34012fcbc377Syt 34022fcbc377Syt /* disable all the interrupts. */ 340313bcbb7aSyt mutex_enter(&ahci_ctlp->ahcictl_mutex); 34042fcbc377Syt ahci_disable_all_intrs(ahci_ctlp); 340513bcbb7aSyt mutex_exit(&ahci_ctlp->ahcictl_mutex); 340613bcbb7aSyt 340713bcbb7aSyt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 340813bcbb7aSyt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 340913bcbb7aSyt continue; 341013bcbb7aSyt } 341113bcbb7aSyt 341213bcbb7aSyt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 341313bcbb7aSyt 341413bcbb7aSyt /* Stop the port by clearing PxCMD.ST */ 341513bcbb7aSyt mutex_enter(&ahci_portp->ahciport_mutex); 341613bcbb7aSyt 341713bcbb7aSyt /* 341813bcbb7aSyt * Here we must disable the port interrupt because 341913bcbb7aSyt * ahci_disable_all_intrs only clear GHC.IE, and IS 342013bcbb7aSyt * register will be still set if PxIE is enabled. 342113bcbb7aSyt * When ahci shares one IRQ with other drivers, the 342213bcbb7aSyt * intr handler may claim the intr mistakenly. 342313bcbb7aSyt */ 3424689d74b0Syt ahci_disable_port_intrs(ahci_ctlp, port); 342513bcbb7aSyt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 342613bcbb7aSyt ahci_portp, port); 342713bcbb7aSyt mutex_exit(&ahci_portp->ahciport_mutex); 342813bcbb7aSyt } 34292fcbc377Syt } 34302fcbc377Syt 34312fcbc377Syt /* 34328aa6aadbSXiao-Yu Zhang * ahci_alloc_pmult() 34338aa6aadbSXiao-Yu Zhang * 1. Setting HBA port registers which are necessary for a port multiplier. 34348aa6aadbSXiao-Yu Zhang * (Set PxCMD.PMA while PxCMD.ST is '0') 34358aa6aadbSXiao-Yu Zhang * 2. Allocate ahci_pmult_info structure. 34368aa6aadbSXiao-Yu Zhang * 34378aa6aadbSXiao-Yu Zhang * NOTE: Must stop port before the function is called. 34388aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before the function is 34398aa6aadbSXiao-Yu Zhang * called. 34408aa6aadbSXiao-Yu Zhang */ 34418aa6aadbSXiao-Yu Zhang static void 34428aa6aadbSXiao-Yu Zhang ahci_alloc_pmult(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 34438aa6aadbSXiao-Yu Zhang { 34448aa6aadbSXiao-Yu Zhang uint32_t port_cmd_status; 34458aa6aadbSXiao-Yu Zhang uint8_t port = ahci_portp->ahciport_port_num; 34468aa6aadbSXiao-Yu Zhang 34478aa6aadbSXiao-Yu Zhang port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34488aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 34498aa6aadbSXiao-Yu Zhang 34508aa6aadbSXiao-Yu Zhang /* The port must have been stopped before. */ 34518aa6aadbSXiao-Yu Zhang ASSERT(!(port_cmd_status & AHCI_CMD_STATUS_ST)); 34528aa6aadbSXiao-Yu Zhang 34538aa6aadbSXiao-Yu Zhang if (!(port_cmd_status & AHCI_CMD_STATUS_PMA)) { 34548aa6aadbSXiao-Yu Zhang /* set PMA bit */ 34558aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 34568aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 34578aa6aadbSXiao-Yu Zhang port_cmd_status|AHCI_CMD_STATUS_PMA); 34588aa6aadbSXiao-Yu Zhang 34598aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_PMULT, ahci_ctlp, 34608aa6aadbSXiao-Yu Zhang "ahci_alloc_pmult: " 34618aa6aadbSXiao-Yu Zhang "PxCMD.PMA bit set at port %d.", port); 34628aa6aadbSXiao-Yu Zhang } 34638aa6aadbSXiao-Yu Zhang 34648aa6aadbSXiao-Yu Zhang /* Allocate port multiplier information structure */ 34658aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_pmult_info == NULL) { 34668aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pmult_info = (ahci_pmult_info_t *) 34678aa6aadbSXiao-Yu Zhang kmem_zalloc(sizeof (ahci_pmult_info_t), KM_SLEEP); 34688aa6aadbSXiao-Yu Zhang } 34698aa6aadbSXiao-Yu Zhang 34708aa6aadbSXiao-Yu Zhang ASSERT(ahci_portp->ahciport_pmult_info != NULL); 34718aa6aadbSXiao-Yu Zhang } 34728aa6aadbSXiao-Yu Zhang 34738aa6aadbSXiao-Yu Zhang /* 34748aa6aadbSXiao-Yu Zhang * ahci_dealloc_pmult() 34758aa6aadbSXiao-Yu Zhang * 1. Clearing related registers when a port multiplier is detached. 34768aa6aadbSXiao-Yu Zhang * (Clear PxCMD.PMA while PxCMD.ST is '0') 34778aa6aadbSXiao-Yu Zhang * 2. Deallocate ahci_pmult_info structure. 34788aa6aadbSXiao-Yu Zhang * 34798aa6aadbSXiao-Yu Zhang * NOTE: Must stop port before the function is called. 34808aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before the function is 34818aa6aadbSXiao-Yu Zhang * called. 34828aa6aadbSXiao-Yu Zhang */ 34838aa6aadbSXiao-Yu Zhang static void 34848aa6aadbSXiao-Yu Zhang ahci_dealloc_pmult(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 34858aa6aadbSXiao-Yu Zhang { 34868aa6aadbSXiao-Yu Zhang uint32_t port_cmd_status; 34878aa6aadbSXiao-Yu Zhang uint8_t port = ahci_portp->ahciport_port_num; 34888aa6aadbSXiao-Yu Zhang 34898aa6aadbSXiao-Yu Zhang port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 34908aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 34918aa6aadbSXiao-Yu Zhang 34928aa6aadbSXiao-Yu Zhang if (port_cmd_status & AHCI_CMD_STATUS_PMA) { 34938aa6aadbSXiao-Yu Zhang /* Clear PMA bit */ 34948aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 34958aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 34968aa6aadbSXiao-Yu Zhang (port_cmd_status & (~AHCI_CMD_STATUS_PMA))); 34978aa6aadbSXiao-Yu Zhang 34988aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_PMULT, ahci_ctlp, 34998aa6aadbSXiao-Yu Zhang "ahci_dealloc_pmult: " 35008aa6aadbSXiao-Yu Zhang "PxCMD.PMA bit cleared at port %d.", port); 35018aa6aadbSXiao-Yu Zhang } 35028aa6aadbSXiao-Yu Zhang 35038aa6aadbSXiao-Yu Zhang /* Release port multiplier information structure */ 35048aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_pmult_info != NULL) { 35058aa6aadbSXiao-Yu Zhang kmem_free(ahci_portp->ahciport_pmult_info, 35068aa6aadbSXiao-Yu Zhang sizeof (ahci_pmult_info_t)); 35078aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pmult_info = NULL; 35088aa6aadbSXiao-Yu Zhang } 35098aa6aadbSXiao-Yu Zhang } 35108aa6aadbSXiao-Yu Zhang 35118aa6aadbSXiao-Yu Zhang /* 35128aa6aadbSXiao-Yu Zhang * The routine is to initialize a port. First put the port in NOTRunning 35132fcbc377Syt * state, then enable port interrupt and clear Serror register. And under 35142fcbc377Syt * AHCI_ATTACH case, find device signature and then try to start the port. 35152fcbc377Syt * 35168aa6aadbSXiao-Yu Zhang * Called by 35178aa6aadbSXiao-Yu Zhang * 1. ahci_initialize_controller 35188aa6aadbSXiao-Yu Zhang * 2. ahci_intr_phyrdy_change (hotplug) 35198aa6aadbSXiao-Yu Zhang * 352082263d52Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 35212fcbc377Syt */ 35222fcbc377Syt static int 35238aa6aadbSXiao-Yu Zhang ahci_initialize_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 35248aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp) 35252fcbc377Syt { 35260a4c4cecSXiao-Yu Zhang uint32_t port_sstatus, port_task_file, port_cmd_status; 35278aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 35288aa6aadbSXiao-Yu Zhang boolean_t pm_mode = B_TRUE; /* Power Management mode */ 35290a4c4cecSXiao-Yu Zhang int ret; 35302fcbc377Syt 35318aa6aadbSXiao-Yu Zhang ASSERT(mutex_owned(&ahci_portp->ahciport_mutex)); 35328aa6aadbSXiao-Yu Zhang 35338aa6aadbSXiao-Yu Zhang /* AHCI_ADDR_PORT: We've no idea of the attached device here. */ 35348aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_PORT(addrp)); 35358aa6aadbSXiao-Yu Zhang 35362fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35372fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 35382fcbc377Syt 3539f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 35408aa6aadbSXiao-Yu Zhang "ahci_initialize_port: port %d ", port); 35418aa6aadbSXiao-Yu Zhang 35422fcbc377Syt /* 35432fcbc377Syt * Check whether the port is in NotRunning state, if not, 35442fcbc377Syt * put the port in NotRunning state 35452fcbc377Syt */ 35460a4c4cecSXiao-Yu Zhang if (port_cmd_status & 35472fcbc377Syt (AHCI_CMD_STATUS_ST | 35482fcbc377Syt AHCI_CMD_STATUS_CR | 35492fcbc377Syt AHCI_CMD_STATUS_FRE | 35500a4c4cecSXiao-Yu Zhang AHCI_CMD_STATUS_FR)) { 35510a4c4cecSXiao-Yu Zhang (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 35520a4c4cecSXiao-Yu Zhang ahci_portp, port); 35532fcbc377Syt } 35542fcbc377Syt 35558aa6aadbSXiao-Yu Zhang /* Disable interrupt */ 35568aa6aadbSXiao-Yu Zhang ahci_disable_port_intrs(ahci_ctlp, port); 35578aa6aadbSXiao-Yu Zhang 35580a4c4cecSXiao-Yu Zhang /* Device is unknown at first */ 35598aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, addrp, SATA_DTYPE_UNKNOWN); 35602fcbc377Syt 3561e2d9bfa6Sying tian - Beijing China /* Disable the interface power management */ 3562e2d9bfa6Sying tian - Beijing China ahci_disable_interface_pm(ahci_ctlp, port); 3563e2d9bfa6Sying tian - Beijing China 35640a4c4cecSXiao-Yu Zhang port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35650a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 35660a4c4cecSXiao-Yu Zhang port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 35670a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 35680a4c4cecSXiao-Yu Zhang 35690a4c4cecSXiao-Yu Zhang /* Check physcial link status */ 35700a4c4cecSXiao-Yu Zhang if (SSTATUS_GET_IPM(port_sstatus) == SSTATUS_IPM_NODEV_NOPHYCOM || 35710a4c4cecSXiao-Yu Zhang SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_NOPHYCOM || 35720a4c4cecSXiao-Yu Zhang 35730a4c4cecSXiao-Yu Zhang /* Check interface status */ 35740a4c4cecSXiao-Yu Zhang port_task_file & AHCI_TFD_STS_BSY || 35757fb8b96eSying tian - Beijing China port_task_file & AHCI_TFD_STS_DRQ || 35767fb8b96eSying tian - Beijing China 35777fb8b96eSying tian - Beijing China /* Check whether port reset must be executed */ 35787fb8b96eSying tian - Beijing China ahci_ctlp->ahcictl_cap & AHCI_CAP_INIT_PORT_RESET) { 35790a4c4cecSXiao-Yu Zhang 35808aa6aadbSXiao-Yu Zhang /* Something went wrong, we need do some reset things */ 35818aa6aadbSXiao-Yu Zhang ret = ahci_port_reset(ahci_ctlp, ahci_portp, addrp); 35820a4c4cecSXiao-Yu Zhang 35830a4c4cecSXiao-Yu Zhang /* Does port reset succeed on HBA port? */ 35840a4c4cecSXiao-Yu Zhang if (ret != AHCI_SUCCESS) { 3585f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ERRS, ahci_ctlp, 35860a4c4cecSXiao-Yu Zhang "ahci_initialize_port:" 35870a4c4cecSXiao-Yu Zhang "port reset faild at port %d", port); 35880a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 35890a4c4cecSXiao-Yu Zhang } 35900a4c4cecSXiao-Yu Zhang 35910a4c4cecSXiao-Yu Zhang /* Is port failed? */ 35928aa6aadbSXiao-Yu Zhang if (AHCIPORT_GET_STATE(ahci_portp, addrp) & 35938aa6aadbSXiao-Yu Zhang SATA_PSTATE_FAILED) { 3594f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ERRS, ahci_ctlp, 35950a4c4cecSXiao-Yu Zhang "ahci_initialize_port: port %d state 0x%x", 35960a4c4cecSXiao-Yu Zhang port, ahci_portp->ahciport_port_state); 35970a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 35980a4c4cecSXiao-Yu Zhang } 35990a4c4cecSXiao-Yu Zhang } 36008aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, addrp, SATA_STATE_READY); 3601f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "port %d is ready now.", port); 36022fcbc377Syt 36032fcbc377Syt /* 360468d33a25Syt * At the time being, only probe ports/devices and get the types of 360513bcbb7aSyt * attached devices during DDI_ATTACH. In fact, the device can be 360613bcbb7aSyt * changed during power state changes, but at the time being, we 360713bcbb7aSyt * don't support the situation. 36082fcbc377Syt */ 36098aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 36108aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_ctlp->ahcictl_mutex); 36118aa6aadbSXiao-Yu Zhang if (ahci_ctlp->ahcictl_flags & AHCI_ATTACH) 36128aa6aadbSXiao-Yu Zhang pm_mode = B_FALSE; 36138aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_ctlp->ahcictl_mutex); 36148aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 36158aa6aadbSXiao-Yu Zhang 36168aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_HOTPLUG) 36178aa6aadbSXiao-Yu Zhang pm_mode = B_FALSE; 36188aa6aadbSXiao-Yu Zhang 36198aa6aadbSXiao-Yu Zhang if (!pm_mode) { 36200a4c4cecSXiao-Yu Zhang /* 3621259105bcSying tian - Beijing China * Try to get the device signature if the port is 3622259105bcSying tian - Beijing China * not empty. 36230a4c4cecSXiao-Yu Zhang */ 3624259105bcSying tian - Beijing China if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) 36258aa6aadbSXiao-Yu Zhang ahci_find_dev_signature(ahci_ctlp, ahci_portp, addrp); 362613bcbb7aSyt } else { 36272fcbc377Syt 362813bcbb7aSyt /* 362913bcbb7aSyt * During the resume, we need to set the PxCLB, PxCLBU, PxFB 363013bcbb7aSyt * and PxFBU registers in case these registers were cleared 363113bcbb7aSyt * during the suspend. 363213bcbb7aSyt */ 3633f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 363413bcbb7aSyt "ahci_initialize_port: port %d " 363513bcbb7aSyt "reset the port during resume", port); 36368aa6aadbSXiao-Yu Zhang (void) ahci_port_reset(ahci_ctlp, ahci_portp, addrp); 36372fcbc377Syt 3638f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 363913bcbb7aSyt "ahci_initialize_port: port %d " 364013bcbb7aSyt "set PxCLB, PxCLBU, PxFB and PxFBU " 364113bcbb7aSyt "during resume", port); 364213bcbb7aSyt 364313bcbb7aSyt /* Config Port Received FIS Base Address */ 364413bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 364513bcbb7aSyt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 364613bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 364713bcbb7aSyt 364813bcbb7aSyt /* Config Port Command List Base Address */ 364913bcbb7aSyt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 365013bcbb7aSyt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 365113bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 365213bcbb7aSyt } 365313bcbb7aSyt 365413bcbb7aSyt /* Return directly if no device connected */ 365513bcbb7aSyt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 3656f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 365713bcbb7aSyt "No device connected to port %d", port); 365813bcbb7aSyt goto out; 365913bcbb7aSyt } 366013bcbb7aSyt 36618aa6aadbSXiao-Yu Zhang /* If this is a port multiplier, we need do some initialization */ 36628aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type == SATA_DTYPE_PMULT) { 36638aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 36648aa6aadbSXiao-Yu Zhang "Port multiplier found at port %d", port); 36658aa6aadbSXiao-Yu Zhang ahci_alloc_pmult(ahci_ctlp, ahci_portp); 36668aa6aadbSXiao-Yu Zhang } 36678aa6aadbSXiao-Yu Zhang 366813bcbb7aSyt /* Try to start the port */ 366913bcbb7aSyt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 367013bcbb7aSyt != AHCI_SUCCESS) { 3671f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 367213bcbb7aSyt "failed to start port %d", port); 367313bcbb7aSyt return (AHCI_FAILURE); 36742fcbc377Syt } 367568d33a25Syt out: 367668d33a25Syt /* Enable port interrupts */ 3677689d74b0Syt ahci_enable_port_intrs(ahci_ctlp, port); 36782fcbc377Syt 36792fcbc377Syt return (AHCI_SUCCESS); 36802fcbc377Syt } 36812fcbc377Syt 368213bcbb7aSyt /* 3683259105bcSying tian - Beijing China * Handle hardware defect, and check the capabilities. For example, 3684259105bcSying tian - Beijing China * power management capabilty and MSI capability. 368513bcbb7aSyt */ 368613bcbb7aSyt static int 368713bcbb7aSyt ahci_config_space_init(ahci_ctl_t *ahci_ctlp) 368813bcbb7aSyt { 3689689d74b0Syt ushort_t caps_ptr, cap_count, cap; 3690689d74b0Syt #if AHCI_DEBUG 3691689d74b0Syt ushort_t pmcap, pmcsr; 36922c742e1fSying tian - Beijing China ushort_t msimc; 3693689d74b0Syt #endif 369413bcbb7aSyt uint8_t revision; 369513bcbb7aSyt 3696*17ac46baSying tian - Beijing China ahci_ctlp->ahcictl_venid = 3697*17ac46baSying tian - Beijing China pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 369813bcbb7aSyt PCI_CONF_VENID); 369913bcbb7aSyt 3700*17ac46baSying tian - Beijing China ahci_ctlp->ahcictl_devid = 3701*17ac46baSying tian - Beijing China pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 3702a9440e8dSyt PCI_CONF_DEVID); 3703a9440e8dSyt 370413bcbb7aSyt /* 370513bcbb7aSyt * Modify dma_attr_align of ahcictl_buffer_dma_attr. For VT8251, those 370613bcbb7aSyt * controllers with 0x00 revision id work on 4-byte aligned buffer, 370713bcbb7aSyt * which is a bug and was fixed after 0x00 revision id controllers. 370813bcbb7aSyt * 370913bcbb7aSyt * Moreover, VT8251 cannot use multiple command slots in the command 371013bcbb7aSyt * list for non-queued commands because the previous register content 371113bcbb7aSyt * of PxCI can be re-written in the register write, so a flag will be 371213bcbb7aSyt * set to record this defect - AHCI_CAP_NO_MCMDLIST_NONQUEUE. 371313bcbb7aSyt */ 3714*17ac46baSying tian - Beijing China if (ahci_ctlp->ahcictl_venid == VIA_VENID) { 371513bcbb7aSyt revision = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 371613bcbb7aSyt PCI_CONF_REVID); 3717f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 371813bcbb7aSyt "revision id = 0x%x", revision); 371913bcbb7aSyt if (revision == 0x00) { 372013bcbb7aSyt ahci_ctlp->ahcictl_buffer_dma_attr.dma_attr_align = 0x4; 3721f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 3722f5f2d263SFred Herard "change ddi_attr_align to 0x4", NULL); 372313bcbb7aSyt } 372413bcbb7aSyt 372513bcbb7aSyt ahci_ctlp->ahcictl_cap = AHCI_CAP_NO_MCMDLIST_NONQUEUE; 3726f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 372713bcbb7aSyt "VT8251 cannot use multiple command lists for " 3728f5f2d263SFred Herard "non-queued commands", NULL); 372913bcbb7aSyt } 373013bcbb7aSyt 3731a9440e8dSyt /* 37325ce0c8ceSying tian - Beijing China * AMD/ATI SB600 (0x1002,0x4380) AHCI chipset doesn't support 64-bit 37335ce0c8ceSying tian - Beijing China * DMA addressing for communication memory descriptors though S64A bit 3734*17ac46baSying tian - Beijing China * of CAP register declares it supports. Even though 64-bit DMA for 3735*17ac46baSying tian - Beijing China * data buffer works on ASUS M2A-VM with newer BIOS, three other 3736*17ac46baSying tian - Beijing China * motherboards are known not, so both AHCI_CAP_BUF_32BIT_DMA and 3737*17ac46baSying tian - Beijing China * AHCI_CAP_COMMU_32BIT_DMA are set for this controller. 37385ce0c8ceSying tian - Beijing China * 37395ce0c8ceSying tian - Beijing China * Due to certain hardware issue, the chipset must do port reset during 37405ce0c8ceSying tian - Beijing China * initialization, otherwise, when retrieving device signature, 37415ce0c8ceSying tian - Beijing China * software reset will get time out. So AHCI_CAP_INIT_PORT_RESET flag 37425ce0c8ceSying tian - Beijing China * need to set. 37437fb8b96eSying tian - Beijing China * 37445ce0c8ceSying tian - Beijing China * For this chipset software reset will get failure if the pmport of 37455ce0c8ceSying tian - Beijing China * Register FIS was set with SATA_PMULT_HOSTPORT (0xf) and no port 37465ce0c8ceSying tian - Beijing China * multiplier is connected to the port. In order to fix the issue, 37475ce0c8ceSying tian - Beijing China * AHCI_CAP_SRST_NO_HOSTPORT flag need to be set, and once software 37485ce0c8ceSying tian - Beijing China * reset got failure, the driver will try to do another software reset 37495ce0c8ceSying tian - Beijing China * with pmport 0. 3750259105bcSying tian - Beijing China */ 3751*17ac46baSying tian - Beijing China if (ahci_ctlp->ahcictl_venid == 0x1002 && 3752*17ac46baSying tian - Beijing China ahci_ctlp->ahcictl_devid == 0x4380) { 3753*17ac46baSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_BUF_32BIT_DMA; 3754259105bcSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_COMMU_32BIT_DMA; 37557fb8b96eSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_INIT_PORT_RESET; 37565ce0c8ceSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_SRST_NO_HOSTPORT; 37575ce0c8ceSying tian - Beijing China 37585ce0c8ceSying tian - Beijing China AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 3759*17ac46baSying tian - Beijing China "ATI SB600 cannot do 64-bit DMA for both data buffer and " 3760*17ac46baSying tian - Beijing China "communication memory descriptors though CAP indicates " 3761*17ac46baSying tian - Beijing China "support, so force it to use 32-bit DMA", NULL); 37625ce0c8ceSying tian - Beijing China AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 37635ce0c8ceSying tian - Beijing China "ATI SB600 need to do a port reset during initialization", 37645ce0c8ceSying tian - Beijing China NULL); 37655ce0c8ceSying tian - Beijing China AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 37665ce0c8ceSying tian - Beijing China "ATI SB600 will get software reset failure if pmport " 37675ce0c8ceSying tian - Beijing China "is set 0xf and no port multiplier is attached", NULL); 3768259105bcSying tian - Beijing China } 3769259105bcSying tian - Beijing China 3770259105bcSying tian - Beijing China /* 37715ce0c8ceSying tian - Beijing China * AMD/ATI SB700/710/750/800 and SP5100 AHCI chipset share the same 37725ce0c8ceSying tian - Beijing China * vendor ID and device ID (0x1002,0x4391). 37735ce0c8ceSying tian - Beijing China * 37745ce0c8ceSying tian - Beijing China * SB700/750 AHCI chipset on some boards doesn't support 64-bit 3775259105bcSying tian - Beijing China * DMA addressing for communication memory descriptors though S64A bit 3776259105bcSying tian - Beijing China * of CAP register declares the support. However, it does support 37775ce0c8ceSying tian - Beijing China * 64-bit DMA for data buffer. So only AHCI_CAP_COMMU_32BIT_DMA is 37785ce0c8ceSying tian - Beijing China * set for this controller. 37795ce0c8ceSying tian - Beijing China * 37805ce0c8ceSying tian - Beijing China * SB710 has the same initialization issue as SB600, so it also need 37815ce0c8ceSying tian - Beijing China * a port reset. That is AHCI_CAP_INIT_PORT_RESET need to set for it. 37825ce0c8ceSying tian - Beijing China * 37835ce0c8ceSying tian - Beijing China * SB700 also has the same issue about software reset, and thus 37845ce0c8ceSying tian - Beijing China * AHCI_CAP_SRST_NO_HOSTPORT flag also is needed. 3785a9440e8dSyt */ 3786*17ac46baSying tian - Beijing China if (ahci_ctlp->ahcictl_venid == 0x1002 && 3787*17ac46baSying tian - Beijing China ahci_ctlp->ahcictl_devid == 0x4391) { 37885ce0c8ceSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_COMMU_32BIT_DMA; 37895ce0c8ceSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_INIT_PORT_RESET; 37905ce0c8ceSying tian - Beijing China ahci_ctlp->ahcictl_cap |= AHCI_CAP_SRST_NO_HOSTPORT; 37915ce0c8ceSying tian - Beijing China 3792f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 3793259105bcSying tian - Beijing China "ATI SB700/750 cannot do 64-bit DMA for communication " 3794259105bcSying tian - Beijing China "memory descriptors though CAP indicates support, " 3795f5f2d263SFred Herard "so force it to use 32-bit DMA", NULL); 37965ce0c8ceSying tian - Beijing China AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 37975ce0c8ceSying tian - Beijing China "ATI SB710 need to do a port reset during initialization", 37985ce0c8ceSying tian - Beijing China NULL); 37995ce0c8ceSying tian - Beijing China AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 38005ce0c8ceSying tian - Beijing China "ATI SB700 will get software reset failure if pmport " 38015ce0c8ceSying tian - Beijing China "is set 0xf and no port multiplier is attached", NULL); 3802a9440e8dSyt } 3803a9440e8dSyt 380413bcbb7aSyt /* 380513bcbb7aSyt * Check if capabilities list is supported and if so, 380613bcbb7aSyt * get initial capabilities pointer and clear bits 0,1. 380713bcbb7aSyt */ 380813bcbb7aSyt if (pci_config_get16(ahci_ctlp->ahcictl_pci_conf_handle, 380913bcbb7aSyt PCI_CONF_STAT) & PCI_STAT_CAP) { 381013bcbb7aSyt caps_ptr = P2ALIGN(pci_config_get8( 381113bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 381213bcbb7aSyt PCI_CONF_CAP_PTR), 4); 381313bcbb7aSyt } else { 381413bcbb7aSyt caps_ptr = PCI_CAP_NEXT_PTR_NULL; 381513bcbb7aSyt } 381613bcbb7aSyt 381713bcbb7aSyt /* 381813bcbb7aSyt * Walk capabilities if supported. 381913bcbb7aSyt */ 382013bcbb7aSyt for (cap_count = 0; caps_ptr != PCI_CAP_NEXT_PTR_NULL; ) { 382113bcbb7aSyt 382213bcbb7aSyt /* 382313bcbb7aSyt * Check that we haven't exceeded the maximum number of 382413bcbb7aSyt * capabilities and that the pointer is in a valid range. 382513bcbb7aSyt */ 382613bcbb7aSyt if (++cap_count > PCI_CAP_MAX_PTR) { 3827f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 3828f5f2d263SFred Herard "too many device capabilities", NULL); 382913bcbb7aSyt return (AHCI_FAILURE); 383013bcbb7aSyt } 383113bcbb7aSyt if (caps_ptr < PCI_CAP_PTR_OFF) { 3832f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 383313bcbb7aSyt "capabilities pointer 0x%x out of range", 383413bcbb7aSyt caps_ptr); 383513bcbb7aSyt return (AHCI_FAILURE); 383613bcbb7aSyt } 383713bcbb7aSyt 383813bcbb7aSyt /* 383913bcbb7aSyt * Get next capability and check that it is valid. 384013bcbb7aSyt * For now, we only support power management. 384113bcbb7aSyt */ 384213bcbb7aSyt cap = pci_config_get8(ahci_ctlp->ahcictl_pci_conf_handle, 384313bcbb7aSyt caps_ptr); 384413bcbb7aSyt switch (cap) { 384513bcbb7aSyt case PCI_CAP_ID_PM: 384613bcbb7aSyt 384713bcbb7aSyt /* power management supported */ 384813bcbb7aSyt ahci_ctlp->ahcictl_cap |= AHCI_CAP_PM; 384913bcbb7aSyt 385013bcbb7aSyt /* Save PMCSR offset */ 385113bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset = caps_ptr + PCI_PMCSR; 385213bcbb7aSyt 3853689d74b0Syt #if AHCI_DEBUG 385413bcbb7aSyt pmcap = pci_config_get16( 385513bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 385613bcbb7aSyt caps_ptr + PCI_PMCAP); 385713bcbb7aSyt pmcsr = pci_config_get16( 385813bcbb7aSyt ahci_ctlp->ahcictl_pci_conf_handle, 385913bcbb7aSyt ahci_ctlp->ahcictl_pmcsr_offset); 3860f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 386113bcbb7aSyt "Power Management capability found PCI_PMCAP " 386213bcbb7aSyt "= 0x%x PCI_PMCSR = 0x%x", pmcap, pmcsr); 386313bcbb7aSyt if ((pmcap & 0x3) == 0x3) 3864f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 386513bcbb7aSyt "PCI Power Management Interface " 3866f5f2d263SFred Herard "spec 1.2 compliant", NULL); 386713bcbb7aSyt #endif 386813bcbb7aSyt break; 386913bcbb7aSyt 387013bcbb7aSyt case PCI_CAP_ID_MSI: 38712c742e1fSying tian - Beijing China #if AHCI_DEBUG 38722c742e1fSying tian - Beijing China msimc = pci_config_get16( 38732c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_pci_conf_handle, 38742c742e1fSying tian - Beijing China caps_ptr + PCI_MSI_CTRL); 3875f5f2d263SFred Herard AHCIDBG(AHCIDBG_MSI, ahci_ctlp, 38762c742e1fSying tian - Beijing China "Message Signaled Interrupt capability found " 38772c742e1fSying tian - Beijing China "MSICAP_MC.MMC = 0x%x", (msimc & 0xe) >> 1); 38782c742e1fSying tian - Beijing China #endif 3879f5f2d263SFred Herard AHCIDBG(AHCIDBG_MSI, ahci_ctlp, 3880f5f2d263SFred Herard "MSI capability found", NULL); 388113bcbb7aSyt break; 388213bcbb7aSyt 388313bcbb7aSyt case PCI_CAP_ID_PCIX: 3884f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3885f5f2d263SFred Herard "PCI-X capability found", NULL); 388613bcbb7aSyt break; 388713bcbb7aSyt 388813bcbb7aSyt case PCI_CAP_ID_PCI_E: 3889f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3890f5f2d263SFred Herard "PCI Express capability found", NULL); 389113bcbb7aSyt break; 389213bcbb7aSyt 389313bcbb7aSyt case PCI_CAP_ID_MSI_X: 3894f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3895f5f2d263SFred Herard "MSI-X capability found", NULL); 389613bcbb7aSyt break; 389713bcbb7aSyt 389813bcbb7aSyt case PCI_CAP_ID_SATA: 3899f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3900f5f2d263SFred Herard "SATA capability found", NULL); 390113bcbb7aSyt break; 390213bcbb7aSyt 3903a9440e8dSyt case PCI_CAP_ID_VS: 3904f5f2d263SFred Herard AHCIDBG(AHCIDBG_PM, ahci_ctlp, 3905f5f2d263SFred Herard "Vendor Specific capability found", NULL); 3906a9440e8dSyt break; 3907a9440e8dSyt 39088aa6aadbSXiao-Yu Zhang default: 39098aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_PM, ahci_ctlp, 39108aa6aadbSXiao-Yu Zhang "unrecognized capability 0x%x", cap); 39118aa6aadbSXiao-Yu Zhang break; 39128aa6aadbSXiao-Yu Zhang } 39138aa6aadbSXiao-Yu Zhang 39148aa6aadbSXiao-Yu Zhang /* 39158aa6aadbSXiao-Yu Zhang * Get next capabilities pointer and clear bits 0,1. 39168aa6aadbSXiao-Yu Zhang */ 39178aa6aadbSXiao-Yu Zhang caps_ptr = P2ALIGN(pci_config_get8( 39188aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_pci_conf_handle, 39198aa6aadbSXiao-Yu Zhang (caps_ptr + PCI_CAP_NEXT_PTR)), 4); 39208aa6aadbSXiao-Yu Zhang } 39218aa6aadbSXiao-Yu Zhang 39228aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 39238aa6aadbSXiao-Yu Zhang } 39248aa6aadbSXiao-Yu Zhang 39258aa6aadbSXiao-Yu Zhang /* 39268aa6aadbSXiao-Yu Zhang * Read/Write a register at port multiplier by SATA READ PORTMULT / SATA WRITE 39278aa6aadbSXiao-Yu Zhang * PORTMULT command. SYNC & POLLING mode is used. 39288aa6aadbSXiao-Yu Zhang * 39298aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before the function 39308aa6aadbSXiao-Yu Zhang * is called. 39318aa6aadbSXiao-Yu Zhang */ 39328aa6aadbSXiao-Yu Zhang static int 39338aa6aadbSXiao-Yu Zhang ahci_rdwr_pmult(ahci_ctl_t *ahci_ctlp, ahci_addr_t *addrp, 39348aa6aadbSXiao-Yu Zhang uint8_t regn, uint32_t *pregv, uint8_t type) 39358aa6aadbSXiao-Yu Zhang { 39368aa6aadbSXiao-Yu Zhang ahci_port_t *ahci_portp; 39378aa6aadbSXiao-Yu Zhang ahci_addr_t pmult_addr; 39388aa6aadbSXiao-Yu Zhang sata_pkt_t *spkt; 39398aa6aadbSXiao-Yu Zhang sata_cmd_t *scmd; 39408aa6aadbSXiao-Yu Zhang sata_device_t sata_device; 39418aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 39428aa6aadbSXiao-Yu Zhang uint8_t pmport = addrp->aa_pmport; 39438aa6aadbSXiao-Yu Zhang uint8_t cport; 39448aa6aadbSXiao-Yu Zhang uint32_t intr_mask; 39458aa6aadbSXiao-Yu Zhang int rval; 39468aa6aadbSXiao-Yu Zhang char portstr[10]; 39478aa6aadbSXiao-Yu Zhang 39488aa6aadbSXiao-Yu Zhang SET_PORTSTR(portstr, addrp); 39498aa6aadbSXiao-Yu Zhang cport = ahci_ctlp->ahcictl_port_to_cport[port]; 39508aa6aadbSXiao-Yu Zhang ahci_portp = ahci_ctlp->ahcictl_ports[port]; 39518aa6aadbSXiao-Yu Zhang 39528aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_PMPORT(addrp) || AHCI_ADDR_IS_PMULT(addrp)); 39538aa6aadbSXiao-Yu Zhang ASSERT(mutex_owned(&ahci_portp->ahciport_mutex)); 39548aa6aadbSXiao-Yu Zhang 39558aa6aadbSXiao-Yu Zhang /* Check the existence of the port multiplier */ 39568aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type != SATA_DTYPE_PMULT) 39578aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 39588aa6aadbSXiao-Yu Zhang 39598aa6aadbSXiao-Yu Zhang /* Request a READ/WRITE PORTMULT sata packet. */ 39608aa6aadbSXiao-Yu Zhang bzero(&sata_device, sizeof (sata_device_t)); 39618aa6aadbSXiao-Yu Zhang sata_device.satadev_addr.cport = cport; 39628aa6aadbSXiao-Yu Zhang sata_device.satadev_addr.pmport = pmport; 39638aa6aadbSXiao-Yu Zhang sata_device.satadev_addr.qual = SATA_ADDR_PMULT; 39648aa6aadbSXiao-Yu Zhang sata_device.satadev_rev = SATA_DEVICE_REV; 39658aa6aadbSXiao-Yu Zhang 39668aa6aadbSXiao-Yu Zhang /* 39678aa6aadbSXiao-Yu Zhang * Make sure no command is outstanding here. All R/W PMULT requests 39688aa6aadbSXiao-Yu Zhang * come from 39698aa6aadbSXiao-Yu Zhang * 39708aa6aadbSXiao-Yu Zhang * 1. ahci_attach() 39718aa6aadbSXiao-Yu Zhang * The port should be empty. 39728aa6aadbSXiao-Yu Zhang * 39738aa6aadbSXiao-Yu Zhang * 2. ahci_tran_probe_port() 39748aa6aadbSXiao-Yu Zhang * Any request from SATA framework (via ahci_tran_start) should be 39758aa6aadbSXiao-Yu Zhang * rejected if R/W PMULT command is outstanding. 39768aa6aadbSXiao-Yu Zhang * 39778aa6aadbSXiao-Yu Zhang * If we are doing mopping, do not check those flags because no 39788aa6aadbSXiao-Yu Zhang * command will be actually outstanding. 39798aa6aadbSXiao-Yu Zhang * 39808aa6aadbSXiao-Yu Zhang * If the port has been occupied by any other commands, the probe 39818aa6aadbSXiao-Yu Zhang * function will return a SATA_RETRY. SATA framework will retry 39828aa6aadbSXiao-Yu Zhang * later. 39838aa6aadbSXiao-Yu Zhang */ 39848aa6aadbSXiao-Yu Zhang if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 39858aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PMULT, ahci_ctlp, 39868aa6aadbSXiao-Yu Zhang "R/W PMULT failed: R/W PMULT in progress at port %d.", 39878aa6aadbSXiao-Yu Zhang port, ahci_portp->ahciport_flags); 39888aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 39898aa6aadbSXiao-Yu Zhang } 39908aa6aadbSXiao-Yu Zhang 39918aa6aadbSXiao-Yu Zhang if (!(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) && ( 39928aa6aadbSXiao-Yu Zhang ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) || 39938aa6aadbSXiao-Yu Zhang NCQ_CMD_IN_PROGRESS(ahci_portp) || 39948aa6aadbSXiao-Yu Zhang NON_NCQ_CMD_IN_PROGRESS(ahci_portp))) { 39958aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PMULT, ahci_ctlp, 39968aa6aadbSXiao-Yu Zhang "R/W PMULT failed: port %d is occupied (flags 0x%x).", 39978aa6aadbSXiao-Yu Zhang port, ahci_portp->ahciport_flags); 39988aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 39998aa6aadbSXiao-Yu Zhang } 40008aa6aadbSXiao-Yu Zhang 40018aa6aadbSXiao-Yu Zhang /* 40028aa6aadbSXiao-Yu Zhang * The port multiplier is gone. This may happen when 40038aa6aadbSXiao-Yu Zhang * 1. Cutting off the power of an enclosure. The device lose the power 40048aa6aadbSXiao-Yu Zhang * before port multiplier. 40058aa6aadbSXiao-Yu Zhang * 2. Disconnecting the port multiplier during hot-plugging a sub-drive. 40068aa6aadbSXiao-Yu Zhang * 40078aa6aadbSXiao-Yu Zhang * The issued command should be aborted and the following command 40088aa6aadbSXiao-Yu Zhang * should not be continued. 40098aa6aadbSXiao-Yu Zhang */ 40108aa6aadbSXiao-Yu Zhang if (!(ahci_portp->ahciport_port_state & SATA_STATE_READY)) { 40118aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PMULT, ahci_ctlp, 40128aa6aadbSXiao-Yu Zhang "READ/WRITE PMULT failed: " 40138aa6aadbSXiao-Yu Zhang "port-mult is removed from port %d", port); 40148aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 40158aa6aadbSXiao-Yu Zhang } 40168aa6aadbSXiao-Yu Zhang 40178aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RDWR_PMULT; 40188aa6aadbSXiao-Yu Zhang 40198aa6aadbSXiao-Yu Zhang spkt = sata_get_rdwr_pmult_pkt(ahci_ctlp->ahcictl_dip, 40208aa6aadbSXiao-Yu Zhang &sata_device, regn, *pregv, type); 40218aa6aadbSXiao-Yu Zhang 40228aa6aadbSXiao-Yu Zhang /* 40238aa6aadbSXiao-Yu Zhang * READ/WRITE PORTMULT command is intended to sent to the control port 40248aa6aadbSXiao-Yu Zhang * of the port multiplier. 40258aa6aadbSXiao-Yu Zhang */ 40268aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PMULT(&pmult_addr, addrp->aa_port); 40278aa6aadbSXiao-Yu Zhang 40288aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_rdwr_pmult_pkt = spkt; 40298aa6aadbSXiao-Yu Zhang 40308aa6aadbSXiao-Yu Zhang /* No interrupt here. Store the interrupt enable mask. */ 40318aa6aadbSXiao-Yu Zhang intr_mask = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 40328aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port)); 40338aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 40348aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 0); 40358aa6aadbSXiao-Yu Zhang 40368aa6aadbSXiao-Yu Zhang rval = ahci_do_sync_start(ahci_ctlp, ahci_portp, &pmult_addr, spkt); 40378aa6aadbSXiao-Yu Zhang 40388aa6aadbSXiao-Yu Zhang if (rval == AHCI_SUCCESS && 40398aa6aadbSXiao-Yu Zhang spkt->satapkt_reason == SATA_PKT_COMPLETED) { 40408aa6aadbSXiao-Yu Zhang if (type == SATA_RDWR_PMULT_PKT_TYPE_READ) { 40418aa6aadbSXiao-Yu Zhang scmd = &spkt->satapkt_cmd; 40428aa6aadbSXiao-Yu Zhang *pregv = scmd->satacmd_lba_high_lsb << 24 | 40438aa6aadbSXiao-Yu Zhang scmd->satacmd_lba_mid_lsb << 16 | 40448aa6aadbSXiao-Yu Zhang scmd->satacmd_lba_low_lsb << 8 | 40458aa6aadbSXiao-Yu Zhang scmd->satacmd_sec_count_lsb; 40468aa6aadbSXiao-Yu Zhang } 40478aa6aadbSXiao-Yu Zhang } else { 40488aa6aadbSXiao-Yu Zhang /* Failed or not completed. */ 40498aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PMULT, ahci_ctlp, 40508aa6aadbSXiao-Yu Zhang "ahci_rdwr_pmult: cannot [%s] %s[%d] at port %s", 40518aa6aadbSXiao-Yu Zhang type == SATA_RDWR_PMULT_PKT_TYPE_READ?"Read":"Write", 40528aa6aadbSXiao-Yu Zhang AHCI_ADDR_IS_PMULT(addrp)?"gscr":"pscr", regn, portstr); 40538aa6aadbSXiao-Yu Zhang rval = AHCI_FAILURE; 40548aa6aadbSXiao-Yu Zhang } 40558aa6aadbSXiao-Yu Zhang out: 40568aa6aadbSXiao-Yu Zhang /* Restore the interrupt mask */ 40578aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 40588aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), intr_mask); 40598aa6aadbSXiao-Yu Zhang 40608aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_RDWR_PMULT; 40618aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_rdwr_pmult_pkt = NULL; 40628aa6aadbSXiao-Yu Zhang sata_free_rdwr_pmult_pkt(spkt); 40638aa6aadbSXiao-Yu Zhang return (rval); 40648aa6aadbSXiao-Yu Zhang } 40658aa6aadbSXiao-Yu Zhang 40668aa6aadbSXiao-Yu Zhang static int 40678aa6aadbSXiao-Yu Zhang ahci_read_pmult(ahci_ctl_t *ahci_ctlp, ahci_addr_t *addrp, 40688aa6aadbSXiao-Yu Zhang uint8_t regn, uint32_t *pregv) 40698aa6aadbSXiao-Yu Zhang { 40708aa6aadbSXiao-Yu Zhang return ahci_rdwr_pmult(ahci_ctlp, addrp, regn, pregv, 40718aa6aadbSXiao-Yu Zhang SATA_RDWR_PMULT_PKT_TYPE_READ); 40728aa6aadbSXiao-Yu Zhang } 40738aa6aadbSXiao-Yu Zhang 40748aa6aadbSXiao-Yu Zhang static int 40758aa6aadbSXiao-Yu Zhang ahci_write_pmult(ahci_ctl_t *ahci_ctlp, ahci_addr_t *addrp, 40768aa6aadbSXiao-Yu Zhang uint8_t regn, uint32_t regv) 40778aa6aadbSXiao-Yu Zhang { 40788aa6aadbSXiao-Yu Zhang return ahci_rdwr_pmult(ahci_ctlp, addrp, regn, ®v, 40798aa6aadbSXiao-Yu Zhang SATA_RDWR_PMULT_PKT_TYPE_WRITE); 40808aa6aadbSXiao-Yu Zhang } 40818aa6aadbSXiao-Yu Zhang 40828aa6aadbSXiao-Yu Zhang #define READ_PMULT(addrp, r, pv, out) \ 40838aa6aadbSXiao-Yu Zhang if (ahci_read_pmult(ahci_ctlp, addrp, r, pv) != AHCI_SUCCESS) \ 40848aa6aadbSXiao-Yu Zhang goto out; 40858aa6aadbSXiao-Yu Zhang 40868aa6aadbSXiao-Yu Zhang #define WRITE_PMULT(addrp, r, v, out) \ 40878aa6aadbSXiao-Yu Zhang if (ahci_write_pmult(ahci_ctlp, addrp, r, v) != AHCI_SUCCESS) \ 40888aa6aadbSXiao-Yu Zhang goto out; 40898aa6aadbSXiao-Yu Zhang 40908aa6aadbSXiao-Yu Zhang /* 40918aa6aadbSXiao-Yu Zhang * Update sata registers on port multiplier, including GSCR/PSCR registers. 40928aa6aadbSXiao-Yu Zhang * ahci_update_pmult_gscr() 40938aa6aadbSXiao-Yu Zhang * ahci_update_pmult_pscr() 40948aa6aadbSXiao-Yu Zhang * 40958aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before those functions 40968aa6aadbSXiao-Yu Zhang * get called. 40978aa6aadbSXiao-Yu Zhang */ 40988aa6aadbSXiao-Yu Zhang static int 40998aa6aadbSXiao-Yu Zhang ahci_update_pmult_gscr(ahci_ctl_t *ahci_ctlp, ahci_addr_t *addrp, 4100918304a3SXiao-Yu Zhang sata_pmult_gscr_t *sg) 41018aa6aadbSXiao-Yu Zhang { 4102918304a3SXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_GSCR0, &sg->gscr0, err); 4103918304a3SXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_GSCR1, &sg->gscr1, err); 4104918304a3SXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_GSCR2, &sg->gscr2, err); 4105918304a3SXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_GSCR64, &sg->gscr64, err); 41068aa6aadbSXiao-Yu Zhang 41078aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 41088aa6aadbSXiao-Yu Zhang 41098aa6aadbSXiao-Yu Zhang err: /* R/W PMULT error */ 41108aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 41118aa6aadbSXiao-Yu Zhang } 41128aa6aadbSXiao-Yu Zhang 41138aa6aadbSXiao-Yu Zhang static int 41148aa6aadbSXiao-Yu Zhang ahci_update_pmult_pscr(ahci_ctl_t *ahci_ctlp, ahci_addr_t *addrp, 41158aa6aadbSXiao-Yu Zhang sata_device_t *sd) 41168aa6aadbSXiao-Yu Zhang { 41178aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_PMPORT(addrp)); 41188aa6aadbSXiao-Yu Zhang 41198aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_REG_SSTS, &sd->satadev_scr.sstatus, err); 41208aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_REG_SERR, &sd->satadev_scr.serror, err); 41218aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_REG_SCTL, &sd->satadev_scr.scontrol, err); 41228aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_REG_SACT, &sd->satadev_scr.sactive, err); 41238aa6aadbSXiao-Yu Zhang 41248aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 41258aa6aadbSXiao-Yu Zhang 41268aa6aadbSXiao-Yu Zhang err: /* R/W PMULT error */ 41278aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 41288aa6aadbSXiao-Yu Zhang } 41298aa6aadbSXiao-Yu Zhang 41308aa6aadbSXiao-Yu Zhang /* 41318aa6aadbSXiao-Yu Zhang * ahci_initialize_pmult() 41328aa6aadbSXiao-Yu Zhang * 41338aa6aadbSXiao-Yu Zhang * Initialize a port multiplier, including 41348aa6aadbSXiao-Yu Zhang * 1. Enable FEATURES register at port multiplier. (SATA Chp.16) 41358aa6aadbSXiao-Yu Zhang * 2. Redefine MASK register. (SATA Chap 16.?) 41368aa6aadbSXiao-Yu Zhang * 41378aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before the function 41388aa6aadbSXiao-Yu Zhang * is called. 41398aa6aadbSXiao-Yu Zhang */ 41408aa6aadbSXiao-Yu Zhang static int 41418aa6aadbSXiao-Yu Zhang ahci_initialize_pmult(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 41428aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp, sata_device_t *sd) 41438aa6aadbSXiao-Yu Zhang { 4144918304a3SXiao-Yu Zhang sata_pmult_gscr_t sg; 41458aa6aadbSXiao-Yu Zhang uint32_t gscr64; 41468aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 41478aa6aadbSXiao-Yu Zhang 41488aa6aadbSXiao-Yu Zhang ASSERT(mutex_owned(&ahci_portp->ahciport_mutex)); 41498aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 41508aa6aadbSXiao-Yu Zhang "[Initialize] Port-multiplier at port %d.", port); 41518aa6aadbSXiao-Yu Zhang 41528aa6aadbSXiao-Yu Zhang /* 41538aa6aadbSXiao-Yu Zhang * Enable features of port multiplier. Currently only 41548aa6aadbSXiao-Yu Zhang * Asynchronous Notification is enabled. 41558aa6aadbSXiao-Yu Zhang */ 41568aa6aadbSXiao-Yu Zhang /* Check gscr64 for supported features. */ 41578aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_GSCR64, &gscr64, err); 41588aa6aadbSXiao-Yu Zhang 41598aa6aadbSXiao-Yu Zhang if (gscr64 & SATA_PMULT_CAP_SNOTIF) { 41608aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 41618aa6aadbSXiao-Yu Zhang "port %d: Port Multiplier supports " 41628aa6aadbSXiao-Yu Zhang "Asynchronous Notification.", port); 41638aa6aadbSXiao-Yu Zhang 41648aa6aadbSXiao-Yu Zhang /* Write to gscr96 to enabled features */ 41658aa6aadbSXiao-Yu Zhang WRITE_PMULT(addrp, SATA_PMULT_GSCR96, 41668aa6aadbSXiao-Yu Zhang SATA_PMULT_CAP_SNOTIF, err); 41678aa6aadbSXiao-Yu Zhang 41688aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 41698aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSNTF(ahci_ctlp, port), 41708aa6aadbSXiao-Yu Zhang AHCI_SNOTIF_CLEAR_ALL); 41718aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 41728aa6aadbSXiao-Yu Zhang "port %d: PMult PxSNTF cleared.", port); 41738aa6aadbSXiao-Yu Zhang 41748aa6aadbSXiao-Yu Zhang } 41758aa6aadbSXiao-Yu Zhang 41768aa6aadbSXiao-Yu Zhang /* 41778aa6aadbSXiao-Yu Zhang * Now we need to update gscr33 register to enable hot-plug interrupt 41788aa6aadbSXiao-Yu Zhang * for sub devices behind port multiplier. 41798aa6aadbSXiao-Yu Zhang */ 41808aa6aadbSXiao-Yu Zhang WRITE_PMULT(addrp, SATA_PMULT_GSCR33, (0x1ffff), err); 41818aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 41828aa6aadbSXiao-Yu Zhang "port %d: gscr33 mask set to %x.", port, (0x1ffff)); 41838aa6aadbSXiao-Yu Zhang 41848aa6aadbSXiao-Yu Zhang /* 41858aa6aadbSXiao-Yu Zhang * Fetch the number of device ports of the port multiplier 41868aa6aadbSXiao-Yu Zhang */ 4187918304a3SXiao-Yu Zhang if (ahci_update_pmult_gscr(ahci_ctlp, addrp, &sg) != AHCI_SUCCESS) 41888aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 41898aa6aadbSXiao-Yu Zhang 4190918304a3SXiao-Yu Zhang /* Register the port multiplier to SATA Framework. */ 4191918304a3SXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 4192918304a3SXiao-Yu Zhang sata_register_pmult(ahci_ctlp->ahcictl_dip, sd, &sg); 4193918304a3SXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 41948aa6aadbSXiao-Yu Zhang 41958aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pmult_info->ahcipmi_num_dev_ports = 4196918304a3SXiao-Yu Zhang sd->satadev_add_info & SATA_PMULT_PORTNUM_MASK; 41978aa6aadbSXiao-Yu Zhang 41988aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 41998aa6aadbSXiao-Yu Zhang "port %d: pmult sub-port number updated to %x.", port, 42008aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pmult_info->ahcipmi_num_dev_ports); 42018aa6aadbSXiao-Yu Zhang 42028aa6aadbSXiao-Yu Zhang /* Till now port-mult is successfully initialized */ 42038aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_state |= SATA_DSTATE_PMULT_INIT; 42048aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 42058aa6aadbSXiao-Yu Zhang 42068aa6aadbSXiao-Yu Zhang err: /* R/W PMULT error */ 42078aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 42088aa6aadbSXiao-Yu Zhang } 42098aa6aadbSXiao-Yu Zhang 42108aa6aadbSXiao-Yu Zhang /* 42118aa6aadbSXiao-Yu Zhang * Initialize a port multiplier port. According to spec, firstly we need 42128aa6aadbSXiao-Yu Zhang * issue a COMRESET, then a software reset to get its signature. 42138aa6aadbSXiao-Yu Zhang * 42148aa6aadbSXiao-Yu Zhang * NOTE: This function should only be called in ahci_probe_pmport() 42158aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before the function. 42168aa6aadbSXiao-Yu Zhang */ 42178aa6aadbSXiao-Yu Zhang static int 42188aa6aadbSXiao-Yu Zhang ahci_initialize_pmport(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 42198aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp) 42208aa6aadbSXiao-Yu Zhang { 42218aa6aadbSXiao-Yu Zhang uint32_t finished_tags = 0, reset_tags = 0, slot_status = 0; 42228aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 42238aa6aadbSXiao-Yu Zhang uint8_t pmport = addrp->aa_pmport; 42248aa6aadbSXiao-Yu Zhang int ret = AHCI_FAILURE; 42258aa6aadbSXiao-Yu Zhang 42268aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_PMPORT(addrp)); 42278aa6aadbSXiao-Yu Zhang 42288aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 42298aa6aadbSXiao-Yu Zhang "ahci_initialize_pmport: port %d:%d", port, pmport); 42308aa6aadbSXiao-Yu Zhang 42318aa6aadbSXiao-Yu Zhang /* Check HBA port state */ 42328aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 42338aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_ERRS, ahci_ctlp, 42348aa6aadbSXiao-Yu Zhang "ahci_initialize_pmport:" 42358aa6aadbSXiao-Yu Zhang "port %d:%d Port Multiplier is failed.", 42368aa6aadbSXiao-Yu Zhang port, pmport); 42378aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 42388aa6aadbSXiao-Yu Zhang } 42398aa6aadbSXiao-Yu Zhang 42408aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_HOTPLUG) { 42418aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 42428aa6aadbSXiao-Yu Zhang } 42438aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_HOTPLUG; 42448aa6aadbSXiao-Yu Zhang 42458aa6aadbSXiao-Yu Zhang /* Checking for outstanding commands */ 42468aa6aadbSXiao-Yu Zhang if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 42478aa6aadbSXiao-Yu Zhang slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 42488aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 42498aa6aadbSXiao-Yu Zhang reset_tags = slot_status & AHCI_SLOT_MASK(ahci_ctlp); 42508aa6aadbSXiao-Yu Zhang } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 42518aa6aadbSXiao-Yu Zhang slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 42528aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 42538aa6aadbSXiao-Yu Zhang reset_tags = slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 42548aa6aadbSXiao-Yu Zhang } 42558aa6aadbSXiao-Yu Zhang 42568aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 42578aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_mop_in_progress++; 42588aa6aadbSXiao-Yu Zhang 42598aa6aadbSXiao-Yu Zhang /* Clear status */ 42608aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, addrp, SATA_STATE_UNKNOWN); 42618aa6aadbSXiao-Yu Zhang 42628aa6aadbSXiao-Yu Zhang /* Firstly assume an unknown device */ 42638aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, addrp, SATA_DTYPE_UNKNOWN); 42648aa6aadbSXiao-Yu Zhang 42658aa6aadbSXiao-Yu Zhang ahci_disable_port_intrs(ahci_ctlp, port); 42668aa6aadbSXiao-Yu Zhang 42678aa6aadbSXiao-Yu Zhang /* port reset is necessary for port multiplier port */ 42688aa6aadbSXiao-Yu Zhang if (ahci_pmport_reset(ahci_ctlp, ahci_portp, addrp) != AHCI_SUCCESS) { 42698aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_ERRS, ahci_ctlp, 42708aa6aadbSXiao-Yu Zhang "ahci_initialize_pmport:" 42718aa6aadbSXiao-Yu Zhang "port reset failed at port %d:%d", 42728aa6aadbSXiao-Yu Zhang port, pmport); 42738aa6aadbSXiao-Yu Zhang goto out; 42748aa6aadbSXiao-Yu Zhang } 42758aa6aadbSXiao-Yu Zhang 42768aa6aadbSXiao-Yu Zhang /* Is port failed? */ 42778aa6aadbSXiao-Yu Zhang if (AHCIPORT_GET_STATE(ahci_portp, addrp) & 42788aa6aadbSXiao-Yu Zhang SATA_PSTATE_FAILED) { 42798aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 42808aa6aadbSXiao-Yu Zhang "ahci_initialize_pmport: port %d:%d failed. " 42818aa6aadbSXiao-Yu Zhang "state = 0x%x", port, pmport, 42828aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_state); 42838aa6aadbSXiao-Yu Zhang goto out; 42848aa6aadbSXiao-Yu Zhang } 42858aa6aadbSXiao-Yu Zhang 42868aa6aadbSXiao-Yu Zhang /* Is there any device attached? */ 42878aa6aadbSXiao-Yu Zhang if (AHCIPORT_GET_DEV_TYPE(ahci_portp, addrp) 42888aa6aadbSXiao-Yu Zhang == SATA_DTYPE_NONE) { 42898aa6aadbSXiao-Yu Zhang /* Do not waste time on an empty port */ 42908aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 42918aa6aadbSXiao-Yu Zhang "ahci_initialize_pmport: No device is found " 42928aa6aadbSXiao-Yu Zhang "at port %d:%d", port, pmport); 42938aa6aadbSXiao-Yu Zhang ret = AHCI_SUCCESS; 42948aa6aadbSXiao-Yu Zhang goto out; 42958aa6aadbSXiao-Yu Zhang } 42968aa6aadbSXiao-Yu Zhang 42978aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, addrp, SATA_STATE_READY); 42988aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 42998aa6aadbSXiao-Yu Zhang "port %d:%d is ready now.", port, pmport); 43008aa6aadbSXiao-Yu Zhang 43018aa6aadbSXiao-Yu Zhang /* 43028aa6aadbSXiao-Yu Zhang * Till now we can assure a device attached to that HBA port and work 43038aa6aadbSXiao-Yu Zhang * correctly. Now try to get the device signature. This is an optional 43048aa6aadbSXiao-Yu Zhang * step. If failed, unknown device is assumed, then SATA module will 43058aa6aadbSXiao-Yu Zhang * continue to use IDENTIFY DEVICE to get the information of the 43068aa6aadbSXiao-Yu Zhang * device. 43078aa6aadbSXiao-Yu Zhang */ 43088aa6aadbSXiao-Yu Zhang ahci_find_dev_signature(ahci_ctlp, ahci_portp, addrp); 43098aa6aadbSXiao-Yu Zhang 43108aa6aadbSXiao-Yu Zhang ret = AHCI_SUCCESS; 43118aa6aadbSXiao-Yu Zhang 43128aa6aadbSXiao-Yu Zhang out: 43138aa6aadbSXiao-Yu Zhang /* Next try to mop the pending commands */ 43148aa6aadbSXiao-Yu Zhang if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) 43158aa6aadbSXiao-Yu Zhang finished_tags = ahci_portp->ahciport_pending_tags & 43168aa6aadbSXiao-Yu Zhang ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 43178aa6aadbSXiao-Yu Zhang else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) 43188aa6aadbSXiao-Yu Zhang finished_tags = ahci_portp->ahciport_pending_ncq_tags & 43198aa6aadbSXiao-Yu Zhang ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 43208aa6aadbSXiao-Yu Zhang reset_tags &= ~finished_tags; 43218aa6aadbSXiao-Yu Zhang 43228aa6aadbSXiao-Yu Zhang ahci_mop_commands(ahci_ctlp, 43238aa6aadbSXiao-Yu Zhang ahci_portp, 43248aa6aadbSXiao-Yu Zhang slot_status, 43258aa6aadbSXiao-Yu Zhang 0, /* failed tags */ 43268aa6aadbSXiao-Yu Zhang 0, /* timeout tags */ 43278aa6aadbSXiao-Yu Zhang 0, /* aborted tags */ 43288aa6aadbSXiao-Yu Zhang reset_tags); /* reset tags */ 43298aa6aadbSXiao-Yu Zhang 43308aa6aadbSXiao-Yu Zhang /* Clear PxSNTF register if supported. */ 43318aa6aadbSXiao-Yu Zhang if (ahci_ctlp->ahcictl_cap & AHCI_CAP_SNTF) { 43328aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 43338aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSNTF(ahci_ctlp, port), 43348aa6aadbSXiao-Yu Zhang AHCI_SNOTIF_CLEAR_ALL); 43358aa6aadbSXiao-Yu Zhang } 43368aa6aadbSXiao-Yu Zhang 43378aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_HOTPLUG; 43388aa6aadbSXiao-Yu Zhang ahci_enable_port_intrs(ahci_ctlp, port); 43398aa6aadbSXiao-Yu Zhang return (ret); 43408aa6aadbSXiao-Yu Zhang } 43418aa6aadbSXiao-Yu Zhang 43428aa6aadbSXiao-Yu Zhang /* 43438aa6aadbSXiao-Yu Zhang * ahci_probe_pmult() 43448aa6aadbSXiao-Yu Zhang * 43458aa6aadbSXiao-Yu Zhang * This function will be called to probe a port multiplier, which will 43468aa6aadbSXiao-Yu Zhang * handle hotplug events on port multiplier ports. 43478aa6aadbSXiao-Yu Zhang * 43488aa6aadbSXiao-Yu Zhang * NOTE: Only called from ahci_tran_probe_port() 43498aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before the function is called. 43508aa6aadbSXiao-Yu Zhang */ 43518aa6aadbSXiao-Yu Zhang static int 43528aa6aadbSXiao-Yu Zhang ahci_probe_pmult(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 43538aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp) 43548aa6aadbSXiao-Yu Zhang { 43558aa6aadbSXiao-Yu Zhang sata_device_t sdevice; 43568aa6aadbSXiao-Yu Zhang ahci_addr_t pmport_addr; 43578aa6aadbSXiao-Yu Zhang uint32_t gscr32, port_hotplug_tags; 43588aa6aadbSXiao-Yu Zhang uint32_t pmport_sstatus; 43598aa6aadbSXiao-Yu Zhang int dev_exists_now = 0, dev_existed_previously = 0; 43608aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 43618aa6aadbSXiao-Yu Zhang int npmport; 43628aa6aadbSXiao-Yu Zhang 43638aa6aadbSXiao-Yu Zhang /* The bits in GSCR32 refers to the pmport that has a hot-plug event. */ 43648aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_GSCR32, &gscr32, err); 43658aa6aadbSXiao-Yu Zhang port_hotplug_tags = gscr32 & AHCI_PMPORT_MASK(ahci_portp); 43668aa6aadbSXiao-Yu Zhang 43678aa6aadbSXiao-Yu Zhang do { 43688aa6aadbSXiao-Yu Zhang npmport = ddi_ffs(port_hotplug_tags) - 1; 43698aa6aadbSXiao-Yu Zhang if (npmport == -1) 43708aa6aadbSXiao-Yu Zhang /* no pending hot plug events. */ 43718aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 43728aa6aadbSXiao-Yu Zhang 43738aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_EVENT|AHCIDBG_PMULT, ahci_ctlp, 43748aa6aadbSXiao-Yu Zhang "hot-plug event at port %d:%d", port, npmport); 43758aa6aadbSXiao-Yu Zhang 43768aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PMPORT(&pmport_addr, port, (uint8_t)npmport); 43778aa6aadbSXiao-Yu Zhang 43788aa6aadbSXiao-Yu Zhang /* Check previous device at that port */ 43798aa6aadbSXiao-Yu Zhang if (AHCIPORT_GET_DEV_TYPE(ahci_portp, &pmport_addr) 43808aa6aadbSXiao-Yu Zhang != SATA_DTYPE_NONE) 43818aa6aadbSXiao-Yu Zhang dev_existed_previously = 1; 43828aa6aadbSXiao-Yu Zhang 43838aa6aadbSXiao-Yu Zhang /* PxSStatus tells the presence of device. */ 43848aa6aadbSXiao-Yu Zhang READ_PMULT(&pmport_addr, SATA_PMULT_REG_SSTS, 43858aa6aadbSXiao-Yu Zhang &pmport_sstatus, err); 43868aa6aadbSXiao-Yu Zhang 43878aa6aadbSXiao-Yu Zhang if (SSTATUS_GET_DET(pmport_sstatus) == 43888aa6aadbSXiao-Yu Zhang SSTATUS_DET_DEVPRE_PHYCOM) 43898aa6aadbSXiao-Yu Zhang dev_exists_now = 1; 43908aa6aadbSXiao-Yu Zhang 43918aa6aadbSXiao-Yu Zhang /* 43928aa6aadbSXiao-Yu Zhang * Clear PxSERR is critical. The transition from 0 to 1 will 43938aa6aadbSXiao-Yu Zhang * emit a FIS which generates an asynchronous notification 43948aa6aadbSXiao-Yu Zhang * event at controller. If we fail to clear the PxSERR, the 43958aa6aadbSXiao-Yu Zhang * Async Notif events will no longer be activated on this 43968aa6aadbSXiao-Yu Zhang * pmport. 43978aa6aadbSXiao-Yu Zhang */ 43988aa6aadbSXiao-Yu Zhang WRITE_PMULT(&pmport_addr, SATA_PMULT_REG_SERR, 43998aa6aadbSXiao-Yu Zhang AHCI_SERROR_CLEAR_ALL, err); 44008aa6aadbSXiao-Yu Zhang 44018aa6aadbSXiao-Yu Zhang bzero((void *)&sdevice, sizeof (sata_device_t)); 44028aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.cport = ahci_ctlp-> 44038aa6aadbSXiao-Yu Zhang ahcictl_port_to_cport[port]; 44048aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.qual = SATA_ADDR_PMPORT; 44058aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.pmport = (uint8_t)npmport; 44068aa6aadbSXiao-Yu Zhang sdevice.satadev_state = SATA_PSTATE_PWRON; 44078aa6aadbSXiao-Yu Zhang 44088aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_EVENT|AHCIDBG_PMULT, ahci_ctlp, 44098aa6aadbSXiao-Yu Zhang "[Existence] %d -> %d", dev_existed_previously, 44108aa6aadbSXiao-Yu Zhang dev_exists_now); 44118aa6aadbSXiao-Yu Zhang 44128aa6aadbSXiao-Yu Zhang if (dev_exists_now) { 44138aa6aadbSXiao-Yu Zhang if (dev_existed_previously) { 44148aa6aadbSXiao-Yu Zhang /* Link (may) not change: Exist -> Exist * */ 44158aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 44168aa6aadbSXiao-Yu Zhang "ahci_probe_pmult: port %d:%d " 44178aa6aadbSXiao-Yu Zhang "device link lost/established", 44188aa6aadbSXiao-Yu Zhang port, npmport); 44198aa6aadbSXiao-Yu Zhang 44208aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 44218aa6aadbSXiao-Yu Zhang sata_hba_event_notify( 44228aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_sata_hba_tran-> 44238aa6aadbSXiao-Yu Zhang sata_tran_hba_dip, 44248aa6aadbSXiao-Yu Zhang &sdevice, 44258aa6aadbSXiao-Yu Zhang SATA_EVNT_LINK_LOST| 44268aa6aadbSXiao-Yu Zhang SATA_EVNT_LINK_ESTABLISHED); 44278aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 44288aa6aadbSXiao-Yu Zhang } else { 44298aa6aadbSXiao-Yu Zhang /* Link change: None -> Exist */ 44308aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_EVENT|AHCIDBG_PMULT, ahci_ctlp, 44318aa6aadbSXiao-Yu Zhang "ahci_probe_pmult: port %d:%d " 44328aa6aadbSXiao-Yu Zhang "device link established", port, npmport); 44338aa6aadbSXiao-Yu Zhang 44348aa6aadbSXiao-Yu Zhang /* Clear port state */ 44358aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, &pmport_addr, 44368aa6aadbSXiao-Yu Zhang SATA_STATE_UNKNOWN); 44378aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_EVENT|AHCIDBG_PMULT, ahci_ctlp, 44388aa6aadbSXiao-Yu Zhang "ahci_probe_pmult: port %d " 44398aa6aadbSXiao-Yu Zhang "ahciport_port_state [Cleared].", port); 44408aa6aadbSXiao-Yu Zhang 44418aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 44428aa6aadbSXiao-Yu Zhang sata_hba_event_notify( 44438aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_sata_hba_tran-> 44448aa6aadbSXiao-Yu Zhang sata_tran_hba_dip, 44458aa6aadbSXiao-Yu Zhang &sdevice, 44468aa6aadbSXiao-Yu Zhang SATA_EVNT_LINK_ESTABLISHED); 44478aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 44488aa6aadbSXiao-Yu Zhang } 44498aa6aadbSXiao-Yu Zhang } else { /* No device exists now */ 44508aa6aadbSXiao-Yu Zhang if (dev_existed_previously) { 44518aa6aadbSXiao-Yu Zhang 44528aa6aadbSXiao-Yu Zhang /* Link change: Exist -> None */ 44538aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_EVENT|AHCIDBG_PMULT, ahci_ctlp, 44548aa6aadbSXiao-Yu Zhang "ahci_probe_pmult: port %d:%d " 44558aa6aadbSXiao-Yu Zhang "device link lost", port, npmport); 44568aa6aadbSXiao-Yu Zhang 44578aa6aadbSXiao-Yu Zhang /* An existing device is lost. */ 44588aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, &pmport_addr, 44598aa6aadbSXiao-Yu Zhang SATA_STATE_UNKNOWN); 44608aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, &pmport_addr, 44618aa6aadbSXiao-Yu Zhang SATA_DTYPE_NONE); 44628aa6aadbSXiao-Yu Zhang 44638aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 44648aa6aadbSXiao-Yu Zhang sata_hba_event_notify( 44658aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_sata_hba_tran-> 44668aa6aadbSXiao-Yu Zhang sata_tran_hba_dip, 44678aa6aadbSXiao-Yu Zhang &sdevice, 44688aa6aadbSXiao-Yu Zhang SATA_EVNT_LINK_LOST); 44698aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 44708aa6aadbSXiao-Yu Zhang } 44718aa6aadbSXiao-Yu Zhang } 44728aa6aadbSXiao-Yu Zhang 44738aa6aadbSXiao-Yu Zhang CLEAR_BIT(port_hotplug_tags, npmport); 44748aa6aadbSXiao-Yu Zhang } while (port_hotplug_tags != 0); 44758aa6aadbSXiao-Yu Zhang 44768aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 44778aa6aadbSXiao-Yu Zhang 44788aa6aadbSXiao-Yu Zhang err: /* R/W PMULT error */ 44798aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 44808aa6aadbSXiao-Yu Zhang } 44818aa6aadbSXiao-Yu Zhang 44828aa6aadbSXiao-Yu Zhang /* 44838aa6aadbSXiao-Yu Zhang * Probe and initialize a port multiplier port. 44848aa6aadbSXiao-Yu Zhang * A port multiplier port could only be initilaizer here. 44858aa6aadbSXiao-Yu Zhang * 44868aa6aadbSXiao-Yu Zhang * WARNING!!! ahcictl_mutex should be acquired before the function 44878aa6aadbSXiao-Yu Zhang * is called. 44888aa6aadbSXiao-Yu Zhang */ 44898aa6aadbSXiao-Yu Zhang static int 44908aa6aadbSXiao-Yu Zhang ahci_probe_pmport(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 44918aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp, sata_device_t *sd) 44928aa6aadbSXiao-Yu Zhang { 44938aa6aadbSXiao-Yu Zhang uint32_t port_state; 44948aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 44958aa6aadbSXiao-Yu Zhang ahci_addr_t addr_pmult; 44968aa6aadbSXiao-Yu Zhang 44978aa6aadbSXiao-Yu Zhang /* 44988aa6aadbSXiao-Yu Zhang * Check the parent - port multiplier first. 44998aa6aadbSXiao-Yu Zhang */ 45008aa6aadbSXiao-Yu Zhang 45018aa6aadbSXiao-Yu Zhang /* 45028aa6aadbSXiao-Yu Zhang * Parent port multiplier might have been removed. This event will be 45038aa6aadbSXiao-Yu Zhang * ignored and failure. 45048aa6aadbSXiao-Yu Zhang */ 45058aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE || 45068aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_device_type != SATA_DTYPE_PMULT) { 45078aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PMULT, ahci_ctlp, 45088aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: " 45098aa6aadbSXiao-Yu Zhang "parent device removed, ignore event.", NULL); 45108aa6aadbSXiao-Yu Zhang 45118aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 45128aa6aadbSXiao-Yu Zhang } 45138aa6aadbSXiao-Yu Zhang 45148aa6aadbSXiao-Yu Zhang /* The port is ready? */ 45158aa6aadbSXiao-Yu Zhang port_state = ahci_portp->ahciport_port_state; 45168aa6aadbSXiao-Yu Zhang if (!(port_state & SATA_STATE_READY)) { 45178aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PMULT, ahci_ctlp, 45188aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: " 45198aa6aadbSXiao-Yu Zhang "parent port-mult is NOT ready.", NULL); 45208aa6aadbSXiao-Yu Zhang 45218aa6aadbSXiao-Yu Zhang if (ahci_restart_port_wait_till_ready(ahci_ctlp, 45228aa6aadbSXiao-Yu Zhang ahci_portp, port, AHCI_PORT_RESET, NULL) != 45238aa6aadbSXiao-Yu Zhang AHCI_SUCCESS) { 45248aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PMULT, ahci_ctlp, 45258aa6aadbSXiao-Yu Zhang "ahci_tran_probe_port: " 45268aa6aadbSXiao-Yu Zhang "restart port-mult failed.", NULL); 45278aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 452813bcbb7aSyt } 45298aa6aadbSXiao-Yu Zhang } 453013bcbb7aSyt 45318aa6aadbSXiao-Yu Zhang /* 45328aa6aadbSXiao-Yu Zhang * If port-mult is restarted due to some reason, we need 45338aa6aadbSXiao-Yu Zhang * re-initialized the PMult. 45348aa6aadbSXiao-Yu Zhang */ 45358aa6aadbSXiao-Yu Zhang if (!(port_state & SATA_DSTATE_PMULT_INIT)) { 45368aa6aadbSXiao-Yu Zhang /* Initialize registers on a port multiplier */ 45378aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PMULT(&addr_pmult, addrp->aa_port); 45388aa6aadbSXiao-Yu Zhang if (ahci_initialize_pmult(ahci_ctlp, ahci_portp, 45398aa6aadbSXiao-Yu Zhang &addr_pmult, sd) != AHCI_SUCCESS) 45408aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 45418aa6aadbSXiao-Yu Zhang } 45428aa6aadbSXiao-Yu Zhang 45438aa6aadbSXiao-Yu Zhang /* 45448aa6aadbSXiao-Yu Zhang * Then we check the port-mult port 45458aa6aadbSXiao-Yu Zhang */ 45468aa6aadbSXiao-Yu Zhang /* Is this pmport initialized? */ 45478aa6aadbSXiao-Yu Zhang port_state = AHCIPORT_GET_STATE(ahci_portp, addrp); 45488aa6aadbSXiao-Yu Zhang if (!(port_state & SATA_STATE_READY)) { 45498aa6aadbSXiao-Yu Zhang 45508aa6aadbSXiao-Yu Zhang /* ahci_initialize_pmport() will set READY state */ 45518aa6aadbSXiao-Yu Zhang if (ahci_initialize_pmport(ahci_ctlp, 45528aa6aadbSXiao-Yu Zhang ahci_portp, addrp) != AHCI_SUCCESS) 45538aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 455413bcbb7aSyt } 455513bcbb7aSyt 455613bcbb7aSyt return (AHCI_SUCCESS); 455713bcbb7aSyt } 455813bcbb7aSyt 45592fcbc377Syt /* 45602fcbc377Syt * AHCI device reset ...; a single device on one of the ports is reset, 45612fcbc377Syt * but the HBA and physical communication remain intact. This is the 45622fcbc377Syt * least intrusive. 45632fcbc377Syt * 45642fcbc377Syt * When issuing a software reset sequence, there should not be other 45652fcbc377Syt * commands in the command list, so we will first clear and then re-set 45662fcbc377Syt * PxCMD.ST to clear PxCI. And before issuing the software reset, 45672fcbc377Syt * the port must be idle and PxTFD.STS.BSY and PxTFD.STS.DRQ must be 4568b2e3645aSying tian - Beijing China * cleared unless command list override (PxCMD.CLO) is supported. 45692fcbc377Syt * 45702fcbc377Syt * WARNING!!! ahciport_mutex should be acquired and PxCMD.FRE should be 45712fcbc377Syt * set before the function is called. 45722fcbc377Syt */ 45732fcbc377Syt static int 45742fcbc377Syt ahci_software_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 45758aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp) 45762fcbc377Syt { 45772fcbc377Syt ahci_fis_h2d_register_t *h2d_register_fisp; 45782fcbc377Syt ahci_cmd_table_t *cmd_table; 45792fcbc377Syt ahci_cmd_header_t *cmd_header; 458068d33a25Syt uint32_t port_cmd_status, port_cmd_issue, port_task_file; 45812fcbc377Syt int slot, loop_count; 45828aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 45838aa6aadbSXiao-Yu Zhang uint8_t pmport = addrp->aa_pmport; 45840a4c4cecSXiao-Yu Zhang int rval = AHCI_FAILURE; 45852fcbc377Syt 4586f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 45878aa6aadbSXiao-Yu Zhang "port %d:%d device software resetting (FIS)", port, pmport); 45882fcbc377Syt 45890a4c4cecSXiao-Yu Zhang /* First clear PxCMD.ST (AHCI v1.2 10.4.1) */ 45900a4c4cecSXiao-Yu Zhang if (ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, 45910a4c4cecSXiao-Yu Zhang port) != AHCI_SUCCESS) { 4592f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 45930a4c4cecSXiao-Yu Zhang "ahci_software_reset: cannot stop HBA port %d.", port); 45940a4c4cecSXiao-Yu Zhang goto out; 45950a4c4cecSXiao-Yu Zhang } 45962fcbc377Syt 45972fcbc377Syt /* Check PxTFD.STS.BSY and PxTFD.STS.DRQ */ 45982fcbc377Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 45992fcbc377Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 46002fcbc377Syt 46012fcbc377Syt if (port_task_file & AHCI_TFD_STS_BSY || 46022fcbc377Syt port_task_file & AHCI_TFD_STS_DRQ) { 46030a4c4cecSXiao-Yu Zhang if (!(ahci_ctlp->ahcictl_cap & AHCI_CAP_SCLO)) { 4604f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 46050a4c4cecSXiao-Yu Zhang "PxTFD.STS.BSY/DRQ is set (PxTFD=0x%x), " 46060a4c4cecSXiao-Yu Zhang "cannot issue a software reset.", port_task_file); 46070a4c4cecSXiao-Yu Zhang goto out; 46080a4c4cecSXiao-Yu Zhang } 46090a4c4cecSXiao-Yu Zhang 46100a4c4cecSXiao-Yu Zhang /* 46110a4c4cecSXiao-Yu Zhang * If HBA Support CLO, as Command List Override (CAP.SCLO is 46120a4c4cecSXiao-Yu Zhang * set), PxCMD.CLO bit should be set before set PxCMD.ST, in 46130a4c4cecSXiao-Yu Zhang * order to clear PxTFD.STS.BSY and PxTFD.STS.DRQ. 46140a4c4cecSXiao-Yu Zhang */ 4615f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 4616f5f2d263SFred Herard "PxTFD.STS.BSY/DRQ is set, try SCLO.", NULL) 46170a4c4cecSXiao-Yu Zhang 4618b2e3645aSying tian - Beijing China port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 4619b2e3645aSying tian - Beijing China (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 46200a4c4cecSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 46210a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 46220a4c4cecSXiao-Yu Zhang port_cmd_status|AHCI_CMD_STATUS_CLO); 46230a4c4cecSXiao-Yu Zhang 46240a4c4cecSXiao-Yu Zhang /* Waiting till PxCMD.SCLO bit is cleared */ 46250a4c4cecSXiao-Yu Zhang loop_count = 0; 46260a4c4cecSXiao-Yu Zhang do { 46270a4c4cecSXiao-Yu Zhang /* Wait for 10 millisec */ 46280a4c4cecSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 46290a4c4cecSXiao-Yu Zhang 46300a4c4cecSXiao-Yu Zhang /* We are effectively timing out after 1 sec. */ 46310a4c4cecSXiao-Yu Zhang if (loop_count++ > 100) { 4632f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 46330a4c4cecSXiao-Yu Zhang "SCLO time out. port %d is busy.", port); 46340a4c4cecSXiao-Yu Zhang goto out; 46350a4c4cecSXiao-Yu Zhang } 46360a4c4cecSXiao-Yu Zhang 46370a4c4cecSXiao-Yu Zhang port_cmd_status = 46380a4c4cecSXiao-Yu Zhang ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 46390a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 46400a4c4cecSXiao-Yu Zhang } while (port_cmd_status & AHCI_CMD_STATUS_CLO); 46410a4c4cecSXiao-Yu Zhang 46420a4c4cecSXiao-Yu Zhang /* Re-check */ 46430a4c4cecSXiao-Yu Zhang port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 46440a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 46450a4c4cecSXiao-Yu Zhang if (port_task_file & AHCI_TFD_STS_BSY || 46460a4c4cecSXiao-Yu Zhang port_task_file & AHCI_TFD_STS_DRQ) { 4647f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 46480a4c4cecSXiao-Yu Zhang "SCLO cannot clear PxTFD.STS.BSY/DRQ (PxTFD=0x%x)", 46490a4c4cecSXiao-Yu Zhang port_task_file); 46500a4c4cecSXiao-Yu Zhang goto out; 46512fcbc377Syt } 46522fcbc377Syt } 46532fcbc377Syt 46540a4c4cecSXiao-Yu Zhang /* Then start port */ 46550a4c4cecSXiao-Yu Zhang if (ahci_start_port(ahci_ctlp, ahci_portp, port) 46560a4c4cecSXiao-Yu Zhang != AHCI_SUCCESS) { 4657f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 46580a4c4cecSXiao-Yu Zhang "ahci_software_reset: cannot start AHCI port %d.", port); 46590a4c4cecSXiao-Yu Zhang goto out; 46602fcbc377Syt } 46612fcbc377Syt 46620a4c4cecSXiao-Yu Zhang /* 46630a4c4cecSXiao-Yu Zhang * When ahci_port.ahciport_mop_in_progress is set, A non-zero 46640a4c4cecSXiao-Yu Zhang * ahci_port.ahciport_pending_ncq_tags may fail 46650a4c4cecSXiao-Yu Zhang * ahci_claim_free_slot(). Actually according to spec, by clearing 46660a4c4cecSXiao-Yu Zhang * PxCMD.ST there is no command outstanding while executing software 46670a4c4cecSXiao-Yu Zhang * reseting. Hence we directly use slot 0 instead of 46680a4c4cecSXiao-Yu Zhang * ahci_claim_free_slot(). 46690a4c4cecSXiao-Yu Zhang */ 46700a4c4cecSXiao-Yu Zhang slot = 0; 46710a4c4cecSXiao-Yu Zhang 4672b2e3645aSying tian - Beijing China /* Now send the first H2D Register FIS with SRST set to 1 */ 46732fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 46742fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 46752fcbc377Syt 46762fcbc377Syt h2d_register_fisp = 46772fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 46782fcbc377Syt 46792fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 46808aa6aadbSXiao-Yu Zhang SET_FIS_PMP(h2d_register_fisp, pmport); 46812fcbc377Syt SET_FIS_DEVCTL(h2d_register_fisp, SATA_DEVCTL_SRST); 46822fcbc377Syt 46832fcbc377Syt /* Set Command Header in Command List */ 46842fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 46852fcbc377Syt BZERO_DESCR_INFO(cmd_header); 46862fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 46872fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 46888aa6aadbSXiao-Yu Zhang SET_PORT_MULTI_PORT(cmd_header, pmport); 46892fcbc377Syt 46902fcbc377Syt SET_CLEAR_BUSY_UPON_R_OK(cmd_header, 1); 46912fcbc377Syt SET_RESET(cmd_header, 1); 46922fcbc377Syt SET_WRITE(cmd_header, 1); 46932fcbc377Syt 46942fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 46952fcbc377Syt 0, 46962fcbc377Syt ahci_cmd_table_size, 46972fcbc377Syt DDI_DMA_SYNC_FORDEV); 46982fcbc377Syt 46992fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 47002fcbc377Syt slot * sizeof (ahci_cmd_header_t), 47012fcbc377Syt sizeof (ahci_cmd_header_t), 47022fcbc377Syt DDI_DMA_SYNC_FORDEV); 47032fcbc377Syt 47042fcbc377Syt /* Indicate to the HBA that a command is active. */ 47052fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 47062fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 47072fcbc377Syt (0x1 << slot)); 47082fcbc377Syt 47092fcbc377Syt loop_count = 0; 47102fcbc377Syt 47112fcbc377Syt /* Loop till the first command is finished */ 47122fcbc377Syt do { 47132fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 47142fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 47152fcbc377Syt 4716b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 47172fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 4718f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 4719259105bcSying tian - Beijing China "the first SRST FIS is timed out, " 4720259105bcSying tian - Beijing China "loop_count = %d", loop_count); 47210a4c4cecSXiao-Yu Zhang goto out; 47222fcbc377Syt } 47232fcbc377Syt /* Wait for 10 millisec */ 47248aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 4725259105bcSying tian - Beijing China } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 47262fcbc377Syt 4727f5f2d263SFred Herard AHCIDBG(AHCIDBG_POLL_LOOP, ahci_ctlp, 47282fcbc377Syt "ahci_software_reset: 1st loop count: %d, " 47292fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 47302fcbc377Syt loop_count, port_cmd_issue, slot); 47312fcbc377Syt 4732a419422eSXiao-Yu Zhang /* According to ATA spec, we need wait at least 5 microsecs here. */ 4733a419422eSXiao-Yu Zhang drv_usecwait(AHCI_1MS_USECS); 4734a419422eSXiao-Yu Zhang 4735b2e3645aSying tian - Beijing China /* Now send the second H2D Register FIS with SRST cleard to zero */ 47362fcbc377Syt cmd_table = ahci_portp->ahciport_cmd_tables[slot]; 47372fcbc377Syt bzero((void *)cmd_table, ahci_cmd_table_size); 47382fcbc377Syt 47392fcbc377Syt h2d_register_fisp = 47402fcbc377Syt &(cmd_table->ahcict_command_fis.ahcifc_fis.ahcifc_h2d_register); 47412fcbc377Syt 47422fcbc377Syt SET_FIS_TYPE(h2d_register_fisp, AHCI_H2D_REGISTER_FIS_TYPE); 47438aa6aadbSXiao-Yu Zhang SET_FIS_PMP(h2d_register_fisp, pmport); 47442fcbc377Syt 47452fcbc377Syt /* Set Command Header in Command List */ 47462fcbc377Syt cmd_header = &ahci_portp->ahciport_cmd_list[slot]; 47472fcbc377Syt BZERO_DESCR_INFO(cmd_header); 47482fcbc377Syt BZERO_PRD_BYTE_COUNT(cmd_header); 47492fcbc377Syt SET_COMMAND_FIS_LENGTH(cmd_header, 5); 47508aa6aadbSXiao-Yu Zhang SET_PORT_MULTI_PORT(cmd_header, pmport); 47512fcbc377Syt 47522fcbc377Syt SET_WRITE(cmd_header, 1); 47532fcbc377Syt 47542fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_tables_dma_handle[slot], 47552fcbc377Syt 0, 47562fcbc377Syt ahci_cmd_table_size, 47572fcbc377Syt DDI_DMA_SYNC_FORDEV); 47582fcbc377Syt 47592fcbc377Syt (void) ddi_dma_sync(ahci_portp->ahciport_cmd_list_dma_handle, 47602fcbc377Syt slot * sizeof (ahci_cmd_header_t), 47612fcbc377Syt sizeof (ahci_cmd_header_t), 47622fcbc377Syt DDI_DMA_SYNC_FORDEV); 47632fcbc377Syt 47642fcbc377Syt /* Indicate to the HBA that a command is active. */ 47652fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 47662fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port), 47672fcbc377Syt (0x1 << slot)); 47682fcbc377Syt 47692fcbc377Syt loop_count = 0; 47702fcbc377Syt 47712fcbc377Syt /* Loop till the second command is finished */ 47722fcbc377Syt do { 47732fcbc377Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 47742fcbc377Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 47752fcbc377Syt 4776b2e3645aSying tian - Beijing China /* We are effectively timing out after 1 sec. */ 47772fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SOFTRESET) { 4778f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 4779259105bcSying tian - Beijing China "the second SRST FIS is timed out, " 4780259105bcSying tian - Beijing China "loop_count = %d", loop_count); 47810a4c4cecSXiao-Yu Zhang goto out; 47822fcbc377Syt } 4783259105bcSying tian - Beijing China 47842fcbc377Syt /* Wait for 10 millisec */ 47858aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 4786259105bcSying tian - Beijing China } while (port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp) & (0x1 << slot)); 47872fcbc377Syt 4788f5f2d263SFred Herard AHCIDBG(AHCIDBG_POLL_LOOP, ahci_ctlp, 47892fcbc377Syt "ahci_software_reset: 2nd loop count: %d, " 47902fcbc377Syt "port_cmd_issue = 0x%x, slot = 0x%x", 47912fcbc377Syt loop_count, port_cmd_issue, slot); 47922fcbc377Syt 47930a4c4cecSXiao-Yu Zhang rval = AHCI_SUCCESS; 47940a4c4cecSXiao-Yu Zhang out: 4795f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 47968aa6aadbSXiao-Yu Zhang "ahci_software_reset: %s at port %d:%d", 47970a4c4cecSXiao-Yu Zhang rval == AHCI_SUCCESS ? "succeed" : "failed", 47988aa6aadbSXiao-Yu Zhang port, pmport); 47990a4c4cecSXiao-Yu Zhang 48000a4c4cecSXiao-Yu Zhang return (rval); 48012fcbc377Syt } 48022fcbc377Syt 48032fcbc377Syt /* 48042fcbc377Syt * AHCI port reset ...; the physical communication between the HBA and device 48052fcbc377Syt * on a port are disabled. This is more intrusive. 48062fcbc377Syt * 480768d33a25Syt * When an HBA or port reset occurs, Phy communication is going to 48082fcbc377Syt * be re-established with the device through a COMRESET followed by the 48092fcbc377Syt * normal out-of-band communication sequence defined in Serial ATA. AT 48102fcbc377Syt * the end of reset, the device, if working properly, will send a D2H 48112fcbc377Syt * Register FIS, which contains the device signature. When the HBA receives 48122fcbc377Syt * this FIS, it updates PxTFD.STS and PxTFD.ERR register fields, and updates 48132fcbc377Syt * the PxSIG register with the signature. 48142fcbc377Syt * 48152fcbc377Syt * Staggered spin-up is an optional feature in SATA II, and it enables an HBA 48162fcbc377Syt * to individually spin-up attached devices. Please refer to chapter 10.9 of 481768d33a25Syt * AHCI 1.0 spec. 48182fcbc377Syt */ 48192fcbc377Syt /* 482082263d52Syt * WARNING!!! ahciport_mutex should be acquired, and PxCMD.ST should be also 482182263d52Syt * cleared before the function is called. 48222fcbc377Syt */ 48232fcbc377Syt static int 48248aa6aadbSXiao-Yu Zhang ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 48258aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp) 48262fcbc377Syt { 48278aa6aadbSXiao-Yu Zhang ahci_addr_t pmult_addr; 48282fcbc377Syt uint32_t cap_status, port_cmd_status; 48290a4c4cecSXiao-Yu Zhang uint32_t port_scontrol, port_sstatus, port_serror; 4830689d74b0Syt uint32_t port_intr_status, port_task_file; 48318aa6aadbSXiao-Yu Zhang uint32_t port_state; 48328aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 48330a4c4cecSXiao-Yu Zhang 48342fcbc377Syt int loop_count; 4835a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 48362fcbc377Syt 48378aa6aadbSXiao-Yu Zhang /* Target is a port multiplier port? */ 48388aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PMPORT(addrp)) 48398aa6aadbSXiao-Yu Zhang return (ahci_pmport_reset(ahci_ctlp, ahci_portp, addrp)); 48408aa6aadbSXiao-Yu Zhang 48418aa6aadbSXiao-Yu Zhang /* Otherwise it must be an HBA port. */ 48428aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_PORT(addrp)); 48438aa6aadbSXiao-Yu Zhang 4844f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 48452fcbc377Syt "Port %d port resetting...", port); 484668d33a25Syt ahci_portp->ahciport_port_state = 0; 48472fcbc377Syt 48482fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 48492fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 48502fcbc377Syt 48512fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 48522fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 48532fcbc377Syt 48542fcbc377Syt if (cap_status & AHCI_HBA_CAP_SSS) { 48552fcbc377Syt /* 48562fcbc377Syt * HBA support staggered spin-up, if the port has 48572fcbc377Syt * not spin up yet, then force it to do spin-up 48582fcbc377Syt */ 48592fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) { 48602fcbc377Syt if (!(ahci_portp->ahciport_flags 486168d33a25Syt & AHCI_PORT_FLAG_SPINUP)) { 4862f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 48632fcbc377Syt "Port %d PxCMD.SUD is zero, force " 48642fcbc377Syt "it to do spin-up", port); 48652fcbc377Syt ahci_portp->ahciport_flags |= 486668d33a25Syt AHCI_PORT_FLAG_SPINUP; 48672fcbc377Syt } 48682fcbc377Syt } 48692fcbc377Syt } else { 48702fcbc377Syt /* 48712fcbc377Syt * HBA doesn't support stagger spin-up, force it 48722fcbc377Syt * to do normal COMRESET 48732fcbc377Syt */ 48742fcbc377Syt if (ahci_portp->ahciport_flags & 487568d33a25Syt AHCI_PORT_FLAG_SPINUP) { 4876f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 48772fcbc377Syt "HBA does not support staggered spin-up " 4878f5f2d263SFred Herard "force it to do normal COMRESET", NULL); 48792fcbc377Syt ahci_portp->ahciport_flags &= 488068d33a25Syt ~AHCI_PORT_FLAG_SPINUP; 48812fcbc377Syt } 48822fcbc377Syt } 48832fcbc377Syt 488468d33a25Syt if (!(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_SPINUP)) { 48852fcbc377Syt /* Do normal COMRESET */ 4886f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 48872fcbc377Syt "ahci_port_reset: do normal COMRESET", port); 48882fcbc377Syt 488995c11c1fSyt /* 489095c11c1fSyt * According to the spec, SUD bit should be set here, 489195c11c1fSyt * but JMicron JMB363 doesn't follow it, so remove 489295c11c1fSyt * the assertion, and just print a debug message. 489395c11c1fSyt */ 489495c11c1fSyt #if AHCI_DEBUG 489595c11c1fSyt if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) 4896f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 489795c11c1fSyt "port %d SUD bit not set", port) 489895c11c1fSyt #endif 48992fcbc377Syt 49002fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 49012fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 490282263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_COMRESET); 49032fcbc377Syt 49042fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 49052fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 49062fcbc377Syt port_scontrol); 49072fcbc377Syt 49082fcbc377Syt /* Enable PxCMD.FRE to read device */ 49092fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 49102fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 49112fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 49122fcbc377Syt 491368d33a25Syt /* 491468d33a25Syt * Give time for COMRESET to percolate, according to the AHCI 491568d33a25Syt * spec, software shall wait at least 1 millisecond before 491668d33a25Syt * clearing PxSCTL.DET 491768d33a25Syt */ 49188aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_1MS_USECS*2); 49192fcbc377Syt 49202fcbc377Syt /* Fetch the SCONTROL again and rewrite the DET part with 0 */ 49212fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 49222fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 492382263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 49242fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 49252fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 49262fcbc377Syt port_scontrol); 49272fcbc377Syt } else { 49282fcbc377Syt /* Do staggered spin-up */ 49292fcbc377Syt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 49302fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 493182263d52Syt SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 49322fcbc377Syt 49332fcbc377Syt /* PxSCTL.DET must be 0 */ 49342fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 49352fcbc377Syt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), 49362fcbc377Syt port_scontrol); 49372fcbc377Syt 49382fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_SUD; 49392fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 49402fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 49412fcbc377Syt port_cmd_status); 49422fcbc377Syt 49432fcbc377Syt /* 0 -> 1 edge */ 49448aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_1MS_USECS*2); 49452fcbc377Syt 49462fcbc377Syt /* Set PxCMD.SUD to 1 */ 49472fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 49482fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 49492fcbc377Syt port_cmd_status |= AHCI_CMD_STATUS_SUD; 49502fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 49512fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 49522fcbc377Syt port_cmd_status); 49532fcbc377Syt 49542fcbc377Syt /* Enable PxCMD.FRE to read device */ 49552fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 49562fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 49572fcbc377Syt port_cmd_status|AHCI_CMD_STATUS_FRE); 49582fcbc377Syt } 49592fcbc377Syt 49602fcbc377Syt /* 496168d33a25Syt * The port enters P:StartComm state, and HBA tells link layer to 496268d33a25Syt * start communication, which involves sending COMRESET to device. 496368d33a25Syt * And the HBA resets PxTFD.STS to 7Fh. 496468d33a25Syt * 496568d33a25Syt * When a COMINIT is received from the device, then the port enters 496668d33a25Syt * P:ComInit state. And HBA sets PxTFD.STS to FFh or 80h. HBA sets 496768d33a25Syt * PxSSTS.DET to 1h to indicate a device is detected but communication 496868d33a25Syt * is not yet established. HBA sets PxSERR.DIAG.X to '1' to indicate 496968d33a25Syt * a COMINIT has been received. 49702fcbc377Syt */ 49712fcbc377Syt /* 49722fcbc377Syt * The DET field is valid only if IPM field indicates 49732fcbc377Syt * that the interface is in active state. 49742fcbc377Syt */ 49752fcbc377Syt loop_count = 0; 49762fcbc377Syt do { 49772fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 49782fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 49792fcbc377Syt 498082263d52Syt if (SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) { 49812fcbc377Syt /* 49822fcbc377Syt * If the interface is not active, the DET field 49832fcbc377Syt * is considered not accurate. So we want to 49842fcbc377Syt * continue looping. 49852fcbc377Syt */ 498682263d52Syt SSTATUS_SET_DET(port_sstatus, SSTATUS_DET_NODEV); 49872fcbc377Syt } 49882fcbc377Syt 49892fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_SSTATUS) { 49902fcbc377Syt /* 49912fcbc377Syt * We are effectively timing out after 0.1 sec. 49922fcbc377Syt */ 49932fcbc377Syt break; 49942fcbc377Syt } 49952fcbc377Syt 49962fcbc377Syt /* Wait for 10 millisec */ 49978aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 499882263d52Syt } while (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM); 49992fcbc377Syt 5000f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 500168d33a25Syt "ahci_port_reset: 1st loop count: %d, " 500268d33a25Syt "port_sstatus = 0x%x port %d", 500368d33a25Syt loop_count, port_sstatus, port); 50042fcbc377Syt 500582263d52Syt if ((SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) || 500682263d52Syt (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM)) { 50072fcbc377Syt /* 50082fcbc377Syt * Either the port is not active or there 50092fcbc377Syt * is no device present. 50102fcbc377Syt */ 50118aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, addrp, SATA_DTYPE_NONE); 50120a4c4cecSXiao-Yu Zhang return (AHCI_SUCCESS); 50132fcbc377Syt } 50142fcbc377Syt 501568d33a25Syt /* Now we can make sure there is a device connected to the port */ 501668d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 501768d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 50180a4c4cecSXiao-Yu Zhang port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 50190a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 50202fcbc377Syt 50210a4c4cecSXiao-Yu Zhang /* 50220a4c4cecSXiao-Yu Zhang * A COMINIT signal is supposed to be received 50230a4c4cecSXiao-Yu Zhang * PxSERR.DIAG.X or PxIS.PCS should be set 50240a4c4cecSXiao-Yu Zhang */ 50250a4c4cecSXiao-Yu Zhang if (!(port_intr_status & AHCI_INTR_STATUS_PCS) && 50260a4c4cecSXiao-Yu Zhang !(port_serror & SERROR_EXCHANGED_ERR)) { 5027a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d " 5028a9440e8dSyt "COMINIT signal from the device not received", 5029a9440e8dSyt instance, port); 50308aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, addrp, SATA_PSTATE_FAILED); 50310a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 503268d33a25Syt } 50332fcbc377Syt 503495c11c1fSyt /* 503595c11c1fSyt * According to the spec, when PxSCTL.DET is set to 0h, upon 503695c11c1fSyt * receiving a COMINIT from the attached device, PxTFD.STS.BSY 503795c11c1fSyt * shall be set to '1' by the HBA. 503895c11c1fSyt * 503995c11c1fSyt * However, we found JMicron JMB363 doesn't follow this, so 504095c11c1fSyt * remove this check, and just print a debug message. 504195c11c1fSyt */ 504295c11c1fSyt #if AHCI_DEBUG 504368d33a25Syt port_task_file = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 504468d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 504568d33a25Syt if (!(port_task_file & AHCI_TFD_STS_BSY)) { 5046f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: " 504795c11c1fSyt "port %d BSY bit is not set after COMINIT signal " 504895c11c1fSyt "is received", port); 504968d33a25Syt } 505095c11c1fSyt #endif 50512fcbc377Syt 505268d33a25Syt /* 505368d33a25Syt * PxSERR.DIAG.X has to be cleared in order to update PxTFD with 505468d33a25Syt * the D2H FIS received by HBA. 505568d33a25Syt */ 505668d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 505768d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 505882263d52Syt SERROR_EXCHANGED_ERR); 50592fcbc377Syt 506068d33a25Syt /* 50610a4c4cecSXiao-Yu Zhang * Devices should return a FIS contains its signature to HBA after 50620a4c4cecSXiao-Yu Zhang * COMINIT signal. Check whether a D2H FIS is received by polling 50630a4c4cecSXiao-Yu Zhang * PxTFD.STS.ERR bit. 506468d33a25Syt */ 506568d33a25Syt loop_count = 0; 506668d33a25Syt do { 50670a4c4cecSXiao-Yu Zhang /* Wait for 10 millisec */ 50688aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 50692fcbc377Syt 507068d33a25Syt if (loop_count++ > AHCI_POLLRATE_PORT_TFD_ERROR) { 507168d33a25Syt /* 507268d33a25Syt * We are effectively timing out after 11 sec. 507368d33a25Syt */ 50740a4c4cecSXiao-Yu Zhang cmn_err(CE_WARN, "!ahci%d: ahci_port_reset port %d " 50750a4c4cecSXiao-Yu Zhang "the device hardware has been initialized and " 50760a4c4cecSXiao-Yu Zhang "the power-up diagnostics failed", 50770a4c4cecSXiao-Yu Zhang instance, port); 50780a4c4cecSXiao-Yu Zhang 5079f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_port_reset: " 50800a4c4cecSXiao-Yu Zhang "port %d PxTFD.STS.ERR is not set, we need another " 50810a4c4cecSXiao-Yu Zhang "software reset.", port); 50820a4c4cecSXiao-Yu Zhang 50830a4c4cecSXiao-Yu Zhang /* Clear port serror register for the port */ 50840a4c4cecSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 50850a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 50860a4c4cecSXiao-Yu Zhang AHCI_SERROR_CLEAR_ALL); 50870a4c4cecSXiao-Yu Zhang 50888aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PMULT(&pmult_addr, port); 50898aa6aadbSXiao-Yu Zhang 50900a4c4cecSXiao-Yu Zhang /* Try another software reset. */ 50910a4c4cecSXiao-Yu Zhang if (ahci_software_reset(ahci_ctlp, ahci_portp, 50928aa6aadbSXiao-Yu Zhang &pmult_addr) != AHCI_SUCCESS) { 50938aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, addrp, 50948aa6aadbSXiao-Yu Zhang SATA_PSTATE_FAILED); 50950a4c4cecSXiao-Yu Zhang return (AHCI_FAILURE); 50960a4c4cecSXiao-Yu Zhang } 509768d33a25Syt break; 509868d33a25Syt } 50992fcbc377Syt 51008aa6aadbSXiao-Yu Zhang /* Wait for 10 millisec */ 51018aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 51028aa6aadbSXiao-Yu Zhang 51030a4c4cecSXiao-Yu Zhang /* 51040a4c4cecSXiao-Yu Zhang * The Error bit '1' means COMRESET is finished successfully 51050a4c4cecSXiao-Yu Zhang * The device hardware has been initialized and the power-up 51060a4c4cecSXiao-Yu Zhang * diagnostics successfully completed. The device requests 51070a4c4cecSXiao-Yu Zhang * that the Transport layer transmit a Register - D2H FIS to 51080a4c4cecSXiao-Yu Zhang * the host. (SATA spec 11.5, v2.6) 51090a4c4cecSXiao-Yu Zhang */ 51100a4c4cecSXiao-Yu Zhang port_task_file = 51110a4c4cecSXiao-Yu Zhang ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 51120a4c4cecSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 511368d33a25Syt } while (((port_task_file & AHCI_TFD_ERR_MASK) 51140a4c4cecSXiao-Yu Zhang >> AHCI_TFD_ERR_SHIFT) != AHCI_TFD_ERR_SGS); 51152fcbc377Syt 5116f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 51170a4c4cecSXiao-Yu Zhang "ahci_port_reset: 2nd loop count: %d, " 51180a4c4cecSXiao-Yu Zhang "port_task_file = 0x%x port %d", 511968d33a25Syt loop_count, port_task_file, port); 51202fcbc377Syt 512168d33a25Syt /* Clear port serror register for the port */ 512268d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 512368d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 512468d33a25Syt AHCI_SERROR_CLEAR_ALL); 512568d33a25Syt 51260a4c4cecSXiao-Yu Zhang /* Set port as ready */ 51278aa6aadbSXiao-Yu Zhang port_state = AHCIPORT_GET_STATE(ahci_portp, addrp); 51288aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, addrp, port_state|SATA_STATE_READY); 512968d33a25Syt 5130f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO|AHCIDBG_ERRS, ahci_ctlp, 51310a4c4cecSXiao-Yu Zhang "ahci_port_reset: succeed at port %d.", port); 51320a4c4cecSXiao-Yu Zhang return (AHCI_SUCCESS); 51332fcbc377Syt } 51342fcbc377Syt 51358aa6aadbSXiao-Yu Zhang /* 51368aa6aadbSXiao-Yu Zhang * COMRESET on a port multiplier port. 51378aa6aadbSXiao-Yu Zhang * 51388aa6aadbSXiao-Yu Zhang * NOTE: Only called in ahci_port_reset() 51398aa6aadbSXiao-Yu Zhang */ 51408aa6aadbSXiao-Yu Zhang static int 51418aa6aadbSXiao-Yu Zhang ahci_pmport_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 51428aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp) 51438aa6aadbSXiao-Yu Zhang { 51448aa6aadbSXiao-Yu Zhang uint32_t port_scontrol, port_sstatus, port_serror; 51458aa6aadbSXiao-Yu Zhang uint32_t port_cmd_status, port_intr_status; 51468aa6aadbSXiao-Yu Zhang uint32_t port_state; 51478aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 51488aa6aadbSXiao-Yu Zhang uint8_t pmport = addrp->aa_pmport; 51498aa6aadbSXiao-Yu Zhang int loop_count; 51508aa6aadbSXiao-Yu Zhang int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 51518aa6aadbSXiao-Yu Zhang 51528aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 51538aa6aadbSXiao-Yu Zhang "port %d:%d: pmport resetting", port, pmport); 51548aa6aadbSXiao-Yu Zhang 51558aa6aadbSXiao-Yu Zhang /* Initialize pmport state */ 51568aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, addrp, 0); 51578aa6aadbSXiao-Yu Zhang 51588aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_REG_SCTL, &port_scontrol, err); 51598aa6aadbSXiao-Yu Zhang SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_COMRESET); 51608aa6aadbSXiao-Yu Zhang WRITE_PMULT(addrp, SATA_PMULT_REG_SCTL, port_scontrol, err); 51618aa6aadbSXiao-Yu Zhang 51628aa6aadbSXiao-Yu Zhang /* PxCMD.FRE should be set before. */ 51638aa6aadbSXiao-Yu Zhang port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 51648aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 51658aa6aadbSXiao-Yu Zhang ASSERT(port_cmd_status & AHCI_CMD_STATUS_FRE); 51668aa6aadbSXiao-Yu Zhang if (!(port_cmd_status & AHCI_CMD_STATUS_FRE)) 51678aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 51688aa6aadbSXiao-Yu Zhang 51698aa6aadbSXiao-Yu Zhang /* 51708aa6aadbSXiao-Yu Zhang * Give time for COMRESET to percolate, according to the AHCI 51718aa6aadbSXiao-Yu Zhang * spec, software shall wait at least 1 millisecond before 51728aa6aadbSXiao-Yu Zhang * clearing PxSCTL.DET 51738aa6aadbSXiao-Yu Zhang */ 51748aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_1MS_USECS*2); 51758aa6aadbSXiao-Yu Zhang 51768aa6aadbSXiao-Yu Zhang /* 51778aa6aadbSXiao-Yu Zhang * Fetch the SCONTROL again and rewrite the DET part with 0 51788aa6aadbSXiao-Yu Zhang * This will generate an Asychronous Notification events. 51798aa6aadbSXiao-Yu Zhang */ 51808aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_REG_SCTL, &port_scontrol, err); 51818aa6aadbSXiao-Yu Zhang SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION); 51828aa6aadbSXiao-Yu Zhang WRITE_PMULT(addrp, SATA_PMULT_REG_SCTL, port_scontrol, err); 51838aa6aadbSXiao-Yu Zhang 51848aa6aadbSXiao-Yu Zhang /* 51858aa6aadbSXiao-Yu Zhang * The port enters P:StartComm state, and HBA tells link layer to 51868aa6aadbSXiao-Yu Zhang * start communication, which involves sending COMRESET to device. 51878aa6aadbSXiao-Yu Zhang * And the HBA resets PxTFD.STS to 7Fh. 51888aa6aadbSXiao-Yu Zhang * 51898aa6aadbSXiao-Yu Zhang * When a COMINIT is received from the device, then the port enters 51908aa6aadbSXiao-Yu Zhang * P:ComInit state. And HBA sets PxTFD.STS to FFh or 80h. HBA sets 51918aa6aadbSXiao-Yu Zhang * PxSSTS.DET to 1h to indicate a device is detected but communication 51928aa6aadbSXiao-Yu Zhang * is not yet established. HBA sets PxSERR.DIAG.X to '1' to indicate 51938aa6aadbSXiao-Yu Zhang * a COMINIT has been received. 51948aa6aadbSXiao-Yu Zhang */ 51958aa6aadbSXiao-Yu Zhang /* 51968aa6aadbSXiao-Yu Zhang * The DET field is valid only if IPM field indicates 51978aa6aadbSXiao-Yu Zhang * that the interface is in active state. 51988aa6aadbSXiao-Yu Zhang */ 51998aa6aadbSXiao-Yu Zhang loop_count = 0; 52008aa6aadbSXiao-Yu Zhang do { 52018aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_REG_SSTS, &port_sstatus, err); 52028aa6aadbSXiao-Yu Zhang 52038aa6aadbSXiao-Yu Zhang if (SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) { 52048aa6aadbSXiao-Yu Zhang /* 52058aa6aadbSXiao-Yu Zhang * If the interface is not active, the DET field 52068aa6aadbSXiao-Yu Zhang * is considered not accurate. So we want to 52078aa6aadbSXiao-Yu Zhang * continue looping. 52088aa6aadbSXiao-Yu Zhang */ 52098aa6aadbSXiao-Yu Zhang SSTATUS_SET_DET(port_sstatus, SSTATUS_DET_NODEV); 52108aa6aadbSXiao-Yu Zhang } 52118aa6aadbSXiao-Yu Zhang 52128aa6aadbSXiao-Yu Zhang if (loop_count++ > AHCI_POLLRATE_PORT_SSTATUS) { 52138aa6aadbSXiao-Yu Zhang /* 52148aa6aadbSXiao-Yu Zhang * We are effectively timing out after 0.1 sec. 52158aa6aadbSXiao-Yu Zhang */ 52168aa6aadbSXiao-Yu Zhang break; 52178aa6aadbSXiao-Yu Zhang } 52188aa6aadbSXiao-Yu Zhang 52198aa6aadbSXiao-Yu Zhang /* Wait for 10 millisec */ 52208aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 52218aa6aadbSXiao-Yu Zhang 52228aa6aadbSXiao-Yu Zhang } while (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM); 52238aa6aadbSXiao-Yu Zhang 52248aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_POLL_LOOP, ahci_ctlp, 52258aa6aadbSXiao-Yu Zhang "ahci_pmport_reset: 1st loop count: %d, " 52268aa6aadbSXiao-Yu Zhang "port_sstatus = 0x%x port %d:%d", 52278aa6aadbSXiao-Yu Zhang loop_count, port_sstatus, port, pmport); 52288aa6aadbSXiao-Yu Zhang 52298aa6aadbSXiao-Yu Zhang if ((SSTATUS_GET_IPM(port_sstatus) != SSTATUS_IPM_ACTIVE) || 52308aa6aadbSXiao-Yu Zhang (SSTATUS_GET_DET(port_sstatus) != SSTATUS_DET_DEVPRE_PHYCOM)) { 52318aa6aadbSXiao-Yu Zhang /* 52328aa6aadbSXiao-Yu Zhang * Either the port is not active or there 52338aa6aadbSXiao-Yu Zhang * is no device present. 52348aa6aadbSXiao-Yu Zhang */ 52358aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_INFO, ahci_ctlp, 52368aa6aadbSXiao-Yu Zhang "ahci_pmport_reset: " 52378aa6aadbSXiao-Yu Zhang "no device attached to port %d:%d", 52388aa6aadbSXiao-Yu Zhang port, pmport); 52398aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, addrp, SATA_DTYPE_NONE); 52408aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 52418aa6aadbSXiao-Yu Zhang } 52428aa6aadbSXiao-Yu Zhang 52438aa6aadbSXiao-Yu Zhang /* Now we can make sure there is a device connected to the port */ 52448aa6aadbSXiao-Yu Zhang /* COMINIT signal is supposed to be received (PxSERR.DIAG.X = '1') */ 52458aa6aadbSXiao-Yu Zhang READ_PMULT(addrp, SATA_PMULT_REG_SERR, &port_serror, err); 52468aa6aadbSXiao-Yu Zhang 52478aa6aadbSXiao-Yu Zhang if (!(port_serror & (1 << 26))) { 52488aa6aadbSXiao-Yu Zhang cmn_err(CE_WARN, "!ahci%d: ahci_pmport_reset: " 52498aa6aadbSXiao-Yu Zhang "COMINIT signal from the device not received port %d:%d", 52508aa6aadbSXiao-Yu Zhang instance, port, pmport); 52518aa6aadbSXiao-Yu Zhang 52528aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, addrp, SATA_PSTATE_FAILED); 52538aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 52548aa6aadbSXiao-Yu Zhang } 52558aa6aadbSXiao-Yu Zhang 52568aa6aadbSXiao-Yu Zhang /* 52578aa6aadbSXiao-Yu Zhang * After clear PxSERR register, we will receive a D2H FIS. 52588aa6aadbSXiao-Yu Zhang * Normally this FIS will cause a IPMS error according to AHCI spec 52598aa6aadbSXiao-Yu Zhang * v1.2 because there is no command outstanding for it. So we need 52608aa6aadbSXiao-Yu Zhang * to ignore this error. 52618aa6aadbSXiao-Yu Zhang */ 52628aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_IGNORE_IPMS; 52638aa6aadbSXiao-Yu Zhang WRITE_PMULT(addrp, SATA_PMULT_REG_SERR, AHCI_SERROR_CLEAR_ALL, err); 52648aa6aadbSXiao-Yu Zhang 52658aa6aadbSXiao-Yu Zhang /* Now we need to check the D2H FIS by checking IPMS error. */ 52668aa6aadbSXiao-Yu Zhang loop_count = 0; 52678aa6aadbSXiao-Yu Zhang do { 52688aa6aadbSXiao-Yu Zhang port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 52698aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 52708aa6aadbSXiao-Yu Zhang 52718aa6aadbSXiao-Yu Zhang if (loop_count++ > AHCI_POLLRATE_PORT_TFD_ERROR) { 52728aa6aadbSXiao-Yu Zhang /* 52738aa6aadbSXiao-Yu Zhang * No D2H FIS received. This is possible according 52748aa6aadbSXiao-Yu Zhang * to SATA 2.6 spec. 52758aa6aadbSXiao-Yu Zhang */ 52768aa6aadbSXiao-Yu Zhang cmn_err(CE_WARN, "ahci_port_reset: port %d:%d " 52778aa6aadbSXiao-Yu Zhang "PxIS.IPMS is not set, we need another " 52788aa6aadbSXiao-Yu Zhang "software reset.", port, pmport); 52798aa6aadbSXiao-Yu Zhang 52808aa6aadbSXiao-Yu Zhang break; 52818aa6aadbSXiao-Yu Zhang } 52828aa6aadbSXiao-Yu Zhang 52838aa6aadbSXiao-Yu Zhang /* Wait for 10 millisec */ 52848aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 52858aa6aadbSXiao-Yu Zhang delay(AHCI_10MS_TICKS); 52868aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 52878aa6aadbSXiao-Yu Zhang 52888aa6aadbSXiao-Yu Zhang } while (!(port_intr_status & AHCI_INTR_STATUS_IPMS)); 52898aa6aadbSXiao-Yu Zhang 52908aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_POLL_LOOP, ahci_ctlp, 52918aa6aadbSXiao-Yu Zhang "ahci_pmport_reset: 2st loop count: %d, " 52928aa6aadbSXiao-Yu Zhang "port_sstatus = 0x%x port %d:%d", 52938aa6aadbSXiao-Yu Zhang loop_count, port_sstatus, port, pmport); 52948aa6aadbSXiao-Yu Zhang 52958aa6aadbSXiao-Yu Zhang /* Clear IPMS */ 52968aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 52978aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 52988aa6aadbSXiao-Yu Zhang AHCI_INTR_STATUS_IPMS); 52998aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_IGNORE_IPMS; 53008aa6aadbSXiao-Yu Zhang 53018aa6aadbSXiao-Yu Zhang /* This pmport is now ready for ahci_tran_start() */ 53028aa6aadbSXiao-Yu Zhang port_state = AHCIPORT_GET_STATE(ahci_portp, addrp); 53038aa6aadbSXiao-Yu Zhang AHCIPORT_SET_STATE(ahci_portp, addrp, port_state|SATA_STATE_READY); 53048aa6aadbSXiao-Yu Zhang 53058aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_ERRS, ahci_ctlp, 53068aa6aadbSXiao-Yu Zhang "ahci_pmport_reset: succeed at port %d:%d", port, pmport); 53078aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 53088aa6aadbSXiao-Yu Zhang 53098aa6aadbSXiao-Yu Zhang err: /* R/W PMULT error */ 53108aa6aadbSXiao-Yu Zhang /* IPMS flags might be set before. */ 53118aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_IGNORE_IPMS; 53128aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_ERRS, ahci_ctlp, 53138aa6aadbSXiao-Yu Zhang "ahci_pmport_reset: failed at port %d:%d", port, pmport); 53148aa6aadbSXiao-Yu Zhang 53158aa6aadbSXiao-Yu Zhang return (AHCI_FAILURE); 53168aa6aadbSXiao-Yu Zhang } 53178aa6aadbSXiao-Yu Zhang 53182fcbc377Syt /* 53192fcbc377Syt * AHCI HBA reset ...; the entire HBA is reset, and all ports are disabled. 53202fcbc377Syt * This is the most intrusive. 53212fcbc377Syt * 532268d33a25Syt * When an HBA reset occurs, Phy communication will be re-established with 532368d33a25Syt * the device through a COMRESET followed by the normal out-of-band 532468d33a25Syt * communication sequence defined in Serial ATA. AT the end of reset, the 532568d33a25Syt * device, if working properly, will send a D2H Register FIS, which contains 532668d33a25Syt * the device signature. When the HBA receives this FIS, it updates PxTFD.STS 532768d33a25Syt * and PxTFD.ERR register fields, and updates the PxSIG register with the 532868d33a25Syt * signature. 53292fcbc377Syt * 53302fcbc377Syt * Remember to set GHC.AE to 1 before calling ahci_hba_reset. 53312fcbc377Syt */ 53322fcbc377Syt static int 53332fcbc377Syt ahci_hba_reset(ahci_ctl_t *ahci_ctlp) 53342fcbc377Syt { 53352fcbc377Syt ahci_port_t *ahci_portp; 53368aa6aadbSXiao-Yu Zhang ahci_addr_t addr; 53372fcbc377Syt uint32_t ghc_control; 53382fcbc377Syt uint8_t port; 53392fcbc377Syt int loop_count; 53402fcbc377Syt int rval = AHCI_SUCCESS; 53412fcbc377Syt 5342f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, "HBA resetting", 5343f5f2d263SFred Herard NULL); 53442fcbc377Syt 534568d33a25Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 534668d33a25Syt 53472fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 53482fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 53492fcbc377Syt 53502fcbc377Syt /* Setting GHC.HR to 1, remember GHC.AE is already set to 1 before */ 53512fcbc377Syt ghc_control |= AHCI_HBA_GHC_HR; 53522fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 53532fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 53542fcbc377Syt 53552fcbc377Syt /* 53562fcbc377Syt * Wait until HBA Reset complete or timeout 53572fcbc377Syt */ 53582fcbc377Syt loop_count = 0; 53592fcbc377Syt do { 53602fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 53612fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 53622fcbc377Syt 53632fcbc377Syt if (loop_count++ > AHCI_POLLRATE_HBA_RESET) { 5364f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 53652fcbc377Syt "ahci hba reset is timing out, " 53662fcbc377Syt "ghc_control = 0x%x", ghc_control); 53672fcbc377Syt /* We are effectively timing out after 1 sec. */ 53682fcbc377Syt break; 53692fcbc377Syt } 53702fcbc377Syt 53712fcbc377Syt /* Wait for 10 millisec */ 53728aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 53732fcbc377Syt } while (ghc_control & AHCI_HBA_GHC_HR); 53742fcbc377Syt 53758aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_POLL_LOOP, ahci_ctlp, 53762fcbc377Syt "ahci_hba_reset: 1st loop count: %d, " 53772fcbc377Syt "ghc_control = 0x%x", loop_count, ghc_control); 53782fcbc377Syt 53792fcbc377Syt if (ghc_control & AHCI_HBA_GHC_HR) { 53802fcbc377Syt /* The hba is not reset for some reasons */ 5381f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 5382f5f2d263SFred Herard "hba reset failed: HBA in a hung or locked state", NULL); 538368d33a25Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 53842fcbc377Syt return (AHCI_FAILURE); 53852fcbc377Syt } 53862fcbc377Syt 53878aa6aadbSXiao-Yu Zhang /* 53888aa6aadbSXiao-Yu Zhang * HBA reset will clear (AHCI Spec v1.2 10.4.3) GHC.IE / GHC.AE 53898aa6aadbSXiao-Yu Zhang */ 53908aa6aadbSXiao-Yu Zhang ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 53918aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 53928aa6aadbSXiao-Yu Zhang ghc_control |= (AHCI_HBA_GHC_AE | AHCI_HBA_GHC_IE); 53938aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 53948aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 53958aa6aadbSXiao-Yu Zhang 53968aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_ctlp->ahcictl_mutex); 53978aa6aadbSXiao-Yu Zhang 53982fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 53992fcbc377Syt /* Only check implemented ports */ 54002fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 54012fcbc377Syt continue; 54022fcbc377Syt } 54032fcbc377Syt 54042fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 54052fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 54062fcbc377Syt 54078aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PORT(&addr, port); 54088aa6aadbSXiao-Yu Zhang 54098aa6aadbSXiao-Yu Zhang if (ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 54108aa6aadbSXiao-Yu Zhang port, AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, NULL) != 54118aa6aadbSXiao-Yu Zhang AHCI_SUCCESS) { 54122fcbc377Syt rval = AHCI_FAILURE; 5413f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 54142fcbc377Syt "ahci_hba_reset: port %d failed", port); 54158aa6aadbSXiao-Yu Zhang /* 54168aa6aadbSXiao-Yu Zhang * Set the port state to SATA_PSTATE_FAILED if 54178aa6aadbSXiao-Yu Zhang * failed to initialize it. 54188aa6aadbSXiao-Yu Zhang */ 54198aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 54202fcbc377Syt } 54212fcbc377Syt 54222fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 54232fcbc377Syt } 54242fcbc377Syt 54252fcbc377Syt return (rval); 54262fcbc377Syt } 54272fcbc377Syt 54282fcbc377Syt /* 54292fcbc377Syt * This routine is only called from AHCI_ATTACH or phyrdy change 54300a4c4cecSXiao-Yu Zhang * case. It first calls software reset, then stop the port and try to 54310a4c4cecSXiao-Yu Zhang * read PxSIG register to find the type of device attached to the port. 54320a4c4cecSXiao-Yu Zhang * 54330a4c4cecSXiao-Yu Zhang * The caller should make sure a valid device exists on specified port and 54340a4c4cecSXiao-Yu Zhang * physical communication has been established so that the signature could 54350a4c4cecSXiao-Yu Zhang * be retrieved by software reset. 54362fcbc377Syt * 54372fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 543868d33a25Syt * is called. And the port interrupt is disabled. 54392fcbc377Syt */ 544068d33a25Syt static void 54418aa6aadbSXiao-Yu Zhang ahci_find_dev_signature(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 54428aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp) 54432fcbc377Syt { 54448aa6aadbSXiao-Yu Zhang ahci_addr_t dev_addr; 54452fcbc377Syt uint32_t signature; 54468aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 54478aa6aadbSXiao-Yu Zhang uint8_t pmport = addrp->aa_pmport; 54488aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_VALID(addrp)); 54495ce0c8ceSying tian - Beijing China int rval; 54502fcbc377Syt 54518aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PORT(addrp)) { 54528aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 54538aa6aadbSXiao-Yu Zhang "ahci_find_dev_signature enter: port %d", port); 54542fcbc377Syt 54558aa6aadbSXiao-Yu Zhang /* 54568aa6aadbSXiao-Yu Zhang * NOTE: when the ahci address is a HBA port, we do not know 54578aa6aadbSXiao-Yu Zhang * it is a device or a port multiplier that attached. we need 54588aa6aadbSXiao-Yu Zhang * try a software reset at port multiplier address (0xf 54598aa6aadbSXiao-Yu Zhang * pmport) 54608aa6aadbSXiao-Yu Zhang */ 54618aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PMULT(&dev_addr, addrp->aa_port); 54628aa6aadbSXiao-Yu Zhang } else { 54638aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 54648aa6aadbSXiao-Yu Zhang "ahci_find_dev_signature enter: port %d:%d", 54658aa6aadbSXiao-Yu Zhang port, pmport); 54668aa6aadbSXiao-Yu Zhang dev_addr = *addrp; 54678aa6aadbSXiao-Yu Zhang } 54688aa6aadbSXiao-Yu Zhang 54698aa6aadbSXiao-Yu Zhang /* Assume it is unknown. */ 54708aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, addrp, SATA_DTYPE_UNKNOWN); 54712fcbc377Syt 54720a4c4cecSXiao-Yu Zhang /* Issue a software reset to get the signature */ 54735ce0c8ceSying tian - Beijing China rval = ahci_software_reset(ahci_ctlp, ahci_portp, &dev_addr); 54745ce0c8ceSying tian - Beijing China if (rval != AHCI_SUCCESS) { 54755ce0c8ceSying tian - Beijing China 54765ce0c8ceSying tian - Beijing China /* 54775ce0c8ceSying tian - Beijing China * Try to do software reset again with pmport set with 0 if 54785ce0c8ceSying tian - Beijing China * the controller is set with AHCI_CAP_SRST_NO_HOSTPORT and 54795ce0c8ceSying tian - Beijing China * the original pmport is set with SATA_PMULT_HOSTPORT (0xf) 54805ce0c8ceSying tian - Beijing China */ 54815ce0c8ceSying tian - Beijing China if ((ahci_ctlp->ahcictl_cap & AHCI_CAP_SRST_NO_HOSTPORT) && 54825ce0c8ceSying tian - Beijing China (dev_addr.aa_pmport == SATA_PMULT_HOSTPORT)) { 54835ce0c8ceSying tian - Beijing China dev_addr.aa_pmport = 0; 54845ce0c8ceSying tian - Beijing China rval = ahci_software_reset(ahci_ctlp, ahci_portp, 54855ce0c8ceSying tian - Beijing China &dev_addr); 54865ce0c8ceSying tian - Beijing China } 54875ce0c8ceSying tian - Beijing China 54885ce0c8ceSying tian - Beijing China if (rval != AHCI_SUCCESS) { 54895ce0c8ceSying tian - Beijing China AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 54905ce0c8ceSying tian - Beijing China "ahci_find_dev_signature: software reset failed " 54915ce0c8ceSying tian - Beijing China "at port %d:%d, cannot get signature.", 54925ce0c8ceSying tian - Beijing China port, pmport); 54935ce0c8ceSying tian - Beijing China 54945ce0c8ceSying tian - Beijing China AHCIPORT_SET_STATE(ahci_portp, addrp, 54955ce0c8ceSying tian - Beijing China SATA_PSTATE_FAILED); 54965ce0c8ceSying tian - Beijing China return; 54975ce0c8ceSying tian - Beijing China } 54982fcbc377Syt } 54992fcbc377Syt 55000a4c4cecSXiao-Yu Zhang /* 55010a4c4cecSXiao-Yu Zhang * ahci_software_reset has started the port, so we need manually stop 55020a4c4cecSXiao-Yu Zhang * the port again. 55030a4c4cecSXiao-Yu Zhang */ 55048aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PORT(addrp)) { 55058aa6aadbSXiao-Yu Zhang if (ahci_put_port_into_notrunning_state(ahci_ctlp, 55068aa6aadbSXiao-Yu Zhang ahci_portp, port) != AHCI_SUCCESS) { 55078aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 55088aa6aadbSXiao-Yu Zhang "ahci_find_dev_signature: cannot stop port %d.", 55098aa6aadbSXiao-Yu Zhang port); 55108aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_PSTATE_FAILED; 55118aa6aadbSXiao-Yu Zhang return; 55128aa6aadbSXiao-Yu Zhang } 551368d33a25Syt } 55142fcbc377Syt 55150a4c4cecSXiao-Yu Zhang /* Now we can make sure that a valid signature is received. */ 55162fcbc377Syt signature = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 55172fcbc377Syt (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port)); 55182fcbc377Syt 55198aa6aadbSXiao-Yu Zhang #ifdef AHCI_DEBUG 55208aa6aadbSXiao-Yu Zhang if (AHCI_ADDR_IS_PMPORT(addrp)) { 55218aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 55228aa6aadbSXiao-Yu Zhang "ahci_find_dev_signature: signature = 0x%x at port %d:%d", 55238aa6aadbSXiao-Yu Zhang signature, port, pmport); 55248aa6aadbSXiao-Yu Zhang } else { 55258aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INIT|AHCIDBG_INFO, ahci_ctlp, 55268aa6aadbSXiao-Yu Zhang "ahci_find_dev_signature: signature = 0x%x at port %d", 55278aa6aadbSXiao-Yu Zhang signature, port); 55288aa6aadbSXiao-Yu Zhang } 55298aa6aadbSXiao-Yu Zhang #endif 55308aa6aadbSXiao-Yu Zhang 55318aa6aadbSXiao-Yu Zhang /* NOTE: Only support ATAPI device at controller port. */ 55328aa6aadbSXiao-Yu Zhang if (signature == AHCI_SIGNATURE_ATAPI && !AHCI_ADDR_IS_PORT(addrp)) 55338aa6aadbSXiao-Yu Zhang signature = SATA_DTYPE_UNKNOWN; 55342fcbc377Syt 55352fcbc377Syt switch (signature) { 55362fcbc377Syt 55372fcbc377Syt case AHCI_SIGNATURE_DISK: 55388aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, addrp, SATA_DTYPE_ATADISK); 5539f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 55402fcbc377Syt "Disk is found at port: %d", port); 55412fcbc377Syt break; 55422fcbc377Syt 55432fcbc377Syt case AHCI_SIGNATURE_ATAPI: 55448aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, addrp, SATA_DTYPE_ATAPI); 5545f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 55462fcbc377Syt "ATAPI device is found at port: %d", port); 55472fcbc377Syt break; 55482fcbc377Syt 55492fcbc377Syt case AHCI_SIGNATURE_PORT_MULTIPLIER: 55508aa6aadbSXiao-Yu Zhang /* Port Multiplier cannot recursively attached. */ 55518aa6aadbSXiao-Yu Zhang ASSERT(AHCI_ADDR_IS_PORT(addrp)); 55528aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, addrp, SATA_DTYPE_PMULT); 5553f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 55542fcbc377Syt "Port Multiplier is found at port: %d", port); 55552fcbc377Syt break; 55562fcbc377Syt 55572fcbc377Syt default: 55588aa6aadbSXiao-Yu Zhang AHCIPORT_SET_DEV_TYPE(ahci_portp, addrp, SATA_DTYPE_UNKNOWN); 5559f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 55602fcbc377Syt "Unknown device is found at port: %d", port); 55612fcbc377Syt } 55622fcbc377Syt } 55632fcbc377Syt 556413bcbb7aSyt /* 556513bcbb7aSyt * According to the spec, to reliably detect hot plug removals, software 556613bcbb7aSyt * must disable interface power management. Software should perform the 556713bcbb7aSyt * following initialization on a port after a device is attached: 556813bcbb7aSyt * Set PxSCTL.IPM to 3h to disable interface state transitions 556913bcbb7aSyt * Set PxCMD.ALPE to '0' to disable aggressive power management 557013bcbb7aSyt * Disable device initiated interface power management by SET FEATURE 557113bcbb7aSyt * 557213bcbb7aSyt * We can ignore the last item because by default the feature is disabled 557313bcbb7aSyt */ 557413bcbb7aSyt static void 557513bcbb7aSyt ahci_disable_interface_pm(ahci_ctl_t *ahci_ctlp, uint8_t port) 557613bcbb7aSyt { 557713bcbb7aSyt uint32_t port_scontrol, port_cmd_status; 557813bcbb7aSyt 557913bcbb7aSyt port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 558013bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port)); 558113bcbb7aSyt SCONTROL_SET_IPM(port_scontrol, SCONTROL_IPM_DISABLE_BOTH); 558213bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 558313bcbb7aSyt (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port), port_scontrol); 558413bcbb7aSyt 558513bcbb7aSyt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 558613bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 558713bcbb7aSyt port_cmd_status &= ~AHCI_CMD_STATUS_ALPE; 558813bcbb7aSyt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 558913bcbb7aSyt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 559013bcbb7aSyt } 559113bcbb7aSyt 55922fcbc377Syt /* 559368d33a25Syt * Start the port - set PxCMD.ST to 1, if PxCMD.FRE is not set 559468d33a25Syt * to 1, then set it firstly. 559568d33a25Syt * 559668d33a25Syt * Each port contains two major DMA engines. One DMA engine walks through 559768d33a25Syt * the command list, and is controlled by PxCMD.ST. The second DMA engine 559868d33a25Syt * copies received FISes into system memory, and is controlled by PxCMD.FRE. 559968d33a25Syt * 560068d33a25Syt * Software shall not set PxCMD.ST to '1' until it verifies that PxCMD.CR 560168d33a25Syt * is '0' and has set PxCMD.FRE is '1'. And software shall not clear 560268d33a25Syt * PxCMD.FRE while PxCMD.ST or PxCMD.CR is set '1'. 560368d33a25Syt * 560468d33a25Syt * Software shall not set PxCMD.ST to '1' unless a functional device is 560568d33a25Syt * present on the port(as determined by PxTFD.STS.BSY = '0', 560668d33a25Syt * PxTFD.STS.DRQ = '0', and PxSSTS.DET = 3h). 56072fcbc377Syt * 56082fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 56092fcbc377Syt * is called. 56102fcbc377Syt */ 56112fcbc377Syt static int 561268d33a25Syt ahci_start_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 56132fcbc377Syt { 561468d33a25Syt uint32_t port_cmd_status; 56152fcbc377Syt 5616f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_start_port: %d enter", port); 56172fcbc377Syt 561868d33a25Syt if (ahci_portp->ahciport_port_state & SATA_PSTATE_FAILED) { 5619f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 562068d33a25Syt "the state for port %d is 0x%x", 562168d33a25Syt port, ahci_portp->ahciport_port_state); 56222fcbc377Syt return (AHCI_FAILURE); 562368d33a25Syt } 56242fcbc377Syt 562568d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 5626f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port failed " 562768d33a25Syt "no device is attached at port %d", port); 562868d33a25Syt return (AHCI_FAILURE); 562968d33a25Syt } 56302fcbc377Syt 563168d33a25Syt /* First to set PxCMD.FRE before setting PxCMD.ST. */ 563268d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 563368d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 56342fcbc377Syt 563568d33a25Syt if (!(port_cmd_status & AHCI_CMD_STATUS_FRE)) { 563668d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_FRE; 56372fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 56382fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 56392fcbc377Syt port_cmd_status); 56402fcbc377Syt } 564168d33a25Syt 564268d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 564368d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 564468d33a25Syt 564568d33a25Syt port_cmd_status |= AHCI_CMD_STATUS_ST; 564668d33a25Syt 564768d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 564868d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), 564968d33a25Syt port_cmd_status); 565068d33a25Syt 565168d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_STARTED; 565268d33a25Syt 5653f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_start_port: " 56540a4c4cecSXiao-Yu Zhang "PxCMD.ST set to '1' at port %d", port); 56550a4c4cecSXiao-Yu Zhang 565668d33a25Syt return (AHCI_SUCCESS); 56572fcbc377Syt } 56582fcbc377Syt 56592fcbc377Syt /* 56602fcbc377Syt * Allocate the ahci_port_t including Received FIS and Command List. 56612fcbc377Syt * The argument - port is the physical port number, and not logical 56622fcbc377Syt * port number seen by the SATA framework. 56632fcbc377Syt * 56642fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 56652fcbc377Syt * is called. 56662fcbc377Syt */ 56672fcbc377Syt static int 56682fcbc377Syt ahci_alloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 56692fcbc377Syt { 5670f8a673adSying tian - Beijing China dev_info_t *dip = ahci_ctlp->ahcictl_dip; 56712fcbc377Syt ahci_port_t *ahci_portp; 5672f8a673adSying tian - Beijing China char taskq_name[64] = "event_handle_taskq"; 56732fcbc377Syt 56742fcbc377Syt ahci_portp = 56752fcbc377Syt (ahci_port_t *)kmem_zalloc(sizeof (ahci_port_t), KM_SLEEP); 56762fcbc377Syt 56772fcbc377Syt ahci_ctlp->ahcictl_ports[port] = ahci_portp; 56782fcbc377Syt ahci_portp->ahciport_port_num = port; 56792fcbc377Syt 56808aa6aadbSXiao-Yu Zhang /* Initialize the port condition variable */ 568168d33a25Syt cv_init(&ahci_portp->ahciport_cv, NULL, CV_DRIVER, NULL); 568268d33a25Syt 568368d33a25Syt /* Initialize the port mutex */ 56842fcbc377Syt mutex_init(&ahci_portp->ahciport_mutex, NULL, MUTEX_DRIVER, 56852fcbc377Syt (void *)(uintptr_t)ahci_ctlp->ahcictl_intr_pri); 568668d33a25Syt 56872fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 56882fcbc377Syt 56892fcbc377Syt /* 56902fcbc377Syt * Allocate memory for received FIS structure and 56912fcbc377Syt * command list for this port 56922fcbc377Syt */ 56932fcbc377Syt if (ahci_alloc_rcvd_fis(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 56942fcbc377Syt goto err_case1; 56952fcbc377Syt } 56962fcbc377Syt 56972fcbc377Syt if (ahci_alloc_cmd_list(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) { 56982fcbc377Syt goto err_case2; 56992fcbc377Syt } 57002fcbc377Syt 5701f8a673adSying tian - Beijing China (void) snprintf(taskq_name + strlen(taskq_name), 5702f8a673adSying tian - Beijing China sizeof (taskq_name) - strlen(taskq_name), 5703f8a673adSying tian - Beijing China "_port%d", port); 5704f8a673adSying tian - Beijing China 5705f8a673adSying tian - Beijing China /* Create the taskq for the port */ 5706f8a673adSying tian - Beijing China if ((ahci_portp->ahciport_event_taskq = ddi_taskq_create(dip, 5707f8a673adSying tian - Beijing China taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) { 5708f8a673adSying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ddi_taskq_create failed for event " 5709f8a673adSying tian - Beijing China "handle", ddi_get_instance(ahci_ctlp->ahcictl_dip)); 5710f8a673adSying tian - Beijing China goto err_case3; 5711f8a673adSying tian - Beijing China } 5712f8a673adSying tian - Beijing China 5713f8a673adSying tian - Beijing China /* Allocate the argument for the taskq */ 571468d33a25Syt ahci_portp->ahciport_event_args = 571568d33a25Syt kmem_zalloc(sizeof (ahci_event_arg_t), KM_SLEEP); 571668d33a25Syt 57178aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_event_args->ahciea_addrp = 57188aa6aadbSXiao-Yu Zhang kmem_zalloc(sizeof (ahci_addr_t), KM_SLEEP); 57198aa6aadbSXiao-Yu Zhang 572068d33a25Syt if (ahci_portp->ahciport_event_args == NULL) 5721f8a673adSying tian - Beijing China goto err_case4; 572268d33a25Syt 57232fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 57242fcbc377Syt 57252fcbc377Syt return (AHCI_SUCCESS); 57262fcbc377Syt 5727f8a673adSying tian - Beijing China err_case4: 5728f8a673adSying tian - Beijing China ddi_taskq_destroy(ahci_portp->ahciport_event_taskq); 5729f8a673adSying tian - Beijing China 573068d33a25Syt err_case3: 573168d33a25Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 573268d33a25Syt 57332fcbc377Syt err_case2: 5734689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 57352fcbc377Syt 57362fcbc377Syt err_case1: 57372fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 57382fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 573968d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 57402fcbc377Syt 57412fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 57422fcbc377Syt 57432fcbc377Syt return (AHCI_FAILURE); 57442fcbc377Syt } 57452fcbc377Syt 57462fcbc377Syt /* 57472fcbc377Syt * Reverse of ahci_dealloc_port_state(). 57482fcbc377Syt * 57492fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 57502fcbc377Syt * is called. 57512fcbc377Syt */ 57522fcbc377Syt static void 57532fcbc377Syt ahci_dealloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port) 57542fcbc377Syt { 57552fcbc377Syt ahci_port_t *ahci_portp = ahci_ctlp->ahcictl_ports[port]; 57562fcbc377Syt 57572fcbc377Syt ASSERT(ahci_portp != NULL); 57582fcbc377Syt 57592fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 57608aa6aadbSXiao-Yu Zhang kmem_free(ahci_portp->ahciport_event_args->ahciea_addrp, 57618aa6aadbSXiao-Yu Zhang sizeof (ahci_addr_t)); 57628aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_event_args->ahciea_addrp = NULL; 576368d33a25Syt kmem_free(ahci_portp->ahciport_event_args, sizeof (ahci_event_arg_t)); 576468d33a25Syt ahci_portp->ahciport_event_args = NULL; 5765f8a673adSying tian - Beijing China ddi_taskq_destroy(ahci_portp->ahciport_event_taskq); 57662fcbc377Syt ahci_dealloc_cmd_list(ahci_ctlp, ahci_portp); 5767689d74b0Syt ahci_dealloc_rcvd_fis(ahci_portp); 57688aa6aadbSXiao-Yu Zhang ahci_dealloc_pmult(ahci_ctlp, ahci_portp); 57692fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 57702fcbc377Syt 57712fcbc377Syt mutex_destroy(&ahci_portp->ahciport_mutex); 577268d33a25Syt cv_destroy(&ahci_portp->ahciport_cv); 57732fcbc377Syt 57742fcbc377Syt kmem_free(ahci_portp, sizeof (ahci_port_t)); 57752fcbc377Syt 57762fcbc377Syt ahci_ctlp->ahcictl_ports[port] = NULL; 57772fcbc377Syt } 57782fcbc377Syt 57792fcbc377Syt /* 57802fcbc377Syt * Allocates memory for the Received FIS Structure 57812fcbc377Syt * 57822fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 57832fcbc377Syt * is called. 57842fcbc377Syt */ 57852fcbc377Syt static int 57862fcbc377Syt ahci_alloc_rcvd_fis(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 57872fcbc377Syt uint8_t port) 57882fcbc377Syt { 57892fcbc377Syt size_t rcvd_fis_size; 57902fcbc377Syt size_t ret_len; 57912fcbc377Syt uint_t cookie_count; 57922fcbc377Syt 57932fcbc377Syt rcvd_fis_size = sizeof (ahci_rcvd_fis_t); 57942fcbc377Syt 57952fcbc377Syt /* allocate rcvd FIS dma handle. */ 57962fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 57972fcbc377Syt &ahci_ctlp->ahcictl_rcvd_fis_dma_attr, 57982fcbc377Syt DDI_DMA_SLEEP, 57992fcbc377Syt NULL, 58002fcbc377Syt &ahci_portp->ahciport_rcvd_fis_dma_handle) != 58012fcbc377Syt DDI_SUCCESS) { 5802f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 5803f5f2d263SFred Herard "rcvd FIS dma handle alloc failed", NULL); 58042fcbc377Syt 58052fcbc377Syt return (AHCI_FAILURE); 58062fcbc377Syt } 58072fcbc377Syt 58082fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_rcvd_fis_dma_handle, 58092fcbc377Syt rcvd_fis_size, 58102fcbc377Syt &accattr, 58112ab9355fSying tian - Beijing China DDI_DMA_CONSISTENT, 58122fcbc377Syt DDI_DMA_SLEEP, 58132fcbc377Syt NULL, 58142fcbc377Syt (caddr_t *)&ahci_portp->ahciport_rcvd_fis, 58152fcbc377Syt &ret_len, 58162fcbc377Syt &ahci_portp->ahciport_rcvd_fis_acc_handle) != NULL) { 58172fcbc377Syt 5818f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 5819f5f2d263SFred Herard "rcvd FIS dma mem alloc fail", NULL); 58202fcbc377Syt /* error.. free the dma handle. */ 58212fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 58222fcbc377Syt return (AHCI_FAILURE); 58232fcbc377Syt } 58242fcbc377Syt 58252fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle, 58262fcbc377Syt NULL, 58272fcbc377Syt (caddr_t)ahci_portp->ahciport_rcvd_fis, 58282fcbc377Syt rcvd_fis_size, 58292ab9355fSying tian - Beijing China DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 58302fcbc377Syt DDI_DMA_SLEEP, 58312fcbc377Syt NULL, 583213bcbb7aSyt &ahci_portp->ahciport_rcvd_fis_dma_cookie, 58332fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 58342fcbc377Syt 5835f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 5836f5f2d263SFred Herard "rcvd FIS dma handle bind fail", NULL); 58372fcbc377Syt /* error.. free the dma handle & free the memory. */ 58382fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 58392fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 58402fcbc377Syt return (AHCI_FAILURE); 58412fcbc377Syt } 58422fcbc377Syt 58432fcbc377Syt bzero((void *)ahci_portp->ahciport_rcvd_fis, rcvd_fis_size); 58442fcbc377Syt 58452fcbc377Syt /* Config Port Received FIS Base Address */ 58462fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 58472fcbc377Syt (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port), 584813bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 58492fcbc377Syt 5850f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 585113bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress); 5852f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 585313bcbb7aSyt ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_address); 58542fcbc377Syt 58552fcbc377Syt return (AHCI_SUCCESS); 58562fcbc377Syt } 58572fcbc377Syt 58582fcbc377Syt /* 58592fcbc377Syt * Deallocates the Received FIS Structure 58602fcbc377Syt * 58612fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 58622fcbc377Syt * is called. 58632fcbc377Syt */ 58642fcbc377Syt static void 5865689d74b0Syt ahci_dealloc_rcvd_fis(ahci_port_t *ahci_portp) 58662fcbc377Syt { 58672fcbc377Syt /* Unbind the cmd list dma handle first. */ 58682fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_rcvd_fis_dma_handle); 58692fcbc377Syt 58702fcbc377Syt /* Then free the underlying memory. */ 58712fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_rcvd_fis_acc_handle); 58722fcbc377Syt 58732fcbc377Syt /* Now free the handle itself. */ 58742fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_rcvd_fis_dma_handle); 58752fcbc377Syt } 58762fcbc377Syt 58772fcbc377Syt /* 58782fcbc377Syt * Allocates memory for the Command List, which contains up to 32 entries. 58792fcbc377Syt * Each entry contains a command header, which is a 32-byte structure that 58802fcbc377Syt * includes the pointer to the command table. 58812fcbc377Syt * 58822fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 58832fcbc377Syt * is called. 58842fcbc377Syt */ 58852fcbc377Syt static int 58862fcbc377Syt ahci_alloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 58872fcbc377Syt uint8_t port) 58882fcbc377Syt { 58892fcbc377Syt size_t cmd_list_size; 58902fcbc377Syt size_t ret_len; 58912fcbc377Syt uint_t cookie_count; 58922fcbc377Syt 58932fcbc377Syt cmd_list_size = 58942fcbc377Syt ahci_ctlp->ahcictl_num_cmd_slots * sizeof (ahci_cmd_header_t); 58952fcbc377Syt 58962fcbc377Syt /* allocate cmd list dma handle. */ 58972fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 58982fcbc377Syt &ahci_ctlp->ahcictl_cmd_list_dma_attr, 58992fcbc377Syt DDI_DMA_SLEEP, 59002fcbc377Syt NULL, 59012fcbc377Syt &ahci_portp->ahciport_cmd_list_dma_handle) != DDI_SUCCESS) { 59022fcbc377Syt 5903f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 5904f5f2d263SFred Herard "cmd list dma handle alloc failed", NULL); 59052fcbc377Syt return (AHCI_FAILURE); 59062fcbc377Syt } 59072fcbc377Syt 59082fcbc377Syt if (ddi_dma_mem_alloc(ahci_portp->ahciport_cmd_list_dma_handle, 59092fcbc377Syt cmd_list_size, 59102fcbc377Syt &accattr, 59112ab9355fSying tian - Beijing China DDI_DMA_CONSISTENT, 59122fcbc377Syt DDI_DMA_SLEEP, 59132fcbc377Syt NULL, 59142fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_list, 59152fcbc377Syt &ret_len, 59162fcbc377Syt &ahci_portp->ahciport_cmd_list_acc_handle) != NULL) { 59172fcbc377Syt 5918f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 5919f5f2d263SFred Herard "cmd list dma mem alloc fail", NULL); 59202fcbc377Syt /* error.. free the dma handle. */ 59212fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 59222fcbc377Syt return (AHCI_FAILURE); 59232fcbc377Syt } 59242fcbc377Syt 59252fcbc377Syt if (ddi_dma_addr_bind_handle(ahci_portp->ahciport_cmd_list_dma_handle, 59262fcbc377Syt NULL, 59272fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_list, 59282fcbc377Syt cmd_list_size, 59292ab9355fSying tian - Beijing China DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 59302fcbc377Syt DDI_DMA_SLEEP, 59312fcbc377Syt NULL, 593213bcbb7aSyt &ahci_portp->ahciport_cmd_list_dma_cookie, 59332fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 59342fcbc377Syt 5935f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 5936f5f2d263SFred Herard "cmd list dma handle bind fail", NULL); 59372fcbc377Syt /* error.. free the dma handle & free the memory. */ 59382fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 59392fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 59402fcbc377Syt return (AHCI_FAILURE); 59412fcbc377Syt } 59422fcbc377Syt 59432fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_list, cmd_list_size); 59442fcbc377Syt 59452fcbc377Syt /* Config Port Command List Base Address */ 59462fcbc377Syt ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle, 59472fcbc377Syt (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port), 594813bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 59492fcbc377Syt 5950f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx", 595113bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress); 59522fcbc377Syt 5953f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x", 595413bcbb7aSyt ahci_portp->ahciport_cmd_list_dma_cookie.dmac_address); 59552fcbc377Syt 59562fcbc377Syt if (ahci_alloc_cmd_tables(ahci_ctlp, ahci_portp) != AHCI_SUCCESS) { 5957e2decd04Syt goto err_out; 59582fcbc377Syt } 59592fcbc377Syt 59602fcbc377Syt return (AHCI_SUCCESS); 5961e2decd04Syt 5962e2decd04Syt err_out: 5963e2decd04Syt /* Unbind the cmd list dma handle first. */ 5964e2decd04Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 5965e2decd04Syt 5966e2decd04Syt /* Then free the underlying memory. */ 5967e2decd04Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 5968e2decd04Syt 5969e2decd04Syt /* Now free the handle itself. */ 5970e2decd04Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 5971e2decd04Syt 5972e2decd04Syt return (AHCI_FAILURE); 59732fcbc377Syt } 59742fcbc377Syt 59752fcbc377Syt /* 59762fcbc377Syt * Deallocates the Command List 59772fcbc377Syt * 59782fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 59792fcbc377Syt * is called. 59802fcbc377Syt */ 59812fcbc377Syt static void 59822fcbc377Syt ahci_dealloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 59832fcbc377Syt { 59842fcbc377Syt /* First dealloc command table */ 59852fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctlp, ahci_portp); 59862fcbc377Syt 59872fcbc377Syt /* Unbind the cmd list dma handle first. */ 59882fcbc377Syt (void) ddi_dma_unbind_handle(ahci_portp->ahciport_cmd_list_dma_handle); 59892fcbc377Syt 59902fcbc377Syt /* Then free the underlying memory. */ 59912fcbc377Syt ddi_dma_mem_free(&ahci_portp->ahciport_cmd_list_acc_handle); 59922fcbc377Syt 59932fcbc377Syt /* Now free the handle itself. */ 59942fcbc377Syt ddi_dma_free_handle(&ahci_portp->ahciport_cmd_list_dma_handle); 59952fcbc377Syt } 59962fcbc377Syt 59972fcbc377Syt /* 59982fcbc377Syt * Allocates memory for all Command Tables, which contains Command FIS, 59992fcbc377Syt * ATAPI Command and Physical Region Descriptor Table. 60002fcbc377Syt * 60012fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 60022fcbc377Syt * is called. 60032fcbc377Syt */ 60042fcbc377Syt static int 60052fcbc377Syt ahci_alloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 60062fcbc377Syt { 60072fcbc377Syt size_t ret_len; 60082fcbc377Syt ddi_dma_cookie_t cmd_table_dma_cookie; 60092fcbc377Syt uint_t cookie_count; 60102fcbc377Syt int slot; 60112fcbc377Syt 6012f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp, 60132fcbc377Syt "ahci_alloc_cmd_tables: port %d enter", 60142fcbc377Syt ahci_portp->ahciport_port_num); 60152fcbc377Syt 60162fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 60172fcbc377Syt /* Allocate cmd table dma handle. */ 60182fcbc377Syt if (ddi_dma_alloc_handle(ahci_ctlp->ahcictl_dip, 60192fcbc377Syt &ahci_ctlp->ahcictl_cmd_table_dma_attr, 60202fcbc377Syt DDI_DMA_SLEEP, 60212fcbc377Syt NULL, 60222fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]) != 60232fcbc377Syt DDI_SUCCESS) { 60242fcbc377Syt 6025f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 6026f5f2d263SFred Herard "cmd table dma handle alloc failed", NULL); 60272fcbc377Syt 60282fcbc377Syt goto err_out; 60292fcbc377Syt } 60302fcbc377Syt 60312fcbc377Syt if (ddi_dma_mem_alloc( 60322fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 60332fcbc377Syt ahci_cmd_table_size, 60342fcbc377Syt &accattr, 60352ab9355fSying tian - Beijing China DDI_DMA_CONSISTENT, 60362fcbc377Syt DDI_DMA_SLEEP, 60372fcbc377Syt NULL, 60382fcbc377Syt (caddr_t *)&ahci_portp->ahciport_cmd_tables[slot], 60392fcbc377Syt &ret_len, 60402fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]) != 60412fcbc377Syt NULL) { 60422fcbc377Syt 6043f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 6044f5f2d263SFred Herard "cmd table dma mem alloc fail", NULL); 60452fcbc377Syt 60462fcbc377Syt /* error.. free the dma handle. */ 60472fcbc377Syt ddi_dma_free_handle( 60482fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 60492fcbc377Syt goto err_out; 60502fcbc377Syt } 60512fcbc377Syt 60522fcbc377Syt if (ddi_dma_addr_bind_handle( 60532fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot], 60542fcbc377Syt NULL, 60552fcbc377Syt (caddr_t)ahci_portp->ahciport_cmd_tables[slot], 60562fcbc377Syt ahci_cmd_table_size, 60572ab9355fSying tian - Beijing China DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 60582fcbc377Syt DDI_DMA_SLEEP, 60592fcbc377Syt NULL, 60602fcbc377Syt &cmd_table_dma_cookie, 60612fcbc377Syt &cookie_count) != DDI_DMA_MAPPED) { 60622fcbc377Syt 6063f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 6064f5f2d263SFred Herard "cmd table dma handle bind fail", NULL); 60652fcbc377Syt /* error.. free the dma handle & free the memory. */ 60662fcbc377Syt ddi_dma_mem_free( 60672fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 60682fcbc377Syt ddi_dma_free_handle( 60692fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 60702fcbc377Syt goto err_out; 60712fcbc377Syt } 60722fcbc377Syt 60732fcbc377Syt bzero((void *)ahci_portp->ahciport_cmd_tables[slot], 60742fcbc377Syt ahci_cmd_table_size); 60752fcbc377Syt 60762fcbc377Syt /* Config Port Command Table Base Address */ 60772fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR( 60782fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 60792fcbc377Syt cmd_table_dma_cookie.dmac_laddress & 0xffffffffull); 60802fcbc377Syt 60812fcbc377Syt #ifndef __lock_lint 60822fcbc377Syt SET_COMMAND_TABLE_BASE_ADDR_UPPER( 60832fcbc377Syt (&ahci_portp->ahciport_cmd_list[slot]), 60842fcbc377Syt cmd_table_dma_cookie.dmac_laddress >> 32); 6085689d74b0Syt #endif 60862fcbc377Syt } 60872fcbc377Syt 60882fcbc377Syt return (AHCI_SUCCESS); 60892fcbc377Syt err_out: 60902fcbc377Syt 60912fcbc377Syt for (slot--; slot >= 0; slot--) { 60922fcbc377Syt /* Unbind the cmd table dma handle first */ 60932fcbc377Syt (void) ddi_dma_unbind_handle( 60942fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 60952fcbc377Syt 60962fcbc377Syt /* Then free the underlying memory */ 60972fcbc377Syt ddi_dma_mem_free( 60982fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 60992fcbc377Syt 61002fcbc377Syt /* Now free the handle itself */ 61012fcbc377Syt ddi_dma_free_handle( 61022fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 61032fcbc377Syt } 61042fcbc377Syt 61052fcbc377Syt return (AHCI_FAILURE); 61062fcbc377Syt } 61072fcbc377Syt 61082fcbc377Syt /* 61092fcbc377Syt * Deallocates memory for all Command Tables. 61102fcbc377Syt * 61112fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 61122fcbc377Syt * is called. 61132fcbc377Syt */ 61142fcbc377Syt static void 61152fcbc377Syt ahci_dealloc_cmd_tables(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp) 61162fcbc377Syt { 61172fcbc377Syt int slot; 61182fcbc377Syt 6119f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 61202fcbc377Syt "ahci_dealloc_cmd_tables: %d enter", 61212fcbc377Syt ahci_portp->ahciport_port_num); 61222fcbc377Syt 61232fcbc377Syt for (slot = 0; slot < ahci_ctlp->ahcictl_num_cmd_slots; slot++) { 61242fcbc377Syt /* Unbind the cmd table dma handle first. */ 61252fcbc377Syt (void) ddi_dma_unbind_handle( 61262fcbc377Syt ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 61272fcbc377Syt 61282fcbc377Syt /* Then free the underlying memory. */ 61292fcbc377Syt ddi_dma_mem_free( 61302fcbc377Syt &ahci_portp->ahciport_cmd_tables_acc_handle[slot]); 61312fcbc377Syt 61322fcbc377Syt /* Now free the handle itself. */ 61332fcbc377Syt ddi_dma_free_handle( 61342fcbc377Syt &ahci_portp->ahciport_cmd_tables_dma_handle[slot]); 61352fcbc377Syt } 61362fcbc377Syt } 61372fcbc377Syt 61382fcbc377Syt /* 61398aa6aadbSXiao-Yu Zhang * Update SATA registers at controller ports 61408aa6aadbSXiao-Yu Zhang * 61418aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before those functions 61428aa6aadbSXiao-Yu Zhang * get called. 61432fcbc377Syt */ 61442fcbc377Syt static void 61452fcbc377Syt ahci_update_sata_registers(ahci_ctl_t *ahci_ctlp, uint8_t port, 61462fcbc377Syt sata_device_t *sd) 61472fcbc377Syt { 61482fcbc377Syt sd->satadev_scr.sstatus = 61492fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 61502fcbc377Syt (uint32_t *)(AHCI_PORT_PxSSTS(ahci_ctlp, port))); 61512fcbc377Syt sd->satadev_scr.serror = 61522fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 61532fcbc377Syt (uint32_t *)(AHCI_PORT_PxSERR(ahci_ctlp, port))); 61542fcbc377Syt sd->satadev_scr.scontrol = 61552fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 61562fcbc377Syt (uint32_t *)(AHCI_PORT_PxSCTL(ahci_ctlp, port))); 61572fcbc377Syt sd->satadev_scr.sactive = 61582fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 61592fcbc377Syt (uint32_t *)(AHCI_PORT_PxSACT(ahci_ctlp, port))); 61602fcbc377Syt } 61612fcbc377Syt 61622fcbc377Syt /* 616368d33a25Syt * For poll mode, ahci_port_intr will be called to emulate the interrupt 61642fcbc377Syt */ 61652fcbc377Syt static void 616682263d52Syt ahci_port_intr(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port) 61672fcbc377Syt { 616868d33a25Syt uint32_t port_intr_status; 616968d33a25Syt uint32_t port_intr_enable; 617068d33a25Syt 6171f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 617268d33a25Syt "ahci_port_intr enter: port %d", port); 617368d33a25Syt 617468d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 617568d33a25Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_POLLING) { 617668d33a25Syt /* For SATA_OPMODE_POLLING commands */ 617768d33a25Syt port_intr_enable = 617868d33a25Syt (AHCI_INTR_STATUS_DHRS | 617968d33a25Syt AHCI_INTR_STATUS_PSS | 618068d33a25Syt AHCI_INTR_STATUS_SDBS | 618168d33a25Syt AHCI_INTR_STATUS_UFS | 618268d33a25Syt AHCI_INTR_STATUS_PCS | 618368d33a25Syt AHCI_INTR_STATUS_PRCS | 618468d33a25Syt AHCI_INTR_STATUS_OFS | 618568d33a25Syt AHCI_INTR_STATUS_INFS | 618668d33a25Syt AHCI_INTR_STATUS_IFS | 618768d33a25Syt AHCI_INTR_STATUS_HBDS | 618868d33a25Syt AHCI_INTR_STATUS_HBFS | 618968d33a25Syt AHCI_INTR_STATUS_TFES); 61908aa6aadbSXiao-Yu Zhang } else { 61918aa6aadbSXiao-Yu Zhang /* 61928aa6aadbSXiao-Yu Zhang * port_intr_enable indicates that the corresponding interrrupt 61938aa6aadbSXiao-Yu Zhang * reporting is enabled. 61948aa6aadbSXiao-Yu Zhang */ 61958aa6aadbSXiao-Yu Zhang port_intr_enable = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 61968aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port)); 619768d33a25Syt } 61988aa6aadbSXiao-Yu Zhang 61998aa6aadbSXiao-Yu Zhang /* IPMS error in port reset should be ignored according AHCI spec. */ 62008aa6aadbSXiao-Yu Zhang if (!(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_IGNORE_IPMS)) 62018aa6aadbSXiao-Yu Zhang port_intr_enable |= AHCI_INTR_STATUS_IPMS; 620268d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 620368d33a25Syt 620468d33a25Syt /* 620568d33a25Syt * port_intr_stats indicates that the corresponding interrupt 620668d33a25Syt * condition is active. 620768d33a25Syt */ 620868d33a25Syt port_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 620968d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port)); 621068d33a25Syt 6211f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 621268d33a25Syt "ahci_port_intr: port %d, port_intr_status = 0x%x, " 621382263d52Syt "port_intr_enable = 0x%x", 621482263d52Syt port, port_intr_status, port_intr_enable); 621568d33a25Syt 621668d33a25Syt port_intr_status &= port_intr_enable; 621768d33a25Syt 621868d33a25Syt /* First clear the port interrupts status */ 621968d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 622068d33a25Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 622168d33a25Syt port_intr_status); 622268d33a25Syt 622382263d52Syt /* Check the completed non-queued commands */ 622468d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_DHRS | 622568d33a25Syt AHCI_INTR_STATUS_PSS)) { 622668d33a25Syt (void) ahci_intr_cmd_cmplt(ahci_ctlp, 6227689d74b0Syt ahci_portp, port); 622868d33a25Syt } 622968d33a25Syt 623082263d52Syt /* Check the completed queued commands */ 623168d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_SDBS) { 623268d33a25Syt (void) ahci_intr_set_device_bits(ahci_ctlp, 623368d33a25Syt ahci_portp, port); 623468d33a25Syt } 623568d33a25Syt 623668d33a25Syt /* Check the port connect change status interrupt bit */ 623768d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PCS) { 623868d33a25Syt (void) ahci_intr_port_connect_change(ahci_ctlp, 623968d33a25Syt ahci_portp, port); 624068d33a25Syt } 624168d33a25Syt 624268d33a25Syt /* Check the device mechanical presence status interrupt bit */ 624368d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_DMPS) { 624468d33a25Syt (void) ahci_intr_device_mechanical_presence_status( 624568d33a25Syt ahci_ctlp, ahci_portp, port); 624668d33a25Syt } 624768d33a25Syt 624868d33a25Syt /* Check the PhyRdy change status interrupt bit */ 624968d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_PRCS) { 625068d33a25Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, 625168d33a25Syt port); 62522fcbc377Syt } 625368d33a25Syt 625468d33a25Syt /* 62558aa6aadbSXiao-Yu Zhang * Check the non-fatal error interrupt bits, there are four 625668d33a25Syt * kinds of non-fatal errors at the time being: 625768d33a25Syt * 625868d33a25Syt * PxIS.UFS - Unknown FIS Error 625968d33a25Syt * PxIS.OFS - Overflow Error 626068d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 62618aa6aadbSXiao-Yu Zhang * PxIS.IPMS - Incorrect Port Multiplier Status Error 626268d33a25Syt * 626368d33a25Syt * For these non-fatal errors, the HBA can continue to operate, 626468d33a25Syt * so the driver just log the error messages. 626568d33a25Syt */ 626668d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_UFS | 626768d33a25Syt AHCI_INTR_STATUS_OFS | 62688aa6aadbSXiao-Yu Zhang AHCI_INTR_STATUS_IPMS | 626968d33a25Syt AHCI_INTR_STATUS_INFS)) { 627068d33a25Syt (void) ahci_intr_non_fatal_error(ahci_ctlp, ahci_portp, 627168d33a25Syt port, port_intr_status); 627268d33a25Syt } 627368d33a25Syt 627468d33a25Syt /* 627568d33a25Syt * Check the fatal error interrupt bits, there are four kinds 627668d33a25Syt * of fatal errors for AHCI controllers: 627768d33a25Syt * 627868d33a25Syt * PxIS.HBFS - Host Bus Fatal Error 627968d33a25Syt * PxIS.HBDS - Host Bus Data Error 628068d33a25Syt * PxIS.IFS - Interface Fatal Error 628168d33a25Syt * PxIS.TFES - Task File Error 628268d33a25Syt * 628368d33a25Syt * The fatal error means the HBA can not recover from it by 628468d33a25Syt * itself, and it will try to abort the transfer, and the software 628568d33a25Syt * must intervene to restart the port. 628668d33a25Syt */ 628768d33a25Syt if (port_intr_status & (AHCI_INTR_STATUS_IFS | 628868d33a25Syt AHCI_INTR_STATUS_HBDS | 628968d33a25Syt AHCI_INTR_STATUS_HBFS | 629068d33a25Syt AHCI_INTR_STATUS_TFES)) 629168d33a25Syt (void) ahci_intr_fatal_error(ahci_ctlp, ahci_portp, 629282263d52Syt port, port_intr_status); 629368d33a25Syt 629468d33a25Syt /* Check the cold port detect interrupt bit */ 629568d33a25Syt if (port_intr_status & AHCI_INTR_STATUS_CPDS) { 629668d33a25Syt (void) ahci_intr_cold_port_detect(ahci_ctlp, ahci_portp, port); 629768d33a25Syt } 629868d33a25Syt 629968d33a25Syt /* Second clear the corresponding bit in IS.IPS */ 630068d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 630168d33a25Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (0x1 << port)); 63022fcbc377Syt } 63032fcbc377Syt 63042fcbc377Syt /* 63052fcbc377Syt * Interrupt service handler 63062fcbc377Syt */ 63072fcbc377Syt static uint_t 63082fcbc377Syt ahci_intr(caddr_t arg1, caddr_t arg2) 63092fcbc377Syt { 6310689d74b0Syt #ifndef __lock_lint 6311689d74b0Syt _NOTE(ARGUNUSED(arg2)) 6312689d74b0Syt #endif 6313689d74b0Syt /* LINTED */ 63142fcbc377Syt ahci_ctl_t *ahci_ctlp = (ahci_ctl_t *)arg1; 63152fcbc377Syt ahci_port_t *ahci_portp; 631668d33a25Syt int32_t global_intr_status; 63172fcbc377Syt uint8_t port; 63182fcbc377Syt 63192fcbc377Syt /* 63202fcbc377Syt * global_intr_status indicates that the corresponding port has 63212fcbc377Syt * an interrupt pending. 63222fcbc377Syt */ 63232fcbc377Syt global_intr_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 63242fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp)); 63252fcbc377Syt 63262fcbc377Syt if (!(global_intr_status & ahci_ctlp->ahcictl_ports_implemented)) { 63272fcbc377Syt /* The interrupt is not ours */ 63282fcbc377Syt return (DDI_INTR_UNCLAIMED); 63292fcbc377Syt } 63302fcbc377Syt 63312fcbc377Syt /* Loop for all the ports */ 63322fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 63332fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 63342fcbc377Syt continue; 63352fcbc377Syt } 63362fcbc377Syt if (!((0x1 << port) & global_intr_status)) { 63372fcbc377Syt continue; 63382fcbc377Syt } 63392fcbc377Syt 63402fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 63412fcbc377Syt 634268d33a25Syt /* Call ahci_port_intr */ 634382263d52Syt ahci_port_intr(ahci_ctlp, ahci_portp, port); 63442fcbc377Syt } 63452fcbc377Syt 63462fcbc377Syt return (DDI_INTR_CLAIMED); 63472fcbc377Syt } 63482fcbc377Syt 63492fcbc377Syt /* 635068d33a25Syt * For non-queued commands, when the corresponding bit in the PxCI register 635168d33a25Syt * is cleared, it means the command is completed successfully. And according 635268d33a25Syt * to the HBA state machine, there are three conditions which possibly will 635368d33a25Syt * try to clear the PxCI register bit. 635468d33a25Syt * 1. Receive one D2H Register FIS which is with 'I' bit set 635568d33a25Syt * 2. Update PIO Setup FIS 635668d33a25Syt * 3. Transmit a command and receive R_OK if CTBA.C is set (software reset) 635768d33a25Syt * 635882263d52Syt * Process completed non-queued commands when the interrupt status bit - 635982263d52Syt * AHCI_INTR_STATUS_DHRS or AHCI_INTR_STATUS_PSS is set. 63602fcbc377Syt * 636168d33a25Syt * AHCI_INTR_STATUS_DHRS means a D2H Register FIS has been received 636268d33a25Syt * with the 'I' bit set. And the following commands will send thus 636368d33a25Syt * FIS with 'I' bit set upon the successful completion: 63642fcbc377Syt * 1. Non-data commands 63652fcbc377Syt * 2. DMA data-in command 63662fcbc377Syt * 3. DMA data-out command 63672fcbc377Syt * 4. PIO data-out command 636868d33a25Syt * 5. PACKET non-data commands 636968d33a25Syt * 6. PACKET PIO data-in command 637068d33a25Syt * 7. PACKET PIO data-out command 637168d33a25Syt * 8. PACKET DMA data-in command 637268d33a25Syt * 9. PACKET DMA data-out command 637368d33a25Syt * 637468d33a25Syt * AHCI_INTR_STATUS_PSS means a PIO Setup FIS has been received 637568d33a25Syt * with the 'I' bit set. And the following commands will send this 637668d33a25Syt * FIS upon the successful completion: 637768d33a25Syt * 1. PIO data-in command 63782fcbc377Syt */ 63792fcbc377Syt static int 638082263d52Syt ahci_intr_cmd_cmplt(ahci_ctl_t *ahci_ctlp, 6381689d74b0Syt ahci_port_t *ahci_portp, uint8_t port) 63822fcbc377Syt { 638368d33a25Syt uint32_t port_cmd_issue = 0; 63842fcbc377Syt uint32_t finished_tags; 638568d33a25Syt int finished_slot; 63862fcbc377Syt sata_pkt_t *satapkt; 63872fcbc377Syt ahci_fis_d2h_register_t *rcvd_fisp; 638838547057Sying tian - Beijing China #if AHCI_DEBUG 638938547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 639038547057Sying tian - Beijing China uint32_t cmd_dmacount; 639138547057Sying tian - Beijing China #endif 63922fcbc377Syt 639368d33a25Syt mutex_enter(&ahci_portp->ahciport_mutex); 639482263d52Syt 639582263d52Syt if (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 63968aa6aadbSXiao-Yu Zhang !RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp) && 639782263d52Syt !NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 63982fcbc377Syt /* 63992fcbc377Syt * Spurious interrupt. Nothing to be done. 64002fcbc377Syt */ 64012fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 64022fcbc377Syt return (AHCI_SUCCESS); 64032fcbc377Syt } 64042fcbc377Syt 640568d33a25Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 640668d33a25Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 64072fcbc377Syt 640882263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 640982263d52Syt /* Slot 0 is always used during error recovery */ 641082263d52Syt finished_tags = 0x1 & ~port_cmd_issue; 6411f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 641282263d52Syt "ahci_intr_cmd_cmplt: port %d the sata pkt for error " 641382263d52Syt "retrieval is finished, and finished_tags = 0x%x", 641482263d52Syt port, finished_tags); 64158aa6aadbSXiao-Yu Zhang } else if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 64168aa6aadbSXiao-Yu Zhang finished_tags = 0x1 & ~port_cmd_issue; 64178aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO, ahci_ctlp, 64188aa6aadbSXiao-Yu Zhang "ahci_intr_cmd_cmplt: port %d the sata pkt for r/w " 64198aa6aadbSXiao-Yu Zhang "port multiplier is finished, and finished_tags = 0x%x", 64208aa6aadbSXiao-Yu Zhang port, finished_tags); 64218aa6aadbSXiao-Yu Zhang 642268d33a25Syt } else { 64238aa6aadbSXiao-Yu Zhang 642468d33a25Syt finished_tags = ahci_portp->ahciport_pending_tags & 642568d33a25Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 64262fcbc377Syt } 64272fcbc377Syt 6428f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 642982263d52Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x, " 643082263d52Syt "port_cmd_issue = 0x%x finished_tags = 0x%x", 643168d33a25Syt ahci_portp->ahciport_pending_tags, port_cmd_issue, 643282263d52Syt finished_tags); 64332fcbc377Syt 643482263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && 643582263d52Syt (finished_tags == 0x1)) { 643682263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 643782263d52Syt ASSERT(satapkt != NULL); 643882263d52Syt 6439f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 644082263d52Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 644182263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 644282263d52Syt 644382263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 644482263d52Syt goto out; 644582263d52Syt } 64462fcbc377Syt 64478aa6aadbSXiao-Yu Zhang if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp) && 64488aa6aadbSXiao-Yu Zhang (finished_tags == 0x1)) { 64498aa6aadbSXiao-Yu Zhang satapkt = ahci_portp->ahciport_rdwr_pmult_pkt; 64508aa6aadbSXiao-Yu Zhang ASSERT(satapkt != NULL); 64518aa6aadbSXiao-Yu Zhang 64528aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 64538aa6aadbSXiao-Yu Zhang "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 64548aa6aadbSXiao-Yu Zhang "with SATA_PKT_COMPLETED", (void *)satapkt); 64558aa6aadbSXiao-Yu Zhang 64568aa6aadbSXiao-Yu Zhang /* READ PORTMULT need copy out FIS content. */ 64578aa6aadbSXiao-Yu Zhang if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 64588aa6aadbSXiao-Yu Zhang rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 64598aa6aadbSXiao-Yu Zhang ahcirf_d2h_register_fis); 64608aa6aadbSXiao-Yu Zhang satapkt->satapkt_cmd.satacmd_status_reg = 64618aa6aadbSXiao-Yu Zhang GET_RFIS_STATUS(rcvd_fisp); 64628aa6aadbSXiao-Yu Zhang ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp); 64638aa6aadbSXiao-Yu Zhang } 64648aa6aadbSXiao-Yu Zhang 64658aa6aadbSXiao-Yu Zhang SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 64668aa6aadbSXiao-Yu Zhang goto out; 64678aa6aadbSXiao-Yu Zhang } 64688aa6aadbSXiao-Yu Zhang 646968d33a25Syt while (finished_tags) { 647068d33a25Syt finished_slot = ddi_ffs(finished_tags) - 1; 647168d33a25Syt if (finished_slot == -1) { 647268d33a25Syt goto out; 647368d33a25Syt } 64742fcbc377Syt 647568d33a25Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 647668d33a25Syt ASSERT(satapkt != NULL); 647738547057Sying tian - Beijing China #if AHCI_DEBUG 647838547057Sying tian - Beijing China /* 647938547057Sying tian - Beijing China * For non-native queued commands, the PRD byte count field 648038547057Sying tian - Beijing China * shall contain an accurate count of the number of bytes 648138547057Sying tian - Beijing China * transferred for the command before the PxCI bit is cleared 648238547057Sying tian - Beijing China * to '0' for the command. 648338547057Sying tian - Beijing China * 648438547057Sying tian - Beijing China * The purpose of this field is to let software know how many 648538547057Sying tian - Beijing China * bytes transferred for a given operation in order to 648638547057Sying tian - Beijing China * determine if underflow occurred. When issuing native command 648738547057Sying tian - Beijing China * queuing commands, this field should not be used and is not 648838547057Sying tian - Beijing China * required to be valid since in this case underflow is always 648938547057Sying tian - Beijing China * illegal. 649038547057Sying tian - Beijing China * 649138547057Sying tian - Beijing China * For data reads, the HBA will update its PRD byte count with 649238547057Sying tian - Beijing China * the total number of bytes received from the last FIS, and 649338547057Sying tian - Beijing China * may be able to continue normally. For data writes, the 649438547057Sying tian - Beijing China * device will detect an error, and HBA most likely will get 649538547057Sying tian - Beijing China * a fatal error. 649638547057Sying tian - Beijing China * 649738547057Sying tian - Beijing China * Therefore, here just put code to debug part. And please 649838547057Sying tian - Beijing China * refer to the comment above ahci_intr_fatal_error for the 649938547057Sying tian - Beijing China * definition of underflow error. 650038547057Sying tian - Beijing China */ 650138547057Sying tian - Beijing China cmd_dmacount = 650238547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[finished_slot]; 650338547057Sying tian - Beijing China if (cmd_dmacount) { 650438547057Sying tian - Beijing China cmd_header = 650538547057Sying tian - Beijing China &ahci_portp->ahciport_cmd_list[finished_slot]; 6506f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_PRDT, ahci_ctlp, 650738547057Sying tian - Beijing China "ahci_intr_cmd_cmplt: port %d, " 650838547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 650938547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 651038547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 651138547057Sying tian - Beijing China cmd_dmacount); 651238547057Sying tian - Beijing China 651338547057Sying tian - Beijing China if (cmd_header->ahcich_prd_byte_count != cmd_dmacount) { 6514f5f2d263SFred Herard AHCIDBG(AHCIDBG_UNDERFLOW, ahci_ctlp, 651538547057Sying tian - Beijing China "ahci_intr_cmd_cmplt: port %d, " 651638547057Sying tian - Beijing China "an underflow occurred", port); 651738547057Sying tian - Beijing China } 651838547057Sying tian - Beijing China } 651938547057Sying tian - Beijing China #endif 65202fcbc377Syt 65212fcbc377Syt /* 652268d33a25Syt * For SATAC_SMART command with SATA_SMART_RETURN_STATUS 652368d33a25Syt * feature, sata_special_regs flag will be set, and the 652468d33a25Syt * driver should copy the status and the other corresponding 652568d33a25Syt * register values in the D2H Register FIS received (It's 652668d33a25Syt * working on Non-data protocol) from the device back to 652768d33a25Syt * the sata_cmd. 652868d33a25Syt * 652968d33a25Syt * For every AHCI port, there is only one Received FIS 653068d33a25Syt * structure, which contains the FISes received from the 653168d33a25Syt * device, So we're trying to copy the content of D2H 653268d33a25Syt * Register FIS in the Received FIS structure back to 653368d33a25Syt * the sata_cmd. 65342fcbc377Syt */ 653568d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 653668d33a25Syt rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 653768d33a25Syt ahcirf_d2h_register_fis); 653868d33a25Syt satapkt->satapkt_cmd.satacmd_status_reg = 653968d33a25Syt GET_RFIS_STATUS(rcvd_fisp); 654068d33a25Syt ahci_copy_out_regs(&satapkt->satapkt_cmd, rcvd_fisp); 654168d33a25Syt } 65422fcbc377Syt 6543f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 654468d33a25Syt "ahci_intr_cmd_cmplt: sending up pkt 0x%p " 654568d33a25Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 65462fcbc377Syt 654768d33a25Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, finished_slot); 654868d33a25Syt CLEAR_BIT(finished_tags, finished_slot); 654968d33a25Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 65502fcbc377Syt 655168d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 65522fcbc377Syt } 65532fcbc377Syt out: 6554f5f2d263SFred Herard AHCIDBG(AHCIDBG_PKTCOMP, ahci_ctlp, 655568d33a25Syt "ahci_intr_cmd_cmplt: pending_tags = 0x%x", 65562fcbc377Syt ahci_portp->ahciport_pending_tags); 65572fcbc377Syt 65582fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 65592fcbc377Syt 65602fcbc377Syt return (AHCI_SUCCESS); 65612fcbc377Syt } 65622fcbc377Syt 65632fcbc377Syt /* 656468d33a25Syt * AHCI_INTR_STATUS_SDBS means a Set Device Bits FIS has been received 656582263d52Syt * with the 'I' bit set and has been copied into system memory. It will 656682263d52Syt * be sent under the following situations: 656782263d52Syt * 65688aa6aadbSXiao-Yu Zhang * 1. NCQ command is completed 656982263d52Syt * 657082263d52Syt * The completion of NCQ commands (READ/WRITE FPDMA QUEUED) is performed 657182263d52Syt * via the Set Device Bits FIS. When such event is generated, the software 657282263d52Syt * needs to read PxSACT register and compares the current value to the 657382263d52Syt * list of commands previously issue by software. ahciport_pending_ncq_tags 657482263d52Syt * keeps the tags of previously issued commands. 65752fcbc377Syt * 65768aa6aadbSXiao-Yu Zhang * 2. Asynchronous Notification 65778aa6aadbSXiao-Yu Zhang * 65788aa6aadbSXiao-Yu Zhang * Asynchronous Notification is a feature in SATA spec 2.6. 65798aa6aadbSXiao-Yu Zhang * 65808aa6aadbSXiao-Yu Zhang * 1) ATAPI device will send a signal to the host when media is inserted or 658168d33a25Syt * removed and avoids polling the device for media changes. The signal 658268d33a25Syt * sent to the host is a Set Device Bits FIS with the 'I' and 'N' bits 658382263d52Syt * set to '1'. At the moment, it's not supported yet. 65848aa6aadbSXiao-Yu Zhang * 65858aa6aadbSXiao-Yu Zhang * 2) Port multiplier will send a signal to the host when a hot plug event 65868aa6aadbSXiao-Yu Zhang * has occured on a port multiplier port. It is used when command based 65878aa6aadbSXiao-Yu Zhang * switching is employed. This is handled by ahci_intr_pmult_sntf_events() 65882fcbc377Syt */ 65892fcbc377Syt static int 659068d33a25Syt ahci_intr_set_device_bits(ahci_ctl_t *ahci_ctlp, 65912fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 65928aa6aadbSXiao-Yu Zhang { 65938aa6aadbSXiao-Yu Zhang ahci_addr_t addr; 65948aa6aadbSXiao-Yu Zhang 65958aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 65968aa6aadbSXiao-Yu Zhang "ahci_intr_set_device_bits enter: port %d", port); 65978aa6aadbSXiao-Yu Zhang 65988aa6aadbSXiao-Yu Zhang /* Initialize HBA port address */ 65998aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PORT(&addr, port); 66008aa6aadbSXiao-Yu Zhang 66018aa6aadbSXiao-Yu Zhang /* NCQ plug handler */ 66028aa6aadbSXiao-Yu Zhang (void) ahci_intr_ncq_events(ahci_ctlp, ahci_portp, &addr); 66038aa6aadbSXiao-Yu Zhang 66048aa6aadbSXiao-Yu Zhang /* Check port multiplier's asynchronous notification events */ 66058aa6aadbSXiao-Yu Zhang if (ahci_ctlp->ahcictl_cap & AHCI_CAP_SNTF) { 66068aa6aadbSXiao-Yu Zhang (void) ahci_intr_pmult_sntf_events(ahci_ctlp, 66078aa6aadbSXiao-Yu Zhang ahci_portp, port); 66088aa6aadbSXiao-Yu Zhang } 66098aa6aadbSXiao-Yu Zhang 66108aa6aadbSXiao-Yu Zhang /* ATAPI events is not supported yet */ 66118aa6aadbSXiao-Yu Zhang 66128aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 66138aa6aadbSXiao-Yu Zhang } 66148aa6aadbSXiao-Yu Zhang /* 66158aa6aadbSXiao-Yu Zhang * NCQ interrupt handler. Called upon a NCQ command is completed. 66168aa6aadbSXiao-Yu Zhang * Only be called from ahci_intr_set_device_bits(). 66178aa6aadbSXiao-Yu Zhang * 66188aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before the function is 66198aa6aadbSXiao-Yu Zhang * called. 66208aa6aadbSXiao-Yu Zhang */ 66218aa6aadbSXiao-Yu Zhang static int 66228aa6aadbSXiao-Yu Zhang ahci_intr_ncq_events(ahci_ctl_t *ahci_ctlp, 66238aa6aadbSXiao-Yu Zhang ahci_port_t *ahci_portp, ahci_addr_t *addrp) 66242fcbc377Syt { 662582263d52Syt uint32_t port_sactive; 662682263d52Syt uint32_t port_cmd_issue; 662782263d52Syt uint32_t issued_tags; 662882263d52Syt int issued_slot; 662982263d52Syt uint32_t finished_tags; 663082263d52Syt int finished_slot; 66318aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 663282263d52Syt sata_pkt_t *satapkt; 66332fcbc377Syt 6634f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 663568d33a25Syt "ahci_intr_set_device_bits enter: port %d", port); 66362fcbc377Syt 663782263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 663882263d52Syt if (!NCQ_CMD_IN_PROGRESS(ahci_portp)) { 663982263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 664082263d52Syt return (AHCI_SUCCESS); 664182263d52Syt } 664282263d52Syt 664382263d52Syt /* 664482263d52Syt * First the handler got which commands are finished by checking 664582263d52Syt * PxSACT register 664682263d52Syt */ 664782263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 664882263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 664968d33a25Syt 665082263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 665182263d52Syt ~port_sactive & AHCI_NCQ_SLOT_MASK(ahci_portp); 665282263d52Syt 6653f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 665482263d52Syt "ahci_intr_set_device_bits: port %d pending_ncq_tags = 0x%x " 665582263d52Syt "port_sactive = 0x%x", port, 665682263d52Syt ahci_portp->ahciport_pending_ncq_tags, port_sactive); 665782263d52Syt 6658f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 665982263d52Syt "ahci_intr_set_device_bits: finished_tags = 0x%x", finished_tags); 666082263d52Syt 666182263d52Syt /* 666282263d52Syt * For NCQ commands, the software can determine which command has 666382263d52Syt * already been transmitted to the device by checking PxCI register. 666482263d52Syt */ 666582263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 666682263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 666782263d52Syt 666882263d52Syt issued_tags = ahci_portp->ahciport_pending_tags & 666982263d52Syt ~port_cmd_issue & AHCI_SLOT_MASK(ahci_ctlp); 667082263d52Syt 6671f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 667282263d52Syt "ahci_intr_set_device_bits: port %d pending_tags = 0x%x " 667382263d52Syt "port_cmd_issue = 0x%x", port, 667482263d52Syt ahci_portp->ahciport_pending_tags, port_cmd_issue); 667582263d52Syt 6676f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 667782263d52Syt "ahci_intr_set_device_bits: issued_tags = 0x%x", issued_tags); 667882263d52Syt 667982263d52Syt /* 668082263d52Syt * Clear ahciport_pending_tags bit when the corresponding command 668182263d52Syt * is already sent down to the device. 668282263d52Syt */ 668382263d52Syt while (issued_tags) { 668482263d52Syt issued_slot = ddi_ffs(issued_tags) - 1; 668582263d52Syt if (issued_slot == -1) { 668682263d52Syt goto next; 668782263d52Syt } 668882263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, issued_slot); 668982263d52Syt CLEAR_BIT(issued_tags, issued_slot); 669082263d52Syt } 669182263d52Syt 669282263d52Syt next: 669382263d52Syt while (finished_tags) { 669482263d52Syt finished_slot = ddi_ffs(finished_tags) - 1; 669582263d52Syt if (finished_slot == -1) { 669682263d52Syt goto out; 669782263d52Syt } 669882263d52Syt 669982263d52Syt /* The command is certainly transmitted to the device */ 670082263d52Syt ASSERT(!(ahci_portp->ahciport_pending_tags & 670182263d52Syt (0x1 << finished_slot))); 670282263d52Syt 670382263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[finished_slot]; 670482263d52Syt ASSERT(satapkt != NULL); 670582263d52Syt 6706f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ, ahci_ctlp, 670782263d52Syt "ahci_intr_set_device_bits: sending up pkt 0x%p " 670882263d52Syt "with SATA_PKT_COMPLETED", (void *)satapkt); 670982263d52Syt 671082263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, finished_slot); 671182263d52Syt CLEAR_BIT(finished_tags, finished_slot); 671282263d52Syt ahci_portp->ahciport_slot_pkts[finished_slot] = NULL; 671382263d52Syt 671482263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 671568d33a25Syt } 671682263d52Syt out: 6717f5f2d263SFred Herard AHCIDBG(AHCIDBG_PKTCOMP|AHCIDBG_NCQ, ahci_ctlp, 671882263d52Syt "ahci_intr_set_device_bits: port %d " 671982263d52Syt "pending_ncq_tags = 0x%x pending_tags = 0x%x", 672082263d52Syt port, ahci_portp->ahciport_pending_ncq_tags, 672182263d52Syt ahci_portp->ahciport_pending_tags); 672282263d52Syt 672382263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 67242fcbc377Syt 67252fcbc377Syt return (AHCI_SUCCESS); 67262fcbc377Syt } 67272fcbc377Syt 67288aa6aadbSXiao-Yu Zhang /* 67298aa6aadbSXiao-Yu Zhang * Port multiplier asynchronous notification event handler. Called upon a 67308aa6aadbSXiao-Yu Zhang * device is hot plugged/pulled. 67318aa6aadbSXiao-Yu Zhang * 67328aa6aadbSXiao-Yu Zhang * The async-notification event will only be recorded by ahcipmi_snotif_tags 67338aa6aadbSXiao-Yu Zhang * here and will be handled by ahci_probe_pmult(). 67348aa6aadbSXiao-Yu Zhang * 67358aa6aadbSXiao-Yu Zhang * NOTE: called only from ahci_port_intr(). 67368aa6aadbSXiao-Yu Zhang */ 67378aa6aadbSXiao-Yu Zhang static int 67388aa6aadbSXiao-Yu Zhang ahci_intr_pmult_sntf_events(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 67398aa6aadbSXiao-Yu Zhang uint8_t port) 67408aa6aadbSXiao-Yu Zhang { 67418aa6aadbSXiao-Yu Zhang sata_device_t sdevice; 67428aa6aadbSXiao-Yu Zhang 67438aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 67448aa6aadbSXiao-Yu Zhang "ahci_intr_pmult_sntf_events enter: port %d ", port); 67458aa6aadbSXiao-Yu Zhang 67468aa6aadbSXiao-Yu Zhang /* no hot-plug while attaching process */ 67478aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_ctlp->ahcictl_mutex); 67488aa6aadbSXiao-Yu Zhang if (ahci_ctlp->ahcictl_flags & AHCI_ATTACH) { 67498aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_ctlp->ahcictl_mutex); 67508aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 67518aa6aadbSXiao-Yu Zhang } 67528aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_ctlp->ahcictl_mutex); 67538aa6aadbSXiao-Yu Zhang 67548aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 67558aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type != SATA_DTYPE_PMULT) { 67568aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 67578aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 67588aa6aadbSXiao-Yu Zhang } 67598aa6aadbSXiao-Yu Zhang 67608aa6aadbSXiao-Yu Zhang ASSERT(ahci_portp->ahciport_pmult_info != NULL); 67618aa6aadbSXiao-Yu Zhang 67628aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pmult_info->ahcipmi_snotif_tags = 67638aa6aadbSXiao-Yu Zhang ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 67648aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSNTF(ahci_ctlp, port)); 67658aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 67668aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxSNTF(ahci_ctlp, port), 67678aa6aadbSXiao-Yu Zhang AHCI_SNOTIF_CLEAR_ALL); 67688aa6aadbSXiao-Yu Zhang 67698aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_pmult_info->ahcipmi_snotif_tags == 0) { 67708aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 67718aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 67728aa6aadbSXiao-Yu Zhang } 67738aa6aadbSXiao-Yu Zhang 67748aa6aadbSXiao-Yu Zhang /* Port Multiplier sub-device hot-plug handler */ 67758aa6aadbSXiao-Yu Zhang if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 67768aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 67778aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 67788aa6aadbSXiao-Yu Zhang } 67798aa6aadbSXiao-Yu Zhang 67808aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_PMULT_SNTF) { 67818aa6aadbSXiao-Yu Zhang /* Not allowed to re-enter. */ 67828aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 67838aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 67848aa6aadbSXiao-Yu Zhang } 67858aa6aadbSXiao-Yu Zhang 67868aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_PMULT_SNTF; 67878aa6aadbSXiao-Yu Zhang 67888aa6aadbSXiao-Yu Zhang /* 67898aa6aadbSXiao-Yu Zhang * NOTE: 67908aa6aadbSXiao-Yu Zhang * Even if Asynchronous Notification is supported (and enabled) by 67918aa6aadbSXiao-Yu Zhang * both controller and the port multiplier, the content of PxSNTF 67928aa6aadbSXiao-Yu Zhang * register is always set to 0x8000 by async notification event. We 67938aa6aadbSXiao-Yu Zhang * need to check GSCR[32] on the port multiplier to find out the 67948aa6aadbSXiao-Yu Zhang * owner of this event. 67958aa6aadbSXiao-Yu Zhang * This is not accord with SATA spec 2.6 and needs further 67968aa6aadbSXiao-Yu Zhang * clarification. 67978aa6aadbSXiao-Yu Zhang */ 67988aa6aadbSXiao-Yu Zhang /* hot-plug will not reported while reseting. */ 67998aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_reset_in_progress == 1) { 68008aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 68018aa6aadbSXiao-Yu Zhang "port %d snotif event ignored", port); 68028aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_PMULT_SNTF; 68038aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 68048aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 68058aa6aadbSXiao-Yu Zhang } 68068aa6aadbSXiao-Yu Zhang 68078aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp, 68088aa6aadbSXiao-Yu Zhang "PxSNTF is set to 0x%x by port multiplier", 68098aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_pmult_info->ahcipmi_snotif_tags); 68108aa6aadbSXiao-Yu Zhang 68118aa6aadbSXiao-Yu Zhang /* 68128aa6aadbSXiao-Yu Zhang * Now we need do some necessary operation and inform SATA framework 68138aa6aadbSXiao-Yu Zhang * that link/device events has happened. 68148aa6aadbSXiao-Yu Zhang */ 68158aa6aadbSXiao-Yu Zhang bzero((void *)&sdevice, sizeof (sata_device_t)); 68168aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.cport = ahci_ctlp-> 68178aa6aadbSXiao-Yu Zhang ahcictl_port_to_cport[port]; 68188aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.pmport = SATA_PMULT_HOSTPORT; 68198aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.qual = SATA_ADDR_PMULT; 68208aa6aadbSXiao-Yu Zhang sdevice.satadev_state = SATA_PSTATE_PWRON; 68218aa6aadbSXiao-Yu Zhang 68228aa6aadbSXiao-Yu Zhang /* Just reject packets, do not stop that port. */ 68238aa6aadbSXiao-Yu Zhang ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 68248aa6aadbSXiao-Yu Zhang 68258aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 68268aa6aadbSXiao-Yu Zhang sata_hba_event_notify( 68278aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 68288aa6aadbSXiao-Yu Zhang &sdevice, 68298aa6aadbSXiao-Yu Zhang SATA_EVNT_PMULT_LINK_CHANGED); 68308aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 68318aa6aadbSXiao-Yu Zhang 68328aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_PMULT_SNTF; 68338aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 68348aa6aadbSXiao-Yu Zhang 68358aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 68368aa6aadbSXiao-Yu Zhang } 68378aa6aadbSXiao-Yu Zhang 68382fcbc377Syt /* 683968d33a25Syt * 1=Change in Current Connect Status. 0=No change in Current Connect Status. 684068d33a25Syt * This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared 684168d33a25Syt * when PxSERR.DIAG.X is cleared. When PxSERR.DIAG.X is set to one, it 684268d33a25Syt * indicates a COMINIT signal was received. 68432fcbc377Syt * 684468d33a25Syt * Hot plug insertion is detected by reception of a COMINIT signal from the 684568d33a25Syt * device. On reception of unsolicited COMINIT, the HBA shall generate a 684668d33a25Syt * COMRESET. If the COMINIT is in responce to a COMRESET, then the HBA shall 684768d33a25Syt * begin the normal communication negotiation sequence as outlined in the 684868d33a25Syt * Serial ATA 1.0a specification. When a COMRESET is sent to the device the 684968d33a25Syt * PxSSTS.DET field shall be cleared to 0h. When a COMINIT is received, the 685068d33a25Syt * PxSSTS.DET field shall be set to 1h. When the communication negotiation 685168d33a25Syt * sequence is complete and PhyRdy is true the PxSSTS.DET field shall be set 685268d33a25Syt * to 3h. Therefore, at the moment the ahci driver is going to check PhyRdy 685368d33a25Syt * to handle hot plug insertion. In this interrupt handler, just do nothing 685468d33a25Syt * but print some log message and clear the bit. 68552fcbc377Syt */ 68562fcbc377Syt static int 685768d33a25Syt ahci_intr_port_connect_change(ahci_ctl_t *ahci_ctlp, 68582fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 68592fcbc377Syt { 6860689d74b0Syt #if AHCI_DEBUG 68612fcbc377Syt uint32_t port_serror; 6862689d74b0Syt #endif 68632fcbc377Syt 68642fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 68652fcbc377Syt 6866689d74b0Syt #if AHCI_DEBUG 68672fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 68682fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 68692fcbc377Syt 6870f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 687168d33a25Syt "ahci_intr_port_connect_change: port %d, " 687268d33a25Syt "port_serror = 0x%x", port, port_serror); 6873689d74b0Syt #endif 68742fcbc377Syt 687568d33a25Syt /* Clear PxSERR.DIAG.X to clear the interrupt bit */ 68762fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 68772fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 687882263d52Syt SERROR_EXCHANGED_ERR); 68792fcbc377Syt 68802fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 68812fcbc377Syt 68822fcbc377Syt return (AHCI_SUCCESS); 68832fcbc377Syt } 68842fcbc377Syt 68852fcbc377Syt /* 68862fcbc377Syt * Hot Plug Operation for platforms that support Mechanical Presence 68872fcbc377Syt * Switches. 68882fcbc377Syt * 68892fcbc377Syt * When set, it indicates that a mechanical presence switch attached to this 68902fcbc377Syt * port has been opened or closed, which may lead to a change in the connection 68912fcbc377Syt * state of the device. This bit is only valid if both CAP.SMPS and PxCMD.MPSP 68922fcbc377Syt * are set to '1'. 68932fcbc377Syt * 689468d33a25Syt * At the moment, this interrupt is not needed and disabled and we just log 68952fcbc377Syt * the debug message. 68962fcbc377Syt */ 68972fcbc377Syt static int 68982fcbc377Syt ahci_intr_device_mechanical_presence_status(ahci_ctl_t *ahci_ctlp, 68992fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 69002fcbc377Syt { 69012fcbc377Syt uint32_t cap_status, port_cmd_status; 69022fcbc377Syt 6903f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 69042fcbc377Syt "ahci_intr_device_mechanical_presence_status enter, " 69052fcbc377Syt "port %d", port); 69062fcbc377Syt 69072fcbc377Syt cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 69082fcbc377Syt (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp)); 69092fcbc377Syt 69102fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 69112fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 69122fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 69132fcbc377Syt 69142fcbc377Syt if (!(cap_status & AHCI_HBA_CAP_SMPS) || 69152fcbc377Syt !(port_cmd_status & AHCI_CMD_STATUS_MPSP)) { 6916f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 69172fcbc377Syt "CAP.SMPS or PxCMD.MPSP is not set, so just ignore " 69182fcbc377Syt "the interrupt: cap_status = 0x%x, " 69192fcbc377Syt "port_cmd_status = 0x%x", cap_status, port_cmd_status); 69202fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 69212fcbc377Syt 69222fcbc377Syt return (AHCI_SUCCESS); 69232fcbc377Syt } 69242fcbc377Syt 6925689d74b0Syt #if AHCI_DEBUG 69262fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_MPSS) { 6927f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 69282fcbc377Syt "The mechanical presence switch is open: " 69292fcbc377Syt "port %d, port_cmd_status = 0x%x", 69302fcbc377Syt port, port_cmd_status); 69312fcbc377Syt } else { 6932f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 69332fcbc377Syt "The mechanical presence switch is close: " 69342fcbc377Syt "port %d, port_cmd_status = 0x%x", 69352fcbc377Syt port, port_cmd_status); 69362fcbc377Syt } 6937689d74b0Syt #endif 69382fcbc377Syt 69392fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 69402fcbc377Syt 69412fcbc377Syt return (AHCI_SUCCESS); 69422fcbc377Syt } 69432fcbc377Syt 69442fcbc377Syt /* 69452fcbc377Syt * Native Hot Plug Support. 69462fcbc377Syt * 69472fcbc377Syt * When set, it indicates that the internal PHYRDY signal changed state. 69482fcbc377Syt * This bit reflects the state of PxSERR.DIAG.N. 694968d33a25Syt * 695068d33a25Syt * There are three kinds of conditions to generate this interrupt event: 695168d33a25Syt * 1. a device is inserted 695268d33a25Syt * 2. a device is disconnected 695368d33a25Syt * 3. when the link enters/exits a Partial or Slumber interface power 695468d33a25Syt * management state 695568d33a25Syt * 695668d33a25Syt * If inteface power management is enabled for a port, the PxSERR.DIAG.N 695768d33a25Syt * bit may be set due to the link entering the Partial or Slumber power 695868d33a25Syt * management state, rather than due to a hot plug insertion or removal 695913bcbb7aSyt * event. So far, the interface power management is disabled, so the 696013bcbb7aSyt * driver can reliably get removal detection notification via the 696113bcbb7aSyt * PxSERR.DIAG.N bit. 69622fcbc377Syt */ 69632fcbc377Syt static int 69642fcbc377Syt ahci_intr_phyrdy_change(ahci_ctl_t *ahci_ctlp, 69652fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 69662fcbc377Syt { 69672fcbc377Syt uint32_t port_sstatus = 0; /* No dev present & PHY not established. */ 69682fcbc377Syt sata_device_t sdevice; 69692fcbc377Syt int dev_exists_now = 0; 69702fcbc377Syt int dev_existed_previously = 0; 69718aa6aadbSXiao-Yu Zhang ahci_addr_t port_addr; 69722fcbc377Syt 6973f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY, ahci_ctlp, 69742fcbc377Syt "ahci_intr_phyrdy_change enter, port %d", port); 69752fcbc377Syt 69762fcbc377Syt /* Clear PxSERR.DIAG.N to clear the interrupt bit */ 69772fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 69782fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 69792fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 698082263d52Syt SERROR_PHY_RDY_CHG); 69812fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 69822fcbc377Syt 69832fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 69842fcbc377Syt if ((ahci_ctlp->ahcictl_sata_hba_tran == NULL) || 69852fcbc377Syt (ahci_portp == NULL)) { 69862fcbc377Syt /* The whole controller setup is not yet done. */ 69872fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 69882fcbc377Syt return (AHCI_SUCCESS); 69892fcbc377Syt } 69902fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 69912fcbc377Syt 69922fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 69932fcbc377Syt 69942fcbc377Syt /* SStatus tells the presence of device. */ 69952fcbc377Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 69962fcbc377Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 69972fcbc377Syt 699882263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 699968d33a25Syt dev_exists_now = 1; 700068d33a25Syt } 70012fcbc377Syt 70022fcbc377Syt if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) { 70032fcbc377Syt dev_existed_previously = 1; 70042fcbc377Syt } 70052fcbc377Syt 700682263d52Syt if (ahci_portp->ahciport_flags & AHCI_PORT_FLAG_NODEV) { 700782263d52Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_NODEV; 7008f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 700982263d52Syt "ahci_intr_phyrdy_change: port %d " 701082263d52Syt "AHCI_PORT_FLAG_NODEV is cleared", port); 701182263d52Syt if (dev_exists_now == 0) 701282263d52Syt dev_existed_previously = 1; 701382263d52Syt } 701482263d52Syt 70152fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 701609121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 70172fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 70182fcbc377Syt sdevice.satadev_addr.pmport = 0; 70192fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 70202fcbc377Syt ahci_portp->ahciport_port_state = SATA_PSTATE_PWRON; 70212fcbc377Syt 70228aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PORT(&port_addr, port); 70238aa6aadbSXiao-Yu Zhang 70248aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_HOTPLUG; 70252fcbc377Syt if (dev_exists_now) { 70268aa6aadbSXiao-Yu Zhang if (dev_existed_previously) { /* 1 -> 1 */ 70272fcbc377Syt /* Things are fine now. The loss was temporary. */ 7028f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 702968d33a25Syt "ahci_intr_phyrdy_change port %d " 70302fcbc377Syt "device link lost/established", port); 70312fcbc377Syt 70322fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 70332fcbc377Syt sata_hba_event_notify( 70342fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 70352fcbc377Syt &sdevice, 70362fcbc377Syt SATA_EVNT_LINK_LOST|SATA_EVNT_LINK_ESTABLISHED); 70372fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 70382fcbc377Syt 70398aa6aadbSXiao-Yu Zhang } else { /* 0 -> 1 */ 7040f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 704168d33a25Syt "ahci_intr_phyrdy_change: port %d " 70422fcbc377Syt "device link established", port); 70432fcbc377Syt 70448aa6aadbSXiao-Yu Zhang /* 70458aa6aadbSXiao-Yu Zhang * A new device has been detected. The new device 70468aa6aadbSXiao-Yu Zhang * might be a port multiplier instead of a drive, so 70478aa6aadbSXiao-Yu Zhang * we cannot update the signature directly. 70488aa6aadbSXiao-Yu Zhang */ 70498aa6aadbSXiao-Yu Zhang (void) ahci_initialize_port(ahci_ctlp, 70508aa6aadbSXiao-Yu Zhang ahci_portp, &port_addr); 70512fcbc377Syt 705268d33a25Syt /* Try to start the port */ 705368d33a25Syt if (ahci_start_port(ahci_ctlp, ahci_portp, port) 705468d33a25Syt != AHCI_SUCCESS) { 705568d33a25Syt sdevice.satadev_state |= SATA_PSTATE_FAILED; 7056f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 705768d33a25Syt "ahci_intr_phyrdy_change: port %d failed " 70582fcbc377Syt "at start port", port); 70592fcbc377Syt } 70602fcbc377Syt 706182263d52Syt /* Clear the max queue depth for inserted device */ 706282263d52Syt ahci_portp->ahciport_max_ncq_tags = 0; 706382263d52Syt 70642fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 70652fcbc377Syt sata_hba_event_notify( 70662fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 70672fcbc377Syt &sdevice, 70682fcbc377Syt SATA_EVNT_LINK_ESTABLISHED); 70692fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 70702fcbc377Syt 70712fcbc377Syt } 70722fcbc377Syt } else { /* No device exists now */ 70732fcbc377Syt 70748aa6aadbSXiao-Yu Zhang if (dev_existed_previously) { /* 1 -> 0 */ 7075f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 707668d33a25Syt "ahci_intr_phyrdy_change: port %d " 70772fcbc377Syt "device link lost", port); 70782fcbc377Syt 70792fcbc377Syt ahci_reject_all_abort_pkts(ahci_ctlp, ahci_portp, port); 708068d33a25Syt (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 70812fcbc377Syt ahci_portp, port); 70822fcbc377Syt 70838aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type == 70848aa6aadbSXiao-Yu Zhang SATA_DTYPE_PMULT) { 70858aa6aadbSXiao-Yu Zhang ahci_dealloc_pmult(ahci_ctlp, ahci_portp); 70868aa6aadbSXiao-Yu Zhang } 70878aa6aadbSXiao-Yu Zhang 70882fcbc377Syt /* An existing device is lost. */ 70892fcbc377Syt ahci_portp->ahciport_device_type = SATA_DTYPE_NONE; 70908aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_STATE_UNKNOWN; 70912fcbc377Syt 70922fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 70932fcbc377Syt sata_hba_event_notify( 70942fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 70952fcbc377Syt &sdevice, 70962fcbc377Syt SATA_EVNT_LINK_LOST); 70972fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 70982fcbc377Syt } 70992fcbc377Syt } 71008aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_HOTPLUG; 71012fcbc377Syt 71022fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 71032fcbc377Syt 71042fcbc377Syt return (AHCI_SUCCESS); 71052fcbc377Syt } 71062fcbc377Syt 71072fcbc377Syt /* 710868d33a25Syt * PxIS.UFS - Unknown FIS Error 71092fcbc377Syt * 711068d33a25Syt * This interrupt event means an unknown FIS was received and has been 711168d33a25Syt * copied into system memory. An unknown FIS is not considered an illegal 711268d33a25Syt * FIS, unless the length received is more than 64 bytes. If an unknown 711368d33a25Syt * FIS arrives with length <= 64 bytes, it is posted and the HBA continues 711468d33a25Syt * normal operation. If the unknown FIS is more than 64 bytes, then it 711568d33a25Syt * won't be posted to memory and PxSERR.ERR.P will be set, which is then 711668d33a25Syt * a fatal error. 71172fcbc377Syt * 71188aa6aadbSXiao-Yu Zhang * PxIS.IPMS - Incorrect Port Multiplier Status 71198aa6aadbSXiao-Yu Zhang * 71208aa6aadbSXiao-Yu Zhang * IPMS Indicates that the HBA received a FIS from a device that did not 71218aa6aadbSXiao-Yu Zhang * have a command outstanding. The IPMS bit may be set during enumeration 71228aa6aadbSXiao-Yu Zhang * of devices on a Port Multiplier due to the normal Port Multiplier 71238aa6aadbSXiao-Yu Zhang * enumeration process. It is recommended that IPMS only be used after 71248aa6aadbSXiao-Yu Zhang * enumeration is complete on the Port Multiplier (copied from spec). 71258aa6aadbSXiao-Yu Zhang * 712668d33a25Syt * PxIS.OFS - Overflow Error 71272fcbc377Syt * 712868d33a25Syt * Command list overflow is defined as software building a command table 712968d33a25Syt * that has fewer total bytes than the transaction given to the device. 713068d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 713168d33a25Syt * will be no room to put the data. 71322fcbc377Syt * 713368d33a25Syt * For an overflow on data read, either PIO or DMA, the HBA will set 713468d33a25Syt * PxIS.OFS, and the HBA will do a best effort to continue, and it's a 713568d33a25Syt * non-fatal error when the HBA can continues. Sometimes, it will cause 713668d33a25Syt * a fatal error and need the software to do something. 71372fcbc377Syt * 713868d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 713968d33a25Syt * DMA and PIO, and it's a fatal error, and a COMRESET is required by 714068d33a25Syt * software to clean up from this serious error. 71412fcbc377Syt * 714268d33a25Syt * PxIS.INFS - Interface Non-Fatal Error 714368d33a25Syt * 714468d33a25Syt * This interrupt event indicates that the HBA encountered an error on 714568d33a25Syt * the Serial ATA interface but was able to continue operation. The kind 714668d33a25Syt * of error usually occurred during a non-Data FIS, and under this condition 714768d33a25Syt * the FIS will be re-transmitted by HBA automatically. 71482fcbc377Syt * 714968d33a25Syt * When the FMA is implemented, there should be a stat structure to 715068d33a25Syt * record how many every kind of error happens. 71512fcbc377Syt */ 71522fcbc377Syt static int 715368d33a25Syt ahci_intr_non_fatal_error(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 715468d33a25Syt uint8_t port, uint32_t intr_status) 71552fcbc377Syt { 71562fcbc377Syt uint32_t port_serror; 715768d33a25Syt #if AHCI_DEBUG 71582fcbc377Syt uint32_t port_cmd_status; 715982263d52Syt uint32_t port_cmd_issue; 716082263d52Syt uint32_t port_sactive; 71612fcbc377Syt int current_slot; 716282263d52Syt uint32_t current_tags; 716368d33a25Syt sata_pkt_t *satapkt; 716438547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 716538547057Sying tian - Beijing China uint32_t cmd_dmacount; 716668d33a25Syt #endif 71672fcbc377Syt 71682fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 71692fcbc377Syt 71702fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 71712fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 71722fcbc377Syt 7173f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ENTRY|AHCIDBG_ERRS, ahci_ctlp, 717468d33a25Syt "ahci_intr_non_fatal_error: port %d, " 71758aa6aadbSXiao-Yu Zhang "PxSERR = 0x%x, PxIS = 0x%x ", port, port_serror, intr_status); 71762fcbc377Syt 7177a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 1); 717868d33a25Syt 717968d33a25Syt if (intr_status & AHCI_INTR_STATUS_UFS) { 7180f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 718168d33a25Syt "ahci port %d has unknown FIS error", port); 71822fcbc377Syt 718368d33a25Syt /* Clear the interrupt bit by clearing PxSERR.DIAG.F */ 718468d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 718568d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 718682263d52Syt SERROR_FIS_TYPE); 718768d33a25Syt } 718868d33a25Syt 718968d33a25Syt #if AHCI_DEBUG 71908aa6aadbSXiao-Yu Zhang if (intr_status & AHCI_INTR_STATUS_IPMS) { 71918aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci port %d " 71928aa6aadbSXiao-Yu Zhang "has Incorrect Port Multiplier Status error", port); 71938aa6aadbSXiao-Yu Zhang } 71948aa6aadbSXiao-Yu Zhang 719568d33a25Syt if (intr_status & AHCI_INTR_STATUS_OFS) { 7196f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 719768d33a25Syt "ahci port %d has overflow error", port); 719868d33a25Syt } 719968d33a25Syt 720068d33a25Syt if (intr_status & AHCI_INTR_STATUS_INFS) { 7201f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 720268d33a25Syt "ahci port %d has interface non fatal error", port); 720368d33a25Syt } 72042fcbc377Syt 72052fcbc377Syt /* 72062fcbc377Syt * Record the error occurred command's slot. 72072fcbc377Syt */ 720882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 720982263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 721082263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 721182263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 721282263d52Syt 721382263d52Syt current_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 721482263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 72152fcbc377Syt 721682263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 721782263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 721882263d52Syt ASSERT(satapkt != NULL); 721982263d52Syt ASSERT(current_slot == 0); 722082263d52Syt } else { 722182263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 722282263d52Syt } 72232fcbc377Syt 722482263d52Syt if (satapkt != NULL) { 7225f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 722682263d52Syt "ahci_intr_non_fatal_error: pending_tags = 0x%x " 722782263d52Syt "cmd 0x%x", ahci_portp->ahciport_pending_tags, 722882263d52Syt satapkt->satapkt_cmd.satacmd_cmd_reg); 72292fcbc377Syt 7230f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 723182263d52Syt "ahci_intr_non_fatal_error: port %d, " 723282263d52Syt "satapkt 0x%p is being processed when error occurs", 723382263d52Syt port, (void *)satapkt); 723438547057Sying tian - Beijing China 723538547057Sying tian - Beijing China /* 723638547057Sying tian - Beijing China * PRD Byte Count field of command header is not 723738547057Sying tian - Beijing China * required to reflect the total number of bytes 723838547057Sying tian - Beijing China * transferred when an overflow occurs, so here 723938547057Sying tian - Beijing China * just log the value. 724038547057Sying tian - Beijing China */ 724138547057Sying tian - Beijing China cmd_dmacount = 724238547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[current_slot]; 724338547057Sying tian - Beijing China if (cmd_dmacount) { 724438547057Sying tian - Beijing China cmd_header = &ahci_portp-> 724538547057Sying tian - Beijing China ahciport_cmd_list[current_slot]; 7246f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 724738547057Sying tian - Beijing China "ahci_intr_non_fatal_error: port %d, " 724838547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 724938547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 725038547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 725138547057Sying tian - Beijing China cmd_dmacount); 725238547057Sying tian - Beijing China } 725382263d52Syt } 7254a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 725582263d52Syt /* 725682263d52Syt * For queued command, list those command which have already 725782263d52Syt * been transmitted to the device and still not completed. 725882263d52Syt */ 725982263d52Syt port_sactive = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 726082263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 726182263d52Syt 726282263d52Syt port_cmd_issue = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 726382263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 726482263d52Syt 7265f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, ahci_ctlp, 726682263d52Syt "ahci_intr_non_fatal_error: pending_ncq_tags = 0x%x " 726782263d52Syt "port_sactive = 0x%x port_cmd_issue = 0x%x", 726882263d52Syt ahci_portp->ahciport_pending_ncq_tags, 726982263d52Syt port_sactive, port_cmd_issue); 727082263d52Syt 727182263d52Syt current_tags = ahci_portp->ahciport_pending_ncq_tags & 727282263d52Syt port_sactive & ~port_cmd_issue & 727382263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 727482263d52Syt 727582263d52Syt while (current_tags) { 727682263d52Syt current_slot = ddi_ffs(current_tags) - 1; 727782263d52Syt if (current_slot == -1) { 727882263d52Syt goto out; 727982263d52Syt } 728082263d52Syt 728182263d52Syt satapkt = ahci_portp->ahciport_slot_pkts[current_slot]; 7282f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_NCQ|AHCIDBG_ERRS, 728382263d52Syt ahci_ctlp, "ahci_intr_non_fatal_error: " 728482263d52Syt "port %d, satapkt 0x%p is outstanding when " 728582263d52Syt "error occurs", port, (void *)satapkt); 7286a419422eSXiao-Yu Zhang 7287a419422eSXiao-Yu Zhang CLEAR_BIT(current_tags, current_slot); 728882263d52Syt } 72892fcbc377Syt } 729082263d52Syt out: 72912fcbc377Syt #endif 72922fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 72932fcbc377Syt 72942fcbc377Syt return (AHCI_SUCCESS); 72952fcbc377Syt } 72962fcbc377Syt 72972fcbc377Syt /* 729868d33a25Syt * According to the AHCI spec, the error types include system memory 729968d33a25Syt * errors, interface errors, port multiplier errors, device errors, 730068d33a25Syt * command list overflow, command list underflow, native command 730168d33a25Syt * queuing tag errors and pio data transfer errors. 730268d33a25Syt * 730368d33a25Syt * System memory errors such as target abort, master abort, and parity 730468d33a25Syt * may cause the host to stop, and they are serious errors and needed 730568d33a25Syt * to be recovered with software intervention. When system software 730668d33a25Syt * has given a pointer to the HBA that doesn't exist in physical memory, 730768d33a25Syt * a master/target abort error occurs, and PxIS.HBFS will be set. A 730868d33a25Syt * data error such as CRC or parity occurs, the HBA aborts the transfer 730968d33a25Syt * (if necessary) and PxIS.HBDS will be set. 731068d33a25Syt * 731168d33a25Syt * Interface errors are errors that occur due to electrical issues on 731268d33a25Syt * the interface, or protocol miscommunication between the device and 731368d33a25Syt * HBA, and the respective PxSERR register bit will be set. And PxIS.IFS 731468d33a25Syt * (fatal) or PxIS.INFS (non-fatal) will be set. The conditions that 731568d33a25Syt * causes PxIS.IFS/PxIS.INFS to be set are 731668d33a25Syt * 1. in PxSERR.ERR, P bit is set to '1' 731768d33a25Syt * 2. in PxSERR.DIAG, C or H bit is set to '1' 731868d33a25Syt * 3. PhyRdy drop unexpectly, N bit is set to '1' 731968d33a25Syt * If the error occurred during a non-data FIS, the FIS must be 732068d33a25Syt * retransmitted, and the error is non-fatal and PxIS.INFS is set. If 732168d33a25Syt * the error occurred during a data FIS, the transfer will stop, so 732268d33a25Syt * the error is fatal and PxIS.IFS is set. 732368d33a25Syt * 732468d33a25Syt * When a FIS arrives that updates the taskfile, the HBA checks to see 732568d33a25Syt * if PxTFD.STS.ERR is set. If yes, PxIS.TFES will be set and the HBA 732668d33a25Syt * stops processing any more commands. 73272fcbc377Syt * 732868d33a25Syt * Command list overflow is defined as software building a command table 732968d33a25Syt * that has fewer total bytes than the transaction given to the device. 733068d33a25Syt * On device writes, the HBA will run out of data, and on reads, there 733168d33a25Syt * will be no room to put the data. For an overflow on data read, either 733268d33a25Syt * PIO or DMA, the HBA will set PxIS.OFS, and it's a non-fatal error. 733368d33a25Syt * For an overflow on data write, setting PxIS.OFS is optional for both 733468d33a25Syt * DMA and PIO, and a COMRESET is required by software to clean up from 733568d33a25Syt * this serious error. 73362fcbc377Syt * 733768d33a25Syt * Command list underflow is defined as software building a command 733868d33a25Syt * table that has more total bytes than the transaction given to the 733968d33a25Syt * device. For data writes, both PIO and DMA, the device will detect 734068d33a25Syt * an error and end the transfer. And these errors are most likely going 734168d33a25Syt * to be fatal errors that will cause the port to be restarted. For 734268d33a25Syt * data reads, the HBA updates its PRD byte count, and may be 734368d33a25Syt * able to continue normally, but is not required to. And The HBA is 734468d33a25Syt * not required to detect underflow conditions for native command 734568d33a25Syt * queuing command. 734668d33a25Syt * 734768d33a25Syt * The HBA does not actively check incoming DMA Setup FISes to ensure 734868d33a25Syt * that the PxSACT register bit for that slot is set. Existing error 734968d33a25Syt * mechanisms, such as host bus failure, or bad protocol, are used to 735068d33a25Syt * recover from this case. 735168d33a25Syt * 735268d33a25Syt * In accordance with Serial ATA 1.0a, DATA FISes prior to the final 735368d33a25Syt * DATA FIS must be an integral number of Dwords. If the HBA receives 735468d33a25Syt * a request which is not an integral number of Dwords, the HBA 735568d33a25Syt * set PxSERR.ERR.P to '1', set PxIS.IFS to '1' and stop running until 735668d33a25Syt * software restarts the port. And the HBA ensures that the size 735768d33a25Syt * of the DATA FIS received during a PIO command matches the size in 735868d33a25Syt * the Transfer Cound field of the preceding PIO Setup FIS, if not, the 735968d33a25Syt * HBA sets PxSERR.ERR.P to '1', set PxIS.IFS to '1', and then 736068d33a25Syt * stop running until software restarts the port. 73612fcbc377Syt */ 73622fcbc377Syt /* 736368d33a25Syt * the fatal errors include PxIS.IFS, PxIS.HBDS, PxIS.HBFS and PxIS.TFES. 736468d33a25Syt * 736568d33a25Syt * PxIS.IFS indicates that the hba encountered an error on the serial ata 736668d33a25Syt * interface which caused the transfer to stop. 73672fcbc377Syt * 736868d33a25Syt * PxIS.HBDS indicates that the hba encountered a data error 736968d33a25Syt * (uncorrectable ecc/parity) when reading from or writing to system memory. 737068d33a25Syt * 737168d33a25Syt * PxIS.HBFS indicates that the hba encountered a host bus error that it 737268d33a25Syt * cannot recover from, such as a bad software pointer. 737368d33a25Syt * 737468d33a25Syt * PxIS.TFES is set whenever the status register is updated by the device 737568d33a25Syt * and the error bit (bit 0) is set. 73762fcbc377Syt */ 73772fcbc377Syt static int 737882263d52Syt ahci_intr_fatal_error(ahci_ctl_t *ahci_ctlp, 737982263d52Syt ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 73802fcbc377Syt { 738168d33a25Syt uint32_t port_cmd_status; 73822fcbc377Syt uint32_t port_serror; 738368d33a25Syt uint32_t task_file_status; 738468d33a25Syt int failed_slot; 738582263d52Syt sata_pkt_t *spkt = NULL; 738668d33a25Syt uint8_t err_byte; 738768d33a25Syt ahci_event_arg_t *args; 7388a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 73892fcbc377Syt 73902fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 73912fcbc377Syt 739268d33a25Syt /* 739368d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 739468d33a25Syt * SATA_DTYPE_NONE. 739568d33a25Syt */ 739668d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 7397f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 739868d33a25Syt "ahci_intr_fatal_error: port %d no device attached, " 739968d33a25Syt "and just return without doing anything", port); 740068d33a25Syt goto out0; 740168d33a25Syt } 74022fcbc377Syt 7403f8a673adSying tian - Beijing China if (intr_status & AHCI_INTR_STATUS_TFES) { 7404f8a673adSying tian - Beijing China task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 7405f8a673adSying tian - Beijing China (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 7406f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 7407f8a673adSying tian - Beijing China "ahci_intr_fatal_error: port %d " 7408f8a673adSying tian - Beijing China "task_file_status = 0x%x", port, task_file_status); 7409f8a673adSying tian - Beijing China } 7410f8a673adSying tian - Beijing China 74118aa6aadbSXiao-Yu Zhang /* 74128aa6aadbSXiao-Yu Zhang * Here we just log the fatal error info in interrupt context. 74138aa6aadbSXiao-Yu Zhang * Misc recovery processing will be handled in task queue. 74148aa6aadbSXiao-Yu Zhang */ 741582263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 741682263d52Syt /* 741782263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 741882263d52Syt * was processing when the error occurred. 741982263d52Syt */ 742082263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 742182263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 742282263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 742382263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 74242fcbc377Syt 742582263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 7426f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 742782263d52Syt "ahci_intr_fatal_error: spkt 0x%p is being processed when " 742882263d52Syt "fatal error occurred for port %d", spkt, port); 74292fcbc377Syt 74308aa6aadbSXiao-Yu Zhang /* A Task File Data error. */ 743182263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 743282263d52Syt err_byte = (task_file_status & AHCI_TFD_ERR_MASK) 743382263d52Syt >> AHCI_TFD_ERR_SHIFT; 74342fcbc377Syt 743582263d52Syt /* 743682263d52Syt * Won't emit the error message if it is an IDENTIFY 743782263d52Syt * DEVICE command sent to an ATAPI device. 743882263d52Syt */ 743982263d52Syt if ((spkt != NULL) && 744082263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == 744182263d52Syt SATAC_ID_DEVICE) && 744282263d52Syt (err_byte == SATA_ERROR_ABORT)) 744382263d52Syt goto out1; 744482263d52Syt 744582263d52Syt /* 744682263d52Syt * Won't emit the error message if it is an ATAPI PACKET 744782263d52Syt * command 744882263d52Syt */ 744982263d52Syt if ((spkt != NULL) && 745082263d52Syt (spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_PACKET)) 745182263d52Syt goto out1; 745282263d52Syt } 745368d33a25Syt } 74542fcbc377Syt 74557095af19Sying tian - Beijing China /* print the fatal error type */ 745668d33a25Syt ahci_log_fatal_error_message(ahci_ctlp, port, intr_status); 74572fcbc377Syt port_serror = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 74582fcbc377Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port)); 74597095af19Sying tian - Beijing China 74607095af19Sying tian - Beijing China /* print PxSERR related error message */ 7461a9440e8dSyt ahci_log_serror_message(ahci_ctlp, port, port_serror, 0); 74627095af19Sying tian - Beijing China 74637095af19Sying tian - Beijing China /* print task file register value */ 74647095af19Sying tian - Beijing China if (intr_status & AHCI_INTR_STATUS_TFES) { 74657095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d task_file_status " 74667095af19Sying tian - Beijing China "= 0x%x", instance, port, task_file_status); 74677095af19Sying tian - Beijing China } 74687095af19Sying tian - Beijing China 7469a9440e8dSyt out1: 747068d33a25Syt /* Prepare the argument for the taskq */ 747168d33a25Syt args = ahci_portp->ahciport_event_args; 747268d33a25Syt args->ahciea_ctlp = (void *)ahci_ctlp; 747368d33a25Syt args->ahciea_portp = (void *)ahci_portp; 747468d33a25Syt args->ahciea_event = intr_status; 74758aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PORT((ahci_addr_t *)args->ahciea_addrp, port); 747668d33a25Syt 747768d33a25Syt /* Start the taskq to handle error recovery */ 7478f8a673adSying tian - Beijing China if ((ddi_taskq_dispatch(ahci_portp->ahciport_event_taskq, 747968d33a25Syt ahci_events_handler, 748068d33a25Syt (void *)args, DDI_NOSLEEP)) != DDI_SUCCESS) { 7481a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: ahci start taskq for event handler " 7482a9440e8dSyt "failed", instance); 748368d33a25Syt } 748468d33a25Syt out0: 74852fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 74862fcbc377Syt 74872fcbc377Syt return (AHCI_SUCCESS); 74882fcbc377Syt } 74892fcbc377Syt 74902fcbc377Syt /* 74912fcbc377Syt * Hot Plug Operation for platforms that support Cold Presence Detect. 74922fcbc377Syt * 74932fcbc377Syt * When set, a device status has changed as detected by the cold presence 74942fcbc377Syt * detect logic. This bit can either be set due to a non-connected port 74952fcbc377Syt * receiving a device, or a connected port having its device removed. 74962fcbc377Syt * This bit is only valid if the port supports cold presence detect as 74972fcbc377Syt * indicated by PxCMD.CPD set to '1'. 74982fcbc377Syt * 749968d33a25Syt * At the moment, this interrupt is not needed and disabled and we just 750068d33a25Syt * log the debug message. 75012fcbc377Syt */ 75022fcbc377Syt static int 75032fcbc377Syt ahci_intr_cold_port_detect(ahci_ctl_t *ahci_ctlp, 75042fcbc377Syt ahci_port_t *ahci_portp, uint8_t port) 75052fcbc377Syt { 75062fcbc377Syt uint32_t port_cmd_status; 75072fcbc377Syt sata_device_t sdevice; 75082fcbc377Syt 7509f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 75102fcbc377Syt "ahci_intr_cold_port_detect enter, port %d", port); 75112fcbc377Syt 75122fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 75132fcbc377Syt 75142fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 75152fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 75162fcbc377Syt if (!(port_cmd_status & AHCI_CMD_STATUS_CPD)) { 7517f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 75182fcbc377Syt "port %d does not support cold presence detect, so " 75192fcbc377Syt "we just ignore this interrupt", port); 75202fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 75212fcbc377Syt return (AHCI_SUCCESS); 75222fcbc377Syt } 75232fcbc377Syt 7524f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 75252fcbc377Syt "port %d device status has changed", port); 75262fcbc377Syt 75272fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 752809121340Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 75292fcbc377Syt sdevice.satadev_addr.qual = SATA_ADDR_CPORT; 75302fcbc377Syt sdevice.satadev_addr.pmport = 0; 75312fcbc377Syt sdevice.satadev_state = SATA_PSTATE_PWRON; 75322fcbc377Syt 75332fcbc377Syt if (port_cmd_status & AHCI_CMD_STATUS_CPS) { 7534f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 75352fcbc377Syt "port %d: a device is hot plugged", port); 75362fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 75372fcbc377Syt sata_hba_event_notify( 75382fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 75392fcbc377Syt &sdevice, 75402fcbc377Syt SATA_EVNT_DEVICE_ATTACHED); 75412fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 75422fcbc377Syt 75432fcbc377Syt } else { 7544f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 75452fcbc377Syt "port %d: a device is hot unplugged", port); 75462fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 75472fcbc377Syt sata_hba_event_notify( 75482fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 75492fcbc377Syt &sdevice, 75502fcbc377Syt SATA_EVNT_DEVICE_DETACHED); 75512fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 75522fcbc377Syt } 75532fcbc377Syt 75542fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 75552fcbc377Syt 75562fcbc377Syt return (AHCI_SUCCESS); 75572fcbc377Syt } 75582fcbc377Syt 75592fcbc377Syt /* 75602fcbc377Syt * Enable the interrupts for a particular port. 75612fcbc377Syt * 75622fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 75632fcbc377Syt * is called. 75642fcbc377Syt */ 75652fcbc377Syt static void 7566689d74b0Syt ahci_enable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 75672fcbc377Syt { 7568f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 75692fcbc377Syt "ahci_enable_port_intrs enter, port %d", port); 75702fcbc377Syt 75712fcbc377Syt /* 75722fcbc377Syt * Clear port interrupt status before enabling interrupt 75732fcbc377Syt */ 75742fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 75752fcbc377Syt (uint32_t *)AHCI_PORT_PxIS(ahci_ctlp, port), 75762fcbc377Syt AHCI_PORT_INTR_MASK); 75772fcbc377Syt 75782fcbc377Syt /* 75792fcbc377Syt * Clear the pending bit from IS.IPS 75802fcbc377Syt */ 75812fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 75822fcbc377Syt (uint32_t *)AHCI_GLOBAL_IS(ahci_ctlp), (1 << port)); 75832fcbc377Syt 75842fcbc377Syt /* 75852fcbc377Syt * Enable the following interrupts: 75862fcbc377Syt * Device to Host Register FIS Interrupt (DHRS) 75872fcbc377Syt * PIO Setup FIS Interrupt (PSS) 758882263d52Syt * Set Device Bits Interrupt (SDBS) 75892fcbc377Syt * Unknown FIS Interrupt (UFS) 75902fcbc377Syt * Port Connect Change Status (PCS) 75912fcbc377Syt * PhyRdy Change Status (PRCS) 75922fcbc377Syt * Overflow Status (OFS) 75932fcbc377Syt * Interface Non-fatal Error Status (INFS) 75942fcbc377Syt * Interface Fatal Error Status (IFS) 75952fcbc377Syt * Host Bus Data Error Status (HBDS) 75962fcbc377Syt * Host Bus Fatal Error Status (HBFS) 75972fcbc377Syt * Task File Error Status (TFES) 75982fcbc377Syt */ 75992fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 76002fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 76012fcbc377Syt (AHCI_INTR_STATUS_DHRS | 76022fcbc377Syt AHCI_INTR_STATUS_PSS | 760382263d52Syt AHCI_INTR_STATUS_SDBS | 76042fcbc377Syt AHCI_INTR_STATUS_UFS | 760568d33a25Syt AHCI_INTR_STATUS_DPS | 76062fcbc377Syt AHCI_INTR_STATUS_PCS | 76072fcbc377Syt AHCI_INTR_STATUS_PRCS | 76082fcbc377Syt AHCI_INTR_STATUS_OFS | 76092fcbc377Syt AHCI_INTR_STATUS_INFS | 76102fcbc377Syt AHCI_INTR_STATUS_IFS | 76112fcbc377Syt AHCI_INTR_STATUS_HBDS | 76122fcbc377Syt AHCI_INTR_STATUS_HBFS | 76132fcbc377Syt AHCI_INTR_STATUS_TFES)); 76142fcbc377Syt } 76152fcbc377Syt 76162fcbc377Syt /* 76172fcbc377Syt * Enable interrupts for all the ports. 76182fcbc377Syt * 76192fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 76202fcbc377Syt * is called. 76212fcbc377Syt */ 76222fcbc377Syt static void 76232fcbc377Syt ahci_enable_all_intrs(ahci_ctl_t *ahci_ctlp) 76242fcbc377Syt { 76252fcbc377Syt uint32_t ghc_control; 76262fcbc377Syt 7627f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_enable_all_intrs enter", NULL); 76282fcbc377Syt 76292fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 76302fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 76312fcbc377Syt 76322fcbc377Syt ghc_control |= AHCI_HBA_GHC_IE; 76332fcbc377Syt 76342fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 76352fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 76362fcbc377Syt } 76372fcbc377Syt 76382fcbc377Syt /* 76392fcbc377Syt * Disable interrupts for a particular port. 76402fcbc377Syt * 76412fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 76422fcbc377Syt * is called. 76432fcbc377Syt */ 76442fcbc377Syt static void 7645689d74b0Syt ahci_disable_port_intrs(ahci_ctl_t *ahci_ctlp, uint8_t port) 76462fcbc377Syt { 7647f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 76482fcbc377Syt "ahci_disable_port_intrs enter, port %d", port); 76492fcbc377Syt 76502fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 76512fcbc377Syt (uint32_t *)AHCI_PORT_PxIE(ahci_ctlp, port), 0); 76522fcbc377Syt } 76532fcbc377Syt 76542fcbc377Syt /* 76552fcbc377Syt * Disable interrupts for the whole HBA. 76562fcbc377Syt * 76572fcbc377Syt * The global bit is cleared, then all interrupt sources from all 76582fcbc377Syt * ports are disabled. 76592fcbc377Syt * 76602fcbc377Syt * WARNING!!! ahcictl_mutex should be acquired before the function 76612fcbc377Syt * is called. 76622fcbc377Syt */ 76632fcbc377Syt static void 76642fcbc377Syt ahci_disable_all_intrs(ahci_ctl_t *ahci_ctlp) 76652fcbc377Syt { 76662fcbc377Syt uint32_t ghc_control; 76672fcbc377Syt 7668f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_disable_all_intrs enter", 7669f5f2d263SFred Herard NULL); 76702fcbc377Syt 76712fcbc377Syt ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 76722fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp)); 76732fcbc377Syt 76742fcbc377Syt ghc_control &= ~ AHCI_HBA_GHC_IE; 76752fcbc377Syt 76762fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 76772fcbc377Syt (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control); 76782fcbc377Syt } 76792fcbc377Syt 76802fcbc377Syt /* 76812c742e1fSying tian - Beijing China * Handle FIXED or MSI interrupts. 76822fcbc377Syt */ 76832fcbc377Syt /* 76842c742e1fSying tian - Beijing China * According to AHCI spec, the HBA may support several interrupt modes: 76852c742e1fSying tian - Beijing China * * pin based interrupts (FIXED) 76862c742e1fSying tian - Beijing China * * single MSI message interrupts 76872c742e1fSying tian - Beijing China * * multiple MSI based message interrupts 76882c742e1fSying tian - Beijing China * 76892c742e1fSying tian - Beijing China * For pin based interrupts, the software interrupt handler need to check IS 76902c742e1fSying tian - Beijing China * register to find out which port has pending interrupts. And then check 76912c742e1fSying tian - Beijing China * PxIS register to find out which interrupt events happened on that port. 76922c742e1fSying tian - Beijing China * 76932c742e1fSying tian - Beijing China * For single MSI message interrupts, MSICAP.MC.MSIE is set with '1', and 76942c742e1fSying tian - Beijing China * MSICAP.MC.MME is set with '0'. This mode is similar to pin based interrupts 76952c742e1fSying tian - Beijing China * in that software interrupt handler need to check IS register to determine 76962c742e1fSying tian - Beijing China * which port triggered the interrupts since it uses a single message for all 76972c742e1fSying tian - Beijing China * port interrupts. 76982c742e1fSying tian - Beijing China * 76992c742e1fSying tian - Beijing China * HBA may optionally support multiple MSI message for better performance. In 77002c742e1fSying tian - Beijing China * this mode, each port may have its own interrupt message, and thus generation 77012c742e1fSying tian - Beijing China * of interrupts is no longer controlled through the IS register. MSICAP.MC.MMC 77022c742e1fSying tian - Beijing China * represents a power-of-2 wrapper on the number of implemented ports, and 77032c742e1fSying tian - Beijing China * the mapping of ports to interrupts is done in a 1-1 relationship, up to the 77042c742e1fSying tian - Beijing China * maximum number of assigned interrupts. When the number of MSI messages 77052c742e1fSying tian - Beijing China * allocated is less than the number requested, then hardware may have two 77062c742e1fSying tian - Beijing China * implementation behaviors: 77072c742e1fSying tian - Beijing China * * assign each ports its own interrupt and then force all additional 77082c742e1fSying tian - Beijing China * ports to share the last interrupt message, and this condition is 77092c742e1fSying tian - Beijing China * indicated by clearing GHC.MRSM to '0' 77102c742e1fSying tian - Beijing China * * revert to single MSI mode, indicated by setting GHC.MRSM to '1' 77112c742e1fSying tian - Beijing China * When multiple-message MSI is enabled, hardware will still set IS register 77122c742e1fSying tian - Beijing China * as single message case. And this IS register may be used by software when 77132c742e1fSying tian - Beijing China * fewer than the requested number of messages is granted in order to determine 77142c742e1fSying tian - Beijing China * which port had the interrupt. 77152c742e1fSying tian - Beijing China * 77162c742e1fSying tian - Beijing China * Note: The current ahci driver only supports the first two interrupt modes: 77172c742e1fSying tian - Beijing China * pin based interrupts and single MSI message interrupts, and the reason 77182c742e1fSying tian - Beijing China * is indicated in below code. 77192fcbc377Syt */ 77202fcbc377Syt static int 77212c742e1fSying tian - Beijing China ahci_add_intrs(ahci_ctl_t *ahci_ctlp, int intr_type) 77222fcbc377Syt { 77232fcbc377Syt dev_info_t *dip = ahci_ctlp->ahcictl_dip; 77242fcbc377Syt int count, avail, actual; 77252c742e1fSying tian - Beijing China int i, rc; 77262fcbc377Syt 7727f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INIT|AHCIDBG_INTR, ahci_ctlp, 77282c742e1fSying tian - Beijing China "ahci_add_intrs enter interrupt type 0x%x", intr_type); 77292fcbc377Syt 77302fcbc377Syt /* get number of interrupts. */ 77312c742e1fSying tian - Beijing China rc = ddi_intr_get_nintrs(dip, intr_type, &count); 77322fcbc377Syt if ((rc != DDI_SUCCESS) || (count == 0)) { 7733f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 77342fcbc377Syt "ddi_intr_get_nintrs() failed, " 77352fcbc377Syt "rc %d count %d\n", rc, count); 77362fcbc377Syt return (DDI_FAILURE); 77372fcbc377Syt } 77382fcbc377Syt 77392fcbc377Syt /* get number of available interrupts. */ 77402c742e1fSying tian - Beijing China rc = ddi_intr_get_navail(dip, intr_type, &avail); 77412fcbc377Syt if ((rc != DDI_SUCCESS) || (avail == 0)) { 7742f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 77432fcbc377Syt "ddi_intr_get_navail() failed, " 77442fcbc377Syt "rc %d avail %d\n", rc, avail); 77452fcbc377Syt return (DDI_FAILURE); 77462fcbc377Syt } 77472fcbc377Syt 7748689d74b0Syt #if AHCI_DEBUG 77492fcbc377Syt if (avail < count) { 7750f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 77512c742e1fSying tian - Beijing China "ddi_intr_get_nintrs returned %d, navail() returned %d", 77522fcbc377Syt count, avail); 77532fcbc377Syt } 7754689d74b0Syt #endif 77552fcbc377Syt 77562c742e1fSying tian - Beijing China /* 77572c742e1fSying tian - Beijing China * Note: So far Solaris restricts the maximum number of messages for 77582c742e1fSying tian - Beijing China * x86 to 2, that is avail is 2, so here we set the count with 1 to 77592c742e1fSying tian - Beijing China * force the driver to use single MSI message interrupt. In future if 77602c742e1fSying tian - Beijing China * Solaris remove the restriction, then we need to delete the below 77612c742e1fSying tian - Beijing China * code and try to use multiple interrupt routine to gain better 77622c742e1fSying tian - Beijing China * performance. 77632c742e1fSying tian - Beijing China */ 77642c742e1fSying tian - Beijing China if ((intr_type == DDI_INTR_TYPE_MSI) && (count > 1)) { 7765f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR, ahci_ctlp, 77662c742e1fSying tian - Beijing China "force to use one interrupt routine though the " 77672c742e1fSying tian - Beijing China "HBA supports %d interrupt", count); 77682c742e1fSying tian - Beijing China count = 1; 77692c742e1fSying tian - Beijing China } 77702c742e1fSying tian - Beijing China 77712fcbc377Syt /* Allocate an array of interrupt handles. */ 77722fcbc377Syt ahci_ctlp->ahcictl_intr_size = count * sizeof (ddi_intr_handle_t); 77732fcbc377Syt ahci_ctlp->ahcictl_intr_htable = 77742fcbc377Syt kmem_alloc(ahci_ctlp->ahcictl_intr_size, KM_SLEEP); 77752fcbc377Syt 77762fcbc377Syt /* call ddi_intr_alloc(). */ 77772fcbc377Syt rc = ddi_intr_alloc(dip, ahci_ctlp->ahcictl_intr_htable, 77782c742e1fSying tian - Beijing China intr_type, 0, count, &actual, DDI_INTR_ALLOC_NORMAL); 77792fcbc377Syt 77802fcbc377Syt if ((rc != DDI_SUCCESS) || (actual == 0)) { 7781f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 77822c742e1fSying tian - Beijing China "ddi_intr_alloc() failed, rc %d count %d actual %d " 77832c742e1fSying tian - Beijing China "avail %d\n", rc, count, actual, avail); 77842fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 77852fcbc377Syt ahci_ctlp->ahcictl_intr_size); 77862fcbc377Syt return (DDI_FAILURE); 77872fcbc377Syt } 77882fcbc377Syt 77892fcbc377Syt /* use interrupt count returned */ 7790689d74b0Syt #if AHCI_DEBUG 77912fcbc377Syt if (actual < count) { 7792f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 77932fcbc377Syt "Requested: %d, Received: %d", count, actual); 77942fcbc377Syt } 7795689d74b0Syt #endif 77962fcbc377Syt 77972fcbc377Syt ahci_ctlp->ahcictl_intr_cnt = actual; 77982fcbc377Syt 77992fcbc377Syt /* 78002c742e1fSying tian - Beijing China * Get priority for first, assume remaining are all the same. 78012fcbc377Syt */ 78022fcbc377Syt if (ddi_intr_get_pri(ahci_ctlp->ahcictl_intr_htable[0], 78032fcbc377Syt &ahci_ctlp->ahcictl_intr_pri) != DDI_SUCCESS) { 7804f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 7805f5f2d263SFred Herard "ddi_intr_get_pri() failed", NULL); 78062fcbc377Syt 78072fcbc377Syt /* Free already allocated intr. */ 78082c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 78092c742e1fSying tian - Beijing China (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[i]); 78102fcbc377Syt } 78112fcbc377Syt 78122fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 78132fcbc377Syt ahci_ctlp->ahcictl_intr_size); 78142fcbc377Syt return (DDI_FAILURE); 78152fcbc377Syt } 78162fcbc377Syt 78172fcbc377Syt /* Test for high level interrupt. */ 78182fcbc377Syt if (ahci_ctlp->ahcictl_intr_pri >= ddi_intr_get_hilevel_pri()) { 7819f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 7820f5f2d263SFred Herard "ahci_add_intrs: Hi level intr not supported", NULL); 78212fcbc377Syt 78222fcbc377Syt /* Free already allocated intr. */ 78232c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 78242c742e1fSying tian - Beijing China (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[i]); 78252fcbc377Syt } 78262fcbc377Syt 78272fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 78282fcbc377Syt sizeof (ddi_intr_handle_t)); 78292fcbc377Syt 78302fcbc377Syt return (DDI_FAILURE); 78312fcbc377Syt } 78322fcbc377Syt 78332fcbc377Syt /* Call ddi_intr_add_handler(). */ 78342c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 78352c742e1fSying tian - Beijing China if (ddi_intr_add_handler(ahci_ctlp->ahcictl_intr_htable[i], 78362fcbc377Syt ahci_intr, (caddr_t)ahci_ctlp, NULL) != DDI_SUCCESS) { 7837f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 7838f5f2d263SFred Herard "ddi_intr_add_handler() failed", NULL); 78392fcbc377Syt 78402fcbc377Syt /* Free already allocated intr. */ 78412c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 78422fcbc377Syt (void) ddi_intr_free( 78432c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 78442fcbc377Syt } 78452fcbc377Syt 78462fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, 78472fcbc377Syt ahci_ctlp->ahcictl_intr_size); 78482fcbc377Syt return (DDI_FAILURE); 78492fcbc377Syt } 78502fcbc377Syt } 78512fcbc377Syt 78522c742e1fSying tian - Beijing China if (ddi_intr_get_cap(ahci_ctlp->ahcictl_intr_htable[0], 78532c742e1fSying tian - Beijing China &ahci_ctlp->ahcictl_intr_cap) != DDI_SUCCESS) { 7854f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_INIT, ahci_ctlp, 7855f5f2d263SFred Herard "ddi_intr_get_cap() failed", NULL); 78562fcbc377Syt 78572c742e1fSying tian - Beijing China /* Free already allocated intr. */ 78582c742e1fSying tian - Beijing China for (i = 0; i < actual; i++) { 78592c742e1fSying tian - Beijing China (void) ddi_intr_free( 78602c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 78612c742e1fSying tian - Beijing China } 78622c742e1fSying tian - Beijing China 78632c742e1fSying tian - Beijing China kmem_free(ahci_ctlp->ahcictl_intr_htable, 78642c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_size); 78652c742e1fSying tian - Beijing China return (DDI_FAILURE); 78662c742e1fSying tian - Beijing China } 78672fcbc377Syt 78682fcbc377Syt if (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK) { 78692fcbc377Syt /* Call ddi_intr_block_enable() for MSI. */ 78702fcbc377Syt (void) ddi_intr_block_enable(ahci_ctlp->ahcictl_intr_htable, 78712fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 78722fcbc377Syt } else { 78732c742e1fSying tian - Beijing China /* Call ddi_intr_enable() for FIXED or MSI non block enable. */ 78742c742e1fSying tian - Beijing China for (i = 0; i < ahci_ctlp->ahcictl_intr_cnt; i++) { 78752fcbc377Syt (void) ddi_intr_enable( 78762c742e1fSying tian - Beijing China ahci_ctlp->ahcictl_intr_htable[i]); 78772fcbc377Syt } 78782fcbc377Syt } 78792fcbc377Syt 78802fcbc377Syt return (DDI_SUCCESS); 78812fcbc377Syt } 78822fcbc377Syt 78832fcbc377Syt /* 78842fcbc377Syt * Removes the registered interrupts irrespective of whether they 78852fcbc377Syt * were legacy or MSI. 78862fcbc377Syt * 78872fcbc377Syt * WARNING!!! The controller interrupts must be disabled before calling 78882fcbc377Syt * this routine. 78892fcbc377Syt */ 78902fcbc377Syt static void 78912fcbc377Syt ahci_rem_intrs(ahci_ctl_t *ahci_ctlp) 78922fcbc377Syt { 78932fcbc377Syt int x; 78942fcbc377Syt 7895f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, "ahci_rem_intrs entered", NULL); 78962fcbc377Syt 78972fcbc377Syt /* Disable all interrupts. */ 78982fcbc377Syt if ((ahci_ctlp->ahcictl_intr_type == DDI_INTR_TYPE_MSI) && 78992fcbc377Syt (ahci_ctlp->ahcictl_intr_cap & DDI_INTR_FLAG_BLOCK)) { 79002fcbc377Syt /* Call ddi_intr_block_disable(). */ 79012fcbc377Syt (void) ddi_intr_block_disable(ahci_ctlp->ahcictl_intr_htable, 79022fcbc377Syt ahci_ctlp->ahcictl_intr_cnt); 79032fcbc377Syt } else { 79042fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 79052fcbc377Syt (void) ddi_intr_disable( 79062fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 79072fcbc377Syt } 79082fcbc377Syt } 79092fcbc377Syt 79102fcbc377Syt /* Call ddi_intr_remove_handler(). */ 79112fcbc377Syt for (x = 0; x < ahci_ctlp->ahcictl_intr_cnt; x++) { 79122fcbc377Syt (void) ddi_intr_remove_handler( 79132fcbc377Syt ahci_ctlp->ahcictl_intr_htable[x]); 79142fcbc377Syt (void) ddi_intr_free(ahci_ctlp->ahcictl_intr_htable[x]); 79152fcbc377Syt } 79162fcbc377Syt 79172fcbc377Syt kmem_free(ahci_ctlp->ahcictl_intr_htable, ahci_ctlp->ahcictl_intr_size); 79182fcbc377Syt } 79192fcbc377Syt 79202fcbc377Syt /* 792168d33a25Syt * This routine tries to put port into P:NotRunning state by clearing 792268d33a25Syt * PxCMD.ST. HBA will clear PxCI to 0h, PxSACT to 0h, PxCMD.CCS to 0h 792368d33a25Syt * and PxCMD.CR to '0'. 79242fcbc377Syt * 79252fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 79262fcbc377Syt * is called. 79272fcbc377Syt */ 79282fcbc377Syt static int 792968d33a25Syt ahci_put_port_into_notrunning_state(ahci_ctl_t *ahci_ctlp, 793068d33a25Syt ahci_port_t *ahci_portp, uint8_t port) 79312fcbc377Syt { 79322fcbc377Syt uint32_t port_cmd_status; 79332fcbc377Syt int loop_count; 79342fcbc377Syt 7935f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 793668d33a25Syt "ahci_put_port_into_notrunning_state enter: port %d", port); 79372fcbc377Syt 79382fcbc377Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 79392fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 79402fcbc377Syt 79412fcbc377Syt port_cmd_status &= ~AHCI_CMD_STATUS_ST; 79422fcbc377Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 79432fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port), port_cmd_status); 79442fcbc377Syt 79452fcbc377Syt /* Wait until PxCMD.CR is cleared */ 79462fcbc377Syt loop_count = 0; 79472fcbc377Syt do { 79482fcbc377Syt port_cmd_status = 79492fcbc377Syt ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 79502fcbc377Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 79512fcbc377Syt 79522fcbc377Syt if (loop_count++ > AHCI_POLLRATE_PORT_IDLE) { 7953f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT, ahci_ctlp, 79542fcbc377Syt "clearing port %d CMD.CR timeout, " 79552fcbc377Syt "port_cmd_status = 0x%x", port, 79562fcbc377Syt port_cmd_status); 79572fcbc377Syt /* 79582fcbc377Syt * We are effectively timing out after 0.5 sec. 79592fcbc377Syt * This value is specified in AHCI spec. 79602fcbc377Syt */ 79612fcbc377Syt break; 79622fcbc377Syt } 79632fcbc377Syt 7964689d74b0Syt /* Wait for 10 millisec */ 79658aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 79662fcbc377Syt } while (port_cmd_status & AHCI_CMD_STATUS_CR); 79672fcbc377Syt 796868d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_STARTED; 79692fcbc377Syt 797068d33a25Syt if (port_cmd_status & AHCI_CMD_STATUS_CR) { 7971f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 797268d33a25Syt "ahci_put_port_into_notrunning_state: failed to clear " 797368d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 797468d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 79752fcbc377Syt return (AHCI_FAILURE); 79762fcbc377Syt } else { 7977f5f2d263SFred Herard AHCIDBG(AHCIDBG_INIT|AHCIDBG_POLL_LOOP, ahci_ctlp, 797868d33a25Syt "ahci_put_port_into_notrunning_state: succeeded to clear " 797968d33a25Syt "PxCMD.CR to '0' after loop count: %d, and " 798068d33a25Syt "port_cmd_status = 0x%x", loop_count, port_cmd_status); 79812fcbc377Syt return (AHCI_SUCCESS); 79822fcbc377Syt } 79832fcbc377Syt } 79842fcbc377Syt 79852fcbc377Syt /* 798668d33a25Syt * First clear PxCMD.ST, and then check PxTFD. If both PxTFD.STS.BSY 798768d33a25Syt * and PxTFD.STS.DRQ cleared to '0', it means the device is in a 798868d33a25Syt * stable state, then set PxCMD.ST to '1' to start the port directly. 798968d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue a 799068d33a25Syt * COMRESET to the device to put it in an idle state. 799168d33a25Syt * 799268d33a25Syt * The fifth argument returns whether the port reset is involved during 799368d33a25Syt * the process. 799468d33a25Syt * 79950a4c4cecSXiao-Yu Zhang * The routine will be called under following scenarios: 79968aa6aadbSXiao-Yu Zhang * + To reset the HBA 79970a4c4cecSXiao-Yu Zhang * + To abort the packet(s) 79980a4c4cecSXiao-Yu Zhang * + To reset the port 79990a4c4cecSXiao-Yu Zhang * + To activate the port 80000a4c4cecSXiao-Yu Zhang * + Fatal error recovery 80010a4c4cecSXiao-Yu Zhang * + To abort the timeout packet(s) 80022fcbc377Syt * 80032fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function 800468d33a25Syt * is called. And ahciport_mutex will be released before the reset 800568d33a25Syt * event is reported to sata module by calling sata_hba_event_notify, 800668d33a25Syt * and then be acquired again later. 800782263d52Syt * 800882263d52Syt * NOTES!!! During this procedure, PxSERR register will be cleared, and 800982263d52Syt * according to the spec, the clearance of three bits will also clear 801082263d52Syt * three interrupt status bits. 801182263d52Syt * 1. PxSERR.DIAG.F will clear PxIS.UFS 801282263d52Syt * 2. PxSERR.DIAG.X will clear PxIS.PCS 801382263d52Syt * 3. PxSERR.DIAG.N will clear PxIS.PRCS 801482263d52Syt * 801582263d52Syt * Among these three interrupt events, the driver needs to take care of 801682263d52Syt * PxIS.PRCS, which is the hot plug event. When the driver found out 801782263d52Syt * a device was unplugged, it will call the interrupt handler. 80182fcbc377Syt */ 80192fcbc377Syt static int 80202fcbc377Syt ahci_restart_port_wait_till_ready(ahci_ctl_t *ahci_ctlp, 802168d33a25Syt ahci_port_t *ahci_portp, uint8_t port, int flag, int *reset_flag) 80222fcbc377Syt { 802382263d52Syt uint32_t port_sstatus; 80242fcbc377Syt uint32_t task_file_status; 80252fcbc377Syt sata_device_t sdevice; 80262fcbc377Syt int rval; 80278aa6aadbSXiao-Yu Zhang ahci_addr_t addr_port; 80288aa6aadbSXiao-Yu Zhang ahci_pmult_info_t *pminfo = NULL; 802982263d52Syt int dev_exists_begin = 0; 803082263d52Syt int dev_exists_end = 0; 80318aa6aadbSXiao-Yu Zhang uint32_t previous_dev_type = ahci_portp->ahciport_device_type; 80328aa6aadbSXiao-Yu Zhang int npmport = 0; 80338aa6aadbSXiao-Yu Zhang uint8_t cport = ahci_ctlp->ahcictl_port_to_cport[port]; 80342fcbc377Syt 8035f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 80362fcbc377Syt "ahci_restart_port_wait_till_ready: port %d enter", port); 80372fcbc377Syt 80388aa6aadbSXiao-Yu Zhang AHCI_ADDR_SET_PORT(&addr_port, port); 80398aa6aadbSXiao-Yu Zhang 80400a4c4cecSXiao-Yu Zhang if (ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) 804182263d52Syt dev_exists_begin = 1; 80422fcbc377Syt 804382263d52Syt /* First clear PxCMD.ST */ 804468d33a25Syt rval = ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp, 804568d33a25Syt port); 804668d33a25Syt if (rval != AHCI_SUCCESS) 804768d33a25Syt /* 804868d33a25Syt * If PxCMD.CR does not clear within a reasonable time, it 804968d33a25Syt * may assume the interface is in a hung condition and may 805068d33a25Syt * continue with issuing the port reset. 805168d33a25Syt */ 805268d33a25Syt goto reset; 80532fcbc377Syt 805468d33a25Syt /* Then clear PxSERR */ 805568d33a25Syt ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 805668d33a25Syt (uint32_t *)AHCI_PORT_PxSERR(ahci_ctlp, port), 805768d33a25Syt AHCI_SERROR_CLEAR_ALL); 80582fcbc377Syt 805982263d52Syt /* Then get PxTFD */ 806068d33a25Syt task_file_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 806168d33a25Syt (uint32_t *)AHCI_PORT_PxTFD(ahci_ctlp, port)); 80622fcbc377Syt 806368d33a25Syt /* 806468d33a25Syt * Check whether the device is in a stable status, if yes, 80658aa6aadbSXiao-Yu Zhang * then start the port directly. However for ahci_tran_reset_dport, 806668d33a25Syt * we may have to perform a port reset. 806768d33a25Syt */ 806868d33a25Syt if (!(task_file_status & (AHCI_TFD_STS_BSY | AHCI_TFD_STS_DRQ)) && 806968d33a25Syt !(flag & AHCI_PORT_RESET)) 80702fcbc377Syt goto out; 80712fcbc377Syt 807268d33a25Syt reset: 807368d33a25Syt /* 807468d33a25Syt * If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to '1', then issue 807568d33a25Syt * a COMRESET to the device 807668d33a25Syt */ 80778aa6aadbSXiao-Yu Zhang ahci_disable_port_intrs(ahci_ctlp, port); 80788aa6aadbSXiao-Yu Zhang rval = ahci_port_reset(ahci_ctlp, ahci_portp, &addr_port); 80798aa6aadbSXiao-Yu Zhang ahci_enable_port_intrs(ahci_ctlp, port); 80808aa6aadbSXiao-Yu Zhang 80818aa6aadbSXiao-Yu Zhang #ifdef AHCI_DEBUG 80828aa6aadbSXiao-Yu Zhang if (rval != AHCI_SUCCESS) 80838aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 80848aa6aadbSXiao-Yu Zhang "ahci_restart_port_wait_till_ready: port %d failed", 80858aa6aadbSXiao-Yu Zhang port); 80868aa6aadbSXiao-Yu Zhang #endif 808768d33a25Syt 808868d33a25Syt if (reset_flag != NULL) 808968d33a25Syt *reset_flag = 1; 80902fcbc377Syt 80912fcbc377Syt /* Indicate to the framework that a reset has happened. */ 809282263d52Syt if ((ahci_portp->ahciport_device_type != SATA_DTYPE_NONE) && 80938aa6aadbSXiao-Yu Zhang (ahci_portp->ahciport_device_type != SATA_DTYPE_PMULT) && 809482263d52Syt !(flag & AHCI_RESET_NO_EVENTS_UP)) { 809582263d52Syt /* Set the reset in progress flag */ 809682263d52Syt ahci_portp->ahciport_reset_in_progress = 1; 80972fcbc377Syt 80982fcbc377Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 809909121340Syt sdevice.satadev_addr.cport = 810009121340Syt ahci_ctlp->ahcictl_port_to_cport[port]; 810168d33a25Syt sdevice.satadev_addr.pmport = 0; 810268d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 81032fcbc377Syt 81042fcbc377Syt sdevice.satadev_state = SATA_DSTATE_RESET | 81052fcbc377Syt SATA_DSTATE_PWR_ACTIVE; 81062fcbc377Syt if (ahci_ctlp->ahcictl_sata_hba_tran) { 81072fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 81082fcbc377Syt sata_hba_event_notify( 81092fcbc377Syt ahci_ctlp->ahcictl_sata_hba_tran->sata_tran_hba_dip, 81102fcbc377Syt &sdevice, 81112fcbc377Syt SATA_EVNT_DEVICE_RESET); 81122fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 81132fcbc377Syt } 81142fcbc377Syt 8115f5f2d263SFred Herard AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 81168aa6aadbSXiao-Yu Zhang "port %d sending event up: SATA_EVNT_DEVICE_RESET", port); 811782263d52Syt } else { 811882263d52Syt ahci_portp->ahciport_reset_in_progress = 0; 81192fcbc377Syt } 812082263d52Syt 81212fcbc377Syt out: 8122a9440e8dSyt (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 812382263d52Syt 812482263d52Syt /* SStatus tells the presence of device. */ 812582263d52Syt port_sstatus = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 812682263d52Syt (uint32_t *)AHCI_PORT_PxSSTS(ahci_ctlp, port)); 812782263d52Syt 812882263d52Syt if (SSTATUS_GET_DET(port_sstatus) == SSTATUS_DET_DEVPRE_PHYCOM) { 812982263d52Syt dev_exists_end = 1; 813082263d52Syt } 813182263d52Syt 81328aa6aadbSXiao-Yu Zhang if (dev_exists_begin == 0 && dev_exists_end == 0) /* 0 -> 0 */ 81338aa6aadbSXiao-Yu Zhang return (rval); 81348aa6aadbSXiao-Yu Zhang 813582263d52Syt /* Check whether a hot plug event happened */ 81368aa6aadbSXiao-Yu Zhang if (dev_exists_begin == 1 && dev_exists_end == 0) { /* 1 -> 0 */ 8137f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 813882263d52Syt "ahci_restart_port_wait_till_ready: port %d " 813982263d52Syt "device is removed", port); 814082263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_NODEV; 8141f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 814282263d52Syt "ahci_restart_port_wait_till_ready: port %d " 814382263d52Syt "AHCI_PORT_FLAG_NODEV flag is set", port); 814482263d52Syt mutex_exit(&ahci_portp->ahciport_mutex); 814582263d52Syt (void) ahci_intr_phyrdy_change(ahci_ctlp, ahci_portp, port); 814682263d52Syt mutex_enter(&ahci_portp->ahciport_mutex); 81478aa6aadbSXiao-Yu Zhang 81488aa6aadbSXiao-Yu Zhang return (rval); 81498aa6aadbSXiao-Yu Zhang } 81508aa6aadbSXiao-Yu Zhang 81518aa6aadbSXiao-Yu Zhang 81528aa6aadbSXiao-Yu Zhang /* 0/1 -> 1 : device may change */ 81538aa6aadbSXiao-Yu Zhang /* 81548aa6aadbSXiao-Yu Zhang * May be called by ahci_fatal_error_recovery_handler, so 81558aa6aadbSXiao-Yu Zhang * don't issue software if the previous device is ATAPI. 81568aa6aadbSXiao-Yu Zhang */ 81578aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type == SATA_DTYPE_ATAPI) 81588aa6aadbSXiao-Yu Zhang return (rval); 81598aa6aadbSXiao-Yu Zhang 81608aa6aadbSXiao-Yu Zhang /* 81618aa6aadbSXiao-Yu Zhang * The COMRESET will make port multiplier enter legacy mode. 81628aa6aadbSXiao-Yu Zhang * Issue a software reset to make it work again. 81638aa6aadbSXiao-Yu Zhang */ 81648aa6aadbSXiao-Yu Zhang ahci_find_dev_signature(ahci_ctlp, ahci_portp, &addr_port); 81658aa6aadbSXiao-Yu Zhang 81668aa6aadbSXiao-Yu Zhang /* 81678aa6aadbSXiao-Yu Zhang * Following codes are specific for the port multiplier 81688aa6aadbSXiao-Yu Zhang */ 81698aa6aadbSXiao-Yu Zhang if (previous_dev_type != SATA_DTYPE_PMULT && 81708aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_device_type != SATA_DTYPE_PMULT) { 81718aa6aadbSXiao-Yu Zhang /* in case previous_dev_type is corrupt */ 81728aa6aadbSXiao-Yu Zhang ahci_dealloc_pmult(ahci_ctlp, ahci_portp); 81738aa6aadbSXiao-Yu Zhang (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 81748aa6aadbSXiao-Yu Zhang return (rval); 81758aa6aadbSXiao-Yu Zhang } 81768aa6aadbSXiao-Yu Zhang 81778aa6aadbSXiao-Yu Zhang /* Device change: PMult -> Non-PMult */ 81788aa6aadbSXiao-Yu Zhang if (previous_dev_type == SATA_DTYPE_PMULT && 81798aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_device_type != SATA_DTYPE_PMULT) { 81808aa6aadbSXiao-Yu Zhang /* 81818aa6aadbSXiao-Yu Zhang * This might happen because 81828aa6aadbSXiao-Yu Zhang * 1. Software reset failed. Port multiplier is not correctly 81838aa6aadbSXiao-Yu Zhang * enumerated. 81848aa6aadbSXiao-Yu Zhang * 2. Another non-port-multiplier device is attached. Perhaps 81858aa6aadbSXiao-Yu Zhang * the port multiplier was replaced by another device by 81868aa6aadbSXiao-Yu Zhang * whatever reason, but AHCI driver missed hot-plug event. 81878aa6aadbSXiao-Yu Zhang * 81888aa6aadbSXiao-Yu Zhang * Now that the port has been initialized, we just need to 81898aa6aadbSXiao-Yu Zhang * update the port structure according new device, then report 81908aa6aadbSXiao-Yu Zhang * and wait SATA framework to probe new device. 81918aa6aadbSXiao-Yu Zhang */ 81928aa6aadbSXiao-Yu Zhang 81938aa6aadbSXiao-Yu Zhang /* Force to release pmult resource */ 81948aa6aadbSXiao-Yu Zhang ahci_dealloc_pmult(ahci_ctlp, ahci_portp); 81958aa6aadbSXiao-Yu Zhang (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 81968aa6aadbSXiao-Yu Zhang 81978aa6aadbSXiao-Yu Zhang bzero((void *)&sdevice, sizeof (sata_device_t)); 81988aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.cport = 81998aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_port_to_cport[port]; 82008aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.pmport = 0; 82018aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 82028aa6aadbSXiao-Yu Zhang 82038aa6aadbSXiao-Yu Zhang sdevice.satadev_state = SATA_DSTATE_RESET | 82048aa6aadbSXiao-Yu Zhang SATA_DSTATE_PWR_ACTIVE; 82058aa6aadbSXiao-Yu Zhang 82068aa6aadbSXiao-Yu Zhang mutex_exit(&ahci_portp->ahciport_mutex); 82078aa6aadbSXiao-Yu Zhang sata_hba_event_notify( 82088aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_dip, 82098aa6aadbSXiao-Yu Zhang &sdevice, 82108aa6aadbSXiao-Yu Zhang SATA_EVNT_DEVICE_RESET); 82118aa6aadbSXiao-Yu Zhang mutex_enter(&ahci_portp->ahciport_mutex); 82128aa6aadbSXiao-Yu Zhang 82138aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 82148aa6aadbSXiao-Yu Zhang "Port multiplier is [Gone] at port %d ", port); 82158aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_EVENT, ahci_ctlp, 82168aa6aadbSXiao-Yu Zhang "port %d sending event up: SATA_EVNT_DEVICE_RESET", port); 82178aa6aadbSXiao-Yu Zhang 82188aa6aadbSXiao-Yu Zhang return (AHCI_SUCCESS); 82198aa6aadbSXiao-Yu Zhang } 82208aa6aadbSXiao-Yu Zhang 82218aa6aadbSXiao-Yu Zhang /* Device change: Non-PMult -> PMult */ 82228aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type == SATA_DTYPE_PMULT) { 82238aa6aadbSXiao-Yu Zhang 82248aa6aadbSXiao-Yu Zhang /* NOTE: The PxCMD.PMA may be cleared by HBA reset. */ 82258aa6aadbSXiao-Yu Zhang ahci_alloc_pmult(ahci_ctlp, ahci_portp); 82268aa6aadbSXiao-Yu Zhang 82278aa6aadbSXiao-Yu Zhang (void) ahci_start_port(ahci_ctlp, ahci_portp, port); 82282fcbc377Syt } 82298aa6aadbSXiao-Yu Zhang pminfo = ahci_portp->ahciport_pmult_info; 82308aa6aadbSXiao-Yu Zhang ASSERT(pminfo != NULL); 82318aa6aadbSXiao-Yu Zhang 82328aa6aadbSXiao-Yu Zhang /* Device (may) change: PMult -> PMult */ 82338aa6aadbSXiao-Yu Zhang /* 82348aa6aadbSXiao-Yu Zhang * First initialize port multiplier. Set state to READY and wait for 82358aa6aadbSXiao-Yu Zhang * probe entry point to initialize it 82368aa6aadbSXiao-Yu Zhang */ 82378aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_state = SATA_STATE_READY; 82388aa6aadbSXiao-Yu Zhang 82398aa6aadbSXiao-Yu Zhang /* 82408aa6aadbSXiao-Yu Zhang * It's a little complicated while target is a port multiplier. we 82418aa6aadbSXiao-Yu Zhang * need to COMRESET all pmports behind that PMult otherwise those 82428aa6aadbSXiao-Yu Zhang * sub-links between the PMult and the sub-devices will be in an 82438aa6aadbSXiao-Yu Zhang * inactive state (indicated by PSCR0/PxSSTS) and the following access 82448aa6aadbSXiao-Yu Zhang * to those sub-devices will be rejected by Link-Fatal-Error. 82458aa6aadbSXiao-Yu Zhang */ 82468aa6aadbSXiao-Yu Zhang /* 82478aa6aadbSXiao-Yu Zhang * The PxSNTF will be set soon after the pmult is plugged. While the 82488aa6aadbSXiao-Yu Zhang * pmult itself is attaching, sata_hba_event_notfiy will fail. so we 82498aa6aadbSXiao-Yu Zhang * simply mark every sub-port as 'unknown', then ahci_probe_pmport 82508aa6aadbSXiao-Yu Zhang * will initialized it. 82518aa6aadbSXiao-Yu Zhang */ 82528aa6aadbSXiao-Yu Zhang for (npmport = 0; npmport < pminfo->ahcipmi_num_dev_ports; npmport++) 82538aa6aadbSXiao-Yu Zhang pminfo->ahcipmi_port_state[npmport] = SATA_STATE_UNKNOWN; 82548aa6aadbSXiao-Yu Zhang 82558aa6aadbSXiao-Yu Zhang /* Report reset event. */ 82568aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_reset_in_progress = 1; 82578aa6aadbSXiao-Yu Zhang 82588aa6aadbSXiao-Yu Zhang bzero((void *)&sdevice, sizeof (sata_device_t)); 82598aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.cport = cport; 82608aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.pmport = SATA_PMULT_HOSTPORT; 82618aa6aadbSXiao-Yu Zhang sdevice.satadev_addr.qual = SATA_ADDR_PMULT; 82628aa6aadbSXiao-Yu Zhang sdevice.satadev_state = SATA_DSTATE_RESET | SATA_DSTATE_PWR_ACTIVE; 82638aa6aadbSXiao-Yu Zhang sata_hba_event_notify(ahci_ctlp->ahcictl_dip, &sdevice, 82648aa6aadbSXiao-Yu Zhang SATA_EVNT_DEVICE_RESET); 82652fcbc377Syt 82662fcbc377Syt return (rval); 82672fcbc377Syt } 82682fcbc377Syt 82692fcbc377Syt /* 827068d33a25Syt * This routine may be called under four scenarios: 827168d33a25Syt * a) do the recovery from fatal error 82722fcbc377Syt * b) or we need to timeout some commands 82732fcbc377Syt * c) or we need to abort some commands 82742fcbc377Syt * d) or we need reset device/port/controller 82752fcbc377Syt * 82762fcbc377Syt * In all these scenarios, we need to send any pending unfinished 82772fcbc377Syt * commands up to sata framework. 82782fcbc377Syt * 827968d33a25Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 82802fcbc377Syt */ 82812fcbc377Syt static void 82822fcbc377Syt ahci_mop_commands(ahci_ctl_t *ahci_ctlp, 82832fcbc377Syt ahci_port_t *ahci_portp, 82842fcbc377Syt uint32_t slot_status, 82852fcbc377Syt uint32_t failed_tags, 82862fcbc377Syt uint32_t timeout_tags, 82872fcbc377Syt uint32_t aborted_tags, 82882fcbc377Syt uint32_t reset_tags) 82892fcbc377Syt { 829082263d52Syt uint32_t finished_tags = 0; 829182263d52Syt uint32_t unfinished_tags = 0; 82922fcbc377Syt int tmp_slot; 82932fcbc377Syt sata_pkt_t *satapkt; 829482263d52Syt int ncq_cmd_in_progress = 0; 829582263d52Syt int err_retri_cmd_in_progress = 0; 82968aa6aadbSXiao-Yu Zhang int rdwr_pmult_cmd_in_progress = 0; 82972fcbc377Syt 8298f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 82992fcbc377Syt "ahci_mop_commands entered: port: %d slot_status: 0x%x", 8300689d74b0Syt ahci_portp->ahciport_port_num, slot_status); 83012fcbc377Syt 8302f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 83032fcbc377Syt "ahci_mop_commands: failed_tags: 0x%x, " 83042fcbc377Syt "timeout_tags: 0x%x aborted_tags: 0x%x, " 83052fcbc377Syt "reset_tags: 0x%x", failed_tags, 83062fcbc377Syt timeout_tags, aborted_tags, reset_tags); 83072fcbc377Syt 83088aa6aadbSXiao-Yu Zhang #ifdef AHCI_DEBUG 83098aa6aadbSXiao-Yu Zhang if (ahci_debug_flags & AHCIDBG_ERRS) { 83108aa6aadbSXiao-Yu Zhang int i; 83118aa6aadbSXiao-Yu Zhang char msg_buf[200] = {0, }; 83128aa6aadbSXiao-Yu Zhang for (i = 0x1f; i >= 0; i--) { 83138aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_slot_pkts[i] != NULL) 83148aa6aadbSXiao-Yu Zhang msg_buf[i] = 'X'; 83158aa6aadbSXiao-Yu Zhang else 83168aa6aadbSXiao-Yu Zhang msg_buf[i] = '.'; 83178aa6aadbSXiao-Yu Zhang } 83188aa6aadbSXiao-Yu Zhang msg_buf[0x20] = '\0'; 83198aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "port[%d] slots: %s", 83208aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_num, msg_buf); 83218aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "[ERR-RT] %p [RW-PM] %p ", 83228aa6aadbSXiao-Yu Zhang (void *)ahci_portp->ahciport_err_retri_pkt, 83238aa6aadbSXiao-Yu Zhang (void *)ahci_portp->ahciport_rdwr_pmult_pkt); 83248aa6aadbSXiao-Yu Zhang } 83258aa6aadbSXiao-Yu Zhang #endif 83268aa6aadbSXiao-Yu Zhang 832782263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 832882263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 832982263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 83302fcbc377Syt 833182263d52Syt unfinished_tags = slot_status & 833282263d52Syt AHCI_SLOT_MASK(ahci_ctlp) & 833382263d52Syt ~failed_tags & 833482263d52Syt ~aborted_tags & 833582263d52Syt ~reset_tags & 833682263d52Syt ~timeout_tags; 8337a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 833882263d52Syt ncq_cmd_in_progress = 1; 833982263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 834082263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 834182263d52Syt 834282263d52Syt unfinished_tags = slot_status & 834382263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp) & 834482263d52Syt ~failed_tags & 834582263d52Syt ~aborted_tags & 834682263d52Syt ~reset_tags & 834782263d52Syt ~timeout_tags; 8348a9440e8dSyt } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 83492fcbc377Syt 8350a9440e8dSyt /* 8351a9440e8dSyt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT is 8352a9440e8dSyt * set, it means REQUEST SENSE or READ LOG EXT command doesn't 8353a9440e8dSyt * complete successfully due to one of the following three 8354a9440e8dSyt * conditions: 8355a9440e8dSyt * 8356a9440e8dSyt * 1. Fatal error - failed_tags includes its slot 8357a9440e8dSyt * 2. Timed out - timeout_tags includes its slot 8358a9440e8dSyt * 3. Aborted when hot unplug - aborted_tags includes its 8359a9440e8dSyt * slot 8360a9440e8dSyt * 8361a9440e8dSyt * Please note that the command is always sent down in Slot 0 8362a9440e8dSyt */ 836382263d52Syt err_retri_cmd_in_progress = 1; 8364f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_NCQ, ahci_ctlp, 836582263d52Syt "ahci_mop_commands is called for port %d while " 836682263d52Syt "REQUEST SENSE or READ LOG EXT for error retrieval " 8367f8a673adSying tian - Beijing China "is being executed slot_status = 0x%x", 8368f8a673adSying tian - Beijing China ahci_portp->ahciport_port_num, slot_status); 836968d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress > 1); 837082263d52Syt ASSERT(slot_status == 0x1); 83718aa6aadbSXiao-Yu Zhang } else if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 83728aa6aadbSXiao-Yu Zhang rdwr_pmult_cmd_in_progress = 1; 83738aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_PMULT, ahci_ctlp, 83748aa6aadbSXiao-Yu Zhang "ahci_mop_commands is called for port %d while " 83758aa6aadbSXiao-Yu Zhang "READ/WRITE PORTMULT command is being executed", 83768aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_num); 83778aa6aadbSXiao-Yu Zhang 83788aa6aadbSXiao-Yu Zhang ASSERT(slot_status == 0x1); 83792fcbc377Syt } 83802fcbc377Syt 83818aa6aadbSXiao-Yu Zhang #ifdef AHCI_DEBUG 83828aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_ENTRY, ahci_ctlp, 83838aa6aadbSXiao-Yu Zhang "ahci_mop_commands: finished_tags: 0x%x, " 83848aa6aadbSXiao-Yu Zhang "unfinished_tags 0x%x", finished_tags, unfinished_tags); 83858aa6aadbSXiao-Yu Zhang #endif 83868aa6aadbSXiao-Yu Zhang 838768d33a25Syt /* Send up finished packets with SATA_PKT_COMPLETED */ 83882fcbc377Syt while (finished_tags) { 83892fcbc377Syt tmp_slot = ddi_ffs(finished_tags) - 1; 83902fcbc377Syt if (tmp_slot == -1) { 83912fcbc377Syt break; 83922fcbc377Syt } 83932fcbc377Syt 83942fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 83952fcbc377Syt ASSERT(satapkt != NULL); 83962fcbc377Syt 8397f5f2d263SFred Herard AHCIDBG(AHCIDBG_INFO, ahci_ctlp, "ahci_mop_commands: " 83982fcbc377Syt "sending up pkt 0x%p with SATA_PKT_COMPLETED", 83992fcbc377Syt (void *)satapkt); 84002fcbc377Syt 840168d33a25Syt /* 840268d33a25Syt * Cannot fetch the return register content since the port 840368d33a25Syt * was restarted, so the corresponding tag will be set to 840468d33a25Syt * aborted tags. 840568d33a25Syt */ 840668d33a25Syt if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) { 840768d33a25Syt CLEAR_BIT(finished_tags, tmp_slot); 840868d33a25Syt aborted_tags |= tmp_slot; 840968d33a25Syt continue; 841068d33a25Syt } 841168d33a25Syt 841282263d52Syt if (ncq_cmd_in_progress) 841382263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 841482263d52Syt tmp_slot); 84152fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 84162fcbc377Syt CLEAR_BIT(finished_tags, tmp_slot); 84172fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 84182fcbc377Syt 84192fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_COMPLETED); 84202fcbc377Syt } 84212fcbc377Syt 842268d33a25Syt /* Send up failed packets with SATA_PKT_DEV_ERROR. */ 84232fcbc377Syt while (failed_tags) { 842482263d52Syt if (err_retri_cmd_in_progress) { 842582263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 842682263d52Syt ASSERT(satapkt != NULL); 842782263d52Syt ASSERT(failed_tags == 0x1); 842882263d52Syt 8429f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 843082263d52Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 843182263d52Syt (void *)satapkt); 843282263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 843382263d52Syt break; 843482263d52Syt } 84358aa6aadbSXiao-Yu Zhang if (rdwr_pmult_cmd_in_progress) { 84368aa6aadbSXiao-Yu Zhang satapkt = ahci_portp->ahciport_rdwr_pmult_pkt; 84378aa6aadbSXiao-Yu Zhang ASSERT(satapkt != NULL); 84388aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 84398aa6aadbSXiao-Yu Zhang "ahci_mop_commands: sending up " 84408aa6aadbSXiao-Yu Zhang "rdwr pmult pkt 0x%p with SATA_PKT_DEV_ERROR", 84418aa6aadbSXiao-Yu Zhang (void *)satapkt); 84428aa6aadbSXiao-Yu Zhang SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 84438aa6aadbSXiao-Yu Zhang break; 84448aa6aadbSXiao-Yu Zhang } 844582263d52Syt 84462fcbc377Syt tmp_slot = ddi_ffs(failed_tags) - 1; 84472fcbc377Syt if (tmp_slot == -1) { 84482fcbc377Syt break; 84492fcbc377Syt } 84502fcbc377Syt 84512fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 84522fcbc377Syt ASSERT(satapkt != NULL); 84532fcbc377Syt 8454f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 84552fcbc377Syt "sending up pkt 0x%p with SATA_PKT_DEV_ERROR", 84562fcbc377Syt (void *)satapkt); 84572fcbc377Syt 845882263d52Syt if (ncq_cmd_in_progress) 845982263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 846082263d52Syt tmp_slot); 84612fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 84622fcbc377Syt CLEAR_BIT(failed_tags, tmp_slot); 84632fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 84642fcbc377Syt 84652fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_DEV_ERROR); 84662fcbc377Syt } 84672fcbc377Syt 846868d33a25Syt /* Send up timeout packets with SATA_PKT_TIMEOUT. */ 84692fcbc377Syt while (timeout_tags) { 847082263d52Syt if (err_retri_cmd_in_progress) { 847182263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 847282263d52Syt ASSERT(satapkt != NULL); 847382263d52Syt ASSERT(timeout_tags == 0x1); 847482263d52Syt 8475f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 847682263d52Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 847782263d52Syt (void *)satapkt); 847882263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 847982263d52Syt break; 848082263d52Syt } 84818aa6aadbSXiao-Yu Zhang if (rdwr_pmult_cmd_in_progress) { 84828aa6aadbSXiao-Yu Zhang satapkt = ahci_portp->ahciport_rdwr_pmult_pkt; 84838aa6aadbSXiao-Yu Zhang ASSERT(satapkt != NULL); 84848aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 84858aa6aadbSXiao-Yu Zhang "ahci_mop_commands: sending up " 84868aa6aadbSXiao-Yu Zhang "rdwr pmult pkt 0x%p with SATA_PKT_TIMEOUT", 84878aa6aadbSXiao-Yu Zhang (void *)satapkt); 84888aa6aadbSXiao-Yu Zhang SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 84898aa6aadbSXiao-Yu Zhang break; 84908aa6aadbSXiao-Yu Zhang } 849182263d52Syt 84922fcbc377Syt tmp_slot = ddi_ffs(timeout_tags) - 1; 84932fcbc377Syt if (tmp_slot == -1) { 84942fcbc377Syt break; 84952fcbc377Syt } 84962fcbc377Syt 84972fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 84982fcbc377Syt ASSERT(satapkt != NULL); 84992fcbc377Syt 8500f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 85012fcbc377Syt "sending up pkt 0x%p with SATA_PKT_TIMEOUT", 85022fcbc377Syt (void *)satapkt); 85032fcbc377Syt 850482263d52Syt if (ncq_cmd_in_progress) 850582263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 850682263d52Syt tmp_slot); 85072fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 85082fcbc377Syt CLEAR_BIT(timeout_tags, tmp_slot); 85092fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 85102fcbc377Syt 85112fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_TIMEOUT); 85122fcbc377Syt } 85132fcbc377Syt 851468d33a25Syt /* Send up aborted packets with SATA_PKT_ABORTED */ 85152fcbc377Syt while (aborted_tags) { 851682263d52Syt if (err_retri_cmd_in_progress) { 851782263d52Syt satapkt = ahci_portp->ahciport_err_retri_pkt; 851882263d52Syt ASSERT(satapkt != NULL); 851982263d52Syt ASSERT(aborted_tags == 0x1); 852082263d52Syt 8521f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 852282263d52Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 852382263d52Syt (void *)satapkt); 852482263d52Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 852582263d52Syt break; 852682263d52Syt } 85278aa6aadbSXiao-Yu Zhang if (rdwr_pmult_cmd_in_progress) { 85288aa6aadbSXiao-Yu Zhang satapkt = ahci_portp->ahciport_rdwr_pmult_pkt; 85298aa6aadbSXiao-Yu Zhang ASSERT(satapkt != NULL); 85308aa6aadbSXiao-Yu Zhang ASSERT(aborted_tags == 0x1); 85318aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 85328aa6aadbSXiao-Yu Zhang "ahci_mop_commands: sending up " 85338aa6aadbSXiao-Yu Zhang "rdwr pmult pkt 0x%p with SATA_PKT_ABORTED", 85348aa6aadbSXiao-Yu Zhang (void *)satapkt); 85358aa6aadbSXiao-Yu Zhang SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 85368aa6aadbSXiao-Yu Zhang break; 85378aa6aadbSXiao-Yu Zhang } 853882263d52Syt 85392fcbc377Syt tmp_slot = ddi_ffs(aborted_tags) - 1; 85402fcbc377Syt if (tmp_slot == -1) { 85412fcbc377Syt break; 85422fcbc377Syt } 85432fcbc377Syt 85442fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 85452fcbc377Syt ASSERT(satapkt != NULL); 85462fcbc377Syt 8547f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 85482fcbc377Syt "sending up pkt 0x%p with SATA_PKT_ABORTED", 85492fcbc377Syt (void *)satapkt); 85502fcbc377Syt 855182263d52Syt if (ncq_cmd_in_progress) 855282263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 855382263d52Syt tmp_slot); 85542fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 85552fcbc377Syt CLEAR_BIT(aborted_tags, tmp_slot); 85562fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 85572fcbc377Syt 85582fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_ABORTED); 85592fcbc377Syt } 85602fcbc377Syt 856168d33a25Syt /* Send up reset packets with SATA_PKT_RESET. */ 85622fcbc377Syt while (reset_tags) { 85638aa6aadbSXiao-Yu Zhang if (rdwr_pmult_cmd_in_progress) { 85648aa6aadbSXiao-Yu Zhang satapkt = ahci_portp->ahciport_rdwr_pmult_pkt; 85658aa6aadbSXiao-Yu Zhang ASSERT(satapkt != NULL); 85668aa6aadbSXiao-Yu Zhang ASSERT(aborted_tags == 0x1); 85678aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 85688aa6aadbSXiao-Yu Zhang "ahci_mop_commands: sending up " 85698aa6aadbSXiao-Yu Zhang "rdwr pmult pkt 0x%p with SATA_PKT_RESET", 85708aa6aadbSXiao-Yu Zhang (void *)satapkt); 85718aa6aadbSXiao-Yu Zhang SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 85728aa6aadbSXiao-Yu Zhang break; 85738aa6aadbSXiao-Yu Zhang } 85748aa6aadbSXiao-Yu Zhang 85752fcbc377Syt tmp_slot = ddi_ffs(reset_tags) - 1; 85762fcbc377Syt if (tmp_slot == -1) { 85772fcbc377Syt break; 85782fcbc377Syt } 85792fcbc377Syt 85802fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 85812fcbc377Syt ASSERT(satapkt != NULL); 85822fcbc377Syt 8583f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 85842fcbc377Syt "sending up pkt 0x%p with SATA_PKT_RESET", 85852fcbc377Syt (void *)satapkt); 85862fcbc377Syt 858782263d52Syt if (ncq_cmd_in_progress) 858882263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 858982263d52Syt tmp_slot); 85902fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 85912fcbc377Syt CLEAR_BIT(reset_tags, tmp_slot); 85922fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 85932fcbc377Syt 85942fcbc377Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 85952fcbc377Syt } 85962fcbc377Syt 859768d33a25Syt /* Send up unfinished packets with SATA_PKT_RESET */ 85982fcbc377Syt while (unfinished_tags) { 85992fcbc377Syt tmp_slot = ddi_ffs(unfinished_tags) - 1; 86002fcbc377Syt if (tmp_slot == -1) { 86012fcbc377Syt break; 86022fcbc377Syt } 86032fcbc377Syt 86042fcbc377Syt satapkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 86052fcbc377Syt ASSERT(satapkt != NULL); 86062fcbc377Syt 8607f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, "ahci_mop_commands: " 860868d33a25Syt "sending up pkt 0x%p with SATA_PKT_RESET", 86092fcbc377Syt (void *)satapkt); 86102fcbc377Syt 861182263d52Syt if (ncq_cmd_in_progress) 861282263d52Syt CLEAR_BIT(ahci_portp->ahciport_pending_ncq_tags, 861382263d52Syt tmp_slot); 86142fcbc377Syt CLEAR_BIT(ahci_portp->ahciport_pending_tags, tmp_slot); 86152fcbc377Syt CLEAR_BIT(unfinished_tags, tmp_slot); 86162fcbc377Syt ahci_portp->ahciport_slot_pkts[tmp_slot] = NULL; 86172fcbc377Syt 861868d33a25Syt SENDUP_PACKET(ahci_portp, satapkt, SATA_PKT_RESET); 86192fcbc377Syt } 86202fcbc377Syt 862168d33a25Syt ahci_portp->ahciport_mop_in_progress--; 862268d33a25Syt ASSERT(ahci_portp->ahciport_mop_in_progress >= 0); 86232fcbc377Syt 862468d33a25Syt if (ahci_portp->ahciport_mop_in_progress == 0) 862568d33a25Syt ahci_portp->ahciport_flags &= ~AHCI_PORT_FLAG_MOPPING; 862668d33a25Syt } 86272fcbc377Syt 862882263d52Syt /* 862982263d52Syt * This routine is going to first request a READ LOG EXT sata pkt from sata 863082263d52Syt * module, and then deliver it to the HBA to get the ncq failure context. 863182263d52Syt * The return value is the exactly failed tags. 863282263d52Syt */ 863382263d52Syt static uint32_t 863482263d52Syt ahci_get_rdlogext_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 863582263d52Syt uint8_t port) 863682263d52Syt { 863782263d52Syt sata_device_t sdevice; 863882263d52Syt sata_pkt_t *rdlog_spkt, *spkt; 863982263d52Syt ddi_dma_handle_t buf_dma_handle; 86408aa6aadbSXiao-Yu Zhang ahci_addr_t addr; 864182263d52Syt int loop_count; 864282263d52Syt int rval; 864382263d52Syt int failed_slot; 864482263d52Syt uint32_t failed_tags = 0; 864582263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page; 864682263d52Syt 8647f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_NCQ, ahci_ctlp, 864882263d52Syt "ahci_get_rdlogext_data enter: port %d", port); 864982263d52Syt 865082263d52Syt /* Prepare the sdevice data */ 865182263d52Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 865282263d52Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 865382263d52Syt 865482263d52Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 865582263d52Syt sdevice.satadev_addr.pmport = 0; 865682263d52Syt 86578aa6aadbSXiao-Yu Zhang /* Translate sata_device.satadev_addr -> ahci_addr */ 86588aa6aadbSXiao-Yu Zhang ahci_get_ahci_addr(ahci_ctlp, &sdevice, &addr); 86598aa6aadbSXiao-Yu Zhang 866082263d52Syt /* 866182263d52Syt * Call the sata hba interface to get a rdlog spkt 866282263d52Syt */ 866382263d52Syt loop_count = 0; 866482263d52Syt loop: 866582263d52Syt rdlog_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 866682263d52Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_NCQ); 866782263d52Syt if (rdlog_spkt == NULL) { 866882263d52Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 866982263d52Syt /* Sleep for a while */ 86708aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 867182263d52Syt goto loop; 867282263d52Syt } 867382263d52Syt /* Timed out after 1s */ 8674f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 867582263d52Syt "failed to get rdlog spkt for port %d", port); 867682263d52Syt return (failed_tags); 867782263d52Syt } 867882263d52Syt 867982263d52Syt ASSERT(rdlog_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 868082263d52Syt 868182263d52Syt /* 868282263d52Syt * This flag is used to handle the specific error recovery when the 868382263d52Syt * READ LOG EXT command gets a failure (fatal error or time-out). 868482263d52Syt */ 868582263d52Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RDLOGEXT; 868682263d52Syt 868782263d52Syt /* 868882263d52Syt * This start is not supposed to fail because after port is restarted, 868982263d52Syt * the whole command list is empty. 869082263d52Syt */ 869182263d52Syt ahci_portp->ahciport_err_retri_pkt = rdlog_spkt; 86928aa6aadbSXiao-Yu Zhang (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, &addr, rdlog_spkt); 869382263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 869482263d52Syt 869582263d52Syt /* Remove the flag after READ LOG EXT command is completed */ 869682263d52Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RDLOGEXT; 869782263d52Syt 869882263d52Syt if (rdlog_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 869982263d52Syt /* Update the request log data */ 870082263d52Syt buf_dma_handle = *(ddi_dma_handle_t *) 870182263d52Syt (rdlog_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 870282263d52Syt rval = ddi_dma_sync(buf_dma_handle, 0, 0, 870382263d52Syt DDI_DMA_SYNC_FORKERNEL); 870482263d52Syt if (rval == DDI_SUCCESS) { 870582263d52Syt ncq_err_page = 870682263d52Syt (struct sata_ncq_error_recovery_page *)rdlog_spkt-> 870782263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr; 870882263d52Syt 870982263d52Syt /* Get the failed tag */ 871082263d52Syt failed_slot = ncq_err_page->ncq_tag; 8711f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 871282263d52Syt "ahci_get_rdlogext_data: port %d " 871382263d52Syt "failed slot %d", port, failed_slot); 871482263d52Syt if (failed_slot & NQ) { 8715f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 8716f5f2d263SFred Herard "the failed slot is not a valid tag", NULL); 871782263d52Syt goto out; 871882263d52Syt } 871982263d52Syt 872082263d52Syt failed_slot &= NCQ_TAG_MASK; 872182263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 8722f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 872382263d52Syt "ahci_get_rdlogext_data: failed spkt 0x%p", 872482263d52Syt (void *)spkt); 872582263d52Syt if (spkt == NULL) { 8726f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 8727f5f2d263SFred Herard "the failed slot spkt is NULL", NULL); 872882263d52Syt goto out; 872982263d52Syt } 873082263d52Syt 873182263d52Syt failed_tags = 0x1 << failed_slot; 873282263d52Syt 873382263d52Syt /* Fill out the error context */ 873482263d52Syt ahci_copy_ncq_err_page(&spkt->satapkt_cmd, 873582263d52Syt ncq_err_page); 873682263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 873782263d52Syt &spkt->satapkt_device); 873882263d52Syt } 873982263d52Syt } 874082263d52Syt out: 874182263d52Syt sata_free_error_retrieval_pkt(rdlog_spkt); 874282263d52Syt 874382263d52Syt return (failed_tags); 874482263d52Syt } 874582263d52Syt 874668d33a25Syt /* 874768d33a25Syt * This routine is going to first request a REQUEST SENSE sata pkt from sata 874868d33a25Syt * module, and then deliver it to the HBA to get the sense data and copy 874968d33a25Syt * the sense data back to the orignal failed sata pkt, and free the REQUEST 875068d33a25Syt * SENSE sata pkt later. 875168d33a25Syt */ 875268d33a25Syt static void 875368d33a25Syt ahci_get_rqsense_data(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 875468d33a25Syt uint8_t port, sata_pkt_t *spkt) 875568d33a25Syt { 875668d33a25Syt sata_device_t sdevice; 875768d33a25Syt sata_pkt_t *rs_spkt; 875868d33a25Syt sata_cmd_t *sata_cmd; 875968d33a25Syt ddi_dma_handle_t buf_dma_handle; 87608aa6aadbSXiao-Yu Zhang ahci_addr_t addr; 876168d33a25Syt int loop_count; 876268d33a25Syt #if AHCI_DEBUG 876368d33a25Syt struct scsi_extended_sense *rqsense; 876468d33a25Syt #endif 876568d33a25Syt 8766f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 876768d33a25Syt "ahci_get_rqsense_data enter: port %d", port); 876868d33a25Syt 876968d33a25Syt /* Prepare the sdevice data */ 877068d33a25Syt bzero((void *)&sdevice, sizeof (sata_device_t)); 877168d33a25Syt sdevice.satadev_addr.cport = ahci_ctlp->ahcictl_port_to_cport[port]; 877268d33a25Syt 877368d33a25Syt sdevice.satadev_addr.qual = SATA_ADDR_DCPORT; 877468d33a25Syt sdevice.satadev_addr.pmport = 0; 877568d33a25Syt 87768aa6aadbSXiao-Yu Zhang /* Translate sata_device.satadev_addr -> ahci_addr */ 87778aa6aadbSXiao-Yu Zhang ahci_get_ahci_addr(ahci_ctlp, &sdevice, &addr); 87788aa6aadbSXiao-Yu Zhang 877968d33a25Syt sata_cmd = &spkt->satapkt_cmd; 878068d33a25Syt 878168d33a25Syt /* 878268d33a25Syt * Call the sata hba interface to get a rs spkt 878368d33a25Syt */ 878468d33a25Syt loop_count = 0; 878568d33a25Syt loop: 878668d33a25Syt rs_spkt = sata_get_error_retrieval_pkt(ahci_ctlp->ahcictl_dip, 878768d33a25Syt &sdevice, SATA_ERR_RETR_PKT_TYPE_ATAPI); 878868d33a25Syt if (rs_spkt == NULL) { 878968d33a25Syt if (loop_count++ < AHCI_POLLRATE_GET_SPKT) { 879068d33a25Syt /* Sleep for a while */ 87918aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_10MS_USECS); 879268d33a25Syt goto loop; 879368d33a25Syt 879468d33a25Syt } 879568d33a25Syt /* Timed out after 1s */ 8796f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 879768d33a25Syt "failed to get rs spkt for port %d", port); 879868d33a25Syt return; 879968d33a25Syt } 880068d33a25Syt 880168d33a25Syt ASSERT(rs_spkt->satapkt_op_mode & SATA_OPMODE_SYNCH); 880268d33a25Syt 880368d33a25Syt /* 880468d33a25Syt * This flag is used to handle the specific error recovery when the 880582263d52Syt * REQUEST SENSE command gets a faiure (fatal error or time-out). 880668d33a25Syt */ 880768d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_RQSENSE; 880868d33a25Syt 880968d33a25Syt /* 881068d33a25Syt * This start is not supposed to fail because after port is restarted, 881182263d52Syt * the whole command list is empty. 881268d33a25Syt */ 881382263d52Syt ahci_portp->ahciport_err_retri_pkt = rs_spkt; 88148aa6aadbSXiao-Yu Zhang (void) ahci_do_sync_start(ahci_ctlp, ahci_portp, &addr, rs_spkt); 881582263d52Syt ahci_portp->ahciport_err_retri_pkt = NULL; 881668d33a25Syt 881768d33a25Syt /* Remove the flag after REQUEST SENSE command is completed */ 881868d33a25Syt ahci_portp->ahciport_flags &= ~ AHCI_PORT_FLAG_RQSENSE; 881968d33a25Syt 882068d33a25Syt if (rs_spkt->satapkt_reason == SATA_PKT_COMPLETED) { 882168d33a25Syt /* Update the request sense data */ 882268d33a25Syt buf_dma_handle = *(ddi_dma_handle_t *) 882368d33a25Syt (rs_spkt->satapkt_cmd.satacmd_err_ret_buf_handle); 882482263d52Syt (void) ddi_dma_sync(buf_dma_handle, 0, 0, 882568d33a25Syt DDI_DMA_SYNC_FORKERNEL); 882682263d52Syt /* Copy the request sense data */ 882782263d52Syt bcopy(rs_spkt-> 882882263d52Syt satapkt_cmd.satacmd_bp->b_un.b_addr, 882982263d52Syt &sata_cmd->satacmd_rqsense, 883082263d52Syt SATA_ATAPI_MIN_RQSENSE_LEN); 883168d33a25Syt #if AHCI_DEBUG 883282263d52Syt rqsense = (struct scsi_extended_sense *) 883382263d52Syt sata_cmd->satacmd_rqsense; 883482263d52Syt 883582263d52Syt /* Dump the sense data */ 8836f5f2d263SFred Herard AHCIDBG(AHCIDBG_SENSEDATA, ahci_ctlp, "\n", NULL); 8837f5f2d263SFred Herard AHCIDBG(AHCIDBG_SENSEDATA, ahci_ctlp, 883882263d52Syt "Sense data for satapkt %p ATAPI cmd 0x%x", 883982263d52Syt spkt, sata_cmd->satacmd_acdb[0]); 8840f5f2d263SFred Herard AHCIDBG(AHCIDBG_SENSEDATA, ahci_ctlp, 884182263d52Syt " es_code 0x%x es_class 0x%x " 884282263d52Syt "es_key 0x%x es_add_code 0x%x " 884382263d52Syt "es_qual_code 0x%x", 884482263d52Syt rqsense->es_code, rqsense->es_class, 884582263d52Syt rqsense->es_key, rqsense->es_add_code, 884682263d52Syt rqsense->es_qual_code); 884768d33a25Syt #endif 884868d33a25Syt } 884968d33a25Syt 885068d33a25Syt sata_free_error_retrieval_pkt(rs_spkt); 88512fcbc377Syt } 88522fcbc377Syt 88532fcbc377Syt /* 88542fcbc377Syt * Fatal errors will cause the HBA to enter the ERR: Fatal state. To recover, 885568d33a25Syt * the port must be restarted. When the HBA detects thus error, it may try 885668d33a25Syt * to abort a transfer. And if the transfer was aborted, the device is 885768d33a25Syt * expected to send a D2H Register FIS with PxTFD.STS.ERR set to '1' and both 885868d33a25Syt * PxTFD.STS.BSY and PxTFD.STS.DRQ cleared to '0'. Then system software knows 885968d33a25Syt * that the device is in a stable status and transfers may be restarted without 886068d33a25Syt * issuing a COMRESET to the device. If PxTFD.STS.BSY or PxTFD.STS.DRQ is set, 886168d33a25Syt * then the software will send the COMRESET to do the port reset. 886268d33a25Syt * 886368d33a25Syt * Software should perform the appropriate error recovery actions based on 886468d33a25Syt * whether non-queued commands were being issued or natived command queuing 886568d33a25Syt * commands were being issued. 886668d33a25Syt * 886768d33a25Syt * And software will complete the command that had the error with error mark 886868d33a25Syt * to higher level software. 88692fcbc377Syt * 88702fcbc377Syt * Fatal errors include the following: 887138547057Sying tian - Beijing China * PxIS.IFS - Interface Fatal Error Status 887238547057Sying tian - Beijing China * PxIS.HBDS - Host Bus Data Error Status 887338547057Sying tian - Beijing China * PxIS.HBFS - Host Bus Fatal Error Status 88742fcbc377Syt * PxIS.TFES - Task File Error Status 88752fcbc377Syt * 88762fcbc377Syt * WARNING!!! ahciport_mutex should be acquired before the function is called. 88772fcbc377Syt */ 887868d33a25Syt static void 887968d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctl_t *ahci_ctlp, 88808aa6aadbSXiao-Yu Zhang ahci_port_t *ahci_portp, ahci_addr_t *addrp, uint32_t intr_status) 88812fcbc377Syt { 888282263d52Syt uint32_t port_cmd_status; 888382263d52Syt uint32_t slot_status = 0; 888468d33a25Syt uint32_t failed_tags = 0; 888568d33a25Syt int failed_slot; 88868aa6aadbSXiao-Yu Zhang int reset_flag = 0, flag = 0; 888768d33a25Syt ahci_fis_d2h_register_t *ahci_rcvd_fisp; 888882263d52Syt sata_cmd_t *sata_cmd = NULL; 888982263d52Syt sata_pkt_t *spkt = NULL; 889038547057Sying tian - Beijing China #if AHCI_DEBUG 889138547057Sying tian - Beijing China ahci_cmd_header_t *cmd_header; 889238547057Sying tian - Beijing China #endif 88938aa6aadbSXiao-Yu Zhang uint8_t port = addrp->aa_port; 88942fcbc377Syt 8895f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 889668d33a25Syt "ahci_fatal_error_recovery_handler enter: port %d", port); 88972fcbc377Syt 88988aa6aadbSXiao-Yu Zhang /* Port multiplier error */ 88998aa6aadbSXiao-Yu Zhang if (ahci_portp->ahciport_device_type == SATA_DTYPE_PMULT) { 89008aa6aadbSXiao-Yu Zhang /* FBS code is neither completed nor tested. */ 89018aa6aadbSXiao-Yu Zhang ahci_pmult_error_recovery_handler(ahci_ctlp, ahci_portp, 89028aa6aadbSXiao-Yu Zhang port, intr_status); 89038aa6aadbSXiao-Yu Zhang 89048aa6aadbSXiao-Yu Zhang /* Force a port reset */ 89058aa6aadbSXiao-Yu Zhang flag = AHCI_PORT_RESET; 89068aa6aadbSXiao-Yu Zhang } 89078aa6aadbSXiao-Yu Zhang 890882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 890982263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 89102fcbc377Syt 891182263d52Syt /* Read PxCI to see which commands are still outstanding */ 891282263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 891382263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 89142fcbc377Syt 891582263d52Syt /* 891682263d52Syt * Read PxCMD.CCS to determine the slot that the HBA 891782263d52Syt * was processing when the error occurred. 891882263d52Syt */ 891982263d52Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 892082263d52Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 892182263d52Syt failed_slot = (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 892282263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 89232fcbc377Syt 892482263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 892582263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 892682263d52Syt ASSERT(spkt != NULL); 892782263d52Syt } else { 892882263d52Syt spkt = ahci_portp->ahciport_slot_pkts[failed_slot]; 892982263d52Syt if (spkt == NULL) { 893082263d52Syt /* May happen when interface errors occur? */ 893182263d52Syt goto next; 893282263d52Syt } 893382263d52Syt } 89342fcbc377Syt 893538547057Sying tian - Beijing China #if AHCI_DEBUG 893638547057Sying tian - Beijing China /* 893738547057Sying tian - Beijing China * Debugging purpose... 893838547057Sying tian - Beijing China */ 893938547057Sying tian - Beijing China if (ahci_portp->ahciport_prd_bytecounts[failed_slot]) { 894038547057Sying tian - Beijing China cmd_header = 894138547057Sying tian - Beijing China &ahci_portp->ahciport_cmd_list[failed_slot]; 8942f5f2d263SFred Herard AHCIDBG(AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 894338547057Sying tian - Beijing China "ahci_fatal_error_recovery_handler: port %d, " 894438547057Sying tian - Beijing China "PRD Byte Count = 0x%x, " 894538547057Sying tian - Beijing China "ahciport_prd_bytecounts = 0x%x", port, 894638547057Sying tian - Beijing China cmd_header->ahcich_prd_byte_count, 894738547057Sying tian - Beijing China ahci_portp->ahciport_prd_bytecounts[failed_slot]); 894838547057Sying tian - Beijing China } 894938547057Sying tian - Beijing China #endif 895038547057Sying tian - Beijing China 895182263d52Syt sata_cmd = &spkt->satapkt_cmd; 89522fcbc377Syt 895382263d52Syt /* Fill out the status and error registers for PxIS.TFES */ 895482263d52Syt if (intr_status & AHCI_INTR_STATUS_TFES) { 895582263d52Syt ahci_rcvd_fisp = &(ahci_portp->ahciport_rcvd_fis-> 895682263d52Syt ahcirf_d2h_register_fis); 895782263d52Syt 895882263d52Syt /* Copy the error context back to the sata_cmd */ 895982263d52Syt ahci_copy_err_cnxt(sata_cmd, ahci_rcvd_fisp); 896082263d52Syt } 89612fcbc377Syt 896282263d52Syt /* The failed command must be one of the outstanding commands */ 896382263d52Syt failed_tags = 0x1 << failed_slot; 896482263d52Syt ASSERT(failed_tags & slot_status); 896582263d52Syt 896682263d52Syt /* Update the sata registers, especially PxSERR register */ 896782263d52Syt ahci_update_sata_registers(ahci_ctlp, port, 896882263d52Syt &spkt->satapkt_device); 89692fcbc377Syt 8970a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 897182263d52Syt /* Read PxSACT to see which commands are still outstanding */ 897282263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 897382263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 897482263d52Syt } 897568d33a25Syt next: 89762fcbc377Syt 897768d33a25Syt #if AHCI_DEBUG 897868d33a25Syt /* 897982263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 898082263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 898182263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 898282263d52Syt * recovery process. At this time, the only outstanding command is 898382263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 898468d33a25Syt */ 898582263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 8986f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 898768d33a25Syt "ahci_fatal_error_recovery_handler: port %d REQUEST SENSE " 898882263d52Syt "command or READ LOG EXT command for error data retrieval " 898982263d52Syt "failed", port); 899082263d52Syt ASSERT(slot_status == 0x1); 8991f8a673adSying tian - Beijing China ASSERT(failed_slot == 0); 899282263d52Syt ASSERT(spkt->satapkt_cmd.satacmd_acdb[0] == 899382263d52Syt SCMD_REQUEST_SENSE || 899482263d52Syt spkt->satapkt_cmd.satacmd_cmd_reg == 899582263d52Syt SATAC_READ_LOG_EXT); 89962fcbc377Syt } 899768d33a25Syt #endif 89982fcbc377Syt 899968d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 900068d33a25Syt ahci_portp->ahciport_mop_in_progress++; 90012fcbc377Syt 900268d33a25Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 90038aa6aadbSXiao-Yu Zhang port, flag, &reset_flag); 90042fcbc377Syt 900568d33a25Syt /* 900668d33a25Syt * Won't retrieve error information: 900768d33a25Syt * 1. Port reset was involved to recover 900882263d52Syt * 2. Device is gone 900968d33a25Syt * 3. IDENTIFY DEVICE command sent to ATAPI device 901082263d52Syt * 4. REQUEST SENSE or READ LOG EXT command during error recovery 901168d33a25Syt */ 901282263d52Syt if (reset_flag || 901382263d52Syt ahci_portp->ahciport_device_type == SATA_DTYPE_NONE || 901482263d52Syt spkt && spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_ID_DEVICE || 901582263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 901668d33a25Syt goto out; 90172fcbc377Syt 901882263d52Syt /* 901982263d52Syt * Deliver READ LOG EXT to gather information about the error when 902082263d52Syt * a COMRESET has not been performed as part of the error recovery 902182263d52Syt * during NCQ command processing. 902282263d52Syt */ 902382263d52Syt if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 902482263d52Syt failed_tags = ahci_get_rdlogext_data(ahci_ctlp, 902582263d52Syt ahci_portp, port); 902682263d52Syt goto out; 902782263d52Syt } 902882263d52Syt 902968d33a25Syt /* 903068d33a25Syt * Deliver REQUEST SENSE for ATAPI command to gather information about 903168d33a25Syt * the error when a COMRESET has not been performed as part of the 903268d33a25Syt * error recovery. 903368d33a25Syt */ 903438547057Sying tian - Beijing China if (spkt && ahci_portp->ahciport_device_type == SATA_DTYPE_ATAPI) 903568d33a25Syt ahci_get_rqsense_data(ahci_ctlp, ahci_portp, port, spkt); 903668d33a25Syt out: 9037f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 903838547057Sying tian - Beijing China "ahci_fatal_error_recovery_handler: port %d fatal error " 903982263d52Syt "occurred slot_status = 0x%x, pending_tags = 0x%x, " 904082263d52Syt "pending_ncq_tags = 0x%x failed_tags = 0x%x", 904182263d52Syt port, slot_status, ahci_portp->ahciport_pending_tags, 904282263d52Syt ahci_portp->ahciport_pending_ncq_tags, failed_tags); 904382263d52Syt 90442fcbc377Syt ahci_mop_commands(ahci_ctlp, 90452fcbc377Syt ahci_portp, 904682263d52Syt slot_status, 90472fcbc377Syt failed_tags, /* failed tags */ 90482fcbc377Syt 0, /* timeout tags */ 90492fcbc377Syt 0, /* aborted tags */ 90502fcbc377Syt 0); /* reset tags */ 905168d33a25Syt } 905268d33a25Syt 90538aa6aadbSXiao-Yu Zhang /* 90548aa6aadbSXiao-Yu Zhang * Used to recovery a PMULT pmport fatal error under FIS-based switching. 90558aa6aadbSXiao-Yu Zhang * 1. device specific.PxFBS.SDE=1 90568aa6aadbSXiao-Yu Zhang * 2. Non-Deivce specific. 90578aa6aadbSXiao-Yu Zhang * Nothing will be done when Command-based switching is employed. 90588aa6aadbSXiao-Yu Zhang * 90598aa6aadbSXiao-Yu Zhang * Currently code is neither completed nor tested. 90608aa6aadbSXiao-Yu Zhang * 90618aa6aadbSXiao-Yu Zhang * WARNING!!! ahciport_mutex should be acquired before the function 90628aa6aadbSXiao-Yu Zhang * is called. 90638aa6aadbSXiao-Yu Zhang */ 90648aa6aadbSXiao-Yu Zhang static void 90658aa6aadbSXiao-Yu Zhang ahci_pmult_error_recovery_handler(ahci_ctl_t *ahci_ctlp, 90668aa6aadbSXiao-Yu Zhang ahci_port_t *ahci_portp, uint8_t port, uint32_t intr_status) 90678aa6aadbSXiao-Yu Zhang { 90688aa6aadbSXiao-Yu Zhang #ifndef __lock_lint 90698aa6aadbSXiao-Yu Zhang _NOTE(ARGUNUSED(intr_status)) 90708aa6aadbSXiao-Yu Zhang #endif 90718aa6aadbSXiao-Yu Zhang uint32_t port_fbs_ctrl; 90728aa6aadbSXiao-Yu Zhang int loop_count = 0; 90738aa6aadbSXiao-Yu Zhang ahci_addr_t addr; 90748aa6aadbSXiao-Yu Zhang 90758aa6aadbSXiao-Yu Zhang /* Nothing will be done under Command-based switching. */ 90768aa6aadbSXiao-Yu Zhang if (!(ahci_ctlp->ahcictl_cap & AHCI_CAP_PMULT_FBSS)) 90778aa6aadbSXiao-Yu Zhang return; 90788aa6aadbSXiao-Yu Zhang 90798aa6aadbSXiao-Yu Zhang port_fbs_ctrl = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 90808aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxFBS(ahci_ctlp, port)); 90818aa6aadbSXiao-Yu Zhang 90828aa6aadbSXiao-Yu Zhang if (!(port_fbs_ctrl & AHCI_FBS_EN)) 90838aa6aadbSXiao-Yu Zhang /* FBS is not enabled. */ 90848aa6aadbSXiao-Yu Zhang return; 90858aa6aadbSXiao-Yu Zhang 90868aa6aadbSXiao-Yu Zhang /* Problem's getting complicated now. */ 90878aa6aadbSXiao-Yu Zhang /* 90888aa6aadbSXiao-Yu Zhang * If FIS-based switching is used, we need to check 90898aa6aadbSXiao-Yu Zhang * the PxFBS to see the error type. 90908aa6aadbSXiao-Yu Zhang */ 90918aa6aadbSXiao-Yu Zhang port_fbs_ctrl = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 90928aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxFBS(ahci_ctlp, port)); 90938aa6aadbSXiao-Yu Zhang 90948aa6aadbSXiao-Yu Zhang /* Refer to spec(v1.2) 9.3.6.1 */ 90958aa6aadbSXiao-Yu Zhang if (port_fbs_ctrl & AHCI_FBS_SDE) { 90968aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, 90978aa6aadbSXiao-Yu Zhang "A Device Sepcific Error: port %d", port); 90988aa6aadbSXiao-Yu Zhang /* 90998aa6aadbSXiao-Yu Zhang * Controller has paused commands for all other 91008aa6aadbSXiao-Yu Zhang * sub-devices until PxFBS.DEC is set. 91018aa6aadbSXiao-Yu Zhang */ 91028aa6aadbSXiao-Yu Zhang ahci_reject_all_abort_pkts(ahci_ctlp, 91038aa6aadbSXiao-Yu Zhang ahci_portp, 0); 91048aa6aadbSXiao-Yu Zhang 91058aa6aadbSXiao-Yu Zhang ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle, 91068aa6aadbSXiao-Yu Zhang (uint32_t *)AHCI_PORT_PxFBS(ahci_ctlp, port), 91078aa6aadbSXiao-Yu Zhang port_fbs_ctrl | AHCI_FBS_DEC); 91088aa6aadbSXiao-Yu Zhang 91098aa6aadbSXiao-Yu Zhang /* 91108aa6aadbSXiao-Yu Zhang * Wait controller clear PxFBS.DEC, 91118aa6aadbSXiao-Yu Zhang * then we can continue. 91128aa6aadbSXiao-Yu Zhang */ 91138aa6aadbSXiao-Yu Zhang loop_count = 0; 91148aa6aadbSXiao-Yu Zhang do { 91158aa6aadbSXiao-Yu Zhang port_fbs_ctrl = ddi_get32(ahci_ctlp-> 91168aa6aadbSXiao-Yu Zhang ahcictl_ahci_acc_handle, (uint32_t *) 91178aa6aadbSXiao-Yu Zhang AHCI_PORT_PxFBS(ahci_ctlp, port)); 91188aa6aadbSXiao-Yu Zhang 91198aa6aadbSXiao-Yu Zhang if (loop_count++ > 1000) 91208aa6aadbSXiao-Yu Zhang /* 91218aa6aadbSXiao-Yu Zhang * Esclate the error. Follow 91228aa6aadbSXiao-Yu Zhang * non-device specific error 91238aa6aadbSXiao-Yu Zhang * procedure. 91248aa6aadbSXiao-Yu Zhang */ 91258aa6aadbSXiao-Yu Zhang return; 91268aa6aadbSXiao-Yu Zhang 91278aa6aadbSXiao-Yu Zhang drv_usecwait(AHCI_100US_USECS); 91288aa6aadbSXiao-Yu Zhang } while (port_fbs_ctrl & AHCI_FBS_DEC); 91298aa6aadbSXiao-Yu Zhang 91308aa6aadbSXiao-Yu Zhang /* 91318aa6aadbSXiao-Yu Zhang * Issue a software reset to ensure drive is in 91328aa6aadbSXiao-Yu Zhang * a known state. 91338aa6aadbSXiao-Yu Zhang */ 91348aa6aadbSXiao-Yu Zhang (void) ahci_software_reset(ahci_ctlp, 91358aa6aadbSXiao-Yu Zhang ahci_portp, &addr); 91368aa6aadbSXiao-Yu Zhang 91378aa6aadbSXiao-Yu Zhang } else { 91388aa6aadbSXiao-Yu Zhang 91398aa6aadbSXiao-Yu Zhang /* Process Non-Device Specific Error. */ 91408aa6aadbSXiao-Yu Zhang /* This will be handled later on. */ 91418aa6aadbSXiao-Yu Zhang cmn_err(CE_NOTE, "!FBS is not supported now."); 91428aa6aadbSXiao-Yu Zhang } 91438aa6aadbSXiao-Yu Zhang } 914468d33a25Syt /* 914568d33a25Syt * Handle events - fatal error recovery 914668d33a25Syt */ 914768d33a25Syt static void 914868d33a25Syt ahci_events_handler(void *args) 914968d33a25Syt { 915068d33a25Syt ahci_event_arg_t *ahci_event_arg; 915168d33a25Syt ahci_ctl_t *ahci_ctlp; 915268d33a25Syt ahci_port_t *ahci_portp; 91538aa6aadbSXiao-Yu Zhang ahci_addr_t *addrp; 915468d33a25Syt uint32_t event; 915568d33a25Syt 915668d33a25Syt ahci_event_arg = (ahci_event_arg_t *)args; 915782263d52Syt 915868d33a25Syt ahci_ctlp = ahci_event_arg->ahciea_ctlp; 915968d33a25Syt ahci_portp = ahci_event_arg->ahciea_portp; 91608aa6aadbSXiao-Yu Zhang addrp = ahci_event_arg->ahciea_addrp; 916168d33a25Syt event = ahci_event_arg->ahciea_event; 916268d33a25Syt 9163f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR|AHCIDBG_ERRS, ahci_ctlp, 916482263d52Syt "ahci_events_handler enter: port %d intr_status = 0x%x", 91658aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_num, event); 916668d33a25Syt 91672fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 91682fcbc377Syt 916968d33a25Syt /* 917068d33a25Syt * ahci_intr_phyrdy_change() may have rendered it to 917168d33a25Syt * SATA_DTYPE_NONE. 917268d33a25Syt */ 917368d33a25Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 9174f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY|AHCIDBG_INTR, ahci_ctlp, 917568d33a25Syt "ahci_events_handler: port %d no device attached, " 91768aa6aadbSXiao-Yu Zhang "and just return without doing anything", 91778aa6aadbSXiao-Yu Zhang ahci_portp->ahciport_port_num); 917868d33a25Syt goto out; 917968d33a25Syt } 918068d33a25Syt 918168d33a25Syt if (event & (AHCI_INTR_STATUS_IFS | 918268d33a25Syt AHCI_INTR_STATUS_HBDS | 918368d33a25Syt AHCI_INTR_STATUS_HBFS | 918468d33a25Syt AHCI_INTR_STATUS_TFES)) 918568d33a25Syt ahci_fatal_error_recovery_handler(ahci_ctlp, ahci_portp, 91868aa6aadbSXiao-Yu Zhang addrp, event); 918768d33a25Syt 918868d33a25Syt out: 918968d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 91902fcbc377Syt } 91912fcbc377Syt 91922fcbc377Syt /* 919368d33a25Syt * ahci_watchdog_handler() and ahci_do_sync_start will call us if they 919468d33a25Syt * detect there are some commands which are timed out. 91952fcbc377Syt */ 91962fcbc377Syt static void 91972fcbc377Syt ahci_timeout_pkts(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, 919882263d52Syt uint8_t port, uint32_t tmp_timeout_tags) 91992fcbc377Syt { 920082263d52Syt uint32_t slot_status = 0; 920182263d52Syt uint32_t finished_tags = 0; 920282263d52Syt uint32_t timeout_tags = 0; 92032fcbc377Syt 9204f5f2d263SFred Herard AHCIDBG(AHCIDBG_TIMEOUT|AHCIDBG_ENTRY, ahci_ctlp, 920568d33a25Syt "ahci_timeout_pkts enter: port %d", port); 92062fcbc377Syt 92072fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 92082fcbc377Syt 920982263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) || 92108aa6aadbSXiao-Yu Zhang RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp) || 921182263d52Syt ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 921282263d52Syt /* Read PxCI to see which commands are still outstanding */ 921382263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 921482263d52Syt (uint32_t *)AHCI_PORT_PxCI(ahci_ctlp, port)); 9215a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 921682263d52Syt /* Read PxSACT to see which commands are still outstanding */ 921782263d52Syt slot_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 921882263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 921982263d52Syt } 922068d33a25Syt 922168d33a25Syt #if AHCI_DEBUG 92222fcbc377Syt /* 922382263d52Syt * When AHCI_PORT_FLAG_RQSENSE or AHCI_PORT_FLAG_RDLOGEXT flag is 922482263d52Syt * set, it means a fatal error happened after REQUEST SENSE command 922582263d52Syt * or READ LOG EXT command is delivered to the HBA during the error 922682263d52Syt * recovery process. At this time, the only outstanding command is 922782263d52Syt * supposed to be REQUEST SENSE command or READ LOG EXT command. 92282fcbc377Syt */ 922982263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) { 9230f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, ahci_ctlp, 923168d33a25Syt "ahci_timeout_pkts called while REQUEST SENSE " 923282263d52Syt "command or READ LOG EXT command for error recovery " 923382263d52Syt "timed out timeout_tags = 0x%x, slot_status = 0x%x, " 923482263d52Syt "pending_tags = 0x%x, pending_ncq_tags = 0x%x", 923582263d52Syt tmp_timeout_tags, slot_status, 923682263d52Syt ahci_portp->ahciport_pending_tags, 923782263d52Syt ahci_portp->ahciport_pending_ncq_tags); 923882263d52Syt ASSERT(slot_status == 0x1); 92398aa6aadbSXiao-Yu Zhang } else if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 92408aa6aadbSXiao-Yu Zhang AHCIDBG(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, ahci_ctlp, 92418aa6aadbSXiao-Yu Zhang "ahci_timeout_pkts called while executing R/W PMULT " 92428aa6aadbSXiao-Yu Zhang "command timeout_tags = 0x%x, slot_status = 0x%x", 92438aa6aadbSXiao-Yu Zhang tmp_timeout_tags, slot_status); 92448aa6aadbSXiao-Yu Zhang ASSERT(slot_status == 0x1); 92452fcbc377Syt } 924668d33a25Syt #endif 92472fcbc377Syt 924868d33a25Syt ahci_portp->ahciport_flags |= AHCI_PORT_FLAG_MOPPING; 924968d33a25Syt ahci_portp->ahciport_mop_in_progress++; 92502fcbc377Syt 92512fcbc377Syt (void) ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp, 92528aa6aadbSXiao-Yu Zhang port, AHCI_PORT_RESET, NULL); 925368d33a25Syt 92542fcbc377Syt /* 92552fcbc377Syt * Re-identify timeout tags because some previously checked commands 92562fcbc377Syt * could already complete. 92572fcbc377Syt */ 925882263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 925982263d52Syt finished_tags = ahci_portp->ahciport_pending_tags & 926082263d52Syt ~slot_status & AHCI_SLOT_MASK(ahci_ctlp); 926182263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 926282263d52Syt 9263f5f2d263SFred Herard AHCIDBG(AHCIDBG_TIMEOUT, ahci_ctlp, 926482263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 926582263d52Syt "timeout_tags = 0x%x, port_cmd_issue = 0x%x, " 926682263d52Syt "pending_tags = 0x%x ", 926782263d52Syt port, finished_tags, timeout_tags, 926882263d52Syt slot_status, ahci_portp->ahciport_pending_tags); 9269a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 927082263d52Syt finished_tags = ahci_portp->ahciport_pending_ncq_tags & 927182263d52Syt ~slot_status & AHCI_NCQ_SLOT_MASK(ahci_portp); 927282263d52Syt timeout_tags = tmp_timeout_tags & ~finished_tags; 927382263d52Syt 9274f5f2d263SFred Herard AHCIDBG(AHCIDBG_TIMEOUT|AHCIDBG_NCQ, ahci_ctlp, 927582263d52Syt "ahci_timeout_pkts: port %d, finished_tags = 0x%x, " 927682263d52Syt "timeout_tags = 0x%x, port_sactive = 0x%x, " 927782263d52Syt "pending_ncq_tags = 0x%x ", 927882263d52Syt port, finished_tags, timeout_tags, 927982263d52Syt slot_status, ahci_portp->ahciport_pending_ncq_tags); 92808aa6aadbSXiao-Yu Zhang } else if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) || 92818aa6aadbSXiao-Yu Zhang RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 928282263d52Syt timeout_tags = tmp_timeout_tags; 928382263d52Syt } 92842fcbc377Syt 92852fcbc377Syt ahci_mop_commands(ahci_ctlp, 92862fcbc377Syt ahci_portp, 928782263d52Syt slot_status, 92888aa6aadbSXiao-Yu Zhang 0, /* failed tags */ 92898aa6aadbSXiao-Yu Zhang timeout_tags, /* timeout tags */ 92908aa6aadbSXiao-Yu Zhang 0, /* aborted tags */ 92918aa6aadbSXiao-Yu Zhang 0); /* reset tags */ 929268d33a25Syt 929368d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 92942fcbc377Syt } 92952fcbc377Syt 92962fcbc377Syt /* 92972fcbc377Syt * Watchdog handler kicks in every 5 seconds to timeout any commands pending 92982fcbc377Syt * for long time. 92992fcbc377Syt */ 93002fcbc377Syt static void 93012fcbc377Syt ahci_watchdog_handler(ahci_ctl_t *ahci_ctlp) 93022fcbc377Syt { 93032fcbc377Syt ahci_port_t *ahci_portp; 930468d33a25Syt sata_pkt_t *spkt; 930582263d52Syt uint32_t pending_tags; 930682263d52Syt uint32_t timeout_tags; 930768d33a25Syt uint32_t port_cmd_status; 930882263d52Syt uint32_t port_sactive; 93092fcbc377Syt uint8_t port; 93102fcbc377Syt int tmp_slot; 931168d33a25Syt int current_slot; 931282263d52Syt uint32_t current_tags; 9313a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 93142fcbc377Syt /* max number of cycles this packet should survive */ 93152fcbc377Syt int max_life_cycles; 93162fcbc377Syt 93172fcbc377Syt /* how many cycles this packet survived so far */ 93182fcbc377Syt int watched_cycles; 93192fcbc377Syt 93202fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 93212fcbc377Syt 9322f5f2d263SFred Herard AHCIDBG(AHCIDBG_ENTRY, ahci_ctlp, 9323f5f2d263SFred Herard "ahci_watchdog_handler entered", NULL); 93242fcbc377Syt 93252fcbc377Syt for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 93262fcbc377Syt if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 93272fcbc377Syt continue; 93282fcbc377Syt } 93292fcbc377Syt 93302fcbc377Syt ahci_portp = ahci_ctlp->ahcictl_ports[port]; 93312fcbc377Syt 93322fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 93332fcbc377Syt if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) { 93342fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 93352fcbc377Syt continue; 93362fcbc377Syt } 93372fcbc377Syt 933868d33a25Syt /* Skip the check for those ports in error recovery */ 933982263d52Syt if ((ahci_portp->ahciport_flags & AHCI_PORT_FLAG_MOPPING) && 934082263d52Syt !(ERR_RETRI_CMD_IN_PROGRESS(ahci_portp))) { 934168d33a25Syt mutex_exit(&ahci_portp->ahciport_mutex); 934268d33a25Syt continue; 934368d33a25Syt } 934468d33a25Syt 934582263d52Syt pending_tags = 0; 934668d33a25Syt port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle, 934768d33a25Syt (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port)); 934868d33a25Syt 93498aa6aadbSXiao-Yu Zhang if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) || 93508aa6aadbSXiao-Yu Zhang RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) { 935182263d52Syt current_slot = 0; 935282263d52Syt pending_tags = 0x1; 9353a9440e8dSyt } else if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp)) { 935482263d52Syt current_slot = 935582263d52Syt (port_cmd_status & AHCI_CMD_STATUS_CCS) >> 935682263d52Syt AHCI_CMD_STATUS_CCS_SHIFT; 935782263d52Syt pending_tags = ahci_portp->ahciport_pending_tags; 9358a9440e8dSyt } else if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 935982263d52Syt port_sactive = ddi_get32( 936082263d52Syt ahci_ctlp->ahcictl_ahci_acc_handle, 936182263d52Syt (uint32_t *)AHCI_PORT_PxSACT(ahci_ctlp, port)); 936282263d52Syt current_tags = port_sactive & 936382263d52Syt ~port_cmd_status & 936482263d52Syt AHCI_NCQ_SLOT_MASK(ahci_portp); 936582263d52Syt pending_tags = ahci_portp->ahciport_pending_ncq_tags; 936682263d52Syt } 936782263d52Syt 93682fcbc377Syt timeout_tags = 0; 93692fcbc377Syt while (pending_tags) { 93702fcbc377Syt tmp_slot = ddi_ffs(pending_tags) - 1; 93712fcbc377Syt if (tmp_slot == -1) { 93722fcbc377Syt break; 93732fcbc377Syt } 93742fcbc377Syt 937582263d52Syt if (ERR_RETRI_CMD_IN_PROGRESS(ahci_portp)) 937682263d52Syt spkt = ahci_portp->ahciport_err_retri_pkt; 93778aa6aadbSXiao-Yu Zhang else if (RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp)) 93788aa6aadbSXiao-Yu Zhang spkt = ahci_portp->ahciport_rdwr_pmult_pkt; 937982263d52Syt else 938082263d52Syt spkt = ahci_portp->ahciport_slot_pkts[tmp_slot]; 938182263d52Syt 938268d33a25Syt if ((spkt != NULL) && spkt->satapkt_time && 938368d33a25Syt !(spkt->satapkt_op_mode & SATA_OPMODE_POLLING)) { 93842fcbc377Syt /* 93852fcbc377Syt * We are overloading satapkt_hba_driver_private 93862fcbc377Syt * with watched_cycle count. 93872fcbc377Syt * 93882fcbc377Syt * If a packet has survived for more than it's 93892fcbc377Syt * max life cycles, it is a candidate for time 93902fcbc377Syt * out. 93912fcbc377Syt */ 93922fcbc377Syt watched_cycles = (int)(intptr_t) 939368d33a25Syt spkt->satapkt_hba_driver_private; 93942fcbc377Syt watched_cycles++; 939568d33a25Syt max_life_cycles = (spkt->satapkt_time + 93962fcbc377Syt ahci_watchdog_timeout - 1) / 93972fcbc377Syt ahci_watchdog_timeout; 939868d33a25Syt 939968d33a25Syt spkt->satapkt_hba_driver_private = 940068d33a25Syt (void *)(intptr_t)watched_cycles; 940168d33a25Syt 940268d33a25Syt if (watched_cycles <= max_life_cycles) 940368d33a25Syt goto next; 940468d33a25Syt 9405259105bcSying tian - Beijing China #if AHCI_DEBUG 9406259105bcSying tian - Beijing China if (NCQ_CMD_IN_PROGRESS(ahci_portp)) { 9407f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, 9408259105bcSying tian - Beijing China ahci_ctlp, "watchdog: the current " 9409259105bcSying tian - Beijing China "tags is 0x%x", current_tags); 9410259105bcSying tian - Beijing China } else { 9411f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS|AHCIDBG_TIMEOUT, 9412259105bcSying tian - Beijing China ahci_ctlp, "watchdog: the current " 9413259105bcSying tian - Beijing China "slot is %d", current_slot); 9414259105bcSying tian - Beijing China } 9415259105bcSying tian - Beijing China #endif 9416259105bcSying tian - Beijing China 941768d33a25Syt /* 941868d33a25Syt * We need to check whether the HBA has 941968d33a25Syt * begun to execute the command, if not, 942068d33a25Syt * then re-set the timer of the command. 942168d33a25Syt */ 942282263d52Syt if (NON_NCQ_CMD_IN_PROGRESS(ahci_portp) && 942382263d52Syt (tmp_slot != current_slot) || 942482263d52Syt NCQ_CMD_IN_PROGRESS(ahci_portp) && 942582263d52Syt ((0x1 << tmp_slot) & current_tags)) { 942668d33a25Syt spkt->satapkt_hba_driver_private = 942768d33a25Syt (void *)(intptr_t)0; 942868d33a25Syt } else { 94292fcbc377Syt timeout_tags |= (0x1 << tmp_slot); 9430a9440e8dSyt cmn_err(CE_WARN, "!ahci%d: watchdog " 94312fcbc377Syt "port %d satapkt 0x%p timed out\n", 9432a9440e8dSyt instance, port, (void *)spkt); 94332fcbc377Syt } 94342fcbc377Syt } 943568d33a25Syt next: 94362fcbc377Syt CLEAR_BIT(pending_tags, tmp_slot); 94372fcbc377Syt } 94382fcbc377Syt 94392fcbc377Syt if (timeout_tags) { 94402fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 94412fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 94422fcbc377Syt ahci_timeout_pkts(ahci_ctlp, ahci_portp, 944382263d52Syt port, timeout_tags); 94442fcbc377Syt mutex_enter(&ahci_ctlp->ahcictl_mutex); 94452fcbc377Syt mutex_enter(&ahci_portp->ahciport_mutex); 94462fcbc377Syt } 94472fcbc377Syt 94482fcbc377Syt mutex_exit(&ahci_portp->ahciport_mutex); 94492fcbc377Syt } 94502fcbc377Syt 94512fcbc377Syt /* Re-install the watchdog timeout handler */ 94522fcbc377Syt if (ahci_ctlp->ahcictl_timeout_id != 0) { 94532fcbc377Syt ahci_ctlp->ahcictl_timeout_id = 94542fcbc377Syt timeout((void (*)(void *))ahci_watchdog_handler, 94552fcbc377Syt (caddr_t)ahci_ctlp, ahci_watchdog_tick); 94562fcbc377Syt } 94572fcbc377Syt 94582fcbc377Syt mutex_exit(&ahci_ctlp->ahcictl_mutex); 94592fcbc377Syt } 94602fcbc377Syt 94612fcbc377Syt /* 946268d33a25Syt * Fill the error context into sata_cmd for non-queued command error. 946368d33a25Syt */ 946468d33a25Syt static void 946568d33a25Syt ahci_copy_err_cnxt(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 946668d33a25Syt { 946768d33a25Syt scmd->satacmd_status_reg = GET_RFIS_STATUS(rfisp); 946868d33a25Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 946968d33a25Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 947068d33a25Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 947168d33a25Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 947268d33a25Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 947368d33a25Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 947468d33a25Syt 947568d33a25Syt if (scmd->satacmd_addr_type == ATA_ADDR_LBA48) { 947668d33a25Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 947768d33a25Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 947868d33a25Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 947968d33a25Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 948068d33a25Syt } 948168d33a25Syt } 948268d33a25Syt 948382263d52Syt /* 948482263d52Syt * Fill the ncq error page into sata_cmd for queued command error. 948582263d52Syt */ 948682263d52Syt static void 948782263d52Syt ahci_copy_ncq_err_page(sata_cmd_t *scmd, 948882263d52Syt struct sata_ncq_error_recovery_page *ncq_err_page) 948982263d52Syt { 949082263d52Syt scmd->satacmd_sec_count_msb = ncq_err_page->ncq_sector_count_ext; 949182263d52Syt scmd->satacmd_sec_count_lsb = ncq_err_page->ncq_sector_count; 949282263d52Syt scmd->satacmd_lba_low_msb = ncq_err_page->ncq_sector_number_ext; 949382263d52Syt scmd->satacmd_lba_low_lsb = ncq_err_page->ncq_sector_number; 949482263d52Syt scmd->satacmd_lba_mid_msb = ncq_err_page->ncq_cyl_low_ext; 949582263d52Syt scmd->satacmd_lba_mid_lsb = ncq_err_page->ncq_cyl_low; 949682263d52Syt scmd->satacmd_lba_high_msb = ncq_err_page->ncq_cyl_high_ext; 949782263d52Syt scmd->satacmd_lba_high_lsb = ncq_err_page->ncq_cyl_high; 949882263d52Syt scmd->satacmd_device_reg = ncq_err_page->ncq_dev_head; 949982263d52Syt scmd->satacmd_status_reg = ncq_err_page->ncq_status; 950082263d52Syt scmd->satacmd_error_reg = ncq_err_page->ncq_error; 950182263d52Syt } 950282263d52Syt 950368d33a25Syt /* 950468d33a25Syt * Put the respective register value to sata_cmd_t for satacmd_flags. 95052fcbc377Syt */ 95062fcbc377Syt static void 95072fcbc377Syt ahci_copy_out_regs(sata_cmd_t *scmd, ahci_fis_d2h_register_t *rfisp) 95082fcbc377Syt { 95092fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_msb) 95102fcbc377Syt scmd->satacmd_sec_count_msb = GET_RFIS_SECTOR_COUNT_EXP(rfisp); 95112fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_msb) 95122fcbc377Syt scmd->satacmd_lba_low_msb = GET_RFIS_CYL_LOW_EXP(rfisp); 95132fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_msb) 95142fcbc377Syt scmd->satacmd_lba_mid_msb = GET_RFIS_CYL_MID_EXP(rfisp); 95152fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_msb) 95162fcbc377Syt scmd->satacmd_lba_high_msb = GET_RFIS_CYL_HI_EXP(rfisp); 95172fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_sec_count_lsb) 95182fcbc377Syt scmd->satacmd_sec_count_lsb = GET_RFIS_SECTOR_COUNT(rfisp); 95192fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_low_lsb) 95202fcbc377Syt scmd->satacmd_lba_low_lsb = GET_RFIS_CYL_LOW(rfisp); 95212fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_mid_lsb) 95222fcbc377Syt scmd->satacmd_lba_mid_lsb = GET_RFIS_CYL_MID(rfisp); 95232fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_lba_high_lsb) 95242fcbc377Syt scmd->satacmd_lba_high_lsb = GET_RFIS_CYL_HI(rfisp); 95252fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_device_reg) 95262fcbc377Syt scmd->satacmd_device_reg = GET_RFIS_DEV_HEAD(rfisp); 95272fcbc377Syt if (scmd->satacmd_flags.sata_copy_out_error_reg) 95282fcbc377Syt scmd->satacmd_error_reg = GET_RFIS_ERROR(rfisp); 95292fcbc377Syt } 95302fcbc377Syt 953168d33a25Syt static void 953268d33a25Syt ahci_log_fatal_error_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 953368d33a25Syt uint32_t intr_status) 953468d33a25Syt { 9535a9440e8dSyt int instance = ddi_get_instance(ahci_ctlp->ahcictl_dip); 953668d33a25Syt 953768d33a25Syt if (intr_status & AHCI_INTR_STATUS_IFS) 95387095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has interface fatal " 9539a9440e8dSyt "error", instance, port); 954068d33a25Syt 954168d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBDS) 95427095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has bus data error", 9543a9440e8dSyt instance, port); 954468d33a25Syt 954568d33a25Syt if (intr_status & AHCI_INTR_STATUS_HBFS) 95467095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has bus fatal error", 9547a9440e8dSyt instance, port); 954868d33a25Syt 954968d33a25Syt if (intr_status & AHCI_INTR_STATUS_TFES) 95507095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d has task file error", 9551a9440e8dSyt instance, port); 955268d33a25Syt 95537095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: ahci port %d is trying to do error " 9554a9440e8dSyt "recovery", instance, port); 955568d33a25Syt } 955668d33a25Syt 95572fcbc377Syt /* 95582fcbc377Syt * Dump the serror message to the log. 95592fcbc377Syt */ 95602fcbc377Syt static void 956168d33a25Syt ahci_log_serror_message(ahci_ctl_t *ahci_ctlp, uint8_t port, 9562a9440e8dSyt uint32_t port_serror, int debug_only) 95632fcbc377Syt { 9564a9440e8dSyt static char err_buf[512]; 9565a9440e8dSyt static char err_msg_header[16]; 9566a9440e8dSyt char *err_msg = err_buf; 9567a9440e8dSyt 9568a9440e8dSyt *err_buf = '\0'; 9569a9440e8dSyt *err_msg_header = '\0'; 95702fcbc377Syt 957182263d52Syt if (port_serror & SERROR_DATA_ERR_FIXED) { 9572a9440e8dSyt err_msg = strcat(err_msg, 9573a9440e8dSyt "\tRecovered Data Integrity Error (I)\n"); 95742fcbc377Syt } 95752fcbc377Syt 957682263d52Syt if (port_serror & SERROR_COMM_ERR_FIXED) { 9577a9440e8dSyt err_msg = strcat(err_msg, 9578a9440e8dSyt "\tRecovered Communication Error (M)\n"); 95792fcbc377Syt } 95802fcbc377Syt 958182263d52Syt if (port_serror & SERROR_DATA_ERR) { 9582a9440e8dSyt err_msg = strcat(err_msg, 9583a9440e8dSyt "\tTransient Data Integrity Error (T)\n"); 95842fcbc377Syt } 95852fcbc377Syt 958682263d52Syt if (port_serror & SERROR_PERSISTENT_ERR) { 9587a9440e8dSyt err_msg = strcat(err_msg, 9588a9440e8dSyt "\tPersistent Communication or Data Integrity Error (C)\n"); 95892fcbc377Syt } 95902fcbc377Syt 959182263d52Syt if (port_serror & SERROR_PROTOCOL_ERR) { 9592a9440e8dSyt err_msg = strcat(err_msg, "\tProtocol Error (P)\n"); 95932fcbc377Syt } 95942fcbc377Syt 959582263d52Syt if (port_serror & SERROR_INT_ERR) { 9596a9440e8dSyt err_msg = strcat(err_msg, "\tInternal Error (E)\n"); 95972fcbc377Syt } 95982fcbc377Syt 959982263d52Syt if (port_serror & SERROR_PHY_RDY_CHG) { 9600a9440e8dSyt err_msg = strcat(err_msg, "\tPhyRdy Change (N)\n"); 96012fcbc377Syt } 96022fcbc377Syt 960382263d52Syt if (port_serror & SERROR_PHY_INT_ERR) { 9604a9440e8dSyt err_msg = strcat(err_msg, "\tPhy Internal Error (I)\n"); 96052fcbc377Syt } 96062fcbc377Syt 960782263d52Syt if (port_serror & SERROR_COMM_WAKE) { 9608a9440e8dSyt err_msg = strcat(err_msg, "\tComm Wake (W)\n"); 96092fcbc377Syt } 96102fcbc377Syt 961182263d52Syt if (port_serror & SERROR_10B_TO_8B_ERR) { 9612a9440e8dSyt err_msg = strcat(err_msg, "\t10B to 8B Decode Error (B)\n"); 96132fcbc377Syt } 96142fcbc377Syt 961582263d52Syt if (port_serror & SERROR_DISPARITY_ERR) { 9616a9440e8dSyt err_msg = strcat(err_msg, "\tDisparity Error (D)\n"); 96172fcbc377Syt } 96182fcbc377Syt 961982263d52Syt if (port_serror & SERROR_CRC_ERR) { 9620a9440e8dSyt err_msg = strcat(err_msg, "\tCRC Error (C)\n"); 96212fcbc377Syt } 96222fcbc377Syt 962382263d52Syt if (port_serror & SERROR_HANDSHAKE_ERR) { 9624a9440e8dSyt err_msg = strcat(err_msg, "\tHandshake Error (H)\n"); 96252fcbc377Syt } 96262fcbc377Syt 962782263d52Syt if (port_serror & SERROR_LINK_SEQ_ERR) { 9628a9440e8dSyt err_msg = strcat(err_msg, "\tLink Sequence Error (S)\n"); 96292fcbc377Syt } 96302fcbc377Syt 963182263d52Syt if (port_serror & SERROR_TRANS_ERR) { 9632a9440e8dSyt err_msg = strcat(err_msg, 9633a9440e8dSyt "\tTransport state transition error (T)\n"); 96342fcbc377Syt } 96352fcbc377Syt 963682263d52Syt if (port_serror & SERROR_FIS_TYPE) { 9637a9440e8dSyt err_msg = strcat(err_msg, "\tUnknown FIS Type (F)\n"); 96382fcbc377Syt } 96392fcbc377Syt 964082263d52Syt if (port_serror & SERROR_EXCHANGED_ERR) { 9641a9440e8dSyt err_msg = strcat(err_msg, "\tExchanged (X)\n"); 9642a9440e8dSyt } 9643a9440e8dSyt 9644a9440e8dSyt if (err_msg == NULL) 9645a9440e8dSyt return; 9646a9440e8dSyt 9647a9440e8dSyt if (debug_only) { 9648a9440e8dSyt (void) sprintf(err_msg_header, "port %d", port); 9649f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, err_msg_header, NULL); 9650f5f2d263SFred Herard AHCIDBG(AHCIDBG_ERRS, ahci_ctlp, err_msg, NULL); 9651a9440e8dSyt } else if (ahci_ctlp) { 96527095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci%d: %s %s", 9653a9440e8dSyt ddi_get_instance(ahci_ctlp->ahcictl_dip), 9654a9440e8dSyt err_msg_header, err_msg); 9655f5f2d263SFred Herard 9656f5f2d263SFred Herard /* sata trace debug */ 9657f5f2d263SFred Herard sata_trace_debug(ahci_ctlp->ahcictl_dip, 9658f5f2d263SFred Herard "ahci%d: %s %s", ddi_get_instance(ahci_ctlp->ahcictl_dip), 9659f5f2d263SFred Herard err_msg_header, err_msg); 9660a9440e8dSyt } else { 96617095af19Sying tian - Beijing China cmn_err(CE_WARN, "!ahci: %s %s", err_msg_header, err_msg); 9662f5f2d263SFred Herard 9663f5f2d263SFred Herard /* sata trace debug */ 9664f5f2d263SFred Herard sata_trace_debug(NULL, "ahci: %s %s", err_msg_header, err_msg); 96652fcbc377Syt } 96662fcbc377Syt } 96672fcbc377Syt 96688aa6aadbSXiao-Yu Zhang /* 96698aa6aadbSXiao-Yu Zhang * Translate the sata_address_t type into the ahci_addr_t type. 96708aa6aadbSXiao-Yu Zhang * sata_device.satadev_addr structure is used as source. 96718aa6aadbSXiao-Yu Zhang */ 96728aa6aadbSXiao-Yu Zhang static void 96738aa6aadbSXiao-Yu Zhang ahci_get_ahci_addr(ahci_ctl_t *ahci_ctlp, sata_device_t *sd, 96748aa6aadbSXiao-Yu Zhang ahci_addr_t *ahci_addrp) 96758aa6aadbSXiao-Yu Zhang { 96768aa6aadbSXiao-Yu Zhang sata_address_t *sata_addrp = &sd->satadev_addr; 96778aa6aadbSXiao-Yu Zhang ahci_addrp->aa_port = 96788aa6aadbSXiao-Yu Zhang ahci_ctlp->ahcictl_cport_to_port[sata_addrp->cport]; 96798aa6aadbSXiao-Yu Zhang ahci_addrp->aa_pmport = sata_addrp->pmport; 96808aa6aadbSXiao-Yu Zhang 96818aa6aadbSXiao-Yu Zhang switch (sata_addrp->qual) { 96828aa6aadbSXiao-Yu Zhang case SATA_ADDR_DCPORT: 96838aa6aadbSXiao-Yu Zhang case SATA_ADDR_CPORT: 96848aa6aadbSXiao-Yu Zhang ahci_addrp->aa_qual = AHCI_ADDR_PORT; 96858aa6aadbSXiao-Yu Zhang break; 96868aa6aadbSXiao-Yu Zhang case SATA_ADDR_PMULT: 9687918304a3SXiao-Yu Zhang case SATA_ADDR_PMULT_SPEC: 96888aa6aadbSXiao-Yu Zhang ahci_addrp->aa_qual = AHCI_ADDR_PMULT; 96898aa6aadbSXiao-Yu Zhang break; 96908aa6aadbSXiao-Yu Zhang case SATA_ADDR_DPMPORT: 96918aa6aadbSXiao-Yu Zhang case SATA_ADDR_PMPORT: 96928aa6aadbSXiao-Yu Zhang ahci_addrp->aa_qual = AHCI_ADDR_PMPORT; 96938aa6aadbSXiao-Yu Zhang break; 96948aa6aadbSXiao-Yu Zhang case SATA_ADDR_NULL: 96958aa6aadbSXiao-Yu Zhang default: 96968aa6aadbSXiao-Yu Zhang /* something went wrong */ 96978aa6aadbSXiao-Yu Zhang ahci_addrp->aa_qual = AHCI_ADDR_NULL; 96988aa6aadbSXiao-Yu Zhang break; 96998aa6aadbSXiao-Yu Zhang } 97008aa6aadbSXiao-Yu Zhang } 97018aa6aadbSXiao-Yu Zhang 97022fcbc377Syt /* 97032fcbc377Syt * This routine is to calculate the total number of ports implemented 97042fcbc377Syt * by the HBA. 97052fcbc377Syt */ 97062fcbc377Syt static int 97072fcbc377Syt ahci_get_num_implemented_ports(uint32_t ports_implemented) 97082fcbc377Syt { 97092fcbc377Syt uint8_t i; 97102fcbc377Syt int num = 0; 97112fcbc377Syt 97122fcbc377Syt for (i = 0; i < AHCI_MAX_PORTS; i++) { 97132fcbc377Syt if (((uint32_t)0x1 << i) & ports_implemented) 97142fcbc377Syt num++; 97152fcbc377Syt } 97162fcbc377Syt 97172fcbc377Syt return (num); 97182fcbc377Syt } 97192fcbc377Syt 9720689d74b0Syt #if AHCI_DEBUG 97212fcbc377Syt static void 97222fcbc377Syt ahci_log(ahci_ctl_t *ahci_ctlp, uint_t level, char *fmt, ...) 97232fcbc377Syt { 9724a9440e8dSyt static char name[16]; 97252fcbc377Syt va_list ap; 97262fcbc377Syt 97272fcbc377Syt mutex_enter(&ahci_log_mutex); 97282fcbc377Syt 97292fcbc377Syt va_start(ap, fmt); 97302fcbc377Syt if (ahci_ctlp) { 9731a9440e8dSyt (void) sprintf(name, "ahci%d: ", 97322fcbc377Syt ddi_get_instance(ahci_ctlp->ahcictl_dip)); 97332fcbc377Syt } else { 9734a9440e8dSyt (void) sprintf(name, "ahci: "); 97352fcbc377Syt } 97362fcbc377Syt 97372fcbc377Syt (void) vsprintf(ahci_log_buf, fmt, ap); 97382fcbc377Syt va_end(ap); 97392fcbc377Syt 9740a9440e8dSyt cmn_err(level, "%s%s", name, ahci_log_buf); 97412fcbc377Syt 97422fcbc377Syt mutex_exit(&ahci_log_mutex); 97432fcbc377Syt } 9744689d74b0Syt #endif 974519397407SSherry Moore 974619397407SSherry Moore /* 974719397407SSherry Moore * quiesce(9E) entry point. 974819397407SSherry Moore * 974919397407SSherry Moore * This function is called when the system is single-threaded at high 975019397407SSherry Moore * PIL with preemption disabled. Therefore, this function must not be 975119397407SSherry Moore * blocked. 975219397407SSherry Moore * 975319397407SSherry Moore * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 975419397407SSherry Moore * DDI_FAILURE indicates an error condition and should almost never happen. 975519397407SSherry Moore */ 975619397407SSherry Moore static int 975719397407SSherry Moore ahci_quiesce(dev_info_t *dip) 975819397407SSherry Moore { 975919397407SSherry Moore ahci_ctl_t *ahci_ctlp; 976019397407SSherry Moore ahci_port_t *ahci_portp; 976119397407SSherry Moore int instance, port; 976219397407SSherry Moore 976319397407SSherry Moore instance = ddi_get_instance(dip); 976419397407SSherry Moore ahci_ctlp = ddi_get_soft_state(ahci_statep, instance); 976519397407SSherry Moore 976619397407SSherry Moore if (ahci_ctlp == NULL) 976719397407SSherry Moore return (DDI_FAILURE); 976819397407SSherry Moore 976919397407SSherry Moore #if AHCI_DEBUG 977019397407SSherry Moore ahci_debug_flags = 0; 977119397407SSherry Moore #endif 977219397407SSherry Moore 977319397407SSherry Moore /* disable all the interrupts. */ 977419397407SSherry Moore ahci_disable_all_intrs(ahci_ctlp); 977519397407SSherry Moore 977619397407SSherry Moore for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) { 977719397407SSherry Moore if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) { 977819397407SSherry Moore continue; 977919397407SSherry Moore } 978019397407SSherry Moore 978119397407SSherry Moore ahci_portp = ahci_ctlp->ahcictl_ports[port]; 978219397407SSherry Moore 978319397407SSherry Moore /* 978419397407SSherry Moore * Stop the port by clearing PxCMD.ST 978519397407SSherry Moore * 978619397407SSherry Moore * Here we must disable the port interrupt because 978719397407SSherry Moore * ahci_disable_all_intrs only clear GHC.IE, and IS 978819397407SSherry Moore * register will be still set if PxIE is enabled. 978919397407SSherry Moore * When ahci shares one IRQ with other drivers, the 979019397407SSherry Moore * intr handler may claim the intr mistakenly. 979119397407SSherry Moore */ 979219397407SSherry Moore ahci_disable_port_intrs(ahci_ctlp, port); 979319397407SSherry Moore (void) ahci_put_port_into_notrunning_state(ahci_ctlp, 979419397407SSherry Moore ahci_portp, port); 979519397407SSherry Moore } 979619397407SSherry Moore 979719397407SSherry Moore return (DDI_SUCCESS); 979819397407SSherry Moore } 9799