Commit b50102d3 authored by Dan Williams's avatar Dan Williams Committed by James Bottomley

[SCSI] isci: atapi support

Based on original implementation from Jiangbi Liu and Maciej Trela.

ATAPI transfers happen in two-to-three stages.  The two stage atapi
commands are those that include a dma data transfer.  The data transfer
portion of these operations is handled by the hardware packet-dma
acceleration.  The three-stage commands do not have a data transfer and
are handled without hardware assistance in raw frame mode.

stage1: transmit host-to-device fis to notify the device of an incoming
atapi cdb.  Upon reception of the pio-setup-fis repost the task_context
to perform the dma transfer of the cdb+data (go to stage3), or repost
the task_context to transmit the cdb as a raw frame (go to stage 2).

stage2: wait for hardware notification of the cdb transmission and then
go to stage 3.

stage3: wait for the arrival of the terminating device-to-host fis and
terminate the command.

To keep the implementation simple we only support ATAPI packet-dma
protocol (for commands with data) to avoid needing to handle the data
transfer manually (like we do for SATA-PIO).  This may affect
compatibility for a small number of devices (see
ATA_HORKAGE_ATAPI_MOD16_DMA).

If the data-transfer underruns, or encounters an error the
device-to-host fis is expected to arrive in the unsolicited frame queue
to pass to libata for disposition.  However, in the DONE_UNEXP_FIS (data
underrun) case it appears we need to craft a response.  In the
DONE_REG_ERR case we do receive the UF and propagate it to libsas.
Signed-off-by: default avatarMaciej Trela <maciej.trela@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 4f3f812d
...@@ -386,6 +386,18 @@ static bool is_remote_device_ready(struct isci_remote_device *idev) ...@@ -386,6 +386,18 @@ static bool is_remote_device_ready(struct isci_remote_device *idev)
} }
} }
/*
* called once the remote node context has transisitioned to a ready
* state (after suspending RX and/or TX due to early D2H fis)
*/
static void atapi_remote_device_resume_done(void *_dev)
{
struct isci_remote_device *idev = _dev;
struct isci_request *ireq = idev->working_request;
sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
}
enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
u32 event_code) u32 event_code)
{ {
...@@ -432,6 +444,16 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, ...@@ -432,6 +444,16 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
if (status != SCI_SUCCESS) if (status != SCI_SUCCESS)
return status; return status;
if (state == SCI_STP_DEV_ATAPI_ERROR) {
/* For ATAPI error state resume the RNC right away. */
if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX ||
scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) {
return sci_remote_node_context_resume(&idev->rnc,
atapi_remote_device_resume_done,
idev);
}
}
if (state == SCI_STP_DEV_IDLE) { if (state == SCI_STP_DEV_IDLE) {
/* We pick up suspension events to handle specifically to this /* We pick up suspension events to handle specifically to this
...@@ -625,6 +647,7 @@ enum sci_status sci_remote_device_complete_io(struct isci_host *ihost, ...@@ -625,6 +647,7 @@ enum sci_status sci_remote_device_complete_io(struct isci_host *ihost,
case SCI_STP_DEV_CMD: case SCI_STP_DEV_CMD:
case SCI_STP_DEV_NCQ: case SCI_STP_DEV_NCQ:
case SCI_STP_DEV_NCQ_ERROR: case SCI_STP_DEV_NCQ_ERROR:
case SCI_STP_DEV_ATAPI_ERROR:
status = common_complete_io(iport, idev, ireq); status = common_complete_io(iport, idev, ireq);
if (status != SCI_SUCCESS) if (status != SCI_SUCCESS)
break; break;
...@@ -1020,6 +1043,7 @@ static const struct sci_base_state sci_remote_device_state_table[] = { ...@@ -1020,6 +1043,7 @@ static const struct sci_base_state sci_remote_device_state_table[] = {
[SCI_STP_DEV_NCQ_ERROR] = { [SCI_STP_DEV_NCQ_ERROR] = {
.enter_state = sci_stp_remote_device_ready_ncq_error_substate_enter, .enter_state = sci_stp_remote_device_ready_ncq_error_substate_enter,
}, },
[SCI_STP_DEV_ATAPI_ERROR] = { },
[SCI_STP_DEV_AWAIT_RESET] = { }, [SCI_STP_DEV_AWAIT_RESET] = { },
[SCI_SMP_DEV_IDLE] = { [SCI_SMP_DEV_IDLE] = {
.enter_state = sci_smp_remote_device_ready_idle_substate_enter, .enter_state = sci_smp_remote_device_ready_idle_substate_enter,
......
...@@ -243,6 +243,15 @@ enum sci_remote_device_states { ...@@ -243,6 +243,15 @@ enum sci_remote_device_states {
*/ */
SCI_STP_DEV_NCQ_ERROR, SCI_STP_DEV_NCQ_ERROR,
/**
* This is the ATAPI error state for the STP ATAPI remote device.
* This state is entered when ATAPI device sends error status FIS
* without data while the device object is in CMD state.
* A suspension event is expected in this state.
* The device object will resume right away.
*/
SCI_STP_DEV_ATAPI_ERROR,
/** /**
* This is the READY substate indicates the device is waiting for the RESET task * This is the READY substate indicates the device is waiting for the RESET task
* coming to be recovered from certain hardware specific error. * coming to be recovered from certain hardware specific error.
......
This diff is collapsed.
...@@ -96,7 +96,6 @@ enum sci_request_protocol { ...@@ -96,7 +96,6 @@ enum sci_request_protocol {
* to wait for another fis or if the transfer is complete. Upon * 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. * 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 * @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 { struct isci_stp_request {
u32 pio_len; u32 pio_len;
...@@ -107,7 +106,6 @@ struct isci_stp_request { ...@@ -107,7 +106,6 @@ struct isci_stp_request {
u8 set; u8 set;
u32 offset; u32 offset;
} sgl; } sgl;
u32 device_cdb_len;
}; };
struct isci_request { struct isci_request {
...@@ -248,6 +246,32 @@ enum sci_base_request_states { ...@@ -248,6 +246,32 @@ enum sci_base_request_states {
*/ */
SCI_REQ_STP_PIO_DATA_OUT, SCI_REQ_STP_PIO_DATA_OUT,
/*
* While in this state the IO request object is waiting for the TC
* completion notification for the H2D Register FIS
*/
SCI_REQ_ATAPI_WAIT_H2D,
/*
* While in this state the IO request object is waiting for either a
* PIO Setup.
*/
SCI_REQ_ATAPI_WAIT_PIO_SETUP,
/*
* The non-data IO transit to this state in this state after receiving
* TC completion. While in this state IO request object is waiting for
* D2H status frame as UF.
*/
SCI_REQ_ATAPI_WAIT_D2H,
/*
* When transmitting raw frames hardware reports task context completion
* after every frame submission, so in the non-accelerated case we need
* to expect the completion for the "cdb" frame.
*/
SCI_REQ_ATAPI_WAIT_TC_COMP,
/* /*
* The AWAIT_TC_COMPLETION sub-state indicates that the started raw * The AWAIT_TC_COMPLETION sub-state indicates that the started raw
* task management request is waiting for the transmission of the * task management request is waiting for the transmission of the
......
...@@ -801,7 +801,7 @@ void sas_slave_destroy(struct scsi_device *scsi_dev) ...@@ -801,7 +801,7 @@ void sas_slave_destroy(struct scsi_device *scsi_dev)
struct domain_device *dev = sdev_to_domain_dev(scsi_dev); struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
if (dev_is_sata(dev)) if (dev_is_sata(dev))
dev->sata_dev.ap->link.device[0].class = ATA_DEV_NONE; sas_to_ata_dev(dev)->class = ATA_DEV_NONE;
} }
int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason) int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason)
......
...@@ -389,6 +389,11 @@ sdev_to_domain_dev(struct scsi_device *sdev) { ...@@ -389,6 +389,11 @@ sdev_to_domain_dev(struct scsi_device *sdev) {
return starget_to_domain_dev(sdev->sdev_target); return starget_to_domain_dev(sdev->sdev_target);
} }
static inline struct ata_device *sas_to_ata_dev(struct domain_device *dev)
{
return &dev->sata_dev.ap->link.device[0];
}
static inline struct domain_device * static inline struct domain_device *
cmd_to_domain_dev(struct scsi_cmnd *cmd) cmd_to_domain_dev(struct scsi_cmnd *cmd)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment