/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ #ifndef _AHCIVAR_H #define _AHCIVAR_H #ifdef __cplusplus extern "C" { #endif /* * AHCI address qualifier flags (in qual field of ahci_addr struct). */ #define AHCI_ADDR_NULL 0x00 #define AHCI_ADDR_PORT 0x01 #define AHCI_ADDR_PMPORT 0x02 #define AHCI_ADDR_PMULT 0x04 #define AHCI_ADDR_VALID (AHCI_ADDR_PORT | \ AHCI_ADDR_PMULT | \ AHCI_ADDR_PMPORT) /* * AHCI address structure. */ struct ahci_addr { /* HBA port number */ uint8_t aa_port; /* Port multiplier port number */ uint8_t aa_pmport; /* * AHCI_ADDR_NULL * AHCI_ADDR_PORT * AHCI_ADDR_PMPORT * AHCI_ADDR_PMULT */ uint8_t aa_qual; }; typedef struct ahci_addr ahci_addr_t; _NOTE(SCHEME_PROTECTS_DATA("unshared data", ahci_addr)) #define AHCI_ADDR_IS_PORT(addrp) \ ((addrp)->aa_qual & AHCI_ADDR_PORT) #define AHCI_ADDR_IS_PMPORT(addrp) \ ((addrp)->aa_qual & AHCI_ADDR_PMPORT) #define AHCI_ADDR_IS_PMULT(addrp) \ ((addrp)->aa_qual & AHCI_ADDR_PMULT) #define AHCI_ADDR_IS_VALID(addrp) \ ((addrp)->aa_port < SATA_MAX_CPORTS) && \ ((addrp)->aa_pmport < SATA_MAX_PMPORTS) && \ ((addrp)->aa_qual & AHCI_ADDR_VALID) #define AHCI_ADDR_SET(addrp, port, pmport, qual) \ { \ (addrp)->aa_port = port; \ (addrp)->aa_pmport = pmport; \ (addrp)->aa_qual = qual; \ } #define AHCI_ADDR_SET_PORT(addrp, port) \ AHCI_ADDR_SET(addrp, port, 0, AHCI_ADDR_PORT) #define AHCI_ADDR_SET_PMPORT(addrp, port, pmport) \ AHCI_ADDR_SET(addrp, port, pmport, AHCI_ADDR_PMPORT) #define AHCI_ADDR_SET_PMULT(addrp, port) \ AHCI_ADDR_SET(addrp, port, SATA_PMULT_HOSTPORT, AHCI_ADDR_PMULT) /* Type for argument of event handler */ typedef struct ahci_event_arg { void *ahciea_ctlp; void *ahciea_portp; void *ahciea_addrp; uint32_t ahciea_event; } ahci_event_arg_t; /* Warlock annotation */ _NOTE(DATA_READABLE_WITHOUT_LOCK(ahci_event_arg_t::ahciea_ctlp)) _NOTE(DATA_READABLE_WITHOUT_LOCK(ahci_event_arg_t::ahciea_portp)) _NOTE(DATA_READABLE_WITHOUT_LOCK(ahci_event_arg_t::ahciea_addrp)) _NOTE(DATA_READABLE_WITHOUT_LOCK(ahci_event_arg_t::ahciea_event)) /* * ahci_pmult_info stores the information of a port multiplier and its * sub-devices in case a port multiplier is attached to an HBA port. */ struct ahci_pmult_info { /* Number of the device ports */ int ahcipmi_num_dev_ports; /* Device type of the sub-devices of the port multipler */ uint8_t ahcipmi_device_type[SATA_MAX_PMPORTS]; /* State of port multiplier port */ uint32_t ahcipmi_port_state[SATA_MAX_PMPORTS]; /* * Port multiplier port on which there is outstanding NCQ * commands. Only make sense in command based switching mode. */ uint8_t ahcipmi_ncq_pmport; /* Pending asynchronous notification events tags */ uint32_t ahcipmi_snotif_tags; }; typedef struct ahci_pmult_info ahci_pmult_info_t; /* * flags for ahciport_flags * * AHCI_PORT_FLAG_MOPPING: this flag will be set when the HBA is stopped, * and all the outstanding commands need to be aborted and sent to upper * layers. * * AHCI_PORT_FLAG_POLLING: this flag will be set when the interrupt is * disabled, and the command is executed in POLLING mode. * * AHCI_PORT_FLAG_RQSENSE: this flag will be set when a REQUEST SENSE which * is used to retrieve sense data is being executed. * * AHCI_PORT_FLAG_STARTED: this flag will be set when the port is started, * that is PxCMD.ST is set with '1', and be cleared when the port is put into * idle, that is PxCMD.ST is changed from '1' to '0'. * * AHCI_PORT_FLAG_RDLOGEXT: this flag will be set when a READ LOG EXT which * is used to retrieve NCQ failure context is being executed. * * AHCI_PORT_FLAG_NODEV: this flag will be set when a device is found gone * during ahci_restart_port_wait_till_ready process. * * AHCI_PORT_FLAG_RDWR_PMULT: this flag will be set when a READ/WRITE * PORTMULT command is being executed. * * AHCI_PORT_FLAG_IGNORE_IPMS: this flag will be set when enumerating a port * multiplier. According AHCI spec, IPMS error should be ignore during * enumeration of port multiplier. * * AHCI_PORT_FLAG_PMULT_SNTF: this flag will be set when the a asynchronous * notification event on the port multiplier is being handled. * * AHCI_PORT_FLAG_HOTPLUG: this flag will be set when a hot plug event is * being handled. * * AHCI_PORT_FLAG_ERRPRINT: this flag will be set when error recovery message * will be printed. Note that, for INDENTIFY DEVICE command sent to ATAPI * device or ATAPI PACKET command, this flag won't be set. */ #define AHCI_PORT_FLAG_MOPPING 0x02 #define AHCI_PORT_FLAG_POLLING 0x04 #define AHCI_PORT_FLAG_RQSENSE 0x08 #define AHCI_PORT_FLAG_STARTED 0x10 #define AHCI_PORT_FLAG_RDLOGEXT 0x20 #define AHCI_PORT_FLAG_NODEV 0x40 #define AHCI_PORT_FLAG_RDWR_PMULT 0x80 #define AHCI_PORT_FLAG_IGNORE_IPMS 0x100 #define AHCI_PORT_FLAG_PMULT_SNTF 0x200 #define AHCI_PORT_FLAG_HOTPLUG 0x400 #define AHCI_PORT_FLAG_ERRPRINT 0x800 typedef struct ahci_port { /* The physical port number */ uint8_t ahciport_port_num; /* Type of the device attached to the port */ uint8_t ahciport_device_type; /* State of the port */ uint32_t ahciport_port_state; /* Port multiplier struct */ ahci_pmult_info_t *ahciport_pmult_info; /* * AHCI_PORT_FLAG_MOPPING * AHCI_PORT_FLAG_POLLING * AHCI_PORT_FLAG_RQSENSE * AHCI_PORT_FLAG_STARTED * AHCI_PORT_FLAG_RDLOGEXT * AHCI_PORT_FLAG_NODEV * AHCI_PORT_FLAG_RDWR_PMULT * AHCI_PORT_FLAG_IGNORE_IPMS * AHCI_PORT_FLAG_PMULT_SNTF * AHCI_PORT_FLAG_HOTPLUG * AHCI_PORT_FLAG_ERRPRINT */ int ahciport_flags; /* Pointer to received FIS structure */ ahci_rcvd_fis_t *ahciport_rcvd_fis; ddi_dma_handle_t ahciport_rcvd_fis_dma_handle; ddi_acc_handle_t ahciport_rcvd_fis_acc_handle; ddi_dma_cookie_t ahciport_rcvd_fis_dma_cookie; /* Pointer to command list structure */ ahci_cmd_header_t *ahciport_cmd_list; ddi_dma_handle_t ahciport_cmd_list_dma_handle; ddi_acc_handle_t ahciport_cmd_list_acc_handle; ddi_dma_cookie_t ahciport_cmd_list_dma_cookie; /* Pointer to cmmand table structure */ ahci_cmd_table_t \ *ahciport_cmd_tables[AHCI_PORT_MAX_CMD_SLOTS]; ddi_dma_handle_t \ ahciport_cmd_tables_dma_handle[AHCI_PORT_MAX_CMD_SLOTS]; ddi_acc_handle_t \ ahciport_cmd_tables_acc_handle[AHCI_PORT_MAX_CMD_SLOTS]; /* Condition variable used for sync mode commands */ kcondvar_t ahciport_cv; /* The whole mutex for the port structure */ kmutex_t ahciport_mutex; /* The maximum number of tags for native queuing command transfers */ int ahciport_max_ncq_tags; /* Keep the tags of all pending non-ncq commands */ uint32_t ahciport_pending_tags; /* * Keep the tags of all pending ncq commands * (READ/WRITE FPDMA QUEUED) */ uint32_t ahciport_pending_ncq_tags; /* Keep all the pending sata packets */ sata_pkt_t *ahciport_slot_pkts[AHCI_PORT_MAX_CMD_SLOTS]; /* Used to check whether corresponding packet is timeout */ int ahciport_slot_timeout[AHCI_PORT_MAX_CMD_SLOTS]; /* Queue of completed (done) sata packet */ sata_pkt_t *ahciport_doneq; /* Pointer of the tail of completed sata packet queue */ sata_pkt_t **ahciport_doneqtail; /* the length of the completed sata packet queue */ uint32_t ahciport_doneq_len; /* Keep the byte count of all PRD entries for every sata packet */ uint32_t \ ahciport_prd_bytecounts[AHCI_PORT_MAX_CMD_SLOTS]; /* Keep the error retrieval sata packet */ sata_pkt_t *ahciport_err_retri_pkt; /* Keep the read/write port multiplier packet */ sata_pkt_t *ahciport_rdwr_pmult_pkt; /* * SATA HBA driver is supposed to remember and maintain device * reset state. While the reset is in progress, it doesn't accept * any more commands until receiving the command with * SATA_CLEAR_DEV_RESET_STATE flag and SATA_IGNORE_DEV_RESET_STATE. */ int ahciport_reset_in_progress; /* Taskq for handling event */ ddi_taskq_t *ahciport_event_taskq; /* This is for error recovery handler */ ahci_event_arg_t *ahciport_event_args; /* This is to calculate how many mops are in progress */ int ahciport_mop_in_progress; } ahci_port_t; /* Warlock annotation */ _NOTE(READ_ONLY_DATA(ahci_port_t::ahciport_rcvd_fis_dma_handle)) _NOTE(READ_ONLY_DATA(ahci_port_t::ahciport_cmd_list_dma_handle)) _NOTE(READ_ONLY_DATA(ahci_port_t::ahciport_cmd_tables_dma_handle)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_device_type)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_port_state)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_flags)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_pending_tags)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_slot_pkts)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_slot_timeout)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_doneq)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_doneqtail)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_doneq_len)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_reset_in_progress)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_mop_in_progress)) _NOTE(MUTEX_PROTECTS_DATA(ahci_port_t::ahciport_mutex, ahci_port_t::ahciport_event_taskq)) #define AHCI_NUM_PORTS(ctlp) \ (ctlp)->ahcictl_num_ports #define AHCIPORT_NUM_PMPORTS(portp) \ (portp)->ahciport_pmult_info->ahcipmi_num_dev_ports #define AHCIPORT_NCQ_PMPORT(ahci_portp) \ (ahci_portp->ahciport_pmult_info->ahcipmi_ncq_pmport) #define AHCIPORT_DEV_TYPE(portp, addrp) \ (portp)->ahciport_device_type #define AHCIPORT_PMDEV_TYPE(portp, addrp) \ (portp)->ahciport_pmult_info->ahcipmi_device_type \ [(addrp)->aa_pmport] #define AHCIPORT_GET_DEV_TYPE(portp, addrp) \ (AHCI_ADDR_IS_PORT(addrp) | AHCI_ADDR_IS_PMULT(addrp) ? \ AHCIPORT_DEV_TYPE(portp, addrp) : \ AHCIPORT_PMDEV_TYPE(portp, addrp)) #define AHCIPORT_SET_DEV_TYPE(portp, addrp, type) \ if (AHCI_ADDR_IS_PORT(addrp) | AHCI_ADDR_IS_PMULT(addrp)) \ AHCIPORT_DEV_TYPE(portp, addrp) = type; \ else \ AHCIPORT_PMDEV_TYPE(portp, addrp) = type; #define AHCIPORT_STATE(portp, addrp) \ (portp)->ahciport_port_state #define AHCIPORT_PMSTATE(portp, addrp) \ (portp)->ahciport_pmult_info->ahcipmi_port_state \ [(addrp)->aa_pmport] #define AHCIPORT_GET_STATE(portp, addrp) \ (AHCI_ADDR_IS_PORT(addrp) | AHCI_ADDR_IS_PMULT(addrp) ? \ AHCIPORT_STATE(portp, addrp) : AHCIPORT_PMSTATE(portp, addrp)) #define AHCIPORT_SET_STATE(portp, addrp, state) \ if (AHCI_ADDR_IS_PORT(addrp) | AHCI_ADDR_IS_PMULT(addrp)) \ AHCIPORT_STATE(portp, addrp) = state; \ else \ AHCIPORT_PMSTATE(portp, addrp) = state; typedef struct ahci_ctl { dev_info_t *ahcictl_dip; ushort_t ahcictl_venid; ushort_t ahcictl_devid; /* To map port number to cport number */ uint8_t ahcictl_port_to_cport[AHCI_MAX_PORTS]; /* To map cport number to port number */ uint8_t ahcictl_cport_to_port[AHCI_MAX_PORTS]; /* Number of controller ports */ int ahcictl_num_ports; /* Number of command slots */ int ahcictl_num_cmd_slots; /* Number of implemented ports */ int ahcictl_num_implemented_ports; /* Bit map to indicate which port is implemented */ uint32_t ahcictl_ports_implemented; ahci_port_t *ahcictl_ports[AHCI_MAX_PORTS]; int ahcictl_flags; int ahcictl_power_level; off_t ahcictl_pmcsr_offset; /* * AHCI_CAP_PIO_MDRQ * AHCI_CAP_NO_MCMDLIST_NONQUEUE * AHCI_CAP_NCQ * AHCI_CAP_PM * AHCI_CAP_BUF_32BIT_DMA * AHCI_CAP_SCLO * AHCI_CAP_COMMU_32BIT_DMA * AHCI_CAP_INIT_PORT_RESET * AHCI_CAP_SNTF * AHCI_CAP_PMULT_CBSS * AHCI_CAP_PMULT_FBSS * AHCI_CAP_SRST_NO_HOSTPORT */ int ahcictl_cap; /* Pci configuration space handle */ ddi_acc_handle_t ahcictl_pci_conf_handle; /* Mapping into bar 5 - AHCI base address */ ddi_acc_handle_t ahcictl_ahci_acc_handle; uintptr_t ahcictl_ahci_addr; /* Pointer used for sata hba framework registration */ struct sata_hba_tran *ahcictl_sata_hba_tran; /* DMA attributes for the data buffer */ ddi_dma_attr_t ahcictl_buffer_dma_attr; /* DMA attributes for the rcvd FIS */ ddi_dma_attr_t ahcictl_rcvd_fis_dma_attr; /* DMA attributes for the command list */ ddi_dma_attr_t ahcictl_cmd_list_dma_attr; /* DMA attributes for command tables */ ddi_dma_attr_t ahcictl_cmd_table_dma_attr; /* Used for watchdog handler */ timeout_id_t ahcictl_timeout_id; /* Per controller mutex */ kmutex_t ahcictl_mutex; /* Components for interrupt */ ddi_intr_handle_t *ahcictl_intr_htable; /* For array of intrs */ int ahcictl_intr_type; /* What type of interrupt */ int ahcictl_intr_cnt; /* # of intrs returned */ size_t ahcictl_intr_size; /* Size of intr array */ uint_t ahcictl_intr_pri; /* Intr priority */ int ahcictl_intr_cap; /* Intr capabilities */ /* FMA capabilities */ int ahcictl_fm_cap; } ahci_ctl_t; /* Warlock annotation */ _NOTE(READ_ONLY_DATA(ahci_ctl_t::ahcictl_ports)) _NOTE(READ_ONLY_DATA(ahci_ctl_t::ahcictl_cport_to_port)) _NOTE(READ_ONLY_DATA(ahci_ctl_t::ahcictl_port_to_cport)) _NOTE(MUTEX_PROTECTS_DATA(ahci_ctl_t::ahcictl_mutex, ahci_ctl_t::ahcictl_power_level)) _NOTE(MUTEX_PROTECTS_DATA(ahci_ctl_t::ahcictl_mutex, ahci_ctl_t::ahcictl_flags)) _NOTE(MUTEX_PROTECTS_DATA(ahci_ctl_t::ahcictl_mutex, ahci_ctl_t::ahcictl_timeout_id)) #define AHCI_SUCCESS (0) /* Successful return */ #define AHCI_TIMEOUT (1) /* Timed out */ #define AHCI_FAILURE (-1) /* Unsuccessful return */ /* Flags for ahcictl_flags */ #define AHCI_ATTACH 0x1 #define AHCI_DETACH 0x2 #define AHCI_SUSPEND 0x4 #define AHCI_QUIESCE 0x8 /* Values for ahcictl_cap */ /* PIO Multiple DRQ Block */ #define AHCI_CAP_PIO_MDRQ 0x1 /* * Multiple command slots in the command list cannot be used for * non-queued commands */ #define AHCI_CAP_NO_MCMDLIST_NONQUEUE 0x2 /* Native Command Queuing (NCQ) */ #define AHCI_CAP_NCQ 0x4 /* Power Management (PM) */ #define AHCI_CAP_PM 0x8 /* 32-bit DMA addressing for buffer block */ #define AHCI_CAP_BUF_32BIT_DMA 0x10 /* Supports Command List Override */ #define AHCI_CAP_SCLO 0x20 /* 32-bit DMA addressing for communication memory descriptors */ #define AHCI_CAP_COMMU_32BIT_DMA 0x40 /* Port reset is needed for initialization */ #define AHCI_CAP_INIT_PORT_RESET 0x80 /* Port Asychronous Notification */ #define AHCI_CAP_SNTF 0x100 /* Port Multiplier Command-Based Switching Support (PMULT_CBSS) */ #define AHCI_CAP_PMULT_CBSS 0x200 /* Port Multiplier FIS-Based Switching Support (PMULT_FBSS) */ #define AHCI_CAP_PMULT_FBSS 0x400 /* Software Reset FIS cannot set pmport with 0xf for direct access device */ #define AHCI_CAP_SRST_NO_HOSTPORT 0x800 /* Flags controlling the restart port behavior */ #define AHCI_PORT_RESET 0x0001 /* Reset the port */ #define AHCI_RESET_NO_EVENTS_UP 0x0002 /* Don't send reset events up */ #define ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) \ (ahci_portp->ahciport_flags & \ (AHCI_PORT_FLAG_RQSENSE|AHCI_PORT_FLAG_RDLOGEXT)) #define RDWR_PMULT_CMD_IN_PROGRESS(ahci_portp) \ (ahci_portp->ahciport_flags & \ AHCI_PORT_FLAG_RDWR_PMULT) #define NON_NCQ_CMD_IN_PROGRESS(ahci_portp) \ (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && \ ahci_portp->ahciport_pending_tags != 0 && \ ahci_portp->ahciport_pending_ncq_tags == 0) #define NCQ_CMD_IN_PROGRESS(ahci_portp) \ (!ERR_RETRI_CMD_IN_PROGRESS(ahci_portp) && \ ahci_portp->ahciport_pending_ncq_tags != 0) /* Command type for ahci_claim_free_slot routine */ #define AHCI_NON_NCQ_CMD 0x0 #define AHCI_NCQ_CMD 0x1 #define AHCI_ERR_RETRI_CMD 0x2 #define AHCI_RDWR_PMULT_CMD 0x4 /* State values for ahci_attach */ #define AHCI_ATTACH_STATE_NONE (0x1 << 0) #define AHCI_ATTACH_STATE_STATEP_ALLOC (0x1 << 1) #define AHCI_ATTACH_STATE_FMA (0x1 << 2) #define AHCI_ATTACH_STATE_REG_MAP (0x1 << 3) #define AHCI_ATTACH_STATE_PCICFG_SETUP (0x1 << 4) #define AHCI_ATTACH_STATE_INTR_ADDED (0x1 << 5) #define AHCI_ATTACH_STATE_MUTEX_INIT (0x1 << 6) #define AHCI_ATTACH_STATE_PORT_ALLOC (0x1 << 7) #define AHCI_ATTACH_STATE_HW_INIT (0x1 << 8) #define AHCI_ATTACH_STATE_TIMEOUT_ENABLED (0x1 << 9) /* Interval used for delay */ #define AHCI_10MS_TICKS (drv_usectohz(10000)) /* ticks in 10 ms */ #define AHCI_1MS_TICKS (drv_usectohz(1000)) /* ticks in 1 ms */ #define AHCI_100US_TICKS (drv_usectohz(100)) /* ticks in 100 us */ #define AHCI_10MS_USECS (10000) /* microsecs in 10 millisec */ #define AHCI_1MS_USECS (1000) /* microsecs in 1 millisec */ #define AHCI_100US_USECS (100) /* * The following values are the numbers of times to retry polled requests. */ #define AHCI_POLLRATE_HBA_RESET 100 #define AHCI_POLLRATE_PORT_SSTATUS 10 #define AHCI_POLLRATE_PORT_TFD_ERROR 1100 #define AHCI_POLLRATE_PORT_IDLE 50 #define AHCI_POLLRATE_PORT_SOFTRESET 100 #define AHCI_POLLRATE_GET_SPKT 100 #define AHCI_POLLRATE_PORT_IDLE_FR 500 /* Clearing & setting the n'th bit in a given tag */ #define CLEAR_BIT(tag, bit) (tag &= ~(0x1<ahcictl_dip,\ fmt, ## args); \ } #else #define AHCIDBG(flag, ahci_ctlp, fmt, args ...) \ if (ahci_debug_flags & (flag)) { \ if (ahci_ctlp == NULL) \ sata_trace_debug(NULL, fmt, ## args); \ else \ sata_trace_debug(ahci_ctlp->ahcictl_dip,\ fmt, ## args); \ } #endif /* DEBUG */ #ifdef __cplusplus } #endif #endif /* _AHCIVAR_H */