Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6: (277 commits)
  [SCSI] isci: fix checkpatch errors
  isci: Device reset should request sas_phy_reset(phy, true)
  isci: pare back error messsages
  isci: cleanup silicon revision detection
  isci: merge scu_unsolicited_frame.h into unsolicited_frame_control.h
  isci: merge sata.[ch] into request.c
  isci: kill 'get/set' macros
  isci: retire scic_sds_ and scic_ prefixes
  isci: unify isci_host and scic_sds_controller
  isci: unify isci_remote_device and scic_sds_remote_device
  isci: unify isci_port and scic_sds_port
  isci: fix scic_sds_remote_device_terminate_requests
  isci: unify isci_phy and scic_sds_phy
  isci: unify isci_request and scic_sds_request
  isci: rename / clean up scic_sds_stp_request
  isci: preallocate requests
  isci: combine request flags
  isci: unify can_queue tracking on the tci_pool, uplevel tag assignment
  isci: Terminate dev requests on FIS err bit rx in NCQ
  isci: fix frame received locking
  ...
This commit is contained in:
Linus Torvalds 2011-07-04 15:54:37 -07:00
commit 532df6f3fa
40 changed files with 23622 additions and 7 deletions

View File

@ -830,6 +830,19 @@ config SCSI_GDTH
To compile this driver as a module, choose M here: the
module will be called gdth.
config SCSI_ISCI
tristate "Intel(R) C600 Series Chipset SAS Controller"
depends on PCI && SCSI
depends on X86
# (temporary): known alpha quality driver
depends on EXPERIMENTAL
select SCSI_SAS_LIBSAS
---help---
This driver supports the 6Gb/s SAS capabilities of the storage
control unit found in the Intel(R) C600 series chipset.
The experimental tag will be removed after the driver exits alpha
config SCSI_GENERIC_NCR5380
tristate "Generic NCR5380/53c400 SCSI PIO support"
depends on ISA && SCSI

View File

@ -73,6 +73,7 @@ obj-$(CONFIG_SCSI_AACRAID) += aacraid/
obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o
obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/
obj-$(CONFIG_SCSI_PM8001) += pm8001/
obj-$(CONFIG_SCSI_ISCI) += isci/
obj-$(CONFIG_SCSI_IPS) += ips.o
obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o
obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o

View File

@ -1037,6 +1037,7 @@ static void complete_scsi_command(struct CommandList *cp)
unsigned char sense_key;
unsigned char asc; /* additional sense code */
unsigned char ascq; /* additional sense code qualifier */
unsigned long sense_data_size;
ei = cp->err_info;
cmd = (struct scsi_cmnd *) cp->scsi_cmd;
@ -1051,10 +1052,14 @@ static void complete_scsi_command(struct CommandList *cp)
cmd->result |= ei->ScsiStatus;
/* copy the sense data whether we need to or not. */
memcpy(cmd->sense_buffer, ei->SenseInfo,
ei->SenseLen > SCSI_SENSE_BUFFERSIZE ?
SCSI_SENSE_BUFFERSIZE :
ei->SenseLen);
if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo))
sense_data_size = SCSI_SENSE_BUFFERSIZE;
else
sense_data_size = sizeof(ei->SenseInfo);
if (ei->SenseLen < sense_data_size)
sense_data_size = ei->SenseLen;
memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size);
scsi_set_resid(cmd, ei->ResidualCnt);
if (ei->CommandStatus == 0) {
@ -2580,6 +2585,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
c->SG[0].Ext = 0; /* we are not chaining*/
}
hpsa_scsi_do_simple_cmd_core(h, c);
if (iocommand.buf_size > 0)
hpsa_pci_unmap(h->pdev, c, 1, PCI_DMA_BIDIRECTIONAL);
check_ioctl_unit_attention(h, c);

View File

@ -4306,7 +4306,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
spin_lock_irqsave(vhost->host->host_lock, flags);
if (rc == H_CLOSED)
vio_enable_interrupts(to_vio_dev(vhost->dev));
else if (rc || (rc = ibmvfc_send_crq_init(vhost)) ||
if (rc || (rc = ibmvfc_send_crq_init(vhost)) ||
(rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
dev_err(vhost->dev, "Error after reset (rc=%d)\n", rc);

View 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 \

View File

@ -0,0 +1,19 @@
# Makefile for create_fw
#
CC=gcc
CFLAGS=-c -Wall -O2 -g
LDFLAGS=
SOURCES=create_fw.c
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=create_fw
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
.c.o:
$(CC) $(CFLAGS) $< -O $@
clean:
rm -f *.o $(EXECUTABLE)

View File

@ -0,0 +1,36 @@
This defines the temporary binary blow we are to pass to the SCU
driver to emulate the binary firmware that we will eventually be
able to access via NVRAM on the SCU controller.
The current size of the binary blob is expected to be 149 bytes or larger
Header Types:
0x1: Phy Masks
0x2: Phy Gens
0x3: SAS Addrs
0xff: End of Data
ID string - u8[12]: "#SCU MAGIC#\0"
Version - u8: 1
SubVersion - u8: 0
Header Type - u8: 0x1
Size - u8: 8
Phy Mask - u32[8]
Header Type - u8: 0x2
Size - u8: 8
Phy Gen - u32[8]
Header Type - u8: 0x3
Size - u8: 8
Sas Addr - u64[8]
Header Type - u8: 0xf
==============================================================================
Place isci_firmware.bin in /lib/firmware
Be sure to recreate the initramfs image to include the firmware.

View File

@ -0,0 +1,99 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <asm/types.h>
#include <strings.h>
#include <stdint.h>
#include "create_fw.h"
#include "../probe_roms.h"
int write_blob(struct isci_orom *isci_orom)
{
FILE *fd;
int err;
size_t count;
fd = fopen(blob_name, "w+");
if (!fd) {
perror("Open file for write failed");
fclose(fd);
return -EIO;
}
count = fwrite(isci_orom, sizeof(struct isci_orom), 1, fd);
if (count != 1) {
perror("Write data failed");
fclose(fd);
return -EIO;
}
fclose(fd);
return 0;
}
void set_binary_values(struct isci_orom *isci_orom)
{
int ctrl_idx, phy_idx, port_idx;
/* setting OROM signature */
strncpy(isci_orom->hdr.signature, sig, strlen(sig));
isci_orom->hdr.version = version;
isci_orom->hdr.total_block_length = sizeof(struct isci_orom);
isci_orom->hdr.hdr_length = sizeof(struct sci_bios_oem_param_block_hdr);
isci_orom->hdr.num_elements = num_elements;
for (ctrl_idx = 0; ctrl_idx < 2; ctrl_idx++) {
isci_orom->ctrl[ctrl_idx].controller.mode_type = mode_type;
isci_orom->ctrl[ctrl_idx].controller.max_concurrent_dev_spin_up =
max_num_concurrent_dev_spin_up;
isci_orom->ctrl[ctrl_idx].controller.do_enable_ssc =
enable_ssc;
for (port_idx = 0; port_idx < 4; port_idx++)
isci_orom->ctrl[ctrl_idx].ports[port_idx].phy_mask =
phy_mask[ctrl_idx][port_idx];
for (phy_idx = 0; phy_idx < 4; phy_idx++) {
isci_orom->ctrl[ctrl_idx].phys[phy_idx].sas_address.high =
(__u32)(sas_addr[ctrl_idx][phy_idx] >> 32);
isci_orom->ctrl[ctrl_idx].phys[phy_idx].sas_address.low =
(__u32)(sas_addr[ctrl_idx][phy_idx]);
isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control0 =
afe_tx_amp_control0;
isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control1 =
afe_tx_amp_control1;
isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control2 =
afe_tx_amp_control2;
isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control3 =
afe_tx_amp_control3;
}
}
}
int main(void)
{
int err;
struct isci_orom *isci_orom;
isci_orom = malloc(sizeof(struct isci_orom));
memset(isci_orom, 0, sizeof(struct isci_orom));
set_binary_values(isci_orom);
err = write_blob(isci_orom);
if (err < 0) {
free(isci_orom);
return err;
}
free(isci_orom);
return 0;
}

View File

@ -0,0 +1,77 @@
#ifndef _CREATE_FW_H_
#define _CREATE_FW_H_
#include "../probe_roms.h"
/* we are configuring for 2 SCUs */
static const int num_elements = 2;
/*
* For all defined arrays:
* elements 0-3 are for SCU0, ports 0-3
* elements 4-7 are for SCU1, ports 0-3
*
* valid configurations for one SCU are:
* P0 P1 P2 P3
* ----------------
* 0xF,0x0,0x0,0x0 # 1 x4 port
* 0x3,0x0,0x4,0x8 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are each x1
* # ports
* 0x1,0x2,0xC,0x0 # Phys 0 and 1 are each x1 ports, phy 2 and phy 3 are a x2
* # port
* 0x3,0x0,0xC,0x0 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are a x2 port
* 0x1,0x2,0x4,0x8 # Each phy is a x1 port (this is the default configuration)
*
* if there is a port/phy on which you do not wish to override the default
* values, use the value assigned to UNINIT_PARAM (255).
*/
/* discovery mode type (port auto config mode by default ) */
/*
* if there is a port/phy on which you do not wish to override the default
* values, use the value "0000000000000000". SAS address of zero's is
* considered invalid and will not be used.
*/
#ifdef MPC
static const int mode_type = SCIC_PORT_MANUAL_CONFIGURATION_MODE;
static const __u8 phy_mask[2][4] = { {1, 2, 4, 8},
{1, 2, 4, 8} };
static const unsigned long long sas_addr[2][4] = { { 0x5FCFFFFFF0000001ULL,
0x5FCFFFFFF0000002ULL,
0x5FCFFFFFF0000003ULL,
0x5FCFFFFFF0000004ULL },
{ 0x5FCFFFFFF0000005ULL,
0x5FCFFFFFF0000006ULL,
0x5FCFFFFFF0000007ULL,
0x5FCFFFFFF0000008ULL } };
#else /* APC (default) */
static const int mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE;
static const __u8 phy_mask[2][4];
static const unsigned long long sas_addr[2][4] = { { 0x5FCFFFFF00000001ULL,
0x5FCFFFFF00000001ULL,
0x5FCFFFFF00000001ULL,
0x5FCFFFFF00000001ULL },
{ 0x5FCFFFFF00000002ULL,
0x5FCFFFFF00000002ULL,
0x5FCFFFFF00000002ULL,
0x5FCFFFFF00000002ULL } };
#endif
/* Maximum number of concurrent device spin up */
static const int max_num_concurrent_dev_spin_up = 1;
/* enable of ssc operation */
static const int enable_ssc;
/* AFE_TX_AMP_CONTROL */
static const unsigned int afe_tx_amp_control0 = 0x000bdd08;
static const unsigned int afe_tx_amp_control1 = 0x000ffc00;
static const unsigned int afe_tx_amp_control2 = 0x000b7c09;
static const unsigned int afe_tx_amp_control3 = 0x000afc6e;
static const char blob_name[] = "isci_firmware.bin";
static const char sig[] = "ISCUOEMB";
static const unsigned char version = 0x10;
#endif

2751
drivers/scsi/isci/host.c Normal file

File diff suppressed because it is too large Load Diff

542
drivers/scsi/isci/host.h Normal file
View File

@ -0,0 +1,542 @@
/*
* 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 "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 *);
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 task_context_dma;
union scu_remote_node_context *remote_node_context_table;
u32 *completion_queue;
u32 completion_queue_get;
u32 logical_port_entries;
u32 remote_node_entries;
u32 task_context_entries;
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 sas_ha_struct sas_ha;
spinlock_t state_lock;
struct pci_dev *pdev;
enum isci_status status;
#define IHOST_START_PENDING 0
#define IHOST_STOP_PENDING 1
unsigned long flags;
wait_queue_head_t eventq;
struct Scsi_Host *shost;
struct tasklet_struct completion_tasklet;
struct list_head requests_to_complete;
struct list_head requests_to_errorback;
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 has successfully been stopped.
* In this state no new IO operations are permitted.
* This state is entered from the STOPPING state.
*/
SCIC_STOPPED,
/**
* 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);
}
#define for_each_isci_host(id, ihost, pdev) \
for (id = 0, ihost = to_pci_info(pdev)->hosts[id]; \
id < ARRAY_SIZE(to_pci_info(pdev)->hosts) && ihost; \
ihost = to_pci_info(pdev)->hosts[++id])
static inline enum isci_status isci_host_get_state(struct isci_host *isci_host)
{
return isci_host->status;
}
static inline void isci_host_change_state(struct isci_host *isci_host,
enum isci_status status)
{
unsigned long flags;
dev_dbg(&isci_host->pdev->dev,
"%s: isci_host = %p, state = 0x%x",
__func__,
isci_host,
status);
spin_lock_irqsave(&isci_host->state_lock, flags);
isci_host->status = status;
spin_unlock_irqrestore(&isci_host->state_lock, flags);
}
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;
}
/* 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))
/* 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->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) &&
!idev->is_direct_attached)
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 *sciphy_to_dev(struct isci_phy *iphy)
{
if (!iphy || !iphy->isci_port || !iphy->isci_port->isci_host)
return NULL;
return &iphy->isci_port->isci_host->pdev->dev;
}
static inline struct device *sciport_to_dev(struct isci_port *iport)
{
if (!iport || !iport->isci_host)
return NULL;
return &iport->isci_host->pdev->dev;
}
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;
}
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);
void sci_controller_copy_task_context(
struct isci_host *ihost,
struct isci_request *ireq);
void sci_controller_register_setup(struct isci_host *ihost);
enum sci_status sci_controller_continue_io(struct isci_request *ireq);
int isci_host_scan_finished(struct Scsi_Host *, unsigned long);
void isci_host_scan_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);
int isci_host_init(struct isci_host *);
void isci_host_init_controller_names(
struct isci_host *isci_host,
unsigned int controller_idx);
void isci_host_deinit(
struct isci_host *);
void isci_host_port_link_up(
struct isci_host *,
struct isci_port *,
struct isci_phy *);
int isci_host_dev_found(struct domain_device *);
void isci_host_remote_device_start_complete(
struct isci_host *,
struct isci_remote_device *,
enum sci_status);
void sci_controller_disable_interrupts(
struct isci_host *ihost);
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);
#endif

565
drivers/scsi/isci/init.c Normal file
View File

@ -0,0 +1,565 @@
/*
* 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 "isci.h"
#include "task.h"
#include "probe_roms.h"
static struct scsi_transport_template *isci_transport_template;
static DEFINE_PCI_DEVICE_TABLE(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 = 20;
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 = 3;
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 = 1;
module_param(max_concurr_spinup, byte, 0);
MODULE_PARM_DESC(max_concurr_spinup, "Max concurrent device spinup");
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,
.slave_destroy = sas_slave_destroy,
.scan_finished = isci_host_scan_finished,
.scan_start = isci_host_scan_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_device_reset_handler = sas_eh_device_reset_handler,
.eh_bus_reset_handler = isci_bus_reset_handler,
.slave_alloc = sas_slave_alloc,
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
};
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,
/* 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,
};
/******************************************************************************
* 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 sucess 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;
/*----------------- Libsas Initialization Stuff----------------------
* Set various fields in the sas_ha struct:
*/
sas_ha->sas_ha_name = DRV_NAME;
sas_ha->lldd_module = THIS_MODULE;
sas_ha->sas_addr = &isci_host->phys[0].sas_addr[0];
/* set the array of phy and port structs. */
for (i = 0; i < SCI_MAX_PHYS; i++) {
sas_phys[i] = &isci_host->phys[i].sas_phy;
sas_ports[i] = &isci_host->ports[i].sas_port;
}
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 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);
static void isci_unregister(struct isci_host *isci_host)
{
struct Scsi_Host *shost;
if (!isci_host)
return;
shost = isci_host->shost;
device_remove_file(&shost->shost_dev, &dev_attr_isci_id);
sas_unregister_ha(&isci_host->sas_ha);
sas_remove_host(isci_host->shost);
scsi_remove_host(isci_host->shost);
scsi_host_put(isci_host->shost);
}
static int __devinit 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(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 struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
{
struct isci_host *isci_host;
struct Scsi_Host *shost;
int err;
isci_host = devm_kzalloc(&pdev->dev, sizeof(*isci_host), GFP_KERNEL);
if (!isci_host)
return NULL;
isci_host->pdev = pdev;
isci_host->id = id;
shost = scsi_host_alloc(&isci_sht, sizeof(void *));
if (!shost)
return NULL;
isci_host->shost = shost;
err = isci_host_init(isci_host);
if (err)
goto err_shost;
SHOST_TO_SAS_HA(shost) = &isci_host->sas_ha;
isci_host->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(isci_host);
if (err)
goto err_shost_remove;
err = device_create_file(&shost->shost_dev, &dev_attr_isci_id);
if (err)
goto err_unregister_ha;
return isci_host;
err_unregister_ha:
sas_unregister_ha(&(isci_host->sas_ha));
err_shost_remove:
scsi_remove_host(shost);
err_shost:
scsi_host_put(shost);
return NULL;
}
static int __devinit 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)
orom = isci_get_efi_var(pdev);
if (!orom)
orom = isci_request_oprom(pdev);
for (i = 0; orom && i < ARRAY_SIZE(orom->ctrl); i++) {
if (sci_oem_parameters_validate(&orom->ctrl[i])) {
dev_warn(&pdev->dev,
"[%d]: invalid oem parameters detected, falling back to firmware\n", i);
devm_kfree(&pdev->dev, orom);
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;
}
err = isci_setup_interrupts(pdev);
if (err)
goto err_host_alloc;
for_each_isci_host(i, isci_host, pdev)
scsi_scan_host(isci_host->shost);
return 0;
err_host_alloc:
for_each_isci_host(i, isci_host, pdev)
isci_unregister(isci_host);
return err;
}
static void __devexit isci_pci_remove(struct pci_dev *pdev)
{
struct isci_host *ihost;
int i;
for_each_isci_host(i, ihost, pdev) {
isci_unregister(ihost);
isci_host_deinit(ihost);
sci_controller_disable_interrupts(ihost);
}
}
static struct pci_driver isci_pci_driver = {
.name = DRV_NAME,
.id_table = isci_id_table,
.probe = isci_pci_probe,
.remove = __devexit_p(isci_pci_remove),
};
static __init int isci_init(void)
{
int err;
pr_info("%s: Intel(R) C600 SAS Controller Driver\n", DRV_NAME);
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);

538
drivers/scsi/isci/isci.h Normal file
View File

@ -0,0 +1,538 @@
/*
* 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 (1024)
#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;
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__ */

1312
drivers/scsi/isci/phy.c Normal file

File diff suppressed because it is too large Load Diff

504
drivers/scsi/isci/phy.h Normal file
View File

@ -0,0 +1,504 @@
/*
* 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
enum sci_phy_protocol {
SCIC_SDS_PHY_PROTOCOL_UNKNOWN,
SCIC_SDS_PHY_PROTOCOL_SAS,
SCIC_SDS_PHY_PROTOCOL_SATA,
SCIC_SDS_MAX_PHY_PROTOCOLS
};
/**
* 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 sci_phy_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;
struct isci_port *isci_port;
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 {
/**
* Simply the initial state for the base domain state machine.
*/
SCI_PHY_INITIAL,
/**
* This state indicates that the phy has successfully been stopped.
* In this state no new IO operations are permitted on this phy.
* This state is entered from the INITIAL state.
* This state is entered from the STARTING state.
* This state is entered from the READY state.
* This state is entered from the RESETTING state.
*/
SCI_PHY_STOPPED,
/**
* This state indicates that the phy is in the process of becomming
* ready. In this state no new IO operations are permitted on this phy.
* This state is entered from the STOPPED state.
* This state is entered from the READY state.
* This state is entered from the RESETTING state.
*/
SCI_PHY_STARTING,
/**
* Initial state
*/
SCI_PHY_SUB_INITIAL,
/**
* Wait state for the hardware OSSP event type notification
*/
SCI_PHY_SUB_AWAIT_OSSP_EN,
/**
* Wait state for the PHY speed notification
*/
SCI_PHY_SUB_AWAIT_SAS_SPEED_EN,
/**
* Wait state for the IAF Unsolicited frame notification
*/
SCI_PHY_SUB_AWAIT_IAF_UF,
/**
* Wait state for the request to consume power
*/
SCI_PHY_SUB_AWAIT_SAS_POWER,
/**
* Wait state for request to consume power
*/
SCI_PHY_SUB_AWAIT_SATA_POWER,
/**
* Wait state for the SATA PHY notification
*/
SCI_PHY_SUB_AWAIT_SATA_PHY_EN,
/**
* Wait for the SATA PHY speed notification
*/
SCI_PHY_SUB_AWAIT_SATA_SPEED_EN,
/**
* Wait state for the SIGNATURE FIS unsolicited frame notification
*/
SCI_PHY_SUB_AWAIT_SIG_FIS_UF,
/**
* Exit state for this state machine
*/
SCI_PHY_SUB_FINAL,
/**
* This state indicates the the 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_READY,
/**
* This state indicates that the 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_RESETTING,
/**
* Simply the final state for the base phy state machine.
*/
SCI_PHY_FINAL,
};
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_) */

