mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
8
drivers/scsi/isci/Makefile
Normal file
8
drivers/scsi/isci/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
obj-$(CONFIG_SCSI_ISCI) += isci.o
|
||||
isci-objs := init.o phy.o request.o \
|
||||
remote_device.o port.o \
|
||||
host.o task.o probe_roms.o \
|
||||
remote_node_context.o \
|
||||
remote_node_table.o \
|
||||
unsolicited_frame_control.o \
|
||||
port_config.o \
|
2807
drivers/scsi/isci/host.c
Normal file
2807
drivers/scsi/isci/host.c
Normal file
File diff suppressed because it is too large
Load diff
517
drivers/scsi/isci/host.h
Normal file
517
drivers/scsi/isci/host.h
Normal file
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _SCI_HOST_H_
|
||||
#define _SCI_HOST_H_
|
||||
|
||||
#include <scsi/sas_ata.h>
|
||||
#include "remote_device.h"
|
||||
#include "phy.h"
|
||||
#include "isci.h"
|
||||
#include "remote_node_table.h"
|
||||
#include "registers.h"
|
||||
#include "unsolicited_frame_control.h"
|
||||
#include "probe_roms.h"
|
||||
|
||||
struct isci_request;
|
||||
struct scu_task_context;
|
||||
|
||||
|
||||
/**
|
||||
* struct sci_power_control -
|
||||
*
|
||||
* This structure defines the fields for managing power control for direct
|
||||
* attached disk devices.
|
||||
*/
|
||||
struct sci_power_control {
|
||||
/**
|
||||
* This field is set when the power control timer is running and cleared when
|
||||
* it is not.
|
||||
*/
|
||||
bool timer_started;
|
||||
|
||||
/**
|
||||
* Timer to control when the directed attached disks can consume power.
|
||||
*/
|
||||
struct sci_timer timer;
|
||||
|
||||
/**
|
||||
* This field is used to keep track of how many phys are put into the
|
||||
* requesters field.
|
||||
*/
|
||||
u8 phys_waiting;
|
||||
|
||||
/**
|
||||
* This field is used to keep track of how many phys have been granted to consume power
|
||||
*/
|
||||
u8 phys_granted_power;
|
||||
|
||||
/**
|
||||
* This field is an array of phys that we are waiting on. The phys are direct
|
||||
* mapped into requesters via struct sci_phy.phy_index
|
||||
*/
|
||||
struct isci_phy *requesters[SCI_MAX_PHYS];
|
||||
|
||||
};
|
||||
|
||||
struct sci_port_configuration_agent;
|
||||
typedef void (*port_config_fn)(struct isci_host *,
|
||||
struct sci_port_configuration_agent *,
|
||||
struct isci_port *, struct isci_phy *);
|
||||
bool is_port_config_apc(struct isci_host *ihost);
|
||||
bool is_controller_start_complete(struct isci_host *ihost);
|
||||
|
||||
struct sci_port_configuration_agent {
|
||||
u16 phy_configured_mask;
|
||||
u16 phy_ready_mask;
|
||||
struct {
|
||||
u8 min_index;
|
||||
u8 max_index;
|
||||
} phy_valid_port_range[SCI_MAX_PHYS];
|
||||
bool timer_pending;
|
||||
port_config_fn link_up_handler;
|
||||
port_config_fn link_down_handler;
|
||||
struct sci_timer timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* isci_host - primary host/controller object
|
||||
* @timer: timeout start/stop operations
|
||||
* @device_table: rni (hw remote node index) to remote device lookup table
|
||||
* @available_remote_nodes: rni allocator
|
||||
* @power_control: manage device spin up
|
||||
* @io_request_sequence: generation number for tci's (task contexts)
|
||||
* @task_context_table: hw task context table
|
||||
* @remote_node_context_table: hw remote node context table
|
||||
* @completion_queue: hw-producer driver-consumer communication ring
|
||||
* @completion_queue_get: tracks the driver 'head' of the ring to notify hw
|
||||
* @logical_port_entries: min({driver|silicon}-supported-port-count)
|
||||
* @remote_node_entries: min({driver|silicon}-supported-node-count)
|
||||
* @task_context_entries: min({driver|silicon}-supported-task-count)
|
||||
* @phy_timer: phy startup timer
|
||||
* @invalid_phy_mask: if an invalid_link_up notification is reported a bit for
|
||||
* the phy index is set so further notifications are not
|
||||
* made. Once the phy reports link up and is made part of a
|
||||
* port then this bit is cleared.
|
||||
|
||||
*/
|
||||
struct isci_host {
|
||||
struct sci_base_state_machine sm;
|
||||
/* XXX can we time this externally */
|
||||
struct sci_timer timer;
|
||||
/* XXX drop reference module params directly */
|
||||
struct sci_user_parameters user_parameters;
|
||||
/* XXX no need to be a union */
|
||||
struct sci_oem_params oem_parameters;
|
||||
struct sci_port_configuration_agent port_agent;
|
||||
struct isci_remote_device *device_table[SCI_MAX_REMOTE_DEVICES];
|
||||
struct sci_remote_node_table available_remote_nodes;
|
||||
struct sci_power_control power_control;
|
||||
u8 io_request_sequence[SCI_MAX_IO_REQUESTS];
|
||||
struct scu_task_context *task_context_table;
|
||||
dma_addr_t tc_dma;
|
||||
union scu_remote_node_context *remote_node_context_table;
|
||||
dma_addr_t rnc_dma;
|
||||
u32 *completion_queue;
|
||||
dma_addr_t cq_dma;
|
||||
u32 completion_queue_get;
|
||||
u32 logical_port_entries;
|
||||
u32 remote_node_entries;
|
||||
u32 task_context_entries;
|
||||
void *ufi_buf;
|
||||
dma_addr_t ufi_dma;
|
||||
struct sci_unsolicited_frame_control uf_control;
|
||||
|
||||
/* phy startup */
|
||||
struct sci_timer phy_timer;
|
||||
/* XXX kill */
|
||||
bool phy_startup_timer_pending;
|
||||
u32 next_phy_to_start;
|
||||
/* XXX convert to unsigned long and use bitops */
|
||||
u8 invalid_phy_mask;
|
||||
|
||||
/* TODO attempt dynamic interrupt coalescing scheme */
|
||||
u16 interrupt_coalesce_number;
|
||||
u32 interrupt_coalesce_timeout;
|
||||
struct smu_registers __iomem *smu_registers;
|
||||
struct scu_registers __iomem *scu_registers;
|
||||
|
||||
u16 tci_head;
|
||||
u16 tci_tail;
|
||||
u16 tci_pool[SCI_MAX_IO_REQUESTS];
|
||||
|
||||
int id; /* unique within a given pci device */
|
||||
struct isci_phy phys[SCI_MAX_PHYS];
|
||||
struct isci_port ports[SCI_MAX_PORTS + 1]; /* includes dummy port */
|
||||
struct asd_sas_port sas_ports[SCI_MAX_PORTS];
|
||||
struct sas_ha_struct sas_ha;
|
||||
|
||||
struct pci_dev *pdev;
|
||||
#define IHOST_START_PENDING 0
|
||||
#define IHOST_STOP_PENDING 1
|
||||
#define IHOST_IRQ_ENABLED 2
|
||||
unsigned long flags;
|
||||
wait_queue_head_t eventq;
|
||||
struct tasklet_struct completion_tasklet;
|
||||
spinlock_t scic_lock;
|
||||
struct isci_request *reqs[SCI_MAX_IO_REQUESTS];
|
||||
struct isci_remote_device devices[SCI_MAX_REMOTE_DEVICES];
|
||||
};
|
||||
|
||||
/**
|
||||
* enum sci_controller_states - This enumeration depicts all the states
|
||||
* for the common controller state machine.
|
||||
*/
|
||||
enum sci_controller_states {
|
||||
/**
|
||||
* Simply the initial state for the base controller state machine.
|
||||
*/
|
||||
SCIC_INITIAL = 0,
|
||||
|
||||
/**
|
||||
* This state indicates that the controller is reset. The memory for
|
||||
* the controller is in it's initial state, but the controller requires
|
||||
* initialization.
|
||||
* This state is entered from the INITIAL state.
|
||||
* This state is entered from the RESETTING state.
|
||||
*/
|
||||
SCIC_RESET,
|
||||
|
||||
/**
|
||||
* This state is typically an action state that indicates the controller
|
||||
* is in the process of initialization. In this state no new IO operations
|
||||
* are permitted.
|
||||
* This state is entered from the RESET state.
|
||||
*/
|
||||
SCIC_INITIALIZING,
|
||||
|
||||
/**
|
||||
* This state indicates that the controller has been successfully
|
||||
* initialized. In this state no new IO operations are permitted.
|
||||
* This state is entered from the INITIALIZING state.
|
||||
*/
|
||||
SCIC_INITIALIZED,
|
||||
|
||||
/**
|
||||
* This state indicates the the controller is in the process of becoming
|
||||
* ready (i.e. starting). In this state no new IO operations are permitted.
|
||||
* This state is entered from the INITIALIZED state.
|
||||
*/
|
||||
SCIC_STARTING,
|
||||
|
||||
/**
|
||||
* This state indicates the controller is now ready. Thus, the user
|
||||
* is able to perform IO operations on the controller.
|
||||
* This state is entered from the STARTING state.
|
||||
*/
|
||||
SCIC_READY,
|
||||
|
||||
/**
|
||||
* This state is typically an action state that indicates the controller
|
||||
* is in the process of resetting. Thus, the user is unable to perform
|
||||
* IO operations on the controller. A reset is considered destructive in
|
||||
* most cases.
|
||||
* This state is entered from the READY state.
|
||||
* This state is entered from the FAILED state.
|
||||
* This state is entered from the STOPPED state.
|
||||
*/
|
||||
SCIC_RESETTING,
|
||||
|
||||
/**
|
||||
* This state indicates that the controller is in the process of stopping.
|
||||
* In this state no new IO operations are permitted, but existing IO
|
||||
* operations are allowed to complete.
|
||||
* This state is entered from the READY state.
|
||||
*/
|
||||
SCIC_STOPPING,
|
||||
|
||||
/**
|
||||
* This state indicates that the controller could not successfully be
|
||||
* initialized. In this state no new IO operations are permitted.
|
||||
* This state is entered from the INITIALIZING state.
|
||||
* This state is entered from the STARTING state.
|
||||
* This state is entered from the STOPPING state.
|
||||
* This state is entered from the RESETTING state.
|
||||
*/
|
||||
SCIC_FAILED,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isci_pci_info - This class represents the pci function containing the
|
||||
* controllers. Depending on PCI SKU, there could be up to 2 controllers in
|
||||
* the PCI function.
|
||||
*/
|
||||
#define SCI_MAX_MSIX_INT (SCI_NUM_MSI_X_INT*SCI_MAX_CONTROLLERS)
|
||||
|
||||
struct isci_pci_info {
|
||||
struct msix_entry msix_entries[SCI_MAX_MSIX_INT];
|
||||
struct isci_host *hosts[SCI_MAX_CONTROLLERS];
|
||||
struct isci_orom *orom;
|
||||
};
|
||||
|
||||
static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev)
|
||||
{
|
||||
return pci_get_drvdata(pdev);
|
||||
}
|
||||
|
||||
static inline struct Scsi_Host *to_shost(struct isci_host *ihost)
|
||||
{
|
||||
return ihost->sas_ha.core.shost;
|
||||
}
|
||||
|
||||
#define for_each_isci_host(id, ihost, pdev) \
|
||||
for (id = 0; id < SCI_MAX_CONTROLLERS && \
|
||||
(ihost = to_pci_info(pdev)->hosts[id]); id++)
|
||||
|
||||
static inline void wait_for_start(struct isci_host *ihost)
|
||||
{
|
||||
wait_event(ihost->eventq, !test_bit(IHOST_START_PENDING, &ihost->flags));
|
||||
}
|
||||
|
||||
static inline void wait_for_stop(struct isci_host *ihost)
|
||||
{
|
||||
wait_event(ihost->eventq, !test_bit(IHOST_STOP_PENDING, &ihost->flags));
|
||||
}
|
||||
|
||||
static inline void wait_for_device_start(struct isci_host *ihost, struct isci_remote_device *idev)
|
||||
{
|
||||
wait_event(ihost->eventq, !test_bit(IDEV_START_PENDING, &idev->flags));
|
||||
}
|
||||
|
||||
static inline void wait_for_device_stop(struct isci_host *ihost, struct isci_remote_device *idev)
|
||||
{
|
||||
wait_event(ihost->eventq, !test_bit(IDEV_STOP_PENDING, &idev->flags));
|
||||
}
|
||||
|
||||
static inline struct isci_host *dev_to_ihost(struct domain_device *dev)
|
||||
{
|
||||
return dev->port->ha->lldd_ha;
|
||||
}
|
||||
|
||||
static inline struct isci_host *idev_to_ihost(struct isci_remote_device *idev)
|
||||
{
|
||||
return dev_to_ihost(idev->domain_dev);
|
||||
}
|
||||
|
||||
/* we always use protocol engine group zero */
|
||||
#define ISCI_PEG 0
|
||||
|
||||
/* see sci_controller_io_tag_allocate|free for how seq and tci are built */
|
||||
#define ISCI_TAG(seq, tci) (((u16) (seq)) << 12 | tci)
|
||||
|
||||
/* these are returned by the hardware, so sanitize them */
|
||||
#define ISCI_TAG_SEQ(tag) (((tag) >> 12) & (SCI_MAX_SEQ-1))
|
||||
#define ISCI_TAG_TCI(tag) ((tag) & (SCI_MAX_IO_REQUESTS-1))
|
||||
|
||||
/* interrupt coalescing baseline: 9 == 3 to 5us interrupt delay per command */
|
||||
#define ISCI_COALESCE_BASE 9
|
||||
|
||||
/* expander attached sata devices require 3 rnc slots */
|
||||
static inline int sci_remote_device_node_count(struct isci_remote_device *idev)
|
||||
{
|
||||
struct domain_device *dev = idev->domain_dev;
|
||||
|
||||
if (dev_is_sata(dev) && dev->parent)
|
||||
return SCU_STP_REMOTE_NODE_COUNT;
|
||||
return SCU_SSP_REMOTE_NODE_COUNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* sci_controller_clear_invalid_phy() -
|
||||
*
|
||||
* This macro will clear the bit in the invalid phy mask for this controller
|
||||
* object. This is used to control messages reported for invalid link up
|
||||
* notifications.
|
||||
*/
|
||||
#define sci_controller_clear_invalid_phy(controller, phy) \
|
||||
((controller)->invalid_phy_mask &= ~(1 << (phy)->phy_index))
|
||||
|
||||
static inline struct device *scirdev_to_dev(struct isci_remote_device *idev)
|
||||
{
|
||||
if (!idev || !idev->isci_port || !idev->isci_port->isci_host)
|
||||
return NULL;
|
||||
|
||||
return &idev->isci_port->isci_host->pdev->dev;
|
||||
}
|
||||
|
||||
static inline bool is_a2(struct pci_dev *pdev)
|
||||
{
|
||||
if (pdev->revision < 4)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool is_b0(struct pci_dev *pdev)
|
||||
{
|
||||
if (pdev->revision == 4)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool is_c0(struct pci_dev *pdev)
|
||||
{
|
||||
if (pdev->revision == 5)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool is_c1(struct pci_dev *pdev)
|
||||
{
|
||||
if (pdev->revision >= 6)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
enum cable_selections {
|
||||
short_cable = 0,
|
||||
long_cable = 1,
|
||||
medium_cable = 2,
|
||||
undefined_cable = 3
|
||||
};
|
||||
|
||||
#define CABLE_OVERRIDE_DISABLED (0x10000)
|
||||
|
||||
static inline int is_cable_select_overridden(void)
|
||||
{
|
||||
return cable_selection_override < CABLE_OVERRIDE_DISABLED;
|
||||
}
|
||||
|
||||
enum cable_selections decode_cable_selection(struct isci_host *ihost, int phy);
|
||||
void validate_cable_selections(struct isci_host *ihost);
|
||||
char *lookup_cable_names(enum cable_selections);
|
||||
|
||||
/* set hw control for 'activity', even though active enclosures seem to drive
|
||||
* the activity led on their own. Skip setting FSENG control on 'status' due
|
||||
* to unexpected operation and 'error' due to not being a supported automatic
|
||||
* FSENG output
|
||||
*/
|
||||
#define SGPIO_HW_CONTROL 0x00000443
|
||||
|
||||
static inline int isci_gpio_count(struct isci_host *ihost)
|
||||
{
|
||||
return ARRAY_SIZE(ihost->scu_registers->peg0.sgpio.output_data_select);
|
||||
}
|
||||
|
||||
void sci_controller_post_request(struct isci_host *ihost,
|
||||
u32 request);
|
||||
void sci_controller_release_frame(struct isci_host *ihost,
|
||||
u32 frame_index);
|
||||
void sci_controller_copy_sata_response(void *response_buffer,
|
||||
void *frame_header,
|
||||
void *frame_buffer);
|
||||
enum sci_status sci_controller_allocate_remote_node_context(struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
u16 *node_id);
|
||||
void sci_controller_free_remote_node_context(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
u16 node_id);
|
||||
|
||||
struct isci_request *sci_request_by_tag(struct isci_host *ihost, u16 io_tag);
|
||||
void sci_controller_power_control_queue_insert(struct isci_host *ihost,
|
||||
struct isci_phy *iphy);
|
||||
void sci_controller_power_control_queue_remove(struct isci_host *ihost,
|
||||
struct isci_phy *iphy);
|
||||
void sci_controller_link_up(struct isci_host *ihost, struct isci_port *iport,
|
||||
struct isci_phy *iphy);
|
||||
void sci_controller_link_down(struct isci_host *ihost, struct isci_port *iport,
|
||||
struct isci_phy *iphy);
|
||||
void sci_controller_remote_device_stopped(struct isci_host *ihost,
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
enum sci_status sci_controller_continue_io(struct isci_request *ireq);
|
||||
int isci_host_scan_finished(struct Scsi_Host *, unsigned long);
|
||||
void isci_host_start(struct Scsi_Host *);
|
||||
u16 isci_alloc_tag(struct isci_host *ihost);
|
||||
enum sci_status isci_free_tag(struct isci_host *ihost, u16 io_tag);
|
||||
void isci_tci_free(struct isci_host *ihost, u16 tci);
|
||||
void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_task *task);
|
||||
|
||||
int isci_host_init(struct isci_host *);
|
||||
void isci_host_completion_routine(unsigned long data);
|
||||
void isci_host_deinit(struct isci_host *);
|
||||
void sci_controller_disable_interrupts(struct isci_host *ihost);
|
||||
bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost);
|
||||
void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status);
|
||||
|
||||
enum sci_status sci_controller_start_io(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
enum sci_task_status sci_controller_start_task(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
enum sci_status sci_controller_terminate_request(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
enum sci_status sci_controller_complete_io(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
void sci_port_configuration_agent_construct(
|
||||
struct sci_port_configuration_agent *port_agent);
|
||||
|
||||
enum sci_status sci_port_configuration_agent_initialize(
|
||||
struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent);
|
||||
|
||||
int isci_gpio_write(struct sas_ha_struct *, u8 reg_type, u8 reg_index,
|
||||
u8 reg_count, u8 *write_data);
|
||||
#endif
|
812
drivers/scsi/isci/init.c
Normal file
812
drivers/scsi/isci/init.c
Normal file
|
@ -0,0 +1,812 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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 <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/efi.h>
|
||||
#include <asm/string.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
#include "host.h"
|
||||
#include "isci.h"
|
||||
#include "task.h"
|
||||
#include "probe_roms.h"
|
||||
|
||||
#define MAJ 1
|
||||
#define MIN 2
|
||||
#define BUILD 0
|
||||
#define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \
|
||||
__stringify(BUILD)
|
||||
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
|
||||
static struct scsi_transport_template *isci_transport_template;
|
||||
|
||||
static const struct pci_device_id isci_id_table[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x1D61),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D63),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D65),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D67),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D69),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D6B),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D60),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D62),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D64),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D66),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D68),},
|
||||
{ PCI_VDEVICE(INTEL, 0x1D6A),},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, isci_id_table);
|
||||
|
||||
/* linux isci specific settings */
|
||||
|
||||
unsigned char no_outbound_task_to = 2;
|
||||
module_param(no_outbound_task_to, byte, 0);
|
||||
MODULE_PARM_DESC(no_outbound_task_to, "No Outbound Task Timeout (1us incr)");
|
||||
|
||||
u16 ssp_max_occ_to = 20;
|
||||
module_param(ssp_max_occ_to, ushort, 0);
|
||||
MODULE_PARM_DESC(ssp_max_occ_to, "SSP Max occupancy timeout (100us incr)");
|
||||
|
||||
u16 stp_max_occ_to = 5;
|
||||
module_param(stp_max_occ_to, ushort, 0);
|
||||
MODULE_PARM_DESC(stp_max_occ_to, "STP Max occupancy timeout (100us incr)");
|
||||
|
||||
u16 ssp_inactive_to = 5;
|
||||
module_param(ssp_inactive_to, ushort, 0);
|
||||
MODULE_PARM_DESC(ssp_inactive_to, "SSP inactivity timeout (100us incr)");
|
||||
|
||||
u16 stp_inactive_to = 5;
|
||||
module_param(stp_inactive_to, ushort, 0);
|
||||
MODULE_PARM_DESC(stp_inactive_to, "STP inactivity timeout (100us incr)");
|
||||
|
||||
unsigned char phy_gen = SCIC_SDS_PARM_GEN2_SPEED;
|
||||
module_param(phy_gen, byte, 0);
|
||||
MODULE_PARM_DESC(phy_gen, "PHY generation (1: 1.5Gbps 2: 3.0Gbps 3: 6.0Gbps)");
|
||||
|
||||
unsigned char max_concurr_spinup;
|
||||
module_param(max_concurr_spinup, byte, 0);
|
||||
MODULE_PARM_DESC(max_concurr_spinup, "Max concurrent device spinup");
|
||||
|
||||
uint cable_selection_override = CABLE_OVERRIDE_DISABLED;
|
||||
module_param(cable_selection_override, uint, 0);
|
||||
|
||||
MODULE_PARM_DESC(cable_selection_override,
|
||||
"This field indicates length of the SAS/SATA cable between "
|
||||
"host and device. If any bits > 15 are set (default) "
|
||||
"indicates \"use platform defaults\"");
|
||||
|
||||
static ssize_t isci_show_id(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct Scsi_Host *shost = container_of(dev, typeof(*shost), shost_dev);
|
||||
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
|
||||
struct isci_host *ihost = container_of(sas_ha, typeof(*ihost), sas_ha);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ihost->id);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(isci_id, S_IRUGO, isci_show_id, NULL);
|
||||
|
||||
struct device_attribute *isci_host_attrs[] = {
|
||||
&dev_attr_isci_id,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct scsi_host_template isci_sht = {
|
||||
|
||||
.module = THIS_MODULE,
|
||||
.name = DRV_NAME,
|
||||
.proc_name = DRV_NAME,
|
||||
.queuecommand = sas_queuecommand,
|
||||
.target_alloc = sas_target_alloc,
|
||||
.slave_configure = sas_slave_configure,
|
||||
.scan_finished = isci_host_scan_finished,
|
||||
.scan_start = isci_host_start,
|
||||
.change_queue_depth = sas_change_queue_depth,
|
||||
.change_queue_type = sas_change_queue_type,
|
||||
.bios_param = sas_bios_param,
|
||||
.can_queue = ISCI_CAN_QUEUE_VAL,
|
||||
.cmd_per_lun = 1,
|
||||
.this_id = -1,
|
||||
.sg_tablesize = SG_ALL,
|
||||
.max_sectors = SCSI_DEFAULT_MAX_SECTORS,
|
||||
.use_clustering = ENABLE_CLUSTERING,
|
||||
.eh_abort_handler = sas_eh_abort_handler,
|
||||
.eh_device_reset_handler = sas_eh_device_reset_handler,
|
||||
.eh_bus_reset_handler = sas_eh_bus_reset_handler,
|
||||
.target_destroy = sas_target_destroy,
|
||||
.ioctl = sas_ioctl,
|
||||
.shost_attrs = isci_host_attrs,
|
||||
};
|
||||
|
||||
static struct sas_domain_function_template isci_transport_ops = {
|
||||
|
||||
/* The class calls these to notify the LLDD of an event. */
|
||||
.lldd_port_formed = isci_port_formed,
|
||||
.lldd_port_deformed = isci_port_deformed,
|
||||
|
||||
/* The class calls these when a device is found or gone. */
|
||||
.lldd_dev_found = isci_remote_device_found,
|
||||
.lldd_dev_gone = isci_remote_device_gone,
|
||||
|
||||
.lldd_execute_task = isci_task_execute_task,
|
||||
/* Task Management Functions. Must be called from process context. */
|
||||
.lldd_abort_task = isci_task_abort_task,
|
||||
.lldd_abort_task_set = isci_task_abort_task_set,
|
||||
.lldd_clear_aca = isci_task_clear_aca,
|
||||
.lldd_clear_task_set = isci_task_clear_task_set,
|
||||
.lldd_I_T_nexus_reset = isci_task_I_T_nexus_reset,
|
||||
.lldd_lu_reset = isci_task_lu_reset,
|
||||
.lldd_query_task = isci_task_query_task,
|
||||
|
||||
/* ata recovery called from ata-eh */
|
||||
.lldd_ata_check_ready = isci_ata_check_ready,
|
||||
|
||||
/* Port and Adapter management */
|
||||
.lldd_clear_nexus_port = isci_task_clear_nexus_port,
|
||||
.lldd_clear_nexus_ha = isci_task_clear_nexus_ha,
|
||||
|
||||
/* Phy management */
|
||||
.lldd_control_phy = isci_phy_control,
|
||||
|
||||
/* GPIO support */
|
||||
.lldd_write_gpio = isci_gpio_write,
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* P R O T E C T E D M E T H O D S
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* isci_register_sas_ha() - This method initializes various lldd
|
||||
* specific members of the sas_ha struct and calls the libsas
|
||||
* sas_register_ha() function.
|
||||
* @isci_host: This parameter specifies the lldd specific wrapper for the
|
||||
* libsas sas_ha struct.
|
||||
*
|
||||
* This method returns an error code indicating success or failure. The user
|
||||
* should check for possible memory allocation error return otherwise, a zero
|
||||
* indicates success.
|
||||
*/
|
||||
static int isci_register_sas_ha(struct isci_host *isci_host)
|
||||
{
|
||||
int i;
|
||||
struct sas_ha_struct *sas_ha = &(isci_host->sas_ha);
|
||||
struct asd_sas_phy **sas_phys;
|
||||
struct asd_sas_port **sas_ports;
|
||||
|
||||
sas_phys = devm_kzalloc(&isci_host->pdev->dev,
|
||||
SCI_MAX_PHYS * sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (!sas_phys)
|
||||
return -ENOMEM;
|
||||
|
||||
sas_ports = devm_kzalloc(&isci_host->pdev->dev,
|
||||
SCI_MAX_PORTS * sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (!sas_ports)
|
||||
return -ENOMEM;
|
||||
|
||||
sas_ha->sas_ha_name = DRV_NAME;
|
||||
sas_ha->lldd_module = THIS_MODULE;
|
||||
sas_ha->sas_addr = &isci_host->phys[0].sas_addr[0];
|
||||
|
||||
for (i = 0; i < SCI_MAX_PHYS; i++) {
|
||||
sas_phys[i] = &isci_host->phys[i].sas_phy;
|
||||
sas_ports[i] = &isci_host->sas_ports[i];
|
||||
}
|
||||
|
||||
sas_ha->sas_phy = sas_phys;
|
||||
sas_ha->sas_port = sas_ports;
|
||||
sas_ha->num_phys = SCI_MAX_PHYS;
|
||||
|
||||
sas_ha->lldd_queue_size = ISCI_CAN_QUEUE_VAL;
|
||||
sas_ha->lldd_max_execute_num = 1;
|
||||
sas_ha->strict_wide_ports = 1;
|
||||
|
||||
sas_register_ha(sas_ha);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void isci_unregister(struct isci_host *isci_host)
|
||||
{
|
||||
struct Scsi_Host *shost;
|
||||
|
||||
if (!isci_host)
|
||||
return;
|
||||
|
||||
sas_unregister_ha(&isci_host->sas_ha);
|
||||
|
||||
shost = to_shost(isci_host);
|
||||
sas_remove_host(shost);
|
||||
scsi_remove_host(shost);
|
||||
scsi_host_put(shost);
|
||||
}
|
||||
|
||||
static int isci_pci_init(struct pci_dev *pdev)
|
||||
{
|
||||
int err, bar_num, bar_mask = 0;
|
||||
void __iomem * const *iomap;
|
||||
|
||||
err = pcim_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed enable PCI device %s!\n",
|
||||
pci_name(pdev));
|
||||
return err;
|
||||
}
|
||||
|
||||
for (bar_num = 0; bar_num < SCI_PCI_BAR_COUNT; bar_num++)
|
||||
bar_mask |= 1 << (bar_num * 2);
|
||||
|
||||
err = pcim_iomap_regions(pdev, bar_mask, DRV_NAME);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
iomap = pcim_iomap_table(pdev);
|
||||
if (!iomap)
|
||||
return -ENOMEM;
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
|
||||
if (err) {
|
||||
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
|
||||
if (err) {
|
||||
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int num_controllers(struct pci_dev *pdev)
|
||||
{
|
||||
/* bar size alone can tell us if we are running with a dual controller
|
||||
* part, no need to trust revision ids that might be under broken firmware
|
||||
* control
|
||||
*/
|
||||
resource_size_t scu_bar_size = pci_resource_len(pdev, SCI_SCU_BAR*2);
|
||||
resource_size_t smu_bar_size = pci_resource_len(pdev, SCI_SMU_BAR*2);
|
||||
|
||||
if (scu_bar_size >= SCI_SCU_BAR_SIZE*SCI_MAX_CONTROLLERS &&
|
||||
smu_bar_size >= SCI_SMU_BAR_SIZE*SCI_MAX_CONTROLLERS)
|
||||
return SCI_MAX_CONTROLLERS;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int isci_setup_interrupts(struct pci_dev *pdev)
|
||||
{
|
||||
int err, i, num_msix;
|
||||
struct isci_host *ihost;
|
||||
struct isci_pci_info *pci_info = to_pci_info(pdev);
|
||||
|
||||
/*
|
||||
* Determine the number of vectors associated with this
|
||||
* PCI function.
|
||||
*/
|
||||
num_msix = num_controllers(pdev) * SCI_NUM_MSI_X_INT;
|
||||
|
||||
for (i = 0; i < num_msix; i++)
|
||||
pci_info->msix_entries[i].entry = i;
|
||||
|
||||
err = pci_enable_msix_exact(pdev, pci_info->msix_entries, num_msix);
|
||||
if (err)
|
||||
goto intx;
|
||||
|
||||
for (i = 0; i < num_msix; i++) {
|
||||
int id = i / SCI_NUM_MSI_X_INT;
|
||||
struct msix_entry *msix = &pci_info->msix_entries[i];
|
||||
irq_handler_t isr;
|
||||
|
||||
ihost = pci_info->hosts[id];
|
||||
/* odd numbered vectors are error interrupts */
|
||||
if (i & 1)
|
||||
isr = isci_error_isr;
|
||||
else
|
||||
isr = isci_msix_isr;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, msix->vector, isr, 0,
|
||||
DRV_NAME"-msix", ihost);
|
||||
if (!err)
|
||||
continue;
|
||||
|
||||
dev_info(&pdev->dev, "msix setup failed falling back to intx\n");
|
||||
while (i--) {
|
||||
id = i / SCI_NUM_MSI_X_INT;
|
||||
ihost = pci_info->hosts[id];
|
||||
msix = &pci_info->msix_entries[i];
|
||||
devm_free_irq(&pdev->dev, msix->vector, ihost);
|
||||
}
|
||||
pci_disable_msix(pdev);
|
||||
goto intx;
|
||||
}
|
||||
return 0;
|
||||
|
||||
intx:
|
||||
for_each_isci_host(i, ihost, pdev) {
|
||||
err = devm_request_irq(&pdev->dev, pdev->irq, isci_intx_isr,
|
||||
IRQF_SHARED, DRV_NAME"-intx", ihost);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void isci_user_parameters_get(struct sci_user_parameters *u)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SCI_MAX_PHYS; i++) {
|
||||
struct sci_phy_user_params *u_phy = &u->phys[i];
|
||||
|
||||
u_phy->max_speed_generation = phy_gen;
|
||||
|
||||
/* we are not exporting these for now */
|
||||
u_phy->align_insertion_frequency = 0x7f;
|
||||
u_phy->in_connection_align_insertion_frequency = 0xff;
|
||||
u_phy->notify_enable_spin_up_insertion_frequency = 0x33;
|
||||
}
|
||||
|
||||
u->stp_inactivity_timeout = stp_inactive_to;
|
||||
u->ssp_inactivity_timeout = ssp_inactive_to;
|
||||
u->stp_max_occupancy_timeout = stp_max_occ_to;
|
||||
u->ssp_max_occupancy_timeout = ssp_max_occ_to;
|
||||
u->no_outbound_task_timeout = no_outbound_task_to;
|
||||
u->max_concurr_spinup = max_concurr_spinup;
|
||||
}
|
||||
|
||||
static enum sci_status sci_user_parameters_set(struct isci_host *ihost,
|
||||
struct sci_user_parameters *sci_parms)
|
||||
{
|
||||
u16 index;
|
||||
|
||||
/*
|
||||
* Validate the user parameters. If they are not legal, then
|
||||
* return a failure.
|
||||
*/
|
||||
for (index = 0; index < SCI_MAX_PHYS; index++) {
|
||||
struct sci_phy_user_params *u;
|
||||
|
||||
u = &sci_parms->phys[index];
|
||||
|
||||
if (!((u->max_speed_generation <= SCIC_SDS_PARM_MAX_SPEED) &&
|
||||
(u->max_speed_generation > SCIC_SDS_PARM_NO_SPEED)))
|
||||
return SCI_FAILURE_INVALID_PARAMETER_VALUE;
|
||||
|
||||
if (u->in_connection_align_insertion_frequency < 3)
|
||||
return SCI_FAILURE_INVALID_PARAMETER_VALUE;
|
||||
|
||||
if ((u->in_connection_align_insertion_frequency < 3) ||
|
||||
(u->align_insertion_frequency == 0) ||
|
||||
(u->notify_enable_spin_up_insertion_frequency == 0))
|
||||
return SCI_FAILURE_INVALID_PARAMETER_VALUE;
|
||||
}
|
||||
|
||||
if ((sci_parms->stp_inactivity_timeout == 0) ||
|
||||
(sci_parms->ssp_inactivity_timeout == 0) ||
|
||||
(sci_parms->stp_max_occupancy_timeout == 0) ||
|
||||
(sci_parms->ssp_max_occupancy_timeout == 0) ||
|
||||
(sci_parms->no_outbound_task_timeout == 0))
|
||||
return SCI_FAILURE_INVALID_PARAMETER_VALUE;
|
||||
|
||||
memcpy(&ihost->user_parameters, sci_parms, sizeof(*sci_parms));
|
||||
|
||||
return SCI_SUCCESS;
|
||||
}
|
||||
|
||||
static void sci_oem_defaults(struct isci_host *ihost)
|
||||
{
|
||||
/* these defaults are overridden by the platform / firmware */
|
||||
struct sci_user_parameters *user = &ihost->user_parameters;
|
||||
struct sci_oem_params *oem = &ihost->oem_parameters;
|
||||
int i;
|
||||
|
||||
/* Default to APC mode. */
|
||||
oem->controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE;
|
||||
|
||||
/* Default to APC mode. */
|
||||
oem->controller.max_concurr_spin_up = 1;
|
||||
|
||||
/* Default to no SSC operation. */
|
||||
oem->controller.do_enable_ssc = false;
|
||||
|
||||
/* Default to short cables on all phys. */
|
||||
oem->controller.cable_selection_mask = 0;
|
||||
|
||||
/* Initialize all of the port parameter information to narrow ports. */
|
||||
for (i = 0; i < SCI_MAX_PORTS; i++)
|
||||
oem->ports[i].phy_mask = 0;
|
||||
|
||||
/* Initialize all of the phy parameter information. */
|
||||
for (i = 0; i < SCI_MAX_PHYS; i++) {
|
||||
/* Default to 3G (i.e. Gen 2). */
|
||||
user->phys[i].max_speed_generation = SCIC_SDS_PARM_GEN2_SPEED;
|
||||
|
||||
/* the frequencies cannot be 0 */
|
||||
user->phys[i].align_insertion_frequency = 0x7f;
|
||||
user->phys[i].in_connection_align_insertion_frequency = 0xff;
|
||||
user->phys[i].notify_enable_spin_up_insertion_frequency = 0x33;
|
||||
|
||||
/* Previous Vitesse based expanders had a arbitration issue that
|
||||
* is worked around by having the upper 32-bits of SAS address
|
||||
* with a value greater then the Vitesse company identifier.
|
||||
* Hence, usage of 0x5FCFFFFF.
|
||||
*/
|
||||
oem->phys[i].sas_address.low = 0x1 + ihost->id;
|
||||
oem->phys[i].sas_address.high = 0x5FCFFFFF;
|
||||
}
|
||||
|
||||
user->stp_inactivity_timeout = 5;
|
||||
user->ssp_inactivity_timeout = 5;
|
||||
user->stp_max_occupancy_timeout = 5;
|
||||
user->ssp_max_occupancy_timeout = 20;
|
||||
user->no_outbound_task_timeout = 2;
|
||||
}
|
||||
|
||||
static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
|
||||
{
|
||||
struct isci_orom *orom = to_pci_info(pdev)->orom;
|
||||
struct sci_user_parameters sci_user_params;
|
||||
u8 oem_version = ISCI_ROM_VER_1_0;
|
||||
struct isci_host *ihost;
|
||||
struct Scsi_Host *shost;
|
||||
int err, i;
|
||||
|
||||
ihost = devm_kzalloc(&pdev->dev, sizeof(*ihost), GFP_KERNEL);
|
||||
if (!ihost)
|
||||
return NULL;
|
||||
|
||||
ihost->pdev = pdev;
|
||||
ihost->id = id;
|
||||
spin_lock_init(&ihost->scic_lock);
|
||||
init_waitqueue_head(&ihost->eventq);
|
||||
ihost->sas_ha.dev = &ihost->pdev->dev;
|
||||
ihost->sas_ha.lldd_ha = ihost;
|
||||
tasklet_init(&ihost->completion_tasklet,
|
||||
isci_host_completion_routine, (unsigned long)ihost);
|
||||
|
||||
/* validate module parameters */
|
||||
/* TODO: kill struct sci_user_parameters and reference directly */
|
||||
sci_oem_defaults(ihost);
|
||||
isci_user_parameters_get(&sci_user_params);
|
||||
if (sci_user_parameters_set(ihost, &sci_user_params)) {
|
||||
dev_warn(&pdev->dev,
|
||||
"%s: sci_user_parameters_set failed\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* sanity check platform (or 'firmware') oem parameters */
|
||||
if (orom) {
|
||||
if (id < 0 || id >= SCI_MAX_CONTROLLERS || id > orom->hdr.num_elements) {
|
||||
dev_warn(&pdev->dev, "parsing firmware oem parameters failed\n");
|
||||
return NULL;
|
||||
}
|
||||
ihost->oem_parameters = orom->ctrl[id];
|
||||
oem_version = orom->hdr.version;
|
||||
}
|
||||
|
||||
/* validate oem parameters (platform, firmware, or built-in defaults) */
|
||||
if (sci_oem_parameters_validate(&ihost->oem_parameters, oem_version)) {
|
||||
dev_warn(&pdev->dev, "oem parameter validation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < SCI_MAX_PORTS; i++) {
|
||||
struct isci_port *iport = &ihost->ports[i];
|
||||
|
||||
INIT_LIST_HEAD(&iport->remote_dev_list);
|
||||
iport->isci_host = ihost;
|
||||
}
|
||||
|
||||
for (i = 0; i < SCI_MAX_PHYS; i++)
|
||||
isci_phy_init(&ihost->phys[i], ihost, i);
|
||||
|
||||
for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
|
||||
struct isci_remote_device *idev = &ihost->devices[i];
|
||||
|
||||
INIT_LIST_HEAD(&idev->node);
|
||||
}
|
||||
|
||||
shost = scsi_host_alloc(&isci_sht, sizeof(void *));
|
||||
if (!shost)
|
||||
return NULL;
|
||||
|
||||
dev_info(&pdev->dev, "%sSCU controller %d: phy 3-0 cables: "
|
||||
"{%s, %s, %s, %s}\n",
|
||||
(is_cable_select_overridden() ? "* " : ""), ihost->id,
|
||||
lookup_cable_names(decode_cable_selection(ihost, 3)),
|
||||
lookup_cable_names(decode_cable_selection(ihost, 2)),
|
||||
lookup_cable_names(decode_cable_selection(ihost, 1)),
|
||||
lookup_cable_names(decode_cable_selection(ihost, 0)));
|
||||
|
||||
err = isci_host_init(ihost);
|
||||
if (err)
|
||||
goto err_shost;
|
||||
|
||||
SHOST_TO_SAS_HA(shost) = &ihost->sas_ha;
|
||||
ihost->sas_ha.core.shost = shost;
|
||||
shost->transportt = isci_transport_template;
|
||||
|
||||
shost->max_id = ~0;
|
||||
shost->max_lun = ~0;
|
||||
shost->max_cmd_len = MAX_COMMAND_SIZE;
|
||||
|
||||
err = scsi_add_host(shost, &pdev->dev);
|
||||
if (err)
|
||||
goto err_shost;
|
||||
|
||||
err = isci_register_sas_ha(ihost);
|
||||
if (err)
|
||||
goto err_shost_remove;
|
||||
|
||||
return ihost;
|
||||
|
||||
err_shost_remove:
|
||||
scsi_remove_host(shost);
|
||||
err_shost:
|
||||
scsi_host_put(shost);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct isci_pci_info *pci_info;
|
||||
int err, i;
|
||||
struct isci_host *isci_host;
|
||||
const struct firmware *fw = NULL;
|
||||
struct isci_orom *orom = NULL;
|
||||
char *source = "(platform)";
|
||||
|
||||
dev_info(&pdev->dev, "driver configured for rev: %d silicon\n",
|
||||
pdev->revision);
|
||||
|
||||
pci_info = devm_kzalloc(&pdev->dev, sizeof(*pci_info), GFP_KERNEL);
|
||||
if (!pci_info)
|
||||
return -ENOMEM;
|
||||
pci_set_drvdata(pdev, pci_info);
|
||||
|
||||
if (efi_enabled(EFI_RUNTIME_SERVICES))
|
||||
orom = isci_get_efi_var(pdev);
|
||||
|
||||
if (!orom)
|
||||
orom = isci_request_oprom(pdev);
|
||||
|
||||
for (i = 0; orom && i < num_controllers(pdev); i++) {
|
||||
if (sci_oem_parameters_validate(&orom->ctrl[i],
|
||||
orom->hdr.version)) {
|
||||
dev_warn(&pdev->dev,
|
||||
"[%d]: invalid oem parameters detected, falling back to firmware\n", i);
|
||||
orom = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!orom) {
|
||||
source = "(firmware)";
|
||||
orom = isci_request_firmware(pdev, fw);
|
||||
if (!orom) {
|
||||
/* TODO convert this to WARN_TAINT_ONCE once the
|
||||
* orom/efi parameter support is widely available
|
||||
*/
|
||||
dev_warn(&pdev->dev,
|
||||
"Loading user firmware failed, using default "
|
||||
"values\n");
|
||||
dev_warn(&pdev->dev,
|
||||
"Default OEM configuration being used: 4 "
|
||||
"narrow ports, and default SAS Addresses\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (orom)
|
||||
dev_info(&pdev->dev,
|
||||
"OEM SAS parameters (version: %u.%u) loaded %s\n",
|
||||
(orom->hdr.version & 0xf0) >> 4,
|
||||
(orom->hdr.version & 0xf), source);
|
||||
|
||||
pci_info->orom = orom;
|
||||
|
||||
err = isci_pci_init(pdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < num_controllers(pdev); i++) {
|
||||
struct isci_host *h = isci_host_alloc(pdev, i);
|
||||
|
||||
if (!h) {
|
||||
err = -ENOMEM;
|
||||
goto err_host_alloc;
|
||||
}
|
||||
pci_info->hosts[i] = h;
|
||||
|
||||
/* turn on DIF support */
|
||||
scsi_host_set_prot(to_shost(h),
|
||||
SHOST_DIF_TYPE1_PROTECTION |
|
||||
SHOST_DIF_TYPE2_PROTECTION |
|
||||
SHOST_DIF_TYPE3_PROTECTION);
|
||||
scsi_host_set_guard(to_shost(h), SHOST_DIX_GUARD_CRC);
|
||||
}
|
||||
|
||||
err = isci_setup_interrupts(pdev);
|
||||
if (err)
|
||||
goto err_host_alloc;
|
||||
|
||||
for_each_isci_host(i, isci_host, pdev)
|
||||
scsi_scan_host(to_shost(isci_host));
|
||||
|
||||
return 0;
|
||||
|
||||
err_host_alloc:
|
||||
for_each_isci_host(i, isci_host, pdev)
|
||||
isci_unregister(isci_host);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void isci_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct isci_host *ihost;
|
||||
int i;
|
||||
|
||||
for_each_isci_host(i, ihost, pdev) {
|
||||
wait_for_start(ihost);
|
||||
isci_unregister(ihost);
|
||||
isci_host_deinit(ihost);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int isci_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct isci_host *ihost;
|
||||
int i;
|
||||
|
||||
for_each_isci_host(i, ihost, pdev) {
|
||||
sas_suspend_ha(&ihost->sas_ha);
|
||||
isci_host_deinit(ihost);
|
||||
}
|
||||
|
||||
pci_save_state(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, PCI_D3hot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isci_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct isci_host *ihost;
|
||||
int rc, i;
|
||||
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
|
||||
rc = pcim_enable_device(pdev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev,
|
||||
"enabling device failure after resume(%d)\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
for_each_isci_host(i, ihost, pdev) {
|
||||
sas_prep_resume_ha(&ihost->sas_ha);
|
||||
|
||||
isci_host_init(ihost);
|
||||
isci_host_start(ihost->sas_ha.core.shost);
|
||||
wait_for_start(ihost);
|
||||
|
||||
sas_resume_ha(&ihost->sas_ha);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(isci_pm_ops, isci_suspend, isci_resume);
|
||||
|
||||
static struct pci_driver isci_pci_driver = {
|
||||
.name = DRV_NAME,
|
||||
.id_table = isci_id_table,
|
||||
.probe = isci_pci_probe,
|
||||
.remove = isci_pci_remove,
|
||||
.driver.pm = &isci_pm_ops,
|
||||
};
|
||||
|
||||
static __init int isci_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
pr_info("%s: Intel(R) C600 SAS Controller Driver - version %s\n",
|
||||
DRV_NAME, DRV_VERSION);
|
||||
|
||||
isci_transport_template = sas_domain_attach_transport(&isci_transport_ops);
|
||||
if (!isci_transport_template)
|
||||
return -ENOMEM;
|
||||
|
||||
err = pci_register_driver(&isci_pci_driver);
|
||||
if (err)
|
||||
sas_release_transport(isci_transport_template);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static __exit void isci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&isci_pci_driver);
|
||||
sas_release_transport(isci_transport_template);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_FIRMWARE(ISCI_FW_NAME);
|
||||
module_init(isci_init);
|
||||
module_exit(isci_exit);
|
539
drivers/scsi/isci/isci.h
Normal file
539
drivers/scsi/isci/isci.h
Normal file
|
@ -0,0 +1,539 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __ISCI_H__
|
||||
#define __ISCI_H__
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DRV_NAME "isci"
|
||||
#define SCI_PCI_BAR_COUNT 2
|
||||
#define SCI_NUM_MSI_X_INT 2
|
||||
#define SCI_SMU_BAR 0
|
||||
#define SCI_SMU_BAR_SIZE (16*1024)
|
||||
#define SCI_SCU_BAR 1
|
||||
#define SCI_SCU_BAR_SIZE (4*1024*1024)
|
||||
#define SCI_IO_SPACE_BAR0 2
|
||||
#define SCI_IO_SPACE_BAR1 3
|
||||
#define ISCI_CAN_QUEUE_VAL 250 /* < SCI_MAX_IO_REQUESTS ? */
|
||||
#define SCIC_CONTROLLER_STOP_TIMEOUT 5000
|
||||
|
||||
#define SCI_CONTROLLER_INVALID_IO_TAG 0xFFFF
|
||||
|
||||
#define SCI_MAX_PHYS (4UL)
|
||||
#define SCI_MAX_PORTS SCI_MAX_PHYS
|
||||
#define SCI_MAX_SMP_PHYS (384) /* not silicon constrained */
|
||||
#define SCI_MAX_REMOTE_DEVICES (256UL)
|
||||
#define SCI_MAX_IO_REQUESTS (256UL)
|
||||
#define SCI_MAX_SEQ (16)
|
||||
#define SCI_MAX_MSIX_MESSAGES (2)
|
||||
#define SCI_MAX_SCATTER_GATHER_ELEMENTS 130 /* not silicon constrained */
|
||||
#define SCI_MAX_CONTROLLERS 2
|
||||
#define SCI_MAX_DOMAINS SCI_MAX_PORTS
|
||||
|
||||
#define SCU_MAX_CRITICAL_NOTIFICATIONS (384)
|
||||
#define SCU_MAX_EVENTS_SHIFT (7)
|
||||
#define SCU_MAX_EVENTS (1 << SCU_MAX_EVENTS_SHIFT)
|
||||
#define SCU_MAX_UNSOLICITED_FRAMES (128)
|
||||
#define SCU_MAX_COMPLETION_QUEUE_SCRATCH (128)
|
||||
#define SCU_MAX_COMPLETION_QUEUE_ENTRIES (SCU_MAX_CRITICAL_NOTIFICATIONS \
|
||||
+ SCU_MAX_EVENTS \
|
||||
+ SCU_MAX_UNSOLICITED_FRAMES \
|
||||
+ SCI_MAX_IO_REQUESTS \
|
||||
+ SCU_MAX_COMPLETION_QUEUE_SCRATCH)
|
||||
#define SCU_MAX_COMPLETION_QUEUE_SHIFT (ilog2(SCU_MAX_COMPLETION_QUEUE_ENTRIES))
|
||||
|
||||
#define SCU_ABSOLUTE_MAX_UNSOLICITED_FRAMES (4096)
|
||||
#define SCU_UNSOLICITED_FRAME_BUFFER_SIZE (1024U)
|
||||
#define SCU_INVALID_FRAME_INDEX (0xFFFF)
|
||||
|
||||
#define SCU_IO_REQUEST_MAX_SGE_SIZE (0x00FFFFFF)
|
||||
#define SCU_IO_REQUEST_MAX_TRANSFER_LENGTH (0x00FFFFFF)
|
||||
|
||||
static inline void check_sizes(void)
|
||||
{
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(SCU_MAX_EVENTS);
|
||||
BUILD_BUG_ON(SCU_MAX_UNSOLICITED_FRAMES <= 8);
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(SCU_MAX_UNSOLICITED_FRAMES);
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(SCU_MAX_COMPLETION_QUEUE_ENTRIES);
|
||||
BUILD_BUG_ON(SCU_MAX_UNSOLICITED_FRAMES > SCU_ABSOLUTE_MAX_UNSOLICITED_FRAMES);
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(SCI_MAX_IO_REQUESTS);
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(SCI_MAX_SEQ);
|
||||
}
|
||||
|
||||
/**
|
||||
* enum sci_status - This is the general return status enumeration for non-IO,
|
||||
* non-task management related SCI interface methods.
|
||||
*
|
||||
*
|
||||
*/
|
||||
enum sci_status {
|
||||
/**
|
||||
* This member indicates successful completion.
|
||||
*/
|
||||
SCI_SUCCESS = 0,
|
||||
|
||||
/**
|
||||
* This value indicates that the calling method completed successfully,
|
||||
* but that the IO may have completed before having it's start method
|
||||
* invoked. This occurs during SAT translation for requests that do
|
||||
* not require an IO to the target or for any other requests that may
|
||||
* be completed without having to submit IO.
|
||||
*/
|
||||
SCI_SUCCESS_IO_COMPLETE_BEFORE_START,
|
||||
|
||||
/**
|
||||
* This Value indicates that the SCU hardware returned an early response
|
||||
* because the io request specified more data than is returned by the
|
||||
* target device (mode pages, inquiry data, etc.). The completion routine
|
||||
* will handle this case to get the actual number of bytes transferred.
|
||||
*/
|
||||
SCI_SUCCESS_IO_DONE_EARLY,
|
||||
|
||||
/**
|
||||
* This member indicates that the object for which a state change is
|
||||
* being requested is already in said state.
|
||||
*/
|
||||
SCI_WARNING_ALREADY_IN_STATE,
|
||||
|
||||
/**
|
||||
* This member indicates interrupt coalescence timer may cause SAS
|
||||
* specification compliance issues (i.e. SMP target mode response
|
||||
* frames must be returned within 1.9 milliseconds).
|
||||
*/
|
||||
SCI_WARNING_TIMER_CONFLICT,
|
||||
|
||||
/**
|
||||
* This field indicates a sequence of action is not completed yet. Mostly,
|
||||
* this status is used when multiple ATA commands are needed in a SATI translation.
|
||||
*/
|
||||
SCI_WARNING_SEQUENCE_INCOMPLETE,
|
||||
|
||||
/**
|
||||
* This member indicates that there was a general failure.
|
||||
*/
|
||||
SCI_FAILURE,
|
||||
|
||||
/**
|
||||
* This member indicates that the SCI implementation is unable to complete
|
||||
* an operation due to a critical flaw the prevents any further operation
|
||||
* (i.e. an invalid pointer).
|
||||
*/
|
||||
SCI_FATAL_ERROR,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the state
|
||||
* of the controller is in a state that prevents successful completion.
|
||||
*/
|
||||
SCI_FAILURE_INVALID_STATE,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because there is
|
||||
* insufficient resources/memory to complete the request.
|
||||
*/
|
||||
SCI_FAILURE_INSUFFICIENT_RESOURCES,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the
|
||||
* controller object required for the operation can't be located.
|
||||
*/
|
||||
SCI_FAILURE_CONTROLLER_NOT_FOUND,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the
|
||||
* discovered controller type is not supported by the library.
|
||||
*/
|
||||
SCI_FAILURE_UNSUPPORTED_CONTROLLER_TYPE,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the
|
||||
* requested initialization data version isn't supported.
|
||||
*/
|
||||
SCI_FAILURE_UNSUPPORTED_INIT_DATA_VERSION,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the
|
||||
* requested configuration of SAS Phys into SAS Ports is not supported.
|
||||
*/
|
||||
SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the
|
||||
* requested protocol is not supported by the remote device, port,
|
||||
* or controller.
|
||||
*/
|
||||
SCI_FAILURE_UNSUPPORTED_PROTOCOL,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the
|
||||
* requested information type is not supported by the SCI implementation.
|
||||
*/
|
||||
SCI_FAILURE_UNSUPPORTED_INFORMATION_TYPE,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the
|
||||
* device already exists.
|
||||
*/
|
||||
SCI_FAILURE_DEVICE_EXISTS,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because adding
|
||||
* a phy to the object is not possible.
|
||||
*/
|
||||
SCI_FAILURE_ADDING_PHY_UNSUPPORTED,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the
|
||||
* requested information type is not supported by the SCI implementation.
|
||||
*/
|
||||
SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD,
|
||||
|
||||
/**
|
||||
* This member indicates the calling function failed, because the SCI
|
||||
* implementation does not support the supplied time limit.
|
||||
*/
|
||||
SCI_FAILURE_UNSUPPORTED_TIME_LIMIT,
|
||||
|
||||
/**
|
||||
* This member indicates the calling method failed, because the SCI
|
||||
* implementation does not contain the specified Phy.
|
||||
*/
|
||||
SCI_FAILURE_INVALID_PHY,
|
||||
|
||||
/**
|
||||
* This member indicates the calling method failed, because the SCI
|
||||
* implementation does not contain the specified Port.
|
||||
*/
|
||||
SCI_FAILURE_INVALID_PORT,
|
||||
|
||||
/**
|
||||
* This member indicates the calling method was partly successful
|
||||
* The port was reset but not all phys in port are operational
|
||||
*/
|
||||
SCI_FAILURE_RESET_PORT_PARTIAL_SUCCESS,
|
||||
|
||||
/**
|
||||
* This member indicates that calling method failed
|
||||
* The port reset did not complete because none of the phys are operational
|
||||
*/
|
||||
SCI_FAILURE_RESET_PORT_FAILURE,
|
||||
|
||||
/**
|
||||
* This member indicates the calling method failed, because the SCI
|
||||
* implementation does not contain the specified remote device.
|
||||
*/
|
||||
SCI_FAILURE_INVALID_REMOTE_DEVICE,
|
||||
|
||||
/**
|
||||
* This member indicates the calling method failed, because the remote
|
||||
* device is in a bad state and requires a reset.
|
||||
*/
|
||||
SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED,
|
||||
|
||||
/**
|
||||
* This member indicates the calling method failed, because the SCI
|
||||
* implementation does not contain or support the specified IO tag.
|
||||
*/
|
||||
SCI_FAILURE_INVALID_IO_TAG,
|
||||
|
||||
/**
|
||||
* This member indicates that the operation failed and the user should
|
||||
* check the response data associated with the IO.
|
||||
*/
|
||||
SCI_FAILURE_IO_RESPONSE_VALID,
|
||||
|
||||
/**
|
||||
* This member indicates that the operation failed, the failure is
|
||||
* controller implementation specific, and the response data associated
|
||||
* with the request is not valid. You can query for the controller
|
||||
* specific error information via sci_controller_get_request_status()
|
||||
*/
|
||||
SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR,
|
||||
|
||||
/**
|
||||
* This member indicated that the operation failed because the
|
||||
* user requested this IO to be terminated.
|
||||
*/
|
||||
SCI_FAILURE_IO_TERMINATED,
|
||||
|
||||
/**
|
||||
* This member indicates that the operation failed and the associated
|
||||
* request requires a SCSI abort task to be sent to the target.
|
||||
*/
|
||||
SCI_FAILURE_IO_REQUIRES_SCSI_ABORT,
|
||||
|
||||
/**
|
||||
* This member indicates that the operation failed because the supplied
|
||||
* device could not be located.
|
||||
*/
|
||||
SCI_FAILURE_DEVICE_NOT_FOUND,
|
||||
|
||||
/**
|
||||
* This member indicates that the operation failed because the
|
||||
* objects association is required and is not correctly set.
|
||||
*/
|
||||
SCI_FAILURE_INVALID_ASSOCIATION,
|
||||
|
||||
/**
|
||||
* This member indicates that the operation failed, because a timeout
|
||||
* occurred.
|
||||
*/
|
||||
SCI_FAILURE_TIMEOUT,
|
||||
|
||||
/**
|
||||
* This member indicates that the operation failed, because the user
|
||||
* specified a value that is either invalid or not supported.
|
||||
*/
|
||||
SCI_FAILURE_INVALID_PARAMETER_VALUE,
|
||||
|
||||
/**
|
||||
* This value indicates that the operation failed, because the number
|
||||
* of messages (MSI-X) is not supported.
|
||||
*/
|
||||
SCI_FAILURE_UNSUPPORTED_MESSAGE_COUNT,
|
||||
|
||||
/**
|
||||
* This value indicates that the method failed due to a lack of
|
||||
* available NCQ tags.
|
||||
*/
|
||||
SCI_FAILURE_NO_NCQ_TAG_AVAILABLE,
|
||||
|
||||
/**
|
||||
* This value indicates that a protocol violation has occurred on the
|
||||
* link.
|
||||
*/
|
||||
SCI_FAILURE_PROTOCOL_VIOLATION,
|
||||
|
||||
/**
|
||||
* This value indicates a failure condition that retry may help to clear.
|
||||
*/
|
||||
SCI_FAILURE_RETRY_REQUIRED,
|
||||
|
||||
/**
|
||||
* This field indicates the retry limit was reached when a retry is attempted
|
||||
*/
|
||||
SCI_FAILURE_RETRY_LIMIT_REACHED,
|
||||
|
||||
/**
|
||||
* This member indicates the calling method was partly successful.
|
||||
* Mostly, this status is used when a LUN_RESET issued to an expander attached
|
||||
* STP device in READY NCQ substate needs to have RNC suspended/resumed
|
||||
* before posting TC.
|
||||
*/
|
||||
SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS,
|
||||
|
||||
/**
|
||||
* This field indicates an illegal phy connection based on the routing attribute
|
||||
* of both expander phy attached to each other.
|
||||
*/
|
||||
SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION,
|
||||
|
||||
/**
|
||||
* This field indicates a CONFIG ROUTE INFO command has a response with function result
|
||||
* INDEX DOES NOT EXIST, usually means exceeding max route index.
|
||||
*/
|
||||
SCI_FAILURE_EXCEED_MAX_ROUTE_INDEX,
|
||||
|
||||
/**
|
||||
* This value indicates that an unsupported PCI device ID has been
|
||||
* specified. This indicates that attempts to invoke
|
||||
* sci_library_allocate_controller() will fail.
|
||||
*/
|
||||
SCI_FAILURE_UNSUPPORTED_PCI_DEVICE_ID
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* enum sci_io_status - This enumeration depicts all of the possible IO
|
||||
* completion status values. Each value in this enumeration maps directly
|
||||
* to a value in the enum sci_status enumeration. Please refer to that
|
||||
* enumeration for detailed comments concerning what the status represents.
|
||||
*
|
||||
* Add the API to retrieve the SCU status from the core. Check to see that the
|
||||
* following status are properly handled: - SCI_IO_FAILURE_UNSUPPORTED_PROTOCOL
|
||||
* - SCI_IO_FAILURE_INVALID_IO_TAG
|
||||
*/
|
||||
enum sci_io_status {
|
||||
SCI_IO_SUCCESS = SCI_SUCCESS,
|
||||
SCI_IO_FAILURE = SCI_FAILURE,
|
||||
SCI_IO_SUCCESS_COMPLETE_BEFORE_START = SCI_SUCCESS_IO_COMPLETE_BEFORE_START,
|
||||
SCI_IO_SUCCESS_IO_DONE_EARLY = SCI_SUCCESS_IO_DONE_EARLY,
|
||||
SCI_IO_FAILURE_INVALID_STATE = SCI_FAILURE_INVALID_STATE,
|
||||
SCI_IO_FAILURE_INSUFFICIENT_RESOURCES = SCI_FAILURE_INSUFFICIENT_RESOURCES,
|
||||
SCI_IO_FAILURE_UNSUPPORTED_PROTOCOL = SCI_FAILURE_UNSUPPORTED_PROTOCOL,
|
||||
SCI_IO_FAILURE_RESPONSE_VALID = SCI_FAILURE_IO_RESPONSE_VALID,
|
||||
SCI_IO_FAILURE_CONTROLLER_SPECIFIC_ERR = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR,
|
||||
SCI_IO_FAILURE_TERMINATED = SCI_FAILURE_IO_TERMINATED,
|
||||
SCI_IO_FAILURE_REQUIRES_SCSI_ABORT = SCI_FAILURE_IO_REQUIRES_SCSI_ABORT,
|
||||
SCI_IO_FAILURE_INVALID_PARAMETER_VALUE = SCI_FAILURE_INVALID_PARAMETER_VALUE,
|
||||
SCI_IO_FAILURE_NO_NCQ_TAG_AVAILABLE = SCI_FAILURE_NO_NCQ_TAG_AVAILABLE,
|
||||
SCI_IO_FAILURE_PROTOCOL_VIOLATION = SCI_FAILURE_PROTOCOL_VIOLATION,
|
||||
|
||||
SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED = SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED,
|
||||
|
||||
SCI_IO_FAILURE_RETRY_REQUIRED = SCI_FAILURE_RETRY_REQUIRED,
|
||||
SCI_IO_FAILURE_RETRY_LIMIT_REACHED = SCI_FAILURE_RETRY_LIMIT_REACHED,
|
||||
SCI_IO_FAILURE_INVALID_REMOTE_DEVICE = SCI_FAILURE_INVALID_REMOTE_DEVICE
|
||||
};
|
||||
|
||||
/**
|
||||
* enum sci_task_status - This enumeration depicts all of the possible task
|
||||
* completion status values. Each value in this enumeration maps directly
|
||||
* to a value in the enum sci_status enumeration. Please refer to that
|
||||
* enumeration for detailed comments concerning what the status represents.
|
||||
*
|
||||
* Check to see that the following status are properly handled:
|
||||
*/
|
||||
enum sci_task_status {
|
||||
SCI_TASK_SUCCESS = SCI_SUCCESS,
|
||||
SCI_TASK_FAILURE = SCI_FAILURE,
|
||||
SCI_TASK_FAILURE_INVALID_STATE = SCI_FAILURE_INVALID_STATE,
|
||||
SCI_TASK_FAILURE_INSUFFICIENT_RESOURCES = SCI_FAILURE_INSUFFICIENT_RESOURCES,
|
||||
SCI_TASK_FAILURE_UNSUPPORTED_PROTOCOL = SCI_FAILURE_UNSUPPORTED_PROTOCOL,
|
||||
SCI_TASK_FAILURE_INVALID_TAG = SCI_FAILURE_INVALID_IO_TAG,
|
||||
SCI_TASK_FAILURE_RESPONSE_VALID = SCI_FAILURE_IO_RESPONSE_VALID,
|
||||
SCI_TASK_FAILURE_CONTROLLER_SPECIFIC_ERR = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR,
|
||||
SCI_TASK_FAILURE_TERMINATED = SCI_FAILURE_IO_TERMINATED,
|
||||
SCI_TASK_FAILURE_INVALID_PARAMETER_VALUE = SCI_FAILURE_INVALID_PARAMETER_VALUE,
|
||||
|
||||
SCI_TASK_FAILURE_REMOTE_DEVICE_RESET_REQUIRED = SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED,
|
||||
SCI_TASK_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS = SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* sci_swab32_cpy - convert between scsi and scu-hardware byte format
|
||||
* @dest: receive the 4-byte endian swapped version of src
|
||||
* @src: word aligned source buffer
|
||||
*
|
||||
* scu hardware handles SSP/SMP control, response, and unidentified
|
||||
* frames in "big endian dword" order. Regardless of host endian this
|
||||
* is always a swab32()-per-dword conversion of the standard definition,
|
||||
* i.e. single byte fields swapped and multi-byte fields in little-
|
||||
* endian
|
||||
*/
|
||||
static inline void sci_swab32_cpy(void *_dest, void *_src, ssize_t word_cnt)
|
||||
{
|
||||
u32 *dest = _dest, *src = _src;
|
||||
|
||||
while (--word_cnt >= 0)
|
||||
dest[word_cnt] = swab32(src[word_cnt]);
|
||||
}
|
||||
|
||||
extern unsigned char no_outbound_task_to;
|
||||
extern u16 ssp_max_occ_to;
|
||||
extern u16 stp_max_occ_to;
|
||||
extern u16 ssp_inactive_to;
|
||||
extern u16 stp_inactive_to;
|
||||
extern unsigned char phy_gen;
|
||||
extern unsigned char max_concurr_spinup;
|
||||
extern uint cable_selection_override;
|
||||
|
||||
irqreturn_t isci_msix_isr(int vec, void *data);
|
||||
irqreturn_t isci_intx_isr(int vec, void *data);
|
||||
irqreturn_t isci_error_isr(int vec, void *data);
|
||||
|
||||
/*
|
||||
* Each timer is associated with a cancellation flag that is set when
|
||||
* del_timer() is called and checked in the timer callback function. This
|
||||
* is needed since del_timer_sync() cannot be called with sci_lock held.
|
||||
* For deinit however, del_timer_sync() is used without holding the lock.
|
||||
*/
|
||||
struct sci_timer {
|
||||
struct timer_list timer;
|
||||
bool cancel;
|
||||
};
|
||||
|
||||
static inline
|
||||
void sci_init_timer(struct sci_timer *tmr, void (*fn)(unsigned long))
|
||||
{
|
||||
tmr->timer.function = fn;
|
||||
tmr->timer.data = (unsigned long) tmr;
|
||||
tmr->cancel = 0;
|
||||
init_timer(&tmr->timer);
|
||||
}
|
||||
|
||||
static inline void sci_mod_timer(struct sci_timer *tmr, unsigned long msec)
|
||||
{
|
||||
tmr->cancel = 0;
|
||||
mod_timer(&tmr->timer, jiffies + msecs_to_jiffies(msec));
|
||||
}
|
||||
|
||||
static inline void sci_del_timer(struct sci_timer *tmr)
|
||||
{
|
||||
tmr->cancel = 1;
|
||||
del_timer(&tmr->timer);
|
||||
}
|
||||
|
||||
struct sci_base_state_machine {
|
||||
const struct sci_base_state *state_table;
|
||||
u32 initial_state_id;
|
||||
u32 current_state_id;
|
||||
u32 previous_state_id;
|
||||
};
|
||||
|
||||
typedef void (*sci_state_transition_t)(struct sci_base_state_machine *sm);
|
||||
|
||||
struct sci_base_state {
|
||||
sci_state_transition_t enter_state; /* Called on state entry */
|
||||
sci_state_transition_t exit_state; /* Called on state exit */
|
||||
};
|
||||
|
||||
extern void sci_init_sm(struct sci_base_state_machine *sm,
|
||||
const struct sci_base_state *state_table,
|
||||
u32 initial_state);
|
||||
extern void sci_change_state(struct sci_base_state_machine *sm, u32 next_state);
|
||||
#endif /* __ISCI_H__ */
|
1487
drivers/scsi/isci/phy.c
Normal file
1487
drivers/scsi/isci/phy.c
Normal file
File diff suppressed because it is too large
Load diff
460
drivers/scsi/isci/phy.h
Normal file
460
drivers/scsi/isci/phy.h
Normal file
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _ISCI_PHY_H_
|
||||
#define _ISCI_PHY_H_
|
||||
|
||||
#include <scsi/sas.h>
|
||||
#include <scsi/libsas.h>
|
||||
#include "isci.h"
|
||||
#include "sas.h"
|
||||
|
||||
/* This is the timeout value for the SATA phy to wait for a SIGNATURE FIS
|
||||
* before restarting the starting state machine. Technically, the old parallel
|
||||
* ATA specification required up to 30 seconds for a device to issue its
|
||||
* signature FIS as a result of a soft reset. Now we see that devices respond
|
||||
* generally within 15 seconds, but we'll use 25 for now.
|
||||
*/
|
||||
#define SCIC_SDS_SIGNATURE_FIS_TIMEOUT 25000
|
||||
|
||||
/* This is the timeout for the SATA OOB/SN because the hardware does not
|
||||
* recognize a hot plug after OOB signal but before the SN signals. We need to
|
||||
* make sure after a hotplug timeout if we have not received the speed event
|
||||
* notification from the hardware that we restart the hardware OOB state
|
||||
* machine.
|
||||
*/
|
||||
#define SCIC_SDS_SATA_LINK_TRAINING_TIMEOUT 250
|
||||
|
||||
/**
|
||||
* isci_phy - hba local phy infrastructure
|
||||
* @sm:
|
||||
* @protocol: attached device protocol
|
||||
* @phy_index: physical index relative to the controller (0-3)
|
||||
* @bcn_received_while_port_unassigned: bcn to report after port association
|
||||
* @sata_timer: timeout SATA signature FIS arrival
|
||||
*/
|
||||
struct isci_phy {
|
||||
struct sci_base_state_machine sm;
|
||||
struct isci_port *owning_port;
|
||||
enum sas_linkrate max_negotiated_speed;
|
||||
enum sas_protocol protocol;
|
||||
u8 phy_index;
|
||||
bool bcn_received_while_port_unassigned;
|
||||
bool is_in_link_training;
|
||||
struct sci_timer sata_timer;
|
||||
struct scu_transport_layer_registers __iomem *transport_layer_registers;
|
||||
struct scu_link_layer_registers __iomem *link_layer_registers;
|
||||
struct asd_sas_phy sas_phy;
|
||||
u8 sas_addr[SAS_ADDR_SIZE];
|
||||
union {
|
||||
struct sas_identify_frame iaf;
|
||||
struct dev_to_host_fis fis;
|
||||
} frame_rcvd;
|
||||
};
|
||||
|
||||
static inline struct isci_phy *to_iphy(struct asd_sas_phy *sas_phy)
|
||||
{
|
||||
struct isci_phy *iphy = container_of(sas_phy, typeof(*iphy), sas_phy);
|
||||
|
||||
return iphy;
|
||||
}
|
||||
|
||||
struct sci_phy_cap {
|
||||
union {
|
||||
struct {
|
||||
/*
|
||||
* The SAS specification indicates the start bit shall
|
||||
* always be set to
|
||||
* 1. This implementation will have the start bit set
|
||||
* to 0 if the PHY CAPABILITIES were either not
|
||||
* received or speed negotiation failed.
|
||||
*/
|
||||
u8 start:1;
|
||||
u8 tx_ssc_type:1;
|
||||
u8 res1:2;
|
||||
u8 req_logical_linkrate:4;
|
||||
|
||||
u32 gen1_no_ssc:1;
|
||||
u32 gen1_ssc:1;
|
||||
u32 gen2_no_ssc:1;
|
||||
u32 gen2_ssc:1;
|
||||
u32 gen3_no_ssc:1;
|
||||
u32 gen3_ssc:1;
|
||||
u32 res2:17;
|
||||
u32 parity:1;
|
||||
};
|
||||
u32 all;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* this data structure reflects the link layer transmit identification reg */
|
||||
struct sci_phy_proto {
|
||||
union {
|
||||
struct {
|
||||
u16 _r_a:1;
|
||||
u16 smp_iport:1;
|
||||
u16 stp_iport:1;
|
||||
u16 ssp_iport:1;
|
||||
u16 _r_b:4;
|
||||
u16 _r_c:1;
|
||||
u16 smp_tport:1;
|
||||
u16 stp_tport:1;
|
||||
u16 ssp_tport:1;
|
||||
u16 _r_d:4;
|
||||
};
|
||||
u16 all;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
|
||||
/**
|
||||
* struct sci_phy_properties - This structure defines the properties common to
|
||||
* all phys that can be retrieved.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct sci_phy_properties {
|
||||
/**
|
||||
* This field specifies the port that currently contains the
|
||||
* supplied phy. This field may be set to NULL
|
||||
* if the phy is not currently contained in a port.
|
||||
*/
|
||||
struct isci_port *iport;
|
||||
|
||||
/**
|
||||
* This field specifies the link rate at which the phy is
|
||||
* currently operating.
|
||||
*/
|
||||
enum sas_linkrate negotiated_link_rate;
|
||||
|
||||
/**
|
||||
* This field specifies the index of the phy in relation to other
|
||||
* phys within the controller. This index is zero relative.
|
||||
*/
|
||||
u8 index;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sci_sas_phy_properties - This structure defines the properties,
|
||||
* specific to a SAS phy, that can be retrieved.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct sci_sas_phy_properties {
|
||||
/**
|
||||
* This field delineates the Identify Address Frame received
|
||||
* from the remote end point.
|
||||
*/
|
||||
struct sas_identify_frame rcvd_iaf;
|
||||
|
||||
/**
|
||||
* This field delineates the Phy capabilities structure received
|
||||
* from the remote end point.
|
||||
*/
|
||||
struct sci_phy_cap rcvd_cap;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sci_sata_phy_properties - This structure defines the properties,
|
||||
* specific to a SATA phy, that can be retrieved.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct sci_sata_phy_properties {
|
||||
/**
|
||||
* This field delineates the signature FIS received from the
|
||||
* attached target.
|
||||
*/
|
||||
struct dev_to_host_fis signature_fis;
|
||||
|
||||
/**
|
||||
* This field specifies to the user if a port selector is connected
|
||||
* on the specified phy.
|
||||
*/
|
||||
bool is_port_selector_present;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* enum sci_phy_counter_id - This enumeration depicts the various pieces of
|
||||
* optional information that can be retrieved for a specific phy.
|
||||
*
|
||||
*
|
||||
*/
|
||||
enum sci_phy_counter_id {
|
||||
/**
|
||||
* This PHY information field tracks the number of frames received.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_FRAME,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of frames transmitted.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_TRANSMITTED_FRAME,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of DWORDs received.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_FRAME_WORD,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of DWORDs transmitted.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_TRANSMITTED_FRAME_DWORD,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of times DWORD
|
||||
* synchronization was lost.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_LOSS_OF_SYNC_ERROR,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of received DWORDs with
|
||||
* running disparity errors.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_DISPARITY_ERROR,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of received frames with a
|
||||
* CRC error (not including short or truncated frames).
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_FRAME_CRC_ERROR,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of DONE (ACK/NAK TIMEOUT)
|
||||
* primitives received.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_DONE_ACK_NAK_TIMEOUT,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of DONE (ACK/NAK TIMEOUT)
|
||||
* primitives transmitted.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_TRANSMITTED_DONE_ACK_NAK_TIMEOUT,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of times the inactivity
|
||||
* timer for connections on the phy has been utilized.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_INACTIVITY_TIMER_EXPIRED,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of DONE (CREDIT TIMEOUT)
|
||||
* primitives received.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_DONE_CREDIT_TIMEOUT,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of DONE (CREDIT TIMEOUT)
|
||||
* primitives transmitted.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_TRANSMITTED_DONE_CREDIT_TIMEOUT,
|
||||
|
||||
/**
|
||||
* This PHY information field tracks the number of CREDIT BLOCKED
|
||||
* primitives received.
|
||||
* @note Depending on remote device implementation, credit blocks
|
||||
* may occur regularly.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_CREDIT_BLOCKED,
|
||||
|
||||
/**
|
||||
* This PHY information field contains the number of short frames
|
||||
* received. A short frame is simply a frame smaller then what is
|
||||
* allowed by either the SAS or SATA specification.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_SHORT_FRAME,
|
||||
|
||||
/**
|
||||
* This PHY information field contains the number of frames received after
|
||||
* credit has been exhausted.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_FRAME_WITHOUT_CREDIT,
|
||||
|
||||
/**
|
||||
* This PHY information field contains the number of frames received after
|
||||
* a DONE has been received.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_RECEIVED_FRAME_AFTER_DONE,
|
||||
|
||||
/**
|
||||
* This PHY information field contains the number of times the phy
|
||||
* failed to achieve DWORD synchronization during speed negotiation.
|
||||
*/
|
||||
SCIC_PHY_COUNTER_SN_DWORD_SYNC_ERROR
|
||||
};
|
||||
|
||||
/**
|
||||
* enum sci_phy_states - phy state machine states
|
||||
* @SCI_PHY_INITIAL: Simply the initial state for the base domain state
|
||||
* machine.
|
||||
* @SCI_PHY_STOPPED: phy has successfully been stopped. In this state
|
||||
* no new IO operations are permitted on this phy.
|
||||
* @SCI_PHY_STARTING: the phy is in the process of becomming ready. In
|
||||
* this state no new IO operations are permitted on
|
||||
* this phy.
|
||||
* @SCI_PHY_SUB_INITIAL: Initial state
|
||||
* @SCI_PHY_SUB_AWAIT_OSSP_EN: Wait state for the hardware OSSP event
|
||||
* type notification
|
||||
* @SCI_PHY_SUB_AWAIT_SAS_SPEED_EN: Wait state for the PHY speed
|
||||
* notification
|
||||
* @SCI_PHY_SUB_AWAIT_IAF_UF: Wait state for the IAF Unsolicited frame
|
||||
* notification
|
||||
* @SCI_PHY_SUB_AWAIT_SAS_POWER: Wait state for the request to consume
|
||||
* power
|
||||
* @SCI_PHY_SUB_AWAIT_SATA_POWER: Wait state for request to consume
|
||||
* power
|
||||
* @SCI_PHY_SUB_AWAIT_SATA_PHY_EN: Wait state for the SATA PHY
|
||||
* notification
|
||||
* @SCI_PHY_SUB_AWAIT_SATA_SPEED_EN: Wait for the SATA PHY speed
|
||||
* notification
|
||||
* @SCI_PHY_SUB_AWAIT_SIG_FIS_UF: Wait state for the SIGNATURE FIS
|
||||
* unsolicited frame notification
|
||||
* @SCI_PHY_SUB_FINAL: Exit state for this state machine
|
||||
* @SCI_PHY_READY: phy is now ready. Thus, the user is able to perform
|
||||
* IO operations utilizing this phy as long as it is
|
||||
* currently part of a valid port. This state is
|
||||
* entered from the STARTING state.
|
||||
* @SCI_PHY_RESETTING: phy is in the process of being reset. In this
|
||||
* state no new IO operations are permitted on this
|
||||
* phy. This state is entered from the READY state.
|
||||
* @SCI_PHY_FINAL: Simply the final state for the base phy state
|
||||
* machine.
|
||||
*/
|
||||
#define PHY_STATES {\
|
||||
C(PHY_INITIAL),\
|
||||
C(PHY_STOPPED),\
|
||||
C(PHY_STARTING),\
|
||||
C(PHY_SUB_INITIAL),\
|
||||
C(PHY_SUB_AWAIT_OSSP_EN),\
|
||||
C(PHY_SUB_AWAIT_SAS_SPEED_EN),\
|
||||
C(PHY_SUB_AWAIT_IAF_UF),\
|
||||
C(PHY_SUB_AWAIT_SAS_POWER),\
|
||||
C(PHY_SUB_AWAIT_SATA_POWER),\
|
||||
C(PHY_SUB_AWAIT_SATA_PHY_EN),\
|
||||
C(PHY_SUB_AWAIT_SATA_SPEED_EN),\
|
||||
C(PHY_SUB_AWAIT_SIG_FIS_UF),\
|
||||
C(PHY_SUB_FINAL),\
|
||||
C(PHY_READY),\
|
||||
C(PHY_RESETTING),\
|
||||
C(PHY_FINAL),\
|
||||
}
|
||||
#undef C
|
||||
#define C(a) SCI_##a
|
||||
enum sci_phy_states PHY_STATES;
|
||||
#undef C
|
||||
|
||||
void sci_phy_construct(
|
||||
struct isci_phy *iphy,
|
||||
struct isci_port *iport,
|
||||
u8 phy_index);
|
||||
|
||||
struct isci_port *phy_get_non_dummy_port(struct isci_phy *iphy);
|
||||
|
||||
void sci_phy_set_port(
|
||||
struct isci_phy *iphy,
|
||||
struct isci_port *iport);
|
||||
|
||||
enum sci_status sci_phy_initialize(
|
||||
struct isci_phy *iphy,
|
||||
struct scu_transport_layer_registers __iomem *transport_layer_registers,
|
||||
struct scu_link_layer_registers __iomem *link_layer_registers);
|
||||
|
||||
enum sci_status sci_phy_start(
|
||||
struct isci_phy *iphy);
|
||||
|
||||
enum sci_status sci_phy_stop(
|
||||
struct isci_phy *iphy);
|
||||
|
||||
enum sci_status sci_phy_reset(
|
||||
struct isci_phy *iphy);
|
||||
|
||||
void sci_phy_resume(
|
||||
struct isci_phy *iphy);
|
||||
|
||||
void sci_phy_setup_transport(
|
||||
struct isci_phy *iphy,
|
||||
u32 device_id);
|
||||
|
||||
enum sci_status sci_phy_event_handler(
|
||||
struct isci_phy *iphy,
|
||||
u32 event_code);
|
||||
|
||||
enum sci_status sci_phy_frame_handler(
|
||||
struct isci_phy *iphy,
|
||||
u32 frame_index);
|
||||
|
||||
enum sci_status sci_phy_consume_power_handler(
|
||||
struct isci_phy *iphy);
|
||||
|
||||
void sci_phy_get_sas_address(
|
||||
struct isci_phy *iphy,
|
||||
struct sci_sas_address *sas_address);
|
||||
|
||||
void sci_phy_get_attached_sas_address(
|
||||
struct isci_phy *iphy,
|
||||
struct sci_sas_address *sas_address);
|
||||
|
||||
struct sci_phy_proto;
|
||||
void sci_phy_get_protocols(
|
||||
struct isci_phy *iphy,
|
||||
struct sci_phy_proto *protocols);
|
||||
enum sas_linkrate sci_phy_linkrate(struct isci_phy *iphy);
|
||||
|
||||
struct isci_host;
|
||||
void isci_phy_init(struct isci_phy *iphy, struct isci_host *ihost, int index);
|
||||
int isci_phy_control(struct asd_sas_phy *phy, enum phy_func func, void *buf);
|
||||
|
||||
#endif /* !defined(_ISCI_PHY_H_) */
|
1770
drivers/scsi/isci/port.c
Normal file
1770
drivers/scsi/isci/port.c
Normal file
File diff suppressed because it is too large
Load diff
283
drivers/scsi/isci/port.h
Normal file
283
drivers/scsi/isci/port.h
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _ISCI_PORT_H_
|
||||
#define _ISCI_PORT_H_
|
||||
|
||||
#include <scsi/libsas.h>
|
||||
#include "isci.h"
|
||||
#include "sas.h"
|
||||
#include "phy.h"
|
||||
|
||||
#define SCIC_SDS_DUMMY_PORT 0xFF
|
||||
|
||||
#define PF_NOTIFY (1 << 0)
|
||||
#define PF_RESUME (1 << 1)
|
||||
|
||||
struct isci_phy;
|
||||
struct isci_host;
|
||||
|
||||
enum isci_status {
|
||||
isci_freed = 0x00,
|
||||
isci_starting = 0x01,
|
||||
isci_ready = 0x02,
|
||||
isci_ready_for_io = 0x03,
|
||||
isci_stopping = 0x04,
|
||||
isci_stopped = 0x05,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isci_port - isci direct attached sas port object
|
||||
* @ready_exit: several states constitute 'ready'. When exiting ready we
|
||||
* need to take extra port-teardown actions that are
|
||||
* skipped when exiting to another 'ready' state.
|
||||
* @logical_port_index: software port index
|
||||
* @physical_port_index: hardware port index
|
||||
* @active_phy_mask: identifies phy members
|
||||
* @enabled_phy_mask: phy mask for the port
|
||||
* that are already part of the port
|
||||
* @reserved_tag:
|
||||
* @reserved_rni: reserver for port task scheduler workaround
|
||||
* @started_request_count: reference count for outstanding commands
|
||||
* @not_ready_reason: set during state transitions and notified
|
||||
* @timer: timeout start/stop operations
|
||||
*/
|
||||
struct isci_port {
|
||||
struct isci_host *isci_host;
|
||||
struct list_head remote_dev_list;
|
||||
#define IPORT_RESET_PENDING 0
|
||||
unsigned long state;
|
||||
enum sci_status hard_reset_status;
|
||||
struct sci_base_state_machine sm;
|
||||
bool ready_exit;
|
||||
u8 logical_port_index;
|
||||
u8 physical_port_index;
|
||||
u8 active_phy_mask;
|
||||
u8 enabled_phy_mask;
|
||||
u8 last_active_phy;
|
||||
u16 reserved_rni;
|
||||
u16 reserved_tag;
|
||||
u32 started_request_count;
|
||||
u32 assigned_device_count;
|
||||
u32 hang_detect_users;
|
||||
u32 not_ready_reason;
|
||||
struct isci_phy *phy_table[SCI_MAX_PHYS];
|
||||
struct isci_host *owning_controller;
|
||||
struct sci_timer timer;
|
||||
struct scu_port_task_scheduler_registers __iomem *port_task_scheduler_registers;
|
||||
/* XXX rework: only one register, no need to replicate per-port */
|
||||
u32 __iomem *port_pe_configuration_register;
|
||||
struct scu_viit_entry __iomem *viit_registers;
|
||||
};
|
||||
|
||||
enum sci_port_not_ready_reason_code {
|
||||
SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS,
|
||||
SCIC_PORT_NOT_READY_HARD_RESET_REQUESTED,
|
||||
SCIC_PORT_NOT_READY_INVALID_PORT_CONFIGURATION,
|
||||
SCIC_PORT_NOT_READY_RECONFIGURING,
|
||||
|
||||
SCIC_PORT_NOT_READY_REASON_CODE_MAX
|
||||
};
|
||||
|
||||
struct sci_port_end_point_properties {
|
||||
struct sci_sas_address sas_address;
|
||||
struct sci_phy_proto protocols;
|
||||
};
|
||||
|
||||
struct sci_port_properties {
|
||||
u32 index;
|
||||
struct sci_port_end_point_properties local;
|
||||
struct sci_port_end_point_properties remote;
|
||||
u32 phy_mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum sci_port_states - port state machine states
|
||||
* @SCI_PORT_STOPPED: port has successfully been stopped. In this state
|
||||
* no new IO operations are permitted. This state is
|
||||
* entered from the STOPPING state.
|
||||
* @SCI_PORT_STOPPING: port is in the process of stopping. In this
|
||||
* state no new IO operations are permitted, but
|
||||
* existing IO operations are allowed to complete.
|
||||
* This state is entered from the READY state.
|
||||
* @SCI_PORT_READY: port is now ready. Thus, the user is able to
|
||||
* perform IO operations on this port. This state is
|
||||
* entered from the STARTING state.
|
||||
* @SCI_PORT_SUB_WAITING: port is started and ready but has no active
|
||||
* phys.
|
||||
* @SCI_PORT_SUB_OPERATIONAL: port is started and ready and there is at
|
||||
* least one phy operational.
|
||||
* @SCI_PORT_SUB_CONFIGURING: port is started and there was an
|
||||
* add/remove phy event. This state is only
|
||||
* used in Automatic Port Configuration Mode
|
||||
* (APC)
|
||||
* @SCI_PORT_RESETTING: port is in the process of performing a hard
|
||||
* reset. Thus, the user is unable to perform IO
|
||||
* operations on this port. This state is entered
|
||||
* from the READY state.
|
||||
* @SCI_PORT_FAILED: port has failed a reset request. This state is
|
||||
* entered when a port reset request times out. This
|
||||
* state is entered from the RESETTING state.
|
||||
*/
|
||||
#define PORT_STATES {\
|
||||
C(PORT_STOPPED),\
|
||||
C(PORT_STOPPING),\
|
||||
C(PORT_READY),\
|
||||
C(PORT_SUB_WAITING),\
|
||||
C(PORT_SUB_OPERATIONAL),\
|
||||
C(PORT_SUB_CONFIGURING),\
|
||||
C(PORT_RESETTING),\
|
||||
C(PORT_FAILED),\
|
||||
}
|
||||
#undef C
|
||||
#define C(a) SCI_##a
|
||||
enum sci_port_states PORT_STATES;
|
||||
#undef C
|
||||
|
||||
static inline void sci_port_decrement_request_count(struct isci_port *iport)
|
||||
{
|
||||
if (WARN_ONCE(iport->started_request_count == 0,
|
||||
"%s: tried to decrement started_request_count past 0!?",
|
||||
__func__))
|
||||
/* pass */;
|
||||
else
|
||||
iport->started_request_count--;
|
||||
}
|
||||
|
||||
#define sci_port_active_phy(port, phy) \
|
||||
(((port)->active_phy_mask & (1 << (phy)->phy_index)) != 0)
|
||||
|
||||
void sci_port_construct(
|
||||
struct isci_port *iport,
|
||||
u8 port_index,
|
||||
struct isci_host *ihost);
|
||||
|
||||
enum sci_status sci_port_start(struct isci_port *iport);
|
||||
enum sci_status sci_port_stop(struct isci_port *iport);
|
||||
|
||||
enum sci_status sci_port_add_phy(
|
||||
struct isci_port *iport,
|
||||
struct isci_phy *iphy);
|
||||
|
||||
enum sci_status sci_port_remove_phy(
|
||||
struct isci_port *iport,
|
||||
struct isci_phy *iphy);
|
||||
|
||||
void sci_port_setup_transports(
|
||||
struct isci_port *iport,
|
||||
u32 device_id);
|
||||
|
||||
void isci_port_bcn_enable(struct isci_host *, struct isci_port *);
|
||||
|
||||
void sci_port_deactivate_phy(
|
||||
struct isci_port *iport,
|
||||
struct isci_phy *iphy,
|
||||
bool do_notify_user);
|
||||
|
||||
bool sci_port_link_detected(
|
||||
struct isci_port *iport,
|
||||
struct isci_phy *iphy);
|
||||
|
||||
enum sci_status sci_port_get_properties(
|
||||
struct isci_port *iport,
|
||||
struct sci_port_properties *prop);
|
||||
|
||||
enum sci_status sci_port_link_up(struct isci_port *iport,
|
||||
struct isci_phy *iphy);
|
||||
enum sci_status sci_port_link_down(struct isci_port *iport,
|
||||
struct isci_phy *iphy);
|
||||
|
||||
struct isci_request;
|
||||
struct isci_remote_device;
|
||||
enum sci_status sci_port_start_io(
|
||||
struct isci_port *iport,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
enum sci_status sci_port_complete_io(
|
||||
struct isci_port *iport,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
enum sas_linkrate sci_port_get_max_allowed_speed(
|
||||
struct isci_port *iport);
|
||||
|
||||
void sci_port_broadcast_change_received(
|
||||
struct isci_port *iport,
|
||||
struct isci_phy *iphy);
|
||||
|
||||
bool sci_port_is_valid_phy_assignment(
|
||||
struct isci_port *iport,
|
||||
u32 phy_index);
|
||||
|
||||
void sci_port_get_sas_address(
|
||||
struct isci_port *iport,
|
||||
struct sci_sas_address *sas_address);
|
||||
|
||||
void sci_port_get_attached_sas_address(
|
||||
struct isci_port *iport,
|
||||
struct sci_sas_address *sas_address);
|
||||
|
||||
void sci_port_set_hang_detection_timeout(
|
||||
struct isci_port *isci_port,
|
||||
u32 timeout);
|
||||
|
||||
void isci_port_formed(struct asd_sas_phy *);
|
||||
void isci_port_deformed(struct asd_sas_phy *);
|
||||
|
||||
int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport,
|
||||
struct isci_phy *iphy);
|
||||
int isci_ata_check_ready(struct domain_device *dev);
|
||||
#endif /* !defined(_ISCI_PORT_H_) */
|
760
drivers/scsi/isci/port_config.c
Normal file
760
drivers/scsi/isci/port_config.c
Normal file
|
@ -0,0 +1,760 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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 "host.h"
|
||||
|
||||
#define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10)
|
||||
#define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10)
|
||||
#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (1000)
|
||||
|
||||
enum SCIC_SDS_APC_ACTIVITY {
|
||||
SCIC_SDS_APC_SKIP_PHY,
|
||||
SCIC_SDS_APC_ADD_PHY,
|
||||
SCIC_SDS_APC_START_TIMER,
|
||||
|
||||
SCIC_SDS_APC_ACTIVITY_MAX
|
||||
};
|
||||
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* General port configuration agent routines
|
||||
* ****************************************************************************** */
|
||||
|
||||
/**
|
||||
*
|
||||
* @address_one: A SAS Address to be compared.
|
||||
* @address_two: A SAS Address to be compared.
|
||||
*
|
||||
* Compare the two SAS Address and if SAS Address One is greater than SAS
|
||||
* Address Two then return > 0 else if SAS Address One is less than SAS Address
|
||||
* Two return < 0 Otherwise they are the same return 0 A signed value of x > 0
|
||||
* > y where x is returned for Address One > Address Two y is returned for
|
||||
* Address One < Address Two 0 is returned ofr Address One = Address Two
|
||||
*/
|
||||
static s32 sci_sas_address_compare(
|
||||
struct sci_sas_address address_one,
|
||||
struct sci_sas_address address_two)
|
||||
{
|
||||
if (address_one.high > address_two.high) {
|
||||
return 1;
|
||||
} else if (address_one.high < address_two.high) {
|
||||
return -1;
|
||||
} else if (address_one.low > address_two.low) {
|
||||
return 1;
|
||||
} else if (address_one.low < address_two.low) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The two SAS Address must be identical */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @controller: The controller object used for the port search.
|
||||
* @phy: The phy object to match.
|
||||
*
|
||||
* This routine will find a matching port for the phy. This means that the
|
||||
* port and phy both have the same broadcast sas address and same received sas
|
||||
* address. The port address or the NULL if there is no matching
|
||||
* port. port address if the port can be found to match the phy.
|
||||
* NULL if there is no matching port for the phy.
|
||||
*/
|
||||
static struct isci_port *sci_port_configuration_agent_find_port(
|
||||
struct isci_host *ihost,
|
||||
struct isci_phy *iphy)
|
||||
{
|
||||
u8 i;
|
||||
struct sci_sas_address port_sas_address;
|
||||
struct sci_sas_address port_attached_device_address;
|
||||
struct sci_sas_address phy_sas_address;
|
||||
struct sci_sas_address phy_attached_device_address;
|
||||
|
||||
/*
|
||||
* Since this phy can be a member of a wide port check to see if one or
|
||||
* more phys match the sent and received SAS address as this phy in which
|
||||
* case it should participate in the same port.
|
||||
*/
|
||||
sci_phy_get_sas_address(iphy, &phy_sas_address);
|
||||
sci_phy_get_attached_sas_address(iphy, &phy_attached_device_address);
|
||||
|
||||
for (i = 0; i < ihost->logical_port_entries; i++) {
|
||||
struct isci_port *iport = &ihost->ports[i];
|
||||
|
||||
sci_port_get_sas_address(iport, &port_sas_address);
|
||||
sci_port_get_attached_sas_address(iport, &port_attached_device_address);
|
||||
|
||||
if (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0 &&
|
||||
sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
|
||||
return iport;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @controller: This is the controller object that contains the port agent
|
||||
* @port_agent: This is the port configruation agent for the controller.
|
||||
*
|
||||
* This routine will validate the port configuration is correct for the SCU
|
||||
* hardware. The SCU hardware allows for port configurations as follows. LP0
|
||||
* -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3) LP1 -> (PE1) LP2 -> (PE2), (PE2,
|
||||
* PE3) LP3 -> (PE3) enum sci_status SCI_SUCCESS the port configuration is valid for
|
||||
* this port configuration agent. SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION
|
||||
* the port configuration is not valid for this port configuration agent.
|
||||
*/
|
||||
static enum sci_status sci_port_configuration_agent_validate_ports(
|
||||
struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent)
|
||||
{
|
||||
struct sci_sas_address first_address;
|
||||
struct sci_sas_address second_address;
|
||||
|
||||
/*
|
||||
* Sanity check the max ranges for all the phys the max index
|
||||
* is always equal to the port range index */
|
||||
if (port_agent->phy_valid_port_range[0].max_index != 0 ||
|
||||
port_agent->phy_valid_port_range[1].max_index != 1 ||
|
||||
port_agent->phy_valid_port_range[2].max_index != 2 ||
|
||||
port_agent->phy_valid_port_range[3].max_index != 3)
|
||||
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
|
||||
|
||||
/*
|
||||
* This is a request to configure a single x4 port or at least attempt
|
||||
* to make all the phys into a single port */
|
||||
if (port_agent->phy_valid_port_range[0].min_index == 0 &&
|
||||
port_agent->phy_valid_port_range[1].min_index == 0 &&
|
||||
port_agent->phy_valid_port_range[2].min_index == 0 &&
|
||||
port_agent->phy_valid_port_range[3].min_index == 0)
|
||||
return SCI_SUCCESS;
|
||||
|
||||
/*
|
||||
* This is a degenerate case where phy 1 and phy 2 are assigned
|
||||
* to the same port this is explicitly disallowed by the hardware
|
||||
* unless they are part of the same x4 port and this condition was
|
||||
* already checked above. */
|
||||
if (port_agent->phy_valid_port_range[2].min_index == 1) {
|
||||
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
|
||||
}
|
||||
|
||||
/*
|
||||
* PE0 and PE3 can never have the same SAS Address unless they
|
||||
* are part of the same x4 wide port and we have already checked
|
||||
* for this condition. */
|
||||
sci_phy_get_sas_address(&ihost->phys[0], &first_address);
|
||||
sci_phy_get_sas_address(&ihost->phys[3], &second_address);
|
||||
|
||||
if (sci_sas_address_compare(first_address, second_address) == 0) {
|
||||
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
|
||||
}
|
||||
|
||||
/*
|
||||
* PE0 and PE1 are configured into a 2x1 ports make sure that the
|
||||
* SAS Address for PE0 and PE2 are different since they can not be
|
||||
* part of the same port. */
|
||||
if (port_agent->phy_valid_port_range[0].min_index == 0 &&
|
||||
port_agent->phy_valid_port_range[1].min_index == 1) {
|
||||
sci_phy_get_sas_address(&ihost->phys[0], &first_address);
|
||||
sci_phy_get_sas_address(&ihost->phys[2], &second_address);
|
||||
|
||||
if (sci_sas_address_compare(first_address, second_address) == 0) {
|
||||
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PE2 and PE3 are configured into a 2x1 ports make sure that the
|
||||
* SAS Address for PE1 and PE3 are different since they can not be
|
||||
* part of the same port. */
|
||||
if (port_agent->phy_valid_port_range[2].min_index == 2 &&
|
||||
port_agent->phy_valid_port_range[3].min_index == 3) {
|
||||
sci_phy_get_sas_address(&ihost->phys[1], &first_address);
|
||||
sci_phy_get_sas_address(&ihost->phys[3], &second_address);
|
||||
|
||||
if (sci_sas_address_compare(first_address, second_address) == 0) {
|
||||
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
|
||||
}
|
||||
}
|
||||
|
||||
return SCI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* Manual port configuration agent routines
|
||||
* ****************************************************************************** */
|
||||
|
||||
/* verify all of the phys in the same port are using the same SAS address */
|
||||
static enum sci_status
|
||||
sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent)
|
||||
{
|
||||
u32 phy_mask;
|
||||
u32 assigned_phy_mask;
|
||||
struct sci_sas_address sas_address;
|
||||
struct sci_sas_address phy_assigned_address;
|
||||
u8 port_index;
|
||||
u8 phy_index;
|
||||
|
||||
assigned_phy_mask = 0;
|
||||
sas_address.high = 0;
|
||||
sas_address.low = 0;
|
||||
|
||||
for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) {
|
||||
phy_mask = ihost->oem_parameters.ports[port_index].phy_mask;
|
||||
|
||||
if (!phy_mask)
|
||||
continue;
|
||||
/*
|
||||
* Make sure that one or more of the phys were not already assinged to
|
||||
* a different port. */
|
||||
if ((phy_mask & ~assigned_phy_mask) == 0) {
|
||||
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
|
||||
}
|
||||
|
||||
/* Find the starting phy index for this round through the loop */
|
||||
for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {
|
||||
if ((phy_mask & (1 << phy_index)) == 0)
|
||||
continue;
|
||||
sci_phy_get_sas_address(&ihost->phys[phy_index],
|
||||
&sas_address);
|
||||
|
||||
/*
|
||||
* The phy_index can be used as the starting point for the
|
||||
* port range since the hardware starts all logical ports
|
||||
* the same as the PE index. */
|
||||
port_agent->phy_valid_port_range[phy_index].min_index = port_index;
|
||||
port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
|
||||
|
||||
if (phy_index != port_index) {
|
||||
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* See how many additional phys are being added to this logical port.
|
||||
* Note: We have not moved the current phy_index so we will actually
|
||||
* compare the startting phy with itself.
|
||||
* This is expected and required to add the phy to the port. */
|
||||
while (phy_index < SCI_MAX_PHYS) {
|
||||
if ((phy_mask & (1 << phy_index)) == 0)
|
||||
continue;
|
||||
sci_phy_get_sas_address(&ihost->phys[phy_index],
|
||||
&phy_assigned_address);
|
||||
|
||||
if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) {
|
||||
/*
|
||||
* The phy mask specified that this phy is part of the same port
|
||||
* as the starting phy and it is not so fail this configuration */
|
||||
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
|
||||
}
|
||||
|
||||
port_agent->phy_valid_port_range[phy_index].min_index = port_index;
|
||||
port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
|
||||
|
||||
sci_port_add_phy(&ihost->ports[port_index],
|
||||
&ihost->phys[phy_index]);
|
||||
|
||||
assigned_phy_mask |= (1 << phy_index);
|
||||
phy_index++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sci_port_configuration_agent_validate_ports(ihost, port_agent);
|
||||
}
|
||||
|
||||
static void mpc_agent_timeout(unsigned long data)
|
||||
{
|
||||
u8 index;
|
||||
struct sci_timer *tmr = (struct sci_timer *)data;
|
||||
struct sci_port_configuration_agent *port_agent;
|
||||
struct isci_host *ihost;
|
||||
unsigned long flags;
|
||||
u16 configure_phy_mask;
|
||||
|
||||
port_agent = container_of(tmr, typeof(*port_agent), timer);
|
||||
ihost = container_of(port_agent, typeof(*ihost), port_agent);
|
||||
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
|
||||
if (tmr->cancel)
|
||||
goto done;
|
||||
|
||||
port_agent->timer_pending = false;
|
||||
|
||||
/* Find the mask of phys that are reported read but as yet unconfigured into a port */
|
||||
configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
|
||||
|
||||
for (index = 0; index < SCI_MAX_PHYS; index++) {
|
||||
struct isci_phy *iphy = &ihost->phys[index];
|
||||
|
||||
if (configure_phy_mask & (1 << index)) {
|
||||
port_agent->link_up_handler(ihost, port_agent,
|
||||
phy_get_non_dummy_port(iphy),
|
||||
iphy);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
}
|
||||
|
||||
static void sci_mpc_agent_link_up(struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent,
|
||||
struct isci_port *iport,
|
||||
struct isci_phy *iphy)
|
||||
{
|
||||
/* If the port is NULL then the phy was not assigned to a port.
|
||||
* This is because the phy was not given the same SAS Address as
|
||||
* the other PHYs in the port.
|
||||
*/
|
||||
if (!iport)
|
||||
return;
|
||||
|
||||
port_agent->phy_ready_mask |= (1 << iphy->phy_index);
|
||||
sci_port_link_up(iport, iphy);
|
||||
if ((iport->active_phy_mask & (1 << iphy->phy_index)))
|
||||
port_agent->phy_configured_mask |= (1 << iphy->phy_index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @controller: This is the controller object that receives the link down
|
||||
* notification.
|
||||
* @port: This is the port object associated with the phy. If the is no
|
||||
* associated port this is an NULL. The port is an invalid
|
||||
* handle only if the phy was never port of this port. This happens when
|
||||
* the phy is not broadcasting the same SAS address as the other phys in the
|
||||
* assigned port.
|
||||
* @phy: This is the phy object which has gone link down.
|
||||
*
|
||||
* This function handles the manual port configuration link down notifications.
|
||||
* Since all ports and phys are associated at initialization time we just turn
|
||||
* around and notifiy the port object of the link down event. If this PHY is
|
||||
* not associated with a port there is no action taken. Is it possible to get a
|
||||
* link down notification from a phy that has no assocoated port?
|
||||
*/
|
||||
static void sci_mpc_agent_link_down(
|
||||
struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent,
|
||||
struct isci_port *iport,
|
||||
struct isci_phy *iphy)
|
||||
{
|
||||
if (iport != NULL) {
|
||||
/*
|
||||
* If we can form a new port from the remainder of the phys
|
||||
* then we want to start the timer to allow the SCI User to
|
||||
* cleanup old devices and rediscover the port before
|
||||
* rebuilding the port with the phys that remain in the ready
|
||||
* state.
|
||||
*/
|
||||
port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
|
||||
port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
|
||||
|
||||
/*
|
||||
* Check to see if there are more phys waiting to be
|
||||
* configured into a port. If there are allow the SCI User
|
||||
* to tear down this port, if necessary, and then reconstruct
|
||||
* the port after the timeout.
|
||||
*/
|
||||
if ((port_agent->phy_configured_mask == 0x0000) &&
|
||||
(port_agent->phy_ready_mask != 0x0000) &&
|
||||
!port_agent->timer_pending) {
|
||||
port_agent->timer_pending = true;
|
||||
|
||||
sci_mod_timer(&port_agent->timer,
|
||||
SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT);
|
||||
}
|
||||
|
||||
sci_port_link_down(iport, iphy);
|
||||
}
|
||||
}
|
||||
|
||||
/* verify phys are assigned a valid SAS address for automatic port
|
||||
* configuration mode.
|
||||
*/
|
||||
static enum sci_status
|
||||
sci_apc_agent_validate_phy_configuration(struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent)
|
||||
{
|
||||
u8 phy_index;
|
||||
u8 port_index;
|
||||
struct sci_sas_address sas_address;
|
||||
struct sci_sas_address phy_assigned_address;
|
||||
|
||||
phy_index = 0;
|
||||
|
||||
while (phy_index < SCI_MAX_PHYS) {
|
||||
port_index = phy_index;
|
||||
|
||||
/* Get the assigned SAS Address for the first PHY on the controller. */
|
||||
sci_phy_get_sas_address(&ihost->phys[phy_index],
|
||||
&sas_address);
|
||||
|
||||
while (++phy_index < SCI_MAX_PHYS) {
|
||||
sci_phy_get_sas_address(&ihost->phys[phy_index],
|
||||
&phy_assigned_address);
|
||||
|
||||
/* Verify each of the SAS address are all the same for every PHY */
|
||||
if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) {
|
||||
port_agent->phy_valid_port_range[phy_index].min_index = port_index;
|
||||
port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
|
||||
} else {
|
||||
port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
|
||||
port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sci_port_configuration_agent_validate_ports(ihost, port_agent);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine will restart the automatic port configuration timeout
|
||||
* timer for the next time period. This could be caused by either a link
|
||||
* down event or a link up event where we can not yet tell to which a phy
|
||||
* belongs.
|
||||
*/
|
||||
static void sci_apc_agent_start_timer(struct sci_port_configuration_agent *port_agent,
|
||||
u32 timeout)
|
||||
{
|
||||
port_agent->timer_pending = true;
|
||||
sci_mod_timer(&port_agent->timer, timeout);
|
||||
}
|
||||
|
||||
static void sci_apc_agent_configure_ports(struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent,
|
||||
struct isci_phy *iphy,
|
||||
bool start_timer)
|
||||
{
|
||||
u8 port_index;
|
||||
enum sci_status status;
|
||||
struct isci_port *iport;
|
||||
enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
|
||||
|
||||
iport = sci_port_configuration_agent_find_port(ihost, iphy);
|
||||
|
||||
if (iport) {
|
||||
if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index))
|
||||
apc_activity = SCIC_SDS_APC_ADD_PHY;
|
||||
else
|
||||
apc_activity = SCIC_SDS_APC_SKIP_PHY;
|
||||
} else {
|
||||
/*
|
||||
* There is no matching Port for this PHY so lets search through the
|
||||
* Ports and see if we can add the PHY to its own port or maybe start
|
||||
* the timer and wait to see if a wider port can be made.
|
||||
*
|
||||
* Note the break when we reach the condition of the port id == phy id */
|
||||
for (port_index = port_agent->phy_valid_port_range[iphy->phy_index].min_index;
|
||||
port_index <= port_agent->phy_valid_port_range[iphy->phy_index].max_index;
|
||||
port_index++) {
|
||||
|
||||
iport = &ihost->ports[port_index];
|
||||
|
||||
/* First we must make sure that this PHY can be added to this Port. */
|
||||
if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index)) {
|
||||
/*
|
||||
* Port contains a PHY with a greater PHY ID than the current
|
||||
* PHY that has gone link up. This phy can not be part of any
|
||||
* port so skip it and move on. */
|
||||
if (iport->active_phy_mask > (1 << iphy->phy_index)) {
|
||||
apc_activity = SCIC_SDS_APC_SKIP_PHY;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have reached the end of our Port list and have not found
|
||||
* any reason why we should not either add the PHY to the port
|
||||
* or wait for more phys to become active. */
|
||||
if (iport->physical_port_index == iphy->phy_index) {
|
||||
/*
|
||||
* The Port either has no active PHYs.
|
||||
* Consider that if the port had any active PHYs we would have
|
||||
* or active PHYs with
|
||||
* a lower PHY Id than this PHY. */
|
||||
if (apc_activity != SCIC_SDS_APC_START_TIMER) {
|
||||
apc_activity = SCIC_SDS_APC_ADD_PHY;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The current Port has no active PHYs and this PHY could be part
|
||||
* of this Port. Since we dont know as yet setup to start the
|
||||
* timer and see if there is a better configuration. */
|
||||
if (iport->active_phy_mask == 0) {
|
||||
apc_activity = SCIC_SDS_APC_START_TIMER;
|
||||
}
|
||||
} else if (iport->active_phy_mask != 0) {
|
||||
/*
|
||||
* The Port has an active phy and the current Phy can not
|
||||
* participate in this port so skip the PHY and see if
|
||||
* there is a better configuration. */
|
||||
apc_activity = SCIC_SDS_APC_SKIP_PHY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if the start timer operations should instead map to an
|
||||
* add phy operation. This is caused because we have been waiting to
|
||||
* add a phy to a port but could not becuase the automatic port
|
||||
* configuration engine had a choice of possible ports for the phy.
|
||||
* Since we have gone through a timeout we are going to restrict the
|
||||
* choice to the smallest possible port. */
|
||||
if (
|
||||
(start_timer == false)
|
||||
&& (apc_activity == SCIC_SDS_APC_START_TIMER)
|
||||
) {
|
||||
apc_activity = SCIC_SDS_APC_ADD_PHY;
|
||||
}
|
||||
|
||||
switch (apc_activity) {
|
||||
case SCIC_SDS_APC_ADD_PHY:
|
||||
status = sci_port_add_phy(iport, iphy);
|
||||
|
||||
if (status == SCI_SUCCESS) {
|
||||
port_agent->phy_configured_mask |= (1 << iphy->phy_index);
|
||||
}
|
||||
break;
|
||||
|
||||
case SCIC_SDS_APC_START_TIMER:
|
||||
sci_apc_agent_start_timer(port_agent,
|
||||
SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
|
||||
break;
|
||||
|
||||
case SCIC_SDS_APC_SKIP_PHY:
|
||||
default:
|
||||
/* do nothing the PHY can not be made part of a port at this time. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sci_apc_agent_link_up - handle apc link up events
|
||||
* @scic: This is the controller object that receives the link up
|
||||
* notification.
|
||||
* @sci_port: This is the port object associated with the phy. If the is no
|
||||
* associated port this is an NULL.
|
||||
* @sci_phy: This is the phy object which has gone link up.
|
||||
*
|
||||
* This method handles the automatic port configuration for link up
|
||||
* notifications. Is it possible to get a link down notification from a phy
|
||||
* that has no assocoated port?
|
||||
*/
|
||||
static void sci_apc_agent_link_up(struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent,
|
||||
struct isci_port *iport,
|
||||
struct isci_phy *iphy)
|
||||
{
|
||||
u8 phy_index = iphy->phy_index;
|
||||
|
||||
if (!iport) {
|
||||
/* the phy is not the part of this port */
|
||||
port_agent->phy_ready_mask |= 1 << phy_index;
|
||||
sci_apc_agent_start_timer(port_agent,
|
||||
SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
|
||||
} else {
|
||||
/* the phy is already the part of the port */
|
||||
port_agent->phy_ready_mask |= 1 << phy_index;
|
||||
sci_port_link_up(iport, iphy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @controller: This is the controller object that receives the link down
|
||||
* notification.
|
||||
* @iport: This is the port object associated with the phy. If the is no
|
||||
* associated port this is an NULL.
|
||||
* @iphy: This is the phy object which has gone link down.
|
||||
*
|
||||
* This method handles the automatic port configuration link down
|
||||
* notifications. not associated with a port there is no action taken. Is it
|
||||
* possible to get a link down notification from a phy that has no assocoated
|
||||
* port?
|
||||
*/
|
||||
static void sci_apc_agent_link_down(
|
||||
struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent,
|
||||
struct isci_port *iport,
|
||||
struct isci_phy *iphy)
|
||||
{
|
||||
port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
|
||||
|
||||
if (!iport)
|
||||
return;
|
||||
if (port_agent->phy_configured_mask & (1 << iphy->phy_index)) {
|
||||
enum sci_status status;
|
||||
|
||||
status = sci_port_remove_phy(iport, iphy);
|
||||
|
||||
if (status == SCI_SUCCESS)
|
||||
port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
|
||||
}
|
||||
}
|
||||
|
||||
/* configure the phys into ports when the timer fires */
|
||||
static void apc_agent_timeout(unsigned long data)
|
||||
{
|
||||
u32 index;
|
||||
struct sci_timer *tmr = (struct sci_timer *)data;
|
||||
struct sci_port_configuration_agent *port_agent;
|
||||
struct isci_host *ihost;
|
||||
unsigned long flags;
|
||||
u16 configure_phy_mask;
|
||||
|
||||
port_agent = container_of(tmr, typeof(*port_agent), timer);
|
||||
ihost = container_of(port_agent, typeof(*ihost), port_agent);
|
||||
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
|
||||
if (tmr->cancel)
|
||||
goto done;
|
||||
|
||||
port_agent->timer_pending = false;
|
||||
|
||||
configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
|
||||
|
||||
if (!configure_phy_mask)
|
||||
goto done;
|
||||
|
||||
for (index = 0; index < SCI_MAX_PHYS; index++) {
|
||||
if ((configure_phy_mask & (1 << index)) == 0)
|
||||
continue;
|
||||
|
||||
sci_apc_agent_configure_ports(ihost, port_agent,
|
||||
&ihost->phys[index], false);
|
||||
}
|
||||
|
||||
if (is_controller_start_complete(ihost))
|
||||
sci_controller_transition_to_ready(ihost, SCI_SUCCESS);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* Public port configuration agent routines
|
||||
* ****************************************************************************** */
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This method will construct the port configuration agent for operation. This
|
||||
* call is universal for both manual port configuration and automatic port
|
||||
* configuration modes.
|
||||
*/
|
||||
void sci_port_configuration_agent_construct(
|
||||
struct sci_port_configuration_agent *port_agent)
|
||||
{
|
||||
u32 index;
|
||||
|
||||
port_agent->phy_configured_mask = 0x00;
|
||||
port_agent->phy_ready_mask = 0x00;
|
||||
|
||||
port_agent->link_up_handler = NULL;
|
||||
port_agent->link_down_handler = NULL;
|
||||
|
||||
port_agent->timer_pending = false;
|
||||
|
||||
for (index = 0; index < SCI_MAX_PORTS; index++) {
|
||||
port_agent->phy_valid_port_range[index].min_index = 0;
|
||||
port_agent->phy_valid_port_range[index].max_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_port_config_apc(struct isci_host *ihost)
|
||||
{
|
||||
return ihost->port_agent.link_up_handler == sci_apc_agent_link_up;
|
||||
}
|
||||
|
||||
enum sci_status sci_port_configuration_agent_initialize(
|
||||
struct isci_host *ihost,
|
||||
struct sci_port_configuration_agent *port_agent)
|
||||
{
|
||||
enum sci_status status;
|
||||
enum sci_port_configuration_mode mode;
|
||||
|
||||
mode = ihost->oem_parameters.controller.mode_type;
|
||||
|
||||
if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) {
|
||||
status = sci_mpc_agent_validate_phy_configuration(
|
||||
ihost, port_agent);
|
||||
|
||||
port_agent->link_up_handler = sci_mpc_agent_link_up;
|
||||
port_agent->link_down_handler = sci_mpc_agent_link_down;
|
||||
|
||||
sci_init_timer(&port_agent->timer, mpc_agent_timeout);
|
||||
} else {
|
||||
status = sci_apc_agent_validate_phy_configuration(
|
||||
ihost, port_agent);
|
||||
|
||||
port_agent->link_up_handler = sci_apc_agent_link_up;
|
||||
port_agent->link_down_handler = sci_apc_agent_link_down;
|
||||
|
||||
sci_init_timer(&port_agent->timer, apc_agent_timeout);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
230
drivers/scsi/isci/probe_roms.c
Normal file
230
drivers/scsi/isci/probe_roms.c
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*/
|
||||
|
||||
/* probe_roms - scan for oem parameters */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/efi.h>
|
||||
#include <asm/probe_roms.h>
|
||||
|
||||
#include "isci.h"
|
||||
#include "task.h"
|
||||
#include "probe_roms.h"
|
||||
|
||||
static efi_char16_t isci_efivar_name[] = {
|
||||
'R', 's', 't', 'S', 'c', 'u', 'O'
|
||||
};
|
||||
|
||||
struct isci_orom *isci_request_oprom(struct pci_dev *pdev)
|
||||
{
|
||||
void __iomem *oprom = pci_map_biosrom(pdev);
|
||||
struct isci_orom *rom = NULL;
|
||||
size_t len, i;
|
||||
int j;
|
||||
char oem_sig[4];
|
||||
struct isci_oem_hdr oem_hdr;
|
||||
u8 *tmp, sum;
|
||||
|
||||
if (!oprom)
|
||||
return NULL;
|
||||
|
||||
len = pci_biosrom_size(pdev);
|
||||
rom = devm_kzalloc(&pdev->dev, sizeof(*rom), GFP_KERNEL);
|
||||
if (!rom) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Unable to allocate memory for orom\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len && rom; i += ISCI_OEM_SIG_SIZE) {
|
||||
memcpy_fromio(oem_sig, oprom + i, ISCI_OEM_SIG_SIZE);
|
||||
|
||||
/* we think we found the OEM table */
|
||||
if (memcmp(oem_sig, ISCI_OEM_SIG, ISCI_OEM_SIG_SIZE) == 0) {
|
||||
size_t copy_len;
|
||||
|
||||
memcpy_fromio(&oem_hdr, oprom + i, sizeof(oem_hdr));
|
||||
|
||||
copy_len = min(oem_hdr.len - sizeof(oem_hdr),
|
||||
sizeof(*rom));
|
||||
|
||||
memcpy_fromio(rom,
|
||||
oprom + i + sizeof(oem_hdr),
|
||||
copy_len);
|
||||
|
||||
/* calculate checksum */
|
||||
tmp = (u8 *)&oem_hdr;
|
||||
for (j = 0, sum = 0; j < sizeof(oem_hdr); j++, tmp++)
|
||||
sum += *tmp;
|
||||
|
||||
tmp = (u8 *)rom;
|
||||
for (j = 0; j < sizeof(*rom); j++, tmp++)
|
||||
sum += *tmp;
|
||||
|
||||
if (sum != 0) {
|
||||
dev_warn(&pdev->dev,
|
||||
"OEM table checksum failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* keep going if that's not the oem param table */
|
||||
if (memcmp(rom->hdr.signature,
|
||||
ISCI_ROM_SIG,
|
||||
ISCI_ROM_SIG_SIZE) != 0)
|
||||
continue;
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"OEM parameter table found in OROM\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= len) {
|
||||
dev_err(&pdev->dev, "oprom parse error\n");
|
||||
rom = NULL;
|
||||
}
|
||||
pci_unmap_biosrom(oprom);
|
||||
|
||||
return rom;
|
||||
}
|
||||
|
||||
struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw)
|
||||
{
|
||||
struct isci_orom *orom = NULL, *data;
|
||||
int i, j;
|
||||
|
||||
if (request_firmware(&fw, ISCI_FW_NAME, &pdev->dev) != 0)
|
||||
return NULL;
|
||||
|
||||
if (fw->size < sizeof(*orom))
|
||||
goto out;
|
||||
|
||||
data = (struct isci_orom *)fw->data;
|
||||
|
||||
if (strncmp(ISCI_ROM_SIG, data->hdr.signature,
|
||||
strlen(ISCI_ROM_SIG)) != 0)
|
||||
goto out;
|
||||
|
||||
orom = devm_kzalloc(&pdev->dev, fw->size, GFP_KERNEL);
|
||||
if (!orom)
|
||||
goto out;
|
||||
|
||||
memcpy(orom, fw->data, fw->size);
|
||||
|
||||
if (is_c0(pdev) || is_c1(pdev))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* deprecated: override default amp_control for pre-preproduction
|
||||
* silicon revisions
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(orom->ctrl); i++)
|
||||
for (j = 0; j < ARRAY_SIZE(orom->ctrl[i].phys); j++) {
|
||||
orom->ctrl[i].phys[j].afe_tx_amp_control0 = 0xe7c03;
|
||||
orom->ctrl[i].phys[j].afe_tx_amp_control1 = 0xe7c03;
|
||||
orom->ctrl[i].phys[j].afe_tx_amp_control2 = 0xe7c03;
|
||||
orom->ctrl[i].phys[j].afe_tx_amp_control3 = 0xe7c03;
|
||||
}
|
||||
out:
|
||||
release_firmware(fw);
|
||||
|
||||
return orom;
|
||||
}
|
||||
|
||||
static struct efi *get_efi(void)
|
||||
{
|
||||
#ifdef CONFIG_EFI
|
||||
return &efi;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct isci_orom *isci_get_efi_var(struct pci_dev *pdev)
|
||||
{
|
||||
efi_status_t status;
|
||||
struct isci_orom *rom;
|
||||
struct isci_oem_hdr *oem_hdr;
|
||||
u8 *tmp, sum;
|
||||
int j;
|
||||
unsigned long data_len;
|
||||
u8 *efi_data;
|
||||
u32 efi_attrib = 0;
|
||||
|
||||
data_len = 1024;
|
||||
efi_data = devm_kzalloc(&pdev->dev, data_len, GFP_KERNEL);
|
||||
if (!efi_data) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Unable to allocate memory for EFI data\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rom = (struct isci_orom *)(efi_data + sizeof(struct isci_oem_hdr));
|
||||
|
||||
if (get_efi())
|
||||
status = get_efi()->get_variable(isci_efivar_name,
|
||||
&ISCI_EFI_VENDOR_GUID,
|
||||
&efi_attrib,
|
||||
&data_len,
|
||||
efi_data);
|
||||
else
|
||||
status = EFI_NOT_FOUND;
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Unable to obtain EFI var data for OEM parms\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
oem_hdr = (struct isci_oem_hdr *)efi_data;
|
||||
|
||||
if (memcmp(oem_hdr->sig, ISCI_OEM_SIG, ISCI_OEM_SIG_SIZE) != 0) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Invalid OEM header signature\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* calculate checksum */
|
||||
tmp = (u8 *)efi_data;
|
||||
for (j = 0, sum = 0; j < (sizeof(*oem_hdr) + sizeof(*rom)); j++, tmp++)
|
||||
sum += *tmp;
|
||||
|
||||
if (sum != 0) {
|
||||
dev_warn(&pdev->dev,
|
||||
"OEM table checksum failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (memcmp(rom->hdr.signature,
|
||||
ISCI_ROM_SIG,
|
||||
ISCI_ROM_SIG_SIZE) != 0) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Invalid OEM table signature\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rom;
|
||||
}
|
330
drivers/scsi/isci/probe_roms.h
Normal file
330
drivers/scsi/isci/probe_roms.h
Normal file
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _ISCI_PROBE_ROMS_H_
|
||||
#define _ISCI_PROBE_ROMS_H_
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/efi.h>
|
||||
#include "isci.h"
|
||||
|
||||
#define SCIC_SDS_PARM_NO_SPEED 0
|
||||
|
||||
/* generation 1 (i.e. 1.5 Gb/s) */
|
||||
#define SCIC_SDS_PARM_GEN1_SPEED 1
|
||||
|
||||
/* generation 2 (i.e. 3.0 Gb/s) */
|
||||
#define SCIC_SDS_PARM_GEN2_SPEED 2
|
||||
|
||||
/* generation 3 (i.e. 6.0 Gb/s) */
|
||||
#define SCIC_SDS_PARM_GEN3_SPEED 3
|
||||
#define SCIC_SDS_PARM_MAX_SPEED SCIC_SDS_PARM_GEN3_SPEED
|
||||
|
||||
/* parameters that can be set by module parameters */
|
||||
struct sci_user_parameters {
|
||||
struct sci_phy_user_params {
|
||||
/**
|
||||
* This field specifies the NOTIFY (ENABLE SPIN UP) primitive
|
||||
* insertion frequency for this phy index.
|
||||
*/
|
||||
u32 notify_enable_spin_up_insertion_frequency;
|
||||
|
||||
/**
|
||||
* This method specifies the number of transmitted DWORDs within which
|
||||
* to transmit a single ALIGN primitive. This value applies regardless
|
||||
* of what type of device is attached or connection state. A value of
|
||||
* 0 indicates that no ALIGN primitives will be inserted.
|
||||
*/
|
||||
u16 align_insertion_frequency;
|
||||
|
||||
/**
|
||||
* This method specifies the number of transmitted DWORDs within which
|
||||
* to transmit 2 ALIGN primitives. This applies for SAS connections
|
||||
* only. A minimum value of 3 is required for this field.
|
||||
*/
|
||||
u16 in_connection_align_insertion_frequency;
|
||||
|
||||
/**
|
||||
* This field indicates the maximum speed generation to be utilized
|
||||
* by phys in the supplied port.
|
||||
* - A value of 1 indicates generation 1 (i.e. 1.5 Gb/s).
|
||||
* - A value of 2 indicates generation 2 (i.e. 3.0 Gb/s).
|
||||
* - A value of 3 indicates generation 3 (i.e. 6.0 Gb/s).
|
||||
*/
|
||||
u8 max_speed_generation;
|
||||
|
||||
} phys[SCI_MAX_PHYS];
|
||||
|
||||
/**
|
||||
* This field specifies the maximum number of direct attached devices
|
||||
* that can have power supplied to them simultaneously.
|
||||
*/
|
||||
u8 max_concurr_spinup;
|
||||
|
||||
/**
|
||||
* This field specifies the number of seconds to allow a phy to consume
|
||||
* power before yielding to another phy.
|
||||
*
|
||||
*/
|
||||
u8 phy_spin_up_delay_interval;
|
||||
|
||||
/**
|
||||
* These timer values specifies how long a link will remain open with no
|
||||
* activity in increments of a microsecond, it can be in increments of
|
||||
* 100 microseconds if the upper most bit is set.
|
||||
*
|
||||
*/
|
||||
u16 stp_inactivity_timeout;
|
||||
u16 ssp_inactivity_timeout;
|
||||
|
||||
/**
|
||||
* These timer values specifies how long a link will remain open in increments
|
||||
* of 100 microseconds.
|
||||
*
|
||||
*/
|
||||
u16 stp_max_occupancy_timeout;
|
||||
u16 ssp_max_occupancy_timeout;
|
||||
|
||||
/**
|
||||
* This timer value specifies how long a link will remain open with no
|
||||
* outbound traffic in increments of a microsecond.
|
||||
*
|
||||
*/
|
||||
u8 no_outbound_task_timeout;
|
||||
|
||||
};
|
||||
|
||||
#define SCIC_SDS_PARM_PHY_MASK_MIN 0x0
|
||||
#define SCIC_SDS_PARM_PHY_MASK_MAX 0xF
|
||||
#define MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT 4
|
||||
|
||||
struct sci_oem_params;
|
||||
int sci_oem_parameters_validate(struct sci_oem_params *oem, u8 version);
|
||||
|
||||
struct isci_orom;
|
||||
struct isci_orom *isci_request_oprom(struct pci_dev *pdev);
|
||||
struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw);
|
||||
struct isci_orom *isci_get_efi_var(struct pci_dev *pdev);
|
||||
|
||||
struct isci_oem_hdr {
|
||||
u8 sig[4];
|
||||
u8 rev_major;
|
||||
u8 rev_minor;
|
||||
u16 len;
|
||||
u8 checksum;
|
||||
u8 reserved1;
|
||||
u16 reserved2;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#else
|
||||
#define SCI_MAX_PORTS 4
|
||||
#define SCI_MAX_PHYS 4
|
||||
#define SCI_MAX_CONTROLLERS 2
|
||||
#endif
|
||||
|
||||
#define ISCI_FW_NAME "isci/isci_firmware.bin"
|
||||
|
||||
#define ROMSIGNATURE 0xaa55
|
||||
|
||||
#define ISCI_OEM_SIG "$OEM"
|
||||
#define ISCI_OEM_SIG_SIZE 4
|
||||
#define ISCI_ROM_SIG "ISCUOEMB"
|
||||
#define ISCI_ROM_SIG_SIZE 8
|
||||
|
||||
#define ISCI_EFI_VENDOR_GUID \
|
||||
EFI_GUID(0x193dfefa, 0xa445, 0x4302, 0x99, 0xd8, 0xef, 0x3a, 0xad, \
|
||||
0x1a, 0x04, 0xc6)
|
||||
#define ISCI_EFI_VAR_NAME "RstScuO"
|
||||
|
||||
#define ISCI_ROM_VER_1_0 0x10
|
||||
#define ISCI_ROM_VER_1_1 0x11
|
||||
#define ISCI_ROM_VER_1_3 0x13
|
||||
#define ISCI_ROM_VER_LATEST ISCI_ROM_VER_1_3
|
||||
|
||||
/* Allowed PORT configuration modes APC Automatic PORT configuration mode is
|
||||
* defined by the OEM configuration parameters providing no PHY_MASK parameters
|
||||
* for any PORT. i.e. There are no phys assigned to any of the ports at start.
|
||||
* MPC Manual PORT configuration mode is defined by the OEM configuration
|
||||
* parameters providing a PHY_MASK value for any PORT. It is assumed that any
|
||||
* PORT with no PHY_MASK is an invalid port and not all PHYs must be assigned.
|
||||
* A PORT_PHY mask that assigns just a single PHY to a port and no other PHYs
|
||||
* being assigned is sufficient to declare manual PORT configuration.
|
||||
*/
|
||||
enum sci_port_configuration_mode {
|
||||
SCIC_PORT_MANUAL_CONFIGURATION_MODE = 0,
|
||||
SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE = 1
|
||||
};
|
||||
|
||||
struct sci_bios_oem_param_block_hdr {
|
||||
uint8_t signature[ISCI_ROM_SIG_SIZE];
|
||||
uint16_t total_block_length;
|
||||
uint8_t hdr_length;
|
||||
uint8_t version;
|
||||
uint8_t preboot_source;
|
||||
uint8_t num_elements;
|
||||
uint16_t element_length;
|
||||
uint8_t reserved[8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct sci_oem_params {
|
||||
struct {
|
||||
uint8_t mode_type;
|
||||
uint8_t max_concurr_spin_up;
|
||||
/*
|
||||
* This bitfield indicates the OEM's desired default Tx
|
||||
* Spread Spectrum Clocking (SSC) settings for SATA and SAS.
|
||||
* NOTE: Default SSC Modulation Frequency is 31.5KHz.
|
||||
*/
|
||||
union {
|
||||
struct {
|
||||
/*
|
||||
* NOTE: Max spread for SATA is +0 / -5000 PPM.
|
||||
* Down-spreading SSC (only method allowed for SATA):
|
||||
* SATA SSC Tx Disabled = 0x0
|
||||
* SATA SSC Tx at +0 / -1419 PPM Spread = 0x2
|
||||
* SATA SSC Tx at +0 / -2129 PPM Spread = 0x3
|
||||
* SATA SSC Tx at +0 / -4257 PPM Spread = 0x6
|
||||
* SATA SSC Tx at +0 / -4967 PPM Spread = 0x7
|
||||
*/
|
||||
uint8_t ssc_sata_tx_spread_level:4;
|
||||
/*
|
||||
* SAS SSC Tx Disabled = 0x0
|
||||
*
|
||||
* NOTE: Max spread for SAS down-spreading +0 /
|
||||
* -2300 PPM
|
||||
* Down-spreading SSC:
|
||||
* SAS SSC Tx at +0 / -1419 PPM Spread = 0x2
|
||||
* SAS SSC Tx at +0 / -2129 PPM Spread = 0x3
|
||||
*
|
||||
* NOTE: Max spread for SAS center-spreading +2300 /
|
||||
* -2300 PPM
|
||||
* Center-spreading SSC:
|
||||
* SAS SSC Tx at +1064 / -1064 PPM Spread = 0x3
|
||||
* SAS SSC Tx at +2129 / -2129 PPM Spread = 0x6
|
||||
*/
|
||||
uint8_t ssc_sas_tx_spread_level:3;
|
||||
/*
|
||||
* NOTE: Refer to the SSC section of the SAS 2.x
|
||||
* Specification for proper setting of this field.
|
||||
* For standard SAS Initiator SAS PHY operation it
|
||||
* should be 0 for Down-spreading.
|
||||
* SAS SSC Tx spread type:
|
||||
* Down-spreading SSC = 0
|
||||
* Center-spreading SSC = 1
|
||||
*/
|
||||
uint8_t ssc_sas_tx_type:1;
|
||||
};
|
||||
uint8_t do_enable_ssc;
|
||||
};
|
||||
/*
|
||||
* This field indicates length of the SAS/SATA cable between
|
||||
* host and device.
|
||||
* This field is used make relationship between analog
|
||||
* parameters of the phy in the silicon and length of the cable.
|
||||
* Supported cable attenuation levels:
|
||||
* "short"- up to 3m, "medium"-3m to 6m, and "long"- more than
|
||||
* 6m.
|
||||
*
|
||||
* This is bit mask field:
|
||||
*
|
||||
* BIT: (MSB) 7 6 5 4
|
||||
* ASSIGNMENT: <phy3><phy2><phy1><phy0> - Medium cable
|
||||
* length assignment
|
||||
* BIT: 3 2 1 0 (LSB)
|
||||
* ASSIGNMENT: <phy3><phy2><phy1><phy0> - Long cable length
|
||||
* assignment
|
||||
*
|
||||
* BITS 7-4 are set when the cable length is assigned to medium
|
||||
* BITS 3-0 are set when the cable length is assigned to long
|
||||
*
|
||||
* The BIT positions are clear when the cable length is
|
||||
* assigned to short.
|
||||
*
|
||||
* Setting the bits for both long and medium cable length is
|
||||
* undefined.
|
||||
*
|
||||
* A value of 0x84 would assign
|
||||
* phy3 - medium
|
||||
* phy2 - long
|
||||
* phy1 - short
|
||||
* phy0 - short
|
||||
*/
|
||||
uint8_t cable_selection_mask;
|
||||
} controller;
|
||||
|
||||
struct {
|
||||
uint8_t phy_mask;
|
||||
} ports[SCI_MAX_PORTS];
|
||||
|
||||
struct sci_phy_oem_params {
|
||||
struct {
|
||||
uint32_t high;
|
||||
uint32_t low;
|
||||
} sas_address;
|
||||
|
||||
uint32_t afe_tx_amp_control0;
|
||||
uint32_t afe_tx_amp_control1;
|
||||
uint32_t afe_tx_amp_control2;
|
||||
uint32_t afe_tx_amp_control3;
|
||||
} phys[SCI_MAX_PHYS];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct isci_orom {
|
||||
struct sci_bios_oem_param_block_hdr hdr;
|
||||
struct sci_oem_params ctrl[SCI_MAX_CONTROLLERS];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif
|
1863
drivers/scsi/isci/registers.h
Normal file
1863
drivers/scsi/isci/registers.h
Normal file
File diff suppressed because it is too large
Load diff
1726
drivers/scsi/isci/remote_device.c
Normal file
1726
drivers/scsi/isci/remote_device.c
Normal file
File diff suppressed because it is too large
Load diff
387
drivers/scsi/isci/remote_device.h
Normal file
387
drivers/scsi/isci/remote_device.h
Normal file
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _ISCI_REMOTE_DEVICE_H_
|
||||
#define _ISCI_REMOTE_DEVICE_H_
|
||||
#include <scsi/libsas.h>
|
||||
#include <linux/kref.h>
|
||||
#include "scu_remote_node_context.h"
|
||||
#include "remote_node_context.h"
|
||||
#include "port.h"
|
||||
|
||||
enum sci_remote_device_not_ready_reason_code {
|
||||
SCIC_REMOTE_DEVICE_NOT_READY_START_REQUESTED,
|
||||
SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED,
|
||||
SCIC_REMOTE_DEVICE_NOT_READY_SATA_REQUEST_STARTED,
|
||||
SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED,
|
||||
SCIC_REMOTE_DEVICE_NOT_READY_SMP_REQUEST_STARTED,
|
||||
SCIC_REMOTE_DEVICE_NOT_READY_REASON_CODE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* isci_remote_device - isci representation of a sas expander / end point
|
||||
* @device_port_width: hw setting for number of simultaneous connections
|
||||
* @connection_rate: per-taskcontext connection rate for this device
|
||||
* @working_request: SATA requests have no tag we for unaccelerated
|
||||
* protocols we need a method to associate unsolicited
|
||||
* frames with a pending request
|
||||
*/
|
||||
struct isci_remote_device {
|
||||
#define IDEV_START_PENDING 0
|
||||
#define IDEV_STOP_PENDING 1
|
||||
#define IDEV_ALLOCATED 2
|
||||
#define IDEV_GONE 3
|
||||
#define IDEV_IO_READY 4
|
||||
#define IDEV_IO_NCQERROR 5
|
||||
#define IDEV_RNC_LLHANG_ENABLED 6
|
||||
#define IDEV_ABORT_PATH_ACTIVE 7
|
||||
#define IDEV_ABORT_PATH_RESUME_PENDING 8
|
||||
unsigned long flags;
|
||||
struct kref kref;
|
||||
struct isci_port *isci_port;
|
||||
struct domain_device *domain_dev;
|
||||
struct list_head node;
|
||||
struct sci_base_state_machine sm;
|
||||
u32 device_port_width;
|
||||
enum sas_linkrate connection_rate;
|
||||
struct isci_port *owning_port;
|
||||
struct sci_remote_node_context rnc;
|
||||
/* XXX unify with device reference counting and delete */
|
||||
u32 started_request_count;
|
||||
struct isci_request *working_request;
|
||||
u32 not_ready_reason;
|
||||
scics_sds_remote_node_context_callback abort_resume_cb;
|
||||
void *abort_resume_cbparam;
|
||||
};
|
||||
|
||||
#define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000
|
||||
|
||||
/* device reference routines must be called under sci_lock */
|
||||
static inline struct isci_remote_device *isci_get_device(
|
||||
struct isci_remote_device *idev)
|
||||
{
|
||||
if (idev)
|
||||
kref_get(&idev->kref);
|
||||
return idev;
|
||||
}
|
||||
|
||||
static inline struct isci_remote_device *isci_lookup_device(struct domain_device *dev)
|
||||
{
|
||||
struct isci_remote_device *idev = dev->lldd_dev;
|
||||
|
||||
if (idev && !test_bit(IDEV_GONE, &idev->flags)) {
|
||||
kref_get(&idev->kref);
|
||||
return idev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void isci_remote_device_release(struct kref *kref);
|
||||
static inline void isci_put_device(struct isci_remote_device *idev)
|
||||
{
|
||||
if (idev)
|
||||
kref_put(&idev->kref, isci_remote_device_release);
|
||||
}
|
||||
|
||||
enum sci_status isci_remote_device_stop(struct isci_host *ihost,
|
||||
struct isci_remote_device *idev);
|
||||
void isci_remote_device_nuke_requests(struct isci_host *ihost,
|
||||
struct isci_remote_device *idev);
|
||||
void isci_remote_device_gone(struct domain_device *domain_dev);
|
||||
int isci_remote_device_found(struct domain_device *domain_dev);
|
||||
|
||||
/**
|
||||
* sci_remote_device_stop() - This method will stop both transmission and
|
||||
* reception of link activity for the supplied remote device. This method
|
||||
* disables normal IO requests from flowing through to the remote device.
|
||||
* @remote_device: This parameter specifies the device to be stopped.
|
||||
* @timeout: This parameter specifies the number of milliseconds in which the
|
||||
* stop operation should complete.
|
||||
*
|
||||
* An indication of whether the device was successfully stopped. SCI_SUCCESS
|
||||
* This value is returned if the transmission and reception for the device was
|
||||
* successfully stopped.
|
||||
*/
|
||||
enum sci_status sci_remote_device_stop(
|
||||
struct isci_remote_device *idev,
|
||||
u32 timeout);
|
||||
|
||||
/**
|
||||
* sci_remote_device_reset() - This method will reset the device making it
|
||||
* ready for operation. This method must be called anytime the device is
|
||||
* reset either through a SMP phy control or a port hard reset request.
|
||||
* @remote_device: This parameter specifies the device to be reset.
|
||||
*
|
||||
* This method does not actually cause the device hardware to be reset. This
|
||||
* method resets the software object so that it will be operational after a
|
||||
* device hardware reset completes. An indication of whether the device reset
|
||||
* was accepted. SCI_SUCCESS This value is returned if the device reset is
|
||||
* started.
|
||||
*/
|
||||
enum sci_status sci_remote_device_reset(
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
/**
|
||||
* sci_remote_device_reset_complete() - This method informs the device object
|
||||
* that the reset operation is complete and the device can resume operation
|
||||
* again.
|
||||
* @remote_device: This parameter specifies the device which is to be informed
|
||||
* of the reset complete operation.
|
||||
*
|
||||
* An indication that the device is resuming operation. SCI_SUCCESS the device
|
||||
* is resuming operation.
|
||||
*/
|
||||
enum sci_status sci_remote_device_reset_complete(
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
/**
|
||||
* enum sci_remote_device_states - This enumeration depicts all the states
|
||||
* for the common remote device state machine.
|
||||
* @SCI_DEV_INITIAL: Simply the initial state for the base remote device
|
||||
* state machine.
|
||||
*
|
||||
* @SCI_DEV_STOPPED: This state indicates that the remote device has
|
||||
* successfully been stopped. In this state no new IO operations are
|
||||
* permitted. This state is entered from the INITIAL state. This state
|
||||
* is entered from the STOPPING state.
|
||||
*
|
||||
* @SCI_DEV_STARTING: This state indicates the the remote device is in
|
||||
* the process of becoming ready (i.e. starting). In this state no new
|
||||
* IO operations are permitted. This state is entered from the STOPPED
|
||||
* state.
|
||||
*
|
||||
* @SCI_DEV_READY: This state indicates the remote device is now ready.
|
||||
* Thus, the user is able to perform IO operations on the remote device.
|
||||
* This state is entered from the STARTING state.
|
||||
*
|
||||
* @SCI_STP_DEV_IDLE: This is the idle substate for the stp remote
|
||||
* device. When there are no active IO for the device it is is in this
|
||||
* state.
|
||||
*
|
||||
* @SCI_STP_DEV_CMD: This is the command state for for the STP remote
|
||||
* device. This state is entered when the device is processing a
|
||||
* non-NCQ command. The device object will fail any new start IO
|
||||
* requests until this command is complete.
|
||||
*
|
||||
* @SCI_STP_DEV_NCQ: This is the NCQ state for the STP remote device.
|
||||
* This state is entered when the device is processing an NCQ reuqest.
|
||||
* It will remain in this state so long as there is one or more NCQ
|
||||
* requests being processed.
|
||||
*
|
||||
* @SCI_STP_DEV_NCQ_ERROR: This is the NCQ error state for the STP
|
||||
* remote device. This state is entered when an SDB error FIS is
|
||||
* received by the device object while in the NCQ state. The device
|
||||
* object will only accept a READ LOG command while in this state.
|
||||
*
|
||||
* @SCI_STP_DEV_ATAPI_ERROR: This is the ATAPI error state for the STP
|
||||
* ATAPI remote device. This state is entered when ATAPI device sends
|
||||
* error status FIS without data while the device object is in CMD
|
||||
* state. A suspension event is expected in this state. The device
|
||||
* object will resume right away.
|
||||
*
|
||||
* @SCI_STP_DEV_AWAIT_RESET: This is the READY substate indicates the
|
||||
* device is waiting for the RESET task coming to be recovered from
|
||||
* certain hardware specific error.
|
||||
*
|
||||
* @SCI_SMP_DEV_IDLE: This is the ready operational substate for the
|
||||
* remote device. This is the normal operational state for a remote
|
||||
* device.
|
||||
*
|
||||
* @SCI_SMP_DEV_CMD: This is the suspended state for the remote device.
|
||||
* This is the state that the device is placed in when a RNC suspend is
|
||||
* received by the SCU hardware.
|
||||
*
|
||||
* @SCI_DEV_STOPPING: This state indicates that the remote device is in
|
||||
* the process of stopping. In this state no new IO operations are
|
||||
* permitted, but existing IO operations are allowed to complete. This
|
||||
* state is entered from the READY state. This state is entered from
|
||||
* the FAILED state.
|
||||
*
|
||||
* @SCI_DEV_FAILED: This state indicates that the remote device has
|
||||
* failed. In this state no new IO operations are permitted. This
|
||||
* state is entered from the INITIALIZING state. This state is entered
|
||||
* from the READY state.
|
||||
*
|
||||
* @SCI_DEV_RESETTING: This state indicates the device is being reset.
|
||||
* In this state no new IO operations are permitted. This state is
|
||||
* entered from the READY state.
|
||||
*
|
||||
* @SCI_DEV_FINAL: Simply the final state for the base remote device
|
||||
* state machine.
|
||||
*/
|
||||
#define REMOTE_DEV_STATES {\
|
||||
C(DEV_INITIAL),\
|
||||
C(DEV_STOPPED),\
|
||||
C(DEV_STARTING),\
|
||||
C(DEV_READY),\
|
||||
C(STP_DEV_IDLE),\
|
||||
C(STP_DEV_CMD),\
|
||||
C(STP_DEV_NCQ),\
|
||||
C(STP_DEV_NCQ_ERROR),\
|
||||
C(STP_DEV_ATAPI_ERROR),\
|
||||
C(STP_DEV_AWAIT_RESET),\
|
||||
C(SMP_DEV_IDLE),\
|
||||
C(SMP_DEV_CMD),\
|
||||
C(DEV_STOPPING),\
|
||||
C(DEV_FAILED),\
|
||||
C(DEV_RESETTING),\
|
||||
C(DEV_FINAL),\
|
||||
}
|
||||
#undef C
|
||||
#define C(a) SCI_##a
|
||||
enum sci_remote_device_states REMOTE_DEV_STATES;
|
||||
#undef C
|
||||
const char *dev_state_name(enum sci_remote_device_states state);
|
||||
|
||||
static inline struct isci_remote_device *rnc_to_dev(struct sci_remote_node_context *rnc)
|
||||
{
|
||||
struct isci_remote_device *idev;
|
||||
|
||||
idev = container_of(rnc, typeof(*idev), rnc);
|
||||
|
||||
return idev;
|
||||
}
|
||||
|
||||
static inline bool dev_is_expander(struct domain_device *dev)
|
||||
{
|
||||
return dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE;
|
||||
}
|
||||
|
||||
static inline void sci_remote_device_decrement_request_count(struct isci_remote_device *idev)
|
||||
{
|
||||
/* XXX delete this voodoo when converting to the top-level device
|
||||
* reference count
|
||||
*/
|
||||
if (WARN_ONCE(idev->started_request_count == 0,
|
||||
"%s: tried to decrement started_request_count past 0!?",
|
||||
__func__))
|
||||
/* pass */;
|
||||
else
|
||||
idev->started_request_count--;
|
||||
}
|
||||
|
||||
void isci_dev_set_hang_detection_timeout(struct isci_remote_device *idev, u32 timeout);
|
||||
|
||||
enum sci_status sci_remote_device_frame_handler(
|
||||
struct isci_remote_device *idev,
|
||||
u32 frame_index);
|
||||
|
||||
enum sci_status sci_remote_device_event_handler(
|
||||
struct isci_remote_device *idev,
|
||||
u32 event_code);
|
||||
|
||||
enum sci_status sci_remote_device_start_io(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
enum sci_status sci_remote_device_start_task(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
enum sci_status sci_remote_device_complete_io(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
void sci_remote_device_post_request(
|
||||
struct isci_remote_device *idev,
|
||||
u32 request);
|
||||
|
||||
enum sci_status sci_remote_device_terminate_requests(
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
int isci_remote_device_is_safe_to_abort(
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
enum sci_status
|
||||
sci_remote_device_abort_requests_pending_abort(
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
enum sci_status isci_remote_device_suspend(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
enum sci_status sci_remote_device_resume(
|
||||
struct isci_remote_device *idev,
|
||||
scics_sds_remote_node_context_callback cb_fn,
|
||||
void *cb_p);
|
||||
|
||||
enum sci_status isci_remote_device_resume_from_abort(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
enum sci_status isci_remote_device_reset(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
enum sci_status isci_remote_device_reset_complete(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev);
|
||||
|
||||
enum sci_status isci_remote_device_suspend_terminate(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
|
||||
enum sci_status isci_remote_device_terminate_requests(
|
||||
struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_request *ireq);
|
||||
enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev,
|
||||
enum sci_remote_node_suspension_reasons reason);
|
||||
#endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */
|
809
drivers/scsi/isci/remote_node_context.c
Normal file
809
drivers/scsi/isci/remote_node_context.c
Normal file
|
@ -0,0 +1,809 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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 <scsi/sas_ata.h>
|
||||
#include "host.h"
|
||||
#include "isci.h"
|
||||
#include "remote_device.h"
|
||||
#include "remote_node_context.h"
|
||||
#include "scu_event_codes.h"
|
||||
#include "scu_task_context.h"
|
||||
|
||||
#undef C
|
||||
#define C(a) (#a)
|
||||
const char *rnc_state_name(enum scis_sds_remote_node_context_states state)
|
||||
{
|
||||
static const char * const strings[] = RNC_STATES;
|
||||
|
||||
return strings[state];
|
||||
}
|
||||
#undef C
|
||||
|
||||
/**
|
||||
*
|
||||
* @sci_rnc: The state of the remote node context object to check.
|
||||
*
|
||||
* This method will return true if the remote node context is in a READY state
|
||||
* otherwise it will return false bool true if the remote node context is in
|
||||
* the ready state. false if the remote node context is not in the ready state.
|
||||
*/
|
||||
bool sci_remote_node_context_is_ready(
|
||||
struct sci_remote_node_context *sci_rnc)
|
||||
{
|
||||
u32 current_state = sci_rnc->sm.current_state_id;
|
||||
|
||||
if (current_state == SCI_RNC_READY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc)
|
||||
{
|
||||
u32 current_state = sci_rnc->sm.current_state_id;
|
||||
|
||||
if (current_state == SCI_RNC_TX_RX_SUSPENDED)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static union scu_remote_node_context *sci_rnc_by_id(struct isci_host *ihost, u16 id)
|
||||
{
|
||||
if (id < ihost->remote_node_entries &&
|
||||
ihost->device_table[id])
|
||||
return &ihost->remote_node_context_table[id];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_construct_buffer(struct sci_remote_node_context *sci_rnc)
|
||||
{
|
||||
struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
|
||||
struct domain_device *dev = idev->domain_dev;
|
||||
int rni = sci_rnc->remote_node_index;
|
||||
union scu_remote_node_context *rnc;
|
||||
struct isci_host *ihost;
|
||||
__le64 sas_addr;
|
||||
|
||||
ihost = idev->owning_port->owning_controller;
|
||||
rnc = sci_rnc_by_id(ihost, rni);
|
||||
|
||||
memset(rnc, 0, sizeof(union scu_remote_node_context)
|
||||
* sci_remote_device_node_count(idev));
|
||||
|
||||
rnc->ssp.remote_node_index = rni;
|
||||
rnc->ssp.remote_node_port_width = idev->device_port_width;
|
||||
rnc->ssp.logical_port_index = idev->owning_port->physical_port_index;
|
||||
|
||||
/* sas address is __be64, context ram format is __le64 */
|
||||
sas_addr = cpu_to_le64(SAS_ADDR(dev->sas_addr));
|
||||
rnc->ssp.remote_sas_address_hi = upper_32_bits(sas_addr);
|
||||
rnc->ssp.remote_sas_address_lo = lower_32_bits(sas_addr);
|
||||
|
||||
rnc->ssp.nexus_loss_timer_enable = true;
|
||||
rnc->ssp.check_bit = false;
|
||||
rnc->ssp.is_valid = false;
|
||||
rnc->ssp.is_remote_node_context = true;
|
||||
rnc->ssp.function_number = 0;
|
||||
|
||||
rnc->ssp.arbitration_wait_time = 0;
|
||||
|
||||
if (dev_is_sata(dev)) {
|
||||
rnc->ssp.connection_occupancy_timeout =
|
||||
ihost->user_parameters.stp_max_occupancy_timeout;
|
||||
rnc->ssp.connection_inactivity_timeout =
|
||||
ihost->user_parameters.stp_inactivity_timeout;
|
||||
} else {
|
||||
rnc->ssp.connection_occupancy_timeout =
|
||||
ihost->user_parameters.ssp_max_occupancy_timeout;
|
||||
rnc->ssp.connection_inactivity_timeout =
|
||||
ihost->user_parameters.ssp_inactivity_timeout;
|
||||
}
|
||||
|
||||
rnc->ssp.initial_arbitration_wait_time = 0;
|
||||
|
||||
/* Open Address Frame Parameters */
|
||||
rnc->ssp.oaf_connection_rate = idev->connection_rate;
|
||||
rnc->ssp.oaf_features = 0;
|
||||
rnc->ssp.oaf_source_zone_group = 0;
|
||||
rnc->ssp.oaf_more_compatibility_features = 0;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @sci_rnc:
|
||||
* @callback:
|
||||
* @callback_parameter:
|
||||
*
|
||||
* This method will setup the remote node context object so it will transition
|
||||
* to its ready state. If the remote node context is already setup to
|
||||
* transition to its final state then this function does nothing. none
|
||||
*/
|
||||
static void sci_remote_node_context_setup_to_resume(
|
||||
struct sci_remote_node_context *sci_rnc,
|
||||
scics_sds_remote_node_context_callback callback,
|
||||
void *callback_parameter,
|
||||
enum sci_remote_node_context_destination_state dest_param)
|
||||
{
|
||||
if (sci_rnc->destination_state != RNC_DEST_FINAL) {
|
||||
sci_rnc->destination_state = dest_param;
|
||||
if (callback != NULL) {
|
||||
sci_rnc->user_callback = callback;
|
||||
sci_rnc->user_cookie = callback_parameter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_setup_to_destroy(
|
||||
struct sci_remote_node_context *sci_rnc,
|
||||
scics_sds_remote_node_context_callback callback,
|
||||
void *callback_parameter)
|
||||
{
|
||||
struct isci_host *ihost = idev_to_ihost(rnc_to_dev(sci_rnc));
|
||||
|
||||
sci_rnc->destination_state = RNC_DEST_FINAL;
|
||||
sci_rnc->user_callback = callback;
|
||||
sci_rnc->user_cookie = callback_parameter;
|
||||
|
||||
wake_up(&ihost->eventq);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This method just calls the user callback function and then resets the
|
||||
* callback.
|
||||
*/
|
||||
static void sci_remote_node_context_notify_user(
|
||||
struct sci_remote_node_context *rnc)
|
||||
{
|
||||
if (rnc->user_callback != NULL) {
|
||||
(*rnc->user_callback)(rnc->user_cookie);
|
||||
|
||||
rnc->user_callback = NULL;
|
||||
rnc->user_cookie = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_continue_state_transitions(struct sci_remote_node_context *rnc)
|
||||
{
|
||||
switch (rnc->destination_state) {
|
||||
case RNC_DEST_READY:
|
||||
case RNC_DEST_SUSPENDED_RESUME:
|
||||
rnc->destination_state = RNC_DEST_READY;
|
||||
/* Fall through... */
|
||||
case RNC_DEST_FINAL:
|
||||
sci_remote_node_context_resume(rnc, rnc->user_callback,
|
||||
rnc->user_cookie);
|
||||
break;
|
||||
default:
|
||||
rnc->destination_state = RNC_DEST_UNSPECIFIED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_validate_context_buffer(struct sci_remote_node_context *sci_rnc)
|
||||
{
|
||||
union scu_remote_node_context *rnc_buffer;
|
||||
struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
|
||||
struct domain_device *dev = idev->domain_dev;
|
||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||
|
||||
rnc_buffer = sci_rnc_by_id(ihost, sci_rnc->remote_node_index);
|
||||
|
||||
rnc_buffer->ssp.is_valid = true;
|
||||
|
||||
if (dev_is_sata(dev) && dev->parent) {
|
||||
sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_96);
|
||||
} else {
|
||||
sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_32);
|
||||
|
||||
if (!dev->parent)
|
||||
sci_port_setup_transports(idev->owning_port,
|
||||
sci_rnc->remote_node_index);
|
||||
}
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_invalidate_context_buffer(struct sci_remote_node_context *sci_rnc)
|
||||
{
|
||||
union scu_remote_node_context *rnc_buffer;
|
||||
struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
|
||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||
|
||||
rnc_buffer = sci_rnc_by_id(ihost, sci_rnc->remote_node_index);
|
||||
|
||||
rnc_buffer->ssp.is_valid = false;
|
||||
|
||||
sci_remote_device_post_request(rnc_to_dev(sci_rnc),
|
||||
SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE);
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_initial_state_enter(struct sci_base_state_machine *sm)
|
||||
{
|
||||
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
|
||||
struct isci_remote_device *idev = rnc_to_dev(rnc);
|
||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||
|
||||
/* Check to see if we have gotten back to the initial state because
|
||||
* someone requested to destroy the remote node context object.
|
||||
*/
|
||||
if (sm->previous_state_id == SCI_RNC_INVALIDATING) {
|
||||
rnc->destination_state = RNC_DEST_UNSPECIFIED;
|
||||
sci_remote_node_context_notify_user(rnc);
|
||||
|
||||
smp_wmb();
|
||||
wake_up(&ihost->eventq);
|
||||
}
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_posting_state_enter(struct sci_base_state_machine *sm)
|
||||
{
|
||||
struct sci_remote_node_context *sci_rnc = container_of(sm, typeof(*sci_rnc), sm);
|
||||
|
||||
sci_remote_node_context_validate_context_buffer(sci_rnc);
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_invalidating_state_enter(struct sci_base_state_machine *sm)
|
||||
{
|
||||
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
|
||||
|
||||
/* Terminate all outstanding requests. */
|
||||
sci_remote_device_terminate_requests(rnc_to_dev(rnc));
|
||||
sci_remote_node_context_invalidate_context_buffer(rnc);
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_machine *sm)
|
||||
{
|
||||
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
|
||||
struct isci_remote_device *idev;
|
||||
struct domain_device *dev;
|
||||
|
||||
idev = rnc_to_dev(rnc);
|
||||
dev = idev->domain_dev;
|
||||
|
||||
/*
|
||||
* For direct attached SATA devices we need to clear the TLCR
|
||||
* NCQ to TCi tag mapping on the phy and in cases where we
|
||||
* resume because of a target reset we also need to update
|
||||
* the STPTLDARNI register with the RNi of the device
|
||||
*/
|
||||
if (dev_is_sata(dev) && !dev->parent)
|
||||
sci_port_setup_transports(idev->owning_port, rnc->remote_node_index);
|
||||
|
||||
sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_RESUME);
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_ready_state_enter(struct sci_base_state_machine *sm)
|
||||
{
|
||||
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
|
||||
enum sci_remote_node_context_destination_state dest_select;
|
||||
int tell_user = 1;
|
||||
|
||||
dest_select = rnc->destination_state;
|
||||
rnc->destination_state = RNC_DEST_UNSPECIFIED;
|
||||
|
||||
if ((dest_select == RNC_DEST_SUSPENDED) ||
|
||||
(dest_select == RNC_DEST_SUSPENDED_RESUME)) {
|
||||
sci_remote_node_context_suspend(
|
||||
rnc, rnc->suspend_reason,
|
||||
SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT);
|
||||
|
||||
if (dest_select == RNC_DEST_SUSPENDED_RESUME)
|
||||
tell_user = 0; /* Wait until ready again. */
|
||||
}
|
||||
if (tell_user)
|
||||
sci_remote_node_context_notify_user(rnc);
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_tx_suspended_state_enter(struct sci_base_state_machine *sm)
|
||||
{
|
||||
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
|
||||
|
||||
sci_remote_node_context_continue_state_transitions(rnc);
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_state_machine *sm)
|
||||
{
|
||||
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
|
||||
struct isci_remote_device *idev = rnc_to_dev(rnc);
|
||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||
u32 new_count = rnc->suspend_count + 1;
|
||||
|
||||
if (new_count == 0)
|
||||
rnc->suspend_count = 1;
|
||||
else
|
||||
rnc->suspend_count = new_count;
|
||||
smp_wmb();
|
||||
|
||||
/* Terminate outstanding requests pending abort. */
|
||||
sci_remote_device_abort_requests_pending_abort(idev);
|
||||
|
||||
wake_up(&ihost->eventq);
|
||||
sci_remote_node_context_continue_state_transitions(rnc);
|
||||
}
|
||||
|
||||
static void sci_remote_node_context_await_suspend_state_exit(
|
||||
struct sci_base_state_machine *sm)
|
||||
{
|
||||
struct sci_remote_node_context *rnc
|
||||
= container_of(sm, typeof(*rnc), sm);
|
||||
struct isci_remote_device *idev = rnc_to_dev(rnc);
|
||||
|
||||
if (dev_is_sata(idev->domain_dev))
|
||||
isci_dev_set_hang_detection_timeout(idev, 0);
|
||||
}
|
||||
|
||||
static const struct sci_base_state sci_remote_node_context_state_table[] = {
|
||||
[SCI_RNC_INITIAL] = {
|
||||
.enter_state = sci_remote_node_context_initial_state_enter,
|
||||
},
|
||||
[SCI_RNC_POSTING] = {
|
||||
.enter_state = sci_remote_node_context_posting_state_enter,
|
||||
},
|
||||
[SCI_RNC_INVALIDATING] = {
|
||||
.enter_state = sci_remote_node_context_invalidating_state_enter,
|
||||
},
|
||||
[SCI_RNC_RESUMING] = {
|
||||
.enter_state = sci_remote_node_context_resuming_state_enter,
|
||||
},
|
||||
[SCI_RNC_READY] = {
|
||||
.enter_state = sci_remote_node_context_ready_state_enter,
|
||||
},
|
||||
[SCI_RNC_TX_SUSPENDED] = {
|
||||
.enter_state = sci_remote_node_context_tx_suspended_state_enter,
|
||||
},
|
||||
[SCI_RNC_TX_RX_SUSPENDED] = {
|
||||
.enter_state = sci_remote_node_context_tx_rx_suspended_state_enter,
|
||||
},
|
||||
[SCI_RNC_AWAIT_SUSPENSION] = {
|
||||
.exit_state = sci_remote_node_context_await_suspend_state_exit,
|
||||
},
|
||||
};
|
||||
|
||||
void sci_remote_node_context_construct(struct sci_remote_node_context *rnc,
|
||||
u16 remote_node_index)
|
||||
{
|
||||
memset(rnc, 0, sizeof(struct sci_remote_node_context));
|
||||
|
||||
rnc->remote_node_index = remote_node_index;
|
||||
rnc->destination_state = RNC_DEST_UNSPECIFIED;
|
||||
|
||||
sci_init_sm(&rnc->sm, sci_remote_node_context_state_table, SCI_RNC_INITIAL);
|
||||
}
|
||||
|
||||
enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_context *sci_rnc,
|
||||
u32 event_code)
|
||||
{
|
||||
enum scis_sds_remote_node_context_states state;
|
||||
u32 next_state;
|
||||
|
||||
state = sci_rnc->sm.current_state_id;
|
||||
switch (state) {
|
||||
case SCI_RNC_POSTING:
|
||||
switch (scu_get_event_code(event_code)) {
|
||||
case SCU_EVENT_POST_RNC_COMPLETE:
|
||||
sci_change_state(&sci_rnc->sm, SCI_RNC_READY);
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case SCI_RNC_INVALIDATING:
|
||||
if (scu_get_event_code(event_code) == SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE) {
|
||||
if (sci_rnc->destination_state == RNC_DEST_FINAL)
|
||||
next_state = SCI_RNC_INITIAL;
|
||||
else
|
||||
next_state = SCI_RNC_POSTING;
|
||||
sci_change_state(&sci_rnc->sm, next_state);
|
||||
} else {
|
||||
switch (scu_get_event_type(event_code)) {
|
||||
case SCU_EVENT_TYPE_RNC_SUSPEND_TX:
|
||||
case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
|
||||
/* We really dont care if the hardware is going to suspend
|
||||
* the device since it's being invalidated anyway */
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: SCIC Remote Node Context 0x%p was "
|
||||
"suspeneded by hardware while being "
|
||||
"invalidated.\n", __func__, sci_rnc);
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SCI_RNC_RESUMING:
|
||||
if (scu_get_event_code(event_code) == SCU_EVENT_POST_RCN_RELEASE) {
|
||||
sci_change_state(&sci_rnc->sm, SCI_RNC_READY);
|
||||
} else {
|
||||
switch (scu_get_event_type(event_code)) {
|
||||
case SCU_EVENT_TYPE_RNC_SUSPEND_TX:
|
||||
case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
|
||||
/* We really dont care if the hardware is going to suspend
|
||||
* the device since it's being resumed anyway */
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: SCIC Remote Node Context 0x%p was "
|
||||
"suspeneded by hardware while being resumed.\n",
|
||||
__func__, sci_rnc);
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SCI_RNC_READY:
|
||||
switch (scu_get_event_type(event_code)) {
|
||||
case SCU_EVENT_TL_RNC_SUSPEND_TX:
|
||||
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED);
|
||||
sci_rnc->suspend_type = scu_get_event_type(event_code);
|
||||
break;
|
||||
case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
|
||||
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED);
|
||||
sci_rnc->suspend_type = scu_get_event_type(event_code);
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case SCI_RNC_AWAIT_SUSPENSION:
|
||||
switch (scu_get_event_type(event_code)) {
|
||||
case SCU_EVENT_TL_RNC_SUSPEND_TX:
|
||||
next_state = SCI_RNC_TX_SUSPENDED;
|
||||
break;
|
||||
case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
|
||||
next_state = SCI_RNC_TX_RX_SUSPENDED;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
if (sci_rnc->suspend_type == scu_get_event_type(event_code))
|
||||
sci_change_state(&sci_rnc->sm, next_state);
|
||||
break;
|
||||
default:
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: invalid state: %s\n", __func__,
|
||||
rnc_state_name(state));
|
||||
return SCI_FAILURE_INVALID_STATE;
|
||||
}
|
||||
return SCI_SUCCESS;
|
||||
|
||||
out:
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: code: %#x state: %s\n", __func__, event_code,
|
||||
rnc_state_name(state));
|
||||
return SCI_FAILURE;
|
||||
|
||||
}
|
||||
|
||||
enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context *sci_rnc,
|
||||
scics_sds_remote_node_context_callback cb_fn,
|
||||
void *cb_p)
|
||||
{
|
||||
enum scis_sds_remote_node_context_states state;
|
||||
|
||||
state = sci_rnc->sm.current_state_id;
|
||||
switch (state) {
|
||||
case SCI_RNC_INVALIDATING:
|
||||
sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
|
||||
return SCI_SUCCESS;
|
||||
case SCI_RNC_POSTING:
|
||||
case SCI_RNC_RESUMING:
|
||||
case SCI_RNC_READY:
|
||||
case SCI_RNC_TX_SUSPENDED:
|
||||
case SCI_RNC_TX_RX_SUSPENDED:
|
||||
sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
|
||||
sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
|
||||
return SCI_SUCCESS;
|
||||
case SCI_RNC_AWAIT_SUSPENSION:
|
||||
sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
|
||||
return SCI_SUCCESS;
|
||||
case SCI_RNC_INITIAL:
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: invalid state: %s\n", __func__,
|
||||
rnc_state_name(state));
|
||||
/* We have decided that the destruct request on the remote node context
|
||||
* can not fail since it is either in the initial/destroyed state or is
|
||||
* can be destroyed.
|
||||
*/
|
||||
return SCI_SUCCESS;
|
||||
default:
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: invalid state %s\n", __func__,
|
||||
rnc_state_name(state));
|
||||
return SCI_FAILURE_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
enum sci_status sci_remote_node_context_suspend(
|
||||
struct sci_remote_node_context *sci_rnc,
|
||||
enum sci_remote_node_suspension_reasons suspend_reason,
|
||||
u32 suspend_type)
|
||||
{
|
||||
enum scis_sds_remote_node_context_states state
|
||||
= sci_rnc->sm.current_state_id;
|
||||
struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
|
||||
enum sci_status status = SCI_FAILURE_INVALID_STATE;
|
||||
enum sci_remote_node_context_destination_state dest_param =
|
||||
RNC_DEST_UNSPECIFIED;
|
||||
|
||||
dev_dbg(scirdev_to_dev(idev),
|
||||
"%s: current state %s, current suspend_type %x dest state %d,"
|
||||
" arg suspend_reason %d, arg suspend_type %x",
|
||||
__func__, rnc_state_name(state), sci_rnc->suspend_type,
|
||||
sci_rnc->destination_state, suspend_reason,
|
||||
suspend_type);
|
||||
|
||||
/* Disable automatic state continuations if explicitly suspending. */
|
||||
if ((suspend_reason == SCI_HW_SUSPEND) ||
|
||||
(sci_rnc->destination_state == RNC_DEST_FINAL))
|
||||
dest_param = sci_rnc->destination_state;
|
||||
|
||||
switch (state) {
|
||||
case SCI_RNC_READY:
|
||||
break;
|
||||
case SCI_RNC_INVALIDATING:
|
||||
if (sci_rnc->destination_state == RNC_DEST_FINAL) {
|
||||
dev_warn(scirdev_to_dev(idev),
|
||||
"%s: already destroying %p\n",
|
||||
__func__, sci_rnc);
|
||||
return SCI_FAILURE_INVALID_STATE;
|
||||
}
|
||||
/* Fall through and handle like SCI_RNC_POSTING */
|
||||
case SCI_RNC_RESUMING:
|
||||
/* Fall through and handle like SCI_RNC_POSTING */
|
||||
case SCI_RNC_POSTING:
|
||||
/* Set the destination state to AWAIT - this signals the
|
||||
* entry into the SCI_RNC_READY state that a suspension
|
||||
* needs to be done immediately.
|
||||
*/
|
||||
if (sci_rnc->destination_state != RNC_DEST_FINAL)
|
||||
sci_rnc->destination_state = RNC_DEST_SUSPENDED;
|
||||
sci_rnc->suspend_type = suspend_type;
|
||||
sci_rnc->suspend_reason = suspend_reason;
|
||||
return SCI_SUCCESS;
|
||||
|
||||
case SCI_RNC_TX_SUSPENDED:
|
||||
if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX)
|
||||
status = SCI_SUCCESS;
|
||||
break;
|
||||
case SCI_RNC_TX_RX_SUSPENDED:
|
||||
if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX)
|
||||
status = SCI_SUCCESS;
|
||||
break;
|
||||
case SCI_RNC_AWAIT_SUSPENSION:
|
||||
if ((sci_rnc->suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX)
|
||||
|| (suspend_type == sci_rnc->suspend_type))
|
||||
return SCI_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: invalid state %s\n", __func__,
|
||||
rnc_state_name(state));
|
||||
return SCI_FAILURE_INVALID_STATE;
|
||||
}
|
||||
sci_rnc->destination_state = dest_param;
|
||||
sci_rnc->suspend_type = suspend_type;
|
||||
sci_rnc->suspend_reason = suspend_reason;
|
||||
|
||||
if (status == SCI_SUCCESS) { /* Already in the destination state? */
|
||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||
|
||||
wake_up_all(&ihost->eventq); /* Let observers look. */
|
||||
return SCI_SUCCESS;
|
||||
}
|
||||
if ((suspend_reason == SCI_SW_SUSPEND_NORMAL) ||
|
||||
(suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)) {
|
||||
|
||||
if (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)
|
||||
isci_dev_set_hang_detection_timeout(idev, 0x00000001);
|
||||
|
||||
sci_remote_device_post_request(
|
||||
idev, SCI_SOFTWARE_SUSPEND_CMD);
|
||||
}
|
||||
if (state != SCI_RNC_AWAIT_SUSPENSION)
|
||||
sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION);
|
||||
|
||||
return SCI_SUCCESS;
|
||||
}
|
||||
|
||||
enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *sci_rnc,
|
||||
scics_sds_remote_node_context_callback cb_fn,
|
||||
void *cb_p)
|
||||
{
|
||||
enum scis_sds_remote_node_context_states state;
|
||||
struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
|
||||
|
||||
state = sci_rnc->sm.current_state_id;
|
||||
dev_dbg(scirdev_to_dev(idev),
|
||||
"%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d; "
|
||||
"dev resume path %s\n",
|
||||
__func__, rnc_state_name(state), cb_fn, cb_p,
|
||||
sci_rnc->destination_state,
|
||||
test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)
|
||||
? "<abort active>" : "<normal>");
|
||||
|
||||
switch (state) {
|
||||
case SCI_RNC_INITIAL:
|
||||
if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX)
|
||||
return SCI_FAILURE_INVALID_STATE;
|
||||
|
||||
sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p,
|
||||
RNC_DEST_READY);
|
||||
if (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) {
|
||||
sci_remote_node_context_construct_buffer(sci_rnc);
|
||||
sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING);
|
||||
}
|
||||
return SCI_SUCCESS;
|
||||
|
||||
case SCI_RNC_POSTING:
|
||||
case SCI_RNC_INVALIDATING:
|
||||
case SCI_RNC_RESUMING:
|
||||
/* We are still waiting to post when a resume was
|
||||
* requested.
|
||||
*/
|
||||
switch (sci_rnc->destination_state) {
|
||||
case RNC_DEST_SUSPENDED:
|
||||
case RNC_DEST_SUSPENDED_RESUME:
|
||||
/* Previously waiting to suspend after posting.
|
||||
* Now continue onto resumption.
|
||||
*/
|
||||
sci_remote_node_context_setup_to_resume(
|
||||
sci_rnc, cb_fn, cb_p,
|
||||
RNC_DEST_SUSPENDED_RESUME);
|
||||
break;
|
||||
default:
|
||||
sci_remote_node_context_setup_to_resume(
|
||||
sci_rnc, cb_fn, cb_p,
|
||||
RNC_DEST_READY);
|
||||
break;
|
||||
}
|
||||
return SCI_SUCCESS;
|
||||
|
||||
case SCI_RNC_TX_SUSPENDED:
|
||||
case SCI_RNC_TX_RX_SUSPENDED:
|
||||
{
|
||||
struct domain_device *dev = idev->domain_dev;
|
||||
/* If this is an expander attached SATA device we must
|
||||
* invalidate and repost the RNC since this is the only
|
||||
* way to clear the TCi to NCQ tag mapping table for
|
||||
* the RNi. All other device types we can just resume.
|
||||
*/
|
||||
sci_remote_node_context_setup_to_resume(
|
||||
sci_rnc, cb_fn, cb_p, RNC_DEST_READY);
|
||||
|
||||
if (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) {
|
||||
if ((dev_is_sata(dev) && dev->parent) ||
|
||||
(sci_rnc->destination_state == RNC_DEST_FINAL))
|
||||
sci_change_state(&sci_rnc->sm,
|
||||
SCI_RNC_INVALIDATING);
|
||||
else
|
||||
sci_change_state(&sci_rnc->sm,
|
||||
SCI_RNC_RESUMING);
|
||||
}
|
||||
}
|
||||
return SCI_SUCCESS;
|
||||
|
||||
case SCI_RNC_AWAIT_SUSPENSION:
|
||||
sci_remote_node_context_setup_to_resume(
|
||||
sci_rnc, cb_fn, cb_p, RNC_DEST_SUSPENDED_RESUME);
|
||||
return SCI_SUCCESS;
|
||||
default:
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: invalid state %s\n", __func__,
|
||||
rnc_state_name(state));
|
||||
return SCI_FAILURE_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc,
|
||||
struct isci_request *ireq)
|
||||
{
|
||||
enum scis_sds_remote_node_context_states state;
|
||||
|
||||
state = sci_rnc->sm.current_state_id;
|
||||
|
||||
switch (state) {
|
||||
case SCI_RNC_READY:
|
||||
return SCI_SUCCESS;
|
||||
case SCI_RNC_TX_SUSPENDED:
|
||||
case SCI_RNC_TX_RX_SUSPENDED:
|
||||
case SCI_RNC_AWAIT_SUSPENSION:
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: invalid state %s\n", __func__,
|
||||
rnc_state_name(state));
|
||||
return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED;
|
||||
default:
|
||||
dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: invalid state %s\n", __func__,
|
||||
rnc_state_name(state));
|
||||
return SCI_FAILURE_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
enum sci_status sci_remote_node_context_start_task(
|
||||
struct sci_remote_node_context *sci_rnc,
|
||||
struct isci_request *ireq,
|
||||
scics_sds_remote_node_context_callback cb_fn,
|
||||
void *cb_p)
|
||||
{
|
||||
enum sci_status status = sci_remote_node_context_resume(sci_rnc,
|
||||
cb_fn, cb_p);
|
||||
if (status != SCI_SUCCESS)
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: resume failed: %d\n", __func__, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
int sci_remote_node_context_is_safe_to_abort(
|
||||
struct sci_remote_node_context *sci_rnc)
|
||||
{
|
||||
enum scis_sds_remote_node_context_states state;
|
||||
|
||||
state = sci_rnc->sm.current_state_id;
|
||||
switch (state) {
|
||||
case SCI_RNC_INVALIDATING:
|
||||
case SCI_RNC_TX_RX_SUSPENDED:
|
||||
return 1;
|
||||
case SCI_RNC_POSTING:
|
||||
case SCI_RNC_RESUMING:
|
||||
case SCI_RNC_READY:
|
||||
case SCI_RNC_TX_SUSPENDED:
|
||||
case SCI_RNC_AWAIT_SUSPENSION:
|
||||
case SCI_RNC_INITIAL:
|
||||
return 0;
|
||||
default:
|
||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||
"%s: invalid state %d\n", __func__, state);
|
||||
return 0;
|
||||
}
|
||||
}
|
236
drivers/scsi/isci/remote_node_context.h
Normal file
236
drivers/scsi/isci/remote_node_context.h
Normal file
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SCIC_SDS_REMOTE_NODE_CONTEXT_H_
|
||||
#define _SCIC_SDS_REMOTE_NODE_CONTEXT_H_
|
||||
|
||||
/**
|
||||
* This file contains the structures, constants, and prototypes associated with
|
||||
* the remote node context in the silicon. It exists to model and manage
|
||||
* the remote node context in the silicon.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "isci.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This constant represents an invalid remote device id, it is used to program
|
||||
* the STPDARNI register so the driver knows when it has received a SIGNATURE
|
||||
* FIS from the SCU.
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX 0x0FFF
|
||||
|
||||
enum sci_remote_node_suspension_reasons {
|
||||
SCI_HW_SUSPEND,
|
||||
SCI_SW_SUSPEND_NORMAL,
|
||||
SCI_SW_SUSPEND_LINKHANG_DETECT
|
||||
};
|
||||
#define SCI_SOFTWARE_SUSPEND_CMD SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX
|
||||
#define SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT SCU_EVENT_TL_RNC_SUSPEND_TX_RX
|
||||
|
||||
struct isci_request;
|
||||
struct isci_remote_device;
|
||||
struct sci_remote_node_context;
|
||||
|
||||
typedef void (*scics_sds_remote_node_context_callback)(void *);
|
||||
|
||||
/**
|
||||
* enum sci_remote_node_context_states
|
||||
* @SCI_RNC_INITIAL initial state for a remote node context. On a resume
|
||||
* request the remote node context will transition to the posting state.
|
||||
*
|
||||
* @SCI_RNC_POSTING: transition state that posts the RNi to the hardware. Once
|
||||
* the RNC is posted the remote node context will be made ready.
|
||||
*
|
||||
* @SCI_RNC_INVALIDATING: transition state that will post an RNC invalidate to
|
||||
* the hardware. Once the invalidate is complete the remote node context will
|
||||
* transition to the posting state.
|
||||
*
|
||||
* @SCI_RNC_RESUMING: transition state that will post an RNC resume to the
|
||||
* hardare. Once the event notification of resume complete is received the
|
||||
* remote node context will transition to the ready state.
|
||||
*
|
||||
* @SCI_RNC_READY: state that the remote node context must be in to accept io
|
||||
* request operations.
|
||||
*
|
||||
* @SCI_RNC_TX_SUSPENDED: state that the remote node context transitions to when
|
||||
* it gets a TX suspend notification from the hardware.
|
||||
*
|
||||
* @SCI_RNC_TX_RX_SUSPENDED: state that the remote node context transitions to
|
||||
* when it gets a TX RX suspend notification from the hardware.
|
||||
*
|
||||
* @SCI_RNC_AWAIT_SUSPENSION: wait state for the remote node context that waits
|
||||
* for a suspend notification from the hardware. This state is entered when
|
||||
* either there is a request to supend the remote node context or when there is
|
||||
* a TC completion where the remote node will be suspended by the hardware.
|
||||
*/
|
||||
#define RNC_STATES {\
|
||||
C(RNC_INITIAL),\
|
||||
C(RNC_POSTING),\
|
||||
C(RNC_INVALIDATING),\
|
||||
C(RNC_RESUMING),\
|
||||
C(RNC_READY),\
|
||||
C(RNC_TX_SUSPENDED),\
|
||||
C(RNC_TX_RX_SUSPENDED),\
|
||||
C(RNC_AWAIT_SUSPENSION),\
|
||||
}
|
||||
#undef C
|
||||
#define C(a) SCI_##a
|
||||
enum scis_sds_remote_node_context_states RNC_STATES;
|
||||
#undef C
|
||||
const char *rnc_state_name(enum scis_sds_remote_node_context_states state);
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This enumeration is used to define the end destination state for the remote
|
||||
* node context.
|
||||
*/
|
||||
enum sci_remote_node_context_destination_state {
|
||||
RNC_DEST_UNSPECIFIED,
|
||||
RNC_DEST_READY,
|
||||
RNC_DEST_FINAL,
|
||||
RNC_DEST_SUSPENDED, /* Set when suspend during post/invalidate */
|
||||
RNC_DEST_SUSPENDED_RESUME /* Set when a resume was done during posting
|
||||
* or invalidating and already suspending.
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sci_remote_node_context - This structure contains the data
|
||||
* associated with the remote node context object. The remote node context
|
||||
* (RNC) object models the the remote device information necessary to manage
|
||||
* the silicon RNC.
|
||||
*/
|
||||
struct sci_remote_node_context {
|
||||
/**
|
||||
* This field indicates the remote node index (RNI) associated with
|
||||
* this RNC.
|
||||
*/
|
||||
u16 remote_node_index;
|
||||
|
||||
/**
|
||||
* This field is the recored suspension type of the remote node
|
||||
* context suspension.
|
||||
*/
|
||||
u32 suspend_type;
|
||||
enum sci_remote_node_suspension_reasons suspend_reason;
|
||||
u32 suspend_count;
|
||||
|
||||
/**
|
||||
* This field is true if the remote node context is resuming from its current
|
||||
* state. This can cause an automatic resume on receiving a suspension
|
||||
* notification.
|
||||
*/
|
||||
enum sci_remote_node_context_destination_state destination_state;
|
||||
|
||||
/**
|
||||
* This field contains the callback function that the user requested to be
|
||||
* called when the requested state transition is complete.
|
||||
*/
|
||||
scics_sds_remote_node_context_callback user_callback;
|
||||
|
||||
/**
|
||||
* This field contains the parameter that is called when the user requested
|
||||
* state transition is completed.
|
||||
*/
|
||||
void *user_cookie;
|
||||
|
||||
/**
|
||||
* This field contains the data for the object's state machine.
|
||||
*/
|
||||
struct sci_base_state_machine sm;
|
||||
};
|
||||
|
||||
void sci_remote_node_context_construct(struct sci_remote_node_context *rnc,
|
||||
u16 remote_node_index);
|
||||
|
||||
|
||||
bool sci_remote_node_context_is_ready(
|
||||
struct sci_remote_node_context *sci_rnc);
|
||||
|
||||
bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc);
|
||||
|
||||
enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_context *sci_rnc,
|
||||
u32 event_code);
|
||||
enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context *sci_rnc,
|
||||
scics_sds_remote_node_context_callback callback,
|
||||
void *callback_parameter);
|
||||
enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc,
|
||||
enum sci_remote_node_suspension_reasons reason,
|
||||
u32 suspension_code);
|
||||
enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *sci_rnc,
|
||||
scics_sds_remote_node_context_callback cb_fn,
|
||||
void *cb_p);
|
||||
enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc,
|
||||
struct isci_request *ireq,
|
||||
scics_sds_remote_node_context_callback cb_fn,
|
||||
void *cb_p);
|
||||
enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc,
|
||||
struct isci_request *ireq);
|
||||
int sci_remote_node_context_is_safe_to_abort(
|
||||
struct sci_remote_node_context *sci_rnc);
|
||||
|
||||
static inline bool sci_remote_node_context_is_being_destroyed(
|
||||
struct sci_remote_node_context *sci_rnc)
|
||||
{
|
||||
return (sci_rnc->destination_state == RNC_DEST_FINAL)
|
||||
|| ((sci_rnc->sm.current_state_id == SCI_RNC_INITIAL)
|
||||
&& (sci_rnc->destination_state == RNC_DEST_UNSPECIFIED));
|
||||
}
|
||||
#endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */
|
598
drivers/scsi/isci/remote_node_table.c
Normal file
598
drivers/scsi/isci/remote_node_table.c
Normal file
|
@ -0,0 +1,598 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file contains the implementation of the SCIC_SDS_REMOTE_NODE_TABLE
|
||||
* public, protected, and private methods.
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include "remote_node_table.h"
|
||||
#include "remote_node_context.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node index table from which the
|
||||
* selection will be made.
|
||||
* @group_table_index: This is the index to the group table from which to
|
||||
* search for an available selection.
|
||||
*
|
||||
* This routine will find the bit position in absolute bit terms of the next 32
|
||||
* + bit position. If there are available bits in the first u32 then it is
|
||||
* just bit position. u32 This is the absolute bit position for an available
|
||||
* group.
|
||||
*/
|
||||
static u32 sci_remote_node_table_get_group_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index)
|
||||
{
|
||||
u32 dword_index;
|
||||
u32 *group_table;
|
||||
u32 bit_index;
|
||||
|
||||
group_table = remote_node_table->remote_node_groups[group_table_index];
|
||||
|
||||
for (dword_index = 0; dword_index < remote_node_table->group_array_size; dword_index++) {
|
||||
if (group_table[dword_index] != 0) {
|
||||
for (bit_index = 0; bit_index < 32; bit_index++) {
|
||||
if ((group_table[dword_index] & (1 << bit_index)) != 0) {
|
||||
return (dword_index * 32) + bit_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table This the remote node table in which to clear the
|
||||
* selector.
|
||||
* @set_index: This is the remote node selector in which the change will be
|
||||
* made.
|
||||
* @group_index: This is the bit index in the table to be modified.
|
||||
*
|
||||
* This method will clear the group index entry in the specified group index
|
||||
* table. none
|
||||
*/
|
||||
static void sci_remote_node_table_clear_group_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_index;
|
||||
u32 bit_index;
|
||||
u32 *group_table;
|
||||
|
||||
BUG_ON(group_table_index >= SCU_STP_REMOTE_NODE_COUNT);
|
||||
BUG_ON(group_index >= (u32)(remote_node_table->group_array_size * 32));
|
||||
|
||||
dword_index = group_index / 32;
|
||||
bit_index = group_index % 32;
|
||||
group_table = remote_node_table->remote_node_groups[group_table_index];
|
||||
|
||||
group_table[dword_index] = group_table[dword_index] & ~(1 << bit_index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table This the remote node table in which to set the
|
||||
* selector.
|
||||
* @group_table_index: This is the remote node selector in which the change
|
||||
* will be made.
|
||||
* @group_index: This is the bit position in the table to be modified.
|
||||
*
|
||||
* This method will set the group index bit entry in the specified gropu index
|
||||
* table. none
|
||||
*/
|
||||
static void sci_remote_node_table_set_group_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_index;
|
||||
u32 bit_index;
|
||||
u32 *group_table;
|
||||
|
||||
BUG_ON(group_table_index >= SCU_STP_REMOTE_NODE_COUNT);
|
||||
BUG_ON(group_index >= (u32)(remote_node_table->group_array_size * 32));
|
||||
|
||||
dword_index = group_index / 32;
|
||||
bit_index = group_index % 32;
|
||||
group_table = remote_node_table->remote_node_groups[group_table_index];
|
||||
|
||||
group_table[dword_index] = group_table[dword_index] | (1 << bit_index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table This is the remote node table in which to modify
|
||||
* the remote node availability.
|
||||
* @remote_node_index: This is the remote node index that is being returned to
|
||||
* the table.
|
||||
*
|
||||
* This method will set the remote to available in the remote node allocation
|
||||
* table. none
|
||||
*/
|
||||
static void sci_remote_node_table_set_node_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 slot_normalized;
|
||||
u32 slot_position;
|
||||
|
||||
BUG_ON(
|
||||
(remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD)
|
||||
<= (remote_node_index / SCU_STP_REMOTE_NODE_COUNT)
|
||||
);
|
||||
|
||||
dword_location = remote_node_index / SCIC_SDS_REMOTE_NODES_PER_DWORD;
|
||||
dword_remainder = remote_node_index % SCIC_SDS_REMOTE_NODES_PER_DWORD;
|
||||
slot_normalized = (dword_remainder / SCU_STP_REMOTE_NODE_COUNT) * sizeof(u32);
|
||||
slot_position = remote_node_index % SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
remote_node_table->available_remote_nodes[dword_location] |=
|
||||
1 << (slot_normalized + slot_position);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table This is the remote node table from which to clear
|
||||
* the available remote node bit.
|
||||
* @remote_node_index: This is the remote node index which is to be cleared
|
||||
* from the table.
|
||||
*
|
||||
* This method clears the remote node index from the table of available remote
|
||||
* nodes. none
|
||||
*/
|
||||
static void sci_remote_node_table_clear_node_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 slot_position;
|
||||
u32 slot_normalized;
|
||||
|
||||
BUG_ON(
|
||||
(remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD)
|
||||
<= (remote_node_index / SCU_STP_REMOTE_NODE_COUNT)
|
||||
);
|
||||
|
||||
dword_location = remote_node_index / SCIC_SDS_REMOTE_NODES_PER_DWORD;
|
||||
dword_remainder = remote_node_index % SCIC_SDS_REMOTE_NODES_PER_DWORD;
|
||||
slot_normalized = (dword_remainder / SCU_STP_REMOTE_NODE_COUNT) * sizeof(u32);
|
||||
slot_position = remote_node_index % SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
remote_node_table->available_remote_nodes[dword_location] &=
|
||||
~(1 << (slot_normalized + slot_position));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table The remote node table from which the slot will be
|
||||
* cleared.
|
||||
* @group_index: The index for the slot that is to be cleared.
|
||||
*
|
||||
* This method clears the entire table slot at the specified slot index. none
|
||||
*/
|
||||
static void sci_remote_node_table_clear_group(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 dword_value;
|
||||
|
||||
BUG_ON(
|
||||
(remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD)
|
||||
<= (group_index / SCU_STP_REMOTE_NODE_COUNT)
|
||||
);
|
||||
|
||||
dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
|
||||
dword_value = remote_node_table->available_remote_nodes[dword_location];
|
||||
dword_value &= ~(SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4));
|
||||
remote_node_table->available_remote_nodes[dword_location] = dword_value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table:
|
||||
*
|
||||
* THis method sets an entire remote node group in the remote node table.
|
||||
*/
|
||||
static void sci_remote_node_table_set_group(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 dword_value;
|
||||
|
||||
BUG_ON(
|
||||
(remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD)
|
||||
<= (group_index / SCU_STP_REMOTE_NODE_COUNT)
|
||||
);
|
||||
|
||||
dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
|
||||
dword_value = remote_node_table->available_remote_nodes[dword_location];
|
||||
dword_value |= (SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4));
|
||||
remote_node_table->available_remote_nodes[dword_location] = dword_value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node table that for which the group
|
||||
* value is to be returned.
|
||||
* @group_index: This is the group index to use to find the group value.
|
||||
*
|
||||
* This method will return the group value for the specified group index. The
|
||||
* bit values at the specified remote node group index.
|
||||
*/
|
||||
static u8 sci_remote_node_table_get_group_value(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 dword_value;
|
||||
|
||||
dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
|
||||
dword_value = remote_node_table->available_remote_nodes[dword_location];
|
||||
dword_value &= (SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4));
|
||||
dword_value = dword_value >> (dword_remainder * 4);
|
||||
|
||||
return (u8)dword_value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table The remote that which is to be initialized.
|
||||
* @remote_node_entries: The number of entries to put in the table.
|
||||
*
|
||||
* This method will initialize the remote node table for use. none
|
||||
*/
|
||||
void sci_remote_node_table_initialize(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_entries)
|
||||
{
|
||||
u32 index;
|
||||
|
||||
/*
|
||||
* Initialize the raw data we could improve the speed by only initializing
|
||||
* those entries that we are actually going to be used */
|
||||
memset(
|
||||
remote_node_table->available_remote_nodes,
|
||||
0x00,
|
||||
sizeof(remote_node_table->available_remote_nodes)
|
||||
);
|
||||
|
||||
memset(
|
||||
remote_node_table->remote_node_groups,
|
||||
0x00,
|
||||
sizeof(remote_node_table->remote_node_groups)
|
||||
);
|
||||
|
||||
/* Initialize the available remote node sets */
|
||||
remote_node_table->available_nodes_array_size = (u16)
|
||||
(remote_node_entries / SCIC_SDS_REMOTE_NODES_PER_DWORD)
|
||||
+ ((remote_node_entries % SCIC_SDS_REMOTE_NODES_PER_DWORD) != 0);
|
||||
|
||||
|
||||
/* Initialize each full DWORD to a FULL SET of remote nodes */
|
||||
for (index = 0; index < remote_node_entries; index++) {
|
||||
sci_remote_node_table_set_node_index(remote_node_table, index);
|
||||
}
|
||||
|
||||
remote_node_table->group_array_size = (u16)
|
||||
(remote_node_entries / (SCU_STP_REMOTE_NODE_COUNT * 32))
|
||||
+ ((remote_node_entries % (SCU_STP_REMOTE_NODE_COUNT * 32)) != 0);
|
||||
|
||||
for (index = 0; index < (remote_node_entries / SCU_STP_REMOTE_NODE_COUNT); index++) {
|
||||
/*
|
||||
* These are all guaranteed to be full slot values so fill them in the
|
||||
* available sets of 3 remote nodes */
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 2, index);
|
||||
}
|
||||
|
||||
/* Now fill in any remainders that we may find */
|
||||
if ((remote_node_entries % SCU_STP_REMOTE_NODE_COUNT) == 2) {
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 1, index);
|
||||
} else if ((remote_node_entries % SCU_STP_REMOTE_NODE_COUNT) == 1) {
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 0, index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table The remote node table from which to allocate a
|
||||
* remote node.
|
||||
* @table_index: The group index that is to be used for the search.
|
||||
*
|
||||
* This method will allocate a single RNi from the remote node table. The
|
||||
* table index will determine from which remote node group table to search.
|
||||
* This search may fail and another group node table can be specified. The
|
||||
* function is designed to allow a serach of the available single remote node
|
||||
* group up to the triple remote node group. If an entry is found in the
|
||||
* specified table the remote node is removed and the remote node groups are
|
||||
* updated. The RNi value or an invalid remote node context if an RNi can not
|
||||
* be found.
|
||||
*/
|
||||
static u16 sci_remote_node_table_allocate_single_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index)
|
||||
{
|
||||
u8 index;
|
||||
u8 group_value;
|
||||
u32 group_index;
|
||||
u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX;
|
||||
|
||||
group_index = sci_remote_node_table_get_group_index(
|
||||
remote_node_table, group_table_index);
|
||||
|
||||
/* We could not find an available slot in the table selector 0 */
|
||||
if (group_index != SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX) {
|
||||
group_value = sci_remote_node_table_get_group_value(
|
||||
remote_node_table, group_index);
|
||||
|
||||
for (index = 0; index < SCU_STP_REMOTE_NODE_COUNT; index++) {
|
||||
if (((1 << index) & group_value) != 0) {
|
||||
/* We have selected a bit now clear it */
|
||||
remote_node_index = (u16)(group_index * SCU_STP_REMOTE_NODE_COUNT
|
||||
+ index);
|
||||
|
||||
sci_remote_node_table_clear_group_index(
|
||||
remote_node_table, group_table_index, group_index
|
||||
);
|
||||
|
||||
sci_remote_node_table_clear_node_index(
|
||||
remote_node_table, remote_node_index
|
||||
);
|
||||
|
||||
if (group_table_index > 0) {
|
||||
sci_remote_node_table_set_group_index(
|
||||
remote_node_table, group_table_index - 1, group_index
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return remote_node_index;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node table from which to allocate the
|
||||
* remote node entries.
|
||||
* @group_table_index: THis is the group table index which must equal two (2)
|
||||
* for this operation.
|
||||
*
|
||||
* This method will allocate three consecutive remote node context entries. If
|
||||
* there are no remaining triple entries the function will return a failure.
|
||||
* The remote node index that represents three consecutive remote node entries
|
||||
* or an invalid remote node context if none can be found.
|
||||
*/
|
||||
static u16 sci_remote_node_table_allocate_triple_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index)
|
||||
{
|
||||
u32 group_index;
|
||||
u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX;
|
||||
|
||||
group_index = sci_remote_node_table_get_group_index(
|
||||
remote_node_table, group_table_index);
|
||||
|
||||
if (group_index != SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX) {
|
||||
remote_node_index = (u16)group_index * SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
sci_remote_node_table_clear_group_index(
|
||||
remote_node_table, group_table_index, group_index
|
||||
);
|
||||
|
||||
sci_remote_node_table_clear_group(
|
||||
remote_node_table, group_index
|
||||
);
|
||||
}
|
||||
|
||||
return remote_node_index;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node table from which the remote node
|
||||
* allocation is to take place.
|
||||
* @remote_node_count: This is ther remote node count which is one of
|
||||
* SCU_SSP_REMOTE_NODE_COUNT(1) or SCU_STP_REMOTE_NODE_COUNT(3).
|
||||
*
|
||||
* This method will allocate a remote node that mataches the remote node count
|
||||
* specified by the caller. Valid values for remote node count is
|
||||
* SCU_SSP_REMOTE_NODE_COUNT(1) or SCU_STP_REMOTE_NODE_COUNT(3). u16 This is
|
||||
* the remote node index that is returned or an invalid remote node context.
|
||||
*/
|
||||
u16 sci_remote_node_table_allocate_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_count)
|
||||
{
|
||||
u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX;
|
||||
|
||||
if (remote_node_count == SCU_SSP_REMOTE_NODE_COUNT) {
|
||||
remote_node_index =
|
||||
sci_remote_node_table_allocate_single_remote_node(
|
||||
remote_node_table, 0);
|
||||
|
||||
if (remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) {
|
||||
remote_node_index =
|
||||
sci_remote_node_table_allocate_single_remote_node(
|
||||
remote_node_table, 1);
|
||||
}
|
||||
|
||||
if (remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) {
|
||||
remote_node_index =
|
||||
sci_remote_node_table_allocate_single_remote_node(
|
||||
remote_node_table, 2);
|
||||
}
|
||||
} else if (remote_node_count == SCU_STP_REMOTE_NODE_COUNT) {
|
||||
remote_node_index =
|
||||
sci_remote_node_table_allocate_triple_remote_node(
|
||||
remote_node_table, 2);
|
||||
}
|
||||
|
||||
return remote_node_index;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table:
|
||||
*
|
||||
* This method will free a single remote node index back to the remote node
|
||||
* table. This routine will update the remote node groups
|
||||
*/
|
||||
static void sci_remote_node_table_release_single_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u16 remote_node_index)
|
||||
{
|
||||
u32 group_index;
|
||||
u8 group_value;
|
||||
|
||||
group_index = remote_node_index / SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
group_value = sci_remote_node_table_get_group_value(remote_node_table, group_index);
|
||||
|
||||
/*
|
||||
* Assert that we are not trying to add an entry to a slot that is already
|
||||
* full. */
|
||||
BUG_ON(group_value == SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE);
|
||||
|
||||
if (group_value == 0x00) {
|
||||
/*
|
||||
* There are no entries in this slot so it must be added to the single
|
||||
* slot table. */
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 0, group_index);
|
||||
} else if ((group_value & (group_value - 1)) == 0) {
|
||||
/*
|
||||
* There is only one entry in this slot so it must be moved from the
|
||||
* single slot table to the dual slot table */
|
||||
sci_remote_node_table_clear_group_index(remote_node_table, 0, group_index);
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 1, group_index);
|
||||
} else {
|
||||
/*
|
||||
* There are two entries in the slot so it must be moved from the dual
|
||||
* slot table to the tripple slot table. */
|
||||
sci_remote_node_table_clear_group_index(remote_node_table, 1, group_index);
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 2, group_index);
|
||||
}
|
||||
|
||||
sci_remote_node_table_set_node_index(remote_node_table, remote_node_index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node table to which the remote node
|
||||
* index is to be freed.
|
||||
*
|
||||
* This method will release a group of three consecutive remote nodes back to
|
||||
* the free remote nodes.
|
||||
*/
|
||||
static void sci_remote_node_table_release_triple_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u16 remote_node_index)
|
||||
{
|
||||
u32 group_index;
|
||||
|
||||
group_index = remote_node_index / SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
sci_remote_node_table_set_group_index(
|
||||
remote_node_table, 2, group_index
|
||||
);
|
||||
|
||||
sci_remote_node_table_set_group(remote_node_table, group_index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: The remote node table to which the remote node index is
|
||||
* to be freed.
|
||||
* @remote_node_count: This is the count of consecutive remote nodes that are
|
||||
* to be freed.
|
||||
*
|
||||
* This method will release the remote node index back into the remote node
|
||||
* table free pool.
|
||||
*/
|
||||
void sci_remote_node_table_release_remote_node_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_count,
|
||||
u16 remote_node_index)
|
||||
{
|
||||
if (remote_node_count == SCU_SSP_REMOTE_NODE_COUNT) {
|
||||
sci_remote_node_table_release_single_remote_node(
|
||||
remote_node_table, remote_node_index);
|
||||
} else if (remote_node_count == SCU_STP_REMOTE_NODE_COUNT) {
|
||||
sci_remote_node_table_release_triple_remote_node(
|
||||
remote_node_table, remote_node_index);
|
||||
}
|
||||
}
|
||||
|
188
drivers/scsi/isci/remote_node_table.h
Normal file
188
drivers/scsi/isci/remote_node_table.h
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SCIC_SDS_REMOTE_NODE_TABLE_H_
|
||||
#define _SCIC_SDS_REMOTE_NODE_TABLE_H_
|
||||
|
||||
#include "isci.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Remote node sets are sets of remote node index in the remtoe node table The
|
||||
* SCU hardware requires that STP remote node entries take three consecutive
|
||||
* remote node index so the table is arranged in sets of three. The bits are
|
||||
* used as 0111 0111 to make a byte and the bits define the set of three remote
|
||||
* nodes to use as a sequence.
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODE_SETS_PER_BYTE 2
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Since the remote node table is organized as DWORDS take the remote node sets
|
||||
* in bytes and represent them in DWORDs. The lowest ordered bits are the ones
|
||||
* used in case full DWORD is not being used. i.e. 0000 0000 0000 0000 0111
|
||||
* 0111 0111 0111 // if only a single WORD is in use in the DWORD.
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD \
|
||||
(sizeof(u32) * SCIC_SDS_REMOTE_NODE_SETS_PER_BYTE)
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This is a count of the numeber of remote nodes that can be represented in a
|
||||
* byte
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODES_PER_BYTE \
|
||||
(SCU_STP_REMOTE_NODE_COUNT * SCIC_SDS_REMOTE_NODE_SETS_PER_BYTE)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This is a count of the number of remote nodes that can be represented in a
|
||||
* DWROD
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODES_PER_DWORD \
|
||||
(sizeof(u32) * SCIC_SDS_REMOTE_NODES_PER_BYTE)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This is the number of bits in a remote node group
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODES_BITS_PER_GROUP 4
|
||||
|
||||
#define SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX (0xFFFFFFFF)
|
||||
#define SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE (0x07)
|
||||
#define SCIC_SDS_REMOTE_NODE_TABLE_EMPTY_SLOT_VALUE (0x00)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Expander attached sata remote node count
|
||||
*/
|
||||
#define SCU_STP_REMOTE_NODE_COUNT 3
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Expander or direct attached ssp remote node count
|
||||
*/
|
||||
#define SCU_SSP_REMOTE_NODE_COUNT 1
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Direct attached STP remote node count
|
||||
*/
|
||||
#define SCU_SATA_REMOTE_NODE_COUNT 1
|
||||
|
||||
/**
|
||||
* struct sci_remote_node_table -
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct sci_remote_node_table {
|
||||
/**
|
||||
* This field contains the array size in dwords
|
||||
*/
|
||||
u16 available_nodes_array_size;
|
||||
|
||||
/**
|
||||
* This field contains the array size of the
|
||||
*/
|
||||
u16 group_array_size;
|
||||
|
||||
/**
|
||||
* This field is the array of available remote node entries in bits.
|
||||
* Because of the way STP remote node data is allocated on the SCU hardware
|
||||
* the remote nodes must occupy three consecutive remote node context
|
||||
* entries. For ease of allocation and de-allocation we have broken the
|
||||
* sets of three into a single nibble. When the STP RNi is allocated all
|
||||
* of the bits in the nibble are cleared. This math results in a table size
|
||||
* of MAX_REMOTE_NODES / CONSECUTIVE RNi ENTRIES for STP / 2 entries per byte.
|
||||
*/
|
||||
u32 available_remote_nodes[
|
||||
(SCI_MAX_REMOTE_DEVICES / SCIC_SDS_REMOTE_NODES_PER_DWORD)
|
||||
+ ((SCI_MAX_REMOTE_DEVICES % SCIC_SDS_REMOTE_NODES_PER_DWORD) != 0)];
|
||||
|
||||
/**
|
||||
* This field is the nibble selector for the above table. There are three
|
||||
* possible selectors each for fast lookup when trying to find one, two or
|
||||
* three remote node entries.
|
||||
*/
|
||||
u32 remote_node_groups[
|
||||
SCU_STP_REMOTE_NODE_COUNT][
|
||||
(SCI_MAX_REMOTE_DEVICES / (32 * SCU_STP_REMOTE_NODE_COUNT))
|
||||
+ ((SCI_MAX_REMOTE_DEVICES % (32 * SCU_STP_REMOTE_NODE_COUNT)) != 0)];
|
||||
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------------- */
|
||||
|
||||
void sci_remote_node_table_initialize(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_entries);
|
||||
|
||||
u16 sci_remote_node_table_allocate_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_count);
|
||||
|
||||
void sci_remote_node_table_release_remote_node_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_count,
|
||||
u16 remote_node_index);
|
||||
|
||||
#endif /* _SCIC_SDS_REMOTE_NODE_TABLE_H_ */
|
3528
drivers/scsi/isci/request.c
Normal file
3528
drivers/scsi/isci/request.c
Normal file
File diff suppressed because it is too large
Load diff
310
drivers/scsi/isci/request.h
Normal file
310
drivers/scsi/isci/request.h
Normal file
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _ISCI_REQUEST_H_
|
||||
#define _ISCI_REQUEST_H_
|
||||
|
||||
#include "isci.h"
|
||||
#include "host.h"
|
||||
#include "scu_task_context.h"
|
||||
|
||||
/**
|
||||
* isci_stp_request - extra request infrastructure to handle pio/atapi protocol
|
||||
* @pio_len - number of bytes requested at PIO setup
|
||||
* @status - pio setup ending status value to tell us if we need
|
||||
* to wait for another fis or if the transfer is complete. Upon
|
||||
* receipt of a d2h fis this will be the status field of that fis.
|
||||
* @sgl - track pio transfer progress as we iterate through the sgl
|
||||
*/
|
||||
struct isci_stp_request {
|
||||
u32 pio_len;
|
||||
u8 status;
|
||||
|
||||
struct isci_stp_pio_sgl {
|
||||
int index;
|
||||
u8 set;
|
||||
u32 offset;
|
||||
} sgl;
|
||||
};
|
||||
|
||||
struct isci_request {
|
||||
#define IREQ_COMPLETE_IN_TARGET 0
|
||||
#define IREQ_TERMINATED 1
|
||||
#define IREQ_TMF 2
|
||||
#define IREQ_ACTIVE 3
|
||||
#define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */
|
||||
#define IREQ_TC_ABORT_POSTED 5
|
||||
#define IREQ_ABORT_PATH_ACTIVE 6
|
||||
#define IREQ_NO_AUTO_FREE_TAG 7 /* Set when being explicitly managed */
|
||||
unsigned long flags;
|
||||
/* XXX kill ttype and ttype_ptr, allocate full sas_task */
|
||||
union ttype_ptr_union {
|
||||
struct sas_task *io_task_ptr; /* When ttype==io_task */
|
||||
struct isci_tmf *tmf_task_ptr; /* When ttype==tmf_task */
|
||||
} ttype_ptr;
|
||||
struct isci_host *isci_host;
|
||||
dma_addr_t request_daddr;
|
||||
dma_addr_t zero_scatter_daddr;
|
||||
unsigned int num_sg_entries;
|
||||
/* Note: "io_request_completion" is completed in two different ways
|
||||
* depending on whether this is a TMF or regular request.
|
||||
* - TMF requests are completed in the thread that started them;
|
||||
* - regular requests are completed in the request completion callback
|
||||
* function.
|
||||
* This difference in operation allows the aborter of a TMF request
|
||||
* to be sure that once the TMF request completes, the I/O that the
|
||||
* TMF was aborting is guaranteed to have completed.
|
||||
*
|
||||
* XXX kill io_request_completion
|
||||
*/
|
||||
struct completion *io_request_completion;
|
||||
struct sci_base_state_machine sm;
|
||||
struct isci_host *owning_controller;
|
||||
struct isci_remote_device *target_device;
|
||||
u16 io_tag;
|
||||
enum sas_protocol protocol;
|
||||
u32 scu_status; /* hardware result */
|
||||
u32 sci_status; /* upper layer disposition */
|
||||
u32 post_context;
|
||||
struct scu_task_context *tc;
|
||||
/* could be larger with sg chaining */
|
||||
#define SCU_SGL_SIZE ((SCI_MAX_SCATTER_GATHER_ELEMENTS + 1) / 2)
|
||||
struct scu_sgl_element_pair sg_table[SCU_SGL_SIZE] __attribute__ ((aligned(32)));
|
||||
/* This field is a pointer to the stored rx frame data. It is used in
|
||||
* STP internal requests and SMP response frames. If this field is
|
||||
* non-NULL the saved frame must be released on IO request completion.
|
||||
*/
|
||||
u32 saved_rx_frame_index;
|
||||
|
||||
union {
|
||||
struct {
|
||||
union {
|
||||
struct ssp_cmd_iu cmd;
|
||||
struct ssp_task_iu tmf;
|
||||
};
|
||||
union {
|
||||
struct ssp_response_iu rsp;
|
||||
u8 rsp_buf[SSP_RESP_IU_MAX_SIZE];
|
||||
};
|
||||
} ssp;
|
||||
struct {
|
||||
struct isci_stp_request req;
|
||||
struct host_to_dev_fis cmd;
|
||||
struct dev_to_host_fis rsp;
|
||||
} stp;
|
||||
};
|
||||
};
|
||||
|
||||
static inline struct isci_request *to_ireq(struct isci_stp_request *stp_req)
|
||||
{
|
||||
struct isci_request *ireq;
|
||||
|
||||
ireq = container_of(stp_req, typeof(*ireq), stp.req);
|
||||
return ireq;
|
||||
}
|
||||
|
||||
/**
|
||||
* enum sci_base_request_states - request state machine states
|
||||
*
|
||||
* @SCI_REQ_INIT: Simply the initial state for the base request state machine.
|
||||
*
|
||||
* @SCI_REQ_CONSTRUCTED: This state indicates that the request has been
|
||||
* constructed. This state is entered from the INITIAL state.
|
||||
*
|
||||
* @SCI_REQ_STARTED: This state indicates that the request has been started.
|
||||
* This state is entered from the CONSTRUCTED state.
|
||||
*
|
||||
* @SCI_REQ_STP_UDMA_WAIT_TC_COMP:
|
||||
* @SCI_REQ_STP_UDMA_WAIT_D2H:
|
||||
* @SCI_REQ_STP_NON_DATA_WAIT_H2D:
|
||||
* @SCI_REQ_STP_NON_DATA_WAIT_D2H:
|
||||
*
|
||||
* @SCI_REQ_STP_PIO_WAIT_H2D: While in this state the IO request object is
|
||||
* waiting for the TC completion notification for the H2D Register FIS
|
||||
*
|
||||
* @SCI_REQ_STP_PIO_WAIT_FRAME: While in this state the IO request object is
|
||||
* waiting for either a PIO Setup FIS or a D2H register FIS. The type of frame
|
||||
* received is based on the result of the prior frame and line conditions.
|
||||
*
|
||||
* @SCI_REQ_STP_PIO_DATA_IN: While in this state the IO request object is
|
||||
* waiting for a DATA frame from the device.
|
||||
*
|
||||
* @SCI_REQ_STP_PIO_DATA_OUT: While in this state the IO request object is
|
||||
* waiting to transmit the next data frame to the device.
|
||||
*
|
||||
* @SCI_REQ_ATAPI_WAIT_H2D: While in this state the IO request object is
|
||||
* waiting for the TC completion notification for the H2D Register FIS
|
||||
*
|
||||
* @SCI_REQ_ATAPI_WAIT_PIO_SETUP: While in this state the IO request object is
|
||||
* waiting for either a PIO Setup.
|
||||
*
|
||||
* @SCI_REQ_ATAPI_WAIT_D2H: The non-data IO transit to this state in this state
|
||||
* after receiving TC completion. While in this state IO request object is
|
||||
* waiting for D2H status frame as UF.
|
||||
*
|
||||
* @SCI_REQ_ATAPI_WAIT_TC_COMP: When transmitting raw frames hardware reports
|
||||
* task context completion after every frame submission, so in the
|
||||
* non-accelerated case we need to expect the completion for the "cdb" frame.
|
||||
*
|
||||
* @SCI_REQ_TASK_WAIT_TC_COMP: The AWAIT_TC_COMPLETION sub-state indicates that
|
||||
* the started raw task management request is waiting for the transmission of
|
||||
* the initial frame (i.e. command, task, etc.).
|
||||
*
|
||||
* @SCI_REQ_TASK_WAIT_TC_RESP: This sub-state indicates that the started task
|
||||
* management request is waiting for the reception of an unsolicited frame
|
||||
* (i.e. response IU).
|
||||
*
|
||||
* @SCI_REQ_SMP_WAIT_RESP: This sub-state indicates that the started task
|
||||
* management request is waiting for the reception of an unsolicited frame
|
||||
* (i.e. response IU).
|
||||
*
|
||||
* @SCI_REQ_SMP_WAIT_TC_COMP: The AWAIT_TC_COMPLETION sub-state indicates that
|
||||
* the started SMP request is waiting for the transmission of the initial frame
|
||||
* (i.e. command, task, etc.).
|
||||
*
|
||||
* @SCI_REQ_COMPLETED: This state indicates that the request has completed.
|
||||
* This state is entered from the STARTED state. This state is entered from the
|
||||
* ABORTING state.
|
||||
*
|
||||
* @SCI_REQ_ABORTING: This state indicates that the request is in the process
|
||||
* of being terminated/aborted. This state is entered from the CONSTRUCTED
|
||||
* state. This state is entered from the STARTED state.
|
||||
*
|
||||
* @SCI_REQ_FINAL: Simply the final state for the base request state machine.
|
||||
*/
|
||||
#define REQUEST_STATES {\
|
||||
C(REQ_INIT),\
|
||||
C(REQ_CONSTRUCTED),\
|
||||
C(REQ_STARTED),\
|
||||
C(REQ_STP_UDMA_WAIT_TC_COMP),\
|
||||
C(REQ_STP_UDMA_WAIT_D2H),\
|
||||
C(REQ_STP_NON_DATA_WAIT_H2D),\
|
||||
C(REQ_STP_NON_DATA_WAIT_D2H),\
|
||||
C(REQ_STP_PIO_WAIT_H2D),\
|
||||
C(REQ_STP_PIO_WAIT_FRAME),\
|
||||
C(REQ_STP_PIO_DATA_IN),\
|
||||
C(REQ_STP_PIO_DATA_OUT),\
|
||||
C(REQ_ATAPI_WAIT_H2D),\
|
||||
C(REQ_ATAPI_WAIT_PIO_SETUP),\
|
||||
C(REQ_ATAPI_WAIT_D2H),\
|
||||
C(REQ_ATAPI_WAIT_TC_COMP),\
|
||||
C(REQ_TASK_WAIT_TC_COMP),\
|
||||
C(REQ_TASK_WAIT_TC_RESP),\
|
||||
C(REQ_SMP_WAIT_RESP),\
|
||||
C(REQ_SMP_WAIT_TC_COMP),\
|
||||
C(REQ_COMPLETED),\
|
||||
C(REQ_ABORTING),\
|
||||
C(REQ_FINAL),\
|
||||
}
|
||||
#undef C
|
||||
#define C(a) SCI_##a
|
||||
enum sci_base_request_states REQUEST_STATES;
|
||||
#undef C
|
||||
const char *req_state_name(enum sci_base_request_states state);
|
||||
|
||||
enum sci_status sci_request_start(struct isci_request *ireq);
|
||||
enum sci_status sci_io_request_terminate(struct isci_request *ireq);
|
||||
enum sci_status
|
||||
sci_io_request_event_handler(struct isci_request *ireq,
|
||||
u32 event_code);
|
||||
enum sci_status
|
||||
sci_io_request_frame_handler(struct isci_request *ireq,
|
||||
u32 frame_index);
|
||||
enum sci_status
|
||||
sci_task_request_terminate(struct isci_request *ireq);
|
||||
extern enum sci_status
|
||||
sci_request_complete(struct isci_request *ireq);
|
||||
extern enum sci_status
|
||||
sci_io_request_tc_completion(struct isci_request *ireq, u32 code);
|
||||
|
||||
/* XXX open code in caller */
|
||||
static inline dma_addr_t
|
||||
sci_io_request_get_dma_addr(struct isci_request *ireq, void *virt_addr)
|
||||
{
|
||||
|
||||
char *requested_addr = (char *)virt_addr;
|
||||
char *base_addr = (char *)ireq;
|
||||
|
||||
BUG_ON(requested_addr < base_addr);
|
||||
BUG_ON((requested_addr - base_addr) >= sizeof(*ireq));
|
||||
|
||||
return ireq->request_daddr + (requested_addr - base_addr);
|
||||
}
|
||||
|
||||
#define isci_request_access_task(req) ((req)->ttype_ptr.io_task_ptr)
|
||||
|
||||
#define isci_request_access_tmf(req) ((req)->ttype_ptr.tmf_task_ptr)
|
||||
|
||||
struct isci_request *isci_tmf_request_from_tag(struct isci_host *ihost,
|
||||
struct isci_tmf *isci_tmf,
|
||||
u16 tag);
|
||||
int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *idev,
|
||||
struct sas_task *task, u16 tag);
|
||||
enum sci_status
|
||||
sci_task_request_construct(struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
u16 io_tag,
|
||||
struct isci_request *ireq);
|
||||
enum sci_status sci_task_request_construct_ssp(struct isci_request *ireq);
|
||||
void sci_smp_request_copy_response(struct isci_request *ireq);
|
||||
|
||||
static inline int isci_task_is_ncq_recovery(struct sas_task *task)
|
||||
{
|
||||
return (sas_protocol_ata(task->task_proto) &&
|
||||
task->ata_task.fis.command == ATA_CMD_READ_LOG_EXT &&
|
||||
task->ata_task.fis.lbal == ATA_LOG_SATA_NCQ);
|
||||
|
||||
}
|
||||
#endif /* !defined(_ISCI_REQUEST_H_) */
|
217
drivers/scsi/isci/sas.h
Normal file
217
drivers/scsi/isci/sas.h
Normal file
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SCI_SAS_H_
|
||||
#define _SCI_SAS_H_
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
/*
|
||||
* SATA FIS Types These constants depict the various SATA FIS types devined in
|
||||
* the serial ATA specification.
|
||||
* XXX: This needs to go into <scsi/sas.h>
|
||||
*/
|
||||
#define FIS_REGH2D 0x27
|
||||
#define FIS_REGD2H 0x34
|
||||
#define FIS_SETDEVBITS 0xA1
|
||||
#define FIS_DMA_ACTIVATE 0x39
|
||||
#define FIS_DMA_SETUP 0x41
|
||||
#define FIS_BIST_ACTIVATE 0x58
|
||||
#define FIS_PIO_SETUP 0x5F
|
||||
#define FIS_DATA 0x46
|
||||
|
||||
/**************************************************************************/
|
||||
#define SSP_RESP_IU_MAX_SIZE 280
|
||||
|
||||
/*
|
||||
* contents of the SSP COMMAND INFORMATION UNIT.
|
||||
* For specific information on each of these individual fields please
|
||||
* reference the SAS specification SSP transport layer section.
|
||||
* XXX: This needs to go into <scsi/sas.h>
|
||||
*/
|
||||
struct ssp_cmd_iu {
|
||||
u8 LUN[8];
|
||||
u8 add_cdb_len:6;
|
||||
u8 _r_a:2;
|
||||
u8 _r_b;
|
||||
u8 en_fburst:1;
|
||||
u8 task_prio:4;
|
||||
u8 task_attr:3;
|
||||
u8 _r_c;
|
||||
|
||||
u8 cdb[16];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* contents of the SSP TASK INFORMATION UNIT.
|
||||
* For specific information on each of these individual fields please
|
||||
* reference the SAS specification SSP transport layer section.
|
||||
* XXX: This needs to go into <scsi/sas.h>
|
||||
*/
|
||||
struct ssp_task_iu {
|
||||
u8 LUN[8];
|
||||
u8 _r_a;
|
||||
u8 task_func;
|
||||
u8 _r_b[4];
|
||||
u16 task_tag;
|
||||
u8 _r_c[12];
|
||||
} __packed;
|
||||
|
||||
|
||||
/*
|
||||
* struct smp_req_phy_id - This structure defines the contents of
|
||||
* an SMP Request that is comprised of the struct smp_request_header and a
|
||||
* phy identifier.
|
||||
* Examples: SMP_REQUEST_DISCOVER, SMP_REQUEST_REPORT_PHY_SATA.
|
||||
*
|
||||
* For specific information on each of these individual fields please reference
|
||||
* the SAS specification.
|
||||
*/
|
||||
struct smp_req_phy_id {
|
||||
u8 _r_a[4]; /* bytes 4-7 */
|
||||
|
||||
u8 ign_zone_grp:1; /* byte 8 */
|
||||
u8 _r_b:7;
|
||||
|
||||
u8 phy_id; /* byte 9 */
|
||||
u8 _r_c; /* byte 10 */
|
||||
u8 _r_d; /* byte 11 */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct smp_req_config_route_info - This structure defines the
|
||||
* contents of an SMP Configure Route Information request.
|
||||
*
|
||||
* For specific information on each of these individual fields please reference
|
||||
* the SAS specification.
|
||||
*/
|
||||
struct smp_req_conf_rtinfo {
|
||||
u16 exp_change_cnt; /* bytes 4-5 */
|
||||
u8 exp_rt_idx_hi; /* byte 6 */
|
||||
u8 exp_rt_idx; /* byte 7 */
|
||||
|
||||
u8 _r_a; /* byte 8 */
|
||||
u8 phy_id; /* byte 9 */
|
||||
u16 _r_b; /* bytes 10-11 */
|
||||
|
||||
u8 _r_c:7; /* byte 12 */
|
||||
u8 dis_rt_entry:1;
|
||||
u8 _r_d[3]; /* bytes 13-15 */
|
||||
|
||||
u8 rt_sas_addr[8]; /* bytes 16-23 */
|
||||
u8 _r_e[16]; /* bytes 24-39 */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct smp_req_phycntl - This structure defines the contents of an
|
||||
* SMP Phy Controller request.
|
||||
*
|
||||
* For specific information on each of these individual fields please reference
|
||||
* the SAS specification.
|
||||
*/
|
||||
struct smp_req_phycntl {
|
||||
u16 exp_change_cnt; /* byte 4-5 */
|
||||
|
||||
u8 _r_a[3]; /* bytes 6-8 */
|
||||
|
||||
u8 phy_id; /* byte 9 */
|
||||
u8 phy_op; /* byte 10 */
|
||||
|
||||
u8 upd_pathway:1; /* byte 11 */
|
||||
u8 _r_b:7;
|
||||
|
||||
u8 _r_c[12]; /* byte 12-23 */
|
||||
|
||||
u8 att_dev_name[8]; /* byte 24-31 */
|
||||
|
||||
u8 _r_d:4; /* byte 32 */
|
||||
u8 min_linkrate:4;
|
||||
|
||||
u8 _r_e:4; /* byte 33 */
|
||||
u8 max_linkrate:4;
|
||||
|
||||
u8 _r_f[2]; /* byte 34-35 */
|
||||
|
||||
u8 pathway:4; /* byte 36 */
|
||||
u8 _r_g:4;
|
||||
|
||||
u8 _r_h[3]; /* bytes 37-39 */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct smp_req - This structure simply unionizes the existing request
|
||||
* structures into a common request type.
|
||||
*
|
||||
* XXX: This data structure may need to go to scsi/sas.h
|
||||
*/
|
||||
struct smp_req {
|
||||
u8 type; /* byte 0 */
|
||||
u8 func; /* byte 1 */
|
||||
u8 alloc_resp_len; /* byte 2 */
|
||||
u8 req_len; /* byte 3 */
|
||||
u8 req_data[0];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct sci_sas_address - This structure depicts how a SAS address is
|
||||
* represented by SCI.
|
||||
* XXX convert this to u8 [SAS_ADDR_SIZE] like the rest of libsas
|
||||
*
|
||||
*/
|
||||
struct sci_sas_address {
|
||||
u32 high;
|
||||
u32 low;
|
||||
};
|
||||
#endif
|
285
drivers/scsi/isci/scu_completion_codes.h
Normal file
285
drivers/scsi/isci/scu_completion_codes.h
Normal file
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SCU_COMPLETION_CODES_HEADER_
|
||||
#define _SCU_COMPLETION_CODES_HEADER_
|
||||
|
||||
/**
|
||||
* This file contains the constants and macros for the SCU hardware completion
|
||||
* codes.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#define SCU_COMPLETION_TYPE_SHIFT 28
|
||||
#define SCU_COMPLETION_TYPE_MASK 0x70000000
|
||||
|
||||
/**
|
||||
* SCU_COMPLETION_TYPE() -
|
||||
*
|
||||
* This macro constructs an SCU completion type
|
||||
*/
|
||||
#define SCU_COMPLETION_TYPE(type) \
|
||||
((u32)(type) << SCU_COMPLETION_TYPE_SHIFT)
|
||||
|
||||
/**
|
||||
* SCU_COMPLETION_TYPE() -
|
||||
*
|
||||
* These macros contain the SCU completion types SCU_COMPLETION_TYPE
|
||||
*/
|
||||
#define SCU_COMPLETION_TYPE_TASK SCU_COMPLETION_TYPE(0)
|
||||
#define SCU_COMPLETION_TYPE_SDMA SCU_COMPLETION_TYPE(1)
|
||||
#define SCU_COMPLETION_TYPE_UFI SCU_COMPLETION_TYPE(2)
|
||||
#define SCU_COMPLETION_TYPE_EVENT SCU_COMPLETION_TYPE(3)
|
||||
#define SCU_COMPLETION_TYPE_NOTIFY SCU_COMPLETION_TYPE(4)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* These constants provide the shift and mask values for the various parts of
|
||||
* an SCU completion code.
|
||||
*/
|
||||
#define SCU_COMPLETION_STATUS_MASK 0x0FFC0000
|
||||
#define SCU_COMPLETION_TL_STATUS_MASK 0x0FC00000
|
||||
#define SCU_COMPLETION_TL_STATUS_SHIFT 22
|
||||
#define SCU_COMPLETION_SDMA_STATUS_MASK 0x003C0000
|
||||
#define SCU_COMPLETION_PEG_MASK 0x00010000
|
||||
#define SCU_COMPLETION_PORT_MASK 0x00007000
|
||||
#define SCU_COMPLETION_PE_MASK SCU_COMPLETION_PORT_MASK
|
||||
#define SCU_COMPLETION_PE_SHIFT 12
|
||||
#define SCU_COMPLETION_INDEX_MASK 0x00000FFF
|
||||
|
||||
/**
|
||||
* SCU_GET_COMPLETION_TYPE() -
|
||||
*
|
||||
* This macro returns the SCU completion type.
|
||||
*/
|
||||
#define SCU_GET_COMPLETION_TYPE(completion_code) \
|
||||
((completion_code) & SCU_COMPLETION_TYPE_MASK)
|
||||
|
||||
/**
|
||||
* SCU_GET_COMPLETION_STATUS() -
|
||||
*
|
||||
* This macro returns the SCU completion status.
|
||||
*/
|
||||
#define SCU_GET_COMPLETION_STATUS(completion_code) \
|
||||
((completion_code) & SCU_COMPLETION_STATUS_MASK)
|
||||
|
||||
/**
|
||||
* SCU_GET_COMPLETION_TL_STATUS() -
|
||||
*
|
||||
* This macro returns the transport layer completion status.
|
||||
*/
|
||||
#define SCU_GET_COMPLETION_TL_STATUS(completion_code) \
|
||||
((completion_code) & SCU_COMPLETION_TL_STATUS_MASK)
|
||||
|
||||
/**
|
||||
* SCU_MAKE_COMPLETION_STATUS() -
|
||||
*
|
||||
* This macro takes a completion code and performs the shift and mask
|
||||
* operations to turn it into a completion code that can be compared to a
|
||||
* SCU_GET_COMPLETION_TL_STATUS.
|
||||
*/
|
||||
#define SCU_MAKE_COMPLETION_STATUS(completion_code) \
|
||||
((u32)(completion_code) << SCU_COMPLETION_TL_STATUS_SHIFT)
|
||||
|
||||
/**
|
||||
* SCU_NORMALIZE_COMPLETION_STATUS() -
|
||||
*
|
||||
* This macro takes a SCU_GET_COMPLETION_TL_STATUS and normalizes it for a
|
||||
* return code.
|
||||
*/
|
||||
#define SCU_NORMALIZE_COMPLETION_STATUS(completion_code) \
|
||||
(\
|
||||
((completion_code) & SCU_COMPLETION_TL_STATUS_MASK) \
|
||||
>> SCU_COMPLETION_TL_STATUS_SHIFT \
|
||||
)
|
||||
|
||||
/**
|
||||
* SCU_GET_COMPLETION_SDMA_STATUS() -
|
||||
*
|
||||
* This macro returns the SDMA completion status.
|
||||
*/
|
||||
#define SCU_GET_COMPLETION_SDMA_STATUS(completion_code) \
|
||||
((completion_code) & SCU_COMPLETION_SDMA_STATUS_MASK)
|
||||
|
||||
/**
|
||||
* SCU_GET_COMPLETION_PEG() -
|
||||
*
|
||||
* This macro returns the Protocol Engine Group from the completion code.
|
||||
*/
|
||||
#define SCU_GET_COMPLETION_PEG(completion_code) \
|
||||
((completion_code) & SCU_COMPLETION_PEG_MASK)
|
||||
|
||||
/**
|
||||
* SCU_GET_COMPLETION_PORT() -
|
||||
*
|
||||
* This macro reuturns the logical port index from the completion code.
|
||||
*/
|
||||
#define SCU_GET_COMPLETION_PORT(completion_code) \
|
||||
((completion_code) & SCU_COMPLETION_PORT_MASK)
|
||||
|
||||
/**
|
||||
* SCU_GET_PROTOCOL_ENGINE_INDEX() -
|
||||
*
|
||||
* This macro returns the PE index from the completion code.
|
||||
*/
|
||||
#define SCU_GET_PROTOCOL_ENGINE_INDEX(completion_code) \
|
||||
(((completion_code) & SCU_COMPLETION_PE_MASK) >> SCU_COMPLETION_PE_SHIFT)
|
||||
|
||||
/**
|
||||
* SCU_GET_COMPLETION_INDEX() -
|
||||
*
|
||||
* This macro returns the index of the completion which is either a TCi or an
|
||||
* RNi depending on the completion type.
|
||||
*/
|
||||
#define SCU_GET_COMPLETION_INDEX(completion_code) \
|
||||
((completion_code) & SCU_COMPLETION_INDEX_MASK)
|
||||
|
||||
#define SCU_UNSOLICITED_FRAME_MASK 0x0FFF0000
|
||||
#define SCU_UNSOLICITED_FRAME_SHIFT 16
|
||||
|
||||
/**
|
||||
* SCU_GET_FRAME_INDEX() -
|
||||
*
|
||||
* This macro returns a normalized frame index from an unsolicited frame
|
||||
* completion.
|
||||
*/
|
||||
#define SCU_GET_FRAME_INDEX(completion_code) \
|
||||
(\
|
||||
((completion_code) & SCU_UNSOLICITED_FRAME_MASK) \
|
||||
>> SCU_UNSOLICITED_FRAME_SHIFT \
|
||||
)
|
||||
|
||||
#define SCU_UNSOLICITED_FRAME_ERROR_MASK 0x00008000
|
||||
|
||||
/**
|
||||
* SCU_GET_FRAME_ERROR() -
|
||||
*
|
||||
* This macro returns a zero (0) value if there is no frame error otherwise it
|
||||
* returns non-zero (!0).
|
||||
*/
|
||||
#define SCU_GET_FRAME_ERROR(completion_code) \
|
||||
((completion_code) & SCU_UNSOLICITED_FRAME_ERROR_MASK)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* These constants represent normalized completion codes which must be shifted
|
||||
* 18 bits to match it with the hardware completion code. In a 16-bit compiler,
|
||||
* immediate constants are 16-bit values (the size of an int). If we shift
|
||||
* those by 18 bits, we completely lose the value. To ensure the value is a
|
||||
* 32-bit value like we want, each immediate value must be cast to a u32.
|
||||
*/
|
||||
#define SCU_TASK_DONE_GOOD ((u32)0x00)
|
||||
#define SCU_TASK_DONE_TX_RAW_CMD_ERR ((u32)0x08)
|
||||
#define SCU_TASK_DONE_CRC_ERR ((u32)0x14)
|
||||
#define SCU_TASK_DONE_CHECK_RESPONSE ((u32)0x14)
|
||||
#define SCU_TASK_DONE_GEN_RESPONSE ((u32)0x15)
|
||||
#define SCU_TASK_DONE_NAK_CMD_ERR ((u32)0x16)
|
||||
#define SCU_TASK_DONE_CMD_LL_R_ERR ((u32)0x16)
|
||||
#define SCU_TASK_DONE_LL_R_ERR ((u32)0x17)
|
||||
#define SCU_TASK_DONE_ACK_NAK_TO ((u32)0x17)
|
||||
#define SCU_TASK_DONE_LL_PERR ((u32)0x18)
|
||||
#define SCU_TASK_DONE_LL_SY_TERM ((u32)0x19)
|
||||
#define SCU_TASK_DONE_NAK_ERR ((u32)0x19)
|
||||
#define SCU_TASK_DONE_LL_LF_TERM ((u32)0x1A)
|
||||
#define SCU_TASK_DONE_DATA_LEN_ERR ((u32)0x1A)
|
||||
#define SCU_TASK_DONE_LL_CL_TERM ((u32)0x1B)
|
||||
#define SCU_TASK_DONE_BREAK_RCVD ((u32)0x1B)
|
||||
#define SCU_TASK_DONE_LL_ABORT_ERR ((u32)0x1B)
|
||||
#define SCU_TASK_DONE_SEQ_INV_TYPE ((u32)0x1C)
|
||||
#define SCU_TASK_DONE_UNEXP_XR ((u32)0x1C)
|
||||
#define SCU_TASK_DONE_INV_FIS_TYPE ((u32)0x1D)
|
||||
#define SCU_TASK_DONE_XR_IU_LEN_ERR ((u32)0x1D)
|
||||
#define SCU_TASK_DONE_INV_FIS_LEN ((u32)0x1E)
|
||||
#define SCU_TASK_DONE_XR_WD_LEN ((u32)0x1E)
|
||||
#define SCU_TASK_DONE_SDMA_ERR ((u32)0x1F)
|
||||
#define SCU_TASK_DONE_OFFSET_ERR ((u32)0x20)
|
||||
#define SCU_TASK_DONE_MAX_PLD_ERR ((u32)0x21)
|
||||
#define SCU_TASK_DONE_EXCESS_DATA ((u32)0x22)
|
||||
#define SCU_TASK_DONE_LF_ERR ((u32)0x23)
|
||||
#define SCU_TASK_DONE_UNEXP_FIS ((u32)0x24)
|
||||
#define SCU_TASK_DONE_UNEXP_RESP ((u32)0x24)
|
||||
#define SCU_TASK_DONE_EARLY_RESP ((u32)0x25)
|
||||
#define SCU_TASK_DONE_SMP_RESP_TO_ERR ((u32)0x26)
|
||||
#define SCU_TASK_DONE_DMASETUP_DIRERR ((u32)0x27)
|
||||
#define SCU_TASK_DONE_SMP_UFI_ERR ((u32)0x27)
|
||||
#define SCU_TASK_DONE_XFERCNT_ERR ((u32)0x28)
|
||||
#define SCU_TASK_DONE_SMP_FRM_TYPE_ERR ((u32)0x28)
|
||||
#define SCU_TASK_DONE_SMP_LL_RX_ERR ((u32)0x29)
|
||||
#define SCU_TASK_DONE_RESP_LEN_ERR ((u32)0x2A)
|
||||
#define SCU_TASK_DONE_UNEXP_DATA ((u32)0x2B)
|
||||
#define SCU_TASK_DONE_OPEN_FAIL ((u32)0x2C)
|
||||
#define SCU_TASK_DONE_UNEXP_SDBFIS ((u32)0x2D)
|
||||
#define SCU_TASK_DONE_REG_ERR ((u32)0x2E)
|
||||
#define SCU_TASK_DONE_SDB_ERR ((u32)0x2F)
|
||||
#define SCU_TASK_DONE_TASK_ABORT ((u32)0x30)
|
||||
#define SCU_TASK_DONE_CMD_SDMA_ERR ((U32)0x32)
|
||||
#define SCU_TASK_DONE_CMD_LL_ABORT_ERR ((U32)0x33)
|
||||
#define SCU_TASK_OPEN_REJECT_WRONG_DESTINATION ((u32)0x34)
|
||||
#define SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1 ((u32)0x35)
|
||||
#define SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2 ((u32)0x36)
|
||||
#define SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3 ((u32)0x37)
|
||||
#define SCU_TASK_OPEN_REJECT_BAD_DESTINATION ((u32)0x38)
|
||||
#define SCU_TASK_OPEN_REJECT_ZONE_VIOLATION ((u32)0x39)
|
||||
#define SCU_TASK_DONE_VIIT_ENTRY_NV ((u32)0x3A)
|
||||
#define SCU_TASK_DONE_IIT_ENTRY_NV ((u32)0x3B)
|
||||
#define SCU_TASK_DONE_RNCNV_OUTBOUND ((u32)0x3C)
|
||||
#define SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY ((u32)0x3D)
|
||||
#define SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED ((u32)0x3E)
|
||||
#define SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED ((u32)0x3F)
|
||||
|
||||
#endif /* _SCU_COMPLETION_CODES_HEADER_ */
|
336
drivers/scsi/isci/scu_event_codes.h
Normal file
336
drivers/scsi/isci/scu_event_codes.h
Normal file
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __SCU_EVENT_CODES_HEADER__
|
||||
#define __SCU_EVENT_CODES_HEADER__
|
||||
|
||||
/**
|
||||
* This file contains the constants and macros for the SCU event codes.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#define SCU_EVENT_TYPE_CODE_SHIFT 24
|
||||
#define SCU_EVENT_TYPE_CODE_MASK 0x0F000000
|
||||
|
||||
#define SCU_EVENT_SPECIFIC_CODE_SHIFT 18
|
||||
#define SCU_EVENT_SPECIFIC_CODE_MASK 0x00FC0000
|
||||
|
||||
#define SCU_EVENT_CODE_MASK \
|
||||
(SCU_EVENT_TYPE_CODE_MASK | SCU_EVENT_SPECIFIC_CODE_MASK)
|
||||
|
||||
/**
|
||||
* SCU_EVENT_TYPE() -
|
||||
*
|
||||
* This macro constructs an SCU event type from the type value.
|
||||
*/
|
||||
#define SCU_EVENT_TYPE(type) \
|
||||
((u32)(type) << SCU_EVENT_TYPE_CODE_SHIFT)
|
||||
|
||||
/**
|
||||
* SCU_EVENT_SPECIFIC() -
|
||||
*
|
||||
* This macro constructs an SCU event specifier from the code value.
|
||||
*/
|
||||
#define SCU_EVENT_SPECIFIC(code) \
|
||||
((u32)(code) << SCU_EVENT_SPECIFIC_CODE_SHIFT)
|
||||
|
||||
/**
|
||||
* SCU_EVENT_MESSAGE() -
|
||||
*
|
||||
* This macro constructs a combines an SCU event type and SCU event specifier
|
||||
* from the type and code values.
|
||||
*/
|
||||
#define SCU_EVENT_MESSAGE(type, code) \
|
||||
((type) | SCU_EVENT_SPECIFIC(code))
|
||||
|
||||
/**
|
||||
* SCU_EVENT_TYPE() -
|
||||
*
|
||||
* SCU_EVENT_TYPES
|
||||
*/
|
||||
#define SCU_EVENT_TYPE_SMU_COMMAND_ERROR SCU_EVENT_TYPE(0x08)
|
||||
#define SCU_EVENT_TYPE_SMU_PCQ_ERROR SCU_EVENT_TYPE(0x09)
|
||||
#define SCU_EVENT_TYPE_SMU_ERROR SCU_EVENT_TYPE(0x00)
|
||||
#define SCU_EVENT_TYPE_TRANSPORT_ERROR SCU_EVENT_TYPE(0x01)
|
||||
#define SCU_EVENT_TYPE_BROADCAST_CHANGE SCU_EVENT_TYPE(0x02)
|
||||
#define SCU_EVENT_TYPE_OSSP_EVENT SCU_EVENT_TYPE(0x03)
|
||||
#define SCU_EVENT_TYPE_FATAL_MEMORY_ERROR SCU_EVENT_TYPE(0x0F)
|
||||
#define SCU_EVENT_TYPE_RNC_SUSPEND_TX SCU_EVENT_TYPE(0x04)
|
||||
#define SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX SCU_EVENT_TYPE(0x05)
|
||||
#define SCU_EVENT_TYPE_RNC_OPS_MISC SCU_EVENT_TYPE(0x06)
|
||||
#define SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT SCU_EVENT_TYPE(0x07)
|
||||
#define SCU_EVENT_TYPE_ERR_CNT_EVENT SCU_EVENT_TYPE(0x0A)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SCU_EVENT_SPECIFIERS
|
||||
*/
|
||||
#define SCU_EVENT_SPECIFIER_DRIVER_SUSPEND 0x20
|
||||
#define SCU_EVENT_SPECIFIER_RNC_RELEASE 0x00
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SMU_COMMAND_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_INVALID_CONTEXT_COMMAND \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_COMMAND_ERROR, 0x00)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SMU_PCQ_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_UNCORRECTABLE_PCQ_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_PCQ_ERROR, 0x00)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SMU_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_UNCORRECTABLE_REGISTER_WRITE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x02)
|
||||
#define SCU_EVENT_UNCORRECTABLE_REGISTER_READ \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x03)
|
||||
#define SCU_EVENT_PCIE_INTERFACE_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x04)
|
||||
#define SCU_EVENT_FUNCTION_LEVEL_RESET \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x05)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* TRANSPORT_LEVEL_ERRORS
|
||||
*/
|
||||
#define SCU_EVENT_ACK_NAK_TIMEOUT_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_TRANSPORT_ERROR, 0x00)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* BROADCAST_CHANGE_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_BROADCAST_CHANGE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x01)
|
||||
#define SCU_EVENT_BROADCAST_RESERVED0 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x02)
|
||||
#define SCU_EVENT_BROADCAST_RESERVED1 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x03)
|
||||
#define SCU_EVENT_BROADCAST_SES \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x04)
|
||||
#define SCU_EVENT_BROADCAST_EXPANDER \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x05)
|
||||
#define SCU_EVENT_BROADCAST_AEN \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x06)
|
||||
#define SCU_EVENT_BROADCAST_RESERVED3 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x07)
|
||||
#define SCU_EVENT_BROADCAST_RESERVED4 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x08)
|
||||
#define SCU_EVENT_PE_SUSPENDED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x09)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* OSSP_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_PORT_SELECTOR_DETECTED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x10)
|
||||
#define SCU_EVENT_SENT_PORT_SELECTION \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x11)
|
||||
#define SCU_EVENT_HARD_RESET_TRANSMITTED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x12)
|
||||
#define SCU_EVENT_HARD_RESET_RECEIVED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x13)
|
||||
#define SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x15)
|
||||
#define SCU_EVENT_LINK_FAILURE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x16)
|
||||
#define SCU_EVENT_SATA_SPINUP_HOLD \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x17)
|
||||
#define SCU_EVENT_SAS_15_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x18)
|
||||
#define SCU_EVENT_SAS_15 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x19)
|
||||
#define SCU_EVENT_SAS_30_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1A)
|
||||
#define SCU_EVENT_SAS_30 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1B)
|
||||
#define SCU_EVENT_SAS_60_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1C)
|
||||
#define SCU_EVENT_SAS_60 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1D)
|
||||
#define SCU_EVENT_SATA_15_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1E)
|
||||
#define SCU_EVENT_SATA_15 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1F)
|
||||
#define SCU_EVENT_SATA_30_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x20)
|
||||
#define SCU_EVENT_SATA_30 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x21)
|
||||
#define SCU_EVENT_SATA_60_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x22)
|
||||
#define SCU_EVENT_SATA_60 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x23)
|
||||
#define SCU_EVENT_SAS_PHY_DETECTED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x24)
|
||||
#define SCU_EVENT_SATA_PHY_DETECTED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x25)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* FATAL_INTERNAL_MEMORY_ERROR_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_TSC_RNSC_UNCORRECTABLE_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_FATAL_MEMORY_ERROR, 0x00)
|
||||
#define SCU_EVENT_TC_RNC_UNCORRECTABLE_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_FATAL_MEMORY_ERROR, 0x01)
|
||||
#define SCU_EVENT_ZPT_UNCORRECTABLE_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_FATAL_MEMORY_ERROR, 0x02)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* REMOTE_NODE_SUSPEND_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_TL_RNC_SUSPEND_TX \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX, 0x00)
|
||||
#define SCU_EVENT_TL_RNC_SUSPEND_TX_RX \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX, 0x00)
|
||||
#define SCU_EVENT_DRIVER_POST_RNC_SUSPEND_TX \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX, 0x20)
|
||||
#define SCU_EVENT_DRIVER_POST_RNC_SUSPEND_TX_RX \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX, 0x20)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* REMOTE_NODE_MISC_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_POST_RCN_RELEASE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, SCU_EVENT_SPECIFIER_RNC_RELEASE)
|
||||
#define SCU_EVENT_POST_IT_NEXUS_LOSS_TIMER_ENABLE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x01)
|
||||
#define SCU_EVENT_POST_IT_NEXUS_LOSS_TIMER_DISABLE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x02)
|
||||
#define SCU_EVENT_POST_RNC_COMPLETE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x03)
|
||||
#define SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x04)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* ERROR_COUNT_EVENT
|
||||
*/
|
||||
#define SCU_EVENT_RX_CREDIT_BLOCKED_RECEIVED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_ERR_CNT_EVENT, 0x00)
|
||||
#define SCU_EVENT_TX_DONE_CREDIT_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_ERR_CNT_EVENT, 0x01)
|
||||
#define SCU_EVENT_RX_DONE_CREDIT_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_ERR_CNT_EVENT, 0x02)
|
||||
|
||||
/**
|
||||
* scu_get_event_type() -
|
||||
*
|
||||
* This macro returns the SCU event type from the event code.
|
||||
*/
|
||||
#define scu_get_event_type(event_code) \
|
||||
((event_code) & SCU_EVENT_TYPE_CODE_MASK)
|
||||
|
||||
/**
|
||||
* scu_get_event_specifier() -
|
||||
*
|
||||
* This macro returns the SCU event specifier from the event code.
|
||||
*/
|
||||
#define scu_get_event_specifier(event_code) \
|
||||
((event_code) & SCU_EVENT_SPECIFIC_CODE_MASK)
|
||||
|
||||
/**
|
||||
* scu_get_event_code() -
|
||||
*
|
||||
* This macro returns the combined SCU event type and SCU event specifier from
|
||||
* the event code.
|
||||
*/
|
||||
#define scu_get_event_code(event_code) \
|
||||
((event_code) & SCU_EVENT_CODE_MASK)
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* PTS_SCHEDULE_EVENT
|
||||
*/
|
||||
#define SCU_EVENT_SMP_RESPONSE_NO_PE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT, 0x00)
|
||||
#define SCU_EVENT_SPECIFIC_SMP_RESPONSE_NO_PE \
|
||||
scu_get_event_specifier(SCU_EVENT_SMP_RESPONSE_NO_PE)
|
||||
|
||||
#define SCU_EVENT_TASK_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT, 0x01)
|
||||
#define SCU_EVENT_SPECIFIC_TASK_TIMEOUT \
|
||||
scu_get_event_specifier(SCU_EVENT_TASK_TIMEOUT)
|
||||
|
||||
#define SCU_EVENT_IT_NEXUS_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT, 0x02)
|
||||
#define SCU_EVENT_SPECIFIC_IT_NEXUS_TIMEOUT \
|
||||
scu_get_event_specifier(SCU_EVENT_IT_NEXUS_TIMEOUT)
|
||||
|
||||
|
||||
#endif /* __SCU_EVENT_CODES_HEADER__ */
|
229
drivers/scsi/isci/scu_remote_node_context.h
Normal file
229
drivers/scsi/isci/scu_remote_node_context.h
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __SCU_REMOTE_NODE_CONTEXT_HEADER__
|
||||
#define __SCU_REMOTE_NODE_CONTEXT_HEADER__
|
||||
|
||||
/**
|
||||
* This file contains the structures and constatns used by the SCU hardware to
|
||||
* describe a remote node context.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct ssp_remote_node_context - This structure contains the SCU hardware
|
||||
* definition for an SSP remote node.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct ssp_remote_node_context {
|
||||
/* WORD 0 */
|
||||
|
||||
/**
|
||||
* This field is the remote node index assigned for this remote node. All
|
||||
* remote nodes must have a unique remote node index. The value of the remote
|
||||
* node index can not exceed the maximum number of remote nodes reported in
|
||||
* the SCU device context capacity register.
|
||||
*/
|
||||
u32 remote_node_index:12;
|
||||
u32 reserved0_1:4;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware how many simultaneous connections that
|
||||
* this remote node will support.
|
||||
*/
|
||||
u32 remote_node_port_width:4;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware which logical port to associate with this
|
||||
* remote node.
|
||||
*/
|
||||
u32 logical_port_index:3;
|
||||
u32 reserved0_2:5;
|
||||
|
||||
/**
|
||||
* This field will enable the I_T nexus loss timer for this remote node.
|
||||
*/
|
||||
u32 nexus_loss_timer_enable:1;
|
||||
|
||||
/**
|
||||
* This field is the for driver debug only and is not used.
|
||||
*/
|
||||
u32 check_bit:1;
|
||||
|
||||
/**
|
||||
* This field must be set to true when the hardware DMAs the remote node
|
||||
* context to the hardware SRAM. When the remote node is being invalidated
|
||||
* this field must be set to false.
|
||||
*/
|
||||
u32 is_valid:1;
|
||||
|
||||
/**
|
||||
* This field must be set to true.
|
||||
*/
|
||||
u32 is_remote_node_context:1;
|
||||
|
||||
/* WORD 1 - 2 */
|
||||
|
||||
/**
|
||||
* This is the low word of the remote device SAS Address
|
||||
*/
|
||||
u32 remote_sas_address_lo;
|
||||
|
||||
/**
|
||||
* This field is the high word of the remote device SAS Address
|
||||
*/
|
||||
u32 remote_sas_address_hi;
|
||||
|
||||
/* WORD 3 */
|
||||
/**
|
||||
* This field reprensets the function number assigned to this remote device.
|
||||
* This value must match the virtual function number that is being used to
|
||||
* communicate to the device.
|
||||
*/
|
||||
u32 function_number:8;
|
||||
u32 reserved3_1:8;
|
||||
|
||||
/**
|
||||
* This field provides the driver a way to cheat on the arbitration wait time
|
||||
* for this remote node.
|
||||
*/
|
||||
u32 arbitration_wait_time:16;
|
||||
|
||||
/* WORD 4 */
|
||||
/**
|
||||
* This field tells the SCU hardware how long this device may occupy the
|
||||
* connection before it must be closed.
|
||||
*/
|
||||
u32 connection_occupancy_timeout:16;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware how long to maintain a connection when
|
||||
* there are no frames being transmitted on the link.
|
||||
*/
|
||||
u32 connection_inactivity_timeout:16;
|
||||
|
||||
/* WORD 5 */
|
||||
/**
|
||||
* This field allows the driver to cheat on the arbitration wait time for this
|
||||
* remote node.
|
||||
*/
|
||||
u32 initial_arbitration_wait_time:16;
|
||||
|
||||
/**
|
||||
* This field is tells the hardware what to program for the connection rate in
|
||||
* the open address frame. See the SAS spec for valid values.
|
||||
*/
|
||||
u32 oaf_connection_rate:4;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware what to program for the features in the
|
||||
* open address frame. See the SAS spec for valid values.
|
||||
*/
|
||||
u32 oaf_features:4;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware what to use for the source zone group in
|
||||
* the open address frame. See the SAS spec for more details on zoning.
|
||||
*/
|
||||
u32 oaf_source_zone_group:8;
|
||||
|
||||
/* WORD 6 */
|
||||
/**
|
||||
* This field tells the SCU hardware what to use as the more capibilities in
|
||||
* the open address frame. See the SAS Spec for details.
|
||||
*/
|
||||
u32 oaf_more_compatibility_features;
|
||||
|
||||
/* WORD 7 */
|
||||
u32 reserved7;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stp_remote_node_context - This structure contains the SCU hardware
|
||||
* definition for a STP remote node.
|
||||
*
|
||||
* STP Targets are not yet supported so this definition is a placeholder until
|
||||
* we do support them.
|
||||
*/
|
||||
struct stp_remote_node_context {
|
||||
/**
|
||||
* Placeholder data for the STP remote node.
|
||||
*/
|
||||
u32 data[8];
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* This union combines the SAS and SATA remote node definitions.
|
||||
*
|
||||
* union scu_remote_node_context
|
||||
*/
|
||||
union scu_remote_node_context {
|
||||
/**
|
||||
* SSP Remote Node
|
||||
*/
|
||||
struct ssp_remote_node_context ssp;
|
||||
|
||||
/**
|
||||
* STP Remote Node
|
||||
*/
|
||||
struct stp_remote_node_context stp;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __SCU_REMOTE_NODE_CONTEXT_HEADER__ */
|
965
drivers/scsi/isci/scu_task_context.h
Normal file
965
drivers/scsi/isci/scu_task_context.h
Normal file
|
@ -0,0 +1,965 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SCU_TASK_CONTEXT_H_
|
||||
#define _SCU_TASK_CONTEXT_H_
|
||||
|
||||
/**
|
||||
* This file contains the structures and constants for the SCU hardware task
|
||||
* context.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* enum scu_ssp_task_type - This enumberation defines the various SSP task
|
||||
* types the SCU hardware will accept. The definition for the various task
|
||||
* types the SCU hardware will accept can be found in the DS specification.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
SCU_TASK_TYPE_IOREAD, /* /< IO READ direction or no direction */
|
||||
SCU_TASK_TYPE_IOWRITE, /* /< IO Write direction */
|
||||
SCU_TASK_TYPE_SMP_REQUEST, /* /< SMP Request type */
|
||||
SCU_TASK_TYPE_RESPONSE, /* /< Driver generated response frame (targt mode) */
|
||||
SCU_TASK_TYPE_RAW_FRAME, /* /< Raw frame request type */
|
||||
SCU_TASK_TYPE_PRIMITIVE /* /< Request for a primitive to be transmitted */
|
||||
} scu_ssp_task_type;
|
||||
|
||||
/**
|
||||
* enum scu_sata_task_type - This enumeration defines the various SATA task
|
||||
* types the SCU hardware will accept. The definition for the various task
|
||||
* types the SCU hardware will accept can be found in the DS specification.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
SCU_TASK_TYPE_DMA_IN, /* /< Read request */
|
||||
SCU_TASK_TYPE_FPDMAQ_READ, /* /< NCQ read request */
|
||||
SCU_TASK_TYPE_PACKET_DMA_IN, /* /< Packet read request */
|
||||
SCU_TASK_TYPE_SATA_RAW_FRAME, /* /< Raw frame request */
|
||||
RESERVED_4,
|
||||
RESERVED_5,
|
||||
RESERVED_6,
|
||||
RESERVED_7,
|
||||
SCU_TASK_TYPE_DMA_OUT, /* /< Write request */
|
||||
SCU_TASK_TYPE_FPDMAQ_WRITE, /* /< NCQ write Request */
|
||||
SCU_TASK_TYPE_PACKET_DMA_OUT /* /< Packet write request */
|
||||
} scu_sata_task_type;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SCU_CONTEXT_TYPE
|
||||
*/
|
||||
#define SCU_TASK_CONTEXT_TYPE 0
|
||||
#define SCU_RNC_CONTEXT_TYPE 1
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SCU_TASK_CONTEXT_VALIDITY
|
||||
*/
|
||||
#define SCU_TASK_CONTEXT_INVALID 0
|
||||
#define SCU_TASK_CONTEXT_VALID 1
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SCU_COMMAND_CODE
|
||||
*/
|
||||
#define SCU_COMMAND_CODE_INITIATOR_NEW_TASK 0
|
||||
#define SCU_COMMAND_CODE_ACTIVE_TASK 1
|
||||
#define SCU_COMMAND_CODE_PRIMITIVE_SEQ_TASK 2
|
||||
#define SCU_COMMAND_CODE_TARGET_RAW_FRAMES 3
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SCU_TASK_PRIORITY
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This priority is used when there is no priority request for this request.
|
||||
*/
|
||||
#define SCU_TASK_PRIORITY_NORMAL 0
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This priority indicates that the task should be scheduled to the head of the
|
||||
* queue. The task will NOT be executed if the TX is suspended for the remote
|
||||
* node.
|
||||
*/
|
||||
#define SCU_TASK_PRIORITY_HEAD_OF_Q 1
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This priority indicates that the task will be executed before all
|
||||
* SCU_TASK_PRIORITY_NORMAL and SCU_TASK_PRIORITY_HEAD_OF_Q tasks. The task
|
||||
* WILL be executed if the TX is suspended for the remote node.
|
||||
*/
|
||||
#define SCU_TASK_PRIORITY_HIGH 2
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This task priority is reserved and should not be used.
|
||||
*/
|
||||
#define SCU_TASK_PRIORITY_RESERVED 3
|
||||
|
||||
#define SCU_TASK_INITIATOR_MODE 1
|
||||
#define SCU_TASK_TARGET_MODE 0
|
||||
|
||||
#define SCU_TASK_REGULAR 0
|
||||
#define SCU_TASK_ABORTED 1
|
||||
|
||||
/* direction bit defintion */
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SATA_DIRECTION
|
||||
*/
|
||||
#define SCU_SATA_WRITE_DATA_DIRECTION 0
|
||||
#define SCU_SATA_READ_DATA_DIRECTION 1
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SCU_COMMAND_CONTEXT_MACROS These macros provide the mask and shift
|
||||
* operations to construct the various SCU commands
|
||||
*/
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_SHIFT 21
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_MASK 0x00E00000
|
||||
#define scu_get_command_request_type(x) \
|
||||
((x) & SCU_CONTEXT_COMMAND_REQUEST_TYPE_MASK)
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_SHIFT 18
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK 0x001C0000
|
||||
#define scu_get_command_request_subtype(x) \
|
||||
((x) & SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK)
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_FULLTYPE_MASK \
|
||||
(\
|
||||
SCU_CONTEXT_COMMAND_REQUEST_TYPE_MASK \
|
||||
| SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK \
|
||||
)
|
||||
#define scu_get_command_request_full_type(x) \
|
||||
((x) & SCU_CONTEXT_COMMAND_REQUEST_FULLTYPE_MASK)
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT 16
|
||||
#define SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_MASK 0x00010000
|
||||
#define scu_get_command_protocl_engine_group(x) \
|
||||
((x) & SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_MASK)
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT 12
|
||||
#define SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK 0x00007000
|
||||
#define scu_get_command_reqeust_logical_port(x) \
|
||||
((x) & SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK)
|
||||
|
||||
|
||||
#define MAKE_SCU_CONTEXT_COMMAND_TYPE(type) \
|
||||
((u32)(type) << SCU_CONTEXT_COMMAND_REQUEST_TYPE_SHIFT)
|
||||
|
||||
/**
|
||||
* MAKE_SCU_CONTEXT_COMMAND_TYPE() -
|
||||
*
|
||||
* SCU_COMMAND_TYPES These constants provide the grouping of the different SCU
|
||||
* command types.
|
||||
*/
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC MAKE_SCU_CONTEXT_COMMAND_TYPE(0)
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_TC MAKE_SCU_CONTEXT_COMMAND_TYPE(1)
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC MAKE_SCU_CONTEXT_COMMAND_TYPE(2)
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_RNC MAKE_SCU_CONTEXT_COMMAND_TYPE(3)
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC MAKE_SCU_CONTEXT_COMMAND_TYPE(6)
|
||||
|
||||
#define MAKE_SCU_CONTEXT_COMMAND_REQUEST(type, command) \
|
||||
((type) | ((command) << SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_SHIFT))
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SCU_REQUEST_TYPES These constants are the various request types that can be
|
||||
* posted to the SCU hardware.
|
||||
*/
|
||||
#define SCU_CONTEXT_COMMAND_REQUST_POST_TC \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC, 0))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC, 1))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_REQUST_DUMP_TC \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_TC, 0))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_POST_RNC_32 \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC, 0))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_POST_RNC_96 \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC, 1))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC, 2))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_DUMP_RNC_32 \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_RNC, 0))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_DUMP_RNC_96 \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_RNC, 1))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 0))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 1))
|
||||
|
||||
#define SCU_CONTEXT_COMMAND_POST_RNC_RESUME \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 2))
|
||||
|
||||
#define SCU_CONTEXT_IT_NEXUS_LOSS_TIMER_ENABLE \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 3))
|
||||
|
||||
#define SCU_CONTEXT_IT_NEXUS_LOSS_TIMER_DISABLE \
|
||||
(MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 4))
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SCU_TASK_CONTEXT_PROTOCOL SCU Task context protocol types this is uesd to
|
||||
* program the SCU Task context protocol field in word 0x00.
|
||||
*/
|
||||
#define SCU_TASK_CONTEXT_PROTOCOL_SMP 0x00
|
||||
#define SCU_TASK_CONTEXT_PROTOCOL_SSP 0x01
|
||||
#define SCU_TASK_CONTEXT_PROTOCOL_STP 0x02
|
||||
#define SCU_TASK_CONTEXT_PROTOCOL_NONE 0x07
|
||||
|
||||
/**
|
||||
* struct ssp_task_context - This is the SCU hardware definition for an SSP
|
||||
* request.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct ssp_task_context {
|
||||
/* OFFSET 0x18 */
|
||||
u32 reserved00:24;
|
||||
u32 frame_type:8;
|
||||
|
||||
/* OFFSET 0x1C */
|
||||
u32 reserved01;
|
||||
|
||||
/* OFFSET 0x20 */
|
||||
u32 fill_bytes:2;
|
||||
u32 reserved02:6;
|
||||
u32 changing_data_pointer:1;
|
||||
u32 retransmit:1;
|
||||
u32 retry_data_frame:1;
|
||||
u32 tlr_control:2;
|
||||
u32 reserved03:19;
|
||||
|
||||
/* OFFSET 0x24 */
|
||||
u32 uiRsvd4;
|
||||
|
||||
/* OFFSET 0x28 */
|
||||
u32 target_port_transfer_tag:16;
|
||||
u32 tag:16;
|
||||
|
||||
/* OFFSET 0x2C */
|
||||
u32 data_offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stp_task_context - This is the SCU hardware definition for an STP
|
||||
* request.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct stp_task_context {
|
||||
/* OFFSET 0x18 */
|
||||
u32 fis_type:8;
|
||||
u32 pm_port:4;
|
||||
u32 reserved0:3;
|
||||
u32 control:1;
|
||||
u32 command:8;
|
||||
u32 features:8;
|
||||
|
||||
/* OFFSET 0x1C */
|
||||
u32 reserved1;
|
||||
|
||||
/* OFFSET 0x20 */
|
||||
u32 reserved2;
|
||||
|
||||
/* OFFSET 0x24 */
|
||||
u32 reserved3;
|
||||
|
||||
/* OFFSET 0x28 */
|
||||
u32 ncq_tag:5;
|
||||
u32 reserved4:27;
|
||||
|
||||
/* OFFSET 0x2C */
|
||||
u32 data_offset; /* TODO: What is this used for? */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct smp_task_context - This is the SCU hardware definition for an SMP
|
||||
* request.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct smp_task_context {
|
||||
/* OFFSET 0x18 */
|
||||
u32 response_length:8;
|
||||
u32 function_result:8;
|
||||
u32 function:8;
|
||||
u32 frame_type:8;
|
||||
|
||||
/* OFFSET 0x1C */
|
||||
u32 smp_response_ufi:12;
|
||||
u32 reserved1:20;
|
||||
|
||||
/* OFFSET 0x20 */
|
||||
u32 reserved2;
|
||||
|
||||
/* OFFSET 0x24 */
|
||||
u32 reserved3;
|
||||
|
||||
/* OFFSET 0x28 */
|
||||
u32 reserved4;
|
||||
|
||||
/* OFFSET 0x2C */
|
||||
u32 reserved5;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct primitive_task_context - This is the SCU hardware definition used
|
||||
* when the driver wants to send a primitive on the link.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct primitive_task_context {
|
||||
/* OFFSET 0x18 */
|
||||
/**
|
||||
* This field is the control word and it must be 0.
|
||||
*/
|
||||
u32 control; /* /< must be set to 0 */
|
||||
|
||||
/* OFFSET 0x1C */
|
||||
/**
|
||||
* This field specifies the primitive that is to be transmitted.
|
||||
*/
|
||||
u32 sequence;
|
||||
|
||||
/* OFFSET 0x20 */
|
||||
u32 reserved0;
|
||||
|
||||
/* OFFSET 0x24 */
|
||||
u32 reserved1;
|
||||
|
||||
/* OFFSET 0x28 */
|
||||
u32 reserved2;
|
||||
|
||||
/* OFFSET 0x2C */
|
||||
u32 reserved3;
|
||||
};
|
||||
|
||||
/**
|
||||
* The union of the protocols that can be selected in the SCU task context
|
||||
* field.
|
||||
*
|
||||
* protocol_context
|
||||
*/
|
||||
union protocol_context {
|
||||
struct ssp_task_context ssp;
|
||||
struct stp_task_context stp;
|
||||
struct smp_task_context smp;
|
||||
struct primitive_task_context primitive;
|
||||
u32 words[6];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scu_sgl_element - This structure represents a single SCU defined SGL
|
||||
* element. SCU SGLs contain a 64 bit address with the maximum data transfer
|
||||
* being 24 bits in size. The SGL can not cross a 4GB boundary.
|
||||
*
|
||||
* struct scu_sgl_element
|
||||
*/
|
||||
struct scu_sgl_element {
|
||||
/**
|
||||
* This field is the upper 32 bits of the 64 bit physical address.
|
||||
*/
|
||||
u32 address_upper;
|
||||
|
||||
/**
|
||||
* This field is the lower 32 bits of the 64 bit physical address.
|
||||
*/
|
||||
u32 address_lower;
|
||||
|
||||
/**
|
||||
* This field is the number of bytes to transfer.
|
||||
*/
|
||||
u32 length:24;
|
||||
|
||||
/**
|
||||
* This field is the address modifier to be used when a virtual function is
|
||||
* requesting a data transfer.
|
||||
*/
|
||||
u32 address_modifier:8;
|
||||
|
||||
};
|
||||
|
||||
#define SCU_SGL_ELEMENT_PAIR_A 0
|
||||
#define SCU_SGL_ELEMENT_PAIR_B 1
|
||||
|
||||
/**
|
||||
* struct scu_sgl_element_pair - This structure is the SCU hardware definition
|
||||
* of a pair of SGL elements. The SCU hardware always works on SGL pairs.
|
||||
* They are refered to in the DS specification as SGL A and SGL B. Each SGL
|
||||
* pair is followed by the address of the next pair.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct scu_sgl_element_pair {
|
||||
/* OFFSET 0x60-0x68 */
|
||||
/**
|
||||
* This field is the SGL element A of the SGL pair.
|
||||
*/
|
||||
struct scu_sgl_element A;
|
||||
|
||||
/* OFFSET 0x6C-0x74 */
|
||||
/**
|
||||
* This field is the SGL element B of the SGL pair.
|
||||
*/
|
||||
struct scu_sgl_element B;
|
||||
|
||||
/* OFFSET 0x78-0x7C */
|
||||
/**
|
||||
* This field is the upper 32 bits of the 64 bit address to the next SGL
|
||||
* element pair.
|
||||
*/
|
||||
u32 next_pair_upper;
|
||||
|
||||
/**
|
||||
* This field is the lower 32 bits of the 64 bit address to the next SGL
|
||||
* element pair.
|
||||
*/
|
||||
u32 next_pair_lower;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct transport_snapshot - This structure is the SCU hardware scratch area
|
||||
* for the task context. This is set to 0 by the driver but can be read by
|
||||
* issuing a dump TC request to the SCU.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct transport_snapshot {
|
||||
/* OFFSET 0x48 */
|
||||
u32 xfer_rdy_write_data_length;
|
||||
|
||||
/* OFFSET 0x4C */
|
||||
u32 data_offset;
|
||||
|
||||
/* OFFSET 0x50 */
|
||||
u32 data_transfer_size:24;
|
||||
u32 reserved_50_0:8;
|
||||
|
||||
/* OFFSET 0x54 */
|
||||
u32 next_initiator_write_data_offset;
|
||||
|
||||
/* OFFSET 0x58 */
|
||||
u32 next_initiator_write_data_xfer_size:24;
|
||||
u32 reserved_58_0:8;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scu_task_context - This structure defines the contents of the SCU
|
||||
* silicon task context. It lays out all of the fields according to the
|
||||
* expected order and location for the Storage Controller unit.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct scu_task_context {
|
||||
/* OFFSET 0x00 ------ */
|
||||
/**
|
||||
* This field must be encoded to one of the valid SCU task priority values
|
||||
* - SCU_TASK_PRIORITY_NORMAL
|
||||
* - SCU_TASK_PRIORITY_HEAD_OF_Q
|
||||
* - SCU_TASK_PRIORITY_HIGH
|
||||
*/
|
||||
u32 priority:2;
|
||||
|
||||
/**
|
||||
* This field must be set to true if this is an initiator generated request.
|
||||
* Until target mode is supported all task requests are initiator requests.
|
||||
*/
|
||||
u32 initiator_request:1;
|
||||
|
||||
/**
|
||||
* This field must be set to one of the valid connection rates valid values
|
||||
* are 0x8, 0x9, and 0xA.
|
||||
*/
|
||||
u32 connection_rate:4;
|
||||
|
||||
/**
|
||||
* This field muse be programed when generating an SMP response since the SMP
|
||||
* connection remains open until the SMP response is generated.
|
||||
*/
|
||||
u32 protocol_engine_index:3;
|
||||
|
||||
/**
|
||||
* This field must contain the logical port for the task request.
|
||||
*/
|
||||
u32 logical_port_index:3;
|
||||
|
||||
/**
|
||||
* This field must be set to one of the SCU_TASK_CONTEXT_PROTOCOL values
|
||||
* - SCU_TASK_CONTEXT_PROTOCOL_SMP
|
||||
* - SCU_TASK_CONTEXT_PROTOCOL_SSP
|
||||
* - SCU_TASK_CONTEXT_PROTOCOL_STP
|
||||
* - SCU_TASK_CONTEXT_PROTOCOL_NONE
|
||||
*/
|
||||
u32 protocol_type:3;
|
||||
|
||||
/**
|
||||
* This filed must be set to the TCi allocated for this task
|
||||
*/
|
||||
u32 task_index:12;
|
||||
|
||||
/**
|
||||
* This field is reserved and must be set to 0x00
|
||||
*/
|
||||
u32 reserved_00_0:1;
|
||||
|
||||
/**
|
||||
* For a normal task request this must be set to 0. If this is an abort of
|
||||
* this task request it must be set to 1.
|
||||
*/
|
||||
u32 abort:1;
|
||||
|
||||
/**
|
||||
* This field must be set to true for the SCU hardware to process the task.
|
||||
*/
|
||||
u32 valid:1;
|
||||
|
||||
/**
|
||||
* This field must be set to SCU_TASK_CONTEXT_TYPE
|
||||
*/
|
||||
u32 context_type:1;
|
||||
|
||||
/* OFFSET 0x04 */
|
||||
/**
|
||||
* This field contains the RNi that is the target of this request.
|
||||
*/
|
||||
u32 remote_node_index:12;
|
||||
|
||||
/**
|
||||
* This field is programmed if this is a mirrored request, which we are not
|
||||
* using, in which case it is the RNi for the mirrored target.
|
||||
*/
|
||||
u32 mirrored_node_index:12;
|
||||
|
||||
/**
|
||||
* This field is programmed with the direction of the SATA reqeust
|
||||
* - SCU_SATA_WRITE_DATA_DIRECTION
|
||||
* - SCU_SATA_READ_DATA_DIRECTION
|
||||
*/
|
||||
u32 sata_direction:1;
|
||||
|
||||
/**
|
||||
* This field is programmsed with one of the following SCU_COMMAND_CODE
|
||||
* - SCU_COMMAND_CODE_INITIATOR_NEW_TASK
|
||||
* - SCU_COMMAND_CODE_ACTIVE_TASK
|
||||
* - SCU_COMMAND_CODE_PRIMITIVE_SEQ_TASK
|
||||
* - SCU_COMMAND_CODE_TARGET_RAW_FRAMES
|
||||
*/
|
||||
u32 command_code:2;
|
||||
|
||||
/**
|
||||
* This field is set to true if the remote node should be suspended.
|
||||
* This bit is only valid for SSP & SMP target devices.
|
||||
*/
|
||||
u32 suspend_node:1;
|
||||
|
||||
/**
|
||||
* This field is programmed with one of the following command type codes
|
||||
*
|
||||
* For SAS requests use the scu_ssp_task_type
|
||||
* - SCU_TASK_TYPE_IOREAD
|
||||
* - SCU_TASK_TYPE_IOWRITE
|
||||
* - SCU_TASK_TYPE_SMP_REQUEST
|
||||
* - SCU_TASK_TYPE_RESPONSE
|
||||
* - SCU_TASK_TYPE_RAW_FRAME
|
||||
* - SCU_TASK_TYPE_PRIMITIVE
|
||||
*
|
||||
* For SATA requests use the scu_sata_task_type
|
||||
* - SCU_TASK_TYPE_DMA_IN
|
||||
* - SCU_TASK_TYPE_FPDMAQ_READ
|
||||
* - SCU_TASK_TYPE_PACKET_DMA_IN
|
||||
* - SCU_TASK_TYPE_SATA_RAW_FRAME
|
||||
* - SCU_TASK_TYPE_DMA_OUT
|
||||
* - SCU_TASK_TYPE_FPDMAQ_WRITE
|
||||
* - SCU_TASK_TYPE_PACKET_DMA_OUT
|
||||
*/
|
||||
u32 task_type:4;
|
||||
|
||||
/* OFFSET 0x08 */
|
||||
/**
|
||||
* This field is reserved and the must be set to 0x00
|
||||
*/
|
||||
u32 link_layer_control:8; /* presently all reserved */
|
||||
|
||||
/**
|
||||
* This field is set to true when TLR is to be enabled
|
||||
*/
|
||||
u32 ssp_tlr_enable:1;
|
||||
|
||||
/**
|
||||
* This is field specifies if the SCU DMAs a response frame to host
|
||||
* memory for good response frames when operating in target mode.
|
||||
*/
|
||||
u32 dma_ssp_target_good_response:1;
|
||||
|
||||
/**
|
||||
* This field indicates if the SCU should DMA the response frame to
|
||||
* host memory.
|
||||
*/
|
||||
u32 do_not_dma_ssp_good_response:1;
|
||||
|
||||
/**
|
||||
* This field is set to true when strict ordering is to be enabled
|
||||
*/
|
||||
u32 strict_ordering:1;
|
||||
|
||||
/**
|
||||
* This field indicates the type of endianess to be utilized for the
|
||||
* frame. command, task, and response frames utilized control_frame
|
||||
* set to 1.
|
||||
*/
|
||||
u32 control_frame:1;
|
||||
|
||||
/**
|
||||
* This field is reserved and the driver should set to 0x00
|
||||
*/
|
||||
u32 tl_control_reserved:3;
|
||||
|
||||
/**
|
||||
* This field is set to true when the SCU hardware task timeout control is to
|
||||
* be enabled
|
||||
*/
|
||||
u32 timeout_enable:1;
|
||||
|
||||
/**
|
||||
* This field is reserved and the driver should set it to 0x00
|
||||
*/
|
||||
u32 pts_control_reserved:7;
|
||||
|
||||
/**
|
||||
* This field should be set to true when block guard is to be enabled
|
||||
*/
|
||||
u32 block_guard_enable:1;
|
||||
|
||||
/**
|
||||
* This field is reserved and the driver should set to 0x00
|
||||
*/
|
||||
u32 sdma_control_reserved:7;
|
||||
|
||||
/* OFFSET 0x0C */
|
||||
/**
|
||||
* This field is the address modifier for this io request it should be
|
||||
* programmed with the virtual function that is making the request.
|
||||
*/
|
||||
u32 address_modifier:16;
|
||||
|
||||
/**
|
||||
* @todo What we support mirrored SMP response frame?
|
||||
*/
|
||||
u32 mirrored_protocol_engine:3; /* mirrored protocol Engine Index */
|
||||
|
||||
/**
|
||||
* If this is a mirrored request the logical port index for the mirrored RNi
|
||||
* must be programmed.
|
||||
*/
|
||||
u32 mirrored_logical_port:4; /* mirrored local port index */
|
||||
|
||||
/**
|
||||
* This field is reserved and the driver must set it to 0x00
|
||||
*/
|
||||
u32 reserved_0C_0:8;
|
||||
|
||||
/**
|
||||
* This field must be set to true if the mirrored request processing is to be
|
||||
* enabled.
|
||||
*/
|
||||
u32 mirror_request_enable:1; /* Mirrored request Enable */
|
||||
|
||||
/* OFFSET 0x10 */
|
||||
/**
|
||||
* This field is the command iu length in dwords
|
||||
*/
|
||||
u32 ssp_command_iu_length:8;
|
||||
|
||||
/**
|
||||
* This is the target TLR enable bit it must be set to 0 when creatning the
|
||||
* task context.
|
||||
*/
|
||||
u32 xfer_ready_tlr_enable:1;
|
||||
|
||||
/**
|
||||
* This field is reserved and the driver must set it to 0x00
|
||||
*/
|
||||
u32 reserved_10_0:7;
|
||||
|
||||
/**
|
||||
* This is the maximum burst size that the SCU hardware will send in one
|
||||
* connection its value is (N x 512) and N must be a multiple of 2. If the
|
||||
* value is 0x00 then maximum burst size is disabled.
|
||||
*/
|
||||
u32 ssp_max_burst_size:16;
|
||||
|
||||
/* OFFSET 0x14 */
|
||||
/**
|
||||
* This filed is set to the number of bytes to be transfered in the request.
|
||||
*/
|
||||
u32 transfer_length_bytes:24; /* In terms of bytes */
|
||||
|
||||
/**
|
||||
* This field is reserved and the driver should set it to 0x00
|
||||
*/
|
||||
u32 reserved_14_0:8;
|
||||
|
||||
/* OFFSET 0x18-0x2C */
|
||||
/**
|
||||
* This union provides for the protocol specif part of the SCU Task Context.
|
||||
*/
|
||||
union protocol_context type;
|
||||
|
||||
/* OFFSET 0x30-0x34 */
|
||||
/**
|
||||
* This field is the upper 32 bits of the 64 bit physical address of the
|
||||
* command iu buffer
|
||||
*/
|
||||
u32 command_iu_upper;
|
||||
|
||||
/**
|
||||
* This field is the lower 32 bits of the 64 bit physical address of the
|
||||
* command iu buffer
|
||||
*/
|
||||
u32 command_iu_lower;
|
||||
|
||||
/* OFFSET 0x38-0x3C */
|
||||
/**
|
||||
* This field is the upper 32 bits of the 64 bit physical address of the
|
||||
* response iu buffer
|
||||
*/
|
||||
u32 response_iu_upper;
|
||||
|
||||
/**
|
||||
* This field is the lower 32 bits of the 64 bit physical address of the
|
||||
* response iu buffer
|
||||
*/
|
||||
u32 response_iu_lower;
|
||||
|
||||
/* OFFSET 0x40 */
|
||||
/**
|
||||
* This field is set to the task phase of the SCU hardware. The driver must
|
||||
* set this to 0x01
|
||||
*/
|
||||
u32 task_phase:8;
|
||||
|
||||
/**
|
||||
* This field is set to the transport layer task status. The driver must set
|
||||
* this to 0x00
|
||||
*/
|
||||
u32 task_status:8;
|
||||
|
||||
/**
|
||||
* This field is used during initiator write TLR
|
||||
*/
|
||||
u32 previous_extended_tag:4;
|
||||
|
||||
/**
|
||||
* This field is set the maximum number of retries for a STP non-data FIS
|
||||
*/
|
||||
u32 stp_retry_count:2;
|
||||
|
||||
/**
|
||||
* This field is reserved and the driver must set it to 0x00
|
||||
*/
|
||||
u32 reserved_40_1:2;
|
||||
|
||||
/**
|
||||
* This field is used by the SCU TL to determine when to take a snapshot when
|
||||
* tranmitting read data frames.
|
||||
* - 0x00 The entire IO
|
||||
* - 0x01 32k
|
||||
* - 0x02 64k
|
||||
* - 0x04 128k
|
||||
* - 0x08 256k
|
||||
*/
|
||||
u32 ssp_tlr_threshold:4;
|
||||
|
||||
/**
|
||||
* This field is reserved and the driver must set it to 0x00
|
||||
*/
|
||||
u32 reserved_40_2:4;
|
||||
|
||||
/* OFFSET 0x44 */
|
||||
u32 write_data_length; /* read only set to 0 */
|
||||
|
||||
/* OFFSET 0x48-0x58 */
|
||||
struct transport_snapshot snapshot; /* read only set to 0 */
|
||||
|
||||
/* OFFSET 0x5C */
|
||||
u32 blk_prot_en:1;
|
||||
u32 blk_sz:2;
|
||||
u32 blk_prot_func:2;
|
||||
u32 reserved_5C_0:9;
|
||||
u32 active_sgl_element:2; /* read only set to 0 */
|
||||
u32 sgl_exhausted:1; /* read only set to 0 */
|
||||
u32 payload_data_transfer_error:4; /* read only set to 0 */
|
||||
u32 frame_buffer_offset:11; /* read only set to 0 */
|
||||
|
||||
/* OFFSET 0x60-0x7C */
|
||||
/**
|
||||
* This field is the first SGL element pair found in the TC data structure.
|
||||
*/
|
||||
struct scu_sgl_element_pair sgl_pair_ab;
|
||||
/* OFFSET 0x80-0x9C */
|
||||
/**
|
||||
* This field is the second SGL element pair found in the TC data structure.
|
||||
*/
|
||||
struct scu_sgl_element_pair sgl_pair_cd;
|
||||
|
||||
/* OFFSET 0xA0-BC */
|
||||
struct scu_sgl_element_pair sgl_snapshot_ac;
|
||||
|
||||
/* OFFSET 0xC0 */
|
||||
u32 active_sgl_element_pair; /* read only set to 0 */
|
||||
|
||||
/* OFFSET 0xC4-0xCC */
|
||||
u32 reserved_C4_CC[3];
|
||||
|
||||
/* OFFSET 0xD0 */
|
||||
u32 interm_crc_val:16;
|
||||
u32 init_crc_seed:16;
|
||||
|
||||
/* OFFSET 0xD4 */
|
||||
u32 app_tag_verify:16;
|
||||
u32 app_tag_gen:16;
|
||||
|
||||
/* OFFSET 0xD8 */
|
||||
u32 ref_tag_seed_verify;
|
||||
|
||||
/* OFFSET 0xDC */
|
||||
u32 UD_bytes_immed_val:13;
|
||||
u32 reserved_DC_0:3;
|
||||
u32 DIF_bytes_immed_val:4;
|
||||
u32 reserved_DC_1:12;
|
||||
|
||||
/* OFFSET 0xE0 */
|
||||
u32 bgc_blk_sz:13;
|
||||
u32 reserved_E0_0:3;
|
||||
u32 app_tag_gen_mask:16;
|
||||
|
||||
/* OFFSET 0xE4 */
|
||||
union {
|
||||
u16 bgctl;
|
||||
struct {
|
||||
u16 crc_verify:1;
|
||||
u16 app_tag_chk:1;
|
||||
u16 ref_tag_chk:1;
|
||||
u16 op:2;
|
||||
u16 legacy:1;
|
||||
u16 invert_crc_seed:1;
|
||||
u16 ref_tag_gen:1;
|
||||
u16 fixed_ref_tag:1;
|
||||
u16 invert_crc:1;
|
||||
u16 app_ref_f_detect:1;
|
||||
u16 uninit_dif_check_err:1;
|
||||
u16 uninit_dif_bypass:1;
|
||||
u16 app_f_detect:1;
|
||||
u16 reserved_0:2;
|
||||
} bgctl_f;
|
||||
};
|
||||
|
||||
u16 app_tag_verify_mask;
|
||||
|
||||
/* OFFSET 0xE8 */
|
||||
u32 blk_guard_err:8;
|
||||
u32 reserved_E8_0:24;
|
||||
|
||||
/* OFFSET 0xEC */
|
||||
u32 ref_tag_seed_gen;
|
||||
|
||||
/* OFFSET 0xF0 */
|
||||
u32 intermediate_crc_valid_snapshot:16;
|
||||
u32 reserved_F0_0:16;
|
||||
|
||||
/* OFFSET 0xF4 */
|
||||
u32 reference_tag_seed_for_verify_function_snapshot;
|
||||
|
||||
/* OFFSET 0xF8 */
|
||||
u32 snapshot_of_reserved_dword_DC_of_tc;
|
||||
|
||||
/* OFFSET 0xFC */
|
||||
u32 reference_tag_seed_for_generate_function_snapshot;
|
||||
|
||||
} __packed;
|
||||
|
||||
#endif /* _SCU_TASK_CONTEXT_H_ */
|
812
drivers/scsi/isci/task.c
Normal file
812
drivers/scsi/isci/task.c
Normal file
|
@ -0,0 +1,812 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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 <linux/completion.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include "sas.h"
|
||||
#include <scsi/libsas.h>
|
||||
#include "remote_device.h"
|
||||
#include "remote_node_context.h"
|
||||
#include "isci.h"
|
||||
#include "request.h"
|
||||
#include "task.h"
|
||||
#include "host.h"
|
||||
|
||||
/**
|
||||
* isci_task_refuse() - complete the request to the upper layer driver in
|
||||
* the case where an I/O needs to be completed back in the submit path.
|
||||
* @ihost: host on which the the request was queued
|
||||
* @task: request to complete
|
||||
* @response: response code for the completed task.
|
||||
* @status: status code for the completed task.
|
||||
*
|
||||
*/
|
||||
static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task,
|
||||
enum service_response response,
|
||||
enum exec_status status)
|
||||
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* Normal notification (task_done) */
|
||||
dev_dbg(&ihost->pdev->dev, "%s: task = %p, response=%d, status=%d\n",
|
||||
__func__, task, response, status);
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
|
||||
task->task_status.resp = response;
|
||||
task->task_status.stat = status;
|
||||
|
||||
/* Normal notification (task_done) */
|
||||
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
||||
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
|
||||
SAS_TASK_STATE_PENDING);
|
||||
task->lldd_task = NULL;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
task->task_done(task);
|
||||
}
|
||||
|
||||
#define for_each_sas_task(num, task) \
|
||||
for (; num > 0; num--,\
|
||||
task = list_entry(task->list.next, struct sas_task, list))
|
||||
|
||||
|
||||
static inline int isci_device_io_ready(struct isci_remote_device *idev,
|
||||
struct sas_task *task)
|
||||
{
|
||||
return idev ? test_bit(IDEV_IO_READY, &idev->flags) ||
|
||||
(test_bit(IDEV_IO_NCQERROR, &idev->flags) &&
|
||||
isci_task_is_ncq_recovery(task))
|
||||
: 0;
|
||||
}
|
||||
/**
|
||||
* isci_task_execute_task() - This function is one of the SAS Domain Template
|
||||
* functions. This function is called by libsas to send a task down to
|
||||
* hardware.
|
||||
* @task: This parameter specifies the SAS task to send.
|
||||
* @num: This parameter specifies the number of tasks to queue.
|
||||
* @gfp_flags: This parameter specifies the context of this call.
|
||||
*
|
||||
* status, zero indicates success.
|
||||
*/
|
||||
int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags)
|
||||
{
|
||||
struct isci_host *ihost = dev_to_ihost(task->dev);
|
||||
struct isci_remote_device *idev;
|
||||
unsigned long flags;
|
||||
bool io_ready;
|
||||
u16 tag;
|
||||
|
||||
dev_dbg(&ihost->pdev->dev, "%s: num=%d\n", __func__, num);
|
||||
|
||||
for_each_sas_task(num, task) {
|
||||
enum sci_status status = SCI_FAILURE;
|
||||
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
idev = isci_lookup_device(task->dev);
|
||||
io_ready = isci_device_io_ready(idev, task);
|
||||
tag = isci_alloc_tag(ihost);
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"task: %p, num: %d dev: %p idev: %p:%#lx cmd = %p\n",
|
||||
task, num, task->dev, idev, idev ? idev->flags : 0,
|
||||
task->uldd_task);
|
||||
|
||||
if (!idev) {
|
||||
isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED,
|
||||
SAS_DEVICE_UNKNOWN);
|
||||
} else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) {
|
||||
/* Indicate QUEUE_FULL so that the scsi midlayer
|
||||
* retries.
|
||||
*/
|
||||
isci_task_refuse(ihost, task, SAS_TASK_COMPLETE,
|
||||
SAS_QUEUE_FULL);
|
||||
} else {
|
||||
/* There is a device and it's ready for I/O. */
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
|
||||
if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
|
||||
/* The I/O was aborted. */
|
||||
spin_unlock_irqrestore(&task->task_state_lock,
|
||||
flags);
|
||||
|
||||
isci_task_refuse(ihost, task,
|
||||
SAS_TASK_UNDELIVERED,
|
||||
SAM_STAT_TASK_ABORTED);
|
||||
} else {
|
||||
task->task_state_flags |= SAS_TASK_AT_INITIATOR;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
/* build and send the request. */
|
||||
status = isci_request_execute(ihost, idev, task, tag);
|
||||
|
||||
if (status != SCI_SUCCESS) {
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
/* Did not really start this command. */
|
||||
task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
if (test_bit(IDEV_GONE, &idev->flags)) {
|
||||
|
||||
/* Indicate that the device
|
||||
* is gone.
|
||||
*/
|
||||
isci_task_refuse(ihost, task,
|
||||
SAS_TASK_UNDELIVERED,
|
||||
SAS_DEVICE_UNKNOWN);
|
||||
} else {
|
||||
/* Indicate QUEUE_FULL so that
|
||||
* the scsi midlayer retries.
|
||||
* If the request failed for
|
||||
* remote device reasons, it
|
||||
* gets returned as
|
||||
* SAS_TASK_UNDELIVERED next
|
||||
* time through.
|
||||
*/
|
||||
isci_task_refuse(ihost, task,
|
||||
SAS_TASK_COMPLETE,
|
||||
SAS_QUEUE_FULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) {
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
/* command never hit the device, so just free
|
||||
* the tci and skip the sequence increment
|
||||
*/
|
||||
isci_tci_free(ihost, ISCI_TAG_TCI(tag));
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
}
|
||||
isci_put_device(idev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct isci_request *isci_task_request_build(struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
u16 tag, struct isci_tmf *isci_tmf)
|
||||
{
|
||||
enum sci_status status = SCI_FAILURE;
|
||||
struct isci_request *ireq = NULL;
|
||||
struct domain_device *dev;
|
||||
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: isci_tmf = %p\n", __func__, isci_tmf);
|
||||
|
||||
dev = idev->domain_dev;
|
||||
|
||||
/* do common allocation and init of request object. */
|
||||
ireq = isci_tmf_request_from_tag(ihost, isci_tmf, tag);
|
||||
if (!ireq)
|
||||
return NULL;
|
||||
|
||||
/* let the core do it's construct. */
|
||||
status = sci_task_request_construct(ihost, idev, tag,
|
||||
ireq);
|
||||
|
||||
if (status != SCI_SUCCESS) {
|
||||
dev_warn(&ihost->pdev->dev,
|
||||
"%s: sci_task_request_construct failed - "
|
||||
"status = 0x%x\n",
|
||||
__func__,
|
||||
status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* XXX convert to get this from task->tproto like other drivers */
|
||||
if (dev->dev_type == SAS_END_DEVICE) {
|
||||
isci_tmf->proto = SAS_PROTOCOL_SSP;
|
||||
status = sci_task_request_construct_ssp(ireq);
|
||||
if (status != SCI_SUCCESS)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ireq;
|
||||
}
|
||||
|
||||
static int isci_task_execute_tmf(struct isci_host *ihost,
|
||||
struct isci_remote_device *idev,
|
||||
struct isci_tmf *tmf, unsigned long timeout_ms)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(completion);
|
||||
enum sci_task_status status = SCI_TASK_FAILURE;
|
||||
struct isci_request *ireq;
|
||||
int ret = TMF_RESP_FUNC_FAILED;
|
||||
unsigned long flags;
|
||||
unsigned long timeleft;
|
||||
u16 tag;
|
||||
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
tag = isci_alloc_tag(ihost);
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
|
||||
if (tag == SCI_CONTROLLER_INVALID_IO_TAG)
|
||||
return ret;
|
||||
|
||||
/* sanity check, return TMF_RESP_FUNC_FAILED
|
||||
* if the device is not there and ready.
|
||||
*/
|
||||
if (!idev ||
|
||||
(!test_bit(IDEV_IO_READY, &idev->flags) &&
|
||||
!test_bit(IDEV_IO_NCQERROR, &idev->flags))) {
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: idev = %p not ready (%#lx)\n",
|
||||
__func__,
|
||||
idev, idev ? idev->flags : 0);
|
||||
goto err_tci;
|
||||
} else
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: idev = %p\n",
|
||||
__func__, idev);
|
||||
|
||||
/* Assign the pointer to the TMF's completion kernel wait structure. */
|
||||
tmf->complete = &completion;
|
||||
tmf->status = SCI_FAILURE_TIMEOUT;
|
||||
|
||||
ireq = isci_task_request_build(ihost, idev, tag, tmf);
|
||||
if (!ireq)
|
||||
goto err_tci;
|
||||
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
|
||||
/* start the TMF io. */
|
||||
status = sci_controller_start_task(ihost, idev, ireq);
|
||||
|
||||
if (status != SCI_TASK_SUCCESS) {
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: start_io failed - status = 0x%x, request = %p\n",
|
||||
__func__,
|
||||
status,
|
||||
ireq);
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
goto err_tci;
|
||||
}
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
|
||||
/* The RNC must be unsuspended before the TMF can get a response. */
|
||||
isci_remote_device_resume_from_abort(ihost, idev);
|
||||
|
||||
/* Wait for the TMF to complete, or a timeout. */
|
||||
timeleft = wait_for_completion_timeout(&completion,
|
||||
msecs_to_jiffies(timeout_ms));
|
||||
|
||||
if (timeleft == 0) {
|
||||
/* The TMF did not complete - this could be because
|
||||
* of an unplug. Terminate the TMF request now.
|
||||
*/
|
||||
isci_remote_device_suspend_terminate(ihost, idev, ireq);
|
||||
}
|
||||
|
||||
isci_print_tmf(ihost, tmf);
|
||||
|
||||
if (tmf->status == SCI_SUCCESS)
|
||||
ret = TMF_RESP_FUNC_COMPLETE;
|
||||
else if (tmf->status == SCI_FAILURE_IO_RESPONSE_VALID) {
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: tmf.status == "
|
||||
"SCI_FAILURE_IO_RESPONSE_VALID\n",
|
||||
__func__);
|
||||
ret = TMF_RESP_FUNC_COMPLETE;
|
||||
}
|
||||
/* Else - leave the default "failed" status alone. */
|
||||
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: completed request = %p\n",
|
||||
__func__,
|
||||
ireq);
|
||||
|
||||
return ret;
|
||||
|
||||
err_tci:
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
isci_tci_free(ihost, ISCI_TAG_TCI(tag));
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void isci_task_build_tmf(struct isci_tmf *tmf,
|
||||
enum isci_tmf_function_codes code)
|
||||
{
|
||||
memset(tmf, 0, sizeof(*tmf));
|
||||
tmf->tmf_code = code;
|
||||
}
|
||||
|
||||
static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf,
|
||||
enum isci_tmf_function_codes code,
|
||||
struct isci_request *old_request)
|
||||
{
|
||||
isci_task_build_tmf(tmf, code);
|
||||
tmf->io_tag = old_request->io_tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain
|
||||
* Template functions.
|
||||
* @lun: This parameter specifies the lun to be reset.
|
||||
*
|
||||
* status, zero indicates success.
|
||||
*/
|
||||
static int isci_task_send_lu_reset_sas(
|
||||
struct isci_host *isci_host,
|
||||
struct isci_remote_device *isci_device,
|
||||
u8 *lun)
|
||||
{
|
||||
struct isci_tmf tmf;
|
||||
int ret = TMF_RESP_FUNC_FAILED;
|
||||
|
||||
dev_dbg(&isci_host->pdev->dev,
|
||||
"%s: isci_host = %p, isci_device = %p\n",
|
||||
__func__, isci_host, isci_device);
|
||||
/* Send the LUN reset to the target. By the time the call returns,
|
||||
* the TMF has fully exected in the target (in which case the return
|
||||
* value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or
|
||||
* was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED").
|
||||
*/
|
||||
isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset);
|
||||
|
||||
#define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */
|
||||
ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_LU_RESET_TIMEOUT_MS);
|
||||
|
||||
if (ret == TMF_RESP_FUNC_COMPLETE)
|
||||
dev_dbg(&isci_host->pdev->dev,
|
||||
"%s: %p: TMF_LU_RESET passed\n",
|
||||
__func__, isci_device);
|
||||
else
|
||||
dev_dbg(&isci_host->pdev->dev,
|
||||
"%s: %p: TMF_LU_RESET failed (%x)\n",
|
||||
__func__, isci_device, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
struct isci_host *ihost = dev_to_ihost(dev);
|
||||
struct isci_remote_device *idev;
|
||||
unsigned long flags;
|
||||
int ret = TMF_RESP_FUNC_COMPLETE;
|
||||
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
idev = isci_get_device(dev->lldd_dev);
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
|
||||
__func__, dev, ihost, idev);
|
||||
|
||||
if (!idev) {
|
||||
/* If the device is gone, escalate to I_T_Nexus_Reset. */
|
||||
dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__);
|
||||
|
||||
ret = TMF_RESP_FUNC_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Suspend the RNC, kill all TCs */
|
||||
if (isci_remote_device_suspend_terminate(ihost, idev, NULL)
|
||||
!= SCI_SUCCESS) {
|
||||
/* The suspend/terminate only fails if isci_get_device fails */
|
||||
ret = TMF_RESP_FUNC_FAILED;
|
||||
goto out;
|
||||
}
|
||||
/* All pending I/Os have been terminated and cleaned up. */
|
||||
if (!test_bit(IDEV_GONE, &idev->flags)) {
|
||||
if (dev_is_sata(dev))
|
||||
sas_ata_schedule_reset(dev);
|
||||
else
|
||||
/* Send the task management part of the reset. */
|
||||
ret = isci_task_send_lu_reset_sas(ihost, idev, lun);
|
||||
}
|
||||
out:
|
||||
isci_put_device(idev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* int (*lldd_clear_nexus_port)(struct asd_sas_port *); */
|
||||
int isci_task_clear_nexus_port(struct asd_sas_port *port)
|
||||
{
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int isci_task_clear_nexus_ha(struct sas_ha_struct *ha)
|
||||
{
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
}
|
||||
|
||||
/* Task Management Functions. Must be called from process context. */
|
||||
|
||||
/**
|
||||
* isci_task_abort_task() - This function is one of the SAS Domain Template
|
||||
* functions. This function is called by libsas to abort a specified task.
|
||||
* @task: This parameter specifies the SAS task to abort.
|
||||
*
|
||||
* status, zero indicates success.
|
||||
*/
|
||||
int isci_task_abort_task(struct sas_task *task)
|
||||
{
|
||||
struct isci_host *ihost = dev_to_ihost(task->dev);
|
||||
DECLARE_COMPLETION_ONSTACK(aborted_io_completion);
|
||||
struct isci_request *old_request = NULL;
|
||||
struct isci_remote_device *idev = NULL;
|
||||
struct isci_tmf tmf;
|
||||
int ret = TMF_RESP_FUNC_FAILED;
|
||||
unsigned long flags;
|
||||
int target_done_already = 0;
|
||||
|
||||
/* Get the isci_request reference from the task. Note that
|
||||
* this check does not depend on the pending request list
|
||||
* in the device, because tasks driving resets may land here
|
||||
* after completion in the core.
|
||||
*/
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
spin_lock(&task->task_state_lock);
|
||||
|
||||
old_request = task->lldd_task;
|
||||
|
||||
/* If task is already done, the request isn't valid */
|
||||
if (!(task->task_state_flags & SAS_TASK_STATE_DONE) &&
|
||||
(task->task_state_flags & SAS_TASK_AT_INITIATOR) &&
|
||||
old_request) {
|
||||
idev = isci_get_device(task->dev->lldd_dev);
|
||||
target_done_already = test_bit(IREQ_COMPLETE_IN_TARGET,
|
||||
&old_request->flags);
|
||||
}
|
||||
spin_unlock(&task->task_state_lock);
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
|
||||
dev_warn(&ihost->pdev->dev,
|
||||
"%s: dev = %p (%s%s), task = %p, old_request == %p\n",
|
||||
__func__, idev,
|
||||
(dev_is_sata(task->dev) ? "STP/SATA"
|
||||
: ((dev_is_expander(task->dev))
|
||||
? "SMP"
|
||||
: "SSP")),
|
||||
((idev) ? ((test_bit(IDEV_GONE, &idev->flags))
|
||||
? " IDEV_GONE"
|
||||
: "")
|
||||
: " <NULL>"),
|
||||
task, old_request);
|
||||
|
||||
/* Device reset conditions signalled in task_state_flags are the
|
||||
* responsbility of libsas to observe at the start of the error
|
||||
* handler thread.
|
||||
*/
|
||||
if (!idev || !old_request) {
|
||||
/* The request has already completed and there
|
||||
* is nothing to do here other than to set the task
|
||||
* done bit, and indicate that the task abort function
|
||||
* was successful.
|
||||
*/
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
||||
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
|
||||
SAS_TASK_STATE_PENDING);
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
ret = TMF_RESP_FUNC_COMPLETE;
|
||||
|
||||
dev_warn(&ihost->pdev->dev,
|
||||
"%s: abort task not needed for %p\n",
|
||||
__func__, task);
|
||||
goto out;
|
||||
}
|
||||
/* Suspend the RNC, kill the TC */
|
||||
if (isci_remote_device_suspend_terminate(ihost, idev, old_request)
|
||||
!= SCI_SUCCESS) {
|
||||
dev_warn(&ihost->pdev->dev,
|
||||
"%s: isci_remote_device_reset_terminate(dev=%p, "
|
||||
"req=%p, task=%p) failed\n",
|
||||
__func__, idev, old_request, task);
|
||||
ret = TMF_RESP_FUNC_FAILED;
|
||||
goto out;
|
||||
}
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
|
||||
if (task->task_proto == SAS_PROTOCOL_SMP ||
|
||||
sas_protocol_ata(task->task_proto) ||
|
||||
target_done_already ||
|
||||
test_bit(IDEV_GONE, &idev->flags)) {
|
||||
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
|
||||
/* No task to send, so explicitly resume the device here */
|
||||
isci_remote_device_resume_from_abort(ihost, idev);
|
||||
|
||||
dev_warn(&ihost->pdev->dev,
|
||||
"%s: %s request"
|
||||
" or complete_in_target (%d), "
|
||||
"or IDEV_GONE (%d), thus no TMF\n",
|
||||
__func__,
|
||||
((task->task_proto == SAS_PROTOCOL_SMP)
|
||||
? "SMP"
|
||||
: (sas_protocol_ata(task->task_proto)
|
||||
? "SATA/STP"
|
||||
: "<other>")
|
||||
),
|
||||
test_bit(IREQ_COMPLETE_IN_TARGET,
|
||||
&old_request->flags),
|
||||
test_bit(IDEV_GONE, &idev->flags));
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
|
||||
SAS_TASK_STATE_PENDING);
|
||||
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
ret = TMF_RESP_FUNC_COMPLETE;
|
||||
} else {
|
||||
/* Fill in the tmf stucture */
|
||||
isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort,
|
||||
old_request);
|
||||
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
|
||||
/* Send the task management request. */
|
||||
#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */
|
||||
ret = isci_task_execute_tmf(ihost, idev, &tmf,
|
||||
ISCI_ABORT_TASK_TIMEOUT_MS);
|
||||
}
|
||||
out:
|
||||
dev_warn(&ihost->pdev->dev,
|
||||
"%s: Done; dev = %p, task = %p , old_request == %p\n",
|
||||
__func__, idev, task, old_request);
|
||||
isci_put_device(idev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* isci_task_abort_task_set() - This function is one of the SAS Domain Template
|
||||
* functions. This is one of the Task Management functoins called by libsas,
|
||||
* to abort all task for the given lun.
|
||||
* @d_device: This parameter specifies the domain device associated with this
|
||||
* request.
|
||||
* @lun: This parameter specifies the lun associated with this request.
|
||||
*
|
||||
* status, zero indicates success.
|
||||
*/
|
||||
int isci_task_abort_task_set(
|
||||
struct domain_device *d_device,
|
||||
u8 *lun)
|
||||
{
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isci_task_clear_aca() - This function is one of the SAS Domain Template
|
||||
* functions. This is one of the Task Management functoins called by libsas.
|
||||
* @d_device: This parameter specifies the domain device associated with this
|
||||
* request.
|
||||
* @lun: This parameter specifies the lun associated with this request.
|
||||
*
|
||||
* status, zero indicates success.
|
||||
*/
|
||||
int isci_task_clear_aca(
|
||||
struct domain_device *d_device,
|
||||
u8 *lun)
|
||||
{
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* isci_task_clear_task_set() - This function is one of the SAS Domain Template
|
||||
* functions. This is one of the Task Management functoins called by libsas.
|
||||
* @d_device: This parameter specifies the domain device associated with this
|
||||
* request.
|
||||
* @lun: This parameter specifies the lun associated with this request.
|
||||
*
|
||||
* status, zero indicates success.
|
||||
*/
|
||||
int isci_task_clear_task_set(
|
||||
struct domain_device *d_device,
|
||||
u8 *lun)
|
||||
{
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isci_task_query_task() - This function is implemented to cause libsas to
|
||||
* correctly escalate the failed abort to a LUN or target reset (this is
|
||||
* because sas_scsi_find_task libsas function does not correctly interpret
|
||||
* all return codes from the abort task call). When TMF_RESP_FUNC_SUCC is
|
||||
* returned, libsas turns this into a LUN reset; when FUNC_FAILED is
|
||||
* returned, libsas will turn this into a target reset
|
||||
* @task: This parameter specifies the sas task being queried.
|
||||
* @lun: This parameter specifies the lun associated with this request.
|
||||
*
|
||||
* status, zero indicates success.
|
||||
*/
|
||||
int isci_task_query_task(
|
||||
struct sas_task *task)
|
||||
{
|
||||
/* See if there is a pending device reset for this device. */
|
||||
if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET)
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
else
|
||||
return TMF_RESP_FUNC_SUCC;
|
||||
}
|
||||
|
||||
/*
|
||||
* isci_task_request_complete() - This function is called by the sci core when
|
||||
* an task request completes.
|
||||
* @ihost: This parameter specifies the ISCI host object
|
||||
* @ireq: This parameter is the completed isci_request object.
|
||||
* @completion_status: This parameter specifies the completion status from the
|
||||
* sci core.
|
||||
*
|
||||
* none.
|
||||
*/
|
||||
void
|
||||
isci_task_request_complete(struct isci_host *ihost,
|
||||
struct isci_request *ireq,
|
||||
enum sci_task_status completion_status)
|
||||
{
|
||||
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
|
||||
struct completion *tmf_complete = NULL;
|
||||
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: request = %p, status=%d\n",
|
||||
__func__, ireq, completion_status);
|
||||
|
||||
set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags);
|
||||
|
||||
if (tmf) {
|
||||
tmf->status = completion_status;
|
||||
|
||||
if (tmf->proto == SAS_PROTOCOL_SSP) {
|
||||
memcpy(&tmf->resp.resp_iu,
|
||||
&ireq->ssp.rsp,
|
||||
SSP_RESP_IU_MAX_SIZE);
|
||||
} else if (tmf->proto == SAS_PROTOCOL_SATA) {
|
||||
memcpy(&tmf->resp.d2h_fis,
|
||||
&ireq->stp.rsp,
|
||||
sizeof(struct dev_to_host_fis));
|
||||
}
|
||||
/* PRINT_TMF( ((struct isci_tmf *)request->task)); */
|
||||
tmf_complete = tmf->complete;
|
||||
}
|
||||
sci_controller_complete_io(ihost, ireq->target_device, ireq);
|
||||
/* set the 'terminated' flag handle to make sure it cannot be terminated
|
||||
* or completed again.
|
||||
*/
|
||||
set_bit(IREQ_TERMINATED, &ireq->flags);
|
||||
|
||||
if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags))
|
||||
wake_up_all(&ihost->eventq);
|
||||
|
||||
if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags))
|
||||
isci_free_tag(ihost, ireq->io_tag);
|
||||
|
||||
/* The task management part completes last. */
|
||||
if (tmf_complete)
|
||||
complete(tmf_complete);
|
||||
}
|
||||
|
||||
static int isci_reset_device(struct isci_host *ihost,
|
||||
struct domain_device *dev,
|
||||
struct isci_remote_device *idev)
|
||||
{
|
||||
int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1;
|
||||
struct sas_phy *phy = sas_get_local_phy(dev);
|
||||
struct isci_port *iport = dev->port->lldd_port;
|
||||
|
||||
dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev);
|
||||
|
||||
/* Suspend the RNC, terminate all outstanding TCs. */
|
||||
if (isci_remote_device_suspend_terminate(ihost, idev, NULL)
|
||||
!= SCI_SUCCESS) {
|
||||
rc = TMF_RESP_FUNC_FAILED;
|
||||
goto out;
|
||||
}
|
||||
/* Note that since the termination for outstanding requests succeeded,
|
||||
* this function will return success. This is because the resets will
|
||||
* only fail if the device has been removed (ie. hotplug), and the
|
||||
* primary duty of this function is to cleanup tasks, so that is the
|
||||
* relevant status.
|
||||
*/
|
||||
if (!test_bit(IDEV_GONE, &idev->flags)) {
|
||||
if (scsi_is_sas_phy_local(phy)) {
|
||||
struct isci_phy *iphy = &ihost->phys[phy->number];
|
||||
|
||||
reset_stat = isci_port_perform_hard_reset(ihost, iport,
|
||||
iphy);
|
||||
} else
|
||||
reset_stat = sas_phy_reset(phy, !dev_is_sata(dev));
|
||||
}
|
||||
/* Explicitly resume the RNC here, since there was no task sent. */
|
||||
isci_remote_device_resume_from_abort(ihost, idev);
|
||||
|
||||
dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n",
|
||||
__func__, idev, reset_stat);
|
||||
out:
|
||||
sas_put_local_phy(phy);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int isci_task_I_T_nexus_reset(struct domain_device *dev)
|
||||
{
|
||||
struct isci_host *ihost = dev_to_ihost(dev);
|
||||
struct isci_remote_device *idev;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||
idev = isci_get_device(dev->lldd_dev);
|
||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||
|
||||
if (!idev) {
|
||||
/* XXX: need to cleanup any ireqs targeting this
|
||||
* domain_device
|
||||
*/
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = isci_reset_device(ihost, dev, idev);
|
||||
out:
|
||||
isci_put_device(idev);
|
||||
return ret;
|
||||
}
|
190
drivers/scsi/isci/task.h
Normal file
190
drivers/scsi/isci/task.h
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _ISCI_TASK_H_
|
||||
#define _ISCI_TASK_H_
|
||||
|
||||
#include <scsi/sas_ata.h>
|
||||
#include "host.h"
|
||||
|
||||
#define ISCI_TERMINATION_TIMEOUT_MSEC 500
|
||||
|
||||
struct isci_request;
|
||||
|
||||
/**
|
||||
* enum isci_tmf_function_codes - This enum defines the possible preparations
|
||||
* of task management requests.
|
||||
*
|
||||
*
|
||||
*/
|
||||
enum isci_tmf_function_codes {
|
||||
|
||||
isci_tmf_func_none = 0,
|
||||
isci_tmf_ssp_task_abort = TMF_ABORT_TASK,
|
||||
isci_tmf_ssp_lun_reset = TMF_LU_RESET,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isci_tmf - This class represents the task management object which
|
||||
* acts as an interface to libsas for processing task management requests
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct isci_tmf {
|
||||
|
||||
struct completion *complete;
|
||||
enum sas_protocol proto;
|
||||
union {
|
||||
struct ssp_response_iu resp_iu;
|
||||
struct dev_to_host_fis d2h_fis;
|
||||
u8 rsp_buf[SSP_RESP_IU_MAX_SIZE];
|
||||
} resp;
|
||||
unsigned char lun[8];
|
||||
u16 io_tag;
|
||||
enum isci_tmf_function_codes tmf_code;
|
||||
int status;
|
||||
};
|
||||
|
||||
static inline void isci_print_tmf(struct isci_host *ihost, struct isci_tmf *tmf)
|
||||
{
|
||||
if (SAS_PROTOCOL_SATA == tmf->proto)
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: status = %x\n"
|
||||
"tmf->resp.d2h_fis.status = %x\n"
|
||||
"tmf->resp.d2h_fis.error = %x\n",
|
||||
__func__,
|
||||
tmf->status,
|
||||
tmf->resp.d2h_fis.status,
|
||||
tmf->resp.d2h_fis.error);
|
||||
else
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: status = %x\n"
|
||||
"tmf->resp.resp_iu.data_present = %x\n"
|
||||
"tmf->resp.resp_iu.status = %x\n"
|
||||
"tmf->resp.resp_iu.data_length = %x\n"
|
||||
"tmf->resp.resp_iu.data[0] = %x\n"
|
||||
"tmf->resp.resp_iu.data[1] = %x\n"
|
||||
"tmf->resp.resp_iu.data[2] = %x\n"
|
||||
"tmf->resp.resp_iu.data[3] = %x\n",
|
||||
__func__,
|
||||
tmf->status,
|
||||
tmf->resp.resp_iu.datapres,
|
||||
tmf->resp.resp_iu.status,
|
||||
be32_to_cpu(tmf->resp.resp_iu.response_data_len),
|
||||
tmf->resp.resp_iu.resp_data[0],
|
||||
tmf->resp.resp_iu.resp_data[1],
|
||||
tmf->resp.resp_iu.resp_data[2],
|
||||
tmf->resp.resp_iu.resp_data[3]);
|
||||
}
|
||||
|
||||
|
||||
int isci_task_execute_task(
|
||||
struct sas_task *task,
|
||||
int num,
|
||||
gfp_t gfp_flags);
|
||||
|
||||
int isci_task_abort_task(
|
||||
struct sas_task *task);
|
||||
|
||||
int isci_task_abort_task_set(
|
||||
struct domain_device *d_device,
|
||||
u8 *lun);
|
||||
|
||||
int isci_task_clear_aca(
|
||||
struct domain_device *d_device,
|
||||
u8 *lun);
|
||||
|
||||
int isci_task_clear_task_set(
|
||||
struct domain_device *d_device,
|
||||
u8 *lun);
|
||||
|
||||
int isci_task_query_task(
|
||||
struct sas_task *task);
|
||||
|
||||
int isci_task_lu_reset(
|
||||
struct domain_device *d_device,
|
||||
u8 *lun);
|
||||
|
||||
int isci_task_clear_nexus_port(
|
||||
struct asd_sas_port *port);
|
||||
|
||||
int isci_task_clear_nexus_ha(
|
||||
struct sas_ha_struct *ha);
|
||||
|
||||
int isci_task_I_T_nexus_reset(
|
||||
struct domain_device *d_device);
|
||||
|
||||
void isci_task_request_complete(
|
||||
struct isci_host *isci_host,
|
||||
struct isci_request *request,
|
||||
enum sci_task_status completion_status);
|
||||
|
||||
u16 isci_task_ssp_request_get_io_tag_to_manage(
|
||||
struct isci_request *request);
|
||||
|
||||
u8 isci_task_ssp_request_get_function(
|
||||
struct isci_request *request);
|
||||
|
||||
|
||||
void *isci_task_ssp_request_get_response_data_address(
|
||||
struct isci_request *request);
|
||||
|
||||
u32 isci_task_ssp_request_get_response_data_length(
|
||||
struct isci_request *request);
|
||||
|
||||
int isci_queuecommand(
|
||||
struct scsi_cmnd *scsi_cmd,
|
||||
void (*donefunc)(struct scsi_cmnd *));
|
||||
|
||||
#endif /* !defined(_SCI_TASK_H_) */
|
211
drivers/scsi/isci/unsolicited_frame_control.c
Normal file
211
drivers/scsi/isci/unsolicited_frame_control.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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 "host.h"
|
||||
#include "unsolicited_frame_control.h"
|
||||
#include "registers.h"
|
||||
|
||||
void sci_unsolicited_frame_control_construct(struct isci_host *ihost)
|
||||
{
|
||||
struct sci_unsolicited_frame_control *uf_control = &ihost->uf_control;
|
||||
struct sci_unsolicited_frame *uf;
|
||||
dma_addr_t dma = ihost->ufi_dma;
|
||||
void *virt = ihost->ufi_buf;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* The Unsolicited Frame buffers are set at the start of the UF
|
||||
* memory descriptor entry. The headers and address table will be
|
||||
* placed after the buffers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Program the location of the UF header table into the SCU.
|
||||
* Notes:
|
||||
* - The address must align on a 64-byte boundary. Guaranteed to be
|
||||
* on 64-byte boundary already 1KB boundary for unsolicited frames.
|
||||
* - Program unused header entries to overlap with the last
|
||||
* unsolicited frame. The silicon will never DMA to these unused
|
||||
* headers, since we program the UF address table pointers to
|
||||
* NULL.
|
||||
*/
|
||||
uf_control->headers.physical_address = dma + SCI_UFI_BUF_SIZE;
|
||||
uf_control->headers.array = virt + SCI_UFI_BUF_SIZE;
|
||||
|
||||
/*
|
||||
* Program the location of the UF address table into the SCU.
|
||||
* Notes:
|
||||
* - The address must align on a 64-bit boundary. Guaranteed to be on 64
|
||||
* byte boundary already due to above programming headers being on a
|
||||
* 64-bit boundary and headers are on a 64-bytes in size.
|
||||
*/
|
||||
uf_control->address_table.physical_address = dma + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE;
|
||||
uf_control->address_table.array = virt + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE;
|
||||
uf_control->get = 0;
|
||||
|
||||
/*
|
||||
* UF buffer requirements are:
|
||||
* - The last entry in the UF queue is not NULL.
|
||||
* - There is a power of 2 number of entries (NULL or not-NULL)
|
||||
* programmed into the queue.
|
||||
* - Aligned on a 1KB boundary. */
|
||||
|
||||
/*
|
||||
* Program the actual used UF buffers into the UF address table and
|
||||
* the controller's array of UFs.
|
||||
*/
|
||||
for (i = 0; i < SCU_MAX_UNSOLICITED_FRAMES; i++) {
|
||||
uf = &uf_control->buffers.array[i];
|
||||
|
||||
uf_control->address_table.array[i] = dma;
|
||||
|
||||
uf->buffer = virt;
|
||||
uf->header = &uf_control->headers.array[i];
|
||||
uf->state = UNSOLICITED_FRAME_EMPTY;
|
||||
|
||||
/*
|
||||
* Increment the address of the physical and virtual memory
|
||||
* pointers. Everything is aligned on 1k boundary with an
|
||||
* increment of 1k.
|
||||
*/
|
||||
virt += SCU_UNSOLICITED_FRAME_BUFFER_SIZE;
|
||||
dma += SCU_UNSOLICITED_FRAME_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
enum sci_status sci_unsolicited_frame_control_get_header(struct sci_unsolicited_frame_control *uf_control,
|
||||
u32 frame_index,
|
||||
void **frame_header)
|
||||
{
|
||||
if (frame_index < SCU_MAX_UNSOLICITED_FRAMES) {
|
||||
/* Skip the first word in the frame since this is a controll word used
|
||||
* by the hardware.
|
||||
*/
|
||||
*frame_header = &uf_control->buffers.array[frame_index].header->data;
|
||||
|
||||
return SCI_SUCCESS;
|
||||
}
|
||||
|
||||
return SCI_FAILURE_INVALID_PARAMETER_VALUE;
|
||||
}
|
||||
|
||||
enum sci_status sci_unsolicited_frame_control_get_buffer(struct sci_unsolicited_frame_control *uf_control,
|
||||
u32 frame_index,
|
||||
void **frame_buffer)
|
||||
{
|
||||
if (frame_index < SCU_MAX_UNSOLICITED_FRAMES) {
|
||||
*frame_buffer = uf_control->buffers.array[frame_index].buffer;
|
||||
|
||||
return SCI_SUCCESS;
|
||||
}
|
||||
|
||||
return SCI_FAILURE_INVALID_PARAMETER_VALUE;
|
||||
}
|
||||
|
||||
bool sci_unsolicited_frame_control_release_frame(struct sci_unsolicited_frame_control *uf_control,
|
||||
u32 frame_index)
|
||||
{
|
||||
u32 frame_get;
|
||||
u32 frame_cycle;
|
||||
|
||||
frame_get = uf_control->get & (SCU_MAX_UNSOLICITED_FRAMES - 1);
|
||||
frame_cycle = uf_control->get & SCU_MAX_UNSOLICITED_FRAMES;
|
||||
|
||||
/*
|
||||
* In the event there are NULL entries in the UF table, we need to
|
||||
* advance the get pointer in order to find out if this frame should
|
||||
* be released (i.e. update the get pointer)
|
||||
*/
|
||||
while (lower_32_bits(uf_control->address_table.array[frame_get]) == 0 &&
|
||||
upper_32_bits(uf_control->address_table.array[frame_get]) == 0 &&
|
||||
frame_get < SCU_MAX_UNSOLICITED_FRAMES)
|
||||
frame_get++;
|
||||
|
||||
/*
|
||||
* The table has a NULL entry as it's last element. This is
|
||||
* illegal.
|
||||
*/
|
||||
BUG_ON(frame_get >= SCU_MAX_UNSOLICITED_FRAMES);
|
||||
if (frame_index >= SCU_MAX_UNSOLICITED_FRAMES)
|
||||
return false;
|
||||
|
||||
uf_control->buffers.array[frame_index].state = UNSOLICITED_FRAME_RELEASED;
|
||||
|
||||
if (frame_get != frame_index) {
|
||||
/*
|
||||
* Frames remain in use until we advance the get pointer
|
||||
* so there is nothing we can do here
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* The frame index is equal to the current get pointer so we
|
||||
* can now free up all of the frame entries that
|
||||
*/
|
||||
while (uf_control->buffers.array[frame_get].state == UNSOLICITED_FRAME_RELEASED) {
|
||||
uf_control->buffers.array[frame_get].state = UNSOLICITED_FRAME_EMPTY;
|
||||
|
||||
if (frame_get+1 == SCU_MAX_UNSOLICITED_FRAMES-1) {
|
||||
frame_cycle ^= SCU_MAX_UNSOLICITED_FRAMES;
|
||||
frame_get = 0;
|
||||
} else
|
||||
frame_get++;
|
||||
}
|
||||
|
||||
uf_control->get = SCU_UFQGP_GEN_BIT(ENABLE_BIT) | frame_cycle | frame_get;
|
||||
|
||||
return true;
|
||||
}
|
282
drivers/scsi/isci/unsolicited_frame_control.h
Normal file
282
drivers/scsi/isci/unsolicited_frame_control.h
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* 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.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SCIC_SDS_UNSOLICITED_FRAME_CONTROL_H_
|
||||
#define _SCIC_SDS_UNSOLICITED_FRAME_CONTROL_H_
|
||||
|
||||
#include "isci.h"
|
||||
|
||||
#define SCU_UNSOLICITED_FRAME_HEADER_DATA_DWORDS 15
|
||||
|
||||
/**
|
||||
* struct scu_unsolicited_frame_header -
|
||||
*
|
||||
* This structure delineates the format of an unsolicited frame header. The
|
||||
* first DWORD are UF attributes defined by the silicon architecture. The data
|
||||
* depicts actual header information received on the link.
|
||||
*/
|
||||
struct scu_unsolicited_frame_header {
|
||||
/**
|
||||
* This field indicates if there is an Initiator Index Table entry with
|
||||
* which this header is associated.
|
||||
*/
|
||||
u32 iit_exists:1;
|
||||
|
||||
/**
|
||||
* This field simply indicates the protocol type (i.e. SSP, STP, SMP).
|
||||
*/
|
||||
u32 protocol_type:3;
|
||||
|
||||
/**
|
||||
* This field indicates if the frame is an address frame (IAF or OAF)
|
||||
* or if it is a information unit frame.
|
||||
*/
|
||||
u32 is_address_frame:1;
|
||||
|
||||
/**
|
||||
* This field simply indicates the connection rate at which the frame
|
||||
* was received.
|
||||
*/
|
||||
u32 connection_rate:4;
|
||||
|
||||
u32 reserved:23;
|
||||
|
||||
/**
|
||||
* This field represents the actual header data received on the link.
|
||||
*/
|
||||
u32 data[SCU_UNSOLICITED_FRAME_HEADER_DATA_DWORDS];
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* enum unsolicited_frame_state -
|
||||
*
|
||||
* This enumeration represents the current unsolicited frame state. The
|
||||
* controller object can not updtate the hardware unsolicited frame put pointer
|
||||
* unless it has already processed the priror unsolicited frames.
|
||||
*/
|
||||
enum unsolicited_frame_state {
|
||||
/**
|
||||
* This state is when the frame is empty and not in use. It is
|
||||
* different from the released state in that the hardware could DMA
|
||||
* data to this frame buffer.
|
||||
*/
|
||||
UNSOLICITED_FRAME_EMPTY,
|
||||
|
||||
/**
|
||||
* This state is set when the frame buffer is in use by by some
|
||||
* object in the system.
|
||||
*/
|
||||
UNSOLICITED_FRAME_IN_USE,
|
||||
|
||||
/**
|
||||
* This state is set when the frame is returned to the free pool
|
||||
* but one or more frames prior to this one are still in use.
|
||||
* Once all of the frame before this one are freed it will go to
|
||||
* the empty state.
|
||||
*/
|
||||
UNSOLICITED_FRAME_RELEASED,
|
||||
|
||||
UNSOLICITED_FRAME_MAX_STATES
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sci_unsolicited_frame -
|
||||
*
|
||||
* This is the unsolicited frame data structure it acts as the container for
|
||||
* the current frame state, frame header and frame buffer.
|
||||
*/
|
||||
struct sci_unsolicited_frame {
|
||||
/**
|
||||
* This field contains the current frame state
|
||||
*/
|
||||
enum unsolicited_frame_state state;
|
||||
|
||||
/**
|
||||
* This field points to the frame header data.
|
||||
*/
|
||||
struct scu_unsolicited_frame_header *header;
|
||||
|
||||
/**
|
||||
* This field points to the frame buffer data.
|
||||
*/
|
||||
void *buffer;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sci_uf_header_array -
|
||||
*
|
||||
* This structure contains all of the unsolicited frame header information.
|
||||
*/
|
||||
struct sci_uf_header_array {
|
||||
/**
|
||||
* This field is represents a virtual pointer to the start
|
||||
* address of the UF address table. The table contains
|
||||
* 64-bit pointers as required by the hardware.
|
||||
*/
|
||||
struct scu_unsolicited_frame_header *array;
|
||||
|
||||
/**
|
||||
* This field specifies the physical address location for the UF
|
||||
* buffer array.
|
||||
*/
|
||||
dma_addr_t physical_address;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sci_uf_buffer_array -
|
||||
*
|
||||
* This structure contains all of the unsolicited frame buffer (actual payload)
|
||||
* information.
|
||||
*/
|
||||
struct sci_uf_buffer_array {
|
||||
/**
|
||||
* This field is the unsolicited frame data its used to manage
|
||||
* the data for the unsolicited frame requests. It also represents
|
||||
* the virtual address location that corresponds to the
|
||||
* physical_address field.
|
||||
*/
|
||||
struct sci_unsolicited_frame array[SCU_MAX_UNSOLICITED_FRAMES];
|
||||
|
||||
/**
|
||||
* This field specifies the physical address location for the UF
|
||||
* buffer array.
|
||||
*/
|
||||
dma_addr_t physical_address;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sci_uf_address_table_array -
|
||||
*
|
||||
* This object maintains all of the unsolicited frame address table specific
|
||||
* data. The address table is a collection of 64-bit pointers that point to
|
||||
* 1KB buffers into which the silicon will DMA unsolicited frames.
|
||||
*/
|
||||
struct sci_uf_address_table_array {
|
||||
/**
|
||||
* This field represents a virtual pointer that refers to the
|
||||
* starting address of the UF address table.
|
||||
* 64-bit pointers are required by the hardware.
|
||||
*/
|
||||
u64 *array;
|
||||
|
||||
/**
|
||||
* This field specifies the physical address location for the UF
|
||||
* address table.
|
||||
*/
|
||||
dma_addr_t physical_address;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sci_unsolicited_frame_control -
|
||||
*
|
||||
* This object contains all of the data necessary to handle unsolicited frames.
|
||||
*/
|
||||
struct sci_unsolicited_frame_control {
|
||||
/**
|
||||
* This field is the software copy of the unsolicited frame queue
|
||||
* get pointer. The controller object writes this value to the
|
||||
* hardware to let the hardware put more unsolicited frame entries.
|
||||
*/
|
||||
u32 get;
|
||||
|
||||
/**
|
||||
* This field contains all of the unsolicited frame header
|
||||
* specific fields.
|
||||
*/
|
||||
struct sci_uf_header_array headers;
|
||||
|
||||
/**
|
||||
* This field contains all of the unsolicited frame buffer
|
||||
* specific fields.
|
||||
*/
|
||||
struct sci_uf_buffer_array buffers;
|
||||
|
||||
/**
|
||||
* This field contains all of the unsolicited frame address table
|
||||
* specific fields.
|
||||
*/
|
||||
struct sci_uf_address_table_array address_table;
|
||||
|
||||
};
|
||||
|
||||
#define SCI_UFI_BUF_SIZE (SCU_MAX_UNSOLICITED_FRAMES * SCU_UNSOLICITED_FRAME_BUFFER_SIZE)
|
||||
#define SCI_UFI_HDR_SIZE (SCU_MAX_UNSOLICITED_FRAMES * sizeof(struct scu_unsolicited_frame_header))
|
||||
#define SCI_UFI_TOTAL_SIZE (SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE + SCU_MAX_UNSOLICITED_FRAMES * sizeof(u64))
|
||||
|
||||
struct isci_host;
|
||||
|
||||
void sci_unsolicited_frame_control_construct(struct isci_host *ihost);
|
||||
|
||||
enum sci_status sci_unsolicited_frame_control_get_header(
|
||||
struct sci_unsolicited_frame_control *uf_control,
|
||||
u32 frame_index,
|
||||
void **frame_header);
|
||||
|
||||
enum sci_status sci_unsolicited_frame_control_get_buffer(
|
||||
struct sci_unsolicited_frame_control *uf_control,
|
||||
u32 frame_index,
|
||||
void **frame_buffer);
|
||||
|
||||
bool sci_unsolicited_frame_control_release_frame(
|
||||
struct sci_unsolicited_frame_control *uf_control,
|
||||
u32 frame_index);
|
||||
|
||||
#endif /* _SCIC_SDS_UNSOLICITED_FRAME_CONTROL_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue