aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/isci/isci_remote_device.c
diff options
context:
space:
mode:
authorJim Harris <jimharris@FreeBSD.org>2012-01-31 19:38:18 +0000
committerJim Harris <jimharris@FreeBSD.org>2012-01-31 19:38:18 +0000
commitf11c7f63056671247335df83a3fe80b94c6616ac (patch)
treecb4ab69703685955650ec588ba9e9a9f80756563 /sys/dev/isci/isci_remote_device.c
parentdb25e24c2f75db6edfb0d0b59f051ae30f62f5cd (diff)
downloadsrc-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.c298
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);
+}