1757
drivers/scsi/isci/port.c Normal file

File diff suppressed because it is too large Load Diff

306
drivers/scsi/isci/port.h Normal file
View File

@ -0,0 +1,306 @@
/*
* 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
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
* @event: counts bcns and port stop events (for bcn filtering)
* @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
* @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 {
enum isci_status status;
#define IPORT_BCN_BLOCKED 0
#define IPORT_BCN_PENDING 1
unsigned long flags;
atomic_t event;
struct isci_host *isci_host;
struct asd_sas_port sas_port;
struct list_head remote_dev_list;
spinlock_t state_lock;
struct list_head domain_dev_list;
struct completion start_complete;
struct completion hard_reset_complete;
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;
u16 reserved_rni;
u16 reserved_tag;
u32 started_request_count;
u32 assigned_device_count;
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 - This enumeration depicts all the states for the
* common port state machine.
*
*
*/
enum sci_port_states {
/**
* This state indicates that the port has successfully been stopped.
* In this state no new IO operations are permitted.
* This state is entered from the STOPPING state.
*/
SCI_PORT_STOPPED,
/**
* This state indicates that the 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_STOPPING,
/**
* This state indicates the 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_READY,
/**
* The substate where the port is started and ready but has no
* active phys.
*/
SCI_PORT_SUB_WAITING,
/**
* The substate where the port is started and ready and there is
* at least one phy operational.
*/
SCI_PORT_SUB_OPERATIONAL,
/**
* The substate where the port is started and there was an
* add/remove phy event. This state is only used in Automatic
* Port Configuration Mode (APC)
*/
SCI_PORT_SUB_CONFIGURING,
/**
* This state indicates the 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_RESETTING,
/**
* This state indicates the 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.
*/
SCI_PORT_FAILED,
};
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_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);
enum isci_status isci_port_get_state(
struct isci_port *isci_port);
void isci_port_formed(struct asd_sas_phy *);
void isci_port_deformed(struct asd_sas_phy *);
void isci_port_init(
struct isci_port *port,
struct isci_host *host,
int index);
int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport,
struct isci_phy *iphy);
#endif /* !defined(_ISCI_PORT_H_) */

