diff options
author | Jim Harris <jimharris@FreeBSD.org> | 2012-01-31 19:38:18 +0000 |
---|---|---|
committer | Jim Harris <jimharris@FreeBSD.org> | 2012-01-31 19:38:18 +0000 |
commit | f11c7f63056671247335df83a3fe80b94c6616ac (patch) | |
tree | cb4ab69703685955650ec588ba9e9a9f80756563 /sys/dev/isci/isci_remote_device.c | |
parent | db25e24c2f75db6edfb0d0b59f051ae30f62f5cd (diff) | |
download | src-f11c7f63056671247335df83a3fe80b94c6616ac.tar.gz src-f11c7f63056671247335df83a3fe80b94c6616ac.zip |
Add isci(4) driver for amd64 and i386 targets.
The isci driver is for the integrated SAS controller in the Intel C600
(Patsburg) chipset. Source files in sys/dev/isci directory are
FreeBSD-specific, and sys/dev/isci/scil subdirectory contains
an OS-agnostic library (SCIL) published by Intel to control the SAS
controller. This library is used primarily as-is in this driver, with
some post-processing to better integrate into the kernel build
environment.
isci.4 and a README in the sys/dev/isci directory contain a few
additional details.
This driver is only built for amd64 and i386 targets.
Sponsored by: Intel
Reviewed by: scottl
Approved by: scottl
Notes
Notes:
svn path=/head/; revision=230843
Diffstat (limited to 'sys/dev/isci/isci_remote_device.c')
-rw-r--r-- | sys/dev/isci/isci_remote_device.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/sys/dev/isci/isci_remote_device.c b/sys/dev/isci/isci_remote_device.c new file mode 100644 index 000000000000..c75105844384 --- /dev/null +++ b/sys/dev/isci/isci_remote_device.c @@ -0,0 +1,298 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dev/isci/isci.h> + +#include <cam/cam_periph.h> +#include <cam/cam_xpt_periph.h> + +#include <dev/isci/scil/scif_task_request.h> +#include <dev/isci/scil/scif_controller.h> +#include <dev/isci/scil/scif_domain.h> +#include <dev/isci/scil/scif_user_callback.h> + +#include <dev/isci/scil/scic_port.h> +#include <dev/isci/scil/scic_phy.h> + +/** + * @brief This callback method informs the framework user that the remote + * device is ready and capable of processing IO requests. + * + * @param[in] controller This parameter specifies the controller object + * with which this callback is associated. + * @param[in] domain This parameter specifies the domain object with + * which this callback is associated. + * @param[in] remote_device This parameter specifies the device object with + * which this callback is associated. + * + * @return none + */ +void +scif_cb_remote_device_ready(SCI_CONTROLLER_HANDLE_T controller, + SCI_DOMAIN_HANDLE_T domain, SCI_REMOTE_DEVICE_HANDLE_T remote_device) +{ + struct ISCI_REMOTE_DEVICE *isci_remote_device = + sci_object_get_association(remote_device); + struct ISCI_CONTROLLER *isci_controller = + sci_object_get_association(controller); + uint32_t device_index = isci_remote_device->index; + + if (isci_controller->remote_device[device_index] == NULL) { + /* This new device is now ready, so put it in the controller's + * remote device list so it is visible to CAM. + */ + isci_controller->remote_device[device_index] = + isci_remote_device; + + if (isci_controller->sim != NULL) { + /* The sim object is not NULL, meaning we have attached + * the controller to CAM already. In that case, create + * a CCB to instruct CAM to rescan this device. + * If the sim object is NULL, this device will get + * scanned as part of the initial scan when the + * controller is attached to CAM. + */ + union ccb *ccb = xpt_alloc_ccb_nowait(); + + xpt_create_path(&ccb->ccb_h.path, xpt_periph, + cam_sim_path(isci_controller->sim), + isci_remote_device->index, CAM_LUN_WILDCARD); + + xpt_rescan(ccb); + } + } + + isci_remote_device_release_device_queue(isci_remote_device); +} + +/** + * @brief This callback method informs the framework user that the remote + * device is not ready. Thus, it is incapable of processing IO + * requests. + * + * @param[in] controller This parameter specifies the controller object + * with which this callback is associated. + * @param[in] domain This parameter specifies the domain object with + * which this callback is associated. + * @param[in] remote_device This parameter specifies the device object with + * which this callback is associated. + * + * @return none + */ +void +scif_cb_remote_device_not_ready(SCI_CONTROLLER_HANDLE_T controller, + SCI_DOMAIN_HANDLE_T domain, SCI_REMOTE_DEVICE_HANDLE_T remote_device) +{ + +} + +/** + * @brief This callback method informs the framework user that the remote + * device failed. This typically occurs shortly after the device + * has been discovered, during the configuration phase for the device. + * + * @param[in] controller This parameter specifies the controller object + * with which this callback is associated. + * @param[in] domain This parameter specifies the domain object with + * which this callback is associated. + * @param[in] remote_device This parameter specifies the device object with + * which this callback is associated. + * @param[in] status This parameter specifies the specific failure condition + * associated with this device failure. + * + * @return none + */ +void +scif_cb_remote_device_failed(SCI_CONTROLLER_HANDLE_T controller, + SCI_DOMAIN_HANDLE_T domain, SCI_REMOTE_DEVICE_HANDLE_T remote_device, + SCI_STATUS status) +{ + +} + +void +isci_remote_device_reset(struct ISCI_REMOTE_DEVICE *remote_device, + union ccb *ccb) +{ + struct ISCI_CONTROLLER *controller = remote_device->domain->controller; + struct ISCI_REQUEST *request; + struct ISCI_TASK_REQUEST *task_request; + SCI_STATUS status; + + if (remote_device->is_resetting == TRUE) { + /* device is already being reset, so return immediately */ + return; + } + + if (sci_pool_empty(controller->request_pool)) { + /* No requests are available in our request pool. If this reset is tied + * to a CCB, ask CAM to requeue it. Otherwise, we need to put it on our + * pending device reset list, so that the reset will occur when a request + * frees up. + */ + if (ccb == NULL) + sci_fast_list_insert_tail( + &controller->pending_device_reset_list, + &remote_device->pending_device_reset_element); + else { + ccb->ccb_h.status &= ~CAM_STATUS_MASK; + ccb->ccb_h.status |= CAM_REQUEUE_REQ; + xpt_done(ccb); + } + return; + } + + isci_log_message(0, "ISCI", + "Sending reset to device on controller %d domain %d CAM index %d\n", + controller->index, remote_device->domain->index, + remote_device->index + ); + + sci_pool_get(controller->request_pool, request); + task_request = (struct ISCI_TASK_REQUEST *)request; + + task_request->parent.remote_device_handle = remote_device->sci_object; + task_request->ccb = ccb; + + remote_device->is_resetting = TRUE; + + status = (SCI_STATUS) scif_task_request_construct( + controller->scif_controller_handle, remote_device->sci_object, + SCI_CONTROLLER_INVALID_IO_TAG, (void *)task_request, + (void *)((char*)task_request + sizeof(struct ISCI_TASK_REQUEST)), + &task_request->sci_object); + + if (status != SCI_SUCCESS) { + isci_task_request_complete(controller->scif_controller_handle, + remote_device->sci_object, task_request->sci_object, + status); + return; + } + + status = (SCI_STATUS)scif_controller_start_task( + controller->scif_controller_handle, remote_device->sci_object, + task_request->sci_object, SCI_CONTROLLER_INVALID_IO_TAG); + + if (status != SCI_SUCCESS) { + isci_task_request_complete( + controller->scif_controller_handle, + remote_device->sci_object, task_request->sci_object, + status); + return; + } +} + +uint32_t +isci_remote_device_get_bitrate(struct ISCI_REMOTE_DEVICE *remote_device) +{ + struct ISCI_DOMAIN *domain = remote_device->domain; + struct ISCI_CONTROLLER *controller = domain->controller; + SCI_PORT_HANDLE_T port_handle; + SCIC_PORT_PROPERTIES_T port_properties; + uint8_t phy_index; + SCI_PHY_HANDLE_T phy_handle; + SCIC_PHY_PROPERTIES_T phy_properties; + + /* get a handle to the port associated with this remote device's + * domain + */ + port_handle = scif_domain_get_scic_port_handle(domain->sci_object); + scic_port_get_properties(port_handle, &port_properties); + + /* get the lowest numbered phy in the port */ + phy_index = 0; + while ((port_properties.phy_mask != 0) && + !(port_properties.phy_mask & 0x1)) { + + phy_index++; + port_properties.phy_mask >>= 1; + } + + /* get the properties for the lowest numbered phy */ + scic_controller_get_phy_handle( + scif_controller_get_scic_handle(controller->scif_controller_handle), + phy_index, &phy_handle); + scic_phy_get_properties(phy_handle, &phy_properties); + + switch (phy_properties.negotiated_link_rate) { + case SCI_SAS_150_GB: + return (150000); + case SCI_SAS_300_GB: + return (300000); + case SCI_SAS_600_GB: + return (600000); + default: + return (0); + } +} + +void +isci_remote_device_freeze_lun_queue(struct ISCI_REMOTE_DEVICE *remote_device, + lun_id_t lun) +{ + if (!(remote_device->frozen_lun_mask & (1 << lun))) { + struct cam_path *path; + + xpt_create_path(&path, xpt_periph, + cam_sim_path(remote_device->domain->controller->sim), + remote_device->index, lun); + xpt_freeze_devq(path, 1); + xpt_free_path(path); + remote_device->frozen_lun_mask |= (1 << lun); + } +} + +void +isci_remote_device_release_lun_queue(struct ISCI_REMOTE_DEVICE *remote_device, + lun_id_t lun) +{ + if (remote_device->frozen_lun_mask & (1 << lun)) { + struct cam_path *path; + + xpt_create_path(&path, xpt_periph, + cam_sim_path(remote_device->domain->controller->sim), + remote_device->index, lun); + xpt_release_devq(path, 1, TRUE); + xpt_free_path(path); + remote_device->frozen_lun_mask &= ~(1 << lun); + } +} + +void +isci_remote_device_release_device_queue( + struct ISCI_REMOTE_DEVICE *remote_device) +{ + lun_id_t lun; + for (lun = 0; lun < ISCI_MAX_LUN; lun++) + isci_remote_device_release_lun_queue(remote_device, lun); +} |