summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/hisi_sas
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/hisi_sas')
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas.h18
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_main.c238
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v1_hw.c36
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v2_hw.c130
4 files changed, 326 insertions, 96 deletions
diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index 4731d3241323..72c98522bd26 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -23,7 +23,7 @@
#include <scsi/sas_ata.h>
#include <scsi/libsas.h>
-#define DRV_VERSION "v1.5"
+#define DRV_VERSION "v1.6"
#define HISI_SAS_MAX_PHYS 9
#define HISI_SAS_MAX_QUEUES 32
@@ -56,6 +56,11 @@ enum dev_status {
HISI_SAS_DEV_EH,
};
+enum {
+ HISI_SAS_INT_ABT_CMD = 0,
+ HISI_SAS_INT_ABT_DEV = 1,
+};
+
enum hisi_sas_dev_type {
HISI_SAS_DEV_TYPE_STP = 0,
HISI_SAS_DEV_TYPE_SSP,
@@ -89,6 +94,13 @@ struct hisi_sas_port {
struct hisi_sas_cq {
struct hisi_hba *hisi_hba;
+ int rd_point;
+ int id;
+};
+
+struct hisi_sas_dq {
+ struct hisi_hba *hisi_hba;
+ int wr_point;
int id;
};
@@ -146,6 +158,9 @@ struct hisi_sas_hw {
struct hisi_sas_slot *slot);
int (*prep_stp)(struct hisi_hba *hisi_hba,
struct hisi_sas_slot *slot);
+ int (*prep_abort)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ int device_id, int abort_flag, int tag_to_abort);
int (*slot_complete)(struct hisi_hba *hisi_hba,
struct hisi_sas_slot *slot, int abort);
void (*phy_enable)(struct hisi_hba *hisi_hba, int phy_no);
@@ -185,6 +200,7 @@ struct hisi_hba {
struct Scsi_Host *shost;
struct hisi_sas_cq cq[HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_dq dq[HISI_SAS_MAX_QUEUES];
struct hisi_sas_phy phy[HISI_SAS_MAX_PHYS];
struct hisi_sas_port port[HISI_SAS_MAX_PHYS];
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c
index 18dd5ea2c721..2f872f784e10 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -17,6 +17,10 @@
static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device,
u8 *lun, struct hisi_sas_tmf_task *tmf);
+static int
+hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
+ struct domain_device *device,
+ int abort_flag, int tag);
static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device)
{
@@ -93,7 +97,7 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task,
slot->task = NULL;
slot->port = NULL;
hisi_sas_slot_index_free(hisi_hba, slot->idx);
- memset(slot, 0, sizeof(*slot));
+ /* slot memory is fully zeroed when it is reused */
}
EXPORT_SYMBOL_GPL(hisi_sas_slot_task_free);
@@ -116,6 +120,14 @@ static int hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba,
return hisi_hba->hw->prep_stp(hisi_hba, slot);
}
+static int hisi_sas_task_prep_abort(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ int device_id, int abort_flag, int tag_to_abort)
+{
+ return hisi_hba->hw->prep_abort(hisi_hba, slot,
+ device_id, abort_flag, tag_to_abort);
+}
+
/*
* This function will issue an abort TMF regardless of whether the
* task is in the sdev or not. Then it will do the task complete
@@ -192,27 +204,13 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_hba *hisi_hba,
return rc;
}
port = device->port->lldd_port;
- if (port && !port->port_attached && !tmf) {
- if (sas_protocol_ata(task->task_proto)) {
- struct task_status_struct *ts = &task->task_status;
-
- dev_info(dev,
- "task prep: SATA/STP port%d not attach device\n",
- device->port->id);
- ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAS_PHY_DOWN;
- task->task_done(task);
- } else {
- struct task_status_struct *ts = &task->task_status;
-
- dev_info(dev,
- "task prep: SAS port%d does not attach device\n",
- device->port->id);
- ts->resp = SAS_TASK_UNDELIVERED;
- ts->stat = SAS_PHY_DOWN;
- task->task_done(task);
- }
- return 0;
+ if (port && !port->port_attached) {
+ dev_info(dev, "task prep: %s port%d not attach device\n",
+ (sas_protocol_ata(task->task_proto)) ?
+ "SATA/STP" : "SAS",
+ device->port->id);
+
+ return SAS_PHY_DOWN;
}
if (!sas_protocol_ata(task->task_proto)) {
@@ -609,6 +607,9 @@ static void hisi_sas_dev_gone(struct domain_device *device)
dev_info(dev, "found dev[%lld:%x] is gone\n",
sas_dev->device_id, sas_dev->dev_type);
+ hisi_sas_internal_task_abort(hisi_hba, device,
+ HISI_SAS_INT_ABT_DEV, 0);
+
hisi_hba->hw->free_device(hisi_hba, sas_dev);
device->lldd_dev = NULL;
memset(sas_dev, 0, sizeof(*sas_dev));
@@ -729,6 +730,12 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == TMF_RESP_FUNC_SUCC) {
+ res = TMF_RESP_FUNC_SUCC;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == SAS_DATA_UNDERRUN) {
/* no error, but return the number of bytes of
* underrun
@@ -826,18 +833,22 @@ static int hisi_sas_abort_task(struct sas_task *task)
}
}
+ hisi_sas_internal_task_abort(hisi_hba, device,
+ HISI_SAS_INT_ABT_CMD, tag);
} else if (task->task_proto & SAS_PROTOCOL_SATA ||
task->task_proto & SAS_PROTOCOL_STP) {
if (task->dev->dev_type == SAS_SATA_DEV) {
- struct hisi_slot_info *slot = task->lldd_task;
-
- dev_notice(dev, "abort task: hba=%p task=%p slot=%p\n",
- hisi_hba, task, slot);
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ hisi_sas_internal_task_abort(hisi_hba, device,
+ HISI_SAS_INT_ABT_DEV, 0);
rc = TMF_RESP_FUNC_COMPLETE;
- goto out;
}
+ } else if (task->task_proto & SAS_PROTOCOL_SMP) {
+ /* SMP */
+ struct hisi_sas_slot *slot = task->lldd_task;
+ u32 tag = slot->idx;
+ hisi_sas_internal_task_abort(hisi_hba, device,
+ HISI_SAS_INT_ABT_CMD, tag);
}
out:
@@ -954,6 +965,157 @@ static int hisi_sas_query_task(struct sas_task *task)
return rc;
}
+static int
+hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, u64 device_id,
+ struct sas_task *task, int abort_flag,
+ int task_tag)
+{
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct device *dev = &hisi_hba->pdev->dev;
+ struct hisi_sas_port *port;
+ struct hisi_sas_slot *slot;
+ struct hisi_sas_cmd_hdr *cmd_hdr_base;
+ int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx;
+
+ if (!device->port)
+ return -1;
+
+ port = device->port->lldd_port;
+
+ /* simply get a slot and send abort command */
+ rc = hisi_sas_slot_index_alloc(hisi_hba, &slot_idx);
+ if (rc)
+ goto err_out;
+ rc = hisi_hba->hw->get_free_slot(hisi_hba, &dlvry_queue,
+ &dlvry_queue_slot);
+ if (rc)
+ goto err_out_tag;
+
+ slot = &hisi_hba->slot_info[slot_idx];
+ memset(slot, 0, sizeof(struct hisi_sas_slot));
+
+ slot->idx = slot_idx;
+ slot->n_elem = n_elem;
+ slot->dlvry_queue = dlvry_queue;
+ slot->dlvry_queue_slot = dlvry_queue_slot;
+ cmd_hdr_base = hisi_hba->cmd_hdr[dlvry_queue];
+ slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot];
+ slot->task = task;
+ slot->port = port;
+ task->lldd_task = slot;
+
+ memset(slot->cmd_hdr, 0, sizeof(struct hisi_sas_cmd_hdr));
+
+ rc = hisi_sas_task_prep_abort(hisi_hba, slot, device_id,
+ abort_flag, task_tag);
+ if (rc)
+ goto err_out_tag;
+
+ /* Port structure is static for the HBA, so
+ * even if the port is deformed it is ok
+ * to reference.
+ */
+ list_add_tail(&slot->entry, &port->list);
+ spin_lock(&task->task_state_lock);
+ task->task_state_flags |= SAS_TASK_AT_INITIATOR;
+ spin_unlock(&task->task_state_lock);
+
+ hisi_hba->slot_prep = slot;
+
+ sas_dev->running_req++;
+ /* send abort command to our chip */
+ hisi_hba->hw->start_delivery(hisi_hba);
+
+ return 0;
+
+err_out_tag:
+ hisi_sas_slot_index_free(hisi_hba, slot_idx);
+err_out:
+ dev_err(dev, "internal abort task prep: failed[%d]!\n", rc);
+
+ return rc;
+}
+
+/**
+ * hisi_sas_internal_task_abort -- execute an internal
+ * abort command for single IO command or a device
+ * @hisi_hba: host controller struct
+ * @device: domain device
+ * @abort_flag: mode of operation, device or single IO
+ * @tag: tag of IO to be aborted (only relevant to single
+ * IO mode)
+ */
+static int
+hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
+ struct domain_device *device,
+ int abort_flag, int tag)
+{
+ struct sas_task *task;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct device *dev = &hisi_hba->pdev->dev;
+ int res;
+ unsigned long flags;
+
+ if (!hisi_hba->hw->prep_abort)
+ return -EOPNOTSUPP;
+
+ task = sas_alloc_slow_task(GFP_KERNEL);
+ if (!task)
+ return -ENOMEM;
+
+ task->dev = device;
+ task->task_proto = device->tproto;
+ task->task_done = hisi_sas_task_done;
+ task->slow_task->timer.data = (unsigned long)task;
+ task->slow_task->timer.function = hisi_sas_tmf_timedout;
+ task->slow_task->timer.expires = jiffies + 20*HZ;
+ add_timer(&task->slow_task->timer);
+
+ /* Lock as we are alloc'ing a slot, which cannot be interrupted */
+ spin_lock_irqsave(&hisi_hba->lock, flags);
+ res = hisi_sas_internal_abort_task_exec(hisi_hba, sas_dev->device_id,
+ task, abort_flag, tag);
+ spin_unlock_irqrestore(&hisi_hba->lock, flags);
+ if (res) {
+ del_timer(&task->slow_task->timer);
+ dev_err(dev, "internal task abort: executing internal task failed: %d\n",
+ res);
+ goto exit;
+ }
+ wait_for_completion(&task->slow_task->completion);
+ res = TMF_RESP_FUNC_FAILED;
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == TMF_RESP_FUNC_COMPLETE) {
+ res = TMF_RESP_FUNC_COMPLETE;
+ goto exit;
+ }
+
+ /* TMF timed out, return direct. */
+ if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+ dev_err(dev, "internal task abort: timeout.\n");
+ if (task->lldd_task) {
+ struct hisi_sas_slot *slot = task->lldd_task;
+
+ hisi_sas_slot_task_free(hisi_hba, task, slot);
+ }
+ }
+ }
+
+exit:
+ dev_info(dev, "internal task abort: task to dev %016llx task=%p "
+ "resp: 0x%x sts 0x%x\n",
+ SAS_ADDR(device->sas_addr),
+ task,
+ task->task_status.resp, /* 0 is complete, -1 is undelivered */
+ task->task_status.stat);
+ sas_free_task(task);
+
+ return res;
+}
+
static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy)
{
hisi_sas_port_notify_formed(sas_phy);
@@ -1063,11 +1225,16 @@ static int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
for (i = 0; i < hisi_hba->queue_count; i++) {
struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+ struct hisi_sas_dq *dq = &hisi_hba->dq[i];
/* Completion queue structure */
cq->id = i;
cq->hisi_hba = hisi_hba;
+ /* Delivery queue structure */
+ dq->id = i;
+ dq->hisi_hba = hisi_hba;
+
/* Delivery queue */
s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS;
hisi_hba->cmd_hdr[i] = dma_alloc_coherent(dev, s,
@@ -1128,7 +1295,7 @@ static int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
memset(hisi_hba->breakpoint, 0, s);
hisi_hba->slot_index_count = max_command_entries;
- s = hisi_hba->slot_index_count / sizeof(unsigned long);
+ s = hisi_hba->slot_index_count / BITS_PER_BYTE;
hisi_hba->slot_index_tags = devm_kzalloc(dev, s, GFP_KERNEL);
if (!hisi_hba->slot_index_tags)
goto err_out;
@@ -1272,6 +1439,12 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev,
&hisi_hba->queue_count))
goto err_out;
+ if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)) &&
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
+ dev_err(dev, "No usable DMA addressing method\n");
+ goto err_out;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hisi_hba->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(hisi_hba->regs))
@@ -1319,13 +1492,6 @@ int hisi_sas_probe(struct platform_device *pdev,
hisi_hba = shost_priv(shost);
platform_set_drvdata(pdev, sha);
- if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)) &&
- dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
- dev_err(dev, "No usable DMA addressing method\n");
- rc = -EIO;
- goto err_out_ha;
- }
-
phy_nr = port_nr = hisi_hba->n_phy;
arr_phy = devm_kcalloc(dev, phy_nr, sizeof(void *), GFP_KERNEL);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
index 1abbc2e162df..c0ac49d8bc8d 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
@@ -490,25 +490,17 @@ static void config_id_frame_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0,
__swab32(identify_buffer[0]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1,
- identify_buffer[2]);
+ __swab32(identify_buffer[1]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2,
- identify_buffer[1]);
+ __swab32(identify_buffer[2]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3,
- identify_buffer[4]);
+ __swab32(identify_buffer[3]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4,
- identify_buffer[3]);
+ __swab32(identify_buffer[4]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5,
__swab32(identify_buffer[5]));
}
-static void init_id_frame_v1_hw(struct hisi_hba *hisi_hba)
-{
- int i;
-
- for (i = 0; i < hisi_hba->n_phy; i++)
- config_id_frame_v1_hw(hisi_hba, i);
-}
-
static void setup_itct_v1_hw(struct hisi_hba *hisi_hba,
struct hisi_sas_device *sas_dev)
{
@@ -774,8 +766,6 @@ static int hw_init_v1_hw(struct hisi_hba *hisi_hba)
msleep(100);
init_reg_v1_hw(hisi_hba);
- init_id_frame_v1_hw(hisi_hba);
-
return 0;
}
@@ -875,12 +865,13 @@ static int get_wideport_bitmap_v1_hw(struct hisi_hba *hisi_hba, int port_id)
static int get_free_slot_v1_hw(struct hisi_hba *hisi_hba, int *q, int *s)
{
struct device *dev = &hisi_hba->pdev->dev;
+ struct hisi_sas_dq *dq;
u32 r, w;
int queue = hisi_hba->queue;
while (1) {
- w = hisi_sas_read32_relaxed(hisi_hba,
- DLVRY_Q_0_WR_PTR + (queue * 0x14));
+ dq = &hisi_hba->dq[queue];
+ w = dq->wr_point;
r = hisi_sas_read32_relaxed(hisi_hba,
DLVRY_Q_0_RD_PTR + (queue * 0x14));
if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) {
@@ -903,10 +894,11 @@ static void start_delivery_v1_hw(struct hisi_hba *hisi_hba)
{
int dlvry_queue = hisi_hba->slot_prep->dlvry_queue;
int dlvry_queue_slot = hisi_hba->slot_prep->dlvry_queue_slot;
+ struct hisi_sas_dq *dq = &hisi_hba->dq[dlvry_queue];
- hisi_sas_write32(hisi_hba,
- DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14),
- ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS);
+ dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS;
+ hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14),
+ dq->wr_point);
}
static int prep_prd_sge_v1_hw(struct hisi_hba *hisi_hba,
@@ -1565,14 +1557,11 @@ static irqreturn_t cq_interrupt_v1_hw(int irq, void *p)
struct hisi_sas_complete_v1_hdr *complete_queue =
(struct hisi_sas_complete_v1_hdr *)
hisi_hba->complete_hdr[queue];
- u32 irq_value, rd_point, wr_point;
+ u32 irq_value, rd_point = cq->rd_point, wr_point;
irq_value = hisi_sas_read32(hisi_hba, OQ_INT_SRC);
hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue);
-
- rd_point = hisi_sas_read32(hisi_hba,
- COMPL_Q_0_RD_PTR + (0x14 * queue));
wr_point = hisi_sas_read32(hisi_hba,
COMPL_Q_0_WR_PTR + (0x14 * queue));
@@ -1600,6 +1589,7 @@ static irqreturn_t cq_interrupt_v1_hw(int irq, void *p)
}
/* update rd_point */
+ cq->rd_point = rd_point;
hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point);
return IRQ_HANDLED;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
index f96560431cf1..9825a3f49f53 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -117,6 +117,8 @@
#define SL_CONTROL (PORT_BASE + 0x94)
#define SL_CONTROL_NOTIFY_EN_OFF 0
#define SL_CONTROL_NOTIFY_EN_MSK (0x1 << SL_CONTROL_NOTIFY_EN_OFF)
+#define SL_CONTROL_CTA_OFF 17
+#define SL_CONTROL_CTA_MSK (0x1 << SL_CONTROL_CTA_OFF)
#define TX_ID_DWORD0 (PORT_BASE + 0x9c)
#define TX_ID_DWORD1 (PORT_BASE + 0xa0)
#define TX_ID_DWORD2 (PORT_BASE + 0xa4)
@@ -124,6 +126,9 @@
#define TX_ID_DWORD4 (PORT_BASE + 0xaC)
#define TX_ID_DWORD5 (PORT_BASE + 0xb0)
#define TX_ID_DWORD6 (PORT_BASE + 0xb4)
+#define TXID_AUTO (PORT_BASE + 0xb8)
+#define TXID_AUTO_CT3_OFF 1
+#define TXID_AUTO_CT3_MSK (0x1 << TXID_AUTO_CT3_OFF)
#define RX_IDAF_DWORD0 (PORT_BASE + 0xc4)
#define RX_IDAF_DWORD1 (PORT_BASE + 0xc8)
#define RX_IDAF_DWORD2 (PORT_BASE + 0xcc)
@@ -174,6 +179,10 @@
/* HW dma structures */
/* Delivery queue header */
/* dw0 */
+#define CMD_HDR_ABORT_FLAG_OFF 0
+#define CMD_HDR_ABORT_FLAG_MSK (0x3 << CMD_HDR_ABORT_FLAG_OFF)
+#define CMD_HDR_ABORT_DEVICE_TYPE_OFF 2
+#define CMD_HDR_ABORT_DEVICE_TYPE_MSK (0x1 << CMD_HDR_ABORT_DEVICE_TYPE_OFF)
#define CMD_HDR_RESP_REPORT_OFF 5
#define CMD_HDR_RESP_REPORT_MSK (0x1 << CMD_HDR_RESP_REPORT_OFF)
#define CMD_HDR_TLR_CTRL_OFF 6
@@ -214,6 +223,8 @@
#define CMD_HDR_DIF_SGL_LEN_MSK (0xffff << CMD_HDR_DIF_SGL_LEN_OFF)
#define CMD_HDR_DATA_SGL_LEN_OFF 16
#define CMD_HDR_DATA_SGL_LEN_MSK (0xffff << CMD_HDR_DATA_SGL_LEN_OFF)
+#define CMD_HDR_ABORT_IPTT_OFF 16
+#define CMD_HDR_ABORT_IPTT_MSK (0xffff << CMD_HDR_ABORT_IPTT_OFF)
/* Completion header */
/* dw0 */
@@ -221,6 +232,13 @@
#define CMPLT_HDR_RSPNS_XFRD_MSK (0x1 << CMPLT_HDR_RSPNS_XFRD_OFF)
#define CMPLT_HDR_ERX_OFF 12
#define CMPLT_HDR_ERX_MSK (0x1 << CMPLT_HDR_ERX_OFF)
+#define CMPLT_HDR_ABORT_STAT_OFF 13
+#define CMPLT_HDR_ABORT_STAT_MSK (0x7 << CMPLT_HDR_ABORT_STAT_OFF)
+/* abort_stat */
+#define STAT_IO_NOT_VALID 0x1
+#define STAT_IO_NO_DEVICE 0x2
+#define STAT_IO_COMPLETE 0x3
+#define STAT_IO_ABORTED 0x4
/* dw1 */
#define CMPLT_HDR_IPTT_OFF 0
#define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF)
@@ -549,25 +567,17 @@ static void config_id_frame_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0,
__swab32(identify_buffer[0]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1,
- identify_buffer[2]);
+ __swab32(identify_buffer[1]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2,
- identify_buffer[1]);
+ __swab32(identify_buffer[2]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3,
- identify_buffer[4]);
+ __swab32(identify_buffer[3]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4,
- identify_buffer[3]);
+ __swab32(identify_buffer[4]));
hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5,
__swab32(identify_buffer[5]));
}
-static void init_id_frame_v2_hw(struct hisi_hba *hisi_hba)
-{
- int i;
-
- for (i = 0; i < hisi_hba->n_phy; i++)
- config_id_frame_v2_hw(hisi_hba, i);
-}
-
static void setup_itct_v2_hw(struct hisi_hba *hisi_hba,
struct hisi_sas_device *sas_dev)
{
@@ -589,6 +599,7 @@ static void setup_itct_v2_hw(struct hisi_hba *hisi_hba,
qw0 = HISI_SAS_DEV_TYPE_SSP << ITCT_HDR_DEV_TYPE_OFF;
break;
case SAS_SATA_DEV:
+ case SAS_SATA_PENDING:
if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type))
qw0 = HISI_SAS_DEV_TYPE_STP << ITCT_HDR_DEV_TYPE_OFF;
else
@@ -672,9 +683,7 @@ static int reset_hw_v2_hw(struct hisi_hba *hisi_hba)
else
reset_val = 0x7ffff;
- /* Disable all of the DQ */
- for (i = 0; i < HISI_SAS_MAX_QUEUES; i++)
- hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0);
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0);
/* Disable all of the PHYs */
for (i = 0; i < hisi_hba->n_phy; i++) {
@@ -810,6 +819,8 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba)
hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x855);
hisi_sas_phy_write32(hisi_hba, i, SAS_PHY_CTRL, 0x30b9908);
hisi_sas_phy_write32(hisi_hba, i, SL_TOUT_CFG, 0x7d7d7d7d);
+ hisi_sas_phy_write32(hisi_hba, i, SL_CONTROL, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, TXID_AUTO, 0x2);
hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 0x10);
hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, 0xffffffff);
hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff);
@@ -901,8 +912,6 @@ static int hw_init_v2_hw(struct hisi_hba *hisi_hba)
msleep(100);
init_reg_v2_hw(hisi_hba);
- init_id_frame_v2_hw(hisi_hba);
-
return 0;
}
@@ -952,14 +961,8 @@ static void start_phys_v2_hw(unsigned long data)
static void phys_init_v2_hw(struct hisi_hba *hisi_hba)
{
- int i;
struct timer_list *timer = &hisi_hba->timer;
- for (i = 0; i < hisi_hba->n_phy; i++) {
- hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x6a);
- hisi_sas_phy_read32(hisi_hba, i, CHL_INT2_MSK);
- }
-
setup_timer(timer, start_phys_v2_hw, (unsigned long)hisi_hba);
mod_timer(timer, jiffies + HZ);
}
@@ -1010,12 +1013,13 @@ static int get_wideport_bitmap_v2_hw(struct hisi_hba *hisi_hba, int port_id)
static int get_free_slot_v2_hw(struct hisi_hba *hisi_hba, int *q, int *s)
{
struct device *dev = &hisi_hba->pdev->dev;
+ struct hisi_sas_dq *dq;
u32 r, w;
int queue = hisi_hba->queue;
while (1) {
- w = hisi_sas_read32_relaxed(hisi_hba,
- DLVRY_Q_0_WR_PTR + (queue * 0x14));
+ dq = &hisi_hba->dq[queue];
+ w = dq->wr_point;
r = hisi_sas_read32_relaxed(hisi_hba,
DLVRY_Q_0_RD_PTR + (queue * 0x14));
if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) {
@@ -1038,9 +1042,11 @@ static void start_delivery_v2_hw(struct hisi_hba *hisi_hba)
{
int dlvry_queue = hisi_hba->slot_prep->dlvry_queue;
int dlvry_queue_slot = hisi_hba->slot_prep->dlvry_queue_slot;
+ struct hisi_sas_dq *dq = &hisi_hba->dq[dlvry_queue];
+ dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS;
hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14),
- ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS);
+ dq->wr_point);
}
static int prep_prd_sge_v2_hw(struct hisi_hba *hisi_hba,
@@ -1563,6 +1569,30 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot,
goto out;
}
+ /* Use SAS+TMF status codes */
+ switch ((complete_hdr->dw0 & CMPLT_HDR_ABORT_STAT_MSK)
+ >> CMPLT_HDR_ABORT_STAT_OFF) {
+ case STAT_IO_ABORTED:
+ /* this io has been aborted by abort command */
+ ts->stat = SAS_ABORTED_TASK;
+ goto out;
+ case STAT_IO_COMPLETE:
+ /* internal abort command complete */
+ ts->stat = TMF_RESP_FUNC_COMPLETE;
+ goto out;
+ case STAT_IO_NO_DEVICE:
+ ts->stat = TMF_RESP_FUNC_COMPLETE;
+ goto out;
+ case STAT_IO_NOT_VALID:
+ /* abort single io, controller don't find
+ * the io need to abort
+ */
+ ts->stat = TMF_RESP_FUNC_FAILED;
+ goto out;
+ default:
+ break;
+ }
+
if ((complete_hdr->dw0 & CMPLT_HDR_ERX_MSK) &&
(!(complete_hdr->dw0 & CMPLT_HDR_RSPNS_XFRD_MSK))) {
@@ -1775,6 +1805,32 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba,
return 0;
}
+static int prep_abort_v2_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ int device_id, int abort_flag, int tag_to_abort)
+{
+ struct sas_task *task = slot->task;
+ struct domain_device *dev = task->dev;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct hisi_sas_port *port = slot->port;
+
+ /* dw0 */
+ hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/
+ (port->id << CMD_HDR_PORT_OFF) |
+ ((dev_is_sata(dev) ? 1:0) <<
+ CMD_HDR_ABORT_DEVICE_TYPE_OFF) |
+ (abort_flag << CMD_HDR_ABORT_FLAG_OFF));
+
+ /* dw1 */
+ hdr->dw1 = cpu_to_le32(device_id << CMD_HDR_DEV_ID_OFF);
+
+ /* dw7 */
+ hdr->dw7 = cpu_to_le32(tag_to_abort << CMD_HDR_ABORT_IPTT_OFF);
+ hdr->transfer_tags = cpu_to_le32(slot->idx);
+
+ return 0;
+}
+
static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
{
int i, res = 0;
@@ -1818,9 +1874,6 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
frame_rcvd[i] = __swab32(idaf);
}
- /* Get the linkrates */
- link_rate = hisi_sas_read32(hisi_hba, PHY_CONN_RATE);
- link_rate = (link_rate >> (phy_no * 4)) & 0xf;
sas_phy->linkrate = link_rate;
hard_phy_linkrate = hisi_sas_phy_read32(hisi_hba, phy_no,
HARD_PHY_LINKRATE);
@@ -1855,16 +1908,21 @@ end:
static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
{
int res = 0;
- u32 phy_cfg, phy_state;
+ u32 phy_state, sl_ctrl, txid_auto;
hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1);
- phy_cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
-
phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
-
hisi_sas_phy_down(hisi_hba, phy_no, (phy_state & 1 << phy_no) ? 1 : 0);
+ sl_ctrl = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL,
+ sl_ctrl & ~SL_CONTROL_CTA_MSK);
+
+ txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO);
+ hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
+ txid_auto | TXID_AUTO_CT3_MSK);
+
hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, CHL_INT0_NOT_RDY_MSK);
hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 0);
@@ -1986,7 +2044,7 @@ static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p)
struct hisi_sas_slot *slot;
struct hisi_sas_itct *itct;
struct hisi_sas_complete_v2_hdr *complete_queue;
- u32 irq_value, rd_point, wr_point, dev_id;
+ u32 irq_value, rd_point = cq->rd_point, wr_point, dev_id;
int queue = cq->id;
complete_queue = hisi_hba->complete_hdr[queue];
@@ -1994,8 +2052,6 @@ static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p)
hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue);
- rd_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_RD_PTR +
- (0x14 * queue));
wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR +
(0x14 * queue));
@@ -2043,6 +2099,7 @@ static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p)
}
/* update rd_point */
+ cq->rd_point = rd_point;
hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point);
return IRQ_HANDLED;
}
@@ -2239,6 +2296,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = {
.prep_smp = prep_smp_v2_hw,
.prep_ssp = prep_ssp_v2_hw,
.prep_stp = prep_ata_v2_hw,
+ .prep_abort = prep_abort_v2_hw,
.get_free_slot = get_free_slot_v2_hw,
.start_delivery = start_delivery_v2_hw,
.slot_complete = slot_complete_v2_hw,
OpenPOWER on IntegriCloud