View File

@ -0,0 +1,754 @@
/*
* 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 (100)
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);
}
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:
/*
* This can occur for either a link down event, or a link
* up event where we cannot yet tell the port to which a
* phy belongs.
*/
if (port_agent->timer_pending)
sci_del_timer(&port_agent->timer);
port_agent->timer_pending = true;
sci_mod_timer(&port_agent->timer,
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_configure_ports(ihost, port_agent, iphy, true);
} else {
/* the phy is already the part of the port */
u32 port_state = iport->sm.current_state_id;
/* if the PORT'S state is resetting then the link up is from
* port hard reset in this case, we need to tell the port
* that link up is recieved
*/
BUG_ON(port_state != SCI_PORT_RESETTING);
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)
return;
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);
}
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;
}
}
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;
}

View File

@ -0,0 +1,243 @@
/*
* 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");
devm_kfree(&pdev->dev, rom);
rom = NULL;
}
pci_unmap_biosrom(oprom);
return rom;
}
enum sci_status isci_parse_oem_parameters(struct sci_oem_params *oem,
struct isci_orom *orom, int scu_index)
{
/* check for valid inputs */
if (scu_index < 0 || scu_index >= SCI_MAX_CONTROLLERS ||
scu_index > orom->hdr.num_elements || !oem)
return -EINVAL;
*oem = orom->ctrl[scu_index];
return 0;
}
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))
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;
}

View File

@ -0,0 +1,249 @@
/*
* 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_number_concurrent_device_spin_up;
/**
* 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);
struct isci_orom;
struct isci_orom *isci_request_oprom(struct pci_dev *pdev);
enum sci_status isci_parse_oem_parameters(struct sci_oem_params *oem,
struct isci_orom *orom, int scu_index);
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"
/* 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_concurrent_dev_spin_up;
uint8_t do_enable_ssc;
uint8_t reserved;
} 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,352 @@
/*
* 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_EH 3
#define IDEV_GONE 4
#define IDEV_IO_READY 5
#define IDEV_IO_NCQERROR 6
unsigned long flags;
struct kref kref;
struct isci_port *isci_port;
struct domain_device *domain_dev;
struct list_head node;
struct list_head reqs_in_process;
struct sci_base_state_machine sm;
u32 device_port_width;
enum sas_linkrate connection_rate;
bool is_direct_attached;
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;
};
#define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000
/* device reference routines must be called under sci_lock */
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);
bool isci_device_is_reset_pending(struct isci_host *ihost,
struct isci_remote_device *idev);
void isci_device_clear_reset_pending(struct isci_host *ihost,
struct isci_remote_device *idev);
/**
* 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.
*
*
*/
enum sci_remote_device_states {
/**
* Simply the initial state for the base remote device state machine.
*/
SCI_DEV_INITIAL,
/**
* 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_STOPPED,
/**
* 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_STARTING,
/**
* 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_DEV_READY,
/**
* 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_IDLE,
/**
* 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_CMD,
/**
* 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,
/**
* 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_NCQ_ERROR,
/**
* This is the READY substate indicates the device is waiting for the RESET task
* coming to be recovered from certain hardware specific error.
*/
SCI_STP_DEV_AWAIT_RESET,
/**
* This is the ready operational substate for the remote device. This is the
* normal operational state for a remote device.
*/
SCI_SMP_DEV_IDLE,
/**
* 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_SMP_DEV_CMD,
/**
* 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_STOPPING,
/**
* 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_FAILED,
/**
* 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_RESETTING,
/**
* Simply the final state for the base remote device state machine.
*/
SCI_DEV_FINAL,
};
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 == EDGE_DEV || dev->dev_type == FANOUT_DEV;
}
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--;
}
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);
enum sci_status sci_remote_device_suspend(
struct isci_remote_device *idev,
u32 suspend_type);
void sci_remote_device_post_request(
struct isci_remote_device *idev,
u32 request);
#endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */

View File

@ -0,0 +1,627 @@
/*
* 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 "isci.h"
#include "remote_device.h"
#include "remote_node_context.h"
#include "scu_event_codes.h"
#include "scu_task_context.h"
/**
*
* @sci_rnc: The RNC for which the is posted request is being made.
*
* This method will return true if the RNC is not in the initial state. In all
* other states the RNC is considered active and this will return true. The
* destroy request of the state machine drives the RNC back to the initial
* state. If the state machine changes then this routine will also have to be
* changed. bool true if the state machine is not in the initial state false if
* the state machine is in the initial state
*/
/**
*
* @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;
}
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->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
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)
{
if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) {
sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY;
sci_rnc->user_callback = callback;
sci_rnc->user_cookie = callback_parameter;
}
}
static void sci_remote_node_context_setup_to_destory(
struct sci_remote_node_context *sci_rnc,
scics_sds_remote_node_context_callback callback,
void *callback_parameter)
{
sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL;
sci_rnc->user_callback = callback;
sci_rnc->user_cookie = callback_parameter;
}
/**
*
*
* 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)
{
if (rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
sci_remote_node_context_resume(rnc, rnc->user_callback,
rnc->user_cookie);
}
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 (!idev->is_direct_attached &&
(dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP))) {
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 (idev->is_direct_attached)
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);
/* 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 = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
sci_remote_node_context_notify_user(rnc);
}
}
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);
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->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) &&
idev->is_direct_attached)
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);
rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
if (rnc->user_callback)
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);
sci_remote_node_context_continue_state_transitions(rnc);
}
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] = { },
};
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 = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_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;
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 == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL)
state = SCI_RNC_INITIAL;
else
state = SCI_RNC_POSTING;
sci_change_state(&sci_rnc->sm, 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_dbg(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_dbg(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->suspension_code = scu_get_event_specifier(event_code);
break;
case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED);
sci_rnc->suspension_code = scu_get_event_specifier(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:
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED);
sci_rnc->suspension_code = scu_get_event_specifier(event_code);
break;
case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED);
sci_rnc->suspension_code = scu_get_event_specifier(event_code);
break;
default:
goto out;
}
break;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: invalid state %d\n", __func__, state);
return SCI_FAILURE_INVALID_STATE;
}
return SCI_SUCCESS;
out:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: code: %#x state: %d\n", __func__, event_code, 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_destory(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:
case SCI_RNC_AWAIT_SUSPENSION:
sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p);
sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
return SCI_SUCCESS;
case SCI_RNC_INITIAL:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: invalid state %d\n", __func__, 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 %d\n", __func__, state);
return SCI_FAILURE_INVALID_STATE;
}
}
enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc,
u32 suspend_type,
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;
if (state != SCI_RNC_READY) {
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: invalid state %d\n", __func__, state);
return SCI_FAILURE_INVALID_STATE;
}
sci_rnc->user_callback = cb_fn;
sci_rnc->user_cookie = cb_p;
sci_rnc->suspension_code = suspend_type;
if (suspend_type == SCI_SOFTWARE_SUSPENSION) {
sci_remote_device_post_request(rnc_to_dev(sci_rnc),
SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX);
}
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;
state = sci_rnc->sm.current_state_id;
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);
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:
if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
return SCI_FAILURE_INVALID_STATE;
sci_rnc->user_callback = cb_fn;
sci_rnc->user_cookie = cb_p;
return SCI_SUCCESS;
case SCI_RNC_TX_SUSPENDED: {
struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
struct domain_device *dev = idev->domain_dev;
sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
/* TODO: consider adding a resume action of NONE, INVALIDATE, WRITE_TLCR */
if (dev->dev_type == SAS_END_DEV || dev_is_expander(dev))
sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
if (idev->is_direct_attached) {
/* @todo Fix this since I am being silly in writing to the STPTLDARNI register. */
sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
} else {
sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
}
} else
return SCI_FAILURE;
return SCI_SUCCESS;
}
case SCI_RNC_TX_RX_SUSPENDED:
sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
return SCI_FAILURE_INVALID_STATE;
case SCI_RNC_AWAIT_SUSPENSION:
sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
return SCI_SUCCESS;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: invalid state %d\n", __func__, 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 %d\n", __func__, state);
return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED;
default:
break;
}
dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: requested to start IO while still resuming, %d\n",
__func__, 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)
{
enum scis_sds_remote_node_context_states state;
state = sci_rnc->sm.current_state_id;
switch (state) {
case SCI_RNC_RESUMING:
case SCI_RNC_READY:
case SCI_RNC_AWAIT_SUSPENSION:
return SCI_SUCCESS;
case SCI_RNC_TX_SUSPENDED:
case SCI_RNC_TX_RX_SUSPENDED:
sci_remote_node_context_resume(sci_rnc, NULL, NULL);
return SCI_SUCCESS;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: invalid state %d\n", __func__, state);
return SCI_FAILURE_INVALID_STATE;
}
}

