|
@@ -270,6 +270,9 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha)
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_severity = 0x01;
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_severity = 0x01;
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt = 0x01;
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt = 0x01;
|
|
|
|
|
|
|
|
+ /* Disable end to end CRC checking */
|
|
|
|
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.crc_core_dump = (0x1 << 16);
|
|
|
|
+
|
|
for (i = 0; i < PM8001_MAX_SPCV_INB_NUM; i++) {
|
|
for (i = 0; i < PM8001_MAX_SPCV_INB_NUM; i++) {
|
|
pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
|
|
pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
|
|
PM8001_MPI_QUEUE | (64 << 16) | (0x00<<30);
|
|
PM8001_MPI_QUEUE | (64 << 16) | (0x00<<30);
|
|
@@ -353,6 +356,8 @@ static void update_main_config_table(struct pm8001_hba_info *pm8001_ha)
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_severity);
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_severity);
|
|
pm8001_mw32(address, MAIN_FATAL_ERROR_INTERRUPT,
|
|
pm8001_mw32(address, MAIN_FATAL_ERROR_INTERRUPT,
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt);
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt);
|
|
|
|
+ pm8001_mw32(address, MAIN_EVENT_CRC_CHECK,
|
|
|
|
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.crc_core_dump);
|
|
|
|
|
|
/* SPCv specific */
|
|
/* SPCv specific */
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping &= 0xCFFFFFFF;
|
|
pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping &= 0xCFFFFFFF;
|
|
@@ -1026,6 +1031,123 @@ pm80xx_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha, u8 vec)
|
|
pm80xx_chip_intx_interrupt_disable(pm8001_ha);
|
|
pm80xx_chip_intx_interrupt_disable(pm8001_ha);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void pm80xx_send_abort_all(struct pm8001_hba_info *pm8001_ha,
|
|
|
|
+ struct pm8001_device *pm8001_ha_dev)
|
|
|
|
+{
|
|
|
|
+ int res;
|
|
|
|
+ u32 ccb_tag;
|
|
|
|
+ struct pm8001_ccb_info *ccb;
|
|
|
|
+ struct sas_task *task = NULL;
|
|
|
|
+ struct task_abort_req task_abort;
|
|
|
|
+ struct inbound_queue_table *circularQ;
|
|
|
|
+ u32 opc = OPC_INB_SATA_ABORT;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!pm8001_ha_dev) {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("dev is null\n"));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ task = sas_alloc_slow_task(GFP_ATOMIC);
|
|
|
|
+
|
|
|
|
+ if (!task) {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("cannot "
|
|
|
|
+ "allocate task\n"));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ task->task_done = pm8001_task_done;
|
|
|
|
+
|
|
|
|
+ res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
|
|
|
|
+ if (res)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ ccb = &pm8001_ha->ccb_info[ccb_tag];
|
|
|
|
+ ccb->device = pm8001_ha_dev;
|
|
|
|
+ ccb->ccb_tag = ccb_tag;
|
|
|
|
+ ccb->task = task;
|
|
|
|
+
|
|
|
|
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
|
|
|
|
+
|
|
|
|
+ memset(&task_abort, 0, sizeof(task_abort));
|
|
|
|
+ task_abort.abort_all = cpu_to_le32(1);
|
|
|
|
+ task_abort.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
|
|
|
|
+ task_abort.tag = cpu_to_le32(ccb_tag);
|
|
|
|
+
|
|
|
|
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort, 0);
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void pm80xx_send_read_log(struct pm8001_hba_info *pm8001_ha,
|
|
|
|
+ struct pm8001_device *pm8001_ha_dev)
|
|
|
|
+{
|
|
|
|
+ struct sata_start_req sata_cmd;
|
|
|
|
+ int res;
|
|
|
|
+ u32 ccb_tag;
|
|
|
|
+ struct pm8001_ccb_info *ccb;
|
|
|
|
+ struct sas_task *task = NULL;
|
|
|
|
+ struct host_to_dev_fis fis;
|
|
|
|
+ struct domain_device *dev;
|
|
|
|
+ struct inbound_queue_table *circularQ;
|
|
|
|
+ u32 opc = OPC_INB_SATA_HOST_OPSTART;
|
|
|
|
+
|
|
|
|
+ task = sas_alloc_slow_task(GFP_ATOMIC);
|
|
|
|
+
|
|
|
|
+ if (!task) {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("cannot allocate task !!!\n"));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ task->task_done = pm8001_task_done;
|
|
|
|
+
|
|
|
|
+ res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
|
|
|
|
+ if (res) {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("cannot allocate tag !!!\n"));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* allocate domain device by ourselves as libsas
|
|
|
|
+ * is not going to provide any
|
|
|
|
+ */
|
|
|
|
+ dev = kzalloc(sizeof(struct domain_device), GFP_ATOMIC);
|
|
|
|
+ if (!dev) {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("Domain device cannot be allocated\n"));
|
|
|
|
+ sas_free_task(task);
|
|
|
|
+ return;
|
|
|
|
+ } else {
|
|
|
|
+ task->dev = dev;
|
|
|
|
+ task->dev->lldd_dev = pm8001_ha_dev;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ccb = &pm8001_ha->ccb_info[ccb_tag];
|
|
|
|
+ ccb->device = pm8001_ha_dev;
|
|
|
|
+ ccb->ccb_tag = ccb_tag;
|
|
|
|
+ ccb->task = task;
|
|
|
|
+ pm8001_ha_dev->id |= NCQ_READ_LOG_FLAG;
|
|
|
|
+ pm8001_ha_dev->id |= NCQ_2ND_RLE_FLAG;
|
|
|
|
+
|
|
|
|
+ memset(&sata_cmd, 0, sizeof(sata_cmd));
|
|
|
|
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
|
|
|
|
+
|
|
|
|
+ /* construct read log FIS */
|
|
|
|
+ memset(&fis, 0, sizeof(struct host_to_dev_fis));
|
|
|
|
+ fis.fis_type = 0x27;
|
|
|
|
+ fis.flags = 0x80;
|
|
|
|
+ fis.command = ATA_CMD_READ_LOG_EXT;
|
|
|
|
+ fis.lbal = 0x10;
|
|
|
|
+ fis.sector_count = 0x1;
|
|
|
|
+
|
|
|
|
+ sata_cmd.tag = cpu_to_le32(ccb_tag);
|
|
|
|
+ sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
|
|
|
|
+ sata_cmd.ncqtag_atap_dir_m_dad |= ((0x1 << 7) | (0x5 << 9));
|
|
|
|
+ memcpy(&sata_cmd.sata_fis, &fis, sizeof(struct host_to_dev_fis));
|
|
|
|
+
|
|
|
|
+ res = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd, 0);
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* mpi_ssp_completion- process the event that FW response to the SSP request.
|
|
* mpi_ssp_completion- process the event that FW response to the SSP request.
|
|
* @pm8001_ha: our hba card information
|
|
* @pm8001_ha: our hba card information
|
|
@@ -1480,22 +1602,50 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
|
|
struct ata_task_resp *resp ;
|
|
struct ata_task_resp *resp ;
|
|
u32 *sata_resp;
|
|
u32 *sata_resp;
|
|
struct pm8001_device *pm8001_dev;
|
|
struct pm8001_device *pm8001_dev;
|
|
- unsigned long flags = 0;
|
|
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
psataPayload = (struct sata_completion_resp *)(piomb + 4);
|
|
psataPayload = (struct sata_completion_resp *)(piomb + 4);
|
|
status = le32_to_cpu(psataPayload->status);
|
|
status = le32_to_cpu(psataPayload->status);
|
|
tag = le32_to_cpu(psataPayload->tag);
|
|
tag = le32_to_cpu(psataPayload->tag);
|
|
|
|
|
|
|
|
+ if (!tag) {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("tag null\n"));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
ccb = &pm8001_ha->ccb_info[tag];
|
|
ccb = &pm8001_ha->ccb_info[tag];
|
|
param = le32_to_cpu(psataPayload->param);
|
|
param = le32_to_cpu(psataPayload->param);
|
|
- t = ccb->task;
|
|
|
|
|
|
+ if (ccb) {
|
|
|
|
+ t = ccb->task;
|
|
|
|
+ pm8001_dev = ccb->device;
|
|
|
|
+ } else {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("ccb null\n"));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (t) {
|
|
|
|
+ if (t->dev && (t->dev->lldd_dev))
|
|
|
|
+ pm8001_dev = t->dev->lldd_dev;
|
|
|
|
+ } else {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("task null\n"));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((pm8001_dev && !(pm8001_dev->id & NCQ_READ_LOG_FLAG))
|
|
|
|
+ && unlikely(!t || !t->lldd_task || !t->dev)) {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("task or dev null\n"));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
ts = &t->task_status;
|
|
ts = &t->task_status;
|
|
- pm8001_dev = ccb->device;
|
|
|
|
- if (status)
|
|
|
|
|
|
+ if (!ts) {
|
|
PM8001_FAIL_DBG(pm8001_ha,
|
|
PM8001_FAIL_DBG(pm8001_ha,
|
|
- pm8001_printk("sata IO status 0x%x\n", status));
|
|
|
|
- if (unlikely(!t || !t->lldd_task || !t->dev))
|
|
|
|
|
|
+ pm8001_printk("ts null\n"));
|
|
return;
|
|
return;
|
|
|
|
+ }
|
|
|
|
|
|
switch (status) {
|
|
switch (status) {
|
|
case IO_SUCCESS:
|
|
case IO_SUCCESS:
|
|
@@ -1503,6 +1653,19 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
|
|
if (param == 0) {
|
|
if (param == 0) {
|
|
ts->resp = SAS_TASK_COMPLETE;
|
|
ts->resp = SAS_TASK_COMPLETE;
|
|
ts->stat = SAM_STAT_GOOD;
|
|
ts->stat = SAM_STAT_GOOD;
|
|
|
|
+ /* check if response is for SEND READ LOG */
|
|
|
|
+ if (pm8001_dev &&
|
|
|
|
+ (pm8001_dev->id & NCQ_READ_LOG_FLAG)) {
|
|
|
|
+ /* set new bit for abort_all */
|
|
|
|
+ pm8001_dev->id |= NCQ_ABORT_ALL_FLAG;
|
|
|
|
+ /* clear bit for read log */
|
|
|
|
+ pm8001_dev->id = pm8001_dev->id & 0x7FFFFFFF;
|
|
|
|
+ pm80xx_send_abort_all(pm8001_ha, pm8001_dev);
|
|
|
|
+ /* Free the tag */
|
|
|
|
+ pm8001_tag_free(pm8001_ha, tag);
|
|
|
|
+ sas_free_task(t);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
} else {
|
|
} else {
|
|
u8 len;
|
|
u8 len;
|
|
ts->resp = SAS_TASK_COMPLETE;
|
|
ts->resp = SAS_TASK_COMPLETE;
|
|
@@ -1807,16 +1970,39 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
|
|
u32 event = le32_to_cpu(psataPayload->event);
|
|
u32 event = le32_to_cpu(psataPayload->event);
|
|
u32 tag = le32_to_cpu(psataPayload->tag);
|
|
u32 tag = le32_to_cpu(psataPayload->tag);
|
|
u32 port_id = le32_to_cpu(psataPayload->port_id);
|
|
u32 port_id = le32_to_cpu(psataPayload->port_id);
|
|
- unsigned long flags = 0;
|
|
|
|
|
|
+ u32 dev_id = le32_to_cpu(psataPayload->device_id);
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
ccb = &pm8001_ha->ccb_info[tag];
|
|
ccb = &pm8001_ha->ccb_info[tag];
|
|
- t = ccb->task;
|
|
|
|
- pm8001_dev = ccb->device;
|
|
|
|
|
|
+
|
|
|
|
+ if (ccb) {
|
|
|
|
+ t = ccb->task;
|
|
|
|
+ pm8001_dev = ccb->device;
|
|
|
|
+ } else {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("No CCB !!!. returning\n"));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
if (event)
|
|
if (event)
|
|
PM8001_FAIL_DBG(pm8001_ha,
|
|
PM8001_FAIL_DBG(pm8001_ha,
|
|
- pm8001_printk("sata IO status 0x%x\n", event));
|
|
|
|
- if (unlikely(!t || !t->lldd_task || !t->dev))
|
|
|
|
|
|
+ pm8001_printk("SATA EVENT 0x%x\n", event));
|
|
|
|
+
|
|
|
|
+ /* Check if this is NCQ error */
|
|
|
|
+ if (event == IO_XFER_ERROR_ABORTED_NCQ_MODE) {
|
|
|
|
+ /* find device using device id */
|
|
|
|
+ pm8001_dev = pm8001_find_dev(pm8001_ha, dev_id);
|
|
|
|
+ /* send read log extension */
|
|
|
|
+ if (pm8001_dev)
|
|
|
|
+ pm80xx_send_read_log(pm8001_ha, pm8001_dev);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (unlikely(!t || !t->lldd_task || !t->dev)) {
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("task or dev null\n"));
|
|
return;
|
|
return;
|
|
|
|
+ }
|
|
|
|
+
|
|
ts = &t->task_status;
|
|
ts = &t->task_status;
|
|
PM8001_IO_DBG(pm8001_ha,
|
|
PM8001_IO_DBG(pm8001_ha,
|
|
pm8001_printk("port_id:0x%x, tag:0x%x, event:0x%x\n",
|
|
pm8001_printk("port_id:0x%x, tag:0x%x, event:0x%x\n",
|
|
@@ -3414,6 +3600,7 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
|
|
u32 ATAP = 0x0;
|
|
u32 ATAP = 0x0;
|
|
u32 dir;
|
|
u32 dir;
|
|
struct inbound_queue_table *circularQ;
|
|
struct inbound_queue_table *circularQ;
|
|
|
|
+ unsigned long flags;
|
|
u32 opc = OPC_INB_SATA_HOST_OPSTART;
|
|
u32 opc = OPC_INB_SATA_HOST_OPSTART;
|
|
memset(&sata_cmd, 0, sizeof(sata_cmd));
|
|
memset(&sata_cmd, 0, sizeof(sata_cmd));
|
|
circularQ = &pm8001_ha->inbnd_q_tbl[inb++];
|
|
circularQ = &pm8001_ha->inbnd_q_tbl[inb++];
|
|
@@ -3438,8 +3625,10 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
|
|
PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA\n"));
|
|
PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA\n"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag))
|
|
|
|
|
|
+ if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag)) {
|
|
|
|
+ task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
|
|
ncg_tag = hdr_tag;
|
|
ncg_tag = hdr_tag;
|
|
|
|
+ }
|
|
dir = data_dir_flags[task->data_dir] << 8;
|
|
dir = data_dir_flags[task->data_dir] << 8;
|
|
sata_cmd.tag = cpu_to_le32(tag);
|
|
sata_cmd.tag = cpu_to_le32(tag);
|
|
sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
|
|
sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
|
|
@@ -3547,6 +3736,55 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
|
|
(task->ata_task.atapi_packet[14] << 16) |
|
|
(task->ata_task.atapi_packet[14] << 16) |
|
|
(task->ata_task.atapi_packet[15] << 24)));
|
|
(task->ata_task.atapi_packet[15] << 24)));
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /* Check for read log for failed drive and return */
|
|
|
|
+ if (sata_cmd.sata_fis.command == 0x2f) {
|
|
|
|
+ if (pm8001_ha_dev && ((pm8001_ha_dev->id & NCQ_READ_LOG_FLAG) ||
|
|
|
|
+ (pm8001_ha_dev->id & NCQ_ABORT_ALL_FLAG) ||
|
|
|
|
+ (pm8001_ha_dev->id & NCQ_2ND_RLE_FLAG))) {
|
|
|
|
+ struct task_status_struct *ts;
|
|
|
|
+
|
|
|
|
+ pm8001_ha_dev->id &= 0xDFFFFFFF;
|
|
|
|
+ ts = &task->task_status;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&task->task_state_lock, flags);
|
|
|
|
+ ts->resp = SAS_TASK_COMPLETE;
|
|
|
|
+ ts->stat = SAM_STAT_GOOD;
|
|
|
|
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
|
|
|
|
+ task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
|
|
|
|
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
|
|
|
|
+ if (unlikely((task->task_state_flags &
|
|
|
|
+ SAS_TASK_STATE_ABORTED))) {
|
|
|
|
+ spin_unlock_irqrestore(&task->task_state_lock,
|
|
|
|
+ flags);
|
|
|
|
+ PM8001_FAIL_DBG(pm8001_ha,
|
|
|
|
+ pm8001_printk("task 0x%p resp 0x%x "
|
|
|
|
+ " stat 0x%x but aborted by upper layer "
|
|
|
|
+ "\n", task, ts->resp, ts->stat));
|
|
|
|
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
|
|
|
|
+ return 0;
|
|
|
|
+ } else if (task->uldd_task) {
|
|
|
|
+ spin_unlock_irqrestore(&task->task_state_lock,
|
|
|
|
+ flags);
|
|
|
|
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
|
|
|
|
+ mb();/* ditto */
|
|
|
|
+ spin_unlock_irq(&pm8001_ha->lock);
|
|
|
|
+ task->task_done(task);
|
|
|
|
+ spin_lock_irq(&pm8001_ha->lock);
|
|
|
|
+ return 0;
|
|
|
|
+ } else if (!task->uldd_task) {
|
|
|
|
+ spin_unlock_irqrestore(&task->task_state_lock,
|
|
|
|
+ flags);
|
|
|
|
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
|
|
|
|
+ mb();/*ditto*/
|
|
|
|
+ spin_unlock_irq(&pm8001_ha->lock);
|
|
|
|
+ task->task_done(task);
|
|
|
|
+ spin_lock_irq(&pm8001_ha->lock);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc,
|
|
ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc,
|
|
&sata_cmd, outb++);
|
|
&sata_cmd, outb++);
|
|
|
|
|