forked from Minki/linux
Merge git://git.kernel.org/pub/scm/linux/kernel/git/djbw/isci
This commit is contained in:
commit
5b45b69c05
@ -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
|
||||
|
@ -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
|
||||
|
8
drivers/scsi/isci/Makefile
Normal file
8
drivers/scsi/isci/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
obj-$(CONFIG_SCSI_ISCI) += isci.o
|
||||
isci-objs := init.o phy.o request.o \
|
||||
remote_device.o port.o \
|
||||
host.o task.o probe_roms.o \
|
||||
remote_node_context.o \
|
||||
remote_node_table.o \
|
||||
unsolicited_frame_control.o \
|
||||
port_config.o \
|
19
drivers/scsi/isci/firmware/Makefile
Normal file
19
drivers/scsi/isci/firmware/Makefile
Normal 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)
|
36
drivers/scsi/isci/firmware/README
Normal file
36
drivers/scsi/isci/firmware/README
Normal 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.
|
||||
|
99
drivers/scsi/isci/firmware/create_fw.c
Normal file
99
drivers/scsi/isci/firmware/create_fw.c
Normal 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;
|
||||
}
|
77
drivers/scsi/isci/firmware/create_fw.h
Normal file
77
drivers/scsi/isci/firmware/create_fw.h
Normal 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
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
542
drivers/scsi/isci/host.h
Normal 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
565
drivers/scsi/isci/init.c
Normal 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
538
drivers/scsi/isci/isci.h
Normal 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
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
504
drivers/scsi/isci/phy.h
Normal 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
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
306
drivers/scsi/isci/port.h
Normal 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_) */
|
754
drivers/scsi/isci/port_config.c
Normal file
754
drivers/scsi/isci/port_config.c
Normal 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;
|
||||
}
|
242
drivers/scsi/isci/probe_roms.c
Normal file
242
drivers/scsi/isci/probe_roms.c
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* 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;
|
||||
ssize_t 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;
|
||||
}
|
249
drivers/scsi/isci/probe_roms.h
Normal file
249
drivers/scsi/isci/probe_roms.h
Normal 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
|
1934
drivers/scsi/isci/registers.h
Normal file
1934
drivers/scsi/isci/registers.h
Normal file
File diff suppressed because it is too large
Load Diff
1501
drivers/scsi/isci/remote_device.c
Normal file
1501
drivers/scsi/isci/remote_device.c
Normal file
File diff suppressed because it is too large
Load Diff
352
drivers/scsi/isci/remote_device.h
Normal file
352
drivers/scsi/isci/remote_device.h
Normal 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_) */
|
627
drivers/scsi/isci/remote_node_context.c
Normal file
627
drivers/scsi/isci/remote_node_context.c
Normal 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;
|
||||
}
|
||||
}
|
224
drivers/scsi/isci/remote_node_context.h
Normal file
224
drivers/scsi/isci/remote_node_context.h
Normal 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_ */
|
598
drivers/scsi/isci/remote_node_table.c
Normal file
598
drivers/scsi/isci/remote_node_table.c
Normal file
@ -0,0 +1,598 @@
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file contains the implementation of the SCIC_SDS_REMOTE_NODE_TABLE
|
||||
* public, protected, and private methods.
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include "remote_node_table.h"
|
||||
#include "remote_node_context.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node index table from which the
|
||||
* selection will be made.
|
||||
* @group_table_index: This is the index to the group table from which to
|
||||
* search for an available selection.
|
||||
*
|
||||
* This routine will find the bit position in absolute bit terms of the next 32
|
||||
* + bit position. If there are available bits in the first u32 then it is
|
||||
* just bit position. u32 This is the absolute bit position for an available
|
||||
* group.
|
||||
*/
|
||||
static u32 sci_remote_node_table_get_group_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index)
|
||||
{
|
||||
u32 dword_index;
|
||||
u32 *group_table;
|
||||
u32 bit_index;
|
||||
|
||||
group_table = remote_node_table->remote_node_groups[group_table_index];
|
||||
|
||||
for (dword_index = 0; dword_index < remote_node_table->group_array_size; dword_index++) {
|
||||
if (group_table[dword_index] != 0) {
|
||||
for (bit_index = 0; bit_index < 32; bit_index++) {
|
||||
if ((group_table[dword_index] & (1 << bit_index)) != 0) {
|
||||
return (dword_index * 32) + bit_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table This the remote node table in which to clear the
|
||||
* selector.
|
||||
* @set_index: This is the remote node selector in which the change will be
|
||||
* made.
|
||||
* @group_index: This is the bit index in the table to be modified.
|
||||
*
|
||||
* This method will clear the group index entry in the specified group index
|
||||
* table. none
|
||||
*/
|
||||
static void sci_remote_node_table_clear_group_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_index;
|
||||
u32 bit_index;
|
||||
u32 *group_table;
|
||||
|
||||
BUG_ON(group_table_index >= SCU_STP_REMOTE_NODE_COUNT);
|
||||
BUG_ON(group_index >= (u32)(remote_node_table->group_array_size * 32));
|
||||
|
||||
dword_index = group_index / 32;
|
||||
bit_index = group_index % 32;
|
||||
group_table = remote_node_table->remote_node_groups[group_table_index];
|
||||
|
||||
group_table[dword_index] = group_table[dword_index] & ~(1 << bit_index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table This the remote node table in which to set the
|
||||
* selector.
|
||||
* @group_table_index: This is the remote node selector in which the change
|
||||
* will be made.
|
||||
* @group_index: This is the bit position in the table to be modified.
|
||||
*
|
||||
* This method will set the group index bit entry in the specified gropu index
|
||||
* table. none
|
||||
*/
|
||||
static void sci_remote_node_table_set_group_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_index;
|
||||
u32 bit_index;
|
||||
u32 *group_table;
|
||||
|
||||
BUG_ON(group_table_index >= SCU_STP_REMOTE_NODE_COUNT);
|
||||
BUG_ON(group_index >= (u32)(remote_node_table->group_array_size * 32));
|
||||
|
||||
dword_index = group_index / 32;
|
||||
bit_index = group_index % 32;
|
||||
group_table = remote_node_table->remote_node_groups[group_table_index];
|
||||
|
||||
group_table[dword_index] = group_table[dword_index] | (1 << bit_index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table This is the remote node table in which to modify
|
||||
* the remote node availability.
|
||||
* @remote_node_index: This is the remote node index that is being returned to
|
||||
* the table.
|
||||
*
|
||||
* This method will set the remote to available in the remote node allocation
|
||||
* table. none
|
||||
*/
|
||||
static void sci_remote_node_table_set_node_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 slot_normalized;
|
||||
u32 slot_position;
|
||||
|
||||
BUG_ON(
|
||||
(remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD)
|
||||
<= (remote_node_index / SCU_STP_REMOTE_NODE_COUNT)
|
||||
);
|
||||
|
||||
dword_location = remote_node_index / SCIC_SDS_REMOTE_NODES_PER_DWORD;
|
||||
dword_remainder = remote_node_index % SCIC_SDS_REMOTE_NODES_PER_DWORD;
|
||||
slot_normalized = (dword_remainder / SCU_STP_REMOTE_NODE_COUNT) * sizeof(u32);
|
||||
slot_position = remote_node_index % SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
remote_node_table->available_remote_nodes[dword_location] |=
|
||||
1 << (slot_normalized + slot_position);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table This is the remote node table from which to clear
|
||||
* the available remote node bit.
|
||||
* @remote_node_index: This is the remote node index which is to be cleared
|
||||
* from the table.
|
||||
*
|
||||
* This method clears the remote node index from the table of available remote
|
||||
* nodes. none
|
||||
*/
|
||||
static void sci_remote_node_table_clear_node_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 slot_position;
|
||||
u32 slot_normalized;
|
||||
|
||||
BUG_ON(
|
||||
(remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD)
|
||||
<= (remote_node_index / SCU_STP_REMOTE_NODE_COUNT)
|
||||
);
|
||||
|
||||
dword_location = remote_node_index / SCIC_SDS_REMOTE_NODES_PER_DWORD;
|
||||
dword_remainder = remote_node_index % SCIC_SDS_REMOTE_NODES_PER_DWORD;
|
||||
slot_normalized = (dword_remainder / SCU_STP_REMOTE_NODE_COUNT) * sizeof(u32);
|
||||
slot_position = remote_node_index % SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
remote_node_table->available_remote_nodes[dword_location] &=
|
||||
~(1 << (slot_normalized + slot_position));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table The remote node table from which the slot will be
|
||||
* cleared.
|
||||
* @group_index: The index for the slot that is to be cleared.
|
||||
*
|
||||
* This method clears the entire table slot at the specified slot index. none
|
||||
*/
|
||||
static void sci_remote_node_table_clear_group(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 dword_value;
|
||||
|
||||
BUG_ON(
|
||||
(remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD)
|
||||
<= (group_index / SCU_STP_REMOTE_NODE_COUNT)
|
||||
);
|
||||
|
||||
dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
|
||||
dword_value = remote_node_table->available_remote_nodes[dword_location];
|
||||
dword_value &= ~(SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4));
|
||||
remote_node_table->available_remote_nodes[dword_location] = dword_value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table:
|
||||
*
|
||||
* THis method sets an entire remote node group in the remote node table.
|
||||
*/
|
||||
static void sci_remote_node_table_set_group(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 dword_value;
|
||||
|
||||
BUG_ON(
|
||||
(remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD)
|
||||
<= (group_index / SCU_STP_REMOTE_NODE_COUNT)
|
||||
);
|
||||
|
||||
dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
|
||||
dword_value = remote_node_table->available_remote_nodes[dword_location];
|
||||
dword_value |= (SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4));
|
||||
remote_node_table->available_remote_nodes[dword_location] = dword_value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node table that for which the group
|
||||
* value is to be returned.
|
||||
* @group_index: This is the group index to use to find the group value.
|
||||
*
|
||||
* This method will return the group value for the specified group index. The
|
||||
* bit values at the specified remote node group index.
|
||||
*/
|
||||
static u8 sci_remote_node_table_get_group_value(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_index)
|
||||
{
|
||||
u32 dword_location;
|
||||
u32 dword_remainder;
|
||||
u32 dword_value;
|
||||
|
||||
dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD;
|
||||
|
||||
dword_value = remote_node_table->available_remote_nodes[dword_location];
|
||||
dword_value &= (SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4));
|
||||
dword_value = dword_value >> (dword_remainder * 4);
|
||||
|
||||
return (u8)dword_value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table The remote that which is to be initialized.
|
||||
* @remote_node_entries: The number of entries to put in the table.
|
||||
*
|
||||
* This method will initialize the remote node table for use. none
|
||||
*/
|
||||
void sci_remote_node_table_initialize(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_entries)
|
||||
{
|
||||
u32 index;
|
||||
|
||||
/*
|
||||
* Initialize the raw data we could improve the speed by only initializing
|
||||
* those entries that we are actually going to be used */
|
||||
memset(
|
||||
remote_node_table->available_remote_nodes,
|
||||
0x00,
|
||||
sizeof(remote_node_table->available_remote_nodes)
|
||||
);
|
||||
|
||||
memset(
|
||||
remote_node_table->remote_node_groups,
|
||||
0x00,
|
||||
sizeof(remote_node_table->remote_node_groups)
|
||||
);
|
||||
|
||||
/* Initialize the available remote node sets */
|
||||
remote_node_table->available_nodes_array_size = (u16)
|
||||
(remote_node_entries / SCIC_SDS_REMOTE_NODES_PER_DWORD)
|
||||
+ ((remote_node_entries % SCIC_SDS_REMOTE_NODES_PER_DWORD) != 0);
|
||||
|
||||
|
||||
/* Initialize each full DWORD to a FULL SET of remote nodes */
|
||||
for (index = 0; index < remote_node_entries; index++) {
|
||||
sci_remote_node_table_set_node_index(remote_node_table, index);
|
||||
}
|
||||
|
||||
remote_node_table->group_array_size = (u16)
|
||||
(remote_node_entries / (SCU_STP_REMOTE_NODE_COUNT * 32))
|
||||
+ ((remote_node_entries % (SCU_STP_REMOTE_NODE_COUNT * 32)) != 0);
|
||||
|
||||
for (index = 0; index < (remote_node_entries / SCU_STP_REMOTE_NODE_COUNT); index++) {
|
||||
/*
|
||||
* These are all guaranteed to be full slot values so fill them in the
|
||||
* available sets of 3 remote nodes */
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 2, index);
|
||||
}
|
||||
|
||||
/* Now fill in any remainders that we may find */
|
||||
if ((remote_node_entries % SCU_STP_REMOTE_NODE_COUNT) == 2) {
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 1, index);
|
||||
} else if ((remote_node_entries % SCU_STP_REMOTE_NODE_COUNT) == 1) {
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 0, index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @out]: remote_node_table The remote node table from which to allocate a
|
||||
* remote node.
|
||||
* @table_index: The group index that is to be used for the search.
|
||||
*
|
||||
* This method will allocate a single RNi from the remote node table. The
|
||||
* table index will determine from which remote node group table to search.
|
||||
* This search may fail and another group node table can be specified. The
|
||||
* function is designed to allow a serach of the available single remote node
|
||||
* group up to the triple remote node group. If an entry is found in the
|
||||
* specified table the remote node is removed and the remote node groups are
|
||||
* updated. The RNi value or an invalid remote node context if an RNi can not
|
||||
* be found.
|
||||
*/
|
||||
static u16 sci_remote_node_table_allocate_single_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index)
|
||||
{
|
||||
u8 index;
|
||||
u8 group_value;
|
||||
u32 group_index;
|
||||
u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX;
|
||||
|
||||
group_index = sci_remote_node_table_get_group_index(
|
||||
remote_node_table, group_table_index);
|
||||
|
||||
/* We could not find an available slot in the table selector 0 */
|
||||
if (group_index != SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX) {
|
||||
group_value = sci_remote_node_table_get_group_value(
|
||||
remote_node_table, group_index);
|
||||
|
||||
for (index = 0; index < SCU_STP_REMOTE_NODE_COUNT; index++) {
|
||||
if (((1 << index) & group_value) != 0) {
|
||||
/* We have selected a bit now clear it */
|
||||
remote_node_index = (u16)(group_index * SCU_STP_REMOTE_NODE_COUNT
|
||||
+ index);
|
||||
|
||||
sci_remote_node_table_clear_group_index(
|
||||
remote_node_table, group_table_index, group_index
|
||||
);
|
||||
|
||||
sci_remote_node_table_clear_node_index(
|
||||
remote_node_table, remote_node_index
|
||||
);
|
||||
|
||||
if (group_table_index > 0) {
|
||||
sci_remote_node_table_set_group_index(
|
||||
remote_node_table, group_table_index - 1, group_index
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return remote_node_index;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node table from which to allocate the
|
||||
* remote node entries.
|
||||
* @group_table_index: THis is the group table index which must equal two (2)
|
||||
* for this operation.
|
||||
*
|
||||
* This method will allocate three consecutive remote node context entries. If
|
||||
* there are no remaining triple entries the function will return a failure.
|
||||
* The remote node index that represents three consecutive remote node entries
|
||||
* or an invalid remote node context if none can be found.
|
||||
*/
|
||||
static u16 sci_remote_node_table_allocate_triple_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 group_table_index)
|
||||
{
|
||||
u32 group_index;
|
||||
u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX;
|
||||
|
||||
group_index = sci_remote_node_table_get_group_index(
|
||||
remote_node_table, group_table_index);
|
||||
|
||||
if (group_index != SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX) {
|
||||
remote_node_index = (u16)group_index * SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
sci_remote_node_table_clear_group_index(
|
||||
remote_node_table, group_table_index, group_index
|
||||
);
|
||||
|
||||
sci_remote_node_table_clear_group(
|
||||
remote_node_table, group_index
|
||||
);
|
||||
}
|
||||
|
||||
return remote_node_index;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node table from which the remote node
|
||||
* allocation is to take place.
|
||||
* @remote_node_count: This is ther remote node count which is one of
|
||||
* SCU_SSP_REMOTE_NODE_COUNT(1) or SCU_STP_REMOTE_NODE_COUNT(3).
|
||||
*
|
||||
* This method will allocate a remote node that mataches the remote node count
|
||||
* specified by the caller. Valid values for remote node count is
|
||||
* SCU_SSP_REMOTE_NODE_COUNT(1) or SCU_STP_REMOTE_NODE_COUNT(3). u16 This is
|
||||
* the remote node index that is returned or an invalid remote node context.
|
||||
*/
|
||||
u16 sci_remote_node_table_allocate_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_count)
|
||||
{
|
||||
u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX;
|
||||
|
||||
if (remote_node_count == SCU_SSP_REMOTE_NODE_COUNT) {
|
||||
remote_node_index =
|
||||
sci_remote_node_table_allocate_single_remote_node(
|
||||
remote_node_table, 0);
|
||||
|
||||
if (remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) {
|
||||
remote_node_index =
|
||||
sci_remote_node_table_allocate_single_remote_node(
|
||||
remote_node_table, 1);
|
||||
}
|
||||
|
||||
if (remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) {
|
||||
remote_node_index =
|
||||
sci_remote_node_table_allocate_single_remote_node(
|
||||
remote_node_table, 2);
|
||||
}
|
||||
} else if (remote_node_count == SCU_STP_REMOTE_NODE_COUNT) {
|
||||
remote_node_index =
|
||||
sci_remote_node_table_allocate_triple_remote_node(
|
||||
remote_node_table, 2);
|
||||
}
|
||||
|
||||
return remote_node_index;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table:
|
||||
*
|
||||
* This method will free a single remote node index back to the remote node
|
||||
* table. This routine will update the remote node groups
|
||||
*/
|
||||
static void sci_remote_node_table_release_single_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u16 remote_node_index)
|
||||
{
|
||||
u32 group_index;
|
||||
u8 group_value;
|
||||
|
||||
group_index = remote_node_index / SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
group_value = sci_remote_node_table_get_group_value(remote_node_table, group_index);
|
||||
|
||||
/*
|
||||
* Assert that we are not trying to add an entry to a slot that is already
|
||||
* full. */
|
||||
BUG_ON(group_value == SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE);
|
||||
|
||||
if (group_value == 0x00) {
|
||||
/*
|
||||
* There are no entries in this slot so it must be added to the single
|
||||
* slot table. */
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 0, group_index);
|
||||
} else if ((group_value & (group_value - 1)) == 0) {
|
||||
/*
|
||||
* There is only one entry in this slot so it must be moved from the
|
||||
* single slot table to the dual slot table */
|
||||
sci_remote_node_table_clear_group_index(remote_node_table, 0, group_index);
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 1, group_index);
|
||||
} else {
|
||||
/*
|
||||
* There are two entries in the slot so it must be moved from the dual
|
||||
* slot table to the tripple slot table. */
|
||||
sci_remote_node_table_clear_group_index(remote_node_table, 1, group_index);
|
||||
sci_remote_node_table_set_group_index(remote_node_table, 2, group_index);
|
||||
}
|
||||
|
||||
sci_remote_node_table_set_node_index(remote_node_table, remote_node_index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: This is the remote node table to which the remote node
|
||||
* index is to be freed.
|
||||
*
|
||||
* This method will release a group of three consecutive remote nodes back to
|
||||
* the free remote nodes.
|
||||
*/
|
||||
static void sci_remote_node_table_release_triple_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u16 remote_node_index)
|
||||
{
|
||||
u32 group_index;
|
||||
|
||||
group_index = remote_node_index / SCU_STP_REMOTE_NODE_COUNT;
|
||||
|
||||
sci_remote_node_table_set_group_index(
|
||||
remote_node_table, 2, group_index
|
||||
);
|
||||
|
||||
sci_remote_node_table_set_group(remote_node_table, group_index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @remote_node_table: The remote node table to which the remote node index is
|
||||
* to be freed.
|
||||
* @remote_node_count: This is the count of consecutive remote nodes that are
|
||||
* to be freed.
|
||||
*
|
||||
* This method will release the remote node index back into the remote node
|
||||
* table free pool.
|
||||
*/
|
||||
void sci_remote_node_table_release_remote_node_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_count,
|
||||
u16 remote_node_index)
|
||||
{
|
||||
if (remote_node_count == SCU_SSP_REMOTE_NODE_COUNT) {
|
||||
sci_remote_node_table_release_single_remote_node(
|
||||
remote_node_table, remote_node_index);
|
||||
} else if (remote_node_count == SCU_STP_REMOTE_NODE_COUNT) {
|
||||
sci_remote_node_table_release_triple_remote_node(
|
||||
remote_node_table, remote_node_index);
|
||||
}
|
||||
}
|
||||
|
188
drivers/scsi/isci/remote_node_table.h
Normal file
188
drivers/scsi/isci/remote_node_table.h
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SCIC_SDS_REMOTE_NODE_TABLE_H_
|
||||
#define _SCIC_SDS_REMOTE_NODE_TABLE_H_
|
||||
|
||||
#include "isci.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Remote node sets are sets of remote node index in the remtoe node table The
|
||||
* SCU hardware requires that STP remote node entries take three consecutive
|
||||
* remote node index so the table is arranged in sets of three. The bits are
|
||||
* used as 0111 0111 to make a byte and the bits define the set of three remote
|
||||
* nodes to use as a sequence.
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODE_SETS_PER_BYTE 2
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Since the remote node table is organized as DWORDS take the remote node sets
|
||||
* in bytes and represent them in DWORDs. The lowest ordered bits are the ones
|
||||
* used in case full DWORD is not being used. i.e. 0000 0000 0000 0000 0111
|
||||
* 0111 0111 0111 // if only a single WORD is in use in the DWORD.
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD \
|
||||
(sizeof(u32) * SCIC_SDS_REMOTE_NODE_SETS_PER_BYTE)
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This is a count of the numeber of remote nodes that can be represented in a
|
||||
* byte
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODES_PER_BYTE \
|
||||
(SCU_STP_REMOTE_NODE_COUNT * SCIC_SDS_REMOTE_NODE_SETS_PER_BYTE)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This is a count of the number of remote nodes that can be represented in a
|
||||
* DWROD
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODES_PER_DWORD \
|
||||
(sizeof(u32) * SCIC_SDS_REMOTE_NODES_PER_BYTE)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This is the number of bits in a remote node group
|
||||
*/
|
||||
#define SCIC_SDS_REMOTE_NODES_BITS_PER_GROUP 4
|
||||
|
||||
#define SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX (0xFFFFFFFF)
|
||||
#define SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE (0x07)
|
||||
#define SCIC_SDS_REMOTE_NODE_TABLE_EMPTY_SLOT_VALUE (0x00)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Expander attached sata remote node count
|
||||
*/
|
||||
#define SCU_STP_REMOTE_NODE_COUNT 3
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Expander or direct attached ssp remote node count
|
||||
*/
|
||||
#define SCU_SSP_REMOTE_NODE_COUNT 1
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Direct attached STP remote node count
|
||||
*/
|
||||
#define SCU_SATA_REMOTE_NODE_COUNT 1
|
||||
|
||||
/**
|
||||
* struct sci_remote_node_table -
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct sci_remote_node_table {
|
||||
/**
|
||||
* This field contains the array size in dwords
|
||||
*/
|
||||
u16 available_nodes_array_size;
|
||||
|
||||
/**
|
||||
* This field contains the array size of the
|
||||
*/
|
||||
u16 group_array_size;
|
||||
|
||||
/**
|
||||
* This field is the array of available remote node entries in bits.
|
||||
* Because of the way STP remote node data is allocated on the SCU hardware
|
||||
* the remote nodes must occupy three consecutive remote node context
|
||||
* entries. For ease of allocation and de-allocation we have broken the
|
||||
* sets of three into a single nibble. When the STP RNi is allocated all
|
||||
* of the bits in the nibble are cleared. This math results in a table size
|
||||
* of MAX_REMOTE_NODES / CONSECUTIVE RNi ENTRIES for STP / 2 entries per byte.
|
||||
*/
|
||||
u32 available_remote_nodes[
|
||||
(SCI_MAX_REMOTE_DEVICES / SCIC_SDS_REMOTE_NODES_PER_DWORD)
|
||||
+ ((SCI_MAX_REMOTE_DEVICES % SCIC_SDS_REMOTE_NODES_PER_DWORD) != 0)];
|
||||
|
||||
/**
|
||||
* This field is the nibble selector for the above table. There are three
|
||||
* possible selectors each for fast lookup when trying to find one, two or
|
||||
* three remote node entries.
|
||||
*/
|
||||
u32 remote_node_groups[
|
||||
SCU_STP_REMOTE_NODE_COUNT][
|
||||
(SCI_MAX_REMOTE_DEVICES / (32 * SCU_STP_REMOTE_NODE_COUNT))
|
||||
+ ((SCI_MAX_REMOTE_DEVICES % (32 * SCU_STP_REMOTE_NODE_COUNT)) != 0)];
|
||||
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------------- */
|
||||
|
||||
void sci_remote_node_table_initialize(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_entries);
|
||||
|
||||
u16 sci_remote_node_table_allocate_remote_node(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_count);
|
||||
|
||||
void sci_remote_node_table_release_remote_node_index(
|
||||
struct sci_remote_node_table *remote_node_table,
|
||||
u32 remote_node_count,
|
||||
u16 remote_node_index);
|
||||
|
||||
#endif /* _SCIC_SDS_REMOTE_NODE_TABLE_H_ */
|
3391
drivers/scsi/isci/request.c
Normal file
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
448
drivers/scsi/isci/request.h
Normal 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
219
drivers/scsi/isci/sas.h
Normal 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
|
283
drivers/scsi/isci/scu_completion_codes.h
Normal file
283
drivers/scsi/isci/scu_completion_codes.h
Normal file
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _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_ */
|
336
drivers/scsi/isci/scu_event_codes.h
Normal file
336
drivers/scsi/isci/scu_event_codes.h
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __SCU_EVENT_CODES_HEADER__
|
||||
#define __SCU_EVENT_CODES_HEADER__
|
||||
|
||||
/**
|
||||
* This file contains the constants and macros for the SCU event codes.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#define SCU_EVENT_TYPE_CODE_SHIFT 24
|
||||
#define SCU_EVENT_TYPE_CODE_MASK 0x0F000000
|
||||
|
||||
#define SCU_EVENT_SPECIFIC_CODE_SHIFT 18
|
||||
#define SCU_EVENT_SPECIFIC_CODE_MASK 0x00FC0000
|
||||
|
||||
#define SCU_EVENT_CODE_MASK \
|
||||
(SCU_EVENT_TYPE_CODE_MASK | SCU_EVENT_SPECIFIC_CODE_MASK)
|
||||
|
||||
/**
|
||||
* SCU_EVENT_TYPE() -
|
||||
*
|
||||
* This macro constructs an SCU event type from the type value.
|
||||
*/
|
||||
#define SCU_EVENT_TYPE(type) \
|
||||
((u32)(type) << SCU_EVENT_TYPE_CODE_SHIFT)
|
||||
|
||||
/**
|
||||
* SCU_EVENT_SPECIFIC() -
|
||||
*
|
||||
* This macro constructs an SCU event specifier from the code value.
|
||||
*/
|
||||
#define SCU_EVENT_SPECIFIC(code) \
|
||||
((u32)(code) << SCU_EVENT_SPECIFIC_CODE_SHIFT)
|
||||
|
||||
/**
|
||||
* SCU_EVENT_MESSAGE() -
|
||||
*
|
||||
* This macro constructs a combines an SCU event type and SCU event specifier
|
||||
* from the type and code values.
|
||||
*/
|
||||
#define SCU_EVENT_MESSAGE(type, code) \
|
||||
((type) | SCU_EVENT_SPECIFIC(code))
|
||||
|
||||
/**
|
||||
* SCU_EVENT_TYPE() -
|
||||
*
|
||||
* SCU_EVENT_TYPES
|
||||
*/
|
||||
#define SCU_EVENT_TYPE_SMU_COMMAND_ERROR SCU_EVENT_TYPE(0x08)
|
||||
#define SCU_EVENT_TYPE_SMU_PCQ_ERROR SCU_EVENT_TYPE(0x09)
|
||||
#define SCU_EVENT_TYPE_SMU_ERROR SCU_EVENT_TYPE(0x00)
|
||||
#define SCU_EVENT_TYPE_TRANSPORT_ERROR SCU_EVENT_TYPE(0x01)
|
||||
#define SCU_EVENT_TYPE_BROADCAST_CHANGE SCU_EVENT_TYPE(0x02)
|
||||
#define SCU_EVENT_TYPE_OSSP_EVENT SCU_EVENT_TYPE(0x03)
|
||||
#define SCU_EVENT_TYPE_FATAL_MEMORY_ERROR SCU_EVENT_TYPE(0x0F)
|
||||
#define SCU_EVENT_TYPE_RNC_SUSPEND_TX SCU_EVENT_TYPE(0x04)
|
||||
#define SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX SCU_EVENT_TYPE(0x05)
|
||||
#define SCU_EVENT_TYPE_RNC_OPS_MISC SCU_EVENT_TYPE(0x06)
|
||||
#define SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT SCU_EVENT_TYPE(0x07)
|
||||
#define SCU_EVENT_TYPE_ERR_CNT_EVENT SCU_EVENT_TYPE(0x0A)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SCU_EVENT_SPECIFIERS
|
||||
*/
|
||||
#define SCU_EVENT_SPECIFIER_DRIVER_SUSPEND 0x20
|
||||
#define SCU_EVENT_SPECIFIER_RNC_RELEASE 0x00
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SMU_COMMAND_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_INVALID_CONTEXT_COMMAND \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_COMMAND_ERROR, 0x00)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SMU_PCQ_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_UNCORRECTABLE_PCQ_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_PCQ_ERROR, 0x00)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* SMU_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_UNCORRECTABLE_REGISTER_WRITE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x02)
|
||||
#define SCU_EVENT_UNCORRECTABLE_REGISTER_READ \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x03)
|
||||
#define SCU_EVENT_PCIE_INTERFACE_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x04)
|
||||
#define SCU_EVENT_FUNCTION_LEVEL_RESET \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x05)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* TRANSPORT_LEVEL_ERRORS
|
||||
*/
|
||||
#define SCU_EVENT_ACK_NAK_TIMEOUT_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_TRANSPORT_ERROR, 0x00)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* BROADCAST_CHANGE_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_BROADCAST_CHANGE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x01)
|
||||
#define SCU_EVENT_BROADCAST_RESERVED0 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x02)
|
||||
#define SCU_EVENT_BROADCAST_RESERVED1 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x03)
|
||||
#define SCU_EVENT_BROADCAST_SES \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x04)
|
||||
#define SCU_EVENT_BROADCAST_EXPANDER \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x05)
|
||||
#define SCU_EVENT_BROADCAST_AEN \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x06)
|
||||
#define SCU_EVENT_BROADCAST_RESERVED3 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x07)
|
||||
#define SCU_EVENT_BROADCAST_RESERVED4 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x08)
|
||||
#define SCU_EVENT_PE_SUSPENDED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x09)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* OSSP_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_PORT_SELECTOR_DETECTED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x10)
|
||||
#define SCU_EVENT_SENT_PORT_SELECTION \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x11)
|
||||
#define SCU_EVENT_HARD_RESET_TRANSMITTED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x12)
|
||||
#define SCU_EVENT_HARD_RESET_RECEIVED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x13)
|
||||
#define SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x15)
|
||||
#define SCU_EVENT_LINK_FAILURE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x16)
|
||||
#define SCU_EVENT_SATA_SPINUP_HOLD \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x17)
|
||||
#define SCU_EVENT_SAS_15_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x18)
|
||||
#define SCU_EVENT_SAS_15 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x19)
|
||||
#define SCU_EVENT_SAS_30_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1A)
|
||||
#define SCU_EVENT_SAS_30 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1B)
|
||||
#define SCU_EVENT_SAS_60_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1C)
|
||||
#define SCU_EVENT_SAS_60 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1D)
|
||||
#define SCU_EVENT_SATA_15_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1E)
|
||||
#define SCU_EVENT_SATA_15 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1F)
|
||||
#define SCU_EVENT_SATA_30_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x20)
|
||||
#define SCU_EVENT_SATA_30 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x21)
|
||||
#define SCU_EVENT_SATA_60_SSC \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x22)
|
||||
#define SCU_EVENT_SATA_60 \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x23)
|
||||
#define SCU_EVENT_SAS_PHY_DETECTED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x24)
|
||||
#define SCU_EVENT_SATA_PHY_DETECTED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x25)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* FATAL_INTERNAL_MEMORY_ERROR_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_TSC_RNSC_UNCORRECTABLE_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_FATAL_MEMORY_ERROR, 0x00)
|
||||
#define SCU_EVENT_TC_RNC_UNCORRECTABLE_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_FATAL_MEMORY_ERROR, 0x01)
|
||||
#define SCU_EVENT_ZPT_UNCORRECTABLE_ERROR \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_FATAL_MEMORY_ERROR, 0x02)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* REMOTE_NODE_SUSPEND_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_TL_RNC_SUSPEND_TX \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX, 0x00)
|
||||
#define SCU_EVENT_TL_RNC_SUSPEND_TX_RX \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX, 0x00)
|
||||
#define SCU_EVENT_DRIVER_POST_RNC_SUSPEND_TX \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX, 0x20)
|
||||
#define SCU_EVENT_DRIVER_POST_RNC_SUSPEND_TX_RX \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX, 0x20)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* REMOTE_NODE_MISC_EVENTS
|
||||
*/
|
||||
#define SCU_EVENT_POST_RCN_RELEASE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, SCU_EVENT_SPECIFIER_RNC_RELEASE)
|
||||
#define SCU_EVENT_POST_IT_NEXUS_LOSS_TIMER_ENABLE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x01)
|
||||
#define SCU_EVENT_POST_IT_NEXUS_LOSS_TIMER_DISABLE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x02)
|
||||
#define SCU_EVENT_POST_RNC_COMPLETE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x03)
|
||||
#define SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x04)
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* ERROR_COUNT_EVENT
|
||||
*/
|
||||
#define SCU_EVENT_RX_CREDIT_BLOCKED_RECEIVED \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_ERR_CNT_EVENT, 0x00)
|
||||
#define SCU_EVENT_TX_DONE_CREDIT_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_ERR_CNT_EVENT, 0x01)
|
||||
#define SCU_EVENT_RX_DONE_CREDIT_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_ERR_CNT_EVENT, 0x02)
|
||||
|
||||
/**
|
||||
* scu_get_event_type() -
|
||||
*
|
||||
* This macro returns the SCU event type from the event code.
|
||||
*/
|
||||
#define scu_get_event_type(event_code) \
|
||||
((event_code) & SCU_EVENT_TYPE_CODE_MASK)
|
||||
|
||||
/**
|
||||
* scu_get_event_specifier() -
|
||||
*
|
||||
* This macro returns the SCU event specifier from the event code.
|
||||
*/
|
||||
#define scu_get_event_specifier(event_code) \
|
||||
((event_code) & SCU_EVENT_SPECIFIC_CODE_MASK)
|
||||
|
||||
/**
|
||||
* scu_get_event_code() -
|
||||
*
|
||||
* This macro returns the combined SCU event type and SCU event specifier from
|
||||
* the event code.
|
||||
*/
|
||||
#define scu_get_event_code(event_code) \
|
||||
((event_code) & SCU_EVENT_CODE_MASK)
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* PTS_SCHEDULE_EVENT
|
||||
*/
|
||||
#define SCU_EVENT_SMP_RESPONSE_NO_PE \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT, 0x00)
|
||||
#define SCU_EVENT_SPECIFIC_SMP_RESPONSE_NO_PE \
|
||||
scu_get_event_specifier(SCU_EVENT_SMP_RESPONSE_NO_PE)
|
||||
|
||||
#define SCU_EVENT_TASK_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT, 0x01)
|
||||
#define SCU_EVENT_SPECIFIC_TASK_TIMEOUT \
|
||||
scu_get_event_specifier(SCU_EVENT_TASK_TIMEOUT)
|
||||
|
||||
#define SCU_EVENT_IT_NEXUS_TIMEOUT \
|
||||
SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT, 0x02)
|
||||
#define SCU_EVENT_SPECIFIC_IT_NEXUS_TIMEOUT \
|
||||
scu_get_event_specifier(SCU_EVENT_IT_NEXUS_TIMEOUT)
|
||||
|
||||
|
||||
#endif /* __SCU_EVENT_CODES_HEADER__ */
|
229
drivers/scsi/isci/scu_remote_node_context.h
Normal file
229
drivers/scsi/isci/scu_remote_node_context.h
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called LICENSE.GPL.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __SCU_REMOTE_NODE_CONTEXT_HEADER__
|
||||
#define __SCU_REMOTE_NODE_CONTEXT_HEADER__
|
||||
|
||||
/**
|
||||
* This file contains the structures and constatns used by the SCU hardware to
|
||||
* describe a remote node context.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct ssp_remote_node_context - This structure contains the SCU hardware
|
||||
* definition for an SSP remote node.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct ssp_remote_node_context {
|
||||
/* WORD 0 */
|
||||
|
||||
/**
|
||||
* This field is the remote node index assigned for this remote node. All
|
||||
* remote nodes must have a unique remote node index. The value of the remote
|
||||
* node index can not exceed the maximum number of remote nodes reported in
|
||||
* the SCU device context capacity register.
|
||||
*/
|
||||
u32 remote_node_index:12;
|
||||
u32 reserved0_1:4;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware how many simultaneous connections that
|
||||
* this remote node will support.
|
||||
*/
|
||||
u32 remote_node_port_width:4;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware which logical port to associate with this
|
||||
* remote node.
|
||||
*/
|
||||
u32 logical_port_index:3;
|
||||
u32 reserved0_2:5;
|
||||
|
||||
/**
|
||||
* This field will enable the I_T nexus loss timer for this remote node.
|
||||
*/
|
||||
u32 nexus_loss_timer_enable:1;
|
||||
|
||||
/**
|
||||
* This field is the for driver debug only and is not used.
|
||||
*/
|
||||
u32 check_bit:1;
|
||||
|
||||
/**
|
||||
* This field must be set to true when the hardware DMAs the remote node
|
||||
* context to the hardware SRAM. When the remote node is being invalidated
|
||||
* this field must be set to false.
|
||||
*/
|
||||
u32 is_valid:1;
|
||||
|
||||
/**
|
||||
* This field must be set to true.
|
||||
*/
|
||||
u32 is_remote_node_context:1;
|
||||
|
||||
/* WORD 1 - 2 */
|
||||
|
||||
/**
|
||||
* This is the low word of the remote device SAS Address
|
||||
*/
|
||||
u32 remote_sas_address_lo;
|
||||
|
||||
/**
|
||||
* This field is the high word of the remote device SAS Address
|
||||
*/
|
||||
u32 remote_sas_address_hi;
|
||||
|
||||
/* WORD 3 */
|
||||
/**
|
||||
* This field reprensets the function number assigned to this remote device.
|
||||
* This value must match the virtual function number that is being used to
|
||||
* communicate to the device.
|
||||
*/
|
||||
u32 function_number:8;
|
||||
u32 reserved3_1:8;
|
||||
|
||||
/**
|
||||
* This field provides the driver a way to cheat on the arbitration wait time
|
||||
* for this remote node.
|
||||
*/
|
||||
u32 arbitration_wait_time:16;
|
||||
|
||||
/* WORD 4 */
|
||||
/**
|
||||
* This field tells the SCU hardware how long this device may occupy the
|
||||
* connection before it must be closed.
|
||||
*/
|
||||
u32 connection_occupancy_timeout:16;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware how long to maintain a connection when
|
||||
* there are no frames being transmitted on the link.
|
||||
*/
|
||||
u32 connection_inactivity_timeout:16;
|
||||
|
||||
/* WORD 5 */
|
||||
/**
|
||||
* This field allows the driver to cheat on the arbitration wait time for this
|
||||
* remote node.
|
||||
*/
|
||||
u32 initial_arbitration_wait_time:16;
|
||||
|
||||
/**
|
||||
* This field is tells the hardware what to program for the connection rate in
|
||||
* the open address frame. See the SAS spec for valid values.
|
||||
*/
|
||||
u32 oaf_connection_rate:4;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware what to program for the features in the
|
||||
* open address frame. See the SAS spec for valid values.
|
||||
*/
|
||||
u32 oaf_features:4;
|
||||
|
||||
/**
|
||||
* This field tells the SCU hardware what to use for the source zone group in
|
||||
* the open address frame. See the SAS spec for more details on zoning.
|
||||
*/
|
||||
u32 oaf_source_zone_group:8;
|
||||
|
||||
/* WORD 6 */
|
||||
/**
|
||||
* This field tells the SCU hardware what to use as the more capibilities in
|
||||
* the open address frame. See the SAS Spec for details.
|
||||
*/
|
||||
u32 oaf_more_compatibility_features;
|
||||
|
||||
/* WORD 7 */
|
||||
u32 reserved7;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stp_remote_node_context - This structure contains the SCU hardware
|
||||
* definition for a STP remote node.
|
||||
*
|
||||
* STP Targets are not yet supported so this definition is a placeholder until
|
||||
* we do support them.
|
||||
*/
|
||||
struct stp_remote_node_context {
|
||||
/**
|
||||
* Placeholder data for the STP remote node.
|
||||
*/
|
||||
u32 data[8];
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* This union combines the SAS and SATA remote node definitions.
|
||||
*
|
||||
* union scu_remote_node_context
|
||||
*/
|
||||
union scu_remote_node_context {
|
||||
/**
|
||||
* SSP Remote Node
|
||||
*/
|
||||
struct ssp_remote_node_context ssp;
|
||||
|
||||
/**
|
||||
* STP Remote Node
|
||||
*/
|
||||
struct stp_remote_node_context stp;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __SCU_REMOTE_NODE_CONTEXT_HEADER__ */
|
942
drivers/scsi/isci/scu_task_context.h
Normal file
942
drivers/scsi/isci/scu_task_context.h
Normal 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_ */
|
1675
drivers/scsi/isci/task.c
Normal file
1675
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
367
drivers/scsi/isci/task.h
Normal 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_) */
|
225
drivers/scsi/isci/unsolicited_frame_control.c
Normal file
225
drivers/scsi/isci/unsolicited_frame_control.c
Normal 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;
|
||||
}
|
278
drivers/scsi/isci/unsolicited_frame_control.h
Normal file
278
drivers/scsi/isci/unsolicited_frame_control.h
Normal 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_ */
|
@ -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
|
||||
|
16
firmware/isci/isci_firmware.bin.ihex
Normal file
16
firmware/isci/isci_firmware.bin.ihex
Normal file
@ -0,0 +1,16 @@
|
||||
:10000000495343554F454D42E80018100002000087
|
||||
:1000100000000000000000000101000000000000DE
|
||||
:10002000FFFFCF5F0100000008DD0B0000FC0F00A8
|
||||
:10003000097C0B006EFC0A00FFFFCF5F010000008F
|
||||
:1000400008DD0B0000FC0F00097C0B006EFC0A00B1
|
||||
:10005000FFFFCF5F0100000008DD0B0000FC0F0078
|
||||
:10006000097C0B006EFC0A00FFFFCF5F010000005F
|
||||
:1000700008DD0B0000FC0F00097C0B006EFC0A0081
|
||||
:100080000101000000000000FFFFCF5F0200000040
|
||||
:1000900008DD0B0000FC0F00097C0B006EFC0A0061
|
||||
:1000A000FFFFCF5F0200000008DD0B0000FC0F0027
|
||||
:1000B000097C0B006EFC0A00FFFFCF5F020000000E
|
||||
:1000C00008DD0B0000FC0F00097C0B006EFC0A0031
|
||||
:1000D000FFFFCF5F0200000008DD0B0000FC0F00F7
|
||||
:0800E000097C0B006EFC0A0014
|
||||
:00000001FF
|
Loading…
Reference in New Issue
Block a user