View File

@ -0,0 +1,224 @@
/*
* 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
#define SCU_HARDWARE_SUSPENSION (0)
#define SCI_SOFTWARE_SUSPENSION (1)
struct isci_request;
struct isci_remote_device;
struct sci_remote_node_context;
typedef void (*scics_sds_remote_node_context_callback)(void *);
/**
* This is the enumeration of the remote node context states.
*/
enum scis_sds_remote_node_context_states {
/**
* This state is the initial state for a remote node context. On a resume
* request the remote node context will transition to the posting state.
*/
SCI_RNC_INITIAL,
/**
* This is a transition state that posts the RNi to the hardware. Once the RNC
* is posted the remote node context will be made ready.
*/
SCI_RNC_POSTING,
/**
* This is a 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_INVALIDATING,
/**
* This is a 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_RESUMING,
/**
* This is the state that the remote node context must be in to accept io
* request operations.
*/
SCI_RNC_READY,
/**
* This is the state that the remote node context transitions to when it gets
* a TX suspend notification from the hardware.
*/
SCI_RNC_TX_SUSPENDED,
/**
* This is the state that the remote node context transitions to when it gets
* a TX RX suspend notification from the hardware.
*/
SCI_RNC_TX_RX_SUSPENDED,
/**
* This state is a 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.
*/
SCI_RNC_AWAIT_SUSPENSION
};
/**
*
*
* This enumeration is used to define the end destination state for the remote
* node context.
*/
enum sci_remote_node_context_destination_state {
SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED,
SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY,
SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL
};
/**
* 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 code or the reason for the remote node
* context suspension.
*/
u32 suspension_code;
/**
* 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);
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,
u32 suspend_type,
scics_sds_remote_node_context_callback cb_fn,
void *cb_p);
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);
enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc,
struct isci_request *ireq);
#endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */

View 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);
}
}

View 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_ */

3391
drivers/scsi/isci/request.c Normal file

File diff suppressed because it is too large Load Diff

448
drivers/scsi/isci/request.h Normal file
View File

@ -0,0 +1,448 @@
/*
* 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"
/**
* struct isci_request_status - This enum defines the possible states of an I/O
* request.
*
*
*/
enum isci_request_status {
unallocated = 0x00,
allocated = 0x01,
started = 0x02,
completed = 0x03,
aborting = 0x04,
aborted = 0x05,
terminating = 0x06,
dead = 0x07
};
enum task_type {
io_task = 0,
tmf_task = 1
};
enum sci_request_protocol {
SCIC_NO_PROTOCOL,
SCIC_SMP_PROTOCOL,
SCIC_SSP_PROTOCOL,
SCIC_STP_PROTOCOL
}; /* XXX remove me, use sas_task.{dev|task_proto} instead */;
/**
* 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
* @device_cdb_len - atapi device advertises it's transfer constraints at setup
*/
struct isci_stp_request {
u32 pio_len;
u8 status;
struct isci_stp_pio_sgl {
int index;
u8 set;
u32 offset;
} sgl;
u32 device_cdb_len;
};
struct isci_request {
enum isci_request_status status;
#define IREQ_COMPLETE_IN_TARGET 0
#define IREQ_TERMINATED 1
#define IREQ_TMF 2
#define IREQ_ACTIVE 3
unsigned long flags;
/* XXX kill ttype and ttype_ptr, allocate full sas_task */
enum task_type ttype;
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;
/* For use in the requests_to_{complete|abort} lists: */
struct list_head completed_node;
/* For use in the reqs_in_process list: */
struct list_head dev_node;
spinlock_t state_lock;
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 sci_request_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 smp_resp rsp;
} smp;
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 - This enumeration depicts all the states for
* the common request state machine.
*
*
*/
enum sci_base_request_states {
/*
* Simply the initial state for the base request state machine.
*/
SCI_REQ_INIT,
/*
* This state indicates that the request has been constructed.
* This state is entered from the INITIAL state.
*/
SCI_REQ_CONSTRUCTED,
/*
* This state indicates that the request has been started. This state
* is entered from the CONSTRUCTED state.
*/
SCI_REQ_STARTED,
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_SOFT_RESET_WAIT_H2D_ASSERTED,
SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG,
SCI_REQ_STP_SOFT_RESET_WAIT_D2H,
/*
* 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_H2D,
/*
* 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_WAIT_FRAME,
/*
* While in this state the IO request object is waiting for a DATA
* frame from the device.
*/
SCI_REQ_STP_PIO_DATA_IN,
/*
* While in this state the IO request object is waiting to transmit
* the next data frame to the device.
*/
SCI_REQ_STP_PIO_DATA_OUT,
/*
* 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_COMP,
/*
* 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_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,
/*
* 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_SMP_WAIT_TC_COMP,
/*
* 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_COMPLETED,
/*
* 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_ABORTING,
/*
* Simply the final state for the base request state machine.
*/
SCI_REQ_FINAL,
};
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);
}
/**
* isci_request_change_state() - This function sets the status of the request
* object.
* @request: This parameter points to the isci_request object
* @status: This Parameter is the new status of the object
*
*/
static inline enum isci_request_status
isci_request_change_state(struct isci_request *isci_request,
enum isci_request_status status)
{
enum isci_request_status old_state;
unsigned long flags;
dev_dbg(&isci_request->isci_host->pdev->dev,
"%s: isci_request = %p, state = 0x%x\n",
__func__,
isci_request,
status);
BUG_ON(isci_request == NULL);
spin_lock_irqsave(&isci_request->state_lock, flags);
old_state = isci_request->status;
isci_request->status = status;
spin_unlock_irqrestore(&isci_request->state_lock, flags);
return old_state;
}
/**
* isci_request_change_started_to_newstate() - This function sets the status of
* the request object.
* @request: This parameter points to the isci_request object
* @status: This Parameter is the new status of the object
*
* state previous to any change.
*/
static inline enum isci_request_status
isci_request_change_started_to_newstate(struct isci_request *isci_request,
struct completion *completion_ptr,
enum isci_request_status newstate)
{
enum isci_request_status old_state;
unsigned long flags;
spin_lock_irqsave(&isci_request->state_lock, flags);
old_state = isci_request->status;
if (old_state == started || old_state == aborting) {
BUG_ON(isci_request->io_request_completion != NULL);
isci_request->io_request_completion = completion_ptr;
isci_request->status = newstate;
}
spin_unlock_irqrestore(&isci_request->state_lock, flags);
dev_dbg(&isci_request->isci_host->pdev->dev,
"%s: isci_request = %p, old_state = 0x%x\n",
__func__,
isci_request,
old_state);
return old_state;
}
/**
* isci_request_change_started_to_aborted() - This function sets the status of
* the request object.
* @request: This parameter points to the isci_request object
* @completion_ptr: This parameter is saved as the kernel completion structure
* signalled when the old request completes.
*
* state previous to any change.
*/
static inline enum isci_request_status
isci_request_change_started_to_aborted(struct isci_request *isci_request,
struct completion *completion_ptr)
{
return isci_request_change_started_to_newstate(isci_request,
completion_ptr,
aborted);
}
#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);
void isci_terminate_pending_requests(struct isci_host *ihost,
struct isci_remote_device *idev);
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);
enum sci_status
sci_task_request_construct_sata(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_) */

219
drivers/scsi/isci/sas.h Normal file
View File

@ -0,0 +1,219 @@
/*
* 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;
#define SMP_RESP_HDR_SZ 4
/*
* 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

View 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 _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_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_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_ */

View 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__ */

View 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__ */

View File

@ -0,0 +1,942 @@
/*
* 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 block_protection_enable:1;
u32 block_size:2;
u32 block_protection_function: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 intermediate_crc_value:16;
u32 initial_crc_seed:16;
/* OFFSET 0xD4 */
u32 application_tag_for_verify:16;
u32 application_tag_for_generate:16;
/* OFFSET 0xD8 */
u32 reference_tag_seed_for_verify_function;
/* OFFSET 0xDC */
u32 reserved_DC;
/* OFFSET 0xE0 */
u32 reserved_E0_0:16;
u32 application_tag_mask_for_generate:16;
/* OFFSET 0xE4 */
u32 block_protection_control:16;
u32 application_tag_mask_for_verify:16;
/* OFFSET 0xE8 */
u32 block_protection_error:8;
u32 reserved_E8_0:24;
/* OFFSET 0xEC */
u32 reference_tag_seed_for_verify;
/* 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;
};
#endif /* _SCU_TASK_CONTEXT_H_ */

1676
drivers/scsi/isci/task.c Normal file

File diff suppressed because it is too large Load Diff

367
drivers/scsi/isci/task.h Normal file
View File

@ -0,0 +1,367 @@
/*
* 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"
struct isci_request;
/**
* enum isci_tmf_cb_state - This enum defines the possible states in which the
* TMF callback function is invoked during the TMF execution process.
*
*
*/
enum isci_tmf_cb_state {
isci_tmf_init_state = 0,
isci_tmf_started,
isci_tmf_timed_out
};
/**
* 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,
isci_tmf_sata_srst_high = TMF_LU_RESET + 0x100, /* Non SCSI */
isci_tmf_sata_srst_low = TMF_LU_RESET + 0x101 /* Non SCSI */
};
/**
* 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;
struct isci_remote_device *device;
enum isci_tmf_function_codes tmf_code;
int status;
/* The optional callback function allows the user process to
* track the TMF transmit / timeout conditions.
*/
void (*cb_state_func)(
enum isci_tmf_cb_state,
struct isci_tmf *, void *);
void *cb_data;
};
static inline void isci_print_tmf(struct isci_tmf *tmf)
{
if (SAS_PROTOCOL_SATA == tmf->proto)
dev_dbg(&tmf->device->isci_port->isci_host->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(&tmf->device->isci_port->isci_host->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 *));
int isci_bus_reset_handler(struct scsi_cmnd *cmd);
/**
* enum isci_completion_selection - This enum defines the possible actions to
* take with respect to a given request's notification back to libsas.
*
*
*/
enum isci_completion_selection {
isci_perform_normal_io_completion, /* Normal notify (task_done) */
isci_perform_aborted_io_completion, /* No notification. */
isci_perform_error_io_completion /* Use sas_task_abort */
};
static inline void isci_set_task_doneflags(
struct sas_task *task)
{
/* Since no futher action will be taken on this task,
* make sure to mark it complete from the lldd perspective.
*/
task->task_state_flags |= SAS_TASK_STATE_DONE;
task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
}
/**
* isci_task_all_done() - This function clears the task bits to indicate the
* LLDD is done with the task.
*
*
*/
static inline void isci_task_all_done(
struct sas_task *task)
{
unsigned long flags;
/* Since no futher action will be taken on this task,
* make sure to mark it complete from the lldd perspective.
*/
spin_lock_irqsave(&task->task_state_lock, flags);
isci_set_task_doneflags(task);
spin_unlock_irqrestore(&task->task_state_lock, flags);
}
/**
* isci_task_set_completion_status() - This function sets the completion status
* for the request.
* @task: This parameter is the completed request.
* @response: This parameter is the response code for the completed task.
* @status: This parameter is the status code for the completed task.
*
* @return The new notification mode for the request.
*/
static inline enum isci_completion_selection
isci_task_set_completion_status(
struct sas_task *task,
enum service_response response,
enum exec_status status,
enum isci_completion_selection task_notification_selection)
{
unsigned long flags;
spin_lock_irqsave(&task->task_state_lock, flags);
/* If a device reset is being indicated, make sure the I/O
* is in the error path.
*/
if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) {
/* Fail the I/O to make sure it goes into the error path. */
response = SAS_TASK_UNDELIVERED;
status = SAM_STAT_TASK_ABORTED;
task_notification_selection = isci_perform_error_io_completion;
}
task->task_status.resp = response;
task->task_status.stat = status;
switch (task_notification_selection) {
case isci_perform_error_io_completion:
if (task->task_proto == SAS_PROTOCOL_SMP) {
/* There is no error escalation in the SMP case.
* Convert to a normal completion to avoid the
* timeout in the discovery path and to let the
* next action take place quickly.
*/
task_notification_selection
= isci_perform_normal_io_completion;
/* Fall through to the normal case... */
} else {
/* Use sas_task_abort */
/* Leave SAS_TASK_STATE_DONE clear
* Leave SAS_TASK_AT_INITIATOR set.
*/
break;
}
case isci_perform_aborted_io_completion:
/* This path can occur with task-managed requests as well as
* requests terminated because of LUN or device resets.
*/
/* Fall through to the normal case... */
case isci_perform_normal_io_completion:
/* Normal notification (task_done) */
isci_set_task_doneflags(task);
break;
default:
WARN_ONCE(1, "unknown task_notification_selection: %d\n",
task_notification_selection);
break;
}
spin_unlock_irqrestore(&task->task_state_lock, flags);
return task_notification_selection;
}
/**
* isci_execpath_callback() - This function is called from the task
* execute path when the task needs to callback libsas about the submit-time
* task failure. The callback occurs either through the task's done function
* or through sas_task_abort. In the case of regular non-discovery SATA/STP I/O
* requests, libsas takes the host lock before calling execute task. Therefore
* in this situation the host lock must be managed before calling the func.
*
* @ihost: This parameter is the controller to which the I/O request was sent.
* @task: This parameter is the I/O request.
* @func: This parameter is the function to call in the correct context.
* @status: This parameter is the status code for the completed task.
*
*/
static inline void isci_execpath_callback(struct isci_host *ihost,
struct sas_task *task,
void (*func)(struct sas_task *))
{
struct domain_device *dev = task->dev;
if (dev_is_sata(dev) && task->uldd_task) {
unsigned long flags;
/* Since we are still in the submit path, and since
* libsas takes the host lock on behalf of SATA
* devices before I/O starts (in the non-discovery case),
* we need to unlock before we can call the callback function.
*/
raw_local_irq_save(flags);
spin_unlock(dev->sata_dev.ap->lock);
func(task);
spin_lock(dev->sata_dev.ap->lock);
raw_local_irq_restore(flags);
} else
func(task);
}
#endif /* !defined(_SCI_TASK_H_) */

View File

@ -0,0 +1,225 @@
/*
* 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"
int sci_unsolicited_frame_control_construct(struct isci_host *ihost)
{
struct sci_unsolicited_frame_control *uf_control = &ihost->uf_control;
struct sci_unsolicited_frame *uf;
u32 buf_len, header_len, i;
dma_addr_t dma;
size_t size;
void *virt;
/*
* Prepare all of the memory sizes for the UF headers, UF address
* table, and UF buffers themselves.
*/
buf_len = SCU_MAX_UNSOLICITED_FRAMES * SCU_UNSOLICITED_FRAME_BUFFER_SIZE;
header_len = SCU_MAX_UNSOLICITED_FRAMES * sizeof(struct scu_unsolicited_frame_header);
size = buf_len + header_len + SCU_MAX_UNSOLICITED_FRAMES * sizeof(dma_addr_t);
/*
* 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.
*/
virt = dmam_alloc_coherent(&ihost->pdev->dev, size, &dma, GFP_KERNEL);
if (!virt)
return -ENOMEM;
/*
* 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 + buf_len;
uf_control->headers.array = virt + buf_len;
/*
* 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 + buf_len + header_len;
uf_control->address_table.array = virt + buf_len + header_len;
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;
}
return 0;
}
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;
}

View File

@ -0,0 +1,278 @@
/*
* 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.
*/
dma_addr_t *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;
};
struct isci_host;
int 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_ */

View File

@ -82,6 +82,7 @@ fw-shipped-$(CONFIG_SERIAL_8250_CS) += cis/MT5634ZLX.cis cis/RS-COM-2P.cis \
fw-shipped-$(CONFIG_PCMCIA_SMC91C92) += ositech/Xilinx7OD.bin
fw-shipped-$(CONFIG_SCSI_ADVANSYS) += advansys/mcode.bin advansys/38C1600.bin \
advansys/3550.bin advansys/38C0800.bin
fw-shipped-$(CONFIG_SCSI_ISCI) += isci/isci_firmware.bin
fw-shipped-$(CONFIG_SCSI_QLOGIC_1280) += qlogic/1040.bin qlogic/1280.bin \
qlogic/12160.bin
fw-shipped-$(CONFIG_SCSI_QLOGICPTI) += qlogic/isp1000.bin

View File

@ -0,0 +1,16 @@
:10000000495343554F454D42E80018100002000087
:1000100000000000000000000101000000000000DE
:10002000FFFFCF5F0100000008DD0B0000FC0F00A8
:10003000097C0B006EFC0A00FFFFCF5F010000008F
:1000400008DD0B0000FC0F00097C0B006EFC0A00B1
:10005000FFFFCF5F0100000008DD0B0000FC0F0078
:10006000097C0B006EFC0A00FFFFCF5F010000005F
:1000700008DD0B0000FC0F00097C0B006EFC0A0081
:100080000101000000000000FFFFCF5F0200000040
:1000900008DD0B0000FC0F00097C0B006EFC0A0061
:1000A000FFFFCF5F0200000008DD0B0000FC0F0027
:1000B000097C0B006EFC0A00FFFFCF5F020000000E
:1000C00008DD0B0000FC0F00097C0B006EFC0A0031
:1000D000FFFFCF5F0200000008DD0B0000FC0F00F7
:0800E000097C0B006EFC0A0014
:00000001FF